WEBVTT

1
00:00:00.120 --> 00:00:03.319
<v Speaker 1>You know, usually when we talk about a diagnosis, there's

2
00:00:03.399 --> 00:00:06.879
<v Speaker 1>this this expectation of precision.

3
00:00:06.480 --> 00:00:07.280
<v Speaker 2>Right like engineering.

4
00:00:07.360 --> 00:00:09.960
<v Speaker 1>Exactly like you break your arm, the x ray shows

5
00:00:10.000 --> 00:00:12.560
<v Speaker 1>that jagged white line and the doctor just points and says,

6
00:00:12.599 --> 00:00:15.160
<v Speaker 1>there it is broken or not broken. It's clean.

7
00:00:15.359 --> 00:00:18.280
<v Speaker 2>Yeah, it's comforting. We really like things to be visible

8
00:00:18.320 --> 00:00:19.440
<v Speaker 2>and neatly categorized.

9
00:00:19.600 --> 00:00:22.000
<v Speaker 1>We do. But then you step into the world of

10
00:00:22.000 --> 00:00:26.160
<v Speaker 1>complex software systems and suddenly that X ray machine is

11
00:00:26.280 --> 00:00:30.280
<v Speaker 1>just broken. We're looking at a diagnostic landscape that is well, honestly,

12
00:00:30.600 --> 00:00:31.160
<v Speaker 1>it's murky.

13
00:00:31.679 --> 00:00:35.920
<v Speaker 2>It is the absolute definition of diagnostic muddy waters. I mean,

14
00:00:35.960 --> 00:00:39.719
<v Speaker 2>when a massive application fails, there is no simple jagged

15
00:00:39.759 --> 00:00:41.640
<v Speaker 2>white line to point out no, and.

16
00:00:41.560 --> 00:00:44.320
<v Speaker 1>That is exactly what we are fixing. In today's deep dive,

17
00:00:44.640 --> 00:00:47.560
<v Speaker 1>we are extracting the core insights from the book Effective

18
00:00:47.640 --> 00:00:51.640
<v Speaker 1>Unit Testing, a Guide for Java Developers by Lasa Cascala.

19
00:00:51.240 --> 00:00:53.240
<v Speaker 2>A fantastic resource, truly.

20
00:00:53.159 --> 00:00:55.280
<v Speaker 1>And you know, whether you write code every day, manage

21
00:00:55.320 --> 00:00:57.920
<v Speaker 1>technical projects, or just want to know why the digital

22
00:00:57.960 --> 00:01:01.359
<v Speaker 1>infrastructure you rely on occasionally just falls apart. Our mission

23
00:01:01.399 --> 00:01:03.640
<v Speaker 1>today is to show you how verifying truth and a

24
00:01:03.679 --> 00:01:05.719
<v Speaker 1>system completely changes how you design it.

25
00:01:05.879 --> 00:01:08.079
<v Speaker 2>Because It's not just a manual on catching bugs.

26
00:01:07.840 --> 00:01:10.719
<v Speaker 1>Right right, We're going to unpack the philosophy of how

27
00:01:10.799 --> 00:01:15.439
<v Speaker 1>writing good tests boosts daily productivity and frankly keeps a

28
00:01:15.519 --> 00:01:18.280
<v Speaker 1>code base from turning into an unreadable nightmare.

29
00:01:18.400 --> 00:01:20.719
<v Speaker 2>And to the listener, even if you don't write a

30
00:01:20.760 --> 00:01:23.120
<v Speaker 2>single line of Java or any code for that matter,

31
00:01:23.680 --> 00:01:24.519
<v Speaker 2>pay close attention.

32
00:01:24.680 --> 00:01:26.120
<v Speaker 1>Yeah, don't tune out.

33
00:01:26.400 --> 00:01:30.040
<v Speaker 2>Exactly, because the concepts we are going to explore, things

34
00:01:30.120 --> 00:01:34.280
<v Speaker 2>like avoiding happy tests and using test doubles, they apply

35
00:01:34.599 --> 00:01:38.959
<v Speaker 2>profoundly to problem solving and system design in general. It

36
00:01:39.040 --> 00:01:42.319
<v Speaker 2>is really about how we build resilient workflows in well,

37
00:01:42.439 --> 00:01:43.519
<v Speaker 2>any complex environment.

38
00:01:43.599 --> 00:01:46.519
<v Speaker 1>Okay, let's unpack this. Before we get into the nuts

39
00:01:46.560 --> 00:01:48.879
<v Speaker 1>and bolts of how to write a good test, we

40
00:01:49.000 --> 00:01:51.599
<v Speaker 1>have to tackle the fundamental why, like why do we

41
00:01:51.640 --> 00:01:52.280
<v Speaker 1>test at all?

42
00:01:52.439 --> 00:01:53.840
<v Speaker 2>That's the core question.

43
00:01:53.760 --> 00:01:56.560
<v Speaker 1>Because I think the default assumption, especially for management, is

44
00:01:56.599 --> 00:01:59.280
<v Speaker 1>that the goal is one hundred percent code coverage, the

45
00:01:59.359 --> 00:02:01.640
<v Speaker 1>logic being you know, if every line of code is tested,

46
00:02:01.719 --> 00:02:02.799
<v Speaker 1>the software is perfect.

47
00:02:02.959 --> 00:02:05.159
<v Speaker 2>Yeah, and that assumption is a very common and very

48
00:02:05.239 --> 00:02:09.319
<v Speaker 2>dangerous trap. The source material shares a great anecdote to illustrate.

49
00:02:09.080 --> 00:02:11.400
<v Speaker 1>So the Marcus story, Yes, Marcus.

50
00:02:11.439 --> 00:02:15.120
<v Speaker 2>So there's this young programming whiz named Marcus who joins

51
00:02:15.120 --> 00:02:18.759
<v Speaker 2>an investment bank. He's tasked with writing unit tests for

52
00:02:18.840 --> 00:02:22.680
<v Speaker 2>their self service web application, and over time, he gets

53
00:02:22.680 --> 00:02:25.919
<v Speaker 2>the team's test coverage metric up to an impressive ninety

54
00:02:25.919 --> 00:02:26.800
<v Speaker 2>eight percent.

55
00:02:26.840 --> 00:02:29.439
<v Speaker 1>Which I mean that looks fantastic on a manager's dashboard.

56
00:02:29.479 --> 00:02:31.759
<v Speaker 1>You'd think he would just push for that last two percent,

