How Do I Convert My Team To TDD ?
Brute Force Does Not Work
TDD Works, it's being used, and the debate is over. It's time to move onto "How Do We Share and Teach / Spread It On My Team"?
I see this question asked all the time by devs who TDD or want to and dream of the day the entire team is dancing together doing TDD. Quite a dream isn't it?
First understand that this is a daunting task. Is it possible? Depends, I do not have that magic answer yet. But Brute force is definitely not going to work unless you are in a position and have the ability to fire all your developers and bring in TDD'ists.
But that's not really fair is it? You probably have great people. And teams need the chance to try and get there. You do this by converting one person at a time gradually toward learning TDD and good design principals. If you don't have good design or know how as a team, you can forget testing.
Why is this Daunting?
It's daunting because the biggest hurdle is getting devs out of a current mindset that's getting them in the place they are in the first place...a mess with no tests. It requires a complete mindset change in many ways and you have to be dedicated to embracing it fully, not half heartedly. It changes the way you code and work quite literally.
The biggest problem is that most developer's mindset is what I call code & run and you can blame that on our profession and IMO excuses, if you want to call it that. They only focus on speed and satisfying the status quo, getting stuff done at all costs (hacks if need be), and receiving that reward from management pat on the back for rushing, continously underestimating, lowballing story points do to fear or pressure, and focusing only on the numbers. To make it even worse, they view and treat tests as features or nice to have. And the biggest problem, the code is not testable to begin with. These are why teams don't test, and will never get there.
But they also need help getting there and want that help.
Understand that it may never happen... it can, but it'll take time. But there are steps that you absoultely need the team to get into the habit of doing first that will start you on that path to getting there.
What will help you is to first get the team thinking in Clean Code and Four Rules of Simple Design, and SOLID principles...IMO focus on clean code first. It's something easily applicable for newbies trying to improve code and it'll start having an immediate big impact for everyone on the team including themselves...trust me, try it.
That is the first step into getting or keeping a codebase "testable". If you can't get there gradually and get the team into those concepts, you'll likely fail and the team themselves will just keep dreaming of a day that they'll have isolated tests and test suites that are useful to them. You can't have isolated tests if your prod code is a mess and coupled.
Obviously those who do TDD know that TDD drastically helps to enforce good design. It's not meant to be full proof or a silver bullet that ensures perfectly designed code. But it definitely forces you to decouple code, refactor, and to be more critical of your design often and frequently as you go in small steps to complete a feature. TDD doesn't give you a choice but to keep your design decoupled as you go. But a team is not going to convert overnight to TDD. So you have to find ways to get them thinking clean code and decoupling code as much as you can in the meantime as much as possible as they go.
Without Some Pairing, This Will Fail
Paring and then them spending time on their own practicing what they learned during paring on code is the only way to convert someone...period. Else they'll spend a whole career trying to learn it and doing it wrong over and over again when they could have saved years learning testing by just pairing here and there with people who already know how to TDD.
If you don't learn how to write tests well, you don't pracice it often, and you don't try apply/learn clean code practices while you code. And along with that are dictated to when you can or can't write tests, then when you do decide to write them, you're likely to write tests that become problematic and a burden for the team as a whole. It'll impede progress...then as a result the team once again resorts to not testing or refactoring; the same dreaded cycle over and over again. That's not a "profession", that's just hard labor and a whip.
I got lucky.
I don't claim to be this all wonder "self learner genius". I worked hard to learn what I know today on top of getting lucky. It's paid off big time on many levels. But being able to learn testing from someone through pairing shaves off a huge part of the learning curve and very quickly.
I was able to pair with people who knew TDD well. It probably shaved 5 years off my learning curve with testing and I only paired for 5 months daily with them. And that along with my own practice is why I am where I am today with tests and why I love and more importantly rely on TDD...it's not a choice for me. It's how I code. And wow, I sure hope if you have someone like that on the team, you take advantage of that through learning by pairing with them more at the beginning of your learning journey.
I get it. You'll hear "We can't pair, those are for product teams". Which I don't agree with. You can pair with any developer for any product.
Listen, I am not saying you have to start out being an XP shop and pair 100% every day with each other. But if you have someone on the team who knows TDD...you gotta get them pairing with individuals, one-at-a-time, convert them gradually. Then the converted can also start to do the same with others. You do this gradually, it doesn't have to be a "we pair every day scenario".
So don't say pairing doesn't make sense. Just do it. Do it at your own pace and frequency but do it. The more you do it, the faster the team will become knowledgeable and independent about how to write good tests and equally as important, keeping the test and prod code decoupled. How to write clean code, and how eventually to TDD.
Pairing often will exponentially decrease the time it takes your team to get good at testing, learn TDD, and to learn good design. If you've seen pairing fail, you've probably got people who don't buy into it and are going against the grain in the first place. Well that kind of attitude's not gonna work anywhere. It's not a learning environment to begin with.
Stop Focusing on Code Coverage
Anyone can try to mimic tests or write them. You can have the highest code coverage but if those tests suck, code coverage doesn't mean a damn thing. And if people don't know what to test, why, and how to write good tests, that's all wasted effort and why teams get the "I got burnt on testing" or "testing is a waste of time" or "we view tests as just a feature" nonsense you hear at times.
One of the big mistakes made is a lot of places in addition will start out or always base builds on integration tests and worse, base code coverage only on integration tests. This is completely backwards of what it should be and completely going against the test pyramid.
Focus First on Isolated Tests! Not Integration!
First, you should care about if your isolated unit tests are good tests.
Second devs and management should be focused on isolated tests. Sometimes it's difficult to get this through their head; it's a lack of undertanding good testing but that's what the main focus should always be. And... your builds should be running automated isolated tests on every commit, not integration. Your build should fail on isolated test failures, not integration test failures. These are test fundamentals that a lot of organizations just get wrong, and if they don't understand that, it's a big problem.
The problem is developers and management usually don't understand what "isolated" means or that it even exists in the first place and that is a critical conversation that needs to be had with your development team and management.
Integration Tests Should NOT be "The Answer" or "Goal"
Integration tests are what teams that don't understand testing or have a non-testable codebase first resort to. But they're for the most part a huge waste of time.
Integration tests are slow, they don't put pressure on your design, and they're an after thought. They don't test small isolated units of behavior, they're fragile, not consistently reliable/repeatable, harder and take longer in many cases to write (due to a slew of reasons) and the list goes on and on. They cost more to write and maintain and you'll never cover all paths in code.
There is a false sense that "they will solve all our testing needs" but as you know the ice cream cone effect does just the opposite.
Teams should be focused on isolated tests first. Way more isolated tests than Integration. But to do that again, you have to apply clean code, good design, and understand also how to write good tests.
For Legacy code, you may not have a choice but to start out with them but the goal should be to aggressively reawork and refactor code over time to get that code testable and decoupled using clean code and the Four Rules of Simple Design techniques.
Don't create too many of them, create a few to cover that legacy code, then start refactoring inside out with small clean code techniques...which present less risk. You can't get rid of risk and use that as a force to say "we can't refactor yet". That's just not going to work. So stop worrying and start doing in baby steps.
What Kind of Baby Steps Do I Start With For Refactoring?
Apply simple clean code techniques like naming variables well which allows you to remove unecessary clutter such as all those code comments, moving lines into methods which are named in a way that self describes so that you don't need comments and so you can isolate pieces of behavior; that's where you'll need to start. That's how you start that refactoring path. You'll start to make very small changes that have BIG impact.
You'll gradually see your codebase become more readable, cleaner, and start to be able to decouple things, first by extracting lines of codes into very small methods. Methods always small, always doing one thing is the goal first.
A moment on code comments again. You really really don't need comments. It's a sign that your code is not clean. If you name variables that self describe, move lines into small methods, name methods well, you'll see what I mean, not going to sit here and argue that...I know it works. So likewise don't argue, just try it, you'll see. Many teams have done this for years already, by following the clean code book. This is no joke. You'll get the hang of it pretty quickly if you apply these very simple practices.
Too many tools up front, and especially heavy tools like resorting automatically to mocking frameworks and too much magic is not what you want in your test infrastructure. That makes things complex. They present a huge learning curve for the team, and it's not being lean.
Example: Are you using Jest? Don't start with Jest Mocks. Are you using Mocha? Don't start with Sinon just for the hell of it. Keep mocking frameworks out as much as you can. And choose light mocking frameworks if you do. Jest Mocks are not light. They're a heavy messy framework that'll just make your life more complex just like Moq in .NET, or Mockito for Java.
Don't just do what everyone else says to do and blindly start using stuff. A lot of times you can create custom fakes, mocks, etc. very easily if you know how. If you don't learn, find someone to teach you that. Keep your tests and codebase as simple as possible.
I use mocha and enzyme as my tools of choice. I'm familiar with them, I get them setup quick, and they keep testing simple. Mocha can be used for front or backend if coding JS.
Chose convention over configuration
This is my experiment based on my experience, failures, and successes in this profession.
Below is me just brainstorming what's in my head starting in order in my approach.
is there any code yet? try to pair with someone after I get mocha and enzyme setup for front-end or mocha for backend depending on what project it is so I can start sharing the conventions and how to test (and try to get them to follow the dsl /approaches to writing tests, getting them aware of looking at those first and trying to stick to those conventions)
if people already started to code, get one person to pair, slowly show them things
if people already coded and some have added tests that don’t really make sense, again try to pair with one person, drive, and show them other ways to do it in mocha. I like mocha, it’s just what I’m comfortable in and I’m free to setup whatever tools I need to for these projects and that really helps, because I can just get right into coding tests and the DSL and to start sharing what I know
I don’t expect them to TDD right off the bat. First thing, get them to understand test fundamentals, even if that’s adding tests to existing code which that often ends up being for them (I often refer them to Roy Ohserove’s Unit Test Best Practices Youtube vid, maybe the clean code, and that’s it, don’t overwhelm stop there…or else it’ll turn them away.. and mention those once, plant that seed and leave it)
Try to get them to read clean code first. To do that they need to read
it over time...or watch vids on cleancoders.com...or both.
So many developers still write comments all over, have huge methods, etc.
That’s the first barrier to entry on making things testable is to clean that up and get things separated out more. If you don't keep the campground
cleaner than you found it, and you don't know how to keep it clean, you'll
continually be cleaning a mess. And if you don't know how to keep things
small, name things well, keep things modular with clean code practices for a
start, you'l tests...period.
If you can’t get people to be aware of the concept of and learn clean code,
your codebase it just never going to get there and you’re climbing a never ending mountain of rot and you’ll never get things testable if people aren’t focused on that. So many devs have yet to even hear about the Clean Code book, Four Rules of Simple Design so I think that is way more important to get
them focused and aware of that first.
after a while, after I help guide them at times (offer your assistance and hope that they do come to you...be inviting), and after I’ve added quite a few tests which serve as examples of our DSL, approach, etc. Try to get them to look at those and reference those as they write their tests to get ideas from. The tests they write won’t be perfect but they’re at least trying to follow the DSL. You can help them refactor and improve their tests as well
after maybe 6-8 months after they have written a decent amount of tests, start talking about TDD. Those who are interested will make it known to you. Work with them, focus your efforts on them only. (one dev at a time, flip one, then they can help others, move to next dev, and so on) if its a team where nobody has done TDD yet
Starting someone off in TDD is fine, but I find that’s also very hard to instill especially on teams who haven't really coded tests much if at all, even if trying to flip one developer at at time. If I can at the least get one dev at a time to focus on clean code, that’s a huge help to the codebase and to me in trying to get them there.
- same really, just that there is already code, and you might have more of a mess to deal with making it hard for even you to write isolated tests
Now that I've both mentioned it about 15 times because I can't stress it enough and hoping you can't stop thinking about it now. Because I hope you look into it and read that book and visit CleanCoders.com. This is what is going to change your life as a developer, literally.
Again start with clean code, and promise you'll put time aside every night to read a couple pages of it.
P.S. No, people don't read Code Complete anymore. Wake up. They're reading these and have been for years now. It's nothing new.
Sharing code skills is not enough and fail on its own. To share, coach, lead, or just be a developer you have to also work on the below. this is mot always easy and also takes continual practice and improvement. You need to be vested in changing yourself, not just talk about it.
be very transparent with everyone equally and in the same way (do not gossip, be careful when you mention something about another member of the team...keep it business and code, not personal)
be aware of tone in your voice and what you write to people
this one has been really hard for me to completely change but slowly turning it around with fallbacks here and there
The slightest negative tone coming out of your mouth can make it or break it
for you professionally. This is often the hardest to master or being just aware
of at the beginning when first trying to turn that around
be constructive but also know when to be empathetic and don’t forget about empathy because that can be easy to forget at times