Legacy Application Modernization for German Mittelstand Companies: A Strategic Guide

#legacy application modernization
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Founder & Lead Developer

Expert in software development and legacy code optimization

Most German mid-sized companies run at least one application that nobody wants to touch. It handles orders, production planning, or billing, it has worked for twelve years, and the developer who understood it left in 2019. Legacy application modernization is the project that keeps getting pushed to next year's budget, until a PHP version reaches end of life, a hosting provider drops support, or an auditor asks why customer data sits in a system that has not had a security patch since 2017.

This guide is for CTOs and IT leads in German Mittelstand companies who have to make that decision now. It covers how to decide between modernizing and rebuilding, the five legacy situations we encounter most often in German companies, a process that works without freezing the business, and what these projects realistically cost.

Modernize or rebuild: a decision framework with real criteria

The instinct of most engineering teams is to rewrite. The instinct of most finance departments is to keep patching. Both are usually wrong, and the right answer depends on a handful of criteria you can assess in a week.

Start with the business logic. If the application encodes rules that nobody has written down anywhere else (pricing exceptions, tax edge cases, the way a particular customer's orders get split across warehouses), a rewrite means rediscovering all of that from scratch. Modernizing in place preserves the behavior while you replace the technology around it. If, on the other hand, the logic is thin and the value sits mostly in the data, a rebuild carries far less risk.

Next, look at the runtime. An application on PHP 5.6 or Symfony 2 can still be upgraded step by step, and tools like Rector automate a large share of that work. An application built on a framework that no longer exists, or a desktop technology like Windows Forms that needs to become a web application, has no upgrade path. That is a rebuild by definition, even if you reuse the database schema and the domain knowledge.

Then check the test coverage. Zero automated tests does not rule out modernization, but it changes the first phase: you need characterization tests before you change anything. Budget for that.

Finally, consider the team. If the people maintaining the system are still around, modernization lets them contribute what they know. If the knowledge left with the last contractor, a rebuild with a fresh team is sometimes the more honest option, because the "modernization" would be a rewrite anyway, just a disguised one.

Our rule of thumb after working through dozens of these: modernize when the business logic is valuable and the platform still has an upgrade path. Rebuild when the platform is dead or the logic is trivial. When you are unsure, a two-week assessment costs less than either wrong decision, and we wrote more about the scoring approach in our rewrite vs. refactor decision framework.

The five legacy scenarios we see most often in German companies

The German Mittelstand has a particular history with software. Many companies built their own systems in the 2000s because nothing on the market fit their processes, and those systems are still running. The situations repeat often enough that we can describe them.

Symfony 2 and 3 applications

These are the most common case in our audits. A Symfony 2.8 or 3.4 application, often started around 2014 to 2017, with a bundle structure from that era, Doctrine annotations, Twig templates, and a few hundred controllers. The good news: the upgrade path to Symfony 7 exists and is well documented. The bad news: dependencies. Half the bundles used in 2016 are abandoned, and each one needs to be replaced or vendored. We covered the mechanics in Legacy PHP refactoring step by step.

PHP 5.x systems without tests

Usually a custom framework or plain procedural PHP, sometimes with a home-grown ORM. No tests, and often no version control history beyond the last few years. These systems run on PHP 5.6 or 7.0 because upgrading broke something once and nobody dared to try again. The modernization here starts with containerizing the current state exactly as it is, so you have a reproducible baseline, and then writing HTTP-level tests against the running application before touching a line of code.

Windows Forms applications that need to become web applications

A surprising number of production planning and warehouse tools in German manufacturing are .NET desktop applications from the 2000s. They cannot be modernized in place if the goal is browser access, mobile use, or integration with a web-based ERP. This is the rebuild scenario, but the desktop application is an excellent specification: every form, validation rule, and report tells you what the web version must do. We treat the old application as the requirements document and build the replacement screen by screen, typically as a custom web application with an API that other systems can also use.

Custom developments integrated with SAP

Many mid-sized companies run SAP for finance and have built their own applications around it for everything SAP does poorly for their industry: configurators, field service tools, customer portals. The custom side is often years behind, while the SAP side gets upgraded on a schedule. Modernizing these means untangling the integration first. Direct database access to SAP tables, RFC calls scattered across the codebase, and nightly CSV exchanges all need to be consolidated behind a single integration layer before the application itself can be moved.

Proprietary frameworks without documentation

The hardest case. A former agency or a former employee built a framework, built the application on top of it, and left. Nobody knows how routing works, the templating system is unique, and there is no documentation. Here the assessment phase matters more than anywhere else: you need to map the framework before you can decide whether the application on top of it is worth keeping. Often the answer is a strangler fig migration where new features go into a Symfony application from day one and the old framework is drained over time.

The modernization process, step by step

Whatever the scenario, the process has the same shape. What changes is how long each phase takes.

The first phase is inventory. Before estimating anything, you need to know what is actually there: which PHP version and extensions, which dependencies and their maintenance status, how many routes and entry points exist, which ones still receive traffic, where the database has grown into an undocumented mess, and which integrations depend on the application. Access logs from the last three months are the single most useful input at this stage, because they tell you which parts of the system you can simply drop. In most audits, 20 to 40 percent of the codebase serves no traffic at all.

The second phase is risk assessment. Not every part of a legacy system is equally dangerous. Payment handling, authentication, and anything that writes to the ERP get the highest scrutiny. Reporting screens that a handful of people open once a month get the lowest. The output is a ranked list of components with a migration order, and this is also where you decide what needs characterization tests before changes begin. Our code quality consulting engagements usually stop here with a written report if the client wants to do the work internally.

The third phase is the migration itself, and here we almost always recommend the strangler fig pattern. Instead of a big-bang cutover, you put a routing layer in front of the old application, build the new system alongside it, and move traffic one route or one feature at a time. Both systems share the database at first, then the schema is migrated piece by piece. The old application shrinks until it can be switched off. The pattern is slower on paper than a rewrite, but it never leaves the business without a working system, and it lets you stop at any point with a partially modernized application that is still better than where you started. We described the mechanics in The strangler fig pattern.

The fourth phase is often forgotten: operational handover. A modernized application that only the external partner understands is a new legacy system in waiting. Documentation, CI pipelines, monitoring, and a period where the internal team ships changes with support are part of the project, not optional extras.

What legacy application modernization costs in Germany, and how long it takes

Every project is different, but after enough of them, ranges emerge. These are figures for the German market with senior engineers, either freelance or from a small consultancy, not agency rates with account management overhead.

A Symfony 2 or 3 to Symfony 7 upgrade for a mid-sized application (50 to 150 thousand lines, a few hundred routes) typically takes three to six months with one to two engineers and lands between 60,000 and 150,000 euros. Rector and a good test harness push it toward the lower end.

A PHP 5.x system without tests costs more per line because of the test-writing phase. Expect four to nine months and 80,000 to 200,000 euros, with the first six weeks spent almost entirely on containerization and characterization tests before any refactoring begins.

A Windows Forms to web rebuild depends heavily on the number of screens and reports. A planning tool with 30 to 50 forms is usually a six to twelve month project with two to three engineers, in the range of 150,000 to 350,000 euros. The old application as specification cuts the discovery phase significantly compared to a greenfield build.

SAP-integrated custom applications add an integration layer phase of one to three months on top of whatever the application itself needs, and the SAP-side work usually requires your SAP partner as well.

Proprietary framework migrations are the hardest to estimate up front. We always insist on a paid two to four week assessment before quoting the migration, because the difference between a framework that is merely undocumented and one that is unsalvageable is easily a factor of three in cost.

Two points on the money. First, compare these numbers with what the legacy system costs you today: hosting for an unsupported stack, the hours your team spends on workarounds, the features you cannot build, and the risk of an incident nobody can fix. Most companies that do this calculation find that the modernization pays for itself within two to three years. Second, phased delivery means the budget is not a single commitment. A strangler fig migration can be funded quarter by quarter, and each quarter ends with a working system.

How Wolf-Tech works through this with clients

We are a small Berlin-based team that has spent most of the last decade inside exactly these systems: Symfony applications from the early 2010s, PHP 5 monoliths in manufacturing and logistics companies, and custom tools bolted onto SAP. Our legacy code optimization engagements start with the assessment described above and produce a written migration plan with the ranked component list, the test strategy, and a phased budget. Clients can take that plan to their internal team, to another partner, or continue with us for the migration.

During the migration, we work alongside the internal team rather than around it. That is slower in the first month and faster in every month after, because the knowledge stays in the company.

If you have a system that fits one of the five scenarios above and want a second opinion before committing a budget, write to hello@wolf-tech.io or find more about how we work at wolf-tech.io. A first conversation about your codebase costs nothing, and it usually saves a few wrong assumptions.