57
00:02:31.800 --> 00:02:34.759
<v Speaker 1>get the perfect score, and you know, pop the champagne.

58
00:02:34.840 --> 00:02:38.479
<v Speaker 2>You'd think he considers it. But then Marcus realizes that

59
00:02:38.639 --> 00:02:41.240
<v Speaker 2>pushing for one hundred percent is actually a complete waste

60
00:02:41.280 --> 00:02:44.039
<v Speaker 2>of time. Wait why because the remaining two percent of

61
00:02:44.039 --> 00:02:47.280
<v Speaker 2>the code consisted of what are called empty method stubs.

62
00:02:47.479 --> 00:02:50.479
<v Speaker 1>Okay, let's translate that for the non coders listening. What

63
00:02:50.680 --> 00:02:52.599
<v Speaker 1>exactly is an empty method stub?

64
00:02:52.719 --> 00:02:56.000
<v Speaker 2>Well, imagine a contractor who installs a doorframe but no

65
00:02:56.120 --> 00:02:56.800
<v Speaker 2>actual door.

66
00:02:56.960 --> 00:02:58.439
<v Speaker 1>Okay, I can picture that, right.

67
00:02:58.719 --> 00:03:01.439
<v Speaker 2>So if a building's comes by with a checklist and

68
00:03:01.479 --> 00:03:04.840
<v Speaker 2>asks is there a frame here? The answer is yes,

69
00:03:05.319 --> 00:03:08.560
<v Speaker 2>check one hundred percent coverage. But if a burglar walks

70
00:03:08.639 --> 00:03:12.199
<v Speaker 2>right through the empty frame, you have a massive security flaw.

71
00:03:12.400 --> 00:03:13.840
<v Speaker 1>Ah, I see.

72
00:03:13.599 --> 00:03:18.560
<v Speaker 2>In code, an empty method stub is essentially a placeholder

73
00:03:18.599 --> 00:03:21.520
<v Speaker 2>required by the architecture, but it doesn't actually perform any

74
00:03:21.520 --> 00:03:24.360
<v Speaker 2>logic testing it yield zero value.

75
00:03:24.560 --> 00:03:28.120
<v Speaker 1>So Marcus realized that one hundred percent coverage doesn't guarantee

76
00:03:28.159 --> 00:03:29.240
<v Speaker 1>a lack of defects.

77
00:03:29.360 --> 00:03:32.759
<v Speaker 2>Exactly. It literally just guarantees that the coin was executed

78
00:03:32.840 --> 00:03:35.039
<v Speaker 2>during the test. It does not mean the code is

79
00:03:35.080 --> 00:03:36.599
<v Speaker 2>doing what it's actually supposed to do.

80
00:03:36.719 --> 00:03:40.319
<v Speaker 1>Wow. So a test could run perfectly touch every single

81
00:03:40.360 --> 00:03:42.759
<v Speaker 1>line of code, and the software could still be entirely

82
00:03:42.800 --> 00:03:46.560
<v Speaker 1>broken from a user's perspective, exactly. That completely shifts how

83
00:03:46.560 --> 00:03:47.360
<v Speaker 1>you have to look at.

84
00:03:47.280 --> 00:03:50.120
<v Speaker 2>Those metrics it has to and the financial cost of

85
00:03:50.159 --> 00:03:52.800
<v Speaker 2>missing those defects because you were chasing a vanity metric.

86
00:03:53.479 --> 00:03:57.680
<v Speaker 2>It's staggerant Google data, right, Yeah. The text references data

87
00:03:57.719 --> 00:04:01.080
<v Speaker 2>presented by Mark Streebeck at a Google conference. They track

88
00:04:01.159 --> 00:04:04.000
<v Speaker 2>the cost of fixing a bug based entirely on what

89
00:04:04.080 --> 00:04:05.759
<v Speaker 2>it was found in the development life cycle.

90
00:04:05.840 --> 00:04:07.680
<v Speaker 1>So how much does it change?

91
00:04:07.759 --> 00:04:10.759
<v Speaker 2>Well, if a programmer catches and fixes a bug immediately

92
00:04:10.879 --> 00:04:13.479
<v Speaker 2>while they are writing the code, it costs about five

93
00:04:13.520 --> 00:04:14.960
<v Speaker 2>dollars in terms of time and effort.

94
00:04:15.120 --> 00:04:18.160
<v Speaker 1>Five bucks. I mean that's just hitting backspace and retyping

95
00:04:18.199 --> 00:04:20.360
<v Speaker 1>a few lines, right, But if.

96
00:04:20.199 --> 00:04:23.319
<v Speaker 2>That bug escapes the programmer and is found during a

97
00:04:23.319 --> 00:04:26.839
<v Speaker 2>full build of the project, the cost jumps to fifty.

98
00:04:26.560 --> 00:04:28.959
<v Speaker 1>Dollars okay, ten times more, and.

99
00:04:28.879 --> 00:04:32.199
<v Speaker 2>It gets worse. If it makes it into integration testing

100
00:04:32.560 --> 00:04:35.639
<v Speaker 2>where different parts of the software are combined, it's five

101
00:04:35.720 --> 00:04:36.360
<v Speaker 2>hundred dollars.

102
00:04:36.560 --> 00:04:37.199
<v Speaker 1>Oh wow.

103
00:04:37.279 --> 00:04:39.319
<v Speaker 2>If it slips all the way to a system test

104
00:04:39.439 --> 00:04:42.360
<v Speaker 2>right before release, it costs five thousand dollars to fix.

105
00:04:42.560 --> 00:04:45.160
<v Speaker 1>Five dollars to five thousand dollars just based on when

106
00:04:45.199 --> 00:04:48.800
<v Speaker 1>you catch it. I'm guessing that exponential explosion isn't just

107
00:04:48.839 --> 00:04:50.079
<v Speaker 1>because the bug got bigger.

108
00:04:50.199 --> 00:04:54.600
<v Speaker 2>No, not at all. It's about context switching and compounding complexity.

109
00:04:54.680 --> 00:04:57.560
<v Speaker 2>If you find the bug immediately, the mental model of

110
00:04:57.600 --> 00:04:59.279
<v Speaker 2>the code is still fresh in your mind.

111
00:04:59.199 --> 00:05:01.199
<v Speaker 1>Right, you're already in the zone exactly.

112
00:05:01.879 --> 00:05:04.120
<v Speaker 2>But if it's found weeks later in a system test,

