In short: A leading Central European oil and gas group runs its refinery operations on an in-house platform of more than 100 Delphi desktop applications on a shared Oracle database. It works and it fits the business, but it was built in Delphi 2007 and could not reach a browser, a phone or another system. We designed the platform’s web architecture, proved it on the hardest kind of application, a 24/7 process-safety workflow, and took it live. On that architecture we then moved the most critical and complex applications to the web, and handed further development and maintenance back to the client’s in-house team.

Rather than rewrite twenty years of tested business logic, we:

  • turned the Delphi code into web services, running behind an API gateway;
  • gave it a new web front end;
  • left the database untouched, so old and new applications could run side by side.
Client A leading Central European oil and gas group, downstream (refining) division
System In-house refinery information platform: 100+ applications for production, movements, quality, planning, safety and accounting
Pilot application Management of Change: the process-safety workflow that reviews and approves every change to units, equipment, chemicals and procedures (24/7)
From Delphi 2007 desktop (VCL) applications, Oracle database, 6 languages
To Web UI (Angular, Material Design) → ASP.NET Core and API gateway → Delphi business logic as web services → the same Oracle database
Phases Discovery and knowledge transfer (2018) → study and pilot (Sept 2018 – Jan 2019) → production rollout → further critical applications (2019–2023) → handover to the in-house team

The problem: a platform that works, on a stack that cannot move

The platform was not failing. It was stuck. The client’s own assessment was that the in-house system had real advantages:

  • Affordable to run compared with vendor products.
  • Mapped closely to refinery processes.
  • Proven in daily use, with a working network of interfaces to the plant historian, data reconciliation, the laboratory systems, scheduling and ERP.

The client had considered replacing parts of it with packaged products, but pilot projects showed that route would cost more, take longer and carry more risk.

What it could not do was follow the business forward:

  • No browser or mobile access. Every application was a Windows desktop program installed on each client machine.
  • Heavy to deploy and run. The applications were installed on hundreds of workstations at sites across Europe. Deployment, maintenance, performance monitoring and every new release took real effort.
  • No standard API. Business users wanted to pull movements, laboratory results, tank inventory and reporting data into Excel and Power BI themselves. Integration and analytics projects needed a proper interface with authorisation and auditing, not database access.
  • An ageing toolset. Delphi 2007 predates Unicode support, the pool of developers who want to work in it is shrinking, and the client’s own experienced development team was getting smaller every year (see the Delphi talent pipeline).
  • No modern development practice. Automated builds, test environments and continuous integration were goals, not reality.

The constraints

A refinery cannot stop for a software project.

  • No interruption. Operations had to continue undisturbed. Development on the existing applications had to continue too: the new platform must not block it.
  • One shared database. More than 100 applications and the interfaces to plant systems all use it. Changing its structure was off the table.
  • Six languages. The applications run in Hungarian, English, Slovak, Croatian, Italian and Romanian. The database stores text in an 8-bit Central European code page (ISO 8859-2) that the existing non-Unicode applications depend on.
  • Strict security rules. These came from the client’s minimum security requirements:
    • single sign-on through the corporate identity provider;
    • least privilege;
    • encryption on every connection, including server to server;
    • no passwords in code;
    • OWASP Top 10 compliance;
    • logs exportable to the security monitoring system;
    • anonymised data in development.

What we did

Phase 1: discovery

Together with the client’s developers, we documented the platform’s business processes, applications and interfaces. That gave everyone a shared map before any architecture was drawn.

Interviews with key stakeholders set out the business priorities, which decided the order of work and where risk had to be managed most closely. Sessions with power users showed us the current system’s pain points and gave us a group of champions and key testers, who later helped with acceptance across the user base and with certifying each system for production.

Phase 2 onwards: study, pilot and rollout

Milestone Delivered
1. Guidelines IT and security requirements that frame every technical decision
2. Architecture The target architecture, plus prototypes of the riskiest parts
3. Persistence layer What it would take to move to Microsoft SQL Server later
4. Development infrastructure Development, test, staging and production environments; know-how transfer
5. Application framework A reusable framework, covering both UX and server-side architecture, for migrating further applications
6. Pilot The most important functions of the Management of Change application, running in the new architecture
7. Findings Lessons learned and recommendations for the full programme
8. Further development Several of the most critical and complex applications re-engineered in follow-on projects; the in-house team trained and given ownership of the new code

The key decision: keep the proven logic, change everything around it

We did not rewrite the business logic. We moved it behind an API. The architecture has four tiers:

  1. A responsive web application (Angular with Material Design) running in any modern browser, over HTTPS only.
  2. An application tier (ASP.NET Core) that handles sign-on, serves the web application and runs an API gateway (Ocelot). The gateway is the single entry point; no internal Delphi service is exposed directly.
  3. Delphi microservices. Each application’s existing business logic and data access, ported to current Delphi and running as web services on IIS (ISAPI DLLs built with Delphi’s WebBroker framework, 64-bit). Where several applications used the same functionality, we merged it into one shared service.
  4. The existing Oracle database, unchanged.

Why keep Delphi in the middle tier:

  • No re-implementation risk. The code had been tested by decades of refinery operations, so rewriting it would have meant re-finding its edge cases.
  • Interfaces and many modules could be reused as they were. That cut both effort and risk.
  • The client’s domain-expert developers stayed productive. They kept working in the code they knew, which also made it much simpler to keep developing the original desktop applications in parallel.
  • The door stays open. The gateway makes each service replaceable, so any service can later be rewritten in another language without the web front end noticing, and new services can be built with different tools.

Keeping the database meant old desktop applications and new web applications could run side by side on the same data. Each new application could go first to a pilot group of users and then to everyone, while the rest of the platform carried on unchanged.

Turning a desktop program into a web service

