Jump to ratings and reviews
Rate this book

Robert C. Martin Series

Working Effectively with Legacy Code

Rate this book
Get more out of your legacy systems, more performance, functionality, reliability, and manageability.Is 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 structure

This book also includes a catalog of twenty-four dependency-breaking techniques that help you work with program elements in isolation and make safer changes.

464 pages, Paperback

First published September 1, 2004

Loading interface...
Loading interface...

About the author

Michael C. Feathers

7 books267 followers

Ratings & Reviews

What do you think?
Rate this book

Friends & Following

Create a free account to discover what your friends think of this book!

Community Reviews

5 stars
1,820 (41%)
4 stars
1,689 (38%)
3 stars
701 (15%)
2 stars
173 (3%)
1 star
53 (1%)
Displaying 1 - 30 of 269 reviews
Profile Image for Erika RS.
724 reviews204 followers
December 24, 2012
We wise programmers know that code should be written to minimize dependencies, inject those that are necessary, use interfaces, be tested from the start, etc. etc., and we always write our new code like that (right?), but our good habits and wisdom don’t matter when working with legacy code. You don’t have the option of going back in time and smacking some sense into the original author (who may have been your younger, more foolish self).

Given that we have to deal with legacy code, how can it be made easier? The fundamental premise of Working Effectively With Legacy Code is that you cannot work effectively with code unless it is covered by tests. When working with untested code you must be extremely careful, and even then the results are uncertain.

As a result, the bulk of this book is dedicated to techniques for helping get legacy code under test (specifically, fast running unit tests). Many of the techniques used for working with legacy code are well known testing or refactoring patterns, but Feathers puts them in a new light which highlights some of the special difficulties of working with legacy code.

Unlike code that was written with tests in mind, legacy code can be notoriously hard to test. The main enemy of getting tests in place is complex dependencies. To test a piece of code, you may have to create many other configuration objects, and some of those may be impossible/expensive to create in tests (e.g., live network connections). Even worse, you may not even have the hooks to create those objects because they are created deep within the code.
This brings up what Feathers calls “The Legacy Code Dilemma”:
When we change code, we should have tests in place. To put tests in place, we often have to change code.

To get around this dilemma, Feathers introduces the reader to low risk ways to introduce tests into existing code. Because these changes still have some risk, he recommends only adding tests to code you are working on. Don’t just go and start introducing tests everywhere because that requires changing code and may introduce bugs.

Feathers presents a catalog of techniques for introducing tests and breaking dependencies. I won’t go into them in detail (there are a lot). I will note that I am not that fond of the format of the chapters. Feathers names each chapter as if it were a question in a FAQ and then uses the chapter to answer that question. This will probably be great when I am trying to do something and think, “Didn’t that book have something to say about that?”, but it feels kind of hokey when just reading it as a book.

There are some common themes throughout this book. Dependency breaking is one. Another is that sometimes you have to make code more ugly to get it under test. You may have to add partial interfaces or classes that exist only to break the dependencies. Feathers gives two pieces of advice to get over that: it’s better to have tested code than untested code, and often the ugliness is an intermediate stage in unearthing the deeper structure of the code.

This book is not all about testing, however. Feathers does spend some time talking about techniques for understanding legacy code and discovering where to change it. In fact, those were some of my favorite chapters since I find getting started on understanding complex code bases to be rather intimidating. Chapters 11, 12, 16, and 17 discuss techniques for understanding code. Feathers talks about different ways of increasing understanding including sketching effects, taking notes about code, scratch refactoring, and telling the story of the system. I have found the effect sketching to be particularly useful in my day-to-day work.

Overall, this was a valuable read for anyone who has to understand and change large, confusing, delicate code bases (and what code base that has been around awhile isn’t?). The structure of the book made it repetitive at times, but the value of the contents outweighed the occasional repetition.
Profile Image for Bill.
221 reviews75 followers
October 15, 2018
This has become a Legacy Book, unfortunately. The title is also misleading. It doesn't cover technical debt, evolving architecture, replacing a dead library, or other topics you might expect around the subject of long-term legacy projects. Instead, it focuses entirely on making changes safely by small steps, which comes down to adding unit tests.