113
00:05:04.360 --> 00:05:07.680
<v Speaker 2>the developer has to stop their current project, dig back

114
00:05:07.720 --> 00:05:11.000
<v Speaker 2>into old code they've completely forgotten, figure out how that

115
00:05:11.040 --> 00:05:16.040
<v Speaker 2>bug corrupted other interconnected systems, and essentially untangle a massive knot.

116
00:05:16.240 --> 00:05:18.199
<v Speaker 1>It's like trying to fix a leaky pipe before the

117
00:05:18.199 --> 00:05:20.480
<v Speaker 1>walls are put up, versus having to tear down the

118
00:05:20.519 --> 00:05:24.279
<v Speaker 1>drywall replace the flooring, and repaint the entire living room

119
00:05:24.439 --> 00:05:25.199
<v Speaker 1>weeks later.

120
00:05:25.439 --> 00:05:28.360
<v Speaker 2>That is the exact mechanism driving the cost. And this

121
00:05:28.480 --> 00:05:31.279
<v Speaker 2>dynamic introduces what the author calls the law of the

122
00:05:31.319 --> 00:05:35.680
<v Speaker 2>two Plateaus, which tracks the evolution of a programmer's mindset.

123
00:05:35.920 --> 00:05:37.399
<v Speaker 1>Okay, so what's the first plateau?

124
00:05:37.680 --> 00:05:41.079
<v Speaker 2>The first plateau is where Marcus started outseeing tests simply

125
00:05:41.079 --> 00:05:44.120
<v Speaker 2>as a quality assurance tool, a way to verify that

126
00:05:44.199 --> 00:05:47.680
<v Speaker 2>the code works and catch regressions. You write tests, you

127
00:05:47.720 --> 00:05:51.319
<v Speaker 2>get diminishing returns around that ninety percent mark, and you plateau.

128
00:05:51.160 --> 00:05:53.920
<v Speaker 1>Because you've extracted all the value you can out of

129
00:05:54.079 --> 00:05:56.800
<v Speaker 1>mirror verifications. So how do you break through to the

130
00:05:56.800 --> 00:05:57.639
<v Speaker 1>second plateau?

131
00:05:57.800 --> 00:06:00.720
<v Speaker 2>You have to completely change your mindset. You stop using

132
00:06:00.759 --> 00:06:03.480
<v Speaker 2>tests just for verification and you start using them as

133
00:06:03.519 --> 00:06:04.279
<v Speaker 2>a design tool.

134
00:06:04.360 --> 00:06:05.199
<v Speaker 1>A design tool.

135
00:06:05.360 --> 00:06:09.600
<v Speaker 2>Yeah, the book introduces Marcus's mentor, a senior architect named Sebastian,

136
00:06:09.759 --> 00:06:12.759
<v Speaker 2>who worked entirely differently. Instead of writing code and then

137
00:06:12.800 --> 00:06:16.079
<v Speaker 2>writing a test to check it, Sebastian wrote a failing test.

138
00:06:15.839 --> 00:06:18.879
<v Speaker 1>First, flipping the order like that, writing the test before

139
00:06:18.920 --> 00:06:23.600
<v Speaker 1>the code even exists feels incredibly counterintuitive. Is that a

140
00:06:23.600 --> 00:06:26.279
<v Speaker 1>standard practice or was he just a robe genius.

141
00:06:26.720 --> 00:06:29.920
<v Speaker 2>It is a profound methodology known as test driven development

142
00:06:30.079 --> 00:06:35.079
<v Speaker 2>or PDD. Sebastian would define the exact behavior he wanted

143
00:06:35.079 --> 00:06:37.839
<v Speaker 2>the code to achieve in a test, watch it fail

144
00:06:38.040 --> 00:06:40.639
<v Speaker 2>because the code to execute it didn't exist yet, and

145
00:06:40.680 --> 00:06:43.360
<v Speaker 2>then write just enough simple code to make that test pass.

146
00:06:43.680 --> 00:06:46.680
<v Speaker 1>It sounds like building the mold before you pour the concrete,

147
00:06:47.040 --> 00:06:49.600
<v Speaker 1>rather than just poking the concrete after it dries to

148
00:06:49.600 --> 00:06:51.399
<v Speaker 1>see if it happens to be the right shape.

149
00:06:51.480 --> 00:06:54.040
<v Speaker 2>I love that analogy. By building the mold first, you

150
00:06:54.079 --> 00:06:57.079
<v Speaker 2>only pour exactly as much concrete as you need. This

151
00:06:57.160 --> 00:07:00.199
<v Speaker 2>keeps the code incredibly simple and avoids what developers call

152
00:07:00.560 --> 00:07:02.560
<v Speaker 2>accidental complexity or gold plating.

153
00:07:02.600 --> 00:07:04.319
<v Speaker 1>Gold plating like making it too fancy.

154
00:07:04.480 --> 00:07:07.199
<v Speaker 2>Basically, yeah, it's when you add a bunch of unnecessary

155
00:07:07.240 --> 00:07:09.879
<v Speaker 2>features that no one actually asked for, which only serves

156
00:07:09.920 --> 00:07:12.639
<v Speaker 2>to introduce more places for those five thousand dollars bugs

157
00:07:12.639 --> 00:07:17.319
<v Speaker 2>to hide. The traditional cycle is design code Test, TDD

158
00:07:17.439 --> 00:07:20.160
<v Speaker 2>flips that to test code refactor.

159
00:07:20.439 --> 00:07:23.959
<v Speaker 1>The text also touches on behavior driven development or BDB.

160
00:07:24.199 --> 00:07:25.639
<v Speaker 1>Is that just a rebranding of TDD.

161
00:07:25.959 --> 00:07:28.839
<v Speaker 2>It's an evolution champion by a consultant named Dan North.

162
00:07:29.480 --> 00:07:32.600
<v Speaker 2>He realized that the very word test was confusing people.

163
00:07:33.160 --> 00:07:36.399
<v Speaker 2>It made managers and developers think only about quality assurance

164
00:07:36.399 --> 00:07:37.319
<v Speaker 2>and catching errors.

165
00:07:37.399 --> 00:07:40.040
<v Speaker 1>Oh so they were getting stuck on the word itself.

166
00:07:39.720 --> 00:07:42.639
<v Speaker 2>Exactly, so he shifted the vocabulary. Instead of saying, write

167
00:07:42.639 --> 00:07:46.279
<v Speaker 2>a test, he said, describe the behavior. It forces you

