Ein Arbeitsraum pro Agent: warum sich Entwicklungsumgebungen nichts teilen sollten
Wenn mehrere Coding-Agenten gleichzeitig an einem Stack arbeiten, ist das Übersprechen zwischen ihnen der teuerste Fehler: eine Migration in der falschen Datenbank, ein Job im falschen Stack, ein Build, der nie ankommt. Wie unser Alltag vor devstack aussah und warum heute jeder Worktree einen in sich geschlossenen Arbeitsraum bekommt.
Ein gewöhnlicher Vormittag bei uns: Im Editor ist ein Ticket offen, in drei Terminals arbeiten Coding-Agenten an drei anderen, jeder in seinem eigenen Branch. Das ist kein Experiment mehr, sondern der Normalfall. Die Frage, die sich dabei stellt, hat weniger mit den Modellen zu tun als mit der Umgebung: Wo läuft eigentlich der Code, den ein Agent gerade geändert hat, und was bekommt er davon zu sehen?
Dieser Artikel beschreibt, wie wir diese Frage lange nur mit Umwegen beantworten konnten, und warum devstack, unser quelloffenes Werkzeug für lokale Entwicklungsstacks, heute eine klare Regel durchsetzt: Jeder Agent arbeitet in einem in sich geschlossenen Arbeitsraum. Was zwischen zwei Arbeitsräumen hin und her spricht, ist kein Feature, sondern ein Fehler.
Der Alltag vorher: ein Stack, viele Schreiber
Unser Ausgangspunkt war ein gewachsener Stack: eine .NET-API mit Datenbankmigrator, ein Angular-Frontend, einige kleinere Hilfsdienste und rund zwanzig Container, von SQL Server über Elasticsearch und einen Job-Scheduler bis zu einer Webhook-Queue. Entwickelt wird auf Windows 11. Orchestriert war das Ganze über einen AppHost, der unter einem Datei-Watcher lief. Für eine Person mit einer IDE passt so ein Aufbau gut. In unserem Alltag gab es aber nie nur eine Person: IDE, Terminals und mehrere Agenten schrieben gleichzeitig in denselben Arbeitsbaum.
Daraus entstand eine Liste von Bauchschmerzen, von denen jede einzelne echte Zeit gekostet hat:
- "Der Endpunkt ist doch da." Ein neuer Endpunkt antwortet mit 404, obwohl der Code steht und das Projekt baut. Der laufende Prozess hält seine eigenen DLLs fest, der neue Build überschreibt sie nur teilweise, und niemand meldet das. Der Agent sucht den Fehler dann in seinem Code, der gar nicht läuft.
- Dreimal derselbe Dienst in einer Sekunde. Mehrere Agenten speichern fast gleichzeitig, und der Watcher startet bei jeder Änderung neu. Doppelte Instanzen fahren ihre Hintergrundarbeiter hoch, bevor sie das Rennen um den Port verlieren, und holen sich dabei Jobs und Queue-Nachrichten ab. Am Ende brauchte die Anwendung selbst eine Sperre gegen Mehrfachstarts.
- Ein halber Edit legt alles lahm. Der Neustart kommt, bevor feststeht, ob der neue Stand überhaupt kompiliert. Ein Agent mitten in einer Umbenennung nimmt allen anderen den funktionierenden Stack weg.
- Die Migration landet in der falschen Datenbank. Ein Stack aus einem zweiten Worktree fand die Container des ersten, weil sie dieselben festen Ports hielten, und spielte seine Migrationen dort ein.
- Der Job landet im falschen Stack. Ein Scheduler oder eine Pull-Queue, die sich zwei Stacks teilen, geben die Arbeit dem, der zuerst fragt. Das ist keine Ressourcenfrage, sondern ein Korrektheitsfehler.
Innerhalb von drei Monaten wuchsen AppHost, Startskript und Runbook auf rund 3.300 Zeilen, ein guter Teil davon Umgehungen. Jede einzelne war für sich vernünftig. Zusammen beschrieben sie ein Grundproblem: Die Arbeitsräume waren nicht getrennt.
Übersprechen ist das eigentliche Anti-Pattern
Wer mit mehreren Agenten arbeitet, denkt zuerst an Konflikte im Code. Die lassen sich mit Branches und Pull-Requests gut beherrschen. Gefährlicher ist das Übersprechen zur Laufzeit: Ein Agent ändert etwas, und ein anderer bekommt es zu spüren, ohne davon zu wissen.
Für einen Agenten ist das besonders teuer. Ein Mensch merkt irgendwann, dass sich etwas seltsam verhält, und fragt nach. Ein Agent nimmt das Verhalten seiner Umgebung als Wahrheit. Antwortet die API mit dem alten Stand, baut er Erklärungen für ein Verhalten, das sein Code nie hatte. Ändert ein anderer Agent die gemeinsame Datenbank, ist der Test, den er gerade grün gesehen hat, beim nächsten Lauf rot. Die Fehlersuche richtet sich gegen den falschen Gegner, und am Ende stehen Commits im Review, die ein Problem lösen, das es nie gab.
Daraus folgt für uns eine einfache Regel: Ein Arbeitsraum muss für den Agenten, der darin arbeitet, vollständig sein, und nichts darin darf von einem anderen Arbeitsraum abhängen.
Was ein in sich geschlossener Arbeitsraum braucht
In devstack heißt so ein Arbeitsraum Lane, und jeder Git-Worktree ist eine. git worktree add, dann devstack up im neuen Verzeichnis, und derselbe Stack läuft daneben ein zweites Mal:
- Eigene Ports. Die Dienste einer Lane und die veröffentlichten Ports ihrer Container sind um einen festen Abstand verschoben. Zwei Lanes kommen sich nie in die Quere, und jede weiß, wo sie ihre eigenen Dienste findet.
- Eigene Container und eigene Daten. Jede Lane startet ein eigenes Compose-Projekt aus den Compose-Dateien ihres eigenen Worktrees, mit eigenen Volumes und Netzwerken. Ein Branch, der eine Compose-Datei ändert, läuft gegen genau diese Änderung und nicht gegen die Infrastruktur eines anderen.
- Keine fremde Arbeit. Hintergrundarbeiter laufen in einer Lane standardmäßig nicht, weil sie auch mit Systemen außerhalb des Stacks sprechen können. Wer sie braucht, schaltet sie für diese Lane ein, und dann gehören Scheduler und Queue nur ihr.
- Ein Build, der nichts Laufendes anfasst. Eine Änderung baut als Kandidat neben der laufenden Instanz und übernimmt erst, wenn sie gesund ist. Ein Compiler-Fehler lässt den alten Build weiter antworten, und eine neuere Änderung bricht den Build einer älteren ab.
Eine Ausnahme haben wir lange zugelassen: Container ohne eigenen Zustand, etwa ein Telemetrie-Dashboard, durften sich die Lanes mit dem Hauptbaum teilen. In der Praxis hat auch das übergesprochen. Die Container einer Lane fanden die geteilten Dienste nicht über ihren Namen, sodass Telemetrie ohne Meldung fehlte. Alle Lanes schrieben in dieselbe Telemetrie, und eine Lane auf einem anderen Branch bekam die Definition des Hauptbaums. Seit Version 0.33 ist das Teilen deshalb als veraltet markiert. Wo der Speicher knapp ist, lässt ein Profil gezielt weg, was eine Lane nicht braucht: sichtbar, statt halb getrennt.
Der Agent fragt die Umgebung, statt zu raten
Ein abgeschlossener Arbeitsraum hilft nur, wenn der Agent ihn auch lesen kann. devstack bringt dafür einen MCP-Server und generierte Skills mit. Ein Agent fragt status und bekommt für seine Lane eine eindeutige Antwort: Läuft seine Änderung schon, oder steht dort not yet active: building, failed oder held? Er liest die Logs seiner eigenen Dienste, startet sie neu und führt Befehle aus, die der Stack deklariert, etwa einen Datenbank-Reset. Alle Werkzeuge wirken auf die Lane des Worktrees, in dem der Agent arbeitet. Den Stack des Nachbarn startet er nicht aus Versehen neu.
Damit verschiebt sich die Fehlersuche. Die Frage "Läuft mein Code überhaupt?" beantwortet die Umgebung, nicht der Agent mit Vermutungen.
Wenn der Rechner nicht mehr reicht
Ein eigener Satz Container pro Lane kostet Speicher. Das ist der Preis dieser Regel, und wir zahlen ihn bewusst. devstack prüft vor dem Start einer Lane, ob der Speicher reicht, und nennt sonst die Zahl und die Auswege. Der wichtigste Ausweg ist keine halb getrennte Lane, sondern eine Lane auf einer anderen Maschine: devstack up --on <node> startet sie auf einem Server des Team-Pools, und ihre Ports werden lokal weitergeleitet, als liefe sie hier. Der Arbeitsraum bleibt vollständig, er steht nur woanders.
Was sich im Alltag geändert hat
Der Vormittag sieht heute ähnlich aus wie früher: ein Ticket im Editor, mehrere Agenten in eigenen Branches. Der Unterschied ist, dass jeder davon einen eigenen, vollständigen Stack hat. Eine Migration in einer Lane trifft nur deren Datenbank. Ein halber Edit ergibt höchstens einen fehlgeschlagenen Kandidaten, und der alte Build dieser Lane antwortet weiter. Die Startskripte, Sperren und Runbook-Seiten von früher brauchen wir nicht mehr.
Wenn Sie selbst mit mehreren Agenten an einem Stack arbeiten, lohnt sich ein Blick auf die eigenen Annahmen: Wo teilen sich Ihre Arbeitsräume heute etwas, eine Datenbank, einen Port, eine Queue, ein Build-Verzeichnis? Jede dieser Stellen ist ein möglicher Weg für Übersprechen. devstack ist quelloffen, noch vor Version 1.0 und bei uns täglich im Einsatz. Es läuft nativ auf Windows und Linux, macOS ist in der Vorschau. Sie finden es auf GitHub.