Code rots. Even the best designs can fall into chaos, weighed down by changing requirements, hard-coded dependencies, and overzealous cut-and-paste. Eventually, the mess has to be cleaned up, but there's just one problem: There are no tests. No one knows exactly what the code is supposed to do. You will think you're fixing it, but instead you'll break it--and you won't even know.
A few months ago, I inherited just such a mess. In this talk, I'll explore Michael Feathers' classic Working Effectively with Legacy Code, a comprehensive and accessible guide to refactoring the even the ugliest, most neglected code bases. Using real examples, I'll explain how Feathers' techniques are working for me.
2. The
Book
by
Michael
C.
Feathers
published
2004
foreword
by
“Uncle
Bob”
Mar-n
influenced
by
Mar-n
Fowler’s
Refactoring
3. What
is
Legacy
Code?
• “difficult
to
change
code
that
I
don’t
understand”
• “code
wriNen
a
long
-me
ago”
• “code
that
somebody
else
wrote”
4. Why
do
I
need
to
understand
the
code?
because
SoTware
is
Never
Done
Refactor
Add
Feature
Fix
Bug
Op4mize
Structure
Changes
Changes
Changes
New
Behavior
Changes
Exis-ng
Behavior
Changes
Resource
Usage
Changes
6. Two
Methods
of
SoTware
Development
“Edit
and
Pray”
vs.
“Cover
and
Modify”
which
brings
us
to…
7. The
Legacy
Code
Dilemma
“When
we
change
code,
we
should
have
tests
in
place.”
“To
put
tests
in
place,
we
oTen
have
to
change
code.”
(But
why?)
8. Legacy
Code
Change
Algorithm
1. Iden-fy
Change
Point
2. Find
Test
Point
3. Break
Dependency
4. Write
Tests
5. Change
and
Refactor
9. Seams
Exchangeable
Behavior
+
an
Enabling
Point.
Seams
let
you
subs7tute
one
behavior
for
another
by
edi-ng
only
at
the
enabling
point.
Seams
let
you
introduce
test
collaborators.
10. Object
Seams
Most
useful
type
of
seam
for
OOP.
Another
way
of
talking
about
polymorphism.
Code
without
seam
Code
with
seam
11. In
the
Wild
No
Tests
==
No
Up-‐to-‐Date
Documenta-on
instead
of
13. In
the
Wild
Long,
Procedural
Methods
def generate_pdf
end
14. In
the
Wild
Stateful
Programming
• Keeping
Track
of
Flags
• HTTP
Session
Abuse
15. In
the
Wild
Curse
of
MVC
• Fat
Models,
Fat
Controllers
• Overloaded
Responsibili-es
16. So
Many
Problems
Chapter
6:
“I
Don’t
Have
Much
Time
and
I
Have
to
Change
It”
Chapter
8:
“It
Takes
Forever
to
Make
a
Change”
Chapter
10:
“I
Can’t
Run
This
Method
in
a
Test”
Chapter
16:
“I
Don’t
Understand
the
Code
Well
Enough
To
Change
It”
Chapter
17:
“My
Applica-on
Has
No
Structure”
Chapter
19:
“My
Project
Is
Not
Object
Oriented.
How
Do
I
Make
Safe
Changes?
Chapter
20:
“This
Class
Is
Too
Big
and
I
Don’t
Want
It
to
Get
Any
Bigger”
18. Single
Responsibility
Principle
“Every
class
should
have
a
single
responsibility:
It
should
have
a
single
purpose
in
the
system,
and
there
should
be
only
one
reason
to
change
it.”
21. Seeing
Responsibili-es
Primary
Responsibility
“Job
presents
damper
inspec-on
data
in
report
form.”
“Job
generates
files
of
reports.”
“Job
deletes
report
files.”
“Job
produces
graphs
of
damper
inspec-on
data.”
“Job
persists
and
retrieves
representa-ons
of
jobs
in
a
database.”
22. Iden-fy
Change
Point
We
want
to
refactor
Job
to
extract
Reporter.
1. Iden-fy
Change
Point
2. Find
Test
Point
3. Break
Dependency
4. Write
Tests
5. Change
and
Refactor
23. Find
Test
Point
Intercep-on
Points
Higher
Level
Tes-ng
– don’t
have
to
break
dependencies
– verifies
that
your
feature
actually
works
– slow,
inconvenient
– hard
to
automate
– not
tes-ng
in
isola-on
Progressive
Strategy
1. Iden-fy
Change
Point
2. Find
Test
Point
3. Break
Dependency
4. Write
Tests
5. Change
and
Refactor
24. Break
Dependencies
Required
for
unit
tes-ng.
Less
necessary
for
higher-‐level
tes-ng.
We’re
gonna
skip
it
(but
just
this
-me).
1. Iden-fy
Change
Point
2. Find
Test
Point
3. Break
Dependency
4. Write
Tests
5. Change
and
Refactor
25. Write
Tests
“Tes-ng”
in
the
Interac-ve
Console:
1. Iden-fy
Change
Point
2. Find
Test
Point
3. Break
Dependency
4. Write
Tests
5. Change
and
Refactor
26. Change
and
Refactor
This
was
harder
than
it
looks.
1. Iden-fy
Change
Point
2. Find
Test
Point
3. Break
Dependency
4. Write
Tests
5. Change
and
Refactor
29. BeNer
With
Seam?
We
can
add
a
seam
by
injec-ng
the
dependency
on
the
reporter.
This
way,
we
can
test
in
isola-on
by
passing
in
a
FakeReporter.
Unfortunately,
this
makes
our
produc-on
code
ugly: