Insights18 February 2025Updated 25 October 20264 min read

Technical Debt Management: Fix the Data Rules Before the Rewrite

Technical debt management from database chaos to structured sanity showing data inconsistencies and improved integrity.

Technical debt management should start with the data rules your application must preserve, not a wholesale rewrite. For an offline field app, we recommend making those rules explicit before changing the database: what identifies a job, what counts as a completed inspection, and what should happen when two people edit the same record.

Ward Cunningham's original account of software debt described the cost of leaving an implementation unconsolidated after an initial delivery. It was an experience report, not a formula for calculating the return on every refactor. Use the idea to investigate repeated work, rather than assign an invented interest rate to untidy code.

Identify the repeated failure

Start with a real operational problem. Perhaps a support worker repeatedly repairs duplicate jobs, or an engineer cannot tell whether an inspection reached the server. Treat these as examples to investigate, not evidence that every field app has the same defects.

For each problem, record the action the user attempted, the data before and after it, and the repair needed. Reproduce it with test records before choosing a remedy. Ask which rule was missing or unenforced. Do not make a framework replacement the acceptance criterion for a data integrity problem.

We would rank a reproducible lost update above a cosmetic inconsistency in an unused module. Make that ranking explicit, including how often the problem occurs and what recovery costs your team. Keep estimated effort separate from measured support time. Neither a large file nor an old dependency is, by itself, a business case for a rewrite.

Separate database rules from synchronisation rules

A relational database can enforce specific conditions on stored data. The PostgreSQL 9.4 data definition documentation describes primary keys for row identification, uniqueness constraints, foreign keys for referential integrity and checks on values.

Translate a business requirement into the appropriate rule before adding a constraint. For example, decide whether an inspection reference must be unique across the organisation or only within a site. Specify whether a job may exist without an assigned engineer. Test the intended exceptions as carefully as the ordinary case.

Keep synchronisation policy as a separate design decision. Amazon's Dynamo paper explains that accepting disconnected, concurrent work can produce conflicting changes that need detection and resolution. Its discussion distinguishes resolution by the data store from resolution informed by application semantics.

That distinction is why we would not accept a tidier schema as proof that synchronisation is correct. Require a separate test for competing edits. Define which changes can combine, which must be rejected and which need a person to decide. Do not silently substitute a timestamp comparison for a business decision about an inspection's outcome.

Refactor one workflow at a time

Use a single workflow as the first change boundary, such as saving and submitting an inspection. Write down its current behaviour, including the behaviour you intend to preserve. Mark any deliberate correction separately so a reviewer can distinguish a bug fix from an accidental change.

Trace that workflow through the local record, queued submission, server operation and reporting view. For each transition, specify the identifier and the acknowledgement the next step expects. Test a repeated submission and an interrupted acknowledgement as separate cases. Define the expected result before running either test.

If the application sits in a mobile wrapper, include the wrapper in the test route, but keep the repair tied to the defect you reproduced. We would not replace the mobile framework merely because the rejected record arrived from a phone. Likewise, do not claim that introducing a local database has resolved conflicts until the competing-edit test passes.

A futuristic digital network with glowing blue and green lines, data streams, and skull icons representing cybersecurity threats.

Keep old clients working during the migration

Danilo Sato describes parallel change, or expand and contract, as a staged approach to an incompatible interface change. The sequence adds support for the new interface, migrates consumers, then removes the old interface. His account also warns that leaving the final stage unfinished can make the situation worse.

Apply that pattern deliberately to a field application's migration plan. Begin by listing the client versions you intend to support and the records they can still submit. Decide what should happen when a device returns with an older queued inspection after the server has changed.

Before release, rehearse that case with representative test data. Include partially migrated records and a failed migration attempt. Set a clear condition for retiring the old representation, rather than relying on a deployment date alone. Record how to stop the rollout and what happens to writes already accepted in the new format.

Measure whether the repair helped

Choose measures that correspond to the original problem. For duplicate submissions, count duplicate records requiring repair. For unclear submission state, count cases where the displayed state disagrees with the accepted server result. Record the observation period and the workload alongside each count.

Compare the same workflow before and after the change. Do not present a quieter week as proof of a better design without considering how much work the app handled. Keep query timing separate from integrity checks. If you propose changing the data model for speed, benchmark the affected operations rather than promise that normalisation will make every query faster.

At Dendro Logic, our recommendation is to make the first debt task small enough to prove: one observed failure, an agreed data rule, a migration route and a repeatable acceptance test. Finish that repair before expanding the programme. Let's have a chat about the workflow your team keeps having to repair.

A crumbling red tower labelled technical debt and bugs collapses beside a stable green stack marked assets.

Update, October 2026

For teams now using coding agents on these repairs, our engineering article on giving an agent its context before it starts describes grounding packs with dated evidence, code structure and explicit gaps. Give the implementer the migration constraints and failure history, then judge the change against the same acceptance tests.

Sources

Want this in your team?

Tell us what you're building and where you want to get to, and we'll think it through with you.

Let's have a chat Follow by RSS →

All insights →