Architecture & SDLC Best Practices
Best practices here are habits that keep architecture honest under change: write decisions down, draw only useful diagrams, evolve with feedback, enforce rules with tests, and keep documentation close to code. They complement pattern pages — practices make patterns survivable.
An ADR captures a single significant decision: context, options, choice, consequences. Store ADRs in the repo (docs/adr/) so they version with code.
- Write when the decision is costly to reverse, contested, or constrains others.
- Include at least two alternatives — a decision without alternatives is a decree.
- Link requirement IDs that justified the choice.
- Mark status: Proposed, Accepted, Deprecated, Superseded.
- Supersede instead of silently editing history when choices change.
| 1 | # ADR-0007: Modular monolith first |
| 2 | |
| 3 | ## Status |
| 4 | Accepted |
| 5 | |
| 6 | ## Context |
| 7 | Team of 8; domain boundaries still moving; Must: weekly releases (NFR-12). |
| 8 | |
| 9 | ## Options |
| 10 | 1. Microservices by team 2. Modular monolith 3. Classic layered big ball |
| 11 | |
| 12 | ## Decision |
| 13 | Modular monolith with package boundaries + dependency cruiser in CI. |
| 14 | |
| 15 | ## Consequences |
| 16 | + Fast iteration, simple txs |
| 17 | - Must revisit if independent deploy becomes Must |
| 18 | See revisit triggers in ADR-0007 appendix. |
best practice
The C4 model offers zoom levels: Context (system in environment), Container (deployables/data stores), Component (inside a container), Code (optional). Most teams need Context + Container living docs; Component for complex containers; Code rarely as permanent docs.
| Level | Audience | Refresh when |
|---|---|---|
| Context | Everyone | Actors/systems change |
| Container | Eng + ops | Deployables/stores change |
| Component | Team owning container | Module structure changes |
| Code | Implementers | Prefer generated views |
Prefer diagrams-as-code (Structurizr, Mermaid) near the repo. A stale PNG in a wiki is worse than no diagram — it lies confidently.
Evolutionary architecture assumes change: make decisions reversible where possible, protect fitness with automated checks, and migrate incrementally (strangler) instead of big-bang rewrites.
- Delay irreversible choices until evidence exists.
- Prefer modularity before distribution.
- Define fitness functions for critical architectural characteristics.
- Budget continuous refactoring — evolution is not free.
A fitness function is an automated check that the architecture still has a desired characteristic: dependency rules, latency budgets, security baselines, schema compatibility.
| Characteristic | Example fitness check |
|---|---|
| Modularity | Dependency cruiser / ArchUnit fails on illegal imports |
| Performance | k6 budget in CI for critical API |
| Security | SAST + dependency CVE gate |
| Operability | Service must expose /health and metrics endpoint |
| Compatibility | Contract tests between provider/consumer |
| 1 | npx depcruise --validate .dependency-cruiser.js src |
| 2 | npm run test:contract |
| 3 | npm run test:perf:budget |
| 4 | # fail build on violations |
pro tip
- Docs that ship with PRs beat wiki novels.
- README per module: purpose, public API, ownership, runbook links.
- OpenAPI/AsyncAPI as living contracts.
- Runbooks for on-call next to services.
- Keep a glossary for ubiquitous language (DDD).
| 1 | [ ] Requirements: FR + measurable NFRs + AC |
| 2 | [ ] Design: ADRs for significant choices; C4 updated |
| 3 | [ ] Implementation: small batches; reviews; flags |
| 4 | [ ] Testing: risk-based pyramid + contracts |
| 5 | [ ] Deployment: automated, progressive, rollback known |
| 6 | [ ] Maintenance: SLOs, postmortems → backlog |
| 7 | [ ] Process: WIP limited; DoD enforced; feedback real |
Threat model early; define trust boundaries on C4 Container diagrams; make authn/z a cross-cutting architectural concern with clear ownership. Security fitness functions belong in CI — not only annual audits.
| 1 | [ ] Drivers (requirements) written before pattern choice |
| 2 | [ ] ADR accepted with alternatives |
| 3 | [ ] C4 Context + Container current |
| 4 | [ ] Fitness functions in CI for critical rules |
| 5 | [ ] Evolution/revisit triggers listed |
| 6 | [ ] Team topology aligned with boundaries |
| 7 | [ ] Observability standards applied |
| 8 | [ ] Rollback / migration strategy exists |
| 9 | [ ] Docs updated in the same PR as structural change |
Lightweight reviews beat ceremonial boards that become bottlenecks. A practical pattern: author drafts ADR + C4 delta; two peers review within 48 hours; record decision; fitness checks enforce ongoing compliance. Escalate only irreversible, cross-team, or high-risk security decisions to a wider group.
- Review for requirement alignment first — not personal taste.
- Ask "what happens if we are wrong?" and require a rollback/migration story.
- Check team topology impact (who gets paged? who can deploy?).
- Reject speculative complexity without Must drivers.
Pick the top 3–5 quality attributes for the system this quarter. Make them visible on the team board. Attach fitness functions and SLOs. Revisit quarterly — architecture focus should track product strategy.
| Attribute | Practice |
|---|---|
| Performance | Budgets in CI; flamegraphs on regressions |
| Security | Threat model per major feature; scan gates |
| Evolvability | Dependency rules; ADR revisit triggers |
| Reliability | SLO, error budgets, game days |
- Architecture powerpoint with no ADRs and no code impact.
- Updating C4 once a year "for compliance" while systems diverge.
- Fitness functions that everyone ignores (skipped CI jobs).
- Separate architecture team that never ships — advice without feedback.
- Documenting everything except the decisions that matter.
note
When proposing architecture, always emit: (1) restated requirements, (2) options ≥ 3, (3) recommendation, (4) risks, (5) fitness checks to add, (6) ADR draft. Refuse to recommend microservices or event sourcing without explicit drivers in the user problem statement.
Architecture practices fail when delivery is chaotic. Keep batches small, protect mainline, automate releases, and make rollbacks boring. Pair ADRs with feature flags when releasing structural change gradually.
- Trunk-based or short-lived branches with mandatory CI.
- Migrations expand/contract compatible.
- Observability for new paths before traffic increases.
- Post-deploy verification scripts as part of the pipeline.
| 1 | docs/adr/0000-template.md |
| 2 | docs/c4/context.md |
| 3 | docs/c4/container.md |
| 4 | docs/quality-attributes.md |
| 5 | .dependency-cruiser.js (or ArchUnit) |
| 6 | CODEOWNERS |
| 7 | runbooks/README.md |
info
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.