Practice 11. Leverage Legacy Systems


Bruce MacIsaac

Maximize the value you get from legacy systems by ensuring that short-term changes are part of a longer-term plan.

Problem

Software 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.

Background

Systems 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.

As software is extended, it can become increasingly difficult to understand and change.


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.

[23] Ulrich 2002.

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]

[24] These categories, and inspiration for much of this practice, come from Kruchten 2001 "Using the RUP to Evolve a Legacy System."

  • Maintenance fixing bugs and incrementally adding small amounts of functionality or features over time. This approach frequently leads to a system that is increasingly difficult to understand and modify.

  • Adaptation adding new capabilities by integrating the existing system with other systems. This approach includes cosmetic makeovers, whereby the underlying functionality remains relatively unchanged but is wrapped in a new user interface or system interface, and migration, whereby the system is adapted to a new platform.

  • Redevelopment building a system with the same or similar functionality to replace an existing system. The existing system defines key requirements for the new system. Some software from the existing system may be reused.

  • Combination combinations of the above.

Applying the Practice

Legacy 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 testing.

  • Define a vision.

  • Evaluate the business case.

  • Balance stakeholder needs against the impact to the asset.

  • Reuse requirements.

  • Reuse architecture, design, and implementation.

  • Reuse other artifacts (test, user documentation, and so on).

  • Apply modern practices.

  • Take an enterprise perspective.

Improve the Code Gradually with Refactoring and Unit Testing

In 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]

[25] Lehman 2000.

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]

[26] Kerievsky (2005) has a good description of this "design debt" metaphor, originally attributed to Ward Cunningham.

Short-term fixes are like credit card debt: if ignored, they add up and cost more in the long term.


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.

Use unit testing and refactoring to improve code with each change.


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 Vision

When 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:

  • Why is a change needed?

  • Who are the changes for?

  • What is the value of the existing system?

  • Is its basic design still usable? Can requirements be reused (does it still do what users need)? Are there algorithms, business rules, or data that can be reused? Does the design encapsulate functionality that would serve as a good component in an enterprise architecture?

  • What kind of evolution is needed (maintenance, adaptation, redevelopment, or combination)?

Evaluate the Business Case

Legacy 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:

  • Is the aging system causing inefficiencies that should be addressed?

  • Does the aging system carry administrative or other hidden costs?

  • Can the system be maintained over the long term, or are there growing concerns over quality or lost knowledge due to staff attrition?

  • Are there other risks associated with "business as usual"? For example, are key business needs being shelved, or are opportunities being lost because they are not supportable with the existing system?

Balance Stakeholder Needs Against the Impact to the Asset

As 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.

Business decisions should balance value to stakeholders against both short-term and long-term costs.


Reuse Requirements

Establishing 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.

Take advantage of what exists already, and reference it rather than recreate it.


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 Implementation

It 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]

[27] Krafzig 2005 provides guidance on transforming legacy applications into services.

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.

[28] Ambler 2002 discusses this in terms of "contract models."

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.

Balance the need for documentation against the effort to create it.


Here are some suggestions for documentation:

  • Ensure that there is a description of the major components, their interfaces, and how they interact to provide externally visible behavior. Identify and document redundancies and assumptions across components, both in terms of functions and data. Creating a "Software Architecture Document" is a good way to do this.

  • Don't document the entire detailed design. Instead, document detailed design as needed to understand which pieces of the software will be evolved and how those pieces need to change. Tools to reverse engineer "design information" may be helpful, but they require an experienced developer to pick out the important elements and make sense of them.

  • Identify the data sources, and determine what data will need to be migrated.

Reuse Other Artifacts

Follow 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 Practices

So 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.

Improve processes and tools incrementally.


Take an Enterprise Perspective

Legacy 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 Methods

Most 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.

[29] Feathers 2005.

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.

[30] Ambler 2002.

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 Adoption

Legacy 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.

  • Basic. For systems remaining in maintenance, minimize degradation by ensuring that fixes are done cleanly and correctly.

    For systems undergoing adaptation or redevelopment, reuse what you can, document just what you need, and apply newer methods sparingly.

    The basic practices of clean fixes and minimal overheads keep the process light.

  • Intermediate. Create a vision and business case for legacy systems to ensure that they get adequate attention and funding. Adopt an incremental improvement approach that includes automated testing and refactoring.

    Incremental improvement requires relatively little ceremony to be effective, but does require commitment from the team and management.

    Establishing a vision and a business case are additional investments that require more management.

  • Advanced. Develop an understanding of how the overall business functions, and create a long-term business vision. Define a strategy for evolving the enterprise architecture to meet that vision, and coordinate across projects to make it happen.

    Establishing enterprise-level processes are additional investments that require more management.

Related Practices

  • Practice 8: Understand the Domain shows how an overall strategy for evolving legacy systems requires a good understanding of business needs.

  • Practice 16: Architect with Components and Services explains how legacy applications are frequently wrapped as a service in an overall enterprise architecture.

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 Information

Information in the Unified Process

OpenUP/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 Reading

Legacy evolution concepts and approaches:

William M. Ulrich. Legacy Systems Transformation Strategies. Prentice Hall, 2002.

Robert S. Arnold. Software Reengineering (IEEE Computer Society Press Tutorial). Institute of Electrical & Electronics Engineers, 1993.

Improving legacy code through refactoring:

Michael C. Feathers. Working Effectively with Legacy Code. Prentice Hall, 2005.

Joshua Kerievsky. Refactoring to Patterns. Addison-Wesley, 2005.

Integrating with existing legacy systems and data:

Dirk Krafzig, Karl Banke, and Dirk Slama. Enterprise SOA Service Oriented Architecture Best Practices. Prentice Hall, 2005.

Gregor Hohpe and Bobby Woolf. Enterprise Integration Patterns. Addison-Wesley 2004.

Scott Ambler. Agile Database Techniques: Effective Strategies for the Agile Software Developer. John Wiley & Sons, 2003.



Agility and Discipline Made Easy(c) Practices from OpenUP and RUP
Agility and Discipline Made Easy: Practices from OpenUP and RUP
ISBN: 0321321308
EAN: 2147483647
Year: 2006
Pages: 98

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net