Protokoll-Spezifikation einmal schreiben, in vier Programmiersprachen generieren lassen

Embedded Systems Software Agentic Engineering

Protokolle sind der Ort, an dem gute Absichten scheitern. Auf dem Papier steht ein sauberes Interface, in der Praxis läuft C-Firmware auf dem Mikrocontroller, C++-Code im Gateway, Rust im Edge-Service und Python in der Test-Pipeline, und jeder der vier hat sein eigenes Verständnis davon, wie ein Byte auf dem Draht aussieht. Reviews bringen das nicht mehr auf, weil vier Reviewer in vier Repositories vier Kopien pflegen.

Der Ausweg liegt in einer einzigen Quelle. Die Spezifikation muss menschenlesbar sein, weil sie diskutiert, reviewed und verhandelt wird. Sie muss aber auch streng genug sein, dass aus ihr Code generiert werden kann, ohne dass eine nachträgliche Anreicherung nötig ist. Wir lösen das in zwei Schritten: erst Markdown für den Menschen, dann JSON für die Maschine. Aus dem JSON generiert ein KI-Agent die vier Sprachimplementierungen (siehe Spec Driven Development, Scrum, SAFe und Wasserfall für die methodische Einordnung).

Der Vorgängerartikel zur SSOT-Pipeline für Firmware, Tests und Doku hat das Grundprinzip an einem einzelnen Gerät durchgespielt. Dieser Artikel weitet es auf ein Mehrsprachen-Protokoll aus und beschreibt den Iterationszyklus, wie er sich im Alltag bewährt hat.

Warum nicht direkt JSON?

JSON ist eine wunderbare Zwischensprache und eine schlechte Diskussionsgrundlage. Niemand liest in einem Review gerne hundert Zeilen verschachtelte Klammern, kommentiert Felder zwischen Kommas und führt eine Architekturdebatte über "frame": {"sof": "0xAA"}. Markdown dagegen ist genau dafür gemacht: Überschriften pro Nachricht, Tabellen für Felder, freier Text für Begründungen, Mermaid für Zustandsdiagramme, Quellenangaben für Normverweise.

Die Spec entsteht also als Markdown Dokument. Sie hat alles, was ein Reviewer braucht: Geschichten, Begründungen, Beispiele, Diagramme, Querverweise. Was sie nicht hat: maschinenlesbare Strenge.

Diese Strenge schiebt ein Parser nach.

Migration aus DOORS, Polarion, Word oder ReqIF

In den meisten Embedded Organisationen liegen die Anforderungen nicht in Markdown, sondern in Tools wie IBM DOORS, Siemens Polarion, Jama Connect, codeBeamer, in ReqIF Exports oder schlicht in Word und Excel. Niemand wird ernsthaft alle Bestandsdokumente von Hand abtippen, um die SSOT Pipeline anzuwerfen.

Hier kommt ein lokales Sprachmodell ins Spiel. Wir lassen die Exporte aus dem bestehenden Tool durch einen LLM laufen, der sie in unser Markdown Spec Format überführt. Konkret nutzen wir ein lokales LLM in der 30 bis 35B-Klasse wie Qwen3 Coder, auf einer Workstation mit ausreichend VRAM. Lokal ist hier nicht Geschmacksfrage, sondern Bedingung aus Vertraulichkeitsgründen: Protokoll-Spezifikationen, Registerkarten und Anforderungstexte sind in der Regel IP, die das Firmennetz nicht verlassen darf. Ein Cloud LLM ist hier disqualifiziert, ein lokales offenes Modell wie Qwen3 Coder erfüllt die Anforderung.

Der LLM macht die mühsame Übersetzung: aus DOORS Modulen werden Markdown Sektionen, aus ReqIF Attributen werden Markdown Tabellen, aus Word Texten wird strukturierter Fliesstext mit den richtigen Konventionen. Der Mensch reviewt das Ergebnis, korrigiert wo nötig, und ab dann ist die Markdown Datei die Quelle. Das Bestandstool darf danach im Lesemodus weiterleben, neue Änderungen passieren in der Markdown Spec.

Der Übergang ist einmalig pro Komponente. Sobald die Markdown Spec steht, läuft der Iterationszyklus wie im nächsten Abschnitt beschrieben.

Idealerweise läuft die Transformation aus dem System-Engineering-Tool in die JSON-Zwischenform vollständig automatisch. Jede neue Spec-Version, die dazwischen einen manuellen Schritt braucht, kostet Zeit und öffnet einen Fehlerkanal: ein Feldname vertippt, eine Enumeration ausgelassen, eine Range falsch übernommen, und der Generator baut still auf einer falschen Grundlage weiter. Das gilt auch dann, wenn eine Organisation DOORS oder Polarion als Quelle behält und den Markdown-Zwischenschritt überspringt: erst wenn die Kette vom Anforderungs-Tool bis zur JSON-Form ohne menschliche Zwischenhand durchläuft, skaliert der Ansatz über viele Iterationen.

