Creating your own progression steps for TDD if you're struggling to learn it.
29 Oct 2019
Notes are based on Test-Driven Development: By Example.
I took a break from reading Kent Beck's TDD book a couple of months ago. I tried out a couple of other resources too, but got overwhelmed by how difficult I found not only understanding the worked examples, but also trying to implement the process in my own workflow.
Between TDD and Design Patterns, my programming confidence steadily got lower and lower until I thought that I could barely build simple programs. So I stopped and moved onto learning about front-end design systems, and more recently SQL. I was surprised to find that I picked up the principles underlying design systems and SQL fairly easily. There is still a lot to learn, but I remember a year or so ago when both of these things were difficult to understand and apply too. The thing that made them easier to understand was exposure to concepts in TDD and OO. So while I feel like I can't 'do' them properly yet, they are slowly but surely sinking in.
Another thing that has helped raise my confidence is having started an intense fitness challenge program about 4 weeks ago. The other day, I was getting worried because my weight hadn't changed much even though I had stuck to the mealplan and was doing 5+ high intensity workouts a week. However, when I took my measurements, I saw that I had in fact lost 6cm of both my waist and hips, which is a huge amount in such a short space of time. Muscle is heavier than fat.
Even though my measurements had drastically changed, I didn't notice any difference in myself, because the changes were so gradual that I couldn't see them, even though the pace of change was quite fast.
Similarly, when I started the fitness program, I had no idea what I was doing. The fitness instructors had to show me how to raise weights, and how to bend properly so that my back didn't get injured. At week four, I have made a massive amount of improvement, but still get regularly corrected for my form. In some cases, I'm not strong enough to do some of the exercises yet, so I do a modified version of them. So instead of doing a full pushup, I do a pushup with my knees on the floor (today I managed to do 2 full pushups before reverting back to them, yayy).
What both of these things have helped me see is that the progress you are making is not obvious to yourself, but that you can use certain measurements to make your progress visible in other ways. So taking a break to learn something else made me realise that I am already a much better programmer than I was a short time ago. Similarly, regular feedback is crucial for helping you correct your form. It's okay if you need to be course corrected multiple times. Finally, if you are not 'strong' enough yet, it's okay to modify the exercises to meet your own level, until you are ready to do the exercises in full.
Modified TDD progression
Progression is where you gradually move towards a more advanced state. So if you can't do a full pushup, you do a half pushup (on your knees) first, until you feel ready to do the full pushup.
In the case of TDD, the progression steps will be different for you than it will be for me. There might be a few ideas in this which could help you develop your own progression steps though.
Step 1. General TDD process outline
The first thing I did is look at the table of contents in Kent Beck's Test-Driven development book. My assumption is that it would give an overview of the steps associated with TDD, which it does to some extent. The part that jumped out to me was the 'Part 3: Patterns for TDD' section. I had not read this part of the book before, and thought that patterns would be a good place to start. Patterns are things or concepts that crop up regularly, so reading these might help me understand TDD better without focusing too much on the implementation details.
After reading some of the patterns section, there were a lot of TDD process cues. I captured them as a series of action points which are listed below. I noticed that I had a few gaps in knowledge. I'm not confident with the process of developing user stories. But that's okay, I'm going to follow the process anyway, fully knowing that my first and subsequent few steps are going to be awkward, stumbly and generally quite ugly. Just like my first day at the gym. It's going to be hilarious.
- You should start building a system with the stories you want to tell about the system. User stories are a gap in knowledge for me. To get moving as quickly as possible, I'll read an article or two, look for an actionable way to come up with a user story and brainstorm a few for a fitness app I want to build.
- Identify the functionality you want to implement. I have heard this phrased as 'implement the behaviour' you want to implement. I'm interesting in reading another persons process for breaking down stories into behaviours/functionality blocks.
- Write a test list. Include worked input output examples for the kind of things you want to test for. Write out all of the tests you know you want to implement. You can update this as you go along. However, we won't work from this one directly. Instead, we will write a test list that covers only the tests you want to implement in the current session.
- Write the assertion first. When you write tests, they include an assertion which is made up of an expected input (real data or (mostly) dummy data). In order to write the assertion, you have to know which class the method you are writing belongs to (if you are writing object-oriented code). That class and method doesn't exist yet, so you can choose the names for each, that make the most sense for what you are trying to do. I read somewhere else that when you write tests, you should write them for the API that you wish you had. Tests let you design that API first (how you USE the program), and then implement it's features and methods afterwards. Pretty nifty.
- Red: Now we are in the three step red,green,refactor process that is a benchmark of TDD. In the red phase, we will have written a failing test (previous step). We now have something to start writing production code for.
- Green: Now that we have a failing test, it's time to write the simplest code possible to make the test pass. The thing I love about this approach is that it helps you overcome perfectionism. You are not trying to work towards the perfect design. You are just trying to make the test pass. I like to start with the error messages. What is the smallest thing I can do to make the error message pass? If it's your first test, then that's likely to be writing a class stub, then a method stub to pass the next error message after that, then the implementation details to make the actual test pass.
- Refactor: This is the stage where you look at the code you have written and think about how you could improve the structure of it. Now that you have a test that passes, you have a safety net. You can change the structure at will. The test will tell you if your structural change has broken anything. I follow the 'rule of three' principle when it comes to removing duplication. I'll only remove it when I have three examples of the same thing (syntax or concept-wise, you can have duplication of ideas, an in some case, the same code may be used for two different purposes, in that case, it isn't duplication. Another thing you can do is refactor all of the previous code you have written after passing a bunch of tests, once you realise that your design would be better expressed in a different way (like you have recognised a design pattern) etc.
The process above shows that I know more about TDD than I thought I did. Have absorbed a lot. However, I am also away of areas that I have limited knowledge in, as pointed out earlier. I haven't experimented enough with refactoring methods, or learned enough about design patterns, user stories etc to be able to make well-informed decisions. This is why I feel like the code I write following TDD is going to be naive. If I knew more, it would be better. Obviously. However, trying the process out anyway will get me more familiar with it. Over time, I'll learn all of the other components and my process will start improving drastically. I won't make any progress if I don't at least try to take those first stumbling steps all the way through.