Bruce MacIsaac Maximize the value you get from legacy systems by ensuring that short-term changes are part of a longer-term plan. ProblemSoftware evolves to meet changing needs. As it evolves, it often becomes increasingly difficult to understand and changethe original developers leave, the documentation becomes outdated, and the code often degrades as a result of suboptimal changes over time. This practice describes how to balance the needs of stakeholders against the cost of change and how to maximize the value derived from legacy systems. BackgroundSystems often outlive the assumptions of their original designers. The millennium bug is a classic example. Assuming that the software would be replaced by 1999, early designers used two-digit dates; that assumption cost billions to correct. My own grandmother was the victim of a similar false assumption. When my grandfather passed away in the 1970s, she bought a dual headstone, precarved with her name and "19__." At the age of 112, Mary MacIsaac, like many legacy systems, continues to outlive the designer's expectations. As needs change and design assumptions prove false, systems need to evolve. One way to evolve is to patch as you go. The heiress to the Winchester family fortune took this approach when she began extending the family mansion in 1884 and continued building extensions to the house until the day she died in 1922. The result (Figure 4.14) is the oddest patchwork of a house you can imagine: 160 rooms, and a maze of stairs and passages that end up in the strangest places, or go nowhere at all. Figure 4.14. Winchester Mystery House.(Photo courtesy of Winchester Mystery House, San Jose, California.)
As software is extended over time to serve new requirements never anticipated by the original designers, it can take on a similar patchwork style, becoming increasingly difficult to understand and change. But before diving into the details of solving this problem, let's start by defining legacy systems and what it means to evolve them.
What Is a Legacy System?While some have defined a legacy system as "any production-enabled software,"[23] the focus of this practice is mature systems that serve ongoing needs. Usually these are old, monolithic systems, built using older design approaches and older technologies.
What Does It Mean to "Evolve" a Legacy System?Evolving a legacy system means updating it to meet changing needs. Some categories of evolution are listed below:[24]
Applying the PracticeLegacy evolution projects can be run much like any other project, with some important differences. Some of the differences and opportunities unique to legacy evolution projects are discussed in these practice guidelines:
Improve the Code Gradually with Refactoring and Unit TestingIn the 1970s, Marty Lehman captured his "laws of software evolution," among which is the trend for software to decline in quality and increase in complexity unless the tendency is actively prevented. This still holds true today.[25]
The natural inclination when working in a large code base is to avoid areas of code that are not well understood. Changes are hacked into places where they do not belong, because to make the change "correctly" would take longer and might introduce new defects. In the short term this method appears to save time, but over time it creates code that is confusing and even harder to modify further. A great analogy is credit card debt: if you don't pay off your debt monthly, it costs a lot more in the long run.[26]
It is possible to create legacy code that improves, rather than degrades, over time. But doing so requires a team that treats each change as an opportunity to improve the code base and has a willingness to invest in that improvement. A major challenge is avoiding errors when modifying poorly understood code, that is, preserving the behavior that isn't supposed to change. Before changing any legacy code, first ensure that there are sufficient unit tests to preserve the existing behavior. You can then add changes and refactor, and run the unit tests to confirm that there are no unintended changes in behavior.
In many cases it is difficult to add unit tests, because the code is composed of monolithic or highly coupled units. See Feathers 2005 for solutions to these challenges, in particular, guidance on breaking dependencies between units so that they can be independently tested. Define a VisionWhen making significant changes to a legacy system, it is important to have a vision. The advantage of a legacy system is that the original problem it was designed to address is already solved. So the vision needs to address how the problem has changed. Here are some specific questions to consider in relation to legacy evolution:
Evaluate the Business CaseLegacy software is paid for, and it works, so it's often an asset worth keeping. But a good business case must consider both short-term and long-term objectives in order to decide how and if existing systems should evolve or be replaced, and over what period of time. A business case often has to consider more than just direct costs. It must also address the following questions:
Balance Stakeholder Needs Against the Impact to the AssetAs with any project, there are often conflicting priorities. There is often pressure to make quick fixes that do not consider longer-term impact. Creating and documenting a vision and business case help ensure that broader concerns, such as maintainability, are addressed. Evaluation of the business case involves more than just assessing the cost of each individual change. As noted above, individual changes can be individually simple, but over time they can seriously degrade the software. When evaluating the business case, consider the cost of doing the changes "right," not just the cost of the quick fix. If the system was never designed for certain changes, the "right" fix may be prohibitively expensive, forcing you to choose a suboptimal solution. But just because you can make a change to a legacy system doesn't always mean you should. Users can often work around limitations in legacy software, just as we all must do with commercial software. Ultimately, the right choice is a business decision, and you may choose short-term gain over long-term cost. However, before making such a compromise, identify the true impact of doing so, and make a conscious decision that balances the value to stakeholders against both the short-term and long-term costs. See Practice 10: Prioritize Requirements for Implementation for more on dealing with conflicting priorities.
Reuse RequirementsEstablishing a baseline is about capturing the valuable aspects of the existing system. The most valuable aspect of an existing system may be its current functional behavior: business rules, how to handle edge conditions, and so on. It's also valuable to know what existing users dislike about the existing system, so that you know what needs to be changed and how.
Should you apply use cases? When redeveloping a poorly documented system, creating use cases is a good way to capture how the system is currently used and how it should work in the future. However, most systems have user manuals and other supporting documentation that captures the behavior, and there is little value in converting such descriptions into some other format. In such cases it is more efficient simply to identify the use cases and reference the existing documentation for the details. Identifying the use cases is still important, because, rather than listing features, use cases focus on how the system provides value to its users, ensuring that system updates continue to provide real value. Reuse Architecture, Design, and ImplementationIt is becoming increasingly important to integrate legacy applications with other applications to provide greater capabilities and seamless operation, both within a business and between businesses. Services are a key enabler for this integration, because they provide flexibility and minimize coupling. Part or all of a legacy application is often wrapped as a service in an overall service-oriented architecture.[27]
In the simplest case, the legacy system is reasonably isolated. Integration focuses on the externally visible behavior and dependencies,[28] as described in Practice 16: Architect with Components and Services. Integration is much more challenging when there is coupling across systems. Functionality and data are often redundant across systems, or split in a way that creates complex interdependencies. In this case you may need to explore the inner workings of these systems to understand existing limitations and evaluate strategies for improvement.
In either case some understanding of the existing system is needed. As with requirements documentation, it is usually not a good idea to create a whole new set of design documentation. Instead, take advantage of what exists already, and reference it rather than recreate it. Aging documentation is often out of date, so proceed with caution. Balance the need for the documentation against the effort to do the updates.
Here are some suggestions for documentation:
Reuse Other ArtifactsFollow the general principle that all existing system artifacts should be considered an asset and kept if still useful. For example, tests for the existing system are often applicable to the evolved system; user documentation may be a good starting point for documenting the evolved system, and so on. Apply Modern PracticesSo far we have focused on reuse to ensure that existing system assets are used effectively. However, when reuse is not possible, modern practices can be directly applied: new requirements can be described in terms of use cases, and new design can leverage visual modeling and patterns. Practices that apply to running the projectincluding tackling risks, developing iteratively and incrementally, prioritizing requirements, and empowering teamscan always be applied. Also, a general caution: making too many changes at once can lead to failure. Usually, the best approach is to improve processes and tools incrementally, because you get a quicker return on investment and can learn from each increment how to be more effective in the next increment.
Take an Enterprise PerspectiveLegacy systems are usually part of a larger enterprise architecture that serves critical business interests. The problem with legacy systems often lies not in the individual systems but rather in the intertwining of multiple systems, as well as in the variety of groups within the organization that have a stake in each of those systems. Solving these issues requires a high level of understanding of how the business functions, an evolution strategy for the enterprise architecture, and coordination across projects to ensure that all the pieces continue to fit. At this enterprise level, problems are similar to those on a small project but larger in scope. These enterprise process topics are beyond the scope of this book. See the Additional Information section for recommendations on how to explore them in more detail. Other MethodsMost maintenance projects have little in the way of methodology. Defects and small enhancements are scheduled and implemented without consideration of, or investment in, long-term sustainability, resulting in long-term system degradation. The approach of improving legacy code through extensive unit testing and refactoring[29] derives from the test-first design and refactoring principles found in XP.
For more significant changes, Unified Process recommends reusing other artifacts (requirements, architecture, and so on) and capturing essential aspects of the architecture (description of major components, their interfaces, and how they interact). Principles for "agile modeling"[30] can be helpful here.
In terms of deciding what changes to make, Unified Process takes a business-driven approach balancing the needs of stakeholders against short- and long-term costs and objectives. This approach is covered in more detail in Practice 10: Prioritize requirements for Implementation. Levels of AdoptionLegacy evolution projects can be relatively simple or very complex. Moving from basic to more advanced levels of this practice will improve your capacity to handle more complex legacy projects.
Related Practices
Most of the other practices also apply, and how they apply has been the subject of much of this practice. In summary, best practices apply, with the caution that reverse engineering design and requirements artifacts may not be worth the effortreusing what already exists is often more practical. Additional InformationInformation in the Unified ProcessOpenUP/Basic describes basic practices applicable to most small projects. As such, OpenUP/Basic includes practices for creating a vision, business case, and architecture, as well as practices for continuous improvement through refactoring and iterative development. However, legacy evolution is also an enterprise concern, including business planning and defining a strategy for legacy architecture transformation. The Business Modeling and Legacy Evolution plug-ins to RUP partly address these topics, and other enterprise process plug-ins are planned. For specialized guidance on integrating legacy systems into a service-oriented architecture, see the Service-Oriented Architecture plug-in to RUP. Additional ReadingLegacy evolution concepts and approaches:
Improving legacy code through refactoring:
Integrating with existing legacy systems and data:
|