Przejdź do głównej zawartości

Struktura projektu zoptymalizowana pod agentów AI grupuje kod według funkcjonalności, utrzymuje małe pliki nazwane od tego, co zawierają, trzyma każdy test obok kodu, który sprawdza, i umieszcza instrukcje w katalogu, którego dotyczą. Claude Code, Codex i Cursor znajdują wtedy właściwe pliki mniejszą liczbą wyszukiwań i ładują tylko pasujące reguły.

Prosisz agenta o endpoint do zwrotów. Szuka validate, otwiera 1400-liniowy utils.ts, czyta 40 niezwiązanych helperów, wybiera zły i zapisuje nowy test w drzewie __tests__, którego CI nie uruchamia. Model zrobił to, na co pozwoliło repozytorium. Ta strona jest dla programistów, którzy przebudowują kod z pomocą agentów, i dla tech leadów, którzy ustalają konwencje układu dziedziczone przez wszystkich agentów w zespole.

Co wyniesiesz ze struktury projektu przyjaznej agentom

Dział zatytułowany „Co wyniesiesz ze struktury projektu przyjaznej agentom”
  • Pięć reguł układu, każdą powiązaną z konkretnym sposobem, w jaki agenci szukają i czytają kod.
  • Instrukcje ograniczone do katalogu dla Claude Code, Codeksa i Cursora, z regułą ładowania, której każde narzędzie faktycznie używa.
  • Cztery prompty do skopiowania: audyt struktury tylko do odczytu, podział pliku bez zmiany zachowania, instrukcje dla najczęściej zmienianych katalogów i test znajdowalności.
  • Kontrole w CI, które nie pozwalają strukturze się rozjechać: budżet rozmiaru pliku, wykrywanie cyklicznych importów i diff złożony wyłącznie z przeniesień.

Strona zakłada, że wiesz, jak agenci przeszukują repozytorium. Jeśli nie, zacznij od strony wyszukiwanie i indeksowanie kodu dla agentów AI.

Agent poznaje kod tak jak nowa osoba w zespole pierwszego dnia: listuje katalogi, szuka nazw i otwiera pliki. Każdy otwarty plik trafia do okna kontekstu, gdzie konkuruje o uwagę z twoimi instrukcjami (zob. jak zapełnia się okno kontekstu). Układ decyduje, ile z tych odczytów idzie na marne.

Cecha układuCo agent z nią robiCo się psuje, gdy jej brak
Katalogi według funkcjonalnościListuje jeden katalog i widzi całą funkcjonalnośćOtwiera controllers/, services/, models/ i types/, żeby złożyć jedną zmianę
Małe plikiCzyta plik w całości i go rozumieDostaje częściowy widok dużego pliku albo czyta 1000 linii, żeby użyć 20
Opisowe nazwyZnajduje plik samym wyszukiwaniem po nazwieOtwiera na próbę helpers.ts, misc.ts i common.ts
Testy obok koduWyprowadza ścieżkę testu ze ścieżki źródłaPisze testy w złym drzewie albo je pomija
Instrukcje katalogoweŁaduje reguły billingu tylko przy pracy nad billingiemNosi reguły wszystkich zespołów w każdej sesji albo żadnych

W Claude Code duże pliki mają jeszcze jeden koszt. Gdy odczyt całego pliku przekracza limit tokenów narzędzia Read, narzędzie zwraca tylko pierwszą stronę z komunikatem PARTIAL view, a Claude musi doczytywać resztę (dokumentacja narzędzi Claude Code, sprawdzone 2026-09-26). Helper zdefiniowany w linii 1300 łatwo przeoczyć.

Wprowadzaj je w tej kolejności. Pierwsze dwie dają większość korzyści, a trzy kolejne od nich zależą.

Układ według typu rozrzuca jedną funkcjonalność po całym drzewie. Układ według funkcjonalności trzyma ją w jednym katalogu, który widać po jednym listowaniu:

# Type-first: one refund change touches four directories
src/controllers/refund-controller.ts
src/services/refund-service.ts
src/models/refund.ts
tests/services/refund-service.test.ts
# Feature-first: one directory, one listing
src/features/refunds/
refund-handler.ts
refund-handler.test.ts
refund-policy.ts
refund-policy.test.ts
refund-types.ts
AGENTS.md # rules for this feature only
CLAUDE.md # one line, @AGENTS.md, for Claude Code

