Amazon.com Customer Reviews
Dreary Title, Very Important Book - Review written on December 21, 2006
Rating: 5 out of 5
5 customers found this review helpful, 1 did not.
Michael Feathers has written a book that is at once very focused on the legacy code problem, but also far more important than what the scary title would indicate.
Feathers went where few software developers would dare to tread. Often hired by organizations to "make us agile" or "make us eXtreme" [sic], he found that the teams had already inherited (or built) a lot of code that needed to be wrangled into a test harness before the team could even consider driving forward with Test-Driven Development. And more often than not, the code was written in C++. Poor Michael. (Oh, wait, he got a book deal out of it! ;-)
So, the examples he uses in the book are rather real-world (with the client's proprietary stuff stripped, disguised, or entirely rewritten, of course). The interesting thing I noted while reading the examples was: "Hey, this code doesn't look bad! It's a lot like what I would have written prior to my indoctrination into test-first programming." Feathers emphasizes that even well-written code can suffer from the most dangerous of ailments: This code is difficult to test.
I appreciate his clear, no-nonsense, line-in-the-sand (or stake-in-the-ground, depending on your choice of metaphors) definitions of "legacy code" and "unit test." If you've met Michael Feathers, you already know that he's not trying to start an argument or cause controversy. Quite the opposite, in fact: He's giving concise definitions of the phrases he uses everywhere in the book, so that you can easily tell whether something fits within, or is outside, the definition. There is no wiggle room.
Aside: Do my projects end up with unit tests that Michael would not define as "unit tests"? Yes. Invariably, my teams have a very small percentage of unit tests (less than 1%) that indeed fail the Feathers definition. I'm okay with that. Better to have a few slow-and-ugly unit tests than to have untested behavior.
Feathers starts later chapters with statements of common problems. In fact, the problem is the chapter title (e.g., "Dependencies on Libraries are Killing Me"). The author then describes the problem, provides examples, gives a general solution or two, and points you to detailed solutions in the catalog toward the back of the book.
This catalog is a catalog of refactorings for many legacy-specific code smells (put another way, "cures" for various "ailments"), all with the goal of getting the code under test, so that it can be further enhanced without fear. I tried to read the book cover-to-cover, but the catalog started to intrigue me early on, and I think I finally read the whole book, but certainly not in any particular order.
No book is perfect, of course. The only thing I could quibble with Feathers about is that his catalog--which, like others of this type, gives memorable names to the refactorings--occasionally renames common refactorings, or implies the use of a particular design pattern where it isn't always appropriate or necessary.
E.g., "Adapt Parameter" is a safe, powerful refactoring, but the name might lead you to believe that you need to wrap the offending parameter in a "Gang of Four" (GoF) Adapter Pattern, when in fact you may want a GoF Proxy (fewer changes to the code you're trying to get under test). Even that may be misleading, though, because someone may mistakenly interpret Proxy to mean that the parameter has to retain its original type (or an Interface, at the very least). Not true (think Smalltalk or Ruby). An object can easily have an identical interface (i.e., set of public method signatures) without being of the same type. If it swims like a duck, and quacks like a duck...
I think "Wrap Parameter" would have been a better name for that refactoring. Proxies, Adapters, Facades...they're all different in important ways, but they're all wrappers (aka "Yet Another Layer of Abstraction"). And our industry needs to be able to embrace such vague terms in order to allow for creative solutions.
I don't think Feathers intended to imply Adapter (and my argument is really picking nits, after all, and perhaps my attempt to look smart), but I would ask the reader to absorb the *intent* rather than the *letter* of this (or any) catalog of solutions.
And the intent of this book is an important one. In fact, Feathers brings together quite a few pragmatic areas of today's world of software development: Test-Driven Development and refactoring, mock objects, design patterns, agile programming practices. This book gives developers the techniques required to lock quality in while allowing the product to mature.
Because of that, I'd say this is one of the most important software development books written at least since the GoF Design Patterns book. Unlike the Gang of Four book, though, this one is easy to read and comprehend! ;)
Valuable, practical advice. - Review written on October 10, 2006
Rating: 5 out of 5
8 customers found this review helpful, 1 did not.
A more accurate title for this book would have been "Unit Testing and Refactoring is How To Work Effectively with Legacy Code"
The book operates entirely from that premise, and only spends a little time trying to sell you on the concept. If you're still struggling with that debate, this book may rub you the wrong way. However, I found it to be intensely practical and found the author repeatedly speaking about scenarios that I run into quite often.
I would describe myself as someone who finds the whole fascination with design patterns a little optimistic and certainly hype-laden. Useful to be familiar with, but nothing to preach about. Feathers' treatment of patterns is exactly what they should be, however -- from the trenches, and goal-oriented. He describes the patterns he uses with the very immediate goal of how they will help you "get code under test" as he would put it, and slowly unwind the spaghetti we all run into.
Although the world didn't need yet another book about unit testing, refactoring, and patterns, there are few books that offer practical advice for the ugly side of software development. To get the most out of this book, you'll need to have slightly better than intermediate experience with object-oriented development, but this is not a hypothetical-scenario type book targeted at gurus. And most importantly, it acknowledges and embraces the fact that we have all inherited reams of code that has either abused and ignored OO principles, or wasn't even OO to begin with. It is quite readable and accessible, and doesn't overwhelm. I found a lot of valuable techniques in here that I have not found anywhere else. I highly recommend this book, and I'm going to be shoving it down my coworkers' throats. :)
In a class by itself - Review written on June 25, 2005
Rating: 5 out of 5
11 customers found this review helpful.
I've never read a technical book quite like this one. The author has distilled several years worth of experience into one highly cohesive and detailed book.
If you think you don't need this book because you aren't working on legacy code, think again. I recently joined a project that had only been going for a few months, and this book has been very valuable to me. This book helps recognize that many projects start off on the right foot and soon the code base turns into a quagmire. The advice here will help you turn it around, but it will not be easy even with this book.
Not only does it give solid advice for dealing with specific situations, it is written in a lively style that contrasts with the sometimes depressing work associated with maintenance.
The book is organized in a FAQ style, with chapters titled as questions. This makes it very easy to pickup and narrow down what part is relevant to your current situation. The final part includes a list of dependency breaking techniques designed to help you get code under test.
Fixing legacy code and preventing new code from going bad - Review written on March 21, 2005
Rating: 5 out of 5
12 customers found this review helpful, 1 did not.
Almost every code base I've worked in previously, whether fifteen years old or just a few weeks, fell into the category of legacy code for exactly the reason mentioned in this book. There were no unit-level tests. Without the presence of tests, design had gone to pieces once the architects were no longer looking (if any architects had ever existed!). Without tests' presence, the code had no need to be easily isolated and changed - so it wasn't. This book provides a set of steps to clean up that sort of legacy code in as safe a way as possible, always moving towards a system that is a joy and productive to code in.
Additionally, this book codifies good design patterns and ways to think about new code during code reviews. Even "green field" developments can benefit from reading this book and ensuring that early-on work is being done to ensure that it never starts the downward legacy spiral. I would encourage developers starting from scratch to ensure they're following the patterns in this book any time that a bad piece of code sneaks in to keep it from spreading - we have on my team, and it's done wonders for our quality level, productivity, and morale.
The only thing I'd like to have seen improved is that there's a an assumption that the code is all in one place - there are no external, uncontrollable sources who build on this code, there are no versioning requirements (i.e. API stability for N years), etc. While I'm sure the majority of this book's audience doesn't have to deal with it, for those who do, noting what constitutes a breaking change and what doesn't (particularly to ensure that each chapter has at least one that isn't) would've really helped for those of us who have to build platforms.
Simply supurb, a tour de force - Review written on January 04, 2005
Rating: 5 out of 5
21 customers found this review helpful, 4 did not.
The vast majority of software texts are about greenfield development: creating a new application from nothing at all. But the vast majority of software work involves existing code: adding features, finding bugs, or refactoring code that someone else wrote. This book is a supurb first step in fixing that imbalance.
This is not a theoretical work. For example, Feathers does not present a grand ontology of legacy systems. Not is this book heavy on methodology. Instead Feathers describes techniques --- lots of techniques, roughly 50 --- for working with legacy code.
The techniques are organized by the problems they address. For example, one chapter is entitled "My Application Is All API Calls" and then presents two techniques for working with existing code that is full of calls to APIs.
Feathers comes from the XP school, and he adopts that school's premise that you can safely change code if (and only if) there are sufficient tests in place to tell you when your new change has introduced a bug. Many of the techniques in this book are about different ways of putting legacy code into test harnesses, and doing that safely without introducing bugs along the way.
He also introduces new concepts, or at least concepts I had never seen before. For example, a "characterization test" is a test that characterizes the actual behavior of the code, not what the code is supposed to do, but what it actually does. Why is that useful? First, it is an effective way of documenting what a system actually does, particularly if --- as with most legacy code --- there is no other documentation. And it can tell you when you have changed that existing behavior.
Feathers' style is easy to read. As you would expect there is lots of example code here, and it is folded naturally into the explanatory text. My only gripe with the book is with the examples: they are largely in Java, C++, or C. I would have liked some examples in older legacy-only languages, like COBOL.
But overall, this book is simply supurb, a tour de force of an important topic that has received little coverage before.
excellent reference and guide - Review written on November 11, 2004
Rating: 5 out of 5
14 customers found this review helpful.
"Working Effectively with Legacy Code" is a very valuable resource. The author defines "legacy code" as "code without tests." It doesn't matter whether the code was written last week or ten years ago. There is more emphasis on old code that nobody understands, mainly because it is messier and harder to work with.
The examples in the book are mainly in C, C++ and Java, but there are a couple in C# and Ruby. While it is essential to know one of these languages, the author provides enough information to understand the others. When a technique only applies to a certain language, it is clearly indicated.
The author shows how different diagrams can help you learn how to understand code. In addition to UML, there are dependency and effect sketches. The author uses these to show how to think about understanding and refactoring. Other tools, such as refactoring browsers and mocks are explained.
Speaking of refactoring, there are "dependency breaking techniques" (aka refactorings) with step-by-step instructions (Martin Fowler style) throughout the book. There are also explanations of why patterns and design rules exist. Most importantly, there are lots and lots of cross-references and an excellent index.
Working with legacy code isn't fun, but this book helps make it as painless as possible. With the split emphasis between psychological/understanding/techniques and refactoring, this book is both a great read and an excellent reference.
A book for coders in the real world - Review written on November 07, 2004
Rating: 5 out of 5
6 customers found this review helpful, 1 did not.
As a software coach, Michael has had to figure out how to work with a great many, um, suboptimal code bases -- and here's his experience all nicely packaged up for the rest of us. There are some neat techniques, such as the diagrams he sketches to help him understand disorganised classes, and a good catalog of patterns for somehow coping with that Big Ball of Mud you've just inherited. As with the best patterns, they all look familiar, but now they have names and you have a very respectable book to help you justify actually doing something about your situation. He also covers C++, which very few of the Agile community are prepared to deal with. One minor quibble is that there are no references -- but maybe that's what Amazon is for nowadays.
Don't maintain code without it! - Review written on October 25, 2004
Rating: 5 out of 5
6 customers found this review helpful.
(Copy of a review found at http://www.masterprogrammer.info)
The Point
If you need to fix, maintain or extend code without tests, and you're trying to do it without having a copy of this book by your side, you might as well just jab yourself in the stomach with a knife. Michael Feathers does for legacy code what the Gang of Four did for design: establish a vocabulary to make it easier to understand and communicate about how to work with legacy code.
The Details
There are some books that we use as references when we teach courses or do some consulting. These are usually the fundamental texts that provide an appropriate foundation for the reader in a given topic area: Java programming, programmer testing, enterprise architecture, what have you. Even before the book had been released, we were recommending it in courses on Test-Driven Development. The reason is clear: true greenfield projects are few and far between. A team trying to adopt Test-Driven Development has enough to learn; we can't leave them to their devices to figure out what they're going to do starting Monday. "How do we deal with the 250,000 lines of untested code we still need to use?" A Master Programmer cannot leave a team like that hanging.
Enter Michael Feathers and his years of experience rescuing legacy codebases. He has distilled his secrets into a collection of fundamental techniques combined with a catalog of strategies and approaches. In many ways, WELC is like other "how to" books: it starts with setting the context for the techniques, describes them with examples, then adds a catalog of ways to break dependencies in the back. One of the things that differentiates this book is the chapter titles. They are whimsical and, as a result, effective. Some examples follow.
* It Takes Forever to Make a Change
* I Need to Make Many Changes in One Area
* My Application Has No Structure
* This Class Is Too Big and I Don't Want It to Get Any Bigger
We have struggled with maintaining existing code, and in the process have had many of the exact complaints that Michael has made into chapter titles. Each chapter is rich with useful examples in Java, C# and C++. You'd be surprised how many requests we get for courses teaching Test-Driven Development in C++, and they have plenty of legacy code to work around! Michael's conversational style gives you the feeling of having him along side while you work, and frankly, you can't get Michael for only USD 45 plus taxes, but the book is a bargain at twice the price.
Post Script
In order to add book reviews to this web site (masterprogrammer.info), we needed to add a feature to a small amount of legacy code in PHP. (We're still learning how to test-drive PHP-based web sites, so we've made the classic mistake of worrying about it later. See how easy it is?) We used techniques like Sprout Method to make safe changes to the code that generates our Self-Study bookshelf while adding the ability to link a book to our review. What more proof do you need? To purchase Michael's excellent book, go back to our Self-Study bookshelf and click on Working Effectively with Legacy Code.
Pragmatically Improving the Past, Present, and Future - Review written on October 23, 2004
Rating: 5 out of 5
7 customers found this review helpful.
This is pragmatic guide to safely dealing with past expediencies, present challenges, and creating a better future in your programs. The catalog of techniques to break dependencies and add unit tests applies whether the legacy code is 10 years old or 10 minutes old. I found many gems I could use immediately in a 15-year old app of over 600K lines of C++.
The basic ideas and perspectives apply on multiple scales. (method, class, component, and system.) I'd bet that developers who acquire these skills to tease apart legacy code would have great odds of writing new code that doesn't quickly turn into legacy code. I'm humbled by the depth of Michael's insight and fluency with these important issues. While it would be ideal to get mentoring by Michael, in your particular code base, this book is a life perserver while waiting for the budget approval. It is a reference I will return to frequently for development, and design & code reviews.
No more fairy-land - Review written on October 20, 2004
Rating: 5 out of 5
12 customers found this review helpful.
The average book on Agile software development describes a fairyland of greenfield projects, with wall-to-wall tests that run after every few edits, and clean & simple source code.
The average software project, in our industry, was written under some aspect of code-and-fix, and without automated unit tests. And we can't just throw this code away; it represents a significant effort debugging and maintaining. It contains many latent requirements decisions. Just as Agile processes are incremental, Agile adoption must be incremental too. No more throwing away code just because it looked at us funny.
Mike begins his book with a very diplomatic definition of "Legacy". I'l skip ahead to the undiplomatic version: Legacy code is code without unit tests.
Before cleaning that code up, and before adding new features and removing bugs, such code must be de-legacified. It needs unit tests.
To add unit tests, you must change the code. To change the code, you need unit tests to show how safe your change was.
The core of the book is a cookbook of recipes to conduct various careful attacks. Each presents a particular problem, and a relatively safe way to migrate the code towards tests.
Code undergoing this migration will begin to experience the benefits of unit tests, and these benefits will incrementally make new tests easier to write. These efforts will make aspects of a legacy codebase easy to change.
It's an unfortunate commentary on the state of our programming industry how much we need this book.
a wealth of practical information and solutions - Review written on October 13, 2004
Rating: 5 out of 5
42 customers found this review helpful, 2 did not.
Martin Fowler's book on Refactoring showed us how to improve the design of our code. We learned to make changes safely, by taking small, rote steps, and by ensuring that we ran our tests after each small change. But what if we're working on the typical ugly system with no tests? In Working Effectively With Legacy Code, Michael Feathers tackles the problem that most of us end up dealing with.
Feathers does an excellent job of articulating the problems and scenarios, using clear examples from C, C++, Java, and C#. Many of the code examples look a lot like real examples I come across all the time--they don't appear to be fabricated.
Working Effectively With Legacy Code contains a catalog that provides a wealth of solutions. The catalog shows how to resolve concerns like, "I'm changing the same code all over the place" and "how do I safely change procedural code?"
The book is highly entertaining and comes across as a conversation with a really sharp, really patient guru developer. Often, it's a chore to slog through code-heavy books. But Feathers manages to keep my attention with interesting stories, loads of examples, and well-written text.
I think that Working Effectively With Legacy Code is an important book. The vast majority of existing code presents the classic catch-22: you can't change it safely because it doesn't have tests, and you can't write tests without changing it to easily support testing. It's not an easy problem, and most people will give you high-level ideas for solving it. Feathers is the first person to dig deep and present a wealth of knowledge and insight on the problem, all in one place. I'll be pulling this book from my shelf for years to come.