This is an HTML rendering of a working paper draft that led to a publication. The publication should always be cited in preference to this draft using the following reference:
Michael C. Feathers
Working Effectively with Legacy Code
Prentice Hall, Upper Saddle River, NJ. 2005
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 inherits 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's book "Working Effectively with Legacy Code" tackles the problem head on, providing valuable practical advice for bringing such code under control.Feathers idiosyncratically defines as legacy code, 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 deals with object-oriented programs and techniques, whereas most legacy code is 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, however 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 supposedly legacy code require significant effort to comprehend. Clear 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 and becomes legacy the book will become increasingly relevant to the practitioners working with it.