Zpět na blog
ai methodology engineering

Vibe Coding vs. Vibe Engineering

Rozdíl není v tom, jak rychle píšete. Ale jak tvrdě donutíte AI přemýšlet. Tady je metodologie za stavbou Kvalty.cz.

Když lidi slyší „programování s AI”, většina si představí jednoduchý proces: napíšeš prompt, AI vyplivne kód, hotovo.

Na jednoduchý skript to možná stačí. Ale ne na projekt jako Kvalty.cz — 237 000 řádků TypeScript, 163 databázových tabulek, 3 Next.js aplikace, Hono/tRPC backend, PostGIS geografické dotazy. Tam uplatňuju něco, čemu říkám Vibe Engineering. A vypadá to úplně jinak.

Můj proces není monolog. Je to strukturovaný adversariální review.

Jak to doopravdy funguje

Fáze 1: Výzkum a strategie

Nikdy nezačínám kódem. Můj první příkaz je vždycky něco jako:

„Chci dosáhnout X. Prozkoumej aktuální dokumentaci a moderní best practices. Vytvoř detailní implementační plán.”

Ale budu konkrétní ohledně toho, jak ten „výzkum” doopravdy vypadá, protože tohle není jen ledabylý prompt.

Ten prompt obsahuje omezení. Technická omezení z existujícího codebase — které balíčky už používáme, jak vypadá databázové schéma, jaké vzory máme zavedené. Byznysová omezení — co ta funkce musí udělat pro uživatele, jaká data máme, jaká nemáme. A explicitní anti-patterny — věci, které jsem už zkoušel a nefungovaly, přístupy, které do naší architektury nezapadají.

Reálný příklad: když jsem stavěl geografické vyhledávání pro Kvalty.cz, prompt pro Fázi 1 měl zhruba 400 slov. Specifikoval, že používáme PostgreSQL s PostGIS, že autoškoly mají sloupce latitude/longitude uložené jako typy geography (ne geometry — tenhle rozdíl je klíčový pro výpočty vzdáleností na zakřivené Zemi), že vyhledávání musí podporovat dotazy podle poloměru i bounding-boxu, že výsledky musí být seřazené podle vzdálenosti, a že dotaz musí běžet pod 150ms pro jakýkoliv region v Česku.

Výstup Fáze 1 není kód. Je to strukturovaný plánovací dokument. Pro geografické vyhledávání to byl třístránkový rozbor: potřebné databázové indexy (GiST index na geography sloupci), konkrétní PostGIS funkce k použití (ST_DWithin pro poloměr, ST_MakeEnvelope pro bounding box), jak ošetřit běžný případ „hledej poblíž”, kdy uživatel sdílí geolokaci z prohlížeče, fallback chování při odmítnutí geolokace a strategie stránkování pro velké sady výsledků.

Ten plánovací dokument se stává smlouvou pro všechno, co následuje.

Fáze 2: Roast

Tady to začíná být zajímavé. Neberu ten plán a nezačnu kódovat.

Vezmu ho a hodím na dalšího AI agenta s touhle instrukcí:

„Jsi senior architekt, který recenzuje návrh od juniora. Tvůj úkol je najít každou chybu. Předpokládej, že plán má minimálně 3 závažné problémy. Zkontroluj: bezpečnostní zranitelnosti, vektory SQL injection, problémy s výkonem při škálování, chybějící edge cases, mezery v error handlingu a jestli ten přístup vůbec bude fungovat s naším existujícím schématem. Buď brutální. Radši opravím problémy v plánu než v produkci.”

To „buď brutální” není dekorace. Bez explicitního povolení být kritický AI automaticky sklouzne ke zdvořilosti. Řekne „tohle vypadá skvěle, ale možná byste mohl zvážit…” místo „tohle spadne pod zátěží, protože…”

Co Roast odhalil u geografického vyhledávání:

Bezpečnostní díra. Původní plán předával uživatelem dodané souřadnice přímo do raw SQL šablony. Roast to okamžitě označil: „Tohle je vektor SQL injection. Uživatelem dodané hodnoty lat/lng musí být parametrizované, ne interpolované.” Zpětně zřejmé. Snadno přehlédnutelné, když se soustředíte na prostorovou logiku.

Výkonnostní bottleneck. Plán používal ST_Distance pro řazení výsledků podle vzdálenosti. Roast upozornil, že ST_Distance na typech geography počítá skutečnou geodetickou vzdálenost pro každý řádek v sadě výsledků, což je drahé. Oprava: použít ST_DWithin pro počáteční filtr (který využije prostorový index), a pak spočítat ST_Distance jen na filtrovaných výsledcích. Rozdíl na našem datasetu 1 700 škol: 340ms vs 85ms.

Chybějící edge case. Co se stane, když uživatel hledá s poloměrem 0 km? Původní plán to neošetřoval. V produkci by to vrátilo nula výsledků bez vysvětlení. Roast navrhl: poloměr < 1 km považovat za „hledání přesné lokace” s minimem 1 km a zobrazit uživateli zprávu vysvětlující úpravu.

