Refaktoryzacja to sztuka poprawiania struktury kodu bez zmiany jego zachowania. To miejsce, gdzie dług techniczny spotyka się ze swoim rozrachunkiem, gdzie legacy systemy otrzymują drugie życie, a gdzie Claude Code naprawdę błyszczy. Ta lekcja bada, jak wykorzystać pomoc sztucznej inteligencji do refaktoryzacji na dużą skalę, która ręcznie zajęłaby miesiące.
Scenariusz: Odziedziczyłeś 5-letnią platformę e-commerce. 200 000 linii kodu. jQuery spaghetti we frontend. PHP 5.6 w backend. Brak testów. Niespójne wzorce. Biznes chce dodać funkcje w czasie rzeczywistym, ale dotknięcie czegokolwiek grozi zepsuciem wszystkiego. Brzmi znajomo?
Miesiąc 1: Analiza i planowanie
- Dokumentowanie wszystkich zależności
- Tworzenie roadmapy refaktoryzacji
Miesiąc 2-3: Pisanie testów dla krytycznych ścieżek
- Ręczne pisanie przypadków testowych
- Nadzieja, że złapiesz edge cases
Miesiąc 4-6: Stopniowa refaktoryzacja
- Aktualizacja jednego modułu na raz
- Naprawianie błędów w miarę pojawiania się
- Modlitwa, że nic nie umknie
Miesiąc 7+: Ciągłe poprawki i regresje
Tydzień 1: Analiza z pomocą AI
> Przeanalizuj naszą bazę kodu i zidentyfikuj:
> - Niespójne nazewnictwo
Tydzień 2-3: Automatyczne generowanie testów
> Wygeneruj kompleksowe testy dla wszystkich krytycznych ścieżek
> Uwzględnij edge cases, które mógłbym przegapić
Tydzień 4-6: Systematyczna refaktoryzacja
> Refaktoryzuj moduł po module z:
> - Automatycznymi aktualizacjami wzorców
> - Zarządzaniem zależności
> - Walidacją w czasie rzeczywistym
Tydzień 7: Polerowanie i optymalizacja
Utwórz sieć bezpieczeństwa
> Zanim zaczniemy refaktoryzację:
> 1. Utwórz kompleksowy zestaw testów dla obecnego zachowania
> 2. Skonfiguruj git hooks dla testowania pre-commit
> 3. Skonfiguruj CI/CD do łapania regresji
> 4. Udokumentuj obecne zachowanie
Przeanalizuj obecny stan
> Przeanalizuj naszą bazę kodu i utwórz plan refaktoryzacji:
> - Zidentyfikuj code smells
> - Znajdź zduplikowany kod
> - Wylistuj przestarzałe wzorce
> - Oszacuj złożoność dla każdego modułu
Skonfiguruj infrastrukturę refaktoryzacji
Utwórz .claude/commands/refactor-safely.md
:
Refaktoryzuj $ARGUMENTS zgodnie z tymi zasadami:
1. Utrzymaj dokładną funkcjonalność
2. Pisz testy przed zmianą kodu
3. Aktualizuj jeden wzorzec naraz
4. Uruchamiaj testy po każdej zmianie
6. Aktualizuj dokumentację
Skonfiguruj podejście przyrostowe
"preserveComments" : true ,
Od chaosu jQuery do nowoczesnego React/Vue/Angular
> Przeanalizuj naszą bazę kodu jQuery i utwórz plan modernizacji.
> Chcemy migrować do React stopniowo zachowując funkcjonalność aplikacji.
Strategia przyrostowa Claude:
Faza 1: Przygotowanie
// 1. Utwórz warstwę abstrakcji
// Owinięcie jQuery dla stopniowego zastąpienia
$ ( selector ) . on ( ' click ' , handler );
this . trackUsage ( ' click ' , selector );
// 2. Zastąp bezpośrednie wywołania jQuery
// Przed: $('#button').click(handler)
// Po: LegacyBridge.$('#button').onClick(handler)
Faza 2: Wyodrębnianie komponentów
// Identyfikuj wzorce UI w jQuery
> Znajdź wszystkie wzorce jQuery UI i wyodrębnij je jako komponenty
// Claude identyfikuje wzorce jak:
$ ( ' .user-card ' ) . each ( function () {
$ ( this ) . find ( ' .name ' ) . text ( userData . name );
$ ( this ) . find ( ' .email ' ) . text ( userData . email );
// Konwertuje na komponent React:
const UserCard = ( { user } ) => (
< div className = " user-card " >
< div className = " name " > { user . name } </ div >
< div className = " email " > { user . email } </ div >
Faza 3: Zarządzanie stanem
// Z rozproszonego stanu jQuery
var userLoggedIn = false ;
$ ( ' #login ' ) . click ( function () {
// Do scentralizowanego stanu
const useAuthStore = create ( ( set ) => ( {
login : () => set ( { isLoggedIn: true } ) ,
logout : () => set ( { isLoggedIn: false } )
Faza 4: Kompletna migracja
> Zaktualizuj system budowania
> Optymalizuj rozmiar bundle
// Wynik: Nowoczesna aplikacja React
// 3x szybsze renderowanie
// Bezpieczne typy z TypeScript
Od PHP 5.6 spaghetti do nowoczesnego PHP 8+ lub Node.js
> Nasz backend to PHP 5.6 z:
> - Brakiem organizacji namespace
> - Mieszanymi plikami HTML/PHP
> - Bezpośrednimi zapytaniami MySQL (bez ORM)
> - Zmiennymi globalnymi wszędzie
> Utwórz plan modernizacji do PHP 8.3 z Laravel
Systematyczne podejście Claude:
function calculateTotal ( $items , $tax ) {
foreach ( $items as $item ) {
$total += $item [ ' price ' ] * $item [ ' quantity ' ];
return $total + ( $total * $tax );
$subtotal = array_reduce (
fn ( $ carry , $ item ) => $ carry + ($ item -> price * $ item -> quantity ),
return $subtotal * ( 1 + $tax );
// Przed: Proceduralny bałagan
$result = mysql_query ( " SELECT * FROM users WHERE id = $ user_id " );
$user = mysql_fetch_assoc ($ result );
// Po: Wzorzec Repository
namespace App\Repositories;
public function __construct (
public function find ( int $id ) : ? User
$stmt = $this-> db -> prepare (
' SELECT * FROM users WHERE id = :id '
$stmt -> execute ([ ' id ' => $id ]);
$data = $stmt -> fetch ( PDO :: FETCH_ASSOC );
return $data ? User :: fromArray ( $data ) : null ;
// Migruj do struktury Laravel
> Konwertuj nasze proceduralne PHP na Laravel :
> 1. Utwórz modele dla każdej tabeli
> 2. Konwertuj zapytania na Eloquent
> 3. Wyodrębnij logikę biznesową do serwisów
> 4. Utwórz właściwy routing
> 5. Dodaj middleware dla auth
// Wynik: Czysta architektura MVC
Route :: middleware ([ ' auth ' ]) -> group ( function () {
Route :: get ( ' /users/{user} ' , [ UserController :: class , ' show ' ]);
Route :: post ( ' /users ' , [ UserController :: class , ' store ' ]);
class UserController extends Controller
public function __construct (
private UserService $userService
public function show ( User $user ) : JsonResponse
Refaktoryzacja struktury bazy danych bez przestoju
> Musimy zrefaktoryzować naszą bazę danych:
> - tabela users ma 80 kolumn (zdenormalizowana)
> - Brak kluczy obcych lub ograniczeń
> - Niespójne nazewnictwo (userId vs user_id)
> - Brak indeksów poza kluczami głównymi
> Zaplanuj stopniową migrację do znormalizowanego schematu
Utwórz strategię migracji
-- Faza 1: Dodaj nowe znormalizowane tabele obok starych
CREATE TABLE user_profiles (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
CREATE TABLE user_addresses (
type ENUM( ' billing ' , ' shipping ' ),
FOREIGN KEY (user_id) REFERENCES users(id),
INDEX idx_user_type (user_id, type )
Zaimplementuj wzorzec dual-write
public function updateProfile ( $userId , $data ) {
// Pisz do obu starych i nowych struktur
DB :: transaction ( function () use ( $userId , $data ) {
DB :: table ( ' users ' ) -> where ( ' id ' , $userId ) -> update ([
' fname ' => $data [ ' first_name ' ],
' lname ' => $data [ ' last_name ' ]
UserProfile :: updateOrCreate (
' first_name ' => $data [ ' first_name ' ],
' last_name ' => $data [ ' last_name ' ]
Migruj dane stopniowo
> Utwórz job w tle do migracji danych użytkowników:
> - Przetwarzaj w batches po 1000
> - Weryfikuj integralność danych
> - Obsługuj niepowodzenia z gracją
Przełącz odczyty na nowy schemat
// Kontrola przez feature flag
if ( Feature :: enabled ( ' use_normalized_schema ' )) {
return User :: with ([ ' profile ' , ' addresses ' ]) -> find ( $id );
return DB :: table ( ' users ' ) -> where ( ' id ' , $id ) -> first ();
Dokończ migrację
-- Po weryfikacji, usuń stare kolumny
Wyzwanie: Wyodrębnij serwisy z monolitycznej aplikacji Rails
> Nasz monolit Rails obsługuje:
> - Uwierzytelnianie użytkowników
> - Przetwarzanie zamówień
> Zaplanuj wyodrębnienie przetwarzania zamówień do mikroserwisu
Strategia wyodrębniania Claude:
# Claude analizuje i znajduje:
> Analizuję granice przetwarzania zamówień...
Znaleziono kod związany z zamówieniami w :
- app / models / order. rb (i 12 powiązanych modeli)
- app / controllers / orders_controller. rb (i 4 powiązane)
- app / services / order_service. rb (i 8 powiązanych)
- app / jobs / order_notification_job. rb
- Model User (dla informacji o kliencie)
- Model Product (dla katalogu)
# Utwórz warstwę abstrakcji
module OrderServiceInterface
def initialize ( internal: true )
@client = internal ? InternalOrderService : HTTPOrderService
@client . create_order (params)
# To pozwala na stopniową migrację
# Nowa struktura mikroserwisu
> Utwórz mikroserwis zamówień z :
> - Oddzielną bazą danych
> - REST API pasującym do naszego interfejsu
> - Integracją kolejki wiadomości
> - Własnym pipeline deploymentu
# Wygenerowany serwis zawiera:
class OrdersController < ApplicationController
order = OrderService . create (order_params)
# Opublikuj event dla innych serwisów
EventBus . publish ( ' order.created ' , order)
render json : OrderSerializer . new (order)
# Routing kontrolowany przez feature flag
class OrdersController < ApplicationController
if Feature . enabled? ( : use_order_microservice , current_user)
response = OrderServiceClient . create_order (params)
@order = Order . create! (order_params)
Wyzwanie: Konwertuj operacje synchroniczne na architekturę event-driven
> Nasz system robi wszystko synchronicznie:
> - Wysyłaj e-mail po zamówieniu
> - Aktualizuj inwentarz natychmiast
> - Obliczaj analityki w żądaniu
> - Generuj PDF podczas wywołania API
> Refaktoryzuj na wzorzec asynchroniczny event-driven
Claude implementuje refaktoryzację event-driven:
// Przed: Sprzężenie synchroniczne
async function createOrder ( orderData : OrderData ) {
const order = await db . orders . create (orderData);
// Wszystko to blokuje odpowiedź
await sendOrderConfirmationEmail (order);
await updateInventory (order . items );
await calculateAnalytics (order);
await generateInvoicePDF (order);
async function createOrder ( orderData : OrderData ) {
const order = await db . orders . create (orderData);
// Opublikuj event i zwróć natychmiast
await eventBus . publish ( ' order.created ' , {
customerId: order . customerId ,
// Oddzielne handlery przetwarzają asynchronicznie
eventBus . subscribe ( ' order.created ' , async ( event ) => {
emailService . sendOrderConfirmation (event),
inventoryService . updateStock (event . items ),
analyticsService . recordOrder (event),
pdfService . generateInvoice (event . orderId )
> Przeskanuj naszą bazę kodu w poszukiwaniu anti-wzorców i utwórz poprawki:
> - God objects (klasy robiące za dużo)
> - Shotgun surgery (zmiany wymagają wielu edycji)
> - Feature envy (metody używające danych innych klas)
> - Data clumps (te same parametry wszędzie)
Wykrywanie wzorców i poprawki Claude:
Poprawka God Object
// Wykryto: UserService z 47 metodami
// Metody uwierzytelniania
// Zrefaktoryzowano na skupione serwisy
class NotificationService {
Poprawka Data Clump
// Wykryto: Te same 4 parametry w 15 metodach
// Zrefaktoryzowano z obiekt value
Do stopniowego zastępowania systemów legacy:
> Zaimplementuj wzorzec strangler fig dla naszego legacy systemu zamówień:
> - Obecny: Monolityczne przetwarzanie zamówień
> - Cel: Nowoczesna architektura mikroserwisów
> - Wymaganie: Zero przestoju, stopniowa migracja
Utwórz fasadę
async createOrder ( data : OrderData ) {
if ( await this . shouldUseNewSystem (data)) {
return this . newOrderService . create (data);
return this . legacyOrderService . create (data);
private async shouldUseNewSystem ( data : OrderData ) {
// Logika stopniowego rollout
if ( this . featureFlags . isEnabled ( ' new_order_system ' , data . customerId )) {
if (data . total < 100 && Math . random () < 0.1 ) { // 10% małych zamówień
Zaimplementuj tryb porównania
// Uruchom oba systemy i porównaj
async createAndCompare ( data : OrderData ) {
const [ legacy , modern ] = await Promise . all ([
this . legacySystem . create (data),
this . modernSystem . create (data)
const differences = this . compareResults (legacy , modern);
if (differences . length > 0 ) {
await this . logDifferences (differences);
// Zwróć wynik legacy ale zaloguj modern
Stopniowe przełączanie
Tydzień 1: 1% ruchu do nowego systemu
Tydzień 6: Usuń legacy kod
> Utwórz dashboard refaktoryzacji, który śledzi:
> - Poprawę pokrycia kodu
> - Redukcję złożoności cyklomatycznej
> - Wynik długu technicznego
Claude generuje kod śledzący:
class RefactoringMetrics {
coverage: await this . getTestCoverage () ,
complexity: await this . getCyclomaticComplexity () ,
performance: await this . getPerformanceMetrics () ,
codeQuality: await this . getCodeQualityScore () ,
technicalDebt: await this . calculateTechnicalDebt ()
improvements: this . compareWithBaseline (metrics),
roi: this . calculateROI (metrics)
async getTestCoverage () {
const coverage = await exec ( ' npm run coverage -- --json ' );
statements: coverage . total . statements . pct ,
branches: coverage . total . branches . pct ,
functions: coverage . total . functions . pct ,
lines: coverage . total . lines . pct
> Wygeneruj kompleksowe testy aby upewnić się, że refaktoryzacja nie zmienia zachowania:
> - Przechwyć obecne zachowanie
> - Utwórz golden master tests
> - Property-based testing
> - Zestaw testów regresji
> Dla każdej refaktoryzacji:
> 1. Utwórz branch funkcji
> 2. Rób przyrostowe commity
> 3. Uruchamiaj testy ciągle
> 4. Używaj pull requestów do przeglądu
> Podczas refaktoryzacji, również:
> - Aktualizuj komentarze w kodzie
> - Dokumentuj nowe wzorce
> - Utwórz przewodniki migracji
> - Zapisuj uzasadnienie decyzji
> - Dodaj nowe endpointy obok starych
> - Przestarzałe stopniowo
> - Dostarczaj narzędzia migracji
> - Wspieraj obie wersje tymczasowo
if (Feature . enabled ( ' new_payment_system ' )) {
return this . newPaymentProcessor . charge (amount);
return this . legacyPaymentGateway . process (amount);
Nauczyłeś się, jak wykorzystać Claude Code do refaktoryzacji na dużą skalę, która byłaby niemożliwie czasochłonna ręcznie. Kluczem jest myślenie systemowe: analizuj kompleksowo, refaktoryzuj przyrostowo, waliduj ciągle.
Pamiętaj: Refaktoryzacja to nie dążenie do perfekcji - to ciągła poprawa. Użyj Claude Code do obsługi mechanicznych transformacji, podczas gdy Ty skupisz się na decyzjach architektonicznych i wartości biznesowej. Z pomocą AI możesz zająć się długiem technicznym, który gromadził się latami i przekształcić legacy systemy w nowoczesne, utrzymywalne bazy kodu.