Betrayed by Your Test Runner
The Halcyon Days of Yore
I was writing some code for Apex this morning and I had a strange sense of deja vu. Without going into painful details, Salesforce is a cloud-based CRM solution and Apex is its proprietary programming language that developers can use to customize their applications. The language is sort of reminiscent of a stripped-down hybrid of Java and C#. The IDE you use to develop this code is Eclipse, armed with an Apex plugin.
The deja vu that I experienced transported me back to my college days working in a 200 level computer systems course where projects assigned to us were the kind of deal involving profs/TAs writing 95% of the code and we filled in the other 5%. I am always grateful to my alma mater for this since one of the things most lacking in university CS education is often concepts like integration and working on large systems. In this particular class, I was writing C code in pico and using a makefile to handle compiling and linking the code on a remote server. This generally took a while because of network latency, server business, it being 13 years ago, a lot of files to link, etc. The end result was that I would generally write a lot of code, run make, and then get up and stretch my legs or get a drink or something, returning later to see what had happened.
This is what developing in Apex reminds me of. But there’s an interesting characteristic of Apex, which is that you have to write unit tests, they have to pass, and they have to cover something like 70% of your code before you’re allowed to run it on their hardware in production. How awesome is that? Don’t you sometimes wish C# or Java enforced that on your coworkers that steal in like ninjas and break half the things in your code base with their checkins? I was pumped when I got this assignment and set about doing TDD, which I’ve done the whole time. I don’t actually know what the minimum coverage is because I’ve been at 100% the entire time.
A Mixed Blessing?
One of the first things that I thought while spacing out and waiting for compile was how much it reminded me of my undergrad days. The second thing I thought of, ruefully, was how much better served I would have been back then to know about unit tests or TDD. I bet that could have same me some maddening debugging sessions. But then again, would I have been better off doing TDD then? And, more interestingly, am I better off doing it now?
Anyone who follows this blog will probably think I’ve flipped my lid and done a sudden 180 on the subject, but that’s not really the case. Consider the following properties of Apex development:
- Sometimes when you save, the IDE hangs because files have to go back to the server.
- Depending on server load, compile may take a fraction of a second or up to a minute.
- It is possible for the source you’re looking at to get out of sync with the feedback from the compiling/testing.
- Tests in a class often take minutes to run.
- Your whole test suite often takes many, many minutes to run.
- Presumably due to server load balancing, network latency and other such factors, feedback time appears entirely non-deterministic.
- It’s normal for me to need to close Eclipse via task manager and try again later.
Effective TDD has a goal of producing clean code that clearly meets requirements at the unit level, but it demands certain things of the developer and the development environment. It is not effective when the feedback loop is extremely slow (or worse, inaccurate) since TDD, by its nature, requires near constant execution of unit tests and for those unit tests to be dependable.
Absent that basic requirement, the TDD practitioner is faced with a conundrum. Do you stick to the practice where you have red (wait 2 minutes), green (what was I doing again, oh yeah, wait 3 minutes), refactor (oops, I was reading reddit and forgot what I was doing)? Or do you give yourself larger chunks of time without feedback so that you aren’t interrupted and thrown out of the flow as often?
My advice would be to add “none of the above” to the survey and figure out how to make the feedback loop tighter. Perhaps, in this case, one might investigate a way to compile/test offline, alter the design, or to optimize somehow. Perhaps one might even consider a different technology. I’d rather switch techs than switch away from TDD, myself. But in the end, if none of these things proves tenable, you might be stuck taking an approach more like one from 20+ years ago: spend a big chunk of time writing code, run it, write down everything that went wrong, and trying again. I’ll call this RDD — restriction driven development. I’d say it’s to be avoided at all costs.
I give force.com an A for effort and concept in demanding quality from developers, but I’d definitely have to dock them for the implementation since they create a feedback loop that actively discourages the same. I’ve got my fingers crossed that as they expand and improve the platform, this will be fixed.