Recently I had a conversation with Ingmar van Dijk who is one of the most talented technical coaches out there, and we were discussing how we can do best for our clients in our testing and refactoring classes. More specifically, we had agreed that most of our clients are not working on green-field codebases and although learning testing and refactoring skills is a good first step, it is not enough to really tackle the hard problems.
We were discussing how mastering TDD, and making it an everyday habit was a multi-month process for both of us from the moment we were introduced to it, until the time we had the skills and abilities to actually perform TDD on all our code.
And we agreed that this probably one of the major reasons that the technical practices like TDD and BDD are not as widespread in the community. They are difficult and that impedes them “catching on”.
Ingmar had suggested the idea that we start with the skills needed for working with legacy code FIRST, and then perhaps go back to the intricacies of TDD and refactoring later. It seems like putting the cart before the horse…. But after a couple of weeks away from the conversation I think he is right.
Think about it – one of the reasons we don’t build a habit is that we don’t have the ability to do TDD on all our code after we are introduced to it. So it is hard to pick one regular trigger. But what if we learned a few micro-skills that would allow us to work with existing code first. And then AFTER we built the habit of writing (very simple) tests for our code before we touched anything, THEN we came back to honing our TDD skills.
This would mean, we would start with:
1) Breaking dependencies in existing code,
2) Creating characterization tests,
3) Build the code in a test-after method regularly.
Then, after the habit is built for testing all code that we work on, we go back to test-first development and refactoring and refine our skills?
Ingmar van Dijk
January 2, 2014 at 9:37 am
What makes doing TDD on legacy codebases hard, is that it is already insanely difficult to just write a single microtest for the code that you would want to change. Most of the legacy codebases that I encounter while coaching and training consist of highly coupled classes that have way too many responsibilities. Therefore breaking dependencies first and extracting small classes are great starting points to getting the existing code under test.
In order to be safe while doing so, most of the time I suggest creating some very high level pin down tests. This should be done to make sure that nothing gets broken while refactoring the code to a better design. After extracting a smaller and more decoupled class add some microtests for it. These simple microtests will be the seeds for growing a more extensive test set for the entire code base. Doing TDD on those classes will be much easier now because there are already some example microtests available.
There is so much more to be said about this topic, but according to me, the key to learning TDD is: learn about good design, learn about bad design, learn how to refactor toward a better design, learn how to write microtests, learn the rules for doing TDD.
If you want to learn more, visit: http://industriallogic.com/shop
josephbeckenbach
January 29, 2014 at 7:48 pm
Didn’t Michael Feathers publish an entire book on this, after the fashion of his “Refactoring”? rummage rummage Ah! Here it is on my shelves: Michael Feathers, “Working Effectively with Legacy Code”, ISBN 0-13-117705-2, Prentice-Hall 2004.
Specific strategies and tactics on how to think about and do all manner of changes, organized around what the problem is. Two chapters whose contents I’ve used often is “I Can’t Get This Class Into a Test Harness” and “I Need to Make a Change But I Don’t Know What Tests to Write”. Very useful as I learned how to apply my TDD skills into legacy code-bases.
samadisy
January 29, 2014 at 8:12 pm
Thanks Joseph for your comment,
Yes he did – and it is referenced in the blog. What I’m suggesting is about how we approach teaching TDD. Michael’s book is a great how-to on working with legacy code.
The problem I am alluding to is that the standard way we deliver TDD training, and the large steps we take in that training – and the fact that it almost always starts with learning TDD on greenfield applications first doesn’t work.
-Amr