I talked about this with a bunch of people at RubyAndRails 2010 a couple of months back, but never found the time to write it down.
In TDD, we use a very short development cycle to get something to work. We write a test, watch it fail, implement the feature and watch it pass. Then we move on to a new test. Simple.
But, to be able to write a test, we need some information to start out with. Most of the time, we probably have that information already. You know your stuff good enough to be able to write tests before doing anything else.
Seriously, stop it. Or TDD rex will eat you.
But sometimes you’re working on something you don’t have any experience with in a library you never worked on before. And you don’t know what code you need to write, meaning you can’t write any tests for it. What now?
You dive in and write a prototype. You don’t worry about tests or clean code, you just hack until the thing works exactly how you like it. It probably won’t take you long before you have something that does what you need. You show your friends and co-workers and they tell you you did an awesome job.
You’re eager to get your library out there, but you remember everybody screaming you should write tests “all the fucking time”. So, for that reason, you dive in once again and slap some tests on your new library. Now it’s tested and your work is done. The last thing you need to do is think of some fun, mildly sexual name before releasing it.
No, no, no, no, no, no, no!
Building that prototype helped you to figure out what needed to happen and what the nicest implementation of your feature was. You didn’t worry about writing tests or clean code, you just wrote the feature.
After that you’re quite proud of your work and there doesn’t seem to be anything wrong with it. So, you decided to add some tests. More often than not, this leads to poorly tested code.
Doing TDD, you would have written as little code as you needed to get your test to pass. When writing your implementation first, chances are you wrote more code than you actually needed. Even if you completely test this extra code afterwards, it still adds bloat to your library. Besides that, unless you’ve seen your test fail, how do you know if it works?
I believe you should’ve thrown the prototype out and started over using the new knowledge you gained from creating it. You probably learned enough about the existing code and your implementation to be able to write proper tests.
So, instead of throwing tests on prototypes, let’s try to throw our prototypes out and start over. Yes, this takes way longer, but I know your clean, stable and maintainable implementation will make up for that.