Jak poznat, že technický dluh začíná brzdit další vývoj
Za roky práce na různých projektech jsem začal technický dluh vnímat hlavně podle toho, jak snadno se dá systém dál rozvíjet. Ne podle toho, jak kód vypadá, ale podle toho, kolik kontextu a opatrnosti potřebuje každá další změna.
Samotná úprava přitom často nebývá složitá. Složitější je pochopit, co všechno na ni navazuje, jaké může mít vedlejší dopady a jestli zásahem do jednoho flow neovlivníte něco úplně jinde.
Dnes je to dobře vidět i při práci s AI agenty. Samotný kód zvládnou napsat velmi dobře a implementaci často výrazně urychlí. V projektu se skrytými vazbami ale narážejí na stejný problém jako vývojář, který systém nezná: nemusí správně odhalit všechny souvislosti.
Právě to je pro mě jeden z nejlepších signálů technického dluhu. Ne to, jak čistě nebo moderně codebase působí, ale kolik kontextu je potřeba znát, aby se v ní dala udělat změna s rozumnou mírou jistoty.
Technický dluh není jen špatný kód
Technický dluh podle mě často není v tom, že je kód vyloženě špatně napsaný. Větší problém nastává ve chvíli, kdy přestává být jasné, kde vzniká chování systému a proč je něco udělané právě takhle.
U starších systémů navíc často narážím na věci, které byly dřív běžnější: slabý naming, nekonzistentní strukturu, minimum dokumentace a pravidla, která existovala spíš v hlavách lidí než v samotném kódu. Dnešní AI si s tím často dokáže poradit překvapivě dobře a význam kódu si z kontextu odvodí. Pro člověka je ale taková codebase pořád výrazně hůř čitelná a každá změna stojí víc mentální energie.
“Programs must be written for people to read, and only incidentally for machines to execute.” — Harold Abelson
A pak jsou tu ještě skryté vazby. Část business logiky je v aplikaci, něco se děje přes eventy nebo fronty, další pravidla mohou být v databázi, integrační vrstvě nebo pravidelných úlohách. Jednotlivě na tom nemusí být nic špatného. Problém je, když už bez hlubší znalosti projektu nejde spolehlivě říct, co všechno se při jedné změně skutečně stane.
Právě tenhle skrytý kontext bývá podle mě dražší než samotná kvalita kódu. Kód se dá upravit. Mnohem hůř se obnovuje znalost toho, proč systém funguje právě tak, jak funguje.
Jak poznat, že už technický dluh začíná brzdit vývoj
Technický dluh podle mě začne být skutečný problém ve chvíli, kdy se běžné změny začnou dělat s nepřiměřenou opatrností.
Najednou jsou v aplikaci místa, na která se nikomu nechce sahat. Ne proto, že by jim tým nerozuměl vůbec, ale protože nikdo nemá jistotu, co všechno se po změně ozve někde jinde.
Podobně to poznáte na code review. Nestačí projít diff a ověřit samotnou implementaci. Reviewer musí znát navazující flow, historické důvody některých rozhodnutí a často i věci, které z kódu nejsou vůbec zřejmé.
Další poměrně spolehlivý signál je věta typu: „Tohle musí vidět Petr, ten ví, jak to funguje.“ Ve chvíli, kdy je důležitá část systému závislá na kontextu jednoho nebo dvou lidí a dokumentace už realitě moc neodpovídá, začíná být každá větší změna dražší, než by měla být.
Stejně tak když se opakovaně odkládá upgrade, refaktor nebo zásah do kritického flow hlavně proto, že nikdo nedokáže rozumně odhadnout jeho dopad.
Martin Fowler technický dluh popisuje právě přes tuto cenu dalších změn: „The extra effort that it takes to add new features is the interest paid on the debt.“ martinfowler.com
A to je podle mě dobré měřítko i v praxi. Ve chvíli, kdy většina práce kolem změny nespočívá v její implementaci, ale v získávání jistoty, že se nerozbije něco dalšího, už technický dluh reálně zpomaluje další vývoj.
Kdy technický dluh řešit a kdy ho nechat být
Ne každý technický dluh má smysl řešit.
Některé části systému nejsou ideální, ale roky fungují, téměř se nemění a zásah do nich by přinesl víc rizika než užitku. Dobrým příkladem může být starší platební flow. Možná bychom ho dnes navrhli jinak, ale pokud je stabilní, dobře otestované a spolehlivě zpracovává platby, kompletní přepis nemusí být vůbec dobrá investice.
Mnohem důležitější je dívat se na to, kde technický dluh skutečně prodražuje další vývoj nebo zvyšuje provozní riziko.
Na opačné straně jsou systémy, kde už problém není v jedné části aplikace, ale v samotném základu. Běží na nepodporované technologii, klíčové balíčky se roky neudržují, upgrade cesta prakticky neexistuje nebo je další vývoj závislý na vlastním frameworku, kterému rozumí jeden člověk.
V takovém případě se postupně mění ekonomika celého rozhodnutí. Každou další funkci sice ještě dokážete dodat, ale zároveň dál investujete do prostředí, které bude za rok ještě dražší a rizikovější na údržbu.
Ani tady ale nemusí být správnou odpovědí kompletní rewrite. Často dává větší smysl postupná migrace, výměna problematických částí nebo oddělení nového vývoje od starého základu.
Technický dluh proto nevnímám jako seznam věcí, které je potřeba jednou uklidit. Spíš jako průběžné rozhodování o tom, kde je levnější dluh nést a kde už začíná být dražší než jeho odstranění.
Pozor na overengineering při refaktoringu s AI
AI dnes velmi dobře zná best practices, návrhové vzory i moderní způsoby, jak systém strukturovat. Právě proto ale při refaktoringu občas navrhne řešení, které je technicky správné, jen zbytečně složité pro problém, který skutečně řešíte.
Z jednoduché služby se snadno stane několik vrstev abstrakcí, interfaces, factories nebo nová architektura jen proto, že jde daný problém navrhnout „čistěji“. Na papíře může výsledek působit lépe, ale v praxi přidá další koncepty, které musí někdo chápat, testovat a dlouhodobě udržovat.
To je podle mě jedna z věcí, na které je potřeba si při práci s AI dávat pozor. Model má přirozenou tendenci směřovat k řešením, která vypadají jako best practice, ale nemusí dostatečně zohlednit, jakou míru složitosti si daný problém skutečně zaslouží.
Jinou architekturu potřebuje kritický billing modul, který se bude dál rozvíjet několik let, a jinou jednoduchá interní funkce, do které se možná další dva roky nesáhne. Stejně tak nedává smysl zavádět novou abstrakci jen proto, že by teoreticky mohla být užitečná někdy v budoucnu.
Cílem refaktoringu podle mě není dostat každou část codebase do ideálního stavu. Cílem je, aby se systém po změně lépe četl, jednodušeji udržoval a další úpravy v něm byly předvídatelnější.
Dobré řešení není to nejchytřejší nebo nejčistší. Je to takové, které má pro daný problém právě tolik složitosti, kolik potřebuje.
Pokud kvůli jednoduchému flow přibydou tři nové vrstvy, několik interfaces a další způsob, jakým se věci v projektu dělají, je dobré se zastavit a zeptat se, jestli jsme systém opravdu zjednodušili.
Overengineering totiž může být jen jiná forma technického dluhu. Nevzniká zanedbáním, ale naopak snahou navrhnout všechno až příliš dobře. Výsledek je ale podobný: další vývojář musí pochopit víc konceptů, než kolik jich daný problém reálně potřebuje.
U AI agentů se dá část tohoto problému zmírnit tím, že mají k dispozici jasný kontext projektu, popsané kritické flow a pravidla pro místa, kde nemají improvizovat. Tomu se podrobněji věnuju v článku Jak připravit projekt na vývoj s AI agenty.
Závěr
Technický dluh bude v dlouhodobě vyvíjeném systému vznikat vždycky. Každý projekt v sobě nese kompromisy, historická rozhodnutí a části, které bychom dnes možná navrhli jinak.
Podstatné podle mě není snažit se všechno průběžně „čistit“. Důležité je poznat, které věci už skutečně prodražují další vývoj, zvyšují riziko změn nebo komplikují pochopení systému.
S AI se samotné psaní kódu výrazně zrychluje. O to větší význam má podle mě schopnost udržet systém jednoduchý, čitelný a předvídatelný i v delším horizontu.
Cílem není perfektní codebase. Cílem je systém, na který se dá za několik let pořád rozumně navázat, aniž by každá další změna vyžadovala archeologický průzkum celé aplikace.