7.2 The "small modules, stitched later" pattern

Overview and links for this section of the guide.

The core idea

When a task is too big for one prompt (or one context window), you win by splitting the work into small modules with explicit contracts—then integrating them.

The flow is:

  1. Choose seams: define module boundaries and responsibilities.
  2. Define contracts: inputs/outputs, error behavior, invariants.
  3. Build modules: implement and test each module in isolation.
  4. Stitch later: integrate modules with minimal glue code.
What makes this a vibe pattern

You’re using the model as a throughput engine inside boundaries you control. The seams are your architecture.

Why this pattern is powerful with LLMs

LLMs have two limitations that show up in multi-file projects:

  • Context limits: they can’t hold the entire codebase at once.
  • Drift: long conversations accumulate conflicting instructions and assumptions.

Small modules solve both. You can paste only the relevant module + its contract, and get high-quality output with less drift.

This is how you build “big things” with small prompts

Instead of one 2000-line generation, you do ten 200-line generations with clear contracts. That’s faster to review, easier to test, and easier to fix.

How to choose seams (boundaries) that work

Good seams have two traits:

  • they can be tested independently,
  • they have a small, stable interface.

Common seam types

  • Pure functions: parse/transform/validate functions with deterministic outputs.
  • Adapters: “wrap this API call” or “wrap this file read” behind an interface.
  • Core vs I/O: keep business logic separate from CLI/web/database layers.
  • Data models: define schemas/types that everything else uses.
Bad seams

If a module’s job is “do the rest of the app,” that’s not a seam. That’s a dumping ground. Make seams small and specific.

Define contracts before implementation

A contract is the thing you can paste into a prompt that prevents the model from guessing.

At minimum, a contract should specify:

  • function signature / inputs,
  • outputs (including types / schema),
  • error behavior (exceptions, error values),
  • invariants (always true conditions),
  • 2–5 examples.

Example contract (calculator evaluator)

Contract: calc.eval.evaluate(expression: str) -> float

Rules:
- Supports +, -, *, /, parentheses, unary minus
- Ignores whitespace
- Raises CalcSyntaxError for invalid expressions
- Raises ZeroDivisionError for division by zero

Examples:
- "2+2" => 4
- "2*(3+4)" => 14
- "2+*3" => CalcSyntaxError
Contracts are context-efficient

You can paste a contract in 10 lines and get a correct module. That’s better than pasting 500 lines of code and hoping it infers intent.

Build modules in isolation

When implementing a module, keep the prompt narrow:

  • paste only the contract and the module file(s),
  • include the tests for that module,
  • explicitly forbid changing other modules.

This produces tighter diffs and reduces accidental coupling.

Combine with “tests first”

The best version of this pattern is “module contract + tests first”:

  • ask the model to write tests for the contract,
  • confirm the tests match your intent,
  • then implement the module until tests pass.

Stitch modules together safely

Integration is where systems break. Keep stitching deliberate:

  • wire one module at a time,
  • add one integration test per seam,
  • avoid “refactor while integrating.”

The goal is to make integration errors obvious (wrong imports, wrong data shapes, wrong error handling).

Use adapters to isolate churn

If you’re calling external APIs, isolate them behind an adapter module. Then you can iterate on internals without changing the whole app.

Context management: paste less, get more

This pattern is also a context strategy:

  • Keep a short “module map” (one paragraph per module).
  • Paste only the module you’re changing + its direct dependencies.
  • Paste tests that prove correctness.
  • Don’t paste the whole repo unless you truly need cross-cutting changes.

You’re treating context like a budget, not a trash can.

Copy-paste prompt sequence

Prompt A: propose seams and contracts

We are using the “small modules, stitched later” pattern.

Goal:
[Describe the feature/app.]

Constraints:
- Language/runtime: [...]
- Dependencies: [...]
- Keep each module’s public API small

Task:
1) Propose 4–8 modules with responsibilities (one paragraph each).
2) For each module, define a contract (inputs/outputs/errors).
3) Identify which modules can be built/tested independently.
4) Stop and wait for confirmation. No code yet.

Prompt B: write tests for one module

Write tests for Module X based on this contract:
(paste contract)

Constraints:
- Do not implement Module X yet
- Do not change other modules
- Tests should be deterministic and cover edge cases

Output:
- Diff-only changes (tests only)

Prompt C: implement one module to pass tests

Implement Module X to satisfy its contract and pass the tests.

Constraints:
- Only edit Module X and any required shared types
- Keep changes minimal
- Do not introduce new dependencies

Output:
- Diff-only changes

Prompt D: stitch one module into the system

Integrate Module X into the app.

Constraints:
- Wire only the minimal path needed
- Add 1 integration test
- Do not refactor unrelated code

Output:
- Plan first, then diff-only changes

Common pitfalls (and fixes)

Pitfall: too many modules

Fix: collapse modules until you have 4–8. Too many modules create integration overhead and slow the loop.

Pitfall: contracts are vague

Fix: add examples and error behavior. “Returns a result” is not a contract. “Returns JSON matching this schema” is a contract.

Pitfall: stitching becomes a refactor

Fix: freeze internals, wire the seam, add an integration test, then refactor later.

Where to go next