168
00:07:46.360 --> 00:07:49.240
<v Speaker 2>to think about what the system should do from a

169
00:07:49.319 --> 00:07:53.279
<v Speaker 2>user's perspective, rather than just how the code functions mechanically.

170
00:07:53.639 --> 00:07:57.360
<v Speaker 1>Okay, so using tests as a design blueprint is clearly powerful.

171
00:07:57.959 --> 00:08:00.839
<v Speaker 1>But a blueprint is completely useless if the next architect

172
00:08:00.879 --> 00:08:03.879
<v Speaker 1>who looks at it can't decipher your handwriting. Does the

173
00:08:03.920 --> 00:08:06.639
<v Speaker 1>book address what happens when these test files become too

174
00:08:06.680 --> 00:08:07.639
<v Speaker 1>complex to read?

175
00:08:07.800 --> 00:08:10.839
<v Speaker 2>Oh, it makes a very strong case. The test readability

176
00:08:10.879 --> 00:08:14.519
<v Speaker 2>is the exact same thing as code maintainability. Tests are code.

177
00:08:14.759 --> 00:08:17.680
<v Speaker 2>If your test code is an unreadable mess, it becomes

178
00:08:17.680 --> 00:08:21.560
<v Speaker 2>a massive cognitive burden for the team. The text highlights

179
00:08:21.600 --> 00:08:24.959
<v Speaker 2>this with a brilliant anti pattern the JS one K competition.

180
00:08:25.319 --> 00:08:28.439
<v Speaker 1>Oh this was wild. Explain what these hackers are doing?

181
00:08:28.800 --> 00:08:32.039
<v Speaker 2>In this competition, developers try to squeeze complex three D

182
00:08:32.159 --> 00:08:36.039
<v Speaker 2>animations into a JavaScript file that is a maximum of

183
00:08:36.080 --> 00:08:38.120
<v Speaker 2>one twenty four bytes.

184
00:08:37.879 --> 00:08:39.679
<v Speaker 1>That is tiny, it really is.

185
00:08:39.799 --> 00:08:43.279
<v Speaker 2>To make it fit, they use insane compression tricks. They

186
00:08:43.279 --> 00:08:47.039
<v Speaker 2>rename variables to single random letters, They strip out all

187
00:08:47.120 --> 00:08:49.440
<v Speaker 2>formatting spaces and line breaks.

188
00:08:49.360 --> 00:08:52.440
<v Speaker 1>And the result is what the author hilariously calls write

189
00:08:52.480 --> 00:08:53.440
<v Speaker 1>only code.

190
00:08:53.799 --> 00:08:56.200
<v Speaker 2>Yes, because you can write it in the moment, but

191
00:08:56.240 --> 00:08:58.399
<v Speaker 2>if you look at it a week later, it is

192
00:08:58.519 --> 00:09:01.919
<v Speaker 2>just an undecipherable wall of symbols. You could never maintain

193
00:09:01.960 --> 00:09:02.320
<v Speaker 2>it or.

194
00:09:02.320 --> 00:09:04.200
<v Speaker 1>Update it, right, It's just jibberish at that point.

195
00:09:04.320 --> 00:09:07.720
<v Speaker 2>Now, workplace code usually isn't that extreme, but studies show

196
00:09:07.759 --> 00:09:13.039
<v Speaker 2>a direct, strong correlation between poor readability and high defect density.

197
00:09:13.440 --> 00:09:15.759
<v Speaker 2>When a human brain has to expend massive amounts of

198
00:09:15.799 --> 00:09:18.000
<v Speaker 2>cognitive energy just to figure out what a block of

199
00:09:18.000 --> 00:09:20.759
<v Speaker 2>code is trying to do, mistakes happen, And if.

200
00:09:20.720 --> 00:09:23.120
<v Speaker 1>You're a manager onboarding a new developer, you're paying them

201
00:09:23.120 --> 00:09:26.200
<v Speaker 1>to sit there and decipher hieroglyphics instead of actually building

202
00:09:26.200 --> 00:09:29.399
<v Speaker 1>the product exactly. The author points out this is especially

203
00:09:29.440 --> 00:09:33.919
<v Speaker 1>toxic when it comes to automated tests, specifically calling out

204
00:09:33.919 --> 00:09:36.360
<v Speaker 1>the nightmare of the thirty minute monolithic test.

205
00:09:36.519 --> 00:09:41.120
<v Speaker 2>Yes, imagine you have a massive application and instead of

206
00:09:41.399 --> 00:09:45.320
<v Speaker 2>many small, targeted tests, you have one giant thirty minute

207
00:09:45.320 --> 00:09:48.399
<v Speaker 2>script that runs through everything at once. You make a

208
00:09:48.440 --> 00:09:51.679
<v Speaker 2>tiny change to how mailing addresses are formatted, and twenty

209
00:09:51.679 --> 00:09:54.000
<v Speaker 2>five minutes into the test, it fails.

210
00:09:54.279 --> 00:09:57.159
<v Speaker 1>So you have to dig through this unstructured monolith to

211
00:09:57.200 --> 00:10:00.799
<v Speaker 1>find exactly where your tiny change broke things. And then

212
00:10:00.799 --> 00:10:02.600
<v Speaker 1>you have to run the whole thirty minute test again

213
00:10:02.799 --> 00:10:04.000
<v Speaker 1>just to see if your fixed.

214
00:10:03.720 --> 00:10:07.519
<v Speaker 2>Work, which completely destroys the fast feedback loop we established earlier.

215
00:10:07.559 --> 00:10:09.360
<v Speaker 1>Wait, if a test is just too long, can I

216
00:10:09.440 --> 00:10:11.759
<v Speaker 1>just you know, chop it up into one hundred tiny,

217
00:10:11.799 --> 00:10:14.120
<v Speaker 1>separate files. Wouldn't that solve the monolith problem?

218
00:10:14.279 --> 00:10:16.879
<v Speaker 2>Arbitrary structure is honestly just as bad as no structure.

219
00:10:17.120 --> 00:10:19.879
<v Speaker 2>If you just chop a monolith up randomly, you aren't

220
00:10:19.879 --> 00:10:22.759
<v Speaker 2>fixing the problem. You're just spreading the mass across one

221
00:10:22.799 --> 00:10:26.000
<v Speaker 2>hundred different file I see, the boundaries of your tests

222
00:10:26.159 --> 00:10:29.799
<v Speaker 2>must map logically to the domain and to the developer's

