skip to main content
Skip header Section
Working Effectively with Legacy CodeSeptember 2004
Publisher:
  • Prentice Hall PTR
  • Upper Saddle River, NJ
  • United States
ISBN:978-0-13-117705-5
Published:01 September 2004
Skip Bibliometrics Section
Bibliometrics
Skip Abstract Section
Abstract

Get more out of your legacy systems: more performance, functionality, reliability, and manageabilityIs your code easy to change? Can you get nearly instantaneous feedback when you do change it? Do you understand it? If the answer to any of these questions is no, you have legacy code, and it is draining time and money away from your development efforts.In this book, Michael Feathers offers start-to-finish strategies for working more effectively with large, untested legacy code bases. This book draws on material Michael created for his renowned Object Mentor seminars: techniques Michael has used in mentoring to help hundreds of developers, technical managers, and testers bring their legacy systems under control.The topics covered include Understanding the mechanics of software change: adding features, fixing bugs, improving design, optimizing performance Getting legacy code into a test harness Writing tests that protect you against introducing new problems Techniques that can be used with any language or platform-with examples in Java, C++, C, and C# Accurately identifying where code changes need to be made Coping with legacy systems that aren't object-oriented Handling applications that don't seem to have any structureThis book also includes a catalog of twenty-four dependency-breaking techniques that help you work with program elements in isolation and make safer changes.© Copyright Pearson Education. All rights reserved.

