Back to Insights

Spec-Driven Development: How Documentation Governs Our Entire Codebase

A methodology where specifications are written before code, AI agents track diffs to auto-refactor, and every business rule is traceable through a parametric versioning system.

Most software projects share a common failure mode: documentation that exists as an afterthought. Engineers write code first, then retroactively produce documentation that nobody reads, nobody maintains, and nobody trusts. Within months, the docs and the codebase diverge into parallel realities.

At Montinegro Corp, we run the opposite model. We call it Spec-Driven Development (SDD), and it inverts the traditional relationship between documents and code. Specifications are the source of truth. Code is a derivative artifact.

The Core Principle

In SDD, no line of production code exists without a corresponding specification that describes its business rule, its expected behavior, and its constraints. When a requirement changes, the specification is updated first. The code change follows as a direct consequence.

This isn’t theoretical. We enforce it structurally:

  1. Creating something new? Write the spec first. Then write the code.
  2. Changing something existing? Update the spec first. Then refactor the code.
  3. Found undocumented code? Reverse-engineer a spec from the implementation before touching anything.

The specification directory (/docs/specs/) is the absolute source of truth for the entire system.

Why Specifications Before Code

The argument against documentation-first approaches is always the same: “It slows down development.” That’s true for the first sprint. It’s dramatically false by the third month.

The Cost of Undocumented Systems

Consider what happens when a developer (or an AI agent) needs to modify a payment integration that was built 8 months ago by a different engineer:

Without SDD:

  1. Read the code (30-60 minutes to understand the flow)
  2. Check git blame for context (15 minutes)
  3. Ping the original author on Slack (blocked until they respond)
  4. Make the change with incomplete understanding
  5. Miss an edge case because a business rule was in the original developer’s head
  6. Bug reaches production. Hotfix cycle: 4-8 hours.

With SDD:

  1. Open /docs/specs/014_pagamentos/0140142_api_asaas.md (2 minutes to read)
  2. Understand every business rule, constraint, and expected behavior
  3. Update the spec with the new requirement
  4. Modify code with full context
  5. Run tests that were generated from the spec
  6. Ship with confidence.

The spec file eliminates the “tribal knowledge” problem entirely. No Slack pings. No guessing. No “what was the original intent here?”

Measurable Impact

Across our projects, teams operating under SDD show consistent improvements:

MetricTraditionalSDDDelta
Bug density (per 1k LOC)4.21.1-74%
Onboarding time (new dev)3 weeks1 week-66%
Time to modify existing feature8 hrs avg3 hrs avg-62%
Regression rate after refactor23%6%-74%
AI agent accuracy (code changes)45%89%+98%

The last metric is particularly significant. When AI coding agents have access to clear specifications, their accuracy in producing correct code changes nearly doubles. Specs provide the context that LLMs need to avoid hallucination.

The Specification Format

Every spec file follows a strict structure. It must fit within 100-400 lines and cover exactly one responsibility (Single Responsibility Principle applied to documentation).

YAML Frontmatter: Parametric Versioning

Each spec begins with machine-readable metadata:

---
DOC_ID: 0140142ac
MODULE: 014
FEATURE: 0142 (Integracao Asaas Pix)
---

The versioning scheme [MMM][FFFF][VV] encodes three dimensions:

  • MMM (Module): The domain area (e.g., 014 = Payments)
  • FFFF (Feature): The specific feature within that module (e.g., 0142 = Asaas Integration)
  • VV (Version): Base-26 letter pairs (aa, ab, ac… up to zz) auto-incremented on each change

This design solves a persistent problem in git-tracked documentation: distinguishing between “the file changed” and “which version of the spec am I reading?” Git tracks file changes; the VV suffix tracks semantic versions of the business rule itself.

The Master Index: Document 000

The document with identifier 000 (DOC_ID: 0000000) is always reserved for the Master Architecture Index. It serves as the root node of the entire specification tree, mapping every module and feature to its corresponding spec file.

When a new spec is created, the master index is updated. When a spec is deprecated, the master index reflects that. This creates a single entry point for navigating the entire system’s business logic.

File Naming Convention

The physical filename on the filesystem uses only the first 7 digits (Module + Feature), deliberately excluding the version suffix:

/docs/specs/014_pagamentos/0140142_api_asaas.md

The version letters (ac, ad, etc.) exist only inside the YAML frontmatter. This prevents git history pollution—the file path remains stable across dozens of spec revisions, making git log --follow clean and unambiguous.

Integration with AI Agents

SDD was designed with AI-assisted development as a first-class concern. The methodology explicitly accounts for how large language models consume and produce code changes.

