
Qa Docs Coverage
- 137 installs
- 73 repo stars
- Updated July 13, 2026
- vasilyu1983/ai-agents-public
Helps with ai & agent building tasks.
About
qa-docs-coverage is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- qa-docs-coverage
- AI & Agent Building
- AI-coding skill
Qa Docs Coverage by the numbers
- 137 all-time installs (skills.sh)
- +9 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #3,568 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/vasilyu1983/ai-agents-public --skill qa-docs-coverageAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 137 |
|---|---|
| repo stars | ★ 73 |
| Last updated | July 13, 2026 |
| Repository | vasilyu1983/ai-agents-public ↗ |
What it does
Helps with ai & agent building tasks.
Files
QA Docs Coverage (Jan 2026) - Discovery, Freshness, and Runbook Quality
Modern Best Practices (January 2026)
- Docs as QA: Treat docs as production artifacts with owners, review cadence, and CI quality gates (links/style/contracts/freshness)
- Contract-first: Validate OpenAPI/AsyncAPI/JSON Schema in CI; use coverage tools (Swagger Coverage / OpenAPI Coverage) to detect gaps
- Runbook testability: Every runbook must be executable in staging; validate with synthetic tests and incident exercises
- Automation + observability: Track coverage %, freshness, and drift via CI dashboards; prevent regressions via PR checklists
This skill provides operational workflows for auditing existing codebases, identifying documentation gaps, and systematically generating missing documentation. It complements docs-codebase by providing the discovery and analysis layer.
Key Principle: Templates exist in docs-codebase. This skill tells you what to document and how to find undocumented components.
Core references: Diataxis (doc structure), OpenAPI (REST), AsyncAPI (events).
When to use
- Auditing an existing repo for missing/outdated documentation
- Adding documentation quality gates (lint/link checks/contracts/freshness) to CI/CD
- Validating runbooks for incident readiness (MTTR reduction)
When to avoid
- Writing new documentation from scratch without a component inventory (use discovery first)
- Publishing AI-generated docs without human review and command/link verification
Quick start
Use progressive disclosure: load only the reference file you need.
1. Discover components: references/discovery-patterns.md 2. Measure coverage + gaps: references/audit-workflows.md (Phase 1-2) and assets/coverage-report-template.md 3. Prioritize work: references/priority-framework.md 4. Create an actionable backlog: assets/documentation-backlog-template.md and templates in docs-codebase 5. Prevent regression: references/cicd-integration.md and references/freshness-tracking.md
Optional (recommended scripts; run from the repo being audited):
- Local link check:
python3 frameworks/shared-skills/skills/qa-docs-coverage/scripts/check_local_links.py docs/ - Freshness report:
python3 frameworks/shared-skills/skills/qa-docs-coverage/scripts/docs_freshness_report.py --docs-root docs/
Docs Folder / LLM Iteration Audit (Critical Option)
Use this when any repository has a docs/ folder with many LLM-generated research and implementation artifacts across phases/iterations.
1. Inventory docs and classify by type (Tutorial, How-to, Reference, Explanation). 2. Detect duplicate topics and define one canonical file per topic/feature. 3. Audit claim quality:
- external claims must include source link + verification date
- implementation claims must map to current code or decision log
4. Enforce lifecycle metadata for non-canonical docs (status, integrates_into, owner, last_verified, delete_by). 5. Trim aggressively at each phase boundary: integrated drafts must be deleted on schedule; track rare retention exceptions in backlog.
Minimum QA gate for docs folders:
- block merge if a canonical doc is missing for a changed feature
- block merge if
delete_byis passed forintegrateddocs - block merge on broken links or stale critical docs without owner
- block merge if
AGENTS.mdorREADME.mdis missing, stale, or not linked to current canonical docs
---
Large Codebase Audit (100K-1M LOC)
For large codebases, the key principle is: LLMs don't need the entire codebase - they need the right context for the current task.
Phase 0: Context Extraction
Before starting an audit, extract codebase context using tools:
| Tool | Command/URL | Use Case |
|---|---|---|
| gitingest | Replace "github.com" with "gitingest.com" | Quick full-repo dump |
| repo2txt | https://github.com/kirill-markin/repo2txt | Selective file extraction |
| tree | `tree -L 3 --dirsfirst -I 'node_modules | .git |
Hierarchical Audit Strategy
For monorepos and large projects, audit hierarchically:
1. Root Level (Week 1)
├── AGENTS.md / CLAUDE.md exists?
├── README.md quality
├── ARCHITECTURE.md exists?
└── docs/ directory structure
2. Module Level (Week 2-3)
├── Each major directory has AGENTS.md?
├── API documentation complete?
└── Service boundaries documented?
3. Component Level (Week 4+)
├── Individual component READMEs
├── Code comments quality
└── Test documentationCross-Platform Documentation Audit
Check for multi-tool compatibility:
[ ] AGENTS.md exists (cross-platform standard)
[ ] CLAUDE.md exists or symlinked to AGENTS.md
[ ] GEMINI.md symlinked (if using Gemini)
[ ] File size under 300 lines (use @references for depth)
[ ] Subdirectory docs for each major moduleLarge Codebase Coverage Checklist
LARGE CODEBASE AUDIT CHECKLIST
Context Extraction:
[ ] Generated codebase dump (gitingest/repo2txt)
[ ] Created directory structure overview
[ ] Identified major modules/services
Root Documentation:
[ ] AGENTS.md / CLAUDE.md present and <300 lines
[ ] README.md with quick start
[ ] ARCHITECTURE.md with system overview
[ ] Symlinks configured for cross-platform
Module Documentation:
[ ] Each major directory has AGENTS.md
[ ] API endpoints documented
[ ] Database schemas documented
[ ] Event/message contracts documented
Maintenance:
[ ] Documentation ownership assigned
[ ] Freshness tracking enabled
[ ] CI/CD checks configuredSources: Anthropic Claude Code Best Practices, OpenAI AGENTS.md Guide
---
Core QA (Default)
What "Docs as QA" Means
- Treat docs as production quality artifacts: they reduce MTTR, enable safe changes, and define expected behavior.
- REQUIRED doc types for reliability and debugging ergonomics:
- "How to run locally/CI" and "how to test"
- Operational runbooks (alerts, common failures, rollback)
- Service contracts (OpenAPI/AsyncAPI) and schema examples
- Known issues and limitations (with workarounds)
Coverage Model (Risk-Based)
- Prioritize docs by impact:
- P1: externally consumed contracts and failure behavior (OpenAPI/AsyncAPI, auth, error codes, SLOs).
- P2: internal integration and operational workflows (events, jobs, DB schema, runbooks).
- P3: developer reference (configs, utilities).
Freshness Checks (Prevent Stale Docs)
- Define owners, review cadence, and a "last verified" field for critical docs.
- CI economics:
- Block PRs only for missing/invalid P1 docs.
- Warn for P2/P3 gaps; track via backlog.
- Run link checks and linting as fast pre-merge steps.
Runbook Testability
- A runbook is "testable" if a new engineer can follow it and reach a measurable end state.
- Include: prerequisites, exact commands, expected outputs, rollback criteria, and escalation paths.
Do / Avoid
Do:
- Keep docs close to code (same repo) and version them with changes.
- Use contracts and examples as the source of truth for integrations.
Avoid:
- Large ungoverned
docs/folders with no owners and no CI gates. - Writing runbooks that cannot be executed in a sandbox/staging environment.
---
Quick Reference
| Audit Task | Tool/Pattern | Output | Reference |
|---|---|---|---|
| Discover APIs | **/*Controller.cs, **/routes/**/*.ts | Component inventory | discovery-patterns.md |
| Calculate Coverage | Swagger Coverage, manual diff | Coverage report | coverage-report-template.md |
| Prioritize Gaps | External → P1, Internal → P2, Config → P3 | Documentation backlog | priority-framework.md |
| Generate Docs | AI-assisted + docs-codebase templates | Documentation files | audit-workflows.md Phase 3 |
| Validate Contracts | Spectral, AsyncAPI CLI, OpenAPI diff | Lint report | cicd-integration.md |
| Track Freshness | Git blame, last-modified metadata | Staleness report | freshness-tracking.md |
| Automate Checks | GitHub Actions, GitLab CI, PR templates | Continuous coverage | cicd-integration.md |
---
Decision Tree: Documentation Audit Workflow
User needs: [Audit Type]
├─ Repo has a docs folder with LLM-generated research/feature docs?
│ └─ Run Docs Folder / LLM Iteration Audit first, then apply P1/P2/P3 prioritization
│
├─ Starting fresh audit?
│ ├─ Public-facing APIs? → Priority 1: External-Facing (OpenAPI, webhooks, error codes)
│ ├─ Internal services/events? → Priority 2: Internal Integration (endpoints, schemas, jobs)
│ └─ Configuration/utilities? → Priority 3: Developer Reference (options, helpers, constants)
│
├─ Found undocumented component?
│ ├─ API/Controller? → Scan endpoints → Use api-docs-template → Priority 1
│ ├─ Service/Handler? → List responsibilities → Document contracts → Priority 2
│ ├─ Database/Entity? → Generate ER diagram → Document entities → Priority 2
│ ├─ Event/Message? → Map producer/consumer → Schema + examples → Priority 2
│ └─ Config/Utility? → Extract options → Defaults + descriptions → Priority 3
│
├─ Large codebase with many gaps?
│ └─ Use phase-based approach:
│ 1. Discovery Scan → Coverage Analysis
│ 2. Prioritize by impact (P1 → P2 → P3)
│ 3. Generate docs incrementally (critical first)
│ 4. Set up maintenance (PR templates, quarterly audits)
│
└─ Maintaining existing docs?
└─ Check for:
├─ Outdated docs (code changed, docs didn't) → Update or remove
├─ Orphaned docs (references non-existent code) → Remove
└─ Missing coverage → Add to backlog → Prioritize---
Navigation: Discovery & Analysis
Component Discovery
Resource: references/discovery-patterns.md
Language-specific patterns for discovering documentable components:
- .NET/C# codebase (Controllers, Services, DbContexts, Kafka handlers)
- Node.js/TypeScript codebase (Routes, Services, Models, Middleware)
- Python codebase (Views, Models, Tasks, Config)
- Go, Java/Spring, React/Frontend patterns
- Discovery commands (ripgrep, grep, find)
- Cross-reference discovery (Kafka topics, external APIs, webhooks)
Priority Framework
Resource: references/priority-framework.md
Framework for prioritizing documentation efforts:
- Priority 1: External-Facing (public APIs, webhooks, auth) - Must document
- Priority 2: Internal Integration (services, events, database) - Should document
- Priority 3: Developer Reference (config, utilities) - Nice to have
- Prioritization decision tree
- Documentation debt scoring (formula + interpretation)
- Compliance considerations (ISO 27001, GDPR, HIPAA)
Audit Workflows
Resource: references/audit-workflows.md
Systematic workflows for conducting audits:
- Phase 1: Discovery Scan (identify all components)
- Phase 2: Coverage Analysis (compare against existing docs)
- Phase 3: Generate Documentation (use templates)
- Phase 4: Maintain Coverage (PR templates, CI/CD checks)
- Audit types (full, incremental, targeted)
- Audit checklist (pre-audit, during, post-audit)
- Tools and automation
CI/CD Integration
Resource: references/cicd-integration.md
Automated documentation checks and enforcement:
- PR template documentation checklists
- CI/CD coverage gates (GitHub Actions, GitLab CI, Jenkins)
- Pre-commit hooks (Git, Husky)
- Documentation linters (markdownlint, Vale, link checkers)
- API contract validation (Spectral, AsyncAPI CLI)
- Coverage tools (Swagger Coverage, OpenAPI Coverage)
- Automated coverage reports
- Best practices and anti-patterns
Freshness Tracking
Resource: references/freshness-tracking.md
Track documentation staleness and drift from code:
- Freshness metadata standards (last_verified, owner, review_cadence)
- Git-based freshness analysis scripts
- Staleness thresholds by priority (P1: 30 days, P2: 60 days, P3: 90 days)
- CI/CD freshness gates (GitHub Actions, GitLab CI)
- Observability dashboards and metrics
- Automated doc reminder bots
API Documentation Validation
Resource: references/api-docs-validation.md
Validate API documentation accuracy against live behavior:
- Schema-to-docs drift detection
- Example request/response validation
- Endpoint coverage auditing
- Contract-first documentation workflows
Runbook Testing
Resource: references/runbook-testing.md
Validate operational runbooks are executable and current:
- Runbook testability criteria and scoring
- Synthetic test execution in staging
- Incident exercise integration
- Staleness detection and refresh cadence
Documentation Quality Metrics
Resource: references/documentation-quality-metrics.md
KPIs and dashboards for documentation health:
- Coverage, freshness, and accuracy metrics
- Documentation debt scoring formulas
- CI dashboard integration patterns
- Trend tracking and alerting thresholds
---
Navigation: Templates
Coverage Report Template
Template: assets/coverage-report-template.md
Structured coverage report with:
- Executive summary (coverage %, key findings, recommendations)
- Coverage by category (API, Service, Data, Events, Infrastructure)
- Gap analysis (P1, P2, P3 with impact/effort)
- Outdated documentation tracking
- Documentation debt score
- Action plan (sprints + ongoing)
Documentation Backlog Template
Template: assets/documentation-backlog-template.md
Backlog tracking with:
- Status summary (In Progress, To Do P1/P2/P3, Blocked, Completed)
- Task organization by priority
- Templates reference (quick links)
- Effort estimates (Low < 2h, Medium 2-8h, High > 8h)
- Review cadence (weekly, bi-weekly, monthly, quarterly)
---
Output Artifacts
After running an audit, produce these artifacts:
1. Coverage Report - .codex/docs/audit/coverage-report.md
- Overall coverage percentage
- Detailed findings by category
- Gap analysis with priorities
- Recommendations and next audit date
2. Documentation Backlog - .codex/docs/audit/documentation-backlog.md
- In Progress items with owners
- To Do items by priority (P1, P2, P3)
- Blocked items with resolution path
- Completed items with dates
3. Generated Documentation - .codex/docs/ (organized by category)
- API reference (public/private)
- Event catalog (Kafka/messaging)
- Database schema (ER diagrams)
- Background jobs (runbooks)
---
Integration with Foundation Skills
This skill works closely with:
[docs-codebase](../docs-codebase/SKILL.md) - Provides templates for:
- api-docs-template.md - REST API documentation
- adr-template.md - Architecture decisions
- readme-template.md - Project overviews
- changelog-template.md - Release history
Workflow:
1. Use qa-docs-coverage to discover gaps 2. Use docs-codebase templates to fill gaps 3. Use qa-docs-coverage CI/CD integration to maintain coverage
---
Anti-Patterns to Avoid
- Documenting everything at once - Prioritize by impact, document incrementally
- Merging doc drafts without review - Drafts must be validated by owners and runnable in practice
- Ignoring outdated docs - Outdated docs are worse than no docs
- Documentation without ownership - Assign owners for each doc area
- Skipping the audit - Don't assume you know what's documented
- Blocking all PRs - Only block for P1 gaps, warn for P2/P3
---
Optional: AI / Automation
Do:
- Use AI to draft docs from code and tickets, then require human review and link/command verification.
- Use AI to propose "freshness diffs" and missing doc sections; validate by running the runbook steps.
Avoid:
- Publishing unverified drafts that include incorrect commands, unsafe advice, or hallucinated endpoints.
---
Success Criteria
Immediate (After Audit):
- Coverage report clearly shows gaps with priorities
- Documentation backlog is actionable and assigned
- Critical gaps (P1) identified with owners
Short-term (1-2 Sprints):
- All P1 gaps documented
- Documentation coverage > 80% for external-facing components
- Documentation backlog actively managed
Long-term (Ongoing):
- Quarterly audits show improving coverage (upward trend)
- PR documentation checklist compliance > 90%
- "How do I" questions in Slack decrease
- Onboarding time for new engineers decreases
---
Related Skills
- [docs-codebase](../docs-codebase/SKILL.md) - Templates for writing documentation (README, ADR, API docs, changelog)
- [docs-ai-prd](../docs-ai-prd/SKILL.md) - PRD and tech spec templates for new features
- [software-code-review](../software-code-review/SKILL.md) - Code review including documentation standards
---
Usage Notes
For Claude: When auditing a codebase:
1. Start with discovery - Use references/discovery-patterns.md to find components 2. Calculate coverage - Compare discovered components vs existing docs 3. Prioritize gaps - Use references/priority-framework.md to assign P1/P2/P3 4. Follow workflows - Use references/audit-workflows.md for systematic approach 5. Use templates - Reference docs-codebase for documentation structure 6. Set up automation - Use references/cicd-integration.md for ongoing maintenance
Remember: The goal is not 100% coverage, but useful coverage for the target audience. Document what developers, operators, and integrators actually need.
Fact-Checking
- Use web search/web fetch to verify current external facts, versions, pricing, deadlines, regulations, or platform behavior before final answers.
- Prefer primary sources; report source links and dates for volatile information.
- If web access is unavailable, state the limitation and mark guidance as unverified.
Documentation Coverage Report
Project: [Project Name] Generated: YYYY-MM-DD Auditor: [Human/AI Name] Audit Type: [Full / Incremental / Targeted]
---
Executive Summary
| Metric | Value |
|---|---|
| Overall Coverage | X% |
| Components Discovered | N |
| Components Documented | M |
| Critical Gaps | P |
| Documentation Health | Good / Moderate / Poor |
Key Findings
1. [Most significant gap] 2. [Second most significant gap] 3. [Third most significant gap]
Recommendations
1. Immediate: [Action for critical gaps] 2. Short-term: [Action for important gaps] 3. Ongoing: [Process improvements]
---
Coverage by Category
API Layer
| Component | Location | Documented | Doc Location | Priority |
|---|---|---|---|---|
| PublicApi Controllers | sources/presentation/PublicApi/ | Yes/No | docs/api/ | P1/P2/P3 |
| PrivateApi Controllers | sources/presentation/PrivateApi/ | Yes/No | ||
| OpenAPI Spec | openapi/ | Yes/No | ||
| Error Codes | Yes/No | |||
| Authentication | Yes/No |
API Coverage: X / Y endpoints (Z%)
Service Layer
| Component | Location | Documented | Doc Location | Priority |
|---|---|---|---|---|
| Commands | sources/core/*/Commands/ | Yes/No | ||
| Handlers | sources/core/*/Handlers/ | Yes/No | ||
| Services | sources/core/*/Services/ | Yes/No |
Service Coverage: X / Y services (Z%)
Data Layer
| Component | Location | Documented | Doc Location | Priority |
|---|---|---|---|---|
| DbContexts | sources/infrastructure/ | Yes/No | ||
| Entities | sources/core/*/Models/ | Yes/No | ||
| Migrations | Yes/No |
Data Coverage: X / Y entities (Z%)
Events/Messaging
| Component | Location | Documented | Doc Location | Priority |
|---|---|---|---|---|
| Kafka Topics | Yes/No | |||
| Message Schemas | sources/core/*/Models/Kafka/ | Yes/No | ||
| Producers | Yes/No | |||
| Consumers | Yes/No |
Event Coverage: X / Y events (Z%)
Infrastructure
| Component | Location | Documented | Doc Location | Priority |
|---|---|---|---|---|
| Background Jobs | sources/infrastructure/*/Jobs/ | Yes/No | ||
| Hosted Services | sources/infrastructure/*/HostedServices/ | Yes/No | ||
| Configuration | sources/core/*/Configuration/ | Yes/No |
Infrastructure Coverage: X / Y components (Z%)
External Integrations
| Integration | Location | Documented | Doc Location | Priority |
|---|---|---|---|---|
| [Provider 1] | Yes/No | |||
| [Provider 2] | Yes/No | |||
| Webhooks | Yes/No |
Integration Coverage: X / Y integrations (Z%)
---
Gap Analysis
Critical Gaps (Priority 1)
These gaps affect external integrators, compliance, or operational safety.
| # | Component | Type | Impact | Effort | Owner |
|---|---|---|---|---|---|
| 1 | API | High | Medium | ||
| 2 | Events | High | Low | ||
| 3 | Config | High | Low |
Important Gaps (Priority 2)
These gaps affect internal developers or cross-team collaboration.
| # | Component | Type | Impact | Effort | Owner |
|---|---|---|---|---|---|
| 1 | Service | Medium | Medium | ||
| 2 | Data | Medium | High |
Nice to Have (Priority 3)
These gaps are helpful but not blocking.
| # | Component | Type | Impact | Effort | Owner |
|---|---|---|---|---|---|
| 1 | Utility | Low | Low | ||
| 2 | Internal | Low | Low |
---
Outdated Documentation
Documentation that exists but may not match current code:
| Document | Last Updated | Code Changed | Action Needed |
|---|---|---|---|
| YYYY-MM-DD | Yes/No | Update/Remove/Verify |
---
Documentation Debt Score
Debt Score = (Critical Gaps * 3) + (Important Gaps * 2) + (Nice to Have * 1)
Current Score: X
Target Score: Y (reduce by Z% by [date])---
Action Plan
Sprint 1 (Immediate)
- [ ] Document [Critical Gap 1]
- [ ] Document [Critical Gap 2]
- [ ] Update [Outdated Doc 1]
Sprint 2-3 (Short-term)
- [ ] Document [Important Gap 1]
- [ ] Document [Important Gap 2]
- [ ] Create [Missing Diagram]
Ongoing
- [ ] Add documentation check to PR template
- [ ] Schedule quarterly audit
- [ ] Assign documentation owners
---
Audit Metadata
| Field | Value |
|---|---|
| Scan Method | Manual / Automated / Hybrid |
| Files Scanned | N |
| Patterns Used | See [discovery patterns] |
| Duration | X hours |
| Tools Used | [List tools] |
---
Next Audit
Scheduled: YYYY-MM-DD Focus Areas: [Areas to re-audit] Success Criteria: Coverage > X%, Critical Gaps = 0
Documentation Backlog
Project: [Project Name] Last Updated: YYYY-MM-DD Backlog Owner: [Name/Team]
---
Summary
| Status | Count |
|---|---|
| In Progress | X |
| To Do (P1) | X |
| To Do (P2) | X |
| To Do (P3) | X |
| Blocked | X |
| Completed | X |
---
In Progress
| Task | Owner | Started | ETA | Notes |
|---|---|---|---|---|
| @owner | YYYY-MM-DD | YYYY-MM-DD |
---
To Do - Priority 1 (Critical)
External-facing, compliance, or operational safety documentation.
| Task | Type | Effort | Template | Notes |
|---|---|---|---|---|
| Document PrivateApi endpoints | API | High | api-docs-template | |
| Create Kafka event schema reference | Events | Medium | Custom | |
| Document error codes | API | Low | Custom | |
| Create database ER diagram | Data | Medium | Custom |
---
To Do - Priority 2 (Important)
Internal developer documentation and cross-team collaboration.
| Task | Type | Effort | Template | Notes |
|---|---|---|---|---|
| Document service layer contracts | Service | High | Custom | |
| Document background jobs | Ops | Medium | Custom | |
| Create configuration reference | Config | Medium | Custom | |
| Document webhook validation | Integration | Low | Custom |
---
To Do - Priority 3 (Nice to Have)
Developer convenience and completeness.
| Task | Type | Effort | Template | Notes |
|---|---|---|---|---|
| Document utility classes | Code | Low | Inline | |
| Add code examples to existing docs | Docs | Low | N/A | |
| Create troubleshooting guide | Ops | Medium | Custom |
---
Blocked
| Task | Blocked By | Since | Action Needed |
|---|---|---|---|
| [Reason] | YYYY-MM-DD | [What unblocks] |
---
Completed
| Task | Completed | By | Location |
|---|---|---|---|
| YYYY-MM-DD | @owner | docs/path/file.md |
---
Templates Reference
Quick links to documentation templates:
| Template | Use For | Location |
|---|---|---|
| API Docs | REST endpoints | api-docs-template.md |
| ADR | Architecture decisions | adr-template.md |
| README | Project overview | readme-template.md |
| Changelog | Release notes | changelog-template.md |
| Tech Spec | Technical specs | tech-spec-template.md |
---
Conventions
Effort Estimates
- Low: < 2 hours
- Medium: 2-8 hours
- High: > 8 hours (consider breaking down)
Task Types
- API: Endpoint documentation
- Events: Kafka/messaging documentation
- Data: Database/entity documentation
- Service: Service layer documentation
- Config: Configuration documentation
- Ops: Operational/runbook documentation
- Integration: External integration documentation
- Code: Inline code documentation
Priority Criteria
- P1: Blocks external integrators, required for compliance, affects production operations
- P2: Affects internal developers, needed for cross-team collaboration
- P3: Improves developer experience, completeness
---
Review Cadence
- Weekly: Review In Progress items
- Bi-weekly: Prioritize backlog, assign owners
- Monthly: Review completed items, update coverage report
- Quarterly: Full documentation audit
{
"metadata": {
"skill": "qa-docs-coverage",
"updated": "2026-01-20",
"version": "3.1",
"total_sources": 23,
"description": "Primary references for documentation coverage, contracts, AI-assisted audits, documentation quality gates, and large codebase workflows."
},
"categories": {
"contracts_and_schemas": [
{
"name": "OpenAPI Specification (Latest)",
"url": "https://spec.openapis.org/oas/latest.html",
"description": "Industry standard for REST API contracts; use for shift-left validation and docs-as-tests.",
"add_as_web_search": true,
"optional": false
},
{
"name": "AsyncAPI Specification (v3)",
"url": "https://www.asyncapi.com/docs/reference/specification/v3.0.0",
"description": "Event-driven API contracts (Kafka, WebSocket, etc.).",
"add_as_web_search": true,
"optional": false
},
{
"name": "JSON Schema",
"url": "https://json-schema.org/",
"description": "Schema standard for payload documentation and validation.",
"add_as_web_search": true,
"optional": false
}
],
"documentation_frameworks": [
{
"name": "Diataxis Framework",
"url": "https://diataxis.fr/",
"description": "Docs structure model (tutorial/how-to/reference/explanation) for reducing doc debt and improving usability.",
"add_as_web_search": true,
"optional": false
},
{
"name": "The Good Docs Project",
"url": "https://www.thegooddocsproject.dev/",
"description": "Templates and best practices for documentation workflows.",
"add_as_web_search": true,
"optional": false
},
{
"name": "Write the Docs - Guide",
"url": "https://www.writethedocs.org/guide/",
"description": "Community guide for documentation practices and workflows.",
"add_as_web_search": true,
"optional": false
}
],
"documentation_quality_gates": [
{
"name": "Vale",
"url": "https://vale.sh/",
"description": "Prose linter for documentation quality checks in CI.",
"add_as_web_search": true,
"optional": false
},
{
"name": "markdownlint",
"url": "https://github.com/DavidAnson/markdownlint",
"description": "Markdown linter for consistent formatting.",
"add_as_web_search": true,
"optional": false
},
{
"name": "markdown-link-check",
"url": "https://github.com/tcort/markdown-link-check",
"description": "Broken link checking for Markdown files.",
"add_as_web_search": true,
"optional": false
},
{
"name": "Spectral",
"url": "https://stoplight.io/spectral",
"description": "OpenAPI and AsyncAPI linting with custom rulesets for CI enforcement.",
"add_as_web_search": true,
"optional": false
}
],
"api_coverage_tools": [
{
"name": "Swagger Coverage",
"url": "https://github.com/viclovsky/swagger-coverage",
"description": "OpenAPI specification coverage tool - tracks which endpoints are tested vs documented.",
"add_as_web_search": true,
"optional": false
},
{
"name": "OpenAPI Coverage",
"url": "https://github.com/meetmatt/open-api-coverage",
"description": "Reports documented and executed parts of OpenAPI spec under test.",
"add_as_web_search": true,
"optional": false
},
{
"name": "AsyncAPI Studio",
"url": "https://studio.asyncapi.com/",
"description": "Visual editor and validator for AsyncAPI documents.",
"add_as_web_search": true,
"optional": false
}
],
"ai_documentation_tools": [
{
"name": "Mintlify",
"url": "https://mintlify.com/",
"description": "AI-powered documentation platform with GitHub integration and docs-as-code workflow.",
"add_as_web_search": true,
"optional": true
},
{
"name": "DocuWriter.ai",
"url": "https://www.docuwriter.ai/",
"description": "Automated code documentation generation with n8n integration for CI/CD workflows.",
"add_as_web_search": true,
"optional": true
},
{
"name": "Documentation.AI",
"url": "https://documentation.ai/",
"description": "AI documentation agent with MCP server support for agent-driven updates.",
"add_as_web_search": true,
"optional": true
}
],
"optional_governance": [
{
"name": "NIST AI Risk Management Framework",
"url": "https://www.nist.gov/itl/ai-risk-management-framework",
"description": "Optional governance baseline when using AI to draft or update documentation.",
"add_as_web_search": true,
"optional": true
},
{
"name": "Model Context Protocol",
"url": "https://modelcontextprotocol.io/",
"description": "MCP servers for connecting AI agents to live documentation context.",
"add_as_web_search": true,
"optional": true
}
],
"large_codebase_workflows": [
{
"name": "OpenAI AGENTS.md Guide",
"url": "https://developers.openai.com/codex/guides/agents-md",
"description": "Official OpenAI Codex documentation for AGENTS.md cross-platform standard.",
"add_as_web_search": true,
"optional": false
},
{
"name": "OpenAI Codex Config Advanced",
"url": "https://developers.openai.com/codex/config-advanced/",
"description": "Advanced configuration for OpenAI Codex including file size limits and directory structure.",
"add_as_web_search": true,
"optional": false
},
{
"name": "Anthropic: Claude Code Best Practices",
"url": "https://www.anthropic.com/engineering/claude-code-best-practices",
"description": "Official Anthropic best practices for Claude Code including large codebase strategies.",
"add_as_web_search": true,
"optional": false
},
{
"name": "Anthropic: Claude Code Memory Documentation",
"url": "https://docs.anthropic.com/en/docs/claude-code/memory",
"description": "Official documentation for CLAUDE.md file format and hierarchical loading.",
"add_as_web_search": true,
"optional": false
}
]
}
}
API Documentation Validation
Systematic approaches to validating API documentation accuracy against live endpoints, detecting spec drift, and enforcing documentation-code sync in CI.
Contents
- OpenAPI and AsyncAPI Spec Linting
- Spec-to-Implementation Drift Detection
- Example Validation with Mock Servers
- Request/Response Sample Testing
- Schema Accuracy Verification
- Endpoint Coverage Audit
- Automated CI Checks for Doc Drift
- Documentation-Code Sync Strategies
- Tools Comparison
- Related Resources
---
OpenAPI and AsyncAPI Spec Linting
Linting catches structural errors, naming inconsistencies, and missing descriptions before specs reach consumers.
Spectral (Stoplight)
Spectral is the most widely adopted OpenAPI/AsyncAPI linter. It supports custom rulesets and integrates with CI.
# Install Spectral
npm install -g @stoplight/spectral-cli
# Lint an OpenAPI spec
spectral lint openapi.yaml
# Lint with a custom ruleset
spectral lint openapi.yaml --ruleset .spectral.yaml
# Lint AsyncAPI spec
spectral lint asyncapi.yaml --ruleset spectral-asyncapiCustom ruleset example (.spectral.yaml):
extends:
- spectral:oas
rules:
operation-description:
description: Every operation must have a description
given: "$.paths[*][get,post,put,patch,delete]"
then:
field: description
function: truthy
severity: error
operation-tags:
description: Every operation must have at least one tag
given: "$.paths[*][get,post,put,patch,delete]"
then:
field: tags
function: length
functionOptions:
min: 1
severity: warn
schema-description:
description: All schema properties should have descriptions
given: "$.components.schemas[*].properties[*]"
then:
field: description
function: truthy
severity: info
no-empty-examples:
description: Response examples must not be empty
given: "$.paths[*][*].responses[*].content[*].examples[*]"
then:
function: truthy
severity: warnRedocly CLI
Redocly provides stricter validation and can bundle multi-file specs.
# Install Redocly CLI
npm install -g @redocly/cli
# Lint with built-in recommended rules
redocly lint openapi.yaml
# Lint with custom config
redocly lint openapi.yaml --config redocly.yaml
# Bundle multi-file spec into single file
redocly bundle openapi.yaml -o bundled.yaml
# Preview docs locally
redocly preview-docs openapi.yamlRedocly config (redocly.yaml):
extends:
- recommended
rules:
no-empty-servers: error
operation-operationId-unique: error
no-path-trailing-slash: error
path-declaration-must-exist: error
operation-summary: warn
tag-description: warn
no-unused-components: warnLinting Checklist
- [ ] All endpoints have
operationId,summary, anddescription - [ ] All request/response schemas reference
$refcomponents (no inline) - [ ] All parameters have
descriptionandexample - [ ] Error responses (4xx, 5xx) are documented with schemas
- [ ] Authentication schemes are declared in
securitySchemes - [ ] No unused components in
components/schemas - [ ] Server URLs are valid and environment-appropriate
---
Spec-to-Implementation Drift Detection
Drift occurs when the API implementation diverges from the documented spec. Detection strategies range from runtime traffic comparison to static code analysis.
Traffic-Based Drift Detection
Capture real API traffic and compare against the spec.
"""
Compare live API responses against OpenAPI spec.
Uses openapi-core for validation.
"""
import requests
import json
from openapi_core import OpenAPI
# Load spec
api = OpenAPI.from_file_path("openapi.yaml")
# Make a real request
response = requests.get("https://api.example.com/users/123")
# Validate response against spec
result = api.validate_response(
request_method="GET",
request_path="/users/123",
response_status=response.status_code,
response_headers=dict(response.headers),
response_data=response.json(),
)
if result.errors:
print("DRIFT DETECTED:")
for error in result.errors:
print(f" - {error}")
else:
print("Response matches spec")Code-Annotation Drift Detection
For frameworks that generate specs from code annotations, compare the generated spec to the committed spec.
#!/bin/bash
# drift-check.sh: Detect drift between generated and committed OpenAPI specs
# Generate spec from code annotations
npx tsoa spec-and-routes # or: ./gradlew generateOpenApiDocs
# Compare generated vs committed
diff <(yq eval -P generated/openapi.yaml) <(yq eval -P docs/openapi.yaml) > drift.diff
if [ -s drift.diff ]; then
echo "DRIFT DETECTED between generated spec and committed spec:"
cat drift.diff
exit 1
else
echo "No drift detected"
exit 0
fiDrift Detection Strategies
| Strategy | How It Works | Best For |
|---|---|---|
| Traffic capture | Record prod traffic, validate against spec | Runtime accuracy |
| Code-gen comparison | Generate spec from code, diff against committed spec | Annotation-based APIs |
| Contract testing | Consumer-driven contracts validate provider | Microservices |
| Integration tests | Hit live endpoints, validate response schema | Pre-deploy validation |
| Proxy validation | API gateway validates traffic against spec | Real-time enforcement |
---
Example Validation with Mock Servers
Mock servers like Prism validate that your documented examples actually conform to your schemas.
Prism (Stoplight)
# Install Prism
npm install -g @stoplight/prism-cli
# Start mock server from spec
prism mock openapi.yaml
# Start with validation mode (strict)
prism mock openapi.yaml --errors
# Proxy mode: validate real traffic against spec
prism proxy openapi.yaml https://api.example.com --errorsProxy validation workflow:
# Start Prism in proxy mode
prism proxy openapi.yaml https://api.staging.example.com --errors &
# Run test suite against proxy
API_BASE_URL=http://localhost:4010 pytest tests/api/
# Prism will flag any response that doesn't match the specValidating Inline Examples
# openapi.yaml snippet with inline examples
paths:
/users/{id}:
get:
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/User"
examples:
standard-user:
summary: A standard active user
value:
id: 123
name: "Jane Doe"
email: "jane@example.com"
status: "active"
created_at: "2025-01-15T10:30:00Z"# Validate that all examples conform to their schemas
npx @schemathesis/cli validate openapi.yaml --check-examples---
Request/Response Sample Testing
Test documented request/response pairs against the live API to verify accuracy.
"""
Automated sample testing: extract examples from OpenAPI spec
and execute them against a live or staging API.
"""
import yaml
import requests
import jsonschema
def load_spec(path: str) -> dict:
with open(path) as f:
return yaml.safe_load(f)
def extract_examples(spec: dict) -> list:
"""Extract all request/response example pairs from spec."""
examples = []
for path, methods in spec.get("paths", {}).items():
for method, operation in methods.items():
if method not in ("get", "post", "put", "patch", "delete"):
continue
for status_code, response in operation.get("responses", {}).items():
content = response.get("content", {})
for media_type, media in content.items():
for name, example in media.get("examples", {}).items():
examples.append({
"path": path,
"method": method,
"status_code": status_code,
"example_name": name,
"example_value": example.get("value"),
"schema": media.get("schema"),
})
return examples
def validate_examples(spec_path: str, base_url: str):
spec = load_spec(spec_path)
examples = extract_examples(spec)
results = []
for ex in examples:
# Validate example against its own schema
try:
resolver = jsonschema.RefResolver.from_schema(spec)
jsonschema.validate(ex["example_value"], ex["schema"], resolver=resolver)
results.append({"example": ex["example_name"], "status": "PASS"})
except jsonschema.ValidationError as e:
results.append({
"example": ex["example_name"],
"status": "FAIL",
"error": str(e.message),
})
return results
# Run validation
results = validate_examples("openapi.yaml", "https://api.staging.example.com")
for r in results:
print(f"[{r['status']}] {r['example']}")---
Schema Accuracy Verification
Ensure that documented schemas match the actual shape of API responses.
Schema Diff Script
"""
Compare documented schema against actual API response shape.
Detects: missing fields, extra undocumented fields, type mismatches.
"""
import requests
import yaml
def get_response_shape(url: str) -> dict:
"""Get the actual response and infer its shape."""
resp = requests.get(url)
return infer_schema(resp.json())
def infer_schema(data, path="$") -> dict:
"""Recursively infer JSON Schema from a response."""
if isinstance(data, dict):
return {
"type": "object",
"properties": {
k: infer_schema(v, f"{path}.{k}") for k, v in data.items()
},
}
elif isinstance(data, list):
if data:
return {"type": "array", "items": infer_schema(data[0], f"{path}[0]")}
return {"type": "array"}
elif isinstance(data, bool):
return {"type": "boolean"}
elif isinstance(data, int):
return {"type": "integer"}
elif isinstance(data, float):
return {"type": "number"}
elif isinstance(data, str):
return {"type": "string"}
return {"type": "null"}
def compare_schemas(documented: dict, actual: dict, path: str = "$") -> list:
"""Compare documented schema vs actual response schema."""
issues = []
doc_props = documented.get("properties", {})
act_props = actual.get("properties", {})
# Fields in actual but not documented
for field in set(act_props.keys()) - set(doc_props.keys()):
issues.append(f"UNDOCUMENTED field: {path}.{field}")
# Fields documented but not in actual
for field in set(doc_props.keys()) - set(act_props.keys()):
issues.append(f"MISSING field: {path}.{field} (documented but not returned)")
# Type mismatches
for field in set(doc_props.keys()) & set(act_props.keys()):
doc_type = doc_props[field].get("type")
act_type = act_props[field].get("type")
if doc_type != act_type:
issues.append(
f"TYPE MISMATCH: {path}.{field} "
f"documented={doc_type}, actual={act_type}"
)
if doc_type == "object":
issues.extend(
compare_schemas(doc_props[field], act_props[field], f"{path}.{field}")
)
return issuesCommon Schema Accuracy Problems
| Problem | Detection | Fix |
|---|---|---|
| Undocumented fields returned | Response diff against schema | Add fields to spec |
| Documented fields missing | Schema validation fails | Remove from spec or fix API |
| Type mismatch (string vs integer) | Type comparison | Correct spec or API |
| Nullable not declared | Non-null constraint fails | Add nullable: true to spec |
| Enum value not listed | Enum validation fails | Expand enum in spec |
| Date format inconsistency | Format validation | Standardize on ISO 8601 |
---
Endpoint Coverage Audit
Measure which endpoints are documented vs discovered through code analysis or traffic.
Coverage Matrix
#!/bin/bash
# endpoint-coverage-audit.sh
# Compare discovered routes against documented endpoints
echo "=== Endpoint Coverage Audit ==="
# Extract routes from code (Express.js example)
echo "Discovering routes from code..."
grep -rn "router\.\(get\|post\|put\|patch\|delete\)" src/routes/ \
| sed 's/.*router\.\(.*\)(\s*["'"'"']\(.*\)["'"'"'].*/\U\1 \2/' \
| sort > /tmp/code-routes.txt
# Extract documented endpoints from OpenAPI spec
echo "Extracting documented endpoints..."
yq eval '.paths | keys[]' openapi.yaml | while read path; do
yq eval ".paths[\"$path\"] | keys[]" openapi.yaml | while read method; do
if [[ "$method" != "parameters" && "$method" != "summary" ]]; then
echo "${method^^} $path"
fi
done
done | sort > /tmp/spec-routes.txt
# Compare
echo ""
echo "--- Documented but not in code (stale?) ---"
comm -23 /tmp/spec-routes.txt /tmp/code-routes.txt
echo ""
echo "--- In code but not documented (gap!) ---"
comm -13 /tmp/spec-routes.txt /tmp/code-routes.txt
echo ""
TOTAL_CODE=$(wc -l < /tmp/code-routes.txt)
TOTAL_SPEC=$(wc -l < /tmp/spec-routes.txt)
DOCUMENTED=$(comm -12 /tmp/spec-routes.txt /tmp/code-routes.txt | wc -l)
echo "Coverage: $DOCUMENTED / $TOTAL_CODE endpoints documented ($(( DOCUMENTED * 100 / TOTAL_CODE ))%)"Coverage Targets by Service Tier
| Service Tier | Endpoint Coverage | Schema Coverage | Example Coverage |
|---|---|---|---|
| Tier 1 (public API) | 100% | 100% | 100% with test validation |
| Tier 2 (internal API) | 95% | 90% | 80% |
| Tier 3 (admin/debug) | 80% | 70% | 50% |
| Tier 4 (deprecated) | Track only | -- | -- |
---
Automated CI Checks for Doc Drift
Integrate documentation validation into your CI pipeline to prevent drift.
GitHub Actions Workflow
# .github/workflows/api-docs-validation.yaml
name: API Documentation Validation
on:
pull_request:
paths:
- "src/routes/**"
- "src/controllers/**"
- "docs/openapi.yaml"
- "docs/asyncapi.yaml"
jobs:
lint-spec:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint OpenAPI spec
uses: stoplightio/spectral-action@latest
with:
file_glob: "docs/openapi.yaml"
spectral_ruleset: ".spectral.yaml"
validate-examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Prism
run: npm install -g @stoplight/prism-cli
- name: Start mock server and validate
run: |
prism mock docs/openapi.yaml --errors --port 4010 &
sleep 3
# Hit every documented endpoint and verify 2xx
npx @schemathesis/cli run docs/openapi.yaml \
--base-url http://localhost:4010 \
--validate-schema true
drift-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate spec from code
run: npm run generate-spec
- name: Check for drift
run: |
diff <(yq eval -P generated/openapi.yaml) \
<(yq eval -P docs/openapi.yaml)
if [ $? -ne 0 ]; then
echo "::error::API spec drift detected. Regenerate docs."
exit 1
fiCI Validation Checklist
- [ ] Spectral lint passes with zero errors
- [ ] All documented examples validate against their schemas
- [ ] No spec drift between code-generated and committed specs
- [ ] Endpoint coverage does not decrease (ratchet)
- [ ] Breaking changes flagged (removed endpoints, changed schemas)
- [ ] Changelog entry required when spec changes
---
Documentation-Code Sync Strategies
| Strategy | Description | Pros | Cons |
|---|---|---|---|
| Code-first | Generate spec from code annotations (tsoa, springdoc, FastAPI) | Always in sync, single source of truth | Spec quality depends on annotation discipline |
| Spec-first | Write spec manually, generate code stubs | Better API design, contract-driven | Drift risk if implementation diverges |
| Hybrid | Spec-first for design, code-first for validation | Best of both worlds | More complex pipeline |
| Traffic-based | Generate spec from recorded API traffic | Captures actual behavior | Misses edge cases, no intent |
Code-First Best Practices
// FastAPI example: code annotations generate spec automatically
// This IS the documentation
from fastapi import FastAPI, Path, Query
from pydantic import BaseModel, Field
from datetime import datetime
class User(BaseModel):
"""A registered user in the system."""
id: int = Field(..., description="Unique user identifier", example=123)
name: str = Field(..., description="Full display name", example="Jane Doe")
email: str = Field(..., description="Primary email address", example="jane@example.com")
status: str = Field(..., description="Account status", example="active", enum=["active", "suspended", "deleted"])
created_at: datetime = Field(..., description="Account creation timestamp")
@app.get(
"/users/{user_id}",
response_model=User,
summary="Get user by ID",
description="Retrieve a single user by their unique identifier.",
responses={
404: {"description": "User not found"},
403: {"description": "Insufficient permissions"},
},
)
async def get_user(
user_id: int = Path(..., description="The user ID to retrieve", ge=1),
):
...Spec-First Best Practices
- Store the canonical spec in
docs/openapi.yaml - Generate server stubs with
openapi-generator - Generate client SDKs from the same spec
- Validate implementation against spec in integration tests
- Require spec review before merging code changes
---
Tools Comparison
| Tool | Type | OpenAPI | AsyncAPI | CI Integration | Custom Rules | Pricing |
|---|---|---|---|---|---|---|
| Spectral | Linter | 2.x, 3.x | 2.x | GitHub Action, npm | YAML rulesets | Free/OSS |
| Redocly CLI | Linter + Bundler | 2.x, 3.x | -- | GitHub Action, npm | YAML config | Free tier + paid |
| Prism | Mock + Proxy | 2.x, 3.x | -- | npm | -- | Free/OSS |
| Schemathesis | Fuzz tester | 2.x, 3.x | -- | Docker, pip | Python hooks | Free/OSS |
| Optic | Diff + Drift | 3.x | -- | GitHub Action | Custom checks | Free tier + paid |
| openapi-diff | Breaking change | 2.x, 3.x | -- | npm, Docker | -- | Free/OSS |
| Swagger Coverage | Coverage audit | 2.x | -- | Maven, Gradle | -- | Free/OSS |
---
Related Resources
- Discovery Patterns - Finding undocumented components
- Freshness Tracking - Detecting stale documentation
- CI/CD Integration - Automation pipeline patterns
- Documentation Quality Metrics - Measuring doc health KPIs
- Runbook Testing - Validating operational runbooks
- SKILL.md - Parent skill overview
Documentation Audit Workflows
This resource provides systematic workflows for conducting documentation audits, from initial discovery to ongoing maintenance.
---
Contents
- Overview
- Phase 1: Discovery Scan
- Component Inventory
- Phase 2: Coverage Analysis
- Phase 3: Generate Documentation
- Phase 4: Maintain Coverage
- Audit Types
- Audit Checklist
- Tools and Automation
- Common Challenges
- Success Criteria
- Related Resources
Overview
A documentation audit is a systematic review of a codebase to identify documentation gaps, outdated content, and orphaned documentation. This guide provides step-by-step workflows for different audit scenarios.
---
Phase 1: Discovery Scan
Objective
Identify all documentable components in the codebase.
Workflow
Step 1: Prepare Audit Scope
Define what you're auditing:
- [ ] API layer (endpoints, models, authentication)
- [ ] Service layer (business logic, handlers)
- [ ] Data layer (database, entities, migrations)
- [ ] Integration layer (external APIs, webhooks, messages)
- [ ] Infrastructure layer (jobs, services, configuration)
Step 2: Identify Documentation Locations
Find where documentation currently exists:
# Common locations
- docs/
- README.md
- wiki/
- openapi/
- *.md files throughout codebase
- Inline code commentsStep 3: Scan for Components
Use discovery patterns from discovery-patterns.md to find components:
# Example: .NET codebase
rg "class.*Controller" --type cs > controllers.txt
rg "class.*Service" --type cs > services.txt
rg "class.*DbContext" --type cs > dbcontexts.txt
rg "Topic = \"" --type cs > kafka-topics.txtStep 4: Create Component Inventory
Organize discovered components by category:
## Component Inventory
### API Layer (23 components)
- PublicApi.Controllers.UsersController
- PublicApi.Controllers.OrdersController
- ...
### Service Layer (45 components)
- Core.Services.UserService
- Core.Services.OrderService
- ...
### Data Layer (12 components)
- Infrastructure.Data.AppDbContext
- Core.Models.User
- ...Output
- Component inventory (text file or spreadsheet)
- Total component counts by category
- Baseline for gap analysis
---
Phase 2: Coverage Analysis
Objective
Compare discovered components against existing documentation to identify gaps.
Workflow
Step 1: Inventory Existing Documentation
List all existing documentation:
# Find all markdown files
find docs/ -name "*.md" -type f
# List documented endpoints (if OpenAPI exists)
yq '.paths | keys' openapi/spec.yaml
# Count documented components
grep -r "## " docs/ | wc -lStep 2: Match Components to Documentation
Create a mapping:
| Component | Type | Documented? | Doc Location | Notes |
|---|---|---|---|---|
| UsersController | API | Yes | docs/api/users.md | Complete |
| OrdersController | API | No | - | GAP |
| UserService | Service | Partial | docs/services.md | Missing dependencies |
Step 3: Calculate Coverage Metrics
Coverage Rate = (Documented Components / Total Components) × 100%
Example:
- Total: 80 components
- Documented: 52 components
- Coverage: 65%Step 4: Categorize Gaps by Priority
Use priority-framework.md to assign priorities:
- Priority 1 (Critical): External-facing APIs, webhooks, auth
- Priority 2 (Important): Internal APIs, events, database schema
- Priority 3 (Nice to Have): Config options, utilities
Step 5: Identify Outdated Documentation
Check for documentation that may be stale:
# Find docs older than code
find docs/ -name "*.md" -mtime +90 # Modified >90 days ago
# Compare doc dates with git history
git log --since="3 months ago" --name-only -- src/Output
Use template: assets/coverage-report-template.md
Key sections:
- Executive summary (coverage %, key findings)
- Coverage by category
- Gap analysis (P1, P2, P3)
- Outdated documentation list
---
Phase 3: Generate Documentation
Objective
Create missing documentation using appropriate templates.
Workflow
Step 1: Prioritize Gaps
Start with Priority 1 (critical) gaps:
## Priority 1 Gaps (5 items)
1. Document PrivateApi endpoints (8 controllers)
- Template: api-docs-template.md
- Effort: High (8 hours)
- Owner: @team-backend
2. Create Kafka event schema reference
- Template: Custom event catalog
- Effort: Medium (4 hours)
- Owner: @team-platformStep 2: Select Templates
Match gaps to templates from docs-codebase:
| Gap Type | Template |
|---|---|
| API endpoints | api-docs-template.md |
| Architecture decisions | adr-template.md |
| Database schema | ER diagram + entity descriptions |
| Event schemas | Custom event catalog |
| Configuration | Config reference template |
Step 3: Generate Documentation
For each gap:
1. Read the code to understand functionality 2. Use the template to structure documentation 3. Add examples (request/response, code snippets) 4. Review with code author for accuracy 5. Commit to docs/ directory
Step 4: Update Coverage Report
After generating documentation:
## Progress Update
- Initial coverage: 65%
- Documented this sprint: 8 components
- Current coverage: 75%
- Remaining P1 gaps: 2Output
- Documentation files in
docs/directory - Updated coverage report
- Documentation backlog with completed items
---
Phase 4: Maintain Coverage
Objective
Ensure documentation stays up-to-date and gaps don't re-emerge.
Workflow
Step 1: Add Documentation to PR Template
## Documentation Checklist
- [ ] New APIs documented in OpenAPI spec
- [ ] New events added to event catalog
- [ ] Configuration changes documented
- [ ] Breaking changes noted in CHANGELOG
- [ ] Architecture decisions recorded (ADR)Step 2: Set Up Documentation Checks (CI/CD)
See cicd-integration.md for implementation details.
Example: GitHub Actions check
- name: Documentation Coverage Check
run: |
# Count undocumented public APIs
./scripts/check-api-coverage.shStep 3: Schedule Regular Audits
- Quarterly: Full documentation audit (all phases)
- Monthly: Review documentation backlog progress
- Weekly: Review PR documentation checklist compliance
Step 4: Assign Documentation Owners
## Documentation Ownership
| Area | Owner | Responsibilities |
|------|-------|------------------|
| Public API docs | @team-api | Keep OpenAPI spec current |
| Event catalog | @team-platform | Document new Kafka topics |
| Database schema | @team-data | Update ER diagrams |
| Runbooks | @team-ops | Document background jobs |Step 5: Track Documentation Debt
Use documentation-backlog-template.md to track:
- In Progress items
- To Do (P1, P2, P3)
- Blocked items
- Completed items
Output
- PR template with documentation checklist
- CI/CD checks for documentation coverage
- Quarterly audit schedule
- Documentation ownership matrix
---
Audit Types
Full Audit (Quarterly)
Scope: All components, all documentation
Duration: 1-2 weeks
Output:
- Complete coverage report
- Updated documentation backlog
- Documentation debt score
When to use:
- New project onboarding
- Pre-compliance audit
- Major architecture changes
---
Incremental Audit (Monthly)
Scope: Recently changed components (last 30 days)
Duration: 1-2 days
Output:
- Mini coverage report (changed areas only)
- Updated backlog with new gaps
When to use:
- Ongoing maintenance
- After major feature releases
Example:
# Find files changed in last 30 days
git diff --name-only @{30.days.ago} HEAD -- src/
# Check if corresponding docs were updated
git diff --name-only @{30.days.ago} HEAD -- docs/---
Targeted Audit (Ad-hoc)
Scope: Specific component or area
Duration: 1-4 hours
Output:
- Gap analysis for target area
- Documentation plan
When to use:
- New team onboarding
- External partner integration
- Pre-feature launch
Example:
## Targeted Audit: Payment Service
**Scope**: All payment-related components
**Findings**:
- PaymentController: Documented [check]
- PaymentService: Not documented [x]
- PaymentWebhook: Not documented [x]
- StripeClient: Partially documented ~
**Action**: Document PaymentService and PaymentWebhook (P1)---
Audit Checklist
Pre-Audit
- [ ] Identify documentation locations (docs/, wiki, README)
- [ ] List all known documentation files
- [ ] Understand project structure and naming conventions
- [ ] Identify target audience (developers, operators, external integrators)
- [ ] Select audit type (full, incremental, targeted)
- [ ] Allocate time (full = 1-2 weeks, incremental = 1-2 days, targeted = 1-4 hours)
During Audit
- [ ] Scan API layer for undocumented endpoints
- [ ] Scan service layer for undocumented services
- [ ] Scan data layer for undocumented entities
- [ ] Scan event layer for undocumented topics/schemas
- [ ] Scan infrastructure for undocumented jobs/configs
- [ ] Check for outdated documentation (code changed, docs didn't)
- [ ] Identify documentation that references non-existent code (orphaned docs)
- [ ] Record findings in coverage report template
- [ ] Categorize gaps by priority (P1, P2, P3)
Post-Audit
- [ ] Generate coverage report (use template)
- [ ] Calculate documentation debt score
- [ ] Prioritize gaps by impact and effort
- [ ] Create documentation backlog
- [ ] Assign ownership for critical gaps (P1)
- [ ] Schedule documentation generation sprints
- [ ] Schedule follow-up audit (quarterly for full, monthly for incremental)
- [ ] Share report with stakeholders (eng managers, tech leads, product)
---
Tools and Automation
Manual Audit Tools
- ripgrep (rg): Fast code search
- grep: Standard text search
- find: File discovery
- diff: Compare component lists with docs
- wc: Count components
- Spreadsheet: Track coverage mapping
Automated Audit Tools
- OpenAPI diff: Compare spec versions
- Swagger coverage: Check endpoint documentation
- Custom scripts: Count components vs documented items
- Git hooks: Prevent commits without docs
Documentation Generation Tools
- Swagger/OpenAPI Generator: Auto-generate API docs
- TypeDoc: Generate TypeScript docs
- Docfx: Generate .NET docs
- Sphinx: Generate Python docs
- Mermaid: Generate diagrams as code
---
Common Challenges
Challenge: Too Many Gaps (Debt Score > 100)
Solution: Break into phases
1. Phase 1: Document P1 gaps only (2-3 sprints) 2. Phase 2: Document top 10 P2 gaps (1-2 sprints) 3. Phase 3: Ongoing P3 documentation (opportunistic)
Challenge: Outdated Documentation
Solution: Archive or update
- If code exists but changed: Update docs
- If code no longer exists: Archive docs (move to
docs/.archive/) - If uncertain: Flag for review (add "[WARNING] Needs verification" badge)
Challenge: No Template Exists
Solution: Create custom template
1. Review similar documentation for inspiration 2. Consult docs-codebase templates 3. Create minimal viable template 4. Iterate based on feedback
Challenge: Documentation Not Used
Solution: Improve discoverability
- Add to main README
- Link from relevant code (inline comments)
- Share in onboarding guides
- Present in team meetings
---
Success Criteria
Immediate (After Audit)
- [ ] Coverage report clearly shows gaps with priorities
- [ ] Documentation backlog is actionable and assigned
- [ ] Critical gaps (P1) identified with owners
Short-term (1-2 Sprints)
- [ ] All P1 gaps documented
- [ ] Documentation coverage > 80% for external-facing components
- [ ] Documentation backlog actively managed
Long-term (Ongoing)
- [ ] Quarterly audits show improving coverage (upward trend)
- [ ] PR documentation checklist compliance > 90%
- [ ] "How do I" questions in Slack decrease
- [ ] Onboarding time for new engineers decreases
---
Related Resources
- Discovery Patterns - How to find components
- Priority Framework - How to prioritize gaps
- CI/CD Integration - How to automate checks
- Coverage Report Template - Report structure
- Documentation Backlog Template - Backlog tracking
CI/CD Integration for Documentation
This resource provides patterns for integrating documentation checks into CI/CD pipelines and PR workflows.
Updated January 2026: Added Spectral API linting, Swagger Coverage integration, AI-assisted doc generation triggers, and freshness tracking.
---
Contents
- Overview
- PR Template Documentation Checklist
- CI/CD Coverage Gates
- Pre-Commit Hooks
- Documentation Linters
- Automated Documentation Coverage Reports
- Summary
- Recommendations
- Automated Reminders
- Anti-Patterns to Avoid
- Best Practices
- API Contract Validation (January 2026)
- API Coverage Tools
- Freshness Tracking in CI
- AI-Assisted Documentation (Optional)
- Complete CI/CD Pipeline Example
- Related Resources
Overview
Automated documentation checks ensure that documentation stays current and new components are documented before merging. This guide covers:
1. PR template additions 2. CI/CD coverage gates 3. Pre-commit hooks 4. Documentation linters 5. API contract validation (Spectral, AsyncAPI CLI) 6. Coverage tools (Swagger Coverage, OpenAPI Coverage) 7. Freshness tracking integration 8. Automated reminders
---
PR Template Documentation Checklist
GitHub Pull Request Template
Add to .github/pull_request_template.md:
## Documentation Checklist
Please ensure documentation is updated for this PR:
- [ ] **New APIs**: Added to OpenAPI spec or API reference
- [ ] **New events/messages**: Added to event catalog with schema
- [ ] **Configuration changes**: Updated configuration reference
- [ ] **Breaking changes**: Noted in CHANGELOG.md with migration guide
- [ ] **Architecture changes**: ADR created if introducing new pattern
- [ ] **Database changes**: ER diagram updated, migration documented
- [ ] **External integrations**: Integration guide updated
**Documentation Location**: [Link to updated docs]
**N/A**: Check here if no documentation changes needed [ ]GitLab Merge Request Template
Add to .gitlab/merge_request_assets/default.md:
## Documentation Changes
- [ ] API documentation updated
- [ ] Event schemas documented
- [ ] Configuration changes documented
- [ ] Breaking changes in CHANGELOG
- [ ] ADR created (if applicable)
**Docs Link**: [Link]BitBucket Pull Request Template
Add to pull_request_template.md:
## Documentation
- [ ] Updated relevant documentation
- [ ] Added examples if introducing new feature
- [ ] Updated CHANGELOG for user-facing changes
**Note**: If no documentation needed, explain why below.---
CI/CD Coverage Gates
GitHub Actions
Create .github/workflows/docs-check.yml:
name: Documentation Coverage Check
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
jobs:
check-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Full history for comparison
- name: Check API Documentation Coverage
run: |
# Count total controllers
CONTROLLERS=$(find . -name "*Controller.cs" | wc -l)
# Count documented endpoints in OpenAPI
if [ -f "openapi/spec.yaml" ]; then
DOCUMENTED=$(yq '.paths | keys | length' openapi/spec.yaml)
else
DOCUMENTED=0
fi
echo "Total Controllers: $CONTROLLERS"
echo "Documented Endpoints: $DOCUMENTED"
# Warning if significant gap
if [ $CONTROLLERS -gt $(($DOCUMENTED + 5)) ]; then
echo "::warning::Some APIs may be undocumented"
fi
- name: Check for Undocumented Public APIs
run: |
# Find public API controllers
PUBLIC_APIS=$(rg "public class.*Controller" --type cs --files-with-matches)
# Check if each has corresponding doc
MISSING=0
for file in $PUBLIC_APIS; do
CONTROLLER=$(basename $file .cs)
if ! grep -q "$CONTROLLER" docs/api/*.md 2>/dev/null; then
echo "::warning file=$file::$CONTROLLER may need documentation"
MISSING=$((MISSING + 1))
fi
done
if [ $MISSING -gt 0 ]; then
echo "::warning::Found $MISSING potentially undocumented controllers"
fi
- name: Check CHANGELOG Updated
run: |
# Check if CHANGELOG.md was updated in this PR
if git diff --name-only origin/main HEAD | grep -q "CHANGELOG.md"; then
echo "CHANGELOG.md updated [check]"
else
# Check if this is a feature/fix branch
if [[ "$GITHUB_HEAD_REF" =~ ^(feature|fix)/ ]]; then
echo "::warning::Consider updating CHANGELOG.md for user-facing changes"
fi
fi
- name: Check for Broken Links
uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
folder-path: 'docs'
config-file: '.github/markdown-link-check-config.json'
- name: Lint Markdown Files
uses: avto-dev/markdown-lint@v1
with:
args: './docs'GitLab CI
Create .gitlab-ci.yml:
docs-check:
stage: test
script:
# Check API coverage
- CONTROLLERS=$(find . -name "*Controller.cs" | wc -l)
- DOCUMENTED=$(grep -r "## Endpoints" docs/api/ | wc -l)
- echo "Controllers: $CONTROLLERS, Documented: $DOCUMENTED"
- |
if [ $CONTROLLERS -gt $(($DOCUMENTED + 5)) ]; then
echo "Warning: Potential documentation gap"
fi
# Check for broken links
- npm install -g markdown-link-check
- find docs/ -name "*.md" -exec markdown-link-check {} \;
only:
- merge_requests
- mainJenkins Pipeline
Create Jenkinsfile:
pipeline {
agent any
stages {
stage('Documentation Check') {
steps {
script {
// Count controllers
def controllers = sh(
script: "find . -name '*Controller.cs' | wc -l",
returnStdout: true
).trim()
// Count documented endpoints
def documented = sh(
script: "grep -r '## Endpoints' docs/api/ | wc -l",
returnStdout: true
).trim()
echo "Controllers: ${controllers}, Documented: ${documented}"
// Warning if gap exists
if (controllers.toInteger() > documented.toInteger() + 5) {
currentBuild.result = 'UNSTABLE'
error "Documentation coverage warning"
}
}
}
}
stage('Markdown Lint') {
steps {
sh 'npx markdownlint docs/'
}
}
}
}---
Pre-Commit Hooks
Git Pre-Commit Hook
Create .git/hooks/pre-commit:
#!/bin/bash
# Check if CHANGELOG.md exists and branch is feature/fix
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [[ "$BRANCH" =~ ^(feature|fix)/ ]]; then
# Check if any staged files are code changes
CODE_CHANGES=$(git diff --cached --name-only | grep -E '\.(cs|ts|py|go|java)$')
if [ -n "$CODE_CHANGES" ]; then
# Check if CHANGELOG.md is staged
if ! git diff --cached --name-only | grep -q "CHANGELOG.md"; then
echo "WARNING: Consider updating CHANGELOG.md for this feature/fix"
echo "Continue anyway? (y/n)"
read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
exit 1
fi
fi
fi
fi
# Check for TODO comments in staged files
TODO_COUNT=$(git diff --cached | grep -c "TODO:")
if [ "$TODO_COUNT" -gt 0 ]; then
echo "Found $TODO_COUNT TODO comments in staged changes"
echo "Consider documenting or creating tickets for TODOs"
fi
exit 0Make executable:
chmod +x .git/hooks/pre-commitHusky Pre-Commit Hook (Node.js)
Install Husky:
npm install --save-dev husky
npx husky installCreate .husky/pre-commit:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Check if package.json changed without updating README
if git diff --cached --name-only | grep -q "package.json"; then
if ! git diff --cached --name-only | grep -q "README.md"; then
echo "[WARNING] package.json changed, consider updating README.md"
fi
fi
# Lint markdown files
npx markdownlint 'docs/**/*.md'---
Documentation Linters
Markdown Linting
Install markdownlint:
npm install -g markdownlint-cliCreate .markdownlint.json:
{
"default": true,
"MD013": false,
"MD033": false,
"MD041": false,
"line-length": false,
"no-inline-html": false,
"first-line-h1": false
}Run linter:
markdownlint 'docs/**/*.md'Vale Prose Linter
Install Vale:
brew install vale # macOSCreate .vale.ini:
StylesPath = .vale/styles
MinAlertLevel = suggestion
[*.md]
BasedOnStyles = write-good, proselintRun linter:
vale docs/Link Checking
Install markdown-link-check:
npm install -g markdown-link-checkCreate .github/markdown-link-check-config.json:
{
"ignorePatterns": [
{
"pattern": "^http://localhost"
}
],
"timeout": "20s",
"retryOn429": true,
"retryCount": 3,
"fallbackRetryDelay": "30s"
}Run link check:
find docs/ -name "*.md" -exec markdown-link-check {} \;---
Automated Documentation Coverage Reports
Coverage Report Script
Create scripts/check-docs-coverage.sh:
#!/bin/bash
# Configuration
DOCS_DIR="docs"
SRC_DIR="src"
REPORT_FILE="docs-coverage-report.md"
# Count components
CONTROLLERS=$(find $SRC_DIR -name "*Controller.cs" | wc -l)
SERVICES=$(find $SRC_DIR -name "*Service.cs" | wc -l)
DBCONTEXTS=$(find $SRC_DIR -name "*DbContext.cs" | wc -l)
# Count documentation
API_DOCS=$(find $DOCS_DIR/api -name "*.md" | wc -l)
SERVICE_DOCS=$(find $DOCS_DIR/services -name "*.md" | wc -l)
DATA_DOCS=$(find $DOCS_DIR/data -name "*.md" | wc -l)
# Calculate coverage
API_COVERAGE=$(awk "BEGIN {printf \"%.0f\", ($API_DOCS/$CONTROLLERS)*100}")
SERVICE_COVERAGE=$(awk "BEGIN {printf \"%.0f\", ($SERVICE_DOCS/$SERVICES)*100}")
DATA_COVERAGE=$(awk "BEGIN {printf \"%.0f\", ($DATA_DOCS/$DBCONTEXTS)*100}")
# Generate report
cat > $REPORT_FILE << EOF
# Documentation Coverage Report
Generated: $(date +"%Y-%m-%d %H:%M:%S")
## Summary
| Category | Components | Documented | Coverage |
|----------|------------|------------|----------|
| APIs | $CONTROLLERS | $API_DOCS | $API_COVERAGE% |
| Services | $SERVICES | $SERVICE_DOCS | $SERVICE_COVERAGE% |
| Data | $DBCONTEXTS | $DATA_DOCS | $DATA_COVERAGE% |
## Recommendations
$(if [ $API_COVERAGE -lt 80 ]; then echo "- Improve API documentation coverage (currently $API_COVERAGE%)"; fi)
$(if [ $SERVICE_COVERAGE -lt 60 ]; then echo "- Improve service documentation coverage (currently $SERVICE_COVERAGE%)"; fi)
$(if [ $DATA_COVERAGE -lt 60 ]; then echo "- Improve data documentation coverage (currently $DATA_COVERAGE%)"; fi)
EOF
echo "Report generated: $REPORT_FILE"
cat $REPORT_FILEMake executable:
chmod +x scripts/check-docs-coverage.shRun in CI:
- name: Generate Coverage Report
run: ./scripts/check-docs-coverage.sh
- name: Upload Coverage Report
uses: actions/upload-artifact@v3
with:
name: docs-coverage-report
path: docs-coverage-report.md---
Automated Reminders
GitHub Issue Templates
Create .github/ISSUE_TEMPLATE/documentation.md:
---
name: Documentation Request
about: Request documentation for a component
title: '[DOCS] '
labels: 'documentation'
assignees: ''
---
## Component to Document
**Type**: API / Service / Data / Event / Config
**Location**: `path/to/component`
**Priority**: P1 / P2 / P3
## Documentation Needed
- [ ] Purpose and responsibilities
- [ ] API reference (if applicable)
- [ ] Configuration options (if applicable)
- [ ] Examples
- [ ] Integration guide
## Context
[Why is this documentation needed?]Slack Reminders (via GitHub Actions)
Create .github/workflows/weekly-docs-reminder.yml:
name: Weekly Documentation Reminder
on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 9 AM
jobs:
remind:
runs-on: ubuntu-latest
steps:
- name: Send Slack Reminder
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "[DOCS] Weekly Documentation Reminder",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "[DOCS] *Weekly Documentation Reminder*\n\nPlease review documentation backlog and update coverage:\n- Priority 1 gaps: <link to backlog>\n- Documentation coverage: <link to report>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}---
Anti-Patterns to Avoid
1. Blocking Merges for Non-Critical Docs
Do NOT block PRs for P3 (nice to have) documentation gaps.
# Bad: Fails build for any missing docs
- name: Check Docs
run: |
if [ $UNDOCUMENTED -gt 0 ]; then
exit 1 # Blocks all PRs
fi
# Good: Warning for P3, error for P1
- name: Check Docs
run: |
if [ $P1_UNDOCUMENTED -gt 0 ]; then
exit 1 # Blocks PR
elif [ $P3_UNDOCUMENTED -gt 0 ]; then
echo "::warning::Nice-to-have docs missing"
fi2. Overly Strict Markdown Linting
Do NOT enforce pedantic markdown rules that slow down documentation.
// Bad: Too many rules
{
"MD001": true,
"MD003": true,
"MD004": true,
"MD005": true,
...50 more rules...
}
// Good: Focus on critical rules
{
"default": true,
"MD013": false, // Line length
"MD033": false, // Inline HTML (useful for tables)
"MD041": false // First line h1
}3. Ignoring Documentation Updates
Do NOT skip documentation checks for "urgent" fixes.
# Bad: Allows skipping checks
if: github.event.pull_request.labels.*.name != 'skip-docs-check'
# Good: Always check, but only warn for hotfixes
if: github.event.pull_request.labels.*.name == 'hotfix'
run: echo "::warning::Hotfix - docs check skipped, follow up required"---
Best Practices
1. Start with warnings, not errors: Introduce checks gradually 2. Make checks fast: Documentation checks should add <30 seconds to CI 3. Provide clear error messages: Tell developers exactly what's missing 4. Link to templates: Include links to documentation templates in error messages 5. Track coverage trends: Generate reports over time to show improvement 6. Celebrate improvements: Highlight teams/PRs that improve documentation coverage
---
API Contract Validation (January 2026)
Spectral for OpenAPI/AsyncAPI Linting
Install and configure Spectral for API documentation standards:
npm install -g @stoplight/spectral-cliCreate .spectral.yaml:
extends: ["spectral:oas", "spectral:asyncapi"]
rules:
# Require descriptions for all operations
operation-description: error
# Require examples for request/response bodies
oas3-valid-media-example: error
# Require tags for organization
operation-tag-defined: error
# Custom: Require error response documentation
operation-4xx-response:
description: "Operations must document 4xx error responses"
given: "$.paths[*][*]"
then:
field: "responses"
function: schema
functionOptions:
schema:
anyOf:
- required: ["400"]
- required: ["401"]
- required: ["403"]
- required: ["404"]GitHub Actions integration:
- name: Lint OpenAPI Spec
run: |
npx spectral lint openapi/spec.yaml --fail-severity error
- name: Lint AsyncAPI Spec
run: |
npx spectral lint asyncapi/events.yaml --fail-severity errorAsyncAPI CLI Validation
npm install -g @asyncapi/cli- name: Validate AsyncAPI
run: |
asyncapi validate asyncapi/events.yaml
asyncapi diff asyncapi/events.yaml asyncapi/events-previous.yaml --fail-on-breaking---
API Coverage Tools
Swagger Coverage
Track which endpoints are documented vs tested:
# Install
npm install -g swagger-coverage-commandline
# Generate coverage report
swagger-coverage-commandline -s openapi/spec.yaml -i postman/newman-results.jsonGitHub Actions integration:
- name: Run API Tests
run: newman run postman/collection.json --reporters cli,json --reporter-json-export newman-results.json
- name: Generate API Coverage Report
run: |
swagger-coverage-commandline \
-s openapi/spec.yaml \
-i newman-results.json \
--output coverage-report
- name: Upload Coverage Report
uses: actions/upload-artifact@v4
with:
name: api-coverage-report
path: coverage-report/OpenAPI Coverage (open-api-coverage)
npm install open-api-coverage// coverage-check.js
const { CoverageCollector } = require('open-api-coverage');
const collector = new CoverageCollector({
specPath: './openapi/spec.yaml'
});
// After running tests
const report = collector.getReport();
console.log(`API Coverage: ${report.coveragePercent}%`);
if (report.coveragePercent < 80) {
console.error('API coverage below 80%');
process.exit(1);
}---
Freshness Tracking in CI
Check Documentation Age
- name: Check P1 Doc Freshness
run: |
STALE=0
for doc in docs/api/*.md; do
AGE=$(( ($(date +%s) - $(git log -1 --format="%ct" -- "$doc")) / 86400 ))
if [ $AGE -gt 30 ]; then
echo "::error file=$doc::P1 doc is $AGE days stale (max 30)"
STALE=$((STALE + 1))
fi
done
[ $STALE -gt 0 ] && exit 1 || exit 0See freshness-tracking.md for complete freshness tracking patterns.
---
AI-Assisted Documentation (Optional)
Trigger Doc Generation on Code Changes
- name: Check for Undocumented Endpoints
id: check-undoc
run: |
# Find new controllers without corresponding docs
NEW_CONTROLLERS=$(git diff --name-only origin/main HEAD | grep -E 'Controller\.(ts|cs|py)$')
UNDOC=""
for ctrl in $NEW_CONTROLLERS; do
DOC_NAME=$(basename "$ctrl" | sed 's/Controller.*//')
if ! ls docs/api/*${DOC_NAME}* 2>/dev/null; then
UNDOC="$UNDOC $ctrl"
fi
done
echo "undocumented=$UNDOC" >> $GITHUB_OUTPUT
- name: Request AI Doc Draft
if: steps.check-undoc.outputs.undocumented != ''
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Documentation Required
New endpoints detected without documentation:
${{ steps.check-undoc.outputs.undocumented }}
Consider using AI-assisted doc generation (Mintlify, DocuWriter.ai) and submit for review.`
});---
Complete CI/CD Pipeline Example
name: Documentation Quality Gate
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
docs-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# Markdown quality
- name: Lint Markdown
uses: avto-dev/markdown-lint@v1
with:
args: './docs'
- name: Check Links
uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
folder-path: 'docs'
# API contract validation
- name: Lint OpenAPI
run: npx @stoplight/spectral-cli lint openapi/spec.yaml --fail-severity error
- name: Lint AsyncAPI
run: npx @asyncapi/cli validate asyncapi/events.yaml
# Coverage analysis
- name: Check Documentation Coverage
run: ./scripts/check-docs-coverage.sh
# Freshness check (P1 only)
- name: Check P1 Freshness
run: |
for doc in docs/api/*.md; do
AGE=$(( ($(date +%s) - $(git log -1 --format="%ct" -- "$doc")) / 86400 ))
[ $AGE -gt 30 ] && echo "::error::$doc is $AGE days stale" && exit 1
done
# Generate report
- name: Generate Coverage Report
run: ./scripts/generate-coverage-report.sh > $GITHUB_STEP_SUMMARY---
Related Resources
- Audit Workflows - How to conduct documentation audits
- Priority Framework - How to prioritize documentation
- Discovery Patterns - How to find undocumented components
- Freshness Tracking - Documentation staleness detection
Component Discovery Patterns
This resource provides language-specific and framework-specific patterns for discovering documentable components in codebases.
---
Contents
- Overview
- .NET/C# Codebase
- Node.js/TypeScript Codebase
- Python Codebase
- Go Codebase
- Java/Spring Codebase
- React/Frontend Codebase
- Discovery Commands
- Cross-Reference Discovery
- Documentation Coverage Commands
- Multi-Language Projects
- Best Practices
- Related Resources
Overview
Discovery patterns help identify all components that should be documented across different technology stacks. Use these patterns with your search tool (grep, ripgrep, IDE search) to find undocumented components.
---
.NET/C# Codebase
API Layer
# Find all controllers
**/*Controller.cs
**/Controllers/**/*.cs
# Find all API models
**/*Request.cs
**/*Response.cs
**/*Dto.cs
**/Models/Api/**/*.csService Layer
# Find all services
**/*Service.cs
**/Services/**/*.cs
# Find all handlers (CQRS)
**/*Handler.cs
**/*CommandHandler.cs
**/*QueryHandler.cs
**/Commands/**/*.cs
**/Queries/**/*.csData Layer
# Find all DbContexts
**/*DbContext.cs
**/*Context.cs
# Find all entities
**/*Entity.cs
**/Entities/**/*.cs
**/Models/Data/**/*.cs
# Find all migrations
**/Migrations/**/*.csMessaging Layer
# Find all Kafka/message handlers
**/*MessageHandler.cs
**/*Consumer.cs
**/*Producer.cs
**/*EventHandler.cs
# Find all message models
**/Models/Kafka/**/*.cs
**/Models/Messages/**/*.cs
**/Events/**/*.csInfrastructure Layer
# Find all background services
**/*HostedService.cs
**/*BackgroundService.cs
**/*Job.cs
**/Jobs/**/*.cs
# Find all configuration options
**/*Options.cs
**/*Settings.cs
**/*Configuration.cs
**/Configuration/**/*.cs---
Node.js/TypeScript Codebase
API Layer
# Find all routes/controllers
**/routes/**/*.ts
**/routes/**/*.js
**/controllers/**/*.ts
**/controllers/**/*.js
# Find all API validators
**/validators/**/*.ts
**/schemas/**/*.tsService Layer
# Find all services
**/services/**/*.ts
**/services/**/*.js
# Find all use cases
**/use-cases/**/*.ts
**/usecases/**/*.tsData Layer
# Find all models
**/models/**/*.ts
**/models/**/*.js
**/entities/**/*.ts
# Find all repositories
**/repositories/**/*.ts
**/repositories/**/*.jsMiddleware
# Find all middleware
**/middleware/**/*.ts
**/middleware/**/*.js
**/middlewares/**/*.tsEvent Handlers
# Find all event handlers
**/events/**/*.ts
**/handlers/**/*.ts
**/subscribers/**/*.ts
**/listeners/**/*.tsConfiguration
# Find all config files
**/config/**/*.ts
**/config/**/*.js
**/*.config.ts
**/*.config.js---
Python Codebase
API Layer
# Find all views/endpoints
**/views.py
**/routes.py
**/api/**/*.py
**/endpoints/**/*.py
# Find all serializers
**/serializers.py
**/schemas.pyService Layer
# Find all services
**/services/**/*.py
**/use_cases/**/*.pyData Layer
# Find all models
**/models.py
**/models/**/*.py
# Find all repositories
**/repositories/**/*.py
**/db/**/*.pyBackground Tasks
# Find all tasks/jobs
**/tasks.py
**/celery/**/*.py
**/workers/**/*.pyConfiguration
# Find all settings
**/settings.py
**/config.py
**/config/**/*.py---
Go Codebase
API Layer
# Find all handlers
**/handlers/**/*.go
**/api/**/*.go
# Find all routes
**/routes/**/*.go
**/router/**/*.goService Layer
# Find all services
**/services/**/*.go
**/service/**/*.goData Layer
# Find all models
**/models/**/*.go
**/entities/**/*.go
# Find all repositories
**/repository/**/*.go
**/repositories/**/*.goConfiguration
# Find all config
**/config/**/*.go---
Java/Spring Codebase
API Layer
# Find all controllers
**/*Controller.java
**/controllers/**/*.java
# Find all REST endpoints
@RestController
@RequestMappingService Layer
# Find all services
**/*Service.java
**/services/**/*.java
# Find all components
@Service
@ComponentData Layer
# Find all entities
**/*Entity.java
**/entities/**/*.java
**/models/**/*.java
# Find all repositories
**/*Repository.java
**/repositories/**/*.javaConfiguration
# Find all configuration
**/*Configuration.java
**/*Config.java
**/config/**/*.java---
React/Frontend Codebase
Components
# Find all components
**/components/**/*.tsx
**/components/**/*.jsx
# Find all pages
**/pages/**/*.tsx
**/app/**/*.tsx (Next.js)State Management
# Find all stores
**/store/**/*.ts
**/stores/**/*.ts
**/state/**/*.ts
# Find all reducers
**/reducers/**/*.ts
**/slices/**/*.tsAPI Layer
# Find all API clients
**/api/**/*.ts
**/services/**/*.ts
**/lib/api/**/*.tsHooks
# Find all custom hooks
**/hooks/**/*.ts
use*.ts---
Discovery Commands
Using ripgrep (rg)
# Find all exported functions
rg "export (function|const)" --type ts
# Find all classes
rg "^class \w+" --type cs
# Find all API routes
rg "@(Get|Post|Put|Delete|Patch)" --type cs
rg "router\.(get|post|put|delete|patch)" --type ts
# Find all database models
rg "class.*DbContext" --type cs
rg "class.*extends Model" --type tsUsing grep
# Find all controllers
grep -r "Controller" --include="*.cs"
# Find all services
grep -r "Service" --include="*.ts"
# Find all endpoints
grep -r "@api" --include="*.py"Using find + grep
# Find all TypeScript service files
find . -name "*Service.ts" -o -name "*service.ts"
# Find all Python models
find . -name "models.py"
# Find all configuration files
find . -name "*Config.cs" -o -name "*Options.cs"---
Cross-Reference Discovery
Find Kafka Topics
# .NET
rg "Topic = \"" --type cs
rg "KafkaTopicAttribute" --type cs
# Node.js
rg "topic:" --type ts
rg "TOPIC_" --type ts
# Python
rg "topic=" --type pyFind External API Integrations
# HTTP clients
rg "HttpClient" --type cs
rg "axios\." --type ts
rg "requests\." --type py
# Base URLs
rg "BaseAddress|baseURL|base_url"Find Webhooks
# Webhook handlers
rg "webhook" -i
rg "callback" -i
# Signature verification
rg "signature|hmac" -i---
Documentation Coverage Commands
Count undocumented components
# Count total controllers
CONTROLLERS=$(rg "class.*Controller" --type cs | wc -l)
# Count documented endpoints
DOCUMENTED=$(rg "## Endpoints" docs/api/ | wc -l)
# Calculate gap
echo "Gap: $(($CONTROLLERS - $DOCUMENTED))"Generate component list
# Extract all controller names
rg "class (\w+)Controller" --type cs -r '$1' --no-filename | sort
# Extract all service names
rg "class (\w+)Service" --type cs -r '$1' --no-filename | sort
# Extract all Kafka topics
rg "Topic = \"(\w+)\"" --type cs -r '$1' --no-filename | sort | uniq---
Multi-Language Projects
For projects with multiple languages:
1. Run discovery for each language separately 2. Consolidate results in coverage report 3. Prioritize by component type (API > Services > Config)
Example workflow
# Discover .NET components
rg "Controller|Service|DbContext" --type cs > discovered-dotnet.txt
# Discover TypeScript components
rg "Controller|Service|Repository" --type ts > discovered-ts.txt
# Discover Python components
rg "views|models|tasks" --type py > discovered-py.txt
# Compare with docs
diff discovered-dotnet.txt documented-components.txt---
Best Practices
1. Start broad, then narrow: Begin with file patterns, then search for specific annotations/decorators 2. Check naming conventions: Adjust patterns based on project conventions (Service vs service, Controller vs controller) 3. Search for interfaces too: Many projects define contracts in separate interface files 4. Look for tests: Test files often reveal undocumented components 5. Check migrations: Database migration files reveal schema changes that may need documentation
---
Related Resources
- Audit Workflows - How to use these patterns in systematic audits
- Priority Framework - How to prioritize discovered components
Documentation Quality Metrics
KPIs, scoring rubrics, and dashboards for measuring documentation quality, coverage, and freshness. Turns doc health from a gut feeling into data.
Contents
- Coverage Metrics
- Freshness Metrics
- Quality Scoring Rubrics
- Readability Metrics
- User Feedback Collection
- Documentation Health Dashboards
- SLOs for Documentation
- Automated Quality Scanning
- Prioritization Framework for Doc Debt
- Related Resources
---
Coverage Metrics
Coverage measures what percentage of your system has corresponding documentation.
Core Coverage Dimensions
| Metric | Formula | Target |
|---|---|---|
| Endpoint coverage | Documented endpoints / Total endpoints | 95% |
| Service coverage | Services with docs / Total services | 100% |
| Runbook coverage | Services with runbooks / Total services | 90% |
| Config coverage | Documented env vars / Total env vars | 85% |
| Event coverage | Documented events / Total events | 90% |
| Error code coverage | Documented errors / Total error codes | 80% |
Automated Coverage Calculation
"""
Calculate documentation coverage across multiple dimensions.
Outputs a structured report for dashboard consumption.
"""
import json
import yaml
import subprocess
from pathlib import Path
from dataclasses import dataclass, asdict
@dataclass
class CoverageResult:
dimension: str
total: int
documented: int
coverage_pct: float
gaps: list
def calculate_endpoint_coverage(spec_path: str, routes_dir: str) -> CoverageResult:
"""Compare documented endpoints against discovered routes."""
# Load documented endpoints from OpenAPI spec
with open(spec_path) as f:
spec = yaml.safe_load(f)
documented = set()
for path, methods in spec.get("paths", {}).items():
for method in methods:
if method in ("get", "post", "put", "patch", "delete"):
documented.add(f"{method.upper()} {path}")
# Discover routes from code (example: Express.js pattern)
result = subprocess.run(
["grep", "-rn", r"router\.\(get\|post\|put\|delete\)", routes_dir],
capture_output=True, text=True
)
discovered = set()
for line in result.stdout.strip().split("\n"):
if line:
# Parse route from grep output
parts = line.split("router.")
if len(parts) > 1:
discovered.add(parts[1].split("(")[0].upper())
gaps = list(discovered - documented)
return CoverageResult(
dimension="endpoints",
total=len(discovered),
documented=len(documented & discovered),
coverage_pct=round(len(documented & discovered) / max(len(discovered), 1) * 100, 1),
gaps=gaps,
)
def calculate_service_coverage(services_dir: str, docs_dir: str) -> CoverageResult:
"""Check which services have corresponding documentation."""
services = [d.name for d in Path(services_dir).iterdir() if d.is_dir()]
documented = [d.name for d in Path(docs_dir).iterdir() if d.is_dir()]
gaps = [s for s in services if s not in documented]
return CoverageResult(
dimension="services",
total=len(services),
documented=len(services) - len(gaps),
coverage_pct=round((len(services) - len(gaps)) / max(len(services), 1) * 100, 1),
gaps=gaps,
)
def generate_coverage_report(results: list[CoverageResult]) -> dict:
"""Generate a structured coverage report."""
return {
"timestamp": "2026-01-15T10:00:00Z",
"overall_coverage": round(
sum(r.coverage_pct for r in results) / len(results), 1
),
"dimensions": [asdict(r) for r in results],
"critical_gaps": [
gap
for r in results
if r.coverage_pct < 80
for gap in r.gaps
],
}Coverage Tracking Over Time
#!/bin/bash
# track-coverage.sh: Record coverage snapshot for trend analysis
DATE=$(date +%Y-%m-%d)
OUTPUT="metrics/coverage-${DATE}.json"
# Count documented vs total endpoints
TOTAL_ENDPOINTS=$(grep -c "router\.\(get\|post\|put\|delete\)" src/routes/*.ts)
DOCUMENTED_ENDPOINTS=$(yq eval '.paths | length' docs/openapi.yaml)
# Count services with runbooks
TOTAL_SERVICES=$(ls -d services/*/ | wc -l | tr -d ' ')
SERVICES_WITH_RUNBOOKS=$(find services -name "runbooks" -type d | wc -l | tr -d ' ')
# Count documented env vars
TOTAL_ENV_VARS=$(grep -c "process.env\." src/**/*.ts 2>/dev/null || echo 0)
DOCUMENTED_ENV_VARS=$(grep -c "^|" docs/configuration.md 2>/dev/null || echo 0)
cat > "$OUTPUT" << EOF
{
"date": "$DATE",
"endpoints": { "total": $TOTAL_ENDPOINTS, "documented": $DOCUMENTED_ENDPOINTS },
"services": { "total": $TOTAL_SERVICES, "with_runbooks": $SERVICES_WITH_RUNBOOKS },
"env_vars": { "total": $TOTAL_ENV_VARS, "documented": $DOCUMENTED_ENV_VARS }
}
EOF
echo "Coverage snapshot saved: $OUTPUT"---
Freshness Metrics
Freshness measures how current documentation is relative to the code it describes.
Docs-to-Code Age Delta
"""
Calculate the age delta between documentation files
and the code they document.
"""
import subprocess
from datetime import datetime, timezone
from pathlib import Path
def get_last_modified(file_path: str) -> datetime:
"""Get last git commit date for a file."""
result = subprocess.run(
["git", "log", "-1", "--format=%aI", "--", file_path],
capture_output=True, text=True
)
if result.stdout.strip():
return datetime.fromisoformat(result.stdout.strip())
return datetime.min.replace(tzinfo=timezone.utc)
def calculate_freshness(doc_code_pairs: list[tuple[str, str]]) -> list[dict]:
"""
Calculate freshness for doc/code pairs.
Args:
doc_code_pairs: List of (doc_path, code_path) tuples
"""
results = []
for doc_path, code_path in doc_code_pairs:
doc_date = get_last_modified(doc_path)
code_date = get_last_modified(code_path)
delta_days = (code_date - doc_date).days
status = "fresh"
if delta_days > 90:
status = "stale"
elif delta_days > 30:
status = "aging"
results.append({
"doc": doc_path,
"code": code_path,
"doc_last_updated": doc_date.isoformat(),
"code_last_updated": code_date.isoformat(),
"delta_days": max(delta_days, 0),
"status": status,
})
return sorted(results, key=lambda r: r["delta_days"], reverse=True)
# Example usage
pairs = [
("docs/api/users.md", "src/routes/users.ts"),
("docs/api/orders.md", "src/routes/orders.ts"),
("docs/architecture.md", "src/"),
]
for result in calculate_freshness(pairs):
print(f"[{result['status'].upper():6s}] {result['delta_days']:4d}d {result['doc']}")Freshness Thresholds
| Status | Age Delta | Action |
|---|---|---|
| Fresh | < 30 days | No action |
| Aging | 30-90 days | Flag for review in next sprint |
| Stale | 90-180 days | Prioritize update, add to sprint |
| Critical | > 180 days | Immediate review, may be dangerously wrong |
---
Quality Scoring Rubrics
Score individual documents on a standardized rubric.
Document Quality Scorecard
| Dimension | Weight | 0 (Missing) | 1 (Poor) | 2 (Adequate) | 3 (Good) | 4 (Excellent) |
|---|---|---|---|---|---|---|
| Accuracy | 30% | Known errors | Partially accurate | Mostly accurate | Accurate, minor gaps | Verified against code |
| Completeness | 25% | Stub only | Major gaps | Core content present | Comprehensive | Complete with edge cases |
| Currency | 20% | > 1 year old | > 6 months | > 3 months | < 3 months | Updated with last code change |
| Clarity | 15% | Unreadable | Confusing structure | Readable | Well-organized | Clear, with examples |
| Findability | 10% | No index entry | Hard to find | Indexed | Indexed + cross-linked | Searchable, tagged, linked |
Scoring Calculation
"""
Calculate documentation quality score for a single document.
"""
RUBRIC_WEIGHTS = {
"accuracy": 0.30,
"completeness": 0.25,
"currency": 0.20,
"clarity": 0.15,
"findability": 0.10,
}
def score_document(scores: dict[str, int]) -> dict:
"""
Score a document against the quality rubric.
Args:
scores: Dict of dimension -> score (0-4)
Returns:
Dict with weighted score and grade
"""
weighted_total = sum(
scores.get(dim, 0) * weight
for dim, weight in RUBRIC_WEIGHTS.items()
)
max_possible = sum(4 * w for w in RUBRIC_WEIGHTS.values())
normalized = round(weighted_total / max_possible * 100, 1)
grade = "F"
if normalized >= 90:
grade = "A"
elif normalized >= 80:
grade = "B"
elif normalized >= 70:
grade = "C"
elif normalized >= 60:
grade = "D"
return {
"raw_scores": scores,
"weighted_score": round(weighted_total, 2),
"normalized_pct": normalized,
"grade": grade,
"lowest_dimension": min(scores, key=scores.get),
}
# Example
result = score_document({
"accuracy": 3,
"completeness": 2,
"currency": 4,
"clarity": 3,
"findability": 2,
})
print(f"Grade: {result['grade']} ({result['normalized_pct']}%)")
print(f"Weakest area: {result['lowest_dimension']}")---
Readability Metrics
Flesch-Kincaid for Technical Docs
"""
Calculate readability metrics for documentation.
Technical docs should target grade level 8-12.
"""
import re
import math
def count_syllables(word: str) -> int:
word = word.lower()
count = 0
vowels = "aeiouy"
if word[0] in vowels:
count += 1
for i in range(1, len(word)):
if word[i] in vowels and word[i - 1] not in vowels:
count += 1
if word.endswith("e"):
count -= 1
return max(count, 1)
def flesch_kincaid_grade(text: str) -> dict:
"""Calculate Flesch-Kincaid grade level and reading ease."""
# Strip code blocks (they skew readability scores)
text = re.sub(r'```[\s\S]*?```', '', text)
text = re.sub(r'`[^`]+`', 'CODE', text)
sentences = re.split(r'[.!?]+', text)
sentences = [s.strip() for s in sentences if s.strip()]
words = re.findall(r'\b[a-zA-Z]+\b', text)
if not sentences or not words:
return {"grade_level": 0, "reading_ease": 0}
total_syllables = sum(count_syllables(w) for w in words)
avg_sentence_length = len(words) / len(sentences)
avg_syllables_per_word = total_syllables / len(words)
grade = 0.39 * avg_sentence_length + 11.8 * avg_syllables_per_word - 15.59
ease = 206.835 - 1.015 * avg_sentence_length - 84.6 * avg_syllables_per_word
return {
"grade_level": round(grade, 1),
"reading_ease": round(ease, 1),
"word_count": len(words),
"sentence_count": len(sentences),
"avg_sentence_length": round(avg_sentence_length, 1),
}
# Target ranges for technical documentation
READABILITY_TARGETS = {
"api_reference": {"grade": (8, 12), "ease": (40, 60)},
"tutorial": {"grade": (6, 10), "ease": (50, 70)},
"runbook": {"grade": (6, 8), "ease": (60, 80)},
"architecture": {"grade": (10, 14), "ease": (30, 50)},
}Readability Targets by Doc Type
| Doc Type | Grade Level | Reading Ease | Rationale |
|---|---|---|---|
| Runbooks | 6-8 | 60-80 | Must be understood under stress |
| Tutorials | 6-10 | 50-70 | Aimed at learners |
| API Reference | 8-12 | 40-60 | Technical but structured |
| Architecture Docs | 10-14 | 30-50 | Complex topics, expert audience |
---
User Feedback Collection
In-Doc Feedback Widget
// Minimal doc feedback component
// Embed at bottom of each documentation page
function DocFeedback({ pageId, pageTitle }) {
const [rating, setRating] = useState(null);
const [feedback, setFeedback] = useState("");
const submitFeedback = async () => {
await fetch("/api/doc-feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
page_id: pageId,
page_title: pageTitle,
rating, // "helpful" | "not_helpful"
feedback, // Free text
timestamp: new Date().toISOString(),
user_agent: navigator.userAgent,
}),
});
};
return (
<div className="doc-feedback">
<p>Was this page helpful?</p>
<button onClick={() => { setRating("helpful"); submitFeedback(); }}>
Yes
</button>
<button onClick={() => { setRating("not_helpful"); submitFeedback(); }}>
No
</button>
{rating === "not_helpful" && (
<textarea
placeholder="What was missing or incorrect?"
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
onBlur={submitFeedback}
/>
)}
</div>
);
}Feedback Metrics to Track
| Metric | Formula | Target |
|---|---|---|
| Helpfulness rate | Helpful votes / Total votes | > 80% |
| Feedback volume | Feedback submissions per week | Increasing trend |
| Issue resolution time | Time from feedback to doc fix | < 5 business days |
| Top unhelpful pages | Pages with lowest helpfulness | Prioritize for rewrite |
---
Documentation Health Dashboards
Dashboard Panels (Grafana)
{
"dashboard": {
"title": "Documentation Health",
"panels": [
{
"title": "Overall Coverage",
"type": "gauge",
"targets": [
{ "expr": "doc_coverage_pct{dimension='endpoints'}" },
{ "expr": "doc_coverage_pct{dimension='services'}" },
{ "expr": "doc_coverage_pct{dimension='runbooks'}" }
],
"thresholds": [
{ "value": 70, "color": "red" },
{ "value": 85, "color": "yellow" },
{ "value": 95, "color": "green" }
]
},
{
"title": "Freshness Distribution",
"type": "piechart",
"targets": [
{ "expr": "count(doc_age_days < 30)", "legendFormat": "Fresh" },
{ "expr": "count(doc_age_days >= 30 and doc_age_days < 90)", "legendFormat": "Aging" },
{ "expr": "count(doc_age_days >= 90)", "legendFormat": "Stale" }
]
},
{
"title": "Quality Score Trend",
"type": "timeseries",
"targets": [
{ "expr": "avg(doc_quality_score)", "legendFormat": "Avg Quality" }
]
},
{
"title": "Top 10 Stale Documents",
"type": "table",
"targets": [
{ "expr": "topk(10, doc_age_days)" }
]
}
]
}
}Dashboard Layout
+---------------------------+---------------------------+
| Overall Coverage (gauge) | Freshness Distribution |
| Endpoints: 94% | [pie chart] |
| Services: 100% | Fresh: 65% |
| Runbooks: 85% | Aging: 25% |
| | Stale: 10% |
+---------------------------+---------------------------+
| Quality Score Trend | User Feedback Rate |
| [line chart over 90d] | Helpful: 82% |
| Avg: 78/100 | Not helpful: 18% |
+---------------------------+---------------------------+
| Top 10 Stale Documents | Coverage Gaps |
| [table: doc, age, owner] | [table: component, type] |
+---------------------------+---------------------------+---
SLOs for Documentation
Recommended Documentation SLOs
| SLO | Target | Window | Measurement |
|---|---|---|---|
| Endpoint coverage | >= 95% | Rolling 30 days | CI coverage check |
| Runbook coverage (Tier 1) | 100% | Rolling 30 days | Runbook inventory scan |
| Freshness (no doc > 180 days stale) | 100% | Rolling 30 days | Git age delta |
| Quality score average | >= 75/100 | Rolling 90 days | Quarterly audit |
| User helpfulness rating | >= 80% | Rolling 30 days | Feedback widget |
| Broken link rate | < 1% | Rolling 7 days | CI link checker |
| Doc review SLA (new service) | Docs within 2 sprints | Per service launch | Tracking ticket |
Error Budget for Documentation
documentation_slos:
- name: endpoint-coverage
target: 0.95
window: 30d
measurement: documented_endpoints / total_endpoints
error_budget_policy:
- threshold: 50%
action: "Flag in sprint planning"
- threshold: 25%
action: "Documentation sprint required"
- threshold: 0%
action: "Block new service launches until coverage restored"
- name: freshness
target: 1.0 # No docs older than 180 days
window: 30d
measurement: fresh_docs / total_docs
error_budget_policy:
- threshold: 50%
action: "Assign stale docs to owners"
- threshold: 0%
action: "Dedicated doc refresh sprint"---
Automated Quality Scanning
CI Pipeline for Doc Quality
# .github/workflows/doc-quality.yaml
name: Documentation Quality Gate
on:
pull_request:
paths:
- "docs/**"
- "*.md"
jobs:
quality-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check broken links
uses: lycheeverse/lychee-action@v1
with:
args: --verbose --no-progress "docs/**/*.md"
fail: true
- name: Lint markdown
uses: DavidAnson/markdownlint-cli2-action@v16
with:
globs: "docs/**/*.md"
- name: Check spelling
uses: streetsidesoftware/cspell-action@v6
with:
files: "docs/**/*.md"
- name: Validate code examples
run: |
# Extract and syntax-check code blocks
python scripts/validate-code-blocks.py docs/
- name: Coverage ratchet check
run: |
# Ensure coverage doesn't decrease
CURRENT=$(python scripts/measure-coverage.py)
BASELINE=$(cat metrics/coverage-baseline.txt)
if [ "$CURRENT" -lt "$BASELINE" ]; then
echo "::error::Documentation coverage decreased from $BASELINE% to $CURRENT%"
exit 1
fiQuality Scanning Tools
| Tool | What It Checks | Integration |
|---|---|---|
| lychee | Broken links | GitHub Action, CLI |
| markdownlint | Markdown formatting | GitHub Action, npm |
| cspell | Spelling errors | GitHub Action, npm |
| vale | Style and tone consistency | GitHub Action, CLI |
| textlint | Custom writing rules | npm |
| alex | Inclusive language | npm |
---
Prioritization Framework for Doc Debt
Doc Debt Priority Matrix
| Impact | High Traffic | Medium Traffic | Low Traffic |
|---|---|---|---|
| Stale + Inaccurate | P0 - Fix now | P1 - This sprint | P2 - Next sprint |
| Stale + Accurate | P2 - Next sprint | P3 - Backlog | P4 - Opportunistic |
| Missing (critical path) | P0 - Fix now | P1 - This sprint | P2 - Next sprint |
| Missing (edge case) | P2 - Next sprint | P3 - Backlog | P4 - Opportunistic |
| Style/formatting only | P3 - Backlog | P4 - Opportunistic | P5 - Skip |
Doc Debt Tracking
"""
Score and prioritize documentation debt items.
Higher score = higher priority.
"""
def calculate_doc_debt_priority(
traffic_percentile: int, # 0-100, page view percentile
staleness_days: int, # Days since last update
is_inaccurate: bool, # Known inaccuracies
is_missing: bool, # Doc doesn't exist yet
is_critical_path: bool, # On a critical user journey
incident_mentions: int, # Times referenced in incidents
) -> dict:
score = 0
# Traffic weight (0-30)
score += min(traffic_percentile * 0.3, 30)
# Staleness weight (0-25)
if staleness_days > 180:
score += 25
elif staleness_days > 90:
score += 15
elif staleness_days > 30:
score += 5
# Accuracy weight (0-25)
if is_inaccurate:
score += 25
if is_missing:
score += 20
# Critical path weight (0-10)
if is_critical_path:
score += 10
# Incident correlation (0-10)
score += min(incident_mentions * 5, 10)
priority = "P4"
if score >= 70:
priority = "P0"
elif score >= 50:
priority = "P1"
elif score >= 30:
priority = "P2"
elif score >= 15:
priority = "P3"
return {"score": round(score, 1), "priority": priority}---
Related Resources
- API Docs Validation - Validating API documentation accuracy
- Runbook Testing - Testing operational runbooks
- Freshness Tracking - Detecting stale documentation
- Priority Framework - Prioritizing documentation work
- CI/CD Integration - Automation pipeline patterns
- SKILL.md - Parent skill overview
Documentation Freshness Tracking
Track documentation staleness, detect drift from code, and maintain up-to-date docs across your codebase.
---
Contents
- Overview
- Freshness Metadata Standards
- Automated Staleness Detection
- CI/CD Freshness Gates
- Observability Dashboards
- Integration with Code Changes
- Freshness Review Process
- Monthly Documentation Freshness Review
- Tools and Integrations
- Related Resources
Overview
Documentation freshness is how current your docs are relative to the code they describe. Stale documentation is often worse than no documentation - it misleads developers and creates debugging overhead.
This guide covers:
1. Freshness metadata standards 2. Automated staleness detection 3. Git-based freshness analysis 4. CI/CD freshness gates 5. Observability dashboards
---
Freshness Metadata Standards
Required Metadata Fields
Add frontmatter to critical documentation:
---
title: User Authentication API
last_verified: 2026-01-15
owner: "@backend-team"
review_cadence: monthly
code_paths:
- src/auth/**
- src/middleware/auth.ts
---Field Definitions
| Field | Required | Description |
|---|---|---|
last_verified | Yes | Date someone confirmed doc matches code (ISO 8601) |
owner | Yes | Team or individual responsible for updates |
review_cadence | Yes | How often to review (weekly, monthly, quarterly) |
code_paths | Recommended | Glob patterns for related source files |
expires | Optional | Hard deadline for mandatory review |
Staleness Thresholds
| Priority | Max Age | Action |
|---|---|---|
| P1 (External APIs) | 30 days | Block deploys if stale |
| P2 (Internal APIs) | 60 days | Warning in CI |
| P3 (Config/Utils) | 90 days | Backlog item |
---
Automated Staleness Detection
Recommended (cross-platform): use scripts/docs_freshness_report.py from this skill to generate a Markdown freshness report from last_verified frontmatter.
Example:
python3 frameworks/shared-skills/skills/qa-docs-coverage/scripts/docs_freshness_report.py --docs-root docs/Note: the bash snippets below assume GNU userland (example: date -d). On macOS, prefer the Python script or adapt commands accordingly.
Git-Based Freshness Analysis
Compare doc modification dates against related code:
#!/bin/bash
# check-doc-freshness.sh
DOC_FILE="$1"
CODE_PATTERN="$2"
# Get last doc update
DOC_DATE=$(git log -1 --format="%ct" -- "$DOC_FILE")
# Get last code update for related files
CODE_DATE=$(git log -1 --format="%ct" -- "$CODE_PATTERN")
# Calculate drift in days
DRIFT=$(( (CODE_DATE - DOC_DATE) / 86400 ))
if [ $DRIFT -gt 30 ]; then
echo "WARNING: $DOC_FILE is $DRIFT days behind code changes"
exit 1
fi
echo "OK: $DOC_FILE is fresh (drift: $DRIFT days)"Usage Example
# Check if API docs are fresh relative to controllers
./check-doc-freshness.sh docs/api/users.md "src/controllers/users*.ts"Batch Freshness Report
#!/bin/bash
# generate-freshness-report.sh
echo "# Documentation Freshness Report"
echo "Generated: $(date -I)"
echo ""
echo "| Document | Last Updated | Code Updated | Drift (days) | Status |"
echo "|----------|--------------|--------------|--------------|--------|"
find docs/ -name "*.md" | while read doc; do
DOC_DATE=$(git log -1 --format="%cs" -- "$doc" 2>/dev/null || echo "unknown")
# Extract code_paths from frontmatter if present
CODE_PATHS=$(grep -A1 "code_paths:" "$doc" | tail -1 | sed 's/- //')
if [ -n "$CODE_PATHS" ]; then
CODE_DATE=$(git log -1 --format="%cs" -- "$CODE_PATHS" 2>/dev/null || echo "unknown")
if [ "$DOC_DATE" != "unknown" ] && [ "$CODE_DATE" != "unknown" ]; then
DOC_TS=$(date -d "$DOC_DATE" +%s)
CODE_TS=$(date -d "$CODE_DATE" +%s)
DRIFT=$(( (CODE_TS - DOC_TS) / 86400 ))
if [ $DRIFT -gt 60 ]; then
STATUS="STALE"
elif [ $DRIFT -gt 30 ]; then
STATUS="WARNING"
else
STATUS="OK"
fi
else
DRIFT="N/A"
STATUS="UNKNOWN"
fi
else
CODE_DATE="N/A"
DRIFT="N/A"
STATUS="NO_TRACKING"
fi
echo "| $doc | $DOC_DATE | $CODE_DATE | $DRIFT | $STATUS |"
done---
CI/CD Freshness Gates
GitHub Actions
name: Documentation Freshness Check
on:
pull_request:
branches: [main]
schedule:
- cron: '0 9 * * 1' # Weekly Monday 9 AM
jobs:
freshness-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for git log
- name: Check P1 Documentation Freshness
run: |
STALE_COUNT=0
# Check each P1 doc
for doc in docs/api/*.md; do
DOC_DATE=$(git log -1 --format="%ct" -- "$doc")
NOW=$(date +%s)
AGE_DAYS=$(( (NOW - DOC_DATE) / 86400 ))
if [ $AGE_DAYS -gt 30 ]; then
echo "::error file=$doc::P1 doc is $AGE_DAYS days old (max 30)"
STALE_COUNT=$((STALE_COUNT + 1))
fi
done
if [ $STALE_COUNT -gt 0 ]; then
echo "::error::Found $STALE_COUNT stale P1 documents"
exit 1
fi
- name: Check P2 Documentation Freshness
run: |
for doc in docs/internal/*.md docs/events/*.md; do
DOC_DATE=$(git log -1 --format="%ct" -- "$doc" 2>/dev/null) || continue
NOW=$(date +%s)
AGE_DAYS=$(( (NOW - DOC_DATE) / 86400 ))
if [ $AGE_DAYS -gt 60 ]; then
echo "::warning file=$doc::P2 doc is $AGE_DAYS days old (recommended max 60)"
fi
done
- name: Generate Freshness Report
run: |
./scripts/generate-freshness-report.sh > freshness-report.md
cat freshness-report.md >> $GITHUB_STEP_SUMMARYGitLab CI
doc-freshness:
stage: validate
script:
- |
STALE=0
for doc in docs/api/*.md; do
AGE=$(( ($(date +%s) - $(git log -1 --format="%ct" -- "$doc")) / 86400 ))
if [ $AGE -gt 30 ]; then
echo "STALE: $doc ($AGE days)"
STALE=$((STALE + 1))
fi
done
if [ $STALE -gt 0 ]; then
echo "Found $STALE stale P1 documents"
exit 1
fi
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "schedule"---
Observability Dashboards
Metrics to Track
| Metric | Description | Target |
|---|---|---|
docs_coverage_percent | % of components with docs | > 80% |
docs_freshness_p1 | % of P1 docs updated in 30 days | 100% |
docs_freshness_p2 | % of P2 docs updated in 60 days | > 90% |
docs_drift_days_avg | Avg days between code and doc updates | < 14 |
docs_orphaned_count | Docs referencing deleted code | 0 |
Prometheus Metrics (Example)
# prometheus/docs-metrics.yml
groups:
- name: documentation
rules:
- record: docs:freshness:stale_p1_count
expr: count(docs_last_verified_days > 30 and docs_priority == "P1")
- record: docs:freshness:stale_p2_count
expr: count(docs_last_verified_days > 60 and docs_priority == "P2")
- alert: StaleP1Documentation
expr: docs:freshness:stale_p1_count > 0
for: 1d
labels:
severity: warning
annotations:
summary: "P1 documentation is stale"
description: "{{ $value }} P1 documents haven't been verified in 30+ days"Grafana Dashboard Query Examples
-- Average documentation drift by priority
SELECT
priority,
AVG(DATEDIFF(NOW(), last_verified)) as avg_drift_days
FROM docs_metadata
GROUP BY priority;
-- Stale documentation by team
SELECT
owner,
COUNT(*) as stale_count
FROM docs_metadata
WHERE DATEDIFF(NOW(), last_verified) >
CASE priority
WHEN 'P1' THEN 30
WHEN 'P2' THEN 60
ELSE 90
END
GROUP BY owner
ORDER BY stale_count DESC;---
Integration with Code Changes
PR Workflow: Detect Related Docs
#!/bin/bash
# find-related-docs.sh
# Run in PR to identify docs that may need updates
CHANGED_FILES=$(git diff --name-only origin/main HEAD)
echo "## Documentation Review Required"
echo ""
for file in $CHANGED_FILES; do
# Find docs that reference this file
RELATED_DOCS=$(grep -l "$file" docs/**/*.md 2>/dev/null)
if [ -n "$RELATED_DOCS" ]; then
echo "### $file"
echo "Related docs to review:"
echo "$RELATED_DOCS" | while read doc; do
echo "- [ ] $doc"
done
echo ""
fi
doneAutomated Doc Reminder Bot
# .github/workflows/doc-reminder.yml
name: Documentation Reminder
on:
pull_request:
types: [opened, synchronize]
jobs:
remind:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for Related Documentation
id: check
run: |
CHANGED=$(git diff --name-only origin/main HEAD | grep -E '\.(ts|js|py|go|cs)$')
RELATED_DOCS=""
for file in $CHANGED; do
DOCS=$(grep -rl "$file" docs/ 2>/dev/null || true)
RELATED_DOCS="$RELATED_DOCS $DOCS"
done
if [ -n "$RELATED_DOCS" ]; then
echo "found=true" >> $GITHUB_OUTPUT
echo "docs<<EOF" >> $GITHUB_OUTPUT
echo "$RELATED_DOCS" | tr ' ' '\n' | sort -u >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
- name: Comment on PR
if: steps.check.outputs.found == 'true'
uses: actions/github-script@v7
with:
script: |
const docs = `${{ steps.check.outputs.docs }}`;
const body = `## Documentation Review Reminder
The following documentation may be affected by this PR:
${docs.split('\n').map(d => `- [ ] ${d}`).join('\n')}
Please review and update if necessary.`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});---
Freshness Review Process
Monthly Review Checklist
## Monthly Documentation Freshness Review
**Date**: [YYYY-MM-DD]
**Reviewer**: [@username]
### P1 Documents (External APIs)
| Document | Last Verified | Action |
|----------|--------------|--------|
| docs/api/users.md | 2026-01-05 | [ ] Reviewed, current |
| docs/api/orders.md | 2025-12-15 | [ ] Needs update |
### P2 Documents (Internal)
| Document | Last Verified | Action |
|----------|--------------|--------|
| docs/events/order-created.md | 2025-11-20 | [ ] Updated |
### Orphaned Documentation
| Document | Issue | Action |
|----------|-------|--------|
| docs/api/legacy-v1.md | API deprecated | [ ] Archive |
### Summary
- Total reviewed: X
- Updated: Y
- Archived: Z
- Next review: [YYYY-MM-DD]---
Tools and Integrations
Recommended Tools
| Tool | Purpose | Integration |
|---|---|---|
| markdown-link-check | Broken link detection | CI/CD |
| Vale | Prose linting | Pre-commit |
| Spectral | OpenAPI/AsyncAPI linting | CI/CD |
| Mintlify | AI-powered doc maintenance | Integration |
Custom Scripts Location
Store freshness scripts in your repo:
scripts/
├── check-doc-freshness.sh
├── generate-freshness-report.sh
├── find-related-docs.sh
└── update-doc-metadata.sh---
Related Resources
- CI/CD Integration - Automated documentation checks
- Priority Framework - P1/P2/P3 classification
- Audit Workflows - Systematic audit processes
- Coverage Report Template - Report structure