elm radio
Tune in to the tools and techniques in the Elm ecosystem.
Incremental Steps
We talk about the value of taking small steps, and techniques to help break problems down in Elm.
Published
October 19, 2020
Episode
#15
Dillon's Elm Europe talk
Incremental Type Driven Development
Test driven development
Fake it till you make it
elm-markdown GitHub-Flavored Markdown table live stream
Succeed is the key to success
Practical refactoring
with Llewllyn Falco and Woody Zuill
Llewllyn Falco doing
Gilded Rose kata with TCR and snapshot testing
Dillon's article on
Relentless, Tiny Habits
Transcript
[00:00:00]
Hello Jeroen. Hello Dillon. How are you doing today? I'm doing quite well, how about you?
[00:00:06]
Doing good and today we're talking about one of my favorite topics and hopefully something that
[00:00:12]
people are practically bored of hearing me talk about. If that's the case then I've done my job.
[00:00:17]
So what are we going to talk about? Today we're talking about incremental steps. So this is really
[00:00:27]
a way of life in my mind. It's been something that I have been very influenced by this way of thinking,
[00:00:35]
working in tiny steps and I just find it so much nicer and I find that I'm so much more productive
[00:00:42]
when I use tiny steps. So I'm looking forward to diving into this topic. Yeah, we met last year at
[00:00:47]
Europe and you were doing a talk on this subject exactly. Yes. So I know you as this guy. Oh nice,
[00:00:55]
that's not a bad thing to be known as. I like it. It's definitely on brand for me. That and ElmGraphQL
[00:01:01]
all the time. Yeah. The more I know about you, the more it's incremental steps and that Elm pages
[00:01:10]
you can't stop talking about. That's true. That's true. The JAMstack has been a fun one too. But
[00:01:17]
yeah, incremental steps are huge if you will. Pardon the... Yeah, I had to. Okay, so can you
[00:01:25]
can explain what tiny steps are? Yeah, so to me a tiny step is a way of breaking down a larger problem
[00:01:36]
into a small action that makes visible progress, not theoretical progress but concrete progress.
[00:01:45]
So I think of it like a pulley that you know, I can't lift a car. That's actually not really true.
[00:01:58]
I actually can lift a car very easily if I use a pulley. If I have a pulley and I spread that work,
[00:02:05]
that force I need to apply over a large enough area, then I can very easily lift a car, right?
[00:02:11]
And to me, that's what the power of tiny steps are is I can take a problem that's too difficult
[00:02:17]
for me to solve, just flat out, okay, go solve the code. Go lift that car. Lift that car. I can't do
[00:02:26]
it. But if I break it down and stretch it over a larger area doing many, many tiny steps, then I
[00:02:32]
can do it with ease. So to me, that's what a tiny step is. Okay. So what does that look like in Elm
[00:02:40]
code for instance, or in programming in general? There are so many forms it can take. I think one
[00:02:47]
of the clearest forms of this idea is this concept in test driven development of fake it till you make
[00:02:53]
it. But as I sort of showed in that Elm Europe talk, you can apply fake it till you make it,
[00:02:59]
even if you're not doing test driven code. So like, I mean, there are many cases when I wouldn't,
[00:03:06]
for example, test drive, like writing my view code, like, I mean, you can use Elm program test
[00:03:12]
to do that sort of thing that I generally don't test drive my view code, because I sort of just
[00:03:18]
change it until it looks like I want it to. And that's, that's, that's usually fine for me. I
[00:03:24]
usually instead try to test the logic behind the view code, because that's the interesting part and
[00:03:29]
separate those out. But so like, you know, like I showed in that Elm Europe talk, without actually
[00:03:35]
writing tests, you can apply that same principle from test driven development of fake it till you
[00:03:40]
make it to to write your view with all the right values, that the values are completely faked out.
[00:03:46]
So you haven't done any logic to drive those values, but you know, they're the values you'd
[00:03:50]
like to have. So it's sort of like wishful thinking that you wish you had this interface to grab this
[00:03:57]
data, you make it so by just wishing it into existence, and faking it till you make it and, and
[00:04:05]
that allows so in that process, you have done nothing, right? You've done no logic, no difficult
[00:04:13]
work, but you've also done everything because you've completely wired it in, you've got working
[00:04:18]
code, you can look at it in the browser, you can make a change and see it reload. So you've done
[00:04:24]
none of the hard work. And yet you've done all of the sort of wiring and all of those things. So you
[00:04:31]
don't trip up on those details. And you have a point to iterate on.
[00:04:35]
Gotcha. Well, when you say fake it until you make it, you do need something that tells you whether
[00:04:40]
you made it or not. So in yes, in test driven developments, that is when the test the tests are
[00:04:47]
green. And when you what you showed in that Elm Europe talk is that you were using the compiler,
[00:04:54]
if the compiler tells you that it's that everything is okay, then you've made it.
[00:04:59]
Right? Yes, exactly. You need some sort of feedback loop. And I think in that talk, I was also using
[00:05:07]
live reloading as a type of feedback loop. So we sort of had the view, presenting the information
[00:05:13]
we wanted, we weren't actually looking at the JSON value to start. And then we started actually
[00:05:20]
pulling out that JSON value. So anything so there are many ways to, to break things down into
[00:05:26]
smaller problems like that. And I just find like, you can think of it as a sort of putting all the
[00:05:33]
risk upfront. Because you, you want to find out, if you're going in the wrong direction, you want
[00:05:39]
to find out earlier. And so taking smaller steps allows you your the goal is to get to working code
[00:05:46]
as soon as possible. And so there are a number of like techniques that then make this easier. Like
[00:05:52]
one technique is, you know, if you're processing a list, maybe you can have an empty list, or maybe
[00:05:58]
you can have a single item in that list, right?
[00:06:01]
Yeah, and it could be hard coded.
[00:06:03]
Exactly, you can hard code a value. I do this often with like, when I'm working on Elm parser stuff,
[00:06:10]
like Elm Markdown, I will start with, hmm, I really wish that this part of the parser that I'm
[00:06:17]
working on, you know, like if it's a GitHub flavored Markdown table, actually, we did a we did a live
[00:06:24]
stream where we built that initial code for that feature. And we used a lot of these techniques. So
[00:06:30]
you, you know, you say, Well, I wish that I had this data structure coming back from this parser.
[00:06:36]
When I go over this GitHub flavored Markdown table, I want to get this data so I can turn it into,
[00:06:43]
you know, the rendered Markdown, you know, the the destination, the goal, and you just sort of
[00:06:48]
short circuit getting there as fast as you can, by passing through a fake value. And then you've,
[00:06:53]
you've wired things through all the way. So like, you can't get feedback until you've wired things
[00:07:00]
through. So the goal is to have as short of a window of time, where you've cut off your feedback,
[00:07:08]
you want to be in a constant feedback cycle.
[00:07:10]
Yeah, the main idea I get from tiny steps is shorter feedback loops, the shorter, the quickest
[00:07:18]
you get to your feedback, the happier you will be, because you will be able to check whether your
[00:07:23]
assumptions are right, whether things are possible this way, you will notice problems early on, just
[00:07:30]
like when you're doing startup, like you do an MVP, but the smaller your MVP, the easier, the
[00:07:38]
quicker you will get your feedback. And exactly, the better you will be able to be flexible around
[00:07:46]
your choices and everything.
[00:07:48]
Yes, exactly. I completely agree. I think it's the exact same principles at play with, you know,
[00:07:56]
that sort of MVP thinking, it's just a way of shortening the feedback loop. And like, to me,
[00:08:01]
these, these are the physics of the software world, or maybe maybe not even just the software
[00:08:07]
world. If you, if you don't have feedback, then you don't know which direction to go. And so you
[00:08:12]
can, you know, I mean, it's like, if you're, if you're lost in the woods, and you just start
[00:08:18]
walking, you don't know if you're making progress, you could be getting more lost by walking.
[00:08:23]
Yeah.
[00:08:24]
But if you're lost in the woods, and you know that you need to get west somehow, and you have a
[00:08:31]
compass, then, you know, you might not go in the perfect route, but you at least have a sense of
[00:08:38]
whether you're going in the right direction. And that's what feedback loops give you, they give
[00:08:43]
you, it just feels like you're going blind, if you don't have a feedback loop, when you when you
[00:08:49]
build this habit and get used to working with tight feedback loops, then you feel totally blind
[00:08:55]
if you don't set them up. So like, the first thing that you want to do is set up a feedback loop,
[00:09:01]
because you just don't have the patience to like, go in the wrong direction. Like one place that I
[00:09:06]
see these short feedback loops at play is with when I'm helping people with Elm GraphQL questions.
[00:09:14]
The first thing I think of if somebody's like, I can't get these types to line up, which happens
[00:09:19]
often, right? I mean, it's, yeah, it can be overwhelming, like you're dealing with a lot of
[00:09:24]
different types, you have the, you have some phantom types, and you have to piece all these
[00:09:29]
things together. And, and people can get really stuck on just like a big compiler error, right?
[00:09:37]
And whenever I get a question, when somebody's in that situation, I always start with, how can we
[00:09:45]
break this down into a smaller step so you can get feedback? And actually, sometimes in those
[00:09:51]
situations, it's better to just revert to the last working state. Oh, yeah, yeah, exactly. Like, if
[00:09:58]
there's one thing that I've learned from this whole tiny step that you've shown me, like, if I
[00:10:05]
notice that I'm doing something that is too big, like, well, okay, the problem with doing things
[00:10:11]
that are too big, is that you have plenty of things in your head. And you need to do plenty of
[00:10:18]
things like, I need to add a message, I need to handle it in the update, I need to handle it in
[00:10:24]
the view, to call it in the view. And if you try to do that all at once, while doing a refactor,
[00:10:32]
yes, exactly, while doing a model change, like, you will get lost. At the end, you, the compiler
[00:10:39]
will help you out and all. So you will get there. But it will be much longer, and sometimes more,
[00:10:47]
way more confusing than if you did one thing at a time. If you did, yes, the refactor of the model,
[00:10:53]
as a one step, yes, you make it a compile, you make the test pass and everything. Then you add
[00:11:00]
that button, but it doesn't do anything that compiles the test pass, and so on and so on. Then
[00:11:09]
you do the message, the update, and so on. And every time that I find myself doing too many things
[00:11:16]
at once, often I will just delete what I just did, like, okay, revert and start one thing at a time.
[00:11:24]
And that is one reason why I try to commit more and more often now, so that can easily,
[00:11:31]
can revert back way easier. Exactly. Yeah, because when you get something that you know is working
[00:11:38]
and something that is almost working mixed together, then suddenly everything is effectively
[00:11:44]
not working. Yeah, yeah. But if you know something is working and you commit it,
[00:11:49]
then you have a point to fall back to if things go wrong. I use the Git gutters, the little sidebar
[00:11:56]
with the colors for the Git diffs in my editor all the time. I very heavily rely on that to
[00:12:02]
understand, like, okay, what are the things that I am changing? What could be causing a problem?
[00:12:08]
Because I know I was in a working state before I made a change. So with like Elm GraphQL,
[00:12:15]
when I'm helping people with these types of problems where they get stuck in too big of a step,
[00:12:19]
one technique I recommend to break down those problems is to just start with getting the type
[00:12:26]
annotation right for this thing you're passing in. Like, if you've got a function that takes like a
[00:12:32]
nested selection set, you know, it's like you've got a person, you're selecting some fields from a
[00:12:37]
person and you want to grab a couple of fields off of that person, then get the type annotations
[00:12:43]
correct. And you can even use, you know, selection set dot succeed, which you can annotate with any
[00:12:51]
type annotation. So once you do that, now you know the problem you need to solve. It's this one small
[00:12:58]
problem. I need to build up something of this type, which is not such an overwhelming problem.
[00:13:04]
You know exactly what the goal is. You know if you're making progress towards it or not. You've
[00:13:07]
put in a type annotation on this smaller piece, and therefore the compiler is going to give you
[00:13:13]
even more fine grained feedback on that small part that you've labeled with the annotation.
[00:13:18]
And so suddenly, now it may be that the problem was somewhere else, or there are other problems
[00:13:23]
you have to fix, which is also good feedback. But you want to find a narrow thing to solve and hone
[00:13:30]
in on that and just follow that path through until it's fixed instead of like working on 10
[00:13:35]
different problems at once. This is another thing I see often, you know, just, you know,
[00:13:39]
like you were saying, having like you're changing the way you model some data, your model changes,
[00:13:46]
you're wiring in a new message, you're changing the view, you're doing all these different things
[00:13:51]
at the same time. It's like when you get used to this way of working in tiny steps,
[00:13:57]
all of that sounds extremely stressful. Yeah, it requires a bit of discipline not to do
[00:14:04]
too many things at once. But if you get in the habit of doing things in tiny steps, like, okay,
[00:14:11]
that discipline is just asking you to hold off for like a minute or 30 seconds. Stash everything,
[00:14:19]
stash everything, rename that model field, commit, then unstash and you're back to where you wanted
[00:14:29]
to start this whole refactor or something. Exactly. A new feature. And you know, one tiny step that
[00:14:37]
maybe doesn't get the credit it deserves is writing something down in your to do list.
[00:14:45]
If something comes up, you're like, I want to refactor this, I want to, you know, like,
[00:14:50]
change this value in the model, I want to wire in this message, just have a to do list of the things
[00:14:56]
that you're doing in the next 10 minutes or whatever, that's fine. Like, don't just start
[00:15:01]
writing the code, just write it in your list, and you'll get to that next. But don't start it until
[00:15:07]
you finish the thing you're doing right now. Yeah, the good thing with the compilers,
[00:15:11]
it tells you what you need to fix. So if you do all the changes, if you start all the changes at once,
[00:15:18]
that the compiler is your to do list. But if you do tiny steps, then you can't use a compiler as a
[00:15:28]
weird kind of to do list. And yeah, to the list will then be a better to do list than the compiler
[00:15:34]
to do list. Right, right. So one technique that I think is worth talking about here, starting with
[00:15:41]
a fake value is a really good starting point. But then how do you take that fake value and turn it
[00:15:48]
into a real value? And there are a lot of techniques that help with that. So I really like
[00:15:54]
this technique of separation, Luell and Falco calls it separation, I think, kind of pulling apart
[00:16:00]
these fake values and splitting them up into multiple fake values, like taking a string and
[00:16:05]
then separating that string into two strings that are concatenated. That's like a tiny step that
[00:16:12]
allows you to take this big hard coded thing and turn it into two smaller hard coded things. And
[00:16:19]
that gives you a sort of insertion point where that you can remove some of that hard coding. So
[00:16:24]
once you have a fake value, then you want to take tiny steps to make it real values. But you don't
[00:16:32]
just put in a fake value and then replace that with a working implementation where everything is
[00:16:38]
there. So if you run through this example, like for instance, you have someone on your page,
[00:16:44]
you have a text that says, Hello, Dillon. Mm hmm. Mm hmm. So you start by hard coding, Hello, Dillon.
[00:16:51]
Yes. What you then do is like HTML that text. Yeah. Mm hmm. Hello, Dillon. So what you then do
[00:16:59]
is you concatenate Hello and Dillon. So one thing that you will need to do then is probably put
[00:17:05]
parents around it. Otherwise, it will not compile. Mm hmm. Yes. Something that you would have run
[00:17:11]
into later as a compiler, but that you could fix right now with just this
[00:17:18]
very tiny step. Mm hmm. Yes. And then what you and I would even do a little step in between there,
[00:17:25]
which is I would commit that. Yeah, yeah. I know it seems I know it seems really extreme, but I
[00:17:31]
really do make very small commits. Well, you know, the the best benefit of tiny commits is that very
[00:17:40]
nice commit contribution graph on GitHub. I know. I know. It's true. Yeah. My repos all.
[00:17:47]
You made 150 commits today. Wow. He's so active. Exactly. My repos really have like massive
[00:17:55]
commit counts on them. It's funny. Yeah. Yeah. So you separate those two in Helen. Hello, plus Dillon.
[00:18:03]
You commit that. I'm not going to say commit every time, but yeah, then you can just run
[00:18:07]
it. I'm not going to say commit every time, but yeah, then you extract Dillon into a variable.
[00:18:13]
Yes, exactly. Lead variable or something. And then maybe you want to pull it out from the model.
[00:18:19]
So what you then do is you remove it, remove that lead variable and use model dot name.
[00:18:29]
And then we'll tell the compiler, tell you, oh, well, name is not a field in model. OK,
[00:18:34]
add that field and how you could hardcoded to Dillon. And then you need to get it through a
[00:18:43]
dynamic value somewhere. So maybe you need to decode it. So you will add that decoder.
[00:18:50]
Yes. And you can hardcode that with the decoder succeed because that's yep.
[00:18:55]
Succeed is the key to success. Exactly.
[00:18:58]
And then once you have that, you can probably just do decode dot string and then get it from a real
[00:19:05]
value somewhere. Yeah, that's right. At that point, you've pretty much wired it all the way
[00:19:10]
through. Yeah. So and all in all, we did what we did. One, we avoided one quoted tricky compiler
[00:19:19]
issue with text having two arguments or something. We added a field to the model. We decoded it.
[00:19:25]
And every step of the way was very easy. Whereas if we did that to start with, then
[00:19:33]
yeah, it would have been too much. This is the way I did it previously. Like, oh, I need a name.
[00:19:40]
OK, so let me add it to the model. Oh, now I need to get it from a value from the HTTP request. OK,
[00:19:49]
so I need to decode it. And then I do the whole update to do everything. And then I
[00:19:54]
display it. And yeah, when I'm there, it's done. But I didn't have a small feedback loop. So
[00:20:04]
I couldn't notice that something was wrong until I was in the middle of it. And as we said,
[00:20:11]
if you're already doing something like adding a new feature, you don't want to change everything
[00:20:17]
because your assumptions were proven wrong at some point. Exactly.
[00:20:22]
Exactly. It's all about validating the assumptions as fast as you can. And with a very straightforward
[00:20:28]
problem like taking something from a flag, decoding it, showing it in the view, that might sound
[00:20:37]
forced. And it might be. I mean, we would probably be fine. We would probably be able to do it just
[00:20:46]
as quickly and with just as little risk if we did it, you know, decoding it right away. But that's
[00:20:54]
not that's not why it's interesting. It's interesting when you're solving the really
[00:20:59]
difficult problems and you have that skill set. Now it's really powerful. So, you know, I was just
[00:21:05]
working this last week on a feature in Elm pages where I'm I'm getting rid of Webpack and doing
[00:21:14]
a new build process, which I'm very excited about. It's kind of intimidating, you know, it's like
[00:21:19]
it's a big change. And I mean, I can't even imagine just working in the dark for days and days and
[00:21:27]
days, like building all these different features like, oh, I'm going to need something that outputs
[00:21:33]
this HTML and uses this template for the HTML and then, you know, fetches this data and whatever.
[00:21:39]
It sounds like a nightmare to just be working in the dark. So I want something that works right
[00:21:45]
away and to just even know that I'm going to be able to wire these pieces through and the
[00:21:50]
architecture is going to fit like I need to know that right away. Otherwise, it's just terrifying.
[00:21:56]
So and that's that's what I did. I started with just getting a hard coded thing working end to end
[00:22:04]
as fast as I could. And I did that by just saying, OK, I'm I'm going to assume that there's one page.
[00:22:11]
I'm going to assume that I'm I'm, you know, doing a Webpack free build for a site with exactly one
[00:22:18]
page. And if it doesn't have that, then I'm going to fail. And I'm going to assume that there's not
[00:22:26]
an error. And I'm going to like make all these assumptions and just cut through this one path.
[00:22:31]
So instead of doing every case partway through, you want to do one case all the way through end
[00:22:38]
to end and see it work. Because if you don't if you don't do it end to end, you can't see it
[00:22:42]
working. So and if you start trying to handle every case, it's only slowing down your ability
[00:22:50]
to see it working end to end. And what you want to do is you want to see it working end to end as
[00:22:55]
soon as possible, which means just relentlessly focusing on that one case getting all the way
[00:23:00]
through. So so that's what I did. I got it all the way through, made as many assumptions as I could.
[00:23:06]
And and then I started iterating from there. And I had a test harness that I could iterate on and
[00:23:13]
and do those other cases. So with more difficult problems, it's so valuable to to have that skill
[00:23:22]
set of taking these tiny steps. Yeah. So I told you before this that this weekend, I was working
[00:23:29]
on some kind of type inference for Elm review. So yes, there are some rules that check whether
[00:23:35]
you have type annotations for your top level declarations and your lead variables. And just
[00:23:43]
told you, hey, you need to type annotation here. Okay, thanks. That's not very helpful.
[00:23:48]
So I started working on having those type annotations work be auto fixed so that you
[00:23:56]
don't have to do anything manually. And what I did was I started really small, like because I need to
[00:24:02]
infer the type of thing. So what I did was I will do the whole thing for if the variable is clearly
[00:24:11]
a string. Oh, cool. Yeah. So if I see a literal string as a no, okay, just add the type annotation
[00:24:19]
where it says that it is a string. Because for the built in type, you can't do that for numbers
[00:24:24]
necessarily, because you don't know if it's an inter float. But for a string literal, you can just
[00:24:29]
give that fix. Yeah. Well, you for number you could but you just say number. Yeah, although it's less
[00:24:37]
satisfying. It is less satisfying. But that's what you got to do. So yeah, I made the whole thing
[00:24:44]
work for strings. So cool. Once I had that I could already run it on some of our projects and it added
[00:24:51]
strings everywhere. That's amazing. And that's and you've both done no work and a lot of work. Yeah,
[00:24:58]
exactly. That's a great example of that. And then yeah, I handle numbers, I handle the type
[00:25:03]
annotation, I handle hex literals, I handle parens, I handle binary operations, I handle lookups to
[00:25:11]
other variables. And all these things, they how do you say that they use each other? Yes, they build
[00:25:19]
off of each other. Yeah, exactly. Right. So every step is quite simple. Yes. And then very nice thing
[00:25:27]
is that since it's easy to test, I write tests for each step. And now I have a huge test with just
[00:25:35]
for type inference. Right. Yeah. And you've got the wiring, you get to build all of the wiring,
[00:25:42]
which is not easy. Like building wiring is hard. But you do that for string, like for if it's a
[00:25:49]
literal string, then the type is string. Yeah. So the first step is to build the type. And then
[00:25:55]
you build the string. Yeah. So the first part might have been the hardest. It's not, but it might
[00:26:01]
have been. And what is also nice is that I can already use this. I can already add type
[00:26:11]
annotations for plenty of things and just don't for all the things that are too complex yet or
[00:26:16]
not handled yet. So I can already see whether there are errors and I can go fix those. If I did
[00:26:23]
it the other way where I try to build the whole type inference things first. Well,
[00:26:31]
I would not be as happy today. I would still not have anything. And I actually wouldn't know
[00:26:39]
if I'm any closer to the destination. Exactly. And I guess that finding bugs. You'd be lost in the
[00:26:45]
woods without a compass. Yeah, exactly. And whenever people tell me, oh, you have a bug,
[00:26:50]
then yeah, I guess somewhere I need to find it, but now I can detect bugs as I go. Yes.
[00:26:57]
By trying it out. That's an amazing example. I love that. There's also one thing that you get,
[00:27:05]
even with very tiny steps where you hard create a thing. Like, you know, when people say,
[00:27:11]
when it compiles, it works, you feel happy when that happens. There's a tiny bit of dope and
[00:27:17]
new rush or whatever. But if you multiply the number of times that thing succeeds,
[00:27:24]
you have like, I won, I won, I won, I won, I won, I won. Yes. So many times that it's just like,
[00:27:31]
I always feel happy. Yes, exactly. It's true. I mean, there is a stress level when you've got
[00:27:39]
something up in the air and you don't know if it's going to be working like that feeling of like,
[00:27:43]
oh, come on, please compile, please compile. That goes away. And it becomes like, okay,
[00:27:51]
it is compiling. And then you do a small step and you're like, you do a tiny step and you're like,
[00:27:55]
oh, I hope this compiles. Oh, it didn't. Okay. I'll just go back to my starting point or revert
[00:28:00]
or whatever, you know, try again. Also, every time you save your file,
[00:28:05]
you get Elm format to format everything. So your code is more readable at every step.
[00:28:11]
So that's also one nice thing I found. Yeah. I think that working in this style of taking
[00:28:17]
tiny steps, it becomes harder to take small steps if you're not confident in the quality of your
[00:28:25]
code. So, you know, for example, if you, if you are working in code that has a lot of bugs,
[00:28:33]
it doesn't have tests, it has, you know, very large functions that don't have good data types.
[00:28:40]
And it's just difficult to work with. Then it's going to be hard to take tiny steps. So, and what
[00:28:46]
you're probably going to end up with in a situation like that is you're probably going to have a
[00:28:52]
manual testing process where, you know, eventually you end up with, you know, a testing team,
[00:28:58]
manual testing team, and they take a couple of days or a week to test the code. You have a code
[00:29:05]
freeze. And so now it's actually, it's the opposite of tiny changes. You make any change and you are
[00:29:14]
afraid of shipping it. And so you, you take a long time, your feedback cycle gets even longer because
[00:29:21]
not only can you not make tiny changes, but those tiny changes don't even get into the, into the
[00:29:27]
finished product until a few weeks later. So there's like a skill set around tiny changes and a set
[00:29:36]
of habits. And you can really learn those by, I mean, I think that practicing test driven development
[00:29:44]
doing Kata's is one of the best ways to learn those skills. And you really can learn them. But then
[00:29:51]
from the other, like the skills are like sort of on one side and on the other side are like sort of
[00:29:56]
the organizational pressures and processes. And you also need to approach it from that end and
[00:30:03]
make sure that you don't have things that are lengthening your feedback cycles. And it's all
[00:30:09]
about building in quality at every step because a tiny step doesn't mean, it doesn't mean getting
[00:30:17]
it compiling in one small step. That's one part of quality, but if it's compiling, but there's a bug,
[00:30:25]
the step is not done. It's not a step.
[00:30:27]
Unless it's a bug that you know and for which you have written a to do or...
[00:30:32]
Yes.
[00:30:33]
Like, like I'm not going to handle the error case. I'm not going to.
[00:30:37]
Exactly. That's right. So the process is you don't need to handle every case. So you don't need
[00:30:45]
completeness, but you need correctness for every case that you handle, it needs to be correct.
[00:30:50]
And that's, it's complete once it's correct. And what happens is the, I think maybe the more
[00:30:57]
intuitive way to work is just to work on several things at once and try to, try to get to completeness.
[00:31:05]
And so you're moving in all these areas, getting, getting things towards completeness
[00:31:11]
and working towards correctness at every step. And the shift is you get things correct very
[00:31:17]
frequently. It's just very small things.
[00:31:20]
Yeah. As you said before, like take one path, the path that you really want to work and make it
[00:31:27]
work from end to end. So don't go for completeness yet. Just go for this path and hard code or mock
[00:31:36]
or debug to do the other paths.
[00:31:39]
Right. So now that's, that's another interesting case, the debug.to do. So I do make use of
[00:31:46]
debug.to do and it's a very helpful tool, but let's talk about that a little bit. When is debug.to do
[00:31:52]
the right tool and when is it problematic? When does debug.to do get in your way? Do you have any
[00:32:00]
thoughts on that? I have some.
[00:32:01]
It's not a tool that I use as often as I think I should. I do find it more and more useful.
[00:32:09]
The thing with debug.to do is that it allows you to write whatever.
[00:32:13]
Yeah. Maybe we should define debug.to do.
[00:32:17]
Yeah. So debug.to do just crashes your application. In Elm 018 was called debug.crash.
[00:32:25]
You give it an error, a string, and when debug.to do will be encountered, it will just crash.
[00:32:34]
Right. And the type is any. It's basically Elm's version of the any type, except instead of just
[00:32:41]
saying that some actual value is any type, it just won't execute it.
[00:32:47]
Yeah. And that is kind of dangerous, but you can't use the optimized flag with Elm make.
[00:32:55]
So you will not see this in production, so it's safe.
[00:32:58]
Mm hmm.
[00:32:59]
So the nice thing about it is that it bypasses the compiler. So you can use it instead of any
[00:33:07]
function or any type, whatever. The downside of that is that you can use it instead of any type.
[00:33:15]
So when what you want to get is a good type, like you want to make sure that a type is correct and
[00:33:24]
will be useful and that you have all the information that you will need, then it might not be the
[00:33:29]
right idea. Because you're not validating one path. Like, oh, I can show an error if I enter this
[00:33:40]
error case, but maybe you can't because you don't have all the information needed yet.
[00:33:44]
And debug.to do will not give you that information.
[00:33:47]
Right. Yeah. One thing that debug.to do is very helpful for is, in that case, I was talking about
[00:33:56]
earlier where you're trying to figure out the right type for something in Elm GraphQL. Some
[00:34:02]
function takes some selection set of a particular type. Then you know I need to pass something in
[00:34:10]
here, but I don't know how to build that thing. And in that case, you can just pass in debug.to do.
[00:34:18]
And that actually answers an interesting question, which is, is there any type that would fulfill
[00:34:23]
the type system here? If it's not compiling when you pass in debug.to do error message as the
[00:34:30]
argument there, then there's nothing that will satisfy the type checker.
[00:34:34]
I've never had that experience yet.
[00:34:38]
Yeah. I really like doing that. Maybe I'll have a chain, a pipeline of functions that I'm applying
[00:34:49]
and I know I start out with this value. I know I need to eventually get it into this function of
[00:34:57]
a particular value, but then is there some way to transform it into this type of value? So maybe I
[00:35:04]
say list.map parentheses debug.to do error string. That answers the question for me. Is there any
[00:35:12]
function that I can pass into list.map in this pipeline here that will satisfy the type checker?
[00:35:18]
So now if it's compiling, the compiler has just answered that question. Yes, there is. There is
[00:35:24]
such a function. You could have a function that satisfies the type checker when you call list.map
[00:35:29]
on it. If it's not compiling, then hmm, what am I doing wrong here? Oh, it's not a list. It's
[00:35:36]
actually a result string list of something. So now I need to do result.map around that list.map
[00:35:46]
or something. And now, so now I do result.map parens list.map parens debug.to do string.
[00:35:54]
So you're basically using debug.to do to figure out, are there any type problems in other places?
[00:36:00]
You ignore the ones that are local to where you put the debug.to do, but you highlight the ones
[00:36:06]
that are somewhere else. Yeah. So it's just telling you like, does the connection fit? Do these jigsaw
[00:36:14]
puzzle pieces fit together here? And so at that point, what I would do is I would take that
[00:36:22]
function that I was using debug.to do for, I would expect it to be a function that would
[00:36:28]
using debug.to do for, I would extract that debug.to do into a little let binding. And then
[00:36:35]
I would try to get a type annotation for it. And now once I've got that type annotation, so it's a,
[00:36:42]
you know, a function from string to int. Now I know that the compiler is happy and now I'm going
[00:36:50]
to just return a hard coded int of course, right? Because now I've got a real value. So I've faked
[00:36:57]
it and I've got it working all the way through. So debug.to do can be really handy for those cases.
[00:37:04]
I think that it's important to, you know, you want to use debug.to do as a tool to sort of like,
[00:37:11]
you know, you're sort of bulldozing through a problem. And I don't, you know, you don't want
[00:37:16]
to keep the debug.to dos in there too long necessarily. I usually use them as a shorter term
[00:37:21]
tool. There are, you know, there are times when I'll sort of like ignore an error case or something
[00:37:27]
like that. But if you use debug.to do, for example, to, instead of having a result, because you
[00:37:34]
ignore the error case and you just say, if it's an error debug.to do, you're actually sort of
[00:37:40]
deferring dealing with this problem that you're going to have to deal with. And you don't have
[00:37:45]
working code that whole time. So you can't ship it. So you've just structured your code in a way
[00:37:51]
where, you know, I was using this distinction of complete versus correct code that your code
[00:37:58]
doesn't have to be complete, but it does have to be correct. Well, it's not exactly correct. It's
[00:38:04]
not shippable when it's in that state. So that's not quite in the spirit of tiny shippable steps,
[00:38:11]
you know, that's sort of the goal is that they're shippable quality steps and you can't ship it. So
[00:38:16]
you can get yourself stuck in this situation where you're working for a long time with code that's
[00:38:21]
not shippable. So that's something to be aware of. I think that debug.to do is a very useful tool
[00:38:26]
for figuring out what step to take, but then follow through that step. And when there's a
[00:38:30]
debug.to do, maybe this is a good rule of thumb. So you can use debug.to do for sort of exploring
[00:38:39]
if types line up, if you're like designing an API, that's more in explore mode. You're not like,
[00:38:44]
your goal is not to build code. Your goal is to like learn and you could even throw that away
[00:38:49]
when you're done, you know, or you can use debug.to do to identify a step and then you
[00:38:56]
follow that step through. So just be sure that you're using debug.to do, you know, you figure
[00:39:02]
out the path and then you go down that path. You don't like ignore it for a long time. That's to
[00:39:08]
me, that's not really, that doesn't feel right when I use debug.to do that way.
[00:39:12]
Yeah. I guess when I've done the end to end scenario work, I guess I would go for the debug.to
[00:39:19]
do afterwards because those are like places where you still don't know whether things will work out
[00:39:27]
or not. Those are known unknowns and it's best to go and figure those out.
[00:39:34]
Exactly. That's exactly it. That's exactly the mindset is like you want to,
[00:39:41]
you want to, anything that's a risk, you want to get out of the way as soon as possible.
[00:39:46]
And if it's a debug.to do, you're cutting yourself off from understanding whether it's going to work out.
[00:39:53]
You're deferring that feedback, you're lengthening the feedback loop. So it's really all about feedback loops.
[00:39:58]
This is a question I asked you after the evening where we met. How often do you need to write to dos?
[00:40:07]
Because you're writing a lot of dummy code, a lot of hard coded code when you do this.
[00:40:12]
How do you make sure that you don't forget it?
[00:40:16]
How do you make sure that you don't forget to handle it?
[00:40:19]
I write to dos all the time. When I'm coding, I've always got, I use bare notes. It's like a
[00:40:27]
markdown notes thing. And I just put in like to do items as they come up. And I work in
[00:40:34]
Pomodoros most of the time, which is like you've set a 25 minute timer and work with no distractions.
[00:40:40]
And then you take a five minute break after. And when I'm getting close to the end of my Pomodoro,
[00:40:45]
I'll often make sure that I take all of the context that I had and dump it out into notes.
[00:40:52]
So I'll write down all the to dos that I can think of, but I'm constantly writing to dos.
[00:40:57]
And you do that at the end. You don't do it every time you hard code something?
[00:41:04]
I do both. If something comes up and I'm like, oh, I need to do this, then I'll write it down.
[00:41:09]
But then sometimes I need to take a moment to sort of collect my thoughts and think about where I am
[00:41:14]
and say like, okay, what are the next steps here? And I usually try to set myself up for being able
[00:41:21]
to jump in and have context quickly when I start my next Pomodoro. And so I'll try to capture all
[00:41:28]
of those to dos and thoughts in notes before I lose that context and then set myself up to get
[00:41:34]
a good start on the next one. Gotcha. And do you also write what the next end to end
[00:41:40]
scenario you want to fulfill is? Like I want to have an add button. I want to have a delete button.
[00:41:47]
So you do the add button, you mark some things and then you do the delete button.
[00:41:52]
Yes, I do. I do that. I usually try to write the to dos in terms of like
[00:42:05]
features. Sometimes I'll have like a heading that will be like, have a delete button. And then,
[00:42:11]
what do I need to do to do that? And then I'll try to break it down into steps that I could do
[00:42:15]
incrementally in tiny steps. But I do try to choose words that reflect the end to end thing
[00:42:23]
rather than the implementation also. And then maybe I'll write a few notes on the implementation
[00:42:28]
if I want to capture a thought. As a developer, I want this button to use stories. Yeah.
[00:42:37]
Yeah, because you want to keep yourself honest about doing something shippable.
[00:42:42]
Yeah. And the words that you use can help guide you towards a shippable step and getting a smaller
[00:42:49]
path to getting something shippable. Yeah. Implement function name is not very useful.
[00:42:55]
Exactly. Yeah. Yes. And on the topic of debug.todo, I just wanted to share one last thought
[00:43:03]
there for anybody who's maybe new to debug.todo. It's okay to use debug.todo in your tests.
[00:43:10]
And it's okay to completely ignore certain cases and tests. That's fine. You don't need to compile
[00:43:17]
your test code with the optimize flag. You don't need to get rid of debug.todo in your test code.
[00:43:23]
If you have something in the test code where you expect to, you know, I don't know, you're
[00:43:31]
decoding some value that you're getting for your test setup and you expect the decoder to succeed.
[00:43:39]
You can just do debug.todo, you know. Yeah, definitely. To handle it. That's fine.
[00:43:45]
Yeah, because if you try to do it the Elm way, then you will probably end up with a test that
[00:43:54]
does nothing otherwise. Right. Or the test fails and says, you know, you have to wire in that test
[00:44:02]
failure, which would be fine, but you don't need to. You can just crash the test. It's okay. Yeah.
[00:44:06]
Debug.todo is a test failure in itself. Exactly. Elm review has a rule that says no debug.todo.
[00:44:15]
And in the documentation, I say it's okay to do it for tests. So just ignore this rule for tests.
[00:44:21]
Cool. Yeah. Yeah. Nice. Because it's very useful there. So I think there's one thing that we
[00:44:29]
haven't hit on. I mean, there's so much we could talk about here. I'm sure we could talk about
[00:44:34]
more, but there's one big thing that we haven't really gotten into too much. And we can touch on
[00:44:41]
it briefly here. I think it's probably a topic for another episode, and that is refactoring. So
[00:44:47]
I think that refactoring plays a large role in this. So sometimes you can't take a small step
[00:44:55]
because of the way the code is structured. So you need to refactor. Yeah. Therefore,
[00:44:59]
small steps and refactoring are connected. I often refer to this Kent Beck quote,
[00:45:07]
which I really like, which is, make the change easy, then make the easy change. And he says,
[00:45:12]
warning, the first part may be hard. I totally thought that this was from you.
[00:45:18]
I was so disappointed to learn that it wasn't. I used to try to credit Kent Beck pretty explicitly.
[00:45:24]
I thought it was yours. I was like, wow, that man has some words of wisdom.
[00:45:31]
And I know it's great. Oh yeah. I will ruthlessly steal any nuggets of wisdom.
[00:45:39]
I do try to credit them, but yeah, there's, I mean, all of these concepts, fake it till you
[00:45:45]
make it, short feedback loops. And I mean, we're standing on the shoulders of giants here. Kent
[00:45:51]
Beck really pioneered a lot of these ideas and it's pretty, I find his thinking on these things
[00:45:58]
very elegant. But yeah, refactoring is intimately connected to this concept of small steps.
[00:46:06]
And refactoring can itself be done in small steps. In fact, I think that often what people
[00:46:13]
use the word refactoring for is not actually refactoring. It's a rewrite, which can be fine
[00:46:20]
in some cases, but I think it's a difference. A refactoring is an atomic transformation
[00:46:30]
of the structure of the code without changing the behavior of the code. A rewrite is just
[00:46:38]
getting rid of some code and saying, okay, I'm going to re implement this.
[00:46:41]
They're two different things. So if you do a rename refactoring, then it's just like,
[00:46:47]
and you can think of it as one atomic thing, just boom. Now the name is this. If you extract
[00:46:53]
some function to a module, ideally it would be great to have, you know, IDE support to do that
[00:47:00]
in an automated way, but you can do that manually and you can, you can do that as a safe atomic
[00:47:05]
step. But also, you know, as you mentioned earlier, if you're like extracting a new module
[00:47:11]
while implementing another thing, just go ahead and extract the module first. It's, it's a habit
[00:47:17]
and I, I really encourage people to try that habit out to separate your behavior changing steps from
[00:47:24]
your structure changing steps and try to like, if you can refactoring is so nice in Elm and if you
[00:47:32]
can make a change with no concern that you're changing behavior at all, that's a huge win.
[00:47:38]
It's less to think about. You, you think about the refactoring and it's dead simple and then you make
[00:47:45]
the change and it's more simple than it would have been if you're trying to do both at the same time.
[00:47:49]
So to me, it's like just a very logical thing, thing to do. It's just hard to build that habit.
[00:47:57]
So I think maybe we'll leave refactoring as a teaser for a future episode and we can dive into
[00:48:03]
that more another time. I do feel like when you do tiny steps, you will feel the pain of bad
[00:48:10]
structures more easily. So you will notice when things need refactoring way more quickly.
[00:48:17]
Right. And as you, as you pointed out earlier too, it's sometimes you start trying to do a tiny step
[00:48:25]
and then you kind of get stuck because you're like, Oh, I, now I need this thing. I need this
[00:48:32]
code to be this way. I need it to take this argument, which I don't have, I, you know,
[00:48:36]
whatever. Well then that's fine. You get stash your changes, you wire in a new argument. You've now
[00:48:45]
made the change easy and now you can get stash pop and make the easy change. Now that you've got
[00:48:51]
the argument that you needed. Well, how should people get started trying out this technique?
[00:48:57]
Well, my main learning from this is to try and shorten your feedback loop.
[00:49:02]
And the biggest thing I've took out of it is try to make your code compile be the first thing,
[00:49:11]
be your priority. So when people say it's nice when you do a huge refactor or rewrites,
[00:49:20]
it lasts one hour and at the end it works when it compiles. That's great. But if you can make
[00:49:28]
the fact that it compiles your priority, you will not feel the pain of that one hour of rewriting.
[00:49:34]
Exactly. So yeah, don't do multiple things at once. Focus on one thing and focus on getting
[00:49:42]
compilation working and test passing. Yeah. Completely agree. I think that's,
[00:49:48]
if you learn one thing, that's probably one of those. Commit often. And
[00:49:55]
as you said before, you could learn a lot from Katas. So go watch Deniz's talk on incremental
[00:50:03]
steps or what was it again? Tiny? Type driven development.
[00:50:07]
Type driven development. Yeah. Or I thought he was going to talk about test driven development
[00:50:13]
because he said it was about TDD. And I basically did except using those principles
[00:50:19]
without the test part. Yeah. And there's also a very good talk about applying automated refactorings
[00:50:30]
from Llewellyn Falco. Yeah. He's got a lot of good talks.
[00:50:38]
Yeah. I really recommend watching those. Are you talking about practical refactoring?
[00:50:43]
Is that the talk with the two minute timer? Yeah.
[00:50:47]
I was thinking of the one from the Gilded Rose Kata.
[00:50:52]
Okay. That's an amazing one too. Actually, I'll put a link to this talk that Llewellyn
[00:51:00]
Falco and Woody Zewald gave about, it's called practical refactoring. That is an amazing talk.
[00:51:06]
It's so good. They take a two minute timer and they have two minutes to get the code
[00:51:12]
to a shippable state for every change they make. Oh, nice. I highly recommend that talk.
[00:51:19]
And I wanted to point one thing out. I could not agree more with everything you said about the core
[00:51:23]
idea and following one thing through, not having multiple things that you're working on at the same
[00:51:29]
time and noticing when you're doing an hour long refactoring. I just wanted to point out
[00:51:35]
for anyone who's new to this sort of approach, there are times when you're doing refactoring
[00:51:42]
that sort of take some time because you're wiring in a new argument and that's one small atomic
[00:51:47]
refactoring, but you have to change 20 places. Yeah. And that's okay. So.
[00:51:53]
Because you can't get it any smaller. Yeah. There's no way unless it's automated. And again,
[00:52:00]
these are the types of things that can be automated. Elm has all the ability to be
[00:52:06]
statically analyzed and automated in those ways. And I think that's a great point.
[00:52:10]
So you can't get it any smaller than that. And again, these are the types of things that can be
[00:52:15]
statically analyzed and automated in those ways. And those things will come. But for now, it's okay.
[00:52:21]
Just pay attention to it could be taking a while, but are you just doing one small refactoring or
[00:52:27]
are you doing a rewrite of all of the code? And how, and if so, how can you break out one small
[00:52:33]
piece, do that refactoring? It's kind of like the doing starting at the leaves of the tree
[00:52:40]
whack, but you want to start up at the leaves and then move towards the root. So yeah. And well,
[00:52:46]
I wrote an article recently called relentless tiny habits. So you might want to give that a read.
[00:52:52]
It kind of talks about some of the principles we've been talking about and sort of how you
[00:52:57]
build those habits into your way of working and how much that relates to taking tiny steps
[00:53:04]
is not something you just do. It's a habit that you continuously build. Yeah. Anything else to
[00:53:10]
point people to? I think that's quite a lot of resources already. I think so. I think so.
[00:53:17]
This takes, this will take a lot of practice. Like even I am doing more and more tiny steps,
[00:53:22]
but I still don't go end to end. And that is something that I need to practice for instance.
[00:53:29]
So yeah, a lot of habits. Yeah. And they are habits and they are as with any habit.
[00:53:34]
If you do that step one time, then you've started a habit and you just need to keep doing that.
[00:53:42]
Also, as with any other habit, you know, you can, you can get better or worse over time. So
[00:53:48]
it, it's a continual learning process. It's something that requires constant, constant care
[00:53:54]
to take care of that habit. But code cadas are a great way to keep that up. I think setting aside
[00:54:01]
some deliberate time where you say, I'm going to work in this way without the pressure of
[00:54:08]
production code. I think that can be very helpful. You know, you can apply these ideas to side
[00:54:13]
projects too. I think you and I would, would both share that experience of learning new techniques
[00:54:19]
by sort of trying to be our best selves in our, in working on a side project. Yeah, definitely.
[00:54:25]
So that's another way, but I, I would recommend employers or, you know, developers to set aside
[00:54:32]
some time and give, give developers some time to do, you know, to practice, to try things out, to,
[00:54:38]
to do exercises and cadas. I think that's a great way to learn these habits. Yeah. With a focus on
[00:54:44]
what you want to learn. Yes, exactly. Short feedback loop. That's not just how you write good
[00:54:50]
code. That's how you learn too. Yep. All right. Well, this one was a lot of fun and don't forget
[00:54:59]
to review us on Apple podcasts, follow us on Twitter and thanks for listening. Yeah. And if
[00:55:06]
you have a question, go to elmradio.com. Yes. elm dash radio.com slash question, or just elm
[00:55:13]
dash radio.com and click the question button. Yep. And Dillon, see you next time. See you next time.
[00:55:19]
Take care.