Pinned Post
- Link abrufen
- X
- Andere Apps
Spec Driven Development mit OpenSpec: ein Praxisbeispiel
Die meisten Änderungen an einer Software fangen mit einem Halbsatz an: „Bau mal eben ein Cover in den Artikel ein.” Und dann geht es sofort in den Editor. Unterwegs treffen wir ein Dutzend kleiner Entscheidungen, raten, was wohl gemeint war, und am Ende läuft es irgendwie. Nur weiß hinterher niemand mehr genau, was das System eigentlich können soll. Diese Wahrheit steckt dann im Code, verteilt über viele Dateien, und entfernt sich mit jedem Commit ein Stück weiter von der ursprünglichen Absicht.
Spec Driven Development, kurz SDD, dreht die Reihenfolge um. Zuerst wird beschrieben, was das System tun soll. Erst danach geht es an das Wie. In diesem Artikel zeige ich, was dahintersteckt, wie der Prozess mit dem Werkzeug OpenSpec (openspec.dev) konkret aussieht und welche Befehle man dafür braucht. Als roter Faden dient eine echte Änderung aus dem Projekt pdf_finder.
Was Spec Driven Development ist
Im Kern trennt SDD zwei Dinge, die sonst gern verschwimmen.
Die Spezifikation beschreibt das Verhalten. Sie ist beobachtbar, überprüfbar und sagt nichts über die technische Umsetzung. Ihre Frage lautet: Was tut das System? Der Code dagegen ist nur eine von vielen möglichen Umsetzungen dieses Verhaltens und beantwortet die Frage nach dem Wie.
Der eigentliche Trick ist, die Absicht nicht länger im Code zu verstecken, sondern zu einem eigenen, versionierten Artefakt zu machen. Das klingt zunächst nach zusätzlicher Arbeit, zahlt sich aber an mehreren Stellen aus. Man kann über eine Spezifikation reden, bevor eine Zeile Code existiert, und Missverständnisse fallen auf, solange sie noch billig zu beheben sind. Die Spec wird zur maßgeblichen Quelle dafür, wie sich das System verhält. Der Code muss dazu passen, nicht umgekehrt. Und jede Verhaltensänderung bekommt eine dokumentierte Begründung samt Verlauf.
Mit Test Driven Development hat das weniger zu tun, als man denkt. TDD schreibt zuerst einen Test, also ausführbaren Code. SDD schreibt zuerst eine Spezifikation, also lesbares Verhalten. Die beiden vertragen sich bestens, denn aus jedem Szenario der Spec wird später ein Testfall.
OpenSpec: das Werkzeug
OpenSpec gibt dem Ganzen eine handhabbare Form. Ein paar Begriffe muss man kennen.
Ein Change ist eine abgeschlossene Änderung, etwa „füge ein Cover für Artikel hinzu”. Jeder Change liegt in einem eigenen Ordner und bündelt seine Planungsartefakte. Davon gibt es üblicherweise vier: die proposal.md mit dem Warum und Was, die design.md mit dem Wie und den Abwägungen, die eigentliche Spezifikation unter specs/<capability>/spec.md und die tasks.md als abhakbare Umsetzungsliste.
Eine Capability ist eine benannte Fähigkeit des Systems, zum Beispiel article-cover. Jede Capability hat genau eine kanonische Spezifikation unter openspec/specs/. Ein Change beschreibt diese Spec nicht komplett neu, sondern nur die Differenz dazu: Was kommt hinzu (ADDED), was ändert sich (MODIFIED), was fällt weg (REMOVED). Man nennt das eine Delta-Spec. Ist ein Change fertig, wandert sein Delta in die kanonische Spec, und der Change selbst landet im Archiv.
Dahinter steht eine einfache Idee. Der Ordner openspec/specs/ hält immer den aktuellen Sollzustand, openspec/changes/ die laufenden Änderungen daran. Nach dem Merge fließt das Delta in die Specs, der Change wird archiviert, und die kanonische Spezifikation bleibt sauber und vollständig.
Der Prozess an einem echten Beispiel
Nehmen wir eine reale Änderung aus pdf_finder. Der KI-Agent des Projekts entwirft Beiträge. Blogger-Entwürfe bekamen schon automatisch ein Titelbild, Artikel-Entwürfe noch nicht. Genau diesen Artikel hier hat derselbe Mechanismus mit einem Cover versehen. Der Change heißt article-cover.
Erst das Proposal
Bevor Code entsteht, wird die Absicht festgehalten. In der proposal.md steht sinngemäß: Blogger-Entwürfe bekommen automatisch ein Titelbild, Artikel bisher nicht; als eigenständiges, teilbares Dokument wirkt ein Artikel mit Titelbild deutlich hochwertiger, besonders im PDF-Export. Dazu benennt das Proposal die betroffene Capability, hier die neue article-cover, und den erwarteten Einfluss auf den Code. Von Implementierungsdetails ist bewusst noch keine Rede.
Dann die Spezifikation
Jetzt kommt das Herzstück, die Frage nach dem genauen Verhalten. Anforderungen werden mit SHALL oder MUST formuliert, Szenarien im WHEN/THEN-Format. So sieht ein Requirement aus der article-cover-Spec aus:
### Requirement: Artikel-Entwurf erhält ein Titelbild
Ein Artikel-Entwurf des KI-Agenten SHALL, analog zum Blogger-Entwurf,
ein automatisch aus dem Titel erzeugtes Cover erhalten, das oben in den
Artikel-Body eingefügt wird. Die Erzeugung MUST best effort sein: Schlägt
das Rendern fehl, bleibt der Artikel ohne Cover, ohne Abbruch.
#### Scenario: Cover wird in den Artikel eingefügt
- **WHEN** ein Artikel-Entwurf erzeugt wird und das Cover-Rendering gelingt
- **THEN** steht das Titelbild als erstes Element oben im Artikel-Body
#### Scenario: Cover-Rendering schlägt fehl
- **WHEN** ein Artikel-Entwurf erzeugt wird, das Cover-Rendering fehlschlägt
- **THEN** entsteht der Artikel-Entwurf unverändert ohne Cover und ohne FehlerAn dem kleinen Ausschnitt sieht man die Disziplin dahinter. Jede Anforderung ist normativ, jedes Szenario ein möglicher Testfall. Sogar der Fehlerfall ist festgeschrieben und bleibt nicht dem Zufall der Implementierung überlassen.
Das Design mit seinen Abwägungen
Erst hier kommt die Technik ins Spiel. Die design.md hält Entscheidungen samt Alternativen fest, etwa dass der vorhandene Cover-Generator wiederverwendet und nur um einen konfigurierbaren „Kicker” ergänzt wird (die Zeile oben links, „ARTIKEL” statt „BLOG”), statt einen zweiten Generator zu bauen. Solche Notizen sind viel wert, wenn man Monate später fragt, warum es eigentlich so gelöst wurde.
Zum Schluss die Tasks
Die tasks.md zerlegt die Arbeit in überprüfbare Schritte, Tests inklusive:
## 2. Cover für Artikel einfügen (apps/common)
- [x] 2.1 with_cover/2: Klausel auf Guard platform in ["blogger","article"]
erweitern; Kicker je Plattform wählen ("ARTIKEL" bzw. "BLOG")
## 4. Tests & Verifikation
- [x] 4.1 Test: Artikel bekommt Cover oben im Body; scheitert das Cover,
bleibt der Body unverändert; korrekter Kicker wird übergebenJede Task ist so formuliert, dass klar ist, wann sie erledigt ist. Erst wenn die Liste steht, beginnt die Umsetzung, und jeder Haken hängt an einem Szenario aus der Spec.
Die Befehle
OpenSpec fährt man komplett über die Kommandozeile. Am Anfang steht oft eine offene Denkphase mit explore, danach läuft die eigentliche Änderung über drei zusammengesetzte Kommandos, die intern das openspec-CLI benutzen.
Vor dem Zyklus: explore, die KI als Sparringspartner
Nicht jeder Change beginnt mit propose. Oft steht am Anfang eine offene Frage, und dafür gibt es explore, den Explore-Modus. Das ist absichtlich kein Workflow, sondern eine Haltung. Die KI wird zum Sparringspartner, der zuhört, nachfragt, Annahmen anzweifelt, Optionen gegenüberstellt und Ideen aufzeichnet. Der Unterschied zu einem beliebigen Chatfenster: Sie kennt den tatsächlichen Code des Projekts und bezieht ihn ein. Gebaut wird in diesem Modus bewusst nichts.
Für Solo-Entwickler ist das der eigentlich spannende Teil. Wer allein arbeitet, hat niemanden, mit dem er ein Feature vor der Umsetzung durchsprechen kann. Kein Whiteboard, kein „Was hältst du davon?” über den Schreibtisch hinweg. Diese Rolle übernimmt der Explore-Modus. Man kann eine Idee laut durchdenken, etwa „Ich überlege, Echtzeit-Kollaboration einzubauen”, und statt sofort loszucoden spannt die KI erst den Möglichkeitsraum auf. Man kann Optionen vergleichen lassen, Postgres gegen SQLite zum Beispiel, und zwar entlang der konkreten Randbedingungen des eigenen Projekts statt nach allgemeinen Faustregeln. Und man bekommt Widerspruch, auch gegen die eigene Lieblingslösung. Das ersetzt zumindest ein Stück weit das fehlende Vier-Augen-Prinzip.
Zwei Leitplanken sorgen dafür, dass nichts aus dem Ruder läuft. Der Modus implementiert nichts, er denkt nur mit. Und er speichert nichts von selbst. Reift eine Entscheidung, bietet er an, sie festzuhalten, im Proposal, im Design oder in der Spec, aber die Entscheidung triffst du.
openspec list # zeigt, woran gerade gearbeitet wird, als Kontext fürs Gespräch
# "Ich denke über ein Cover für Artikel nach. Lohnt sich das? Wie würde das aussehen?"Kristallisiert sich etwas heraus, ist der Übergang fließend. „Klingt tragfähig, soll ich ein Proposal daraus machen?” Aus dem Gespräch wird ein Change, und der eigentliche Zyklus beginnt. So wird SDD auch für Einzelkämpfer zum Dialog.
Der Lebenszyklus: propose, apply, archive
propose legt einen neuen Change an und erzeugt in einem Rutsch alle Artefakte aus einer Beschreibung:
# "Passe den Artikel so an, dass auch ein Cover erstellt und eingefügt wird"
openspec new change "article-cover"Heraus kommt der Change-Ordner mit den vier Dateien, fertig zur Durchsicht.
apply arbeitet die tasks.md ab. Man liest die Kontext-Artefakte, setzt Schritt für Schritt um und hakt ab. Nach jedem Schritt sollte der zugehörige Test grün sein.
archive schließt einen fertigen Change ab. Das Delta wandert in die kanonischen Specs unter openspec/specs/, der Change selbst nach openspec/changes/archive/JJJJ-MM-TT-<name>/.
Die Befehle darunter
Diese Kommandos ruft man auch einzeln auf. Sie bilden das Rückgrat des Ablaufs:
openspec new change "<name>"legt das Gerüst an.openspec listzeigt die aktiven Changes samt Fortschritt.openspec status --change "<name>"zeigt, welche Artefakte fertig sind und ob der Change umsetzungsbereit ist.openspec instructions <artefakt> --change "<name>"liefert Vorlage und Regeln für ein einzelnes Artefakt.openspec validate "<name>"prüft die Struktur. Hat jedes Requirement ein Szenario? Steht dortSHALLstatt „should”? Stimmt das Delta-Format?openspec archive "<name>"verschiebt den Change ins Archiv.
Ein typischer Durchlauf:
openspec new change "article-cover"
openspec status --change article-cover
# proposal, specs, design, tasks schreiben
openspec validate article-cover
# implementieren, Tasks abhaken
openspec archive article-covervalidate ist dabei der stille Helfer, der einen vor Flüchtigkeitsfehlern bewahrt. Ein Requirement ohne Szenario oder ein „should” statt „SHALL” fällt sofort auf.
Besonders stark in Bestandsprojekten
Es hält sich das Gerücht, SDD lohne sich nur auf der grünen Wiese. Meine Erfahrung ist das Gegenteil. Gerade in gewachsenen Bestandssystemen, im sogenannten Brownfield, spielt OpenSpec seine Stärken aus. Dort steckt das Verhalten meist ungeschrieben im Code, verteilt über Jahre und viele Hände. Der delta- und change-basierte Ansatz passt gut dazu.
Man muss nicht das ganze System auf einmal spezifizieren. Jeder Change bringt nur sein Delta mit, und openspec/specs/ wächst mit jeder Änderung, die man ohnehin anfasst. Wer ein bestehendes Feature ändert, hält dabei den gewünschten Sollzustand als Requirement fest, und so entsteht die Spezifikation nach und nach dort, wo wirklich gearbeitet wird, statt in einem separaten Dokument, das schnell veraltet. Die design.md jedes Change dokumentiert die Entscheidungen. In Altcode, wo niemand mehr weiß, warum etwas so ist, schafft das Nachvollziehbarkeit für später. Und ist das gewünschte Verhalten erst als WHEN/THEN-Szenario festgeschrieben, kann man die Implementierung umbauen und gegen die Spec prüfen, ohne Angst, unbemerkt etwas zu zerstören.
Das Beispiel oben ist ein solcher Fall. pdf_finder ist kein frisches Projekt, sondern ein gewachsenes Umbrella Projekt. Der article-cover-Change hat einer bestehenden Fähigkeit, dem Beitrags-Agenten, neues Verhalten hinzugefügt und dieses Verhalten zuerst als Spec festgehalten, statt es still im Code zu vergraben. So gewinnt man verlorenes Wissen Stück für Stück zurück, ohne den Betrieb anzuhalten.
Fazit
SDD macht aus einer vagen Absicht ein überprüfbares Artefakt und verschiebt Diskussion und Review dorthin, wo sie am wenigsten kosten, nämlich vor den Code. OpenSpec gibt dem Vorgehen eine schlanke, dateibasierte Form und einen klaren Ablauf über explore, propose, apply und archive. Das Beispiel aus pdf_finder zeigt, dass das kein Selbstzweck ist. Selbst dieser Artikel verdankt sein Titelbild einer Anforderung, die zuerst als WHEN/THEN-Szenario dastand und dann umgesetzt wurde. Erst denken, dann coden, und zwar an einer Stelle, die auch morgen noch die Wahrheit sagt.
- Link abrufen
- X
- Andere Apps
Kommentare