Having added such tests to a legacy codebase, this is admittedly a tough task, especially to know where to begin, but most of the basics could be covered in a blog post. Now, I'm sure these were revolutionary ideas when this book emerged, but reading it in 2018 felt quite pointless. TDD may not be followed habitually everywhere but I haven't seen a lot of projects in the last ten years without at least some unit tests.

Don't bother with this one. If you're new to unit tests and refactoring, just read a classic on one of those subjects instead.
407 reviews67 followers
July 9, 2012
This book should be considered a required companion book to Martin Fowler's Refactoring. Refactoring is about slowly and progressively turning ugly code into well-designed code. I'd read Refactoring, and tried its techniques, but I just couldn't figure out how to make it work for my purposes. I knew refactoring was based on having a robust suite of regression tests. Let's face it, most ugly code lacks such a suite of tests. If you want to refactor something and you don't have a test, you need to write one. But try writing tests for said ugly code! Good luck, and I'll see you when you've lost all your hair.

That's where this book comes in. This, I think, is what I need to make refactoring work for me. Its title is a little deceiving though. It's not really about working effectively with legacy code--it's about how to make legacy code testable. TDD and refactoring take it from there. Making legacy code testable is really the tough part, and this book does a great job of helping you tame it. It can be a messy process. A lot of this book feels like hacks. But it's way better than the alternative, traditional approach, which this book calls "edit and pray."

Since this book is geared toward traditional languages, C, C++, and Java, much of it is about showing you how to overcome the limitations and barriers of these (inferior, IMO) languages. Most of these stupid problems don't exist in newer languages. But they're still quite good to know. Especially important is some of the concepts this book introduces, such as sensing, separating, seams, interception points, pinch points, fakes, and mocks. Some of the techniques are quite powerful and useful everywhere, such as sprouting, wrapping, scratch refactoring, characterization tests, and sensing variables.

Some of this book is a little hard to follow, especially if you don't like or care for C++ and Java. This is partly because he uses actual production code from various projects as his examples. While it make it harder to follow, this adds a lot more realism to the book, which you'll appreciate if you're like me and have read many programming books with contrived and unrealistic examples.

Unless you have legacy code with a full suite of regression tests (very unlikely), don't even bother reading Refactoring unless you plan to read this book afterward.
Profile Image for Avdi.
Author 4 books254 followers
February 4, 2009
This is without question one of the essential books on my software development bookshelf. The dirty secret of software is that 80% or more if it is hacking your way through thick tangles of legacy code. This book is your survival guide in that jungle.
Profile Image for Regis Hattori.
124 reviews8 followers
December 2, 2017
I think the entire book can be resumed as:

"Legacy code is a codebase without tests. While working in there, you must introduce tests before. If you cannot introduce test because it is hard and/or you have not enough time, use some techniques (listed in the book) that change the code as little as possible or use other techniques that help you write at least some tests."

I think the majority of techniques are pretty known by developers but we tend not to use them because they sometimes can worsen the production code. The book tries to changes our mind saying that sometimes this trade-off is valid because the tests can help you improve your code later. And I think this is the best contribution of the book.

Given what was said, I think the book is a good advisor but could be much more succinct.
Profile Image for Ash Moran.
79 reviews32 followers
January 29, 2010
I've heard this called "genius" and it is. Legacy code is defined as untested code. Changing it involves various strategies to safely and incrementally get tests in place. The "seam" model of thinking, where you identify points you can influence behaviour without changing the code, is extremely powerful. Feathers gives several types of seam, and many techniques for exploiting them.

