What a legacy system is (and why age is not the criterion)
A legacy system is one the business still uses and depends on, but that has become hard to change safely. Age helps explain how it got there, but it defines nothing: there are ten-year-old systems in great shape and three-year-old systems nobody dares to touch.
What characterizes a legacy system is a combination of three things: the knowledge of how it works lives in a few heads (or in none), there are no automated tests telling you whether a change broke something, and the technology underneath has lost support or has become hard to hire for.
So the useful question is not “is this system old?” but “can I change this system safely, at the pace the business needs?”. If the answer is no, you have a legacy system, and modernization becomes a business decision, not just an IT one.
Signs that legacy system modernization can no longer wait
The most reliable signs do not show up in any tool. They show up in day-to-day operations:
- Small changes take weeks, because nobody knows what else will break.
- The system depends on one or two people. When one of them leaves or goes on vacation, the company holds its breath.
- The system crashes, freezes or slows down at peak hours, and the fix is always a patch.
- You cannot integrate with new tools (payments, email, messaging, BI) without hacks.
- The vendor of the language, the database or the server stopped supporting it, and security updates no longer arrive.
- Every fix opens two new defects and nobody can test everything before releasing.
- Maintenance cost goes up every year and the business request list only grows.
The cost of doing nothing
Keeping the legacy system as it is looks like the cheap option because there is no proposal and no visible investment. But the cost exists and shows up elsewhere: in team time spent on patches, in opportunities the system will not let you pursue, in security risk and in dependence on key people.
A simple exercise: add up the monthly hours spent on fixes and rework, note what the business has not done because the system does not allow it, and compare that with what an incremental project would cost. It does not need to be exact, it needs to be honest. In many cases, the cost of waiting is already higher than the cost of starting with one part.
There is also tail risk: the day the server, the database or the only person who knows the system fails. That cost does not show up in the spreadsheet until it happens, and then it tends to be the most expensive one.
Three or more items from the list above already justify a diagnosis. One or two can be solved with targeted adjustments, and that is fine: not every annoyance calls for a modernization project.
The five modernization strategies, from least to most invasive
Modernizing is not a synonym for rewriting. There are five paths, and choosing the right one for each part of the system is half the work:
- Keep and surround with care: do not touch the code, but add monitoring, tested backups and documentation. It suits stable systems of low strategic value.
- Encapsulate: put a layer (an API, for example) in front of the legacy system so new systems can talk to it without depending on its internal code.
- Refactor: reorganize the code from the inside, without changing behavior, so it becomes readable, testable and upgradable.
- Migrate in stages: build the new version next to the old one and move one feature at a time, until you can switch the legacy system off.
- Rewrite from scratch: build everything again and switch over at once. It is the riskiest option and the most tempting.
Refactor or rewrite: why we prefer to start with refactoring
A full rewrite is attractive because it promises a clean start. The problem is that the old code, however bad it looks, carries years of business rules and fixes that nobody documented. Joel Spolsky described this in 2000 in the text that became the reference on the subject (“Things You Should Never Do”), arguing that rewriting from scratch tends to throw away accumulated knowledge.
That does not mean rewriting is always wrong. It makes sense when the technology has no way forward, when the architecture prevents the business from growing, or when the system is small enough for the risk to be low. The point is to decide with criteria, not out of tiredness with the old code. We detail those criteria in refactor or rewrite: how to decide without regret.
If what you really need is a new app (and not the modernization of something that already exists), it is worth seeing how a free clickable prototype works before deciding anything.
Incremental migration: how to modernize without stopping operations
Incremental migration is the way to modernize without stopping the business. The idea is simple: the new system grows next to the old one, and traffic and features move from one to the other little by little. This pattern is known as the strangler fig, described by Martin Fowler: the new application “wraps around” the old one, piece by piece, until the legacy system can be switched off.
The roadmap we follow, in broad strokes:
- Inventory: map modules, database, integrations, scheduled jobs and who uses what.
- Characterization tests: before changing anything, record the current behavior (inputs and outputs), so you can tell later whether something changed by accident.
- Coexistence layer: an entry point (proxy or router) that decides, by route or feature, whether a request goes to the old system or the new one.
- Data: decide whether the database is shared during the transition or migrated before the features. In general, keeping a single database at the start avoids having two sources of truth to synchronize.
- Small slices: start with a low-risk module with visible value, release, measure, and only then move to the next one.
- Rollback plan: every stage must be quickly reversible (feature flag or route switch).
- Switch off the old system: only when nothing goes through it anymore, with a backup and an observation period.
What AI changes (and what it does not) in code modernization
AI has become part of our process and speeds up specific steps: reading and explaining unfamiliar code, mapping dependencies, generating a first version of the documentation, suggesting characterization tests and translating repetitive parts from one framework to another.
What it does not do: decide architecture, discover business rules that were never written down anywhere, guarantee that behavior stays the same, or take responsibility for the result. Code generated in bulk, without tests and without review, just swaps one legacy system for another, newer and equally hard to understand.
That is why our rule is: small steps, tests first, human review on every delivery and a comparison of old and new behavior. AI reduces the effort; responsibility stays with engineering. What works and what does not, in practice, is in AI in code refactoring.
How much modernization costs: what weighs on the budget
There is no off-the-shelf price, and it is wise to distrust anyone who gives you a number without looking at the system. Cost depends mainly on the size and condition of the code, the existence of documentation and tests, the number of integrations, the complexity of the database, the number of user roles and modules, and the operational risk (what an hour of downtime costs the business).
The incremental approach helps because it spreads the investment: instead of one big project, there are stages with their own deliveries and costs, and you can adjust course at each one. Before any proposal, a diagnosis of the legacy system (code, database, integrations and critical points) turns uncertainty into a scope that can be priced.
The factors are covered in detail in how much it costs to modernize a legacy system, and the general budgeting logic in how much custom software costs.
Where to start: a simple criterion
If the system has twenty modules, you cannot attack them all at once. Prioritize by crossing two axes: how much it hurts (incidents, slowness, rework) and how much it is worth (revenue, customers, decisions that depend on it). The module that hurts a lot and is worth a lot goes first. The one that hurts little and is worth little can wait, or never be modernized.
Picking a pilot slice works well: small enough to finish quickly, important enough for the result to be noticed. It validates the approach, the team and the coexistence layer before you invest in the rest.
And define in advance how you will measure: time to release a change, number of incidents, response time, maintenance hours. Without a metric, nobody knows whether the modernization worked.
After the migration: ongoing maintenance
Modernizing without maintaining only postpones the problem. A new system ages the same way: outdated dependencies, accumulated security fixes, modules nobody reviews. Ongoing maintenance (updates, monitoring, fixes and evolution in small steps) is what keeps the new system from becoming the legacy system of five years from now.
Organized code, with tests and a current framework, also makes maintenance lighter and more predictable. It is one of the gains that shows up most after refactoring, and it is what a well-designed maintenance contract must reflect, as we explain in software maintenance: what is included and how to hire it.
From our experience
At SKAD, we refactored five systems in production with the help of AI: SyncStop, 4Car, KaraokeApp, The Bee App and DEM (equipment inspection). All of them were plain PHP with jQuery and Bootstrap, and the apps were built in Cordova. Today they run on PHP 8.3 with Slim 4 for the API, React with Vite for the web and React Native with Expo for the app. We kept PHP on purpose: what changed was the framework and the architecture, not the language.
On another front, we rebuilt an animal care and management platform that was PHP 7 on Azure SQL. We kept the database, migrated the data first and the features afterward, with one developer implementing from a plan document and us following progress through high-level lists per user role. There are also two long-standing clients on their way to the same refactoring to the new stack: Inspefire, a fire-safety inspection system we have served for 10 years, and The Horus, an app we maintain and host.
Frequently asked questions
How long does legacy system modernization take?
It depends on the size and condition of the system, which is why we do not give a deadline without a diagnosis. With incremental migration, the gain shows up in stages: the first slice is ready well before the whole project, and the system keeps working throughout the process.
Do I need to take the system down during modernization?
No. With incremental migration, the old system keeps running while the new one is built next to it, and each feature only moves to the new version after it is validated, with a rollback plan in case something goes wrong.
Is it better to rewrite everything from scratch?
Generally, no. A full rewrite carries the highest risk, because it discards business rules embedded in the old code and freezes product evolution during the project. It makes sense in specific cases: technology with no way forward, architecture that blocks the business, or a small system.
Can AI modernize the system on its own?
No. It speeds up reading, documentation, tests and repetitive tasks, but diagnosis, architecture decisions and review remain human. Code migrated in bulk, without tests and without review, tends to hide errors instead of solving them.
Do I need to replace the database?
Not always. The database can be kept and migrated later, or never. Starting with a single database shared between the old and the new system avoids keeping two sources of truth during the transition.
How do I start modernizing my system?
With a diagnosis of the legacy system: a map of modules, database, integrations and critical points. From it comes a staged migration plan, with priorities, risks and a first pilot module. See how legacy system modernization at SKAD works.
Conclusion
If you got here with a specific system in mind, the recommendation is the same we would give in a first conversation: do not start with the technology, start with the diagnosis. List the modules, mark where it hurts and where the value is, pick a small slice and plan the way back. Modernizing is a sequence of small, reversible decisions, not a leap. If you want help with that diagnosis, we are happy to talk, with no commitment, about modernizing your system.