Architecture
Designing for growth without premature complexity
What deserves attention in the first version, and which technical decisions can wait for a real need.
Scaling conversations often start with microservices, queues and clusters. The first useful diagram describes how the product works instead: who creates data, who can change it, which actions depend on one another and what happens when an external service is unavailable.
A booking service needs clear responsibilities for its catalogue, available slots, reservations and notifications. That does not require a separate service for every responsibility. It does require code where changing notifications does not mean rewriting booking rules.
These boundaries are easier to discuss with a business because they reflect user behaviour. An architecture diagram is useful when it explains where state lives, who changes it and which failures are handled.
Access control, integrity of important data, state-change rules and error diagnosis can be expensive to add later. They deserve attention before launch even with a small audience. Reliability is not something that only large products need.
Hiding an unavailable slot in the interface does not prevent two simultaneous booking requests. The rule must hold where the result is stored. PostgreSQL documents uniqueness and relationship constraints: mechanisms that help enforce rules in the database rather than only in the interface.
A useful foundation also includes repeatable deployment, backups and tested recovery. A backup matters because it can restore usable data, not because a file exists. Important operations need retry behaviour: another attempt must not create a duplicate payment or request.
One deployable service with clear internal modules can be a reasonable starting point. It reduces the connections that need maintenance and makes unsettled business rules easier to change. Simplicity does not mean one enormous file or no tests.
In Monolith First, Martin Fowler describes an approach where boundaries become clearer within a monolith before services are separated. This is a useful reference, not a universal rule. If independent deployment or isolation is already necessary, account for it from the start.
Ask which current problem a more complex solution will remove. If the answer is only “it might be useful someday”, compare the cost today with the likely cost of changing it later. The cheapest decision is not always the least work now, but speculation also has a price.
After launch, look at where time and resources actually go. A slow page may come from a database query, repeated file processing or an external API. More servers will not fix every one of these problems.
Introduce a service, cache or queue for a specific workload and a clear failure scenario. A lengthy report can become a background job so a request does not remain open until it finishes. That also requires status, retry behaviour and a way to deliver the result.
Record growth plans as conditions: what changes when the audience grows, the data increases or a new role appears? The architecture keeps a direction without turning the first version into infrastructure for every imaginable future.
Clear boundaries and reliable rules create room for growth. Extra complexity earns its place by solving a defined problem.
- Architecture
- Development