What do you think about when you hear the term legacy code? If you are at
all like me, you think of tangled, unintelligible structure, code that you have to
change but don’t really understand. You think of sleepless nights trying to add
in features that should be easy to add, and you think of demoralization, the
sense that everyone on the team is so sick of a code base that it seems beyond
care, the sort of code that you just wish would die. Part of you feels bad for
even thinking about making it better. It seems unworthy of your efforts. That
deﬁnition of legacy code has nothing to do with who wrote it. Code can
degrade in many ways, and many of them have nothing to do with whether the
code came from another team.
In the industry, legacy code is often used as a slang term for difﬁcult-to-change
code that we don’t understand. But over years of working with teams, helping
them get past serious code problems, I’ve arrived at a different deﬁnition.
To me, legacy code is simply code without tests. I’ve gotten some grief for
this deﬁnition. What do tests have to do with whether code is bad? To me, the
answer is straightforward, and it is a point that I elaborate throughout the
You might think that this is severe. What about clean code? If a code base is
very clean and well structured, isn’t that enough? Well, make no mistake. I love
clean code. I love it more than most people I know, but while clean code is
good, it’s not enough. Teams take serious chances when they try to make large
changes without tests. It is like doing aerial gymnastics without a net. It
requires incredible skill and a clear understanding of what can happen at every
step. Knowing precisely what will happen if you change a couple of variables is
often like knowing whether another gymnast is going to catch your arms after
you come out of a somersault. If you are on a team with code that clear, you are
in a better position than most programmers. In my work, I’ve noticed that
teams with that degree of clarity in all of their code are rare. They seem like a
statistical anomaly. And, you know what? If they don’t have supporting tests,
their code changes still appear to be slower than those of teams that do.
Yes, teams do get better and start to write clearer code, but it takes a long
time for older code to get clearer. In many cases, it will never happen com-
pletely. Because of this, I have no problem deﬁning legacy code as code without
tests. It is a good working deﬁnition, and it points to a solution.
I’ve been talking about tests quite a bit so far, but this book is not about test-
ing. This book is about being able to conﬁdently make changes in any code
Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t mat-
ter how pretty or object-oriented or well-encapsulated it is. With tests, we can change
the behavior of our code quickly and veriﬁably. Without them, we really don’t know
if our code is getting better or worse.
base. In the following chapters, I describe techniques that you can use to under-
stand code, get it under test, refactor it, and add features.
One thing that you will notice as you read this book is that it is not a book
about pretty code. The examples that I use in the book are fabricated because I
work under nondisclosure agreements with clients. But in many of the exam-
ples, I’ve tried to preserve the spirit of code that I’ve seen in the ﬁeld. I won’t
say that the examples are always representative. There certainly are oases of
great code out there, but, frankly, there are also pieces of code that are far
worse than anything I can use as an example in this book. Aside from client
conﬁdentiality, I simply couldn’t put code like that in this book without boring
you to tears and burying important points in a morass of detail. As a result,
many of the examples are relatively brief. If you look at one of them and think
“No, he doesn’t understand—my methods are much larger than that and much
worse,” please look at the advice that I am giving at face value and see if it
applies, even if the example seems simpler.
The techniques here have been tested on substantially large pieces of code. It
is just a limitation of the book format that makes examples smaller. In particu-
lar, when you see ellipses (…) in a code fragment like this, you can read them as
“insert 500 lines of ugly code here”:
If this book is not about pretty code, it is even less about pretty design. Good
design should be a goal for all of us, but in legacy code, it is something that we
arrive at in discrete steps. In some of the chapters, I describe ways of adding
new code to existing code bases and show how to add it with good design prin-
ciples in mind. You can start to grow areas of very good high-quality code in
legacy code bases, but don’t be surprised if some of the steps you take to make
changes involve making some code slightly uglier. This work is like surgery. We
have to make incisions, and we have to move through the guts and suspend
some aesthetic judgment. Could this patient’s major organs and viscera be bet-
ter than they are? Yes. So do we just forget about his immediate problem, sew
him up again, and tell him to eat right and train for a marathon? We could, but
what we really need to do is take the patient as he is, ﬁx what’s wrong, and
move him to a healthier state. He might never become an Olympic athlete, but
we can’t let “best” be the enemy of “better.” Code bases can become healthier
and easier to work in. When a patient feels a little better, often that is the time
when you can help him make commitments to a healthier life style. That is
what we are shooting for with legacy code. We are trying to get to the point at
which we are used to ease; we expect it and actively attempt to make code
change easier. When we can sustain that sense on a team, design gets better.
The techniques I describe are ones that I’ve discovered and learned with
coworkers and clients over the course of years working with clients to try to
establish control over unruly code bases. I got into this legacy code emphasis
accidentally. When I ﬁrst started working with Object Mentor, the bulk of my
work involved helping teams with serious problems develop their skills and
interactions to the point that they could regularly deliver quality code. We often
used Extreme Programming practices to help teams take control of their work,
collaborate intensively, and deliver. I often feel that Extreme Programming is
less a way to develop software than it is a way to make a well-jelled work team
that just happens to deliver great software every two weeks.
From the beginning, though, there was a problem. Many of the ﬁrst XP
projects were “greenﬁeld” projects. The clients I was seeing had signiﬁcantly
large code bases, and they were in trouble. They needed some way to get con-
trol of their work and start to deliver. Over time, I found that I was doing the
same things over and over again with clients. This sense culminated in some
work I was doing with a team in the ﬁnancial industry. Before I’d arrived,
they’d realized that unit testing was a great thing, but the tests that they were
executing were full scenario tests that made multiple trips to a database and
exercised large chunks of code. The tests were hard to write, and the team
didn’t run them very often because they took so long to run. As I sat down with
them to break dependencies and get smaller chunks of code under test, I had a
terrible sense of déjà vu. It seemed that I was doing this sort of work with every
team I met, and it was the sort of thing that no one really wanted to think
about. It was just the grunge work that you do when you want to start working
with your code in a controlled way, if you know how to do it. I decided then
that it was worth really reﬂecting on how we were solving these problems and
writing them down so that teams could get a leg up and start to make their code
bases easier to live in.
A note about the examples: I’ve used examples in several different program-
ming languages. The bulk of the examples are written in Java, C++, and C. I
picked Java because it is a very common language, and I included C++ because it
presents some special challenges in a legacy environment. I picked C because it
highlights many of the problems that come up in procedural legacy code. Among
them, these languages cover much of the spectrum of concerns that arise in leg-
acy code. However, if the languages you use are not covered in the examples,
take a look at them anyway. Many of the techniques that I cover can be used in
other languages, such as Delphi, Visual Basic, COBOL, and FORTRAN.
I hope that you ﬁnd the techniques in this book helpful and that they allow
you to get back to what is fun about programming. Programming can be very
rewarding and enjoyable work. If you don’t feel that in your day-to-day work, I
hope that the techniques I offer you in this book help you ﬁnd it and grow it on
First of all, I owe a serious debt to my wife, Ann, and my children, Deborah
and Ryan. Their love and support made this book and all of the learning that
preceded it possible. I’d also like to thank “Uncle Bob” Martin, president and
founder of Object Mentor. His rigorous pragmatic approach to development
and design, separating the critical from the inconsequential, gave me something
to latch upon about 10 years ago, back when it seemed that I was about to
drown in a wave of unrealistic advice. And thanks, Bob, for giving me the
opportunity to see more code and work with more people over the past ﬁve
years than I ever imagined possible.
I also have to thank Kent Beck, Martin Fowler, Ron Jeffries, and Ward Cun-
ningham for offering me advice at times and teaching me a great deal about
team work, design, and programming. Special thanks to all of the people who
reviewed the drafts. The ofﬁcial reviewers were Sven Gorts, Robert C. Martin,
Erik Meade, and Bill Wake; the unofﬁcial reviewers were Dr. Robert Koss,
James Grenning, Lowell Lindstrom, Micah Martin, Russ Rufer and the Silicon
Valley Patterns Group, and James Newkirk.
Thanks also to reviewers of the very early drafts I placed on the Internet.
Their feedback signiﬁcantly affected the direction of the book after I reorga-
nized its format. I apologize in advance to any of you I may have left out. The
early reviewers were: Darren Hobbs, Martin Lippert, Keith Nicholas, Phlip
Plumlee, C. Keith Ray, Robert Blum, Bill Burris, William Caputo, Brian Mar-
ick, Steve Freeman, David Putman, Emily Bache, Dave Astels, Russel Hill,
Christian Sepulveda, and Brian Christopher Robinson.
Thanks also to Joshua Kerievsky who gave a key early review and Jeff Langr
who helped with advice and spot reviews all through the process.
The reviewers helped me polish the draft considerably, but if there are errors
remaining, they are solely mine.
Thanks to Martin Fowler, Ralph Johnson, Bill Opdyke, Don Roberts, and
John Brant for their work in the area of refactoring. It has been inspirational.
Documents you may be interested
Documents you may be interested