The main value of Working Effectively with Legacy Code is low risk ways to deal with untested code. There's a large catalogue of "safe" refactorings. It's important to note, though, that most of the mechanical coding techniques are to work around constraints in statically-typed languages such as C++ and Java. Many, if not most, are unnecessary in eg Ruby. (If you use a dynamic language, the book is a very quick read.) That's possibly my only gripe with the book - that the theoretical core is not cleanly separated from the language-specific issues. On the other hand, it's clear from the tone of the book that one intention is to motivate possibly junior developers to take action on legacy code - so a certain amount of concrete (even laboured) explanation and justification is inevitable.

Effect Sketches and Feature Sketches are two ideas I hadn't seen before. The former is to reason about how changes may impact other code, and provides a means to identify effective testing points. The latter is a way of identifying separate responsibilities inside large classes.

I highly recommend this to anyone committed to improving the quality of their code. It's value is _not_ restricted to 30-year-old COBOL systems.
Profile Image for Michael Koltsov.
93 reviews58 followers
December 14, 2020
The world of software moves with such a pace that this book written in 2004 looks like a relict from the distant past. However, it's still capable enough to teach the old dog new tricks.

These days most of the software is written with a great help from IDEs that have become much more than just text editors. Every IDE gives you hints on how to optimize your code, most have static analysis tools built-in and most code can be verified even before it gets compiled/interpreted.

Although, most IDE have capabilities to help you refactor your code, they usually run away in tears when they see hundreds of lines of smelly code in one class not covered by any tests.

And that's the situation where this book actually shines. It still can teach you a few trick how to keep yourself sane when you're thrown to the snake pit of filthy legacy code. And no matter how dated this book is, I'd still recommend it to anyone who finds himself in a situation where he has do maintain what he doesn't even want to touch.

My score is 3+/5 Every developer should read this book, but it desperately needs an update
Profile Image for Vitor.
16 reviews5 followers
October 21, 2017
This book is a must for anyone who wants to master software design. It teaches you many techniques and principles to help you on the task of refactoring legacy code (with tests). Even if you are writing a software from scratch, you’ll definitely get to a point where your code will become legacy and difficult to test and to deal with. So I’d definitely keep this book by my side. Besides all that, it kind of helps you see legacy and rotting code from a different perspective: it isn’t just a rotting mess, it’s an opportunity to improve the design of the system, help your teammates, learn software design techniques and keep you motivated. The only cons is that many techniques in this book might be outdated, but you never know when you might need it.
Profile Image for Aurelian.
40 reviews
October 4, 2018
4.2/5 great techniques on how to effectively work with legacy code.

Most of my highlights and notes were from " Chapter 17: My Application Has No Structure " e.g. telling the story of the system, a technique that I'll surely use.
Profile Image for Matt Diephouse.
92 reviews36 followers
December 20, 2014
The tl;dr is that (1) tests make it easier to change and improve code, and (2) it can make sense to “worsen” the code as an intermediate step so you can test and improve the code.

Noting that “Testing in isolation is an important part of the definition of a unit test”, Feathers gives a long list of ways to break dependencies for testing:

- Using macros to redefine methods during compilation
- Substituting in a different behavior for a dependency at link time
- Passing in objects instead of creating them or accessing a global
- Moving isolated methods to an abstract superclass that can be subclassed and tested in isolation
- Accessing objects through getters instead of direct property access
- …and many more

He also gives some other suggestions that don’t have to do with dependencies:

- Using a sensing variable to add tests during a refactoring. The variable can be conditionally set in a method so that you can verify that the correct behavior is achieved.
- “Extract small pieces first”, as doing so will often help you see the big picture.
- “Programming is the art of doing one thing at a time”.
- “One pervasive problem in legacy code bases is that there often aren’t any layers of abstraction”.
- “Remember that it is okay to extract methods with poor names or poor structure to get tests in place. Safety first. After the tests are in place, you can make the code much cleaner”.
- “A unit test that takes 1/10th of a second to run is a slow unit test”.
- “Pay attention to private and protected methods. If a class has many of them, it often indicates that there is another class in the class dying to get out.”

I found many of those viewpoints to be thought provoking. I also thought this bit about effects was interesting:

