Microservices are an architectural style where an application is built as a collection of small, independently deployable services, each owning a narrow slice of business logic and communicating over a network. Adjacent concepts include monoliths, modular monoliths, service-oriented architecture, and distributed systems. For operators running production services on constrained, intermittent, or off-grid infrastructure—community ISPs, rural clinics, fintech branch offices, small manufacturing sites—the question is not whether microservices are fashionable. The question is whether they reduce your failure surface or multiply it. This framework helps you decide before you spend a single hour refactoring.

Start with the failure you can already see
Most microservices discussions begin with scaling, team size, or code complexity. Those are real concerns, but they are not your first concern. Your first concern is what happens when the link drops, the generator runs out of diesel, or a backhaul microwave fails. A monolith fails as one unit. Microservices fail as many units, often in partial and confusing ways. If you cannot already operate a single deployable artifact reliably, adding network calls between your own components is not modernization. It is borrowing trouble.
Before evaluating microservices, write down the last three production incidents you handled. For each one, ask: would this incident have been shorter or longer if the affected component had been a separate service? If the answer is “longer” for two out of three, you have your answer for now.
The real cost is not the code
Microservices are often sold as a way to make code easier to change. In practice, the code becomes easier to change only if you also invest in observability, deployment pipelines, service discovery, and network policy. On a constrained site, each of those investments has a local cost:
- Observability means more logs, metrics, and traces to store and query. If your monitoring server is a Raspberry Pi in a cabinet, that cost is not abstract.
- Deployment pipelines mean more moving parts during an upgrade. A single artifact can be rolled back with one command. Ten artifacts require ten rollbacks, or a coordinator that can itself fail.
- Service discovery means DNS or a registry that must be available before any service can talk to another. If that registry is unreachable during a brownout, your application is down even if every business service is healthy.
- Network policy means you now have an internal attack surface. A compromised service can reach others. On a shared community network, that is not a theoretical risk.
None of this means microservices are wrong. It means the cost is paid in operational attention, not just developer hours. If you have one person who handles both code and outages, that person is now on call for a distributed system. That is a different job.

A five-question framework
Use these questions in order. If you cannot answer “yes” to the first two, stop. You do not need microservices yet.
1. Do you have at least two teams that need to deploy independently?
Microservices solve an organizational problem: multiple teams stepping on each other inside one codebase. If you have one team, or one person, you do not have that problem. A modular monolith—where the code is separated into clear modules but deployed as one artifact—gives you most of the maintainability benefit without the network cost. Many community ISP billing systems and clinic record systems run perfectly well as modular monoliths for years.
2. Do you have a component that must scale independently of the rest?
Scaling is the most common justification, but it is often based on a guess. Measure first. If your patient registration service handles 50 requests per hour and your pharmacy inventory service handles 20, neither needs independent scaling. If your payment gateway handles 5,000 transactions per day while your reporting service handles 10, you still may not need microservices—you may need a queue and a background worker inside the same deployable. Independent scaling matters when one component has a load profile that is orders of magnitude different and bursty. Even then, a separate process on the same host may be enough.
3. Can you tolerate partial failure?
Microservices give you the ability to keep part of the system alive when another part fails. That is a real benefit for a rural clinic: if the lab results service is down, the pharmacy can still dispense. But partial failure requires you to design for it. Every call between services needs a timeout, a retry policy, and a fallback. If you do not have time to build those, you have built a distributed monolith—one where a failure in any service takes down the whole user journey, but now with extra network hops.
4. Do you have a deployment pipeline that can handle many artifacts?
If your current deployment is “copy a JAR file to the server and restart,” microservices will hurt. You need a way to build, test, and deploy each service independently, and to roll back one without rolling back all. That pipeline itself is a production system. It needs monitoring, backups, and someone who understands it. On a site with intermittent connectivity, the pipeline must also handle partial syncs and resume after a link drop. If that sounds like a project, it is.
5. Can you observe the system when it is broken?
When a monolith fails, you look at one log file. When ten services fail, you look at ten log files, plus the network between them. You need centralized logging, distributed tracing, and metrics that tell you which service is slow, which is down, and which is lying. If your observability stack is “SSH in and tail the log,” microservices will make every incident longer. Build the observability first, even if you stay on a monolith. It pays for itself either way.
What a modular monolith gives you
Before you choose microservices, consider the middle path. A modular monolith is a single deployable artifact with clear internal boundaries. Modules communicate through in-process calls, not network calls. You get:
- One deployment—one artifact to build, test, ship, and roll back.
- One failure domain—if the process dies, everything dies, but you know exactly what happened.
- Enforced boundaries—if you are disciplined, modules do not reach into each other’s internals.
- A future path—if you later need to extract a service, the boundary is already drawn.
For a fintech branch office running a local ledger, a modular monolith is often the right answer. The ledger, the teller interface, and the reporting module can live in one process. If the branch loses connectivity to headquarters, the whole system keeps working locally. If you split it into microservices, you now have to manage inter-service communication over a local network that may be flaky. That is a worse failure mode, not a better one.
When microservices do make sense
There are cases where microservices earn their keep, even on constrained infrastructure. They tend to look like this:
- You have a component with a very different availability requirement. A payment gateway that must be up 99.9% of the time, while the reporting dashboard can be down for an hour, is a candidate for separation.
- You have a component with a very different security boundary. A service that handles patient identities should not share a process with a service that handles public web forms. Separation reduces the blast radius of a compromise.
- You have a component that must be deployed on different hardware. A machine vision service for a small manufacturing line may need a GPU. A billing service does not. Splitting them lets you put each on the right hardware.
- You have multiple teams in different locations. If one team in Lagos and one in Nairobi need to ship changes without coordinating, independent deployables reduce friction. But this is an organizational reason, not a technical one.
In each case, the decision is driven by a specific, measurable difference in requirements. Not by a conference talk or a job posting.

