Modernizing a Legacy Delphi Monolith Without a Rewrite
Many established companies run daily operations on software a small team wrote over a decade ago. The software still works, but only one or two people maintain it. This article describes how Plexteq approaches such a system, using a recent engagement with a European production facility as an example: a 75,000-line Delphi XE2 application with a companion PHP application and a shared MySQL database, developed and maintained by a single engineer.
We recommended not rewriting it because the existing codebase includes numerous sophisticated business flows that a new system would have to reproduce fully. The client's major concern was product stability: stable daily operations were the highest priority, with building a foundation for a next-generation system second. We proposed stabilizing the product first, then modernizing it incrementally using an API wrapper around the legacy application and the Strangler Fig pattern. The sections below explain each step and the reasoning behind it.
The engagement
The system is an in-house product that supports the client's core business operations. It has three parts: a Delphi XE2 desktop application, a PHP application, and a MySQL database they both use. The application code alone is about 75,000 source lines, not counting third-party components.
The Delphi application is the primary interface, and users work with it through Remote Desktop Connection (RDC). The PHP application emerged as a side application for people who cannot access RDC. It gives them a limited interface to the same database.
This history matters for the plan in two ways. Dependence on RDC restricts who can use the full system, which a web frontend removes. The PHP application shows that a second interface against the same database is already an established pattern in this environment.
The client's priorities were clear from the outset. The major concern was product stability: the business needed stable daily operations above everything else. The second goal was to build the foundation for a next-generation system, pursued only in ways that did not put the first at risk.
We began with a structured software audit. Our team prepared audit requirements covering technology, tooling, source control, documentation, process, and data handling. The client's development team answered them in writing. Those answers formed the evidence base for an executive report addressing three questions:
How maintainable is the system today, and how dependent is the business on its current developer?
What would it take to transfer ownership or bring new engineers onto the product?
What is a realistic and affordable path to a modern platform?
Assessment: six areas of maintainability
We assess maintainability across six areas. Together they answer a practical question: could a competent engineer who has never seen the system build it, change it safely, and trace what happened when something goes wrong? Each area receives an assessment and the specific risk factors behind it.
Area | Assessment | What we found |
|---|---|---|
Technology stack | Outdated and fragile | Delphi XE2, released in 2011, plus more than a hundred third-party components, many no longer available or supported. |
Development environment | Single point of failure | The build runs only on the developer's machine. The company holds no IDE or component licenses, and recovery depends on a server disk image. |
Source code and control | Non-standard and risky | Code lives on the developer's internal network, not in Git. "Live" is the latest executable on the server, with no staging environment and no safe rollback. |
Documentation and knowledge | Inadequate | Notes are developer-centric and compiled as work goes on. There are no architecture or data models, and business rules and past fixes are unwritten. |
Development process | Chaotic and reactive | No documented business requirements, no development backlog and no service level standards. Work is prioritized reactively, by perceived urgency. |
Data integrity and logging | Major gaps | No user activity logs, no audit trail, no triggers or formal constraints. The only control is the name of the last person who saved a record. |
What the findings mean for the business
Taken together, the findings describe a "bus factor of one": the entire operation relies on a single person, that person's specific environment, and limited documentation. The system is functional but fragile and carries significant technical debt. We translated the technical findings into four business exposures.
Business continuity. If the developer becomes unavailable, nobody else can build, release, or restore the product. Recovery depends on a server disk image.
Legal and intellectual property. The source code sits outside company-controlled repositories, and the company does not hold the licenses required to compile its own product.
Security and compliance. No audit trail or logging of user activity means changes to business data cannot be traced. Unsupported components may carry vulnerabilities and may stop working on newer operating systems.
Cost of change. Without architecture documentation or recorded business rules, every fix is a trial-and-error exercise. Onboarding is slow, and engineers experienced in this stack are increasingly hard to hire.
These conditions are typical of products that have grown over many years without supporting engineering practices. This shaped our recommendation: the product is worth keeping, and the practices around it need to change first.
Rewrite, low-code or incremental modernization?
A fragile legacy system invites a simple answer: replace it. We evaluated three options before making a recommendation.
Option | What it involves | Our assessment |
|---|---|---|
Rewrite from scratch | Specify and build a new system, then switch over on a single date. | Years of undocumented business logic must be rediscovered before anything is delivered. The business carries the full risk until cut-over. Not realistic in the mid term for a product of this size. |
Move to a SaaS or low-code platform | Re-implement the workflows on a public platform. | The same discovery problem applies, and custom workflows must be reshaped to fit the platform. Also not realistic in the mid term. |
Incremental modernization | Keep the system in service and replace it function by function. | Preserves the knowledge held in the code, spreads the investment over time, and lets each step be validated before the next begins. Recommended. |
The deciding factor was the value of the existing code. Those 75,000 lines hold significant knowledge about the client's operations and workflows, much of it recorded nowhere else. We treat that as a corporate asset to carry forward, not write off.
The client's priorities pointed the same way. A rewrite or platform migration puts daily operations at risk for the project's duration. The incremental route keeps the proven system in service and builds the foundation for the next-generation system alongside it.
The report therefore set three priorities, in this order:
Stabilize the product codebase and documentation before modernization or handover.
Introduce formal project management practices, performance indicators and a quality assurance process.
Modernize continuously through an API wrapper, the Strangler Fig pattern and incremental decoupling.
The plan at a glance
The plan has two stages. The first removes immediate risks without changing functionality. The second wraps the legacy application behind an API, then replaces it gradually in three phases of increasing difficulty.
Risk is removed first. Functions move only once the new stack is proven:

