“You can't expect to meet the challenges of today with yesterday's tools and expect to be in business tomorrow.” Anonymous
Legacy applications don't just afflict the Corporate IT departments of the world, commercial software companies have their fair share. In a software company a legacy application can be a shipping application. The characteristics of a legacy application are the same for both the IT department and the software company. Here is my top 10 list of Legacy Application Characteristics:
1.) Legacy applications were originally written in a language whose modern day syntax and semantics are considerably different from the original version, or worst case the language is extinct. Procedures such as sorting, certain math functions, string manipulation functions may have been developed from scratch in the old language but are now available in a more optimized form in the current language built-in functions. More importantly, these language subsumed functions are for all intents and purposes bug free, portable, scalable and don't need to be maintained.
I have experience with legacy applications originally developed in a language that is is extinct, Quick Basic. Much of the application had been ported to Visual Basic 6.0 but the calculation and validation areas were not ported (I say areas because this was a tightly coupled non layered application so the use of component, module, or layer would be misleading). This was due to the fact that after the original calculation and validation code was written in Quick Basic it was processed by a homegrown converter that converted the code into 'C' and then compiled to a DLL for performance optimization. So rather than update this tool the developers left it as a converter for Quick Basic code, and the calculation and validation developers continued to write the code in the Quick Basic language, which can only be called a meta language at this point since there is currently no commercially available compiler or IDE for Quick Basic.
In addition, the application may predate modern language design techniques of type safe variables, parameter passing and the limiting of global variable declarations resulting in a high number of globals, scopeless variables, and routines that seem to conjure up variables out of thin air making it impossible to trace the variables state as it travels through the code.
2.)Legacy applications were originally developed for platforms with significantly less memory than modern platforms available today. Strategies to deal with memory consumption are proliferated throughout the code, ie. short non-descriptive variable names, developing in-house memory management routines for allocating and de-allocating memory blocks and splitting large related array data into multiple arrays because the old size limits of a single array were exceeded. These strategies provide opportunities for memory corruption and in general make the code unreadable and far from understandable.
3.) Legacy applications were originally developed for platforms with significantly less power/processor speed than modern platforms available today. Strategies to optimize performance such as the use of alternate language DLL's for optimized calculations leaves the code disjointed and many times unmaintainable as can be seen by my reference in #1 above.
4.) Legacy applications have a set of homegrown satellite tools and utilities the original developers created to expedite certain maintenance efforts. These include tools to generate screen coordinates, define data models, process meta data, convert meta data etc. These tools many times have been replaced by modern IDE's, such as the form designers and data model designers in the Microsoft Visual Studio line of products. This leads to a separate maintenance projects for the tools that can sometimes rival the main application maintenance effort. In addition, these tools are sometimes the limiting factor to "what" can be done in the application because the "how" is implicitly immutable in the tools itself, the utilities dictate the applications future feature set.
5.) Legacy applications have limited applicable bandwidth Or put another way, there is a huge learning curve/time sink for engineers to get up to speed on the application in order to make any kind of meaningful contribution to legacy application development. This results in an inability to apply engineering resources when market needs demand, there is no burst mode, that's limited applicable bandwidth. Legacy applications are usually handed down from one development staff to another. They usually suffer from a lack of consistency in coding standards, comments and coding styles. This results in large parts of the application not being understood by anyone, knowledge silos have moved on and an understanding of how and why the code works the way it does left with them. This also results in the satellite utilities mentioned in #4 being unmaintainable or changeable.
6.) Legacy applications have redundant sometimes no op calls to routines. The first 1 or 2 calls don't get the desired results, the current developer has no idea how the code works, so a couple more calls and the number comes back correct. I have seen this in production applications and I am sure you have seen comments in code where it may say "Not sure why this works but..."
7.) Legacy applications are good at what they do, that's why they are still around. They have a maintenance cycle with accompanying maintenance team to keep them up to date with whatever core functionality they provide, ie changing rates, changing laws, changing report formats etc.
8.) Legacy applications are difficult to rewrite. Many of the characterstics mentioned above such as no one knows how the application really works, lack of coding standards and documentation and knowledge silos have left, all make a rewrite more than a porting excersise.
Usually the first phase of a rewrite involves creating some type of requirements or product backlog to define what the rewritten application needs to do, understanding all the intricacies of what the application does from the users perspective and coming to some kind of understanding of how these features are implemented in the code.
Usually there is a need to temper the level of change to user experience/user interface primarily because this is a production application, there is no value proposition to bringing production to a halt when the rewritten "improved" application goes live. This is especially true for commercial software applications where you can lose a significant number of "power" users because after they install the new version they now find themselves in the exasperating position of being unable to accomplish tasks they used to complete in their sleep.
9.) Rewriting legacy applications is difficult to justify.
The cost for rewriting a legacy application can be daunting, especially when considering the learning curve of understanding the application from a customer and engineering perspective as mentioned above. Management assumes there is intimate engineering knowledge of the application, after all this is considered an asset it's part of the company's Intellectual Property (IP), so how could we not know. After tallying up all the costs, including keeping the legacy application running in production which dictates that there be 2 team, one maintaining the production version and one working on the new version, rewriting a legacy application is hard to justify. If the end result is no new features which translates to no new revenue than it's just cost. Quantifying future cost reductions in easier maintainability, or future revenue potential through easier feature development is difficult and usually discounted.
10.) Most important for modern day software, legacy applications are closed architecture, no SOA or SAS concepts here, they are standalone islands that perform their function. Integration opportunities using the application as a process in a larger workflow are not possible. This leads to some very ugly integrations involving import and export to some common intermediary such as a spreadsheet.
So what's in your toolbox to manage and extend legacy applications?
Of course there is always the re-write, but thats not really not a tool, thats a strategy demanding a pretty robust business case to justify the significant cost as mentioned above.
A tool would be something you could apply with limited work on the legacy application to extend it and make it able to capitalize on new revenue generating opportunities. These opportunities may be integrations with partner applications, integrations into common customer workflows, or simply to extend the application to do something new, a new feature that the market demands but which due to the architecture and implementation of the application are impossible to achieve.
There are design patterns which can be stretched to apply to legacy applications, the stretched version of the Adapter Design Pattern can be a useful addition to your toolbox. The basic motivation for the adapter pattern is to enable wider use of an existing system. In the legacy application world this would be an application that does a particular job, and does it well. There is a need to use this legacy application in a larger workflow or set of integrated applicationa, but it just doesn't fit all the requirements, namely there are no integration interfaces. We may want to expand its features, or combine it with some other applicaitons/services to provide additional functionality. The bottom line is that we must adapt this existing legacy application to fit the new requirements. This is what the adapter pattern does; it allows an application to use an existing legacy application by converting or establishing an interface into one that fits the new context. An important point to remember is that the existing legacy application isn't significantly modified, but an adapter application that has the right interface uses it or extends it to provide the necessary functionality.
An example from Oreilly.com in their Safari Books Online Section;
A good example of an adapter is the toilet seat adapter used by toddlers that fits on top of a traditional toilet seat. Let's take a look at the context. We have a legacy object that is the toilet seat, whose basic design hasn't changed in years. It functions well for its original adult users (probably why the design hasn't changed). Let's look at the new context in Figure 5-1. We now need to adapt it for use by a toddler. The problem is obvious - incompatible interfaces! The legacy toilet seat was designed to fit an adult's bottom, and we now need to convert that interface to fit a toddler's smaller bottom. We need an adapter that presents the correct interface to fit the current context. The toilet seat adapter was built to do precisely that. We get to use an existing object whose interface has been converted to one that the client expects.
I have used the Adapter Design Pattern to integrate a legacy applicaiton into an enterpise production system installed on over 4000 desktops. The adaptor, written in C#/.NET established a modern set of services for the enterprise production system to
interface with, the legacy application written in VB6 had minor modifications to fulfill some of the service requests but for the most part remain unchanged.
Another interesting tool to add to you toolbox follows the adaptor design pattern and comes from a company named OpenSpan. From the OpenSpan website their technology offers to:
Integrate - rapidly integrate applications
Automate - automate business processes
Extend - add new functionality to applicaitons
Build - Create composite applicaitions
Well that seems to sum up what we need to do in a nutshell, so how does it work.
OpenSpan takes a unique approach to integration. By leveraging the communications between applications and the operating system, OpenSpan is able to integrate with an application’s objects and events directly. The event-driven architecture minimizes any impact on application performance and is relatively non-invasive, meaning there are few if any changes to the application required. Interacting with an application’s objects and events at the operating system level means that OpenSpan can integrate “closed” applications without an available API, adapter, or web service.
Maybe there is hope.