You build a feature.
It is a "small" change, maybe adding one field, one flag, one extra bit of data.
Then you realize you have to:
That is not you being bad at planning.
That is coupling.
When modules are glued together by shared state, every change becomes a negotiation.
Even if you are a tiny team, you still pay the coordination bill.
It shows up as context switching, longer review cycles, and "wait, who owns this?" threads.
Over time, your velocity drops and your maintenance surface area grows.
The idea is simple.
Across module boundaries, the only stable connection should be an immutable identifier.
Not a 200 field object.
Not a shared table.
Not a magic JSON blob everyone silently depends on.
Just an ID.
Example:
User object around, pass user_idThe bolt stays stable.
Everything behind it can change without breaking other modules.
The shared database or API endpoint.
It feels fast.
It is also a welded joint.
Change a column and you just created an unplanned integration project.
*_idThis is "scale by subtraction" applied to software architecture.
You remove hidden sync work instead of adding process to manage it.
If you are building a SaaS with 1 to 5 engineers, what is your rule of thumb for when shared DB or endpoint access stops being a shortcut and starts being a long term drag?
Passing giant objects feels fast on day one, but it's basically taking out a high-interest loan on your architecture. You hit the nail on the head: it's not bad planning, it's coupling. To your question: for a team of 1 to 5, the shared DB stops being a shortcut the moment someone asks "Wait, is it safe to delete this column?" and nobody in the room knows the answer for sure. Once fear enters the deployment process, you need boundaries.