Although I’ve been a believer in TDD/BDD for quite a while, rush was the first time I started a project and said, ok, THIS time I’m going to get really serious about it. My very first commit was a bunch of empty specs, and since then, I don’t think I’ve committed any new feature or even a bugfix without an accompanying spec.
A few weeks into the project, I had made really substantial progress and had quite a lot of functionality. One day, while using it to access a remote machine to do some file operations, something very surprising happened: I typed a certain command and it didn’t behave the way I expected it to.
I figured I must have a typo or something, but double-checking it, I realized that, no: my command was correct, it was the program which was behaving incorrectly. I had found, it seemed, a bug.
I felt a sudden sense of disorientation and panic. The program wasn’t behaving as expected? What should I do? Log things? Run a debugger? Just squint at the code for a while and see if I could spot the problem? These methods seemed so…crude.
And yet, I realized, these techniques are the very ones I’ve been using my whole career, that I use every single day to get work done. I don’t give them a second thought. Yet, after two weeks of doing pure BDD, the idea of spending time dealing with a bug seemed foreign and painful.
I ended up tracking down the problem and discovered that, despite my nearly 100% code coverage (as reported by RCov), I had a fork in a single-line conditional that was not speced. Upon realizing this, another feeling washed over me: a sense of mistrust in the project’s codebase, because a fragment of one line of code did not have spec coverage. I wrote the spec, confirmed that it failed / exposed the bug, and then fixed the bug itself ten seconds later. Relief flooded over me: things were right with the world again.
Nearly all developers, myself included, spend most of our time in that state: not quite trusting that the code all works. I only had the chance to realize how stressful and unpleasant this state is because I lived outside it for a little while.
This experience also told me something else: after several weeks of intense development, this was the first time I had encountered a bug. I feel absolutely certain that that’s never happened to me before.
You could argue that rush was not then (and is still not now) a very big or complicated program, but I don’t think that’s significant. First, by this time I was already using it to control a remote server (i.e., it was communicating commands over the network via rushd); that’s the sort of thing that is highly prone to bugs. But more importantly, even very short and simple programs often have bugs lurking in them.
My whole life I’ve assumed that bugs are a given in software, and that trying to eliminate them completely is a waste of time. And I think I was correct, for the traditional, non-BDD approach. (“Code-driven development,” I guess?)
But it may be that a very disciplined approach to BDD means the opportunity to have truly bug-free software. Just imagine what it would be like to have all the time you spend debugging code go into writing it instead. Granted, a lot of that goes into writing specs; but for me, writing specs is far more fun than debugging. It’s programming, not sleuthing.
Bug-free software is a very bold claim, though it does require a somewhat narrow definition of the word “bug.” A bug is a situation where the code has been specified to behave one way, and instead it behaves another. It does not include things that users or developers may like the software to do, but that it is not specified to do currently. It also doesn’t include external depedencies (libraries, web services..) behaving in an unexpected manner - good software should try to recover gracefully from failures in external services, but doing so is a type of feature.
Look back at that definition of bug again: ”…the code has been specified to behave one way…” BDD is the very act of writing executable specifications. In non-BDD, there is no specification; so by definition, software written that way can be said to either have an infinite number of bugs, or perhaps just no functionality that you can rely on.