spotifyovercastrssapple-podcasts

API Design Lessons

We share what we've learned from designing Elm APIs and how it applies when building applications and tools.
July 5, 2021
#34

Lessons

  1. Avoid unexpected or silent behavior
  2. Give context/feedback when things go wrong so the user knows their change was registered, to enhance trust
  3. Good errors aren't just for beginners - Curb Cut Effect
  4. Sandi metz - code has a tendency to be duplicated - be a good role model - we're influenced by precedence
  5. Matt Griffith - API design is holistic. It's a problem domain. Rethink from the ground up.
  6. Learn from the domain and terms, but don't limit yourself to it when you can create better abstractions.
  7. Linus Torvalds' definition of elegance/good taste - recognize two code paths as one. Reduce the number of concepts in your API when you can treat two things as one, then things compose more easily. How Elm Code Tends Towards Simplicity.
  8. You don't need a direct mapping of your domain, but start with the spec and terms. Leverage existing concepts, and have an economy of concepts. Tereza's talk: elm-plot The Big Picture
  9. API design is a tool to help you solve problems.
  10. There's a qualitative difference when you wire up feedback before you up front.
  11. Avoid toy examples, use meaningful use cases to direct your design.
  12. Design for concrete use cases, and drive changes through feedback from concrete use cases. Legal standing. Better to do it right than to do it right now Evan's concept from the Elm philosophy. If you don't have a motivating use case, then wait. Extract APIs from real world code. It's okay for there to be duplication. Premature abstraction is the root of all evil. sSmplicity is the best thing you can do to anticipate future API design needs.
  13. Come up with an API with the most benefits and the least pain points.
  14. If there's something that you want to make really good, invest in giving it a good feedback mechanism.
  15. Rich Hickey's talk Hammock Driven Development. We don't design APIs, our extremely creative subconscious designs APIs - let your conscious brain do the hard work to put all the information in front of your subconscious so it can do what it does best. elm-pages 2.0 screencast with Jeroen and Dillon.
  16. Pay no attention to the man behind the curtain. Parse, Don't validate at the high level, but under the hood you may need a low level implementation.
  17. Have a clear message/purpose - whether it's an API, or an internal module.
  18. Take responsibility for user experiences.

Transcript

