HOT TIP: Ted Young’s "Make Your Code More Testable" class is coming up August 23rd. The class is excellent (and covers much of what I talk about below), Ted is a wonderful teacher – and I scored you a discount code. Go to MakeTestable.com and use code GEEPAW when you sign up to get $75 off!
In the spirit of my Ten I-Statements about TDD, here’s ten more, this time about refactoring. I’m not covering everything, just hitting some of the high points of my refactoring activity here.
Folks, I like geekery so much, I do, but the most important thing to me isn’t changing code, it’s changing how we all live together. Please work for social change, and support the others who are doing it, too.
Stay safe. Stay strong. Stay angry. Stay kind. Black Lives Matter.
Before we begin, one reminder-proviso, and one reminder-tip, based on my experience from last time, which was overall very wonderful and positive, but still had a couple of glitches I might have helped prevent had I been a little more coherent in the setup. 🙂
Reminder-proviso: These are truly "I-Statements".
My goal here is to share how I work, not tell you how you should work. There are a lot of ways to geek, and this is just my way of doing it. I am neither searching for nor proposing universal laws of programming.
Reminder-tip: These I-Statements go together.
I received many questions last time that were tied to one statement, but already answered pretty straightforwardly in another one. To me, they’re not isolatable planks in a platform, just callouts of specific features of my style.
1. I regard refactoring — changing the code without changing its external behavior — as absolutely fundamental to my productivity.
I do it as I do TDD, to ship more value faster, not for hygiene, professionalism, aesthetics, morality, or craft. Color me mercenary & pragmatic.
When math-izing my productivity, the three big terms are: 1) My skill, 2) My domain, and 3) What I start with. Of those three, "what I start with" is the easiest one for me to control. Refactoring is what I do to make what I start with represent my best current understanding.
2. I refactor continuously at small scale, and at that scale I don’t distinguish it much from TDD, nor do I "phase" it.
At this level, the most common refactorings I perform are rename variable/method, inline variable, & line-reordering.
I routinely extract loops, loop-bodies, try-bodies, and the little vertical white space chunks people think of as paragraphs, each into separate methods. Each time I do this I get a naming opportunity.
3. I often refactor just to ease testability, as I find that highly-tested code and highly-meaningful code are almost synonymous in my style of working.
The most common refactorings I make for testability include interface-extraction, signature-changing, and several closely related extractions amounting to "get this code somewhere else where it’s easier to test".
4. I do small-scale refactorings on red all the time.
(This is likely to upset some of you. Please go to the back of the complaints line and wait your turn.)
I understand the risk. But it seems quite small, because of other aspects of my programming style.
Those aspects: I am normally well-buttressed by tests, so screw-ups are unlikely to evade notice. I am normally taking very small steps, so the cost of a screw-up is usually just a few minutes. I generally learn something when I do this, which makes up for that cost.
5. I am a very heavy user of automated refactorings, because they are so much faster and safer than manual ones.
There are refactorings one can’t do automatically, because they require too much human intervention. But if I can do it using my IDE, I do.
I’ve learned a lot of clever tricks for mixing automated & manual refactoring into the same sequence, to get larger refactorings than I could with automation alone, and faster refactorings than I could with manual alone.
I love my tools & invest heavily in knowing them well.
6. I use larger-scale refactorings to make holes for new functionality, before I even begin that new functionality.
This often amounts to taking a vertical stripe of old functionality and splitting it into two stripes that initially do the same thing.
I then divide the stripes based on condition or entry point. I then change the second one so it does the new thing. This can sometimes be rocket science, so I use special technique, see the next item.
7. I rely on spikes — coding time that does not result in a push — for larger scale refactorings. Just as I do for larger scale feature changes.
Spikes are off-road driving, with only one rule: the only thing you keep is the thinking.
Read more here:
An Intro to Spikes | GeePawHill.org
I use spikes, periods of code-changing activity that end with no pushes, all the time, at small scale and large, as a way to develop my path…
In a refactoring spike, what I’m usually doing is making a bunch of rapid changes to see if where I want to go will get me what I want. I then take these messy mixed-up changes and path out a series of smaller steps that will do the same thing. Reset, then walk the path for real.
8. When I’m refactoring, I tend to ignore what I know about the domain.
I’m not changing the behavior of the code when I’m refactoring, so the important thing to me is what the code does, not what it’s supposed to do. Juniors often lose sight of this.
This is especially important when I’m working in legacy or in totally new-to-me code. If the code says it returns 1 if the input’s < 0, I don’t go find a domain expert, I preserve the behavior. Domain ratholes can slow you down in environments like this.
9. I will rename anything anytime.
Names are of such tremendous importance in conveying meaning, a name change is nearly always a rapid payback net-positive for me. I will change names in code I’ve never seen before, by way of capturing my growing understanding.
A common slack activity for me: post a snippet or some psuedo-code or a sketch, and ask folks, "what’s a better name for that?" I love the conversations that result from these questions, and often, they turn into significant design changes, too.
10. I do not ask for permission to refactor.
I regard it as a perfectly ordinary and critically important part of my job. If a story comes into an area of the code that needs refactoring to optimize its meaning, I refactor it. Refactoring is a profit-center.
Sometimes, I have to do a large refactoring by spreading its full impact across several stories. As long as each story’s refactoring conveys meaning, I don’t fear losing the thread of my plan.
So there ya go.
These ten I-statements certainly don’t capture every aspect of my refactoring practice, but I hope they give you the flavor of how I work. Please feel free to bring me your questions, comments, controversy, or QWAN.
Do you love the GeePaw Podcast?
If so, consider a monthly donation to help keep the content flowing. You can also subscribe for free to get weekly posts sent straight to your inbox. And to get more involved in the conversation, jump into the Camerata and start talking to other like-minded Change-Harvesters today.