Przejdź do głównej zawartości

Zmiany w wielu plikach

Praca na wielu plikach w Claude Code rozdziela trzy zadania: tryb planowania wyznacza każdy plik, którego dotknie zmiana, zanim padnie pierwsza edycja; lista kontroli akceptacyjnych definiuje „gotowe” przed rozpoczęciem edycji; a same zmiany idą warstwa po warstwie, rozdzielane przez /batch lub --worktree tylko wtedy, gdy części są niezależne. Wynik potwierdzają kontrole, a nie czytanie linijka po linijce.

Masz zmienić nazwę pola customerId na clientId. Pole przechodzi przez model, serializer, trzy kontrolery, migrację, publiczny kontrakt API i 60 testów. Jeden prompt „zmień to wszędzie” zwraca diff na 40 plików, który wygląda wiarygodnie, a nikt w zespole nie potrafi powiedzieć, czy jutro nie padnie klient mobilny, który wciąż wysyła customerId. Ta strona jest dla programisty, który prowadzi taką zmianę, i dla tech leada, który musi ją zatwierdzić bez czytania wszystkich 40 plików.

Co daje ci uporządkowany proces zmian w wielu plikach

Dział zatytułowany „Co daje ci uporządkowany proces zmian w wielu plikach”
  • Prompt „najpierw plan”, po którym Claude Code wypisuje zasięg zmiany pogrupowany według warstw, zanim cokolwiek zmieni.
  • Listę kontroli akceptacyjnych pisaną przed edycją, dzięki której poprawności wyniku dowodzą polecenia, a nie lektura kodu.
  • Wzorzec zmiany nazwy zgodny wstecz (rozszerz, przenieś, zawęź), który przetrwa klientów, nad którymi nie masz kontroli.
  • Regułę wyboru między jedną sesją, /batch, równoległymi worktree, subagentami i --add-dir.
  • Tabelę odzyskiwania po typowych awariach edycji wieloplikowych, z poleceniem, które wyciąga z każdej z nich.

Wybieraj mechanizm według tego, jak niezależne są części zmiany, a nie według liczby plików. Zmiana nazwy w 40 plikach to jedna ściśle powiązana zmiana; trzy niezwiązane funkcje to trzy zmiany.

Twoja zmianaUżyjDlaczego
Jedna powiązana zmiana przez warstwy (zmiana nazwy, nowe pole, nowa kontrola ról)Jednej sesji: tryb planowania, potem edycja warstwa po warstwieKażda edycja zależy od poprzedniej; podział rodzi konflikty scalania i niespójne nazwy.
Ta sama mechaniczna edycja w wielu niezależnych miejscach (migracja 25 wywołań na nowe API)/batchWbudowany skill dzieli zmianę na 5–30 jednostek i uruchamia każdą w subagencie w tle, we własnym worktree.
Kilka niezwiązanych strumieni pracy (auth, płatności, analityka)Jednej sesji claude --worktree <nazwa> na strumieńKażda sesja dostaje własny checkout i gałąź, więc edycje się nie zderzają.
Rozpoznanie przed edycją (kto to wywołuje, co się zepsuje)Subagenta, np. wbudowanego agenta ExploreWyniki wyszukiwania nie zapychają głównego okna kontekstu.
Jedna zmiana w sąsiednich katalogach (wspólna biblioteka i aplikacja, która ją importuje)--add-dir w jednej sesjiClaude Code czyta i edytuje oba katalogi według jednego planu.

Zespoły agentów (agent teams), czyli sesja prowadząca koordynująca sesje równorzędne ze wspólnymi zadaniami, są eksperymentalne i domyślnie wyłączone (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1). Sięgaj po nie dopiero wtedy, gdy powyższe opcje przestaną wystarczać.

