If I told you that writing functional tests was my most favorite thing as a software developer, you would most likely laugh and tell me I was joking. And you would be right because writing functional tests is not my favorite thing (though it might be better than drinking really bad coffee… not sure yet).
Most developers share my not-so-enthusiastic response to writing functional tests. Many loath the task more than me and do everything in their power to avoid it. Or they get it done as soon as possible and write worse-than-maintainable code to do so (guilty as charged there too).
Why is this? Is it really that bad? Well, as the title of my article partially implies, I believe that part of the problem is that when a developer is asked to test a piece software at the functional (a.k.a. black-box, end-to-end, etc) level, they often don’t know what its actually supposed to do. They have to guess or ask the implementing developer and it all gets confusing and becomes a big mess.
And I think the reason many developers don’t know what some piece of software is supposed to do is because it was never agreed in the first place. And it certainly wasn’t documented.
Documentation as a Chore
Documentation is viewed of as an “after thought” in a lot of software development teams — though we get angry when a third-party library treated it the same way. No one wants to write it, review, keep it up to date. We view it as a chore we have to do. In fact, if your a developer, you’ve liked rolled your eyes once or twice at me brining it up.
An example of viewing documentation as a chore might be the following documentation for an Ansible module :
The short and long descriptions are the same, I don’t explain anything helpful about why I built it and how to use it. Its just there. It doesn’t add any real value.
Documentation as as Tool
I think if we can embrace documentation as a tool, it can help us write better software.
I recently took a quick week long Sabbatical at my company Bandwidth.com, and I found myself struggling to understand the whats and hows of an Ansible module I was writing. I won’t go into the details right now, maybe a blog post later, but suffice it to say that I couldn’t nail down exactly what my module was doing and how it was doing it. I had some vague ideas, hence the Sabbatical, but nothing concrete.
I had recently heard Micheal DeHaan, creator of Ansible, speak at a lunch-n-learn event, and one of the key takeaways from his lecture was to view your documentation as a necessity to keep and maintain users. Users want both a quick 2–3 minute summary, and then a thorough set of all the features your software offers. If you don’t have both, you’ll either not get them to try your software, or not retain them long enough to actually use it.
This got me thinking: What if I used documentation in the same way that DeHaan was talking about, but for myself? What if spent time up front writing up documentation to convince myself that what I was attempting build was worth trying and using? After all, I was writing this module to be used as a tool by myself and my team.
Having Documentation Means Easier Coding
So, I followed DeHaan’s advice. Instead of spending hours and hours trying to keep things straight in my head as I hacking away at my
module.py script, I spent hours and hours writing documentation in a
README.md file. Not the most exciting thing in the world, but I got through it.
Contrast this example of my module documentation with the first example earlier:
The behavior of the module is implicit if you read the set of options allowed. And since I did that work first, I already knew the behavior I was aiming for instead trying to figure it out as I went along.
And I got to tell you, after that first day of writing documentation over and over again, writing code was just, well, easier. I knew the functional tests I needed to write, and I roughly knew the units tests I needed to write if I kept things to simple behavior-to-code implementations (ie if I needed to say print “Hello World!”, maybe I would just start with a
printHellowWorld() method; not scalable for large systems I know, but perfect for a small python file). I had already figured out the gotchas I would likely face in my code and had also realized how to simplify code I hadn’t even written yet.
I cranked out the tests and actual code in roughly two days and then spent the last day and half writing even more example documentation, which made me realize additional places I could simplify things. It was pretty cool.
Lets Not Get Carried Away Though…
I’m not suggesting the every project needs to do this to the same level I did — you might likely never start if you had to write documentation to answer everything about your software and is one of the reasons the Agile Manifesto calls out “working software over comprehensive documentation”. However, I think that my experience showed me that treating documentation as a first-class software artifact can help you write better code. And for those of you thinking “just write self-documenting code”, I totally agree that you should aim for that. I just also know that the idea of self-documenting code is viewed more of as a style and fall-back measure than treating documentation as a vital part of healthy software systems.
All I’m trying to do here is encourage developers to stop putting off writing good documentation as a chore and instead embrace it as another way to write better code. I’m not a saint myself when it comes to this either, but my experience with this pattern has motivated me to put more effort into it. Plus, the users of and contributors to your project will be happier with more (accurate) documentation too!