Brutal Refactoring
Brutal Refactoring = Refactoring + TCR
tl;dr Refactoring requires small changes and always green tests. TCR ensures both. Together, they are Brutal Refactoring and a great fit.
TCR — test && commit || revert
With TCR You replace your test command with the Unix-style command “test && commit || revert” (TCR). In pseudo-code this means:
if tests are green:
commit code
else
reset to last commit
Every time my tests pass, the command commits my code. Every time one fails, it resets my code-base to the last green state (the last commit). This mechanism leads to small, focused steps (means changes) as I take the risk that TCR resets many lines if the change is too big.
Because the TCR tool brings you back into a green state as soon as you are in a red one, TCR is an “always green” technique. For details on TCR see here.
Refactoring
Every keystroke slows me down when programming. However, with a good internal structure, I slow down later (Design Stemia Hypothesis). I can achieve a good internal structure only through iterative development paired with refactoring.
Coming up in a good first draft isn’t possible because I learn whenever a finger touches a letter on my keyboard. The different degrees of knowledge while programming leads to different models for every line of code. I sync up my code to a consistent model with refactoring. Every line becomes “pretty much, what my reader expects” (freely cited Cunningham).
With refactoring, I mean cleaning up and reorganizing my code-base through a series of little steps (refactorings) without changing its observable behavior. I define observable behavior as the set of my tests. The test suite is a good enough heuristic for me as I also deploy on passing tests (“green tests”).
To learn about refactoring, buy “Refactoring” by Martin Fowler and read it multiple times.
Refactoring is an (almost) always green technique. When you stay too long in a state with broken tests (not green), it is not refactoring. To stay green, you have to take small steps (changes). To perform a big change, you chain the little changes. For every step, you make the change easy first (this may be hard to do) and then make the easy change (freely Kent Beck).
Brutal Refactoring = Refactoring + TCR
Refactoring requires small changes and always green tests. TCR ensures both. Together, they are Brutal Refactoring and a great fit.
I defined refactoring previously, and I mean brutal in the sense of:
brutal: Direct and without attempting to disguise unpleasantness. — Oxford Dictionary
When I refactor the small steps, and the always green tests are a real challenge because they require discipline. However, it is always tempting to drop this discipline (e.g., when I get tired or distracted). With TCR I can stop caring and focus myself on the task of refactoring the code. Refactoring is challenging enough, and I am happy about every free mental resource.
Concrete Advice
When doing Brutal Refactoring a try, I replace my test command with:
./build.sh && (./test.sh && git commit --am working || git reset --hard)
The build-step aborts the command I have a typo or something else and prevents resetting.
You recognized that the command pollutes my commit-log, so every time I reached a meaningful goal, I can clean up my log and provide a useful message via this script:
$ cat commit_message.shn=$(git log --pretty=format:'%H||%s' | grep '||working' | wc -l)
git reset --soft HEAD~$n && git commit
Brutal Refactoring makes the most sense when I execute the TCR-command every time I change a file. Working on Linux, I have ‘inotify,’ which watches my file-changes:
while true
do
inotifywait -r -e modify .
./build.sh && (./test.sh && git commit --am working || git reset --hard)
done
In pseudocode, this means:
while(true):
whaitForFilesytemChange()
if not programBuilds():
continue; if testsAreGreen():
commit()
else
revert()
This TCR variant is called “The watch buddy.” There are others, and you can find the one, that fits your needs best on this article.
To stay up to date, you can look up my twitter account: @deniffel.