In general, programming gets easier as we narrow effects in a program. We need to know less to understand a piece of code. At the extreme, we end up with functional programming in languages such as Scheme and Haskell. Programs can actually be very easy to understand in those languages, but those languages aren’t in widespread use. Regardless, in OO languages, restricting effects can make testing much easier, and there aren’t any hurdles to doing it.

Many of his techniques involve inheritance, which he acknowledges towards the end of the book. So I’m not sure how many I’ll use (I’m more interested in factoring my code to avoid dependencies), though I’ll definitely be thinking about them in the coming months.

My key takeaway is probably that tests can be more important than the ideal structure (if you can’t have both).
Profile Image for Mahmood Hanif.
6 reviews2 followers
January 11, 2014
For me, the 'big idea' of this book was the view that legacy code is any code without sufficient unit tests. That makes complete sense to me and is a great way to look at software development. So, the idea gets 5 stars ... The book overall is a bit 'meh'. It's not really something to read from cover to cover although there are valuable insights scattered throughout the book. I think it's a good book to have in your library and reference when you run into a situation that needs these techniques. For most experienced developers they probably have used or at least had exposure to a lot of the techniques already.
Profile Image for Martin Nachev.
22 reviews29 followers
November 14, 2018

Ironically, in 2018 this has become a Legacy book, with a lot of the techniques explained being outdated. Unless you're an absolute beginner that hasn't read better titles like Clean Code you won't get much out of this book.
Most of this book can be summed up as simply:
1. Do Unit Tests so you can refactor safely
2. Use your common sense (a.k.a. heuristics)
Profile Image for César.
6 reviews
July 19, 2020
Fantastic book. A must-read book since most of the software we'll end up working with would be Legacy Code.

Legacy code is successful software, software that survived. But Legacy code it's, often, bad code. Bad code, the author argues, and I agree, it's code without tests. It's bad because it hinders the evolution of software to adapt to new requirements while still delivering its current value.

Techniques presented in this book such as Characterisation Tests, Effect Sketches and Feature Sketches are easy to understand and can be applied right away. I've applied the very next day after reading their chapters and could immediately benefit from them.

Some techniques, though, only make sense to certain languages such as Compiler and Link Seams in C, or would be somewhat cumbersome to do in other languages.

It would be interesting to have a second edition on modern legacy systems as weird as that sounds. Systems built with currently popular languages like Javascript, Scala and Go, for example. A version per language would work great, similarly to what Sandi Metz, Katrina Owen and TJ Stankus did with the second edition of 99 Bottles of OOP though it means, for the author, to write multiple books at once.
Profile Image for Isidro López.
120 reviews21 followers
December 22, 2019
Despite the years elapsed since it was written, and the high expectations... it didn't disappoint me at all :-)

I already knew some techniques thanks to Sandro Mancuso screencasts. I got lots of useful reminders, new learnings and reflections and some new techniques.

It's also nice to see techniques for different languages/paradigms. This book is a great complement to the Refactoring one, I can only highly recommend it! :-)
Profile Image for Mark.
8 reviews11 followers
December 29, 2012
This was the most helpful testing book I've ever read. Most books assume that you'll be starting with a greenfield project and can stay on the straight-and-narrow path of TDD all the way to glory: as most devs know, you're usually coming to a project that sorta-kinda-works-in-production and usually has inadequate or no automated testing. This is terrifying: you know change needs to happen, but you have no certainty about how to make it.

Feathers lays out a taxonomy of strategies for grappling with the beast. The benefits, drawbacks and applicability of each are lucidly explained. Many techniques actually make the code uglier temporarily in the interests of testability; his honesty about this is very refreshing.

