We’ve all done it. There’s a little bug somewhere in an app, an unexpected null or the like, and we throw together a quick commit to fix it. Then we push it straight to master, confidence overflowing, telling ourselves that there’s no way this doesn’t solve the problem. We’ll triumphantly declare something to a colleague to the effect of “I’ve fixed that little bug by the way – was just a one-liner”.
Before the endorphin high has even truly subsided, a notification pops up. “Build Failed”. Panic sets in, ego in tatters, and we discover that there was in fact another small error that needs our attention. We’ll take a look and “hey, it’s just another tiny tweak…”
20 GOTO 10
Something akin to this happened to me recently whilst pairing and ended up proving very costly in terms of how long our piece of work took. On reflection, it was such a blunder that we ended up coining the term “Faith Driven Development” to describe the phenomenon (credit to my colleagues Richard Wilmer & Hannah Brown). It’s been stuck in my mind ever since and has been a useful lens to examine how efficiently we can write and maintain software.
What is Faith Driven Development (FDD)?
Many of you will be familiar with Test Driven Development (TDD) – the process of writing tests first and then creating the production code to make them pass. This is a well-established technique to help create clean code with good test coverage. You may also have heard of Behaviour Driven Development (BDD), which is a higher-level process and helps when adding large new features. There are several other _DDs out there, so what’s the harm in one more? Unlike most of the existing _DDs, however, this one is something to avoid whenever possible.
We’ve defined FDD as “A development process in which modifications to the code are tested by pushing them straight in and seeing what happens.”. We are relying on little more than a developer’s faith in their own ability.
Reading that definition, you may have recoiled in horror at the suggestion you would do such a thing. Be honest with yourself though. Look at the example at the start of this post and truly consider if you ever exhibit similar behaviours. (If you can honestly say you haven’t, please check out our careers site here.)
When does it happen?
The situation at the start of this post isn’t the only time you can end up taking this brute-force approach to development. There’s plenty of corners this ghoul is hiding around, just waiting to ruin your day. As mentioned earlier, I started thinking about this following a story I’d been working on (a story is a constrained task or piece of work as part of an agile workflow, see more here), and I’d like to outline what happened so you can see how easy it is to fall into this anti-pattern.
We were working on a fairly cutting-edge bit of software, expanding the capabilities of the Auto Trader Data Mesh. As with many things in the data engineering realm it was pretty hard to test out locally. We needed some fairly large datasets to run the code against and so when we ran into issues, we found ourselves releasing our changes and seeing what happened in the data store. Each time we did it took around 30 minutes to see the full outcome. The first couple of times it wasn’t too bad, but as we pulled on the threads and found more and more issues the slow feedback loop was dampening our spirits and hurting our productivity.
The cost of FDD
So what is the true cost of falling into this trap? With hindsight it’s usually easy to see – time. Having to make multiple pushes to the same repository and waiting for builds in a CI pipeline is almost always slower than testing that changes actually solve the problem before releasing them. It’s also more mentally taxing; by forcing you to check back in infrequently to see how you’ve impacted the app you are prevented from concentrating on your next piece of work. Context switching ruins productivity.
The cruel and ironic part of FDD is that when it occurs you’ve usually done so because you were trying to be quicker. Trying to get that bug fix released as soon as possible led to it staying live longer. A better philosopher than me could probably get a whole book out of that.
How to avoid it
Slow down. Breathe. Think. Write a test to capture the thing you’re trying to fix. It’s usually the little things we all know we should do all the time but are easily pushed aside when there’s a little bit of pressure.
In the more subtle examples, such as the story I’d been working on, it’s worth considering up front how you’re going to get feedback on your work. If it requires a release, try and find some way to make the loop shorter. On our data mesh story, we eventually broke the cycle by starting to work in a Databricks notebook (a tool that lets you run code in chunks at time) to confirm we were happy with our code before copying it back into our production codebase and doing a full release.
It can be useful to bring this sort of thing up in story kick-offs when discussing requirements. It’s important to not only know what the requirements are, but also how you’ll know you’ve met them. If that loop is long or expensive, try and come up with ways to avoid falling into the pit of FDD.
A little bit of Faith Driven Development is unavoidable, but it’s vital that we recognise when it’s happening and to what extent. Letting it continue unabated can make stories drag on for significantly longer that they should, crushing the morale of those working on them. Keep an eye out for a string of failing builds in quick succession, or for a story that continually “should be done today or tomorrow” over the course of a week. If you spot yourself, or a teammate, caught in the snare, then make sure to take a step back, think about what’s being done, and ask if there’s a better way to be going about it.
Enjoyed that? Read some other posts.