Tři problémy. Všechny odchycené dřív, než existoval jediný řádek implementačního kódu.

Fáze 3: Konsenzus

Tihle agenti se hádají (pod mým vedením). Iterujeme, dokud nedojdeme ke kuloodolnému návrhu, se kterým jsem spokojený i já.

Tady je nuance: někdy se Roast agent mýlí. Může označit „problém s výkonem”, který na naší škále nehraje roli, nebo navrhnout přeinženýrované řešení problému, který je lepší vyřešit jednoduše. Moje práce ve Fázi 3 je mediovat.

U geografického vyhledávání Roast agent taky navrhl implementovat cachovací vrstvu s Redis pro často vyhledávané lokace. Zamítl jsem to. Dotaz už byl dostatečně rychlý na 85ms po opravě s ST_DWithin. Přidání Redis by znamenalo další infrastrukturní závislost, složitost cache invalidace (co se stane, když škola aktualizuje adresu?) a režii údržby. Nestojí to za to pro sub-100ms dotaz.

Roast agent taky doporučil implementovat systém prostorového dělení — rozdělit Česko do buněk mřížky a předpočítat, které školy spadají do které buňky. Technicky zajímavé. Naprosto zbytečné pro 1 700 záznamů. Možná až dosáhneme 50 000. Dnes ne.

To je ta část „konsenzu”. Nejde o slepé přijímání každého návrhu. Jde o úsudek — vědět, která vylepšení mají význam teď, která později a která jsou jen inženýrská marnost.

Teprve potom se napíše první řádek kódu.

Funkce od začátku do konce

Projdu celý flow na dalším reálném příkladu: 200bodový hodnotící algoritmus, který určuje, jak se autoškoly řadí ve výsledcích vyhledávání.

Výstup Fáze 1: Specifikace bodování. 14 faktorů, každý s váhou. Počet a průměr recenzí (max 40 bodů). Úplnost dat — má škola uvedený ceník, popis kurzů, fotky? (30 bodů). Čerstvost — kdy byla data naposledy aktualizovaná? (20 bodů). Geografické pokrytí — obsluhuje více lokalit? (15 bodů). Míra odpovědí na dotazy uživatelů (15 bodů). A několik menších signálů. Plán specifikoval přesnou strukturu SQL dotazu: materializovaný pohled, který předpočítává skóre každou noc, s manuálním triggerem pro administrátorské přepisy.

Fáze 2 (Roast) odhalila: Váhování bylo zneužitelné. Škola mohla uměle nafouknout skóre přidáním prázdných výpisů kurzů (zvýšení „úplnosti dat” bez poskytnutí reálné hodnoty). Oprava: bodování úplnosti kontroluje minimální prahy obsahu — výpis kurzu potřebuje alespoň název, cenu a popis, aby se počítal. Roast taky odhalil, že refresh materializovaného pohledu zamkne tabulku pro čtení během přepočtu. Na našem datasetu by ten zámek trval 2-3 sekundy — ne katastrofa, ale znatelné. Oprava: REFRESH MATERIALIZED VIEW CONCURRENTLY s unikátním indexem.

Konsenzus ve Fázi 3: Roast navrhl přidat „skóre důvěryhodnosti” na základě toho, jak dlouho je škola registrovaná u dopravního úřadu. Koncept se mi líbil, ale zamítl jsem implementaci — nemáme spolehlivě data registrace pro všechny školy a bodování na základě neúplných dat by nespravedlivě penalizovalo novější školy. Odloženo na později, až se zlepší pokrytí dat.

Implementace: 4 kola iterace. Kolo 1: základní bodovací logika a materializovaný pohled. Kolo 2: ošetření edge cases (školy s nula recenzemi, nově přidané školy, školy označené kvůli problémům s daty). Kolo 3: integrace do administrátorského dashboardu pro manuální úpravy skóre. Kolo 4: výkonnostní testování a optimalizace indexů.

Celkový čas od Fáze 1 do produkce: 2 dny. Funkce, která by normálně zahrnovala produktovou specifikaci, architektonický review meeting, implementační sprint a QA cyklus. Stejná důkladnost, komprimovaný časový rámec.

Realita iterací

Ne každá funkce zabere 2 dny. Jednoduché CRUD endpointy nebo UI komponenty zvládnu za 1-2 kola — plán ve Fázi 1, rychlá kontrola, implementace. Geografické vyhledávání a hodnotící algoritmus byly střední složitost — 3-4 kola každý.

Nejsložitější funkce, kterou jsem takhle stavěl, byl automatizovaný datový pipeline — scrapování, parsování, normalizace a slučování dat autoškol ze 14 různých zdrojových formátů. To zabralo 8 kol iterací přes týden. Roast pořád nacházel edge cases: co když dva zdroje mají protichůdné ceny pro stejnou školu? Co když se název školy lehce liší mezi zdroji („Autoškola Novák” vs „Autoškola Ing. Novák s.r.o.”)? Co když jeden zdroj uvádí ceny včetně DPH a druhý bez? Každé kolo zachytilo problémy, které by byly produkční bugy.

