A few years ago, our team was building microservices. We relied on MongoDB because it was schemaless and allowed us to add fields on the fly without worrying about migrations. This was good for the first few years when the company was trying to find its way to the market and data structures changed more frequently.
We had to keep runtime speed high, so we decided to create a script that ran every time a document in Collection A was updated. It would automatically propagate those changes to another database managed by another service. It felt like a win because it required zero software changes on the other side.
That domain data propagation coupled both services with an undefined interface. We didn't need that. We could've just included that change in the estimation, but instead, we let the top-down pressure win, and everyone lost.
Instead of forcing data propagation across services, we should've used an immutable identifier, a unique ID that allows data entities to cross boundaries independently, avoiding the need for shared objects that cause coupling.
When you propagate domain data directly, you are creating an invisible dependency. Every time you change one service, you risk breaking another. That is how a 2-week feature suddenly takes 2 months.
Let's do a quick thought experiment to look at the math of coupling:
Your original $1 of feature work now costs $2.75 in coordination tax. That tax is a run-rate killer for any product company.
When a software product grows, its components need to be well defined and interact with clear boundaries so they can evolve independently and keep the costs of runtime and maintenance low.
At what point do you stop adding features and start the "Strategy of the Cut"? Are you trading long-term speed for short-term fixes, or have you found a better way to manage service boundaries?