Rebuilding an existing system

What should we reuse?

Charles Weir
cweir@cix.co.uk

Abstract

This paper describes the problems in reengineering an existing system implemented in procedural languages in OO. It recommends reusing the existing system wherever possible, and discusses the four main opportunities for reuse: code, engineering, design and test.

Introduction

Suppose you have an existing legacy system written in a procedural language: C, FORTRAN, COBOL, or even a more specialist language like APL or assembly language. You want to bring it forward into the twentieth century, but there are major problems with this. The language isn’t motivating to programmers or suited to the design (OOD) and infrastructure technologies (CORBA/COM). The code is poorly structured (see [Johnson+]) and parts are poorly understood.

You’ll almost certainly be thinking of re-writing the entire system. You’ll go back and derive the specification from the users and the existing system. Using the superior technology available now and the superior talents of the your current development team you’ll produce a leaner, better structured and in all ways superior development. That’s the best plan, isn’t it?

Well, no. In fact I strongly urge you not to do this. My experience has been that projects that attempt to ‘redo from start’ in this way have a very low success rate. I know of some dozen projects that have tried this; only one has succeeded and even then the company concerned nearly went bankrupt because of it. There are several reasons why this happens:

The alternative approach

Instead it’s better to rebuild the system as a restructuring and translation exercise. You can almost always leverage the existing code, designs, test specifications and knowledge far more than one might think at first glance. In the following sections I shall consider four different approaches to reusing the existing software in the new product:

Reusing existing code directly

It is often possible to call language routines in procedural languages from OO languages. We can call C or Pascal directly from most OO languages; other procedural languages using CORBA or ActiveX or more proprietary solutions. This approach is suitable for procedures and entire components which:

Procedures like this are relatively rare. It’s much more common to split off entire components in the procedural language. Typical examples would be components to handle a complicated communications protocol, an interface to a piece of hardware, report-writing, or detailed financial modelling.

Another approach is to use a translation tool to convert the existing code to the new language. The resulting code is often inelegant, to say the least, but it can be a highly productive approach.

Reuse by Reengineering

The most effective form of reuse is to study the code, and translate its algorithms into the new OO language. It is important that all the existing business functionality in the current system is reflected in the new system. The best approach will be to work through every line of every file, to make sure that nothing is lost. The programmers can provide ‘audit trail’ so that other team members can help make sure nothing has been overlooked. This might be pen markings on a listing of each file, or perhaps annotations in the code using a tool such as CodeWright.

This approach will be most valuable for code which:

It is likely that a major proportion of the existing code will be candidate for this kind of reuse. I recommend that your team establish a recipe for doing the conversions. Such recipes depend critically on the particular system being translated, and may vary between components. The recipe might expand on the following:

The developers will also need code reengineering tools to help discover the workings of the existing system. The following are some possibilities:

Design reuse

You can also reuse the design of the existing systems. How practical this is depends on the quality of the existing design, and how different the future requirements are likely to be. However a good rule of thumb is to have a good idea of the reasons for the existing design strategy before deciding on a new design. It’s also important to be clear why the reasons for taking a new and different approach.

Some aspects of design which may be reusable are:

Often design reuse happens by default. If the team are familiar with the existing system, they may tend to use the existing design ‘zeitgeist’ because they are unaware of other approaches, or because "that’s the way everyone does it".

Test reuse

If your existing system has test documentation, test scripts or automated tests you should expect to reuse them too.

Test documentation should generally carry over to the new system with minor changes. Test scripts can often be kept intact, or changed mechanically to suit the new system. Automated testing scripts may be more complicated to port. It’s unlikely that any User Interface-based testing could port directly to support a new system. However it’s usually reasonably easy to print out the tests and recreate them manually.

Summary

In this paper I’ve looked at four different ways to reuse the huge amount of development effort typically put into developing an existing legacy system:

Code

Functions and modules can carry over to the new system.

Reengineering

Developers translate and restructure the existing code line by line.

Design

Any good design decisions in the existing structure are well worth preserving.

Test

Tests of the business functionality can carry over as well.

Completely reimplementing an existing system just from functional specification is very likely to fail. Reusing an existing system in the ways described above has a high chance of success.

References

[Shaw+] Software Architecture - Perspectives on a Emerging Discipline, Shaw and Garlan, Prentice Hall ISBN 0-13-182957-2

[Johnson+] Big Ball of Mud Johnson et al. published on the web as http://laputa.isdn.uiuc.edu/mud/mud.html