Zdědili jsme kód: Nejhorší nálezy v legacy projektech

V naší branži existuje speciální druh vzrušení. Ne to příjemné. Spíš takové to mrazení v zádech, když otevřete projekt po předchozím vývojáři a postupně vám dochází rozsah katastrofy. Říkáme tomu "dědický šok" a zažili jsme ho mnohokrát.
Toto jsou naše nejpamátnější nálezy. Jména a detaily jsou změněné, ale hrůza je autentická.
Hesla v plaintextu (a ještě hůř)
Začněme klasikou, která by neměla nikoho překvapit — a přesto nás překvapuje pořád. Převzali jsme e-shop středně velké firmy. První, co jsme udělali, bylo podívat se do databáze. Tabulka users, sloupec password. A tam, krásně čitelně: Heslo123, MojePrvniHeslo, admin.
Žádné hashování. Žádné solení. Prostě holý text. Tisíce uživatelských hesel, které mohl přečíst kdokoli s přístupem k databázi.
Ale to nejlepší teprve přijde. Ve stejném projektu jsme našli soubor passwords_backup.txt v kořenové složce webu. Veřejně přístupný. Obsahoval přihlašovací údaje k databázi, FTP serveru, e-mailovému účtu a platební bráně. Prostě takový pohodlný seznam pro případ, že by vývojář zapomněl přihlašovací údaje. Nebo pro případ, že by je chtěl někdo ukrást.
Pět tisíc řádků v jednom souboru
Jeden z našich oblíbených exponátů: PHP soubor s názvem functions.php. Pět tisíc dvě stě řádků. Jedna funkce. Ne, nepřehlédli jste se — celý soubor byl jedna obrovská funkce, která dělala doslova všechno. Zpracovávala formuláře, generovala HTML, posílala e-maily, komunikovala s databází, počítala DPH a v jednom zvláštním bloku kolem řádku 3400 dokonce kontrolovala počasí přes externí API.
Funkce se jmenovala doStuff(). A dělala opravdu hodně stuff.
Nejkrásnější na tom bylo, že obsahovala vnořené if-else bloky do hloubky dvanácti úrovní. Kdo někdy viděl kód odsazený o 48 mezer, ví, o čem mluvím. Při čtení jste museli horizontálně scrollovat jako při prohlížení panoramatické fotky.
SQL injection: otevřené dveře pro každého
Tenhle nález nás upřímně vyděsil. Převzali jsme webovou aplikaci pro správu firemních dokumentů. Citlivé smlouvy, faktury, osobní údaje zaměstnanců. A v kódu:
$query = "SELECT * FROM documents WHERE id = " . $_GET['id'];
Žádný prepared statement. Žádný escaping. Žádná validace vstupu. Doslova každý vstup od uživatele se rovnou lepil do SQL dotazu. Útočník mohl přes URL získat celou databázi, smazat ji, nebo si vytvořit administrátorský účet.
A tohle nebyl ojedinělý výskyt. Celá aplikace — desítky souborů — fungovala stejně. Bylo to jako najít dům, kde nejsou dveře zamčené, ale vlastně ani nemá dveře.
Dva tisíce !important
CSS může být záludné, to uznáváme. Ale to, co jsme našli v jednom projektu, překonalo všechna očekávání. Stylopis o 8 000 řádcích s téměř 2 000 výskyty !important.
Proč? Protože každý nový vývojář, který na projektu pracoval, zjistil, že jeho styly nefungují (kvůli !important od předchůdce), a tak přidal vlastní !important. Vznikla tak eskálující válka specifičnosti, kde nakonec doslova každé pravidlo potřebovalo !important, aby vůbec fungovalo.
Nejlepší řádek, který jsme tam našli:
.button { color: red !important; /* TODO: WHY DOESN'T THIS WORK */ }
Nefungovalo to, protože o dvacet řádků níž bylo:
.container .button { color: blue !important; }
Verzování zipem přes e-mail
"Jak verzujete kód?" je otázka, kterou klademe vždy, když přebíráme projekt. Většinou slyšíme "Git" nebo alespoň "SVN". Jednou jsme slyšeli odpověď, která nás na chvíli vyřadila z provozu:
"Posíláme si zipy mailem. Poslední verze je ta, co poslal Petr minulý čtvrtek. Nebo možná Honza v pátek. Zkontrolujte oba."
Projekt neměl žádný verzovací systém. Žádnou historii změn. Žádnou možnost vrátit se k předchozí verzi. Vývojáři si posílali soubory e-mailem a doufali, že si navzájem nepřepíší práci. Spoiler: přepisovali.
Při předávání projektu jsme dostali složku s názvem web_final_v2_FINAL_opraveno_FINAL2_hotovo.zip. To vám řekne všechno, co potřebujete vědět.
Stack Overflow s podpisem autora
Tohle je spíš úsměvné. V kódu jednoho projektu jsme našli komentáře, které jasně prozrazovaly svůj původ:
// Source: https://stackoverflow.com/questions/1234567
// answered by user xXxCoderPro2003xXx
// This might not work for all cases but it works for me
function parseDate(str) {
return new Date(str.split('-').reverse().join('/'));
}
Samozřejmě to nefungovalo pro všechny případy. Datum "31-02-2024" prošlo bez chyby a vytvořilo 3. března. Ale hlavně — celý projekt byl takto poskládaný z kopírovaných odpovědí ze Stack Overflow. Bez pochopení, co kód dělá. Bez úpravy pro konkrétní kontext. Někdy dokonce včetně komentářů odpovídajícího, kde vysvětloval, proč je to jen rychlé řešení a ne produkční kód.
- ✗Hesla v plaintextu v databázi
- ✗Funkce doStuff() s 5 000 řádky
- ✗Přímá konkatenace SQL dotazů
- ✗Verze kódu posílané zipem přes e-mail
- ✗2 000× !important v jednom CSS souboru
- ✓Hashovaná a solená hesla (bcrypt/argon2)
- ✓Malé, specializované funkce (do 50 řádků)
- ✓Prepared statements pro SQL dotazy
- ✓Verzování kódu v Gitu s code review
- ✓CSS metodologie (BEM, utility classes)
Proč se to děje
Většinou za těmito hrůzami není zlý úmysl. Je za tím kombinace faktorů: nízký rozpočet ("udělejte to co nejlevněji"), časový tlak ("potřebujeme to včera"), nezkušení vývojáři, chybějící code review a postupná eroze kvality, kdy každá zkratka vytváří prostor pro další zkratku.
Technický dluh funguje přesně jako finanční dluh. Malý dluh je normální a zvládnutelný. Ale pokud splátky ignorujete, úroky rostou exponenciálně. A jednoho dne zjistíte, že místo přidávání nových funkcí trávíte veškerý čas hasením požárů ve starém kódu.
Poučení na závěr
Za každým legacy projektem je příběh. Příběh o tlaku, omezeních a kompromisech. Neodsuzujeme — víme, že v reálném světě ne vždy existuje prostor pro dokonalé řešení.
Morál příběhu: Technický dluh je skutečný dluh. Můžete ho ignorovat, ale nezmizí. Naopak — roste. A jednou ho budete muset splatit, obvykle s úroky. Pokud stavíte produkt, investujte do kvality kódu od začátku. Je to levnější než pozdější záchranná operace. Věřte nám — my ty záchranné operace děláme a víme, kolik stojí.
Přebíráte legacy projekt? Dělejte audit systematicky: 1) Bezpečnost — hesla, SQL injection, autorizace. 2) Architektura — velikost souborů, závislosti, duplicity. 3) Infrastruktura — verzování, CI/CD, prostředí. 4) Dokumentace — je vůbec nějaká? Nezkoušejte opravit všechno najednou. Prioritizujte podle rizika a opravujte postupně s každým novým úkolem.