Tipps für Spec Autoren

Vier Regeln, die sich im Alltag bewährt haben:

Kontext beschreiben. Worum geht es in dieser Spec, warum machen wir sie, wie ist der Fluss der Telegramme im typischen Ablauf. Es braucht nicht viel, ein paar Absätze reichen. Aber genau dieses Wenige entscheidet, ob der Test-Generator gleich sinnvolle automatisierte Tests ableiten kann oder ob man später mühsam mit einem Use-Case-Dokument nachbessern muss.

Kurz halten. Eine Spec, die niemand mehr in einem Stück liest, wird auch von keinem Agenten in einem Stück verarbeitet. Sowohl menschliche Reviewer als auch das Kontextfenster eines LLM haben Grenzen. Spec Dokumente bleiben kompakt, redundante Formulierungen weichen, eine Aussage steht an einer Stelle.

Präzise statt verschwurbelt. “Üblicherweise”, “sollte normalerweise”, “in der Regel” sind in einer Spec keine Beschreibungen sondern Fehlerquellen. Ist etwas optional, steht es als optional in der Tabelle. Ist etwas bedingt, steht die Bedingung explizit dabei. Schwammige Formulierungen führen sowohl Menschen als auch Generatoren in die Irre, und der Generator wird zuverlässig die falsche Annahme treffen.

Textuell statt visuell. Die schönste Zeichnung mit Bus und Teilnehmern ist für menschliche Reviewer hilfreich, kostet aber im Pipeline Alltag einen zusätzlichen Schritt: Bilder müssen extrahiert, interpretiert und in eine maschinenlesbare Form überführt werden, und dieser Schritt ist selten ohne manuelle Nacharbeit. Spec Inhalte gehören deshalb so weit wie möglich in textuelle Form: Tabellen, geordnete Listen, klare Sektionen, klar typisierte Codeblöcke. Wenn eine Zeichnung wirklich nötig ist, lohnt es sich, sie als textbasiertes Quellformat zu pflegen, etwa Mermaid oder PlantUML. Im Repository liegen beide Versionen: die gerenderte Grafik für die Doku, der Source für den Parser.

Spec-Sprache und englisches Interface

Die Spec darf in der Sprache geschrieben werden, in der die Domäne diskutiert wird, meist Deutsch oder Englisch, in Einzelfällen auch Französisch oder Italienisch. Für die AI ist das in aller Regel kein Problem, moderne LLM verarbeiten Fachtexte in allen gängigen europäischen Sprachen zuverlässig. Wichtig ist aber, dass die generierten Code-Identifier durchgängig englisch bleiben und dass dieselben Fachbegriffe immer gleich übersetzt werden. Dafür pflegen wir pro Projekt ein kurzes Glossar in der Repo-Wurzel: die Spec-Begriffe der Ausgangssprache auf der einen Seite, die englischen Feldnamen, Enum-Werte und Funktionsnamen auf der anderen. Der Generator liest dieses Glossar mit und garantiert, dass ein mehrdeutiges Wort wie “Zustand” jedes Mal die im Projekt festgelegte Übersetzung bekommt, sei es state (Zustandsmaschine), condition (Gesundheits-/Fehlerstatus) oder status (allgemeine Betriebslage). Ohne Festlegung würde die AI je nach Satzkontext mal so, mal so übersetzen.

Markdown zu JSON: der Parser

Der Parser ist ein kurzes Python Skript, das die Markdown Datei liest und alle für die Code Generierung relevanten Strukturen in eine JSON Form überführt: Nachrichten mit ihren Feldern, Datentypen, Bereichen, Defaults, Zustandsübergängen, Versionsangaben. Die Regeln, was extrahiert wird, sind im Markdown durch klare Konventionen markiert (eine Tabelle in einer Sektion mit der Überschrift “Felder” wird zur Feldliste, eine state Codeblock-Annotation wird zum Zustandsdiagramm, und so weiter).

Wichtig: der Parser ist deterministisch und idempotent. Dieselbe Markdown Datei produziert dasselbe JSON, byteweise. Damit ist das JSON ein verlässliches Build Artefakt, gegen das alle nachgelagerten Generatoren laufen.

Das JSON selbst wird nicht von Hand editiert. Wer am Protokoll etwas ändern will, ändert die Markdown Spec. Der Parser läuft im Build als erster Schritt und scheitert hart, wenn die Markdown Konventionen verletzt sind. Das fängt die häufigste Fehlerquelle ab, nämlich Inkonsistenzen, die ein Mensch im Markdown übersieht, aber der Parser als Mehrdeutigkeit erkennt.

