Renovating Legacy Software

A rewrite is the highest risk path unless…

Insights

An Allegory

Imagine that you are the captain of a big rowboat that has been rowing back and forth across the ocean for years hauling cargo. It has paid for itself many times over because of the value delivered by those crossings. The crew are very experienced and know how to keep the boat seaworthy and even make improvements to it. This is becoming increasingly difficult because a relentless focus on short term efficiency has led you to increase crossings at the expense of maintenance. The crew are getting on in years, and it's challenging to recruit new crew members. Candidates fresh out of boat school want to work on boats with new tech like hydrofoils and composite sails and do not view experience on the legacy boat as the basis for a good career.

You describe the situation to the admiralty and they agree that this deserves emergency investment. You propose to build a new boat with all-new technology to replace the old one, and to hire a small team of new boat-school graduates along with some low-cost boatwrights from a foreign land to build it, catch up with the legacy boat, and eventually take over its cargo business. 

The admiralty offers enough funding for a crew for three years. Even with the boatwrights it's not as big as the crew rowing the legacy boat, but you reason that they are working with new technology, so they ought to be able to go faster. The legacy boat's crew are tasked with continuing to move cargo across the ocean, and are still making changes to their boat. You reassign one person from the legacy crew to write a document that details everything that the boat does so that the new team can use that as a specification for their boat.

The legacy boat and its experienced team are delivering value today, and must be kept competitive. The smaller inexperienced team has no boat, and their only guide is the specification and whatever they learned in boating school. The specification was one person's idea at one instant of what was important to your business. The legacy boat is still under development and is changing with every passing day. 

Quiz

  • What are the new crew's chances of getting their boat seaworthy before the admirals pull the funding? 

  • If they could build a boat quickly, what are their chances of out-rowing the experienced and larger crew?

  • What other ways might there be to solve the problem?

  • What was the customer problem in the first place?

Some observations

  • We didn't define the customer problem carefully before proposing a solution. Is a boat the best solution?

  • We jumped to the “build from scratch” approach without exploring other ways to solve the problem

  • The inexperienced team lacks the context that the experienced team has

  • The experienced team are busy keeping the business in business

  • The specification is almost instantly out of date, and there is no way to prove that it captures what is important for your customers

  • A smaller, inexperienced team starting from scratch is unlikely to overtake the experienced team if your customers have something to say about it

A good problem statement is half the solution

What is the problem that we are trying to solve by modernizing or rewriting a valuable software product? 

Some candidate issues include: 

  • Cost of Change - adding new capabilities costs too much. Note: Consider the contribution of sub-optimal development practices (like over-reliance on manual testing).

  • Technical obsolescence of key components or platform. 

  • Difficulty hiring new people to maintain the code (ancient toolchain, business model)

  • Customer and business fit - desire to move to a SaaS business model or to offer capabilities delivered by new technology (like AI/ML). Eg. Customers want to reduce their cost of ownership with cheaper scaling, browser based UI, central data management and remote administration.

  • Maintenance cost (security, platform obsolescence or instability, complexity, compliance issues)

What is your problem statement? Ask “why” enough times to be sure you have gotten to the real issue before identifying possible solutions.

Automated testing is your life jacket

The best time to have begun developing  automated tests is when you began developing your legacy product. The second best time is now. It almost doesn’t matter what your problem statement turns out to be.

There is no way to test a specification, so invest in test automation before modernizing legacy code. If your legacy product doesn't have automated test coverage, this may be contributing to the problem you want to solve. Automated tests assure that you have captured the critical behaviors of the legacy code. With good automated coverage, defects stay fixed. Test automation reduces release cycle times and build breaks, and guards against unanticipated side-effects when code is changed. I cannot emphasize enough how important it is. Yet many organizations with legacy products have failed to invest in it. Neither of these approaches (spec or test automation) tell you whether you are actually solving a problem your customers care about.

No matter what approach you take to solving the problem you articulate about your legacy product, you will be better off if you have surrounded it with good automated test coverage. It makes sense economically because you pay for manual testing every time you perform it. Automated testing is essentially free to run; you only pay to keep it current and to expand coverage as you find defect leaks. It is also much faster than manual testing for a given level of coverage. You can run it as soon as a change is committed, which improves developer productivity. Any issues that arise are fixed more quickly. Broken builds are rarer so all developers benefit.