Pominięty plik w planie poprawiasz jedną linijką, a w diffie na 40 plików szukasz go przez całe popołudnie, dlatego wymuś plan przed pierwszym zapisem. Od Claude Code v2.1.283 na kanale latest sesje interaktywne startują w trybie auto, który edytuje pliki bez pytania (claude -p, Agent SDK, sesje, w których plik ustawień ustawia disableAutoMode, oraz modele nieobsługiwane przez tryb auto nadal startują w trybie Manual). Przełączenie w tryb planowania to więc świadomy krok, a nie ustawienie domyślne.

  1. Uruchom sesję w trybie planowania albo przełącz otwartą sesję przez Shift+Tab lub /plan:

    Okno terminala
    claude --permission-mode plan
  2. Poproś o plan, nie o zmianę. Tryb planowania jest tylko do odczytu, więc Claude Code przegląda kod i proponuje, nie zapisując żadnego pliku.

  3. Porównaj listę plików z tym, co wiesz o systemie. Brakującą warstwę (klient mobilny, eksport CSV, zadanie w tle) poprawisz tu za darmo; w diffie znajdziesz ją drogo.

  4. Zapisz zatwierdzony plan w pliku w repozytorium, np. plans/client-id-rename.md. Lista plików staje się kontraktem dla edycji i dla przeglądu.

Śledzenie po importach i typach, a nie tylko po tekście, to powód, by pytać agenta zamiast uruchamiać grep: wyszukiwanie tekstowe przeoczy destrukturyzację const { customerId: id } i generyczne wywołanie pick(user, fields). Punkt 5 promptu jest równie ważny: mówi ci, czego plan nie obejmuje.

Diffu na wiele plików nie da się zweryfikować czytaniem. Zdecyduj, co dowodzi poprawności, zanim Claude Code napisze pierwszą linijkę, i niech każda kontrola będzie poleceniem, które przy błędzie kończy się niezerowym kodem wyjścia. Typowe kontrole dla zmiany nazwy:

  • Przechodzą sprawdzanie typów i linter (npm run typecheck, npm run lint lub odpowiedniki w twoim stosie).
  • Przechodzi istniejący zestaw testów, bez zmian w asercjach niezwiązanych ze zmianą nazwy.
  • Wyszukiwanie starej nazwy zwraca tylko linie dozwolone w planie (alias wejściowy i log o wycofaniu).
  • Nowy test dowodzi, że żądanie wciąż wysyłające customerId dostaje tę samą odpowiedź co dziś.

Ostatnia kontrola łapie awarię produkcyjną i musi istnieć przed zmianą, żeby w ogóle mogła wykryć regresję. Zobacz, jak pisać kryteria akceptacji, które agent potrafi sprawdzić, oraz jak nie dopuścić, by agent edytował testy, które go oceniają.

Zmień nazwę publicznego pola, nie psując wywołujących

Dział zatytułowany „Zmień nazwę publicznego pola, nie psując wywołujących”

Zmiana nazwy jest wewnętrzna, dopóki nie zależy od niej żaden zewnętrzny klient. Dla wszystkiego, co plan oznaczył jako PUBLIC, stosuj schemat rozszerz, przenieś, zawęź zamiast prostej podmiany:

  1. Rozszerz. Zwracaj nową nazwę wszędzie i przyjmuj obie nazwy na wejściu, mapując customerId na clientId.

  2. Przenieś. Przestaw wszystkie wewnętrzne wywołania na nową nazwę, podczas gdy API wciąż przyjmuje starą.

  3. Ostrzegaj. Loguj ostrzeżenie o wycofaniu przy każdym żądaniu, które wciąż używa starej nazwy, żeby widzieć, kto jeszcze ją wysyła.

  4. Zawęź. Usuń starą nazwę osobną zmianą, gdy logi przez uzgodniony okres nie pokazują ruchu.

Reguła „not in plan” to najtańszy detektor regresji, jaki masz. Nieplanowana edycja oznacza, że albo plan był niepełny, albo agent zbacza z kursu, i chcesz wiedzieć, które z nich, zanim następna warstwa na tym zbuduje. Głębsze przepisy na zmianę nazw, wydzielanie i restrukturyzację znajdziesz we wzorcach refaktoryzacji, a zmiany schematu wymagające migracji danych opisuje strona o migracjach bazy danych.

Edytuj warstwa po warstwie i commituj między warstwami