The sequence is deliberate. Each step lowers the risk of the next, and the business keeps a working system throughout.
Stage one: stabilization
Modernizing a system that cannot be reliably built, restored, or audited only moves the risk elsewhere. The first stage therefore changes no features. Its purpose is to mitigate the immediate risks and lay the foundation for modernization.
Area | Measure | Why it comes first |
|---|---|---|
Source control | Move all Delphi and PHP code into centralized, managed Git repositories such as GitLab or GitHub Enterprise. | This is priority one. The company gains custody of its own source, an auditable change history, and the ability to collaborate and roll back. |
Build environment | Document the Delphi XE2 setup step by step with the lead developer, including every component and license key, in a shared knowledge base such as Confluence. | A build that can be reproduced on a second machine removes the single point of failure. |
Components | Audit all third-party components and identify viable replacements or open-source alternatives. | Obsolete components are the main obstacle to fixing bugs or compiling under newer Delphi versions. |
Licenses | Acquire the IDE and key component licenses in the company's name. | This secures the legal right to compile and maintain the product. |
Database schema | Introduce schema versioning, and document every relation that is not in at least third normal form. | Uncontrolled changes to attributes stop, and later services get a known data model to build on. |
Data integrity | Add database-level constraints. | Integrity is enforced by the database itself, not by application convention. |
Backups | Implement regular database backups that allow quick restoration to a previous state. | Protects against hardware failure, corruption, cyberattack and human error, and supports business continuity. |
Documentation | Formalize system documentation, onboarding procedures and knowledge transfer sessions. Document the backend API in Swagger or Postman. | One person's knowledge becomes a company asset, and onboarding time falls. |
Observability | Introduce structured application logs and centralized monitoring, for example Filebeat and ELK, or simple email reports to begin with. | Incidents can be diagnosed from evidence. Today there is none. |
At the end of this stage, the company, not an individual, holds the code, the licenses, the build procedure, and the data safeguards. Only then is it safe to start changing the architecture.
Stage two: incremental modernization
Modernization rests on two techniques used together. An API wrapper makes the logic inside the Delphi application callable from the new stack. The Strangler Fig pattern governs how that logic is then replaced.
New services are built around the old application and take over its functions one at a time, until the old application has nothing left to do and can be switched off. The legacy system handles only the functions not yet migrated.
The target architecture places an API gateway in front of new backend services and a new web frontend. During the migration both the new stack and the Delphi application work against the existing MySQL database. Business logic that still lives in the Delphi application is reached through a gRPC wrapper and a standalone Java proxy.