all in all, if you're looking at tackling the horrible, awful, mission-critical codebase at work that no-one wants to touch, you need this at your side.
14 reviews1 follower
March 10, 2019
This book definitely improves your programming skills. The talk about unit testing and it's benefits is good. More often than not we work in projects or on software that's old. Not every development is Greenfield. In such cases we wonder how to implement our newly learnt unit test skills. That's where this book sits and tells you exactly what to do. Don't read this book as a fresher. Gain some experience, fight some legacy code, invent some techniques of your own. Then come here and you would appreciate the book.
Profile Image for Emad Elsaid.
18 reviews27 followers
February 23, 2018
it's a good book, examples are mostly statically typed languages which is not so helpful in the case you're a ruby developer.
but it's good for setting the mentality and perspective for you no matter what language you're using.
39 reviews
October 8, 2018
The books on object-oriented programming written or edited by one of the signatories of the Agile manifesto (Robert Martin, Martin Fowler or Kent Beck, to name a few) tend to have some common characteristics. They use Smalltalk or Java as the language for the code samples, pack a lot of wisdom and interesting examples, and unfortunately tend to be long. This book, from the Robert C. Martin series, discusses cases of Java and C++ code bases that are painful to work on, and how to improve them. At 500 pages, it doesn't count as a lightweight, and to be perfectly honest, there are many pages that could have been left out, or banished to the appendix because they concern details of how C++ compilers work.

The author defines legacy code simply as code without tests. There is a rather convincing reasoning behind this seemingly oversimplified definition, and it goes like this. Changing an existing code base is all about feedback. You understand as much of the code as you need,change a few lines, and then verify your change. How this verification happens, the feedback, is what differentiates code bases. If you get your feedback only when the diff is online, and the customers are screaming, you will be developing under a lot of pressure, leading to anxiety and thus suboptimal code, written slowly. You will refrain from making experiments or refactoring, because keeping existing functionality is of utmost importance. If there is dedicated manual testing, at least some security before reaching the customer is provided, but the feedback still comes with too much delay to develop with confidence. The ideal condition is one where we can write code,verify that it is not breaking any existing functionality, and also put in the feedback mechanism which verifies our addition. The only feedback mechanism that can provide this is unit testing. If a code base has unit tests, we can improve it by refactoring anyway. If there are none, however, the first task in improving it is writing these tests.

What exactly are unit tests? This is one of those things in software development that everyone knows the correct definition of, but very few people do according to the book. As the name implies, unit tests put the smallest unit in a language (i.e. functions for procedural languages, classes for OOP langs) in a "software vise". They fixate the features we are focusing on, and give us quick feedback on whether we are on the right path. For this purpose, they should execute very fast and be robust, i.e. fail only when the feature fails. Michael Feathers, together with many others, posites unit tests as not depending on code-external factors such as databases or network connections. I definitely agree with him, but it has become common place these days to call tests that make requests to an application and then check the resulting database condition also unit tests, which is in fact incorrect usage.

With this role attributed to unit tests, and the definition of legacy code as code lacking them, it is obvious what one should strive for when working on a legacy code base: Identify what to change, put relevant parts of codebase into a vise with tests, proceed to make changes with confidence. The rest of the book is concerned with various techniques to accomplish each of these steps, demonstrated in concrete project scenarios such as "It takes forever to make a change", or "I can't run this method in a test harness". The book is directed mainly at Java and C++ programmers, the most popular object oriented languages, and the case studies are tailored to the difficulties faced by developers working in large projects with these languages.

The concrete obstacle to applying the above mentioned isolate-test-modify method in most legacy systems is that complicated dependencies make it difficult to instantiate classes individually, or verify effects of calling their methods. The techniques presented to solve this dependency problem fall into two rough categories: platform-level and language-level. Platform-level methods concern the specifics of how the language platforms function, making use of the different features to circumvent the limits to testing the platforms introduces in other places. Some such methods discussed are the preprocessor in C++ and classpath in Java. These both can be used to modify behavior at compile-time and break dependencies. Language-level techniques, on the other hand, concern the use of various language features, especially object-oriented ones, to modify behavior and ease testing. Some examples are mocks, where common interfaces are implemented by functional and verification classes, or subclassing and overriding effectful methods to circumvent effects (a technique the author calls Wrap Class). There are too many of both techniques,especially of the second category, to list here, which makes the book rather relevant for detailed study by programmers in the targeted languages. I felt myself a bit lost at times as a Python developer,and skipped pages, but the detailed discussions of object oriented techniques for testing still had some interesting surprises for me.

