Architektura aplikacji rozwijanych z AI. Przegląd badań i opinii
Wyobraźmy sobie zwykłe zadanie: aplikacja ma obsługiwać częściowe zwroty płatności. Agent musi znaleźć limit zwrotu, sprawdzić kontrakt operatora i ustalić, co zrobić z ponowionym żądaniem. Jeżeli reguła jest rozrzucona między płatnościami, zamówieniami i rozliczeniami, drobna zmiana zamienia się w wycieczkę po całym systemie. Wydzielenie tych fragmentów do osobnych usług dorzuci jeszcze uzgadnianie ich wersji. Kwotę należną klientowi nadal trzeba policzyć poprawnie.
Parnas i koszt następnej zmiany
David Parnas w pracy z 1972 roku o podziale systemu na moduły porównał dwie organizacje tego samego programu. W jednej moduły odpowiadały kolejnym etapom przetwarzania. W drugiej ukrywały decyzje projektowe, na przykład sposób przechowywania danych. Zmiana reprezentacji mogła wtedy pozostać wewnątrz modułu, bez poprawek u wszystkich jego klientów. W naszym przykładzie podobną rolę pełni jedno miejsce odpowiedzialne za obliczenie dostępnego zwrotu. Reszta aplikacji korzysta z wyniku, zamiast samodzielnie odtwarzać rachunek.
W „Monolith First” z 2015 roku Martin Fowler argumentował na podstawie niewielkiej liczby obserwowanych projektów, że warto zaczynać od monolitu. Granice usług trudno dobrze wyznaczyć, zanim pozna się domenę, a później drożej je przesuwać w systemie rozproszonym. Samo ułatwienie agentowi czytania kodu to słaby powód, żeby od razu ponosić koszt osobnej usługi.
Jimmy Bogard pokazał konkretny sposób organizacji kodu w tekście o przekrojach funkcjonalnych z 2018 roku. Vertical slice, czyli przekrój funkcjonalny, skupia elementy obsługujące jeden przypadek użycia: od wejścia żądania po walidację i zapis. Dzięki temu przy częściowym zwrocie łatwiej znaleźć razem kod wykonujący tę operację. Wspólne reguły nadal trzeba rozpoznawać i wydzielać, co Bogard wiązał z umiejętnością refaktoryzacji. Tak można uporządkować modularny monolit albo wnętrze pojedynczego mikroserwisu.
Agent musi odnaleźć kod i z niego skorzystać
HumanEval, przedstawiony razem z Codexem w 2021 roku, zawierał 164 ręcznie przygotowane zadania generowania samodzielnych funkcji Pythona. Model dostawał opis, pisał rozwiązanie, a testy sprawdzały wynik. To rozsądny początek oceny generatora kodu, ale odległy od grzebania w systemie płatności. Cała trudność odnalezienia reguły biznesowej pozostawała poza zadaniem.
Dwa lata później SWE-bench wprowadził rzeczywiste zgłoszenia i repozytoria: 2294 problemy z 12 projektów Pythona. Trzeba było znaleźć właściwe miejsce i przygotować poprawkę, czasem w kilku plikach. Nadal oceniano pojedyncze zgłoszenie, lecz struktura istniejącego kodu zaczęła wpływać na drogę do rozwiązania. Agent miał już coś do zepsucia poza własną funkcją.
W badaniu z 2024 roku porządkowanie przykładów do uczenia poprawiło wyniki generatora kodu. Autorzy zmieniali nazwy zmiennych, wydzielali funkcje pomocnicze i dodawali opisy planu rozwiązania. Code Llama 7B uczony na przekształconym, modularnym kodzie osiągał lepsze wyniki na zadaniach algorytmicznych APPS i CodeContests.
Tyle że podczas takiego sprzątania zmienia się kilka cech naraz. Autorzy badania kontrolnego dotyczącego modularności usunęli modularność z jednego z wariantów przekształconego kodu, ręcznie wstawiając treść funkcji w miejsca wywołań. Na APPS i CodeContests, dla badanych wariantów Code Llama, DeepSeekCoder i GPT-4o-mini, modularne przykłady nie dawały konsekwentnej przewagi. Wydzielenie funkcji nie okazało się niezawodną receptą na lepsze generowanie rozwiązań tych zadań w Pythonie.
Potem pojawiło się pytanie bardziej dokuczliwe niż liczba poprawnych odpowiedzi: czy model w ogóle korzysta z tego, co zastaje? RepoExec z 2025 roku sprawdzał 18 modeli na 355 zadaniach generowania funkcji w repozytorium. Autorzy podawali implementacje potrzebnych zależności, sygnatury z dokumentacją albo same sygnatury. Ogólnie najlepiej działał pełny kontekst tych zależności. Chodziło o kod potrzebny do zadania, a nie o wklejenie całego repozytorium do promptu.
Modele potrafiły przechodzić testy, jednocześnie odtwarzając istniejącą funkcjonalność. Dlatego używanie wskazanych zależności mierzono osobno; ich wywołanie też mogło być błędne. W aplikacji ze zwrotami taka duplikacja oznaczałaby drugą wersję rachunku, którą trzeba uwzględnić przy kolejnej zmianie limitu. Zielony test nowej funkcji nie pokaże tego kosztu, jeśli sprawdza tylko zwracaną kwotę.
Zespół Anthropic w tekście o dobieraniu kontekstu dla agentów opisywał praktykę dobierania informacji i doczytywania materiałów podczas pracy. Czytelne nazwy plików oraz narzędzia wyszukiwania mają prowadzić agenta do potrzebnego kodu. Dla projektanta aplikacji jest tu konkretne zadanie: ułatwić odnalezienie reguły zwrotu razem z jej kontraktem i testami. Skrócenie kontekstu przez wyrzucenie kontraktu operatora płatności byłoby oszczędnością w złym miejscu.
Bardziej dosadny przykład przyniosło „The Modular Imperative”. W próbie z pulpitem blogowym Cursor z Claude Sonnet 4 tworzył osobne komponenty, ale pozostawiał ukryte zależności, duplikował dane i zwiększał złożoność. Późniejsza migracja do React poszła sprawniej z wersją zapisaną w jednym pliku. Autorzy zestawiali małe implementacje, a modularny wariant miał konkretne wady i mógł być gorzej dopasowany do komponentów React. Praca przedstawia postulaty badawcze zilustrowane eksperymentami, w części z użyciem Rubric DSL, narzędzia związanego z Midspiral, afiliacją jednego ze współautorów.
Polecenie „zrób to modularnie” wymaga więc kryterium odbioru. Przy zwrotach takim kryterium może być jedna implementacja reguły limitu i zakaz sięgania do wewnętrznych klas płatności z modułu zamówień. Samo pojawienie się nowych plików jest za słabym powodem, żeby zaakceptować zmianę.
Unmesh Joshi i Martin Fowler w rozmowie o budowaniu abstrakcji z LLM rozdzielali odkrywanie abstrakcji od stosowania tych już ustalonych. Dobre nazwy i granice powstają podczas poznawania problemu. Kiedy znaczenie pojęć jest stabilne, łatwiej zlecić modelowi kolejną implementację. Dodatkowa warstwa kodu ma sens, jeśli pomaga opisać następne zadanie; inaczej agent dostaje jeszcze jedno miejsce, przez które musi się przekopać.
Za niezależne wdrożenia warto płacić, kiedy są potrzebne
Chris Richardson w tekście o architekturze dla pracy z AI broni mikroserwisów przy większej skali organizacji. Mała usługa ogranicza zakres zmiany, szybkie testy pozwalają agentowi poprawiać błędy, a niezależne wdrożenia obsługują pracę wielu zespołów równolegle. Autor „Microservices Patterns” i konsultant specjalizujący się w tym podejściu zostawia monolit małym aplikacjom i niewielu zespołom. Przedstawia argument architektoniczny, a nie wynik porównania agentów.
Jeśli zespół płatności potrzebuje wdrażać swoje zmiany bez czekania na wydanie całej aplikacji, osobna usługa rozwiązuje konkretny problem. Podobnie z osobnym skalowaniem. Jeżeli natomiast każdy zwrot nadal wymaga skoordynowanej zmiany trzech usług, samo zmniejszenie repozytoriów niewiele ułatwia. Trzeba obsłużyć również częściowe niepowodzenie: operator wykonał zwrot, ale odpowiedź nie dotarła. Ponowienie żądania nie może wypłacić pieniędzy drugi raz. Monolit korzystający z zewnętrznego operatora też musi sobie z tym poradzić, ponieważ lokalna transakcja bazodanowa nie obejmuje operacji u operatora.
W 2026 roku badanie generowania mikroserwisów przez agentów przyniosło 144 wygenerowane implementacje w czterech projektach, dwa warianty promptów i dwa scenariusze pracy. Porównano Claude Code z Sonnet 4, Codex z GPT-5 oraz Code Qwen z Qwen3-Coder. Poprawność zależała od konfiguracji i projektu, a dodatkowe streszczenie implementacji nie zapewniało poprawy. Monolitu w tym eksperymencie nie było. Co więcej, odtwarzanie usługi sprawdzano testami jednostkowymi, a wariant clean state testami integracyjnymi korzystających z niej usług. Różnicy wyników tych scenariuszy nie da się przypisać wyłącznie ilości kontekstu, skoro zmieniono także sposób oceny.
ModulithBench trafia już wprost w spór o architekturę: zawiera odpowiadające sobie implementacje domen w Java/Spring oraz zadania wymagające zmian między modułami. Autor argumentuje za modularnym monolitem. Niestety raport uruchomienia Antigravity opisuje końcowe oceny jako samoocenę, podaje organizację zamiast dokładnej wersji modelu i przyznaje, że mikroserwisów formalnie nie zweryfikowano. Taki raport nie nadaje się na dowód przewagi architektury. Zadania można wykorzystać we własnym eksperymencie, po ujednoliceniu walidacji i warunków obu wariantów.
Zostawmy agentowi kod po poprzedniej zmianie
Zbyt łatwo ogłosić sukces po pierwszej działającej implementacji. Kolejny limit zwrotu trafi przecież do kodu, w którym agent zdążył już podjąć decyzje, wydzielić funkcje albo skopiować regułę. Benchmark rozpoczynający każdą próbę od czystej wersji usuwa te konsekwencje z pomiaru.
Autorzy SWE-CI próbują je uchwycić. Pierwsza wersja obejmowała 100 zadań z par wersji rzeczywistych repozytoriów, oddzielonych średnio 233 dniami i 71 commitami historycznego rozwoju. W eksperymencie agent w roli architekta wyznaczał wymagania, a drugi zmieniał kod, w pętli do 20 iteracji, z kontrolą regresji. Wymagania wyprowadzano z różnicy względem testów docelowej wersji. Te 233 dni opisują historię projektu, nie długość samodzielnej pracy agenta. W pomiarze pojawia się jednak skutek jego wcześniejszych zmian, z którym trzeba żyć w następnej iteracji.
Agent zmieniający istniejący kod potrzebuje szybkiego, konkretnego sygnału błędu. Birgitta Böckeler w tekście o narzędziach kontrolujących pracę agentów opisuje instrukcje kierujące pracą agenta oraz testy, kontrolę typów i analizę zależności sprawdzające wynik. Granice modułów można zapisać jako reguły egzekwowane przez narzędzia. W Javie ArchUnit sprawdza zależności pakietów, warstw i cykle, więc odwołanie z zamówień do wewnętrznej klasy płatności może od razu zakończyć walidację błędem. Dokumentacja powinna wyjaśniać powód zakazu, żeby model nie próbował usunąć niewygodnego testu.
Böckeler zaznacza też, że kontrola struktury nie naprawi źle zrozumianego wymagania. Potrzebne są niezależne kryteria weryfikacji kodu generowanego przez AI. W naszym przykładzie agent powinien dostać regułę zwrotu wraz z testami obejmującymi kolejne operacje. Po wpłacie 100 zł i zwrocie 60 zł do zwrotu pozostaje 40 zł, a ponowienie pierwotnego żądania nie może uruchomić kolejnej wypłaty.