Running only part of your microservices
Your system has twelve services and you are going to touch two. The machine can handle twelve —barely— but starting them all takes four minutes, eats the memory and adds nothing to the change you are making. The question is not whether you can cut down: it is where the ones you do start call.
Cutting down is not the problem; the destination is
Starting a subset is trivial in any tool: you pick two and start them. What nobody solves for you is what happens when one of those two calls a third that is no longer there.
Every dependency that falls outside the cut has exactly three possible destinations:
- Local — it is up right next door. The easy case, and also the one that drags: if you start it, you drag its own tail of dependencies.
- The cloud — your shared environment. It saves you starting it, in exchange for VPN, for data that changes under your feet, and for the fact that if that environment is down, so are you.
- A mock — perfect when its response is not what you are testing. In exchange for maintaining the stub.
The decision is per dependency, not per environment. That is the difference with almost everything else: you do not want "local mode" or "cloud mode", you want this call to go to the service you have open, that one to the cloud and a third to a mock, and to change your mind halfway through the afternoon.
Why a compose file cannot tell the difference
docker compose up bff orders does start two services. But inside a compose
file everything is a container on the same network, and the URLs between
them are service names on that network. The configuration of bff says
http://payments:8081, and payments is not there now: you get a
name resolution failure.
The usual way out is editing the URL by hand in the configuration of bff —a
repository that is often not yours—, remembering not to commit it, and undoing it when
you want to try something else. Multiplied by services and by times a day, that is where
the forty-step LOCAL_SETUP.md comes from.
What breaks when you cut badly
Two failures, and the second is far worse than the first:
-
It fails loudly.
connection refused, or a name that does not resolve. Annoying, but honest: you find out on the first try. - It works without anybody having decided it. The configuration shipped the shared environment's URL as its default, so the call goes out to the cloud, answers, and everything looks fine. You find out the day that call writes: a migration, a test order, an email sent to a real customer from your machine.
The second is what justifies making this an explicit decision rather than a default. A local environment that reaches production by accident does not warn you.
And one case almost everybody gets wrong
If a service is up right next door, the call should go to it —even if the general configuration file says "cloud"—. It sounds obvious and hardly any tool does it: you end up with a service running on your machine that its neighbour calls over the internet. It burns VPN, it is slower, and the day the VPN goes down you have a whole local scenario failing to start because of a service you had right there.
The correct rule: what is started right next door gets called locally, and the general configuration only decides about what is not in the scenario.
How Aseptic solves it
A scenario is exactly this: the subset you want to start, with the decision taken for every dependency that falls outside it.
- You choose what goes in. Two of twelve, or however many, grouped by flow —"Checkout", "Customer onboarding"— and saved with a name, so you do not have to decide again tomorrow.
- For each dependency you choose a destination —local, cloud or mock—
and the engine rewrites the URL on start-up, injected as a
-Dproperty or an environment variable. Your repositories are not touched. - What is in the scenario gets called locally, above whatever the manifest says: the recent, specific decision beats the general one.
- If the declarations contradict each other, start-up is cut instead of breaking the tie silently. Two truths about the same dependency is the bug that ends with the panel saying "cloud" while a service calls its local neighbour.
- The scenario is shared as a self-contained file: whoever joins imports and starts, without reading anybody's document.
How much you actually need to start
The rule that works: only the services you are going to change, plus those whose behaviour you are verifying. Everything else is a URL pointing somewhere else. In practice, most afternoons that is two or three services out of a system of twelve.
If while cutting down you find that one of the two will not start, there is why a microservice fails to come up locally; and if what you need is to replace the ones you leave out, mocking a service you do not control. The full picture is in what a local microservice environment needs.
Frequently asked questions
Can I start only two of twelve services and have the rest still work?
Yes, but you have to decide where the ones you do start call. Every dependency pointing at a service that is not up has to go somewhere else: to your cloud environment or to a mock. If it is not decided, it decides itself and fails.
Why does docker compose not let me do this without editing the file?
Because inside a compose file everything is a container on the same network and the URLs are service names. docker compose up bff orders starts two, but the URL bff uses to call a third still points at a name that no longer exists.
What breaks when I cut badly?
The typical case is a connection refused towards a service name that is gone, or worse: a call that goes out to the shared environment by default without anybody deciding it. The second is worse because it works, and you do not find out until you write to everybody's database.
How much of a system do you actually need to start?
Less than people usually start. The useful rule: only the services you are going to change or whose behaviour you are verifying. Everything else is a URL pointing somewhere else.