
What to automate first in a small business
Choose a first automation by scoring the work, data, risk, ownership, and reversibility. Then validate one pilot before you expand it.
Microservices buy independent change at a real operating cost. Compare both models through boundaries, ownership, data, deployment, and signals that justify extraction.

Billing and inventory share a release even though different teams develop them. Start the architecture decision with that constraint and the cost of removing it. A separate service can enable independent deployment while adding network calls and operational work.
A modular monolith is one application whose insides are split by business capability. Billing asks inventory to reserve stock through an interface that inventory owns, never by reading inventory's tables. Orders work the same way. Microservices take that same boundary and put a process on either side of it, usually separate data stores as well. What changes is the price of running the boundary, and that price is worth knowing before anyone opens a second repository, only to find that the two-field form now needs two deployments.
Same domain, two operational price tags
Boundaries can be identical. What differs is the process, the network and who answers for it.
Modular monolith
One process and deployment. Modules communicate through internal contracts.
Microservices
Independent processes and deployments. Every boundary adds network and operations.
If a team cannot draw a useful domain boundary inside one process, the network will not find it for them. An uncertain split shows up as frequent remote calls over shared tables, plus releases that two teams have to schedule for the same hour every time. The system looks distributed. It still has to change in one piece.
A module needs a public contract. An architecture test plus a dependency check will keep that contract honest, provided every package has an owner who notices when it breaks. Once the boundary holds locally, moving it behind a network API stops being a leap in the dark, and becomes work somebody can estimate without guessing.
Useful modules follow business capabilities. Folders called controllers and repositories name layers rather than capabilities. A pricing change then stays inside pricing instead of surfacing in shipping two weeks later, when nobody on the team remembers what that release had actually touched. The same thinking runs one floor down, at class level, in our guide to design patterns in practice.
An independently deployed service changes on its own cadence. Search can ship on a Tuesday afternoon while the rest of the product carries on serving the last approved build, while compatibility between services still needs testing. A compute-heavy workload scales on its own. A sensitive capability can sit behind a stricter security boundary than everything around it. Independence has a price and a value, and only an organisation that can use it gets the second one.
The bill arrives immediately. A function call becomes a network request. The answer can be slow or arrive twice. Sometimes it never arrives. From then on the system needs timeouts, idempotent retries and tracing across services, and somebody has to decide in advance what happens when one call in the chain fails. A single database transaction no longer holds a business operation together.
Testing changes too. A green unit test proves nothing about whether two deployed services still agree on what an order looks like. Consumer and provider contracts need their own compatibility check on every release. Without that discipline, diagnosis time grows faster than the service count, and the bill for it lands on whoever happens to be holding the pager that week.
A service that owns its code but queries another service's tables is not independent. A schema change in another team can break it without touching its own repository, which is why data ownership gets decided when somebody first draws the boundary on the whiteboard. Not later, at the storage decision.
Separate stores mean some views are stale for a moment. An order is accepted and billing learns about it an instant later. The team has to name the places where that delay is safe. A reporting dashboard survives it. An account balance, or a reservation on the last item in stock, needs a different design, because a moment of drift there turns into a phone call from the customer, who was already promised the goods.
A distributed transaction should not be disguised as a fragile chain of synchronous calls.
Events with idempotent consumers help. So does a workflow that resumes where it stopped after an outage. That is another operating layer. It has to be watched and exercised. Where the product does not yet need independent data ownership, one database behind well-guarded module boundaries is cheaper, and nobody has to reconcile it afterwards with the second copy of the same numbers.
Microservices work best where a team owns a service from the first design through deployment to the incident at two in the morning, and not where somebody merely inherited it. A separate repository with no clear owner does not concentrate responsibility. It scatters it. Changes then wait on strangers.
A smaller product team usually gains more from one deployment and one development environment, where refactoring can cross module boundaries without waiting on somebody else's merge. That does not license a tangled monolith. Distribution is simply an expensive way to enforce discipline from outside.
It tips the other way when two teams keep blocking the same release and the boundary between them is stable, while each side needs its own deployment rhythm. Extraction then cuts the coordination bill. The evidence comes from work that is already happening.
Signals that a module has outgrown one deployment
Split for a measured problem, not an imagined future scale.
Independent change cadence
One module blocks releases of the rest.
Different scaling profile
Load is isolated and repeatedly measured.
Clear owner
A team can own delivery and operations.
Stable contract
The domain boundary no longer moves every sprint.
One signal is an independent reason to change. The module has its own roadmap and most of its work leaves the rest of the system untouched, so the people around it are not waiting on anyone. The second signal is operational. The module needs different scaling or different availability from the rest of the application, sometimes one technology nobody else would willingly add.
The third signal is a stable contract. The team can name inputs and outputs without guessing, can say who owns the data, and can describe what the service actually does when it fails. If the contract moves every sprint, separate deployment does not remove the instability. It moves it into API versioning.
The practical route is to harden the boundary inside the monolith first. Kill the direct database shortcuts. Add contract tests, measure how many calls cross the boundary, then count how often those calls fail under real load rather than in the demo environment. Only then is it worth comparing the benefit of extraction against the price of distribution. The service is a conclusion a team reaches with numbers in hand.
A modular monolith is often the sensible starting point for a new business system whose product and boundaries are still moving. One deployment keeps feedback fast. Strong modules leave the door open for the capability that later earns its independence, which can limit the scope of later extraction.
Microservices are the right call once a company already pays a measurable price for shared releases or unclear ownership, and once the same is already true of shared scaling. The budget then has to carry the platform and the observability, plus the on-call rota. A source-code split creates none of it.
In custom software development we write these assumptions down as a decision somebody can come back to in a year and ask whether it actually held. The next article covers legacy modernization without a big-bang rewrite, for the system that already makes every change dangerous. Judge the decision by the cost of making a safe change.
Maroš Bednár prepared the article with AI support for research and language editing. He reviewed and approved the technical conclusions, examples, and final text.

Choose a first automation by scoring the work, data, risk, ownership, and reversibility. Then validate one pilot before you expand it.
A model plan for one recurring task. Measure today’s work, set data permissions and output review, then decide whether the trial should continue.

Replace risky parts of an older system in controlled stages. Learn how seams, a facade, contract tests, parallel runs, and rollback turn modernization into managed delivery.