Speaker 1: You know, usually when we talk about a diagnosis, there's
Speaker 1: this this expectation of precision.
Speaker 2: Right like engineering.
Speaker 1: Exactly like you break your arm, the x ray shows
Speaker 1: that jagged white line and the doctor just points and says,
Speaker 1: there it is broken or not broken. It's clean.
Speaker 2: Yeah, it's comforting. We really like things to be visible
Speaker 2: and neatly categorized.
Speaker 1: We do. But then you step into the world of
Speaker 1: complex software systems and suddenly that X ray machine is
Speaker 1: just broken. We're looking at a diagnostic landscape that is well, honestly,
Speaker 1: it's murky.
Speaker 2: It is the absolute definition of diagnostic muddy waters. I mean,
Speaker 2: when a massive application fails, there is no simple jagged
Speaker 2: white line to point out no, and.
Speaker 1: That is exactly what we are fixing. In today's deep dive,
Speaker 1: we are extracting the core insights from the book Effective
Speaker 1: Unit Testing, a Guide for Java Developers by Lasa Cascala.
Speaker 2: A fantastic resource, truly.
Speaker 1: And you know, whether you write code every day, manage
Speaker 1: technical projects, or just want to know why the digital
Speaker 1: infrastructure you rely on occasionally just falls apart. Our mission
Speaker 1: today is to show you how verifying truth and a
Speaker 1: system completely changes how you design it.
Speaker 2: Because It's not just a manual on catching bugs.
Speaker 1: Right right, We're going to unpack the philosophy of how
Speaker 1: writing good tests boosts daily productivity and frankly keeps a
Speaker 1: code base from turning into an unreadable nightmare.
Speaker 2: And to the listener, even if you don't write a
Speaker 2: single line of Java or any code for that matter,
Speaker 2: pay close attention.
Speaker 1: Yeah, don't tune out.
Speaker 2: Exactly, because the concepts we are going to explore, things
Speaker 2: like avoiding happy tests and using test doubles, they apply
Speaker 2: profoundly to problem solving and system design in general. It
Speaker 2: is really about how we build resilient workflows in well,
Speaker 2: any complex environment.
Speaker 1: Okay, let's unpack this. Before we get into the nuts
Speaker 1: and bolts of how to write a good test, we
Speaker 1: have to tackle the fundamental why, like why do we
Speaker 1: test at all?
Speaker 2: That's the core question.
Speaker 1: Because I think the default assumption, especially for management, is
Speaker 1: that the goal is one hundred percent code coverage, the
Speaker 1: logic being you know, if every line of code is tested,
Speaker 1: the software is perfect.
Speaker 2: Yeah, and that assumption is a very common and very
Speaker 2: dangerous trap. The source material shares a great anecdote to illustrate.
Speaker 1: So the Marcus story, Yes, Marcus.
Speaker 2: So there's this young programming whiz named Marcus who joins
Speaker 2: an investment bank. He's tasked with writing unit tests for
Speaker 2: their self service web application, and over time, he gets
Speaker 2: the team's test coverage metric up to an impressive ninety
Speaker 2: eight percent.
Speaker 1: Which I mean that looks fantastic on a manager's dashboard.
Speaker 1: You'd think he would just push for that last two percent,
Speaker 1: get the perfect score, and you know, pop the champagne.
Speaker 2: You'd think he considers it. But then Marcus realizes that
Speaker 2: pushing for one hundred percent is actually a complete waste
Speaker 2: of time. Wait why because the remaining two percent of
Speaker 2: the code consisted of what are called empty method stubs.
Speaker 1: Okay, let's translate that for the non coders listening. What
Speaker 1: exactly is an empty method stub?
Speaker 2: Well, imagine a contractor who installs a doorframe but no
Speaker 2: actual door.
Speaker 1: Okay, I can picture that, right.
Speaker 2: So if a building's comes by with a checklist and
Speaker 2: asks is there a frame here? The answer is yes,
Speaker 2: check one hundred percent coverage. But if a burglar walks
Speaker 2: right through the empty frame, you have a massive security flaw.
Speaker 1: Ah, I see.
Speaker 2: In code, an empty method stub is essentially a placeholder
Speaker 2: required by the architecture, but it doesn't actually perform any
Speaker 2: logic testing it yield zero value.
Speaker 1: So Marcus realized that one hundred percent coverage doesn't guarantee
Speaker 1: a lack of defects.
Speaker 2: Exactly. It literally just guarantees that the coin was executed
Speaker 2: during the test. It does not mean the code is
Speaker 2: doing what it's actually supposed to do.
Speaker 1: Wow. So a test could run perfectly touch every single
Speaker 1: line of code, and the software could still be entirely
Speaker 1: broken from a user's perspective, exactly. That completely shifts how
Speaker 1: you have to look at.
Speaker 2: Those metrics it has to and the financial cost of
Speaker 2: missing those defects because you were chasing a vanity metric.
Speaker 2: It's staggerant Google data, right, Yeah. The text references data
Speaker 2: presented by Mark Streebeck at a Google conference. They track
Speaker 2: the cost of fixing a bug based entirely on what
Speaker 2: it was found in the development life cycle.
Speaker 1: So how much does it change?
Speaker 2: Well, if a programmer catches and fixes a bug immediately
Speaker 2: while they are writing the code, it costs about five
Speaker 2: dollars in terms of time and effort.
Speaker 1: Five bucks. I mean that's just hitting backspace and retyping
Speaker 1: a few lines, right, But if.
Speaker 2: That bug escapes the programmer and is found during a
Speaker 2: full build of the project, the cost jumps to fifty.
Speaker 1: Dollars okay, ten times more, and.
Speaker 2: It gets worse. If it makes it into integration testing
Speaker 2: where different parts of the software are combined, it's five
Speaker 2: hundred dollars.
Speaker 1: Oh wow.
Speaker 2: If it slips all the way to a system test
Speaker 2: right before release, it costs five thousand dollars to fix.
Speaker 1: Five dollars to five thousand dollars just based on when
Speaker 1: you catch it. I'm guessing that exponential explosion isn't just
Speaker 1: because the bug got bigger.
Speaker 2: No, not at all. It's about context switching and compounding complexity.
Speaker 2: If you find the bug immediately, the mental model of
Speaker 2: the code is still fresh in your mind.
Speaker 1: Right, you're already in the zone exactly.
Speaker 2: But if it's found weeks later in a system test,
Speaker 2: the developer has to stop their current project, dig back
Speaker 2: into old code they've completely forgotten, figure out how that
Speaker 2: bug corrupted other interconnected systems, and essentially untangle a massive knot.
Speaker 1: It's like trying to fix a leaky pipe before the
Speaker 1: walls are put up, versus having to tear down the
Speaker 1: drywall replace the flooring, and repaint the entire living room
Speaker 1: weeks later.
Speaker 2: That is the exact mechanism driving the cost. And this
Speaker 2: dynamic introduces what the author calls the law of the
Speaker 2: two Plateaus, which tracks the evolution of a programmer's mindset.
Speaker 1: Okay, so what's the first plateau?
Speaker 2: The first plateau is where Marcus started outseeing tests simply
Speaker 2: as a quality assurance tool, a way to verify that
Speaker 2: the code works and catch regressions. You write tests, you
Speaker 2: get diminishing returns around that ninety percent mark, and you plateau.
Speaker 1: Because you've extracted all the value you can out of
Speaker 1: mirror verifications. So how do you break through to the
Speaker 1: second plateau?
Speaker 2: You have to completely change your mindset. You stop using
Speaker 2: tests just for verification and you start using them as
Speaker 2: a design tool.
Speaker 1: A design tool.
Speaker 2: Yeah, the book introduces Marcus's mentor, a senior architect named Sebastian,
Speaker 2: who worked entirely differently. Instead of writing code and then
Speaker 2: writing a test to check it, Sebastian wrote a failing test.
Speaker 1: First, flipping the order like that, writing the test before
Speaker 1: the code even exists feels incredibly counterintuitive. Is that a
Speaker 1: standard practice or was he just a robe genius.
Speaker 2: It is a profound methodology known as test driven development
Speaker 2: or PDD. Sebastian would define the exact behavior he wanted
Speaker 2: the code to achieve in a test, watch it fail
Speaker 2: because the code to execute it didn't exist yet, and
Speaker 2: then write just enough simple code to make that test pass.
Speaker 1: It sounds like building the mold before you pour the concrete,
Speaker 1: rather than just poking the concrete after it dries to
Speaker 1: see if it happens to be the right shape.
Speaker 2: I love that analogy. By building the mold first, you
Speaker 2: only pour exactly as much concrete as you need. This
Speaker 2: keeps the code incredibly simple and avoids what developers call
Speaker 2: accidental complexity or gold plating.
Speaker 1: Gold plating like making it too fancy.
Speaker 2: Basically, yeah, it's when you add a bunch of unnecessary
Speaker 2: features that no one actually asked for, which only serves
Speaker 2: to introduce more places for those five thousand dollars bugs
Speaker 2: to hide. The traditional cycle is design code Test, TDD
Speaker 2: flips that to test code refactor.
Speaker 1: The text also touches on behavior driven development or BDB.
Speaker 1: Is that just a rebranding of TDD.
Speaker 2: It's an evolution champion by a consultant named Dan North.
Speaker 2: He realized that the very word test was confusing people.
Speaker 2: It made managers and developers think only about quality assurance
Speaker 2: and catching errors.
Speaker 1: Oh so they were getting stuck on the word itself.
Speaker 2: Exactly, so he shifted the vocabulary. Instead of saying, write
Speaker 2: a test, he said, describe the behavior. It forces you
Speaker 2: to think about what the system should do from a
Speaker 2: user's perspective, rather than just how the code functions mechanically.
Speaker 1: Okay, so using tests as a design blueprint is clearly powerful.
Speaker 1: But a blueprint is completely useless if the next architect
Speaker 1: who looks at it can't decipher your handwriting. Does the
Speaker 1: book address what happens when these test files become too
Speaker 1: complex to read?
Speaker 2: Oh, it makes a very strong case. The test readability
Speaker 2: is the exact same thing as code maintainability. Tests are code.
Speaker 2: If your test code is an unreadable mess, it becomes
Speaker 2: a massive cognitive burden for the team. The text highlights
Speaker 2: this with a brilliant anti pattern the JS one K competition.
Speaker 1: Oh this was wild. Explain what these hackers are doing?
Speaker 2: In this competition, developers try to squeeze complex three D
Speaker 2: animations into a JavaScript file that is a maximum of
Speaker 2: one twenty four bytes.
Speaker 1: That is tiny, it really is.
Speaker 2: To make it fit, they use insane compression tricks. They
Speaker 2: rename variables to single random letters, They strip out all
Speaker 2: formatting spaces and line breaks.
Speaker 1: And the result is what the author hilariously calls write
Speaker 1: only code.
Speaker 2: Yes, because you can write it in the moment, but
Speaker 2: if you look at it a week later, it is
Speaker 2: just an undecipherable wall of symbols. You could never maintain
Speaker 2: it or.
Speaker 1: Update it, right, It's just jibberish at that point.
Speaker 2: Now, workplace code usually isn't that extreme, but studies show
Speaker 2: a direct, strong correlation between poor readability and high defect density.
Speaker 2: When a human brain has to expend massive amounts of
Speaker 2: cognitive energy just to figure out what a block of
Speaker 2: code is trying to do, mistakes happen, And if.
Speaker 1: You're a manager onboarding a new developer, you're paying them
Speaker 1: to sit there and decipher hieroglyphics instead of actually building
Speaker 1: the product exactly. The author points out this is especially
Speaker 1: toxic when it comes to automated tests, specifically calling out
Speaker 1: the nightmare of the thirty minute monolithic test.
Speaker 2: Yes, imagine you have a massive application and instead of
Speaker 2: many small, targeted tests, you have one giant thirty minute
Speaker 2: script that runs through everything at once. You make a
Speaker 2: tiny change to how mailing addresses are formatted, and twenty
Speaker 2: five minutes into the test, it fails.
Speaker 1: So you have to dig through this unstructured monolith to
Speaker 1: find exactly where your tiny change broke things. And then
Speaker 1: you have to run the whole thirty minute test again
Speaker 1: just to see if your fixed.
Speaker 2: Work, which completely destroys the fast feedback loop we established earlier.
Speaker 1: Wait, if a test is just too long, can I
Speaker 1: just you know, chop it up into one hundred tiny,
Speaker 1: separate files. Wouldn't that solve the monolith problem?
Speaker 2: Arbitrary structure is honestly just as bad as no structure.
Speaker 2: If you just chop a monolith up randomly, you aren't
Speaker 2: fixing the problem. You're just spreading the mass across one
Speaker 2: hundred different file I see, the boundaries of your tests
Speaker 2: must map logically to the domain and to the developer's
Speaker 2: mental model. If a test is chepping the shopping cart,
Speaker 2: all the shopping cart logic needs to be structured together
Speaker 2: in a way that makes sense. Otherwise you're sending the
Speaker 2: next developer on a wild goose chase across fifty unrelated files.
Speaker 1: A perfectly readable, well structured blueprint is great, but what
Speaker 1: if the blueprint lies about the structural integrity. A beautifully
Speaker 1: structured test is wonderful, but if that test lies to you,
Speaker 1: it is vastly more dangerous than having no test at all.
Speaker 2: False sets of security will sink a project faster than
Speaker 2: anything else. We are moving from the structure of the
Speaker 2: test to the integrity of its results, and the.
Speaker 1: Book gives some terrifying examples of how tests can lie,
Speaker 1: starting with something as simple as poor naming.
Speaker 2: Yeah, the author talks about auditing a code base and
Speaker 2: finding a test named to imply it was validating a
Speaker 2: highly complex mask function for security. Sounds important, it does,
Speaker 2: but when you look closely at the actual code, the
Speaker 2: test was only checking if the parameters were a valid combination.
Speaker 2: It wasn't testing the actual security behavior at all. If
Speaker 2: you just looked at the test report, you'd think the
Speaker 2: mask feature was secure when its core logic was completely untested.
Speaker 1: Then there is the happy test. This happens when developers
Speaker 1: are pressured by management to increase their code coverage metrics
Speaker 1: like we talked about earlier. Tell us how the happy
Speaker 1: test fakes success.
Speaker 2: Well, They write a test that runs the production code,
Speaker 2: but they wrap the whole thing in what's called a
Speaker 2: trycatch block, and they don't write a single assertion.
Speaker 1: Okay, for the non coder's listening, break down those two terms.
Speaker 1: What is a tricatch block and what is an assertion?
Speaker 2: Think of a tricatch block as a piece of code
Speaker 2: that says, if this system crashes, just sweep the air
Speaker 2: under the rug and act like nothing happened. It catches
Speaker 2: the error before it fails the test and the assertion.
Speaker 2: An assertion is the actual question you are asking. It says,
Speaker 2: if I input two plus two, assert that the answer
Speaker 2: is for A happy test runs the math, sweeps any
Speaker 2: crashes under the rug and never actually asks what the
Speaker 2: answer is for.
Speaker 1: So it looks beautiful on paper. The boss sees ninety
Speaker 1: nine percent coverage and is thrilled, But the test will
Speaker 1: only fail if the server literally catches fire. It completely
Speaker 1: ignores whether the logic is actually correct.
Speaker 2: It's pure noise designed to satisfy a metric. Then you
Speaker 2: have the issue of interdependent tests, which is a massive
Speaker 2: trust breaker. Tests often secretly rely on the environment. Oh so,
Speaker 2: let's say you have tests A which creates a user
Speaker 2: in a database, and test B, which checks that user's permissions.
Speaker 2: They run fine in that order. But let's say the
Speaker 2: test framework decides to run them in alphabetical order. Oh no,
Speaker 2: Test B runs first, tries to check a user that
Speaker 2: hasn't been created yet, and fails.
Speaker 1: A massive false alarm. The code itself isn't broken, the
Speaker 1: environment is just flaky exactly.
Speaker 2: The book mentions a real world example where Java seven
Speaker 2: updated its internal plumbing. It changed the default sorting order
Speaker 2: of how methods were returned, something called method reflection. Suddenly,
Speaker 2: massive test sites broke everywhere.
Speaker 1: Just because of an internal update.
Speaker 2: The production code was fine, but the tests were secretly
Speaker 2: relying on the assumption that things would always be returned
Speaker 2: in the old order.
Speaker 1: And speaking of assumptions, the author goes on a tear
Speaker 1: about relying on the system clock or network connections, and
Speaker 1: tests using commands to check the real time, like system
Speaker 1: dot current time millis.
Speaker 2: Using real time in a test guarantees intermittent failures. If
Speaker 2: your test calculates a route based on current traffic, it
Speaker 2: might pass at two point zero pm but fail at
Speaker 2: five zero pm during rush hour.
Speaker 3: That sounds infuriating, it is It forces developers into this
Speaker 3: horrible habit of running a test suite four or five
Speaker 3: times until they get lucky and it randomly passes just
Speaker 3: to get a green light.
Speaker 1: So what does this all mean? If my tests are
Speaker 1: randomly failing fifty percent of the time because of an
Speaker 1: unpredictable clock or a dropped network connection, how on earth
Speaker 1: am I supposed to test time sensitive or internet reliant code.
Speaker 2: That is the million dollar question for any software engineer.
Speaker 2: To test the unpredictable, we have to completely isolate the
Speaker 2: code from its unpredictable environment. This brings us to the
Speaker 2: developer's secret weapon, test doubles.
Speaker 1: The text actually quotes Mahatma Gandhi here, which I was
Speaker 1: definitely not expecting in a software engineering book. Be the
Speaker 1: change you want to see in the world.
Speaker 2: Surprisingly fitting in testing, you become the change you want
Speaker 2: to see in your code. By replacing external, unreliable dependencies
Speaker 2: with controlled, predictable environments, you swap out the messy real
Speaker 2: world for a test.
Speaker 1: The book outlines five distinct reasons you would want to
Speaker 1: use a double The first is isolating the code so
Speaker 1: it's not dragged down by outside factors. The second is
Speaker 1: speeding up execution.
Speaker 2: Speed is so important, right, say you have.
Speaker 1: A piece of code that runs a really slow weighted
Speaker 1: graph routing algorithm. If every single test has to wait
Speaker 1: for a complex GPS calculation, your test suite will take hours.
Speaker 1: You use a double to instantly return a pre calculated route.
Speaker 2: Because speed is crucial for that fast feedback loop. The
Speaker 2: third reason is making things deterministic. Think of a craps game.
Speaker 2: If the dice are truly random, you can't write a
Speaker 2: test for what happens when a player rolls snake eyes
Speaker 2: because you might have to run the test one hundred
Speaker 2: times to get that specific roll. A test double lets
Speaker 2: you rig the dice to roll snake eyes on command.
Speaker 1: Which naturally leads into the fourth reason, simulating impossible conditions.
Speaker 1: There's a hilarious anecdote in the text about trying to
Speaker 1: test how an app handles a dropped Internet connection.
Speaker 2: Oh yeah, short of programming a Lego Mindstorm's robot to
Speaker 2: physically drive over and unplug your computer's network cable at
Speaker 2: the exact right.
Speaker 1: Millisec which would be amazing to watch but.
Speaker 2: Very impractical, Yeah, it is nearly impossible to automate a
Speaker 2: dropped connection test in the real physical world. A test
Speaker 2: double lets you simulate that network failure perfectly in software
Speaker 2: every single time.
Speaker 1: And the fifth reason is exposing hidden information, letting a
Speaker 1: test check internal states without ruining the actual code design
Speaker 1: to get to it. This whole concept makes total sense.
Speaker 1: If you're shooting an action movie, you don't throw your
Speaker 1: lead actor out.
Speaker 2: Of a helicopter I hope not.
Speaker 1: You use a stunt double. The camera gets the shot,
Speaker 1: the story moves forward, but nobody actually dies exactly.
Speaker 2: You get the validation without the real world risk or delay.
Speaker 2: But just like in movies, a car chase needs a
Speaker 2: different type of stunt double than a martial arts.
Speaker 1: Fight scene, which brings us to the specific taxonomy of
Speaker 1: test doubles based on what the developer needs to achieve.
Speaker 1: Let's go through the cast of characters, starting with test stubs.
Speaker 2: A test stub is the simplest form. As the dictionary
Speaker 2: definition of the book says, it's an unusually short thing.
Speaker 2: It stands in with the simplest possible implementation.
Speaker 1: Give us an example.
Speaker 2: For example, if your code is supposed to log an
Speaker 2: event to a remote server, you might use a stublogger
Speaker 2: that just instantly returns a hard coded warning message and
Speaker 2: does absolutely nothing else. It satisfies the code's request without
Speaker 2: triggering a massive network operation.
Speaker 1: Okay, next flavor up fake objects.
Speaker 2: A fake object is a step up because it actually
Speaker 2: contains real logic, but it operates without the real world
Speaker 2: side effects. A great example is an in memory user repository.
Speaker 1: How does that differ from a stub?
Speaker 2: Instead of connecting to a heavy, slow, real world database
Speaker 2: to save a user, the fake object just saves the
Speaker 2: user data to a simple list in the computer's temporary memory.
Speaker 2: It quacts like a database. It stores data like a database,
Speaker 2: but it is vastly faster and doesn't require a network connection.
Speaker 1: Love that third on the list. Test spies.
Speaker 2: Test spies are used in tricky situations where a method
Speaker 2: doesn't return to value you can check. Say you have
Speaker 2: a distributed log that sends messages to multiple targets. Okay,
Speaker 2: you can't just ask the log what it returned, so
Speaker 2: you pass in a test spy. The spy silently records
Speaker 2: every single message it receives. Then, after the code runs,
Speaker 2: the test debreachs the spy and says what did you see?
Speaker 2: Did you get the correct.
Speaker 1: Message, which leads to the final and most strict flavor.
Speaker 2: Mock objects mark objects are the ultimate control freaks of
Speaker 2: the test double world. You program them with extreme precision
Speaker 2: before the test even runs. For instance, you might create
Speaker 2: a mock Internet object and you tell it expect exactly
Speaker 2: one call to this specific method and expect these exact
Speaker 2: specific parameters.
Speaker 1: Here's where it's really interesting. So a stub is a
Speaker 1: cardboard cutout, a spy is an undercover cop taking notes.
Speaker 1: But a mock object is like a cyborg terminator. You
Speaker 1: program its mission before you send it back in time.
Speaker 1: You tell it John Connor will ask you for help
Speaker 1: exactly one time.
Speaker 2: I love the terminator analogy, right.
Speaker 1: If John asks twice, or if Sarah Connor asks instead,
Speaker 1: the mock instantly triggers an alarm and fails the test.
Speaker 1: It enforces absolute rigid adherence to the expected.
Speaker 2: Interaction that maps perfectly to the mechanism. If the code
Speaker 2: calls that mock Internet object zero times, it instantly fails
Speaker 2: the test. If it calls it two times, it fails.
Speaker 2: If the parameters are off by one character, it fails.
Speaker 2: It ensures the system behaves exactly is designed with zero deviation.
Speaker 1: So to pull all of this together for the listener.
Speaker 1: Writing good tests is not a chore. It is not
Speaker 1: just about keeping the boss happy with a coverage metric.
Speaker 1: It is a profound design exercise. It forces you to
Speaker 1: think about how your code behaves in isolation, how it
Speaker 1: handles the unpredictable, and how it communicates its intent clearly
Speaker 1: to the next person who has to read it. Good
Speaker 1: tests save time, they save money, and frankly, they save
Speaker 1: your sanity.
Speaker 2: If we connect this to the bigger picture, treating code
Speaker 2: testing with this level of rigor fundamentally changes how you
Speaker 2: view all common complex systems.
Speaker 1: It really does.
Speaker 2: When you realize how easily a system can be derailed
Speaker 2: by a hidden dependency or a false assumption, you start
Speaker 2: demanding better verification everywhere. You stop trusting the happy test
Speaker 2: in your business processes, in your supply chains, and in
Speaker 2: your organizational structures. You start looking for the empty method
Speaker 2: stubs in your own workflows.
Speaker 1: I want to leave you with a final thought to
Speaker 1: mole over taking the source material just one step further
Speaker 1: into everyday life. Think about applying the concept of test
Speaker 1: driven development, defining the success criteria before you act to
Speaker 1: your own life.
Speaker 2: That's a fascinating way to look at it.
Speaker 1: What if before starting a major home project, before sitting
Speaker 1: down for a difficult conversation with a partner, or before
Speaker 1: making a massive career decision, you first define the exact
Speaker 1: tests that would prove you succeeded.
Speaker 2: Writing the mole before you pour the concrete of your
Speaker 2: life's next chapter.
Speaker 1: How much accidental complexity, how much unnecessary drama and wasted
Speaker 1: effort would you avoid if you knew exactly what passing
Speaker 1: life like before you wrote the first line. At the
Speaker 1: start of this deep dive, we talked about that broken
Speaker 1: X ray machine, the murky diagnostic waters of trying to
Speaker 1: fix a problem after it has already become a tangled mess.
Speaker 1: TDD is essentially building a perfect, customized X ray machine
Speaker 1: for a specific bone before you even take the step
Speaker 1: that might cause the break. Keep asking questions, keep defining
Speaker 1: your success criteria, and keep tuning in for the next
Speaker 1: deep dive.