223
00:10:29.840 --> 00:10:33.559
<v Speaker 2>mental model. If a test is chepping the shopping cart,

224
00:10:33.879 --> 00:10:36.440
<v Speaker 2>all the shopping cart logic needs to be structured together

225
00:10:36.720 --> 00:10:39.480
<v Speaker 2>in a way that makes sense. Otherwise you're sending the

226
00:10:39.519 --> 00:10:43.600
<v Speaker 2>next developer on a wild goose chase across fifty unrelated files.

227
00:10:44.080 --> 00:10:48.399
<v Speaker 1>A perfectly readable, well structured blueprint is great, but what

228
00:10:48.559 --> 00:10:52.480
<v Speaker 1>if the blueprint lies about the structural integrity. A beautifully

229
00:10:52.519 --> 00:10:56.360
<v Speaker 1>structured test is wonderful, but if that test lies to you,

230
00:10:56.759 --> 00:11:00.000
<v Speaker 1>it is vastly more dangerous than having no test at all.

231
00:11:00.120 --> 00:11:02.879
<v Speaker 2>False sets of security will sink a project faster than

232
00:11:02.919 --> 00:11:05.240
<v Speaker 2>anything else. We are moving from the structure of the

233
00:11:05.279 --> 00:11:07.679
<v Speaker 2>test to the integrity of its results, and the.

234
00:11:07.639 --> 00:11:10.559
<v Speaker 1>Book gives some terrifying examples of how tests can lie,

235
00:11:10.639 --> 00:11:12.519
<v Speaker 1>starting with something as simple as poor naming.

236
00:11:13.000 --> 00:11:15.200
<v Speaker 2>Yeah, the author talks about auditing a code base and

237
00:11:15.240 --> 00:11:17.759
<v Speaker 2>finding a test named to imply it was validating a

238
00:11:17.840 --> 00:11:22.080
<v Speaker 2>highly complex mask function for security. Sounds important, it does,

239
00:11:22.519 --> 00:11:25.240
<v Speaker 2>but when you look closely at the actual code, the

240
00:11:25.279 --> 00:11:28.320
<v Speaker 2>test was only checking if the parameters were a valid combination.

241
00:11:28.840 --> 00:11:31.600
<v Speaker 2>It wasn't testing the actual security behavior at all. If

242
00:11:31.639 --> 00:11:33.200
<v Speaker 2>you just looked at the test report, you'd think the

243
00:11:33.200 --> 00:11:37.320
<v Speaker 2>mask feature was secure when its core logic was completely untested.

244
00:11:37.720 --> 00:11:41.039
<v Speaker 1>Then there is the happy test. This happens when developers

245
00:11:41.039 --> 00:11:44.480
<v Speaker 1>are pressured by management to increase their code coverage metrics

246
00:11:44.559 --> 00:11:47.320
<v Speaker 1>like we talked about earlier. Tell us how the happy

247
00:11:47.399 --> 00:11:49.559
<v Speaker 1>test fakes success.

248
00:11:49.600 --> 00:11:52.960
<v Speaker 2>Well, They write a test that runs the production code,

249
00:11:53.279 --> 00:11:55.159
<v Speaker 2>but they wrap the whole thing in what's called a

250
00:11:55.200 --> 00:11:58.320
<v Speaker 2>trycatch block, and they don't write a single assertion.

251
00:11:58.519 --> 00:12:01.600
<v Speaker 1>Okay, for the non coder's listening, break down those two terms.

252
00:12:01.639 --> 00:12:04.039
<v Speaker 1>What is a tricatch block and what is an assertion?

253
00:12:04.679 --> 00:12:07.080
<v Speaker 2>Think of a tricatch block as a piece of code

254
00:12:07.120 --> 00:12:11.000
<v Speaker 2>that says, if this system crashes, just sweep the air

255
00:12:11.159 --> 00:12:13.759
<v Speaker 2>under the rug and act like nothing happened. It catches

256
00:12:13.759 --> 00:12:16.200
<v Speaker 2>the error before it fails the test and the assertion.

257
00:12:16.399 --> 00:12:18.720
<v Speaker 2>An assertion is the actual question you are asking. It says,

258
00:12:18.879 --> 00:12:22.200
<v Speaker 2>if I input two plus two, assert that the answer

259
00:12:22.240 --> 00:12:25.639
<v Speaker 2>is for A happy test runs the math, sweeps any

260
00:12:25.639 --> 00:12:28.399
<v Speaker 2>crashes under the rug and never actually asks what the

261
00:12:28.440 --> 00:12:29.120
<v Speaker 2>answer is for.

262
00:12:29.519 --> 00:12:32.759
<v Speaker 1>So it looks beautiful on paper. The boss sees ninety

263
00:12:32.799 --> 00:12:36.159
<v Speaker 1>nine percent coverage and is thrilled, But the test will

264
00:12:36.200 --> 00:12:39.960
<v Speaker 1>only fail if the server literally catches fire. It completely

265
00:12:39.960 --> 00:12:42.080
<v Speaker 1>ignores whether the logic is actually correct.

266
00:12:42.279 --> 00:12:45.960
<v Speaker 2>It's pure noise designed to satisfy a metric. Then you

267
00:12:46.039 --> 00:12:48.960
<v Speaker 2>have the issue of interdependent tests, which is a massive

268
00:12:49.000 --> 00:12:52.759
<v Speaker 2>trust breaker. Tests often secretly rely on the environment. Oh so,

269
00:12:53.279 --> 00:12:55.279
<v Speaker 2>let's say you have tests A which creates a user

270
00:12:55.320 --> 00:12:58.879
<v Speaker 2>in a database, and test B, which checks that user's permissions.

271
00:12:59.120 --> 00:13:01.600
<v Speaker 2>They run fine in that order. But let's say the

272
00:13:01.639 --> 00:13:04.759
<v Speaker 2>test framework decides to run them in alphabetical order. Oh no,

273
00:13:05.000 --> 00:13:07.559
<v Speaker 2>Test B runs first, tries to check a user that

274
00:13:07.600 --> 00:13:09.600
<v Speaker 2>hasn't been created yet, and fails.

275
00:13:09.440 --> 00:13:12.840
<v Speaker 1>A massive false alarm. The code itself isn't broken, the

276
00:13:12.919 --> 00:13:14.759
<v Speaker 1>environment is just flaky exactly.

