I've moved this blog over to wordpress. Feature-wise, it appears to be the superior platform.
There are still a few things I don't like about it, but it appears to be customizable enough that I can fix them as I have time.
Anyway, I've imported all (ok, both) of the posts from this blog over there, and put a new post up.
Please update your feed readers to point to http://kbaribeau.wordpress.com/
Sunday, May 03, 2009
Thursday, March 19, 2009
Hard Problems
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:
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.
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
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.
Tuesday, March 03, 2009
Focused Practice
We all want to be better at what we do, right? But how do we go about improving our skills? I think the answer is the same, whether you're an aspiring software developer, musician, martial artist, or whatever. The key is focused practice. You need to put time into developing your skill. You can't just put time in either. It has to be focused time. This is time where you're consciously thinking about your craft, critically analyzing your work, and looking for ways to improve it.
Some of this possible during your day-to-day life; but in my experience, you always get better results by setting aside a special block of time to work on a skill.
Code Katas
The best way I've found to apply this to software is through code katas. A code kata is a problem simple enough that developers of any skill-level should be able to solve them. A kata is also small enough to solve in a reasonable period of time. My learning process currently goes something like this.
- Pick a skill I want to improve at.
- Pick a kata to solve, and a language to solve it in.
- Solve the kata while focusing on how to apply that skill to the kata.
- How much code am I writing per test? Should I be writing more? Less?
- Is my test naming clear?
- Am I remembering to think about refactoring every time I see a green bar?
- Am I aware enough of what's going on to know when I've made a mistake and chosen a bad test?
A Coding Dojo
There's been lots of success stories recently about coding dojos. A dojo gives us a social context in which to work on our katas with other like-minded people. It's a great mechanism to get feedback on your work, but the downside I've found is that a lot of people are intimidated by this. I think the best way to deal with it is just to work on a kata in your own time until you're comfortable with it, and then bite the bullet and seek some feedback.
You can find more information about code katas and dojos at http://codingdojo.org. See the kata catalogue if you just want to get started. There is another list of problems I've found useful here.
Subscribe to:
Posts (Atom)