I got some comments from Renzo on a previous post I wrote about Clojure and testing, and some feedback on a recent talk I gave, via email and talk down the pub after. I ponder some of these points here.
I’ve made the point that using Clojure really does force a reappraisal of the TDD situation, especially if you’re coming from an OO background. I still think this is the case, but at the same time I acknowledge that’s there nothing uniquely special about Clojure that makes this so.
Renzo suggests that some people are fine without tests because ‘they can easily grasp a problem in their head and move on’. I agree with this but I also think that the language of choice does have a direct bearing. Put bluntly, I think that most people using Clojure will write less unit-tests compared to traditional OO programming.
It’s all about feedback.
If you were to create a shopping list of things you really want for your development experience then what would you put at the top? I think most people would put ‘rapid development’ at first place. I.e. you want to make a change somewhere in your codebase, hit F5 in your browser or wherever, and then to see the changes appear immediately.
And to be honest I think this aspect of development – rapid feedback – is absolutely key. If you can just change code and see the results immediately then it really does lessen the value of writing actual test classes to gauge the results of intended changes. This may not be completely healthy, and one could imagine more cases of sloppy hacking as a result, but I’d be surprised if there wasn’t a coloration between fast feedback of changes and the amount of unit-test classes written. For example I’d be interested to know if Java devs using the Play web framework end up writing less unit-tests as a result of moving from Spring/Struts etc. I’d expect so.
Clojure is nothing special for giving rapid-feedback on changes, just glance across at the dynamic scripting languages such Ruby, Python and PhP. But if you’re coming from the enterprise Java world, where I’ve spent some years, then this is one of the first things that really knocks you on the head.
I think the next item on the list of ‘top development wants’ will often be an interactive command prompt; the REPL, for reasons that are well documented.
I gave a recent talk and one person said that it feels strange for him as a Python developer that I and others are talking up a tool that so many have taken for granted for decades, like we’re raving about this thing called the wheel.
This is a fair point, but it doesn’t take away the amazing difference for devs coming from the traditional statically typed OO landscape, where the REPL is not so much employed.
And I think this does have a direct affect on the TDD process. Primarily, you have a place to explore and to play with your code. Put the other way if you don’t have a REPL and you don’t have ultra-fast feedback on changes, then what do you do? You’d probably want to write lots of persistent unit-tests, or main methods, just so that you can pop into the codebase and run small parts of it.
FP and Immutability
Immutability makes for a saner code-base. I make the point that if you work with a language that’s opinionated in favour of propagating state-changes, then it would perfectly reasonable to want more tests in place, as to pin desired behavour down.
FP and dynamic languages lead to a lot less code. There’s less ceremony, less modeling. Because you’re managing less code you do less large scale refactorings. Stuff doesn’t change as much once it’s been written. The part of traditional TDD that leaves behind test-classes for regression purposes is less needed.
It’s my current opinion that what you get left out of TDD once you have amazingly fast feedback and a REPL is regression testing.
This is still needed of course, but there’s huge value as treating it as a separate concern to other forms of testing. I’ve been guilty of seeing it all wrapped up in TDD, as not something distinct warranting its own attention.
In some respects deciding how much regression tests to apply becomes a bit like ‘how much performance tuning do we need’. It depends on the business context, and it’s probably better not to do it prematurely, and with consideration and measurement.
There is an argument that unit-tests serve a purpose for live documentation. I would just question this. Although some FP code can be difficult at first glance it can still be written expressively, using vars with names more than one character. And there’s always comments with examples.
This is my view on why devs using Clojure seem to write less unit-tests. I wouldn’t go to the next level of dogma and dare say that devs shouldn’t ever write unit-tests. In fact I’d expect any unit-tests on a Clojure project to be of high quality and to really have a purpose. I’m also a bit reluctant to judge TDD as a whole. I think TDD for masses of devs including myself has taught us how to effectively refactor, and through some definition of TDD we’re probably still doing it, whether it’s through the REPL or just by hitting F5.