Semantycznie, nie tekstowo
Dzięki indeksowi wektorowemu zapytanie „znajdź wszystkie przepływy uwierzytelniania” wydobywa kod OAuth, JWT i sesji, nawet gdy żaden z nich nie dzieli słowa kluczowego.
Bazy kodu liczące miliony linii przekraczają każde okno kontekstu, więc praca z AI opiera się w nich na strukturze, a nie na pojemności: warstwowa piramida kontekstu zamiast ładowania repozytorium, wyszukiwanie semantyczne zamiast grepa, raport promienia rażenia przed każdą edycją i zmiany przyrostowe weryfikowane moduł po module. Cursor, Claude Code i Codex wymagają innej konfiguracji indeksowania.
Odziedziczyłeś monolit liczący 1,8 miliona linii. Pierwotni architekci odeszli dwa lata temu, dokumentacja opisuje system, który już nie istnieje, a twój pierwszy ticket zmienia nazwę pola w User. Agent z pełnym przekonaniem edytuje trzy pliki w packages/web, ogłasza sukces, a dwie godziny po wdrożeniu usługa billing-worker wyrzuca błąd na produkcji, bo czytała to samo pole ze współdzielonego schematu, którego agent nigdy nie otworzył. Model nie pomylił się dlatego, że jest głupi. Pomylił się, bo nigdy nie zobaczył pliku, który miał znaczenie.
Obecne flagowe modele wspierają okna kontekstu o pojemności 1M tokenów (Claude Fable 5, Opus 5, Sonnet 5, Gemini 3.1 Pro), a baza kodu licząca wiele milionów linii i tak się w nich nie zmieści. Odpowiedzią nie jest większa pojemność, lecz lepszy dobór — i właśnie tam AI zarabia na swoje miejsce w tej skali.
Semantycznie, nie tekstowo
Dzięki indeksowi wektorowemu zapytanie „znajdź wszystkie przepływy uwierzytelniania” wydobywa kod OAuth, JWT i sesji, nawet gdy żaden z nich nie dzieli słowa kluczowego.
Śledzenie zależności
AI podąża za importami i miejscami wywołań przez granice modułów znacznie szybciej, niż zdołasz klikać przez „znajdź użycia”.
Testy charakteryzacyjne
Dla nieudokumentowanego kodu legacy AI tworzy testy, które przypinają obecne zachowanie, żebyś mógł refaktoryzować bez strachu.
To ty pozostajesz architektem
AI wykonuje mechaniczne skanowanie i boilerplate. To ty podejmujesz decyzje domenowe i architektoniczne, których ono podjąć nie potrafi.
Myśl o kontekście jak o czterech warstwach ładowanych w malejącej trwałości:
Warstwa architektoniczna to ta, którą kodujesz raz i przestajesz za nią płacić. Każde narzędzie ma na nią swój plik, a kształt tego pliku decyduje o tym, czy agent rozumuje o całym systemie, czy zgaduje z trzech otwartych zakładek.
Indeksowanie bazy kodu w Cursorze dostarcza część warstwy architektonicznej automatycznie. Resztę przypnij w Project Rule, żeby obowiązywała bez przepisywania jej za każdym razem:
# .cursor/rules/architecture.mdc (alwaysApply: true)Large-scale payment processing platform, 1.8M LOC.Key modules:- /src/payments/ - Payment processing (Stripe, PayPal, internal ledger)- /src/accounts/ - User account management and KYC- /src/notifications/ - Event-driven notification system- /src/shared/ - Shared types, utilities, and base classes
When modifying any module, always check:1. The module's public API in its index.ts barrel export2. Integration tests in /tests/integration/{module-name}/3. The event contracts in /src/shared/events/Warstwę domenową zawężaj regułą przypiętą globem, leżącą obok kodu, którym zarządza:
# .cursor/rules/payment.mdc (glob: services/payment/**)When working with payment code:- All monetary amounts are integer cents — never floats- Mutations require an idempotency key- Never log full card numbers (PCI)- Add audit logging for every state transitionKonkretny kontekst wciągaj oznaczeniami @file i @folder. Przy zmianach przekrojowych odwołaj się najpierw do współdzielonego kontraktu: @src/shared/types/payment.ts przed dotknięciem jakiegokolwiek modułu płatności. Dla prostych projektów AGENTS.md w katalogu głównym sprawdza się jako prostsza alternatywa dla strukturalnych reguł.
Claude Code czyta hierarchię plików CLAUDE.md, gdzie plik z każdego katalogu nakłada się na swoich rodziców — co odwzorowuje piramidę wprost:
# /CLAUDE.md (root - architecture layer)Monorepo with 1.8M LOC. Key architectural decisions:- Event-driven architecture using RabbitMQ- Each service owns its database schema- Shared types live in /packages/shared-types/- All inter-service communication goes through /packages/event-bus/
# /packages/payments/CLAUDE.md (domain layer)Payment service handles Stripe and PayPal integrations.Never modify PaymentProcessor directly - extend via strategy pattern.All new payment methods must implement IPaymentStrategy interface.Następnie przełączaj się czysto między niepowiązanymi obszarami za pomocą /clear i /add-dir, tak by warstwa domenowa się wymieniała, a architektoniczna została:
/clear/add-dir services/paymentAnalyze the payment-processing flow.
/clear/add-dir services/usersReview the authentication implementation.Codex czyta AGENTS.md z katalogu głównego repozytorium i z każdego podkatalogu, więc warstwa architektoniczna trafia do korzenia, a domenowa obok kodu:
# AGENTS.md (root)Large monorepo navigation rules:- Always run `find . -name "*.ts" -path "*/payments/*" | head -20` to orient before modifying payment code- Check /docs/architecture/ for system design documents before cross-service changes- Use git log --oneline -20 on target files to understand recent change patterns
When making changes that span multiple packages:1. List all affected packages first2. Check each package's README for modification guidelines3. Run the package's test suite after each change# services/payment/AGENTS.md (domain layer)This service handles all payment processing.- Amounts are integer cents to avoid floating-point error- Idempotency keys required on all transactions- PCI: never log full card numbersOsobne git worktree umożliwiają równoległą eksplorację Codeksem bez konfliktów; zarządzane worktree są opcjonalnym wyborem w ChatGPT desktop, a nie właściwością każdego zadania.
Wyszukiwanie tekstowe zawodzi w skali, bo powiązany kod rzadko dzieli słownictwo. Indeks semantyczny zbudowany na embeddingach wektorowych to naprawia: odpowiada na pytanie „gdzie obsługiwane jest przetwarzanie płatności?” bez otwierania pliku. Utrzymywany serwer to Zilliz Claude Context (@zilliz/claude-context-mcp — wcześniej publikowany jako code-context). Konfiguracja MCP jest niemal identyczna we wszystkich trzech narzędziach; różni się tylko polecenie rejestracji.
Dodaj do ~/.cursor/mcp.json:
{ "mcpServers": { "claude-context": { "command": "npx", "args": ["-y", "@zilliz/claude-context-mcp@latest"], "env": { "EMBEDDING_PROVIDER": "OpenAI", "OPENAI_API_KEY": "your-api-key", "MILVUS_TOKEN": "your-zilliz-key" } } }}claude mcp add claude-context \ -e OPENAI_API_KEY=your-api-key \ -e MILVUS_TOKEN=your-zilliz-key \ -- npx -y @zilliz/claude-context-mcp@latestcodex mcp add claude-context \ --env OPENAI_API_KEY=your-api-key \ --env MILVUS_TOKEN=your-zilliz-key \ -- npx -y @zilliz/claude-context-mcp@latestAlbo dodaj go bezpośrednio do ~/.codex/config.toml:
[mcp_servers.claude-context]command = "npx"args = ["-y", "@zilliz/claude-context-mcp@latest"]env = { EMBEDDING_PROVIDER = "OpenAI", OPENAI_API_KEY = "your-api-key", MILVUS_TOKEN = "your-zilliz-key" }Po zaindeksowaniu pytasz o koncepcje, a serwer zwraca odpowiednie pliki niezależnie od nazewnictwa. Dla wrażliwych baz kodu, które nie mogą sięgnąć do chmurowego API embeddingów, LuotoCompany/cursor-local-indexing uruchamia lokalny indeks ChromaDB i udostępnia go przez lokalny endpoint SSE:
Dodaj do ~/.cursor/mcp.json:
{ "mcpServers": { "workspace-code-search": { "url": "http://localhost:8978/sse" } }}claude mcp add --transport sse workspace-code-search http://localhost:8978/ssecodex mcp add workspace-code-search --url http://localhost:8978/sseTo utrzymuje kod źródłowy na twojej własnej infrastrukturze — właściwy wybór dla usług finansowych, opieki zdrowotnej czy pracy w sektorze obronnym, gdzie kod nie może opuścić sieci.
Zewnętrzny indeks wektorowy to jedna połowa; drugą jest to, co samo narzędzie widzi natywnie — a to różni się między tą trójką ostro.
Cursor indeksuje przestrzeń roboczą automatycznie i wylicza embeddingi do wyszukiwania semantycznego. Utrzymuj indeks szczupły dzięki plikom ignorowania:
.cursorignore blokuje pliki przed indeksowaniem oraz przed dostępem agenta (używaj go do sekretów, node_modules/, wyników budowania)..cursorindexingignore wyklucza pliki tylko z indeksu — pozostają dostępne przez jawne @-wskazanie. Używaj go do dużych plików generowanych (lockfile’i, dist/, snapshoty), które zaśmiecają wyniki wyszukiwania.dist/**/*.snappnpm-lock.yamlNastępnie zawężaj kontekst symbolami, a nie całymi plikami: @accountType (symbol) daje ciaśniejszy, mniej zaszumiony kontekst niż @user-service.ts (plik z 2000 linii).
Claude Code nie indeksuje z wyprzedzeniem; eksploruje na żądanie za pomocą Grep/Glob i czyta pliki w miarę potrzeby. Twoim zadaniem jest dać mu trwałą mapę i pilnować okna:
/init, aby wygenerować CLAUDE.md zapisujący układ monorepo, granice pakietów i komendy budowania/testowania. To kontekst, którego nie wywnioskuje z zimnego startu./context, aby zobaczyć, co zużywa okno przed dużym zadaniem — jeśli współdzielony schemat i trzy usługi już je wypełniają, jesteś o krok od przepełnienia..claude/agents/), tak by grep plik po pliku działał w odizolowanym kontekście, a do głównego wątku wracało tylko podsumowanie.Codex czyta AGENTS.md z korzenia repozytorium (oraz osobno dla każdego pakietu) jako swój trwały kontekst projektu — udokumentuj tam układ przestrzeni roboczej i zasadę „zawsze sprawdzaj współdzielony schemat”, żeby przetrwała każdy nowy wątek.
Wewnątrz TUI /init tworzy szkielet AGENTS.md na podstawie twojej bazy kodu. Przy zmianach dotykających wielu pakietów prowadź pracę w worktree (osobnym checkoucie na wątek), żeby duża refaktoryzacja była odizolowana od głównego drzewa roboczego i łatwa do odrzucenia, gdyby promień rażenia okazał się większy, niż się spodziewano.
Od czego zacząć? Z góry na dół. Skłoń AI do zbudowania modelu mentalnego, zanim czegokolwiek dotkniesz, a potem zagłęb się w obszar, którego naprawdę dotyczy twój ticket.
Agent Cursora sam zbiera kontekst z zaindeksowanej bazy kodu — wystarczy opisać, czego chcesz. Użyj @Folders, aby zawęzić pytanie do jednego obszaru, i @Code, aby wskazać konkretny fragment:
@Folders services/authExplain the authentication and authorization architecture: where tokensare issued, how refresh works, and which services validate them.Aby uzyskać precyzyjne odniesienie, zaznacz funkcję w edytorze i dodaj ją przez @Code, zanim poprosisz agenta o prześledzenie miejsc jej wywołań.
Zawęź sesję do katalogu, który cię interesuje, flagą --add-dir przy starcie (lub /add-dir <path> w trakcie sesji), a potem pytaj od ogółu do szczegółu. Użyj wzmianek ścieżkowych z @, aby wciągnąć konkretny plik do kontekstu:
Analyze this codebase and build a mental model of the system architecture.Cover: core business domains, service boundaries, data-flow patterns, andexternal dependencies. Present it as an overview for a new senior engineer.Następnie zagłęb się przez wyszukiwanie semantyczne za pomocą serwera MCP:
Using claude-context, find all payment-processing flows. I need entrypoints, state management during processing, external provider integration,and the retry/error-handling logic. Reference @services/payment as you go.Umieść AGENTS.md w katalogu głównym repozytorium, opisując domeny i konwencje, a potem uruchom /init wewnątrz TUI, aby Codex go zainicjował. Przy dużym refactoringu pracuj w dedykowanym git worktree, żeby eksploracja nigdy nie dotykała twojego głównego checkoutu:
Map this codebase top-down: business domains, service boundaries, dataflow, and external dependencies. Then locate the payment-processing flowand summarize its entry points and retry logic.Rekonesans, który wyrzucasz, to rekonesans, za który zapłacisz ponownie za tydzień. Zapisz go do pliku, który narzędzie odczyta w każdej przyszłej sesji — ten plik jest twoją warstwą architektoniczną, a utrzymywanie go w aktualności kosztuje mniej niż wyprowadzanie go od nowa.
Miejsce docelowe różni się w zależności od narzędzia, a to właśnie ono sprawia, że plik działa automatycznie, a nie tylko wtedy, gdy pamiętasz o @-wskazaniu:
Zapisz go do .cursor/architecture.md i odwołuj się przez @.cursor/architecture.md albo wskaż go zawsze aktywną Project Rule, żeby każdy czat startował z nim w kontekście.
Zapisz go do /docs/architecture-summary.md i podlinkuj z głównego CLAUDE.md. Możesz go wygenerować bezgłowo, poza sesją:
claude "Analyze the entire /src directory structure and generatean architecture summary. For each package in /packages/:- What it does (one line)- Its public exports- Which other packages it depends on- Its test coverage statusSave to /docs/architecture-summary.md"Zapisz go do /docs/architecture-map.md i odwołaj się do niego z głównego AGENTS.md. Zadanie chmurowe z pełnym dostępem do repozytorium to dobre miejsce na pierwszy głęboki przebieg, bo może przeczytać całe drzewo, nie konkurując o kontekst z twoją lokalną sesją.
Największy błąd przy dużych bazach kodu to ładowanie wszystkiego naraz. Twój asystent nie potrzebuje wszystkich 1,8 miliona linii — potrzebuje właściwego wycinka we właściwym momencie. Pomyśl o tym jak o przybliżaniu na mapie: kontynent, kraj, miasto, ulica.
Poziom domeny (widok z 10 000 stóp)
What are the main bounded contexts in this system, and how do the payment,user, and inventory domains interact?Poziom usługi (widok z 1000 stóp)
Within the payment domain, explain the service architecture and the mainAPIs each service exposes.Poziom komponentu (widok ze 100 stóp)
Show me how PaymentProcessor handles credit-card transactions and what itsretry strategy is for failed charges.Poziom implementacji (poziom gruntu)
In PaymentProcessor.processCard(), why is there a 30-second timeout, and isthe synchronized block safe to remove?Ruchem o największej dźwigni w dużym repozytorium jest zmuszenie agenta, by znalazł i zaraportował pliki dotknięte zmianą, zanim cokolwiek wyedytuje. To wyłapuje zależność między pakietami, którą agent w innym wypadku by przeoczył, a kosztuje grosze: przebieg w trybie tylko do odczytu to ułamek ceny spartaczonej refaktoryzacji.
Ten przebieg ma dwa kształty, dla dwóch różnych zmian. Pierwszy jest dla zmiany rozchodzącej się po nazwie przez pakiety — zmiany nazwy, pola, klucza konfiguracyjnego — a jego wynikiem jest uszeregowana tabela, względem której prowadzisz przegląd:
Jeśli raport pomija usługę, o której wiesz, że istnieje, twój indeks jest niekompletny albo jego reguły ignorowania są zbyt agresywne — napraw to, zanim ruszysz dalej. Trzymaj raport otwarty jako artefakt przeglądu i odhaczaj pliki w miarę wprowadzania zmiany.
Drugi jest dla zmiany skupionej na jednej klasie lub module. Prosi o graf, a nie o listę, i to on wyłapuje konsumentów, których wyszukiwanie po nazwie nie zobaczy: nasłuchy zdarzeń i subskrybentów kolejek komunikatów, którzy w ogóle nie importują symbolu.
Nigdy nie dawaj dużemu repozytorium otwartego zadania w stylu „zrefaktoryzuj cały system uwierzytelniania”. Model rozprasza się, gubi wątek w połowie, a ty dostajesz diff na 40 plików, którego nie da się przejrzeć. Zmuś go najpierw do wypisania listy kontrolnej, a potem wykonuj po jednym elemencie na turę.
Wypisz każdy plik, który musi się zmienić
Zdobądź tę listę, zanim powstanie choć jedna linia kodu, i zweryfikuj ją z własnym zrozumieniem systemu.
Modyfikuj najpierw współdzielone interfejsy
Zacznij od definicji typów, interfejsów i kontraktów. Te zmiany propagują błędy kompilacji, które ujawniają ukryte zależności pominięte przez raport.
Aktualizuj implementacje moduł po module
Modyfikuj każdy konsumujący moduł niezależnie i uruchom testy tego modułu przed przejściem do następnego.
Uruchamiaj testy integracyjne po każdym module
Nie czekaj, aż wszystkie moduły zostaną zaktualizowane. Wyłapuj problemy integracyjne, póki diff jest jeszcze na tyle mały, że da się go przeczytać.
Zweryfikuj w poprzek całości
Uruchom pełny zestaw testów, sprawdź typy w całej bazie kodu i przejrzyj kompletnego diffa przed zatwierdzeniem.
Gdy plan zostanie zatwierdzony, realizuj go po jednym elemencie i nazywaj konkretny symbol, żeby agent trzymał się celu, zamiast od nowa wyprowadzać zakres:
Gdy fazy wynikają wprost z kształtu zmiany — najpierw interfejs, potem jego konsumenci — możesz pominąć osobną turę planowania i wbudować je w jeden prompt z jawnymi przystankami. To zwięzła forma tej samej dyscypliny:
Po pełną dyscyplinę planowania stojącą za tą pętlą zajrzyj do PRD → plan → todo.
Refactoring milionowej bazy kodu to jak remont szpitala podczas trwającej operacji — nie możesz wszystkiego wyłączyć. Wzorzec, który działa: odkryj, ustanów szablon, migruj małymi partiami, zweryfikuj.
Weźmy bazę kodu Node.js wciąż naszpikowaną callbackami error-first. Ręczna migracja do async/await zajęłaby miesiące. Zamiast tego skłoń AI do skategoryzowania pracy według ryzyka, a potem wygenerowania jednej wielokrotnego użytku transformacji na kategorię:
// Before — error-first callbackfunction loadUser(id, callback) { db.query('SELECT * FROM users WHERE id = ?', [id], (err, rows) => { if (err) return callback(err); callback(null, rows[0]); });}
// After — async, with a backward-compatible callback shimasync function loadUser(id, callback) { try { const rows = await db.query('SELECT * FROM users WHERE id = ?', [id]); if (callback) return callback(null, rows[0]); return rows[0]; } catch (err) { if (callback) return callback(err); throw err; }}Shim pozwala wywołującym migrować we własnym tempie. Stosuj transformację katalog po katalogu, uruchamiaj istniejące testy po każdej partii i śledź postęp — nigdy nie transformuj całego drzewa w jednym przebiegu.
Przy dużym przedsięwzięciu rozłożonym na zespół skłoń AI do podzielenia pracy tak, by zminimalizować konflikty między zespołami, a potem pilnuj uczciwości gałęzi:
Podziel według granic zależności
Analyze module dependencies and propose how to split this refactor acrossfour developers so their territories barely overlap. Flag any shared filesthat two teams would both need to edit.Gałąź na terytorium
git checkout -b refactor/user-servicesgit checkout -b refactor/payment-servicesgit checkout -b refactor/shared-utilsWykrywaj kolizje wcześnie
Review the diffs across all refactor/* branches and identify conflictingor breaking changes between teams before we attempt to merge.Każda duża baza kodu ma warstwy archeologiczne — kod z różnych epok i filozofii, część z niego sprzed czasów obecnego zespołu. Klasyczny koszmar: 15 000-liniowa procedura składowana, której nikt nie rozumie, a która wciąż codziennie przetwarza prawdziwe pieniądze.
Wzorzec strangler fig pozwala modernizować bez przepisywania: opakuj kod legacy za czystym interfejsem, a potem wyodrębniaj fragmenty po jednym, uruchamiając stary i nowy równolegle, aż zaufasz nowej ścieżce.
Nie każda zmiana w kodzie legacy zasługuje na fasadę. Gdy musisz tylko coś do starego kodu dodać, daj AI Kamień z Rosetty: najnowszy, dobrze napisany moduł stosujący aktualne konwencje jako wzorzec do naśladowania. To utrzymuje diff małym tam, gdzie prompt strangler fig celowo buduje nową powierzchnię.
Gdy dokumentacja nie istnieje, testy stają się dokumentacją. Poproś AI o napisanie testów charakteryzacyjnych, które przypną obecne zachowanie — łącznie z dziwnymi fragmentami — tak by każda przyszła zmiana zmieniająca wyjście zawodziła głośno:
describe('Legacy OrderProcessor — current behavior', () => { it('returns status code 1 on a standard single-item order', async () => { const result = await processOrder({ customerId: 123, items: [{ sku: 'WIDGET-1', quantity: 1 }], }); expect(result.status).toBe(1); // 1 = success (undocumented magic number) expect(result.orderId).toMatch(/^ORD-\d{8}$/); });
it('returns -99 when inventory is unavailable', async () => { const result = await processOrder({ customerId: 123, items: [{ sku: 'OUT-OF-STOCK', quantity: 1 }], }); expect(result.status).toBe(-99); // -99 = inventory error });});W milionowej bazie kodu różne zespoły posiadają różne terytoria. Najtrudniejsze jest wprowadzenie zmiany przekraczającej granicę bez zepsucia czegoś komuś innemu. Powyższe prompty do promienia rażenia kończą się na raporcie; ten idzie dalej i prosi o ścieżkę migracji, bo zmiana łamiąca, której nie da się rozłożyć na etapy, to zmiana, której nie da się wdrożyć.
Połącz to z automatycznie generowanymi kontraktami. Poproś AI o wytworzenie specyfikacji OpenAPI i schematów zdarzeń dla usługi, z której korzysta inny zespół — to zamienia „idź przeczytaj nasz kod” w stabilną granicę, względem której mogą się integrować bez grzebania w twoich wnętrznościach.
W długiej sesji sama historia rozmowy staje się nieaktualnym kontekstem — agent wciąż „pamięta” błąd, który naprawiłeś godzinę temu. Resetuj świadomie, gdy zmieniasz zadanie. Mechanika różni się w zależności od narzędzia:
Rozpoczynaj nowy czat dla każdego odrębnego zadania (nowa funkcja, nowy błąd). Cursor trzyma kontekst każdego czatu osobno, więc świeży czat oznacza, że agent rozumuje wyłącznie o zadaniu, które ma przed sobą. Używaj punktów kontrolnych, by cofnąć czat, jeśli eksploracyjna edycja pójdzie nie tak.
Uruchom /clear, gdy przechodzisz między niepowiązanymi zadaniami, aby całkowicie wyczyścić okno kontekstu. Gdy chcesz zachować wątek, ale przyciąć szum, użyj /compact <instructions> — np. /compact Focus on the JWT migration, drop the earlier CSS work. Jeśli poprawiałeś model dwa razy w tej samej sprawie, zrób /clear i zacznij od ostrzejszego promptu; czysta sesja niemal zawsze bije długą, zagraconą.
Użyj /new wewnątrz TUI, aby rozpocząć świeży wątek (Codex używa /new, nie /clear). Nowy wątek resetuje kontekst rozmowy, ale pozostaje w bieżącym checkoucie; utwórz osobny git worktree, gdy nowe zadanie wymaga także izolacji systemu plików.
Przepływy pracy AI w dużych bazach kodu zawodzą na konkretne, rozpoznawalne sposoby. Poznaj sposób naprawy każdego z nich.
CLAUDE.md/AGENTS.md dźwigały ciężar