An organization that delivers health, safety and environment training manages courses, deadlines and risk assessments with an application born in 2008 and grown by accretion. Inside it are years of law encoded in code — Italian workplace-safety law, national training agreements, risk calculations. The question was: rewrite it? The honest answer is no. Here is why, and what we did instead.
01Why "rewrite from scratch" was the wrong answer
Faced with an old application, the temptation is to throw it away and start over. But here the value isn't in the interface: it's in the regulatory rules encoded on the server — the risk assessment, the training requirements per job role, the legal deadlines. These are rules with legal weight, written into the code over the years, with no tests and no specification. Rewriting them blind isn't modernization: it's reintroducing errors where an error has legal consequences.
02What the code actually says
The census captured the real picture: a Java desktop client of hundreds of thousands of lines (UI only, except four well-identified pockets of logic) and a server holding the real value — the layer that computes and applies the rules. The two are welded together: they speak a dated, non-standard protocol (XML-RPC, now end-of-life), exchanging serialized Java objects. There is no neutral API: no web client, no other language can speak that protocol.
And the problems no one had ever looked for: MD5 passwords over health data (a GDPR art. 9 matter), secrets in cleartext, end-of-life libraries, a proprietary, closed-source persistence framework (key-man risk), Windows-only 32-bit deployment, and no automated tests — no safety net for any change.
03The direction: keep the brain, redo the delivery
The path chosen isn't a rewrite, it's the Strangler Fig: you strangle the old one piece at a time, you don't tear it down.
Concretely: you keep the brain (the layer that computes the rules, the data, the database), put a real, neutral REST API in front of it alongside the old protocol, and rebuild the client as web one functional area at a time. The old protocol and old interface are switched off last, only once migration is complete.
04Risk confined where it can be managed
The strength of the Strangler is where it does not reach: the regulatory core — the rules with legal weight — stays put, proven by years of use. Change lives on the perimeter (the API and the interface), the part you can replace without losing rules. And the full "screen → service → rule → table" map already exists from the census: the future API's spec isn't reinvented blind, it's read from there.
The four pockets of logic left in the client — calculations the server doesn't replicate — move server-side as a first step, so the interface becomes truly replaceable without taking a rule with it.
05You start with a pilot
A project like this isn't done in one shot: you begin with a small, self-contained area, bring it onto the new track (API + web) next to the old one, and actually use it. That's how you validate the approach in the field before widening it, and how you surface problems while they're cheap. Meanwhile, during the API work, you fix what can't wait: MD5 over health data, cleartext secrets, end-of-life libraries.
06What we learned
1. A legacy app's value is often invisible. It's not the interface you see: it's the law written into the code. That is preserved, not rewritten blind.
2. Measuring closes the unknowns. A full census turned "who knows what's inside" into a small, closed list.
3. Strangler beats big-bang when there's a core with legal weight: risk confined to the perimeter, an incremental and reversible project.
4. Security gets fixed along the way. MD5 over health data and cleartext secrets don't wait for the end: they're closed while building the API.