Documentation Best Practices β Markdown + Mermaid + KaTeX
Comprehensive guide to creating exceptional technical documentation using Markdown, Mermaid diagrams, and KaTeX equations together
Introduction
Great technical documentation is the foundation of successful software projects, research papers, and educational content. By combining Markdown's simplicity, Mermaid's visual power, and KaTeX's mathematical precision, you can create documentation that is both beautiful and functional.
This comprehensive guide covers best practices for creating exceptional technical documentation using all three tools together. Whether you're documenting APIs, explaining algorithms, or writing research papers, these principles will help you communicate complex ideas clearly and effectively.
The Power of the Trilogy
Markdown, Mermaid, and KaTeX form a powerful trilogy for technical documentation. Each tool excels in its domain, and together they cover virtually every documentation need:
π Markdown
Structure and prose. Headers, lists, tables, code blocks, and formatted text for clear communication.
π Mermaid
Visual diagrams. Flowcharts, sequence diagrams, architecture diagrams, and state machines for system visualization.
β KaTeX
Mathematical precision. Equations, formulas, algorithms, and mathematical notation for technical accuracy.
Complete Documentation Example
A single document combining all three tools
Algorithm Analysis: Binary Search
Overview
Binary search is an efficient algorithm for finding an item in a sorted array. It works by repeatedly dividing the search interval in half.
Time Complexity
The time complexity of binary search is O(logn), where n is the number of elements in the array. This is because we eliminate half of the remaining elements with each comparison.
T(n)=T(2nβ)+O(1)=O(logn)Algorithm Flow
Rendering diagram...
Implementation
def binary_search(arr, target): low, high = 0, len(arr) - 1 while low <= high: mid = (low + high) // 2 if arr[mid] == target: return mid elif arr[mid] > target: high = mid - 1 else: low = mid + 1 return -1
Space Complexity
Binary search uses O(1) auxiliary space for the iterative version, making it very memory-efficient.
Document Structure Best Practices
Hierarchical Organization
Use a clear heading hierarchy to organize your content. Start with a single H1 for the document title, then use H2 for major sections, H3 for subsections, and so on. This creates a logical structure that's easy to navigate and understand.
Proper Heading Hierarchy
Clear structure with logical nesting
Main Document Title
Introduction
Brief overview of the topic.
Core Concepts
Concept 1
Detailed explanation of the first concept.
Implementation Details
Specific implementation notes.
Concept 2
Detailed explanation of the second concept.
Advanced Topics
Performance Optimization
Tips for improving performance.
Conclusion
Summary and next steps.
Progressive Disclosure
Start with high-level concepts and progressively reveal details. This approach helps readers build mental models before diving into complexity. Use this pattern: Overview β Core Concepts β Details β Advanced Topics.
Writing Clear Explanations
The Explanation Pattern
Great technical explanations follow a consistent pattern: Concept β Analogy β Example β Visualization. This multi-modal approach ensures understanding across different learning styles.
Complete Explanation Example
Using all four elements to explain a concept
Understanding Recursion
The Concept
Recursion is when a function calls itself to solve smaller instances of the same problem.
The Analogy
Think of Russian nesting dolls. To see the smallest doll, you must first open each larger doll. Each doll contains a smaller version of itself, until you reach the base caseβthe smallest doll that doesn't open.
The Example
Here's a recursive function to calculate factorial:
def factorial(n): # Base case: factorial of 0 or 1 is 1 if n <= 1: return 1 # Recursive case: n! = n Γ (n-1)! return n * factorial(n - 1)
The Visualization
Rendering diagram...
The mathematical representation: n!=nΓ(nβ1)! with base case 0!=1
Active Voice and Clarity
Use active voice and direct language. Instead of "The function can be called," write "Call the function." Instead of "It should be noted that," write "Note that." This makes documentation more engaging and easier to follow.
β Passive and Vague
"The data should be validated before it is processed by the system."
"It is recommended that error handling be implemented."
β Active and Clear
"Validate the data before processing it."
"Implement error handling for all API calls."
Creating Effective Diagrams
Choose the Right Diagram Type
Mermaid offers many diagram types. Choose based on what you're trying to communicate:
Flowcharts
Use for: Algorithms, decision trees, process flows
Best when: Showing step-by-step logic with branches and loops
Sequence Diagrams
Use for: API interactions, message passing, time-ordered events
Best when: Showing communication between components over time
Class Diagrams
Use for: Object-oriented design, data models, relationships
Best when: Documenting class hierarchies and associations
State Diagrams
Use for: State machines, lifecycle management, mode transitions
Best when: Showing how systems change state based on events
Diagram Design Principles
Well-Designed Architecture Diagram
Clear labels, logical flow, and appropriate detail level
System Architecture
Our microservices architecture separates concerns and enables independent scaling:
Rendering diagram...
Key Components:
- API Gateway: Single entry point, handles routing and rate limiting
- Auth Service: JWT-based authentication, session management
- User Service: User profiles, preferences, and settings
- Order Service: Order processing, inventory management
- Redis Cache: Reduces database load, improves response times
- Keep it simple: Show only what's necessary for understanding
- Use consistent styling: Same colors/shapes for similar components
- Label everything: No unlabeled boxes or arrows
- Show direction: Use arrows to indicate data/control flow
- Add a legend: Explain colors, line styles, and symbols
Integrating Mathematical Notation
When to Use Math Notation
Use mathematical notation when it adds precision and clarity. Don't use it just to look technical. Good candidates include: algorithm complexity, formulas, statistical measures, and mathematical proofs.
Algorithm Complexity Documentation
Combining prose, math, and code
Time Complexity Analysis
Bubble Sort
Bubble sort repeatedly steps through the list, compares adjacent elements, and swaps them if they're in the wrong order.
Best Case: O(n) - Array is already sorted, only one pass needed
Average Case: O(n2) - Random order requires multiple passes
Worst Case: O(n2) - Array is reverse sorted
The number of comparisons in the worst case is:
i=1βnβ1βi=2n(nβ1)β=O(n2)def bubble_sort(arr): n = len(arr) for i in range(n): swapped = False for j in range(0, n - i - 1): if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] swapped = True if not swapped: # Optimization for best case break return arr
Space Complexity: O(1) - Sorts in place with no additional arrays
Explain Before You Formalize
Always explain concepts in plain language before introducing mathematical notation. The formula should reinforce understanding, not replace explanation.
β Math Without Context
"The Euclidean distance is calculated as:"
$d = \sqrt{(x_2-x_1)^2 + (y_2-y_1)^2}$
β Context Then Math
"To find the straight-line distance between two points, we use the Pythagorean theorem. The distance $d$ is the length of the hypotenuse:"
$d = \sqrt{(x_2-x_1)^2 + (y_2-y_1)^2}$
Code Examples Best Practices
Complete, Runnable Examples
Code examples should be complete and runnable. Include imports, setup, and teardown. Readers should be able to copy-paste and run your examples without modifications.
Complete Example with Context
Everything needed to understand and run the code
Binary Search Implementation
Here's a complete implementation of binary search with error handling:
from typing import List, Optional def binary_search(arr: List[int], target: int) -> Optional[int]: """ Search for target in sorted array using binary search. Args: arr: Sorted list of integers target: Value to search for Returns: Index of target if found, None otherwise Time Complexity: O(log n) Space Complexity: O(1) """ if not arr: return None low, high = 0, len(arr) - 1 while low <= high: mid = (low + high) // 2 if arr[mid] == target: return mid elif arr[mid] > target: high = mid - 1 else: low = mid + 1 return None # Example usage numbers = [1, 3, 5, 7, 9, 11, 13, 15] result = binary_search(numbers, 7) print(f"Found at index: {result}") # Output: Found at index: 3 result = binary_search(numbers, 6) print(f"Found at index: {result}") # Output: Found at index: None
Key Points:
- Type hints for clarity
- Docstring with complexity analysis
- Handles edge case (empty array)
- Includes example usage with expected output
Annotate Complex Logic
Add inline comments for non-obvious logic, but don't comment the obvious. Comments should explain "why," not "what."
// Increment i (obvious from code)Good comment:
// Skip even numbers to optimize for prime checking (explains reasoning)Combining All Three Tools
The real power comes from using Markdown, Mermaid, and KaTeX together. Here's a complete example documenting a sorting algorithm with all three tools:
Complete Algorithm Documentation
Prose, diagrams, math, and code working together
Merge Sort: A Divide-and-Conquer Algorithm
Overview
Merge sort is an efficient, stable sorting algorithm that uses the divide-and-conquer paradigm. It divides the array into smaller subarrays, sorts them recursively, and then merges them back together.
Algorithm Visualization
Rendering diagram...
Time Complexity
The recurrence relation for merge sort is:
T(n)=2T(2nβ)+O(n)Using the Master Theorem, this resolves to:
T(n)=O(nlogn)This holds for all cases (best, average, and worst), making merge sort very predictable.
Space Complexity
Merge sort requires O(n) auxiliary space for the temporary arrays used during merging.
Implementation
def merge_sort(arr): """ Sort array using merge sort algorithm. Time: O(n log n) Space: O(n) """ if len(arr) <= 1: return arr # Divide mid = len(arr) // 2 left = merge_sort(arr[:mid]) right = merge_sort(arr[mid:]) # Conquer (merge) return merge(left, right) def merge(left, right): """Merge two sorted arrays.""" result = [] i = j = 0 while i < len(left) and j < len(right): if left[i] <= right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result.extend(left[i:]) result.extend(right[j:]) return result
When to Use Merge Sort
Advantages:
- Guaranteed O(nlogn) performance
- Stable sort (preserves relative order)
- Predictable performance
Disadvantages:
- Requires O(n) extra space
- Slower than quicksort in practice for small arrays
Best for: Large datasets where stability matters and extra space is available.
Consistency and Style
Establish Conventions
Create and follow consistent conventions throughout your documentation:
Terminology
Use the same terms consistently. If you call something a "handler" in one place, don't call it a "processor" elsewhere. Create a glossary for domain-specific terms.
Code Style
Follow language conventions. Use PEP 8 for Python, follow the language's official style guide. Consistent formatting makes code examples easier to read.
Diagram Style
Use consistent colors and shapes. For example, always use blue for user-facing components, green for backend services, and red for external dependencies.
Math Notation
Be consistent with variable names. If you use $n$ for array size, use it everywhere. If you use $O(n)$ for complexity, don't switch to $\Theta(n)$ without explanation.
Document Templates
Create templates for common documentation types. This ensures consistency and speeds up documentation creation:
API Endpoint Documentation Template
Consistent structure for all API endpoints
POST /api/users
Create a new user account.
Request
POST /api/users HTTP/1.1 Content-Type: application/json Authorization: Bearer <token> { "email": "user@example.com", "name": "John Doe", "role": "developer" }
Response
Success (201 Created):
{ "id": "usr_123", "email": "user@example.com", "name": "John Doe", "role": "developer", "created_at": "2024-01-15T10:30:00Z" }
Error (400 Bad Request):
{ "error": "validation_error", "message": "Invalid email format", "field": "email" }
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | Valid email address | |
| name | string | Yes | User's full name |
| role | string | No | User role (default: "user") |
Authentication
Requires a valid JWT token with users:create permission.
Rate Limiting
- 10 requests per minute per IP
- 100 requests per hour per account
Example
import requests response = requests.post( "https://api.example.com/api/users", headers={"Authorization": f"Bearer {token}"}, json={ "email": "user@example.com", "name": "John Doe", "role": "developer" } ) if response.status_code == 201: user = response.json() print(f"Created user: {user['id']}")
Maintenance and Updates
Keep Documentation Current
Documentation becomes outdated quickly. Establish processes to keep it current:
- Review documentation with every code change
- Add "Last Updated" dates to pages
- Mark deprecated features clearly
- Update examples when APIs change
- Test code examples regularly
- Remove outdated content rather than leaving it
Version Documentation
For APIs and libraries, maintain documentation for multiple versions. Clearly indicate which version each document applies to.
Version-Specific Documentation
Clear version indicators and migration guides
Authentication API
Version: 2.0 | Last Updated: 2024-01-15
What's New in v2.0
- JWT tokens replace API keys
- OAuth 2.0 support added
- Refresh token rotation implemented
Migration from v1.0
If you're using v1.0 API keys, follow this migration guide:
Before (v1.0)
headers = {"X-API-Key": "your-api-key"} response = requests.get(url, headers=headers)
After (v2.0)
headers = {"Authorization": f"Bearer {jwt_token}"} response = requests.get(url, headers=headers)
Deprecation Notice
β οΈ v1.0 API keys will be deprecated on June 1, 2024. Please migrate to JWT tokens before this date.
View v1.0 Documentation (archived)
Accessibility and Inclusivity
Write for Everyone
Good documentation is accessible to readers with different backgrounds and abilities:
Best Practices Summary
- Use clear heading hierarchy (H1 β H2 β H3)
- Follow progressive disclosure (simple β complex)
- Create consistent document templates
- Use active voice and direct language
- Follow the pattern: Concept β Analogy β Example β Visualization
- Explain before formalizing with math
- Choose the right diagram type for your content
- Keep diagrams simple and focused
- Use consistent styling and colors
- Label everything clearly
- Provide complete, runnable examples
- Include imports and setup code
- Add comments for non-obvious logic
- Show expected output
- Use math notation to add precision, not complexity
- Explain concepts before showing formulas
- Be consistent with variable names
- Include units and context
- Review docs with every code change
- Add "Last Updated" dates
- Test code examples regularly
- Version documentation for APIs
Common Pitfalls to Avoid
Next Steps
Now that you understand the principles of great documentation, explore these related guides:
π Markdown Syntax Reference
Complete guide to Markdown, Mermaid, and KaTeX syntax
β KaTeX Integration Guide
Practical guide to mathematical notation in documentation
π Getting Started
Learn the basics and create your first document
π Creating Effective Diagrams
Deep dive into visual communication in technical docs
Related Content
Markdown, Mermaid & KaTeX Syntax
Complete syntax reference for Markdown, Mermaid diagrams, and KaTeX math equations
Using KaTeX in AutEng
Practical guide to integrating mathematical equations in your documentation
Getting Started
Learn the basics of AutEng and create your first document
Features
Explore all the powerful features AutEng offers for technical documentation
Ready to Start?
Start creating beautiful technical documentation with AutEng.
Get Started with AutEng