[00:00:00]
Hello, Jeroen.
[00:00:02]
Hello, Dillon.
[00:00:03]
What are we talking about today?
[00:00:04]
Today we're talking about API design.
[00:00:06]
API design?
[00:00:08]
That's not a topic that we talk about.
[00:00:10]
Never, never.
[00:00:13]
So we figured we'd go out on a limb
[00:00:15]
and talk about something entirely different than usual.
[00:00:21]
Topics we know nothing about,
[00:00:22]
topics we don't care about.
[00:00:27]
I think this is going to be a fun one.
[00:00:29]
I think we're just going to share some stories
[00:00:33]
from our API design experience
[00:00:36]
and try to draw out some lessons learned from them.
[00:00:39]
And I think we're going to be building up
[00:00:42]
a little API design manifesto in the process.
[00:00:45]
So maybe we should...
[00:00:47]
I'm wondering if I should update
[00:00:49]
the idiomatic Elm package guide readme
[00:00:54]
because we'll see how it goes.
[00:00:56]
Does the manifesto say that you should use
[00:00:58]
the Elm package starter as well?
[00:01:00]
Oh, I haven't linked to that in there yet.
[00:01:02]
I totally should.
[00:01:04]
But hopefully this episode will be interesting
[00:01:07]
for library authors as well as application authors.
[00:01:11]
Because if there's one thing we've learned
[00:01:13]
from making this podcast, Jeroen,
[00:01:15]
it's that lessons about API design for library authors
[00:01:20]
are also very relevant to API design
[00:01:23]
for application designers.
[00:01:25]
Totally.
[00:01:26]
Because when you're writing an application,
[00:01:28]
you're in a way building plenty of smaller libraries
[00:01:31]
that you can post together
[00:01:32]
and that create your application.
[00:01:34]
So building tools and packages are very useful
[00:01:37]
for learning how to build your application.
[00:01:40]
Right.
[00:01:41]
Yeah, it's almost like designing...
[00:01:44]
Let's call them external APIs versus internal APIs.
[00:01:49]
And the process of building external APIs
[00:01:52]
is this highly condensed form
[00:01:54]
because basically you have more users,
[00:01:58]
more use cases you're supporting.
[00:02:00]
So you rapidly go through this design process
[00:02:03]
because you're getting a lot of feedback
[00:02:05]
at once in a short time.
[00:02:07]
Whereas internal API design,
[00:02:09]
you don't have to be as intentional about it.
[00:02:12]
But whether you know it or not,
[00:02:17]
the principles are going to help you out.
[00:02:20]
But you can just sort of write code
[00:02:23]
without having clear APIs.
[00:02:25]
Whereas when you build an external package,
[00:02:27]
you have to put that extra thought into it.
[00:02:30]
So it forces you to be more deliberate
[00:02:32]
about those design choices.
[00:02:34]
Yeah.
[00:02:35]
With an internal API,
[00:02:36]
there's also the scope.
[00:02:38]
You know exactly what you're building the library for
[00:02:41]
or more than from an external API.
[00:02:46]
The use case you're thinking of,
[00:02:48]
maybe a few others.
[00:02:49]
And there's always the question,
[00:02:51]
is that thing over there something
[00:02:54]
that I should support as well?
[00:02:56]
If so, does that change how I should design my API?
[00:02:59]
For an internal API,
[00:03:01]
you also have those kinds of questions.
[00:03:03]
For an internal API,
[00:03:04]
you also have those kinds of questions,
[00:03:06]
but less so, a tiny bit less so.
[00:03:09]
Yeah.
[00:03:10]
I mean, you can be fuzzy
[00:03:15]
about how a module solves
[00:03:17]
or a set of related modules.
[00:03:19]
And it's interesting because you can publish a package
[00:03:22]
that's a little bit fuzzy about that.
[00:03:24]
And you can also have modules
[00:03:27]
in an internal application
[00:03:29]
that are a little bit fuzzy
[00:03:30]
about what their purpose is,
[00:03:32]
what their reason for existence is.
[00:03:34]
And what happens a lot
[00:03:37]
in application design
[00:03:39]
is you kind of have all of these functions in line
[00:03:44]
together in this one module
[00:03:46]
as their reason for existing,
[00:03:49]
but they're sort of scattered all over,
[00:03:52]
and everything has to be concerned about them.
[00:03:54]
So the process of building external APIs
[00:03:58]
forces you to create
[00:04:01]
this condensed external cohesive set of things.
[00:04:05]
But really, those are good lessons to learn
[00:04:08]
for internal application design as well.
[00:04:13]
So do you have API design stories and lessons?
[00:04:16]
So there's one design lesson
[00:04:17]
which I don't think is necessarily all that obvious
[00:04:21]
or one that people point towards.
[00:04:23]
But unexpected behavior is something
[00:04:26]
that I try to avoid in my API.
[00:04:29]
Mm hmm.
[00:04:30]
Or silent behavior.
[00:04:32]
So when you use a function
[00:04:35]
and it does not what you expect it to,
[00:04:37]
or it unexpectedly does nothing,
[00:04:42]
that I think you should try to avoid
[00:04:45]
because that creates surprises.
[00:04:47]
Mm hmm.
[00:04:48]
And people don't like surprises.
[00:04:50]
People, when they are surprised by the API,
[00:04:52]
they will create issues.
[00:04:54]
They will create bug reports.
[00:04:55]
Right.
[00:04:56]
And that requires time for you to spend on support.
[00:05:00]
Yeah.
[00:05:01]
So that's one thing that I try to avoid in my APIs.
[00:05:04]
And I've done so pretty successfully, I think,
[00:05:09]
reports bugs in the API.
[00:05:11]
Uh huh.
[00:05:12]
So I'm happy about that.
[00:05:14]
So there's one, for instance, that I'm thinking about.
[00:05:17]
I'm going to talk about Elm Review mostly.
[00:05:20]
So do you know how to create an error in Elm Review?
[00:05:24]
Yeah.
[00:05:25]
There's the tuple of an error.
[00:05:28]
There's the tuple where you can do
[00:05:30]
sort of the equivalent of commands like we talked about,
[00:05:33]
which is giving an error
[00:05:38]
so you would return an error in that tuple
[00:05:41]
that's equivalent to the update function
[00:05:43]
in the Elm architecture.
[00:05:45]
Yeah.
[00:05:46]
So instead of returning a list of commands, or a command,
[00:05:49]
you return a list of errors.
[00:05:51]
And those errors, you can create them using several functions.
[00:05:54]
One of them is the error function.
[00:05:57]
So the error function, you pass it a title,
[00:06:00]
a message, and details, and a location,
[00:06:05]
for the current module.
[00:06:06]
There's also another one for the Elm JSON file,
[00:06:10]
and another one for the README.
[00:06:12]
And there's another function to create an error
[00:06:15]
for a different module,
[00:06:17]
using something like the navigation key,
[00:06:19]
where you say, oh, I have this navigation key
[00:06:21]
because this module key, because I have visited previously.
[00:06:25]
Oh, okay.
[00:06:26]
Now create an error for that module.
[00:06:31]
So if you haven't, you need proof of having visited a module
[00:06:35]
in order to mark an error on that module.
[00:06:37]
That's cool.
[00:06:38]
Yeah, so the reason why I did that is because
[00:06:41]
if people pass in just a path to a file,
[00:06:44]
or a module name, and it's not right,
[00:06:47]
then they will probably not see an error anywhere,
[00:06:50]
and things would fail silently.
[00:06:53]
But that is actually not what I wanted to talk about.
[00:06:58]
The F function creates an error for the current module
[00:07:01]
that you're visiting.
[00:07:02]
But you can create errors for the Elm JSON file,
[00:07:06]
or in the final evaluation, where you have seen all files,
[00:07:10]
and you are not in the context of an error anymore,
[00:07:13]
but where you can still create error.
[00:07:15]
So what happens if you call error?
[00:07:17]
Right.
[00:07:18]
There is no local module.
[00:07:21]
Right.
[00:07:22]
But the API, the types still say,
[00:07:27]
I have an error.
[00:07:28]
So if I didn't try to be very smart about that,
[00:07:30]
people could report issues because,
[00:07:33]
hey, I reported an error here, or I returned an error,
[00:07:36]
and nothing's happening.
[00:07:38]
So that was a bit tricky to solve,
[00:07:41]
and one of the thoughts I had is,
[00:07:43]
this is tricky, but I can determine this at runtime,
[00:07:48]
so I can detect this in tests.
[00:07:50]
So I'm going to, in my test checker,
[00:07:55]
I'm going to look at whether people call that function
[00:07:58]
outside of the module visitor,
[00:08:01]
and people are going to be happy
[00:08:03]
as long as they create a test for that.
[00:08:05]
And that would have worked,
[00:08:07]
but maybe I would have had those bug reports,
[00:08:09]
and if they didn't write a test,
[00:08:11]
then the user would not see what they were supposed to see.
[00:08:14]
So what I did is I added the Phantom type to the error,
[00:08:19]
to the error type,
[00:08:24]
and the ElmJSON returns an error,
[00:08:27]
a type error, empty record,
[00:08:29]
and all the other errors,
[00:08:31]
they return a type error with the unbound Phantom type.
[00:08:35]
So you can basically return them from wherever.
[00:08:38]
And in the final evaluation,
[00:08:40]
in the ElmJSON visitor,
[00:08:41]
or anywhere that is not really a module,
[00:08:44]
or not in the context of a visitor module,
[00:08:46]
I say you need to return an error with type.
[00:08:51]
Exactly again?
[00:08:52]
I was going to guess never,
[00:08:54]
but you've got your own specific type?
[00:08:56]
I have a specific type.
[00:08:57]
It is of type error records with a property
[00:09:02]
useErrorForModule colon unit.
[00:09:06]
Nice, nice, nice.
[00:09:07]
So basically I'm saying,
[00:09:09]
if you have this error,
[00:09:10]
you should use ErrorForModule,
[00:09:12]
which is the appropriate function most of the time.
[00:09:15]
I'm going to go ahead and call that a Phantom error.
[00:09:20]
It's a bad thing that happens
[00:09:21]
when your continuous integration server
[00:09:23]
is failing intermittently.
[00:09:25]
So maybe that's not a good term for it.
[00:09:26]
Who's that a thing?
[00:09:28]
Yeah, people do talk about Phantom build failures,
[00:09:32]
which is not a positive term.
[00:09:36]
I know about CI pain,
[00:09:38]
and I know about Phantom pain.
[00:09:41]
So that could be CI Phantom pain.
[00:09:44]
So yeah,
[00:09:49]
I can find this solution,
[00:09:52]
but once I found it,
[00:09:52]
I'm like,
[00:09:53]
I don't even need to handle
[00:09:54]
this in my tests anymore,
[00:09:56]
and people can't have this error
[00:09:57]
because the compiler will check for it.
[00:09:59]
And I have never gotten an error report for that.
[00:10:02]
That's cool.
[00:10:03]
So the lesson is,
[00:10:05]
don't let people do things
[00:10:07]
with undefined behavior
[00:10:08]
or with silent behavior.
[00:10:10]
Yeah.
[00:10:11]
Yeah.
[00:10:12]
Like when you do,
[00:10:13]
when you run Elm test
[00:10:18]
and I can hear Richard's voice
[00:10:20]
because I'm pretty sure
[00:10:22]
he wrote that original message.
[00:10:23]
It's like,
[00:10:24]
there aren't any tests.
[00:10:25]
Let's define some
[00:10:26]
with an exclamation point,
[00:10:28]
which is great.
[00:10:29]
I've been thinking
[00:10:31]
about this principle as well,
[00:10:33]
and I think that as a community,
[00:10:36]
we sort of develop
[00:10:37]
these principles together
[00:10:39]
and kind of come up
[00:10:41]
with what's important
[00:10:42]
to us as a community.
[00:10:47]
of doubling down on.
[00:10:48]
So like I've been working on,
[00:10:51]
for the Elm pages 2.0 beta,
[00:10:54]
I've been working on
[00:10:56]
improved 404 pages
[00:10:58]
for the dev server.
[00:10:59]
And basically,
[00:11:00]
like I'm thinking about
[00:11:02]
when you go to a 404 page,
[00:11:03]
like most static site frameworks,
[00:11:05]
whether it's Next.js or Nuxt
[00:11:08]
or whatever popular tool,
[00:11:11]
the 404 page will just say
[00:11:16]
here are some pages
[00:11:17]
and you can search through
[00:11:18]
and here's how you create a page,
[00:11:21]
which is better,
[00:11:22]
gives you more feedback.
[00:11:23]
But I've been thinking
[00:11:25]
what is the user experiencing
[00:11:27]
when they're seeing that page?
[00:11:30]
So if you've just created a page,
[00:11:32]
you've got this sort of
[00:11:33]
file based routing system,
[00:11:35]
what are you thinking?
[00:11:36]
And I actually know
[00:11:38]
from my experience,
[00:11:39]
what I'm thinking is
[00:11:41]
did it pick that up?
[00:11:45]
Exactly.
[00:11:46]
And like did I do something wrong
[00:11:48]
or did it do something wrong
[00:11:49]
or is there some weird
[00:11:51]
environment thing or do I need
[00:11:52]
to restart the dev server?
[00:11:54]
Did I put the file
[00:11:55]
in the right folder?
[00:11:56]
Exactly.
[00:11:57]
Yeah, is there some weird
[00:11:58]
configuration thing?
[00:11:59]
And you feel like
[00:12:01]
you don't have visibility
[00:12:03]
into what's actually happening.
[00:12:05]
So in the 404 page,
[00:12:07]
I'm like well,
[00:12:08]
the 404 errors should tell you
[00:12:13]
what should say,
[00:12:14]
if you go to slash article slash hello,
[00:12:18]
is it, hey, there's no matching route.
[00:12:21]
Here are some routes.
[00:12:22]
There's slash blog slash slug.
[00:12:25]
There's a root route.
[00:12:28]
There are these other,
[00:12:29]
there's an about route.
[00:12:31]
And then you can look at that
[00:12:32]
and you can say, oh, right,
[00:12:33]
I typed slash article
[00:12:35]
instead of slash blog.
[00:12:36]
Or you can look at,
[00:12:38]
if you just tried to add a route,
[00:12:41]
it's not working very well.
[00:12:43]
It picked it up.
[00:12:44]
So you get that feedback
[00:12:45]
that I just added a thing
[00:12:47]
and oh, it has the thing,
[00:12:49]
but I'm typing it in a way
[00:12:51]
that it's not pulling up
[00:12:52]
the right thing.
[00:12:53]
So you understand
[00:12:55]
why it's not working
[00:12:57]
and you trust the system better
[00:12:59]
because you can see
[00:13:00]
it's picking up changes
[00:13:02]
when you add something,
[00:13:04]
the dev server,
[00:13:05]
the page just reloads
[00:13:10]
and it's not changed.
[00:13:12]
So this is one of those principles
[00:13:14]
that I think we have built into
[00:13:17]
our ethos in the Elm community,
[00:13:19]
which is that good errors
[00:13:21]
aren't just for beginners.
[00:13:23]
And this kind of makes me think
[00:13:24]
of what Tessa was telling us about
[00:13:27]
in our last episode
[00:13:28]
of the curb cutting effect
[00:13:30]
where in Berkeley,
[00:13:32]
they cut the sidewalks
[00:13:37]
so if you're in a wheelchair,
[00:13:38]
you can more easily,
[00:13:40]
you can access those sidewalks,
[00:13:43]
but if you have a stroller,
[00:13:45]
then it's easier
[00:13:46]
to access those sidewalks
[00:13:47]
and it becomes better
[00:13:49]
for everybody.
[00:13:50]
And I think in the same way,
[00:13:52]
good error messages aren't just
[00:13:54]
about making things accessible
[00:13:56]
for beginners.
[00:13:57]
They're a type of feedback
[00:13:59]
and feedback is really good
[00:14:01]
for experts too
[00:14:06]
because we recognize the value
[00:14:08]
of great error feedback.
[00:14:10]
I'm going to go on a bit
[00:14:11]
of a tangent question here,
[00:14:12]
but I wonder why the documentation
[00:14:16]
for Elm packages and Elm tools
[00:14:18]
is usually so good.
[00:14:20]
I'm wondering whether
[00:14:20]
it's just something like
[00:14:22]
the people who were there
[00:14:23]
before us, mostly Evan
[00:14:25]
and other core contributors,
[00:14:27]
they did a very good job
[00:14:29]
of having good documentation
[00:14:34]
and we are just copying them
[00:14:35]
or honoring their legacy in a way
[00:14:38]
because we want to contribute
[00:14:40]
at the same level as they did.
[00:14:42]
Right.
[00:14:43]
I do think that there's like the,
[00:14:45]
actually Sandy Metz talks about this
[00:14:47]
in the context of code
[00:14:49]
in one of her books
[00:14:51]
of this idea that,
[00:14:53]
I can't remember what she calls it,
[00:14:55]
like replicability
[00:14:56]
or whatever it is,
[00:14:58]
but the idea is that code
[00:15:03]
is people,
[00:15:04]
when you're building a new page,
[00:15:06]
what's the first thing you do?
[00:15:08]
You go and look at another page,
[00:15:10]
you copy paste it
[00:15:11]
or you see how it's done.
[00:15:13]
I don't know about you, Yaron,
[00:15:15]
but if I'm like writing
[00:15:16]
a new test module,
[00:15:18]
I know how to write
[00:15:19]
a test module.
[00:15:20]
I know how to say,
[00:15:22]
describe string, list,
[00:15:24]
test, string, left arrow.
[00:15:28]
I know how to do that from scratch,
[00:15:33]
and I know how to do it
[00:15:35]
from the beginning.
[00:15:36]
It's convenient.
[00:15:37]
Probably a similar amount of time,
[00:15:39]
but for some reason,
[00:15:41]
like is there some pattern?
[00:15:43]
Is it importing
[00:15:44]
some helper functions?
[00:15:45]
You want to replicate
[00:15:47]
the styles and patterns
[00:15:48]
in an existing code base.
[00:15:50]
Yeah, totally.
[00:15:51]
People say copy pasting is bad,
[00:15:54]
especially if you don't understand
[00:15:55]
the code,
[00:15:56]
and there are some truths to that.
[00:16:01]
I can just copy paste a test,
[00:16:03]
and then I know what I should
[00:16:05]
and should not change.
[00:16:07]
Even when I make a mistake,
[00:16:10]
I usually have some good feedback
[00:16:12]
from Elm test or from the compiler.
[00:16:14]
I copy paste code a lot.
[00:16:17]
I think that's just
[00:16:17]
what we do.
[00:16:19]
We look at how has
[00:16:21]
it been done before.
[00:16:23]
That's just how our minds work
[00:16:25]
because maybe like,
[00:16:30]
convention in this code base
[00:16:31]
where it maps over a list
[00:16:34]
of test data for each test case,
[00:16:37]
or it uses some special
[00:16:38]
test helper functions.
[00:16:42]
We tend to follow conventions.
[00:16:44]
We tend to follow precedents.
[00:16:47]
I think we're very influenced
[00:16:49]
by APIs that have been out there.
[00:16:51]
I know like,
[00:16:53]
Matt Griffith's talk
[00:16:58]
on style elements at the time,
[00:16:59]
which is now Elm UI,
[00:17:01]
really changed the way
[00:17:03]
I thought about API design
[00:17:05]
of this idea that rethinking
[00:17:08]
the thing you're doing
[00:17:09]
from the ground up.
[00:17:11]
Basically this idea that
[00:17:12]
API design is more holistic.
[00:17:15]
It's not just,
[00:17:16]
how do I write a function
[00:17:18]
that does this?
[00:17:19]
It's not a set of functions.
[00:17:20]
It's a problem domain,
[00:17:22]
and you can actually
[00:17:27]
change the domain
[00:17:28]
in order to make a simpler API.
[00:17:30]
You can change the rules
[00:17:31]
of the game.
[00:17:32]
If you don't like the game,
[00:17:34]
you're making the game up,
[00:17:35]
so change the rules of the game.
[00:17:37]
It made me rethink that.
[00:17:39]
You look at the docs,
[00:17:41]
and they had pictures
[00:17:43]
of here's what it looks like
[00:17:45]
if you use a row or a column
[00:17:48]
or an EL.
[00:17:51]
We're very influenced
[00:17:56]
by having the mechanism
[00:17:59]
for package docs
[00:18:00]
where the types are in line.
[00:18:02]
Elm has such a nice,
[00:18:05]
elegant type system,
[00:18:06]
and you put the types
[00:18:08]
in the doc comments above it.
[00:18:09]
It lends itself
[00:18:10]
to really good documentation.
[00:18:11]
I agree, when I go to documentation
[00:18:13]
in other communities,
[00:18:14]
I feel a little lost sometimes.
[00:18:16]
It's so easy to navigate packages
[00:18:18]
in the Elm ecosystem.
[00:18:19]
That actually reminds me
[00:18:24]
of a couple of stories
[00:18:25]
from my experience,
[00:18:27]
which is this change
[00:18:30]
in Elm GraphQL
[00:18:31]
where I entirely removed
[00:18:34]
this field type,
[00:18:36]
and everything became
[00:18:37]
a selection set.
[00:18:38]
It is before my time,
[00:18:40]
because I learned Elm GraphQL
[00:18:41]
with the selection sets.
[00:18:43]
Only the selection set,
[00:18:44]
so I have no clue
[00:18:45]
what you're talking about.
[00:18:46]
Yeah, so the way it worked
[00:18:51]
was called GraphQL,
[00:18:52]
which I know,
[00:18:54]
it's extremely funny.
[00:18:55]
Yeah, yeah.
[00:18:59]
I know what you're thinking,
[00:19:01]
Dillon, you're so clever,
[00:19:02]
and I can't argue with that.
[00:19:06]
I would love to open a shop
[00:19:10]
just for the sake
[00:19:11]
of having a pun in its name.
[00:19:15]
Thankfully,
[00:19:20]
we used to have a cheese store
[00:19:23]
in my hometown here
[00:19:25]
called Say Cheese,
[00:19:27]
like C apostrophe E S T,
[00:19:29]
because when you're taking
[00:19:30]
a picture of someone,
[00:19:31]
you say Say Cheese.
[00:19:32]
But in French,
[00:19:34]
it's very clever.
[00:19:35]
It's very clever.
[00:19:37]
So this field type,
[00:19:39]
there used to be a field type
[00:19:41]
and a selection set,
[00:19:42]
because in GraphQL,
[00:19:44]
if you look at all the concepts
[00:19:49]
in the GraphQL spec,
[00:19:50]
there are selection sets,
[00:19:51]
there are fields.
[00:19:52]
A field would be like,
[00:19:54]
if you want the first and last
[00:19:56]
name from a user,
[00:19:58]
then first and last are fields,
[00:20:01]
and you're building
[00:20:01]
a selection set
[00:20:03]
where you're selecting
[00:20:04]
first and last from a user.
[00:20:06]
And so it's a natural mapping
[00:20:08]
that you would,
[00:20:10]
the obvious thing
[00:20:11]
when you're mapping
[00:20:12]
those concepts of GraphQL
[00:20:13]
into an Elm API would be
[00:20:18]
to have a selection set.
[00:20:19]
So you would differentiate
[00:20:21]
the properties or the fields
[00:20:23]
and whatever contains it, right?
[00:20:25]
Yes.
[00:20:25]
Is that it?
[00:20:26]
Exactly.
[00:20:27]
And so the breakthrough was,
[00:20:30]
well, a field is actually
[00:20:33]
a selection set with one item.
[00:20:35]
And if you can combine together
[00:20:37]
two different fields,
[00:20:39]
if you can combine
[00:20:40]
two selection sets,
[00:20:41]
then you can have a field
[00:20:46]
that's just a selection set.
[00:20:47]
So if you just say every field
[00:20:49]
is just a selection set of one,
[00:20:51]
then when you combine
[00:20:52]
them together,
[00:20:53]
it's the same API experience
[00:20:54]
of building selection sets,
[00:20:56]
except you could build
[00:20:57]
a selection set by just saying,
[00:20:59]
I only want this field.
[00:21:00]
Now you have a selection set.
[00:21:01]
Or you could build
[00:21:02]
a selection set by saying,
[00:21:03]
here are these three fields
[00:21:04]
which happen to be
[00:21:05]
of type selection set.
[00:21:07]
Oh, and I can do
[00:21:08]
selectionSet.map3,
[00:21:09]
or use the pipeline API
[00:21:14]
to do that.
[00:21:14]
So what was it
[00:21:16]
that spawned this change?
[00:21:18]
Was it because it felt
[00:21:19]
a bit hard or unnatural
[00:21:21]
or painful to have to query
[00:21:24]
the individual fields
[00:21:26]
compared to what was
[00:21:27]
contained in them?
[00:21:28]
Like, was there...
[00:21:30]
So when you use
[00:21:30]
selection sets,
[00:21:32]
everything is the same.
[00:21:33]
So you have one API
[00:21:34]
for everything.
[00:21:35]
So before,
[00:21:36]
did you have two APIs
[00:21:38]
and it was a bit hard
[00:21:43]
to get the right one?
[00:21:44]
One thing is,
[00:21:47]
if you wanted to do
[00:21:47]
what's called a fragment
[00:21:49]
in GraphQL,
[00:21:50]
so GraphQL has these fragments
[00:21:52]
which are just
[00:21:53]
little composable units.
[00:21:55]
It's almost like a variable
[00:21:57]
you can sort of
[00:21:57]
store something off.
[00:21:59]
A fragment is just,
[00:22:00]
oh, here are these things
[00:22:01]
and I want to select
[00:22:03]
this selection of things
[00:22:04]
that I want to use,
[00:22:06]
share between code.
[00:22:07]
It's like a little
[00:22:12]
subselection set
[00:22:16]
that you can pull
[00:22:16]
into a selection set.
[00:22:21]
So it's like a higher order
[00:22:21]
selection set sort of.
[00:22:24]
And using the previous API
[00:22:24]
where things were fields,
[00:22:29]
it was a lot more clunky
[00:22:29]
to pull in a selection set
[00:22:31]
to a selection set.
[00:22:33]
There had to be whole new
[00:22:33]
APIs for dealing with that.
[00:22:38]
Essentially, one of my favorite
[00:22:38]
little tidbits
[00:22:42]
of API design wisdom
[00:22:42]
that I've heard
[00:22:46]
is Linus Torvalds was
[00:22:46]
talking about
[00:22:49]
his coding principles
[00:22:49]
and how one of the things
[00:22:50]
that he looks for in good code
[00:22:54]
is this concept of elegance.
[00:22:54]
And what he defined elegance
[00:22:59]
as was a way to
[00:22:59]
a sensibility
[00:23:02]
where you're able to see
[00:23:02]
two different code paths
[00:23:07]
and so like, you know,
[00:23:09]
if you say,
[00:23:09]
if the list is empty, do this.
[00:23:11]
If the list is not empty,
[00:23:11]
do this.
[00:23:14]
If you can get the exact
[00:23:14]
same output in a single code path
[00:23:17]
and see the way to treat it
[00:23:17]
as a single code path,
[00:23:20]
that's what he calls elegance.
[00:23:22]
And I think that same
[00:23:22]
principle of elegance
[00:23:25]
applies in API design.
[00:23:25]
So there's an elegance
[00:23:28]
to reducing the number
[00:23:28]
of concepts
[00:23:31]
because then you can treat
[00:23:31]
different things interchangeably
[00:23:34]
and they just compose
[00:23:34]
together more elegantly.
[00:23:37]
So another key lesson
[00:23:37]
from that design
[00:23:41]
was again, like, going outside
[00:23:41]
of the boundaries
[00:23:45]
and rethinking how you're
[00:23:45]
approaching the problem
[00:23:48]
from the ground up.
[00:23:49]
And in order to do
[00:23:49]
that API design,
[00:23:51]
I wrote a blog post about this
[00:23:51]
called How Elm Guides
[00:23:55]
Towards Simplicity.
[00:23:56]
And I talk about how I had
[00:23:56]
to introduce this technique
[00:24:01]
of using hashes for performing
[00:24:01]
the GraphQL queries under the hood.
[00:24:07]
And the reason is because Elm
[00:24:07]
GraphQL builds up decoders
[00:24:12]
under the hood, but what's
[00:24:12]
it going to decode if...
[00:24:15]
Because you can have
[00:24:15]
collisions in fields.
[00:24:18]
You can select multiple fields
[00:24:19]
with the same name
[00:24:21]
but with slightly different
[00:24:22]
arguments passed to them,
[00:24:24]
in which case it's actually
[00:24:24]
a GraphQL error.
[00:24:26]
So the old API would combine
[00:24:27]
them together
[00:24:30]
and it would add numbers
[00:24:30]
as you added them,
[00:24:32]
and it knew how many had been
[00:24:32]
added before, so it knew
[00:24:35]
what field alias to use
[00:24:35]
as you added new ones.
[00:24:39]
But the breakthrough idea
[00:24:39]
I came up with was,
[00:24:43]
what if I made it deterministic
[00:24:44]
by taking all of the things
[00:24:44]
that make a field unique
[00:24:49]
and then building hash with that
[00:24:49]
and always using that hash?
[00:24:54]
And which I get questions
[00:24:54]
about a lot because people are
[00:24:56]
like, why are there hashes in here?
[00:24:59]
But it ends up giving you this
[00:24:59]
much more high level API.
[00:25:02]
And another principle that I
[00:25:02]
learned building Elm GraphQL
[00:25:06]
is that you don't necessarily
[00:25:06]
need to have a direct mapping
[00:25:11]
of your domain to your library.
[00:25:14]
That said, when I'm working
[00:25:14]
on a library, the first,
[00:25:19]
the last, the middle, the second,
[00:25:22]
the fifth thing I'm doing
[00:25:22]
is looking at the spec,
[00:25:25]
if there is a spec,
[00:25:26]
and looking at the terms
[00:25:28]
that are used in that spec.
[00:25:29]
Because I do want to leverage
[00:25:29]
existing concepts.
[00:25:34]
If the GraphQL spec calls
[00:25:34]
something a field alias,
[00:25:39]
then even if it's under the
[00:25:39]
hood internally in my code,
[00:25:42]
I'm going to try to use that
[00:25:43]
concept of the field alias.
[00:25:45]
But at a certain point,
[00:25:46]
sometimes you break out
[00:25:47]
of those concepts and you say,
[00:25:49]
actually, I want to decouple
[00:25:49]
myself from this particular
[00:25:53]
concept, I want to abstract
[00:25:54]
this concept away.
[00:25:55]
Then you have to be able
[00:25:55]
to do both, I think.
[00:25:57]
You have to be able to take
[00:25:57]
the domain, understand it
[00:26:00]
extremely well, and then use
[00:26:03]
the concepts and terms from
[00:26:03]
that domain as much as possible,
[00:26:06]
except where deviating from
[00:26:06]
them would give you a better
[00:26:11]
design or allow you to simplify
[00:26:12]
or have a more elegant design
[00:26:14]
where multiple concepts
[00:26:14]
fold together into one.
[00:26:16]
And having an economy of
[00:26:16]
concepts is so valuable
[00:26:21]
because people need to learn
[00:26:22]
those different concepts.
[00:26:24]
People need to have a mental
[00:26:25]
model of how they work, so the
[00:26:27]
fewer concepts you have
[00:26:27]
operating, the better.
[00:26:30]
So with Elm GraphQL, I've tried
[00:26:31]
to abstract away a lot of things
[00:26:34]
about GraphQL and also make a
[00:26:35]
lot of impossible states
[00:26:38]
impossible through the API
[00:26:39]
design.
[00:26:40]
Yeah, you want to hide all the
[00:26:41]
confusing parts of the spec
[00:26:44]
from the public API.
[00:26:45]
Yes.
[00:26:46]
That's why when we are writing
[00:26:47]
JavaScript, we don't refer to
[00:26:50]
the same names as the ECMAScript
[00:26:53]
aspects tells us, right?
[00:26:56]
That would be hell.
[00:26:59]
Yeah, Teresa gave a really cool
[00:27:00]
talk at Elm Europe one year
[00:27:04]
about building a chart library.
[00:27:06]
And I thought it was really cool
[00:27:10]
because it was kind of like
[00:27:11]
about how do you most effectively
[00:27:14]
present information?
[00:27:15]
And that really hammered home
[00:27:18]
the point that it's not about
[00:27:19]
API design, it's about solving
[00:27:21]
problems, and API design is this
[00:27:23]
tool to help people solve
[00:27:25]
problems.
[00:27:26]
So how did you notice the problem
[00:27:28]
for the selection sets?
[00:27:31]
Like, did you create a lot of
[00:27:32]
examples or were you just using
[00:27:35]
Elm GraphQL everywhere and
[00:27:36]
noticed that there was something
[00:27:38]
to be improved?
[00:27:39]
Well, one of the, I'm a big fan
[00:27:41]
of feedback of all kinds, and I
[00:27:44]
actually give a talk about this
[00:27:46]
concept of basically, and this is
[00:27:48]
actually one of my principles,
[00:27:50]
is that there's a qualitative
[00:27:52]
difference when you wire up
[00:27:54]
feedback mechanisms before you
[00:27:56]
do the thing.
[00:27:57]
So if you can get user feedback
[00:28:00]
before you finalize an API or,
[00:28:03]
you know, 1.0 the API, if you
[00:28:06]
can write examples before you
[00:28:10]
write the code, right?
[00:28:11]
That's like when writing examples
[00:28:14]
is an afterthought, it's
[00:28:15]
qualitatively different than when
[00:28:17]
writing examples comes first.
[00:28:19]
When you write tests as an
[00:28:22]
afterthought, the tests become
[00:28:24]
qualitatively different than when
[00:28:26]
you write the tests first.
[00:28:27]
And there are a lot less of them.
[00:28:30]
Yeah, yeah.
[00:28:31]
The coverage is way lower.
[00:28:33]
Right.
[00:28:34]
And it just doesn't, you don't
[00:28:37]
get the design benefits from it
[00:28:40]
in the same way.
[00:28:41]
And the same is true for, you
[00:28:44]
know, getting user feedback.
[00:28:46]
And I really like to write
[00:28:49]
examples first as a way of
[00:28:50]
getting feedback.
[00:28:51]
So that's like one of my main
[00:28:52]
ways of getting feedback when
[00:28:53]
I'm playing around with an API
[00:28:55]
is I use it in tests, I use it
[00:28:57]
in an examples folder and see
[00:28:58]
how it feels.
[00:28:59]
And I try really hard to have
[00:29:01]
meaningful examples, not toy
[00:29:02]
examples, because toy examples
[00:29:04]
don't give you that benefit.
[00:29:06]
I try to avoid, you know, foo,
[00:29:08]
bar, baz in my examples.
[00:29:10]
I try to avoid it in my test
[00:29:11]
cases.
[00:29:12]
I try, like, I try really hard
[00:29:14]
when I'm writing unit tests to
[00:29:16]
have meaningful, relevant
[00:29:18]
examples that because I know
[00:29:21]
that those examples are actually
[00:29:23]
going to direct the design that
[00:29:25]
I come up with.
[00:29:26]
And I don't want foo, bar, baz
[00:29:28]
directing my design.
[00:29:30]
I want useful examples that my
[00:29:33]
API is designed to solve to be
[00:29:36]
directing the design direction.
[00:29:38]
So those are sort of the main
[00:29:40]
main things.
[00:29:42]
I often try, like, I really like
[00:29:45]
hearing how people are using
[00:29:47]
APIs in Slack.
[00:29:48]
I ask people frequently.
[00:29:50]
When people when people post an
[00:29:53]
issue, the first thing I do is
[00:29:56]
if it's not clear from the issue,
[00:29:58]
I try to understand what were
[00:29:59]
you trying to accomplish when you
[00:30:01]
ran into this problem or this
[00:30:03]
confusing piece of the API or
[00:30:05]
whatever it might be.
[00:30:06]
What were you expecting?
[00:30:08]
What were you expecting and what
[00:30:09]
were you trying to accomplish?
[00:30:10]
And how and let's let's see if
[00:30:13]
we can accomplish that using the
[00:30:15]
existing API and then examine it
[00:30:17]
and understand your use case.
[00:30:18]
And then maybe we find, oh,
[00:30:20]
actually, for the particular
[00:30:22]
thing you're trying to solve, it
[00:30:23]
would be much more elegant if we
[00:30:26]
had these new pieces in the API.
[00:30:28]
There's a concept in law called
[00:30:29]
standing.
[00:30:30]
I can't remember if I brought
[00:30:31]
this up on the podcast before,
[00:30:32]
but this doesn't ring a bell.
[00:30:34]
So this is an analogy that I
[00:30:36]
that I think of sometimes that
[00:30:38]
so in law, you can't go and sue
[00:30:42]
somebody or, you know, in
[00:30:44]
American law, you can't like go
[00:30:45]
to the Supreme Court and say,
[00:30:47]
hey, you can't limit free speech
[00:30:50]
in this particular way that I
[00:30:52]
think you're limiting free
[00:30:53]
speech.
[00:30:53]
I can't just go do that if it
[00:30:55]
wasn't done to me.
[00:30:56]
I don't have legal standing.
[00:30:58]
So you need to find a defendant
[00:31:00]
or a victim.
[00:31:02]
Yeah, not a defendant.
[00:31:04]
You need to find somebody who
[00:31:05]
has standing in order to bring
[00:31:06]
a case to court.
[00:31:08]
And you can't just say this is
[00:31:10]
wrong.
[00:31:10]
You have to say you wronged me
[00:31:13]
in order to bring a case to
[00:31:14]
court.
[00:31:15]
And in the same way, I think
[00:31:18]
that I'm not so interested to
[00:31:20]
hear people's feedback about how
[00:31:22]
they think an API should be.
[00:31:24]
I'm more interested to hear how
[00:31:27]
this API is serving you or not
[00:31:29]
that I really want to hear.
[00:31:30]
But it's not that interesting
[00:31:32]
to like I feel like my job as an
[00:31:34]
API designer is to come up with
[00:31:38]
designs.
[00:31:39]
And what users can really help
[00:31:42]
me do is not tell me what the
[00:31:44]
designs could be because I mean
[00:31:45]
they're going to have a different
[00:31:46]
vision and different ways things
[00:31:47]
could be.
[00:31:48]
What I really want users to help
[00:31:50]
me understand is what's
[00:31:51]
confusing to you when you're
[00:31:53]
trying to use it.
[00:31:54]
What problem are you trying to
[00:31:55]
solve?
[00:31:56]
What problem can you solve
[00:31:57]
elegantly?
[00:31:58]
What problem can you not solve
[00:31:59]
elegantly?
[00:31:59]
What's intuitive about it?
[00:32:01]
Because I can't come up with
[00:32:03]
those things.
[00:32:04]
I need people to help me
[00:32:05]
understand it.
[00:32:06]
So I want people with standing
[00:32:08]
who actually are trying to solve
[00:32:10]
something with my API to give me
[00:32:11]
feedback.
[00:32:12]
Yeah, you basically don't want
[00:32:13]
people to come up with ideas
[00:32:15]
like, hey, it would be cool if
[00:32:17]
we could do this without having
[00:32:19]
a proper use case for it.
[00:32:20]
Yes, because those things are
[00:32:23]
they're just not as solid.
[00:32:25]
The API designs that come out of
[00:32:28]
thin air from the abstract are
[00:32:30]
not as useful.
[00:32:31]
So design for concrete use cases
[00:32:34]
and drive your designs from
[00:32:37]
feedback from concrete use cases.
[00:32:39]
Yeah, there's one request that
[00:32:41]
I've got a lot from, or a lot, a
[00:32:43]
few times for Elm Review is, hey,
[00:32:46]
Elm Review is great.
[00:32:47]
I think it would be cool if we
[00:32:49]
could ignore rules with using
[00:32:53]
comments, just like ESLint does.
[00:32:55]
Right.
[00:32:56]
And the few times where it got
[00:32:59]
on it, I replied with, sure, do
[00:33:02]
you have a good use case?
[00:33:03]
Yes.
[00:33:04]
Why would you use this?
[00:33:06]
And I never got a good reply.
[00:33:08]
Right.
[00:33:09]
And this is one of the one of
[00:33:10]
those cases where it's, well,
[00:33:12]
all the other tools have it.
[00:33:14]
Surely there must be a good
[00:33:15]
reason, which is something that
[00:33:17]
we in the Elm community feel a
[00:33:19]
bit like, we should wait until
[00:33:22]
we implement this.
[00:33:23]
So there's this, as you said,
[00:33:25]
like, there needs to be some
[00:33:26]
standing, there needs to be a
[00:33:28]
proper use case.
[00:33:29]
And if we don't, then we're just
[00:33:32]
not going to implement the
[00:33:33]
feature.
[00:33:34]
And we're going to wait for it
[00:33:35]
to pop up.
[00:33:36]
Exactly.
[00:33:37]
Exactly.
[00:33:38]
Which is exactly how the legal
[00:33:39]
system is designed.
[00:33:40]
I mean, it's flawed in some ways,
[00:33:42]
but it is a really interesting
[00:33:44]
concept that let's not
[00:33:46]
preemptively create laws or, you
[00:33:49]
know, legal rulings.
[00:33:51]
Let's let people respond to
[00:33:53]
specific cases because we can't
[00:33:55]
really do these things in the
[00:33:57]
abstract.
[00:33:58]
We need concrete things.
[00:33:59]
And fortunately, it's much more
[00:34:00]
clear cut in the API design
[00:34:01]
world.
[00:34:02]
Yeah.
[00:34:03]
Did you know that it was not
[00:34:04]
legal to adopt a Martian?
[00:34:06]
What?
[00:34:07]
But how am I going to adopt a
[00:34:09]
Martian?
[00:34:10]
There's no standing for it.
[00:34:11]
It has never happened.
[00:34:12]
Touche.
[00:34:13]
There's no law for it.
[00:34:14]
I'm guessing.
[00:34:15]
Well, there go all my dreams,
[00:34:17]
Jeroen.
[00:34:18]
Is that the kind of kid that you
[00:34:20]
want with your wife?
[00:34:22]
Absolutely.
[00:34:23]
I guess he would be unique.
[00:34:25]
He would be special.
[00:34:26]
Well, you will just have to tell
[00:34:28]
him that he's special, just like
[00:34:30]
my parents did.
[00:34:31]
Yeah, there's this principle that
[00:34:36]
Evan talks about in his little
[00:34:39]
tweet thread that I often look
[00:34:41]
back on about the sort of Elm
[00:34:43]
philosophy that it's better to
[00:34:46]
do it right than to do it right
[00:34:48]
now.
[00:34:49]
And I think that maybe this is a
[00:34:51]
good refinement or addition or
[00:34:54]
complementary concept to that,
[00:34:57]
that it's like if you don't have
[00:35:00]
a use case, a motivating use
[00:35:02]
case, then wait.
[00:35:04]
Like don't preemptively make API
[00:35:07]
designs.
[00:35:08]
And I think this is very much
[00:35:09]
true for application code too.
[00:35:10]
For products or for application
[00:35:12]
code?
[00:35:13]
Yeah, for application code.
[00:35:14]
I think extracting APIs from
[00:35:18]
real world code is a great idea.
[00:35:20]
Like duplication is okay.
[00:35:21]
It's okay to allow there to be
[00:35:23]
some duplication.
[00:35:24]
It's okay to allow there to be
[00:35:26]
some code that doesn't quite
[00:35:28]
feel right.
[00:35:29]
You just have to be able to trust
[00:35:31]
yourself in your process to when
[00:35:34]
you've gathered enough information
[00:35:36]
like, oh, this doesn't quite feel
[00:35:37]
right, but I don't have clarity on
[00:35:39]
what it needs to be yet.
[00:35:41]
And then another case comes up.
[00:35:43]
Oh, it doesn't feel right here
[00:35:44]
either.
[00:35:45]
But now I have another data point
[00:35:46]
and now I'm starting to get
[00:35:47]
clarity on what direction it can
[00:35:49]
go, then move in that direction.
[00:35:51]
But don't anticipate it.
[00:35:52]
Yeah, the reason why once you
[00:35:54]
wait is because if we implement it
[00:35:57]
preemptively, then there's a good
[00:35:59]
chance that we are doing it in a way
[00:36:01]
that will not support the use case
[00:36:03]
well.
[00:36:04]
We're going to do it wrong.
[00:36:05]
We're going to do it slightly off
[00:36:07]
or simply this will never come up
[00:36:09]
because it's not a good use case.
[00:36:12]
It's not something that people will
[00:36:14]
need.
[00:36:15]
Yes, exactly.
[00:36:16]
Premature optimization is the root
[00:36:18]
of all evil.
[00:36:19]
And if you like, as you say, sorry,
[00:36:23]
premature abstraction is the root
[00:36:25]
of all evil.
[00:36:26]
So if you create an abstraction
[00:36:29]
preemptively, then what happens is
[00:36:32]
you create that abstraction.
[00:36:33]
And as you say, that abstraction
[00:36:36]
is for an imagined problem.
[00:36:38]
Then when the real problem comes
[00:36:39]
along.
[00:36:40]
So now there's like a second place
[00:36:42]
you need to use, you know, solve
[00:36:45]
that problem.
[00:36:46]
Now you have more clarity on what
[00:36:47]
the abstraction should have been.
[00:36:48]
Now you need to back out that
[00:36:50]
abstraction that was the wrong
[00:36:51]
abstraction because it was
[00:36:52]
designed in the abstract and then
[00:36:54]
create a new abstraction.
[00:36:56]
So you've created an extra step
[00:36:57]
for yourself.
[00:36:58]
So you want to be in like a state
[00:37:01]
of readiness where you're and
[00:37:03]
simplicity is the best thing you
[00:37:05]
can do to be ready to anticipate
[00:37:08]
future design needs.
[00:37:09]
Farm review.
[00:37:10]
I don't have as many tests as I
[00:37:12]
would like to.
[00:37:13]
But what I do have and what serves
[00:37:16]
as a test are a lot of rule
[00:37:19]
examples.
[00:37:20]
So when I started off writing on
[00:37:22]
review, I wrote both the library
[00:37:25]
and the CLI and the rules that I
[00:37:27]
would like to see implemented.
[00:37:29]
So if I know that one of them is
[00:37:31]
failing, then I probably have
[00:37:32]
some issue in that I should fix.
[00:37:35]
Surprisingly, that happens not
[00:37:36]
very often.
[00:37:37]
So I don't feel the need to add
[00:37:39]
tests.
[00:37:40]
But in retrospect, I should have
[00:37:41]
added more tests, obviously.
[00:37:42]
So I work a lot by not by
[00:37:44]
examples, but by use cases.
[00:37:46]
And I really tried running a lot
[00:37:49]
of rules.
[00:37:50]
One reason is because it would
[00:37:52]
give me a lot of feedback because
[00:37:54]
that would be the more important
[00:37:56]
and more instant feedback that I
[00:37:59]
would get to have without having
[00:38:01]
to talk to users or other people
[00:38:03]
who would write rules.
[00:38:04]
And what data also allowed me was
[00:38:07]
to push the boundaries of what I
[00:38:09]
wanted to support.
[00:38:10]
I was like, OK, I now have this
[00:38:12]
rule that reports unused
[00:38:14]
variables.
[00:38:15]
But would it be interesting if I
[00:38:17]
could detect unused dependencies?
[00:38:20]
Oh, well, that would be
[00:38:21]
interesting.
[00:38:22]
Let me try and see how that would
[00:38:25]
work.
[00:38:25]
And then I would try to write the
[00:38:27]
rule.
[00:38:27]
And I would notice, oh, well, it
[00:38:30]
wouldn't work because I would
[00:38:31]
need to have the Elm JSON file as
[00:38:34]
a string or as an ASD.
[00:38:36]
And by doing this over and over
[00:38:39]
again, I can know what cases I
[00:38:43]
want to support and get a lot of
[00:38:45]
feedback.
[00:38:46]
And when you do that a lot, what
[00:38:48]
also happens is that you feel the
[00:38:50]
pain points a lot more.
[00:38:52]
So I went through a lot of
[00:38:55]
versions for the Elm Review API
[00:38:57]
before it got to 1.0.
[00:38:59]
And a lot of them were painful to
[00:39:01]
write.
[00:39:02]
So I had a version where you
[00:39:05]
would say, these are my visitors.
[00:39:08]
I have a default visitor.
[00:39:10]
And you override the expression
[00:39:12]
one, which by default does
[00:39:14]
nothing.
[00:39:15]
And you have another one for the
[00:39:17]
import visitor.
[00:39:18]
Or then I went to a list of
[00:39:20]
visitors, just kind of like the
[00:39:23]
HTML API.
[00:39:24]
And after a while, you notice
[00:39:26]
this doesn't work out for Elm
[00:39:28]
Review.
[00:39:29]
So you try it a few other things.
[00:39:31]
And ultimately, you come up with
[00:39:34]
an API where you have the most
[00:39:37]
benefits and the least pain
[00:39:39]
points for the use case that
[00:39:41]
you're working on.
[00:39:43]
So if you can write tests,
[00:39:46]
that's great.
[00:39:47]
If you can use examples, that's
[00:39:50]
better.
[00:39:51]
But if you can write actual
[00:39:53]
proper applications of your
[00:39:56]
library, that is the best.
[00:39:58]
Because then you are both a user
[00:40:00]
and a designer.
[00:40:01]
And you get to have all the
[00:40:03]
benefits of all positions.
[00:40:05]
Right.
[00:40:07]
They play different roles.
[00:40:09]
If you have real world use cases
[00:40:12]
or like, so examples can be
[00:40:14]
really good for showing off an
[00:40:17]
API.
[00:40:18]
Like, look at how you do this.
[00:40:20]
This is how you do this thing.
[00:40:21]
Maybe here's a more simple
[00:40:23]
example to teach this concept or
[00:40:25]
to show off how the API makes
[00:40:27]
this easier.
[00:40:28]
But it's some sort of motivating
[00:40:31]
example that's demonstrating the
[00:40:33]
API.
[00:40:34]
So that can be really good for
[00:40:36]
like, what is the simplest thing
[00:40:38]
and can we make the simplest
[00:40:40]
thing simpler?
[00:40:41]
That can help drive that
[00:40:43]
feedback.
[00:40:44]
That's that feedback loop that's
[00:40:46]
telling you, hey, even in the
[00:40:48]
simplest case, there are a lot of
[00:40:49]
things that it seems like we
[00:40:50]
could still simplify.
[00:40:51]
And so that's going to let you
[00:40:53]
know that that's a feedback
[00:40:54]
mechanism to tell you that.
[00:40:55]
Real application code that's
[00:40:57]
solving real problems is the
[00:40:59]
point.
[00:41:00]
And if it's doing that well, or
[00:41:02]
if it's painful to do certain
[00:41:03]
things in that context, that's
[00:41:04]
real.
[00:41:05]
There's no that's the that's the
[00:41:07]
whole point.
[00:41:08]
And so that's like the most real
[00:41:10]
kind of feedback.
[00:41:11]
But then if you don't have a
[00:41:13]
great test suite, then when you
[00:41:14]
get all that feedback about how
[00:41:16]
things could be simpler, less
[00:41:18]
confusing, solve different types
[00:41:20]
of problems that it's not
[00:41:21]
currently able to solve.
[00:41:22]
Now you can't change your code to
[00:41:24]
do that safely because you can't.
[00:41:27]
And I think that I often think
[00:41:31]
about like if there's something
[00:41:33]
that you want to make really
[00:41:34]
good, invest in giving it a great
[00:41:37]
feedback mechanism.
[00:41:39]
Like if you build a great feedback
[00:41:41]
mechanism for something, then
[00:41:42]
it's going to evolve over time to
[00:41:45]
give you even better feedback.
[00:41:47]
And the design is going to evolve
[00:41:49]
over time because you're going to
[00:41:50]
be able to more easily experiment
[00:41:52]
with it.
[00:41:53]
So I think that's like that's one
[00:41:56]
of the most important things.
[00:41:57]
So there's also like I really
[00:42:00]
liked this Rich Hickey talk about
[00:42:03]
hammock driven design.
[00:42:04]
And I think that there's Rich
[00:42:07]
Hickey in that talk talks about
[00:42:09]
like when there's more work to be
[00:42:11]
done, then you're not in the
[00:42:12]
hammock, you're working, you're
[00:42:14]
figuring out how to solve the API.
[00:42:17]
So like, for example, how do
[00:42:19]
other libraries solve this
[00:42:20]
problem?
[00:42:21]
Or, you know, what collecting all
[00:42:24]
the use cases you're looking at
[00:42:26]
or finding all the code examples
[00:42:28]
and comparing them, finding
[00:42:29]
common threads or, you know,
[00:42:30]
whatever.
[00:42:31]
That's the API design process
[00:42:33]
where there's active work to do.
[00:42:35]
And in a way, your job there is
[00:42:37]
to put useful information in
[00:42:39]
front of your brain.
[00:42:40]
But we don't design APIs are
[00:42:43]
extremely creative, subconscious
[00:42:46]
designs APIs, right?
[00:42:48]
But us are sort of conscious
[00:42:51]
minds can go do the hard work of
[00:42:54]
gathering prior art.
[00:42:55]
How did other things design this
[00:42:57]
understanding the spec,
[00:42:59]
understanding the problems,
[00:43:00]
understanding how the current
[00:43:02]
API design fall short, gather
[00:43:04]
all that information, put it in
[00:43:05]
front of your subconscious brain.
[00:43:07]
And then when you've hit a wall
[00:43:10]
and there's nothing more you can
[00:43:11]
do there, all you can do is sit
[00:43:13]
in a hammock.
[00:43:14]
You can't work harder to come up
[00:43:15]
with API design breakthroughs.
[00:43:17]
You have to go sit in a hammock
[00:43:18]
at that point.
[00:43:19]
But if you haven't given your
[00:43:21]
subconscious mind all these great
[00:43:23]
bits of information to consider
[00:43:25]
first, then it's not going to
[00:43:27]
come up with anything
[00:43:28]
interesting.
[00:43:29]
So that's sort of something I
[00:43:31]
think about often.
[00:43:32]
Now, that doesn't necessarily
[00:43:34]
literally mean I'm going to go
[00:43:35]
sit in a hammock, although I
[00:43:36]
often do like go for a walk if
[00:43:38]
I'm trying to if I'm noticing
[00:43:40]
that I'm not getting clear ideas
[00:43:43]
and I need to come up with some
[00:43:44]
API concept.
[00:43:46]
But also, like sometimes I'll
[00:43:48]
just put things on the
[00:43:49]
backburner.
[00:43:50]
You know, I'll think as hard as
[00:43:52]
I can about an API, make sure I
[00:43:53]
get all the information I need.
[00:43:54]
And then I'll work on a
[00:43:56]
different thing.
[00:43:57]
Yeah.
[00:43:58]
And give that some time.
[00:43:59]
What I like to do is to write a
[00:44:00]
lot of notes.
[00:44:01]
So, oh, I want to figure out a
[00:44:04]
nice API for this or because I
[00:44:06]
have this pain point and I'm
[00:44:08]
just throw all the thoughts I
[00:44:10]
have in a note application, a
[00:44:13]
note taking application,
[00:44:14]
something.
[00:44:15]
And while you're writing, you're
[00:44:18]
thinking about the problem and
[00:44:20]
you're getting new thoughts, or
[00:44:22]
at least I am.
[00:44:23]
I'm going to say I instead of
[00:44:24]
you now.
[00:44:25]
And at some point, yeah, you're
[00:44:29]
I am out of ideas.
[00:44:32]
Yeah.
[00:44:33]
And I will just leave it at
[00:44:35]
that.
[00:44:36]
And I will think about other
[00:44:37]
things, other things I would
[00:44:39]
like to add support for.
[00:44:41]
And I will wait a few days.
[00:44:43]
I will come back to the notes
[00:44:45]
every so often and reread it
[00:44:48]
again.
[00:44:49]
And I'm like, no, no, actually
[00:44:50]
this doesn't make sense.
[00:44:51]
Or, oh, you know what could be
[00:44:54]
possible?
[00:44:55]
Yes.
[00:44:56]
Yes.
[00:44:57]
Yeah.
[00:44:58]
So I think it's the same ideas
[00:44:59]
like, yeah, absolutely.
[00:45:00]
Write down all the thoughts you
[00:45:01]
have.
[00:45:02]
You reread it sometimes and your
[00:45:04]
subconscious will figure things
[00:45:07]
out for you at some point.
[00:45:08]
Yes.
[00:45:09]
And this can take a while, but
[00:45:11]
this can be very effective too.
[00:45:14]
Yes.
[00:45:15]
So I noticed that I'm a lot more
[00:45:17]
creative when I do this.
[00:45:19]
Yeah.
[00:45:21]
Because I need to go back to the
[00:45:22]
notes sometimes.
[00:45:23]
Yes.
[00:45:24]
So when I go on Twitter too much
[00:45:26]
and I'm doom scrolling, I feel
[00:45:27]
like I'm not productive.
[00:45:30]
But instead, when I have some
[00:45:33]
time and I go to the notes
[00:45:35]
application, then I think about
[00:45:38]
things a lot more and I advance
[00:45:40]
a lot more.
[00:45:41]
Yeah.
[00:45:42]
Yeah, I think there's an art to
[00:45:44]
assigning work to your background
[00:45:46]
processing mind.
[00:45:47]
Yeah.
[00:45:48]
But the thing is you can't
[00:45:49]
micromanage it.
[00:45:51]
You can't give it a deadline.
[00:45:53]
You have to be a good manager and
[00:45:55]
trust your background processing
[00:45:57]
and set it up for success as much
[00:45:59]
as possible and say, like, all
[00:46:00]
right, here's the problem I'm
[00:46:01]
trying to solve.
[00:46:02]
Here are the things I've
[00:46:04]
considered.
[00:46:05]
Here are my goals.
[00:46:06]
And I trust you to figure
[00:46:08]
something out and I'm not going
[00:46:10]
to pressure you to do it quickly
[00:46:12]
because that's not how our
[00:46:13]
background processing works.
[00:46:15]
But you do need a good system for
[00:46:17]
taking notes, capturing these
[00:46:19]
concepts and organizing your
[00:46:21]
thoughts in a way that's going to
[00:46:22]
trigger these creative ideas in
[00:46:24]
your mind.
[00:46:25]
And yeah, like for, you know, for
[00:46:29]
Elm pages 2.0, there were
[00:46:31]
problems that I knew I was trying
[00:46:33]
to solve.
[00:46:34]
I need to like look back at the
[00:46:35]
specific timeline.
[00:46:36]
But for over a year, I've been
[00:46:40]
trying to solve these problems,
[00:46:42]
if not longer.
[00:46:43]
For example, the problem of
[00:46:45]
being able to have pages based on
[00:46:48]
external data sources like a
[00:46:50]
content management system, CMS,
[00:46:52]
like a like a blog management
[00:46:54]
system or based on or creating
[00:46:57]
an arbitrary mapping of files to
[00:47:01]
creating new pages, right?
[00:47:02]
Like, I never really considered
[00:47:04]
Elm pages to be a proper 1.0.
[00:47:09]
I knew I wouldn't consider it a
[00:47:10]
proper 1.0 until that
[00:47:13]
functionality existed where you
[00:47:15]
could say, hey, here's an API and
[00:47:18]
take all this data from this API.
[00:47:20]
And these are the pages you're
[00:47:21]
going to have.
[00:47:22]
And I thought about so many
[00:47:25]
different ways to approach that.
[00:47:27]
I have so many notes.
[00:47:29]
I had so many interesting
[00:47:31]
conversations with users with,
[00:47:33]
you know, with Elm friends.
[00:47:35]
And I have sketches of API
[00:47:38]
design ideas.
[00:47:39]
I knew and what I did is I sort
[00:47:41]
of like wrote out here are the
[00:47:44]
problems like here are some
[00:47:46]
potential solutions and here's
[00:47:48]
why this solution doesn't quite
[00:47:49]
work.
[00:47:50]
Here's what I want a solution to
[00:47:52]
have.
[00:47:53]
Like the main challenge is that
[00:47:57]
since Elm pages hydrates a page,
[00:48:00]
all the data that that page
[00:48:01]
depends on, it's not enough to
[00:48:03]
have it when you're sort of
[00:48:05]
pre rendering the HTML for that
[00:48:07]
page.
[00:48:08]
You then need to have just that
[00:48:10]
data that was needed available
[00:48:12]
to hydrate the Elm app because
[00:48:14]
maybe you have like a listing of
[00:48:16]
all of your your blog posts.
[00:48:18]
Well, in your Elm app, you need
[00:48:20]
to have access to that data
[00:48:22]
because you need to be able to
[00:48:23]
say, oh, well, OK, display the
[00:48:26]
blog posts with this sorting
[00:48:29]
initially.
[00:48:30]
But if you want to sort it by
[00:48:32]
most popular, you can click this
[00:48:34]
thing and it's a full Elm app
[00:48:36]
that has that data.
[00:48:37]
So the data needs to be pulled
[00:48:40]
through.
[00:48:41]
Yeah, it needs to be accessible
[00:48:43]
to to the Elm application once
[00:48:45]
it's hydrated and and it needs
[00:48:47]
to be optimized.
[00:48:48]
So you can't just pull in all
[00:48:50]
the data that you used ever.
[00:48:52]
You have to have a way to narrow
[00:48:53]
down just the data that you
[00:48:55]
need, because if you pull down a
[00:48:56]
giant API response, you can't
[00:48:58]
just be like, OK, well, let's
[00:48:59]
shove that in the app bundle.
[00:49:01]
Now, now you just have all this
[00:49:03]
data.
[00:49:04]
It doesn't work.
[00:49:05]
You need to strip down the
[00:49:06]
minimum amount of data.
[00:49:07]
So I thought so hard about that
[00:49:09]
problem, but I gave my
[00:49:11]
background processing all the
[00:49:13]
information that needed.
[00:49:14]
I looked at all these different.
[00:49:16]
I mean, really, I watched a lot
[00:49:19]
of next J.S. tutorials.
[00:49:21]
I've never written a full next
[00:49:24]
J.S. app, but I know a lot about
[00:49:27]
next J.S. because I've watched so
[00:49:29]
many tutorials, conference
[00:49:30]
talks.
[00:49:31]
I've looked at how people
[00:49:33]
thought about these new features
[00:49:34]
as they're being designed through
[00:49:36]
conference talks, RFCs in their
[00:49:39]
GitHub issues.
[00:49:40]
I read through issues about like
[00:49:43]
minutia and release notes.
[00:49:45]
I follow this with Svelte kit.
[00:49:47]
And so that's the prior art I'm
[00:49:49]
putting in front of my brain so
[00:49:51]
it can do some creative thinking
[00:49:53]
about it.
[00:49:54]
Just look at a bunch of
[00:49:55]
different ideas, write down all
[00:49:56]
the ways I can imagine doing it.
[00:49:57]
And finally, that all those
[00:50:00]
things sort of mixed together
[00:50:02]
and, you know, Ryan's LMS PA
[00:50:06]
really gave me a lot of great
[00:50:09]
material there too.
[00:50:10]
And, you know, we had some
[00:50:12]
conversations early on when I did
[00:50:13]
this sort of beta version of
[00:50:16]
these template modules.
[00:50:18]
But the template modules, I tried
[00:50:20]
this design.
[00:50:21]
It was in beta.
[00:50:22]
I never actually merged it into
[00:50:26]
the main release.
[00:50:27]
It was always just there as a
[00:50:29]
beta, but that was an important
[00:50:31]
step too.
[00:50:32]
So I did this experimental beta
[00:50:34]
for template modules where you
[00:50:36]
could have these different types
[00:50:38]
of pages and for each type of
[00:50:40]
page, a blog post type of page,
[00:50:42]
an author information type of
[00:50:45]
page, you could have a specific
[00:50:47]
sort of mini Elm architecture
[00:50:49]
type thing for those pages.
[00:50:50]
But it didn't quite come
[00:50:52]
together, but that was an
[00:50:54]
important step to have that
[00:50:56]
experiment.
[00:50:57]
And finally, I realized that this
[00:51:00]
abstraction of metadata and the
[00:51:02]
rendered body was the thing that
[00:51:04]
was holding me back.
[00:51:05]
And I rethought the model to be a
[00:51:08]
pull model rather than a push
[00:51:10]
model.
[00:51:11]
So instead of, hey, you need to
[00:51:12]
define your metadata for your
[00:51:14]
page types.
[00:51:15]
Because I had this idea that what
[00:51:17]
would an Elm static site
[00:51:19]
generator be like if it was more
[00:51:21]
Elm it would be based around a
[00:51:22]
custom type, which seems
[00:51:24]
intuitive.
[00:51:25]
And then you have like, okay,
[00:51:27]
well, so what's the custom type?
[00:51:28]
Well, each page is just a custom
[00:51:30]
type.
[00:51:31]
It's like a blog post and here's
[00:51:32]
the information for that blog
[00:51:33]
post.
[00:51:34]
But then what about your route
[00:51:35]
route, just your landing page?
[00:51:37]
There's really no data associated
[00:51:39]
with that.
[00:51:39]
It's just a route.
[00:51:40]
There's no like metadata
[00:51:42]
describing that page.
[00:51:43]
It's just, oh, this is what my
[00:51:44]
root page looks like.
[00:51:47]
So the abstraction falls apart in
[00:51:49]
these one off pages.
[00:51:51]
And the more I pulled on that
[00:51:53]
abstraction, the more it didn't
[00:51:55]
work out elegantly.
[00:51:56]
And I finally had the
[00:51:58]
realization if I like I'd been
[00:52:00]
wanting to remove that
[00:52:02]
abstraction for a while, but I
[00:52:03]
didn't know how.
[00:52:04]
And I finally realized if I
[00:52:06]
if I do it with file based
[00:52:09]
routing and then the based on
[00:52:12]
the route parameters in your
[00:52:14]
file based router, you tell me
[00:52:16]
what static data to go fetch for
[00:52:18]
that page, then it solves all of
[00:52:20]
the problems.
[00:52:21]
But it was so hard to get to that
[00:52:23]
point.
[00:52:24]
Like I can describe it in like a
[00:52:25]
couple of sentences now what the
[00:52:27]
breakthrough was and the design
[00:52:29]
that that solved it.
[00:52:30]
But it took so long to get
[00:52:32]
there.
[00:52:32]
And that was your preview of the
[00:52:35]
Elm pages 2.0 episode.
[00:52:38]
That's our teaser.
[00:52:40]
That's our teaser.
[00:52:41]
So designing Elm pages 2.0 has
[00:52:44]
been so interesting because there
[00:52:46]
was that big insight.
[00:52:47]
And suddenly with that design
[00:52:50]
insight, it opened up all of
[00:52:52]
these new possibilities in the
[00:52:53]
design.
[00:52:55]
And so I've designed more APIs in
[00:52:59]
the last couple of months than
[00:53:01]
like in the rest of my coding
[00:53:04]
career.
[00:53:04]
I built a glob API.
[00:53:07]
I built an API for accessing
[00:53:10]
query parameters the way I want
[00:53:12]
to.
[00:53:12]
I built an API for the routing
[00:53:15]
because the routing the Elm URL
[00:53:17]
parser only works for a single
[00:53:20]
level route.
[00:53:21]
It doesn't let you say slash star
[00:53:24]
for a route.
[00:53:25]
There's no sort of way to do
[00:53:26]
something like that.
[00:53:27]
And I wanted a way to do that in
[00:53:28]
my routing API.
[00:53:29]
So that's been a really
[00:53:30]
interesting experience.
[00:53:32]
So one of the things I've
[00:53:34]
encountered along the way
[00:53:35]
building some of these APIs like
[00:53:37]
the glob API, for example, which
[00:53:39]
glob is just a way of describing
[00:53:41]
patterns that match certain
[00:53:42]
files on the file system.
[00:53:44]
Like kind of like a regex.
[00:53:46]
Yeah, regex like you can say
[00:53:48]
like star star slash star dot
[00:53:51]
TXT and that gives you all your
[00:53:52]
nested TXT files.
[00:53:54]
It was really interesting
[00:53:55]
designing that because it's so
[00:53:57]
there's this existing concept of
[00:53:59]
globs, right?
[00:54:00]
And I looked for prior art as
[00:54:02]
much as possible.
[00:54:03]
I looked in Haskell and Rust and
[00:54:05]
Python and Unix and all these
[00:54:08]
different APIs looked at the
[00:54:09]
documentation, looked at the
[00:54:11]
terms they used and the APIs they
[00:54:13]
exposed.
[00:54:13]
But for the most part, it's
[00:54:14]
pretty much the same.
[00:54:16]
There was no sort of globbing API
[00:54:18]
that had this concept of pulling
[00:54:20]
out pieces of the glob.
[00:54:21]
And I knew I wanted that.
[00:54:23]
So I had to come up with some
[00:54:25]
ways to do that.
[00:54:26]
But it's interesting because
[00:54:27]
under the hood, it's actually
[00:54:29]
executing the glob with a glob
[00:54:31]
pattern using like this node JS
[00:54:33]
package to match files because
[00:54:35]
you can't just do that in Elm.
[00:54:36]
But then I have Elm code that
[00:54:38]
matches that builds up a regex.
[00:54:40]
So it's the API simultaneously
[00:54:43]
builds up a regex and a glob
[00:54:45]
pattern.
[00:54:46]
It sends out the glob pattern to
[00:54:48]
node JS, gets the files, the
[00:54:50]
matching files, and you get the
[00:54:52]
full file paths back.
[00:54:53]
Then using Elm, it matches the
[00:54:56]
regex as it builds up, gets the
[00:54:58]
captured parts of it, and gives
[00:55:00]
it to your applicative glob
[00:55:01]
thing.
[00:55:02]
So when you say glob and you
[00:55:04]
capture a part of it, so you can,
[00:55:05]
like we did in our screencast
[00:55:07]
recently, you can do like glob
[00:55:10]
dot capture, and then you can
[00:55:12]
capture like the slug part in
[00:55:14]
like star star slash some slug
[00:55:16]
dot MD.
[00:55:17]
You can like pull out that one
[00:55:19]
piece of it.
[00:55:20]
So anyway, one of the
[00:55:21]
interesting realizations I had
[00:55:23]
doing this is there's this
[00:55:24]
concept of parse don't validate,
[00:55:25]
right?
[00:55:26]
And which we have talked about
[00:55:27]
before.
[00:55:28]
Yeah, it's I mean, it's a
[00:55:29]
brilliant concept and something
[00:55:31]
I think about often now.
[00:55:33]
So, you know, having an API where
[00:55:35]
you can use that like
[00:55:36]
applicative pattern to sort of
[00:55:38]
pick out pieces of it and into
[00:55:40]
structured data, that's sort of
[00:55:41]
like a parse don't validate
[00:55:43]
thing, right?
[00:55:44]
But under the hood, so I had an
[00:55:47]
interesting conversation with
[00:55:48]
Philip about this, how you can
[00:55:52]
like you don't actually have that
[00:55:54]
guarantee because I'm building up
[00:55:55]
a regex.
[00:55:56]
So under the hood, the code is
[00:55:57]
actually technically not really
[00:55:59]
parse don't validate.
[00:56:00]
It's just like a hacked together
[00:56:02]
regex.
[00:56:03]
But so that's that's an
[00:56:05]
interesting thing is like at some
[00:56:07]
level, you have to be doing
[00:56:10]
something clever.
[00:56:11]
Maybe it's at like the machine
[00:56:12]
code level that something clever
[00:56:13]
is happening.
[00:56:14]
But you give these high level
[00:56:16]
guarantees and abstractions and
[00:56:18]
sometimes you just need API
[00:56:19]
designed to do that.
[00:56:20]
You can't I mean, it's kind of
[00:56:22]
like does the type make
[00:56:23]
impossible states impossible or
[00:56:24]
does the API make impossible
[00:56:26]
states impossible?
[00:56:27]
Yeah.
[00:56:28]
And as long as it works, the
[00:56:30]
user doesn't care, right?
[00:56:32]
Yeah.
[00:56:32]
I mean, sometimes you have to
[00:56:33]
leverage like in this case, I'm
[00:56:36]
leveraging these properties of a
[00:56:38]
glob pattern to say, OK, I know
[00:56:41]
that if I limit the API to being
[00:56:43]
able to describe these types of
[00:56:45]
globs, then I know that I can
[00:56:47]
build an equivalent regex that
[00:56:49]
will that is guaranteed to match
[00:56:52]
the data in the way that I need
[00:56:54]
to to be able to give it to the
[00:56:55]
user in a in a way it's you know,
[00:56:58]
but behind the curtain, it's, you
[00:57:01]
know, pay no attention to the man
[00:57:02]
behind the curtain kind of thing,
[00:57:03]
right?
[00:57:04]
It's like you create these air
[00:57:06]
tight abstractions.
[00:57:08]
But under the hood, you sometimes
[00:57:10]
have to do some dirty hacks to to
[00:57:12]
do that.
[00:57:13]
And in that case, you just kind
[00:57:14]
of test it like crazy.
[00:57:16]
But but the you want to create a
[00:57:19]
quality abstraction.
[00:57:20]
And sometimes you have to get a
[00:57:22]
little bit clever to do that
[00:57:24]
behind the scenes.
[00:57:25]
So from what I'm gathering from
[00:57:26]
what you said before is that
[00:57:28]
there is a need to to get
[00:57:30]
inspiration from other tools and
[00:57:32]
other solutions and to just have
[00:57:35]
a broad knowledge of what is
[00:57:36]
available to you to create your
[00:57:38]
API because when you design an
[00:57:40]
API, there are so many choices
[00:57:43]
that you can make.
[00:57:44]
If you look at the Elm session
[00:57:46]
HTML library, you have the list
[00:57:50]
of attributes.
[00:57:51]
Those are all just functions that
[00:57:53]
return an attribute and you put
[00:57:54]
them into a list.
[00:57:55]
That is one kind of API.
[00:57:57]
Then you have records.
[00:58:00]
Oh, like I'm your eyes.
[00:58:03]
Maybe I don't know if I'm your I
[00:58:05]
has that.
[00:58:06]
Maybe the Elm UI button has a
[00:58:08]
record with an click because that
[00:58:11]
is mandatory.
[00:58:12]
And same with images.
[00:58:13]
Images have the alt text and the
[00:58:15]
URL because it's trying to say
[00:58:18]
images need to have explicit alt
[00:58:20]
text.
[00:58:21]
This is mandatory.
[00:58:22]
You have this list.
[00:58:23]
You have the records.
[00:58:24]
You have builder pattern.
[00:58:26]
Yep.
[00:58:27]
You have style things like
[00:58:28]
decoders.
[00:58:29]
Yeah.
[00:58:30]
You have custom types, opaque
[00:58:31]
types, not opaque types.
[00:58:34]
You have functions exposing
[00:58:37]
under the hood custom types.
[00:58:39]
And then you've got other tools
[00:58:40]
like code generation.
[00:58:41]
You've got compiler checks and
[00:58:44]
you need to have a good overview
[00:58:46]
of all those, of all that is
[00:58:48]
possible and how you can mix
[00:58:50]
those together.
[00:58:51]
And then you see whether they
[00:58:53]
make sense for your use case.
[00:58:55]
You list the pain points, you
[00:58:57]
list the benefits.
[00:58:58]
Yes.
[00:58:59]
And understand the use cases
[00:59:01]
intimately, build things with it,
[00:59:03]
get feedback from actual pain
[00:59:05]
points from users.
[00:59:07]
From what I'm getting from your
[00:59:08]
one year exploration is that you
[00:59:11]
had most of it right.
[00:59:13]
And then you had one or two new
[00:59:16]
tools to your arsenal or your
[00:59:18]
toolkits that just made the few
[00:59:21]
last pain points go away.
[00:59:23]
Right.
[00:59:24]
Right.
[00:59:24]
And it opened up a new
[00:59:26]
architecture fundamentally.
[00:59:27]
And I went through a lot of
[00:59:29]
different design ideas too for,
[00:59:32]
you know, I went through a
[00:59:33]
design idea of instead of doing
[00:59:35]
a file based router, doing a sort
[00:59:37]
of URL parser style router.
[00:59:39]
And there are some interesting
[00:59:41]
ideas there, but there are some
[00:59:43]
real problems.
[00:59:44]
And I thought long and hard
[00:59:46]
about that and had some really
[00:59:49]
interesting discussions about
[00:59:51]
that.
[00:59:51]
And I just had to sort of pick
[00:59:53]
one.
[00:59:53]
There were a couple of problems
[00:59:55]
that came up with the sort of
[00:59:56]
URL parser like API that didn't
[00:59:59]
quite line up.
[01:00:00]
But I just had to explore it and
[01:00:03]
pay attention to the design
[01:00:05]
goals.
[01:00:06]
And sometimes you need to let
[01:00:08]
something sit there for a while
[01:00:09]
like that template modules beta
[01:00:12]
that I had.
[01:00:13]
And I also had a sort of beta
[01:00:14]
for an Elm pages build step that
[01:00:17]
didn't use Webpack that did all
[01:00:21]
the pre rendering and everything
[01:00:22]
without Webpack.
[01:00:23]
And that was sitting around for
[01:00:24]
a while, but it served its
[01:00:26]
purpose, which was to I mean, all
[01:00:28]
of Elm radio dot com and all of
[01:00:31]
the Elm pages sites that I
[01:00:32]
maintain were using it.
[01:00:34]
So I was able to get feedback
[01:00:35]
from my own experiences using it.
[01:00:36]
Some other people gave me
[01:00:37]
feedback and it served its
[01:00:39]
purpose to prove out a concept
[01:00:41]
and learn from it.
[01:00:42]
But sometimes an experiment takes
[01:00:44]
a while and you need to have
[01:00:45]
patience in API design.
[01:00:47]
But then you need to know when
[01:00:48]
you need to be decisive and say,
[01:00:50]
OK, I know what I know three
[01:00:52]
options that I'm considering
[01:00:54]
that all have trade offs.
[01:00:55]
I've laid out the pros and cons
[01:00:57]
and now I need to pick one.
[01:00:59]
I've gathered all the information
[01:01:00]
I'm going to gather.
[01:01:01]
I've thought about it as much as
[01:01:02]
I'm going to think about it.
[01:01:03]
And then you have to just go
[01:01:04]
for something to wave life to
[01:01:06]
lifestyle API design.
[01:01:08]
So we've talked a lot about API
[01:01:10]
design during this episode.
[01:01:11]
Do you know when we stop talking
[01:01:13]
about it when it's good?
[01:01:16]
I.e. never.
[01:01:17]
It's never good enough, right?
[01:01:18]
Could always improve.
[01:01:20]
Could always improve.
[01:01:21]
But I mean, when people stop
[01:01:23]
talking about it.
[01:01:24]
Yeah, it's kind of a
[01:01:26]
sign that it's a good API.
[01:01:28]
Yeah.
[01:01:29]
Yeah.
[01:01:30]
With with Elm Graph QL, Elm Graph
[01:01:32]
QL has definitely gotten to the
[01:01:33]
point where when people bring up
[01:01:36]
new ideas or new things that
[01:01:38]
like, hey, I wonder if this could
[01:01:39]
be improved.
[01:01:40]
This was a little awkward using,
[01:01:42]
you know, using this part of the
[01:01:43]
API or something.
[01:01:44]
It's gotten to the point where
[01:01:46]
it's harder and harder to come up
[01:01:48]
with obvious wins.
[01:01:50]
And it's more like, OK, well,
[01:01:52]
we could try these different
[01:01:54]
approaches, but they would
[01:01:55]
actually make things worse in
[01:01:57]
this way and better in this way.
[01:01:59]
And not that the API is is
[01:02:01]
perfect, but it is getting to the
[01:02:03]
point of diminishing returns in
[01:02:05]
a lot of cases when there are new
[01:02:07]
ideas.
[01:02:08]
It's like not as much of a wow,
[01:02:10]
this is just an obvious win in
[01:02:12]
every way.
[01:02:13]
This is like so much nicer to
[01:02:14]
work with.
[01:02:15]
So I mean, it depends on how
[01:02:17]
narrowly you define the problem
[01:02:19]
you're solving to.
[01:02:20]
If you're defining a static
[01:02:22]
analysis tool, then you can
[01:02:24]
always optimize it more.
[01:02:26]
You could always squeeze a
[01:02:27]
little more performance out of
[01:02:29]
it.
[01:02:30]
Sure, but that is not an API
[01:02:31]
design anymore.
[01:02:32]
Yeah, or probably.
[01:02:34]
Well, Elm Review is an
[01:02:37]
API design space where you
[01:02:39]
could, I mean, you could
[01:02:40]
reinvent an AST to be custom
[01:02:43]
tailored to give very useful
[01:02:46]
static analysis information.
[01:02:47]
So you could have like instead
[01:02:49]
of having like identifiers, you
[01:02:51]
could have like a type that
[01:02:53]
describes what it actually is
[01:02:55]
and what it points to uniquely.
[01:02:57]
A reference, yeah.
[01:02:58]
Yeah, concrete references and
[01:03:01]
you could have type information
[01:03:03]
associated with certain parts of
[01:03:05]
the AST.
[01:03:06]
You could have a typed AST.
[01:03:07]
Don't get me started on that.
[01:03:11]
But at the end of the day, you
[01:03:12]
do need to sort of pick the
[01:03:15]
problem you're solving and
[01:03:16]
being, I mean, there is a
[01:03:19]
balance, isn't there, between
[01:03:20]
like being ambitious and
[01:03:23]
choosing a narrow goal.
[01:03:25]
There's a push and pull there.
[01:03:27]
But I think there's something
[01:03:29]
like I've noticed this is a
[01:03:31]
common thread in many things is
[01:03:35]
that there needs to be a clear
[01:03:37]
message.
[01:03:38]
There needs to be a clear
[01:03:39]
message, a clear goal, clear
[01:03:40]
purpose.
[01:03:41]
For the tool or library?
[01:03:42]
Yes, or for in an internal API
[01:03:45]
for a module.
[01:03:46]
It needs to have a clear purpose,
[01:03:47]
a clear thing that it solves.
[01:03:49]
If it tries to be everything,
[01:03:50]
then it's not going to be
[01:03:52]
anything effectively.
[01:03:53]
Like I noticed this sometimes
[01:03:55]
when, you know, like if I'm
[01:03:56]
watching a movie or a TV show,
[01:03:58]
when something really hits home,
[01:04:00]
it often has like some sort of
[01:04:03]
clear theme or takeaway or
[01:04:05]
message or lesson or idea it's
[01:04:08]
exploring.
[01:04:09]
If it's too scattered and there
[01:04:10]
are too many lessons in a movie
[01:04:13]
or too many themes that it's
[01:04:15]
exploring, then it feels muddy
[01:04:18]
and unsatisfying.
[01:04:19]
And I think the same thing is
[01:04:21]
true in anything in writing and
[01:04:24]
storytelling in APIs for external
[01:04:28]
packages and for internal API
[01:04:30]
design.
[01:04:31]
It needs to have a clear story,
[01:04:33]
a clear message.
[01:04:34]
So what you're saying is that
[01:04:35]
you don't like my input module
[01:04:37]
and my button module?
[01:04:39]
You don't like those?
[01:04:40]
They don't have clear purposes?
[01:04:41]
Is that what you're saying?
[01:04:43]
That could be a clear purpose.
[01:04:46]
It needs a raison d'tre.
[01:04:50]
I like the say cheese better.
[01:04:54]
That one was actually very good.
[01:04:58]
Well, any parting API lessons
[01:05:02]
that we want to share here?
[01:05:04]
One that you bring up a lot,
[01:05:05]
Jeroen, is this idea that to take
[01:05:08]
responsibility for user
[01:05:09]
experiences.
[01:05:10]
Oh, it's not mine.
[01:05:11]
It's not mine.
[01:05:12]
It's Richard's.
[01:05:13]
That's Richard's.
[01:05:14]
And Evan's got that in his Elm
[01:05:16]
philosophy as well.
[01:05:18]
Yeah, what Richard told me was
[01:05:20]
the author is responsible for the
[01:05:21]
experience of the tool or library.
[01:05:24]
And that is something that I took
[01:05:25]
by heart when I was working on Elm
[01:05:28]
review or Elm lint at the time
[01:05:31]
before it was released.
[01:05:32]
And that changed a lot of things
[01:05:35]
for the tool, for the future of the
[01:05:37]
tool.
[01:05:38]
And all the things that I tried to
[01:05:40]
make impossible, including the
[01:05:43]
things that I talked before about
[01:05:45]
trying to have to remove all the
[01:05:47]
unexpected behaviors.
[01:05:49]
If I didn't get that advice, I
[01:05:50]
wouldn't have done it.
[01:05:52]
I would have released it.
[01:05:53]
It would have worked, but it
[01:05:54]
would not have worked in some
[01:05:55]
cases or as expected.
[01:05:57]
So, yeah, when you're working on
[01:06:00]
the API design, do what you think
[01:06:02]
will be the best for your users
[01:06:04]
and the results will show and
[01:06:06]
people's responses will show for
[01:06:08]
it, I think.
[01:06:09]
Yeah, there's a classic scene in
[01:06:11]
the show Silicon Valley where
[01:06:13]
they're doing this user feedback
[01:06:16]
session behind a one way mirror
[01:06:19]
or something.
[01:06:20]
These users are trying out the
[01:06:22]
product and giving feedback.
[01:06:24]
And the founder who created the
[01:06:27]
tool is behind the mirror looking
[01:06:30]
at them yelling, no, you're using
[01:06:32]
it right.
[01:06:33]
And he storms into the room and
[01:06:34]
shows everybody how to use it
[01:06:35]
correctly.
[01:06:36]
But what if all this time we
[01:06:40]
haven't actually been talking
[01:06:41]
about API design and we've been
[01:06:43]
talking about problem solving?
[01:06:44]
And I think really that is API
[01:06:46]
design is a narrow thing.
[01:06:49]
Problem solving is this broad
[01:06:50]
thing that requires a lot of
[01:06:53]
different skills that you bring
[01:06:55]
to bear and taking a lot of
[01:06:57]
things into consideration and
[01:06:58]
seeing the big picture.
[01:07:00]
And that's really what it comes
[01:07:01]
down to is we're solving
[01:07:02]
problems.
[01:07:03]
Using APIs.
[01:07:04]
And tools, which you can see
[01:07:09]
kind of as an API as well.
[01:07:11]
Yes.
[01:07:12]
Yeah.
[01:07:13]
But don't forget that you're
[01:07:14]
solving real problems.
[01:07:15]
Because an API in search of a
[01:07:16]
problem.
[01:07:18]
I mean, you can solve problems
[01:07:20]
that no one has.
[01:07:22]
You can create a foobar
[01:07:23]
generator that generates foobar
[01:07:26]
baz.
[01:07:27]
Just find someone on the
[01:07:28]
street, $5 just to say the words
[01:07:33]
I have a problem where I need a
[01:07:35]
foobar generator and then design
[01:07:38]
an API for it.
[01:07:40]
There you go.
[01:07:41]
Someone has a problem, you
[01:07:42]
become a problem solver.
[01:07:43]
And you lost $5.
[01:07:48]
Well, I think that won't be the
[01:07:49]
last you'll be hearing from us
[01:07:51]
about API design, but hopefully
[01:07:52]
it was interesting.
[01:07:54]
Let us know what you thought.
[01:07:55]
If there's something else you
[01:07:56]
want to hear about, go to
[01:07:57]
elm.radio.com question and let
[01:08:00]
a friend know about Elm Radio.
[01:08:02]
Review us on Apple Podcasts.
[01:08:04]
And Jeroen, until next time.
[01:08:06]
ΒΆΒΆ