The situation
A manufacturing client ran three systems that mattered: an ERP, a shipping tool, and a homegrown reporting app. All three wrote to the same database tables. It had not been designed that way. It happened the way it always happens: one app needed a number that lived in another app’s tables, someone granted a read, and over a few years four systems were reading from and two were writing into one schema that nobody owned.
By the time we arrived, that schema was the integration layer, except it had no owner and no version. It was just the shared place everything reached into.
What was really going on
The cost of that arrangement is invisible until you try to change something. Renaming a single column meant grepping four codebases, two of which had no tests, then freezing a release the sales team was waiting on while we proved that nothing downstream broke.
One column. Three weeks of two engineers, plus the delayed release. Roughly 30,000 in loaded labour for a rename that on a system with clear ownership would have been an afternoon. That figure is an openly rounded estimate of loaded cost rather than an audited invoice line, but the shape is the point: a boundary you backed into charges you on every change, forever, until someone pays to fix it properly.
The deeper problem was not the schema. It was that no one had ever decided which system owned each piece of data. That is a governance question wearing a technical costume: two managers arguing whose definition of a customer is canonical. Until that decision is made and signed, no amount of clever architecture removes the coupling.
What we did
We did not start with the code. We started with ownership. In a room, with the people who would argue about it, we named a single owner for each contested piece of data and got it in writing.
Only then did the engineering follow, and we did it in a deliberate order. We collapsed each table to one writer, which is the genuinely hard part, because moving a second writer onto the owner’s interface means running both paths in parallel, building parity tests from scratch because none existed, and having a rollback ready for when the legacy writer does something inside its own transaction you cannot see. Read coupling is annoying but survivable. Write coupling is the liability, so we removed that first. Once one writer remained per table, a view or a small interface in front of the readers was the easy last step.
This is the core of how we approach systems integration: the boundary is a decision about who owns what, not a diagram. We wrote about the underlying trap in the shared database nobody owns.
What changed
The rename that took three weeks became routine. More importantly, the next change did too, and the one after that, because the tax on every future change was what we had actually removed. The three-week rename was never the problem. It was the symptom of a boundary nobody had decided.
You do not need event sourcing or a service mesh to fix this. You need a decision about who owns each piece of data and the patience to migrate one writer at a time. The decision is what stalls projects. The migration is just hard, not mysterious.
If a one-line change in your systems quietly costs weeks, the boundary is probably the thing to look at. If you are not sure whether your blocker is the live-writer migration or the two people who both think they own the customer record, talk to us.