Udostępnij za pośrednictwem


#NoTDD

Did that title get your attention? Good.

Like the other #no<x> assertions – NoEstimates, NoBugs – I'm really not saying that you shouldn't do TDD. Well, maybe I am…

I was an early TDD advocate, as I really liked the way it helped me organize my thoughts, and I respected some of the other people who were early advocates – people like Ron Jeffries.

But looking back on the 15 years since I started with TDD, I have to say that it really did not live up to my expectations.

What I've seen is a consistent pattern of TDD working in a laboratory setting – developers are quick to pick up the workflow and can create working code and tests during classes/exercises/katas – and then failing in the real world.

My hypothesis for this is very simple. When you look at the TDD evangelists, all of them share something: they are all very good – probably even great – at design and refactoring. They see issues in existing code and they know how to transform the code so it doesn't have those issues, and specifically, they know how to separate concerns and reduce coupling.

If you have those skills, TDD works great; you see what the issues are as the arise and fix them incrementally, and your simple tests prove that your design has low coupling. And, you have the tests to lean on in the future.

A hypothetical

Let's take the TDD workflow and remove the third step – the refactoring step. What would we expect to happen?

Well, we would expect to end up with classes that have multiple concerns in them – because we didn't split them apart – and they would be inconvenient to test. We would need to write a lot of test code, and would need a lot of help to write the code, either creating many hand-written mocks or using mock libraries.

Which I submit is precisely the result that most developers get using TDD; the resulting code looks exactly like what we would expect if developers are skipping the third step. Which isn't really very surprising, given that the tests in most non-TDD code look the same way, and we know that most developers do not have great design/refactoring skills.

At this point we should ask ourselves, "are these developers getting a net positive benefit from using TDD?" Let's list the pros/cons:

Pros

We end up with tests that verify the behavior of the code and help prevent regressions

 

Cons

 

It takes us longer to write code using TDD

The tests get in the way. Because my design does not have low coupling, I end up with tests that also do not have low coupling. This means that if I change the behavior of how class <x> works, I often have to fix tests for other classes.

Because I don't have low coupling, I need to use mocks or other tests doubles often. Tests are good to the extent that the tests use the code in precisely the same way the real system uses the code. As soon as I introduce mocks, I now have a test that only works as long as that mock faithfully matches the behavior of the real system. If I have lots of mocks – and since I don't have low coupling, I need lots of mocks – then I'm going to have cases where the behavior does not match. This will either show up as a broken test, or a missed regression.

Design on the fly is a learned skill. If you don't have the refactoring skills to drive it, it is possible that the design you reach through TDD is going to be worse than if you spent 15 minutes doing up-front design.

 

I think that's a good summary of why many people have said, "TDD doesn't work"; if you don't have a design with low-coupling, you will end up with a bunch of tests that are expensive to maintain and may not guard against regression very well.

And to be fair, the level of design and refactoring skills required is relative to the state of the codebase you're working in. There are a lot of legacy codebases with truly monumental amounts of coupling in them and figuring out how to do anything safely is very difficult and complex.

TDD—, Refactoring++

Instead of spending time teaching people TDD, we should instead be spending time teaching them more about design and especially more about refactoring, because that is the important core skill. The ability to write and refactor code to a state with low coupling, well-separated concerns, and great names will be applicable in all the codebases that we work in.

That is not to say that I don't dust off Ncrunch now and then to either create a new set of functionality with great tests or to create tests around a concern as I do a refactoring, because I still do that often. I just don't think it's the right place to start from a educational or evangelical perspective.