Kod naprawdę współdzielony (klient bazy danych, logowanie, typy błędów) trzymaj w src/shared/, podzielony na małe pliki o konkretnych nazwach. shared/ to nie nowy utils.ts: jeśli funkcja ma jedno miejsce wywołania, należy do katalogu funkcjonalności, która ją wywołuje.

Utrzymuj pliki na tyle małe, żeby dało się je przeczytać w całości

Dział zatytułowany „Utrzymuj pliki na tyle małe, żeby dało się je przeczytać w całości”

Żadne opublikowane badanie nie wyznacza właściwej liczby linii, więc wybierz budżet, zapisz go i oddaj linterowi. Zacznij od 400 linii, wartości z konfiguracji poniżej, i skoryguj ją, gdy zobaczysz wynik audytu. Ważniejsze od liczby jest to, żeby podział szedł za odpowiedzialnością: refund-policy.ts i refund-handler.ts, nigdy refund-1.ts i refund-2.ts.

W TypeScripcie i JavaScripcie egzekwuj budżet wbudowaną regułą ESLinta max-lines, najpierw jako ostrzeżenie, a po wyczyszczeniu zaległości jako błąd:

eslint.config.js
export default [
{
files: ['src/**/*.{ts,tsx}'],
rules: {
'max-lines': ['warn', { max: 400, skipBlankLines: true, skipComments: true }],
},
},
];

Nazwa pliku jest indeksem wyszukiwania. Agent, który szuka password, powinien dostać password-hashing.ts, a nie trafienie w linii 612 pliku auth-helpers.ts.

MglisteKonkretne
utils.ts, helpers.ts, misc.tspassword-hashing.ts, currency-format.ts, csv-parse.ts
service.ts w notifications/notification-sender.ts
types.ts na 500 liniirefund-types.ts, invoice-types.ts
index.ts reeksportujący 60 symboliBezpośrednie importy albo barrel poniżej 20 linii

Stała reguła x.ts → x.test.ts w tym samym katalogu pozwala agentowi znaleźć test dowolnego pliku bez wyszukiwania i zapisywać nowe testy tam, gdzie szuka ich runner. Jeśli framework wymaga osobnego drzewa testów (np. projekty w Pythonie z katalogiem tests/ odwzorowującym pakiet), odwzoruj ścieżkę źródła dokładnie i zapisz tę regułę w pliku instrukcji.

Plik barrel (index.ts), który reeksportuje cały pakiet, zmusza agenta do przeczytania jednego pliku, żeby znaleźć następny. Cykliczne importy zacierają granice modułów, więc agent nie wie, który moduł jest właścicielem funkcji. Oba problemy wyłapują kontrole CI opisane niżej. Reguły warstw (UI nie importuje klienta bazy danych) zrealizuj kontrolami granic z artykułu jak przygotować repozytorium do pracy agentów, zamiast dokładać drugie narzędzie.

Główny plik instrukcji powinien zawierać to, co obowiązuje wszędzie. Reguły dla jednego katalogu należą do tego katalogu: ładują się, gdy agent tam pracuje, i nic nie kosztują, gdy nie pracuje. Trzy narzędzia ładują takie instrukcje inaczej, a ta różnica decyduje, gdzie startujesz sesję.

Claude Code ładuje przy starcie pliki CLAUDE.md z katalogu roboczego i wszystkich katalogów powyżej. CLAUDE.md w podkatalogu ładuje się na żądanie, gdy Claude czyta pliki w tym podkatalogu. Dla reguł, które idą za wzorcem ścieżki, a nie za katalogiem, użyj reguły z polem paths w .claude/rules/:

---
paths:
- "src/features/billing/**"
---
# Billing rules
- Money is integer cents: use the `Cents` type from src/features/billing/money.ts. Never use floats.
- Every handler in this folder has a sibling `*.test.ts`. Add one with every new handler.

Zapisz ją jako .claude/rules/billing.md. Reguły z paths uruchamiają się, gdy Claude czyta pasujący plik, a nie przy każdym użyciu narzędzia (dokumentacja pamięci Claude Code, sprawdzone 2026-09-26). Claude Code czyta też AGENTS.md w projekcie bez CLAUDE.md, w tym pliki AGENTS.md w podkatalogach, o ile w katalogu roboczym ani powyżej nie ma CLAUDE.md; wymaga to v2.1.277 lub nowszej (v2.1.281 na Bedrocku, Google Cloud, Foundry, bramkach LLM albo z wyłączoną telemetrią); działa to na obu kanałach wydań, odkąd stable osiągnął v2.1.287 (sprawdzone 2026-10-12).