Cited By

  1. Gazzinelli Cruz V, Rocha H and Valente M (2023). Snapshot testing in practice, Journal of Systems and Software, 204:C, Online publication date: 1-Oct-2023.
  2. Reich P and Maalej W Testability Refactoring in Pull Requests: Patterns and Trends Proceedings of the 45th International Conference on Software Engineering, (1508-1519)
  3. Guo P and Cooper R (2023). Six Opportunities for Scientists and Engineers to Learn Programming Using AI Tools Such as ChatGPT, Computing in Science and Engineering, 25:3, (73-78), Online publication date: 1-May-2023.
  4. ACM
    Mejstrik T and Hollomey C Injection testing backed refactoring Proceedings of the 27th European Conference on Pattern Languages of Programs, (1-7)
  5. Rebai S, Alizadeh V, Kessentini M, Fehri H and Kazman R (2022). Enabling Decision and Objective Space Exploration for Interactive Multi-Objective Refactoring, IEEE Transactions on Software Engineering, 48:5, (1560-1578), Online publication date: 1-May-2022.
  6. Silva L, Castro R and Guimaraes M Supporting real demands in software engineering with a four steps project-based learning approach Proceedings of the 43rd International Conference on Software Engineering: Joint Track on Software Engineering Education and Training, (50-59)
  7. Mosseri I, Alon L, Harel R and Oren G ComPar: Optimized Multi-compiler for Automatic OpenMP S2S Parallelization OpenMP: Portable Multi-Level Parallelism on Modern Systems, (247-262)
  8. ACM
    Megas I, Zarras A and Karelis C How to Test the Extract Method Refactoring Proceedings of the European Conference on Pattern Languages of Programs 2020, (1-7)
  9. Gergely T, Balogh G, Horváth F, Vancsics B, Beszédes Á and Gyimóthy T (2019). Differences between a static and a dynamic test-to-code traceability recovery method, Software Quality Journal, 27:2, (797-822), Online publication date: 1-Jun-2019.
  10. Wiese E, Rafferty A and Fox A Linking code readability, structure, and comprehension among novices Proceedings of the 41st International Conference on Software Engineering: Software Engineering Education and Training, (84-94)
  11. Fairbanks G (2019). Ignore, Refactor, or Rewrite, IEEE Software, 36:2, (133-136), Online publication date: 1-Mar-2019.
  12. Nanthaamornphong A, Leatongkam A and Mateos C (2019). Extended ForUML for Automatic Generation of UML Sequence Diagrams from Object-Oriented Fortran, Scientific Programming, 2019, Online publication date: 1-Jan-2019.
  13. ACM
    Zaytsev V An industrial case study in compiler testing (tool demo) Proceedings of the 11th ACM SIGPLAN International Conference on Software Language Engineering, (97-102)
  14. ACM
    Li F, Fröhlich J, Schall D, Lachenmayr M, Stückjürgen C, Meixner S and Buschmann F Microservice Patterns for the Life Cycle of Industrial Edge Software Proceedings of the 23rd European Conference on Pattern Languages of Programs, (1-11)
  15. Chatley R and Field T Lean learning Proceedings of the 39th International Conference on Software Engineering: Software Engineering and Education Track, (117-126)
  16. Hovy C and Kunkel J Towards automatic and flexible unit test generation for legacy HPC code Proceedings of the Fourth International Workshop on Software Engineering for HPC in Computational Science and Engineering, (42-49)
  17. ACM
    Lin Y, Peng X, Cai Y, Dig D, Zheng D and Zhao W Interactive and guided architectural refactoring with search-based recommendation Proceedings of the 2016 24th ACM SIGSOFT International Symposium on Foundations of Software Engineering, (535-546)
  18. ACM
    Steinhauser A and Gauthier F JSPChecker Proceedings of the 2016 ACM Workshop on Programming Languages and Analysis for Security, (57-68)
  19. ACM
    Bernstein M Storyspace 3 Proceedings of the 27th ACM Conference on Hypertext and Social Media, (201-206)
  20. ACM
    Romano S, Fucci D, Scanniello G, Turhan B and Juristo N Results from an Ethnographically-informed Study in the Context of Test Driven Development Proceedings of the 20th International Conference on Evaluation and Assessment in Software Engineering, (1-10)
  21. Yao Z, Jia Y, Wang D, Steed C and Atchley S (2016). In Situ Data Infrastructure for Scientific Unit Testing Platform1, Procedia Computer Science, 80:C, (587-598), Online publication date: 1-Jun-2016.
  22. ACM
    Clune T, Finkel H and Rilee M Testing and debugging exascale applications by mocking MPI Proceedings of the 3rd International Workshop on Software Engineering for High Performance Computing in Computational Science and Engineering, (5-8)
  23. ACM
    Terrell J KinEdit: a tool to help developers refactor manually Companion Proceedings of the 2015 ACM SIGPLAN International Conference on Systems, Programming, Languages and Applications: Software for Humanity, (71-72)
  24. Rilee M and Clune T Towards test driven development for computational science with pFUnit Proceedings of the 2nd International workshop on Software Engineering for High Performance Computing in Computational Science and Engineering, (20-27)
  25. ACM
    Rajlich V Software evolution and maintenance Future of Software Engineering Proceedings, (133-144)
  26. ACM
    Choudhari J and Suman U (2014). Extended iterative maintenance life cycle using eXtreme programming, ACM SIGSOFT Software Engineering Notes, 39:1, (1-12), Online publication date: 11-Feb-2014.
  27. Simic B and Walden J Eliminating SQL injection and cross site scripting using aspect oriented programming Proceedings of the 5th international conference on Engineering Secure Software and Systems, (213-228)
  28. Winkler D, Schmidt M, Ramler R and Biffl S Improving unfamiliar code with unit tests Proceedings of the 13th international conference on Product-Focused Software Process Improvement, (290-304)
  29. Rüegg M and Sommerlad P Refactoring towards seams in C++ Proceedings of the 7th International Workshop on Automation of Software Test, (117-123)
  30. ACM
    Fox A and Patterson D (2012). Crossing the software education chasm, Communications of the ACM, 55:5, (44-49), Online publication date: 1-May-2012.
  31. Otani Y, Hashiura H and Seiichi K A software testing tool with the facilities to restore the state at program execution of a program under test Proceedings of the 11th WSEAS international conference on Applied informatics and communications, and Proceedings of the 4th WSEAS International conference on Biomedical electronics and biomedical informatics, and Proceedings of the international conference on Computational engineering in systems applications, (154-159)
  32. de Halleux J and Tillmann N Moles Proceedings of the 48th international conference on Objects, models, components, patterns, (253-270)
  33. ACM
    Strasser A, Mayr H and Naderhirn T Harmonizing the test support for object-oriented legacy systems using state-of-the-art test tools Proceedings of the 1st Workshop on Testing Object-Oriented Systems, (1-7)
  34. ACM
    Kropp M and Schwaiger W Reverse generation and refactoring of fit acceptance tests for legacy code Proceedings of the 24th ACM SIGPLAN conference companion on Object oriented programming systems languages and applications, (659-664)
  35. Bartlett R Integration strategies for Computational Science & Engineering software Proceedings of the 2009 ICSE Workshop on Software Engineering for Computational Science and Engineering, (35-42)
  36. ACM
    Zlotnick A and Raz O Hardware-less testing for RAS software Proceedings of SYSTOR 2009: The Israeli Experimental Systems Conference, (1-11)
  37. ACM
    Bohnet J, Voigt S and Döllner J Projecting code changes onto execution traces to support localization of recently introduced bugs Proceedings of the 2009 ACM symposium on Applied Computing, (438-442)
  38. Van Rompaey B, Du Bois B, Demeyer S and Rieger M (2007). On The Detection of Test Smells, IEEE Transactions on Software Engineering, 33:12, (800-817), Online publication date: 1-Dec-2007.
  39. ACM
    Evans R and Savoia A Differential testing Proceedings of the the 6th joint meeting of the European software engineering conference and the ACM SIGSOFT symposium on The foundations of software engineering, (549-552)
  40. ACM
    Monteiro M and Aguiar A Patterns for refactoring to aspects Proceedings of the 14th Conference on Pattern Languages of Programs, (1-10)
  41. ACM
    Evans R and Savoia A Differential testing The 6th Joint Meeting on European software engineering conference and the ACM SIGSOFT symposium on the foundations of software engineering: companion papers, (549-552)
  42. ACM
    Saff D, Artzi S, Perkins J and Ernst M Automatic test factoring for java Proceedings of the 20th IEEE/ACM International Conference on Automated Software Engineering, (114-123)
  43. Smith M, Kwan A, Martin A and Miller J E-TDD – embedded test driven development a tool for hardware-software co-design projects Proceedings of the 6th international conference on Extreme Programming and Agile Processes in Software Engineering, (145-153)
  44. ACM
    Overbey J, Xanthos S, Johnson R and Foote B Refactorings for Fortran and high-performance computing Proceedings of the second international workshop on Software engineering for high performance computing system applications, (37-39)
