TDD and quality of life (yours)

Be it as employees or as freelancers, we sell our time and work to companies and clients. One thing that distinguishes our profession from others is he fact that we sell intellectual work. Sometimes, even high-level intellectual work.

So, taking care of our mind and our intelligence seems like a reasonable activity that we should practice frequently.

There are many people in the software development world that think, or even claim, that testing is hard or expensive. And that’s without even talking about Test Driven Development. But what we want to show is that TDD is the most advisable path if you want to have a healthier software development life.

But first, let’s take a look at a couple of question about how our brain works.

Knowledge in the world, knowledge in the head

Doors

Do you know how to use a door? Sure? Have you ever seen an instruction manual for a door? I have. Lots of them, actually: all of of those doors with signs indicating whether you have to pull or push. I bet you’ve come across more than one of those.

Have you ever found yourself before a closed door and not known how to open it? I have. In fact, there are dozens of them across the world, such as automatic sliding doors with poorly adjusted sensors, or doors that open inwards when everything points to them opening outwards.

The point is that a door should be something easy to use, and that doesn’t always happen. The way to use a door should be obvious,right?

Switches

And what if we talk about switches? I mean those switch panels whose arrangement is not related to that of the lights they control. Sometimes they’re located in corners where you can’t even see the lamps, and you have to try and fail many times until you find the secret combination that turns on the lamp that you want.

The relationship between a switch and the light that it controls should be obvious, right?

Let’s talk about the obvious

When we talk about something obvious, we refer to knowledge that we shouldn’t have to search our head for. The knowledge is there, in the world. We just have to use it while doing other stuff. We want to be able to open doors and turn on lights without having to think about it for even a fraction of a second.

For this reason, when we’re forced to think about things that should be obvious, we’re wasting part of our mental resources, taking space in our working memory that we’d prefer to -or even should- be using for other purposes.

The knowledge is in the world when all of the clues that we need to use or interact with an object are present in the object itself. That’s why we needn’t worry, reason or remember how to use them. If we need to do it -that is, when we need to reason or remember instructions, we have to put the knowledge in our head in order to achieve our goal.

Therefore, if we have access to more knowledge in the world when we execute a task, we need less knowledge in the head, leaving more free space that we can use to think better about what we’re doing.

The less we have to think about the way of using the tools, the more we can think about what we’re doing with them.

But, how much space do we have in our working memory?

Well… it’s actually not a lot.

The capacity of our working memory

Our memory features a practically infinite storage capacity. Think about it as a huge intelligent hard drive that can store memories and data for years and years. It’s not a passive storage. In fact, it’s constantly rebuilding our memory in order to save and retrieve things. This is important, because when we think about it, we have to use our working memory to keep the data we’re using. Much like a computer.

However, our working memory is quite different to our long-term memory. There are those who call it “short-term memory”, while others refer to it as “working memory”. I think we can see it as a processor, with some registers that can store a limited amount of information units called chunks while it works. The chunks can be variable in size, but they’re significant units.

As computer programmers, a good way of understanding these chunks is to think about them as pointers that indicate memory positions. Those positions can have structures of any size. Sometimes they’re very small, like a single letter or number, and other times they’re enormous.

Can you remember a phone number? I bet you’ve grouped the digits so you only have to retain two or three numbers.

This is because our processor can ony handle a limited number of chunks. This number is approximately 7 (plus or minus 2). It’s something that changes with age and between individuals, but it’s a very good approximation. Therefore, we try to save as many registers as we can, grouping the information in chunks, and keeping some of the registers free.

What happens if we fill up all of the registers? Well, our precision and speed when doing a task decrease, increasing errors. In general, we perform worse if we try to keep too many things in our working memory at the same time.

Of course, this is an oversimplification. However, I think you get the idea. We can reduce the overload if we put the knowledge in the world instead of keeping it inside our head. This way, our performance in any task will improve.

You can take knowledge out of the working memory and put it outside with practice. This is what happens when we introduce a new technique, try to apply a new feature of a programming language, or use a new tool. In the beginning we go slow and make mistakes. We need time to automate things in our mind while we put knowledge in the world.

It’s time to go back to the main goal of this article. Let’s talk about our life as developers.

