I ran into a hard problem today at work. Here it is:
Given two strings, check that the first does not contain a sequence of 3 consecutive letters that are also contained in the second.
Look easy? It should. I don’t know of very many easier problems. After implementing it though, I can’t truthfully say that it was easy for me. But, I don’t think the lessons I learned today are easy either. As far as I can tell, I made three mistakes:
Lesson #1: Be careful when picking your test cases
It’s easy to pick a test case that’s either too hard, or too easy to implement. If you pick a test case that’s too easy, you and your pair risk getting bored. Oddly enough, this is almost never a problem unless you’re actively trying to avoid picking a test case that’s too hard.
So, if you’re bored, then your tests are too easy… how do you tell when your tests are too hard? Well, that’s another hard problem. I don’t think I’ve nailed this down completely yet, but here are some clues I’ve learned to spot:
- It takes more than one try to get your newest test to pass
- When you do get your test to pass, another test fails
- Your pair doesn’t understand what you just did
- You find yourself wanting to refactor against a red bar
My advice when you run into any of these indicators is to stop. Stop writing code. Revert to a green bar. Now you have two options: Refactor, to make things easier; or pick a different test.
Sound easy? Try it. There’s two reason why it’s not.
First it’s hard to admit when you’ve picked a test case that’s too hard to implement. We’re programmers (craftsmen if you will), it’s our job to solve hard problems. We take pride in our ability to do so. We want to make that next test case work. Taking a step backward and reverting the code you just wrote (that doesn’t work) is an admission that you’ve made a mistake. Admitting this mistake is especially hard to do when you’re working on an “easy” problem.
Second, both of these options require you to THINK. It’s tempting to think that if you tweak a conditional here or extract a method there you’ll see a green bar soon. This rarely turns out to be the case. Even the easiest problem is going to require you to stop and think about the solution; probably more than you expected to.
If you run into this, Stop. Think about the problem. Discuss it with your pair. Then, take another crack at it.
Lesson #2: Commit Often
Today, my pair and I ran into all of the clues listed above, and failed to stop. It was at this point that we got burned by lesson #2. We realized what was going on, and wanted to revert to our last green bar. We hadn’t committed in 90 minutes. We were stuck with broken code. Oops.
I don’t have a strong opinion on when you should commit. Ideally, I would say you should commit on every green bar, or after every refactor step; whichever is easiest to remember for you. Of course, some teams are cursed with long running unit tests. Since you don’t want to commit without running your tests; this makes it difficult to commit so frequently. Do what you can to keep your tests running quickly, but in the meantime, find some balance between committing often and not letting your tests slow you down.
Lesson #3: Focus on your work — No Distractions
Today, during the “embarrassing pairing session”, I had an interesting email thread zipping through my phone, which dutifully beeped at me every few minutes. Every once in a while I’d try to keep up with it, meanwhile completely losing my focus on the problem and relying on my pair to get me back up to speed when I was done.
Please, be wary of checking your email or other distractions when pairing on a problem. If you’re not at the keybaord, your job is (among other things) to help figure out the next test, watch for warning signs like the ones I listed above, and maintain code quality. You probabaly can’t (I know I can’t) do any of these things while checking your email, or carrying on a casual conversation with a friend. Also, respect your pair. If they’re working, you should be too. They’re going to resent you if you don’t pull your weight.
By: Kevin Baribeau