Rewrite nebo refactor? 8 důležitých otázek, než se rozhodnete

Rewrite vypadá jako řešení problémů. Obvykle je to chyba. Tohle je 8 otázek, které byste si měli před rozhodnutím položit.

// obsah 12
  1. 01 Proč rewrite tak láká?
  2. 02 1. Je stávající kód nepoužitelný, nebo jen nepříjemný?
  3. 03 2. Máme pokrytí testy?
  4. 04 3. Rozumí někdo v týmu dané doméně?
  5. 05 4. Máme spoustu měsíců na vývoj bez tlaku?
  6. 06 5. Má nový tech stack měřitelnou výhodu?
  7. 07 6. Máme rezervu alespoň 40 % na neočekávané věci?
  8. 08 7. Máme cestu zpět, když to selže?
  9. 09 8. Víme, proč je kód takový, jaký je?
  10. 10 Co s tím?
  11. 11 Jak tohle mění AI?
  12. 12 Příběhy z praxe

TL;DR: Rewrite vypadá jako řešení problémů. Obvykle je to chyba. Tohle je 8 otázek, které byste si měli před rozhodnutím položit.

Proč rewrite tak láká?

Znám to. Sedíte nad cizím kódem přiděleného projektu, nebo i vlastním (pár let starým), koukáte na ty nehezké části kódu, dlouhé controllery, zase se něco rozbije a řeknete si: „Tohle už fakt nejde, pojďme to přepsat na zelené louce.“ a celý tým zajásá, že to konečně někdo řekl nahlas. Přijde krásný pocit z toho, že nový kód konečně bude hezký, dobře strukturovaný, udržovatelný, testovatelný. Konečně to přece bude správně. Úleva.

Jenže bacha. Je to falešná úleva.

Jasně. Zděděný kód děsí každého, každá nová funkce je boj, oprava chyby rozbije další 3 věci, zasvěcení nováčka do projektu trvá týdny. Vždyť se tím nakonec ušetří na vývoji, že jo?

Hezky to popsal Joel Spolsky v článku z roku 2000, kde popisuje rozhodnutí přepsat Netscape (to byl populární prohlížeč tehdy) od nuly a jak velká to byla chyba. Tři roky vývoje, během kterých Microsoft vydával nové verze IE a získával uživatele. Netscape mezitím přicházel o tržní podíl. Kód, který chtěli zahodit, obsahoval roky oprav skutečných bug reportů od skutečných uživatelů. Nový kód ty bug reporty nikdy neviděl, a tak chyby zopakoval.

Kompletní přepis projektu je někdy správnou volbou, ale méně často, než si jako programátoři sami myslíme v okamžiku, když jsme frustrovaní aktuálním stavem. Zkuste si položit těchto 8 otázek.

1. Je stávající kód nepoužitelný, nebo jen nepříjemný?

Tohle je nejdůležitější otázka a zároveň nejtěžší. Když jsme z kódu naštvaní, hodnotíme ho špatně.

Nepoužitelný kód je takový, který blokuje byznys. Například databáze, která nejde horizontálně škálovat, když je to nutné. Nebo technologie, která již nedostává bezpečnostní aktualizace a je děravá. Nebo jedna změna znamená týden ručního testování.

2. Máme pokrytí testy?

Pokud ne, nebo minimální, jak víte, co vše kód řeší?

Stávající kód (sebeošklivější) řeší byznys logiku a edge casy, které nejsou zdokumentované a nikdo si je nepamatuje, řeší specifické výjimky pro určité případy. Pokud nemáte dobré pokrytí testy, tak přijdete o funkce, které jste ani nevěděli, že máte. No, to se dozvíte až od zákazníka a to je problém.

3. Rozumí někdo v týmu dané doméně?

Princip Chesterton’s fence: než odstraníte plot, zjistěte, proč tam je.

Každá „divná věc“ v legacy kódu má historický důvod. Ta podivná if podmínka v platebním modulu? Třeba tam je hodně důležitá, nebo možná taky ne, kdo ví?

