TDD: The Best Counterintuitive Idea in Programming.

Kyo
6 min readMar 18, 2021

This tiresome approach might give you unexpected and valuable benefits.

Ahh, Test-Driven Development. I’m pretty sure almost all software developers know what it is by now, or at least get the gist of what it is. Whether or not developers like the idea, though, is a bit more dividing.

At first glance, TDD seems very counterintuitive and cumbersome, where the benefits are minute when compared to the heaps of extra work and thinking you have to do to properly implement it. In other words, it doesn’t seem like a good idea at all. But the more we delve into the topic and learn more about it, and the more we actually try to implement it, the advantages of using this practice stack up beyond good code quality.

First, let’s quickly skim through the actual process of TDD. In short, it follows this cycle.

source : https://marsner.com/blog/why-test-driven-development-tdd/

The three stages that make up the process are RED, GREEN, and REFACTOR. You write tests that fail in RED and write code that passes those tests in GREEN. In REFACTOR, you clean the code you wrote in GREEN, while still making sure it passes the tests. Anytime you want to add new functionality to your app, you start over in RED.

Here’s an example of how my team implemented TDD, back when we were just getting started on the project and were simply building the database as required.

This is a very simple “model” test, designed just to see if an instance of the object can be created without any problems. The thing is, when we wrote this test, the object doesn’t even exist yet. So needless to say, the test will fail. We then pushed this piece of code into our repository and were greeted by the red X we were all expecting.

Such is the RED phase. We write tests on features that are yet to be implemented, and push them into our repository knowing they would fail. We then step into the GREEN phase where we actually implement the feature and pass the tests, REFACTOR the code if needed, and start over in the RED phase. When we first implemented our database, the process looked a little bit like this:

Seems counterintuitive, no? after all, what’s the point of writing tests that we know are going to fail? Why not just implement the features first and test them later? Well, I could tell you the advantages of TDD from a technical perspective, like how it modularizes your code into bite-sized chunks and therefore make it cleaner, or how it makes it easier to debug when something does go wrong, etc. But I’m sure there are other (and better) articles that will tell you all about it, from people who are much more qualified than I. Instead, what I think often gets overlooked are the advantages of using TDD in the long run, beyond the technical perspective.

The Unforeseen Benefits

There won’t be chunks of code in this section; we will be talking about how using TDD frequently affects how you think when you code, not the code itself.

Early Detection of Poorly Defined Requirements

As a Computer Science student who has been (unwantedly) using TDD for about a year now, I’ve noticed that the way I look at things at the start of the project, before any of the coding starts, has changed. Prior to using TDD, I’ve always thought I understood the requirements of a project completely. It is only after I started coding that I realized I needed more clarity and that some things are left too vague.

By using TDD and writing the tests first, I am forced to think ahead, to think about the implementation thoroughly. This means that vague requirements will be detected much earlier, and by extension reducing the probability of me having to rework an entire solution after putting significant time into said solution.

Charting the Road Ahead

Another benefit of having to use TDD is that over time, you get better and better at predicting how you are going to solve a problem. When writing tests for models, you’re basically writing tests for a database table, you only need to check if its attributes are correct, and that CRUD operations work smoothly. However, when writing tests for something far less uniform, such as views, for example, it is far more difficult to predict what lines of code are going to be there. Oftentimes, it feels like we’re trying to look into the future, and at times it certainly feels as futile.

But here’s the thing: you get better at it. You get better at mapping out what those complex implementations are going to look like, and you get better at designing them to be more manageable and modular before you even start to implement them. This leads to far fewer “wing it” moments, cleaner code, and all-around better consistency and readability.

The Drawbacks

Unfortunately, like pretty much all things, there are some drawbacks that you’re gonna have to weigh if you’re considering on implementing TDD. Just as how the “benefits” segment was mostly subjective, this part will mostly be subjective too. However, I think it’s safe to say that my sentiments are far less unique here, because I’ve seen many people complain about the things that are written here.

Time Consuming

TDD is often criticized as being too time-consuming. Some people would argue that by using TDD, your development time overall decreases, because you spend less time on debugging and scratching your head on what went wrong. However, in my personal (and admittedly limited) experience, TDD is never the way to go when a client wants something up and running as fast as possible.

Way. More. Code.

In every one of my TDD projects, there are substantially more lines of code when compared to my non-TDD projects. And I mean substantially. I’m talking twice the amount of my implementation code, maybe even more. This also means that maintaining the code becomes significantly harder as well, which ties into the “time-consuming” part. Say, for example, our client wants to change a feature. That means we have to change the implementation, and therefore we have to change the unit tests as well. This means twice the work, if you’re lucky.

TLDR

TDD is not perfect. In fact, whether or not it is a good idea at all will change depending on who you ask. It certainly isn’t the universal best practice, let alone required.
However, I think there’s a reason so many people adopt it. Despite its counterintuitiveness, its tiring process, and monotonous cycle, the benefits that you will get by implementing it are worth it, because it goes beyond code quality and gives you skills that really come in handy.

That’s not to say that TDD’s just a set of training wheels and that you should throw it away after a good while, not at all. It certainly has its place, and writing all those tests and watch your code pass them also gives you confidence and reassurance that your code’s thoroughly planned. But it isn’t the be-all and end-all of approaches.

After being forced to use it for a year, I’m convinced that TDD is a good approach, but, despite what I’ve been led to believe, TDD turns out to be just a tool. This means we gotta know when to use it, and sometimes, it just isn’t right for the job.

--

--

Kyo

If you're reading an article from me, It's probably a part of my college course.