Run two instances from day one ✌️
If you’re building a service that will eventually run multiple instances, this article is for you. I think you should run at least two instances from day one.1 Doing so helps you uncover hidden bugs early—like port conflicts, stale caches, and locking issues—so you can avoid expensive rewrites later.
This may sound a bit uncharacteristic as I usually argue for simplicity and against premature scaling, but I think this is different. If you know that you will be running multiple instances, running a single instance is an architectural risk because it is too easy to make assumptions that will not hold.
- You start the service on a fixed port. That fails when two instances attempt to bind the same port. Instead, use dynamic ports with a reverse proxy or load balancer.
- You have a local cache. Local caches risk serving stale data across instances. Consider only caching immutable data or using a shared cache.
- You make complex database queries using locks and transaction isolation. That may cause collisions and poor performance with multiple instances? You may need to look at idempotency, unique constraints, and optimistic locking.
- You keep session state on the server. That won’t work when a user’s requests are load balanced across the instances. Move away from session state using JWT:s, or use a distributed cache.
- Your service always runs migrations on startup and executes scheduled jobs. How will multiple instances coordinate such tasks? You may want to determine a leader for migrations, or move it to a separate service entirely.
These concerns ripple through the whole system. Fixing them later is invasive and expensive. Run two instances early and validate that your service behaves correctly.2
-
This advice only applies if multiple instances are part of the design. Don’t add complexity where it is not needed. ↩︎
-
In a sense, the advice to run two instances from day one is similar to how a walking skeleton constructs a thin but complete architecture. ↩︎