Alignment
Multiple teams need to agree on shared boundaries, API contracts, and migration sequence. A spec is the shared reference that prevents “I thought you were handling that” conversations.
“Just start coding” works for small features. It does not work for modernization. Migration decisions are hard to reverse — extract the wrong bounded context first and you create a tangle of cross-domain dependencies. Choose the wrong data model and every subsequent module inherits the mistake. Skip the rollback plan and a failed migration becomes an incident.
Specifications prevent these problems by forcing clarity before code.
Alignment
Multiple teams need to agree on shared boundaries, API contracts, and migration sequence. A spec is the shared reference that prevents “I thought you were handling that” conversations.
Risk Reduction
Writing down what could go wrong (and the mitigation plan) before building is cheaper than discovering it in production. A spec’s risk section is a forcing function for this thinking.
Decision Record
Six months from now, someone will ask “why did we migrate payments before inventory?” The spec answers that question. Without it, the rationale is lost when the engineers who made the decision move on.
Scope Control
Modernization projects are magnets for scope creep. A spec with explicit “Won’t Have” boundaries gives teams a reference point when someone suggests adding “just one more thing.”
A modernization specification is not a single document — it is a small set of files, each serving a distinct purpose.
specs/├── INDEX.md # Catalog of all specs├── payment-extraction/│ ├── spec.md # What and why│ ├── plan.md # How and when│ └── tasks.md # Actionable work items├── inventory-migration/│ ├── spec.md│ ├── plan.md│ └── tasks.mdThis structure separates concerns:
Changes to the plan do not require rewriting the spec. New tasks can be added without modifying the plan. Each file evolves at its own pace.
Every spec starts with 3-5 governing principles that guide all decisions. These are the tiebreakers when the team disagrees.
## Constitution
1. **Behavior preservation over modernization speed.** Every migrated component must pass parity tests before cutover.
2. **Independent deployability.** Each extracted service must be deployable without coordinating with other services.
3. **Rollback within 15 minutes.** Every migration step must be reversible without data loss within 15 minutes.
4. **No big bang.** Legacy and modern systems run in parallel until parity is proven. Cutover happens per-module, not all at once.The constitution is the most important section. When a developer asks “should I optimize this query or keep it identical to legacy?”, the constitution answers: behavior preservation comes first.
Document what exists today, including pain points. This section prevents the team from solving the wrong problems.
## Current State
The payment module is embedded in the monolith's `PaymentController`(4,200 LOC). It handles:- Credit card processing via Stripe- Invoice generation (PDF + email)- Refund workflows (full and partial)- Payment reconciliation (nightly batch)
### Pain Points- Controller mixes HTTP handling with business logic- No unit tests for refund edge cases- Reconciliation logic duplicated in 3 places- Stripe API version is 2 years behind current
### Dependencies- Reads from: `orders`, `customers`, `products` tables- Writes to: `payments`, `refunds`, `reconciliation_log` tables- Calls: Stripe API, email service, PDF generator- Called by: Order service, admin dashboard, webhook handlerUse Must Have / Should Have / Won’t Have to set clear scope boundaries.
## Requirements
### Must Have- Extract payment processing into standalone service- Parity tests for all 47 existing payment scenarios- Database migration with rollback script- Feature flag for gradual cutover
### Should Have- Upgrade Stripe API to current version- Consolidate duplicated reconciliation logic- Add monitoring dashboards for payment latency
### Won't Have (Scope Boundaries)- New payment methods (Apple Pay, crypto) — separate project- Payment analytics dashboard — separate project- Multi-currency support rewrite — too risky to combine with extractionThe “Won’t Have” section is critical. Without it, scope expands until the project is late.
Name what could go wrong and how you will handle it.
## Risks and Mitigations
| Risk | Likelihood | Impact | Mitigation ||------|-----------|--------|------------|| Stripe webhook delivery gap during cutover | Medium | High | Run dual receivers for 48 hours, deduplicate by idempotency key || Reconciliation logic has undocumented edge cases | High | Medium | Record all legacy reconciliation runs for 2 weeks before migration || Database migration takes longer than maintenance window | Low | High | Test migration on production-sized dataset, have abort script ready || Feature flag adds latency to payment path | Low | Low | Benchmark with flag enabled, accept up to 50ms overhead |The plan translates the spec into sequenced work.
Break migration into phases that follow dependency order:
## Phases
### Phase 1: Foundation (Week 1-2)- Define payment service API contract- Set up service skeleton with build/test/deploy pipeline- Create database migration scripts (forward + rollback)- Write parity test fixtures from legacy system output
### Phase 2: Core Logic (Week 3-4)- Extract payment processing business logic- Extract refund workflow- Wire to existing database (shared, not yet migrated)- Run parity tests: target 100% pass rate
### Phase 3: Integration (Week 5-6)- Connect order service to new payment service- Deploy behind feature flag (1% traffic)- Monitor error rates, latency, reconciliation accuracy- Gradual rollout: 1% → 10% → 50% → 100%
### Phase 4: Cleanup (Week 7)- Remove legacy payment code from monolith- Remove feature flag- Archive legacy reconciliation scriptsRecord architecture decisions with rationale. This prevents relitigating settled decisions.
## Decision Log
| Date | Decision | Rationale | Alternatives Considered ||------|----------|-----------|------------------------|| 2026-01-15 | Shared database during migration | Avoids data sync complexity; extract database after service extraction | Separate database from day 1 (too risky) || 2026-01-18 | Feature flag per-endpoint, not per-service | Allows granular rollback; one failed endpoint doesn't roll back all | Service-level flag (too coarse) || 2026-01-22 | Keep Stripe API version until Phase 4 | Mixing extraction with API upgrade doubles risk surface | Upgrade during extraction (rejected: too many variables) |Every spec must define measurable outcomes. Without them, “done” is subjective.
## Success Criteria
| Criterion | Measurement | Target ||-----------|-------------|--------|| Parity | All 47 legacy payment scenarios pass | 100% || Latency | P99 payment processing time | < 500ms (current: 450ms) || Availability | Payment service uptime | 99.95% over 30 days || Rollback | Time to revert to legacy path | < 15 minutes || Test coverage | New payment service code | > 90% |TODO Lists Masquerading as Specs
A list of tasks without rationale, principles, or scope boundaries is not a specification. It tells the team what to do but not why or where to stop.
Missing Success Criteria
Without measurable outcomes, the project is never “done.” Teams keep adding work because there is no definition of complete.
No Won't Have Section
Scope creep is the leading cause of modernization project failure. Explicit boundaries prevent “while we are at it” expansions that delay delivery by months.
No Rollback Plan
Every migration step should be reversible. A spec that does not address “what if this fails?” is incomplete.
Prose specifications work for human teams. But modernization projects increasingly involve AI coding agents that benefit from structured, machine-readable specifications.
This is the core insight behind ModernizeSpec: specifications should be structured data that both humans and agents can consume.
| Format | Audience | Strengths |
|---|---|---|
| Markdown specs (spec.md, plan.md) | Human teams | Readable, flexible, supports nuance |
| JSON specs (domains.json, extraction-plan.json) | AI agents + tooling | Parseable, validatable, queryable |
| Both together | Full team (humans + agents) | Best of both worlds |
The markdown spec captures intent, rationale, and nuance. The JSON spec captures structure, sequencing, and measurable data. They complement each other — the markdown explains why, the JSON describes what in a format that tools and agents can process directly.
See the Specification Overview for the full ModernizeSpec file format.
If you have never written a modernization spec before, start with three things:
Everything else (phases, decision log, risk matrix) can be added as the project progresses. But these three establish the foundation that keeps modernization on track.