Contributors

Recommendations

Reviews

Fernando Berzal

For most people, legacy code is "difficult-to-change code that we do not understand." In practice, Feathers translates this definition into "code without tests." If we had automated unit tests for the code we have to modify, we could change the code quickly and verifiably. Even if we might not understand it completely, test failures would immediately warn us about potential problems in our modifications. The problem is that it is not easy to get tests in place. Even though good designs should always be testable, many actual designs are so convoluted that system dependencies make individual components almost impossible to test separately. Fortunately, Feathers shows us a handful of useful techniques that can help our maintenance nightmares disappear. As Robert C. Martin states in the foreword, this book is about "taking a tangled, opaque, convoluted system and . . . turning it into a simple, nicely structured, well-designed system" in as safe a way as possible. The mechanics of change in software systems are presented in five introductory chapters. Since change is always risky, preserving existing behavior is key in software maintenance. Hence, refactoring is a fundamental skill for the maintenance of software systems. However, the old-style "edit and pray" modification of source code is clearly insufficient. Feathers advocates for "cover and modify," an incarnation of test-driven development for software maintenance. Here, unit testing frameworks like xUnit, the application of dependency-breaking techniques, and the use of fake objects are a must. The core of this book deals with practical techniques for changing software. Nineteen chapters describe typical problems software maintainers must face, using an original frequently asked questions (FAQ) format. This format makes chapter titles lengthy and rather whimsical, but they also convert this book into a useful guide to have at hand when dealing with legacy code. You will discover techniques for changing the way that code is organized to make builds (and, hence, testing) easier. You will also learn how to add features to code being tested using test-driven development and programming by difference. Plenty of examples, mostly in Java and C++, will show you how to get tests into place. Easy to understand effect-analysis techniques, such as effect and feature sketches, will lead you to the places where you should test the behavior of your legacy code, also known as "pinch points." Simple techniques will help you uncover and communicate the architecture of large systems. But, most of all, all of the practical advice in this book is given without forgetting the rationale behind your decisions, such as when to sacrifice object encapsulation in order to improve unit testability and, more importantly, your understanding. The sections in this book are heavily cross-referenced for easier navigation, and a catalog of dependency-breaking techniques completes this landmark volume. This catalog includes 24 conservative refactorings to make a system testable, and it is completed by a final appendix where the classical "extract method" refactoring is explained. Most development environments will probably include support for such refactorings in the future, although, in the meantime, this catalog constitutes a valuable resource for those who want to untangle the dependencies that make a piece of code hard to understand, test, and modify. In summary, this is an outstanding book that fills an important gap in the existing literature. Since not all development projects are "green field" projects, and software maintenance eats up a considerable amount of software development resources, an applied book such as this was clearly needed. It is an excellent complement to existing books on testing, refactoring, and design patterns, even though only a few references to other books are provided in this book. If you are a software maintainer, this well-written book might make your job more productive and enjoyable. Even if you are not, you should not discard this "legacy code" title too quickly. As a developer, when you follow any iterative development process, most of the time you are maintaining (and hopefully enhancing) an existing system. Online Computing Reviews Service

