TDD and refactoring are permanently intertwined activities, neither of which can be grasped all at once, so start learning how to write tests at the same time you’re learning how to change code w/o adding bugs to it.
Some activities are what I think of as "life games". The better you get at it, the more there is to learn. I call them games, cuz my first two noticed experiences of this were in the games of pool and of go.
I would get to a certain level of skill and insight at pool, thinking I would, idunno, solve pool, only to discover time and again that conquering the game at level X just means that now you get to tackle level X+1.
So, too, with tdd & refactoring. You’re not gonna solve tdd or refactoring. You’re just gonna get past level X and discover level X+1. That’s the part where you should start now, and assume it’s gonna take a while.
What about the intertwining part? What i’m saying is that we shouldn’t even be calling them separate skills, really. It’s not possible to do TDD w/o refactoring code. It’s possible to refactor code w/o doing TDD, but only at the first few levels of the game, and not beyond that.
At the very first level of both skills, your central focus should be entirely on rhythm. On seeing the beat of your mentors doing it, and on noticing the beat you keep, or lack thereof.
In benjamin zander’s excellent and amusing ted talk, he talks about how one grows in musical skill by experiencing the music as unitary in progressively larger and more complex chunks.
The pieces of the modern synthesis — TDD & refactoring & ci — that your mentors play, & the way they play them, are typically far outside the reach of a newcomer. They can play for you, but they can’t magically give you their ability to handle chunks at their level.
So then, what are the level 0 skills, and what should you do to get to level 1?
On the TDD side, the first thing you need to do is be able to take a toy problem, a leaf or near-leaf in the dependency tree, and write tests such that it would be impossible to break it w/o making some of those tests red.
On the refactoring side, there are a handful of tiny code changes you need to be able to do with zero fear. I think I might stumble in this muse if I gave a definitive list, but here are a couple just to give a sense of how easy/hard i’m talking about.
Move a variable declaration or initialization up or down the body of a method. Transition between the standard loop forms in your language. Replace nested no-op branches with exit clauses. Invert straightforward conditional logic.
From there, at the next level, for testing you want to be able to take a similar-sized toy problem and do red/green red/green red/green, developing the answer as you go.
Again, on refactoring, rename anything, extract/inline method, parameter, variable. Basically all the things that static-language ide’s can do for you guaranteed-safely. U need to know how do them manually, too. 🙂 and this goes on and on. There’s a level 3, a 9, an eleven-ty-seven.
Ultimately, to get to a strong beginner level, you’ll know a few dozen transitions you can apply on the refactoring side. And you’ll know why you want to apply them, because TDD style assumes we start without having finished everything but the typing.
On the testing side, one gets progressively better both at reworking untestable code to make it testable, and at ordering your tests so that they create less and less untestable code along the way.
You learn about the variety of awkward collaborators, & a range of tricks for working with them. You’ll get a feel for tests you shouldn’t bother writing, and ways to avoid testing code like databases or html transports that you don’t own and you have to take for granted work.
Even tho you can learn to make modest changes in code w/o adding bugs not having any tests at all, sooner or later you realize that the changes you want are too damned big and scary to work on without test backups.
So this tip is vastly shorter than the last one. This tip isn’t so much about the practice of the modern synthesis as about understanding how to grow yourself in that practice.
(and understanding that it is indeed a steady ratcheting, not an all-at-once thing.)
Don’t mentally isolate refactoring & tdd,. Don’t think they are one-level techniques. Assume that doing is the center, not knowing.
TDD & refactoring are permanently intertwined, and they’re both life games.
Get started, eh?