Dział zatytułowany „Edytuj warstwa po warstwie i commituj między warstwami”

Duże zmiany pozostają dokładne, gdy każdy prompt obejmuje jedną warstwę: migrację i modele, potem serwisy, potem trasy, potem UI, na końcu testy. Każda warstwa to diff na tyle mały, by go sprawdzić, a każdy commit to znany dobry punkt powrotu.

Kontekstem między warstwami zarządzasz poleceniami interaktywnymi:

  • /context pokazuje, co wypełnia okno kontekstu.
  • /compact Keep the plan file path, the acceptance checks and the list of files already changed streszcza rozmowę, zachowując to, czego potrzebuje następna warstwa.
  • /clear zaczyna od zera; zakotwicz sesję ponownie, wskazując Claude Code plik planu.
  • /usage pokazuje koszt sesji i limity planu.
  • /rewind albo dwukrotne Esc przy pustym prompcie przywraca kod i rozmowę do wcześniejszego punktu kontrolnego.

Commituj po każdej warstwie, która przeszła kontrole. „Cała zmiana jest zepsuta” zamienia się wtedy w „ostatni commit jest zepsuty”, a git revert jednego commita zastępuje popołudnie rozplątywania.

Gdy plan pokazuje tę samą edycję w wielu miejscach, które od siebie nie zależą, np. przeniesienie 25 handlerów tras ze starego loggera na nowy, jedna sesja marnuje czas, robiąc to po kolei. Wbudowany skill /batch dzieli zmianę na 5–30 jednostek i uruchamia każdą w subagencie w tle, we własnym worktree.

/batch pasuje do edycji, których poprawność sprawdzisz dla każdej jednostki osobno. To złe narzędzie do powyższej zmiany clientId, w której każda jednostka zależy od wcześniejszej zmiany modelu. Wiele niezwiązanych zadań w repozytorium, a nie jedną zmianę, opisuje strona o operacjach wsadowych.

Uruchom niezależne strumienie w równoległych worktree

Dział zatytułowany „Uruchom niezależne strumienie w równoległych worktree”

Niezwiązanym strumieniom pracy daj osobne sesje i checkouty. --worktree (-w) tworzy dla sesji git worktree i gałąź, a --tmux otwiera ją we własnej sesji tmux (gdy to możliwe, używa natywnych paneli iTerm2; --tmux=classic wymusza zwykły tmux):

Okno terminala
# W terminalu w katalogu głównym repozytorium: jedna sesja, worktree i gałąź na strumień
claude --worktree auth --tmux
claude --worktree payments --tmux
claude --worktree analytics --tmux

W Claude Code 2.1.283 worktree trafiają do .claude/worktrees/, więc dopisz tę ścieżkę do .gitignore, jeśli repozytorium jeszcze jej nie ignoruje. Worktree izolują pliki i gałęzie, ale nie całą resztę: każdy checkout wciąż sięga tej samej deweloperskiej bazy danych, tych samych portów, konta chmurowego i sekretów. Daj każdemu strumieniowi własny blok portów i lokalne dane, a zdalne migracje i wdrożenia trzymaj poza sesjami agentów. Przewodnik po równoległych worktree opisuje przydział portów i kolejność integracji, a tmux dla floty agentów — obserwowanie wielu sesji naraz.

Gdy jedna zmiana obejmuje wspólną bibliotekę i aplikacje, które ją importują, prowadź ją w jednej sesji, żeby jeden plan obejmował obie strony. Uruchom sesję z dodatkowymi katalogami, a potem wpisz prompt:

Okno terminala
# W terminalu w katalogu apps/web
claude --add-dir ../../packages/shared-logger

Przy osobnych repozytoriach idź od producenta do kolejnych konsumentów, jedna sesja na repozytorium. Utrzymuj etap „rozszerz” u producenta, dopóki każdy konsument nie wyda swojej zmiany, i zapisz migrację w CLAUDE.md każdego repozytorium, żeby następna sesja nie przywróciła starej nazwy:

