May 8, 2017

...Learn TDD with Codemanship

How To Avoid The TDD Slowdown

Both personal experience and several empirical studies has taught me that TDD works. By "works", I mean that it can help us to deliver more useful, reliable software that's easier to change, and at little or no extra cost in time and effort.


That describes the view from the top of the TDD hill. To enjoy the view, you've got to climb the hill. And this may be where TDD get's it reputation for taking longer and slowing teams down.

First of all, TDD's learning curve should not be underestimated. I try to make it crystal clear to the developers I train and mentor not to expect amazing results overnight. Plan for a journey of 4-6 months before you get the hang of TDD. Plan for a lead time of maybe a year or more before your business starts to notice tangible results. Adopting TDD is not for the impatient.

Instead, give yourself time to learn TDD. Make a regular appointment with yourself to sit down and mindfully practice it on increasingly ambitious problems. Perhaps start with simpler TDD katas, and then maybe try test-driving one or two personal projects. Or set aside one day a week where your focus will be on TDD and getting it right, while the other four days you "get shit done" the way you currently know how.

Eventually, developers make the transition to test-driving most of their code most of the time, with no apparent loss of productivity.

After this rookie period, the next obstacle teams tend to hit is the unmaintainability of their test code. It's quite typical for newly minted Test-Driven Developers to under-refactor their test code, and over time the tests themselves become a barrier to change. However much refactoring you're doing, you should probably do more. I say that with high confidence, because I've never actually seen test code that was cleaner than it needed to be. (Though I've seen plenty that was over-engineered - let's not get those two problems mixed up!)

Refactoring is one of the most undervalued skills in software development, but it is hard to learn. And employers routinely make the mistake of not emphasising it when they're hiring. Your refactoring skills need to be well-developed if you want to sustain TDD. More bluntly, you cannot learn TDD if you don't learn refactoring.

The other barrier I'm increasingly seeing teams hit is slow-running tests. I think this is in part a result of teams relying exclusively on acceptance tests using tools like Cucumber and Fitnesse, leading to test suites that can - in extreme cases - take hours to run. To sustain the pace of change, we need feedback far sooner. Experienced TDD-ers endeavour to test as much of the logic of their code as possible using fast-running unit tests (or "developer tests", if you like) that exclude external dependencies and don't rely on layers of interpretation or external test data files.

Learn to organise your tests into a pyramid, with the base of the pyramid - the vast bulk of the tests - being fast-running unit tests that we can run very frequently to check our logic. Experienced TDD-ers treat acceptance tests as... well... acceptance tests. Not regression tests.

Another pitfall is over-mocking. When too many of our tests know too much about the internal interactions within the objects they're testing, we can end up baking in a bad design. When we try to refactor, a bunch of tests can get broken, even though we haven't changed the logic at all. Used as an interface design tool, mocks can help us achieve a loosely-coupled "Tell, Don't Ask" style of design. Abused as a testing crutch to get around dependency issues, however, and mocks can hurt us. I tend to use them sparingly, typically at system or component or service boundaries, to help me design the interfaces for my integration code.

(And, to be clear, I'm talking here specifically about mock objects in the strictest sense: not stubs that return test data, or dummies.)

So, if you want to avoid the TDD slowdown:

1. Make a realistic plan to learn and practice

2. Work on those refactoring muscles, and keep your test code clean

3. Aim for a pyramid of tests, with the bulk being fast-running unit tests

4. Watch those mocks!

Posted 3 years, 3 months ago on May 8, 2017