This is where most of the engineering went. A typical desktop application assumes one user, one thread and a screen. A web service has none of those.

  • From Delphi 2007 to current Delphi.

    • First, we ported the applications to 64-bit and verified that they behaved exactly as before.
    • String became Unicode, so code using strings as byte buffers was rewritten.
    • Calls to a non-Unicode third-party library fell back to AnsiString.
    • Locale-dependent conversions moved to explicit TFormatSettings.
    • Version-specific compiler directives, component library changes and a few deprecated APIs were updated.

    Our article on upgrading Delphi 7 to Delphi 13 covers the same ground.

  • A code-page boundary. Text is converted between ISO 8859-2 and Unicode at the database boundary. The new services work in Unicode, internally and towards their clients, yet read and write exactly what the existing non-Unicode applications expect, so both generations share the data safely.

  • No user interface on the server. Business rules were moved out of form event handlers into ordinary procedures; forms are never shown, or even created where avoidable. Messages and user interaction moved to the web front end.

  • Thread safety. A web server runs many requests inside the same DLL at once, so global variables had to go.

    • The methods, in order of preference: pass state as parameters, wrap state in an object, and only as a last resort use threadvar.
    • Shared resources were protected with critical sections.
  • Speed.

    • The tables of the pilot application (25 tables) are cached in memory when the service starts, behind a multiple-reader, single-writer lock.
    • User account data is cached too.
    • The JSON responses are written with Delphi’s streaming JSON writer instead of temporary objects.
  • Robust request handling. A central dispatcher handles routing, errors and HTTP status codes. Every handler catches its own exceptions, and memory is freed per request.

  • The desktop build kept working. The ported code still compiles as the original desktop application with the same behaviour. The two could run in parallel, and new business requirements could still be implemented while we re-engineered the application.

The front end, and how we let two teams work at once

The web application reuses the platform’s existing translation files, so all six languages came across with the original wording.

For the teams:

  • Mock services. The API gateway can send any route to a mock service answering from recorded JSON responses. The front-end team could build screens before the matching back-end service existed.
  • API testing. A small debug server let developers fire requests from Postman straight into the Delphi debugger. The same Postman collections served as API tests and response-time checks.
  • Sign-on. The gateway verifies the user’s identity from the corporate single sign-on (SAML) and passes it to the services, which reuse the platform’s existing session and role model.

Development process

The pilot also set up how the programme would run:

  • version control in Git, one feature branch per ticket;
  • a build server with developer, stable and release builds;
  • regression testing before anything reaches the stable build;
  • tagged releases;
  • separate development, test, staging and production environments;
  • established feature and issue tracking best practices for the client’s business analysts, developers and DevOps team.

What the pilot found

A pilot is meant to surface problems early, and it did:

  • SQL injection. The legacy data-access code built SQL statements from user input. On a closed desktop network that is a contained risk; behind a web front end it is not. Fixing it was part of the production version’s scope from the start.
  • Cache coherence. Database writes had to be separated cleanly from cache updates.
  • Parallel development. Keeping the desktop and web versions building from the same source needed an explicit strategy.

The result

The Management of Change application went live in its web version, and the architecture became the path for the rest of the platform. We went on to further applications of the same family, one at a time, on the same pattern: Delphi logic as services, a web front end, and the shared database, so that old and new ran side by side throughout. Development and maintenance of the re-engineered applications has since passed to the client’s in-house team.

UX and architecture guidelines, set early in the programme, gave every application the same visual language and a repeatable way of re-engineering it. The web interface is new and responsive, but it carries over the usage patterns people already knew, so users could switch to it with little retraining (enhancing productivity and user acceptance).

What the study and pilot delivered:

  • a documented target architecture, with prototypes of its riskiest parts;
  • development, test, staging and production environments;
  • a reusable framework for migrating the remaining applications;
  • the pilot application’s main functions running in the browser;
  • workshops that transferred the know-how to the client’s own developers.

What we would do the same today, and what we would change

We would keep the pattern:

  • business logic behind an API gateway;
  • the database shared during the transition;
  • one application at a time;
  • the desktop and web versions running in parallel.

It is the same slice-by-slice approach we describe in how we migrate Delphi to Java and the web, and the “wrap the Delphi logic” step from why Delphi applications need an API.

We would start on current versions. The 2018 stack has aged: .NET Core 2.1 has been out of support since August 2021, and the front-end framework has moved on several major versions. We upgraded key frameworks during the programme, but a project starting today would build the gateway and application tier on a current long-term-support platform: Java with Spring Boot, or .NET 10. Behind the same gateway, each Delphi service can later be replaced by a Java or .NET service, one at a time, without users noticing.

Lessons for anyone with a large Delphi platform

  1. “It works” is a reason to migrate carefully, not a reason to rewrite. Proven logic is an asset; keep it until you can prove a replacement behaves the same.
  2. Keep the database. It lets old and new run side by side, and that is what makes a no-downtime migration possible.
  3. Pilot on something hard. A 24/7 safety workflow tests the architecture far better than a read-only report. Choose something well-defined and non-trivial, with both UX and back-end work, that can still be delivered in a relatively short time. That reduces risk and wins business buy-in.
  4. Put a gateway in front from day one. It hides the internal services, centralises security and compliance, enables mock services, and makes each service replaceable later.
  5. Desktop code is not thread-safe by default. Budget for removing global state; it is most of the work of turning a desktop program into a service.

Running a production platform on Delphi? Talk to an engineer about a technical audit, or read the business case for moving off Delphi.

Sources

  • Client project documents: the study and pilot document (revision January 2019) and the technical kickoff presentation. Not public.
  • Microsoft, .NET and .NET Core support policy (.NET Core 2.1 end of support: 21 August 2021)
  • OWASP, Top 10