
Documentation Authoring
- 18 installs
- 7 repo stars
- Updated August 2, 2026
- practicalswan/agent-skills
documentation-authoring is a Claude Code skill for documentation.
About
documentation-authoring is a Claude Code skill for documentation. It helps solo builders move faster with AI-assisted development.
- documentation-authoring
- Documentation
- AI-coding skill
Documentation Authoring by the numbers
- 18 all-time installs (skills.sh)
- Ranked #1,021 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/practicalswan/agent-skills --skill documentation-authoringAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 18 |
|---|---|
| repo stars | ★ 7 |
| Last updated | August 2, 2026 |
| Repository | practicalswan/agent-skills ↗ |
How do I helps with documentation tasks.?
Helps with documentation tasks.
Who is it for?
Best when you're working on documentation and need structured help with documentation authoring.
Skip if: Teams with no documentation needs, or anyone wanting a generic chat assistant without this specific workflow.
When should I use this skill?
When you need to helps with documentation tasks., or when documentation-authoring is a claude code skill for documentation.
What you get
Structured output aligned to documentation-authoring: documentation-authoring, Documentation.
Files
Documentation Authoring Master
Expert guidance for creating structured, high-quality documentation across all types of technical and business documents.
- Leverage native parallel subagent dispatch and 200k+ context windows where available.
Activation Conditions
Use symptom -> action triggers: when one matches, apply this skill and verify with the protocol below.
Trigger Conditions:
- User mentions writing documentation: "write a doc", "draft a proposal", "create a spec", "write up"
- User mentions specific doc types: "PRD", "design doc", "decision doc", "RFC"
- User asks to "create an implementation plan", "document requirements", "plan a feature"
- Creating technical specifications or business requirements
- Starting a new product or feature development cycle
- Translating vague ideas into concrete technical specifications
- Stakeholders need unified "source of truth" for project scope
Part 2: Context Gathering
Initial Questions
Start by asking for meta-context about the document:
1. What type of document is this?
- Technical spec, decision doc, proposal, RFC, PRD, knowledge base
2. Who's the primary audience?
- Developers, executives, stakeholders, end-users? Understanding affects tone and depth
3. What's the desired impact when someone reads this?
- Make a decision, implement a feature, understand a concept?
4. Is there a template or specific format to follow?
- Company templates, industry standards, regulatory requirements
5. Any other constraints or context to know?
- Deadlines, sensitive information, existing related documents
Inform them they can answer in shorthand or dump information however works best for them.
Template Handling
If user provides a template:
- Analyze structure and requirements
- Adapt co-authoring workflow to template format
- Ensure all required sections are covered
If user mentions editing an existing document:
- Fetch the existing document
- Understand current state and gaps
- Plan revisions strategically
---
Part 3: Refinement & Structure
Collaborative Building
Process: 1. Brainstorm each section together - let ideas flow without judgment 2. Organize and refine - structure ideas into coherent sections 3. Edit for clarity - improve readability and flow 4. Add professional polish - formatting, consistency, tone
Guiding Principles:
- Active voice: Use direct, clear language
- Show, don't just tell: Use examples and scenarios
- Progressive disclosure: Start with overview, then dive deeper
- Visual aids: Include diagrams, tables, and examples where helpful
Section-by-Section Approach
Work through document methodically:
## Recommended Section Structure
### 1. Executive Summary (for decision-makers)
- What is this about?
- Why does it matter?
- What are we recommending/deciding?
### 2. Background & Context (for implementers)
- What led us here?
- What problem are we solving?
- What constraints exist?
### 3. Requirements/Objectives
- What must we achieve?
- What are success criteria?
- What are non-goals?
### 4. Proposed Solution/Design
- What are we proposing?
- How does it work?
- What are alternatives considered?
### 5. Implementation Plan
- How do we build this?
- What are the steps?
- Who needs to do what?
### 6. Risks & Considerations
- What could go wrong?
- How do we mitigate?
- What decisions are still needed?---
Part 4: Reader Testing
The Fresh Eye Test
Before finalizing, put yourself in the reader's shoes:
Test Questions: 1. Can I understand the goal without knowing context? 2. Are technical terms explained or linked? 3. Is there a logical flow from problem to solution? 4. Would a skeptical reader be convinced? 5. Is action clear - what should I do next?
Blind Spot Detection
Common issues to catch:
- Context assumptions: "We already discussed this" but wasn't documented
- Missing alternatives: Only one option presented (shows lack of thoroughness)
- Unanswered questions: Reader left with "what about X?"
- Unclear responsibilities: Who needs to do what is vague
- Missing examples: Abstract concepts without concrete illustration
---
Part 5: Product Requirements Document (PRD)
PRD Structure
When users specifically request PRDs or feature planning, use this structure:
# [Feature/Product Name] - PRD
## Executive Summary
**Goal**: [What problem are we solving?]
**Impact**: [Why does this matter now?]
**Success Metrics**: [How will we know it worked?]
## Background
**Current State**: [What's the situation today?]
**Problem Statement**: [What pain points exist?]
**Constraints**: [Budget, timeline, tech stack limitations?]
## Requirements
### Functional Requirements
- User stories with acceptance criteria
- Core features and capabilities
- Integration requirements
### Non-Functional Requirements
- Performance requirements
- Security requirements
- Compliance and regulatory needs
### User StoriesAs a [persona], I want to [action], So that [benefit].
**Acceptance Criteria**:
- [ ] [Specific, measurable criterion]
- [ ] [Another criterion]Proposed Solution
Architecture Overview
[High-level system architecture or approach]
Technical Specifications
[API contracts, data models, interfaces]
UI/UX Requirements
[Wireframes or flow descriptions if applicable]
Implementation Plan
Phases
| Phase | Tasks | Owners | Timeline |
|---|---|---|---|
| Phase 1 | |||
| Phase 2 |
Dependencies
- [ ] External APIs or services
- [ ] Other teams or systems
- [ ] Third-party libraries
Risk Analysis
| Risk | Impact | Probability | Mitigation |
|---|---|---|---|
| [Risk] | High/Med/Low | High/Med/Low | [Mitigation] |
Alternatives Considered
| Option | Pros | Cons | Why Not Chosen |
|---|---|---|---|
| Alt 1 |
Success Criteria
Quantitative
- [ ] [Measurable metric: e.g., "reduce load time by 50%"]
- [ ] [Another metric]
Qualitative
- [ ] [User feedback threshold]
- [ ] [Stakeholder alignment]
Open Questions
- [ ] [Decision still needed]
- [ ] [Information to gather]
### PRD Creation Workflow
**Phase 1: Discovery (The Interview)**
Before writing a single line, you **MUST** interrogate user to fill knowledge gaps. Do not assume context.
**Ask about:**
- **The Core Problem**: Why are we building this now?
- **Success Metrics**: How do we know it worked?
- **Constraints**: Budget, tech stack, or deadline?
- **Stakeholders**: Who needs to approve? Who will use?
**Phase 2: Analysis & Scoping**
Synthesize user input. Identify dependencies and hidden complexities.
- **Map out User Flow**
- **Define Non-Goals** to protect timeline
**Phase 3: Technical Drafting**
Generate document using strict structure above.
---
## Part 6: Common Document Types
### Implementation Plans
**Purpose**: Guide building process with clear phases, responsibilities, and timeline.
**Structure:**
- **Overview**: What are we building and why?
- **Phases**: Break into logical chunks with dependencies
- **Tasks**: Trackable, specific implementation items
- **Timeline**: Realistic dates with buffers
- **Dependencies**: What must happen before what?
### Design Docs
**Purpose**: Document technical decisions and architecture.
**Structure:**
- **Problem Statement**: What problem are we solving?
- **Alternatives**: What did we consider?
- **Decision**: What did we choose and why?
- **Implications**: What does this mean for the system?
- **Risks**: What could go wrong?
### Decision Records
**Purpose**: Capture important decisions for future reference.
**Template:**Decision - [DATE]
Decision: [What was decided] Context: [Situation and driving data] Options: [Alternatives with pros/cons] Rationale: [Why selected option is superior] Impact: [Anticipated consequences] Review: [Reassessment conditions/trigger]
### Knowledge Base Articles
**Purpose**: Reusable reference material, not project-specific docs.
**Structure:**
- **Quick Reference**: TL;DR summary at top
- **Problem**: What question does this answer?
- **Solution**: How do you solve it?
- **Examples**: Concrete, runnable examples
- **Common Pitfalls**: What mistakes do people make?
- **Related Topics**: Links to related info
---
## Part 7: Best Practices
### For All Documentation
✅ **DO**:
- Use active voice and clear language
- Structure information progressively (simple to complex)
- Include examples and concrete scenarios
- Define terms before using them
- Add diagrams for complex systems
- Maintain consistent formatting and style
❌ **DON'T**:
- Write without clear audience in mind
- Mix jargon without explanation
- Skip alternatives or trade-offs analysis
- Assume readers have context they don't
- Create long paragraphs without breaks
### For Technical Docs
- Include code snippets that actually run
- Link to external references for deeper dives
- Use standard terminology when possible
- Version specific code/commands (e.g., "for node v16+")
### For Business/Stakeholder Docs
- Start with executive summary
- Use business impact metrics
- Hide unnecessary technical detail
- Include clear next steps or approvals needed
- Highlight risks and mitigations prominently
---
## Part 8: Action Documentation Format
Use this format for tracking implementation work and decisions:
### [TYPE] - [ACTION] - [TIMESTAMP]
**Objective**: [Goal being accomplished]
**Context**: [Current state, requirements, reference to prior steps]
**Decision**: [Approach chosen and rationale]
**Execution**: [Steps taken with parameters and commands]
**Output**: [Complete results, logs, metrics]
**Validation**: [Success verification and results]
**Next**: [Continuation plan to next action]
---
## Part 9: Summary Formats
### Streamlined Action Log (for changelogs)
`[TYPE][TIMESTAMP] Goal: [X] → Action: [Y] → Result: [Z] → Next: [W]`
### Quick Summary (for updates)
**What**: [Brief description]
**Why**: [Context/rationale]
**How**: [Approach taken]
**Status**: [Current state]
**Next**: [Upcoming step]
---
## Documentation Stack Reference
Inherit the shared stack from [documentation-patterns](../documentation-patterns/SKILL.md#shared-documentation-stack): source-of-truth discovery, audience framing, structure selection, verification, and freshness checks. Keep this skill focused on drafting and refinement instead of restating the full stack.
## Anti-Patterns
- Writing for the author instead of the reader: It bakes in unstated context and leaves the actual audience unsure what to do next.
- Skipping concrete examples or commands: Abstract guidance is easy to approve and hard to apply correctly.
- Letting links, screenshots, or versions drift: Polished formatting does not help if the instructions are no longer true.
## Verification Protocol
Before claiming "skill applied successfully":
1. Pass/fail: The Documentation Authoring output identifies audience, purpose, source of truth, and freshness requirements.
2. Pass/fail: Shared documentation-stack guidance is referenced instead of duplicating another documentation skill.
3. Pass/fail: Claims, links, commands, examples, and screenshots are verified or explicitly marked unverified.
4. Pressure-test scenario: Apply the skill to a doc request with a stale command, missing owner, and conflicting audience.
5. Success metric: Zero undocumented assumptions; every reader-facing claim is sourced or scoped.
## Documentation Quality Checklist
### Completeness
- [ ] All required sections filled
- [ ] Context and background provided
- [ ] Alternatives considered where applicable
- [ ] Examples and diagrams included where helpful
### Clarity
- [ ] Language is clear and direct
- [ ] Technical terms defined or linked
- [ ] Flowlogical and easy to follow
- [ ] Active voice used consistently
### Accuracy
- [ ] Technical details are correct
- [ ] Links work and are up-to-date
- [ ] Code examples actually run
- [ ] No contradictory information
### Accessibility
- [ ] Multiple levels of detail for different readers
- [ ] Executive summary for decision-makers
- [ ] Deep-dive sections for implementers
- [ ] Visual aids for complex concepts
---
## References & Resources
### Documentation
- [Document Templates](./references/document-templates.md) — Templates for PRD, RFC, ADR, Tech Spec, Design Doc, Runbook, Postmortem, KB Article
- [Writing Style Guide](./references/writing-style-guide.md) — Technical writing best practices, formatting conventions, and readability
### Scripts
- [Doc Structure Validator](./scripts/doc-structure-validator.py) — Python script to validate markdown document quality
### Examples
- [PRD Example](./examples/prd-example.md) — Complete PRD for Recipe Search Enhancement in Kitchen Odyssey
---
## Cross-Skill Workflow
- Start in this skill when you need to draft or reshape the document itself.
- Pull in [documentation-patterns](../documentation-patterns/SKILL.md) when the structure or template is the main decision.
- Finish with [documentation-quality](../documentation-quality/SKILL.md) when the draft needs an explicit review against quality bars.
## Agent Prompt Template
Use the documentation-authoring skill to draft a [document type] for [audience]. Goal: [decision, rollout, implementation, or explanation target]. Required sections: [list]. Constraints: [scope, timeline, compliance, or tooling notes]. Include open questions, trade-offs, and next steps at the end.
<!-- PORTABILITY:START -->
## Cross-Client Portability
This skill is written to stay usable across GitHub Copilot, Claude Code, Codex, and Gemini CLI.
- GitHub Copilot: keep the folder in a Copilot-visible skill or plugin path, or wrap the workflow as project instructions if the host does not support portable skill folders directly.
- Claude Code: keep the folder in a local skills directory or a compatible plugin or marketplace source.
- Codex: install or sync the folder into `$CODEX_HOME/skills/<skill-name>` and restart Codex after major changes.
- Gemini CLI: this repository generates a project command named `/skills:documentation-authoring` from this skill. Rebuild commands with `python scripts/export-gemini-skill.py documentation-authoring` and then run `/commands reload` inside Gemini CLI.
<!-- PORTABILITY:END -->
<!-- MCP:START -->
## MCP Availability And Fallback
Preferred MCP Server: None required
- Fallback prompt: "Use the Documentation Authoring Master skill without MCP. Rely on the local `SKILL.md`, bundled references or scripts, and manual verification. Show the exact commands, evidence, and final checks you used before concluding."
- If the current host does not expose a matching server, use the bundled references, scripts, native toolchain, and manual workflow already described in this skill.
- Treat direct local verification, rendered output, logs, tests, or screenshots as the fallback evidence path before completion.
<!-- MCP:END -->
## Related Skills
- [documentation-patterns](../documentation-patterns/SKILL.md): Use it when the workflow also needs reusable documentation structures and templates.
- [documentation-quality](../documentation-quality/SKILL.md): Use it when the workflow also needs documentation review standards and quality gates.
- [documentation-verification](../documentation-verification/SKILL.md): Use it when the workflow also needs final documentation validation before publishing.
- [notion-docs](../notion-docs/SKILL.md): Use it when the workflow also needs Notion page and database publishing workflows.
Changelog
[2026-04-25] - Version 1.2 Verification Protocol Refresh
Added
- Added a
Verification Protocolsection with skill-specific pass/fail checks, one pressure-test scenario, and a measurable success metric. - Added guidance to leverage native parallel subagent dispatch and 200k+ context windows where available.
- Added or referenced the shared documentation-stack approach to reduce duplicated documentation guidance.
Changed
- Updated
SKILL.mdfrontmatter toversion: "1.2"andlast_updated: 2026-04-25. - Reframed activation guidance toward symptom -> action triggers and standardized two-stage review wording where applicable.
Fixed
- Replaced remaining directly related legacy review wording with two-stage review wording in support documentation.
[2026-04-24] - Version 1.1 Refresh
Changed
- Updated the SKILL frontmatter version to
1.1for the 2026-04-24 catalog refresh.
[2026-04-24] - Verification Follow-Up
Fixed
- Moved the Anti-Patterns section so it now appears immediately before the documentation quality checklist.
[2026-04-24] - Skill Refresh
Changed
- Standardized the SKILL frontmatter with version metadata, last-updated date, tags, and a concise catalog description.
- Reformatted the portability and MCP guidance with a preferred server line, a copy-paste fallback prompt, and consistent bullet lists.
- Added a catalog-standard Anti-Patterns section and refreshed the Related Skills links at the end of the skill.
- Added stronger cross-references to documentation-patterns and documentation-quality, plus a ready-to-copy agent prompt template.
[2026-04-24] - Catalog Audit Cleanup
Fixed
- Removed obsolete standalone Skill Paths guidance that duplicated the generated portability section.
- Removed stale placeholder links from documentation examples so repo-wide Markdown link validation resolves cleanly.
All notable changes to this skill will be documented in this file.
[2026-04-04] - Cross-Client Portability Refresh
Changed
- Added a standard portability note covering GitHub Copilot, Claude Code, Codex, and Gemini CLI.
- Clarified that the core workflow does not require a dedicated MCP server and can run with local tools alone.
Tested
- Validated
SKILL.mdfrontmatter, portability sections, and Gemini export readiness withpython scripts/validate-skills.py.
[2026-03-09] - Workspace Modernization
Changed
- Removed duplicated related-skill content from
SKILL.mdto keep the authored-doc workflow concise
[2026-02-28] — Description Rewrite & Cross-References
Changed
- Rewrote skill description to ~200 characters with clear, specific activation keywords
- Improved keyword specificity to reduce overlap with related skills
Added
## Related Skillscross-reference table with 2-4 related skills and "Use When" guidance
PRD: Recipe Search Enhancement
Author: Kitchen Odyssey Product Team Status: Approved Created: 2026-01-15 Last Updated: 2026-02-08 Related: Design Overhaul Plan (example placeholder)
---
1. Problem Statement
Kitchen Odyssey users currently browse recipes through a simple list on the Home page or navigate to the Search page, which supports only basic text matching against recipe titles. User feedback and analytics reveal significant friction:
- 72% of search sessions end without a recipe selection — users cannot find what they are looking for.
- Users search by ingredient ("chicken thighs", "leftover rice") but the system only matches titles.
- No filtering by dietary restriction — vegetarian, vegan, gluten-free, and dairy-free users must scan results manually.
- No sorting options — results appear in insertion order regardless of relevance, rating, or preparation time.
- Mobile users abandon search 2x more often than desktop users due to cramped filter UX.
Improving search directly impacts user engagement, recipe discovery, and retention.
2. Goals & Non-Goals
Goals
- Enable ingredient-based search so users can find recipes by what they have on hand.
- Provide filterable, sortable search results with dietary, cuisine, difficulty, and time filters.
- Deliver a mobile-first search experience that is fast and intuitive.
- Increase search-to-view conversion rate from 28% to 55% within 60 days of launch.
Non-Goals
- Full-text search across recipe instructions (deferred to Phase 2).
- AI-powered recipe recommendations (separate initiative).
- Search across external recipe APIs or third-party content.
- Changes to recipe creation or editing flows.
3. User Stories
Story 1: Home Cook — Search by Ingredient
As a home cook, I want to search for recipes by the ingredients I have, so that I can decide what to make without going to the store.
Acceptance Criteria:
- [ ] Given I type "chicken, broccoli" in the search bar, when I submit, then I see recipes that include both "chicken" and "broccoli" in their ingredient lists.
- [ ] Given a recipe contains "chicken breast" and I search for "chicken", then that recipe appears in results (partial ingredient matching).
- [ ] Given I search for an ingredient that matches zero recipes, then I see a friendly empty state with suggestions.
Story 2: Health-Conscious User — Filter by Dietary Restriction
As a health-conscious user, I want to filter search results by dietary labels, so that I only see recipes I can eat.
Acceptance Criteria:
- [ ] Given I select the "Vegetarian" filter, when results load, then every displayed recipe is tagged as vegetarian.
- [ ] Given I select multiple filters ("Gluten-Free" + "Under 30 min"), then results match ALL selected filters (AND logic).
- [ ] Given I clear all filters, then the full unfiltered result set is displayed.
- [ ] Given filters are applied, then the active filter count is shown on the filter button (mobile).
Story 3: Busy Parent — Sort by Preparation Time
As a busy parent, I want to sort recipes by total preparation time, so that I can find quick meals on weeknights.
Acceptance Criteria:
- [ ] Given I select "Sort by: Prep Time (Low to High)", then results reorder by ascending total time.
- [ ] Given I change the sort to "Rating (High to Low)", then results reorder by descending average rating.
- [ ] Given the current sort is active, then the sort button label reflects the active sort option.
Story 4: Mobile User — Responsive Filter Panel
As a mobile user, I want to access filters in a slide-up panel, so that I can refine results without losing my scroll position.
Acceptance Criteria:
- [ ] Given I am on a screen narrower than 768px, when I tap the filter icon, then a bottom sheet slides up with all filter options.
- [ ] Given I apply filters in the bottom sheet and tap "Apply", then results update and the sheet closes.
- [ ] Given I am on desktop (>=1024px), then filters appear as a sidebar alongside results.
Story 5: Guest User — Search Without Account
As a guest user, I want to search and filter recipes without logging in, so that I can explore before creating an account.
Acceptance Criteria:
- [ ] Given I am not logged in, when I use search, then I see the same search/filter functionality as authenticated users.
- [ ] Given I try to save a recipe from search results as a guest, then I am prompted to sign up.
4. Success Metrics
| Metric | Baseline | Target | Measurement Method |
|---|---|---|---|
| Search-to-view conversion rate | 28% | 55% | Analytics: search result clicks / total searches |
| Average searches per session | 1.2 | 2.0 | Analytics: search events per session |
| Search abandonment rate | 72% | 35% | Analytics: searches with 0 result clicks |
| Filter usage rate | 0% (N/A) | 40% of searches | Analytics: searches with ≥1 filter applied |
| Mobile search task completion | 38% | 65% | Analytics: mobile search-to-view conversion |
| Page load time (search results) | N/A | < 500ms (P95) | Performance monitoring |
5. Design & UX
5.1 Search Bar Enhancement
- Unified search bar at the top of the Home and Search pages.
- Supports both title and ingredient search with auto-detection.
- Placeholder text cycles: "Search recipes...", "Try 'pasta with tomatoes'...", "Search by ingredient..."
- Shows recent searches (stored locally) when focused.
5.2 Search Results Page
Desktop (≥1024px):
- Left sidebar (280px): Filters panel — always visible.
- Main content: Recipe cards in a responsive grid (3 columns).
- Top bar: Result count, active sort dropdown, view toggle (grid/list).
Tablet (768px–1023px):
- Collapsible filter sidebar with toggle button.
- Recipe cards in 2-column grid.
Mobile (<768px):
- Full-width search bar with filter icon button.
- Filter bottom sheet (slide-up panel) triggered by filter icon.
- Recipe cards in single-column stack.
- Infinite scroll with "Load more" fallback.
5.3 Filter Panel Design
| Filter Category | Type | Options |
|---|---|---|
| Dietary | Multi-select checkboxes | Vegetarian, Vegan, Gluten-Free, Dairy-Free, Nut-Free |
| Cuisine | Multi-select checkboxes | Thai, Italian, Mexican, Japanese, Indian, American, Mediterranean, Other |
| Difficulty | Single-select radio | Easy, Medium, Hard |
| Prep Time | Range slider | 0–120+ minutes |
| Rating | Minimum rating stars | 1–5 stars |
5.4 Empty & Error States
- No results: Illustration + "No recipes found. Try different ingredients or relax your filters." + "Clear all filters" button.
- Search error: "Something went wrong. Please try again." + Retry button.
- Loading: Skeleton cards matching the recipe card layout.
6. Technical Requirements
6.1 Frontend (React + Vite + Tailwind)
- New
SearchFilterscomponent with debounced input (300ms). - New
FilterPanelcomponent (responsive: sidebar on desktop, bottom sheet on mobile). - Update existing
RecipeCardcomponent to highlight matched ingredients. - URL-based filter state (
/search?q=chicken&diet=vegetarian&sort=time) for shareable search links. - Client-side filter/sort logic for the current dataset size (<500 recipes). Migrate to server-side if dataset exceeds 1,000.
6.2 Data Model Changes
Add fields to existing recipe schema:
| Field | Type | Description |
|---|---|---|
ingredients | string[] | Normalized ingredient list (lowercase, trimmed) |
dietaryTags | string[] | Dietary labels: vegetarian, vegan, gluten-free, etc. |
cuisine | string | Cuisine category |
difficulty | string | easy \ |
prepTimeMinutes | number | Total preparation time in minutes |
averageRating | number | Computed average rating (1.0–5.0) |
6.3 Search Algorithm
1. Title match: Fuzzy match against recipe title (Levenshtein distance ≤ 2 or substring match). 2. Ingredient match: Substring match against normalized ingredient list. Score by number of matched ingredients. 3. Combined ranking: relevanceScore = (titleMatchWeight * 0.6) + (ingredientMatchWeight * 0.4). Apply dietary/cuisine/difficulty filters as post-filter (AND logic). Sort by selected sort option or relevance by default.
6.4 Performance
- Search results must render within 500ms (P95) on a 4G mobile connection.
- Debounce input to prevent excessive re-renders.
- Virtualize result lists beyond 50 items using a windowing library.
- Lazy-load recipe card images with placeholder skeletons.
6.5 Accessibility
- All filter controls must be keyboard-navigable.
- Screen reader announcements for result count updates ("12 recipes found").
- Filter panel must trap focus when open on mobile (modal behavior).
- Minimum touch target size: 44x44px for mobile filter controls.
- Color contrast ratio ≥ 4.5:1 for all filter labels and result text.
7. Dependencies
| Dependency | Owner | Status | Risk |
|---|---|---|---|
| Recipe data migration (add new fields) | Backend team | Not started | Medium — blocks filter functionality |
| Design mockups finalized | Design team | In progress | Low — 80% complete |
| Guest mode feature | Frontend team | In progress | Low — search works with existing auth context |
| RecipeCard component update | Frontend team | Not started | Low — isolated component change |
8. Rollout Plan
Phase 1: Internal Testing (Week 1–2)
- Deploy to staging environment.
- QA regression testing on all breakpoints (mobile, tablet, desktop).
- Accessibility audit with screen reader and keyboard-only testing.
- Performance benchmarking against P95 latency target.
Phase 2: Beta Release (Week 3)
- Enable for 20% of users via feature flag.
- Monitor search conversion metrics and error rates.
- Collect qualitative feedback via in-app survey.
- Fix critical issues identified during beta.
Phase 3: General Availability (Week 4)
- Enable for 100% of users.
- Remove feature flag.
- Publish user-facing changelog entry.
- Monitor metrics for 2 weeks post-launch.
Phase 4: Iteration (Week 5–8)
- Analyze search analytics for common queries with low conversion.
- Add autocomplete suggestions based on popular searches.
- Evaluate full-text search across recipe instructions for Phase 2.
9. Risks & Mitigations
| Risk | Probability | Impact | Mitigation |
|---|---|---|---|
Recipe data missing ingredients field for existing recipes | High | High | Run data backfill script; flag incomplete recipes for admin review |
| Search performance degrades with dataset growth | Medium | Medium | Implement client-side indexing; plan server-side search migration |
| Filter UX too complex for casual users | Medium | Medium | Progressive disclosure: show top 3 filters, "More filters" expandable |
| Mobile bottom sheet conflicts with browser gestures | Low | Medium | Test on iOS Safari and Android Chrome; use native-feel spring animation |
| Accessibility regression in existing components | Low | High | Run automated a11y tests in CI; manual screen reader testing |
10. Open Questions
- [ ] Should we support search by recipe tags in addition to ingredients and titles?
- [ ] Do we need an "Advanced Search" mode or keep everything unified?
- [ ] What is the maximum number of concurrent filters before UX degrades?
- [ ] Should dietary tags be self-reported by recipe authors or computed from ingredients?
Appendix
A. Competitive Analysis
| Feature | Kitchen Odyssey (Current) | AllRecipes | Tasty | Cookpad |
|---|---|---|---|---|
| Ingredient search | No | Yes | Yes | Yes |
| Dietary filters | No | Yes (6 types) | Yes (4 types) | Limited |
| Sort options | No | 4 options | 3 options | 2 options |
| Mobile filter UX | N/A | Bottom sheet | Inline | Modal |
| Search autocomplete | No | Yes | Yes | No |
B. User Research Highlights
- 8 of 10 interviewed users mentioned "search by ingredient" as their most-wanted feature.
- 6 of 10 users said they would use dietary filters at least weekly.
- Mobile users specifically requested a "less cluttered" filter experience.
- Users preferred seeing ingredient matches highlighted in results.
C. Wireframe References
- Desktop search layout: [Figma link placeholder]
- Mobile bottom sheet filter: [Figma link placeholder]
- Empty state design: [Figma link placeholder]
MIT License
Copyright (c) 2026 Sithu Win San
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.Document Templates Reference
Reusable templates for common technical documents. Each includes purpose, audience, standard sections, writing tips, and a fill-in skeleton.
---
1. Product Requirements Document (PRD)
Purpose: Define what to build and why, aligning stakeholders on scope, goals, and success criteria before development begins.
Audience: Product managers, engineers, designers, QA, leadership.
Tips:
- Lead with the problem, not the solution
- User stories should be testable — write acceptance criteria as concrete scenarios
- Quantify success metrics with baselines and targets
- Keep scope tight; link to separate docs for deep dives
Skeleton
# PRD: [Feature Name]
**Author:** [Name]
**Status:** Draft | In Review | Approved | Superseded
**Created:** YYYY-MM-DD
**Last Updated:** YYYY-MM-DD
## 1. Problem Statement
What pain point exists? Who experiences it? What evidence do we have (data, feedback, research)?
## 2. Goals & Non-Goals
### Goals
- [Measurable outcome 1]
- [Measurable outcome 2]
### Non-Goals
- [Explicitly out of scope item 1]
## 3. User Stories
### Story 1: [Persona] — [Action]
> As a [persona], I want to [action] so that [benefit].
**Acceptance Criteria:**
- [ ] Given [context], when [action], then [outcome]
- [ ] Given [context], when [action], then [outcome]
## 4. Success Metrics
| Metric | Baseline | Target | Measurement Method |
|--------|----------|--------|--------------------|
| [KPI] | [Current]| [Goal] | [How measured] |
## 5. Design & UX
Link to mockups or describe key screens/flows.
## 6. Technical Requirements
- Performance constraints
- Data requirements
- Integration points
- Security considerations
## 7. Dependencies
| Dependency | Owner | Status | Risk |
|------------|-------|--------|------|
| [Item] | [Team]| [State]| [Impact if delayed] |
## 8. Rollout Plan
- Phase 1: [Internal/beta]
- Phase 2: [Gradual rollout]
- Phase 3: [GA]
## 9. Risks & Mitigations
| Risk | Probability | Impact | Mitigation |
|------|------------|--------|------------|
| [Risk] | High/Med/Low | High/Med/Low | [Action] |
## 10. Open Questions
- [ ] [Question needing resolution]
## Appendix
Supporting data, research links, or detailed specs.---
2. Request For Comments (RFC)
Purpose: Propose a significant technical change, gather feedback from peers, and reach consensus before implementation.
Audience: Engineering team, architect leads, affected stakeholders.
Tips:
- Present alternatives honestly — show why the proposed approach wins
- Include a "non-goals" section to prevent scope creep in discussion
- Set a review deadline to avoid indefinite open RFCs
- Number your RFCs for easy reference
Skeleton
# RFC-[NNN]: [Title]
**Author:** [Name]
**Status:** Draft | Open for Review | Accepted | Rejected | Withdrawn
**Review Deadline:** YYYY-MM-DD
**Created:** YYYY-MM-DD
## Summary
One-paragraph description of the proposal.
## Motivation
Why is this change needed? What problem does it solve?
## Detailed Design
### Architecture
Describe the proposed system design with diagrams if helpful.
### API / Interface Changes[code or schema changes]
### Data Model Changes
Describe any storage, schema, or migration requirements.
### Migration Plan
How do we transition from current state to proposed state?
## Alternatives Considered
| Alternative | Pros | Cons | Why Not Chosen |
|-------------|------|------|----------------|
| [Option A] | ... | ... | ... |
| [Option B] | ... | ... | ... |
## Risks & Open Questions
- [Risk or question 1]
- [Risk or question 2]
## Implementation Plan
- [ ] Phase 1: [Description] — [Estimated effort]
- [ ] Phase 2: [Description] — [Estimated effort]
## References
- [Link to related RFC, doc, or research]---
3. Architecture Decision Record (ADR)
Purpose: Capture a single architectural decision with its context and consequences so future teams understand why a choice was made.
Audience: Current and future engineers, architects, tech leads.
Tips:
- Keep each ADR focused on one decision
- Write in present tense at the time of the decision
- Never delete ADRs — supersede them with new ones
- Store ADRs close to the code (e.g.,
docs/adr/)
Skeleton
# ADR-[NNN]: [Decision Title]
**Status:** Proposed | Accepted | Deprecated | Superseded by ADR-[NNN]
**Date:** YYYY-MM-DD
**Deciders:** [Names or roles]
## Context
What is the issue that we're seeing that motivates this decision?
## Decision
What is the change that we're proposing and/or doing?
## Consequences
### Positive
- [Benefit 1]
- [Benefit 2]
### Negative
- [Tradeoff 1]
- [Tradeoff 2]
### Neutral
- [Observation]
## Alternatives Considered
1. **[Alternative A]** — [Brief description and why rejected]
2. **[Alternative B]** — [Brief description and why rejected]
## Related
- [Link to related ADRs, RFCs, or docs]---
4. Technical Specification
Purpose: Provide implementation-level detail for a feature or system so that any engineer can build it without ambiguity.
Audience: Implementing engineers, two-stage reviewers (spec compliance first, then code quality), QA.
Tips:
- Include sequence diagrams for multi-component flows
- Define error handling explicitly — do not leave it implicit
- Specify data formats (JSON schemas, DB columns) precisely
- Call out what is not changing
Skeleton
# Technical Specification: [Feature/System Name]
**Author:** [Name]
**Status:** Draft | Approved | Implemented
**Created:** YYYY-MM-DD
**Related PRD:** [Link]
## Overview
Brief description of what this spec covers.
## System Context
Where does this feature fit in the overall architecture? Include a diagram.
## Detailed Design
### Component A: [Name]
**Responsibility:** [What it does]
**Interface:**[function signatures, API endpoints, or message formats]
**Behavior:**
1. [Step-by-step logic]
2. [Error handling paths]
### Component B: [Name]
[Same structure as above]
## Data Model
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| [name]| [type]| [nullable, unique, etc.] | [purpose] |
## API Contracts
### `METHOD /path`
**Request:**{ "field": "type" }
**Response (200):**{ "field": "type" }
**Error Responses:**
| Status | Body | When |
|--------|------|------|
| 400 | `{ "error": "..." }` | [condition] |
## Security Considerations
- Authentication/authorization requirements
- Input validation rules
- Data privacy implications
## Performance Considerations
- Expected load
- Latency requirements
- Caching strategy
## Testing Strategy
- Unit test focus areas
- Integration test scenarios
- Edge cases to cover
## Rollback Plan
How to revert if something goes wrong post-deployment.---
5. Design Document
Purpose: Communicate a high-level design approach for a project or feature, bridging the gap between requirements and implementation.
Audience: Engineers, designers, product managers, technical leads.
Tips:
- Use diagrams liberally — architecture diagrams, flow charts, wireframes
- Balance detail: enough to evaluate the approach, not so much it becomes a spec
- Identify the hardest parts and address them directly
- Get feedback early before investing in implementation detail
Skeleton
# Design Document: [Project/Feature Name]
**Author:** [Name]
**Reviewers:** [Names]
**Status:** Draft | Under Review | Approved
**Created:** YYYY-MM-DD
## Background
Context and motivation. What problem are we solving?
## Goals
- [Goal 1]
- [Goal 2]
## High-Level Design
Describe the overall approach with architecture diagrams.
### System Architecture
[Diagram: show major components and their interactions]
### Key Flows
#### Flow 1: [Name]
[Sequence or flow diagram with numbered steps]
#### Flow 2: [Name]
[Sequence or flow diagram with numbered steps]
## Detailed Design
### Module/Component 1
- Purpose
- Key interfaces
- Important implementation notes
### Module/Component 2
- [Same structure]
## Trade-offs & Decisions
| Decision | Options Considered | Choice | Rationale |
|----------|--------------------|--------|-----------|
| [Topic] | A, B, C | B | [Why] |
## Timeline & Milestones
| Milestone | Target Date | Description |
|-----------|-------------|-------------|
| M1 | YYYY-MM-DD | [What] |
## Open Questions
- [ ] [Question]---
6. Runbook
Purpose: Provide step-by-step procedures for operating, troubleshooting, or recovering a system. Written for the on-call engineer at 3 AM.
Audience: Operations engineers, SREs, on-call responders.
Tips:
- Write for someone unfamiliar with the system under stress
- Every step should be copy-pastable where possible
- Include expected output for each command
- Link to dashboards and alert configurations
Skeleton
# Runbook: [System/Service Name]
**Last Verified:** YYYY-MM-DD
**Owner:** [Team]
**On-Call Escalation:** [Contact info or PagerDuty link]
## Service Overview
- **What it does:** [Brief description]
- **Dashboard:** [Link]
- **Logs:** [Link or command]
- **Dependencies:** [Upstream/downstream services]
## Common Alerts
### Alert: [Alert Name]
**Severity:** P1 / P2 / P3
**Meaning:** [What triggered this alert]
**Diagnosis Steps:**
1. Check [metric/dashboard]: `[command or link]`
- Expected: [normal range]
- If abnormal: proceed to step 2
2. Check [logs]: `[command]`
- Look for: [pattern]
**Resolution:**
1. `[command to fix]`
- Expected output: `[output]`
2. Verify recovery: `[verification command]`
**Escalation:** If unresolved after [time], escalate to [team/person].
## Operational Procedures
### Procedure: [Name, e.g., "Scale Service"]
**When to use:** [Trigger condition]
1. `[step 1 command]`
2. `[step 2 command]`
3. Verify: `[command]` — expected: [output]
### Procedure: [Name, e.g., "Rollback Deployment"]
1. `[step 1]`
2. `[step 2]`
3. Verify: `[command]`
## Recovery Procedures
### Disaster Recovery
1. [Step-by-step recovery]
2. [Data restoration]
3. [Verification]
## Contacts
| Role | Name | Contact |
|------|------|---------|
| Service Owner | [Name] | [Email/Slack] |
| Escalation | [Name] | [Email/Phone] |---
7. Postmortem
Purpose: Document an incident, its root cause, and action items to prevent recurrence. Blameless by design.
Audience: Engineering team, leadership, affected stakeholders.
Tips:
- Stick to facts and timelines — avoid blame language
- "5 Whys" is a useful technique for root cause analysis
- Every action item needs an owner and a deadline
- Publish widely to maximize organizational learning
Skeleton
# Postmortem: [Incident Title]
**Date of Incident:** YYYY-MM-DD
**Duration:** [Start time] – [End time] ([total duration])
**Severity:** P1 / P2 / P3
**Author:** [Name]
**Status:** Draft | Published
## Summary
One-paragraph description: what happened, who was affected, what was the impact.
## Impact
- **Users affected:** [Number or percentage]
- **Revenue impact:** [If applicable]
- **Data loss:** [If applicable]
- **SLA breach:** [Yes/No — details]
## Timeline (all times in UTC)
| Time | Event |
|------|-------|
| HH:MM | [First sign of issue] |
| HH:MM | [Alert fired / user report] |
| HH:MM | [Investigation started] |
| HH:MM | [Root cause identified] |
| HH:MM | [Fix deployed] |
| HH:MM | [Service restored] |
## Root Cause Analysis
### What happened
[Factual description of the technical failure chain]
### Why it happened (5 Whys)
1. Why? [Proximate cause]
2. Why? [Deeper cause]
3. Why? [Deeper cause]
4. Why? [Deeper cause]
5. Why? [Root cause]
### Contributing Factors
- [Factor 1]
- [Factor 2]
## What Went Well
- [Positive observation 1]
- [Positive observation 2]
## What Went Poorly
- [Issue 1]
- [Issue 2]
## Action Items
| ID | Action | Owner | Priority | Deadline | Status |
|----|--------|-------|----------|----------|--------|
| 1 | [Action] | [Name] | P1/P2 | YYYY-MM-DD | Open |
| 2 | [Action] | [Name] | P1/P2 | YYYY-MM-DD | Open |
## Lessons Learned
- [Key takeaway 1]
- [Key takeaway 2]---
8. Knowledge Base Article
Purpose: Provide a self-contained answer to a recurring question or how-to guide for internal or external users.
Audience: Developers, users, support team — varies per article.
Tips:
- Start with the answer or solution, then explain context
- Use progressive disclosure: summary → details → deep dive
- Include "Related Articles" to build a connected knowledge base
- Test with someone unfamiliar — if they can follow it, it works
Skeleton
# [Title: Action-Oriented, e.g., "How to Configure SSO"]
**Last Updated:** YYYY-MM-DD
**Applies To:** [Product version, environment, etc.]
**Tags:** [tag1, tag2, tag3]
## Quick Answer
[1–3 sentence summary or the direct answer]
## Prerequisites
- [Requirement 1]
- [Requirement 2]
## Step-by-Step Guide
### Step 1: [Action]
[Detailed instruction]
[command or code example]
### Step 2: [Action]
[Detailed instruction]
### Step 3: [Action]
[Detailed instruction]
## Verification
How to confirm the procedure worked:[verification command or check]
Expected result: [description]
## Troubleshooting
### Problem: [Common issue]
**Cause:** [Why it happens]
**Solution:** [How to fix]
### Problem: [Another common issue]
**Cause:** [Why]
**Solution:** [Fix]
## Related Articles
- [Link to related KB article]
- [Link to related KB article]
## FAQ
**Q: [Common question]**
A: [Answer]Technical Writing Style Guide
Practical rules and patterns for clear, consistent, and inclusive technical documentation.
---
Voice and Tone
Active vs Passive Voice
Prefer active voice. It is shorter, clearer, and identifies who does what.
| Passive (avoid) | Active (prefer) |
|---|---|
| "The configuration file is loaded by the server." | "The server loads the configuration file." |
| "An error will be returned if the input is invalid." | "The function returns an error if the input is invalid." |
| "Tests should be run before merging." | "Run tests before merging." |
Acceptable passive uses:
- When the actor is unknown or irrelevant: "The bug was first reported in v2.1."
- When emphasizing the object: "All user data is encrypted at rest."
- Error messages where the system is the actor: "Your session has expired."
Imperative Mood for Instructions
Use direct commands in procedural content:
- Yes: "Install the dependencies." / "Open the configuration file."
- No: "You should install the dependencies." / "The user needs to open the configuration file."
Consistent Perspective
- Guides and tutorials: Use "you" to address the reader directly.
- Reference docs: Use third person ("the function returns...").
- Avoid "we" unless genuinely collaborative ("In this tutorial, we will build...").
---
Sentence Structure
Sentence Length
- Target: 15–25 words per sentence.
- Maximum: 40 words. If longer, split.
- One idea per sentence. Compound sentences with multiple clauses slow comprehension.
| Too long | Better |
|---|---|
| "When the user clicks the submit button, the form data is validated on the client side, and if validation passes, the data is sent to the server, which processes it and returns a response." | "When the user clicks Submit, the client validates the form data. If validation passes, the client sends the data to the server. The server processes the request and returns a response." |
Front-Load Key Information
Put the most important content at the beginning of the sentence:
- Yes: "To deploy, run
npm run buildfirst." - No: "Before you can deploy your application to the production server, you need to first run
npm run build."
Parallel Structure
Keep list items and related clauses grammatically parallel:
- Yes: "The API supports creating, reading, updating, and deleting records."
- No: "The API supports creation of records, to read records, record updates, and you can delete them."
---
Paragraph Organization
One Topic Per Paragraph
Each paragraph should convey a single idea. Start with a topic sentence, then support it.
Paragraph Length
- Target: 3–5 sentences.
- Maximum: 7 sentences. Break longer paragraphs.
- Single-sentence paragraphs are acceptable for emphasis or transitions.
Transitional Flow
Connect paragraphs logically:
- Sequence: "Next," "Then," "After completing this step,"
- Contrast: "However," "Unlike," "In contrast,"
- Cause/Effect: "As a result," "Because of this," "Therefore,"
- Addition: "Additionally," "Also," "Furthermore,"
---
Jargon and Terminology
Define Terms on First Use
Introduce jargon or acronyms with a definition the first time they appear in a document:
"The API uses JWT (JSON Web Token) for authentication. Each JWT contains..."
Use Consistent Terminology
Pick one term and stick with it throughout a document:
| Inconsistent | Consistent |
|---|---|
| "endpoint" / "route" / "path" / "URL" interchangeably | Pick "endpoint" and use it everywhere |
| "click" / "tap" / "press" / "select" | Use "select" for all UI interactions, or platform-specific terms consistently |
Avoid Unnecessary Jargon
| Jargon-heavy | Clearer |
|---|---|
| "Leverage the SDK to instantiate a client" | "Use the SDK to create a client" |
| "Utilize the endpoint to facilitate data retrieval" | "Call the endpoint to get data" |
Glossary
For documents with heavy domain terminology, include a glossary section or link to a shared glossary.
---
Progressive Disclosure
Structure content so readers get the level of detail they need without wading through information they do not.
The Inverted Pyramid
┌─────────────────────────────┐
│ Essential / Summary │ ← Everyone reads this
├─────────────────────────────┤
│ Important Details │ ← Most readers need this
├─────────────────────────────┤
│ Background / Deep Dive │ ← Advanced readers explore this
└─────────────────────────────┘Techniques
1. TL;DR at the top — Start documents with a summary. 2. Expandable sections — Use <details> for advanced content. 3. Layered headings — Top-level for overview, sub-headings for depth. 4. Cross-links — Reference deep-dive docs instead of inlining everything. 5. Quick-start vs Full guide — Provide both paths.
---
Inclusive Language
Avoid Gendered Terms
| Avoid | Use |
|---|---|
| "he/she", "his/her" | "they/their" (singular they) |
| "manpower" | "workforce", "staffing" |
| "master/slave" | "primary/replica", "leader/follower" |
| "whitelist/blacklist" | "allowlist/denylist" |
| "sanity check" | "confidence check", "smoke test" |
Avoid Ableist Language
| Avoid | Use |
|---|---|
| "blind to" | "unaware of" |
| "cripple" | "disable", "degrade" |
| "dumb" (component) | "silent", "passive" |
Avoid Assumptions About Reader Expertise
- Do not write "simply", "just", "obviously", "of course". These words imply the content is trivial and alienate readers who struggle with it.
- Do not assume prior tooling knowledge without stating prerequisites.
---
Visual Aids: Tables vs Lists vs Diagrams
When to Use Tables
- Comparing multiple items across the same attributes
- Reference data (API parameters, config options, status codes)
- Anything with a clear row-column structure
| Parameter | Type | Required | Default | Description |
|-----------|--------|----------|---------|----------------------|
| `page` | number | No | 1 | Page number |
| `limit` | number | No | 20 | Results per page |When to Use Lists
- Sequential steps (ordered list)
- Non-comparative collections (unordered list)
- Feature highlights or key points
- Fewer than 4 attributes per item
When to Use Diagrams
- System architecture and component relationships
- Request/response flows or sequences
- State machines and decision trees
- Anything spatial or relational
Diagram guidelines:
- Use Mermaid for text-based diagrams in markdown
- Label all components and connections
- Keep diagrams focused — one concept per diagram
- Provide alt text or a text description for accessibility
---
Formatting Conventions
Headings
- H1 (`#`): Document title only — one per document
- H2 (`##`): Major sections
- H3 (`###`): Subsections
- H4 (`####`): Use sparingly for deeply nested content
- Never skip levels (e.g., H2 → H4 without H3)
- Use sentence case: "Configure the database" not "Configure The Database"
Code Blocks
Always specify the language for syntax highlighting:
````markdown
const result = await fetchData(url);````
- Inline code (`
``) for: variable names, function names, file paths, CLI commands, config values - Code blocks for: multi-line code, command output, file contents, API payloads
Callouts and Admonitions
Use consistent callout patterns:
> **Note:** Supplementary information that adds context.
> **Tip:** Helpful suggestion that improves the experience.
> **Warning:** Important caveat that could cause issues if ignored.
> **Caution:** Action that could result in data loss or security risk.Links
- Use descriptive link text:
[Configure the database](./db-setup.md)not[click here](./db-setup.md) - Prefer relative links for internal docs
- external links should open concepts, not duplicate content
Numbers
- Spell out one through nine; use numerals for 10 and above
- Always use numerals with units: "5 MB", "3 seconds"
- Use numerals in technical contexts: "Set
retriesto 3"
---
Common Mistakes in Technical Writing
1. Wall of Text
Problem: Large unbroken paragraphs with no visual structure. Fix: Break into paragraphs, add headings, use lists and code blocks.
2. Missing Context
Problem: Jumping into steps without explaining prerequisites or goals. Fix: Always include a "Prerequisites" section and a brief overview of what the reader will accomplish.
3. Ambiguous Pronouns
Problem: "It returns the value after it processes it." Fix: "The parse() function returns the value after the validator processes the input."
4. Outdated Examples
Problem: Code examples that no longer match the current API. Fix: Test examples regularly. Include version numbers. Automate example validation.
5. Undocumented Error Cases
Problem: Only documenting the happy path. Fix: Document at least the most common error scenarios with causes and solutions.
6. Inconsistent Formatting
Problem: Mixing formatting conventions within a document (some code in backticks, some in quotes). Fix: Establish and follow a style guide (this document). Review before publishing.
7. Too Much Detail Too Soon
Problem: Explaining every edge case before the reader understands the basics. Fix: Use progressive disclosure. Basic usage first, advanced topics later.
8. No Verification Step
Problem: Instructions that end without confirming success. Fix: Always include a "Verify" or "Expected result" step after procedures.
---
Readability Scoring
Flesch-Kincaid Grade Level
Target: Grade 8–10 for general technical docs, Grade 10–12 for advanced engineering docs.
Formula simplified: Longer sentences and longer words raise the grade level.
How to improve readability:
- Shorten sentences
- Replace long words with shorter synonyms
- Break complex ideas into multiple sentences
- Use concrete examples instead of abstract explanations
Quick Self-Check
Before publishing, read your document and ask: 1. Can a new team member follow this without asking questions? 2. Is every step actionable and verifiable? 3. Are all terms defined or linked to definitions? 4. Does the structure help scanning (headings, lists, bold key terms)? 5. Have I removed every word that does not add value?
---
Checklist: Before Publishing
- [ ] Title clearly describes the document's purpose
- [ ] Summary/TL;DR at the top
- [ ] Prerequisites listed
- [ ] Headings follow hierarchy (no skipped levels)
- [ ] Code blocks have language tags
- [ ] All links are valid and descriptive
- [ ] No TODO/TBD/FIXME markers remain
- [ ] Examples are tested and current
- [ ] Inclusive language review complete
- [ ] Spellcheck and grammar check passed
#!/usr/bin/env python3
"""
Markdown Document Structure & Quality Validator
Validates a markdown document for common quality issues:
- Heading hierarchy (no skipped levels)
- Empty sections
- Internal link targets
- Word count per section
- TODO/TBD/FIXME markers
- Code blocks missing language tags
- Images missing alt text
Usage:
python doc-structure-validator.py <path-to-markdown-file>
python doc-structure-validator.py <path-to-markdown-file> --json
python doc-structure-validator.py <directory> --recursive
Stdlib only — no third-party dependencies.
"""
import argparse
import json
import os
import re
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional
@dataclass
class Issue:
line: int
severity: str # "error", "warning", "info"
category: str
message: str
@dataclass
class SectionInfo:
heading: str
level: int
line: int
word_count: int
@dataclass
class ValidationReport:
file_path: str
total_words: int = 0
total_headings: int = 0
total_code_blocks: int = 0
total_images: int = 0
total_links: int = 0
sections: list = field(default_factory=list)
issues: list = field(default_factory=list)
@property
def error_count(self) -> int:
return sum(1 for i in self.issues if i.severity == "error")
@property
def warning_count(self) -> int:
return sum(1 for i in self.issues if i.severity == "warning")
@property
def info_count(self) -> int:
return sum(1 for i in self.issues if i.severity == "info")
MARKER_PATTERN = re.compile(r"\b(TODO|TBD|FIXME|HACK|XXX)\b", re.IGNORECASE)
HEADING_PATTERN = re.compile(r"^(#{1,6})\s+(.+)$")
IMAGE_PATTERN = re.compile(r"!\[([^\]]*)\]\(([^)]+)\)")
LINK_PATTERN = re.compile(r"(?<!!)\[([^\]]+)\]\(([^)]+)\)")
ANCHOR_PATTERN = re.compile(r"<a\s+(?:name|id)\s*=\s*[\"']([^\"']+)[\"']", re.IGNORECASE)
def slugify_heading(text: str) -> str:
"""Convert heading text to a GitHub-style anchor slug."""
text = text.strip().lower()
text = re.sub(r"[^\w\s-]", "", text)
text = re.sub(r"[\s]+", "-", text)
return text
def count_words(text: str) -> int:
"""Count words in a string, excluding code blocks and markdown syntax."""
cleaned = re.sub(r"`[^`]+`", "", text)
cleaned = re.sub(r"[#*_\[\]()>|]", " ", cleaned)
return len(cleaned.split())
def parse_document(lines: list[str]) -> tuple[list, list, list, list, list]:
"""Parse markdown lines into headings, code blocks, images, links, and content sections."""
headings = []
code_blocks = []
images = []
links = []
in_code_block = False
code_block_start = -1
code_block_lang = None
for i, line in enumerate(lines, 1):
stripped = line.strip()
if stripped.startswith("```"):
if not in_code_block:
in_code_block = True
code_block_start = i
lang = stripped[3:].strip().split()[0] if len(stripped) > 3 else ""
code_block_lang = lang
else:
in_code_block = False
code_blocks.append({
"start": code_block_start,
"end": i,
"lang": code_block_lang,
})
code_block_lang = None
continue
if in_code_block:
continue
heading_match = HEADING_PATTERN.match(stripped)
if heading_match:
level = len(heading_match.group(1))
text = heading_match.group(2).strip()
headings.append({"level": level, "text": text, "line": i})
for img_match in IMAGE_PATTERN.finditer(line):
images.append({
"alt": img_match.group(1),
"src": img_match.group(2),
"line": i,
})
for link_match in LINK_PATTERN.finditer(line):
links.append({
"text": link_match.group(1),
"href": link_match.group(2),
"line": i,
})
if in_code_block:
code_blocks.append({
"start": code_block_start,
"end": len(lines),
"lang": code_block_lang,
"unclosed": True,
})
return headings, code_blocks, images, links
def validate_heading_hierarchy(headings: list, issues: list[Issue]) -> None:
"""Check that heading levels don't skip (e.g., H2 -> H4)."""
if not headings:
issues.append(Issue(
line=1, severity="warning", category="structure",
message="Document has no headings.",
))
return
prev_level = 0
for h in headings:
level = h["level"]
if prev_level > 0 and level > prev_level + 1:
issues.append(Issue(
line=h["line"], severity="error", category="heading-hierarchy",
message=f"Heading level skipped: H{prev_level} -> H{level} "
f"('{h['text']}'). Expected H{prev_level + 1} or lower.",
))
prev_level = level
def validate_empty_sections(
headings: list, lines: list[str], issues: list[Issue]
) -> list[SectionInfo]:
"""Check for sections with no content between headings."""
sections = []
total_lines = len(lines)
for idx, h in enumerate(headings):
start_line = h["line"]
end_line = headings[idx + 1]["line"] - 1 if idx + 1 < len(headings) else total_lines
content_lines = []
in_code = False
for li in range(start_line, end_line):
raw = lines[li].strip() if li < total_lines else ""
if raw.startswith("```"):
in_code = not in_code
continue
if not in_code and raw and not HEADING_PATTERN.match(raw):
content_lines.append(raw)
word_count = sum(count_words(cl) for cl in content_lines)
sections.append(SectionInfo(
heading=h["text"], level=h["level"],
line=h["line"], word_count=word_count,
))
if word_count == 0:
issues.append(Issue(
line=h["line"], severity="warning", category="empty-section",
message=f"Section '{h['text']}' appears to have no content.",
))
return sections
def validate_internal_links(
headings: list, links: list, lines: list[str], issues: list[Issue]
) -> None:
"""Validate that internal anchor links (#...) point to existing headings."""
heading_slugs = set()
for h in headings:
heading_slugs.add(slugify_heading(h["text"]))
for anchor in ANCHOR_PATTERN.finditer("\n".join(lines)):
heading_slugs.add(anchor.group(1).lower())
for link in links:
href = link["href"]
if href.startswith("#"):
target = href[1:].lower()
if target not in heading_slugs:
issues.append(Issue(
line=link["line"], severity="error", category="broken-link",
message=f"Internal link '#{target}' does not match any heading or anchor.",
))
def validate_code_blocks(code_blocks: list, issues: list[Issue]) -> None:
"""Check that code blocks have language tags and are properly closed."""
for cb in code_blocks:
if cb.get("unclosed"):
issues.append(Issue(
line=cb["start"], severity="error", category="code-block",
message="Unclosed code block (missing closing ```).",
))
elif not cb["lang"]:
issues.append(Issue(
line=cb["start"], severity="warning", category="code-block",
message="Code block missing language tag (e.g., ```python).",
))
def validate_images(images: list, issues: list[Issue]) -> None:
"""Check that images have alt text."""
for img in images:
if not img["alt"].strip():
issues.append(Issue(
line=img["line"], severity="warning", category="accessibility",
message=f"Image missing alt text: {img['src']}",
))
def validate_markers(lines: list[str], issues: list[Issue]) -> None:
"""Find TODO/TBD/FIXME/HACK/XXX markers."""
in_code = False
for i, line in enumerate(lines, 1):
if line.strip().startswith("```"):
in_code = not in_code
continue
if in_code:
continue
for match in MARKER_PATTERN.finditer(line):
issues.append(Issue(
line=i, severity="info", category="marker",
message=f"Found '{match.group()}' marker: {line.strip()[:80]}",
))
def validate_document(file_path: str) -> ValidationReport:
"""Run all validations on a markdown file and return a report."""
report = ValidationReport(file_path=file_path)
path = Path(file_path)
if not path.exists():
report.issues.append(Issue(
line=0, severity="error", category="file",
message=f"File not found: {file_path}",
))
return report
try:
content = path.read_text(encoding="utf-8")
except UnicodeDecodeError:
content = path.read_text(encoding="latin-1")
lines = content.split("\n")
headings, code_blocks, images, links = parse_document(lines)
report.total_headings = len(headings)
report.total_code_blocks = len(code_blocks)
report.total_images = len(images)
report.total_links = len(links)
in_code = False
word_lines = []
for line in lines:
if line.strip().startswith("```"):
in_code = not in_code
continue
if not in_code:
word_lines.append(line)
report.total_words = count_words(" ".join(word_lines))
validate_heading_hierarchy(headings, report.issues)
report.sections = validate_empty_sections(headings, lines, report.issues)
validate_internal_links(headings, links, lines, report.issues)
validate_code_blocks(code_blocks, report.issues)
validate_images(images, report.issues)
validate_markers(lines, report.issues)
return report
def format_report_text(report: ValidationReport) -> str:
"""Format a validation report as human-readable text."""
out = []
out.append(f"\n{'='*60}")
out.append(f" Document Quality Report")
out.append(f" {report.file_path}")
out.append(f"{'='*60}\n")
out.append(f" Words: {report.total_words} | Headings: {report.total_headings} "
f"| Code blocks: {report.total_code_blocks}")
out.append(f" Images: {report.total_images} | Links: {report.total_links}")
out.append(f" Issues: {report.error_count} errors, {report.warning_count} warnings, "
f"{report.info_count} info\n")
if report.sections:
out.append(" Sections:")
out.append(f" {'Ln':<5} {'Lvl':<4} {'Words':<7} {'Heading'}")
out.append(f" {'---':<5} {'---':<4} {'-----':<7} {'-------'}")
for s in report.sections:
indent = " " * (s.level - 1)
out.append(f" {s.line:<5} H{s.level:<3} {s.word_count:<7} {indent}{s.heading}")
out.append("")
if report.issues:
out.append(" Issues:")
severity_order = {"error": 0, "warning": 1, "info": 2}
sorted_issues = sorted(report.issues, key=lambda i: (severity_order.get(i.severity, 3), i.line))
for issue in sorted_issues:
icon = {"error": "[ERR]", "warning": "[WRN]", "info": "[INF]"}.get(issue.severity, "[???]")
out.append(f" {icon} Line {issue.line}: [{issue.category}] {issue.message}")
out.append("")
else:
out.append(" No issues found. Document looks good!\n")
score = max(0, 100 - (report.error_count * 10) - (report.warning_count * 3) - report.info_count)
out.append(f" Quality Score: {score}/100")
out.append(f"{'='*60}\n")
return "\n".join(out)
def format_report_json(report: ValidationReport) -> str:
"""Format a validation report as JSON."""
data = {
"file": report.file_path,
"stats": {
"words": report.total_words,
"headings": report.total_headings,
"code_blocks": report.total_code_blocks,
"images": report.total_images,
"links": report.total_links,
},
"sections": [
{"heading": s.heading, "level": s.level, "line": s.line, "words": s.word_count}
for s in report.sections
],
"issues": [
{"line": i.line, "severity": i.severity, "category": i.category, "message": i.message}
for i in report.issues
],
"summary": {
"errors": report.error_count,
"warnings": report.warning_count,
"info": report.info_count,
"score": max(0, 100 - (report.error_count * 10) - (report.warning_count * 3) - report.info_count),
},
}
return json.dumps(data, indent=2)
def find_markdown_files(directory: str) -> list[str]:
"""Recursively find all .md files in a directory."""
md_files = []
for root, _dirs, files in os.walk(directory):
for f in files:
if f.lower().endswith(".md"):
md_files.append(os.path.join(root, f))
return sorted(md_files)
def main() -> None:
parser = argparse.ArgumentParser(
description="Validate markdown document structure and quality.",
)
parser.add_argument("path", help="Path to a markdown file or directory.")
parser.add_argument("--json", action="store_true", help="Output report as JSON.")
parser.add_argument("--recursive", "-r", action="store_true",
help="Recursively validate all .md files in a directory.")
parser.add_argument("--fail-on-warnings", action="store_true",
help="Exit with non-zero status if warnings exist.")
args = parser.parse_args()
target = Path(args.path)
if target.is_dir():
if not args.recursive:
print(f"Error: '{args.path}' is a directory. Use --recursive to validate all .md files.",
file=sys.stderr)
sys.exit(1)
files = find_markdown_files(str(target))
if not files:
print(f"No .md files found in '{args.path}'.", file=sys.stderr)
sys.exit(1)
elif target.is_file():
files = [str(target)]
else:
print(f"Error: '{args.path}' not found.", file=sys.stderr)
sys.exit(1)
total_errors = 0
total_warnings = 0
reports = []
for file_path in files:
report = validate_document(file_path)
reports.append(report)
total_errors += report.error_count
total_warnings += report.warning_count
if args.json:
if len(reports) == 1:
print(format_report_json(reports[0]))
else:
combined = [json.loads(format_report_json(r)) for r in reports]
print(json.dumps(combined, indent=2))
else:
for report in reports:
print(format_report_text(report))
if len(reports) > 1:
print(f" Validated {len(reports)} files: "
f"{total_errors} total errors, {total_warnings} total warnings.\n")
if total_errors > 0:
sys.exit(1)
if args.fail_on_warnings and total_warnings > 0:
sys.exit(1)
if __name__ == "__main__":
main()
Related skills
FAQ
What does documentation-authoring do?
documentation-authoring is a Claude Code skill for documentation.
When should I use documentation-authoring?
When you need to helps with documentation tasks., or when documentation-authoring is a claude code skill for documentation.
What are the main capabilities?
documentation-authoring; Documentation; AI-coding skill.