Behavioral Evidence

If possible, instrument the legacy system so that you can measure what capabilities are actually being used and how. This evidence informs decisions about what to deliver first, and what can safely be dropped or changed. Here is a starting point: a logfile upload can be part of any support case for on-premise software. If retained, this information can help your product team analyze what parts of the product are most involved in support calls (implying both that they are being used and that they may need some design attention). If you have a SaaS offering that lacks tools to measure how customers use the service, you are missing a huge opportunity to improve your product-market fit and focus effort on things that deliver the most value.

With test automation and behavioral evidence you are in position to begin upgrading your legacy system with confidence that you know what behaviors are important and will have early warning if they are broken.

Consider alternative approaches

With a well-crafted problem statement in hand (yes?), here are some alternatives to a cold rewrite to consider. All need support from a good suite of automated tests.

Does your legacy code assume it is running on a dedicated desktop PC? It may be coded to assume that it can expose user interface elements from the desktop OS. This can make a move to the cloud challenging. There are commercial tools that emulate those services and make the native UI available in a browser. This is a quick way to get your venerable code onto a remote server, but it will not directly solve cost of ownership problems. Eventually you will need to rearchitect the code to separate the presentation layer from the underlying logic.

Careful architecture will help reduce risk in moving a legacy desktop or “fat client” application to the cloud. Teasing Windows UI elements out of monolithic code is one of the challenges. You may be able to do this in stages using one of the techniques below. ***

Automated refactoring - you can clean up crufty code using automated refactoring tools offered by modern Integrated Development Environments (IDEs), assuming your toolchain has one.

Break monolithic code into modules by inserting application programming interfaces (APIs) - this is a relatively low-risk approach as long as you have the coverage to test that the system behaves the same way after inserting the interfaces. Write automated tests at the module level as you do this - you may then be able to rehost or rewrite each module independently.

Encapsulate critical code - wrap legacy code in an API and expose as a service. If compatibility is essential and the code is stable. You can refactor it from here if necessary. This approach wraps legacy code in a modern interface layer. To succeed the legacy code must have minimal external dependencies.

Rehost the code on new infrastructure - lift and shift to the cloud for example. This is usually a dead end for monolithic code, but may allow you to learn from customers as a step toward a SaaS offering. Once you have isolated modules with APIs, this can be an effective scaling strategy.

Move to a new runtime platform - this entails breaking dependencies with the old platform, which is careful work.

Rebuild (from scratch, but with the assistance of your automated test suite)

Replace, considering updated needs - you risk leaving your existing customers unable to move to the new system unless you are very careful. Again a good test suite is critical. You can write tests for the updated needs in advance. The test is better than a specification.

Many of our clients face the hard problem: a venerable legacy system that is brittle, has accumulated technical debt, facing technical obsolescence and a shrinking team of people who can maintain it (if there are any left), AND a desire to transition to Cloud hosting, a SaaS business model, or deliver AI/ML capabilities. Proceed with caution - a good safety net of automated tests is an essential starting point in this case. You may be able to do this piecewise rather than attempting to write tests for the entire system.

When updating legacy software, rewriting from scratch is expensive and risky. Using some of the intermediate steps outlined above can significantly reduce that risk and the cost to get where you need to go.

Summary

Customer satisfaction measures can be used to drive continuous improvement if:

  • The feedback captured contains enough context to allow for root cause diagnosis

    • Net Promoter Scores and other single-valued measures are not sufficient

    • Context must be captured (who, when, where, what, why)

    • Somebody makes use of this information while it is still warm

    • User comments and callback information are very helpful

  • Designated people regularly use the feedback results to develop a Pareto analysis of root causes and then injects changes into the development cycle to counter those root causes

  • The cycle time from customer feedback to follow-up is short so that the trail is still warm

  • Extra credit: Customers see that their feedback is being used to make improvements. Builds your credibility.

  • Extra credit: Customers who have had a subpar experience get quick follow-up and a genuine effort to make things right. Directly improves the customer experience where it most needs improvement. 

highlight

Copyright © 2023-2025 John W Sadler Jr - All Rights Reserved

We didn't define the customer problem carefully before proposing a solution