Sharing one database makes the approach practical. Users move to new screens module by module, and there is no single data migration date when everything must work at once.
Wrapping the legacy application
Sharing the database covers data but not behavior. Much of the business logic will remain inside the Delphi application for some time, and the new stack must use that logic before migrating it. Re-implementing early would defeat the purpose of an incremental plan. Letting each new service call the monolith in its own way would spread legacy dependencies across the new code. We proposed a two-part wrapper.
A gRPC interface on the Delphi side. The Delphi application exposes the functions the new stack needs as APIs wrapped in gRPC. The application's internals are otherwise left untouched.
A standalone proxy application written in Java. The proxy consumes those gRPC APIs and is the single point through which the new stack reaches legacy functionality.
The reasoning behind this design is as follows.
An explicit contract. A gRPC API is defined in a strongly typed, language-neutral interface description. Each wrapped function gains a documented signature the legacy system never had.
Isolation. Knowledge of the Delphi application is confined to one component. The gateway, the services, and the frontend are built against the proxy and need no awareness of Delphi.
Minimal change to legacy code. The Delphi application is modified only to expose existing functions. This is consistent with treating it as a black box.
A clean retirement path. As each function is rebuilt as a new service, we withdraw the corresponding wrapped call. When none remain, the proxy and the Delphi application can be switched off together.
Phase 1: verify the approach
The objective is to prove the new technology stack in production at the lowest possible risk.
Introduce an API gateway as the single entry point for the new web application. Candidates include PHP, Node.js with Express, Spring Boot, or managed services.
Stand up a minimal modern backend and a modern web frontend in React, Vue, or Angular.
Route only read-only requests from the new frontend to the existing database.
Read-only traffic cannot corrupt business data. The team validates the stack, deployment pipeline, and working process before touching any write logic.
Phase 2: strategic service extraction
The objective is to move the first real functionality, chosen for high value, low risk, or frequent change.
Reporting and lookup. Rewrite simple data-retrieval functions, such as client lookup or inventory status, as dedicated services that query MySQL directly. This decouples simple data queries from the monolith.
One complete UI module. A small, non-critical module such as a settings page is rewritten in full on the new frontend and services, and its Delphi screen is retired. This validates the modern stack end to end.
Starting with low-logic features gives users visible progress early and lets the team establish a repeatable extraction routine before difficult work begins.
Phase 3: core logic strangling
The objective is to move the complex transactional logic that still lives inside the Delphi application.
New write path. One core transactional process at a time, such as order creation or an inventory adjustment, is rebuilt as a new service used by the new frontend.
Data synchronization. The new service writes to the MySQL database. Where an integration has not yet been migrated, it calls the relevant function of the old application through the Java proxy and the gRPC wrapper.
Retirement. Once the new service is validated and stable, remove or disable the equivalent code in the monolith.
Interface migration. Retire legacy, undocumented data protocols in favor of standard REST APIs exposed through the gateway. The true source for each data object is defined and enforced.
Event-driven communication, where justified. As the number of services grows, a message broker such as ActiveMQ lets them communicate asynchronously, further decoupling them and preparing the system to scale.
By this point, the team has extracted several simpler modules and knows the data model. The highest-risk work is done with the most experience.
Process and governance
New architecture does not fix a reactive development model. If requirements stay undocumented and priorities are set by urgency alone, new services will accumulate the same debt as the old application. The plan pairs technical work with process changes introduced during phase two.
Documented business requirements. Each change starts from a written requirement, so the reasoning behind the system's behavior is recorded as it evolves.
A managed backlog. Basic Scrum or Kanban in tools such as Jira or Asana replaces ad hoc prioritization with a visible, ordered queue of work.
A change request process. Stakeholders have one defined route to request changes, and the business sets priorities based on stated criteria.
Performance indicators and quality assurance. Formal project management practices, measurable indicators, and a QA process make delivery predictable and give management a way to track progress.
The goal is a sustainable, documented development process that survives team changes.
Managing the risks of the migration
An incremental migration reduces risk but does not eliminate it. We identify engagement-specific risks at the outset and build mitigation for each into the plan. In this case, there were four: two technical and two concerning people and budget.
Risk | How we handle it |
|---|---|
Developer resistance or burnout | New developers pair with the lead developer to document and rewrite components. Knowledge spreads, and the sole maintenance burden shrinks over time. |
Licensing and compatibility | The Delphi application is treated as a black box. It is changed only where extraction requires it, for example exposing an internal function through the gRPC wrapper. |
Database lock-in | New services reach MySQL only through an abstraction layer such as an ORM or the Repository pattern. The database can later be upgraded or replaced without rewriting the services. |
Budget and resource strain | Migration order follows business value and stability risk: new features, customer-facing modules, the most fragile parts. Only what the modern features need gets rewritten. |
The first of these deserves emphasis. The lead developer is the most important source of knowledge in the project, and the plan depends on working with that person, not around them. Pairing shares knowledge and relieves the developer of being the only one who can keep the system running.
The team behind the approach
An engagement of this kind draws on several disciplines at once. Plexteq keeps them in one company, so the team that audits a system can also stabilize it, re-architect it, and keep it running.
What the engagement requires | Plexteq practice |
|---|---|
Independent audit and target architecture | |
Repositories, reproducible builds, backups, logging and monitoring | DevOps |
New backend services and web frontend | Software engineering, including modernization of existing products |
Schema audit, integrity constraints and database abstraction | |
Quality assurance process and performance indicators | |
Stable production operation during the migration | Managed services, with incidents resolved within an agreed SLA |
Three corporate values guide how we work: efficient communication, technical excellence, and high-quality processes. We control product quality and customer satisfaction at every stage of the project. Our delivery model is Agile, built on hands-on experience with processes aligned with CMMI v3 and ISO 9001, giving clients flexibility without sacrificing clarity or predictability.
Conclusion
A legacy system with a bus factor of one is a business risk before it is a technology problem. The approach described here addresses it in the order that risk demands. Secure the code, the build, the licenses, and the data. Wrap the legacy application behind a defined API. Prove the new stack on read-only traffic. Move one function at a time, and retire legacy code only when its replacement is stable.
This way, the business keeps operating throughout, the investment is spread over time, and each step can be judged on its results before the next begins.
If your organization depends on a system that only one person can build, we would be glad to start with the same audit.
FAQ
What is legacy application modernization?
Legacy application modernization means updating important but outdated apps to make them more flexible, easier to connect, able to grow, and more secure. This can be done by adding APIs, or by fully redesigning or replacing the app.
Why not rewrite the Delphi application from scratch or move it to a SaaS platform?
The application has 75,000 lines of code with complex business processes, and much of it is undocumented. Rewriting or moving the app would mean figuring out these processes again, which could disrupt daily work during the project.
How does the new system use business logic that still lives inside the Delphi application?
The Delphi app makes its needed functions available as APIs using the gRPC protocol. A separate Java proxy uses these APIs, so the new system can access the old logic through one component without needing Delphi skills.
Will daily operations be disrupted while the system is modernized?
There is no plan for a single big switch. The Delphi app will keep running, both systems will use the same MySQL database, and the new system will start by handling read-only traffic. Users will move to new screens one module at a time.
How is the current lead developer involved?
The lead developer knows the most about the project. New engineers work with them to document and rewrite parts of the system. This helps share knowledge and makes sure the lead developer is not the only one who can keep things running.
External references
Strangler Fig pattern, https://martinfowler.com/bliki/StranglerFigApplication.html
Introduction to gRPC, https://grpc.io/docs/what-is-grpc/introduction/