Dwie pułapki. Wbudowane subagenty Explore i Plan pomijają pliki CLAUDE.md, więc mapa katalogów trzymana tylko tam do nich nie dociera. W monorepo ładują się pliki CLAUDE.md przodków należące do innych zespołów; wyklucz je ustawieniem claudeMdExcludes w .claude/settings.local.json.

Zespół, który używa obu narzędzi, powinien zapisać reguły katalogu raz, w jego AGENTS.md. Dwie kopie tych samych reguł rozjeżdżają się. Claude Code zobaczy ten plik na jeden z dwóch sposobów (dokumentacja pamięci Claude Code, sprawdzone 2026-09-26):

  • Dodaj obok CLAUDE.md, który zawiera tylko linię importu @AGENTS.md. Importy rozwijają się, gdy ładuje się CLAUDE.md.
  • Ustaw Project instructions na claude-md-and-agents-md w /config, żeby Claude Code czytał pliki CLAUDE.md i AGENTS.md razem.

Nie polegaj na ustawieniu domyślnym. Przy domyślnej wartości claude-md-or-agents-md Claude Code czyta pliki AGENTS.md tylko wtedy, gdy w katalogu roboczym ani w żadnym katalogu powyżej nie ma CLAUDE.md, .claude/CLAUDE.md ani CLAUDE.local.md. W repozytorium z CLAUDE.md w korzeniu sam src/features/billing/AGENTS.md jest więc dla Claude Code niewidoczny, choć Codex nadal go ładuje.

W każdym pliku katalogowym trzymaj tylko reguły, których agent nie wywnioskuje z kodu: niezmienniki, jeden zatwierdzony helper, miejsce testów. Strona dokumentacja jako kontekst opisuje, co jeszcze należy do plików instrukcji, a przycinanie CLAUDE.md i AGENTS.md — jak usuwać to, co przestało być warte swoich tokenów.

Przebudowa struktury to mechaniczna operacja, która zachowuje działanie kodu: agent robi takie rzeczy dobrze, a bramka może je w pełni sprawdzić. Prowadź ją w małych pull requestach, po jednym katalogu albo jednym pliku-śmietniku, żeby równoległe gałęzie nie zderzyły się z przeniesieniem na skalę całego repozytorium.

  1. Zrób audyt, zanim cokolwiek przeniesiesz. Uruchom prompt audytu poniżej w nowej sesji, tylko do odczytu. Żądaj zmierzonej liczby linii, a nie szacunków, i sortuj wyniki według częstotliwości zmian, bo duży plik, którego nikt nie dotyka, kosztuje niewiele.

  2. Zatwierdź docelowy układ, a nie diff. Agent proponuje nowe drzewo i nazwę każdego nowego pliku. Ty albo tech lead przeglądacie tę listę: jest krótka, a zły podział najszybciej widać po nazwach.

  3. Przenoś jedną jednostkę na pull request. Użyj promptu do podziału. Zabrania zmian w ciałach funkcji, więc diff to tylko przeniesienia i aktualizacje importów.

  4. Udowodnij, że nic się nie zmieniło. Uruchom bramki z następnej sekcji. Czyste przeniesienie nie wymaga przeglądu linia po linii, gdy bramki przechodzą.

  5. Napisz instrukcje katalogowe. Gdy katalog funkcjonalności już istnieje, dodaj jego CLAUDE.md, AGENTS.md albo regułę .mdc promptem do instrukcji katalogowych.

  6. Zamień budżet w błąd. Gdy lista z audytu jest pusta, przełącz max-lines z warn na error, żeby struktura nie mogła się cofnąć.