A day in the life

Programming without tests

Let’s analyze for a second what happens when we program without doing tests.

Actually, we’re always doing tests, but they’re frequently manual ones. We call it “debugging”. We use a trial and error process: Does this work? No…? Try again. Yes…? Keep going.

We try to write code and verify that it works at the same time that we write it, until it looks like it’s finished. After that, we try to verify that the code works as a whole. Then we realize that we had forgotten some details… After that, we deploy and discover new details that don’t work, so we need to fix them.

At the end of the day, we find ourselves suffering from huge headaches and under the impression of having missed something.

This happens because we try to keep all of the information in our head at the same time (remember it has a limited capacity!). We overload ourselves. The best strategy is to write a list of objectives and tasks, and try keep a certain organization and focus using this external support.

For example, writing a simple API end point requires a bunch of things:

1. An action in a controller
2. A route to that controller
3. An use case or command that executes that action
4. Probably one or more domain entities and their repository
5. The definition in the dependency container
6. Probably some service
7. Its definition in the dependency container
8. A response object
9. etc.

With this, our memory overloads as we greatly exceed the 7+/-2 items. This explains why we feel tired and stressed, with the feeling that we might have missed or forgotten something. And unsure about what we’re doing or about whether we’ve left anything important behind.

So, let’s take a look at how we’d execute the same process, this time with testing at the end.

Programming with tests at the end

This is actually almost the same, but now there’s tests at the end of the process. The kind of tests that we automate.

The end result is better, because now we’re more confident in the code. But we still have that same headache at the end of the day.

Yes. We’ve done the same amount of work, with the same mental overload and with the addition of having to write a test suite, while our brain screams at us: “Hey! The work is finished! What are you doing?”

In these conditions, it’s possible that our tests are not the best tests ever. It’s also possible that the suite doesn’t cover all of the possible scenarios.

In fact, we’re already tired when we start the testing phase. This explains why a lot of people think that testing is hard and even painful.

So the tests improve our confidence in the code, but at the cost of forcing us to do a lot of extra work. Our life isn’t made better by tests, even if we sleep better at night. Then, what is it that is wrong?

To improve our life you should try a different approach. You should try Test Driven Development.

Programming with TDD

This is what TDD is about: one thing at a time, and postponing decisions.

  • A simple failing test: don’t write any code while you don’t have a test.
  • Add code to make the test pass: don’t write no more no less than necessary.
  • Review the code to improve things, but don’t implement anything new and keep the existing tests passing.

Let’s see the process from the point of view of our working memory model. When we write the first failing test we’re focusing on that test. Therefore, we don’t have to waste our attention on anything besides it. Writing the test also means that we put the knowledge that we need in the world. Our memory is almost unoccupied.

Then, we focus in writing the code necessary to pass the test. The knowledge that we need is in the test, not in our head, and it’s what we need to achieve our most immediate goal.

We only have to think about the way to make the test pass. If it’s the first test, we only need to write the most obvious implementation that is possible. Even if that implementation is as simple as returning the exact same response that the test expects.

And once the test has passed, we can take a look at the code and see if we can make any improvement by refactoring. We don’t have to add any feature. We must keep the test passing while we tidy things up, eliminating unnecessary duplication, introducing better names, etc.

We’ll repeat the cycle until having completely implemented the functionality. We don’t need to write extra tests, we don’t run the risk of having forgotten anything. Our head doesn’t hurt. We’ve use the brain to think, preventing the overload.

It’s not magic, is TDD. Of course, this requires training. TDD is an intellectual tool, and the utilization of a tool should be automated. Therefore, you should do exercises such as the kata, both by yourself and with the help of colleagues, in a practice community, in the way that better suits you and your team. Practice, practice and practice. Once you’re able to proceed step by step, you’ll find out that you’re happier and less stressed in the medium and long term.

A final piece of advice

Store as much knowledge in the world as you need: use a backlog, use post-its, write a to-do list, draw diagrams, models, concept maps… Free your mind and leave room to work in one thing at a time.

TDD is more than writing tests. It’s putting the knowledge you need in the code and freeing your ming. It’s postponing decisions until the moment you’re ready to make them.

For real, try TDD, your life as a developer will improve.