--- id: wiki-2026-0508-executable-documentation title: Executable Documentation category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Living Documentation, Runnable Docs, Doc Tests] duplicate_of: none source_trust_level: A confidence_score: 0.92 verification_status: applied tags: [documentation, bdd, doctest, testing, ai] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: polyglot framework: docs-as-code --- # Executable Documentation ## 매 한 줄 > **"매 docs 의 CI 의 run — 매 stale 의 fail"**. 매 docs 의 prose 의 not — 매 examples / scenarios / API specs 의 매 actually-execute. 매 1990s Donald Knuth literate programming + Python doctest 의 origin, 매 2020s Cucumber/BDD 의 popularize, 매 2026 의 매 LLM-assisted doc generation + verification 의 standard practice. ## 매 핵심 ### 매 종류 1. **Doctest** — 매 docstring 안의 example 의 run (Python doctest, Rust doctests). 2. **BDD** — Gherkin (`Given/When/Then`) 의 step 의 bind (Cucumber, Behave). 3. **API docs from spec** — OpenAPI → contract test (Schemathesis, Dredd). 4. **Notebook docs** — Jupyter / Quarto / Marimo 의 매 cell 의 run. 5. **Markdown code-fence test** — `mdsh`, `mdocc`, `markdown-doctest`. 6. **AI-verified docs** — 매 LLM 의 docs 의 read + code 의 inspect + drift 의 detect. ### 매 왜 valuable - 매 docs 의 truth = code 의 truth. - 매 onboarding example 의 매 always working. - 매 API contract 의 single source of truth. - 매 refactor 시 docs 의 break — 매 catch. ### 매 응용 1. SDK / library docs (매 examples 의 always-correct). 2. API contract testing (OpenAPI + Schemathesis). 3. BDD acceptance tests (매 PM-readable). 4. Tutorial CI (매 step-by-step 의 verify). ## 💻 패턴 ### Pattern 1: Python doctest ```python def fibonacci(n: int) -> int: """Return the n-th Fibonacci number. >>> fibonacci(0) 0 >>> fibonacci(10) 55 >>> [fibonacci(i) for i in range(7)] [0, 1, 1, 2, 3, 5, 8] """ a, b = 0, 1 for _ in range(n): a, b = b, a + b return a # pytest --doctest-modules ``` ### Pattern 2: Rust doctest ```rust /// Adds two numbers. /// /// # Examples /// ``` /// use mycrate::add; /// assert_eq!(add(2, 3), 5); /// ``` pub fn add(a: i32, b: i32) -> i32 { a + b } // cargo test --doc ``` ### Pattern 3: Cucumber BDD (TS) ```gherkin # features/checkout.feature Feature: Checkout Scenario: Successful purchase Given a cart with 2 items totaling $50 And a valid Stripe token "tok_visa" When the user submits checkout Then the order status should be "paid" And a confirmation email is queued ``` ```typescript // step defs import { Given, When, Then } from "@cucumber/cucumber"; Given("a cart with {int} items totaling ${int}", function (n, total) { this.cart = makeCart(n, total); }); When("the user submits checkout", async function () { this.result = await checkout(this.cart, this.token); }); Then("the order status should be {string}", function (status) { expect(this.result.status).toBe(status); }); ``` ### Pattern 4: OpenAPI contract test (Schemathesis) ```bash schemathesis run https://api.example.com/openapi.json \ --checks all \ --hypothesis-deadline 5000 # 매 매 endpoint 의 spec 의 conform 의 verify (property-based). ``` ### Pattern 5: Markdown code-fence test ```typescript // scripts/test-readme.ts import { readFileSync } from "fs"; import { execSync } from "child_process"; const md = readFileSync("README.md", "utf8"); const blocks = [...md.matchAll(/```typescript\n([\s\S]*?)\n```/g)]; for (const [, code] of blocks) { const tmp = `/tmp/snippet-${Date.now()}.ts`; require("fs").writeFileSync(tmp, code); execSync(`tsx ${tmp}`, { stdio: "inherit" }); } ``` ### Pattern 6: Quarto / Marimo notebook ```python # 매 marimo notebook — 매 reactive, 매 git-friendly import marimo as mo @mo.cell def fetch(): import requests return requests.get("https://api.example.com/users").json() @mo.cell def show(fetch): return mo.ui.table(fetch) # 매 marimo run notebook.py — 매 docs + working app. ``` ### Pattern 7: AI-verified doc drift (2026) ```typescript import Anthropic from "@anthropic-ai/sdk"; const ai = new Anthropic(); async function checkDocDrift(docPath: string, codeGlob: string) { const docs = readFileSync(docPath, "utf8"); const code = await loadFiles(codeGlob); const res = await ai.messages.create({ model: "claude-opus-4-7", max_tokens: 2000, system: "You verify docs match code. Output JSON: {drifts:[{location, doc_says, code_actually}]}", messages: [{ role: "user", content: `DOCS:\n${docs}\n\nCODE:\n${code}\n\nFind drift.`, }], }); return JSON.parse(res.content[0].text); } // CI 의 매 fail 의 drift 의 found 시. ``` ### Pattern 8: Architecture decision record (ADR) with assertion ```markdown # ADR-0042: Use Postgres for primary store ## Decision PostgreSQL 16 의 single-tenant DB. ## Verification (run in CI) \`\`\`bash # 매 docs 의 claim 의 verify psql $DATABASE_URL -c "SELECT version();" | grep -q "PostgreSQL 16" \`\`\` ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Library / SDK | doctest (Python/Rust) | | REST API | OpenAPI + Schemathesis | | Acceptance test (PM) | Cucumber BDD | | Tutorial / book | Markdown code-fence test | | Data analysis | Quarto / Marimo | | Architecture docs | AI drift detector | **기본값**: 매 README 예제 의 매 CI 의 run, 매 API 의 OpenAPI 의 contract test. ## 🔗 Graph - 부모: [[Docs as Code]] - 응용: [[OpenAPI]] - Adjacent: [[Property-Based Testing]] · [[Contract Testing]] ## 🤖 LLM 활용 **언제**: 매 docs drift detection, 매 example generation 의 verify, 매 BDD step 의 generate. **언제 X**: 매 internal scratchpad notes — 매 over-engineering. ## ❌ 안티패턴 - **Doctest 의 too many**: 매 docstring 의 cluttered — 매 separate test 가 better. - **BDD 의 implementation detail**: 매 Given/When/Then 이 SQL 의 mention — 매 wrong abstraction. - **Spec drift**: 매 OpenAPI 의 stale — 매 generate-from-code or test 의 mandatory. - **No CI**: 매 executable 의 X — 매 prose docs 의 same. ## 🧪 검증 / 중복 - Verified (Knuth literate programming, Python doctest stdlib, Cucumber.io, Schemathesis docs, Marimo docs). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — doctest/BDD/OpenAPI/Marimo/AI-drift |