Let's talk
engineering

We deleted four hundred and eighty files before writing a line of the new system

The expense module had an approval workflow. A list filter for pending approval, a status column, and approve and reject buttons on the detail screen. It had been built, reviewed and demonstrated.

No expense could ever be pending. The creating code set every expense to approved on the way in, so the pending state was unreachable and the approve button had never once done anything. Nobody noticed for months, because a filter that returns nothing looks exactly like a filter with nothing to show.

That is the clearest example of what the system had accumulated, and it set the approach for the rebuild: before adding anything, take out everything that is not doing work.

What came out

A read-only inventory came first — fifty-two models, forty-three route files, around thirty-three screens, with the concrete redundancies listed rather than described. Then the removal, in phases, code first and reversible, with the destructive database step gated behind its own approval.

The frontend lost three hundred and forty-two files: route files, component folders, services, translation namespaces. The backend lost a hundred and thirty-nine, and thirty-nine tables were dropped once the code that referenced them was gone. Two dashboards became one. Nine entity modules became a workflow. Several whole subsystems went — a one-time-code login path whose endpoints had been superseded, a recharge feature nobody used, two translation locales that had never been completed, an authentication layout component every route shadowed inline.

Everything was verified by building at each phase and by searching for references to what had just been removed. That is the mechanical part and it is easy. The judgement part is deciding what goes.

Three tests for whether a feature is real

The approval workflow gave a good first test: can the state it manages ever occur? Trace back from the screen to the code that writes the field, and check whether any path produces the value the screen exists to handle. A surprising number of workflow features fail this, usually because a simplification was made on the write path long after the read path was built.

The second: is anything reading it? The recharge feature had a checkbox, a list column, a filter and a set of cards on the detail view, and an endpoint behind them. It was never used because the business handles that arrangement outside the system. Removing it took the interface out and left the columns dormant in the model, which is the right asymmetry — user-facing surface is expensive to carry, a nullable column is not.

The third: is it true? A dead one-time-code path is harmless. A screen that promises approvals to a business that has none is a false statement about the software, and false statements about software are expensive because people plan around them. The same instinct later removed marketing claims that were not true of the product, and a line in an app’s own copy telling users their photographs would upload automatically when they reconnected, when nothing in the app listened for reconnection.

The safety net came before the deletion

Nothing here is brave and it should not be. Before the first file was removed: a full database dump in a restorable format, verified by listing its contents rather than by the absence of an error; a tag on the current commit; and a branch from that tag, both pushed. The rebuild then happened against a clone of the database rather than the live one, with a pre-flight check in the destructive scripts that refuses to run if the target name is the wrong one.

The rollback path was written down before it was needed and it was one sentence: point the server’s configuration back at the previous database and restart, or redeploy the pre-trim branch. A rollback plan you have to invent under pressure is not a rollback plan.

What I would do differently

The trim was executed by several parallel agents working from an exact file list, with me reviewing each diff and re-running the build. That worked, and the reason it worked is that the plan named specific files rather than describing intent. An instruction like “remove the payouts module” produces guesses at the boundary. A list of twenty-three route files, thirty component folders and thirty services produces a diff you can check.

What I got wrong was sequencing one thing: the frontend trim ran while a separate session was trimming the backend, and I deliberately did not touch the backend to avoid a collision. That was the right call in the moment and it left a window where the two halves disagreed about which endpoints existed. It built and it ran, but nothing verified the pairing. If I were doing it again the two halves would be one plan with an ordering, not two plans with a boundary.

Delete the unreachable code before you rebuild around it, and take the backup before you decide what to delete. The cost of carrying a feature is not the disk it occupies. It is that everybody who reads the system afterwards, including you, believes it works.

Working on something like this?

We build this kind of software, and we staff the teams that do.

Get in touch