AI Code Documentation Generation System

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1All 1566 services
AI Code Documentation Generation System
Medium
~5 days
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1196
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1119
  • image_logo-advance_0.webp
    B2B Advance company logo design
    586
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    853
  • image_logo-aider_0.webp
    AIDER company logo development
    783
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    900

AI Code Documentation Generation

Documentation becomes outdated the day after it's written — that's a development constant. AI solves the documentation problem differently: not "write once and maintain," but "regenerate on every change." Docstrings, API documentation, README files, architectural descriptions — all can be automated with quality exceeding average team output.

Docstring Generator

from anthropic import Anthropic
import ast
from pathlib import Path

client = Anthropic()

DOCSTRING_SYSTEM = """You are a technical writer specializing in Python documentation.
Write docstrings in Google Style format:

```python
def function(param: type) -> type:
    \"\"\"Brief description in one line (imperative mood).

    Detailed description if needed. Explain WHAT the function does,
    not HOW. Mention non-trivial algorithms or important side effects.

    Args:
        param: Parameter description. Don't specify type — it's in annotation.

    Returns:
        What it returns. Don't specify type.

    Raises:
        ValueError: When and why it arises.

    Example:
        >>> result = function(value)
        >>> assert result == expected
    \"\"\"

Rules:

  • Brief line: imperative mood (Calculates/Returns/Creates, not Calculate/Return)
  • Don't repeat function name and types from annotations
  • Add Example only for non-trivial functions
  • For simple getter/setter — minimal documentation"""

def generate_docstring(function_source: str) -> str: """Generates docstring for a function"""

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    system=DOCSTRING_SYSTEM,
    messages=[{
        "role": "user",
        "content": f"Write docstring for this function. Return only docstring (triple quotes), no function code:\n\n```python\n{function_source}\n```"
    }]
)

return response.content[0].text.strip()

def add_docstrings_to_file(file_path: str, overwrite: bool = False) -> str: """Adds docstrings to all functions in a file""" source = Path(file_path).read_text() tree = ast.parse(source) lines = source.splitlines()

# Collect functions without docstrings
functions_to_document = []

for node in ast.walk(tree):
    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
        has_docstring = (
            node.body and
            isinstance(node.body[0], ast.Expr) and
            isinstance(node.body[0].value, ast.Constant) and
            isinstance(node.body[0].value.value, str)
        )

        if not has_docstring or overwrite:
            func_source = ast.unparse(node)
            functions_to_document.append({
                "name": node.name,
                "line": node.lineno,
                "source": func_source,
                "indent": len(lines[node.lineno - 1]) - len(lines[node.lineno - 1].lstrip()),
            })

# Generate docstrings
insertions = {}  # line_number -> docstring_text
for func_info in functions_to_document:
    docstring = generate_docstring(func_info["source"])

    # Indent docstring
    indent = " " * (func_info["indent"] + 4)
    docstring_lines = docstring.strip().splitlines()
    indented = "\n".join(indent + line if line.strip() else line for line in docstring_lines)

    # Line after def ... :
    insertions[func_info["line"]] = indented

# Insert docstrings into source
result_lines = []
for i, line in enumerate(lines, 1):
    result_lines.append(line)
    if i in insertions:
        result_lines.append(insertions[i])

return "\n".join(result_lines)

### API Documentation (OpenAPI/Swagger)

```python
from pydantic import BaseModel
import json

def generate_api_docs(router_source: str, existing_models: str = "") -> str:
    """Generates OpenAPI-compatible documentation for FastAPI router"""

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Analyze FastAPI router and add complete OpenAPI documentation:
- summary and description for each endpoint
- description for all parameters (Path, Query, Body)
- response_model with examples
- HTTPException with codes and descriptions

FastAPI router:
```python
{router_source}

{f"Existing models:{chr(10)}python{chr(10)}{existing_models}{chr(10)}" if existing_models else ""}

Return improved router with complete documentation.""" }] )

return response.content[0].text

def generate_readme_section(module_path: str) -> str: """Generates README section for Python module""" source = Path(module_path).read_text()

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=2048,
    messages=[{
        "role": "user",
        "content": f"""Write README section for this Python module.

Structure:

Module Name

Brief description (1-2 sentences).

Usage

# Example code