277
00:13:15.000 --> 00:13:17.799
<v Speaker 2>The book mentions a real world example where Java seven

278
00:13:17.879 --> 00:13:21.360
<v Speaker 2>updated its internal plumbing. It changed the default sorting order

279
00:13:21.360 --> 00:13:25.480
<v Speaker 2>of how methods were returned, something called method reflection. Suddenly,

280
00:13:25.679 --> 00:13:27.879
<v Speaker 2>massive test sites broke everywhere.

281
00:13:27.440 --> 00:13:28.759
<v Speaker 1>Just because of an internal update.

282
00:13:29.720 --> 00:13:32.440
<v Speaker 2>The production code was fine, but the tests were secretly

283
00:13:32.480 --> 00:13:34.840
<v Speaker 2>relying on the assumption that things would always be returned

284
00:13:34.840 --> 00:13:35.559
<v Speaker 2>in the old order.

285
00:13:35.799 --> 00:13:38.639
<v Speaker 1>And speaking of assumptions, the author goes on a tear

286
00:13:38.759 --> 00:13:42.399
<v Speaker 1>about relying on the system clock or network connections, and

287
00:13:42.480 --> 00:13:46.320
<v Speaker 1>tests using commands to check the real time, like system

288
00:13:46.360 --> 00:13:47.720
<v Speaker 1>dot current time millis.

289
00:13:47.960 --> 00:13:51.919
<v Speaker 2>Using real time in a test guarantees intermittent failures. If

290
00:13:51.960 --> 00:13:55.279
<v Speaker 2>your test calculates a route based on current traffic, it

291
00:13:55.399 --> 00:13:58.639
<v Speaker 2>might pass at two point zero pm but fail at

292
00:13:58.679 --> 00:14:00.600
<v Speaker 2>five zero pm during rush hour.

293
00:14:00.879 --> 00:14:04.360
<v Speaker 3>That sounds infuriating, it is It forces developers into this

294
00:14:04.440 --> 00:14:07.399
<v Speaker 3>horrible habit of running a test suite four or five

295
00:14:07.480 --> 00:14:10.679
<v Speaker 3>times until they get lucky and it randomly passes just

296
00:14:10.679 --> 00:14:11.480
<v Speaker 3>to get a green light.

297
00:14:11.840 --> 00:14:13.919
<v Speaker 1>So what does this all mean? If my tests are

298
00:14:14.000 --> 00:14:16.639
<v Speaker 1>randomly failing fifty percent of the time because of an

299
00:14:16.799 --> 00:14:20.399
<v Speaker 1>unpredictable clock or a dropped network connection, how on earth

300
00:14:20.399 --> 00:14:23.919
<v Speaker 1>am I supposed to test time sensitive or internet reliant code.

301
00:14:24.399 --> 00:14:27.519
<v Speaker 2>That is the million dollar question for any software engineer.

302
00:14:27.720 --> 00:14:30.840
<v Speaker 2>To test the unpredictable, we have to completely isolate the

303
00:14:30.879 --> 00:14:34.240
<v Speaker 2>code from its unpredictable environment. This brings us to the

304
00:14:34.279 --> 00:14:36.279
<v Speaker 2>developer's secret weapon, test doubles.

305
00:14:36.559 --> 00:14:39.480
<v Speaker 1>The text actually quotes Mahatma Gandhi here, which I was

306
00:14:39.519 --> 00:14:43.200
<v Speaker 1>definitely not expecting in a software engineering book. Be the

307
00:14:43.320 --> 00:14:45.039
<v Speaker 1>change you want to see in the world.

308
00:14:45.840 --> 00:14:49.720
<v Speaker 2>Surprisingly fitting in testing, you become the change you want

309
00:14:49.759 --> 00:14:53.759
<v Speaker 2>to see in your code. By replacing external, unreliable dependencies

310
00:14:53.960 --> 00:14:58.759
<v Speaker 2>with controlled, predictable environments, you swap out the messy real

311
00:14:58.799 --> 00:14:59.919
<v Speaker 2>world for a test.

312
00:15:00.679 --> 00:15:03.600
<v Speaker 1>The book outlines five distinct reasons you would want to

313
00:15:03.679 --> 00:15:06.360
<v Speaker 1>use a double The first is isolating the code so

314
00:15:06.399 --> 00:15:09.159
<v Speaker 1>it's not dragged down by outside factors. The second is

315
00:15:09.240 --> 00:15:10.480
<v Speaker 1>speeding up execution.

316
00:15:10.720 --> 00:15:13.159
<v Speaker 2>Speed is so important, right, say you have.

317
00:15:13.120 --> 00:15:15.639
<v Speaker 1>A piece of code that runs a really slow weighted

318
00:15:15.720 --> 00:15:18.879
<v Speaker 1>graph routing algorithm. If every single test has to wait

319
00:15:18.879 --> 00:15:22.639
<v Speaker 1>for a complex GPS calculation, your test suite will take hours.

320
00:15:23.039 --> 00:15:26.279
<v Speaker 1>You use a double to instantly return a pre calculated route.

321
00:15:26.039 --> 00:15:29.240
<v Speaker 2>Because speed is crucial for that fast feedback loop. The

322
00:15:29.279 --> 00:15:33.159
<v Speaker 2>third reason is making things deterministic. Think of a craps game.

323
00:15:33.440 --> 00:15:36.639
<v Speaker 2>If the dice are truly random, you can't write a

324
00:15:36.679 --> 00:15:39.120
<v Speaker 2>test for what happens when a player rolls snake eyes

325
00:15:39.440 --> 00:15:41.360
<v Speaker 2>because you might have to run the test one hundred

326
00:15:41.399 --> 00:15:44.799
<v Speaker 2>times to get that specific roll. A test double lets

327
00:15:44.840 --> 00:15:48.320
<v Speaker 2>you rig the dice to roll snake eyes on command.

328
00:15:48.039 --> 00:15:52.120
<v Speaker 1>Which naturally leads into the fourth reason, simulating impossible conditions.

329
00:15:52.840 --> 00:15:55.440
<v Speaker 1>There's a hilarious anecdote in the text about trying to

330
00:15:55.480 --> 00:15:58.360
<v Speaker 1>test how an app handles a dropped Internet connection.

331
00:15:58.639 --> 00:16:02.440
<v Speaker 2>Oh yeah, short of programming a Lego Mindstorm's robot to

332
00:16:02.480 --> 00:16:06.159
<v Speaker 2>physically drive over and unplug your computer's network cable at