Pokud nikdo v týmu nerozumí doméně, proč kód dělá to, co dělá, přepis od nuly problémy neodstraní. Přepíšete kód, ale znovu budete narážet na stejné situace, jako někdo kdysi předtím, protože nevíte, na co máte narazit a za jakých okolností.

4. Máme spoustu měsíců na vývoj bez tlaku?

Tohle bývá otázka, na kterou skoro nikdy nikdo neřekne „ano“. A to je informace sama o sobě. Projekt musí běžet dál i během přepisu, musí se řešit bugy, nové funkce.

Přepis projektu pod tlakem skončí špatně. V polovině zjistíte, že kód měl více funkcí a edge casů (a mezitím přibyly další), než jste předpokládali a deadline se blíží. Tým je ve stresu. Začnete přepisovat „narychlo“, aby to aspoň nějak bylo hotové a jste tam, kde jste byli na začátku.

5. Má nový tech stack měřitelnou výhodu?

Nemyslím tím, že „je modernější“. Konkrétní, kvantifikovatelný benefit.

„React je modernější než jQuery“ není důvod. „React nám umožní sdílet komponenty napříč třemi produkty a ušetří odhadem 30 % vývojového času na UI“ už důvod je – protože je to číslo, které se po roce dá ověřit. To samé platí pro databáze, frameworky, message brokery, cokoliv.

Pokud nedokážete napsat měřitelný benefit do jedné věty, pravděpodobně ho zkrátka nemáte. Máte jen pocit, že nová technologie je lepší. A pocit je špatný základ pro přepis projektu.

6. Máme rezervu alespoň 40 % na neočekávané věci?

Nejen časovou rezervu, ale i rozpočet, kapacitu týmu, a hlavně trpělivost vedení.

40% je ještě konzervativní, reálně to může být i 100% a víc. Neočekávané věci jako třeba byznys logika, o které nikdo nevěděl, že existuje; integrace se třetími stranami, které se chovají jinak, než dokumentace říká. Výkon nové technologie je problematický a projeví se až při zátěžovém testování na konci.

Pokud vedení nebo zákazník očekává spuštění za půl roku a vy jste práci odhadli na půl roku, tak to skončí špatně.

7. Máme cestu zpět, když to selže?

Rollback strategie. Ještě než začnete, musíte vědět, jak z toho ven.

Nejlepší rewrity probíhají paralelně: nový systém běží vedle starého, provoz se postupně přesouvá. Databáze se migruje s možností zpětné migrace.

Pokud si řeknete „smažeme starý kód a jedeme“ a cestu zpět nemáte, tak to znamená, že rewrite neprovedete bez velkých porodních bolestí a to je dobré vědět předem.

8. Víme, proč je kód takový, jaký je?

Touhle otázkou se vracím ke 3., ale trochu jinak. Než si řeknete „přepíšeme to lépe“, zeptejte se: jak se původní kód dostal do tohoto stavu? Špatný design od začátku? Hromadění ad-hoc oprav pod tlakem? Bobtnání zprvu jednoduchých funkcí bez refaktoringu? Střídání vývojářů s různými styly?

Pokud neznáte odpověď, nový kód skončí na stejném místě za 3 roky, jen s modernějším frameworkem.

Co s tím?

Alternativa k rewrite není nechat kód „hnít“ dál, ale je to postupná, řízená transformace.

Strangler Fig pattern – nový kód obklopuje starý, nové funkce se píšou v novém kódu a starý se postupně nahrazuje.

Scientist pattern – nový kód běží paralelně se starým, výsledky se porovnávají. Produkční data, produkční chování, ale bez velkého rizika, protože starý kód stále odpovídá uživateli. Odchylky se logují a vyhodnocují. Postupně provoz přebírá nový kód.

Žádná z těhle cest není rychlá. Ale jsou bezpečnější než rewrite. A přitom vás dovedou na stejné místo, jen bez ročního výpadku nových funkcí.

Jak tohle mění AI?

Možná by se dalo čekat, že AI udělá rewrite jednodušším. Mně se ukazuje úplný opak: AI výrazně zvýhodňuje refactor proti rewrite.