JSON zu Code: ein Generator pro Sprache

Aus der JSON Form generiert ein AI Agent die Implementierung je Zielsprache. Konkret betreiben wir vier Generatoren:

Jeder Generator erzeugt nur das, was sich aus der Spec ableiten lässt. Anwendungslogik, Transport, Threading, alles was nicht im Wire Format steht, bleibt der Hand des Entwicklers überlassen. Diese Trennung ist wichtig: der generierte Code wird bei jedem Spec Change rücksichtslos überschrieben. Was nicht generiert ist, lebt in eigenen Dateien und bleibt unangetastet.

Idiomatik je Sprache, nicht “C in vier Sprachen”

Der häufigste Fehler bei mehrsprachigen Generatoren ist, dass der C Code mechanisch in andere Sprachen übersetzt wird und überall fremd aussieht. Wir geben den Agenten pro Sprache ein Idiomatik Skill mit: in Rust werden Fehlertypen explizit, in Python werden Felder als dataclass deklariert und nicht als dict, in C++ wird enum class benutzt statt blanke Enums. Der Reviewer auf der Rust Seite muss Rust Code sehen, nicht maschinenübersetzten C Code.

Das ist die Stelle, an der ein AI Agent mehr Hebel hat als ein klassischer Codegenerator: er kann je Sprache die passende Form wählen, weil er die Sprache als Sprache versteht, nicht nur als Templatesystem.

Der Iterationszyklus

Eine typische Änderung läuft so:

  1. Ein Reviewer entdeckt, dass ein Feld in einer Nachricht einen erweiterten Wertebereich braucht.
  2. Die Markdown Spec wird angepasst, im selben Dokument an der gleichen Stelle, wo der Wert bisher beschrieben war. Die Spec Version wird inkrementiert.
  3. Der Build läuft. Der Parser regeneriert das JSON. Alle vier Generatoren regenerieren ihre Sprachausgaben.
  4. Die Diffs werden im Pull Request gezeigt. Jede Sprache zeigt eine vergleichbare, kleine Änderung. Reviewer können prüfen, ob die Übersetzung in jede Sprache plausibel ist.
  5. Die Tests laufen gegen den neuen Code. Sind sie grün, wird gemergt.

Wenn ein Generator etwas falsch macht (z.B. eine Rust Idiomatik bricht), wird nicht der generierte Code von Hand korrigiert, sondern der Generator selbst geschärft. Fehler gehören an der Quelle korrigiert. Die Folge wäre sonst, dass man den generierten Code bei jedem Lauf von Hand nachbessern müsste, und genau das wollen wir nicht. Generierter Code ist generierter Code, immer und ohne Ausnahme.

Versionierung der Spec selbst

Jede Spec trägt eine Version. Die Version ist Teil der Wire Repräsentation, sodass ein Empfänger sofort weiss, ob er die Nachricht überhaupt sicher decodieren kann. Inkompatible Änderungen erhöhen die Major Version, abwärtskompatible Ergänzungen die Minor.

Die Versionsregeln stehen ebenfalls in der Markdown Spec, und der Parser prüft sie. Wer ein Feld umbenennt, ohne die Major Version zu erhöhen, bekommt einen Build Fehler. Wer ein optionales Feld dazunimmt und die Minor Version vergisst, ebenso.

Stolpersteine, ehrlich gesagt

Was übrig bleibt

Eine einzige Markdown Datei, ein Parser, vier Generatoren, vier Sprachausgaben. Spec Änderungen sind ein Pull Request, der in jeder Sprache nachvollziehbar ist. Reviewer diskutieren auf der Spec Ebene, nicht über handgeschriebene Kopien des Protokolls. Die Wahrheit liegt im Markdown, nicht im Code.

Im nächsten Artikel zeigen wir, wie aus Use Cases im selben Markdown JSON pytest Test Pipeline Stil eine Testbench entsteht, die das generierte Protokoll auf echter Hardware durchexerziert.

Schlüsselwörter: Spec Driven Development, Protokoll Spezifikation, Markdown zu JSON, Codegenerierung C C++ Rust Python, Qwen3 Coder, DOORS Polarion Migration, Protokoll Generator Beratung, Wire Format ABI Stabilität, Firmware Protokolle, Embedded Consulting Schweiz, swissEmbedded Firmware Entwicklung

Protokoll Spezifikationen, die in mehreren Sprachen leben sollen? Wir bauen die Pipeline aus Markdown Spec, JSON Zwischenform und sprachidiomatischen Generatoren, damit Reviewer und Engineering in derselben Quelle arbeiten.

Kontaktieren Sie uns →