Рефакторирање за отпорност: Кога и како да ги обновите вашите PHP системи
Повеќето PHP системи не откажуваат затоа што јазикот е неспособен. Тие откажуваат затоа што корисната апликација тивко акумулира премногу одговорности, премногу скриени претпоставки и премногу ризични патишта за промени. Малата функционалност станува исклучок во контролер. Барање кон базата на податоци се копира на три места. Docker сликата работи локално, но зависи од детал на околината што никој не го документирал.
Рефакторирањето за отпорност е дисциплина на правење на промените побезбедни пред да пристигне следната итна промена. Тоа не е барање да се преработи секоја несовршена апликација. Всушност, широка преработка често е најмалку отпорната опција: ја запира испораката, го губи тешко стекнатото знаење за граничните случаи и создава долг период во кој и стариот и новиот систем мора да бидат разбрани.
Препознајте ја разликата помеѓу неуредно и кревко
Не секој мирис на код оправдува архитектонска работа. Една класа може да биде неелегантна, но предвидлива. Поважниот сигнал е кревкоста: мала, разумна промена има неочекувано голем радиус на влијание.
Барајте обрасци како овие:
- Објавувањето мала промена на endpoint бара измени во неповрзани контролери, задачи и шаблони.
- Деловните правила се вградени во обработката на HTTP барања, ORM повратни повици или SQL фрагменти.
- Тестовите поминуваат само со споделена база на податоци или зависат од редоследот на извршување.
- Задачите во заднина можат да ја повторат работата без јасна стратегија за идемпотентност.
- Миграциите на базата на податоци тешко се враќаат или не можат да коегзистираат со претходната верзија на апликацијата.
- Падовите во продукција се дијагностицираат со рачно читање логови, бидејќи барањата, задачите и грешките немаат корисен контекст.
Ова се оперативни грижи исто колку и грижи за кодот. Отпорноста значи дека системот може да апсорбира делумен неуспех, да поддржи безбедно распоредување и да остане разбирлив за луѓето што прават промени под притисок.
Изберете рефакторирање пред повторно градење кога системот сè уште има јасна граница
Целосно повторно градење може да биде оправдано кога клучна зависност од платформата не е поддржана, моделот на доменот е суштински погрешен или апликацијата не може да исполни ненадминлив услов за доверливост или безбедност. И тогаш, повторното градење обично треба да биде постепено.
За повеќето зрели PHP апликации, почнете со насочено рефакторирање. Зачувајте го однесувањето додека создавате граници околу деловите што најчесто се менуваат: HTTP граници, перзистентност, надворешни API-ја, асинхрона работа и доменски правила. Целта не е апстрактна „чиста архитектура“. Целта е систем во кој важните одлуки имаат едно очигледно место.
Корисен тест е едноставен: дали развивачот може да објасни каде се валидира нарачка, каде се зачувува и каде се активираат нејзините споредни ефекти? Ако одговорот бара следење код од контролери, настани на модели, слушачи на редици и тригери во базата на податоци, на апликацијата ѝ се потребни појасни граници.
Извлекувајте однесување, а не слоеви поради самите нив
Претпоставете дека endpoint валидира барање, пресметува цена, создава нарачка, наплатува преку давател и испраќа потврда. Преместувањето на секоја линија во друга класа не го подобрува автоматски дизајнот. Наместо тоа, извлечете стабилни одговорности: сервис за нарачки или случај на употреба за координирање на работниот тек, компонента за цени за правилата за цени и адаптер за давателот на плаќања.
final class PlaceOrder
{
public function __construct(
private PricingService $pricing,
private OrderRepository $orders,
private PaymentGateway $payments,
) {
}
public function handle(PlaceOrderCommand $command): Order
{
$total = $this->pricing->calculate($command->items);
$order = Order::createPending($command->customerId, $command->items, $total);
$this->orders->save($order);
$this->payments->charge($order->id(), $total);
return $order;
}
}
Овој пример намерно е нецелосен: продукцискиот код мора да дефинира што се случува кога плаќањето успева, но подоцнежен чекор откажува, или кога клиентот го повторува барањето. Вредноста на структурата е во тоа што овие прашања сега се експлицитни и проверливи.
Направете ги патиштата на неуспех првокласна работа во дизајнот
Рефакторирањето на среќниот пат може да направи системот да изгледа почисто, додека неговите најголеми ризици остануваат недопрени. Прегледајте ја секоја надворешна граница: услуги за плаќање, даватели на е-пошта, складирање на објекти, внатрешни HTTP повици, редици и базата на податоци.
За секоја од нив, одлучете што треба да направи апликацијата кога ќе истече времето, ќе врати грешка или ќе ја обработи истата порака двапати. Не додавајте повторни обиди наслепо. Повторниот обид за читање може да е безопасен; повторниот обид за наплата или е-пошта може да создаде дупликат ефекти, освен ако низводната операција поддржува клуч за идемпотентност или вашата апликација безбедно го запишува резултатот.
Асинхроните работни текови имаат корист од outbox образец: извршете ја промената на доменот и запис за настан во иста трансакција на базата на податоци, а потоа објавете го тој запис одделно. Ова го спречува вообичаениот јаз каде што нарачката е зачувана, но процесот паѓа пред да биде испратена нејзината порака во редицата. Исто така, на неуспешното објавување му дава траен пат за опоравување.
Рефакторирајте ја базата на податоци имајќи го предвид распоредувањето
Промените во базата на податоци се местото каде што елегантниот код сè уште може да предизвика прекин. Третирајте ја еволуцијата на шемата како проблем на компатибилност помеѓу верзиите на апликацијата.
Користете низа на проширување и стеснување за промени што влијаат на живи податоци:
- Додадете нови nullable колони, табели или индекси без да ја отстраните старата структура.
- Распоредете код што може да ги чита двете претставувања и ги запишува потребните нови податоци.
- Пополнете ги постојните записи во контролирани серии, со видливост на напредокот и грешките.
- Префрлете ги читањата на новото претставување по верификацијата.
- Отстранете ја старата патека само откако ниедна распоредена верзија повеќе не ѝ е потребна.
Овој пристап привремено чини малку повеќе код, но поддржува постепени распоредувања и му дава на враќањето назад реална шанса да успее. Избегнувајте да споите деструктивна миграција со издание што веднаш ја бара новата шема.
Користете Docker за да ги намалите изненадувањата од околината
Контејнерите помагаат кога ги прават видливи претпоставките за извршувањето. PHP сликата треба да ги декларира PHP верзијата, потребните екстензии, инсталацијата на зависности и командата што го стартува процесот. Чувајте ја конфигурацијата надвор од сликата каде што е можно и избегнувајте да третирате контејнер како изменлив сервер.
Изградете ги зависностите во една фаза и извршувајте ја апликацијата во полесна runtime фаза кога тоа одговара на моделот на распоредување. Уште поважно, направете го договорот за стартување јасен: конфигурацијата мора да биде валидирана, неуспесите на поврзување со базата на податоци мора да создаваат логови што овозможуваат дејствување, а проверките на здравјето треба да разликуваат „процесот постои“ од „услугата може да прифати работа“.
Не правете веб-процесот автоматски да извршува миграции при секое стартување. Во распоредување со повеќе инстанци, истовременото стартување може да го направи тоа небезбедно. Извршете ги миграциите како контролиран чекор на распоредувањето, а потоа распоредете инстанци на апликацијата што се компатибилни со добиената шема.
Заштитете ја работата со повратни јамки
Рефакторирањето е уверливо кога ја подобрува сигурноста за испорака. Додајте тестови околу набљудливото однесување пред да преместувате сложена логика. Unit тестовите се корисни за цени, дозволи и премини на состојби; интеграциските тестови се суштински за репозиториуми, миграции, серилизација на редици и API адаптери. Тенок пакет end-to-end тестови може да ги валидира патиштата што клиентите навистина ги користат.
Комбинирајте ги тестовите со оперативни повратни информации. Структурираните логови треба да вклучуваат идентификатор на барање или корелација. Метриките треба да ги изложуваат стапките на грешки, длабочината на редицата, латентноста и активноста на повторни обиди таму каде што постојат тие сигнали. Алармите треба да упатуваат кон состојба што овозможува дејствување, а не само да известуваат некого дека се променила графика.
Работата на перформансите го следи истиот принцип. Прво измерете го бавниот endpoint или барање. Потоа испитајте го бројот на барања, индексите, големината на товарот, однесувањето на кешот и конкуренцијата. Заменувањето на јасно барање со паметен кеш пред мерењето често заменува видливо доцнење со потежок проблем на конзистентност.
Повторно изградете го само делот што заслужува повторно градење
Отпорниот PHP систем ретко е резултат на еден херојски напор за модернизација. Тој произлегува од намерни, повратни подобрувања: појасна граница, побезбедна миграција, идемпотентна задача, подобар тест, распоредување што ја почитува компатибилноста.
Кога некоја компонента постојано го спречува остатокот од апликацијата да еволуира, повторно изградете ја таа компонента зад стабилен интерфејс. Оставете ги старите и новите имплементации накратко да коегзистираат, потврдете го однесувањето во продукција и отстранете ја старата патека кога доказите — а не оптимизмот — велат дека е безбедно. Така рефакторирањето станува отпорност: не со тоа што кодот станува совршен, туку со тоа што следната неопходна промена станува помалку опасна од претходната.