The road to TDD

Applying production code maintenance techniques to the development of short-term prototypes.

etermax technology
Published in
5 min readJul 15, 2021

--

By Gustavo Furini, Technical Owner at etermax.

INTRODUCTION

“Can you make prototypes and TDD at the same time?” is the question that came to me over and over again weeks before integrating Proteo, the team that was in charge of making prototypes and testing new ideas in the company.

I come from a long tradition of quick and dirty prototypes, from the moment an industry acquaintance told me: “The code has to be so ugly that you are embarrassed to show it. Then is when you will be making prototypes. ”

The team, while small, was already famous for its speed of production. The problem was that to have that speed they were skipping the processes and practices of the company, that’s where my participation came in.

“Those two things are diametrically opposed, we will not be able to prototype quickly if we do it with TDD or if they try to apply some SOLID concept.” So the chosen approach was to use time wisely and only use TDD in the important parts, which for that matter were the intrinsic logic of the game, and the odd service that involved a complex algorithm, at the same time as the weekly katas (2-hour exercises to consolidate concepts or practices new things) were going to be useful so that the team would face different challenges little by little that would later be applied in production.

“If they start TDD only with the essentials, maybe the impact over time will be less,” I told myself.

FIRST STEPS WITH THIS TECHNIQUE

The team was already doing pair programming (and even mob programming) and taking their first steps in TDD thanks to the previous leader, so it was very easy to get into the workflow with them and start showing them how I wanted the job done. We spent the next 4 months working side by side.

We started with a very simple strategy: we did the tests of what we were going to iterate many times, this implied the logic of the prototype, and we implemented the minimum possible code (generally a spaghetti of ifs and elses), then we ran to do an early validation of what we had.

If the validation was positive, we went back and refactored trying to apply SOLID. Otherwise, we changed the tests and continued implementing in a messy way until the next validation.

Then we would take a short time to refactor, and if we did not have time, we would do an analysis of how the refactor could have been, and what aspects of SOLID it involved.

As we had anticipated, that made the impact on times to be not so terrible (on average we added 40% or 50% more time), and gave us a good initial balance between speed and refactoring capacity, since the test coverage gave us a “safety net” to change things and know that the logic was still correct.

SURPRISE AFTER SURPRISE

But perhaps the best surprise was not the ability to refactor quickly thanks to TDD, but months later we had to retrace our steps and reuse some prototypes as a basis for new experimentation, and we discovered with great pleasure that the tests we had done explained perfectly what the prototype was about, while fulfilling use cases such as documentation of how it should be used (a kind of “user manual”).

“We made the changes very quickly” was the phrase that showed the amazement at that moment. “We read the tests and we knew right away where to make the changes”, and in this way one of the squads had converted one prototype into another in a matter of 2 days.

At another time we had to adapt a prototype to have several game modes, be it multiplayer if an opponent was found, or use a bot otherwise.

Not only were we able to separate the logic common to all game modes through tests, but we were also able to postpone the bot implementation using mocks, and later model the bot separately using a stub of it and testing all the intermediate steps of its logic.

The fun fact of this anecdote is that we decided to dispense with the remaining tests when integrating that game into another project to meet the date, and all the bugs that we later had in production came from that code without testing! Great lesson learned!

Another discovery that we experienced was that forcing ourselves to do the tests led us to define the feature before implementing it, and that made the design and validation work more agile and without fallbacks.

At a certain point, the tests were no longer a thing alien to our process, and each time the prototypes began to be completed faster, and with more refined code.

“HERE COMES A NEW CHALLENGER”

As the months went by, the team began to have new challenges. We already had several stakeholders from other teams, and due to that we had the need to grow. At this point we had another happy revelation: each new member took less time to learn.

After onboarding, the new member went on to pair programming during a whole prototype with the veterans of the group, which had already mastered TDD.

The learning curve and implementation of TDD improved markedly: each “newbie” began to take less and less time to absorb the practice of TDD, while the “veterans” with whom they worked strengthened their knowledge due to the obligation of having to transmit it and teach them.

To all this it was necessary to add that when they started a prototype from an existing one, they very quickly understood what it was about. The tests served as use cases for it, and as a user manual when it came to services or algorithms.

This non-zero sum situation became one of our most valuable resources for introducing new members to the team.

And while the five monkeys experiment is a fable to negatively exemplify behavior without question, in our case it had a positive effect: each new member was mentored by someone who already mastered both TDD and pair programming, so they accepted that work dynamic as “natural”.

Six months later we were back to the initial development times. We had a team 4 times bigger, and a tracking system for the prototypes already made so that we could easily start from one if the case arose.

CONCLUSION

While this story that I am telling should not be taken as irrefutable proof that TDD supports the rapid and agile development of well-defined requirements, it is an important milestone that serves as confirmation that TDD is no stranger to LEAN practices, and that both can be aligned in pursuit of the “fail fast to learn faster” strategy, not only having a reduced impact in the long term, but also providing the necessary tools to make the changes in the code that are required by the product strategy.

By Gustavo Furini, Technical Owner at etermax.

--

--