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.

What You'll Learn
This guide covers the complete documentation workflow: structuring content, writing clear explanations, creating effective diagrams, integrating mathematical notation, and maintaining consistency across large documentation projects.

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(log⁑n)O(\log n)O(logn), where nnn is the number of elements in the array. This is because we eliminate half of the remaining elements with each comparison.

T(n)=T(n2)+O(1)=O(log⁑n)T(n) = T\left(\frac{n}{2}\right) + O(1) = O(\log n)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)O(1)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.

Structure Pattern
Introduction (What and Why) β†’ Core Concepts (How it works) β†’ Examples (Practical usage) β†’ Advanced Topics (Edge cases and optimization) β†’ Reference (Complete API/syntax)

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)!n! = n \times (n-1)!n!=nΓ—(nβˆ’1)! with base case 0!=10! = 10!=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
Diagram Best Practices
  • 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)O(n)O(n) - Array is already sorted, only one pass needed

Average Case: O(n2)O(n^2)O(n2) - Random order requires multiple passes

Worst Case: O(n2)O(n^2)O(n2) - Array is reverse sorted

The number of comparisons in the worst case is:

βˆ‘i=1nβˆ’1i=n(nβˆ’1)2=O(n2)\sum_{i=1}^{n-1} i = \frac{n(n-1)}{2} = O(n^2)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)O(1)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."

Comment Quality
Bad comment: // 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(n2)+O(n)T(n) = 2T\left(\frac{n}{2}\right) + O(n)T(n)=2T(2n​)+O(n)

Using the Master Theorem, this resolves to:

T(n)=O(nlog⁑n)T(n) = O(n \log n)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)O(n)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(nlog⁑n)O(n \log n)O(nlogn) performance
  • Stable sort (preserves relative order)
  • Predictable performance

Disadvantages:

  • Requires O(n)O(n)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

FieldTypeRequiredDescription
emailstringYesValid email address
namestringYesUser's full name
rolestringNoUser 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:

Documentation Maintenance Checklist
  • 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:

Use Descriptive Link Text
Instead of "click here," use descriptive text: "See the authentication guide for details."
Provide Alt Text for Diagrams
While Mermaid diagrams render visually, include a text description for screen readers: "This flowchart shows the user authentication process, starting with login credentials and ending with a JWT token."
Avoid Jargon
Define technical terms on first use. Not everyone knows what "idempotent" or "eventual consistency" means.
Use Inclusive Language
Use "they" instead of "he/she," "allowlist" instead of "whitelist," and avoid unnecessarily gendered examples.

Best Practices Summary

Structure
  • Use clear heading hierarchy (H1 β†’ H2 β†’ H3)
  • Follow progressive disclosure (simple β†’ complex)
  • Create consistent document templates
Writing
  • Use active voice and direct language
  • Follow the pattern: Concept β†’ Analogy β†’ Example β†’ Visualization
  • Explain before formalizing with math
Diagrams
  • Choose the right diagram type for your content
  • Keep diagrams simple and focused
  • Use consistent styling and colors
  • Label everything clearly
Code
  • Provide complete, runnable examples
  • Include imports and setup code
  • Add comments for non-obvious logic
  • Show expected output
Math
  • Use math notation to add precision, not complexity
  • Explain concepts before showing formulas
  • Be consistent with variable names
  • Include units and context
Maintenance
  • Review docs with every code change
  • Add "Last Updated" dates
  • Test code examples regularly
  • Version documentation for APIs

Common Pitfalls to Avoid

Assuming Knowledge
Don't assume readers know your domain. Define terms, provide context, and link to prerequisite reading.
Incomplete Examples
Code snippets that can't run frustrate readers. Always provide complete, working examples with all necessary imports and setup.
Outdated Content
Nothing erodes trust faster than documentation that doesn't match the actual behavior. Keep docs current or clearly mark deprecated content.
Over-Complicated Diagrams
A diagram with 50 boxes and 100 arrows helps no one. Break complex systems into multiple focused diagrams.
Math Without Explanation
Dropping formulas without context alienates readers. Always explain what the math represents and why it matters.
Inconsistent Terminology
Using different terms for the same concept confuses readers. Create a glossary and stick to it.

Next Steps

Now that you understand the principles of great documentation, explore these related guides:

Related Content

Ready to Start?

Start creating beautiful technical documentation with AutEng.

Get Started with AutEng