API

Table of public functions/classes with descriptions.

Requirements

Dependencies if any.

Module code:

{source[:3000]}
```"""
        }]
    )

    return response.content[0].text

Architectural Documentation

def generate_architecture_doc(project_root: str) -> str:
    """Generates architecture description for project"""
    from pathlib import Path
    import os

    # Collect project structure
    structure = []
    for root, dirs, files in os.walk(project_root):
        # Skip vendor, cache
        dirs[:] = [d for d in dirs if d not in {
            ".git", "__pycache__", "node_modules", ".venv", "migrations"
        }]
        level = root.replace(project_root, "").count(os.sep)
        indent = " " * 2 * level
        structure.append(f"{indent}{os.path.basename(root)}/")
        for file in files:
            if file.endswith((".py", ".ts", ".tsx")):
                structure.append(f"{indent}  {file}")

    # Read key files
    key_files_content = {}
    key_patterns = ["models.py", "urls.py", "routes.ts", "app.py", "main.py"]
    for root, _, files in os.walk(project_root):
        for file in files:
            if file in key_patterns:
                file_path = os.path.join(root, file)
                try:
                    key_files_content[file_path] = Path(file_path).read_text()[:1500]
                except Exception:
                    pass
        if len(key_files_content) >= 5:
            break

    key_content = "\n\n".join([
        f"### {path}\n```python\n{content}\n```"
        for path, content in key_files_content.items()
    ])

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Write architecture documentation for project.

Project structure:

{chr(10).join(structure[:100])}


Key files:
{key_content}

Include:
1. Architecture overview (2-3 paragraphs)
2. Main components and their roles
3. Interaction diagram (text)
4. Key patterns used in code
5. Entry points and startup

Format: Markdown for README."""
        }]
    )

    return response.content[0].text

Auto-update Documentation in CI/CD

# .github/workflows/docs.yml
name: Update Documentation

on:
  push:
    branches: [main]
    paths:
      - "src/**/*.py"
      - "app/**/*.py"

jobs:
  update-docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Generate docstrings
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python scripts/generate_docs.py \
            --source src/ \
            --check-missing-only \
            --output-report docs/coverage.json

      - name: Update API docs
        run: |
          python scripts/generate_api_docs.py \
            --routers app/api/ \
            --output docs/api/

      - name: Commit if changed
        run: |
          git config user.email "[email protected]"
          git config user.name "Docs Bot"
          git add docs/
          git diff --staged --quiet || git commit -m "docs: auto-update documentation"
          git push

Practical Case: Microservice with 0% Documentation

Task: Python FastAPI service, 4200 lines, 67 endpoints, 0 docstrings. Onboarding new developer took 3 weeks.

Automation:

  • Batch docstring generation for all functions (182 functions, 45 min)
  • OpenAPI description generation for all endpoints
  • Architectural README with component descriptions

Results:

  • Docstring coverage: 0% → 91%
  • New developer onboarding time: 3 weeks → 1 week
  • "How does X work" Slack questions: -68%
  • Team documentation quality rating: 4.1/5.0

Nuance: for 8% of functions with non-trivial business logic, AI-generated documentation was inaccurate — required manual fixes. System marks such functions (cyclomatic complexity > 10) for review.

Docstring Coverage as CI Metric

def check_docstring_coverage(source_dir: str, threshold: float = 0.8) -> bool:
    """Checks docstring coverage, returns False if below threshold"""
    total = 0
    documented = 0

    for py_file in Path(source_dir).rglob("*.py"):
        source = py_file.read_text()
        try:
            tree = ast.parse(source)
        except SyntaxError:
            continue

        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                if not node.name.startswith("_"):  # Public functions
                    total += 1
                    if (node.body and isinstance(node.body[0], ast.Expr) and
                            isinstance(node.body[0].value, ast.Constant)):
                        documented += 1

    coverage = documented / total if total > 0 else 1.0
    print(f"Docstring coverage: {coverage:.1%} ({documented}/{total})")
    return coverage >= threshold

Timeline

  • Docstring generator for existing codebase: 2–3 days
  • OpenAPI documentation for FastAPI/Django REST: 3–5 days
  • Automatic update in CI/CD: 1 week
  • Architecture documentation + wiki: 1–2 weeks