Throughout my growth as a developer, I’ve heard a lot about TDD and it’s greatness. I’ve seen, felt, what it strives to prevent and have learned the importance of solid code coverage. It’s not about lines covered, but scenarios, edge cases, and complex inter-object interactions thoroughly tested.
But time and again I find TDD getting in my way of getting shit done. Let’s face it, for all the time it saves you in the future, TDD is a laborious process. Maybe not when you’ve mastered it, and I certainly haven’t, but there is something more counterintuitive – it’s very approach – which is why I develop using EDD or evolution Driven Development.
This doesn’t mean that I don’t write tests. The code I write is backed by a safety net of testing meant both to convey expectations and prevent the new from breaking the old. I just don’t write them first. As the name implies, I got this idea from natural selection and evolution.
Mother Nature doesn’t use TDD, and she’s done pretty damn well.
Think of what time and struggles went on for humans to evolve: innumerable failed prototypes, abandoned branches, and billions of lifeforms dead from experimentation. Luckily, our craft has little penalty for shitty iterations. Command-N in most text editors gets us back, or a well executed git revert.
What boggles my head about TDD is the assumptions it makes. When I begin a project or a new feature, I don’t know what the fuck is going to come out of my head. That blank-canvas-driven creativity leads to novel solutions and unique approaches that keep my code fresh. Many TDD’ers I know would say that if you’re doing TDD correctly, you make 0 assumptions about implementation. I hear that, and it’s total bullshit.
While you’re writing code, deciding what to test and hat is trivial, you make choices. You think about the behavior of the code. At that point, the desired behavior already implies an implementation strategy, whether you care to admit it or not. So by the time you actually get around to writing the code, you are already primed with the complexity of the project at hand.
Just shit it out
With EDD, you just shit it out. If something breaks, you fix it. You build fast on top of code you wrote before and write new code to be as open as possible. You make no assumptions about the future because you are literally writing it. When you have a finished product, you go back through and test for the essentials: the PRE and POST conditions of methods, the functionality of classes, etc.
Keep that hackathon creativity, fail fast, and learn faster. Feel free to experiment and then delete it if it doesn’t work. You’ll stop shipping shitty code because it makes the tests pass.
I ran into an unfortunate scenario recently where a team member of mine had written a beautiful navbar in only JS that would dynamically update based on cart contents and logged-in user. This meant that many pages could be entirely cached and a significant improvement in page load time. And we could use it. Because it broke all of the tests. We toiled away with Capybara and Selenium, but it was just taking too much time. We could see that it work, could feel that it was fast. But our test suite stopped us from implementing a new feature.
And that sucks.
Let me be clear – this does not mean you should ship code without tests. Thorough tests. Carefully written tests. I’m saying that you shouldn’t write tests first, because it limits your creativity and ultimately slows you down. Stop fixing tests when your implementation breaks them, and start writing awesome code without the fear of red.
Comments on: "Mother Nature doesn’t use TDD, and she’s done pretty damn well." (2)
Sounds like a “Test everything” strategy and IMHO just like the wrong way.
How about some virtual machines with win, mac and linux and different browsers running on a server somewhere? There no better way to test JavaScript and CSS.
“I’m saying that you shouldn’t write tests first, because it limits your creativity and ultimately slows you down.”
Interesting. I never thought about the creativity aspect.
I find TDD doesn’t work because I often write code that I then change. If I had written tests before the change, then I need to change the tests as well. If I write the code when I’m more certain of its longevity, then I skip the waste.
We have five teams in our department: two use TDD, three don’t. The three that don’t consistently release code quicker, with just as high quality as the slower, TDD teams.
I wasn’t surprised at this when we first began this experiement, but I was surprised that the TDD teams never increased their speed: they were always slower. Even top TDD folks say that TDD is hard, so I had expected that the TDD teams simply hadn’t hit their stride yet.
But they still haven’t, even now, when they’re proficient at TDD.
And of course, they never will: they’re using a process that’s inherently wasteful.
Eventually, of course, management will have to step in and demand at least as much productivity as the better, three teams.
TDD doesn’t work.
No amout of wishing will make it so.