Vor kurzem bin ich auf den Artikel Literate
Programming von Donald Knuth
gestoßen. Da ich auch selber immer auf der Suche bin meine Projekte besser zu
dokumentieren habe ich mich ein wenig mit seinem Ansatz beschäftigt.
Die Grundidee ist folgende: Programme sollten nicht in erster Linie für
Maschinen geschrieben werden sondern damit andere Menschen sie gut verstehen
können. Ein Programm sollte sich lesen lassen wie ein literarisches Werk und es
sollte Freude machen sich damit zu beschäftigen. Um das zu erreichen wird der
Programmcode wie eine Erzählung aufgebaut bei der Erklärungen und Quellcode
miteinander verwoben werden. Aus dem so erstellten Text kann man zwei
verschiedene Ausgaben erzeugen:
Eine saubere Dokumentation, die sich wie ein Buch liest
Ein compilierbarer Quelltext (oder bei interpretierten Sprachen ein
ausführbares Skript)
Die Idee dahinter ist gut (wer will nicht eine Dokumentation, welche genauso
interessant wie eine Erzählung ist) zeigt aber in der Praxis einige Probleme.
Man hat einen weiteren Erzeugungsschritt
Da der Quelltext in dieser Form nicht direkt kompilierbar ist braucht man immer
ein weiteres Programm. Das macht es schwieriger mit anderen an einem Projekt
zusammen zu arbeiten. Erzeugt man den funktionierenden Quelltext und macht
diesen anderen zugänglich (damit sich der Kompilieraufwand verkleinert) hat man
den eigentlichen Nutzen des ganzen Konzeptes wieder verpasst und eventuelle
Änderungen von dritten müssen mit großem Aufwand in den ursprünglichen
Quelltext eingefügt werden.
Auch beim Debuggen ergeben sich schnell Probleme da die Zeilenverweise auf den
generierten Quelltext statt auf die ursprüngliche Quelle verweisen.
Möglicherweise kann man beiden Problemen mit besseren Tools abhelfen aber diese
zu erstellen (und dauerhaft zu pflegen) ist mit großem Aufwand verbunden.
Es ist schwer fehlerhafte oder ineffektive Implementierungen aufzuzeigen
Vor kurzem habe ich einige Softwaredokumentationen gelesen die tatsächlich sehr
interssant waren (eines war ein Dokument über die Implementierung von lua
5.0 bei dem anderen ging es um
Graphendatenbanken). Keiner dieser Texte wurde gemeinsam mit dem Quelltext als
“literate” Programm erstellt obwohl sie sich zum Teil intensiv mit Details der
Implementierung beschäftigten. Was sehr dazu beitrug, dass diese Texte
interessant waren und die Entscheidungen nachvollziehbar waren war, dass das
propagierte Softwaredesign als ineffektiv oder fehlerhaft dargetstellten
Ansätzen gegenübergestellt wurde. Das ist mit literate programing sehr schwer
umsetzbar, da entweder das Programm nicht kompilieren würde oder die
fehlerhaften Teile in keiner Weise validiert werden könnten.
Die Dokumentation ist statisch
Da die Dokumentation in einem nicht editierbaren Format vorliegt kann der Leser
nicht unmittelbar Änderungen vornehmen. Entwickler am Projekt werden also mit
hoher Wahrscheinlichkeit die Dokumentation nicht (oder nur ein einziges Mal
ganz am Anfang) durchlesen.
Um das zu umgehen ist es sehr wichtig, das der beschreibende Text bereits im
Quelltext gut lesbar ist und sich natürlich in den Programmcode einfügt.
Lösungsansätze
Das es Probleme gibt soll nicht heißen, die Idee hinter literate programing
wäre schlecht. Gerade der Gedanke die Anordnung des Quelltextes danach zu
wählen, wie man die Prinipien einem anderen erklären würde kann die
Quellcodestruktur deutlich verbessern. Dadurch werden automatisch die Details
einer Implementierung erst nach dem Konzept der Architektur erklärt was sofort
zu einem besseren Verständnis sowohl der Details als auch der übergeordneten
Konzepte beiträgt.
Den Quelltext als Erzählung aufbauen
In den meisten Sprachen lassen sich diese Prinzipien anwenden, indem man die
Kommentare richtig nutzt und das Programm geschickt struckturiert, ohne ein
Programm zu gebrauchen welches aus dem Ursprungstext erst richtigen Quellcode
erzeugt. Bei Sprachen, welche nicht flexibel genug in Bezug auf die
Struckturierung oder ihre Kommentare sind, sollte man ohnehin überdenken, ob
man nicht lieber eine andere Sprache nutzen will.
Will man einige übergeordnete Konzepte oder zum Quelltext verschiedene
Betrachtungsweisen (z.B. wenn man mehrere Erzählstränge paralell führt um einen
bestimmten Aspeckt einer Architektur besonders heraus zu stellen) erklären kann
man ein markdown-Dokument (oder welche Auszeichnungssprache man nutzen möchte)
erstellen, in welchem Verweise auf den Quelltext eingefügt sind. Oft ist der
Text nur dann verständlich, wenn man die Quelltexte sofort lesen kann und nicht
erst ein weiteres Dokument öffnen muss. Daher sollte man für Dokumente dieser
Art immer ein script oder ein tool haben, welches überprüft, ob die
referenzierte Stelle mit dem Text im Dokument übereinstimmt und ansonsten eine
Fehlermeldung ausgibt.
Einfache Markupsprachen in Kommentaren verwenden
Die Kommentare sollten in einem normalen Editor gut lesbar sein. Daher sollten
keine Sprachen wie Tex oder html in den Kommentaren verwendet werden, sondern
Markup-Sprachen wie asciidoc, markdown oder textile. Wenn der eigene Editor
Bilder nicht unmittelbar anzeigt (was bei den meisten Texteditoren der Fall
sein dürfte) sollten alle schematischen Darstellungen als Ascii-Art oder in
einer sich intuitiv erschließenden Darstellungssprache erstellt werden
(Verwendet man einen anderen Editor sollte man sich definitiv als Team darauf
einigen und sich bewusst machen, wie stark man sich dadurch einschränkt).
Druckausgaben generieren (aber nicht Zwischenquellcode)
Man kann extra Programme nutzen um aus diesem Quelltext dann eine druckbare
Dokumentation zu erstellen. Dabei sollte man darauf achten, dass an Stellen wo
der Quelltext erklärt wird man unmittelbare Verweise auf den editierbaren
Quellcode hat. Bei Opensource-Programmen kann man z.B. einen Link auf die
passenden Zeilen in github hinterlegen (dort kann man online editieren). Oder
man könnte mit einem extra Programm (z.B. als Browser Plugin) den Editor der
Wahl des Benutzers an der passenden Zeile öffnen.
Tests als Dokumentation aufbauen
Viele Unit Test Systeme verfolgen die Idee von
BDD. Dabei wird in
weiten Teilen ohnehin normale, menschliche Sprache verwendet um das gewünschte
Verhalten von Software zu dokumentieren. Man kann diese Unittest auch als
Dokumentation schreiben und (mit den richtigen tools) auch eine Druckausgabe
generieren.
Submodule bieten die Möglichkeit Unterprojekte in ein Repository einzubinden
ohne dass ihre Versionsinformationen verloren gehen. Das ist sehr nützlich wenn
ein Projekt von einem anderen Projekt als Abhängigkeit benötigt wird und dabei
die genaue Version entscheidend ist.
Git stellt dafür den Befehl git submodule zur Verfügung.
$ git submodule add ${SUBREPO_URL}
Wird dieser Befehl ausgeführt lädt git das Projekt automatisch herunter und
speichert die aktuellen Versionsinformationen im Index des Hauptprojektes. Dies
ist sehr platzsparend da nur Verweise auf das Submodul gespeichert werden statt
alles zu kopieren.
Umfassendere Informationen zu Submodulen finden sich bei Progit.
Bestehende Unterverzeichnisse als Submodul einbinden
Oft entwickelt sich die Projektstruktur aus den Gegebenheiten heraus ohne eine
gezielte Planung. Oder Unterprojekte mussten erstellt werden bevor mit der
Entwicklung des Hauptprojektes begonnen werden konnte. So kann es passieren,
dass ein Projekt entsteht welches bereits Repositories in seinen
Unterverzeichnissen enthält. In der Vergangenheit hatte ich das Problem, dass
ich Unterprojekte nur über den weiter oben beschriebenen Befehl aufnehmen
konnte. Wenn bereits ein Unterverzeichnis mit gleichem Namen existierte gab git
beim Versuch das Repository des Unterprojektes auszuchecken eine Fehlermeldung
aus. Damit das Repository als Unterprojekt aufgenommen wird muss auf jeden
Fall der Pfad dorthin als Argument übergeben werden.
Beim Arbeiten mit Vim kommt man immer wieder mit einem
temporären Puffer in Berührung, in welchem beispielsweise die beim Kompilieren
zwischengespeichert werden. Dieser Buffer funktioniert als eine Art Link-Liste
mit deren Hilfe man an die Position des jeweiligen Fehlers springen kann.
Beim Kompilieren wird der Puffer mit dem Befehl
:make
gefüllt werden. Das aktuelle Editierfenster springt automatisch zum ersten
Fehler. Das ist sehr nützlich um sofort an die Stellen im Quelltext zu kommen,
an denen man Korrekturen vornehmen muss.
Mit
:cfirst:cnext
:cprevious
:clast
navigiert man von einer Stelle zur nächsten. Manchmal ist es allerdings
sinnvoll die ganze Liste sehen zu können und anhand dessen eine Vorauswahl
treffen zu können um Folge- oder weniger wichtige Fehler zu übergehen und sich
auf die wichtigen Stellen konzentrieren zu können. Dazu wird folgender Befehl
verwendet:
:copen
Es öffnet sich ein neues Fenster, in dem die Fehler und deren Stellen
aufgelistet sind. Durch drücken der Entertaste springt man an die Stelle,
welche auf der aktuellen Zeile verlinkt ist.
Weitere Anwendungen
Natürlich ist die Verwendung als Fehlerliste nur eine von vielen möglichen
Verwendungen. Viele weitere Plugins setzen diese Funktionalität ebenfalls ein.
Zum Beispiel benutzt das Plugin
fugitive (Ein Wrapper zur Benutzung
von git aus Vim heraus) sie für die Befehle Ggrep und
Glog.
Eines der wichtigsten Werkzeuge für jeden Programmierer ist der Editor. Ich
persönlich bevorzuge Vim für diese Aufgabe. Auf ein
besonders nützliches Feature dieses Editors möchte ich heute eingehen:
Den visuellen Block Modus
Dieser Modus ermöglicht es Text spaltenweise zu bearbeiten. Man erreicht ihn
unter normalen Umständen mit der Tastenkombination “CTRL-V”. Vor allem, wenn
man Tabellarische Daten oder Quelltexte bearbeitet kann man das gut gebrauchen.
So lassen sich beispielsweise ähnliche Anweisungen in einem Quelltext mit nur
einem Befehl schreiben, ändern oder Teile davon löschen, ohne dass man die
entsprechende Befehlsfolge in jeder Zeile neu ausführen muss. Allerdings
funktioniert das nur dann so richtig gut, wenn die jeweiligen Spalten genau
übereinander liegen und exakt die gleiche Länge haben. Was kann man tun um mit
diesem Problem fertig zu werden.
Suchen und Ersetzen im visuellen Block Modus
Ein weiteres nützliches Feature von Vim ist der “substitute”-Befehl. Dieser
kann in Kombination mit dem visuellen Block Modus viele Problemlösungen
erleichtern. Wechselt man bei gewähltem visuellen Bereich in den ex-Modus wird
als Kontext aber leider nicht der ausgewählte Bereich, sondern alle Zeilen in
denen der Bereich liegt ausgewählt. Den visuellen Bereich gibt man mit /\%V an.
Zum Beispiel
:'<,'>s/\%Vpattern/ersatz/g
Den visuellen Bereich nachträglich anpassen
Manchmal möchte man den visuellen Bereich für mehrere Befehle verwenden. Um den
selben Bereich wie beim vorigen Mal auszuwählen gibt man einfach gv ein.
In anderen Fällen möchte man die Auswahl des Bereiches vielleicht erweitern
oder anpassen, ohne den ganzen Bereich erneugt auszuwählen. Hierzu gibt man
während man im visuellen Modus ist die Befehle o oder O ein um den Curser
jeweils in die engegegngesetzte Ecke des visuellen Bereiches zu bewegen bzw. an
das andere Ende der Zeile.
Weitere Möglichkeit und Erklärungen zum visuellen Modus bekommt man mit
Manchmal möchte man gerne die Defines des Compilers wissen. Dies geschieht zum
Beispiel wenn man platformspezifische Informationen mit #ifdef Anweisungen
berücksichtigen möchte oder muss. Leider ist es kaum möglich diese Angaben aus
der Dokumentation zu ermitteln. Die folgende Befehlsfolge hilft weiter:
Manchmal möchte man Dateien nur einmal in git einchecken und sie danach in die
.gitignore-Datei packen. Sie dienen dann als eine Art template, welches jeder
bei sich anpassen kann ohne das diese Änderungen Auswirkungen auf den auf dem
Server gespeicherten Stand haben. Dabei tritt das Problem auf, dass git Dateien
welche einmal in das Repository aufgenommen wurden immer im Index aktualisiert
auch wenn sie in der Ignore-Liste stehen.
Mit dem Befehl
$ git update-index --assume-unchanged ${FILE} ...
können die betreffenden Dateien und Ordner ignoriert werden. Dies hat ausserdem
den Vorteil dass der
$ git status
Befehl unwichtige Änderungen nicht anzeigt und man sich auf das konzentrieren
kann was wichtig ist. Will man die Datei wieder aufnehmen so führt man den
Befehl
Dies ist der erste Eintrag in meinem Blog. Anders als gewöhnliche Blogs basiert
er auf Jekyll.
Was ist so cool daran?
Normalerweise sitzt man an seinem Browser und tippt die Einträge direkt über das
Webinterface ein. Aber wie wir alle wissen ist ein Browser kein ordentlicher
Editor. Ausserdem lassen sich die einmal eingegebenen Daten nicht mehr
ordentlich verwalten, sobald sie einmal auf dem Server sind.
Welche Möglichkeiten haben wir?
Ideal wäre es mit git arbeiten zu können und die Dateien in einem einfachen
ASCII-Format zu speichern. Sprachen wie
textile,
asciidoc und
markdown bieten sich dafür an.
Auf meiner Suche nach einer Lösung dieser Art stieß ich auf den Artikel
Blogging like a
Hacker
von Tom Preston Werner (einem der github-Gründer). Offensichtlich hatte er das
gleiche Problem. Er schrieb die Blogging-Software mit der dieser Blog erstellt
wird. Da das ganze aber git-basiert ist lässt es sich wunderbar mit github
integrieren. Eine Anleitung dazu findet sich unter http://pages.github.com.
Endlich ist es möglich Webseiten und Blogs so zu verwalten, wie man es sich
immer gewünscht hat.