8 kol zní jako hodně. Ale každé kolo trvá minuty, ne dny. Celkový čas investovaný do adversariálního review pro tuhle funkci byl tak 3 hodiny. Bugy, kterým se tím předešlo, by zabraly týdny debugování v produkci, protože bugy v datovém pipeline jsou tiché — nevíte, že vaše data jsou špatně, dokud si někdo nestěžuje.

Nástroje a nastavení

Moje denní nastavení je jednoduché. Claude Code CLI běží v terminálu, většinou 2-3 sessions otevřené současně. Jedna session pro aktuální implementační úkol. Jedna pro Roast review. Někdy třetí pro dokumentaci nebo psaní testů.

Správa kontextu je kritická. Každá session dostane zaměřený kontext — relevantní soubory, plánovací dokument a konkrétní instrukce o její roli. Implementační session nevidí zpětnou vazbu z Roastu, dokud ji záměrně nesdílím. Roast session nevidí implementaci, dokud nesdílím plán. Toto oddělení brání agentům být na sebe „hodní” nebo se kotvit na předchozí výstup.

Pro větší funkce udržuji průběžný plánovací dokument — markdown soubor, který sleduje, co bylo rozhodnuto, co implementováno a co je stále otevřené. Funguje jako single source of truth, když se kontextová okna protahují.

Proč tohle není „zeptat se ChatGPT dvakrát”

Dostávám tenhle pushback. „Jen žádáš stejné AI, aby recenzovalo vlastní práci. To je kruhové.”

Není. A tady je proč.

Když požádáte jedno AI, aby „napsalo kód a pak ho zkontrolovalo,” review je kontaminovaný generovacím procesem. AI je předpojaté vůči vlastnímu výstupu. Napsalo ten kód, takže mu „rozumí”, a to porozumění maskuje chyby.

Vibe Engineering používá strukturální oddělení. Výzkumný agent a recenzní agent pracují s různými prompty, různými rolemi a různými motivačními rámci. Výzkumnému agentovi se říká, aby byl kreativní a důkladný. Recenznímu agentovi se říká, aby byl destruktivní a skeptický. To nejsou stejné myšlenkové módy.

A co je důležitější — já jsem třetí uzel. Nepřeposílám pasivně výstup mezi dvěma AI sessions. Hodnotím obě strany, vnáším doménovou znalost, kterou žádný agent nemá („tohle nebude fungovat, protože naše Cloud Run instance má 30sekundový timeout na request”), a dělám úsudková rozhodnutí, která vyžadují porozumění byznysovému kontextu, ne jen kódu.

Tradiční code review má člověka, který recenzuje lidský kód. Recenzent přináší zkušenosti, rozpoznávání vzorů a doménové znalosti — ale je omezený časem a pozorností. Na složitém PR stráví třeba 20 minut.

Vibe Engineering má AI recenzující AI plány, přičemž člověk dělá finální rozhodnutí. AI recenzenti jsou neúnavní, důkladní a mechanicky kontrolují každý edge case. Zachytí vektor SQL injection i race condition i chybějící null check — všechno v jednom průchodu. Lidský recenzent zachytí možná jeden ze tří, v závislosti na tom, na co zrovna dává pozor.

Kompromis: AI recenzenti přehlíží systémové problémy. Nevědí, že vaše Cloud Run instance má omezení paměti, nebo že vaši uživatelé jsou většinou na mobilu s pomalým připojením, nebo že český trh má specifické regulační požadavky na data autoškol. To přináší člověk.

Výsledek

Kvalty.cz neběží na prvním nápadu, který AI nahalucinovalo. Běží na řešení, které prošlo několika koly kritického review — jen se to odehrálo v minutách, ne týdnech.

Zatím to drží. 237 000 řádků TypeScript, které udržuji sám, systém se 163 databázovými tabulkami, který dokážu vysvětlit a s jistotou upravovat, a funkce, které jdou do produkce bez toho „prosím ať to nerozbije” modlení, které obvykle doprovází nasazení od sólo vývojáře.

Na čem doopravdy záleží

Vibe Engineering není o tom, že AI dělá práci za vás. Je o řízení, koordinaci a rozhodování, kdo má pravdu, když se dva agenti neshodnou.

Psaní syntaxe už dávno není to nejtěžší. Nejtěžší je úsudek — vědět, co stavět, vědět, kdy se AI mýlí, a vědět, kdy je „dost dobré” opravdu dost dobré versus kdy se v tom schovává problém, který najdete ve dvě ráno.

Pořád zjišťuju, že těch 10 minut strávených adversariálním review mi ušetří hodiny debugování. Ta matematika je zřejmá, jakmile to člověk zkusí.

Martin Svoboda

Martin Svoboda

Android vývojář ve Fortuně, zakladatel Kvalty.cz a Ferda App. Stavím produkty s Kotlin, React a AI-asistovaným vývojem z Prahy.