This book being about legacy code, there are many examples of bad code that is difficult to disentangle and test, coupled with concrete techniques to do so, and illustrations of object-oriented principles. An example of a very big class, for example, is discussed around not only testing it and breaking it down, but instead coupled with why it is a bad idea to build such a class in the first place, and how to avoid doing it in new code. The same example is used to illustrate the single responsibility principle and the interface segregation principle. In one other discussion, the importance of encapsulation is demonstrated with how properly respecting it can constrain effects within a class, making it much easier to understand and to test. Although these topics are frequently coupled with Java or C++ code, they are the most rewarding, and worth studying through.

One frequent topic the author has to grudgingly go into are all the silly features of Java & C++ that make testing difficult or impossible. Among these, all the different ways to control subclassing definitely take the cake. For example, I was astonished to find out that in C#, one can prevent a class from getting instantiated or subclassed. This is obviously a huge roadblock to testing classes that use this class as a constructor argument or methods that need such an object as argument. Another weird example is the combination of const and mutable in C++, which allows a method to change a const field on a class. Why the const keyword even exists is something only the language designers can explain. Reading through these examples, one comes to appreciate permissive languages such as Python which take a more convention-oriented approach.

Another aspect of the book is the way discussions of developer and team psychology are interleaved with the technical topics. This is a neglected topic in most programming books, but Feathers, thanks to his experience with consulting engagements, knows how valuable it is, and scatters discussions on the importance of various decisions and methods for developer sanity throughout the text. The most prevalent one is why fast unit tests are a great boon for programmer productivity and well-being. TDD-wise tests allow the developer to focus on one thing at a time, get fast feedback, and change code with confidence. The alternative to having tests is anxiety, and having to read tons of irrelevant code and concentrating on trivial things just so that nothing breaks. There are other brilliant insights, such as how ugly code convinces you that things will always be this ugly, or how big chunks of procedural code call for more of the same. One that I found really interesting is his point on how programming books do not contain really ugly code, because if they did, no one would buy them.

