Dokumentace je něco, co správný programátor čte až ve chvíli, kdy všechno selže, a k jejímu vytváření se staví úplně stejně: s odporem. Já vím, slyšel jsem o firmách a týmech, kde prý mají skvělou dokumentaci. Vy jste o nich slyšeli taky. Ale kolik jich je, co?
S příchodem agentů se všechno zjednodušilo: přece se podívá do kódu a přijde na to, jak to je, takže se bez dokumentace obejde, jelikož je kód tak dobře okomentovaný, že je sám dokumentací. Cha chaaaa, vy blázni!
Nedávno mi Claude Code sebevědomě navrhl použít Redis jako cache pro jednu věc. Našel v kódu někde zmínku o Redisu, tak ho tam vrznul. Jenže Redis jsem před půl rokem zavrhnul. Já to vím, protože jsem to rozhodnutí dělal a pamatuju se, proč to bylo, ale za rok si to nevzpomenu, nový kolega to vědět nebude, a Claude už vůbec. Halucinuje s plnou sebedůvěrou nad starým rozhodnutím a je mu celkem fuk, že je dokumentace, protože ji stejně nikdo nečte. Ani agent, ani já. Je to přeci v kódu!
Jenže už mě otravuje psát všechny tyhle věci do nějakých “docs/*” a říkat mu “Přečti si to”. Protože vždycky narazí na něco, co by udělal jinak. Stačí jednou zapomenout, a už básní a kreativně tvoří.
Zkoušel jsem všechny možné “perzistentní paměti” a “ultimátní paměti” a “grafová paměť pro váš Claude Code”, než mi došlo, že tudy cesta nepovede. Stejně jako vám OpenClaw nebude psát kód, i když by mohl, tak vám zázračná “někdo právě vydal toto a to je revoluce v agentic codingu” paměť taky nepomůže, protože je jen tak dobrá, jak dobrý je ten, kdo do ní zapisuje, a protože to je “coding agent”, tak je výsledek očekávatelný.
Confluence, Notion a další pohřbené naděje
Samozřejmě že jsem si prošel všemi obvyklými nástroji. Confluence jsme používali ve dvou firmách, Notion v jedné, a vzpomínky mám u obou stejné, jen v jiných barvách.
Zaprvé hodně záleželo na tom, kdo konkrétní stránku v dokumentaci napsal. Někdo má rád strukturu, takže má nadpisy, odrážky a tabulky. Někdo má rád prózu, tak píše souvislý text na patnáct odstavců. Někdo má rád úsporný styl, tak hodí pět vět a odkaz do jiné stránky. Hledat v tom konzistenci bylo marné, a to nemluvě o lidech, co napsali titulek “Mongo poznámky” a obsah, cituji: “TODO”.
Zadruhé: nikdo to neaktualizoval. Stránka vznikla v okamžiku, kdy se něco rozhodovalo nebo instalovalo, a od té chvíle žila vlastním životem, čím dál vzdálenějším od reality. Vlastně se z ní okamžitě stal muzejní exponát. Když přišla revize, ukázalo se, že polovina informací neplatí, ale nikdo neměl chuť to celé projít a aktualizovat, takže stránka tiše stárla, aniž by se o ni kdokoli zajímal. Ale naštěstí ji nikdo nečetl, tak to bylo jedno.
Zatřetí: psalo se to “někdy”, což ve většině případů znamenalo “nikdy”. Vývojář dořeší problém, je vyždímaný, kontext mu odtéká, a sednout si ke Confluence a sepsat to je ta úplně poslední věc, kterou chce dělat. Místo toho jde na oběd nebo na další ticket, a to známé “pak to napíšu” se nikdy nestane.
Navíc v té jedné struktuře byly utopené úplně různé věci. Specifikace API, postupy instalace produkčních serverů, návod jak hlásit bug, marketingové texty, zápisy z porady, koncepty, které jsme dávno nedělali. Člověk na ně občas narazil, a to, že jsou zdechlé, zjistil jen když si náhodou všimnul, že stránka je osm let stará (znáte to… takové malé číslíčko kdesi v záhlaví nebo v zápatí). Hledat v tom bylo za trest, často bylo rychlejší zeptat se na StackOverflow nebo si to vyhledat ve zdrojácích, než to dolovat z Confluence.
Takže klasika: Dokumentaci nikdo nečte. Z toho plyne, že nemá smysl psát něco, co nikdo nečte. A proč by někdo četl něco, kam nikdo nic nepíše a kde nic nenajde? A nakonec je Confluence hlavně úložiště zápisů z porad, které po týdnu nikdo neotevře (pokud není potřeba se za pět měsíců dohadovat, kdo tu blbost vymyslel a rozhodnul).
S agentama se problém ještě zhoršil. Najednou nečte dokumentaci jen člověk, ale i agent, a agent na rozdíl od člověka neumí říct “hele, tohle je osm let staré, asi to neplatí”. Prostě tomu věří jak naivní junior v prvním zaměstnání a začne s tím pracovat. Vyhalucinuje na základě nesmyslu jiný nesmysl, který ale vypadá užitečně, ovšem jen do chvíle, než...
A co MCP v Confluence a Notionu?
Jo. Atlassian má od léta 2025 oficiální Rovo MCP server (od února 2026 GA), Notion má oficiální hosted MCP server od dubna 2025. Funguje to, Claude se tam dostane, najde stránky, vytvoří nové.
Problém je jinde. To “MCP” řeší přístup, ale neřeší obsah. Claude přes Rovo MCP najde stránku o cache, jenže ta stránka je pořád stejný plochý wiki text. Žádný strojově čitelný typ “tohle je rozhodnutí, ne howto”, žádné semantické vazby “tahle stránka nahrazuje tamtu”, žádné vynucené pole “co bylo zvažováno a zamítnuto”. MCP ukáže to, co tam je. A to, co tam je, je pořád ten samý plochý wiki text, co tam byl vždycky, protože - viz výš.
Notion to má stejně, jen zabalené v posh UI a s pěkným flow. Page a database model je univerzální, což znamená, že strukturu si stejně musím vymyslet sám pomocí properties a templates. A ani potom nemám server, který by zápis odmítl, když poruším schéma. Notion je tolerantní, takže když se agent odchýlí od štábní kultury, tak mu to nehodí na hlavu, odchylky se začnou kumulovat, ...
Vector RAG, další obvyklý kandidát z nadšených tweetů, je na tohle obzvlášť slepý. Najde podobné texty, ale netuší, že dokument, který vrátil, je sice přesný, ale většina toho už neplatí, protože byla změněna v méně podobných dokumentech. Vrací odpovědi, které jsou historicky přesné a aktuálně špatné, což je nejhorší kategorie chyb, protože vypadají správně.
Seděl jsem a dumal: potřebuju dokumentaci, která slouží lidem i agentům. To je něco, co kombinuje různé vlastnosti najednou. Plain text v gitu kvůli verzování. Strojově čitelnou strukturu kvůli agentům a kvůli tomu, aby všichni psali aspoň přibližně podobně. Vazby mezi dokumenty kvůli životnímu cyklu obsahu. A nějakou formu pomoci se zápisem, protože jinak to nikdo nebude psát.
Dokumentace má smysl, když je živá. Živá je tehdy, když se do ní zapisují nové věci a aktualizují staré. Je to práce, kterou nikdo nechce dělat. Může ji dělat stroj? Může, ale musí ji dělat správně! Musí mít striktně dané meze a pravidla.
Tři vrstvy čtení
První věc, kterou jsem řešil, byl samotný formát dokumentů. Agent má omezený kontext, a když má každý dokument 2 kB, dvacet jich ucpe celé okno. Reálně to znamená, že buď čte málo a přehlédne věci, nebo čte hodně a platíte tokeny za balast.
Vyřešil jsem to tak, že každý dokument má tři vrstvy. Vrstva L1 je jedna věta, maximálně 200 znaků. Něco jako “Rozhodnutí použít Redis jako cache vrstvu mezi Content API a MongoDB”. Vrstva L2 je strukturovaný výtah o délce 200 až 1500 znaků, šablona se liší podle typu dokumentu. Pro rozhodnutí je to chronologie variant V1/V2/V3, pro runbook to jsou sekce kdy spustit, kroky a eskalace, pro konfiguraci je to zas něco jiného. L3 je plný text dokumentu, ale otevírá se až na výslovnou žádost.
Agent si v dokumentaci vyžádá jen L1 napříč všemi výsledky. V jednom volání projede třeba dvacet L1 a rozhodne, který dokument číst dál. Pro vybrané si pak řekne o L2. L3 otevírá jen když opravdu potřebuje detail. Spotřeba tokenů reálně klesne o řád proti stavu, kdy čte plné dokumenty.
Stejnou strukturu používají i lidi. Když si v Web UI otevřu detail dokumentu, vidím L1 jako velký nadpis, L2 jako strukturovaný blok, L3 jako tělo. Agentovi i mně to vyhovuje.
Celý dokument vypadá třeba takhle:
---
id: rozhodnuti-pouzit-redis-jako-cache-content-api-7a3f
type: decision
status: active
title: Cache strategie pro Content API
chosen: [redis]
tools: [redis, mongo, nodejs]
projects: [content-api]
l1: "Rozhodnutí použít Redis jako cache vrstvu mezi Content API a MongoDB."
l2: |
V1: Zvažovali jsme in-memory cache v Node procesu, zamítnuto kvůli více
instancím za nginx (nekonzistentní stav po publish).
V2: Přijat Redis s lazy invalidací při publikaci článku přes webhook z CMS.
V3 (otevřené): Zvážit Redis Cluster až při překročení 50 GB dat.
---
[L3 následuje jako tělo dokumentu, několik kilobytů konkrétních detailů]Mít tři vrstvy znamená, že je někdo musí napsat. To není tak jednoduché, jak to zní. Vrátím se k tomu.
Právě tyhle “vrstvy dokumentu” mě inspirovaly, když jsem to celé pojmenoval Strata.
Devět typů dokumentů
Druhá věc, která zásadně zjednodušuje práci agentovi, jsou typy dokumentů. Confluencový model “všechno je page s tagy” je pro agenta šum. Když agent řeší pád MongoDB v produkci ve tři ráno, chce runbook, ne blog post o agregátech v Mongu. Jiný typ obsahu, jiná role, jiná pravidla. Jo, máte tagy, ale nemáte jejich hierarchii.
Strata má devět typů. Decision je volba s důvody a alternativami, typicky “Mongo místo Postgresu”. Spec je formální popis, třeba API spec nebo datový model. Config je faktický záznam stavu konkrétní instance, něco jako “Mongo na hetzner-1, port 27017, replica setup”. Howto je edukační postup “Jak napsat agregát v Mongu”. Runbook je krizový scénář pro situaci typu “Co dělat, když Mongo přestane odpovídat”. Tool je záznam o nástroji, jeho licenci, přístupech, verzích atd. Reference je anotovaný odkaz ven, třeba na DeepWiki k Mongoose s poznámkou, co odtud používáme. Glossary je definice domácího pojmu.
Devátý typ je source a stojí za zvláštní zmínku. Je to typ pro surovinu, ze které se vytěžují ostatní dokumenty. Má zatím dva podtypy. První je conversation, záznam Claude Code session, Claude Chat session, Codex session nebo třeba nahrávky z porady. Konverzace, kde padla rozhodnutí, ale ještě nebyla extrahovaná do strukturovaných dokumentů. Druhý je bash_history, výstup příkazu history, dokumentace skutečných instalačních postupů. Když na server instaluju MongoDB, výsledný mongod.conf zachytí, co jsem nakonec měl, ale ne, jak jsem se k tomu dostal. Které balíčky jsem nainstaloval, jaké proměnné jsem nastavil, kde jsem narazil, co jsem musel přeinstalovat znovu, jinak, kde jsem dával nestandardní parametry, protože to prostředí má jinak a já 10 minut hledal, jak tu naši konkrétní situaci vyřešit. Bash_history je archeologický záznam reality, který se vytěží do howto (návod pro příště) a config (cílový stav). Bez něj bych rekonstrukci postupu dělal z paměti, což je peklo, protože sice máte Ansible, ale zrovna na tohle jste si ho kvůli (doplňte si libovolný důvod) nedělali.
Typ neříká, o jaké části systému dokument je, to řeší tagy tools a projects. Typ říká, jakou roli dokument hraje, co od něj agent čeká, jaká pravidla ho svazují. Decision musí mít status (active/superseded/rejected), config má stav (current/outdated), runbook má sekci “Kdy spustit”. Když agent ví typ, ví, co kde hledat, a hlavně na druhé straně může samotný dokumentační server vynutit šablonu L2 a sadu povinných polí podle typu dokumentu.
Ano, vynutit. Pokud nevynutíte, tak LLM v coding agentech básní…
Vazby a omezení (protože bez nich to nemá smysl)
Tady přichází jádro celého systému. Bez vazeb a omezení je dokumentace plochá a agent v ní halucinuje.
Vazby
Strata má sedm typů vazeb mezi dokumenty. Supersedes říká, že tento dokument nahrazuje jiný (typicky decision nahrazuje jiné decision). Implements říká, že tento config nebo howto realizuje rozhodnutí. Depends_on je technická závislost. References je měkká zmínka. Contradicts je explicitní rozpor, živý spor v týmu, který je vědomý a zdokumentovaný, třeba vědomá výjimka. Triggered_by říká, že bez něčeho by dokument neexistoval, typicky runbook vznikl kvůli incidentu. Extracted_from říká, že tento dokument byl vytěžen ze surového zdroje (source).
K tomu jsou ještě odvozené párové vazby (superseded_by, extracted_to), které si server dopočítává sám.
Konkrétní příklad. Mám dvě propojená rozhodnutí, kde jedno pozměňuje to druhé:
# decision-redis-cache-2024.md
---
id: decision-redis-cache-2024-7a3f
type: decision
status: superseded # automaticky doplněno serverem
title: Cache strategie pro Content API
chosen: [redis]
links:
- to: decision-cache-elevenlabs-2025-c9d2
rel: superseded_by # automaticky doplněno serverem
---
# decision-cache-elevenlabs-2025.md
---
id: decision-cache-elevenlabs-2025-c9d2
type: decision
status: active
title: Přechod na in-memory cache na úrovni aplikace
chosen: [in-process-cache]
considered:
- tool: redis
reason_short: "Síťová latence dominovala oproti samotné CMS query."
links:
- to: decision-redis-cache-2024-7a3f
rel: supersedes
---A teď agent dostane otázku “jak cachujeme v Content API”. Najde fulltextem decision-redis-cache-2024. Bez vazeb odpoví “používáte Redis”, což je historicky pravdivé a aktuálně špatné. S vazbami vidí superseded_by, otevře nástupce a odpoví něco ve smyslu “původně Redis, ale rozhodnutí bylo přehodnoceno, aktuální stav je in-process cache, důvod přechodu byla síťová latence dominující oproti samotné CMS query”. Stejný fulltext, jiná odpověď. Rozdíl dělá právě ten graf vazeb.
Server vazby vynucuje atomicky. Zápis nového rozhodnutí se supersedes: X v jedné transakci vytvoří nový dokument, překlopí X z active do superseded, doplní X zpětnou hranu superseded_by, aktualizuje databázi a udělá jeden git commit. Žádný drift mezi soubory. Cílové ID musí existovat, odkazy do prázdna server odmítne.
Omezení
Druhá vrstva ochrany samotné dokumentace je, že server nedůvěřuje agentovi. Volný text na vstupu je v pořádku, ale schéma se vynucuje v operaci zápisu, ne v promptu agenta. Schéma v promptu agent klidně ignoruje, sám to dobře vím. Schéma vynucené serverem ne, protože server je tvrdohlavější než LLM.
Pole tools a projects musí být ze slovníku. Agent nemůže napsat “MongoDB”, když máme “mongo”, dostane chybu:
{
"ok": false,
"error_code": "unknown_tool",
"field": "tools",
"message": "Unknown tool 'mongodb'.",
"suggestion": "Did you mean 'mongo'?",
"valid_values": ["mongo", "redis", "nodejs", "..."]
}Slovníky ale musí organicky růst. Rozhodnutí se zapsaným chosen: [svelte] automaticky přidá svelte do tools.yaml ve stejném commitu. Synonyma do nich neproniknou, server slugifikuje vstup, takže “Vue.js” se uloží jako “vuejs”.
L1 musí začínat povoleným podstatným jménem podle typu (Rozhodnutí, Volba, Změna pro decision; Konfigurace, Nastavení pro config a tak dál) a musí obsahovat aspoň jednu klíčovou entitu z tools nebo projects. Vynucuje to LLM normalizátor na serveru. Agent posílá draft, server ho přepíše do předepsané podoby. Když normalizátor selže, třeba protože L3 nezmiňuje žádnou klíčovou entitu, zápis se odmítne. Žádný fallback, raději nic, než nestandardní zápis.
Pole chosen a considered u rozhodnutí je dobrý nápad (a jsem na něj přiměřeně hrdý). Decision musí mít chosen (co bylo vybráno) a může mít considered, což je seznam zvažovaných a zamítnutých variant s důvodem do 120 znaků. Když za půl roku přijde nový vývojář s nápadem použít Vue místo Svelte, server mu rovnou ukáže historický důvod (přes doc_search filtr considered_tool). Informace, která se obvykle ztrácí v textu nebo v hlavách, je tady strukturovaná a dotazovatelná.
Pointa omezení je, že běžně jsou systémy (Confluence, Notion) tolerantní k vstupu. Akceptují cokoli, page může mít title, nemusí mít title, může mít typ, nemusí mít typ. Strata tolerantní není. Když agent porušuje schéma, server zápis odmítne s konkrétní chybou a suggestion. Agent se musí opravit a udělat to podle pravidel, aby dokumentace zůstala konzistentní.
Dvě rozhraní
Server vystavuje pět standardních nástrojů přes Model Context Protocol. CRUD + index.
Claude Code, Codex i Claude Chat tyhle nástroje volají přirozeně. V CLAUDE.md u projektu je instrukce ve smyslu “před řešením problému ověř existující dokumentaci, při netriviálním rozhodnutí zapiš vše podstatné zpět do dokumentace”. Tahle instrukce sama o sobě by ovšem nestačila.
Vývojář (já v tomhle případě) by v praxi na nějaké zapisování každého rozhodnutí nebo poznámky časem kašlal, pokud by to znamenalo něco formulovat nebo vymýšlet nebo zpětně hledat... Proto vznikl skill make-doc.

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.