333
00:16:06.200 --> 00:16:07.159
<v Speaker 2>the exact right.

334
00:16:07.080 --> 00:16:09.039
<v Speaker 1>Millisec which would be amazing to watch but.

335
00:16:09.360 --> 00:16:12.559
<v Speaker 2>Very impractical, Yeah, it is nearly impossible to automate a

336
00:16:12.600 --> 00:16:16.519
<v Speaker 2>dropped connection test in the real physical world. A test

337
00:16:16.559 --> 00:16:20.200
<v Speaker 2>double lets you simulate that network failure perfectly in software

338
00:16:20.480 --> 00:16:21.399
<v Speaker 2>every single time.

339
00:16:21.440 --> 00:16:24.960
<v Speaker 1>And the fifth reason is exposing hidden information, letting a

340
00:16:25.000 --> 00:16:28.960
<v Speaker 1>test check internal states without ruining the actual code design

341
00:16:29.120 --> 00:16:32.080
<v Speaker 1>to get to it. This whole concept makes total sense.

342
00:16:32.320 --> 00:16:34.679
<v Speaker 1>If you're shooting an action movie, you don't throw your

343
00:16:34.720 --> 00:16:35.600
<v Speaker 1>lead actor out.

344
00:16:35.440 --> 00:16:36.679
<v Speaker 2>Of a helicopter I hope not.

345
00:16:37.000 --> 00:16:39.600
<v Speaker 1>You use a stunt double. The camera gets the shot,

346
00:16:39.879 --> 00:16:43.799
<v Speaker 1>the story moves forward, but nobody actually dies exactly.

347
00:16:44.080 --> 00:16:47.039
<v Speaker 2>You get the validation without the real world risk or delay.

348
00:16:47.639 --> 00:16:50.600
<v Speaker 2>But just like in movies, a car chase needs a

349
00:16:50.639 --> 00:16:53.159
<v Speaker 2>different type of stunt double than a martial arts.

350
00:16:52.960 --> 00:16:55.720
<v Speaker 1>Fight scene, which brings us to the specific taxonomy of

351
00:16:55.759 --> 00:16:58.519
<v Speaker 1>test doubles based on what the developer needs to achieve.

352
00:16:59.159 --> 00:17:03.120
<v Speaker 1>Let's go through the cast of characters, starting with test stubs.

353
00:17:03.440 --> 00:17:06.920
<v Speaker 2>A test stub is the simplest form. As the dictionary

354
00:17:06.920 --> 00:17:09.839
<v Speaker 2>definition of the book says, it's an unusually short thing.

355
00:17:10.359 --> 00:17:12.960
<v Speaker 2>It stands in with the simplest possible implementation.

356
00:17:13.200 --> 00:17:14.039
<v Speaker 1>Give us an example.

357
00:17:14.160 --> 00:17:16.240
<v Speaker 2>For example, if your code is supposed to log an

358
00:17:16.240 --> 00:17:19.119
<v Speaker 2>event to a remote server, you might use a stublogger

359
00:17:19.160 --> 00:17:22.480
<v Speaker 2>that just instantly returns a hard coded warning message and

360
00:17:22.519 --> 00:17:26.599
<v Speaker 2>does absolutely nothing else. It satisfies the code's request without

361
00:17:26.640 --> 00:17:28.599
<v Speaker 2>triggering a massive network operation.

362
00:17:28.920 --> 00:17:31.359
<v Speaker 1>Okay, next flavor up fake objects.

363
00:17:31.480 --> 00:17:33.839
<v Speaker 2>A fake object is a step up because it actually

364
00:17:33.960 --> 00:17:36.880
<v Speaker 2>contains real logic, but it operates without the real world

365
00:17:36.920 --> 00:17:40.599
<v Speaker 2>side effects. A great example is an in memory user repository.

366
00:17:40.880 --> 00:17:42.240
<v Speaker 1>How does that differ from a stub?

367
00:17:42.400 --> 00:17:45.599
<v Speaker 2>Instead of connecting to a heavy, slow, real world database

368
00:17:45.640 --> 00:17:49.160
<v Speaker 2>to save a user, the fake object just saves the

369
00:17:49.240 --> 00:17:52.519
<v Speaker 2>user data to a simple list in the computer's temporary memory.

370
00:17:53.079 --> 00:17:56.480
<v Speaker 2>It quacts like a database. It stores data like a database,

371
00:17:56.680 --> 00:17:59.880
<v Speaker 2>but it is vastly faster and doesn't require a network connection.

372
00:18:00.480 --> 00:18:03.559
<v Speaker 1>Love that third on the list. Test spies.

373
00:18:04.000 --> 00:18:06.480
<v Speaker 2>Test spies are used in tricky situations where a method

374
00:18:06.480 --> 00:18:08.839
<v Speaker 2>doesn't return to value you can check. Say you have

375
00:18:08.839 --> 00:18:11.680
<v Speaker 2>a distributed log that sends messages to multiple targets. Okay,

376
00:18:11.720 --> 00:18:13.799
<v Speaker 2>you can't just ask the log what it returned, so

377
00:18:13.880 --> 00:18:17.160
<v Speaker 2>you pass in a test spy. The spy silently records

378
00:18:17.200 --> 00:18:20.160
<v Speaker 2>every single message it receives. Then, after the code runs,

379
00:18:20.400 --> 00:18:22.960
<v Speaker 2>the test debreachs the spy and says what did you see?

380
00:18:23.000 --> 00:18:24.000
<v Speaker 2>Did you get the correct.

381
00:18:23.720 --> 00:18:27.200
<v Speaker 1>Message, which leads to the final and most strict flavor.

382
00:18:27.240 --> 00:18:30.799
<v Speaker 2>Mock objects mark objects are the ultimate control freaks of

383
00:18:30.799 --> 00:18:34.079
<v Speaker 2>the test double world. You program them with extreme precision

384
00:18:34.119 --> 00:18:37.000
<v Speaker 2>before the test even runs. For instance, you might create

385
00:18:37.039 --> 00:18:40.160
<v Speaker 2>a mock Internet object and you tell it expect exactly

386
00:18:40.200 --> 00:18:43.079
<v Speaker 2>one call to this specific method and expect these exact

387
00:18:43.160 --> 00:18:44.400
<v Speaker 2>specific parameters.

388
00:18:44.640 --> 00:18:47.880
<v Speaker 1>Here's where it's really interesting. So a stub is a

