Small enough to hold entirely
Each service does one job and fits in one person's head. There is no part of the system that requires a two-week orientation before it can be safely touched.
Almost every other engineering argument is taste, and we will happily lose those. This one decides whether your system is still worth changing in three years.
Nobody decides to build a system that resists change. It happens gradually, and the symptoms show up in the business long before anyone names the cause.
Complexity is not the number of parts. It is the number of relationships between them. Ten things that all know about each other are far more complicated than fifty things that each know about two.
In a tightly coupled system, adding a part adds a relationship to everything already there. The count of things you must hold in your head, test, and keep working grows multiplicatively — so the eleventh part costs far more than the third did, for no visible reason.
Past a certain point, the rational move is to stop changing the system. Teams usually reach that conclusion without ever saying it out loud; it just becomes obvious that the safest release is no release.
Break the system into many small services, give each one job and a contract, and the arithmetic changes. Adding a part adds its own complexity and nothing else — the total moves by +n rather than ^n.
Each service does one job and fits in one person's head. There is no part of the system that requires a two-week orientation before it can be safely touched.
A service that breaks is a service that breaks. It degrades the system rather than stopping it, and the search for the cause starts inside one boundary.
Deleting a part subtracts exactly what adding it cost. That symmetry is what keeps a system from silently accumulating features nobody uses any more.
Services talk through versioned interfaces verified on both sides in CI, so a breaking change fails in the pipeline rather than in production. Contracts also hide the implementation: a service can be rewritten in a different language and the rest of the system never notices.
You do not need to be right up front. The shape can keep changing during the build and long after it — restructuring is a normal operation here.
A bigger system is more small parts, not a harder one. Scale turns into a question of count and capacity — something you can plan, price and staff for.
Microservices are not free. You pay for independent deployability and bounded failure with operational overhead — more moving parts to deploy, observe and debug across. Below a certain size that trade is a bad one, and we will tell you when your system is below it.
What makes the trade work above that size is the platform: most of the overhead is a solved problem in Kubernetes, so you pay it once for the cluster instead of repeatedly per service.
The technology stack for your particular kind of product. Services can be rewritten in an afternoon; a wrong storage decision is felt for years, because by the time it is clearly wrong there is data in it.
We ask simple questions about how the work happens today and what would count as better. No specification required from you — writing one is our job, not yours.
→ domain notes, constraints, what must never break
Technology picked for this product type specifically — above all the database and the data model. This is where we deliberately go slowly.
→ stack decision with the reasoning written down
The core microservices, and more importantly the flow of data between them — which deployments run continuously, which jobs run on a schedule, how the pipelines chain, and how services communicate.
→ architecture sketch, main flow nodes, contracts
Each service in the language that fits its job — Go for throughput and operations, Python for data and models, TypeScript for interfaces, or whatever else the problem calls for. Contracts hide the implementation, so the choice is free per service. Small scope, its own tests, its own metrics.
→ running services, images, CI
Make the stream steady end to end: throughput under load, backpressure where it is needed, retries that are safe, and job pipelines that fail loudly.
→ pipelines, schedules, queues, throughput tests
Connect the system to what your people already use, and to your existing products. Where AI genuinely helps, it goes here — often as a chat bot or an assistant sitting on top of services that already have the answers.
→ integrations, bots, AI-backed services
Across as many hosts as the workload needs, wired into a single cluster, with monitoring and alerting live before anything depends on the system.
→ cluster, dashboards, alerts, runbooks
The system becomes yours: repositories, infrastructure, documentation and the reasoning behind the decisions. Continued support is available afterwards and is never something we design you into needing.
→ handover, documentation, optional support
The usual arrangement asks the client to arrive with a technical brief — which means asking the person who knows the business least about software to do the hardest translation in the project. We run it the other way round.
What happens today between the data arriving and someone making a decision on it?
Two people export it, merge it in a spreadsheet, and send it round by mail before lunch.
What has gone wrong with that in the last year?
Twice the source changed format and nobody noticed for a few days.
If you could ask this data one question at any time of day, what would it be?
Where we are exposed right now, without waiting for the morning file.
Ingest with schema validation that alarms on format drift, a merge service that replaces the spreadsheet and keeps its audit trail, a continuously maintained exposure view, and a bot that answers the question on demand. Four small services — none of which the client had to specify.
Not negotiated per engagement. This is the baseline that makes everything above it mean something.
If it does, the next step is a conversation about your business — not about technology. If it does not, better to find that out now than three months in.