Thursday, October 13, 2016

Martin Fowler @ OOP2014 "Workflows of Refactoring"

 time and I sent in the details of my
talk the tight all and the description people are a bit alarmed because I said
you spoke here two years ago with the same title and the same description we
don't want the same talk again
of course what they did not realize is that this is a very generic title i can
talk about anything with a title like this and I do and the way i give my
talks now is that rather than giving you one long boring talk i'm going to give
you too short boring talks instead and I have this little cluster of short 20
minute talks that I arranged for all of my various keynotes I give and I just so
all may be devious and maybe do that depending on the conference and my mood
and all the rest of it the two you're going to get today are in fact both
completely new talks i have not given them anywhere before
so forgive me when they all go completely wrong
so the first talk i'm going to talk about the workflows of refactoring and I
started thinking about this from conversations with various of my
colleagues and from various things are read on the Internet where I got a sense
that people were not necessarily utilizing refactoring in as many ways as
they could
a lot of times people were set for certain particular circumstances when
they might consider doing some refactoring and missing out on other
circumstances where I think it's valuable
so what I wanted to try to do was to capture and write down the various
situations when it makes sense to do some refactoring I've written this up
hours an info deck on my website so you may have seen the content from that but
this is the first time I've given this as a full talk now for many what seems
to be the most common case that people talk about doing refactoring is in the
context of the cycle of test-driven development
you've probably seen this many many times
we'll talk about this cycle of red green refactor we're in the third you begin by
saying i want some new functionality so I'm going to add a test a failing test
that turns my test suite $ADDRESSNUM red to indicate the beginning and then i'm
going to make that test pass by doing the work in order to add the
functionality this takes my test suite from red failing to green successful but
while I do this if I'm doing this in inappropriate test-driven development
style i don't really worry about making the code very nice i try to get it
working in the simplest most straightforward why I can I don't
concern myself about how it fits in with the rest of the system and then I take a
third step to refactor which is to say okay this new code that i put in now
let's make it clean let's remove duplication with other parts of the
system
let's reassess how i factored the code how it interrelates with the rest of the
code that's there and you might wonder how many people might wonder why do we
separate out the two steps of of writing the cold which is making it work and
making it clean
why do we think of those as two steps rather than one is particularly because
of course beginners often tend to leave out the second step don't tend to put
enough attention into the refactoring but even if you're good
why separate this out why do it into bits and I like to explain this by going
back to I old metaphor goes back to the very beginnings of when we talked about
refactoring I was a metaphor that can back told me and it's the metaphor of
the two hats
he said that when you're programming you should be thinking of two distinctly
different modes of work that you're operating in one is the mode when you're
adding new functionality making a test work or in any circumstance not
necessarily within testing and development you are thinking about
making code do something it didn't do before adding the new feature to it and
then you have a completely different mode when you're refactoring
in that mode you are making the code do anything different in fact you want to
preserve what it does and not make any changes to its outward behavior but you
want to improve its internal structure that is the essence of refactoring and
typically done on top of us a test suite but you're able to run regularly as you
go so that you're able to detect if you make any mistakes when you're
refactoring even if you're coming across a bug you wouldn't fix it because that
would mean changing your behavior so what you do is the rule is that you can
only wear wear one hat at a time but you're allowed to frequently swap
between the hats so if you were refactoring and you came across a book
that you wanted to fix you have to stop refactoring switch back into your adding
function mode fix it
the steps when you allowed to change the external behavior and then go back to
refactoring again you must always be conscious of what mode you're in now
these are the only two modes in which you operate when you're programming for
others to and performance improvement for instance is similar to refactoring
in many ways but you're focused on making your code faster rather than
making it more clear
I'm so that's a different mode another node is when you're exploring sketching
out an idea not really quite sure what you're gonna do and you're probably
going to throw it away when you're done
that's the kind of sketching hat a friend of mine said that really should
be a jaunty beret of some kind maps but i'm going to really just focus on these
two modes adding function and refactoring and obviously within the
test driven development cycle that's the separation between these two parts where
you're touching your production code at the beginning stage you're adding
function in the second stage refactor so why do we separate these so much and it
really comes down to the way in which the the attitude in the mode that you
working when you refactoring you're always operating on this basis of are
never going to change the behavior i'm going to take small steps i'm going to
run tests after you every time any change causes me to back out if I make a
mistake i undo what I just did
then tried again preferably with small steps that rhythm is very different to
the river when you're saying I want to induce some new behavior into the
program and as you work with this you get used to that two different styles
now this style of refactoring initially refactoring in this context of
test-driven development i think of one of the potential workflows of tests of
refactoring what are referred to as TD refactoring because it's within
test-driven development and other said it seems to be the most common one that
people think about because they're often talked about refactoring in the context
or together with test-driven development but let's look at a couple of other
potential cut cases so you probably all had this situation you're looking at
some existing code it's probably somebody else's but if you're a good
program it's probably around and you said all my god that's awful code right
your arm
what was I thinking what was that idiot thinking which is me of course you know
why is this this is just compresses doing something completely the wrong way
yuck and a lot of the time certainly in my younger days the the the thing that
was said was well you know if the code works if it's doing what it's doing
you don't want to go and change it you want to leave it as it is because hey
it's working
if you touch it you might break it the refactoring mindset says that when you
see something yucky you actually should go in and fix it you should say I CMS I
immediately have got to do something to make it clear that refactoring is very
much part about cycle you're always working through you see some piece of
code you so that's not right and then you go in and you immediately do
something about it
this is how you fight back against the entropy of software is designed
gradually falling apart over time you have to have a conscious counterforce
and one of those counter forces is an intolerance the stuff that does is just
not
fucking right it may have been fine two months ago when you wrote it but now you
realize it's not right and you have to change it and you have to fix it i think
this is a very opportunistic form of refactoring and it may not be on the
code that you're actually working on a time sometimes you do this kind of thing
when you're looking to use some code that's already been written
it's already separated out to some degree from the code you're working on
and you look at your son you say now this is just not looking right at all
the thing to do is to go and fix it not let it so the city and stagnate investor
so I think this is a little pickup refactoring because you're cleaning up
you may have heard it sometimes expressed as the Boy Scout rule always
leave somewhere cleaner than it was before
now it doesn't necessarily mean you've got a completely clean it up and there's
a judgment here about how much refactoring is appropriate depending on
all the areas of the things you've got to do but I think you should always try
to make it at least a little bit better
I'm us at least a small improvement and then if its code that you're regularly
hitting over time multiple small improvements will make it really quite
nice but on the other hand if it's not something you hit very often when you
don't want to spend a huge amount of time on it so don't necessarily want to
completely clean it up but you do want to remove the worst features and make it
a little bit better for the next time through and that's where I think the boy
scout metaphor and comes through quite nicely now is very closely related
version to this which is when you're looking at some code that you don't
understand you look at it you think what on earth is this bit of code doing and
you have to puzzle it out you have to think it through and so now that's going
there this is doing there starts connecting to that and that's gonna of
doing that
where is it all fitting together and at some point of course you'll figure it
out hopefully you'll fit say all that's what's happening
that is a very important moment that moment when you got all that's what's
going on here because the
wrong thing to do at that point is to say okay i understand it now carry on
doing what I was doing before because the problem is as Ward Cunningham put it
so well what that moment the understanding you have the code is in
your head but it's fragile in there
I mean you likely to forget it and the rest of your team is another share their
understanding so what you need to do is to move it out of your head and put it
into the code itself by refactoring it by going in and making it so that it's
as object clearly obvious as you can and that use next time that you or somebody
else goes through that bit of code you haven't got to spend time figuring out
how it works figuring out how something complicated and and are not easy to see
works
that's a good thing in a detective novel it's a bad thing in code so make sure
that you go and you clean that stuff up and again that has I'm giving that name
I think about his comprehension refactoring now comprehension
refactoring little pickup refactoring a very similar things right you could
argue they're really the same thing because any code that's hard to
understand is almost by definition yuck but i tend to think of them a slightly
different triggers in one case you can have seen something ugly in the other
case you're seeing something that's hard to understand that a slightly different
things are leading you into it but they both require the same reaction now
there's a bit of salty to the reaction i like to think of it mr. kind of two
tracks but you want to consider whenever you see something that you don't
understand or is just plain lucky the first thing you have to ask yourself is
do I wanted fix it now
now you may have seen in the the moment of some other refactoring that you doing
you may have been seeing in the moment of adding a new feature and the first
thing you have to ask yourself is is it as quick simple fix but I can do right
away without impending what I'm doing or is fixing it going to take a bit of time
it may be just something like renaming the method that you can do in your ID a
it
just want to click them there you are in which case just do it right away and
which you can do that but otherwise you might have to think about it a bit if
you are working on adding a new feature and as a result you're in a red code
base you've got some failing tests the first if you want to fix it immediately
the first thing you've got to do is you've got to get onto a green test base
and his various ways you might do that you might for instance you might disable
the tests that you're currently working on to make sure that the rest of your
test basis okay and then you begin the actual refactoring and then when you've
done you re-enable the tests that you're working on and you get back to your
regular work if you can't do it straight away the thing to do is to immediately
make a note I'm some people like to have a some index cards or a spare emacs
bother making out or going to fix this right away
put that to one side and then as soon as you've finished adding the feature what
you doing on you go back and you make that little bit of cleanup it is really
important that you treat refactoring as an integral part of your regular work as
soon as refactoring becomes a separate task when it becomes a big discussion in
a big argument as to whether you should do it instead that the easiest way to go
is to just make sure that you always doing the appropriate amount of
refactoring as you doing your regular work another trigger for refactoring is
when you look at some code and you realize i'm now that new insight about
how the code could work but i didn't have before
particularly in the context with some new functionality but I now want to add
it would have been really you look at you often have the situation i imagine
where you look at some code and you say I've got some new functionality to put
in and it would be really easy to slip this functionality in if only the code
was organized slightly differently to the way it is and you feel frustrated
but you didn't foresee in the past what you now need to do now it does that
frustration but let a lot of people to very ill intense up front design
trying to think through all the design issues at the beginning so that you
don't get those moments if like me you're a fan of more evolutionary
approach to design you get these moments all the time you look at your code and
you say well that was okay it was the correct thing to do yesterday but now
i'm gonna add this new functionality
it's not looking right anymore this situation also calls for refactoring
what I think of as preparator refactoring he basically says that if
you're in a situation where you're not a good fit for what you want to add you
want to first begin by refactoring into the shape but now is obvious is the
shape that you wanted to be in do that in purely refactoring mode with your
refactoring hat on your tests the green you should be able them produce that
transformation quite quickly and then once you don't then slip the new
functionality in on your now better reflected codebase a lot of people
complain about refactoring taking time away from other things and slowing you
down which is all . out in a moment is is definitely not the case but usually
the benefits you get from refactoring you in the medium term this is a case
where you get them you can get that benefit actually in the middle of a task
you doing it often can be faster to do a bit of preparative refactoring and then
add the new functionality that is to try and get the new functionality in with
the code not really quite fitting it even if you don't refactor to clean up
later on would you want to be doing anyway so whenever you get that feeling
of well if the code was better organized was organized in a different way
stop and refactor get in that shape and then bring in Union New change so those
refactorings that reflecting workflows i just talked about a little pick up the
comprehension the preparatory these are three refactoring workflows I feel
aren't used widely enough the next one is one that is clearly used a lot
because i often get questions about it and that is what to do
do about refactoring as part of your project plan this is when a significant
amount of crafters built up in the code base but you actually want to market on
your project plan as something that needs to be cleaned up my finger is a
planned refactoring because it's something that's very visible as part of
of your tasks in my view is that planned refactoring a good team should hardly
ever have to do planned refactoring in fact the perfect team would never do it
at all
not because they're not refactoring because of a team is always refactoring
but they're able to break it down into smaller pieces it it disappears inside
the work of adding new functionality and therefore never becomes a line item on
the plan
most teams however take a while to get to that point and as a result some plan
refactoring has to be done but the problem with planned refactoring is it
is always an exercise to justify and that's why I try to avoid it i tried to
do little bits all the time as part of the regular work i see but as software
developers our responsibility is to keep the codebase clean and i'll expand a
little bit on that later on but plan refactoring I see is a kind of necessary
evil and if you're doing it then that's a smell that you're not doing enough of
the other kinds of refactoring now this of course presumes all of this kind of
presumes something which is it kind of assumes that you have refactoring tasks
that you can always complete in a relatively short period of time but as
you work on a codebase overtime larger things will become apparent you might
say to yourself well I've got a bunch of modules with a lot of messy dependences
and maybe they just developed over time and you didn't pay enough attention to
keeping them steady maybe you're inheriting the codebase somebody else
and i didn't pay enough attention to these how do you clean things up in this
kind of situation
how do you do a long-term refactoring and my advice here is
really very much using the workflows i talked about earlier on and preferably
not the plan refactoring workflow what you need to do is get some vision of
where you would like your codebase to be I'm you might have seen this done in a
session the team might spend an error to indesign meetings like this is how we
like things to be in the future this is our goal our long-term goal the rather
than set up some tasks in your plan to get to that goal you say okay from now
on every time you working through this area of the codebase refactor it a
little bit to get it a touch closer to that goal and then over the course of
several weeks maybe several months you can an inch away their little bit of the
time gradually one dependency gets broken number one gets done until the
final step can be done as a relatively short piece that gradually inching
towards goal i find it preferable because for started doesn't get into the
whole planning refactoring steps in a plan but it also allows you to learn
from the process of reaching the goal itself and keeping the code stable and
clean and always ready to deploy and a good continuous delivery environment as
well you can actually surprisingly achieve quite large refactorings by
doing them in this very small gradual step-by-step way the key I think to good
refactoring is to remember it the essence of it is small steps
how can you break down a change into the smallest possible changes and even a big
change can be broken down into little steps it may not seem very easy at first
to figure out how to do that and that's another reason why doing these small
steps can be handy if you lay out the gardens I i'm not quite sure how we're
gonna get there but let's at least try and push in that direction and exchange
of each other ideas we have how to reach it
sometimes it may take a bit of time before you begin to see the path but by
gradually making progress there sometimes that clears the the mess out
of the way enough that you can see your path forwards so all of this has been
about
how to refactor but I need to finish by talking a little bit about why because a
question as often comes up is easy refactoring effectively just wasteful
research we work in fact for a long time in my in my early days that's how people
saw any changes like this of existing code it was just reworking stuff it was
an indication that you failed it was waste the reason that I feel that
refactoring is important comes down to something that i referred to as the
design stamina a hypothesis a long complicated phrase it sounds like one of
these German words right
it basically says if you don't pay attention to design on your system over
time as you continue to develop it seems like you've steadily slowing down until
you reach a point where you seem to make it about any new functionality takes
ages because you're having to the code is fighting you how many people have
worked on projects were had that situation everything slowing down the
code is fighting yeah pretty much everybody if you pay attention to good
design however you can hit another curve in this curve adding new functionality
becomes really easy because you've got all of these nice modules and all you
got to do is combine the modules together in a slightly different
configuration and you suddenly got some awesome new functionality and you feel
that you've got the code bases now this wonderful collection of components that
you can quickly configure to add new things
how many have worked in code base like that a few usually are less but there's
always some what you want is that better design curve and refactoring is the key
to be able to reach that better design curve
I mean you could do it by coming up with a really wonderful design in advance but
I've mostly by of the hardly ever seen anyone succeed with that what
refactoring does is it constantly keeps your code base in a good state always
pushing you towards that good side of a line if you're diligent about doing that
that allows you to get onto that good curve and what does that mean it means
that you're able to deliver more
functionality more quickly than somebody who's got an ill design code base and
that's the essence of why refactoring matters when I hear people talking about
refactoring they often say why should we refactor well it's because we want a
good quality code base we wanted to be clean with professionals we don't want
to leave a mess we want to do the right thing and I hear teams frustrated
because I say well I managers won't let us refactor we've told them that we need
to do this it's the right thing to do and they say well maybe later we'll do
it but not now
the trouble is as soon as you talk about refactoring or attention to good design
and modularity in terms of professionalism and keeping your code
clean and all that kind of stuff you've lost soon as you try to make this
argument you screwed this argument you want to ignore get rid of it instead
focus your argument on simple economics
we're doing this in order to be able to deliver more functionality more quickly
that's the only reason you should be refactoring and you should think about
that in terms of your own refactoring is this refactoring i'm going to do is it
going to make a difference is it gonna help me go faster in the longer term and
there are times and he tells you not to refactor if you're looking at an area of
your code base which nobody ever touches and he does work but um people don't
ever go into it they don't need to make changes to it then you can leave it
messy because it's not slowing you down when you're constantly feeling arm
slowed down by the code here I could go so much faster if the code was cleaner
that is a sign that says you've gotta work on refactoring that area of the
code and it's very agreed it's very hard to justify this to management because if
they're not in the coal based can't see it but my argument is we should is
somewhat controversial on you should actually tell them it's part of your
professional responsibilities as a programmer to keep your codebase clean
only you can only those in the codebase can assess whether it's slowing you down
or speeding you up
and what it teams to get onto that good curve from about perth and it's your job
to do that because if you're not doing it you're effectively stealing from your
customer you're making them spend more money take more time for the same
functionality when they would have done had you been paying attention to the
economics of how you work
so remember refactoring is about an economic argument clean code allows you
to go faster
that's the only justification behind it and on that note
hence I finish my first talk