A worked example: rural clinic records
Imagine a rural clinic in northern Nigeria running an electronic medical records system. The system handles patient registration, lab orders, pharmacy dispensing, and monthly reporting to the state health ministry. It runs on a single server in the clinic, with a satellite link for syncing reports.
Monolith version: One application, one database, one process. The clinic staff use it every day. When the satellite link drops, the system keeps working locally. Reports queue and sync when the link returns. The operator—often a nurse with IT responsibilities—can restart the whole system with one command.
Microservices version: Patient registration, lab orders, pharmacy, and reporting are four services. They communicate over HTTP on the clinic LAN. Each has its own database. The reporting service syncs to the state ministry. When the satellite link drops, the reporting service retries. But now the pharmacy service depends on the patient registration service to verify a patient ID. If the registration service is slow—perhaps its database is locked—the pharmacy queue backs up. The nurse now has to diagnose which of four services is misbehaving, on top of her clinical duties.
The monolith is not perfect. A bug in the reporting module could take down the whole system. But the microservices version introduces a new class of failure: partial outages that are hard to explain to a pharmacist who just wants to dispense amoxicillin. For this clinic, the modular monolith is the right call. Extract the reporting module into a separate process only if the sync logic becomes so complex that it threatens the stability of the core system.
What to do instead of microservices
If you decide against microservices, you still have work to do. The goal is to keep the system maintainable without paying the network tax. Here is a practical sequence:
- Draw module boundaries on paper. List the core business capabilities: registration, billing, inventory, reporting. For each, write down what it owns and what it must never touch.
- Enforce boundaries in code. Use packages, namespaces, or modules. If a module needs data from another, it goes through a defined interface, not a direct database query.
- Add a queue for slow or bursty work. Reporting, sync, and batch jobs can run as background workers inside the same deployable. This gives you many of the scaling benefits of microservices without the network calls.
- Build observability now. Centralized logs, basic metrics, and a simple health check endpoint. When you do split a service later, you will already have the tools to see what is happening.
- Practice failure. Once a quarter, kill the main process and time how long it takes to recover. Then kill the database and do the same. If recovery is slow, fix that before you add more moving parts.
Common objections, answered
“But microservices are the industry standard.” The industry standard is whatever keeps your users working when the power flickers. Many large companies run monoliths in production. Many others run microservices and regret it. The standard is not the architecture; it is the outcome.
“We will need to scale eventually.” Eventually is not now. When you have the traffic numbers to prove it, you can extract a service. The modular monolith is designed for that extraction. You are not painting yourself into a corner; you are deferring a cost until it is justified.
“Our developers want to learn microservices.” That is a training goal, not an architecture decision. Let them build a side project or a non-critical internal tool. Do not let a learning exercise become the foundation of a clinic’s patient records system.
FAQ
What is the difference between a monolith and a modular monolith?
A monolith is a single deployable artifact where code may be tangled across modules. A modular monolith is also a single deployable artifact, but the code is organized into clear, enforced boundaries. Modules communicate through in-process interfaces, not network calls. The modular monolith gives you maintainability without the operational cost of distributed systems.
How do I know if my team is ready for microservices?
You are ready when you can answer yes to all five questions in the framework: you have at least two teams that need independent deploys, a component with a genuinely different scaling profile, a tolerance for partial failure, a deployment pipeline that handles many artifacts, and observability that works when the system is broken. If any answer is no, start with a modular monolith.
Can microservices work on intermittent or off-grid infrastructure?
They can, but the cost is higher. Every inter-service call is a network call that can fail when the local link is saturated or the power is unstable. You need timeouts, retries, circuit breakers, and fallbacks for every call. If you do not have the operational capacity to build and maintain those, a monolith is more resilient in practice, because it fails as one unit and recovers as one unit.
What is the first step if I decide to stay on a monolith?
Draw module boundaries and enforce them in code. Then add a queue for background work and build basic observability. These steps make the monolith easier to maintain and prepare you for a future extraction if the need ever becomes real.
Next step for this site
This article is part of a series on architecture decisions for constrained infrastructure. The next piece will cover how to design a deployment pipeline that survives intermittent connectivity—including offline package mirrors, delta syncs, and rollback strategies when the link drops mid-deploy. If you have a question about your own setup, send it in. The best questions become future articles.