Harness Engineering: So schreibt ein KI-Agent Produktionscode in einem Legacy-Monorepo
Bei Harness Engineering definieren Menschen die architektonischen Grenzen, Test-Szenarien und überprüfen die Ergebnisse, während der KI-Agent den gesamten Produktionscode erzeugt. Ein Experiment in einem Legacy-Monorepo mit 310.000 Zeilen Scala/Java hat gezeigt, dass der Ansatz für gängige Aufgaben wie die Auslagerung eines Moduls ohne Verhaltensänderung funktioniert. Die Rolle des Entwicklers verschiebt sich zur Steuerung von Kontext und Prozess, während der Agent die mechanische Routinearbeit übernimmt.
Monorepo: 25 SBT-Module, 8 Jahre Geschichte, hochbelastetes System. Aufgabe: Auslagerung der Channel-X-Webhook-Verarbeitung in einen eigenständigen Service nach dem Muster bestehender Adapter.
Experimentaufbau und Rollen
Menschen schreiben keinen Produktionscode. Die Rollen sind klar getrennt:
- Mensch: Architektonische Grenzen, Aufgabenzerlegung, Anforderungen, Tests, Reviews, finale Validierung.
- Agent: Codeerstellung/Änderungen, Verschieben von Komponenten, Ausführen szenariobasierter Tests, Infrastruktur-Setup, Bugfixes.
Typische Aufgabe: Mechanische Auslagerung mit Verkabelung und Abhängigkeitsanpassungen, kein Refactoring der Geschäftslogik. Überprüfbar via Unit- und E2E-Tests.
Kontextvorbereitung: Dokumentation als Steueroberfläche
Dokumentation wird zum Werkzeug: ADRs skizzieren Optionen, Pläne definieren Phasen. Der Agent hält sich strikt an die vorgegebenen Rahmen, kein Raten.
Beispiel-ADR:
# ADR-42: Auslagerung der Channel-X-Webhook-Verarbeitung aus dem Monolithen
## Kontext
Channel X wird derzeit im Kern gehandhabt. Andere Channels laufen bereits als separate Services hinter einem Message Broker.
## Optionen
1. Dünner Wrapper: Bestehenden Code mit minimalen Anpassungen verschieben (Pakete, Verkabelung).
2. Handler von Grund auf in einem anderen Stack neu schreiben.
3. Beibehalten wie bisher.
## Entscheidung
Option 1: Minimiert Verlust impliziten Verhaltens bei der Auslagerung; passt zur Architektur anderer Channels.
## Folgen
+ Konsistentes Muster über alle Channels
− Temporäre Code-Duplikation; Refactoring als separate Aufgabe
Phasierte Pläne reduzieren Fehler: Vom Skelett bis zum vollständigen E2E-Zyklus.
Phasen und Prompts: Schritt-für-Schritt-Agentensteuerung
Zerlegung in Schritte gewährleistet Kontrolle. Prompt-Beispiele:
- Architektonischer Bauplan:
Untersuche, wie Channel-A- und B-Adapter funktionieren: Wie sie sich am Broker abonnieren und eingehende Events publizieren.
Finde die Channel-X-Webhook-Verarbeitung im Monolithen.
Schreibe eine ADR mit drei Optionen: Wrapper / Neuschreibung / Im Kern belassen. Empfehle Wrapper und detailliere Folgen.
Platziere Dateien in architecture/adr/, berühre noch keinen Code.
- Tests als Vertrag:
In der WebhookHandler-Klasse gibt es Verzweigungen nach Chat-Typ. Lagere sie in eine reine Funktion routeEvent(...) aus.
Verbessere das Verhalten nicht – kopiere Verzweigungen 1:1. Schreibe Unit-Tests: privater Chat, Gruppe, unbekannter Typ, leerer Body.
Erfolg: `sbt test` für das Modul läuft grün durch.
- Neues Modul:
Erstelle SBT-Modul channel-x-adapter gemäß SPEC in ADR-42.
Kopiere aufgelistete Dateien aus dem Monolithen; ändere nur Pakete, Imports und Deps für sauberen Compile ohne `dependsOn(core)`.
Regel: Kein Refactoring der Geschäftslogik in dieser Phase.
- E2E-Zyklus:
Füge e2e/-Verzeichnis mit pytest + testcontainers hinzu: Broker-Container, PostgreSQL, externer API-Mock auf separatem Port.
Schreibe einen Test: POST Webhook mit "ping"-Text → Broker erhält Event auf erwartetem Subject mit text=ping.
Tests lenken den Agenten: Er strebt grünes sbt test oder pytest an.
Stärken des Agenten: Routine unter Kontrolle
Der Agent glänzt bei:
- Code-Auslagerung und -Anpassung.
- Nachbilden von Mustern aus Beispielen.
- Generieren von Boilerplate.
- Aufbau von Testinfrastruktur.
- Batches ähnlicher Bearbeitungen.
Beispiel-E2E-Test mit pytest + testcontainers:
# Fixtures: Broker-Container, Adapter, externer HTTP-API-Mock
def test_incoming_webhook_publishes_to_broker(e2e):
e2e.http.post("/webhook/channel-x", json={"message": {"text": "hello", "chat": {"id": 42}}})
msg = e2e.broker.await_message("inbound.channel-x", timeout_s=5)
assert msg["text"] == "hello"
assert msg["chat_id"] == 42
def test_outgoing_command_reaches_external_api(e2e):
e2e.broker.publish("outbound.channel-x", {"chat_id": "42", "text": "hi"})
calls = e2e.external_mock.wait_for_requests(method="POST", path_contains="sendMessage")
assert any(b"hi" in c.body for c in calls)
Überprüft nicht nur Kompilierung, sondern integriertes Verhalten: Broker, DB, API-Mock.
Grenzen und Rahmenbedingungen
Der Agent ersetzt keine architektonischen Entscheidungen, beschleunigt aber Mechanik. Risiken: Verborgene Regressionen (grüne Tests kaschieren Probleme), Bedarf an strengen Grenzen. Passt zu Legacy mit Mustern, nicht zu Greenfield oder Forschung.
Entwickler-Meinungen gehen auseinander: Routineeinsparungen vs. Kontextaufwand.
Wichtige Erkenntnisse
- Harness Engineering: Kontrollierte Agentenumgebung – Regeln, Tools, Feedback.
- Erfolge bei Code-Auslagerung/Anpassung und Testinfrastruktur in Legacy-Projekten.
- Tests: Kernsteueroberfläche, kein Ritual.
- Grenzen: Routineaufgaben mit Beispielen, keine architektonische Innovation.
- Branche: Anthropic (90 % KI-Code), Meta, Amazon – Übergang zu KI-nativer Entwicklung.
— Editorial Team
Noch keine Kommentare.