Diomidis Spinellis

In the 1980s, Lehman and Belady observed that programs used in real-world environments must continuously evolve to remain useful, and, more importantly, as evolving programs change, their structure becomes more complex unless active efforts are made to avoid this decay. Valuable code that one generation of developers passes on to the next is often termed legacy code, and, more often than not, this code through its evolution suffers from a complex structure, outdated development methods, and useless documentation. Feathers' book tackles the problem head on, providing valuable practical advice for bringing such code under control. Feathers idiosyncratically defines legacy code as code that is lacking tests, and by tests he typically means unit tests that developers can swiftly execute after a change to ensure that they didn't break anything. While this definition does provide a clarity of purpose, it steers the subject's treatment toward directions that are neither widely accepted, nor universally practical. For example, a large part of the book discusses object-oriented programs and techniques, whereas legacy code is much more likely to be in procedural languages like Cobol, Fortran, and C. Furthermore, although many modern development approaches espousing unit testing have demonstrated encouraging results, we currently lack empirical evidence regarding the risks and practicality of retrofitting unit tests onto legacy code. The book is divided into three parts: an introductory part discussing the high-level mechanics of program change, a sequence of chapters offering answers to commonly encountered maintenance challenges (for example, "How Do I Add A Feature" or "I Need to Make a Change, but I Don't Know What Tests to Write"), and a final part detailing 24 different refactorings that developers can use for breaking dependencies. All material is extensively cross-referenced, but the organization of the book's chapters could be improved by adopting a more consistent narrative structure. The book ends with a small glossary and an index, but lacks a separate bibliography or list of references. The technical advice contained in the book is excellent. Feathers obviously has a deep understanding of object-oriented design and development techniques, and a rich experience in applying them in practice. The advice spans both design and coding techniques, and, where appropriate, references how changes can be automated by using refactoring tools. The writing is clear, although, unsurprisingly, many examples of legacy code require significant effort to comprehend. Clear unified modeling language (UML) diagrams are extensively used to illustrate the code's structure. In summary, Feathers is to be commended for addressing the vitally important subject of legacy code maintenance. As object-oriented code ages, this book will become increasingly relevant to the practitioners working with it. Online Computing Reviews Service

Access critical reviews of Computing literature here

Become a reviewer for Computing Reviews.