This book is a great addition to the library of every developer working in OOP languages, especially those who feel the pain of having to maintain legacy code they themselves did not write.
Profile Image for João Paiva.
41 reviews5 followers
June 12, 2020
Found the book quite outdated. I live the definition that legacy code is code without tests, and the book includes several interesting techniques (even some I'd never used). Having said that, the book is mostly about adding unit tests to code single threaded programs written in statically typed languages. Further, IDEs today can automated most of the techniques described in the book. In conclusion, I mostly I missed techniques around handling dynamically typed languages and thread-safety issues.
51 reviews1 follower
November 11, 2019
Yes, some of the sections were outdated, but still this book offers a really good glimpse on how to handle work with legacy code. Basically it is just a set of ways helping to cover codebase with tests (breaking dependencies, extracting methods and so on). Really enjoyed it, can give you a feeling that you are not alone if you are working with some legacy code.
Profile Image for Adolfo.
26 reviews2 followers
January 6, 2021
100% recommend this book for devs working on Java-like projects.This book goes deep in lots of different strategies to put complex classes into what the author calls a test harness.Some of the techniques presented can be replaced with mocks, and some examples in C++ are irrelevant for me atm. However the experience shared by the author is invaluable.It has also interesting ways to approach & understand complex software.
Profile Image for Farid Bekran.
30 reviews1 follower
June 22, 2022
این کتاب رو اخیرا برای بار دوم مطالعه کردم.
دفعه قبل خیلی عمیق نخونده بودمش. برای کسب تجربه از راه مطالعه تجربیات دیگران، گزینه خیلی خوبیه.
مثال‌های کتاب ممکنه به جهت قدیمی بودنش یکم جالب به نظرتون نیاد. حتی یکی دو فصل کتاب کلا با تکنولوژی‌های الان موضوعیت خاصی ندارن.
کتاب الزاما در مورد کدهای پروژه‌های خیلی قدیمی نیست. برای هر پروژه‌ای که کدش براتون نا آشناست می‌تونین از راهنمایی‌های کتاب استفاده کنین.
از کتاب انتظار دستورالعمل‌های معجزه وار نداشته باشید. مفاهیمی بسیار ساده اما کاربردی در کتاب ذکر شده.

خلاصه کتاب ارزش مطالعه داره.
January 22, 2021
Great book! With a list of practical suggestions. Sometimes during reading, I had to stop and jump to my code as some improvement ideas came from the book.
Profile Image for Yehia Abo el-nga.
24 reviews46 followers
May 17, 2019
This author has a very hands-on writing style. If he is explaining a problem, he is far from high level abstractions. He dives deep into details of the problem with detailed examples; And shows a very thorough thought process. The idea I liked the most was effect graphs. I could solidly say that I learned something after reading this book.
Profile Image for Max Wolffe.
157 reviews13 followers
August 6, 2019
This book was originally written in 2004 and published in 2005. Given that, it is a remarkably relevant text, with some real gems to help one approach refactoring a large “legacy” code base.

I particularly appreciated the definition of legacy code - as code which is not under test - the rest of the book gives one a toolset for putting that code under test, after which any refactoring is trivial.

That said - there are a few aspects which will not be for everyone.

1. It is dated - refactoring and testing tools have advanced dramatically (for most OOP languages at least) in the last decade, so some of the dependency breaking procedures and testing strategies like “Fake” objects have been replaced with frameworks like Mockito or EasyMock.
2. The format is not super conducive to a through read. Each section is formatted as a question “I have a Monster Method”. I strongly suspect that this is going to make it easier to use as a reference later (Random access vs Sequential access I guess :p)

Overall a useful addition to the programmer’s toolkit.
Profile Image for James.
48 reviews1 follower
March 21, 2019
A fantastic reference book. Dozens upon dozens of specific, concrete solutions to specific real-world problems that software engineers tend to run into when dealing with legacy code. Highly recommended.
Author 2 books109 followers
October 12, 2016
This was one of the books that “laid” on my ‘virtual’ to-read shelf for a long time. I’ve heard a lot about this book but never have time to get my own opinion.

So finally I’ve read it and I have good part and not so good part of it.

Good part is that the book is really good. Michael is trying to give reasonable plan for everyone who is working with legacy code (i.e. for all of us): with specific patterns that help to break dependencies, untangle the mess and a testing suite and start working on the redesign.

Not so good part is about timing. The book is awesome, but I should have read it several years ago. No, this is nothing to do with the book’s age, it’s more about your personal experience. The topic of unit testing, refactoring, clean code and design is very popular these days. So if you already read “The Art of Unit Testing”, “Growing Object-Oriented Software Guided by Tests”, “Dependency Injection in .NET” and dozens of articles on this topics, you’d hardly find anything new here.

On the other hand, even if you read all of this it would be a good refreshing material to begin with, so your time investments would worth it.

Book is good: old, but definitely not obsolete!
Profile Image for Todor.
46 reviews9 followers
February 9, 2017
This was a perfect book, written by a person that's been involved in a lot of legacy projects. I found some useful tips and approaches in it. Sadly most of the patterns are either java or c++ related.
1 review5 followers
January 13, 2022
This book introduces many techniques to make maintaining legacy software easier. However, one must be aware that a lot of these techniques will leave your code base in a worse state for the sake of adding tests. The author admits this fact, and argues that having test is the basis for rigorous refactoring later. Some techniques can only be done with certain programming languages, and even considered bad practices by many developers though. The book focuses a lot on object oriented languages, which is understandable given the date it was published. I would love to see the author explore more on newer languages.
Displaying 1 - 30 of 269 reviews

Can't find what you're looking for?

Get help and learn more about the design.