Architecture decisions are unusually expensive because they are hard to reverse and they shape how a team works every day. They are also unusually susceptible to fashion. A pattern that solved a genuine scaling problem at a company with three hundred engineers gets adopted by a team of six, where its costs are identical and its benefits are theoretical.
A more useful question than 'what is the best architecture?' is 'what does this product need to survive the next eighteen months?'
Stage one: finding out if it works
Early on, the dominant risk is building the wrong thing. Optimise for change speed: one deployable application, one relational database, boring frameworks, managed infrastructure, and no service boundaries you cannot justify with a whiteboard sentence. A well-structured monolith with clear internal modules is not technical debt — it is the correct answer to genuine uncertainty, and it keeps refactoring cheap while the product definition is still moving.
Modules are nearly free to move. Services are not. Draw boundaries in code long before you draw them in infrastructure.
Stage two: it works, and now it hurts
Load, team size, and reliability expectations grow together. This is where targeted extraction pays: pull out the parts with genuinely different scaling or reliability profiles — file processing, notifications, scheduled jobs, anything that must not slow the request path. Add read replicas, caching and a queue where measurements show pain. Every change should be traceable to a metric you can name, not to a diagram you admire.
- Instrument before you optimise: latency percentiles, database query time, error rates by endpoint.
- Extract the noisy neighbour, not the whole domain.
- Introduce asynchronous work where the user does not need the result immediately.
- Keep one source of truth for each entity; duplicated ownership is where consistency bugs live.
Stage three: organisational scale
The strongest argument for service decomposition is rarely technical. It is that many teams need to ship independently without coordinating releases. When that becomes the bottleneck, service boundaries drawn along team ownership lines are worth their operational cost — and the cost is real: distributed tracing, contract testing, versioned APIs, on-call rotations, and a platform capability to keep it all sane. If you are not ready to fund those, you are not ready for the architecture.
What to standardise regardless of stage
Some practices earn their place from day one because they reduce the cost of every later decision: automated tests around the critical paths, one-command local setup, deployment from CI with an easy rollback, structured logging with request correlation, and secrets kept out of the codebase. None of these are architecture in the diagram sense. All of them determine whether an architectural change is a routine week or a quarter of risk.
The most maintainable systems we work with are not the most advanced. They match the size of the team, the shape of the load and the tolerance for failure of the business — and they leave room to be changed when any of those three moves.