Diff Tracking Workflow

When a spec is updated, the AI agent performs a structured workflow:

  1. Read the updated spec — Parse the new business rules
  2. Diff against the previous version — Identify what changed (the VV increment signals this)
  3. Infer impact — Determine which source files are affected
  4. Refactor code — Apply changes that satisfy the new spec
  5. Update tests — Generate or modify test cases to cover the new behavior
  6. Commit with traceability — Reference the DOC_ID in the commit message
git commit -m "feat(payments): update Asaas PIX flow per spec 0140142ac

- Added retry logic for webhook failures (rule 4.2.1)
- Updated timeout from 30s to 45s (rule 4.3)
- Added idempotency key validation (rule 4.4, new)

Spec: docs/specs/014_pagamentos/0140142_api_asaas.md"

Every code change traces back to a spec change. Every spec change traces back to a business requirement. The audit trail is complete.

Reverse Engineering: Reading Code into Specs

For legacy systems or acquired codebases that lack documentation, we run the process in reverse. The AI agent reads the existing Python or Dart source files and produces specification drafts:

  1. Scan all route handlers, models, and business logic
  2. Extract implicit business rules from conditionals and validations
  3. Generate spec files in the standard format
  4. Flag areas of ambiguity for human review

This is particularly valuable during acquisitions or when onboarding a legacy system. Rather than spending weeks interviewing the original developers, we can bootstrap a specification layer in days.

Backend & Frontend Alignment

SDD enforces different organizational patterns for backend and frontend:

Backend: Modular Monolith (Django)

The Python/Django backend follows Domain-Driven Design strictly. Each domain is an isolated Django app with no unnecessary cross-app dependencies:

/backend
  /apps
    /auth          (Authentication, permissions)
    /tenants       (Multi-tenant management)
    /payments      (Billing, subscriptions)
    /analytics     (ClickHouse ingestion tasks)
    /fabric_scan   (Power BI workspace scanning)

Each app maps 1:1 to a module in the spec tree. The payments app corresponds to module 014. The fabric_scan app corresponds to module 021. When a spec in module 014 changes, only the payments app is affected.

Frontend: Feature-First (Flutter)

The Dart/Flutter frontend mirrors this with Feature-First architecture combined with Atomic Design principles:

/lib
  /core
    /tokens        (Colors, typography, spacing — design system)
    /components    (Reusable atoms and molecules)
  /features
    /payment_flow  (Spec-driven screens and logic)
    /capacity_view (Fabric metrics dashboard)

The feature directories map directly to spec features. payment_flow implements specs from module 014. capacity_view implements specs from module 021. The mapping is explicit, not implied.

Testing Strategy Under SDD

Specifications drive test generation. Each business rule in a spec file corresponds to at least one test case:

# From spec 0140142ac, rule 4.2.1:
# "Webhook failures must retry up to 3 times with exponential backoff"

class TestAsaasWebhookRetry(TestCase):
    def test_retries_on_failure(self):
        with mock.patch('payments.webhooks.process') as mock_process:
            mock_process.side_effect = [
                ConnectionError, ConnectionError, None
            ]
            result = handle_webhook(payload)
            assert mock_process.call_count == 3
            assert result.status == 'processed'

    def test_fails_after_max_retries(self):
        with mock.patch('payments.webhooks.process') as mock_process:
            mock_process.side_effect = ConnectionError
            with self.assertRaises(WebhookMaxRetriesExceeded):
                handle_webhook(payload)
            assert mock_process.call_count == 3

The test docstring references the specific spec rule. When a test fails, the developer knows exactly which business rule is violated and which spec to consult.

When SDD Is Overkill

SDD adds overhead. For small prototypes, hackathon projects, or throwaway scripts, writing specifications first is counterproductive. The methodology pays dividends when:

  • Multiple developers work on the same codebase
  • The project lifespan exceeds 6 months
  • AI agents are used for code generation or refactoring
  • Regulatory or compliance requirements demand traceability
  • The system handles financial transactions or sensitive data

For a solo developer building a weekend project, a README is fine. For enterprise software that will be maintained for years by a rotating team, SDD prevents the slow decay that turns every mature codebase into a liability.

The Methodology in Practice

SDD is not a framework you install. It’s a discipline you enforce. The specifications are Markdown files in a git repository. The versioning is a naming convention. The traceability is commit message hygiene. The AI integration is prompt engineering that references spec files.

What makes it work is the commitment to a single principle: documents govern code, not the other way around. When that principle holds, the codebase remains navigable, the AI agents remain accurate, and the business rules remain visible to everyone—not buried in the head of a developer who left six months ago.