Clarification: this isn’t a post about BDD vs. TDD, it’s a post about Spike and Stabilize. Tagging those now.
For love of TDD
Dan North and I have been talking about some different ways of writing software that matters. We’ve been talking about not doing TDD, and not automating BDD scenarios.
A lot of people have reacted to these suggestions with something approaching visceral horror, as if we’ve committed outright heresey, which I guess we have. TDD is such a game-changing practice that those who’ve discovered it recently may not be able to even imagine a world without it, since that would mean going backwards. So I want to explain a little bit about my take on TDD, just to make it really clear, and then introduce some different ideas.
TDD is amazing.
TDD is a truly important practice that every developer who writes code for a living ought to know about and attempt to master. TDD is how I learnt to write maintainable code; design classes with appropriate and single responsibilities; understand what it was that I was really trying to achieve.
The people who came up with TDD are deserving of every respect, which I haven’t always given them. I decided to start with that this time just to make it clear. TDD is a flippin’ awesome practice. When we talk about “BDD isn’t just TDD”, it definitely evolved from it. If TDD hadn’t existed, BDD and ATDD wouldn’t have either. So a big, massive thank you to all those people who helped create and espouse the values of TDD.
If you haven’t experienced really great TDD, stop reading. This post is not for you. Go read Bob Martin‘s many articles and posts, or Kent Beck’s book, or find your local code retreat instead. You will get more out of that time than reading this article.
Dan North and I have been talking about moving beyond TDD for a while. Here’s a confession: I don’t always write my tests first. I often don’t automate scenarios up-front either. And I consider it right for me not to do so.
Dan recently blogged on the opportunity costs of our various practices, and used TDD as an example of a practice that carries such a cost. It can take longer to produce software with TDD than without. I know this because I’ve tried it both ways, as has Dan. We do something that Dan calls “Spike and Stabilize” – trying one or several things quickly to get feedback, then stabilizing the end result after we’ve managed to eliminate some of the uncertainty around what we’re doing. I can’t tell the difference between the end result when I TDD something to when I spike and stabilize, except that the second one lets me get feedback from my stakeholders faster, and is easier to change in response to that feedback.
For me, that means starting with a UI and knocking out something approximate, frequently with hard-coded data. If the UI is the place of uncertainty, I’ll show it to the stakeholder immediately, or try it a couple of ways. Sometimes it might be a performance concern, in which case the UI will be something that exercises the performance. Sometimes I might need some simple behaviour behind the UI, which I also just “hack out”.
I keep all the code I’m writing clear, readable and easy to change. I can do this because I can write examples of how my class is going to work (a.k.a. unit tests) in my head. That’s come from hours of doing TDD, at work, at home, at code retreats, after work, during lunch hours. If you want to be really, really great at coding, I think you do need to have the 10,000 hours of practice that Malcolm Gladwell talks about. I’m pretty good rather than really good (and particularly, I have shallow rather than deep knowledge) but I get TDD enough to move beyond it. I’m emphasising this not to show off my knowledge, but to stop anyone who’s still learning TDD from thinking, “Oh, this is great! I can just do what Liz does.” Get good at TDD first.
Dan works with amazing people who are way better than I am. He’s damned good too. Developers like Dan and his team don’t need TDD in order to create good designs, separate responsibilities or simple code that does just enough to do the job. They’re really good at TDD, and have moved beyond it.
For most people, TDD is a mechanism for discovery and learning. For some of us, if we can write an example in our heads, our biggest areas of learning probably lie elsewhere. Since ignorance is the constraint in our system, and we’re not ignorant about much that TDD can teach us, skipping TDD allows us to go faster. This isn’t true of everything. Occasionally the feedback is on some complex piece of business logic. Every time I’ve tried to do that without TDD it’s stung me, so I’m getting better at working out when to do it, and when it’s OK to skip it. If in doubt, I do TDD. I recommend this as a general principle. Otherwise, I try to eliminate the ignorance I have. Here are the rules I’ve learnt which help me to spike things out and stabilize them.
- If in doubt, do TDD.
- If it’s complex enough or has enough learning in it that pairing is useful, do TDD.
- When spiking, hard-code the context rather than adding behaviour wherever possible. Hard-coded data doesn’t require TDDing.
- Don’t pick the right libraries or technology. Pick the technology that’s easy to change. And don’t write tests around other people’s libraries – isolate them through interfaces or adapters instead.
- Once the uncertainty you’re dealing with is no longer the area of greatest ignorance, stabilize by adding tests. You’ll be able to tell at this stage if you should have TDD’d first based on how much rework you have to do to make the tests readable.
- Adding tests afterwards is especially important if you’re working in a team with mixed skills or silo’d roles, or other teams in the same code-base, as your team-mates and colleagues will need them to provide documentation.
- Adding tests afterwards is especially important if you’re the kind of person who forgets what it was you were working on after a couple of months (i.e.: just about everyone).
- Adding tests afterwards requires even more discipline than adding them before-hand. If you find you’re under pressure to skip that step, do TDD instead.
- No amount of automated testing is a substitute for trying out the code you’ve just written manually.
- If your user is another system, hack something out to allow you to pretend to be that other system, then stabilize that. You’ll need it later.
By doing TDD – writing our tests first, or before we’ve got feedback on what it is we’d like to achieve – we are creating a premature commitment. The principles of Real Options say, “Never commit early unless you know why”. If you’re working with uncertainty, and if you can avoid adding investment before getting feedback – for any practice – then you will actually go quicker and be able to respond to discovery faster. You will be more agile.
However, if you don’t yet know how to write software that’s easy to change and maintain, every piece of code you write is a more concrete commitment than it will be if you learn to do TDD, since TDD helps you to adopt these practices, as well as providing nice documentation and living examples of how to use your code, how it behaves and why it’s valuable. If you haven’t yet experienced amazing TDD and you try doing things this way, you’ll probably find that you end up with a lot of rework when you come to stabilize the spike (and if you don’t stabilize the spike, you’ll eat up cost in maintenance later).
It’s interesting to me that this is actually how a lot of people learn to do TDD – by adding tests afterwards, until they gain enough confidence to add tests first. It really does remind me of this quote from Bruce Lee:
Before I studied the art, a punch to me was just like a punch, a kick just like a kick. After I learned the art, a punch was no longer a punch, a kick no longer a kick. Now that I’ve understood the art, a punch is just like a punch, a kick just like a kick. The height of cultivation is really nothing special. It is merely simplicity; the ability to express the utmost with the minimum.
Before I studied the art, TDD to me was just like TDD, you write a test, you make it pass. After I learned the art, TDD was no longer TDD, The order of the tests and code blurred. Now that I’ve understood the art, TDD is just TDD, You write a test, you make it pass. The height of cultivation is really nothing special. It is merely simplicity; the ability to express the utmost with the minimum.
Bob – unless you can type faster than you can think, why are you writing the test for something that you will probably end up throwing away? I get that going through TDD means we’re less likely to end up with something we throw away… I just think there are faster ways to get to *there*, especially in high-uncertainty situations. Hanging out with a lot of start-ups right now; hardly any of them do strict TDD, and it works (they have really great TDDers there too).
Picking on the quote is easy. I would love to have a conversation on the arguments. If I can write the tests / examples in my head, and produce well-designed code, and I think faster than I type, then why do I need to write the tests first?
(I genuinely believe that there are places where I’m right; I am fully prepared to believe that I haven’t covered all the constraints under which I’m wrong! So please help me find them.)
Or, putting it a different way – why would you throw away the spike if you automatically write code that’s easy to make production-ready anyway?
I think the disconnect here is equating “TDD” with “programming.” TDD is a useful part of programming, but it is not the only part of programming. Nor is it the most important part. I can write useful and valuable code without TDD. I can write valueless and unhelpful code with TDD. There is no correlation between TDD and business value, so we need other things too.
This is a great article, Liz. I’d like to add one clarification though: I wouldn’t say the people I’m working with have moved “beyond TDD” or test automation, because that would imply TDD is a waypoint or marker on the way to something better. I would say we’ve moved beyond only TDD which was the big readjustment for me.
@Bob: TDD isn’t the only way to write software. It’s a very useful one – I believe should be part of every programming course – and it’s one I will always have in my back pocket. But it won’t be there alone and it won’t always be the one I’m using.
@Dan, yeah, your team is amazing. I still need TDD usually, even if it’s just in my head, and even if I’m not using the word “test”. Hoping to get some TDD coaching in over the next month – that would be fun!
Liz, imagine you’re an accountant.
If you’re an accountant, I guarantee you that you’re doing
double-entry book-keeping. It’s a certainty. You won’t be doing it on
pen and paper with those flappy-eared ledgers and journals; you’ll be
using the latest account software package.
But you will be doing double-entry book-keeping.
You’ll be doing it because it’s the standard. It’s standard because it
works. Thousands of accountants have tried other things and
failed. When they try double-entry accounting, their books balance.
They don’t skip doing the books to go faster.
It’s not about speed.
It’s about the goal you have, where you want to go and what quality
you want to arrive their with.
Accountants don’t go through the whole year and cobble-together the
finances at the end. They do it as they go along. Sounds familiar?
You must think of TDD like double-entry book-keeping.
You can’t skip TDD to go faster. It doesn’t work. You’ll be a laughing
When you do TDD, others appreciate that you’re doing. They see you’re
not working in the stone-age. They acknowledge that you’re doing
proper accounting, if you will.
Stop talking about alternatives to TDD. You sound as though you’re
trying to push accountants away from double-entry book keeping. They
won’t listen to you.
TDD IS THE ONLY WAY TO WRITE SOFTWARE.
IT’S THAT SIMPLE.
IF YOU’RE NOT USING TDD, YOU’VE LIVING IN THE STONE AGE. I’M SORRY IF
THAT’S A SHOCK TO YOU, BUT IT’S THE TRUTH.
TDD: THERE IS NO SUBSTITUTE.
Sam, accountancy is predictable. You add numbers together; you get a predictable result. Software is full of uncertainty, and your analogy doesn’t hold.
Interesting arguments — “you’ll be a laughingstock”, “you’ve[sic] living in the stone age,” others will “appreciate what you’re doing.”
So then, it boils down to “always do TDD or people like me will call you names?” Well, fair enough. I’m always glad when an argument is boiled down to its essentials. You have clearly done so with this one.
Sam, that is a very extremist view on TDD. I do agree with Liz to some degree, creating a spike and wrapping unit tests as an after thought is a useful approach. I only stumbled upon this recently where I was using a Microsoft Azure library and attempted to use TDD but was failing. It’s often viewed as a stigma if you’re not using TDD and people who are obsessed with this ideology would frown upon your article (like Sam).
Thank you for this post! I’m having an absolutely passionate internal debate over whether or not I love it or hate it. That’s a good thing: I like to have my assumptions challenged.
I do like the emphasis on the issue that TDD is not enough. TDD is not a substitution for knowing what a good software design looks like, for communication with the customer, and so on. But I rarely hear anyone suggest that TDD is The Silver Bullet that Solves All Problems. It’s just the simplest and most powerful solution found to-date for a host of nasty root challenges.
You say to Bob: “unless you can type faster than you can think, why are you writing the test for something that you will probably end up throwing away?” Here is the biggest difficulty I have with the post: We would have to predict up-front what is going to require TDD. I’ve seen numerous production cases where the boundary conditions were challenging, but initially misunderstood. Having no tests around “simple” code tends to leave the code in a state where a “simple” change could break “simple” assumptions.
I ask teams to do TDD because otherwise they’re assuming that someone on the team (usually the “Architect”) is precognitive, and knows where the challenges and future changes will happen.
Having done TDD in a disciplined fashion (“discipline” includes doing something even when you’re tired and you don’t know why you’re doing it) has resulted in some amazingly rapid turn-arounds on surprising and delightful (and lucrative) features.
Besides, once you’ve mastered TDD (as the quote goes) then it’s usually faster to do it TDD than to just write the production code. As you have expressed, it’s a thought-process. “What doesn’t the code know how to do?” but if you skip the test, you also skip the opportunity for the occasional surprise. “Oh! I had expected that test to pass now!”
See the following for more on why I do TDD even though I know I’m smart enough to write defect-free code: http://powersoftwo.agileinstitute.com/2011/11/essential-test-driven-development-just.html
So my recommendation to you and your readers would be to do TDD even when it seems painful, try that for another decade, then look back and see how often reflection (vs. prediction) has paid back for all the “overhead” of using TDD even for the simple stuff.
Disclaimer: My teams have always been four or more people, and were building something that had to be maintainable over months or years. Were I building a quick, one-time-only smartphone app, alone, I’d…use TDD! 😉
PS: Since I’m big on accurate attributions when writing:
(1) “Reflective” vs. “Predictive” process is a notion put forth by James Shore.
(2) You accurately quoted Bruce Lee, but I recognized that Bruce was paraphrasing, so I did a little digging. From the Compendium of the Five Lamps (Wudeng Huiyuan, 1252):
“Before I practiced Ch’an, I saw that mountains are mountains and rivers are rivers. However, after having achieved intimate knowledge and having gotten a way in, I saw that mountains are not mountains and rivers are not rivers. But now that I have found rest, as before I see mountains are mountains and rivers are rivers.”
— Ch’an Master Qingyuan
The Agile community now has a Bob Martin quote paraphrasing Zen literature! 😉
PPS: Liz, if you want to edit my first comment for me (or just include this one ) I really wanted to point you to this blog post (the other one is not as focused):
Rob, thanks. And, yes, you would have to predict up-front, or get it a bit wrong. If you’re only making small spikes you’ll find out pretty quickly. Sometimes I spike something, realise it’s going to me more complex than I thought and add the tests – it’s no slower than TDD these days.
There are plenty of things which require spikes, and I don’t think anyone has arguments about those. The only thing I’m doing differently is spiking more often (because I do the uncertain, risky things up front and want quick feedback) and keeping the code.
We’re not looking for defect-free code here, that’s the thing. We’re looking to get something we can stick in front of our PO, or send a few messages over to check the performance, etc. Aftewards, we add tests (and then of course TDD the rest of the missing behaviour). Maybe I didn’t make that last bit clear… oh, well, it’s here now!
As I said – if in doubt, do TDD. I don’t have doubts about spiking any more. So many times now we’ve been able to get something up and running that simply didn’t *have* any behaviour to be tested, just so that people could see it…
Practising TDD encouraged me to do things in a certain sequence and style: write only a few lines at a time, get a simple example working by hardcoding some data, remove duplication mercilessly. I now do those things whether I test-drive my code or don’t. Even so, I tend to prefer to test-drive, most of the time.
I used to stop myself from writing code without test-driving it. I no longer do. As with Liz, with Bob, with others, that comes from my myriad-plus-hours of practice.
I have taught people the first rule in Liz’s bullet list for years: when in doubt, write the test. I call this a Novice Rule (Dreyfus Model) and teach it that way: “The Novice Rule is ‘if you’re not sure, then write the test; you need the practice.'”
One thing i wonder about, isn’t the whole purpose of testing to assert what you think? Most people who start with TDD tend to say to me: I already know what I want to write. Eventually, using TDD they end up with something simpler. If you are “so good at TDD” then isn’t this a slippery slope?
Also, what about your safety net? If you don’t write tests, how are you going to make sure nobody breaks something without you noticing it?
Great article Liz. We’ve been doing “loose” TDD at work for a while now. While we haven’t defined it quite as eloquently as you, we like to think of it as a slightly more pragmatic approach. Some of our code has very good coverage, some of it has none, but we can live with that because most of the time the coverage is a result of the code’s function and purpose (and sometimes it’s age!).
I think “when in doubt, write a test” is an excellent rule-of-thumb, and something we have been applying subconsciously for many months now.
@Sam Weisen > this kind of religious zeal is destructive, because it limits you to a specific practice which may not necessarily be appropriate in some scenarios. TDD is a “good” way to write software, but it’s not the “only” way, or the only good way.
You may think “TDD IS THE ONLY WAY TO WRITE SOFTWARE” but it’s clear that others may not agree. That doesn’t make them a laughing stock.
Liz: good article! I really like your bullet list about the Hows, Whys and Whens.
While isolating other people’s libraries through interfaces or adapters _is_ a good idea, I also _do_ think it can help to write tests for them because of two reasons:
1 You learn the true behaviour of this library (and not the “expected” or “promised” behaviour through reading the always lying documentation).
2 Tests build-up your net of safety under this foreign software you use. Then you can take advantage of getting alerted if their behaviour or interfaces change over the complete project life time.
Stefan, what if you’re not sure what the outcome should be? If the business are unsure, or you’re working with a framework or library you’re not familiar with and you don’t know what it should do, or there’s some other high uncertainty? This is the situation I find we’re often facing in teams, especially if we’re focusing on new, differentiating functionality or doing it in a new context. If you have a clear outcome and you know it can be achieved then yes, I agree, you might as well do TDD.
For me, a test isn’t as much about pinning the code down so that others won’t break it as it is about helping people understand how it behaves and why it’s valuable. If I’m not sure whether a piece of code is actually going to be valuable, I find it hard to write a test and can’t see why you’d want to.
Thomas, I think that might apply to libraries written by people you don’t trust or whose updates are hard to roll back. The teams I work with tend to use a lot of open-source, and I hope we’d contribute a test to their source first, before writing our own internal tests.
If I really distrusted the code enough to need to test a library myself, and I couldn’t provide a realistic and public example of why I wanted it, I’d probably want similar protection in production – an anti-corruption or validation layer, for instance. Then I would test those, rather than the library, and would get alerted in production. We do this all the time with foreign web services. I do think we give a lot more trust sometimes to commercial products than they deserve though!
For professional pilots the many practices they follow, like using checklists, help them to achieve greater speed with fewer defects. The most junior airline pilot performs much better by following the checklist, the mid-career pilot somewhat better, and even a seasoned veteran performs better with the checklist. However, the most experienced pilots, the ones who write the checklists, are actually slowed down by the checklists. For them, using a checklist can introduce errors. Why? They have excellent judgment, skill, and instinct. Is this not true for a world-class cellist?
I’d imagine that when a programmer gets really, really good that their judgment, skill, and instinct is wildly better than mine. I still do not believe I’m beyond TDD or BDD, but I do understand how experience and expertise can take others into a style of development that transcends those approaches.
TDD/BDD are not checklists. They are a tool for coding. They are not just steps you go through because you must, they help you focus your problem solving skills on one particular problem at a time while giving you a sense of it’s impact on your other code. A better analogy would be whether pilots use the altitude indicator or not. Could you technically fly a plane without one? Yes, absolutely. Is it a valuable tool that even seasoned pilots rely on, you betcha – because it solves a number of problems that every pilot experiences, no matter how many times they’ve been off the runway.
Another function of TDD and BDD is that they act as documentation for other programmers to quickly determine the “genius/wisdom” of your code. It also allows them a level of confidence when adding features that may interact with your mad skills. If your product was nicely packaged in a bubble and you were the only person working on it then I say “bravo!” throw caution to the wind, however, if others will be relied on to support or continue development on it then… Well, I guess to me, I do it because it’s not just added functionality/stability/tool to the program or the code, but also to the system of people that will support it when I’m gone. It’s almost a matter of respect for fellow developers.
I don’t think TDD/BDD is law, but I am more likely to ask why I am NOT using it, rather than why I am.
Joe, I agree that the altitude indicator is useful, particularly as you get closer to the ground. But I also bet the Wright brothers didn’t have one, and we seem able to build on their achievements just fine. Sometimes it’s worth looking to see if something’s possible before making it perfect, especially if nobody’s ever done it before.
@Liz. Yes. Sure. If your final product is a page of code that does just one thing then yes, why not. But even then it’s still worth considering. The Wight brothers may not have had an altimeter but they had blueprints… even for the prototype.
I don’t really care if someone uses TDD/BDD of NoDD. Nor do I see TDD as a tool that is just used to make something perfect. For me, it’s a swiss army knife with heaps of functions. One is to, of course, test your code, another is to communicate your intention to devs who look at your code; yet another is to focus your thought process on what you are trying to accomplish both large and small. Some people like writing tests first just because it encourages design thinking.
TDD/BDD can be much more than just tests. It can be helpful before you write the code (design), while you write your code (focus) and after you write your code (communication). If you don’t like it, don’t use it, but there are reasons people consider using TDD/BDD for prototyping and spikes outside of just being about to test the code.
Liz, I really like this article, it is thought provoking and, in places controversial, nice. And… I find that I mostly agree with you, but with one big caveat.
Skipping any best practice by reasoned judgement (not by arrogant claims of ‘I’m good at this so I don’t need to do it the same way that lesser mortal do) seems perfectly acceptable to me. There are plenty of complementary analogies we could think of, like building a UI that has ‘learner’ and ‘expert’ configuration options. However (a) it implies a level of maturity in the practice in order to make that judgement…. If you happen to be working in a group where the majority are at that point and can easily support those that aren’t, I don’t see a problem. Most times for me I’m working with teams of very mixed and uncertain ability (and attitude to software development), particularly where we have a mix of permie’s, contractors, and off-shore. In these case (which are the most typical in my world) I prefer a consistent approach of ‘use TDD’ rather than ‘use it unless you feel confident not to’. Second, and it was mentioned by someone above, it’s not unusual to get bitten in the ass (‘oh I didn’t expect that to fail’). Without the TDD safety net, you will still find that out, but probably later.
Now I’m going to have a little nit-pick. You said above ‘… if I find it hard to write a test …’ … To me that’s a classic ‘smell’, if you can’t figure how to write the test …. Stop
I know you were questioning whether to do TDD for code where the value may not be certain, but, again, a tough judgement call only appropriate for experienced practitioners, not your average joe/Jane programmer (and that’s most of us).
Dan also said, ‘… There’s no correlation between TDD and business value’… I think I’d have to challenge that idea. If my tests have no relationshipship to the acceptance criteria for the user story which describes the business feature and it’s associated business value (given that that feature has been sufficiently prioritised to be included in the sprint that I’m delivering it in – by my business customer), well yeah, in that case the’s no correlation. But that’s not how I do TDD (do you ??), every line of code and every test has a direct a measurable business value, if not, I need to stop right away.
Fraser, it’s not that I can’t figure out how to write the test; it’s that I can’t figure out *what* to test if I’m not sure that code will be valuable. This only applies for “complex” code ( https://lizkeogh.com/2012/03/11/cynefin-for-devs/ ).
For instance, I’ve just written a sudoku game with just the basic grid working ( http://github.com/lunivore/sudoque ). Most of the code I wrote was tying something at the back end with the grid, and took a lot of experimentation. Even though it’s possible to instantiate all the view models without the views (so they could theoretically be unit-tested) their job is so tied to the GUI that I would have had to throw almost all the tests away as I changed the GUI as well.
As soon as the GUI stabilized a bit I extracted out a model to represent the domain (which I now understand better) and stabilized that too. I found one small bug that I’d introduced which wasn’t obvious until I started writing the examples of how a cell should behave.
Your comment is applicable to situations where we’re sure of the outcome we want. A lot of times I find great programmers insisting that business should be certain about what they want in order to get the acceptance criteria you mention, instead of embracing the uncertainty and trying something out instead. I don’t think it’s necessary to stop in those situations. It would be fantastic if business customers could prioritise the elements they’re *least* certain about – the most risky – and have developers work with them to try different solutions, but as long as we insist on clear outcomes and value I don’t think that will happen.
Kent Beck said in a fairly recent interview that if your just knocking up some code to figure out whether a given approach is even feasible, then don’t bother writing tests. Wait until you have some level of certainty. I agree, and it seems, so do you insofar as your Suduko example. The skill is in knowing when experimentation stops and the first throws of production ready code begins, and not being concerned about starting over once you have reached that point as opposed to polishing up your PoC.
I envy you your apparent time with enlightened start-up style organisations. Whilst it ought not to matter whether you work for a company large or small in terms of finding the best way to achieve software quality, it just does. I work for a large company that talks a good game in terms of agile and the peripheral disciplines, but mostly that’s as far as it goes. I don’t think we’re that atypical (in fact I know so since we have a procession of contractors thru from many of our counterparts and this is usually the first conversation I have with them). I’m not complaining, the greater the challenge, the harder it makes you think about alternatives, and at the end of the day, agile is not so different from RAD and iterative prototyping approaches of 20 years ago !
I’m going to try one more time.
I’m going to give you the best argument I’ve ever heard for using TDD (and not going to some mythical, “Beyond,” TDD, which is really just not using TDD).
This argument doesn’t talk about TDD’s being actually a design methodology rather than a test methodology, it doesn’t talk about red-green-refactor, it doesn’t talk about not using stone-age tools, or all the other, commonly accepted TDD stuff.
The argument is this.
The greatest medical advance ever accomplished by the human race – in terms of the numbers of lives saved – happen not when penicillin was discovered, not when CPR was standardised, not when the Red Cross was established: it was when surgeons finally started washing their hands before operating.
That simple act alone rid the surgeons’ hands of most bacteria and saved patients from secondary infection to their wounds, thus saving their lives.
TDD is like a surgeon washing his hands before operating.
Sure, there are probably other things a surgeon needs to do prior to his work: studying for 9 years to be a surgeon in the first place, for instance.
But no new invention is going to obviate the need for every surgeon to wash his/her hands before operating.
And when you tell people to stop doing TDD, that’s what you’re doing. You’re telling surgeons to stop washing their hands before operating.
It’s that ridiculous.
Sam, I only use TDD as a design methodology. I haven’t mentioned it here because that’s not what the post is for (there are a ton of other posts on this blog about that, most of which avoid using the word “test”).
Oh, no, wait – I have. Right here: “TDD is how I learnt to write maintainable code; design classes with appropriate and single responsibilities.” Now I am suspecting you haven’t actually read the post.
Rather than resorting to yet another misplaced analogy – which was cute when Bob Martin did it, by the way, though I notice you haven’t attributed it to him – why don’t you speak to the same question I asked him?
I am presuming you occasionally write spikes. Even Kent Beck writes spikes.
If you could write a spike well enough to wrap tests around it later and stabilize it into production code, why would you throw it away? If spiking became easier as a result of not throwing it away, would you do it more?
(That’s pretty different to saying “Stop doing TDD”, too. I haven’t done that and would appreciate you not putting words in my mouth.)
I’ve been a programmer for over thirty years (in my forties now), working both as an employee and as an independent software consultant. I never use TDD, and hardly ever write unit tests. According to Sam, this makes me a plague carrier. And yet, I’ve received enough positive feedback from employers, colleagues, clients and recruiters to know that I am in fact a very good programmer.
What value would TDD add to my work? Designing classes is only a small part of what I do, I don’t feel I need a methodology to help me do it. As two examples of what I do, I’m currently working on improving the loading time of an existing (and highly event-driven and asynchronous) application, and last week I was wrestling with browser idiosynchroncies when manipulating dynamically loaded content inside an iframe. How is TDD going to help me here?
Even in the last blue-sky project I worked on, the challenge wasn’t so much the design of the classes, but the overall system design – which included writing a code-generation tool to both reduce the chance of human error and eliminate the need for any tests. If I’d gone the TDD route, I think I’d be so focused on the design of individual classes that I wouldn’t have had the mental capacity to think on a higher design level and come up with a simpler solution. My experience is that after you get beyond the novice level of programming, the challenges in software aren’t about classes and algorithms, but are about complex interactions between moving pieces, and that seems to be outside of the scope of TDD.
“TDD is like a surgeon washing his hands before operating.”
No it is not. A skilled surgeon will spread just as much disease as a bad surgeon with unwashed hands. But a very good programmer is not going to benefit from TDD as much as a novice programmer. If TDD teaches good design lessons, then eventually the lessons get internalized as Liz points out. It seems to me that you could learn the same lessons without TDD, such as by studying well-written code. If very good chess players can play a game without a board, why can’t very good programmers write tests in their heads? And no, they won’t catch every bug that way, but neither will TDD.
“TDD IS THE ONLY WAY TO WRITE SOFTWARE.”
Demonstrably false and smacking of religious extremism. Sam, you’re clearly smarter than that – try again.
“TDD IS THE ONLY WAY TO WRITE SOFTWARE.”
Much of the software that people run on a daily basis was written without TDD. I am talking about Firefox, Google Search, Chrome, Windows, Linux, Halo, World of Warcraft, and more. If you are going to take such a strong position in favour of TDD, you should be prepared to show us some TDD examples of world class software, and explain how TDD makes them superior to their non-TDD equivalent. As someone else said in another comment thread on the same topic: “First, show us the code. _Then_, you can preach”.
And note that I am not saying that TDD is useless, or that unit testing has no value. But they are tools among many, and like any other tool there are situations where they are appropriate, and others where they aren’t.
Scott, how do you refactor your code, or how do others refactor your code without unit tests?
Manuel, as a note – not writing tests first is not the same as not having unit tests. You *can* write them afterwards, and I recommend doing that. For me, that’s part of the “Stabilize” bit of “Spike and Stabilize”.
Having said that, lots of teams are working with legacy code bases, and have to refactor them just to get the first unit tests in anyway. How do *they* refactor to get the tests in, if they have no tests to start with?
Pingback: » FinishHim Progress, the Texas Method, and RTK progress Just another day…
This is similar to my own line for TDD. I usually say “you don’t do TDD when sketching”, but “spike and stabilize” is also a great phrase for it.
A bit late to the party (I read the article this year, but only read the comments because I was twitter stalking Dan 🙂 )
The double entry book-keeping analogy is a good one for the predictable ins and outs of software delivery. I know where I’m going, I know my inputs and I know my outputs…
But, that’s not all there is to accountancy. At the end of the year, we need to submit our accounts for the year. There is some flexibility in how the numbers can be arranged (was that an expense or an asset? Can I write this off in some way? Were all my payments to my shareholders classed as dividends, or could they be expenses or something else?). The accountant will know that they want to minimise the tax burden; this is a wide spectrum, ranging from absolutely no effort and probably paying more tax than necessary, passing through the perfectly reasonable deductions, past the realm of the “that’s not really the spirit of what that means” and “that’s still legal, but not really moral”, until you get to the pits of hell where the books are cooked. Double entry book-keeping doesn’t help here. It gives you the inputs to the system; but balancing the legal, moral and necessary expenditure is something that requires experimentation and a vague sense of “that looks right”-ness.
And what we’re talking about here is “that looks right”-ness. It would be great to be omniscient (I’m working on it), but until then I like to take a step, have a look round and work out if I’m in a better place than I was, and/or am I heading in the right direction?
Not forgetting, of course, that it’s much easier to check “that looks right”-ness in hindsight than foresight 🙂
I agree with you, but I think there are a bit of ambiguity in there. First, as Dan mentioned, I also don’t feel right about the use of “moved beyond…”. It’s seems like you’ve left TDD behind when I read the sentence. But you obviously have not. TDD is still in your toolset, just not over glorified as it used to be. Now just as important as any other tool.
The other one is, as I understood from the comments, lots of people had misunderstood what you meant by Spike. To my understanding it’s a little throw away piece of code that you can write in an hour or maybe two and when you know it’s what you want you Stabilize it with TDD. If it goes beyond that I’d say it gets risky.
Andho, my spikes are occasionally a bit bigger. The trading GUI I knocked out took a week or so, thrashing out the fields with the BA, and including a bit of a learning curve for some of the technology. The back-end to it was hard-coded, so there really wasn’t much behaviour to test at that point.
Most of that week’s work got thrown away, so I’m glad I didn’t try to do a better job. I guess I could have got the feedback on it more quickly if we had had better access to the traders, but we didn’t. A couple of subsequent experiments worked better, and I started stabilizing a new GUI after 2 days, wrapping tests around existing code. 2 weeks after that the module had close to 100% test coverage, I was adding new behaviour using TDD, and the classes were reasonably neat and readable.
So sometimes I do work at the timescales you describe, but not always.
“Beyond” was a silly name, though. You’re right about that.
Pingback: TDD: Links, News And Resources (14) | Angel "Java" Lopez on Blog
Pingback: Making the right thing work before making it work right – pavsaund
Pingback: Defining a Test Strategy for Continuous Delivery - Simple Oriented Architecture