Strukturyzowanie projektów Claude Code, aby agenci mogli skupić się w dużych repozytoriach
Sześć wzorców dla dużych baz kodu: warstwowe pliki CLAUDE.md, umiejętności, podagenty, reguły odrzucania, rzadkie drzewa zadań oraz wtyczki language-server, które utrzymują kontekst agenta w czystości.
Źródła: How I Structure Claude Code Projects So Agents Don’t Get Lost in Large Codebases
Wyobraź sobie prostą prośbę skierowaną do Claude Code: naprawić błąd timeoutu sesji w middleware służącym do autoryzacji. Rzeczywista naprawa zajmuje zaledwie trzy linie kodu. Jednak zanim agent do nich dotrze, błądzi po systemie logowania, historii migracji, połowie punktów końcowych API, a z powodów, których nikt nie potrafi wyjaśnić, także po konfiguracjach Terraform. Po przeczytaniu 147 plików programista musi użyć klawisza Escape.
W takiej sytuacji winowajcą nie jest model, lecz struktura projektu. Każda konwencja, polecenie kompilacji oraz decyzja architektoniczna została zgromadzona w jednym pliku CLAUDE.md liczącym 340 linii, który agent sumiennie czytał przy uruchamianiu, po czym przeznaczał resztę swojego „budżetu” na analizę kodu niewiążącego się z procesem autoryzacji.
To się dzieje, gdy z agentem postępuje się jak z nowym pracownikiem, który musi przeczytać cały podręcznik firmy, zanim będzie mógł poprawić błąd ortograficzny. Potrzebuje on odpowiedniego kontekstu we właściwym momencie, a nie wszystkiego naraz. Poniżej przedstawiono sześć sposobów na rozwiązanie tego problemu wraz z metodą oceny, kiedy warto zastosować każdy z nich.
Dlaczego kontekst szybko się wyczerpuje
Okno kontekstowe Claude Code, wynoszące około 200 000 tokenów w używanym tutaj modelu, wydaje się ogromne, dopóki nie wymienimy wszystkiego, co go zużywa: każdą wysłaną wiadomość, każdy przeczytany plik, wszystkie wyniki poleceń, każdą instrukcję CLAUDE.md oraz wszelkie automatycznie zapisane notatki pamięciowe. Jeden token odpowiada mniej więcej czterem znakom.
W dużym kodzie źródłowym 50 000 tokenów może zniknąć, zanim napisana zostanie choć jedna linia kodu. Plik CLAUDE.md składający się z 300 linii kosztuje około 1 200 tokenów, plik z 500 liniami – kolejne 2 000, a przeszukiwanie za pomocą grep lub find dodatkowo zwiększa tę liczbę. Zanim agent zrozumie zadanie, połowa dostępnej przestrzeni może już zostać zużyta. W miarę jej wypełniania wydajność spada: wcześniejsze instrukcje są zapominane, pliki są czytane ponownie, a pojawiają się błędy, których nie byłoby w czystym kontekście. Większe okna nie są rozwiązaniem – kluczowe jest wykluczanie nieistotnych materiałów z tej przestrzeni.
Wzorzec 1: warstwowe pliki CLAUDE.md
Pierwszym błędem jest umieszczanie wszystkiego w pliku głównym: poleceń budowania dla API, wzorów komponentów frontendowych, reguł migracji, list kontrolnych wdrożenia oraz konwencji testowych razem.
Claude Code odczytuje pliki CLAUDE.md z katalogu roboczego aż do korzenia repozytorium po rozpoczęciu sesji. Jeśli uruchomisz go w podkatalogu, pobierze plik z tego folderu oraz wszystkie pliki z katalogów nadrzędnych aż do korzenia. Dlatego podziel treść według zakresu:
monorepo/
CLAUDE.md # repository-wide rules only
packages/
api/
CLAUDE.md # API-specific conventions
.claude/skills/
src/
web/
CLAUDE.md # frontend-specific conventions
.claude/skills/
src/
shared/
CLAUDE.md # shared library conventions
src/
Plik korzeniowy zawiera jedynie strukturę repozytorium oraz zasady komitowania, około 40 linijek, a każdy pakiet posiada własne szczegóły specyficzne dla danego środowiska. Uruchom agent w katalogu packages/api/ – załaduje on plik korzeniowy oraz plik dotyczący API, nie widząc przy tym zasad stosowanych w interfejsie użytkownika. Przejdź do katalogu packages/web/, a ta sama logika dostarczy inny fragment kontekstu.
Plik korzeniowy może wyglądać następująco:
# Repository Structure
This is a monorepo with three packages under packages/:
- packages/api: Node.js REST API with Express, TypeScript, PostgreSQL
- packages/web: React frontend with Vite, TypeScript, TailwindCSS
- packages/shared: shared TypeScript utilities
Run commands from the package directory, not the monorepo root.
Each package has its own tsconfig.json, package.json, and test suite.
# Commit Conventions
- Prefix commits with the package name: "api: fix session timeout"
- One commit per logical change
- Run tests before committing
A pakiet API zawiera wyłącznie to, co jest ważne wewnątrz tego pakietu:
# API Package
This is the REST API server.
## Commands
- Run tests: `npm test` (uses Vitest)
- Run dev server: `npm run dev` (port 3001)
- Database migrations: `npm run migrate`
- Environment variables: copy `.env.example` to `.env`
## Code Patterns
API routes are in src/routes/. Each route file exports an Express router.
Database queries use Knex in src/db/. Never write raw SQL in route handlers.
## Testing
Tests are in src/__tests__/ mirroring the src/ directory.
Use supertest for HTTP assertions, not raw fetch.
Always wrap database tests in a transaction that rolls back.
W tym przykładzie efektem jest 80 linii załadowanego kontekstu zamiast 340, a naprawa problemów z autoryzacją dotyczy 12 plików zamiast 147.
Wzorzec 2: umiejętności do uzyskiwania wiedzy na żądanie
Nawet struktura warstwowa może powodować nadmierną objętość. Wzorce testowania, listy kontrolne wdrażania oraz podręczniki do naprawiania błędów to procedury, a nie fakty; agent nie potrzebuje ich przy uruchamianiu, tylko wtedy, gdy zadanie tego wymaga. To właśnie jest zadaniem umiejętności.
Umiejętności znajdują się w katalogu .claude/skills/ i są ładowane, gdy Claude uzna je za istotne lub gdy są wywoływane bezpośrednio za pomocą polecenia z użyciem słowa „/”. Opis umiejętności wskazuje, kiedy należy ją użyć, a jej treść zawiera faktyczny content. W tym przypadku wskazówki dotyczące testowania przenoszą się z pliku API do umiejętności:
# .claude/skills/api-testing/SKILL.md
---
name: api-testing
description: Testing patterns for the API package. Use when writing or modifying tests in packages/api/.
---
## Test Structure
Tests are in `src/__tests__/` mirroring the `src/` directory structure.
Each route file has a corresponding `.test.ts` file.
## Running Tests
- All tests: `npm test`
- Single file: `npm test -- src/__tests__/routes/users.test.ts`
- Watch mode: `npm test -- --watch`
## Test Utilities
- `src/__tests__/helpers/db.ts`: provides `setupTestDb()` and `teardownTestDb()`
- `src/__tests__/helpers/auth.ts`: provides `createTestUser()` and `getAuthToken()`
## Patterns
- Use `supertest` for HTTP assertions, not raw fetch
- Always wrap database tests in a transaction that rolls back
- Mock external services in `src/__tests__/mocks/`
Zapytaj o testy dla middleware autoryzacyjnego, a agent załaduje umiejętność testową i będzie postępować zgodnie z jej wzorcami. Poproś go o naprawę błędu czasu oczekiwania, ale umiejętność pozostanie nelożona, ponieważ testowanie nie jest w tym przypadku istotne.
Umiejętności nadają się również do powtarzalnych procesów pracy. Umiejętność wywołana za pomocą /fix-pr-comments mogłaby odczytać komentarze do recenzji w prośbie o połączenie w GitHubie, określić wymagane zmiany, edytować kod, uruchomić testy i zapisać zmiany. Cała procedura znajduje się w umiejętności, a nie w pliku CLAUDE.md, który za każdym razem pobiera kontekst.
Zasada ogólna: fakty potrzebne przy każdej sesji umieszcza się w CLAUDE.md, natomiast procedury i materiały referencyjne stają się umiejętnościami.
Wzorzec 3: podagenty do izolowanej eksploracji
Nawet przy użyciu warstw i specjalistycznych umiejętności eksploracja generuje zbyt dużo danych. Pytanie „Jak działa autoryzacja?” może skutkować 40 operacjami odczytu plików, gdy agent analizuje proces logowania, przechowywanie sesji, odnowę tokenów oraz logowanie wylogowania, a cały ten tekst pozostaje w głównej rozmowie po udzieleniu odpowiedzi, zanieczyszczając resztę sesji.
Podagent działa w swoim własnym oknie kontekstowym, przeprowadza eksplorację i wysyła tylko streszczenie. Operacje odczytu plików oraz wyniki z grep pozostają w jego kontekście. Główny wątek otrzymuje streszczenie, dzięki czemu całość pozostaje uporządkowana. Prośba o badanie może wyglądać w ten sposób:
Use a subagent to investigate how our authentication system handles
session timeout and token refresh. Report back what files are involved
and how the flow works.
Podagent czyta to, czego potrzebuje, śledzi przebieg i składa raport, dzięki czemu główna rozmowa otrzymuje zwięzłe wyjaśnienie liczące około 200 słów, a nie treść 40 plików. Podagenci pomagają również przy sprawdzaniu po wdrożeniu:
Use a subagent to review the session timeout fix for edge cases
and consistency with our existing auth patterns.
Podagent odpowiedzialny za przeglądanie widzi aktualną wersję pliku, bez wyjaśnień, które spowodowały zmianę.
Jedna uwaga: streszczenia tracą szczegóły. Jeśli potrzebujesz dokładnych numerów wierszy lub fragmentów kodu, główny agent może musieć ponownie przeczytać pliki, które już widział podagent. Podagenci utrzymują główny tok realizacji, podczas gdy eksploracja odbywa się gdzie indziej.
Wzorzec 4: zachowywanie niewypróbowanych plików wygenerowanych lub dostarczonych
Wszystko, co jest wymienione w pliku .gitignore, jest domyślnie pomijane, dzięki czemu foldery node_modules/, dist/ i build/ nie pojawiają się w wynikach wyszukiwania. Mimo to niektóre wygenerowane lub dostarczone pliki są mimo to zapisywane: skompilowane definicje protobuf, dostarczone SDK-y, wygenerowane typy GraphQL. Agent chętnie je otworzy, jeśli wyszukiwanie je ujawni, chociaż nie powinno to się zdarzyć.
Zasady odrzucania uprawnień blokują odczyt wybranych wzorów plików. Ogólny kształt tych zasad jest przedstawiony poniżej, a dokładną składnię należy sprawdzić w dokumentacji dotyczącej Twojej wersji Claude Code:
# packages/api/.claude/settings.json
{
"permissions": {
"deny": [
"Read(./**/dist/**)",
"Read(./**/build/**)",
"Read(./**/*.generated.*)",
"Read(./vendor/**)"
]
}
}
Gdy wyszukiwanie „GraphQL resolvers” znajduje teraz utworzony plik schematu, wynik się pojawia, ale narzędzie Read odpowiada, że dostęp odmówiono, a agent zazwyczaj pyta, gdzie znajduje się prawdziwy źródło. Efekt uboczny, o którym warto wiedzieć: te same zasady blokują również próby odczytu dokonywane ręcznie w oknie czatu. Jeśli poprosisz o otwarcie pliku src/schema.generated.ts, zostanie to odrzucone – należy tymczasowo usunąć tę zasadę. Korzyścią jest to, że generowane dane nie wchodzą w kontekst, a agent jest kierowany ku prawdziwemu źródłu.
Wzorzec 5: rzadkie worktree’y
Claude Code może tworzyć worktree’y git do pracy równoległej lub izolowanej, a domyślnie każdy z nich pobiera całe repozytorium. W monorepo jest to marnotrawstwo, ponieważ naprawa API nie wymaga frontendu.
Git’s sparse-checkout zapisuje na dysku tylko wybrane katalogi, a pozostałe pozostawia w indeksie, a ustawienie worktree.sparsePaths opiera się na tym mechanizmie. Sprawdź klucz konfiguracyjny w dokumencacji dotyczącej Twojej wersji:
# packages/api/.claude/settings.json
{
"worktree": {
"sparsePaths": [
".claude",
"packages/api",
"packages/shared"
]
}
}
Worktrees zawierają jedynie katalogi .claude/, packages/api/ oraz packages/shared/ wraz z plikami korzeniowymi takimi jak package.json i tsconfig.base.json. Są szybciej tworzone i zajmują mniej miejsca na dysku. Jeśli agent próbuje odczytać plik, który nie został wypożyczony, git wyświetla błąd, a agent zazwyczaj pyta o istniejące katalogi. Funkcja Sparse-checkout wymaga git 2.25 lub nowszej wersji, co można potwierdzić za pomocą polecenia git --version.
Wzorzec 6: inteligencja kodu zamiast skanowania plików
Znalezienie miejsca, w którym funkcja jest zdefiniowana lub używana w dużym kodzie, wymaga wielu wywołań grep oraz odczytów plików, ponieważ agent przegląda wyniki, otwiera potencjalne opcje i kontynuuje poszukiwania.
Wtyczki inteligencji kodu łączą agenta z serwerem językowym. Zamiast skanowania, pyta, gdzie zdefiniowano refreshAuthToken, i natychmiast otrzymuje plik oraz numer wiersza. W przypadku projektów TypeScript oficjalna wtyczka LSP instaluje się w ten sposób, chociaż składnia instalacji różni się w zależności od wersji, więc sprawdź dokumentację:
/plugin install typescript-lsp@claude-plugins-official
Zadaj pytanie o to, gdzie używany jest refreshAuthToken, a agent zapyta serwer językowy i otrzyma odpowiedź:
src/middleware/auth.ts:47
src/routes/users.ts:103
src/__tests__/auth.test.ts:22
Nie było potrzeby odczytywania żadnych plików, dzięki czemu odpowiedzi są szybsze, a kontekst pozostaje czysty. Dodatek zgłasza również błędy typu po edycjach bez uruchamiania tsc, więc błędy pojawiają się natychmiast. Dobrze współpracuje z regułami odrzucania: one blokują niepotrzebne elementy, a funkcja analityki kodu zmniejsza konieczność skanowania tego, co pozostało.
Wszystkie sześć razem
Przy zastosowaniu każdego wzorca, oryginalna korekta błędu wygląda mniej więcej w ten sposób:
- Rozpocznij pracę Claude z katalogu
packages/api/. - Ładuje on plik główny
CLAUDE.md(40 linii) oraz plik API (50 linii). - Proszesz go o naprawę błędu czasu wygaśnięcia sesji w komponentie auth middleware.
- Wysyła on podagenta w celu zbadania procesu autoryzacji, który w swoim kontekście odczytuje około 20 plików.
- Podagent zgłasza, że logika czasu wygaśnięcia znajduje się w pliku
src/middleware/auth.tsna linii 47 i używa niewłaściwego operatora porównania. - Główny programista czyta ten plik, naprawia błąd, pisze test i go uruchamia, edytując około czterech plików w głównym kontekście.
W tym przykładzie przed i po przekształceniu widać 147 odczytów plików, trzy nieudane próby oraz 35 minut w porównaniu z około czterema odczytami, jedną udaną próbą i 6 minutami. Traktuj to jako anegdotę jednego programisty, a nie wskaźnik wydajności, ale ważna jest kierunek rozwoju: taka struktura usunęła przeszkody pomiędzy pytaniem a odpowiedzią.
Wybór tego, czego potrzebujesz
Nie każdy projekt wymaga wszystkich sześciu elementów. Jeden repozytorium z 50 plikami nie potrzebuje warstwowych plików ani rozproszonych drzew prac. Gdy baza kodu przewyższa to, co jedna osoba może zapamiętać, te elementy zaczynają przynosić korzyści:
- Warstwowe pliki
CLAUDE.mdsą przydatne, gdy przewijasz się poza nieistotne sekcje w pliku głównym lub gdy koledzy z zespołu pytają, dlaczego plik API zawiera zasady frontendu. - Umiejętności są przydatne, gdy sekcja
CLAUDE.mdprzekształciła się w procedurę lub listę kontrolną, albo gdy ciągle wklejasz te same instrukcje do czatu. - Podagenty są przydatne, gdy dochodzenie kończy się poleceniem „przeczytaj te 30 plików” lub gdy chcesz recenzję bez uprzedzeń.
- Zasady odrzucenia są przydatne, gdy agent ciągle otwiera generowany lub dostarczony kod pomimo obecności pliku
.gitignore. - Rzadkie drzewa robocze są przydatne, gdy monorepo zajmuje ponad 10 sekund na otwarcie lub gdy tworzysz wiele drzew roboczych równocześnie.
Celem nie jest ślepe stosowanie wszystkiego. Chodzi o to, by zauważyć, gdy kontekst staje się zbyt zagracony, i wybrać wzorzec, który go uporządkuje. Inne rozwiązania mogą też sprawdzić się w dużych bazach kodu, ale te sześć przekształca agenta, który czyta wszystko, w takiego, który czyta właściwe pliki, wpisuje poprawki i przestaje.