Harness Engineering: jak agent pisze kod produktowy w legacy monorepo
W Harness Engineering człowiek określa ramy architektoniczne, scenariusze testowe i recenzuje wyniki, a agent generuje cały kod produktowy. Eksperyment na legacy monorepo z 310 tys. linii Scala/Java pokazał przydatność podejścia do typowych zadań: wydzielenie modułu bez zmiany zachowania. Rola inżyniera przesuwa się w stronę zarządzania kontekstem i procesem, agent przejmuje mechaniczną rutynę.
Monorepo: 25 modułów SBT, 8 lat historii, system o wysokim obciążeniu. Zadanie — wydzielić obsługę webhooków kanału X do osobnego serwisu na wzór istniejących adapterów.
Warunki eksperymentu i role uczestników
Człowiek nie pisze kodu produktowego. Role są wyraźnie rozdzielone:
- Człowiek: granice architektoniczne, podział na etapy, wymagania, testy, recenzje, ostateczna weryfikacja.
- Agent: tworzenie/zmiana kodu, przenoszenie fragmentów, testy według scenariuszy, infrastruktura, naprawa defektów.
Zadanie typowe: mechaniczne przeniesienie z adaptacją wiring, zależności, bez refaktoryzacji logiki biznesowej. Weryfikowalność przez testy unitowe i e2e.
Przygotowanie kontekstu: dokumentacja jako interfejs zarządzania
Dokumentacja staje się narzędziem: ADR opisuje warianty, plan — etapy. Agent działa w zadanych ramach, bez domysłów.
Przykład ADR:
# ADR-42: wydzielenie obsługi webhooków kanału X z monolitu
## Kontekst
Kanał X jest obecnie obsługiwany wewnątrz jądra. Pozostałe kanały działają jako osobne serwisy za brokerem wiadomości.
## Warianty
1. Cienka powłoka: przenieść istniejący kod z minimalnymi zmianami (pakiety, wiring).
2. Przepisać handler „od nowa” na innym stosie.
3. Pozostawić bez zmian.
## Decyzja
Wariant 1: najmniejsza utrata niejawnego zachowania przy przenoszeniu; architektura wyrównuje się z pozostałymi kanałami.
## Konsekwencje
+ jednolity wzorzec dla wszystkich kanałów
− tymczasowe duplikowanie kodu; refaktoryzacja — osobne zadanie
Plan po etapach zmniejsza błędy: od szkieletu do pełnego konturu e2e.
Etapy i prompty: etapowe sterowanie agentem
Podział na kroki zapewnia kontrolę. Przykłady promptów:
- Szkic architektoniczny:
Przeanalizuj, jak zbudowane są adaptery kanałów A i B: jak subskrybują brokera i publikują przychodzące zdarzenia.
Znajdź w monolitcie obsługę webhooków kanału X.
Napisz ADR z trzema wariantami: powłoka / przepisać / zostawić w jądrze. Poleć powłokę i opisz konsekwencje.
Pliki umieść w architecture/adr/, kodu jeszcze nie zmieniaj.
- Testy jako kontrakt:
W klasie WebhookHandler jest rozgałęzienie po typie czatu. Wydziel je do czystej funkcji routeEvent(...).
Zachowania nie poprawiaj — kopiuj gałęzie as-is. Napisz testy unitowe: czat prywatny, grupa, nieznany typ, puste ciało.
Kryterium: `sbt test` dla modułu zielony.
- Nowy moduł:
Utwórz moduł sbt channel-x-adapter wg SPEC z ADR-42.
Skopiuj wymienione pliki z monolitu; zmieniaj tylko pakiety, importy i zależności do udanej kompilacji bez `dependsOn(core)`.
Zakaz: nie refaktoryzuj logiki biznesowej wiadomości na tym etapie.
- Kontur E2E:
Dodaj katalog e2e/ na pytest + testcontainers: kontener brokera, PostgreSQL, mock zewnętrznego API na osobnym porcie.
Napisz jeden test: POST webhooka z tekstem "ping" → w brokerze na oczekiwanym subject pojawiło się zdarzenie z text=ping.
Testy — środek zarządzania: agent orientuje się na zielony sbt test lub pytest.
Zalety agenta: rutyna pod kontrolą
Agent wyróżnia się w:
- Przenoszeniu i adaptacji kodu.
- Powtarzaniu wzorców na wzór.
- Generowaniu boilerplate.
- Budowie infrastruktury testowej.
- Serii jednorodnych poprawek.
Przykład testu e2e na pytest + testcontainers:
# fixture'y: kontener brokera, adapter, mock zewnętrznego HTTP API
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)
Sprawdza nie kompilację, a zachowanie w powiązaniu: broker, BD, mock API.
Granice i ograniczenia podejścia
Agent nie zastępuje decyzji architektonicznych, ale przyspiesza mechanikę. Ryzyka: uzgodnione błędy (zielone testy maskują regresje), potrzeba ścisłych ram. Stosowane do legacy z wzorcami, nie do greenfield czy R&D.
Programiści mogą dojść do różnych wniosków: oszczędność na rutynie vs. narzut na kontekst.
Co ważne
- Harness Engineering — kontrolowane środowisko dla agenta: reguły, narzędzia, feedback.
- Zysk na przenoszeniu/adaptacji kodu i infrastrukturze testowej w projektach legacy.
- Testy — kluczowy interfejs zarządzania, nie rytuał.
- Granice: typowe zadania ze wzorcami, bez innowacji architektonicznych.
- Branża: Anthropic (90% kodu AI), Meta, Amazon — przejście do AI-native dev.
— Editorial Team
Brak komentarzy.