389
00:18:47.880 --> 00:18:51.400
<v Speaker 1>cardboard cutout, a spy is an undercover cop taking notes.

390
00:18:51.759 --> 00:18:55.480
<v Speaker 1>But a mock object is like a cyborg terminator. You

391
00:18:55.559 --> 00:18:57.960
<v Speaker 1>program its mission before you send it back in time.

392
00:18:58.079 --> 00:19:00.440
<v Speaker 1>You tell it John Connor will ask you for help

393
00:19:00.480 --> 00:19:01.680
<v Speaker 1>exactly one time.

394
00:19:01.799 --> 00:19:03.559
<v Speaker 2>I love the terminator analogy, right.

395
00:19:03.720 --> 00:19:07.599
<v Speaker 1>If John asks twice, or if Sarah Connor asks instead,

396
00:19:07.880 --> 00:19:10.839
<v Speaker 1>the mock instantly triggers an alarm and fails the test.

397
00:19:11.119 --> 00:19:15.519
<v Speaker 1>It enforces absolute rigid adherence to the expected.

398
00:19:14.960 --> 00:19:18.160
<v Speaker 2>Interaction that maps perfectly to the mechanism. If the code

399
00:19:18.160 --> 00:19:21.839
<v Speaker 2>calls that mock Internet object zero times, it instantly fails

400
00:19:21.880 --> 00:19:24.160
<v Speaker 2>the test. If it calls it two times, it fails.

401
00:19:24.279 --> 00:19:26.559
<v Speaker 2>If the parameters are off by one character, it fails.

402
00:19:26.759 --> 00:19:30.400
<v Speaker 2>It ensures the system behaves exactly is designed with zero deviation.

403
00:19:30.599 --> 00:19:32.319
<v Speaker 1>So to pull all of this together for the listener.

404
00:19:32.359 --> 00:19:34.759
<v Speaker 1>Writing good tests is not a chore. It is not

405
00:19:34.839 --> 00:19:37.200
<v Speaker 1>just about keeping the boss happy with a coverage metric.

406
00:19:37.440 --> 00:19:40.480
<v Speaker 1>It is a profound design exercise. It forces you to

407
00:19:40.480 --> 00:19:43.039
<v Speaker 1>think about how your code behaves in isolation, how it

408
00:19:43.079 --> 00:19:46.119
<v Speaker 1>handles the unpredictable, and how it communicates its intent clearly

409
00:19:46.160 --> 00:19:48.079
<v Speaker 1>to the next person who has to read it. Good

410
00:19:48.119 --> 00:19:50.839
<v Speaker 1>tests save time, they save money, and frankly, they save

411
00:19:50.839 --> 00:19:51.400
<v Speaker 1>your sanity.

412
00:19:51.640 --> 00:19:55.160
<v Speaker 2>If we connect this to the bigger picture, treating code

413
00:19:55.160 --> 00:19:59.160
<v Speaker 2>testing with this level of rigor fundamentally changes how you

414
00:19:59.279 --> 00:20:00.880
<v Speaker 2>view all common complex systems.

415
00:20:00.960 --> 00:20:01.680
<v Speaker 1>It really does.

416
00:20:02.000 --> 00:20:04.960
<v Speaker 2>When you realize how easily a system can be derailed

417
00:20:05.039 --> 00:20:08.799
<v Speaker 2>by a hidden dependency or a false assumption, you start

418
00:20:08.839 --> 00:20:13.599
<v Speaker 2>demanding better verification everywhere. You stop trusting the happy test

419
00:20:14.079 --> 00:20:17.279
<v Speaker 2>in your business processes, in your supply chains, and in

420
00:20:17.319 --> 00:20:20.599
<v Speaker 2>your organizational structures. You start looking for the empty method

421
00:20:20.640 --> 00:20:22.319
<v Speaker 2>stubs in your own workflows.

422
00:20:22.799 --> 00:20:24.720
<v Speaker 1>I want to leave you with a final thought to

423
00:20:24.839 --> 00:20:28.000
<v Speaker 1>mole over taking the source material just one step further

424
00:20:28.079 --> 00:20:31.759
<v Speaker 1>into everyday life. Think about applying the concept of test

425
00:20:31.799 --> 00:20:35.799
<v Speaker 1>driven development, defining the success criteria before you act to

426
00:20:35.839 --> 00:20:36.599
<v Speaker 1>your own life.

427
00:20:36.759 --> 00:20:38.400
<v Speaker 2>That's a fascinating way to look at it.

428
00:20:38.480 --> 00:20:41.400
<v Speaker 1>What if before starting a major home project, before sitting

429
00:20:41.400 --> 00:20:44.319
<v Speaker 1>down for a difficult conversation with a partner, or before

430
00:20:44.319 --> 00:20:47.920
<v Speaker 1>making a massive career decision, you first define the exact

431
00:20:47.960 --> 00:20:50.079
<v Speaker 1>tests that would prove you succeeded.

432
00:20:49.640 --> 00:20:51.759
<v Speaker 2>Writing the mole before you pour the concrete of your

433
00:20:51.759 --> 00:20:52.640
<v Speaker 2>life's next chapter.

434
00:20:53.119 --> 00:20:57.039
<v Speaker 1>How much accidental complexity, how much unnecessary drama and wasted

435
00:20:57.079 --> 00:20:59.720
<v Speaker 1>effort would you avoid if you knew exactly what passing

436
00:20:59.759 --> 00:21:02.680
<v Speaker 1>life like before you wrote the first line. At the

437
00:21:02.720 --> 00:21:05.000
<v Speaker 1>start of this deep dive, we talked about that broken

438
00:21:05.160 --> 00:21:08.359
<v Speaker 1>X ray machine, the murky diagnostic waters of trying to

439
00:21:08.400 --> 00:21:10.880
<v Speaker 1>fix a problem after it has already become a tangled mess.

440
00:21:11.279 --> 00:21:15.000
<v Speaker 1>TDD is essentially building a perfect, customized X ray machine

441
00:21:15.000 --> 00:21:17.799
<v Speaker 1>for a specific bone before you even take the step

442
00:21:17.839 --> 00:21:20.880
<v Speaker 1>that might cause the break. Keep asking questions, keep defining

443
00:21:20.920 --> 00:21:23.359
<v Speaker 1>your success criteria, and keep tuning in for the next

444
00:21:23.519 --> 00:21:24.039
<v Speaker 1>deep dive.
