Modernizing legacy software starts by recovering a reliable version
A legacy application can still run in production even when no one can reproduce its exact build. Different source copies, database scripts, libraries and deployment instructions ma
Why this matters
Software operations improve when decisions are tied to real delivery constraints
These resources help technical leaders make clearer decisions about software modernization, delivery constraints, continuity, and operating risk.
25
Sections
12
Minutes
In this guide12 min read
Article
A guide to move from the decision to the operating criteria and next step without losing the thread.
The first delivery can preserve existing behavior
Before promising new features, identify the version that is actually running, reconstruct its dependencies, and build and run a reproducible release. Technical modernization and functional evolution need separate criteria: a successful build does not validate the business behavior.
Sometimes the problem with a legacy system is not missing source code. It is exactly the opposite: there are too many copies, and none can establish on its own which version reached production.
There may be one copy on a developer's computer, another in a shared folder, an old backup, separate SQL scripts, and an executable that has continued to run correctly for years. The knowledge needed to bring those pieces together often lives in manual procedures and in the memories of a few people.
In that situation, starting modernization directly with a new language, a cloud migration, or an AI tool adds activity before restoring control.
The first goal should be both simpler and more demanding:
Be able to rebuild a known release of the system without depending on the computer or the memory of the person who made the last change.
Microsoft describes modernization as a lifecycle that begins with assessment and inventory, including applications, data, infrastructure, and dependencies before transformation begins. Microsoft - Application modernization lifecycle
1. The first modernization release can be the same application
Not every modernization effort needs to begin by changing functionality.
A valuable first milestone can be to take the system as it exists and establish a reproducible baseline.
At a minimum, that baseline should answer:
- Which sources are authoritative?
- Which runtime, IDE, generator, or toolchain version is required?
- What are the external dependencies?
- Which database schema and scripts belong to that version?
- What configuration does each environment need, without storing secrets in the code?
- How is the deployable artifact generated?
- Which version is currently running?
Microsoft recommends beginning modernization by building an inventory and mapping dependencies among applications, databases, and services. This is not an administrative step: it prevents an invisible dependency from becoming an unexpected disruption later. Microsoft - Make an inventory and collect data
The Baseline Rebuild Test
A simple test helps establish how much control the team actually has:
Can we take a clean machine, retrieve the declared sources and dependencies, and generate a working version of the system again?
If the answer is no, operational knowledge still exists outside the source of truth.
The initial goal is not to eliminate all of it in one day. It is to identify it and gradually turn it into something repeatable.
2. Putting the code in Git does not solve the whole problem
Version control involves much more than copying files into a repository.
In modern systems, Git is often the central component. Legacy platforms may require a combination of mechanisms: Git, platform-native repositories, database projects, exportable packages, or specialized change-control tools.
For example, a solution may need:
- Git / Azure Repos for code, scripts, declarative configuration, documentation, and pipelines;
- a platform-native repository for components that cannot be represented correctly as conventional source files;
- SQL Database Projects or migration scripts to control schema evolution;
- an artifact repository to retain identifiable, deployable builds.
The technology changes. The rule does not:
There must be a source of truth that can explain what makes up a release.
Version control exists to record changes and recover earlier states. Git was designed to preserve the history of files under revision, not just their latest copy. Git - About Version Control
3. A release is more than code
One of the most dangerous mistakes during modernization is to version the application while leaving the rest of the system outside that control.
An actual release may depend on:
- application code;
- platform objects or components;
- libraries and packages;
- runtime versions;
- database scripts and schema;
- scheduled jobs;
- configuration;
- external integrations;
- infrastructure parameters;
- generated artifacts.
If any of those elements changes without traceability, the organization may have the correct code and still be unable to reproduce production behavior.
The useful question is therefore not only, "Is the code in Git?"
It is:
"What do I need to rebuild this release, and where is each element versioned?"
4. Make changes reviewable
Once a baseline exists, the next challenge is to control how new changes enter it.
In Git, a typical implementation might be:
Work Item → branch → Pull Request → review → build validation → merge
Azure Repos can protect branches with policies that require Pull Requests, reviewers, and build validation before changes are merged. Microsoft - Azure Repos branch policies
On a platform that does not use Git in this way, the mechanism may differ, but the same principles should remain:
- There is a traceable reason for the change.
- The modified objects can be identified precisely.
- Another person can review the impact when the risk requires it.
- There is validation evidence before integration or release.
The point is not to add ceremonies to old software. It is to reduce the number of changes that only one person can explain.
5. The build must also move out of people's memories
A repository offers little help if compilation still requires twenty manual steps that work only on a particular workstation.
Modernization starts to gain traction when the build becomes repeatable:
- an identified toolchain;
- declared dependencies;
- secrets kept out of the code;
- scripts or a pipeline for compilation;
- a minimum set of automated checks;
- a consistently packaged result.
Microsoft recommends carrying out modernization incrementally with source control and CI/CD, including when working with legacy code, and adding tests for critical functions to detect behavioral changes during refactoring. Microsoft - Execute cloud modernization
This does not mean that a legacy system must have perfect unit-test coverage before work can proceed.
In many cases, the first goal will be more pragmatic: automate what we can already verify repeatably.
That can begin with compilation, smoke tests, output comparisons, query checks, or critical functional cases.
6. The database is also part of version control
This point deserves particular attention because reverting an application to an earlier version can be much easier than reverting its data.
Every database change should be able to answer:
- What does it modify?
- Which version does it start from?
- In what order does it run?
- How is it validated?
- What dependencies does it have?
- What is the recovery plan?
Simple changes may allow a direct rollback. For more complex schema or data changes, trying to reverse the change may be riskier than moving forward with a correction.
Azure's safe deployment guidance warns that rolling back changes to a database, schema, or other stateful components can be complex. It also recommends designing backward-compatible schema changes when versions need to coexist during deployment. Microsoft - Safe deployment practices
In critical systems, rollback therefore does not always mean running an inverse script. It may mean restoring data, rolling forward, or designing the change in stages to preserve compatibility.
7. The artifact needs an identity
After the build, we need to know exactly what we are deploying.
A release should produce an identifiable artifact with:
- a version or build ID;
- its associated commit or baseline;
- a date;
- validation results;
- related database scripts;
- change notes;
- deployment and recovery instructions where applicable.
Azure Pipelines can publish pipeline artifacts and use them across build and deployment stages. Operational Excellence guidance also recommends versioning artifacts to support rollback and roll-forward. Microsoft - Pipeline artifacts Microsoft - Safe deployments
This changes an important question during an incident.
Instead of asking:
"Which files did someone copy to the server?"
We can ask:
"Which release is deployed, and which baseline was it built from?"
8. Deployment and recovery are two parts of the same design
Automating deployment without considering recovery automates only the way in.
Before moving a modernization into a critical environment, define:
- prerequisites;
- deployment order;
- post-deployment validation;
- stop conditions;
- a rollback or roll-forward procedure;
- backup or restore procedures when the change affects data;
- evidence of which version remains in operation.
Microsoft recommends writing rollback steps alongside deployment steps and using safe deployment strategies according to the risk of the change. Microsoft - Plan cloud modernization
The important capability is not just deploying faster.
It is knowing what we did and having a way out when it does not work.
9. Regaining control does not mean freezing the system
This is where one of the most important trade-offs appears.
While we establish the baseline, the business may still need changes to the current system.
Several strategies are possible:
- A temporary freeze reduces uncertainty but limits the ability to address changes while establishing the baseline.
- A controlled window allows only critical changes, which are reconciled afterward.
- Parallel work maintains business momentum but requires discipline to prevent the baseline from becoming obsolete before it is complete.
There is no universal answer.
The decision depends on criticality, change frequency, the number of teams, and the ability to reconstruct differences.
What matters is that the organization knows which baseline it is stabilizing and how it incorporates any changes made during that process.
10. Modernization strategy comes after control
Recovering reliable versions does not require a rewrite.
On the contrary, it enables better decisions.
With a reliable baseline, we can assess each component and decide whether to:
- maintain it;
- rehost it;
- replatform it;
- refactor it;
- replace it;
- retire it.
In large systems, an incremental strategy often reduces risk compared with a complete replacement. The Strangler Fig pattern, for example, allows functionality to be replaced gradually while the legacy system continues to operate. Microsoft - Strangler Fig pattern
The important architectural question stops being:
"Which technology should we migrate everything to?"
And becomes:
"Which part should we change first, what risk does it remove, and how do we check that it still works?"
11. Where AI fits
AI can be extremely useful in legacy software.
It can help:
- explain modules that are difficult to read;
- classify dependencies;
- find duplicate code;
- generate initial documentation;
- propose characterization tests;
- assist with framework upgrades;
- compare implementations;
- speed up the search for incompatibilities.
Microsoft already offers GitHub Copilot-assisted modernization tools that analyze the codebase, dependencies, configuration, workflows, and data models to help plan transformations. Microsoft - GitHub Copilot modernization
But one rule should not change:
AI-generated code does not need a special lane into production.
If AI proposes a modification, that modification enters the same process as any human contribution:
version control → review → build → tests → artifact → controlled deployment.
AI accelerates the work. The control system determines whether that work is acceptable.
12. Make trade-offs explicit
<table header-row="true">
<tr>
<td>Decision</td>
<td>Benefit</td>
<td>Trade-off</td>
</tr>
<tr>
<td>Establish a baseline without reconstructing older history</td>
<td>Start quickly from a reliable point</td>
<td>Detailed traceability of earlier changes is lost</td>
</tr>
<tr>
<td>Freeze changes while establishing the baseline</td>
<td>Reduce divergence</td>
<td>May affect the business's operational needs</td>
</tr>
<tr>
<td>Bring everything into Git</td>
<td>Consistent visibility and tooling</td>
<td>Some legacy platforms require native repositories or mechanisms</td>
</tr>
<tr>
<td>Automate every test before modernizing</td>
<td>Strong regression assurance</td>
<td>May delay the start by months; incremental characterization tests may be more realistic</td>
</tr>
<tr>
<td>Automatically roll back database changes</td>
<td>Fast recovery for simple changes</td>
<td>Can be dangerous when data transformations or destructive changes are involved</td>
</tr>
<tr>
<td>Rewrite the entire system</td>
<td>Maximum architectural freedom</td>
<td>A larger change surface and more behavior to demonstrate again</td>
</tr>
</table>
Responsible modernization does not eliminate these trade-offs. It makes them visible before costly decisions are made.
13. A minimum Modernization Control Loop
Before transforming an entire application, we can establish a small but verifiable control loop.
1. Inventory + Baseline
Identify the sources, runtime, dependencies, database, integrations, and production version.
2. Source of Truth
Every component of a release has a defined repository or versioning mechanism.
3. Change Gate
Every change has a reason, traceability, and review proportionate to its risk.
4. Reproducible Build + Validation
The system can be built again and run repeatable checks.
5. Versioned Release Package
Artifacts, scripts, evidence, and version information are brought together in an identifiable package.
6. Controlled Deployment + Recovery
Promotion between environments and recovery have procedures and evidence.
AI can accelerate any of these layers, but it replaces none of them.
Diagram
View diagram source
flowchart LR
A["Legacy system"] --> B["Inventory and baseline"]
B --> C["Authoritative sources"]
C --> D["Review and approval"]
D --> E["Build and validation"]
E --> F["Versioned release"]
F --> G["Controlled deployment"]
G --> H{"Next decision"}
H -->|"Maintain"| C
H -->|"Modernize"| I["Assess options"]
J["AI assistance"] -.-> B
J -.-> D
J -.-> E14. Measure whether control has actually been restored
A few simple indicators are more useful than claiming that the project "has DevOps now":
- the percentage of components with an identified source of truth;
- the percentage of database changes under version control;
- the percentage of builds reproducible from a clean environment;
- the percentage of releases generated through a repeatable process;
- the percentage of releases with identifiable artifacts and baselines;
- the number of manual steps needed for deployment;
- changes found in production that do not exist in the repository;
- the time needed to rebuild a known release;
- the percentage of critical changes with validation evidence and a recovery plan.
The goal is not to achieve zero manual steps immediately.
The goal is to reduce progressively the parts of the system that depend on invisible knowledge.
15. The modernization opportunity is to recover the ability to make decisions
For a company with critical software, a complete rewrite may sound attractive, but it is not always the right first investment.
Sometimes the first modernization phase should produce something less spectacular and much more useful:
- a reliable baseline;
- a technical inventory;
- a source of truth;
- a reproducible build;
- a change process;
- a release package;
- a map of what is worth modernizing next.
This approach fits the way we are shaping modernization projects at Eximus: first restore control and evidence, then decide where to transform.
Not because Git, CI/CD, or Azure DevOps are the final objective.
But because they make something much more important possible: changing an old system without having to rely on heroics every time we touch production.
The goal is not to make the software look new.
The goal is for the organization to be able to answer confidently again:
What changed, why did it change, which version is running, and what do we do if we need to recover?
Next step
Can you rebuild the version of your system that is in production today using only its sources, dependencies, and documented process?
If the answer depends on a particular person or machine, that is probably already a good place to begin modernization.
Technical references
- Microsoft - Application modernization lifecycle
- Microsoft - Make an inventory and collect data
- Microsoft - Azure Repos branch policies
- Microsoft - Execute cloud modernization
- Microsoft - Pipeline artifacts
- Microsoft - Safe deployment practices
- Microsoft - Plan cloud modernization
- Microsoft - Strangler Fig pattern
- Microsoft - GitHub Copilot modernization
- Git - About Version Control
Related topics
Explore more on this topic
This article connects with other resources that explain the operating, commercial, and technical context behind the flow.
Recommended path
How to turn this topic into an execution path
Related services
Related solution
Business Continuity & Resilience
Keep banking channels and critical platforms available with tested recovery paths, rehearsed playbooks, and engineers who know your stack.
View solutionRelated proof
Accounting process optimization with functional equivalence
An approach and validation criteria are documented; no benchmark or final acceptance is established here.
View caseMore content
Keep exploring
it continuity resilience
Before leaving GeneXus, map the system you actually have
GeneXus modernization gets risky when teams move before they understand the real object inventory, dependencies, and migration blockers inside the system.
it continuity resilience
Documentation stops being bureaucracy when it enables delivery without relying on one person
A useful handover includes locatable source code, identified dependencies, reproducible instructions, validation checks and known risks. For legacy GeneXus applications, preparing
it continuity resilience
GeneXus support after migration
What to protect in the first months after a GeneXus migration so production does not drift into regressions.
it continuity resilience
IT Continuity & Resilience
How to protect operations in GeneXus, Bantotal, SQL and Azure without turning every change into a business risk.