Guide: Documenting a Django Legacy Codebase

Django projects are notorious for accumulating undocumented code over time. Views that do too much, model methods without descriptions, and templates with no context become a maintenance nightmare. Documenting legacy Django apps requires a different approach than starting fresh.

Why Django Documentation Matters

Unlike vanilla Python, Django has specific documentation needs:

The Django Documentation Challenge

Legacy Django codebases face unique hurdles:

A Systematic Documentation Approach

Phase 1: Audit Models First

Start with your models—they define the data structures everything else depends on. Identify:

Phase 2: Document Views

Views need to document their request/response contracts. Focus on:

Phase 3: API Layer

For Django REST Framework projects, document serializers and viewsets:

Example: Documenting a Django View

Before (undocumented):

def invoice_report(request):
    if not request.user.is_authenticated:
        return redirect('login')
    month = request.GET.get('month')
    year = request.GET.get('year')
    invoices = Invoice.objects.filter(
        month=month, year=year
    ).select_related('customer')
    total = invoices.aggregate(Sum('amount'))['amount__sum']
    return render(request, 'reports/invoice.html', {
        'invoices': invoices,
        'total': total,
        'month': month,
        'year': year
    })

After (documented):

def invoice_report(request):
    """
    Generate an invoice report for a specific month and year.

    Requires authentication. Uses select_related to minimize DB queries
    when fetching invoice data with customer information.

    Args:
        request: HttpRequest object with user authentication and GET params

    Query Parameters:
        month: Month to filter invoices (1-12)
        year: Year to filter invoices (e.g., 2024)

    Returns:
        HttpResponse: Rendered HTML template with invoice list, total,
        and the month/year parameters passed back for context

    Permissions:
        login_required: Only authenticated users can access

    Template:
        reports/invoice.html
    """
    if not request.user.is_authenticated:
        return redirect('login')
    month = request.GET.get('month')
    year = request.GET.get('year')
    invoices = Invoice.objects.filter(
        month=month, year=year
    ).select_related('customer')
    total = invoices.aggregate(Sum('amount'))['amount__sum']
    return render(request, 'reports/invoice.html', {
        'invoices': invoices,
        'total': total,
        'month': month,
        'year': year
    })

Example: Documenting a Django Model

Before:

class Invoice(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    month = models.IntegerField()
    year = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)

    def total_with_tax(self):
        return self.amount * 1.1

After:

class Invoice(models.Model):
    """
    Represents a single customer invoice in the billing system.

    Each invoice is tied to a Customer and tracks amount, period,
    and system timestamps. The amount field is in the company's
    base currency.
    """
    customer = models.ForeignKey(
        Customer,
        on_delete=models.CASCADE,
        help_text='Customer this invoice belongs to'
    )
    amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name='Invoice Amount',
        help_text='Total amount in base currency (no tax)'
    )
    month = models.IntegerField(verbose_name='Billing Month')
    year = models.IntegerField(verbose_name='Billing Year')
    created_at = models.DateTimeField(auto_now_add=True)

    def total_with_tax(self):
        """
        Calculate the invoice total including standard VAT rate.

        Returns:
            Decimal: Amount with 10% tax added
        """
        return self.amount * Decimal('1.1')

Tools That Help

The Legacy Doc-AI CLI can parse Django projects and generate docstrings for views, models, serializers, and more. It respects Django conventions and can integrate with your existing code style. Run batch scans to find undocumented code, review diffs, and apply changes systematically.

Conclusion

Documentation is especially valuable for Django legacy codebases. It helps onboard new developers, reduces the risk of breaking implicit contracts, and makes refactoring safer. A systematic approach—models first, then views, then APIs—combined with diff review tools makes the process manageable.

Legacy Doc-AI