PHP refaktoriranje: pouzdano snalaženje u labirintu naslijeđenog koda
Naslijeđeni PHP rijetko se najavljuje jednim katastrofalnim problemom. Češće djeluje poput labirinta: kontroler koji također validira unos i zapisuje SQL, pomoćna funkcija sa skrivenim globalnim stanjem, upit baze podataka kopiran na četiri mjesta i postupak isporuke koji nitko ne želi dirati. Aplikacija možda i dalje zarađuje, upravo zato njezino refaktoriranje zahtijeva pažnju, a ne drskost.
Cilj nije da stari kod izgleda moderno. Cilj je učiniti promjene sigurnijima, ponašanje jasnijim, a kvarove lakšima za dijagnosticiranje. Dobro refaktoriranje štiti vrijednost već ugrađenu u sustav koji radi, istodobno stvarajući prostor za sljedeću značajku, migraciju ili operativno poboljšanje.
Započnite ponašanjem, a ne arhitekturom
Najskuplja pogreška pri refaktoriranju jest promijeniti previše prije razumijevanja onoga što sustav zapravo radi. Naslijeđeni kod često sadrži nezgrapnu logiku jer kodira poslovno pravilo koje nigdje drugdje nije dokumentirano. Iznenađujući uvjet može biti nedostatak, ali može biti i jedina zaštita od neuobičajenog slučaja naplate ili osobitosti API-ja treće strane.
Prije premještanja klasa ili uvođenja novog sloja okvira, mapirajte jedan smisleni tijek rada od početka do kraja. Na primjer: HTTP zahtjev ulazi u kontroler, podaci zahtjeva se normaliziraju, servis izračunava rezultat, zapisi se učitavaju ili ažuriraju te se vraća odgovor. Pratite podatke, uključujući putanje pogrešaka.
Izradite malu sigurnosnu mrežu oko ponašanja koje namjeravate promijeniti. U PHP aplikaciji to može započeti fokusiranim testovima na spoju do kojeg možete najlakše doći:
public function testInvoiceIsRejectedWhenCustomerIsInactive(): void
{
$customer = new Customer(id: 42, active: false);
$service = new InvoiceService($this->repository);
$result = $service->create($customer, 1500);
self::assertFalse($result->isAccepted());
self::assertSame('customer_inactive', $result->reason());
}
To ne znači da se svaki naslijeđeni sustav može potpuno testirati prije početka rada. To je praktično polazište: opišite ponašanje najbliže svojoj promjeni, a zatim ga očuvajte dok poboljšavate dizajn.
Pronađite spojeve koji omogućuju promjene
Spoj je mjesto na kojem možete izmijeniti jedan dio sustava bez prepisivanja svega oko njega. U PHP-u uobičajeni spojevi uključuju granicu repozitorija oko pristupa bazi podataka, omotač klijenta oko vanjskog API-ja ili servis izdvojen iz velikog kontrolera.
Razmotrite kontroler koji provodi validaciju, izračunava ukupan iznos narudžbe, umeće retke i šalje e-poštu. Zamjena svega odjednom potpunom novom arhitekturom rizična je. Sigurniji potez jest izdvojiti jednu odgovornost uz očuvanje postojećeg ponašanja zahtjeva i odgovora.
final class OrderController
{
public function store(Request $request): Response
{
$order = $this->orderCreator->create(
$request->input('customer_id'),
$request->input('items', [])
);
return Response::json(['id' => $order->id()], 201);
}
}
Izdvojeni OrderCreator tada može preuzeti poslovnu operaciju. Njegove ovisnosti postaju eksplicitne: repozitorij narudžbi, provjeravatelj zaliha, granica transakcije i pošiljatelj obavijesti. Ta je jasnoća vrjednija od impresivne strukture direktorija.
Dajte prednost uskim sučeljima
Nemojte graditi generičku apstrakciju samo zato što apstrakcija zvuči čisto. Ako servis treba samo učitati i spremiti narudžbu, fokusirano je sučelje lakše razumjeti i testirati nego repozitorij s desecima nepovezanih metoda.
- Izložite operacije koje odgovaraju poslovnoj potrebi.
- Držite objekte zahtjeva okvira i ORM modele na rubu aplikacije gdje je to moguće.
- U osnovnu logiku prosljeđujte jednostavne vrijednosti ili objekte usmjerene na domenu.
- Uvedite novi sloj samo kada uklanja stvarnu spregu ili pojašnjava odgovornost.
Namjerno raspetljajte pristup podacima
Kod baze podataka često je mjesto na kojem naslijeđeni PHP postaje krhak. SQL može biti raspršen kroz predloške, kontrolere, cron skripte i pomoćne funkcije. To čini rad na performansama, promjene sheme i upravljanje transakcijama nepotrebno opasnima.
Centraliziranje svakog upita u jednoj klasi za „pristup podacima” nije automatski bolje. Grupirajte upite prema dijelu sustava kojem služe i jasno odredite vlasništvo nad transakcijama. Tijek stvaranja plaćanja, primjerice, ne bi trebao neprimjetno potvrditi transakciju usred operacije s više koraka.
$connection->beginTransaction();
try {
$orderRepository->save($order);
$paymentRepository->save($payment);
$connection->commit();
} catch (Throwable $exception) {
$connection->rollBack();
throw $exception;
}
Refaktoriranje ovog područja također stvara priliku za pregled oblika upita. Pazite na ponovljene upite unutar petlji, neograničene skupove rezultata i stupce koji se dohvaćaju, ali se nikad ne koriste. Rješavanje tih problema može poboljšati odzivnost bez preuranjenog dodavanja predmemorija ili prepisivanja sloja baze podataka.
Budite oprezni s ponovnim pokušajima. Ponovni pokušaj neuspjelog čitanja može biti razuman kada ga opravdavaju ovisnost i vrsta kvara. Ponovni pokušaj pisanja je drukčiji: narudžba, terećenje ili poruka mogu se duplicirati ako operacija nije osmišljena kao idempotentna. Pouzdanost nije jednostavno „pokušaj ponovno”; ona znači znati je li ponavljanje radnje sigurno.
Učinite ovisnosti vidljivima
Skrivene ovisnosti glavni su izvor iznenađenja u starijem PHP kodu. Statički lokatori servisa, globalna polja konfiguracije, izravni pozivi na new PDO() i čitanja okruženja duboko unutar poslovne logike otežavaju testove i čine ponašanje u produkciji manje predvidljivim.
Ubrizgavanje ovisnosti ne zahtijeva složen spremnik da bi bilo korisno. Samo ubrizgavanje kroz konstruktor jasno pokazuje potrebe klase:
final class ExchangeRateService
{
public function __construct(
private RateProvider $provider,
private Logger $logger
) {
}
}
To također poboljšava obradu kvarova. Pozivatelj može odlučiti kako konstruirati stvarnog pružatelja u produkciji i kontroliranu zamjenu u testovima. Na granici bilježite koristan kontekst bez otkrivanja vjerodajnica, tokena ili osobnih podataka. Interno sačuvajte izvornu iznimku kada pomaže dijagnostici, ali korisnicima API-ja vraćajte stabilne, namjerne odgovore o pogreškama.
Refaktorirajte isporuku usporedno s kodom
Baza koda nije održiva ako je programeri ne mogu dosljedno pokretati. Docker može smanjiti odstupanja „radi na mom računalu” kada su slika, konfiguracija vremena izvođenja i instalacija ovisnosti eksplicitne. Važna je ponovljivost, a ne kontejnerizacija sama po sebi.
Držite konfiguraciju aplikacije izvan slike kada se razlikuje prema okruženju, validirajte obvezne postavke tijekom pokretanja i izbjegavajte ugrađivanje tajni u kontrolu izvornog koda ili slojeve spremnika. Ako implementacija mijenja PHP ekstenzije, radne procese ili varijable okruženja, tretirajte to kao dio iste promjene kao i kod koji o tome ovisi.
Implementacija bi trebala biti dosadna: izgradite poznati artefakt, pokrenite automatizirane provjere primjerene riziku, primijenite migracije baze podataka s planom koji uzima u obzir vraćanje promjena i provjerite zdravlje pokrenutog servisa. Promjene sheme zaslužuju posebnu pažnju. Aditivne promjene obično je lakše sigurno implementirati od promjena koje odmah uklanjaju ili preimenuju polja koja još koriste starije instance aplikacije.
Odaberite napredak umjesto čistoće
Ne treba svakom naslijeđenom razredu odmah popravak. Dajte prednost kodu koji se često mijenja, uzrokuje incidente, blokira isporuku, obrađuje osjetljive podatke ili se nalazi na kritičnoj putanji zahtjeva. Stabilan, izoliran kod ostavite na miru dok ne postoji konkretan razlog da ga dirate.
Mala refaktoriranja se zbrajaju. Jasnije ime, uklonjeni duplicirani upit, fokusirani test i čista granica oko jednog vanjskog servisa možda neće izgledati dramatično u zahtjevu za spajanje. S vremenom mijenjaju trošak promjene.
Povjerenje u naslijeđeni kod ne dolazi iz pretvaranja da je labirint jednostavan. Dolazi iz osvjetljavanja jednog hodnika odjednom, bilježenja onoga što naučite i ostavljanja svakog dijela sigurnijim nego što ste ga zatekli.
To je trajna disciplina iza PHP refaktoriranja: očuvajte ponašanje, stvorite spojeve, učinite ovisnosti i tok podataka vidljivima te poboljšajte put isporuke s jednakom pažnjom kao i kod. Rezultat nije samo čišći PHP. To je sustav koji vaš tim može dovoljno dobro razumjeti da ga razvija.