Vor einigen Tagen habe ich über Teeodohr berichtet – den praktischen Alltagshelfer beim Zubereiten von Tee. Im letzten Beitrag ging es hauptsächlich um die hardwareseitigen Verbesserungen, die ich an dem hasenförmigen Tee-Bot vorgenommen habe, und die dabei aufgetretenen Probleme. Diesmal befasse ich mich mit der Software, welche Teeodohr erst in die Lage versetzt, seine Aufgaben bravourös zu erfüllen. Vor diesem Hintergrund möchte ich einige Punkte aus der professionellen Softwareentwicklung heranziehen und für private Maker greifbar machen.
Die Original-Software von Teeodohr (Download auf der Seite des Make Magazins) umfasst die wichtigsten Grundfunktionen, mit denen die meisten Teetrinker schon glücklich werden sollten. Würde man alle Funktionen – wie zu Beginn eines professionellen Entwicklungsprojekts – in Form einer Anforderungsanalyse darstellen, sähe das in etwa so aus:
- Teeodohr soll gestartet werden, wenn der Nutzer ihm die Möhre in die Pfote gibt.
- Beim Starten soll Teeodohr eine einfache Melodie spielen und sein Ohr bis in die aufgestellte Position bewegen. Danach soll er in den betriebsbereiten Zustand übergehen.
- In aufgestellter Position soll es möglich sein, einen Teebeutel an Teeodohrs Ohr zu befestigen, ohne dass dieser in eine neben Teeodohr stehende, mit Wasser gefüllte Tasse ragt.
- Im betriebsbereiten Zustand soll der Nutzer mittels des Tasters „3-5-7“ zwischen folgenden Ziehzeiten wechseln können: 3, 5, 7, 10 und 12 Minuten.
- Die Ziehzeit von 3 Minuten soll beim Wechsel in den betriebsbereiten Zustand standardmäßig ausgewählt sein.
- Im betriebsbereiten Zustand soll dem Nutzer mittels drei LEDs, die jeweils 3, 5 und 7 Minuten repräsentieren, die momentan ausgewählte Ziehzeit anzeigen.
- Im betriebsbereiten Zustand soll der Nutzer mittels des Tasters „Start/Stop“ die Teezubereitung starten können. Teeodohr soll daraufhin für die zuvor ausgewählten Zeit in den Zustand „Zubereitung“ übergehen.
- Zu Beginn des Zustands „Zubereitung“ soll Teeodohrs Ohr in die abgesenkte Position gebracht werden.
- In abgesenkter Position soll ein an Teeodohrs Ohr befestiger Teebeutel in eine neben ihm stehende Tasse mit Wasser ragen.
- Der aktive Zustand „Zubereitung“ soll dem Nutzer durch Animation der drei LEDs angezeigt werden.
- Während des Zustands „Zubereitung“ soll Teeodohrs Ohr nach jeder vollen Minute kurzzeitig in aufgestellte Position bewegt werden.
- Nach Ablauf der Zubereitungszeit soll eine einfache Meldie abgespielt, Teeodohrs Ohr in aufgestellte Positoin bewegt und vom Zustand „Zubereitung“ in den betriebsbereiten Zustand gewechselt werden.
- Der Nutzer soll die Zubereitung durch drücken des „Start/Stop“ Tasters jederzeit abbrechen können.
- Die Taster sollen entprellt sein, sodass kein mehrfaches Auslösen einer Aktion auftritt.
- Die Bewegung von Teeodohrs Ohr soll nicht dazu führen, dass ein daran befestiger Teebeutel herunterfällt, weggeschleudert wird oder neben die Tasse tropft.
- Teeodohr soll ausgeschaltet werden, sobald der Nutzer ihm die Möhre aus der Pfote nimmt.
Diese schon recht lange Liste ist bei Weitem nicht ausreichend oder vollständig. Es fehlen z. B. wichtige Details zur Melodie und zu den Abmaßen der zu unterstützenden Tassen und Teebeutel. Weiterhin ist unklar, wie weit Teeodohr beim Zubereiten sein Ohr senken soll. Welche Animation sollen eigentlich die LEDs während der Zubereitung ausführen? Ich bin mir sicher, dass du noch weitere Spezifikationslücken und Interpretationsspielraum entdeckt hast.
Bei einer professionellen Softwareentwicklung müssten all diese Fragen und getroffenen Annahmen entweder gleich zu Beginn (Wasserfall-Modell) oder im Projektverlauf iterativ (Agile Methode) mit dem Auftraggeber geklärt und abgestimmt werden. Dies hat den Zweck, dass Auftraggeber und Entwickler das gleiche Verständnis vom Endprodukt haben, Missverständnisse aus dem Weg geräumt werden und sich keine bösen Überraschungen ergeben, wenn ein Zwischenstand oder das fertige Programm präsentiert wird. Da wir hier glücklicherweise unser eigener Kunde sind und alles selbst entscheiden dürfen, ersparen wir uns den nicht unerheblichen Aufwand. Letztendlich hat Andreas Engel, Teeodohrs Schöpfer und Autor der Original-Software, schon jedes Detail explizit kodiert. Man könnte hier sagen: Der Code ist die Wahrheit. Er ist eine vollständige Spezifikation geworden, die sogar ausführbar ist.
Apropos Code
Das schöne an der Arduino-Plattform ist, dass so gut wie jeder Interessierte sofort loslegen und ohne große Mühe ein lauffähiges Programm erstellen kann. Gerade als Neuling ist das wichtig, denn frühe und fortwährende Erfolgserlebnisse steigern die Motivation und den Ansporn zur Fertigstellung des eigenen Projekts. Implementiert man nun nach und nach die geforderten Funktionen (Anforderungen), zeigen sich unter Umständen bald weitere Probleme:
- Mit komplexer werdenden Logiken und steigender Zeilenzahl verliert man immer mehr den Überblick über das eigene Programm.
- Programmierfehler (Bugs) schleichen sich ein und führen zu unerwartetem Verhalten.
- Funktionen, die früher schon einmal funktioniert haben, tun dies nicht mehr.
- Die Implementierung weiterer Funktionen wird immer komplizierter und benötigt immer mehr und mehr Zeit.
In der professionellen Softwareentwicklung versucht man dieses Szenario so gut wie möglich zu vermeiden. Hier können wir von den Profis lernen und einige von deren Prinzipien anwenden, nämlich:
Clean Code ist ein Begriff aus der Softwaretechnik […]. Als „sauber“ bezeichnen Softwareentwickler in erster Linie Quellcode, aber auch Dokumente, Konzepte, Regeln und Verfahren, die intuitiv verständlich sind. Als intuitiv verständlich gilt alles, was mit wenig Aufwand und in kurzer Zeit richtig verstanden werden kann. Vorteile von Clean Code sind stabilere und effizient wartbarere Programme, d. h. kürzere Entwicklungszeiten bei Funktionserweiterung und Fehlerbehebungen.
Wikipedia
Um zu „sauberem“ Code zu gelangen, haben sich einige Regeln, Prinzipien und Vorgehensweisen etabliert. Ich möchte hier nur kurz auf die eingehen, die ich bei Teeodohrs ursprünglicher Software vermisst habe. Eine sehr lesenswerte Lektüre zu diesem Thema ist das Buch Clean Code von Robert C. Martin*, welches sich sehr detailliert und mit vielen Beispielen diesem Thema widmet.
- Einheitliche Formatierungen (Coding-Style): Code lässt sich leichter verstehen und ein Bug schneller entdecken, wenn man nicht durch sich ständig ändernde Formatierungen abgelenkt oder verunsichert wird. Zu Formatierungen zählen z. B. Einrückungen, maximale Zeilenlänge, Zeilenumbrüche, Leerzeilen, Leerzeichen zwischen Operatoren oder Klammersetzungen. Deshalb sollte einem Regelwerk, dem so genannten Style Guide, gefolgt werden, das man entweder selbst festlegt oder von etablierten Quellen übernimmt. Es existieren auch Tools, welche den eigenen Code untersuchen und Verstöße gegen den Style anzeigen oder sogar automatisch korrigieren.
- Konsistenz bei der Wahl von Bezeichnern: „Namen sind Schall und Rauch“ – NEIN! Namen von Variablen, Makros, Funktionen, Klassen und Dateien sind essenziell, wenn es um die Kommunikation ihrer Intention geht. Im Idealfall lässt sich von einem Namen schon der Zweck und die Daseinsberechtigung eines Konstrukts ableiten, was das intuitive Verstädnis des Programmcodes fördert. Wichtig ist hier auch, die Schreibweise konsistent zu halten, d. h. sich z. B. entweder für Camel Case oder Snake Case zu entscheiden. Verbreitet ist beispielsweise auch, Konstanten in Großbuchstaben zu schreiben. Weiterhin sollte auf genau eine natürliche Sprache zurückgegriffen und nicht zwischen deutschen und englischen Bezeichnungen gewechselt werden..
- Keine Magic Numbers: Werden im Programmcode Zahlenwerte verwendet, deren Herkunft oder Bedeutung nicht offensichtlich ist, spricht man von sog. Magic Numbers. Diese sollten zum besseren Verständnis als klar benannte Konstante definiert werden. Dieses Vorgehen zwingt den Entwickler, das Kind beim Namen zu nennen und seine Intention explizit auszudrücken. Beispiele für Magic Numbers im Arduino-Umfeld sind z. B. Pin-Nummern, Wartezeiten oder Analog-Schwellwerte. Ein weiterer Vorteil der Konstantendefinition ist übrigens, dass man Änderungen an den Zahlenwerten nur noch an einer Stelle durchführen muss, was mich gleich zum nächsten Punkt bringt.
- Don’t Repeat Yourself: Dieses Prinzip sagt aus, gleiche oder ähnliche Funktionsweisen nicht mehrfach zu implementieren, d. h. vor allem Codeteile nicht per Copy-and-Paste zu vermehren. Besser ist es, sie in (ggf. parametrisierte) Funktionen oder Klassen zu extrahieren. Das verhindert Flüchtigkeitsfehler, erleichtert spätere Anpassungen und beschleunigt das Verständnis.
- Kommentare machen schlechten Code nicht besser: Sieht man sich gezwungen, zu einer oder mehreren Codezeilen einen Kommmentar zu schreiben, dann hat man im Sinne von Clean Code etwas falsch gemacht. Es ist ein Zeichen dafür, dass der Code allein die Intention des Entwicklers nicht gut genug ausdrückt. Natürlich muss man diese Aussage pragmatisch sehen, denn es gibt Situationen, in denen ein Kommentar wertvolle (Rand-)Informationen ergänzt, die sich nur sehr schwer in Code ausdrücken lassen. Der Aufwand für eine Code-Umstrukturierung wäre hier nicht gerechtfertigt. Trotzdem sollte die Priorität lieber auf Code-Refactorings (Funktionen extrahieren, Namen ändern, Konstanten einführen etc.) als auf dem Hinzufügen von Kommentaren liegen.
Teeodohrs Original-Programm umfasst 163 Zeilen. Man könnte meinen, dass dies noch eine einigermaßen überschaubare Menge an Code ist und der Overhead durch Clean Code Prinzipien hier nicht gerechtfertigt ist. Es kommt darauf an. Sieht man Teeodohr als einmaliges Spaßprojekt mit wenigen Beteiligten und soll voraussichtlich nie wieder etwas daran geändert werden, ist es durchaus legitim, den Weg des geringsten Widerstands zu gehen und das Programm einfach irgendwie zum Laufen zu bekommen. Soll die Software dagegen ein langes Leben haben und auch zukünftig von fremden Entwicklern verstanden und erweitert werden (Stichwort Open Source), halte ich die Clean Code Prinzipien für unverzichtbar. Eine Codezeile wird nur einmal in ihrem Leben geschrieben aber unzählige Male gelesen. Der Fokus bei der Code-Erstellung sollte daher auf intuitiver Verständlichkeit liegen.
Ein weiteres essenzielles Thema bei der Softwareentwicklung im Profi-Bereich sind automatisierte Tests. Diese habe ich in diesem Beitrag bewusst ausgeklammert, da ich damit noch zu wenig Erfahrung auf der Arduino-Plattform gesammelt habe.
Fazit
Auch im privaten oder Hobby-Umfeld können einige Praktiken aus der professionellen Softwareentwicklung genutzt werden, um strukturiert und möglichst fehlerfrei Arduino-Programme zu schreiben. Eine ausufernde schriftliche Anforderungsanalyse ist für Teeodohr wohl nicht notwendig. Dennoch sollte man vor der dem Schreiben der ersten Zeile Code zumindest darüber nachdenken, wozu der Teehase am Ende überhaupt im Stande sein soll. Die Clean Code Regeln, Prinzipien und Vorgehensweisen helfen dabei, den Code intuitiv verständlich zu gestalten. Soweit es praktikabel ist, würde Ich immer darauf zurückgreifen, denn man weiß nie, wann man selbst oder Andere den Code wieder einmal anfassen müssen.