Tactical GIT for Test-Driven Development
When doing TDD, one of the most important things to do is learning to use GIT in a tactical way: let's see what I mean.
Hello, developers! 🚀
Welcome back to the Learn Agile Practices newsletter, your weekly dose of insights to power up your software development journey through Agile Technical Practices and Methodologies!
Before starting, I quickly remind you that my brand new Test-Driven Development 101 5-day Email Course is available: it is the introduction to TDD I wish I had when I started learning it, so I think you will find it very useful!
As a subscriber to my newsletter, you can have it with a 10EUR discount! 👇
Now, let's dive into today's micro-topic!
In short
🛟 Tactical GIT means using GIT to our own advantage to create checkpoints to progress in our work safely.
✅ TDD allows continuous, incremental progress in software development by using small, manageable steps: each GIT commit serves as a stable checkpoint in this process
🥅 This method encourages experimentation and refactoring, providing a safety net to revert to a previous stable state if new changes don't work as planned.
👉 3 simple rules:
Execute the test suite after every commit
Commit every time you pass a test that was failing
Commit every time you successfully refactor without breaking any test
What does GIT have to do with TDD?
Version control systems like GIT have become a fundamental tool in software development. The way GIT manages branches, local stages of changes, and everything else has been a disruptive change from simpler version control tools such as SVN. In the Open Source especially, this was a game changer.
Leaving aside any discussion about the benefits of Trunk-Based development and why a complex usage of branches via GIT can be a problem for software development in a business context, I want to emphasize a different point of view about GIT when it comes to Test-Driven Development (TDD): this tool is a powerful ally that enhances the TDD cycle.
The key is to approach GIT not as a mere backup system or collaboration tool, but as an integral part of your development strategy. By using GIT tactically, we can create a safety net that allows us to experiment freely, track our progress, and easily recover from missteps.
We use GIT to our own advantage instead of adapting our flow to it.
Checkpoints, like in videogames
Think of tactical GIT usage in TDD as similar to checkpoints in video games. In many games, you reach checkpoints where your progress is automatically saved. These points serve as safety nets, allowing you to take risks and explore without fear of losing all your progress if something goes wrong: this empowers and maximizes the typical trial-and-error approach of video games providing a recent restart point in case we lose the game.
When we do TDD, we continuously progress in the “game” of software development by making baby steps of progress in the path from the current to the new version of our software: in this path, we can use GIT commits as our checkpoints. Each commit represents a stable state of our code, a point to which we can always return if our next steps don't pan out as expected. This approach encourages experimentation and bold refactoring moves, knowing we always have a safe state to fall back on.
3 rules, 3 examples
We can achieve a successful tactical usage of GIT by following 3 simple rules:
Execute the test suite after every commit
Commit every time you pass a test that was failing
Commit every time you successfully refactor without breaking any test
By creating a verified checkpoint every time the software is stable, we achieve multiple objectives easily:
we continuously keep the shared codebase releasable, enabling Continuous Integration
we continuously verify the correctness of our work, removing the need for debugging: if something breaks up, since the change is so small, either the solution will be immediate or we can just rollback and retry because the amount of work is so small that it costs very little to go back a little step
we take advantage of the baby steps of TDD effectively: even if we do TDD and we progress in baby steps, if we do not create these checkpoints, we will still have some hard times investigating when something breaks up; by making rollback so easy and low-cost, we remove the fear of making a mistake and foster experimentation on our code
If you are worried about GIT history, you can take advantage of the local staging of commits from GIT, and then squash your local commit on a single one via rebase before pushing them.
In addition to TDD, we can use the same approach in any refactoring session, as soon as we are doing refactoring as it should be done, meaning that we have a test suite to verify the behavior implemented doesn’t change: as Martin Fowler suggests in his book, baby steps apply to any refactoring, and there is no refactoring without automated tests.
When we are refactoring a piece of code (for example, to “make the change easy, then make the easy change” before adding a new behavior, or to clean up a bit our newly created code following the “boy scout rule”), we can commit after every successful step of refactoring that doesn’t break the tests and make our life easier.
Until next time, happy coding! 🤓👩💻👨💻