It's a hallmark of "experienced" non-dogmatic product people (UI/UX/Dev) that can use their intuition to know what are the happy paths that they need to test, what interfaces are likely to not change (e.g. a user is probabilistically always going to be a member of an organization, so unit tests around that are likely not waste), and what level of quality to introduce for a given feature relative to the probability the feature is going to exist in perpetuity.
You can concretize this by calling it "spike driven development" if you want (that's what we do) but the point isn't that TDD is faster or slower but that high coverage might be inappropriate (TDD isn't binary - degree of test coverage goes from 0-100%) at different phases of the build/learn/refine cycle.
For example, if we're building a speculative feature that we think has value but hasn't been proven yet, we want to spend the least amount of effort (code/time/money/pick some) possible to prove that it's viable.
Small bugs aren't really that interesting (provided the thing "works") and so we're explicitly willing to live with degraded quality to prove the feature is worth further investment. We're also explicitly not building a "large" feature (so it's very likely to get coded in a day or two) so the surface area for major showstopper bugs is minimized.
Often the feature will be thrown away or majorly refactored into something that is completely different.
In this case, full-bore 9x% coverage TDD is probably waste as the feature existed for a short period of time, the interface changed dramatically, or otherwise was proven invalid. High test coverage makes major interface refactors really expensive and you really don't need that level of quality for speculative features.
After you prove this feature is a "thing" you want to keep around (and you've nailed down the interface), then it's a perfect time to re-write it with a much higher degree of test coverage.
This is exactly how we did things at Everlane (http://everlane.com) and Dev Bootcamp (http://devbootcamp.com).
I find that tests help me write things that are complete and "nailed down" the very first time.
I know this author is advocating it, but has 0 (zero) solid data or evidence to back this assertion. He is just trying to sell another religion. What I have noticed, TDD is being pedled heavy by RoR contracting shops that just care about billable hours and not vested if the startup or company is going to make it on the long run. I see often that it is the usually youngish and guidable engineers that fall in the trap of eating it up. TDD is something that makes younger and less experienced developers feel good (more locs) and a false sense that the code is correct while there is solid data that even 100% unit testing coverage finds only 20% of defects, at best.
Having shipped many world class products, and none of the processes that build those products had any resemble of TDD in them. So, to all the pragmatists out there feel free to ignore the advice on the article. I'd yet to hear a super successful startup that used TDD.
Having Tested Code (which is a good goal) != TDD. TDD to me is putting the carriage in front of the horse. Anybody that have created large systems from scratch, TDD is a major slowdown on the refining process. Your job is to SHIP a working product. Testing is only a small part of it, and its positives should be considered alongside its negatives and side effects on the shipping timelines.
Having worked for over 10 years in the industry I have to meet any great programmer that preferred TDD. I hate to attack the author, but from his bio he seems a bit like a process peddler. I don't see him have worked on any successful startups, or anything that had a good exit. So take his claims with a grain of salt. http://www.8thlight.com/our-team/robert-martin
He is trying to sell a religion, so buyer beware.
The first is that they ignore or gloss over the fact that there is no shortage of world class software written without TDD. I also have yet to see any great programmer preach the virtues of TDD.
The second is that they blur the line between TDD and unit testing, maybe intentionally. I don't use TDD, but that doesn't mean I don't write unit tests. I just don't write unit tests all the time, and I don't write them first most of the time. In fact I think the advocates would have better success if they advocated plain unit testing instead of TDD.
What irritates me though is that several valid criticisms of TDD have been published (1), and in some cases the advocates (Uncle Bob included) were given an opportunity to respond, but did so poorly, or just didn't bother at all. This sends a message that they are trying to sell something.
TDD has its place, but it is not always the right tool, and it is certainly not a silver bullet.
(1) - Andrew Dalke's "Problems with TDD" may be the best criticism of TDD that I've read so far. The exchange between Dalke and Uncle Bob in the comments is also revealing. http://dalkescientific.blogspot.ca/2009/12/problems-with-tdd...
- Jacob Proffitt also wrote a classic (IMO) on the subject. http://theruntime.com/blogs/jacob/archive/2008/01/22/tdd-pro...
The reference to a "silver bullet" is a straw man. TDD is not a silver bullet. TDD doesn't keep you from screwing up. TDD will not guarantee success. But adopting and following the discipline of TDD can be remarkably beneficial, and shift the odds more towards success.
These days it just makes me sad, for two reasons and not (probably) the ones you think. It makes me sad mainly because, well, in a LOT of ways you're not wrong. You can live (and live, and live) in a comfortable bubble between the time a new codebase is started and the time technical debt settles in and makes new features of nearly any kind far more expensive than they're worth, for an entire career. You can deliver a lot of software that way.
You can also develop some pretty good habits (as they did in the days before TDD frameworks) around manual testing and general experience, that mitigates the onset of technical debt and bad architectures. The further down that path you go, the better you get and the more successful projects you complete, the less TDD will seem like a Good Idea, and more like, as you put it, someone trying to "sell a religion."
I've been in the industry 15 years, and I've worked with a lot of guys like that think like this and gone a long way down that path. Probably like you. Some really, really smart guys that I've learned from and admired. Some of the smartest coders I've worked with. They had unreal IQs and analytical strengths that always knocked me out. They could crank out features very, very fast.
And they wrote some of the worst code I've ever seen. Not dumb code. Bad code. Awful. A trail of debt to make the heart sink and the eyes roll back: long, 300-600 line methods, inlined sorting algorithms because maybe they thought built-in Hash's search was slower than they could write themselves. Early denormalization. Unclear intentions, poorly named methods, and (here's the overall characteristic): non-idiomatic code. Code that doesn't follow conventions, code that is hard to read, impossible to reuse, obviously unclear in its original intent, and therefore brittle, brittle, brittle.
The business consequences in specific instances were more than tangible. Six weeks for a refactoring here. Months added to timelines for new features there. Features actually abandoned. Millions in revenue lost.
There has been an uncanny relationship between these kinds of coders and a common theme of resistance to the following ideas: TDD, SOLID, Design Patterns. The same guys who leave a trail of maddening debt (all the faster because they also tended to be some of the smartest coders on the team), also tended to have a deep suspicion of "high level" thinking, frameworks, big-picture conventions. And always, always, the same old red herring, false dichotomy you present here: "Do you want to ship code or write elegant, well-tested code?" As if the two were opposed. As if the two weren't actually directly related.
Here's the thing: they had a point. The inverse errors -- over-architected, over-tested, over-thought code can be just as crazy-making. Some junior dev who just read Design Patterns in 2005 and thought they'd Seen the Light and starts spraying Bridge Patterns all over the codebase was just as bad as the aforementioned analytical, structured-code debt machines.
I can only go on what I know, and here's what I know: without exception every guy I've worked with that thought this way -- suspicious of TDD, plays "shipping" against "tested/craftsmanship" -- also created code that in the mid-to-long term cost the company far more money than they apparently saved by "shipping fast" and writing untested, unclear code.
The most frustrating thing to me about this is that, once you integrate these practices -- TDD, patterns, SOLID -- into your workflow, once you master them, they cost nothing in terms of time spent. You get practical about it, use it judiciously where it makes sense, don't use it where it's unnecessary and (like a lot of the Design Pattern stuffs) ignore it altogether. But you can only do this if you can see both sides, and you can only see both sides if you've mastered the practices. I spend much less time writing tested code than I would without it. I spend much less time managing well-designed codebases than I would thrashing around in a tangled mess.
It's like learning Vim enough to get proficient. You have to commit to it. Then you get pragmatic about it, learn its strengths and weaknesses and over time get to know the real benefits. But you have to have that willingness to commit to it up front and anticipate the benefits down the line.
And as an aside, in my 15 years, the great coders I've worked with (a few name-drop-worthy dudes in the mix) have, without exception, been committed to a pragmatic, healthy approach to TDD that was neither religious nor slow, and in fact made the entire team faster, the codebase more robust and the software far more shippable.
And while I have run into a lot of guys who have never written a single test - much less mastered TDD to the point of being able to speak intelligently about the pros and cons from a standpoint of actual experience - declare loudly and confidently that TDD is "religious" ("snake oil" is another favorite) and "feel free to ignore", I have never run into an experienced coder who has integrated TDD into their workflow in a credible way, who actually can speak to both sides of the argument say the same thing.
I think his whole crusade grew out of being frustrated by an insanely crufty and rigid waterfall process that he saw on enterprise projects he worked on in the 80s and 90s. I don't actually know what he worked on, but I imagine it was the kind of projects where they would throw a couple dozen C++ programmers at various subsystems conceived by an architect, code like crazy for 6 months, and then spend another 6 months attempting to flush out bugs with human QA.
Post-XP/Agile I think it's easy to forget that automated testing was not a standardized practice 20 years ago. Everyone had their own methods of doing it if they did it all. To me, TDD was a phase I went through for developing good testing discipline. The power of TDD in my mind is a training tool to develop strong testing skills. If you are not good at testing, there is always a high burden to write tests, so you do it less because it doesn't seem worth it. However if you are very good at writing tests, then you find that a lot of the time you can crank out a test/spec suite in about the same amount of time as manually testing. The benefit of course is that then you have documented and programmatically verifiable proof of your intention being fulfilled. Practicing TDD is a way to force yourself to learn how to test things in all cases, after you've mastered that you can step back and consider what the truly valuable tests are without having your judgement clouded by overweighting the relative difficulty of producing certain tests.
Now that I'm good at testing I almost never do true TDD, but I never produce any long-lived application code without some kind of test coverage.
Good TDD forces you into building loosely-coupled code that's easy to refactor and change. When new business requirements come in, the effort for implementing them isn't increased because of your previous code getting in the way. There's an overhead to writing the tests, but it's a fixed continuous cost. As a result, you're always in a position where you can ship a new feature in a predictable (and reasonably fast) fashion.
Not doing TDD often leads to tightly-coupled brittle software, which can be very fast to implement but also difficult to change down the road. It certainly doesn't have to, but in reality that's what happens 90% of the time.
There's a few successful startups that immediately spring to mind that use TDD (Taskrabbit and Modcloth spring immediately to mind). However, the real question you should be asking is, "Which startups died because they got buried under the weight of their own codebase?" That's a long and very depressing list. In many of those situations, some TDD might have helped.
In short: TDD won't make your startup healthy, but it helps ward off some of the more common fatal diseases.
TDD can test a monolithic `public static void main(String[] args)` just as well as the most dainty collection of python scripts. In fact, assuming you wrote the two to behave the same, your tests wouldn't know the difference. Isn't one of the larger problems of TDD that it simply tests quantifiable results, not profile the machinations of the code?
Writing truly good code is hard. It takes time, practice and a wealth of knowledge... No "process" (as the GP states) will shortcut these requirements for you.
Could you give some examples? PG doesn't include that in his list: http://www.paulgraham.com/startupmistakes.html, and in my personal experience I've never met a startup that failed because they had an unmanageable codebase.
This is flatly wrong. It may be a tool for productive programmers to keep their code on the right track, but assuming that loosely-coupled code isn't written without it is a huge overreach. Certainly, tests do help, but TDD is just a tool in the toolbox. I have seen many very talented programmers and many of them do not need TDD to produce stable, well-architected projects.
https://github.com/unclebob -- critique his code. I find it to be pretty dang clean.
He means something very specific when he talks about TDD, and while he's certainly emphatic about it, I dont think he's wrong. He might not be right either, but he also isn't lacking in "solid data or evidence to back up this assertion".
http://vimeo.com/43536488 is a talk he gave at NDC2012 that you might want to watch and ponder before dismissing the dude. He might be peddling, but he practices what he preaches while associating with people who care deeply (and publicly) about maintainable software.
I had an exchange on Twitter with him today in which I asked what startups he was involved in, and he said that he consulted for several, and then said 8th Light (the consulting firm he works at) is one. To me, that's not relevant experience.
If he wants to make the argument that TDD makes for better software, then fine. I just strongly disagree that it's worth the cost in an early startup environment.
That said, I think the article has a solid thesis. Just because you don't practice TDD doesn't that mean building things correctly won't help you ship working software faster. If your code is brittle, write some tests around it, refactor a bit, and keep running fast. We can debate over whether you should always write tests first or only write tests to cover brittle and important features, but that's a "how much" argument.
The real problem is when you run into methodologies that always involve work-arounds, and you let the technical debt continually pile up for weeks or months because "you're a startup." This will cause you to run very slowly, and sooner rather than later. I feel that's Uncle Bob's real point, and it's a valid one.
I read the whole thing but there was no mention of hard numbers. Facts were also lacking.
And yet you don't seem to know who the author is...
Since when does one have to work on a successful startup or have a good exit to have opinions on the best way to start projects? I work at a BigCo and have started a couple of projects.
I would love to see an empirical study proving the claims made about TDD in this article and numerous others.
It seems to me TDD is a huge waste of time when prototyping a minimum viable product. You want me to spend 2 hours writing tests for a feature that someone's going to tell me to rip out 15 minutes later? No thanks.
It's really easy to tell others to use TDD, and even admonish them for not using it. But unless you are the one in their shoes, you will not know the whole of their reality.
In a perfect world I'd write tests for everything. In the real world, I write tests for almost nothing. Most of the work I do is on stuff that might be gone tomorrow.
Likewise, when I'm figuring out how something might need to work, like learning a new API or trying to figure out how to build one, I don't do TDD. But I also don't do this as part of my main project -- it goes into a special '~/playground' where I can experiment with ideas.
Other than that, I do everything TDD. Here's why.
It doesn't take two hours to write tests, because by and large, you're writing the same code you'd throw into the REPL, or performing the same actions (in code) that you would need to repeat over-and-over again in a browser to see if the feature works. It's the same amount of work, you just need to write it down so that it can be repeated.
I've found that this saves me tons of time, because I can run the same test over-and-over while I make stuff work.
This assumes you are familiar with your testing tools -- if not, then yes, getting going requires learning the tools, which is the same overhead as learning a new library, algorithm, or language.
If you're working on stuff that might be gone tomorrow, you're doing your customer discovery wrong. Sure, you're going to implement features that it turns out were needless from time to time, but if you're regularly implementing work that gets thrown away fifteen minutes later, you're wasting a huge amount of time.
There are better ways to solve the question of "what does the customer want" other than building it and showing it to them -- check out all the work that Janice Fraser has done over at Luxr.
This point is so important that I think it should have been the only one you made. Tests look good on paper. And they are great intuitively. We can make great arguments for them. But last time I asked for a clear study indicating that TDD led to better results than a non-TDD development, everyone seemed to come up blank.
What it sounds like to me is religion. Doesn't mean I won't test. And it certainly doesn't mean I'll eschew testing on a team that does testing. But it still smells suspiciously like religion, and that's very worrisome to me.
So rather than having to make a change, run up the app in my browser, and manually test that the latest bit of code is working, I can test individual functions just by running my unit tests.
Of course this doesn't obviate the need for browser based testing too - but it can reduce the amount of it you need.
TDD allows me to produce working code faster by helping me notice and fix my errors sooner. If you're an excellent coder, maybe you don't need this- but I certainly do!
This is classic case of why you need tests and integration policy. When your APIs change or break, the tests break and you are quickly made aware. You just saved yourself a huge embarrassment and likely have the competitive advantage to those we don't look forward like you should do. Need to make a release quick and need to update your handlers? Go for it. If you're on a deadline and secure with the release, go ahead and push it with a couple tests breaking with false-negatives then swing back for an hour and fix them.
You'll thank yourself when they release v2 of that API and sunset your methods.
If you are going to rip out the feature 15 minutes later, why even waste the two hours writing it? Also, as someone who typically practices TDD, writing the feature + tests doesn't tend to take any more time than just writing the feature (and I have the cycle time metrics from my current project to prove it).
One of the first concepts you learn in introductory finance is that, in a mature market (ie the stock market -- basically something with a bunch of people and a bunch of information) you have to be compensated for risk. This is why a riskier stock -- like tech stocks or penny stocks -- can boast incredible amounts of risk but generally will give you higher returns over the aggregate than something like pork commodities or a CD. When you choose to purchase a tech stock -- or any stock at all -- you're saying "okay, I recognize that this is riskier, but I think I'm being fairly compensated for the risk, too."
Choosing to eschew TDD is like purchasing stocks. By definition, going through TDD is going to be a safe route, but it's rare that TDD (at least in my and my friends'/peers' experiences) is actually going to make you get from Point A to Point B any faster. TDD isn't, by default, a superior or inferior approach to anything: it's a tradeoff -- do you want risk or do you want return?
Sometimes, you want to minimize risk, and that's probably smart. Sometimes, you just want to produce an MVP -- and that's okay, too.
Why is TDD safe? Most TDD advocates seem to be blind to the fact that testing is a terrible way to prove many important properties about software systems, security properties for example. If you're betting your ever-so-scarce programming resources on TDD, you're probably paying too much, getting a lower return than you could be getting, and leaving some serious holes in your software. As I wrote in [1]:
If all you know about getting your code right is TDD, you’ll never bet on types or proofs or constructive correctness because you don’t know how to place those bets. But those bets are often dirt cheap and pay in spades. If you’re not betting on them at least some of the time, whatever you are betting on probably costs more and pays less. You could be doing better.
So I don't think that TDD is a "safe" bet. I think it's an expensive bet that has relatively poor payoffs.
[1] http://blog.moertel.com/posts/2012-04-15-test-like-youre-bet...
What is the risk of _not_ doing TDD? The risk is that slowdown. We've all experienced it. What is the cost of TDD? You'd like to say that it takes time; but since the risk is a slowdown, the net is positive no matter how you look at it.
That's the irony of all these complaints. They assume, and sometimes they simply state, that TDD slows you down. And yet, the primary effect of TDD is to speed you up, and speed you up a lot.
Some folks suggest that it's a short-term slowdown for a long-term speedup. But in my experience the short-term is measured in minutes. Yes, it might take you a few extra minutes to write that test first; but by the end of the day you've refactored and cleaned the code so much that you've gone much faster _that day_ than you would have without TDD.
It is not.
It is about enabling easy refactoring. The other stuff - preventing regressions, catching bugs, etc. is gravy.
TDD (and BDD) is an assurance that the promises that your code made yesterday will be kept tomorrow. If you break those promises, you do so consciously.
Not doing TDD is like investing in the pink sheets -- the company you invest in might be a fly-by-night operation, or it might be a legitimate business. It could be both, but you never know, and assuming you're not part of a pump-and-dump scheme, the rewards aren't really higher than listed stocks.
But the risk is a lot higher than listed stocks.
Lots and lots of risk, but very little upside, which is the same as writing code without tests. You save a little time getting started, but spend a lot more time doing the repetitive work that testing automates, and in doing so, eat a lot of long-term technical risk that no company should consider acceptable.
TDD has its place, as most all main-stream methodologies do. But, lets just admit that the people that use this methodology are in the minority. The rest of us are working on smallish projects that are struggling to be worthy of the time-budget that they've been granted, and we're more worried about shipping than caring about how not having TDD in place will slow us down in phase III. Assuming phase III ever happens. We're using all the best-practices we can, but TDD doesn't rise above the bar most of the time.
Uncle Bob isn't saying "If you aren't using TDD at the startup phase, you suck!" What he's saying is "Just because you're in the startup phase and think you're invincible doesn't mean you should throw best practices out the window." There are good excuses for not using TDD, and I tend to agree that "We're in a startup phase. We don't have time for this crap." isn't one of them.
As for the time argument, I hear it, but I don't buy it. This is an institutional problem, and should be dealt with accordingly. Your responsibility is to advocate what can be done within the allotted timeframe. There's a time and a place for slapping things together and making it work, but it should never be 100% of the time.
If it is, it's a sick institution. And there are a lot of those.
I previously worked in environments which were very stringent on things. Ex: I formerly worked on simulation software for fighter jets. So, there are definitely places where it makes sense, but it still think its minority. TDD is definitely better for VERY LARGE and SENSITIVE products and have long, ongoing development cycles involving large teams.
I strongly agree with this point, and don't buy the blanket rationale that speed trumps everything in a startup. Yes, speed is quite important when you are bringing something new to market; it lets you get your foot in the door and rapidly find the right fit for your product. But if you cut corners then your product will suffer, and you'll end up treating your customers poorly by it.
There's a fine balance to all these things, of course. We all have to find the right balance between keeping engineering standards high and heavy-handed structures.
I was hoping for tips about how to find a balance when other team members have bought the blanket rationale that speed trumps everything and then complain about why we spend so much time fixing bugs and fighting fires instead of working on the next thing.
Instead a bunch of reading comprehension challenged commenters are bitching about TDD again.
Test "Driven" Development is a real invitation to write too many tests. A small, good set of tests gives you freedom to work fast, to refactor and to have multiple developers pushing simultaneously. Not having them really isn't sustainable past a certain point but equally to many tests will drag you to the ground.
Tests are overhead, they cost time to write, maintain and run and TDD tends to drag you into the deep end. Avoiding them altogether ends up being a false economy (if only in that it makes you too nervous to push often).
To those who fear the slippery slope, a nice self-annealing approach to get into testing is to only write tests for something that has failed. That way you waste no time writing tests for things that are actually pretty robust but equally you avoid addressing (and fearing) the same issue twice.
It is great for old-timer enterprises without any test, and balances the time pressures of startups. However, you need to write those test not only for prod failures, but also if that something failed in your dev environment.
First, yes, TDD slows you down. The reason this matters is because a lot of our time as developers is spent exploring ideas, and it's pretty well understood that the faster you can get feedback on your ideas the easier it is to creatively develop them. In fact, the final result of your creative process can be completely different depending on the feedback cycle you have. From the micro to the macro perspective, religious TDD introduces an constant factor slowdown for small projects that yes, ends up being a net win in the long run if you live with the code for long, but is a net loss in the short run and also can prevent you from finding a solution to a problem since your creativity is stifled by the slow speed of idea development.
Second, when building web applications, building tests turns out to be fairly overrated. First, people will tolerate most bugs. (Write good tests around the parts they won't.) Second, if you have a lot of traffic, bugs will be surfaced nearly instantly in logs and very quickly via support tickets/forum posts. (What if there are bugs that don't make it into the logs and don't affect people? If a tree falls in a forest...) Much more important than mean time between failures is mean time to recovery. I'd rather have ten bugs that I fix within 5 minutes of them affecting someone than one bug that festers for a month. Not only because this is healthier for the code base in terms of building robustness, but also because human behavior is such that many fast bug fixes make everyone feel good but few lingering bugs make everyone miserable. People want to feel like you care, and are much more likely to feel cared for when you fix their problems quickly, and are not often interested in just how few bugs you ship if the one that you do has been affecting them for a month.
This isn't theoretical nonsense, it's a very real phenomenon where you use tests and manual testing to get up to a basic working prototype and then just throw it over the fence to flush out the bugs. It's the only way to do it anyway. (This only really works if you have traffic and can deploy changes quickly. To paraphrase a colleague, production traffic is the blood of continuous deployment.) Obsession over deploying bug-free software (an oxymoron anyway) is usually coming from people who haven't gotten over the fact that we don't need to ship software once a month or once a year but can do it every 10 minutes in certain domains. Instead of focusing on not shipping bugs, focus on shipping faster.
Not for reasons of finding bugs – though that is often a nice side effect – but because once I have decent test coverage I don't need to look at the application anymore. Being able to run the tests in the background to verify my work is sane, while I move on to the next feature in parallel, I find, is considerably faster than the code/build/review cycle you find yourself in without a decent test suite.
My own performance, being a limited commodity, is the most important factor and I find tests help me increase output, not slow it down as you suggest. They are certainly not a panacea though. As always, use the right tool for the job.
But I think your comment misses out on the middle ground in common dynamic languages. What a good test suite does is reflect on code to document the intention of the author and provide an automated means of verifying that that intention was in fact fulfilled. Granted, test suites have bugs too, so a passing test does not guarantee there wasn't a bug, but the fact that you have two (hopefully) orthogonal descriptions of the code in question means that you stand a much better chance of actually teasing apart what happened when something blows up down the line. I've been saved and emboldened by my test suite enough in Ruby that I now consider it irresponsible not to have complete-ish coverage. I just see it as good code hygiene.
I think it is necessary hygiene for Ruby. In dynamic language web apps, everything is so loosely coupled together by a mixture of conventions and magic strings (or symbols, if that makes you feel any better about them) that it's very easy for one of the strings to break without noticing.
But IMO you are mostly (perhaps 80%) working around an inadequacy in the tools. It is not a virtue in and of itself.
I'm in the odd position of having transitioned to working at a startup, writing Backbone / RoR, after having been a compiler engineer, and before that, in ~2006, the author of a server-side web app framework designed to be strongly typed throughout (using a custom language for control binding to achieve this goal). RoR is very error-prone and a lot of work by comparison, especially when interfaced with Backbone so that the UI doesn't need constant whole page round trips.
So many bits and pieces need to be glued together, from attr_accessible through to update params slicing, binding controls in JS, JBuilder json templates, the whole game of finding just the right elements with jQ and friends, so much busywork with so many opportunities for mistakes to creep in - so that tests are absolutely critical.
Writing tests doesn't take much time since it's just a restatement of the code you already know you want to write. If you don't know what code you want to write, you can't write it. So you think about it. Once you know what you want to write, the test is trivial. Yet that trivial test let's you see the code execute, and gives you the confidence that the code you write was the code you _intended_ to write.
I agree with you about shipping faster. I don't agree with you about bugs. TDD can eliminate the more stupid of those bugs, while helping you go faster.
Instead, many artists -- or software authors, or chefs, find a way to repeatedly sample and appraise their work as it develops over time.
I think this is far more important than TDD, because the truly important problems of software engineering are not in making software that can achieve simple correctness, but in making something that people want.
Red -> Green is the sampling and Green -> Refactor is the appraising.
Why do so many TDD people think that people who don't use TDD don't write tests?
On the opposite side of the spectrum, I worked on software that processed millions of dollars in daily transactions and there were maybe 5 "tests" in the whole system. This was about 500,000 lines of C and C++ code in the early 2000's.
My personal philosophy is to write unit tests where I think it's important, not test everything.
That's some serious strawman. In between 0 tests and TDD, there's a lot of room to maneuver.
There’s a wide spectrum of successful coding practices that get products shipped. Bob’s article only compares the outliers.
So yes, its foolish to skip testing. And yes, the race goes to the foolish.
What slows a programmer down? Debugging. Messy code. Fear of change. How do you minimize those risks? A comprehensive suite of tests. How do you get that suite? TDD.
This is the point at which I stopped reading.
Yes, some bugs are acceptable. The point of writing software is not to create bug-free code; it's to create value. The marginal returns on eliminating the last bug are much lower than implementing new functionality.
As for the gist of the post, from my experience, TDD is OK; but it is unit tests that are essential. They are far more important in dynamic languages, because you otherwise have very little feedback when you make mistakes as simple as a typo. They're also important in static languages, but fairly large programs can successfully be written with without anything near the volume of testing needed with a dynamic language.
In a Rails project, the Rails boot-up time makes TDD a painful experience if you are not using tools like Zeus or Spork. Even in the presence of such tools, you need a powerful machine to not hate the slowness of the whole thing and worse still, break your flow.
My recommendation for someone who hates TDD for the wrong reason, aka: breaking the flow, would be to get a fast machine, fix the code to make TDD as painless as possible and to use the right tools.
But once you start using TDD as a tool to organize your thoughts and model your domain, you might end up becoming too dependant on it and find it hard to work any other way. This is anecdotal experience.
Also, learning how to wield TDD properly takes a lot of time, error and practice. Good things don't come easy. There are obviously places where TDD isn't a good fit - a spike, requirement that is known in advance to change soon, and exploratory programming are all candidates. However, good practice dictates that you refactor your code once a spike calcifies into production code. At this point, TDD becomes just unit-testing.
Most of the arguments against TDD in this thread seems to be against unit testing in general. But we know unit tests are important. Doing it before the fact increases the value of the unit tests manifold and also ensures that you do have coverage (though that is not at a primary objective).
"The road to programming hell is paved with “best practices” applied too early."
There's a reason people use the metaphor "Design Debt". Debt is a tool with tradeoffs, financial or design, use appropriately.
What if you can't measure the debt?
- Agile - Agile IMHO largely screws you in making sound technical decisions. Its not necessarily because agile is flawed - its usually because business/management uses agile as an excuse to randomly take a hard left turn every other week making it much harder to make long term architectural choices that are beneficial.
- No agreed upon standards or unification amongst software professionals - The accountant in this situation has a set of standards and expectations that allow/force him or her to do things in a moderately set way. This allows for the accountant to usually fend off management from pressuring things to be done in a shoddy or just get it done way. On the other hand its much more difficult for software professionals to say "we as a group do not condone writing shitty software" (because ironically a large number of us do ... "move fast, break things" has done more good than bad from my perspective)
- Ageism - Also known as experience doesn't really matter. Some will say thats because technology changes so much - but it actually doesn't. Just because you've been grappling with software problems and design patterns in Java doesn't mean that when you switch over to Python that really anything changes. Same shit, same problems, different words - but in all honesty we seem to be pretty bad at building on the experience of our elders because they are over the hill at 35... what could they possibly teach us.
For me it at times has been frustrating because coming out of school I really enjoyed the fun of designing systems and code that are sustainable, performant, etc etc but there seemingly is typically more reward for just throwing quality out the window in startups. Just my personal experience.
The moment you can't hold the whole thing in your head with ease is the moment you should have done TDD a while ago.
In the very earliest days of a startup, you can easily hold the entirety of the problem space in your head. Refactoring is simple, tests or not. I can destroy and rewrite a feature from scratch in hours or days. A non-trivial number of the features I'm writing will not exist in any form a week or two later. This is not a problem with process or planning; this is the nature of a startup.
I think that TDD is immensely valuable as a team and product grows, but claiming that your startup will fail because you aren't applying engineering "best practices" from day 1 is counter to most successful startups I've worked with.
Startups _are_ different:
1. What you are doing starts at 0 value. 2. You can (usually) break it and it's ok. 3. You need to change stuff a LOT to figure out something that someone wants to use, rewriting tests makes that slower.
Every test case you write has a cost: It verifies that something of unknown value works. What if the value of that code is 0? Well, you just doubled down on something useless.
There are no absolutes in software development, but how we did it was have uncomfortably too few test cases when something is new, change the product for a while until someone actually wants to use it, then add a bunch of tests until we know it works from there on out.
And I hesitate to add: Also we work in Java so a bunch of testing comes for free, so there's that.
For example, if I'm writing a module that does validation for a specific type (for example, in JS) I tend to test the functionality while writing the tests. I am unable to speak amongst others, but you have to test your code eventually, and it's either going to be in the classes that are using the code, the REPL, or it's going to be in your unit test/integration framework. I usually pick the test framework first because it serves 2 benefits:
1) I can prove my code works. (I must prove this to my self, or the people using the code at some point.)
2) I can reliably change the code later.
Complexity is also mentioned in a couple of comments here. TDD helps simplify complexity by writing code that is testable. It also makes you think about your API as you're writing the different possibilities the module/code can be used in.
Further, I think there are multiple issues with tests, and depending on the type of test your doing, different problems can arrise. You have your unit tests, and then you have your integration tests.
Unit tests are relatively easy to write (if your code is split into individual chunks, that do a specific task.) and they should always be written regardless of the time you have.
I think the major issues arrise in terms of time are the integration tests. When you're testing the functionality between complicated modules that require databases, i/o writes, network communication, it is sometimes hard to write the tests, and it may not even be worth it.
The majority of people who disregard TDD, usually disregard a specific sub-problem of TDD. I think TDD has its benefits depending on what type of tests you're doing, and how easily the problems can be solved with those types of tests. You can always pick to write unit tests, and disregard integration tests, etc.
I think there are a lot more issues to expand upon, like the language you're using, the platform you're using and how often your code is required to change, but TDD has large benefits, and code rot is very real and TDD can help mitigate it.
To those of you who consider TDD a religion; you are being silly. TDD is a discipline, like double-entry bookkeeping, or sterile procedure. Like those disciplines it has aspects that are dogmatic; but that aim at a purpose. That purpose is to help you write software fast and well.
To those of you who think I'm a process peddler. You're right; I am. I make a very good living teaching people how to do TDD. I have been doing so for over ten years now. I hope to continue for some years to come. My goal is to help raise our industry to a higher level of professional behavior; because at the moment our behavior is pretty poor.
To those of you who wonder whether I've ever worked at a real start-up. I've worked at several over the years. My first was in 1976; and it was very successful. Another was in 1989, and it didn't do so well. I've recently founded cleancoders.com a startup in video training for software developers. The website is done entirely with TDD. And this doesn't count the rather large number of startups I have consulted for in the last 10 years. So I've got a _lot_ of experience with startups.
Folks, I am 60 years old. I got my first job as a programmer when I was 18. I wrote my first program when I was 12 (on a plastic 3-bit computer). I started using TDD in 1999, after I'd been programming for thirty years. I continue to write code using TDD today. I've seen the startup trap. I've lived the startup trap. I strongly advise you to avoid that trap.
Just preaching TDD is not helpful, we need a coding process that allows clean separation of early and production code.
You'd fire his ass! You'd fire it so fast that the rest of
his worthless carcass would be left outside the door
wondering where his ass went!
Brilliant.But for the minority who miraculously dodge the bullet, the extra investment will eventually become a lifesaver.
Well, at least someone noticed!
Sometimes, having deadlines is unavoidable. They might be set externally and exist for regulatory reasons. Deadline Culture is when the company starts encouraging the formation of unreasonable deadlines that triumph over professional integrity and intellectual honesty.
VC-istan seems to encourage it with the whole "if we get bored with you, we'll fund your competitors" arrangement.
Deadline Culture is, however, great for the true fascist. Fascists love (real or imaginary) existential threats, especially vague ones they can continually adapt to their needs, but that come with time pressure.