System Design

Sharing a Database Between Microservices Defeats the Point of Having Them

Key takeaway: A shared database means any service can be broken by a schema change in a completely different team’s service, which is precisely the tight coupling that separating into services was supposed to remove.

What This Pattern Actually Preserves

An organisation splits a monolith into several independently deployable services, each with its own codebase, its own deployment pipeline, and its own team — and all of them continue reading and writing the same shared database that the monolith used, because migrating the data itself felt like the harder and more disruptive part of the split, so it was deferred or skipped entirely.

The services are now independently deployable in name, and in practice a schema change made by one team for its own service’s needs can silently break another team’s service that happens to depend on the same table, in a way that is invisible until that other service’s next deployment or, worse, until it fails at runtime in production. The database has become the same kind of tightly coupled shared dependency the monolith’s codebase was, just relocated to the schema layer instead of the code layer.

Why This Specific Coupling Is Worse Than It Sounds

Coupling point In a monolith In “microservices” sharing one database
Shared code Visible in the same codebase, caught by compilation Invisible, caught only by runtime failure
Schema changes One team, one release, one set of tests Multiple teams, uncoordinated releases
Deployment independence Not claimed Claimed and not actually true
Blast radius of a bad change Contained to the monolith’s own deploy Spans every service touching that table

The visibility difference matters enormously — a shared-code coupling in a monolith is caught at compile time or by a test suite that exercises the affected code path, because everything lives in one codebase that gets built and tested together. A shared-database coupling between separate services is invisible until a schema migration in service A happens to break a query that service B was silently relying on, discovered only when service B starts failing, potentially well after the schema change that caused it has already been deployed and forgotten.

What Genuine Service Independence Requires

Each service owning its own data store — not necessarily a separate physical database server for every service, but a genuine ownership boundary where only that service’s code ever directly queries its own tables — is the actual requirement for the independent deployability microservices are meant to provide. Other services needing that data go through the owning service’s API, not through a direct query against its tables, which means the owning service can change its internal schema freely as long as its API contract remains stable, exactly mirroring how a well-designed module boundary works inside a single codebase.

This does introduce real costs that a shared database avoided — data that used to be joined in a single query across tables now requires either an API call to fetch it from the owning service, or a deliberately maintained, explicitly synchronised local copy, both of which are genuine engineering work that a shared database’s single-query joins made unnecessary.

Migrating Away From a Shared Database

Migrating an existing shared-database setup toward genuine per-service ownership is significant, disruptive work precisely because the coupling has likely been relied upon, sometimes unknowingly, for a long time — identifying which service should genuinely own each table, then converting every other service’s direct queries against that table into calls to a proper API instead, one table and one dependent service at a time rather than as a single disruptive cutover, is the realistic incremental path most organisations actually take.

The Bottom Line

Treat a shared database across independently deployed services as equivalent to shared mutable global state across modules in a single codebase — the same coupling problem, relocated somewhere with far weaker tooling to catch it before it breaks something. Assign clear data ownership per service and require cross-service data access to go through an API rather than a direct query, and treat migrating an existing shared database as the significant incremental project it genuinely is.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button