Start With Less, Add When It Hurts

Why starting simple and adding complexity only when the pain is real leads to better architecture than planning for every scenario upfront.

architecture lessons simplicity

The Temptation

When starting a new project, it’s tempting to set up everything “properly” from day one: microservices, event sourcing, CQRS, Kubernetes, the full stack. The reasoning is sound — “we’ll need it eventually, so why not start right?”

Vivolar taught me the answer: because you don’t know what “right” looks like until you’ve built something.

What We Started With

Sprint 1 was dead simple:

No events, no module boundaries, no AI integration, no sophisticated error handling.

When Complexity Was Earned

Each architectural improvement came from real pain:

Module boundaries (ArchUnit) — added after noticing that ItemService had accumulated 8 dependencies. The pain was real: changes in one area cascaded into unrelated areas. The fix was enforceable module boundaries.

Event-driven design (ADR-001) — added after audit logging, notifications, and analytics each added new dependencies to core services. The pain: every new cross-cutting concern required modifying existing services. The fix: publish events, let listeners handle side effects.

TestContainers — added after a mock-based test passed but production broke due to a column type mismatch. The pain was a production bug that tests should have caught. The fix: test against real infrastructure.

Pre-deployment validation — added after a CORS misconfiguration broke production. The pain: a preventable deployment failure. The fix: an automated checklist that catches common mistakes.

The Pattern

Every good architectural decision in Vivolar followed the same pattern:

  1. Start simple
  2. Notice the pain (coupling, bugs, deployment failures)
  3. Understand why the current approach doesn’t work
  4. Add the minimum complexity to solve the actual problem
  5. Document the decision (ADR) so future you remembers why

The Anti-Pattern

The decisions I regret were the opposite: adding complexity before the pain materialized.

Early on, I over-engineered the category system with a complex hierarchy anticipating future needs. Most of that complexity was never used and had to be simplified later.

The Takeaway

Architecture is not a one-time design exercise — it’s an ongoing response to real problems. The best architecture for day one is almost never the best architecture for day one hundred. And that’s fine, as long as your codebase is structured to evolve.

The modular monolith pattern makes this explicit: strict boundaries today, extraction tomorrow if needed. You earn the complexity when the pain is real, not when the whiteboard says you might need it.