Skip to main content
Bonsai Software
All field notes
Field Note3 September 20266 min read

Scalability in custom software architecture: three choices that pay you back

Scalability in custom software architecture is a problem that only becomes visible once it is already too late. On day one the system fits perfectly: it does exactly what the operation demands, performance is solid, and the team is proud. Two years later the system grinds to a halt at twice the user count, every new integration takes weeks, and nobody dares change anything without breaking something else. The cause is rarely a single bad decision, but rather an accumulation of small architectural choices that each seemed reasonable at the time.

By Yeslin Beljaars

Architectural choice 1: putting business logic in the database

It starts innocuously. A stored procedure here, a trigger there. The database is fast, the logic works, and the developer writes less code. But after two years the business logic is scattered across dozens of interdependent procedures. Adjusting a pricing rule means figuring out which triggers you are touching. Connecting a second application to the same database is impossible without duplicating or breaking that logic. Scalability in custom software begins with a clear separation: the database stores data, the application decides. Once that boundary blurs, you pay for it later in weeks of rework per change.

Architectural choice 2: coupling everything synchronously without event-driven patterns

In a small system with ten users, a direct synchronous coupling between modules is perfectly fine. System A calls system B, waits for a response, and moves on. That works until the volume grows. With a hundred concurrent users, or an integration with an external system that occasionally responds slowly, those wait times become visible. One slow link brings the entire chain down. Event-driven patterns, where a module places a message and the receiving module processes it at its own pace, are not always necessary. But if you know the system needs to grow in volume or in the number of integrations, the absence of that layer is an architectural choice you will pay back. Adding it after the fact typically costs more than building it in from the start.

Architectural choice 3: unclear domain boundaries in custom software architecture

The third choice is the hardest to spot, because it lives not in the code but in the structure of the system. When an order module also maintains inventory information, writes customer data, and creates invoice lines, there is no clear domain boundary. Everything is connected to everything. That feels efficient in a small team, but the moment you add a second team, want to scale a module independently, or need to build a new feature, every change requires an analysis of the entire system. Clear domain boundaries, where each module has a single responsibility and communicates through explicit interfaces, are what separate software that scales from software that has a ceiling.

When is a 'too simple' architecture acceptable?

Not every situation calls for a fully distributed, event-driven architecture from day one. If you are validating an internal process, testing a new market, or serving a small team that will remain stable, a simpler setup is often the better choice. Complexity has a price: more infrastructure, more knowledge required, more overhead in the build. The question is not whether your architecture is perfect, but whether you can see the debt you are taking on. Technical debt is acceptable when you know what you are trading off and when you plan to settle it. It becomes a problem when the debt stays hidden: when nobody on the team knows that business logic lives in the database, that there is no domain boundary, or that every integration is synchronous. Then you pay twice: once when building the new feature that does not fit, and again when cleaning up what was already there.

When do you end up paying architectural debt twice?

There are three moments when the debt spirals out of control. The first is an acquisition or merger, where two systems need to work together and each carries its own hidden logic. The second is a mandatory external integration, such as new regulation or a client that requires an API integration. The third is a scaling spike: a new contract, a seasonal peak, or sudden growth the system was not designed for. At each of those moments you are not only facing the question of how to build the new functionality, but also how to untangle what is already there. An architecture that scales from day one requires more time upfront. But that investment pays back at exactly the moments when you have the least time for remediation work.

Seeing this in your own operations?

Book a call

Frequently asked questions

What makes custom software architecture scalable?

Scalable custom software architecture is built on three principles: clear domain boundaries (each module with a single responsibility), loose coupling between modules (preferably via events or explicit interfaces), and business logic in the application layer rather than the database. That combination makes it possible to scale, modify, and integrate modules independently without affecting the entire system.

When is it acceptable to start with a simple custom software architecture?

If you are serving a small, stable team, validating a new process, or deliberately choosing a manageable scope, a simpler architecture is perfectly fine. The condition is that the team understands the limitations and knows when they intend to settle the debt. Unknown debt is more dangerous than debt you are aware of.

How do I recognise that my custom software is losing scalability?

Warning signs include: every new integration takes weeks, a change in one module breaks something in another, the system slows down with more users even though the infrastructure is sufficient, and nobody on the team dares make changes without extensive analysis. These patterns point to hidden dependencies and unclear domain boundaries.

What does it cost to add scalability to custom software after the fact?

It depends heavily on how deep the dependencies run, but it is structurally more expensive than building it in upfront. You are not just building the new functionality; you are also cleaning up what was already there. In a system with business logic in the database and no domain boundaries, that can mean rebuilding a significant portion of the system from scratch.