• Does TDD mean someone is a better developer?

    • Does TDD mean someone is a better developer? Does TDD automatically lead to amazing design?

      Does TDD turn development into a mindless factory? After all there’s less cognitive load compared to TLD, does it mean we stop thinking?

      TDD is a discipline which helps developers focus on the WHAT first, and the HOW second. The WHAT is the executable form of requirements, and the HOW is the code needed to satisfy the requirements. We can say that TDD makes developers more goal-focused. The TDD discipline itself forces the developer to make testable designs, whereas in TLD it depends on the developer whether or not the design will be testable.

      We can say that a developer who practices TDD becomes mentally trained in testable design, though it is possible to achieve testable design without TDD (based on own mental effort). We can say that a developer who practices TDD will be able to have an automated feedback mechanism right at the start regarding whether the requirement is testable, whether the API is consumer-friendly (because they see the design in front of their eyes whilst writing the test), whereas the TLD developer would have to perform mental gymnastics (higher cognitive load) to get the same feedback earlier.

      Does TDD mean our designs are amazing? No. TDD by discipline ensures our designs are testable, but all other aspects of design quality (testability is not the only one) are left up to the developer.

      Does TDD mean we make less mistakes? No, humans make mistakes. BUT the difference is how long it takes to discover the mistake – in TDD, we have shorter feedback loops. In TLD we have longer feedback loops. So the difference is how quickly we can react to mistakes we make.

      The essence of TDD is – we start off with requirements (which represent a solution to some business needs), we translate the requirements from human language into code language (“executable” specifications) and then we implement the software solution to satisfy the requirements. Simple.

  • What’s worse? TDD does badly? Or TLD done badly?

    • What’s worse? TDD does badly? Or TLD done badly? Which one causes MORE damage?

      When talking about TDD, we always refer to TDD being done well.. whereas when we talk about TLD we talk about just the worst-case. Isn’t that an unfair comparison?

      Yes, we should compare worst-case TDD and worse-case TLD. Let’s see which one is worse.

      WORST CASE TDD

      Worse-case TDD is when developers don’t follow the Red-Green-Refactor cycle, but instead they just omit Refactor and do ONLY Red-Green. The result is – yes, we have tests, and yes the code is testable. BUT unfortunately, aside from testability, all other aspects of good design are missing, the design is really bad!

      This occurs whereby someone mindlessly for each requirement writes the test, writes the code to make the test pass, BUT doesn’t ever stand-back and look at the code and thinking about how to refactor it. Instead they just keep on adding to the existing design, without improving it. So then the design stagnates, and then starts rotting… For example, for each test case, someone decided to keep extending an endless if-statement instead of thinking about design patterns, and the code becomes unmaintainable, duplicated code, etc…

      WORST CASE TLD

      Worst-case TLD is when developers “just code”, so they churn out code, don’t pay attention to testability, manually debug code because they don’t have time for tests. They see tests as inferior, as secondary, as beneath them. Their job is just coding, right? I mean, if they wrote the code, and ran it in the debugger and it works, then are tests really needed?

      These developers theoretically know they “should” have tests (because some famous people in software craftsmanship said that you should write tests), but for them it’s a formality, they will do the tests AFTER the code, because the code is what’s most important. The tests are there just to satisfy some minimal code coverage that the Engineering Director set for the company.

      By the time they get to actually writing the tests, they can feel the pain. Tests become so painful, so cumbersome, so time-consuming, such a waste of time! And the tests would actually force them to change the design.. So then, they complain to the management that tests take up so much time, and why should tests be written for something that works, after all they can work on the next functionality. And then the tests are written… never.

      SUMMARY

      – In worst-case TDD, we are guaranteed to have automated tests, that code is fully covered by the tests, that code is testable BUT other aspects of the design might not be done well if the Refactor step was skipped. But the good news is, we can improve the design because we have the safety net provided by tests.

      – In worst-case TLD, we will neither have automated tests nor will the code be testable. No tests, no refactoring possible. We stuck.

      In summary, TDD is better than TLD even in the worst-case.

      No alternative text description for this image
  • TDD isn’t as easy as it looks. Clean architecture isn’t either.

    • TDD isn’t as easy as it looks in tutorials. Clean architecture isn’t either.

      A common complaint regarding TDD training is that we can’t translate it to the real world. For example, when there are tutorials about the calculator, it looks simple… but then we get stuck when we try to apply it. The real world is much more complex.

      Perhaps you tried using TDD in your company, and it failed, you felt that time on tests was wasted. Or you were using TLD because you felt that you couldn’t write tests up-front, and the tests never got written. Or there were too many dependencies on external services, and it was really confusing how to make tests from it. Or the code turned out really convoluted in any case.

      Yesterday I posted a poll regarding an open-source sample project I’ll be creating to illustrate TDD & Clean Architecture on a minimalist example BUT with complexities akin to real-world complexities (so it won’t be the generic calculator add-subtract example).

      The question now comes, what could I use as an example? Which domain? What could be the use cases and user stories? (preferably something based on real-life experiences where it was hard to write tests, hard to design, convoluted…)

      This is where I’d like to hear your thoughts

      1. Which business domain?
      2. Which use cases?
      3. What’s the business value?
      4. What are the technical complexities?

      Looking forward to seeing your suggestions?

  • TLD = Running around in circles!

    • TLD = Running around in circles!

      TLD provides longer feedback loops, you have more rework.
      TDD provides shorter feedback loops, you can work in a linear way.

      Let’s compare TDD & TLD:

      – TDD: I start off with writing the requirement in an executable form (the test). I might discover that the requirement is unclear because I can’t write the test! That’s ok, I’ll stop there, need to get the requirement clarified. Then I proceed to write the test, and as I’m writing the test, I’m modeling the contract for the code, to make sure it’s consumer-friendly. Then, I then run it to see it fail, so that I can be sure that the test is falsifiable. (If the test doesn’t fail, it could be due to incorrectly writing the test, or perhaps the functionality already exists, or I didn’t reproduce the bug which I’m trying to fix). I proceed to write the minimal code to make the test pass. I run the test and verify that it’s green. I can then safely refactor.

      – TLD-A: The “best case” of TLD (closest to TDD for sake of fair comparison) is, based on the requirement, to first write the code, then write ONE test. But, during writing the test, I might discover that I can’t write the test because my code wasn’t designed in a testable way! So then I have to rework BOTH the code and test. Great, I’ve now written the test, but I might discover that the way I’m calling the interface isn’t really consumer-friendly (after all, the test is the first consumer of the code). To modify the contract, I have to rework BOTH the code and the test. Finally, I run the test to check it passes. Green, great! But how do I know that it’s actually my code which caused the test to pass, or was it a false positive (case of the test always passing)? I don’t know. To get that assurance, I’d need to comment out the code, run the test to see it fail, then uncomment the code, and run the test to see it pass. Then I can refactor the code.

      – TLD-B: The next case of TLD, is where I do coding first, then write MULTIPLE tests. So unlike TLD-A, I’m taking a more coarse approach, whereby I might want to cover multiple requirements/scenarios in the implementation all-at-once and THEN to write SEVERAL tests after writing the code. The problem here is my short-term memory. By the time I get to writing tests, I’m not sure if I remember which requirements/scenarios I had covered (I might accidentally miss some). Perhaps I also got carried away with “gold-plating” and over-engineering. Furthermore, since I had written much more code before getting to the multiple tests, what if I discover then that my code isn’t testable? Or that my interface isn’t consumable? Oh no, I’ll have to rework so much code and the tests? Hmm, I feel these tests are dragging me down…

      – TLD-C: Lastly, we have the case where I say to myself that I’ll write the tests “later”, but it turns into “never”. Developers just do manual testing instead.

      https://media-exp1.licdn.com/dms/image/C4E22AQFA2yXW3l3XAA/feedshare-shrink_800/0/1641888033393?e=1647475200&v=beta&t=xQBu3ugUGDg1lG2xBqWYfIf9Jjl8KJBbwOrIWjSne3I
  • TDD provides 5 rapid feedback loops

    • TDD provides 5 rapid feedback loops during software development.

      Feedback loops are essential in keeping us on-track towards reaching goals. Getting feedback provides assurance that we’re going in the right direction, or indicates that we need to take corrective action in case we’re going in the wrong direction.

      What kind of feedback do we get from TDD? (RED-GREEN-REFACTOR)

      1. REQUIREMENT TESTABILITY: Is the requirement testable? If the requirement is not testable (e.g. we have no example or cannot construct an example to verify whether the requirement is met), then it’s pointless to do any implementation (i.e. we’re wasting time without knowing what we expect as the result). In TDD, if we can’t write the test, it means the requirement is not testable, we can’t go further.

      2. TEST FALSIFIABILITY: Is the test falsifiable? A test is falsifiable if we saw it failing. This serves as “proof” that later, when we add code that works, that it’s the code that actually made the test pass. This helps us avoid false positives (which occur in the case of tests that always pass). In TDD, the “RED” step is essential because it shows that our test CAN fail.

      3. INTERFACE DESIGN: Is the interface consumable? An interface is a contract between the producer and the consumer, it needs to be suitable for both the producer and the consumer. We know that a contract is consumable (usable for the consumer) when the consumer attempts to use it. If an interface is hard for the consumer to use, then this indicates problems in interface design. In TDD, the test is the first consumer.

      4. IMPLEMENTATION CORRECTNESS: Does the implementation work correctly? We can say that the implementation works correctly if we’ve seen a failing test, and then only after the code was implemented, that we see the test pass. A working implementation is essential for external quality (satisfying the requirements). In TDD, this refers to the GREEN step.

      5. IMPLEMENTATION QUALITY: Is the implementation clean? After the GREEN step, we may see ways how we can make our code clean through refactoring. A clean implementation is essential for internal quality (reducing future maintenance costs). In TDD, this is the REFACTOR step.

      The main benefits of this incremental micro-level feedback are:
      – Focusing on requirements: the TDD cycle is initiated by writing requirements in an executable form
      – Both working code and clean code: the TDD cycle enables developers to be sure that their code works, as well as to safely refactor to clean code

      In the above, we talked just about TDD. But how about TLD? TLD also can have the above feedback loops but the difference is that feedback loops are shorter in TDD compared to TLD (we will be looking at that in an upcoming post).

      What is your experience with feedback loops in TDD? What benefits do you see? Are there any other feedback loops you experienced?

Better Software Faster

Helping backend development teams deliver quality software faster with TDD, Clean Code & Clean Architecture.

Learn More
chart

Better Software

Do you want to build higher quality software products?

Find out more
globe

Faster Delivery

Do you want to accelerate your software product delivery?

Find out more
laptop

Scalable Teams

Do you want to easily scale teams to support business growth?

Find out more

What we do

We help software product companies to deliver quality software products faster and to expand development teams in a scalable way.

Take your development teams to the next level

Download our whitepaper to find out how standardization can boost profitability.

Optivem Group LLC 30 North Gould Street Sheridan, WY 82801 United States

Optivem DOO, MB 21609722, PIB 112111924, Heroja Maričića 93, Kraljevo 36000, Serbia

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.