Two services can share a customer table for years without anyone calling it a problem. Then one team needs to release independently, and a column change in its service still requires a joint deployment. The schema did not decay overnight. The work around it changed.
Consider a fictional subscription business with an Accounts service and a Billing service. Accounts handles customer contact details. Billing creates invoices and reads the customer name and billing address from the same customers table. Both services use one database schema and coordinate migrations in one release. This is a deliberately narrow example; it leaves out payment processing, data retention rules, and the rest of the application.
At first, the choice makes sense. One team owns both services, ships them together, and needs a working invoice flow. A shared table avoids a new API and the question of how Billing would keep a copy of customer details current. The team knows that service boundaries are loose and records that assumption. It has chosen a smaller delivery cost now in exchange for possible coordination later.
The requirement that changes the price
Later, Accounts and Billing get separate owners and release schedules. Billing also needs access to a limited set of customer fields, while Accounts holds contact details that Billing should no longer read. These are requirements in this fictional scenario, not a claim that every growing system needs independent services or separate databases.
The shared schema now carries two kinds of work. For a routine Accounts change, the team checks Billing queries before renaming a field, arranges a migration both versions can tolerate, and schedules the rollout around both services. For a Billing release, its tests need a representative Accounts schema even when the feature has nothing to do with Accounts. The database grant is another problem: granting Billing access to the table may expose columns outside its job, so the team must find a narrower access path or accept a security exception.
None of these costs proves that the original decision was foolish. The same design saved work under the original release model. The issue is whether continuing to rely on it now makes ordinary changes cost more than a feasible alternative. The extra reviews, coupled tests, migration steps, and access work are the "interest" the team pays while the shortcut remains. A one-time migration prompted by a new requirement is normal system evolution; recurring coordination because the old boundary stays in place is a stronger debt signal.
Martin Fowler's technical-debt quadrant is useful here because it allows for prudent choices and for debt discovered only after learning more about a system. The label describes a cost relationship, not a verdict on code quality. Clean queries and well-tested migrations can still sit on the wrong side of a boundary for today's work.
Choose what to repay
The team has several credible paths. It could keep the shared schema and make coordination explicit: assign a schema owner, test both services against pending migrations, and restrict Billing through a database view or narrowly scoped account. That may be the cheapest choice if releases still overlap often and access controls satisfy the requirement. It reduces risk without removing the coupling.
It could instead put an Accounts API in front of customer details. Billing would ask Accounts for the fields it needs. This creates a service dependency at invoice time unless the team adds caching or another way to keep invoices available when Accounts is down. The security boundary becomes clearer, but latency, availability, and API versioning become design questions.
Or Billing could own a copy of the few invoicing fields it needs, updated through an explicit change feed. That supports a more independent release path, but the team must define what happens when an update is late, repeated, or missing. It also needs a policy for correcting invoices created from stale details. Splitting the table is not a free repayment; it trades one set of costs for another.
The right choice depends on evidence from this system. Look at recent schema changes: which ones required coordinated releases, how long did those releases wait, and which tests failed because another service's schema moved? Check whether the current database permissions actually violate the new access requirement. Ask whether Billing must use the latest address at invoice creation or a recorded address from an earlier point in time. Those answers can rule out an attractive-looking design before anyone starts a migration.
If coordination is rare and access can be constrained safely, the team may keep the table and revisit it when a specific release or audit exposes a cost. If independent releases repeatedly stall on shared migrations, or the access boundary cannot be met with the shared table, repayment has a concrete trigger. The team can then plan a staged change: define the data owner, introduce the new read path, compare its results with the old one, move Billing traffic, and only then remove its direct table access. Each stage needs an owner and a rollback condition because two data paths can disagree during the move.
A 2015 study by Ernst and colleagues found architecture choices prominent in practitioners' accounts of technical debt. Its survey covered three large organizations; among the 544 people who answered a ranking question, 296 placed bad architecture choices among their top three debt sources. That is a ranking within those respondents, not a measure of how much debt architecture causes in every system. The example here is a way to reason about one decision, not an outcome measured by that study.
For that review, sketch Accounts, Billing, and the shared customers table in Lycana, then revise the Billing read path for the candidate Accounts API. The changed connection makes the new runtime dependency visible; the decision history still has to explain why the team would accept it.
The useful record is a short decision history: why the services shared the schema, which assumption no longer holds, what the coupling currently costs, and what event would justify changing it. That lets the next team judge the decision against its present requirements instead of treating the mere age of the design as proof of debt.


