Nazwy plików decydują, czy agent AI znajdzie kod. Wyniki naszego badania
Zmierzyliśmy, jak architektura wpływa na skuteczność agentów kodujących. Nazwy wzorcowe (policy.py, adapters.py) lokalizują zmianę tak samo źle jak nazwy bezsensowne. Decyduje pojęcie domenowe w nazwie pliku.
Zbudowaliśmy eksperyment, który sprawdza jedną rzecz: czy architektura kodu zmienia skuteczność agenta AI wykonującego zadanie programistyczne. Nie jakość kodu w ocenie człowieka - skuteczność maszyny, która ma znaleźć właściwy plik i wprowadzić zmianę.
Wynik jest ostrzejszy, niż zakładaliśmy. Największą karę płaci się nie za podział na moduły, tylko za nazwy plików opisujące wzorzec architektoniczny zamiast dziedziny.
Badanie jest w formie preprintu, jeszcze nieopublikowanego - link podamy, gdy trafi do obiegu. Poniżej metoda i liczby.
Jak to mierzyliśmy
Protokół par minimalnych: dla każdego repozytorium tworzymy dwa lub więcej wariantów identycznych pod względem zachowania, API, zależności i zestawu testów. Różnią się wyłącznie architekturą. Wariant wchodzi do eksperymentu dopiero po przejściu tego samego ukrytego zestawu testów, więc różnice w wynikach nie biorą się z różnic w działaniu kodu.
Repozytoria budowaliśmy w obie strony - dodając pośredniość do płaskiego kodu i spłaszczając kod pośredni - żeby wykluczyć artefakty konstrukcji. Agent, model, narzędzia i limity były stałe. Ocena końcowa: ukryty zestaw testów uruchamiany po zakończeniu pracy agenta, więc nie da się jej ugrać edytując testy.
Użyliśmy pięciu modeli z trzech rodzin (Qwen 2.5-coder 7B/14B/32B, GPT-OSS-20B, DeepSeek V4-flash) oraz dwóch języków (Python, Go).
Wynik 1: pośredniość obniża skuteczność
Skuteczność (pass@k, k=5) na semantycznie identycznej funkcjonalności:
| Repozytorium | 14B: płaskie | 14B: pośrednie | 7B: płaskie | 7B: pośrednie |
|---|---|---|---|---|
| order-pricing | 1,00 | 0,73 | 1,00 | 0,40 |
| inventory-sync | 1,00 | 0,73 | - | - |
Po rozszerzeniu do 20 zadań w dwóch dziedzinach: 1,00 wobec 0,68, średnia kara +0,32, test permutacyjny p=0,002. Ten sam wzorzec odtworzył się w Go (1,00 wobec 0,65), gdzie pośredniość jest realizowana inaczej niż w Pythonie - więc nie jest to właściwość jednego mechanizmu językowego.
Tryb porażki jest zawsze ten sam: agent otwiera niewłaściwy plik i przepisuje go do wyczerpania limitu, nigdy nie zaglądając tam, gdzie leży logika.
Wynik 2: winna jest nazwa, nie podział
Rozłożyliśmy „pośredniość” na czynniki i zmierzyliśmy każdy osobno - sondą lokalizacji: model dostaje listę plików i opis zadania, i ma wskazać plik do edycji. Jedno wywołanie, bez edycji i testów, więc da się zrobić tysiące powtórzeń.
| Wariant | Nazwy plików | Trafność lokalizacji |
|---|---|---|
| jeden plik | - | 1,00 |
| podział, nazwy domenowe | discount.py, tax.py | 0,74 |
| podział, nazwy wzorcowe | policy.py, calculator.py | 0,21 |
| podział, nazwy bezsensowne | component_a.py | 0,28 |
| pełna pośredniość | adapters.py + wstrzykiwanie zależności | 0,53 |
Nazwy wzorcowe wypadają tak samo źle jak nazwy bez znaczenia - a właściwie odrobinę
gorzej. Powód: policy.py to sygnał mylący. Model nie waha się, tylko z przekonaniem
wybiera zły plik.
To uderza w konwencję Clean/Hexagonal: katalogi adapters/, handlers/, policies/
są dokładnie tym przypadkiem.
Wynik 3: liczy się rzeczownik domenowy, nie słowo-rola
Sprawdziliśmy wariant hybrydowy - plik zachowuje pojęcie domenowe i przyjmuje nazwę roli architektonicznej:
| Nazwa pliku | Trafność |
|---|---|
tax.py (domena) | 0,71 |
tax_calculator.py (domena + rola) | 0,70 |
calculator.py (sama rola) | 0,31 |
component_b.py (bez znaczenia) | 0,33 |
Dodanie słowa-roli nie kosztuje nic. Szkodzi dopiero usunięcie pojęcia domenowego. Zasada jest więc prostsza, niż się wydaje na pierwszy rzut oka: dziel moduły swobodnie, ale zostaw w nazwie rzeczownik z dziedziny.
Wynik 4: to nie jest cecha jednego modelu
Różnica między nazwami domenowymi a wzorcowymi, pięć modeli z trzech rodzin:
| Model | Domenowe | Wzorcowe | Różnica |
|---|---|---|---|
| DeepSeek V4-flash | 0,78 | 0,20 | +0,58 |
| GPT-OSS-20B | 0,74 | 0,33 | +0,41 |
| Qwen 2.5-coder 32B | 0,67 | 0,25 | +0,42 |
| Qwen 2.5-coder 14B | 0,80 | 0,25 | +0,55 |
| Qwen 2.5-coder 7B | 0,68 | 0,17 | +0,51 |
Efekt dodatni w każdym modelu, łącznie +0,49 z przedziałem ufności wykluczającym zero. To własność informacji zawartej w nazwie, nie cecha konkretnego modelu.
Wynik 5: na prawdziwym kodzie jest gorzej, a przy skali - najgorzej
Powtórzyliśmy manipulację na źródłach realnych projektów, zmieniając wyłącznie nazwy plików. Kod bajt w bajt ten sam, więc zachowanie jest identyczne z definicji.
Biblioteka marshmallow, sześć modułów przemianowanych z nazw domenowych na słowa-role
(fields.py → descriptors.py, validate.py → constraints.py), a potem na nazwy
bez znaczenia: trafność 1,00 → 0,56 → 0,01. W drugiej rodzinie modeli 0,92 → 0,47 → 0,03.
Skala pogarsza sprawę zamiast ją rozmywać. Na mitmproxy (253 pliki) nazwy domenowe
dają 0,82-0,90, a warianty bez znaczenia dokładnie 0,00 - we wszystkich zadaniach
i obu rodzinach modeli. Im więcej plików-dystraktorów, tym nazwa domenowa staje się
jedyną działającą wskazówką.
Wynik 6: karę płaci się za zakopanie, nie za podział
| Czynnik | Kod płaski | Kod pośredni | Kara |
|---|---|---|---|
| zmiana w punkcie wejścia | 1,00 | 1,00 | 0,00 |
| zmiana zakopana za adapterami | 1,00 | 0,60 | +0,40 |
| dopisanie nowego zachowania | 1,00 | 0,83 | +0,17 |
| modyfikacja istniejącego | 1,00 | 0,60 | +0,40 |
Zmiany na powierzchni są darmowe. Płaci się za logikę schowaną za warstwą, a modyfikacja kosztuje więcej niż dopisanie.
Wynik 7: przy czytelnych nazwach podział jest tańszy
Gdy nazwy są przejrzyste, oba warianty rozwiązują każde zadanie - różnica przenosi się z sukcesu na koszt. Duży plik trzeba przepisać w całości przy każdej edycji, mały moduł nie:
| Liczba operacji | Czas: jeden duży plik | Czas: mały moduł | Tokeny: duży | Tokeny: mały |
|---|---|---|---|---|
| 4 | 17 s | 10 s | 3 891 | 3 479 |
| 16 | 42 s | 11 s | 5 745 | 3 845 |
| 32 | 81 s | 12 s | 8 462 | 4 398 |
| 64 | 163 s | 14 s | 13 835 | 5 422 |
Przy 64 operacjach podział jest około 12 razy tańszy w czasie. Dwie siły działają więc przeciwnie: pośredniość utrudnia znalezienie zmiany, ale potania jej wprowadzenie. O tym, czy w ogóle dojdziesz do tej oszczędności, decyduje nazewnictwo.
Wynik 8: silniejszy model nie usuwa problemu, tylko inaczej za niego płaci
Na najmocniejszym testowanym modelu kara w zadaniach znika - skuteczność wraca do 1,00.
Ale sonda lokalizacji pokazuje, że deficyt zostaje: 0,67 dla nazw domenowych wobec 0,25
dla wzorcowych. Model nadrabia eksploracją - liczba wywołań grep rośnie średnio
z 0,67 do 1,02 na zadanie.
Słaby agent płaci porażką, mocny płaci dodatkowym szukaniem i nadal kończy zadanie. Deficyt informacyjny jest ten sam; zmienia się tylko waluta.
Wynik 9: metryki utrzymywalności tego nie widzą
Wariant pośredni wypada lepiej we wszystkich klasycznych miarach jakości: mniejszy największy moduł (43 → 25 linii), niższa średnia złożoność funkcji (3,0 → 1,74). Ten sam refaktor, który podnosi wskaźnik utrzymywalności, obniża skuteczność agenta.
Metryki pisane pod czytelnika-człowieka nie mierzą ani tej kary, ani oszczędności kosztowej z podziału. Jeśli oceniacie kod wyłącznie nimi, ta zmienna jest dla Was niewidoczna.
Co z tego stosujemy u siebie
To jest techniczne uzasadnienie tego, co nazywamy podejściem AI First - nie hasło, tylko reguły wyprowadzone z pomiarów:
- Nazwa pliku zawiera pojęcie z dziedziny.
tax_calculator.py, niecalculator.py. Słowo-rola może zostać, rzeczownik domenowy musi. - Katalogi po dziedzinie, nie po wzorcu.
platnosci/, nieadapters/. - Dziel moduły, ale nie zakopuj logiki. Kara rośnie z głębokością ukrycia, nie z liczbą plików.
- Powierzchnia zmian blisko wejścia. Rzeczy zmieniane najczęściej nie chowamy za trzema warstwami.
- Nie ufamy samym metrykom utrzymywalności. Dobrze wyglądający refaktor potrafi pogorszyć pracę agenta o kilkadziesiąt punktów procentowych.
Przy dużych projektach to przestaje być kwestią estetyki. Jeśli model nie trafia w plik, każda zmiana kosztuje więcej - w czasie, w tokenach i w liczbie prób, po których ktoś musi to poprawić ręcznie.
Zastrzeżenia
Trafność lokalizacji to wskaźnik zastępczy dla mechanizmu „znajdź właściwy plik”, mierzony jednym wywołaniem, a nie pełne wykonanie zadania. Repozytoria end-to-end są małe i budowane na potrzeby eksperymentu, a zadania utrzymaniowe łatwe - dlatego na mocnym modelu efekt widać jako koszt, nie jako porażkę. Wyniki na prawdziwych źródłach i przy 253 plikach pochodzą z sondy lokalizacji. Strona ludzka opiera się na metrykach statycznych; badania z udziałem programistów jeszcze nie prowadziliśmy.
Piszemy o tym mimo zastrzeżeń, bo kierunek jest spójny w pięciu modelach, trzech rodzinach, dwóch językach i na kodzie produkcyjnym trzech projektów open source.
Co dalej
Rozpoznajesz problem? Sprawdźmy Wasz przepływ danych.
Audyt konta BaseLinker i wymiany danych z partnerami: 2 500 - 6 000 zł. Wynik to konkretny dokument z rekomendacją architektury i orientacyjnym kosztem kolejnego etapu. Bez zobowiązania kontynuacji u nas.