This guide demonstrates how to determine docs coverage and create documentation for your codebase.

This primarily leverages two APIs:

Determining Documentation Coverage

In order to determine the extent of your documentation coverage, you can iterate through all symbols of interest and count the number of docstrings:

To see your current documentation coverage, you can iterate through all symbols of interest and count the number of docstrings:

python
# Initialize counters
total_functions = 0
functions_with_docs = 0
total_classes = 0
classes_with_docs = 0

# Check functions
for function in codebase.functions:
    total_functions += 1
    if function.docstring:
        functions_with_docs += 1

# Check classes
for cls in codebase.classes:
    total_classes += 1
    if cls.docstring:
        classes_with_docs += 1

# Calculate percentages
func_coverage = (functions_with_docs / total_functions * 100) if total_functions > 0 else 0
class_coverage = (classes_with_docs / total_classes * 100) if total_classes > 0 else 0

# Print results with emojis
print("\n📊 Documentation Coverage Report:")
print(f"\n📝 Functions:")
print(f"  • Total: {total_functions}")
print(f"  • Documented: {functions_with_docs}")
print(f"  • Coverage: {func_coverage:.1f}%")

print(f"\n📚 Classes:")
print(f"  • Total: {total_classes}")
print(f"  • Documented: {classes_with_docs}")
print(f"  • Coverage: {class_coverage:.1f}%")

print(f"\n🎯 Overall Coverage: {((functions_with_docs + classes_with_docs) / (total_functions + total_classes) * 100):.1f}%")

Which provides the following output:

📊 Documentation Coverage Report:
📝 Functions:
  • Total: 1384
  • Documented: 331
  • Coverage: 23.9%
📚 Classes:
  • Total: 453
  • Documented: 91
  • Coverage: 20.1%
🎯 Overall Coverage: 23.0%

Identifying Areas of Low Documentation Coverage

To identify areas of low documentation coverage, you can iterate through all directories and count the number of functions with docstrings.

Learn more about Directories here.
python
# Track directory stats
dir_stats = {}

# Analyze each directory
for directory in codebase.directories:
    # Skip test, sql and alembic directories
    if any(x in directory.path.lower() for x in ['test', 'sql', 'alembic']):
        continue
        
    # Get undecorated functions
    funcs = [f for f in directory.functions if not f.is_decorated]
    total = len(funcs)
    
    # Only analyze dirs with >10 functions
    if total > 10:
        documented = sum(1 for f in funcs if f.docstring)
        coverage = (documented / total * 100)
        dir_stats[directory.path] = {
            'total': total,
            'documented': documented,
            'coverage': coverage
        }

# Find lowest coverage directory
if dir_stats:
    lowest_dir = min(dir_stats.items(), key=lambda x: x[1]['coverage'])
    path, stats = lowest_dir
    
    print(f"📉 Lowest coverage directory: '{path}'")
    print(f"  • Total functions: {stats['total']}")
    print(f"  • Documented: {stats['documented']}")
    print(f"  • Coverage: {stats['coverage']:.1f}%")
    
    # Print all directory stats for comparison
    print("\n📊 All directory coverage rates:")
    for path, stats in sorted(dir_stats.items(), key=lambda x: x[1]['coverage']):
        print(f"  '{path}': {stats['coverage']:.1f}% ({stats['documented']}/{stats['total']} functions)")

Which provides the following output:

📉 Lowest coverage directory: 'codegen-backend/app/utils/github_utils/branch'
  • Total functions: 12
  • Documented: 0
  • Coverage: 0.0%
📊 All directory coverage rates:
  'codegen-backend/app/utils/github_utils/branch': 0.0% (0/12 functions)
  'codegen-backend/app/utils/slack': 14.3% (2/14 functions)
  'codegen-backend/app/modal_app/github': 18.2% (2/11 functions)
  'codegen-backend/app/modal_app/slack': 18.2% (2/11 functions)
  'codegen-backend/app/utils/github_utils/webhook': 21.4% (6/28 functions)
  'codegen-backend/app/modal_app/cron': 23.1% (3/13 functions)
  'codegen-backend/app/utils/github_utils': 23.5% (39/166 functions)
  'codegen-backend/app/codemod': 25.0% (7/28 functions)

Leveraging AI for Generating Documentation

For non-trivial codebases, it can be challenging to achieve full documentation coverage.

The most efficient way to edit informative docstrings is to use codebase.ai to generate docstrings, then use the set_docstring method to update the docstring.

Learn more about using AI in our guides.
python
# Import datetime for timestamp
from datetime import datetime

# Get current timestamp
timestamp = datetime.now().strftime("%B %d, %Y")

print("📚 Generating and Updating Function Documentation")

# Process all functions in the codebase
for function in codebase.functions:
    current_docstring = function.docstring()

    if current_docstring:
        # Update existing docstring to be more descriptive
        new_docstring = codebase.ai(
            f"Update the docstring for {function.name} to be more descriptive and comprehensive.",
            target=function
        )
        new_docstring += f"\n\nUpdated on: {timestamp}"
    else:
        # Generate new docstring for function
        new_docstring = codebase.ai(
            f"Generate a comprehensive docstring for {function.name} including parameters, return type, and description.",
            target=function
        )
        new_docstring += f"\n\nCreated on: {timestamp}"

    # Set the new or updated docstring
    function.set_docstring(new_docstring)

Adding Explicit Parameter Names and Types

Alternatively, you can also rely on deterministic string formatting to edit docstrings.

To add “Google-style” parameter names and types to a function docstring, you can use the following code snippet:

python
# Iterate through all functions in the codebase
for function in codebase.functions:
    # Skip if function already has a docstring
    if function.docstring:
        continue

    # Build parameter documentation
    param_docs = []
    for param in function.parameters:
        param_type = param.type.source if param.is_typed else "Any"
        param_docs.append(f"    {param.name} ({param_type}): Description of {param.name}")

    # Get return type if present
    return_type = function.return_type.source if function.return_type else "None"

    # Create Google-style docstring
    docstring = f'''"""
    Description of {function.name}.

    Args:
{chr(10).join(param_docs)}

    Returns:
        {return_type}: Description of return value
    """'''

    # Set the new docstring
    function.set_docstring(docstring)