Confessions of a Cowboy Coder

First off, I'll say, Hi. I'm Jiva, and I'm a cowboy coder. I didn't used to think I was a cowboy coder, but some things have happened lately that make it impossible to deny.

I've been interested in XP since I first heard about it in 1999 at SDWest in San Francisco. At first, I remember summarizing it to a friend as "Anal retentive testing, micromanaging the schedule, and no up-front design." Over time, however, as I came to understand it better, I realized how wrong I was in this first impression. The areas I saw as restrictive and difficult were actually liberating in their power to help free me from the cost-of-change curve.

In the end, I came to appreciate XP as a real enabler for coders to do what they love best, CODING. I became convinced it was an excellent tool to add to my arsenal of methodologies. I took it upon myself to educate my fellow coders, and spread the gospel.

And then one day, it happened. I became... a manager! But not just a manager... a manager for a project starting completely from scratch! With no baggage from previous projects! What a perfect opportunity to actually TRY XP in a real-world-environment!!

I educated the members of the development team I was herding^H^H^H^H^H^H^Hmananaging about XP. I taught them about test-first programming. I showed them the cost-of-change spiral. I brought index cards to every meeting, and we specced the project on those cards. I was an XP *machine*. I bought all the books and read them twice. I brought in local experts on XP to give us classes.

And in the end... the entire team saw that it was good, and everyone did their part to help implement it. People who previously hated methodologies and processes, were using XP every day. We built our own automated test environment, and we held to the policy, "100% of the tests run 100% of the time!!!" If the code we were working on didn't run 100% of the tests, it didn't get merged into the development tree! The work was spread out amongst us, so that no one person could diverge far from the main tree without merging. I was very pleased.

The quality of code we developed in pairs was *easily* heads and shoulders above code we wrote by ourselves.

And then, it happened.

In order to get a particular peice of code working, I went home one night, and from 11pm to 2am, hacked out some beautiful code that we needed. It was just one module. And we needed it to get some other things done.

"I don't need tests for this." I said to myself. "The other modules that depend on this will test it just fine! And besides, it works! See! I don't need all that extra stuff. That makes me so inefficient. My code usually works pretty good the first time through anyway. This will be OK for now."

"I don't need to pair program this." I also said. "This is really easy. I'll just slip it in there and noone will notice."

Yes, you know where this is going, don't you?

So I brought in the code, and I put it in the code base, and VOILA! It worked! It worked GREAT! See! I was right! And we continued on with our coding.

About a week later, we began noticing some new problems in a particular part of the code. A part which was wrapped up fairly closely with the code I had written in my midnight coding spree.

We began debugging the system. We wrote more tests for the modules that used my code. We ran it through the debugger. We poked it and prodded it. We peer reviewed it.

"Must be a rogue pointer!" We said. "The comm library must be too slow!" We said. "Damn that C++ string class! It's always acting strange." We said. "I'll bet this is a bug in the compiler!" We said.

We didn't write tests for my code because, well, afterall, it was working before, right? And besides, NOW, after the fact, it would take too long to write all those tests! We don't really need them, right?

Right?

Well, anyway, we spent 3 days trying to figure out this problem. We found lots of bugs, and we fixed them all, but none of them were the problem we were experiencing. Interestingly, as we worked the problem, it seemed like we kept following through on theories that lead no where. We went through a TON of rigamarole to find this elusive bug, that just wouldn't show itself, and was even hard to narrow down to a particular module.

Finally, I said "Forget this! We're spending all this time contorting ourselves to test my code that I have no unit tests for. I'm just going to write the tests and see what the damn thing is doing."

It took me about 2 hours to write the tests. And within 30 minutes of having the tests done, we found the bug.

Was it in my code?

Well, yes and no.

Was it in someone else's code?

Well, yes and no.

It was in a misunderstanding of how the two peices of code work together. But that doesn't really matter. The reasons we couldn't find the bug before were several:

  1. Because we had no tests, noone actually trusted my code! (Including myself) which meant we spent a TON of time looking at code that a test would have shown *conclusively* worked.
  2. Because it had not been pair programmed, my misunderstanding of how another component worked caused me to write my code in such a way that it might fail *occaisionally* if given a particular circumstance.
  3. Because we had no tests, noone but me actually knew how my code worked. If we had had tests, at least we could have consulted the tests and compared that to our code that used it.
  4. "If you cannot test it. It does not exist." Interestingly, and this is an strange psychological situation, MANY times during the process, we *all* ignored my code as being part of the problem BECAUSE IT HAD NO TESTS and so therefore, in our minds, subconciously, IT DID NOT EXIST!

In any case, in the end, I have learned my lesson. I admit, I *am* a cowboy coder at heart, but that's not what the team and the project needs most. I must restrain myself from coding against the rules *I* myself set! The resulting code is not *nearly* of the quality of the rest of the system, no matter how hard I try.

(C) Copyright 2002, Jiva DeVoe

PowerCard | TreeTracker | Contact Us