Comments

  • Anonymous
    June 23, 2017
    Nice point. In my view I would prefer having TDD in place as a learning tool. Blindly using it without understanding the architectural benefits it provides would be unwise. But it puts some nice checks in place when we are learning while working on commercial/production code bases.
    • Anonymous
      June 23, 2017
      @Michael: TDD - or required unit tests - in many production codebases is an exercise either in frustration or a mockathon. Neither is really good for the long-term health of the codebase.
      • Anonymous
        June 29, 2017
        I respectfully disagree with the implication that unit testing your code is not worth the frustration. Maybe with a caveat that 100% unit test coverage is often overly restrictive. If you're frustrated with unit tests, you've probably done something in a way that could be improved. As far as mockathons... My approach is to mock just enough to get to atomic, isolated tests, and write multiple tests so that each one isolates every individual bit of logic. Mocks aren't bad. Lots of mocks aren't even bad, as long as you're not mocking a positive assertion, and you've tested every bit of logic in isolation. :)
        • Anonymous
          June 29, 2017
          I had a discussion with the Arlo Belshee about testing production code, and we agreed that that there is code that is untestable and there is code that is aggressively untestable. I am not the best designer around, but I have worked with developers who spent 2 hours trying to break dependencies in a meaningful way (ie not "sprout method") to make things testable, and the situation would have taken me a morning to work on and I only gave myself a 70/30 chance of coming up with a good way to make it testable. At that point, I don't think the effort is worth it; my experience is that the code is worse after the changes to make it testable when the changes are made by most developers.
        • Anonymous
          July 27, 2017
          "Maybe with a caveat that 100% unit test coverage is often overly restrictive."Many of the best who push for Unit testing or even TDD consider 100% coverage a code smell. Every acronym for a development model is an ideal that is not practical unless used in moderation. TDD is a great learning tool and forces you to think about things you never had to before, but it is not very practical. Use it, learn from it, move on.TDD is just a tool. Not only should you use the correct tool for the job, but you may need to partially use many tools for different aspects.
  • Anonymous
    June 23, 2017
    These are exactly my thoughts!!! I know it is kind of blasphemy to say it. These days you get insulted if you say something like this. I've been in some projects that did not work well with TDD. The answer from the TDD-disciples were "you are not doing it right". Ok, with this kind of argumentation you are always right :-) . One of our TDD-followers wanted to show the others "the right way". He created a small program using TDD and about 300 different tests. The program went to our quality assurance and they found 90 bugs in just some days. They were the typical UI bugs (pressing a "Run" button twice starting a process that should not work in parallell, users doing "unexpected" things, ...). Fixing the first two bugs took three days because many tests had to be rewritten. As you can imagine we had to stop this nightmare.Please, don't answer with "he did it wrong". He was one of the TDD "fans" that would show the others how to do it right. As Eric says it is more important to learn how to design and some people think that TDD will solve their design problems ("if I do TDD I am designing it right").I do understand the benefit of doing unit testing, but I think that sometimes people are too religious about axioms. "You must do SOLID", "you must do TDD", "Clean Code", ... There are enough valid points in all of them, but many developers these days make a war out of it (like many extreme religious people).
  • Anonymous
    June 23, 2017
    I agree that TDD without having a good design foundation can be like spinning your wheels in mud. However I think the answer is when you teach or mentor a jr engineer in the value of TDD you should also be mentoring in the design as well. It's not a "instead of" but "along with". In my experience the value of TDD and unit tests in general (if done correctly) is that it validates the design, pointing out the flaws and giving you the opportunity to correct the design before it's too late.
    • Anonymous
      June 23, 2017
      @Jay,I've had decent luck with that approach when I was the lead and I was able to instruct/mandate on an ongoing basis and I could keep the schedule pressure off. I've had very limited luck otherwise. Only the pair/TDD curious are willing to work with me that way, so my leverage is limited, and when I see the results when those people try to do TDD themselves, I'm not very excited. Assuming they can figure out how to write tests at all in our codebase. I've had better luck pairing with people on refactoring and working through different design approaches.
  • Anonymous
    June 23, 2017
    Totally agree. Refactoring is the crucial skill. You think refactoring Katas are good tools? What else can one do to improve themselves or their team? Code reviews/Pairing?
    • Anonymous
      June 23, 2017
      Pairing as much as possible is my first choice. I like the idea of katas - and paired katas are better - but I think you are limited by the total knowledge of the pair; if they aren't very skilled their ability to find insight will be somewhat limited. I facilitated a code retreat last fall, and that was definitely a pattern.I've been piloting a sort of guided instruction with a small group of engaged people at work, where we evaluate and discuss a specific part of code and a specific issue. I've seen some good results but the approach still needs a lot of tuning.
  • Anonymous
    June 23, 2017
    But, refactoring takes longer without tests. Tests should have influenced the design.No one ever said TDD precluded up front design or that it was even "Design on the fly."You get even higher coupling by not using TDD. TDD has driven me to think more about how I can loosely couple my code. That was one of the intentions of TDD to begin with.If it takes you longer to write tests and they get in the way, your design is wrong.No, the problem isn't TDD. The problem is that all we seem to be able to teach in the college system these days is syntax for a particular language rather than design patterns. That's probably because most of the guys teaching don't know any better and syntax is easier to teach and grade. This is where I think you and I agree. If we had people who knew what a good designed looked like, we could move on to tools that helped us think about design up front.
    • Anonymous
      June 23, 2017
      The comment has been removed
      • Anonymous
        June 24, 2017
        The comment has been removed
      • Anonymous
        June 26, 2017
        No, Dave's right about higher coupling without TDD. I'm arguably still an amateur TDDer, and have been for about eight years (i.e. I do TDD as often as I can, but have never worked in an environment that embraces it). When I talk to my team about TDD and just writing unit tests in general, the point I always try to hammer home hardest is that you can write your code in such a way that it's difficult to test. By writing the test first, TDD guides you toward a design that is easier to test, and that design will almost always have lower coupling than a codebase which has no unit tests at all. That's a great point about missing the third step in the red-green-refactor cycle. There have only been a handful of times I've done TDD where I've embraced that third step and refactored frequently, and it has always made an improvement in the design. Resharper makes that third step easier by reducing the refactoring friction. I suspect the one thing that would make it easiest is combining TDD and pair programming, so you have a second set of eyes looking at the big picture and seeing where the design needs to pivot. Great article!
  • Anonymous
    June 28, 2017
    Excellent post. I attended TDD course 18 months ago tried to do it and gave it up. However I then attended clean code course with uncle bob about 4 months ago and what I learned on that course really helped. Before TDD you do need to have a good grasp of clean code, decoupling etc.
  • Anonymous
    June 29, 2017
    I am not sure of what design means,it is used a bit abstractly imho.
  • Anonymous
    June 29, 2017
    I think your argument rests on a false dichotomy. The code that is hard to test is the perfect feedback loop for the developer that signifies a need for better design and refactoring. The value of this feedback is immeasurable, and to me it's one of the fundamental forces for people to learn refactoring and design. If we view TDD as Test Driven Design, then TDD is a gateway for newcomers to learn refactoring and design, and I fail to see why you claim we should choose to teach one and not the other.Even for a skilled developer TDD questions the domain model and helps fleshing out some details that may have been overlooked.
    • Anonymous
      June 29, 2017
      Sam,I both agree and disagree with you strongly.When I am doing TDD, I find the feedback very useful to guide my design. But when I've been working with other developers, they are generally not sensitive to the feedback, or if they are, they don't know how to go from "this test is hard to write" to "do to make the code better". Further, I see a number of cases where heavy use of mock libraries mutes the design feedback. My experience is that TDD is not an effective way of teaching design.
      • Anonymous
        June 30, 2017
        I tend to agree with Sam's comment in it's fullest. This article creates a set-in-stone connection between TDD with good design skills, while this connection may exist, it does not have to. That is like saying ORMs are bad because a lot of people do not know how to use them efficiently or even when to use them.
  • Anonymous
    June 29, 2017
    The comment has been removed
  • Anonymous
    June 29, 2017
    By the time I left college in 2000, I had also read Writing Solid Code and Code Complete. The rest I picked up along the way. What do people recommend today as good contemporary design and refactoring books?
  • Anonymous
    June 29, 2017
    The comment has been removed
  • Anonymous
    June 29, 2017
    Don't teach TDD because it requires design skills, teach design skills instead? Once I learned the design skills, wouldn't I be able to do TDD effectively?To be clear, my experience teaching TDD mirrors yours. For personal application, though, I'm stubborn about improving my designs and my design skills.
  • Anonymous
    June 29, 2017
    I think there's a strong balance to be struck between tests and static analysis. I see a lot of tests that a decent static analyzer should be doing for you automatically without the need to maintain the test.
  • Anonymous
    June 29, 2017
    The comment has been removed
  • Anonymous
    June 29, 2017
    I don't see this as an either-or proposition. As Kent Beck says in XP Explained, "Quality is not a control variable."TDD doesn't automatically give you a good design. "Listening" to your tests (or feeling their Design Pressure) is a skill that takes time to hone, and one that I continue hone daily.Rather than drive toward good design, TDD drives code away from bad design. If the tests are difficult to write, it's time to refactor. Removing that crucial step causes TDD not to be complete, much like removing the dough from a pizza recipe would no longer yield a pizza. When teaching folks, I always try to show "Look how this test becomes difficult to write/setup/etc., and notice how that exposes a design flaw here. Let's do a quick refactor."If practicing TDD made all code great, we'd all be doing it.Knowing when to turn the different dials of TDD is an art that takes time to learn, but I don't think we should advocate for eliminating TDD entirely. Very rarely do I look at a piece of code and wish there were less tests.I've written a few blog posts on these topics which the audience here may find interesting:- http://kellysutton.com/2017/04/18/design-pressure.html- http://kellysutton.com/2017/05/29/deletability.html
  • Anonymous
    June 29, 2017
    It's Red, Green, RefactorNot Red Green Red GreenWrite a new failing test, make that test pass, refactor to a better design. I was taught that TDD is a feedback mechanism to help you improve your design. The fact that you're experiencing pain while maintaining your tests is a symptom of poor design. TDD encourages decoupled code, and decoupled code tends to be easier to test. If changing one piece of code breaks 20 tests that should be unrelated, that tells you that there's too much coupling and you have to refactor.
  • Anonymous
    June 29, 2017
    The comment has been removed
    • Anonymous
      June 29, 2017
      Agree with most of what you write. Note that when I listed the pros and cons, those were related to what I've seen with most developers using TDD, not what I experience myself.
      • Anonymous
        June 30, 2017
        So this blog post is fiction, then? Having never flown with China Eastern, from my observations of their missing planes, I deem #NoChinaEastern... except it was Malaysian Air, but whatever, their logos look the same.
        • Anonymous
          July 05, 2017
          I'm not quite sure what you are trying to say with your comment. My experience is based on working in a number of different teams experimenting with TDD and teaching TDD to others. If you look at the other comments, you will find others whose experience aligns with mine.
  • Anonymous
    June 29, 2017
    I have got a lot of value from TDD, but its really easy to take it too far and end up with brittle tests that slow down development. I use it only on interfaces/abstract classes and with real objects; and staying away from mocks entirely. TDD has been a real valuable design step for me, and lets me sleep at night knowing the build is in good shape.I used to put user behaviour in unit tests, but have learned that stuff belongs in BDD tests. These tests actually use a persona and their interaction the the product. BDD first, then filling in the architectural gaps with TDD tests. Outside-in.Reducing complexity is the real goal in the end, so don't let your tests get in the way, and don't be afraid to delete dead tests (or dead code of any flavor). I'll never go back to not using BDD/TDD tests as it has been a real benefit to me.
  • Anonymous
    June 29, 2017
    It sounds like your saying, "If they don't do TDD well, it doesn't work". Refactoring is, in my opinion, an essential part of TDD. I'm not stuck on TDD but it's my favorite way to develop still. I was hoping you were going to introduce us to something new that was the next evolutionary step. Got any of that?
    • Anonymous
      June 29, 2017
      The simple alternative is to learn to write code that is naturally decoupled and can easily be tested. I've written some about this previously, but I don't have an great overall guide.
  • Anonymous
    June 29, 2017
    Well said, I have been waiting for an article like this for years.
  • Anonymous
    June 29, 2017
    Good point. What's your opinion on the claims that test should be written in the very beginning because:1. Force you think in certain aspects and help in design.Personally I don't feel this advantage. There are lots of things need to consider in design phrase, and writing tests need too much efforts and gain relative little. I'd rather write some specifications and claims, even it cannot be executed like tests. Especially when I need to iterate rapidly.2. Write a simple even dumb test, fail it, then make it pass to get some sense of achievement.I don't feel I did something worth my time for fail and pass a simple test.
    • Anonymous
      June 29, 2017
      For me, I like doing TDD, because the incremental approach works well with me and I like doing things that make me get better at refactoring. And it gives me code that is easily testable, and therefore loosely coupled.But what I've found is that I can now write code that is loosely coupled - or refactor my way to that code - without having the tests to guide me, so sometimes I do just that. I do find that through TDD I can focus on one thing and know that I won't miss things as I build up the class I want. As for the second things, when I am doing TDD I often write a first test that is something like Assert.IsNotNull(new MyClass());It seems pointless, but it verifies that my tool chain is up and working properly and reminds me that I'm doing TDD, and it takes me about 30 seconds.
  • Anonymous
    June 29, 2017
    The comment has been removed
    • Anonymous
      June 29, 2017
      Dan,I think you miss my point. I am not arguing that unit tests are bad or that TDD is bad. I am asserting that TDD as practiced by most developers in most codebases is not an effective technique.
  • Anonymous
    June 29, 2017
    I've always found that you can't apply things like TDD, OOP and things like it religiously. It works great on toy problems, "a vehicle has an engine, wheels and so on and a car is a type of vehicle", but things in real life seldom break down into tiny perfect pieces.What I do is design/think about my API or what not beforehand, which I should not do according to TDD, then I apply TDD. That has served me well for 10 years or so. Also, I don't go for maximum code coverage, some things does not need tests, but for some code I find it almost crucial to have them.As with all things, use what works for you and apply the needed amount of it.
  • Anonymous
    June 29, 2017
    The comment has been removed
    • Anonymous
      June 29, 2017
      This is definitely true.I think it is combined with the fact that small problems are simple enough that you can achieve success without really doing much refactoring.
    • Anonymous
      June 30, 2017
      The comment has been removed
  • Anonymous
    June 29, 2017
    To paraphrase this article: "Practicing TDD is not sufficient you have to refactor as part of the process and refactoring requires design skills".The previous is a good point, but I think that the title #NoTDD is misleading and communicates "TDD is wrong" which I think we both agree is not the case.
    • Anonymous
      June 29, 2017
      Well, I did want to commit heresy...I don't think TDD is wrong for everybody, I just think it is wrong for a lot of developers in a lot of situations.
  • Anonymous
    June 29, 2017
    Sadly, I have to greatly agree with this. In my experience people really are not good at design and refactoring. This TDD has ended up being exactly this in most places I have worked.
  • Anonymous
    June 29, 2017
    The comment has been removed
    • Anonymous
      June 29, 2017
      I'm not saying that people shouldn't try to get better; I wouldn't have written all that I have written on design, given presentations, led discussions, etc. if I believed that. I just no longer think trying to do TDD in production code is an effective way to get better for most developers.
  • Anonymous
    June 29, 2017
    The comment has been removed
    • Anonymous
      June 29, 2017
      Interesting question. My experience is that, for the majority of developers, writing code the way that they are most familiar with - which is typically what I would call "think about it a little, maybe talk to somebody, and then write it" - results in better code than if those same developers try to do the same task with TDD. I think this is because they are better at doing this little up-front design than they are at taking the code that comes out of TDD and evolving it into a decent design.So I don't think we should advocate that those developers use TDD, at least in most cases. We should instead try to improve their design and refactoring skills. If they want to do TDD, then sure. I explicitly did not say "junior developer" because I have known many senior developers with poor design skills.
      • Anonymous
        June 29, 2017
        I think it really depends on how you evaluate a senior developer - how would that be possible that a senior developer has very low design skills? Likely you're referring to some domain expert that are good at actual business logic or domain knowledge but poor software programming skills.I agree with your point that even senior developer may need some sort of adequate up-front design, which should be based on actual business scenario analysis and responsibilities split - good design skill (and refactoring skills) should be essential for nowadays software engineers in my opinion.
        • Anonymous
          June 30, 2017
          The comment has been removed
      • Anonymous
        June 30, 2017
        I think this is an excellent point. For me, TDD helped me improve my refactoring skills. It also helped me think about how to create simple solutions for simple problems and avoid over-engineering. This stuff isn't easy. Trying to learn TDD by applying it everywhere in production code is going to be like learning to fly airplane by starting out using 747. It's just going to result in frustration and probably cause more problems than it solves. However, I think it is valuable. It is good to think about how to apply it, and try to apply it at an appropriate level based on your skills. I believe thinking about how to you can make your code easier to test helps push you toward better designs, but don't try to go too far beyond your depth.
  • Anonymous
    June 29, 2017
    Finally someone said this. I was having similar thoughts, but was unable to express it as clearly as Eric has done here. Nicely done.
  • Anonymous
    June 30, 2017
    For me TDD is mainly three things.Firstly it's about testing myself so I've understood the task properly before writing any code at all. In this step I also can tell if my code is doing too much and therefore can tell if my method or function conforms to the "single responsibility" rule.Secondly it's about maintainability and logical reason. A codebase where I don't know what it's supposed to do, or forgot about it, I can always rely on the tests to skip the parts that's not interesting making me to move faster.Thirdly it's about the ability to refactor and therefore evolving design. Even if this is a step in TDD you will always need to refactor since requirements changes over time. The solution you had is not the optimal solution anymore and therefore you must refactor anyway. Evolving design is a strength where you continuously strengthen your code while getting work done faster and faster since you can offload the reasoning on the tests, covering your back.AFAIK TDD is actually the only way to produce code in a systematic way. When reading TDD driven code I can make certain assumptions which I cannot do with randomly produced code (there's no system to the code). TDD code is developed automatically with tests in mind and are always a lot easier to test when you need to do it, and you always need to do it. (I would argue that there is possible code which is not testable as is, unless you refactor and then you don't know what the code is doing the same thing as it did before).If your tests get coupled with the code, I'd say it's because either your method/function is doing to much or it's a language problem, not giving you the necessary tools to ignore implementation details, which mocks usually are an indication of. Since TDD is a systematic way of producing code (at least more systematic than not doing it), code which isn't produced with TDD will not play well with TDD produced code since it won't follow the same conventions, designs and possibilities.TDD doesn't automatically make the code less bug free, but I don't believe that it won't cause more bugs just because you use TDD.If programmers cannot learn or deal with TDD, you have a different problem on your hands.
  • Anonymous
    June 30, 2017
    This reminds me of DHH's "TDD is Dead" from a few years ago. That prompted me to write this to help me firm up my opinion: https://blog.kranzky.com/tdd-is-canard-tell-it-to-the-duck-207b1d574f23I think a lot of people confuse "No TDD" with "no testing", which isn't the case at all. But if you happen to be a TDD advocate I'd love to hear your defence of how "Uncle Bob" solves the bowling game kata, as mentioned in my article.In my opinion, TDD is flawed scaffolding that actively constrains good design. The holistic nature of a well-architected solution can't be achieved by iteratively chipping away at the red-green-refactor coalface.
    • Anonymous
      June 30, 2017
      "The holistic nature of a well-architected solution can’t be achieved by iteratively chipping away at the red-green-refactor coalface."Strictly speaking, this feels like hyperbole to me, because through refactoring one can go from one implementation of a solution to any other. At least theoretically. Practically, this doesn't happen.There's a tension between a "design up front" approach and a TDD approach. If you do design up front, you will likely get to whatever design you choose up front. Is that the best design? Are there other alternatives? That question generally doesn't get asked. TDD is a tool that you can use to bring those questions more to the forefront, so that you get the opportunity to say something like, "hey, there's a really a chain of responsibility hiding under here". Of course, that assumes that you have the skills and time to see those things, and one of my main points was that you don't typically see that in TDD in production scenarios. And I agree strongly that in those cases, you are better off if the developers do design up front because their skills are a better match to that approach.
      • Anonymous
        June 30, 2017
        "Strictly speaking, this feels like hyperbole to me, because through refactoring one can go from one implementation of a solution to any other."I've never seen a good example of that though, and I've always been left with the impression that the red-green-refactor cycle was supposed to be about making small changes very quickly, and didn't permit long breaks to introspect about design. But even if it did, refactoring bad code into good would require many small changes that keep the tests green (and introduce new tests), in code-compile-test cycles of only a couple of minutes long. I'd love to see a good example of how a well-designed system evolves from scratch under these constraints. My claim is that you either have a good idea of what the final code will look like (and slow implementation down by following TDD) or end up with a mess (that you'll want to rewrite).In the bowling game kata, "Uncle Bob" writes a scoring routing that returns 0, just to make a test green. He knows it's wrong when he writes it. He knows the final program will score a game of bowling differently. But he writes it anyway, because that's the process. What is the utility of doing that? There is none. The process results in poor code and tests that don't even cover the requirements he explicitly states at the beginning. I think it's the perfect anti-example of TDD.
        • Anonymous
          July 05, 2017
          The comment has been removed
  • Anonymous
    June 30, 2017
    Indeed - so many projects I'm on, I am discouraged to re factor: I do it anyway, since removing cruft is the only way I can understand what is going on and move forward.Usually it is legacy projects; but I have had pressure to leave things as they are on my own projects too. In this case, refactoring is a risk, since your manager has already said not to - I need to make those changes without saying I am as well :/
  • Anonymous
    June 30, 2017
    The comment has been removed
    • Anonymous
      July 01, 2017
      Having lots of tests and 90% code coverage is great, but does not imply TDD. I fully support comprehensive unit testing, but I've never clicked with the idea of writing tests first to "drive"development.I think "test-driven development" could also be named "mistake-driven testing", because examples often show code being written that is just enough to get existing tests to pass but it's still known to be wrong a priori (to "drive" the creation of a new, failing test).
  • Anonymous
    June 30, 2017
    The comment has been removed
  • Anonymous
    June 30, 2017
    Well duh,suppose. If you don't use TDD to refactor and reshape your code as you go, you are missing out on one of the primary advantages. However, I will quibble on two points. Many, maybe most at this point, shops require good unit test coverage. If you write the tests after the fact, it most certainly takes longer (and seems like a waste of time).I can't think of more than one or two times in the last 15 years where not working in a TDD fashion was the faster path to getting production quality code.
    • Anonymous
      June 30, 2017
      My experience - and it would be great if it was not typical - is that many teams have codebases that are very hard to test in, so while they have a mandate to write unit tests, they don't really write that many of them, and many of the ones they do write are more integration tests than unit tests.I sometimes explain this by saying that unit testing in a lot of a production codebases is a graduate-level research project and most developers only have 200-level refactoring/design/testing skills.
  • Anonymous
    June 30, 2017
    Tests are only a by product of TDD but not design. Tests will prove your design works. It can be used as a discovery tool to help you tease design/architecture or behavior. It is just a tool. Every problem can not be solved by one tool. If you are not great at design and refactoring you should not be coding.
  • Anonymous
    June 30, 2017
    Unit tests are the seat belt we all need before we do any refactoring, or believe me, you'll crash, and it hurts. When you write TDD, the code you produce is decoupled, and your tests simple.When you have code without unit tests, you have legacy code. And just like any legacy code, working it this out is hard, there is lot of coupling, and the code is hard to test.Nobody wants to be there, without a seat belt, and if you are, you have to do the cleaning , or you will always have a mess.
  • Anonymous
    July 01, 2017
    Nonsense. Same old rant, just another day.http://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html
    • Anonymous
      March 26, 2018
      ha! it appears I already commented on this in the past, oh well. Now you have my updated thoughts in the new reply :P
  • Anonymous
    July 01, 2017
    According to Beck, Refactoring is the 5th step in the TDD workflow. What workflow are you referring to?
  • Anonymous
    July 02, 2017
    Thanks for the great article. If you don't mind, can I translate the article to Korean and post it in my personal blog(haruair.com) with a reference?
  • Anonymous
    July 02, 2017
    The comment has been removed
  • Anonymous
    July 02, 2017
    Thanks for another excellent article. Where else could anybody get that type of information in such a perfect way of writing? I have a presentation next week, and I am on the look for such info.
  • Anonymous
    July 03, 2017
    The comment has been removed
    • Anonymous
      July 05, 2017
      The comment has been removed
  • Anonymous
    July 03, 2017
    It's a brilliant place to start education though. Consistency. Think about the basics we teach kids all the time. Math with simplified trigonometry, because teaching fermat to a group of teens is going to be problematic. We teach English without attempting to fix the language because it's a horrid pig-sty of a language and hundreds of years of patching hasn't yet come up with a perfect solution for the language gods.I'd argue that if I learned TDD I'd have learned a heap more than having to retro-fit it into my practises. I may have caught errors that I didn't think of before, or abandoned lines of interest because if you cannot express in a test (which means you likely don't understand what you are doing enough to be making code around it).I won't lie, I don't TDD half as much as I should; I prefer to get others to write tests and specification I then test my code against. I'm my own boss, 75-80% of my code never leaves my offices, so I get to make rash, stupid decisions that will haunt me later. I think that is a better point to make about TDD. If you bought a car and Ford didn't test it. How mad would you be? What if you were involved in an accident with said car? How mad are people that Musk is testing cars on roads, with other users that have not consented to participating in his experiment? We don't TDD because it makes us feel good, or makes us individually APEX, the same as language doesn't help us internally communicate easier. We TDD when it's required, because as well as freedom, we have responsibility to those using our code.What I'd like to see attacked more with TDD is the cargo-cult mentality that every test must be a unit-test, that it's infallible, a hammer to hit all nails with, and that it makes individuals faster or better coders.
  • Anonymous
    July 03, 2017
    Thanks for a great article, but I don't agree with your conclusions.I think it hinges on this:"If you have those skills, TDD works great; you see what the issues are as the arise and fix them incrementally, and your simple tests prove that your design has low coupling. And, you have the tests to lean on in the future."For me this is the crux of the matter. TDD without up-front design skills is indeed pointless. Developers need to learn the design skills! They need to master them - or at least, strive for mastery, because I don't think anyone can truly claim mastery in any of this :)Designing loosely coupled code is a skill and any developer who refuses to learn this skill can't complain when their code ends up in a big tightly coupled mess that cannot easily be modified or tested.But what could be taken from this article is"people don't have the design skills, so those people should avoid TDD until they learn these skills". But I have personally found that TDD is a great way to help people to learn and apply those skills in practice; because it's a positive feedback loop. So if you don't take for e.g. the SOLID principles into account when designing your system, then you will have a harder time writing tests for your code; therefore that should prompt you to question your design and ask yourself "How can I make this better, how can I test this more easily?". I don't believe it is a happy accident that following the clean code guidelines and the SOLID principles of OOD lead to easily testable code - I think they complement each other by design and this this was that Uncle Bob had in mind all along, I seem to recall him writing something along those lines before.So for example, when I talk to people about the Dependency Inversion Principle, and people ask "yeah but why is this important, it sounds like just some theoretical dogma" then it's easy to immediately demonstrate one advantage of adhering to the DIP - it makes the code more testable. That's a tangible, in-your-face-you-cannot-deny benefit that as a learner you can grasp. So now you have started to bridge the gap...we all know that this is not the only reason to apply the principle, but it is one easy reason to understand. Later on you can start to understand the other less tangible benefits of it in terms of overall system decoupling.For me that's the real point of the feedback loop that TDD enables.So whilst I agree with the sentiment behind this:"Instead of spending time teaching people TDD, we should instead be spending time teaching them more about design and especially more about refactoring", I disagree strongly that TDD is not one of the most useful tools by which we can teach those design skills.
    • Anonymous
      July 05, 2017
      Can TDD be used as a method to help developers expand their skill set? Definitely. But my experience - and if your experience is different I'd love to know what was successful for you - is that it takes the right scenario, the right mindset, and the right codebase to make that possible.I've been playing around with different ways of teaching design. Pairing works great, but has low leverage and cultural issues in many places. Group study kindof works, if you have the right environment and somebody who knows how to teach that way (I've had some success but am not very good at it (yet)). Do-it-yourself techniques (of which TDD is one) can work for some people, but I've rarely seen it do much.
      • Anonymous
        July 06, 2017
        The comment has been removed
  • Anonymous
    July 03, 2017
    I feel this post is saying "TDD is great, do TDD if you're really good. But to require TDD for developers who do not have a strong design foundation, it will cause more frustration than it does benefit. In those, cases, it's more important to train them up to the standard when TDD becomes a useful workflow rather than a hindrance."Would that be accurate?
    • Anonymous
      July 05, 2017
      Exactly.
  • Anonymous
    July 03, 2017
    Unit tests let you refactor with confidence that you're not breaking things. Refactoring makes it easier to write unit tests.It's a chicken-and-egg situation. Unless you're starting a new project from scratch (and how often does that happen to us?) each side of the equation essentially requires that the other already be done.I value adding lightweight unit tests after a new feature is completed to guard against regressions. But writing the tests first? On anything other than a trivially small codebase? It would be pretty to think so.
    • Anonymous
      July 05, 2017
      Automated refactorings that are trustworthy (not all are) and a small amount of meticulous hand refactorings are the way that I get out of it.
  • Anonymous
    July 03, 2017
    This article would have been so much easier to read and more professional if the author had written out the full expression, followed by the abbreviation in parentheses (TDD) the first time it was used, rather than leaving the reader to scour various abbreviation lists on the Web to try to guess what he meant. I found no fewer than 31 definitions for "TDD" at Abbreviations.com.
  • Anonymous
    July 03, 2017
    I've been exposed to TDD for over 12 years now and am all in favour of #NoTDD.The mantra is Red->Green->Refactor. But in my experience only the most trivial level of refactoring is possible before we have to start re-writing our tests. So the real world situation is actually Green->Refactor->Red->Refactor Tests->Green. I've been doing this a long time and I've seen countless man hours wasted in this frustrating cycle on many different code bases. And the benefit we get in return, that refactoring is now safe because the tests are a safety net, I don't see it actually happen very often.
  • Anonymous
    July 03, 2017
    Teach them refactoring he says. However, he forgets that the tests are the safety mechanism that enables refactoring to safely occur. The tests are the mechanism that allows the architecture a decision the design to become flexible variables.How can non refactoring experts attain progress in the art of refactoring without that safety net?
    • Anonymous
      July 05, 2017
      > How can non refactoring experts attain progress in the art of refactoring without that safety net?1) Work with tools that do refactorings, and know them well enough to learn which ones are safe and which ones aren't. You can do an amazing amount with tools but few developers are willing to learn much beyond the basics (extract method & rename). 2) Practice on non-production code, both with automated and manual refactoring. Ideally do this in a group setting.3) Pair with people who know how to do this.4) Read extensively. Those are the best ways that I know. Doing TDD is a decent part of #2.My experience with unit tests and refactoring is not great. Unit tests can cover you when changing how a single class works, but those are only the simplest refactorings; many refactorings involve extracting classes or changing how classes work together. Unit tests are of less help there.
  • Anonymous
    July 03, 2017
    Teach them refactoring he says. However, he forgets that the tests are the safety mechanism that enables refactoring to safely occur. The tests are the mechanism that allows the architecture and the design to become flexible variables.How can non refactoring experts attain progress in the art of refactoring without that safety net?
  • Anonymous
    July 03, 2017
    The comment has been removed
    • Anonymous
      July 05, 2017
      I agree with you on mock frameworks; they make it far too easy to patch around very bad coupling issues.I go beyond that, however, and try to avoid mocks in general. There are a few places where I find them very useful - port/adapter/simulator is a mocking pattern that I really like - but in general, I like to create code that can be tested without mocks. #NoMocks
  • Anonymous
    July 03, 2017
    The comment has been removed
    • Anonymous
      July 05, 2017
      Preaching to the choir. I could write a book on that topic...
  • Anonymous
    July 03, 2017
    100% agree. I've heard so many people say "yay!TDD" or "you must know embrace TDD" to find no one really knows what they are doing. Another buzz word band wagon to jump on. Patterns and decoupled code are the real benefit of driving towards testable code. The benefits of decoupled code serve themselves
  • Anonymous
    July 03, 2017
    In my experience, TDD without refactoring to a better design is meaningless, but fefactoring without tests is unnecessary risk. About 8 months ago, I was assigned to a project with about 17 years worth of badly designed legacy code with even worse implementation. While some of the developers on the project were reasonably astute in the design department, most were not. None of the team were proficient in refactoring or unit testing. Shortly after I started, one of the better developers attempted to refactor a fairly significant portion of the code without any unit testing at all. We ended up rolling it back twice, and the company is now short one developer and one manager. Since then, I have taken on the task of improving the skills of the team in refactoring and in developer written testing (unit & integration), which naturally includes teaching design principles. I make use of developer led training, confluence articles, code reviews and one-on-one mentoring. Our rollback rate has diminished, our code and design quality have improved, and our management and product owners are much happier than was the case six months ago.TDD is only one tool in what should be a full toolbox. As with any tool, its use is only as good as the skill of the developer using it.
    • Anonymous
      July 05, 2017
      Sounds like you are taking a wonderful approach and we would get along well...I do want to comment a bit on one thing you said...> refactoring without tests is unnecessary risk. I would qualify this by saying, "It depends on what you mean by refactoring", which can mean anything from a simple extract method to a full rearchitecture. I have certainly seen too many refactoring fiascos in my days. Far to many. Part of what I like to teach is how to do them automatically (ie tool-assisted) in a way that is safe without tests, how to approach refactoring without tests in an organized manner (and perhaps what tests to write before the refactoring), how to refactor while pairing, and - most importantly - how to do refactorings as a series of incremental changes rather that a rewrite approach.
  • Anonymous
    July 04, 2017
    Agreed with @Michael. Benefits of TDD cannot be ignored, both in terms of quality of code and the development of the developers themselves.
  • Anonymous
    July 04, 2017
    I have been at TDD 'evangelist) for 18 years and I absolutely agree with what you are saying here. When I started I did not fully understand the importance of Refactor after every passing test look at code look at tests refactor them both. Then next test. When the refactor step is not skipped TDD takes everything to a new level. My teams and I code faster produce solutions easier and have fewer bugs ..... I have seen and coached teams who did not Refactor continually and I have seen how teams can slow down. It seems that refactoring, naming variables, methods etc is an absolutely essential skill for any programmer no matter where when or what. I have found that teaching TDD right encourages developers to refactor because they feel safer doing it. I believe that another option is that the focus on TDD shifts to the importance of TDD for continually refactoring and producing clean code. As always TDD was unfortunately named and focusing on the tests is a common failure. Yes to Test Driven Refactoring.
  • Anonymous
    July 12, 2017
    The comment has been removed
    • Anonymous
      July 19, 2017
      Your approach is in alignment with mine; I am a huge fan of value objects and a card-carrying member of the #NoMocks club.I find that it can be a hard style to teach because the code structure is a fair bit different than what people usually do.
  • Anonymous
    July 26, 2017
    I get fed up with job interviewers going on and on about TDD...Meanwhile in the real world TDD/Unit Testing has not caught a single bug or issue that has happened on my own personal website this year. Far more likely to happen in the real world are web hosting issues, configuration issues and weird Microsoft stuff.