AI je tak dobrá, jak dobrý kontext jí dodáte. Refactor staví na existujícím kódu, AI tedy vidí byznys logiku, edge casy i integrační detaily. Při refactoringu AI rozumí, co kód dělá, a navrhne, jak to udělat lépe. Rewrite naopak začíná na zelené louce a množství kontextu je pro AI zatím příliš velké na to, aby udržela kontext úplně celého projektu. Naproti tomu postupný refaktor, to je něco jiného. Navíc je snazší testovat postupné přepisování než to kompletní.

Konkrétně, kde AI v refactoringu šetří hromady času:

  • Pochopení legacy kódu. AI projde desítky tisíc řádků PHP a najde edge casy za pár minut a pomůže s pochopením do pár hodin. Před AI to byla práce na dny, někdy týdny.
  • Generování testů pro netestovaný kód. AI navrhne testy z existujícího chování. Pokrytí, které by ručně trvalo měsíce, je teď otázka týdnů.
  • Postupné nahrazování modulů. AI napíše náhradu starého modulu. Strangler Fig migrace, která dřív vyžadovala tým nebo hromady času, dnes zvládne sólo developer za zlomek času.

Co AI v této rovnici nedělá:

  • Nerozhodne za vás, jestli refaktorovat, nebo přepsat. To rozhodnutí je strategické a leží na vás.
  • Nevyřeší absenci znalosti domény. AI najde edge casy v kódu, ale nevysvětlí, proč tam jsou.
  • Nezvládne rewrite za týden. Když si zítra někdo v týmu řekne „dnes AI zvládne rewrite za týden, jdeme do toho“, tak se sakra mýlí, ale to je o zkušenostech.

Refactor s AI ale zvládnete podstatně rychleji než dřív. A díky tomu má dnes rewrite ještě méně důvodů, než míval.

Příběhy z praxe

Kdy jsem rewrite udělal a byla to chyba

Moc dobře si pamatuji tenhle případ, protože byl obzvlášť ošklivý. Bylo to před 7 lety. E-commerce platforma, cca 200 000 řádků PHP (na míru psaného kódu, žádný framework, přesně to, co si představíte pod pojmem „špagety kód“), asi 8 let starý kód. Velký a dobře fungující eshop. Přesvědčil jsem nadřízeného, že potřebujeme rewrite na Symfony. Odhadoval jsem 9 měsíců. Reálně to trvalo 18. Během vývoje jsme objevovali pravidelně kód, který něco řešil a my nevěděli co a jak přesně. Hádky s klientem, s týmem. Tým se pod stresem rozpadl. Původní kód byl nepříjemný, ne nepoužitelný. Měl jsem refaktorovat, teď už to vím.

Kdy jsem odolal a vyplatilo se

Interní systém klienta, cca 15 000 řádků smíšeného PHP a Javascriptu, žádné testy. Můj vnitřní tlak na rewrite byl silný. Místo toho jsem navrhl jiný postup: 2 měsíce psaní testů pro kritické cesty, pak postupný refaktoring modul po modulu. Za 6 měsíců byl kód snesitelný. Za další měsíc byla napsaná nová funkce, která by při rewrite nebyla hotová ještě rok. Klient si (v dobrém smyslu) nevšiml rozdílu, kromě rychlejšího vývoje poté. Systém prostě fungoval a postupně se zlepšoval.

Kdy jsem neodolal a přesto se to vyplatilo

Menší B2B systém pro komunikaci firmy s jejich partnery. Jednotky tisíc řádků PHP. V požadavku byly nové funkce a neopak některé už nebyly potřeba. Nebyl tlak mít nové funkce i na původním systému. Přepsali jsme to v týmu do Symfony a Reactu během asi 2 měsíců. Chvíli běžely paralelně stará i nová verze aplikace nad stejnou databází, takže nebyl takový stres, kdyby něco nefungovalo. Nakonec jsme starou verzi zařízli a dnes vyvíjíme další nové funkce v novém systému.

Michal Katuščák
Michal Katuščák

Navrhuji a vyvíjím aplikace nad Symfony a Reactem, zajímám se architekturu softwaru. Žiju v Českých Budějovicích.