Co se stane, když tým přestane větvit a začne rebasovat
Většina týmů se zasekne na tom, že každý člen pracuje přímo v hlavní větvi. Výsledkem jsou konflikty, přepsaná práce kolegy a hodiny strávené nad hledáním, kdo co rozbil. Řešení není složité: zaveďte krátké větve pro každou změnu a hlavní větev držte vždy funkční. Větev by měla žít maximálně dva dny. Čím déle ji držíte otevřenou, tím větší je riziko, že se vzdálí od zbytku kódu a sloučení bude bolet.
Před podpisem smlouvy si zjisti, jak vypadá běžný den. Na pohovoru se ptej, kdo bude tvůj mentor, jak často probíhá code review a jestli se testy píšou před nebo po nasazení. Slušná odpověď zní konkrétně: jméno, frekvence, nástroj. Vyhýbavé „nějak to řešíme" znamená, že to neřeší. Stejně důležité je zjistit, jestli tým používá verzovací systém a jak řeší konflikty větví. Pokud ne, budeš trávit čas ručním kopírováním souborů.
Největší problém nastává u dynamických částí dotazu, které nelze parametrizovat: názvy tabulek, sloupců, směr řazení nebo klauzule LIMIT. Tady parametrizace nefunguje a je potřeba použít whitelist. Uživatel nesmí poslat název sloupce přímo. Aplikace má mít předem daný seznam povolených hodnot a z něj vybrat. Totéž platí pro směr řazení: hodnota musí být buď vzestupně, nebo sestupně, nic jiného se nepřijímá. Pokud se whitelist vynechá a nahradí se kontrolou pomocí regulárního výrazu, útočník často najde cestu, jak filtr obejít.
SQL injection vzniká ve chvíli, kdy aplikace vloží vstup od uživatele přímo do databázového dotazu. Útočník pak místo očekávané hodnoty pošle fragment SQL a databáze ho vykoná. Nejde o teoretickou hrozbu: stačí jeden neošetřený parametr v přihlašovacím formuláři nebo ve filtru vyhledávání a útočník může číst cizí data, měnit je nebo je smazat. Ochrana nespočívá v tajném schématu tabulek ani v komplikovaných názvech sloupců. Spočívá v tom, že se vstup nikdy nestane součástí dotazu jako kód.
Pyramida se časem vyvíjí. U mikroservis nebo složitých frontendových aplikací se někdy mluví o testovacím diamantu – více integračních testů než unit testů. Není to zrada, ale reakce na to, že izolované jednotky neodhalí chyby v komunikaci. Klíčové je měřit, jak dlouho testy běží, jak často padají z nesouvisejících důvodů a kolik jich je potřeba opravit po každé změně. Pokud testovací sada roste rychleji než produkt, něco je špatně. Pravidelně ji procházejte a mažte testy, které nic neověřují nebo jen kopírují jiné.
Nakonec sledujte statistiky a cache. Zastaralé statistiky vedou k odhadům, které neodpovídají realitě, a optimizér zvolí špatný plán. Pravidelně spouštějte aktualizaci statistik. Pokud se dotaz opakuje, databáze si ho může naplánovat znovu – pomoci může příprava dotazu (prepared statement) nebo plánovací rady. Bez měření a porozumění plánu ale žádná magie nefunguje.
Prvním konkrétním krokem je omezení množství vracených dat. Místo SELECT * vyjmenujte jen potřebné sloupce. Tím snížíte režii na přenos a často i nutnost čtení z disku. Zároveň filtrujte co nejdříve – klauzule WHERE má být co nejkonkrétnější. Pokud dotaz vrací tisíce řádků a aplikace jich zobrazí dvacet, použijte LIMIT a stránkování. Pozor na OFFSET u velkých čísel – ten nutí databázi projít všechny předchozí řádky.
Základem je parametrizace dotazů neboli prepared statements. Místo skládání řetězce s hodnotami se do dotazu vloží zástupné symboly a hodnoty se předají zvlášť. Tím se vstup vždy bere jako data, ne jako SQL příkaz. Platí to pro všechny jazyky a knihovny, které to podporují. Pokud někde prepared statements nejsou dostupné, je nutné hodnoty ošetřit ručně a velmi pečlivě, ale to je nouzové řešení, ne standard.
Integrační vrstva odhalí to, co unit testy nikdy nemohou Integrační testy ověřují spolupráci mezi komponentami – například že se data správně uloží do databáze, že API vrací správný formát nebo že se zpráva odešle do fronty. Na rozdíl od unit testů mohou používat reálné závislosti, ale v izolovaném prostředí (testovací databáze, kontejnery). Běžně jich stačí 15–20 %. Pozor na dvě věci: první je sdílený stav mezi testy. Pokud testy běží paralelně a sahají na stejná data, budou jeden druhému přepisovat výsledky. Druhá je přílišná složitost – integrační test nemá simulovat celý svět, ale jednu konkrétní hranici.
Prevence nekončí u kódu. Do vývojového procesu patří kontrola závislostí, protože zranitelnosti přicházejí i z knihoven a frameworků. Pomáhá jednotný přístup k databázovým dotazům v celém projektu, aby se nezaváděly výjimky. A v neposlední řadě testování: zkuste do vstupů poslat uvozovky, komentáře a logické výrazy a sledujte, co aplikace udělá. Když se chová jinak než s běžným textem, máte co řešit.