Aktualizováno 11. 9. 2026 Témata: provoz, testovani, zalohy

Nejdražší poruchy, které jsem za posledních pár měsíců řešil, nebyly ty, které spadly. Byly to ty, které vydaly kladný signál. Skript skončil nulou, endpoint vrátil 200, kontejner hlásil healthy, testy svítily zeleně. A přitom se reálně nestalo nic, nebo se stalo něco jiného. Doba mezi vznikem a odhalením se u těch případů měřila v týdnech a měsících, ne v hodinách.

Vzorec

Kladný signál není důkaz účinku. Dokud se člověk dívá na signál a ne na následek, je porucha neviditelná libovolně dlouho. Společná příčina není nepozornost, ale návyk brát poslední řádek výstupu za verdikt. Nástroje napříč stackem tomu jdou naproti: chybí kontrola návratového kódu, výjimka se mapuje na stav 200, healthcheck se ptá procesu, ne aplikace.

Pět případů z vlastního provozu

Webhook vracel 200 a tři měsíce zahazoval každou zprávu. V agentuře, kde jsem vedl vývoj, posílá git server události o review pull requestů do týmového chatu. Tři měsíce nedorazila ani jedna. Git server hlásil doručeno, reverzní proxy logovala 200, chat mlčel. Příčina: endpoint chatu ty tři typy událostí neznal a výjimku „nepodporovaný typ” mapoval na stav 200. Ztraceno 370 událostí. Měřilo se to nakonec tam, kde měl být následek: počtem řádků v tabulce zpráv, kde byla nula, a po opravě přehráním devíti reálných událostí z logu.

Záloha se 17 z 31 webů a exit 0. Měsíční záloha webů na jednom hostingovém serveru: z 31 webů se zazálohovalo 17. Čtrnáct nemělo ani prázdný soubor, protože disk byl plný. Skript ale pokračoval, na konci proběhl úklid starých záloh a skončil s nulou, protože nikdo nekontroloval návratový kód jednotlivých kroků. Zjistilo se to počítáním souborů a jejich velikostí, ne z logu běhu.

Cron, který od instalace nikdy neběžel. Skener malwaru byl na serveru nainstalovaný od začátku. Řádek v crontabu byl ale zapsaný v syntaxi systémového adresáře pro cron, takže uživatelský cron bral slovo „root” jako příkaz. Ten neexistoval, cron to zapsal do syslogu a jel dál. Log skeneru měl od instalace nula bajtů. Poznávací znamení: log s nulovou velikostí od data instalace.

Test, který si vyrobil vstup. U přehledu pro skupinu e-shopů klasifikoval kód konflikt v databázi podle pole, které vrací ORM. Tři testy zelené, všechny tři si ale to pole samy sestavily. Proti skutečnému Postgresu bylo pole prázdné, takže by každý konflikt spadl do obecné hlášky. Horší než kód, který to nahrazovalo. Odhalilo se to jediným možným způsobem: pustit to proti živé databázi a vypsat, co doopravdy přijde. Když si test staví vstup, který normálně vyrábí cizí vrstva, netestuje chování. Testuje můj odhad, jak ta vrstva vypadá.

Import, který hlásil „Vytvořeno: 0”. V dispečerském CRM pro montážní firmu stínicí techniky importuje tlačítko obchodníky z externího systému. Uživatelka na straně klienta ho zmáčkla třikrát a pokaždé dostala zelené „Vytvořeno: 0”. Import ale četl jen noční cache a živého systému se nezeptal, takže člověk přidaný během dne v ní nebyl. Z obrazovky to nešlo poznat: nula změn vypadá stejně jako „všechno už je naimportované”. Měření na druhém konci: 17 lidí v cache proti 20 v živém API. Oprava: import si obnoví cache sám a nulový výsledek je od té doby varování, ne úspěch.

Signál, následek, měření

SignálSkutečný následekJak se měří
HTTP 200 na webhookřádek v cílové databázipočet zpráv v tabulce, přehrání reálných událostí
exit 0 zálohovacího skriptusoubor pro každý webpočet a velikost archivů
cron „nasazený”log s nenulovou velikostívelikost logu od instalace, syslog
zelená sada testůčervená po rozbití kódumutace zdrojáku, běh proti živé databázi
„vytvořeno: N” u importustav dat po běhupočet záznamů v cíli proti zdroji
kontejner healthyodpověď aplikacelog kontejneru, hodnoty uvnitř běžícího procesu

Jak se bráním

  1. Ověřuju reálným průchodem, ne stavovým hlášením. Oprava webhooku se ověřovala přehráním devíti skutečných událostí, ne tím, že endpoint vrátil 200.
  2. Po zápisu čtu zpátky, a jiným nástrojem, než který mohl sám lhát.
  3. Měřím na druhém konci: řádek v databázi, bajty v logu, proměnné uvnitř běžícího kontejneru. Ne „Up”, ne „success”.
  4. Zelený test ověřuju mutací. Když po rozbití kódu nezčervená, tvrdí pokrytí, které tam není.
  5. Ve vlastních skriptech kontroluju návratový kód každého kroku a alertuju z logu selhání, ne z toho, že skript doběhl.
  6. U každé nové integrace nebo brány si pojmenuju, kde se dá naměřit následek, a jednou ho ověřím reálným průchodem.

Co si z toho odnést

Zelená není důkaz, je to tvrzení. Systém, který hlásí úspěch, může lhát stejně snadno jako ten, který hlásí chybu, jen si toho nikdo nevšimne. Pro každou automatizaci proto potřebujete vědět, kde se dá změřit její následek, a aspoň jednou se tam podívat. Kdo měří jen signál, dozví se o poruše, až když ji někdo zaplatí.

Další poznámka

141 460 dokladů do haléře

Přehled pro skupinu 17 e-shopů měl odpovědět na otázku „kolik jsme vydělali". Co všechno nesedělo, než čísla seděla, a proč se ověřuje proti API.

Napište mi, co potřebujete

Napište mi