Преосмислување на рефакторирањето: Зошто на вашата кодна база ѝ е потребно редовно освежување
Рефакторирањето често се претставува како задача за расчистување: нешто што треба да се закаже откако ќе заврши „вистинската“ работа. Таквото гледиште е скапо. Во backend систем, кодот што испорачува функционалности е истовремено и кодот што мора да го издржи следното менување на шемата, неуспехот на интеграција, скокот на сообраќајот и инцидентот за време на дежурство.
Редовното дотерување не значи стремеж кон естетско совршенство. Станува збор за тоа промените да останат достапни. Кога структурата на PHP апликацијата одговара на одлуките што треба да ги донесува, развивачите можат да работат со сигурност. Кога не одговара, секое навидум мало барање станува ризична експедиција низ контролери, пребарувања, конфигурација и недокументирани претпоставки.
Рефакторирањето ја штити брзината на испорака
Брзината на развој на функционалности и рефакторирањето не се спротивставени приоритети. Тие стануваат противници само кога рефакторирањето се третира како голем, одвоен проект со нејасен исход. Малите, насочени подобрувања направени паралелно со работата на производот обично ја зголемуваат брзината на испорака бидејќи отстрануваат повторливо триење.
Разгледајте endpoint за наплата чиј контролер валидира влез, пресметува данок, запишува нарачки, повикува давател на услуги за плаќање, испраќа е-пошта и го форматира одговорот. Можеби работи денес, но неговите одговорности се испреплетени. Додавањето нов давател на услуги за плаќање или повторното испраќање неуспешно известување сега носи ризик од менување на редоследот на деловно критичните дејства.
Подобрата граница не бара модерен архитектонски дијаграм. Таа бара одговорностите да се направат јасни:
- контролерот ги преведува HTTP барањата и одговорите;
- апликациската услуга го координира случајот на употреба;
- доменската логика ги пресметува правилата и промените на состојбата;
- репозиториумите и клиентите се справуваат со трајното складирање и оддалечените системи;
- позадинските задачи се справуваат со работата што не мора да заврши пред одговорот.
Овие граници го олеснуваат расудувањето за неуспесите. Истекување на времето за плаќање припаѓа на клиентот за плаќање и на работниот тек што одлучува дали да се обиде повторно. Невалидно обликувано барање припаѓа на HTTP границата. Нарушување на уникатност во базата на податоци можеби треба да се претвори во значаен исход на ниво на апликација. Рефакторирањето помага овие одлуки да се постават таму каде што можат да се тестираат и разберат.
Започнете таму каде што промената веќе е болна
Најдобрите цели за рефакторирање ретко се избираат со пребарување на најстарите датотеки. Барајте код што редовно ја забавува работата: класа што сите се плашат да ја допрат, пребарување копирано во неколку endpoint-и, услов што расте за секој нов тип на клиент или Docker поставување што само еден развивач знае да го поправи.
Повторливата збунетост е корисен доказ. Ако развивачот мора постојано повторно да открива зошто постои некој метод, кодот бара појасно име, помал опсег или тест што објаснува. Ако некоја вредност мора да се промени на четири места, таа бара единствен извор на вистина.
Најпрво направете го најмалото безбедно подобрување
Рефакторирањето најдобро функционира како низа чекори што го зачувуваат однесувањето. Пред да преместите логика, утврдете какво е тековното однесување преку тестови, примери на барања, логови или насочена рачна проверка. Потоа направете една структурна промена и проверете ја.
На пример, извлечете пресметка на данок од контролер пред да го редизајнирате целиот модул за нарачки:
final class TaxCalculator
{
public function calculate(int $subtotalCents, float $rate): int
{
return (int) round($subtotalCents * $rate);
}
}
Примерот намерно е тесно фокусиран. Во реален финансиски систем, стапките, правилата за заокружување и ракувањето со валути заслужуваат експлицитни доменски типови и тестирани деловни правила. Поентата не е дека на секоја пресметка ѝ е потребна класа. Поентата е дека значајно правило не треба да биде скриено помеѓу парсирањето на барањето и JSON серијализацијата.
Рефакторирајте ги API-јата околу договори, а не околу погодност
Рефакторирањето на backend може случајно да ги наруши клиентите дури и кога внатрешните тестови остануваат зелени. API-јата се договори: полиња во одговорот, статусни кодови, однесување при пагинација, пораки за валидација, правила за идемпотентност и очекувања за време може да зависат од некој друг.
Кога преобликувате endpoint, одделете го внатрешното подобрување од надворешната промена. Одржете го јавниот договор стабилен додека ги заменувате деталите на имплементацијата каде што е можно. Ако договорот мора да се промени, обезбедете намерна патека за миграција наместо тивко да менувате поле од низа во објект.
За endpoint-и за запишување, идемпотентноста заслужува посебно внимание. Повторен обид на клиентот по мрежен неуспех не смее да создаде втора нарачка само затоа што првиот одговор бил изгубен. Деталите на имплементацијата варираат, но работниот тек треба да идентификува повторено барање и безбедно да го врати или повторно да го состави првичниот резултат.
Исто така, оддалечените повици не треба да се наоѓаат во трансакција на база на податоци, освен ако моделот на конзистентност навистина го бара тоа. Држењето трансакција отворена додека се чека API од трета страна го зголемува времето на заклучување и го отежнува справувањето со неуспеси. Често побезбеден дизајн е да се потврди локалната состојба, да се запише настан или задача во истата трансакција, а потоа надворешната работа да се изврши асинхроно со повторни обиди и набљудливост.
Чистењето на базата на податоци е архитектонска работа
Промените во базата на податоци се меѓу можностите за рефакторирање со најголема вредност бидејќи лошите модели за пристап до податоци брзо се шират. Endpoint што вчитува листа записи, а потоа пребарува поврзани податоци во циклус, може скромно барање да претвори во десетици или стотици пребарувања.
Измерете пред да го промените. Потоа изберете ја најмалку сложената поправка: однапред вчитајте ги релациите кога моделот на податоци го поддржува тоа, земете ги потребните записи со едно пребарување, додајте соодветен индекс за докажан модел на пристап или редизајнирајте endpoint што враќа многу повеќе податоци отколку што им се потребни на потрошувачите.
Индексите не се безопасни украси. Тие можат да ги подобрат читањата, додека додаваат трошоци за складирање и запишување. Редовното дотерување значи прегледување на индексите во контекст на реални пребарувања, а не додавање индекс за секоја колона што се појавува во филтер.
Миграциите ја заслужуваат истата дисциплина. Миграција безбедна за распоредување ги зема предвид постојните редови, однесувањето на заклучувањето, верзиите на апликацијата што работат за време на воведувањето и ограничувањата за враќање назад. Додавањето nullable колона обично е полесно да се воведе по фази отколку веднаш да се додаде non-null колона со скап стандард на голема табела. Внимателно пополнете ги постојните податоци, распоредете код што ги разбира двете состојби, а потоа заострете ги ограничувањата штом податоците се подготвени.
Одржувајте ја runtime околината едноставна
Docker може да го направи локалниот развој повторлив, но само ако конфигурацијата остане намерна. Фиксирајте ги главните избори за runtime што ѝ се потребни на вашата апликација, чувајте ја конфигурацијата во променливи на околината или управувани тајни и избегнувајте вградување променливи акредитиви во слики.
Корисно прашање при дотерување е едноставно: дали нов развивач може да ја стартува апликацијата со документирани команди и да ги добие истите услуги, порти и зависности како остатокот од тимот? Ако одговорот зависи од ненапишан локален заобиколен начин, тоа е оперативен долг.
На продукциските распоредувања им е потребна истата јасност. Изградете непроменлив артефакт, применувајте ги промените во базата на податоци преку контролиран процес, изложете сигнали за подготвеност и здравје соодветни за платформата и направете ги логовите доволно корисни за да проследите барање низ апликацијата. Рефакторирањето на конфигурацијата вреди кога ја забрзува дијагнозата на неуспеси, а не само кога го преуредува YAML.
Користете ги тестовите како заштитни огради, а не како ритуал
Тестовите го прават рефакторирањето побезбедно кога опишуваат однесување што е важно. Насочен unit тест може да заштити деловно правило; интеграциски тест може да потврди пребарување во репозиториум и граница на трансакција; API тест може да зачува договор за одговор. Ниту еден поединечен слој не открива сè.
Кревкиот пакет тестови е сам по себе сигнал за рефакторирање. Тестовите што зависат од приватни детали на имплементацијата го обесхрабруваат чистењето бидејќи безопасното преструктурирање ги крши. Претпочитајте да проверувате набљудливи исходи: вратени вредности, зачувана состојба, емитирани настани или HTTP одговори.
Пред поголема промена, запишете ги инваријантите што мора да останат вистинити. Примери се „залихата никогаш не станува негативна“, „успешното плаќање се запишува еднаш“ или „корисник не може да чита записи на друга организација“. Овие изјави му даваат на тимот практична дефиниција за безбедност.
Дотерувањето е навика на одговорно одржување
Здравите кодни бази не се оние без незгодни области. Тие се оние каде што незгодните области се видливи, ограничени и постојано подобрувани. Редовната практика на рефакторирање го претвора одржувањето од чувство на вина по завршената работа во дел од нормалната одговорност на инженерството.
Клучната промена е следнава: рефакторирањето не е полирање на кодот откако вредноста е испорачана. Тоа е начинот на кој тимот ја зачувува способноста да испорачува вредност кога ќе пристигне следната важна промена.