Nie czytasz przeniesienia 60 plików linia po linii. Sprawiasz, że da się je sprawdzić, i oddajesz akceptację kontrolom:

  • Diff złożony z samych przeniesień. git diff -M --summary main...HEAD pokazuje każdy przeniesiony plik jako rename z procentem podobieństwa. Czyste przeniesienie ma wysokie podobieństwo w każdym pliku; niski procent wskazuje plik, w którym agent zmienił więcej niż importy. Podział, na przykład utils.ts na moduły, pojawia się jako create mode i delete mode, a nie jako przeniesienia, więc przy podziale uruchom git diff --color-moved=zebra --color-moved-ws=allow-indentation-change main...HEAD: przeniesione bloki mają kolory przeniesień, a każda linia w zwykłym kolorze dodania lub usunięcia to prawdziwa zmiana do przejrzenia.
  • Te same testy, te same wyniki. Type checker, linter i pełny zestaw testów przechodzą, a liczba testów się nie zmienia. Spadek liczby oznacza, że testy zgubiły się przy przenoszeniu.
  • Brak nowych cykli. npx madge --circular --extensions ts,tsx src/ kończy się kodem 1, gdy znajdzie cykliczny import (madge 8.0.0), więc działa jako krok CI bez przeróbek.
  • Budżet się trzyma. max-lines działa w tym samym zadaniu lintowania co reszta reguł.
  • Znajdowalność się poprawiła. Uruchom test poniżej w nowej sesji przed i po zmianie i porównaj, ile plików agent otworzył i ile kontekstu zużył (/context w Claude Code, /status w Codeksie).

Uruchom test z trzema pytaniami, które zespół zadaje najczęściej. Jeśli po zmianie agent otwiera tyle samo plików co przed nią, nowe nazwy nie spełniają swojej roli. Tech lead odpowiada za konwencje układu i zatwierdza docelowe drzewo w kroku 2; od tego momentu odpowiada CI, a pull request, który nie przechodzi bramki, nie trafia do gałęzi głównej.

Co się psuje, gdy przebudowujesz strukturę pod agentów?

Dział zatytułowany „Co się psuje, gdy przebudowujesz strukturę pod agentów?”

Plik instrukcji katalogowych nigdy się nie ładuje. Codex wystartował w korzeniu repozytorium, więc AGENTS.md pakietu poniżej nie został przeczytany; albo reguła ścieżkowa w Claude Code nie zadziałała, bo Claude zapisał nowy plik, nie czytając żadnego pasującego. Jak to naprawić: dodaj do pliku katalogowego linię kanarkową („When you use a rule from this file, say BILLING-RULES”), uruchom sesję tak, jak robi to zespół, i zadaj pytanie o billing. Uruchamiaj Codeksa w pakiecie przez -C albo poproś Claude’a, żeby najpierw przeczytał sąsiedni plik.

Konwencje frameworka psują się po cichu. Routery oparte na plikach, katalogi migracji, wykrywanie wtyczek i dynamiczne import() ze ścieżką w stringu zależą od nazw i lokalizacji, których type checker nie widzi. Jak to naprawić: wpisz te katalogi do promptu podziału jako nietykalne, po przeniesieniu wyszukaj starą ścieżkę jako tekst i uruchom testy end-to-end, a nie tylko jednostkowe.

Podział idzie za budżetem linii, a nie za odpowiedzialnością. Agent tnie 900-liniowy plik na trzy według pozycji, i każda zmiana dotyka teraz wszystkich trzech. Jak to naprawić: odrzuć listę plików w kroku 2, gdy nazwy są numerowane albo ogólnikowe, i poproś o podział według odpowiedzialności z jednym zdaniem na plik o tym, za co odpowiada.

Barrel po cichu zastępuje dawny plik-śmietnik. shared/index.ts reeksportuje wszystko, więc agent czyta barrel i każdy moduł. Jak to naprawić: usuń barrel, przejdź na bezpośrednie importy i uruchom ponownie prompt audytu, którego krok 3 wyłapuje każdy plik index dłuższy niż 20 linii.

Przeniesienie w całym repozytorium zderza się z otwartymi gałęziami. Wszystkie otwarte pull requesty mają konflikty naraz. Jak to naprawić: przenoś jeden katalog na pull request, scalaj każdy w ciągu dnia i zapowiadaj przeniesienia, zanim wejdą.

Kod generowany leży w katalogach funkcjonalności. Agent go przeszukuje, edytuje, a następne uruchomienie generatora kasuje zmianę. Jak to naprawić: przenieś wynik generatora do wyraźnie nazwanego katalogu i wyklucz go z wyszukiwania agenta, jak opisuje strona wyszukiwanie i indeksowanie kodu dla agentów AI.