<!-- CLAUDE.md in each consuming repository -->
## Active migrations
- /users returns `clientId`; `customerId` is still accepted on input until v3. Do not add new reads of `customerId`.

Gdzie umieścić taką notatkę i jak Claude Code ją wczytuje, opisuje strona o systemie pamięci.

Jak zweryfikować diff na 40 plików bez czytania każdej linijki?

Dział zatytułowany „Jak zweryfikować diff na 40 plików bez czytania każdej linijki?”

Weryfikujesz zmianę względem planu i kontroli, a czytasz tylko tam, gdzie one wskazują. Po ostatniej warstwie wykonaj tę sekwencję:

  1. Zakres. Porównaj git diff --stat main...HEAD z listą plików w planie. Każdy plik spoza planu wymaga uzasadnienia.

  2. Bramki. Uruchom kontrole akceptacyjne z planu samodzielnie, w czystej powłoce, a nie tylko wewnątrz sesji. Hook, który uruchamia sprawdzanie typów po każdej edycji, łapie błędy na poziomie warstwy zamiast na końcu.

  3. Zachowanie. Test zgodności napisany przed zmianą musi nadal przechodzić, bez modyfikacji. Sprawdź to przez git log -p na pliku tego testu.

  4. Niezależny przegląd. Uruchom /code-review na gałęzi, najlepiej w świeżej sesji (/clear albo nowe claude), żeby przegląd nie opierał się na rozumowaniu, które wyprodukowało zmianę. Jak uruchamiać go przy każdym pull requeście, opisuje strona o automatyzacji code review.

  5. Akceptacja. Recenzent zatwierdza plan, wyniki kontroli i uwagi z przeglądu, a czyta tylko pliki, na które wskazuje kontrola lub uwaga. Na tym polega zmiana opisana w artykule dowody zamiast diffów.

Każda linia „NOT COVERED” to albo test do dopisania, albo ryzyko, które recenzent akceptuje na piśmie.

Co psuje się przy edycji wielu plików i jak z tego wyjść?

Dział zatytułowany „Co psuje się przy edycji wielu plików i jak z tego wyjść?”
ObjawPrawdopodobna przyczynaWyjście
Claude Code edytuje plik spoza planuŚledzenie zależności pominęło wywołanie albo agent zbacza z kursuZatrzymaj sesję klawiszem Esc. Poproś o wyjaśnienie edycji, potem zaktualizuj plik planu albo cofnij się przez /rewind.
Błędy typów lub brakujące importy po warstwieWarstwa zmieniła sygnaturę, na której opiera się następnaWklej wynik sprawdzania: „Fix these errors from the rename without changing behaviour or test assertions.”
Stara nazwa zostaje w komentarzach, fixture’ach lub dokumentacjiZmiana rozciągnęła się na kilka promptów, a wyszukiwanie było tylko tekstoweUruchom kontrolę z ripgrepem i poproś o poprawienie lub uzasadnienie każdego pozostałego trafienia.
Odpowiedzi stają się ogólnikowe, wcześniejsze decyzje ginąOkno kontekstu zapełniło się w długiej sesji/compact z jawnymi instrukcjami, co zachować, albo /clear i ponowne zakotwiczenie na pliku planu.
Testy przechodzą, bo zmieniono asercjeAgent „naprawił” test zamiast koduPrzywróć plik testu (git restore --source=main -- path/to/test) i napisz w prompcie, że asercje są tylko do odczytu.
Jeden strumień się psuje, reszta działaNiezwiązana praca zmieszana w jednej sesjiCofnij tylko tę część promptem do chirurgicznego odzyskiwania poniżej.

Do cofania używaj najmniejszego narzędzia, które wystarczy:

Okno terminala
/rewind # w Claude Code: powrót do punktu kontrolnego, kod i rozmowa
git restore path/to/file.ts # w terminalu: odrzuć niezacommitowane zmiany w jednym pliku
git revert <commit> # cofnij jedną zacommitowaną warstwę, zachowując historię
git stash push -m "failed-rename-attempt" # odłóż wszystko i zacznij od czystego drzewa