Writing Great Docs

We discuss why the baseline for Elm docs is so high, how to navigate docs as a user, and how to make your own docs great.
June 19, 2023


Hello Jeroen.
Hey, what's up Doc?
Oh, you're trying to beat me at my own game, are we?
Yes, I found a pun.
And I'm going with it.
Even if I have to destroy the holy intro that we have.
Wow, breaking with the format.
Well, I will admit, I was trying to think of a good one,
but I was having a little bit of writer's block, so...
I'm gonna say it's funny, but I don't think I get it.
But it's funny, Dillon. It's very funny. Please continue doing that.
Maybe it's just me. When I think of documentation,
the first thing that comes to mind is writer's block,
because I am sitting there staring at a blank page,
and it is very painful.
But, Jeroen, as always, we endure that pain for our dear users.
Definitely true, yeah.
We will suffer through writing JavaScript,
we will suffer through writing English words,
all in the name of our users.
And it's a very noble view of, I guess, all of us then?
I was not trying to praise myself, but I guess I am.
I don't know about you, but I do find it way more difficult
than writing the code.
Yeah, definitely agree.
So maybe before we delve into that, what is our topic today?
What is our topic? I'm gonna say it is writing great docs.
Not just good docs, because, as Evan says in the Elm Philosophy tweet,
it's not done until the docs are great.
And I do love that as a sort of foundational principle in Elm.
And, you know, I mean, when you write an Elm package,
the docs are already pretty good as a starting point,
because you are required to have, you know, types for all of your exposed APIs,
and it comes with already a really good, easy-to-navigate set of documentation
for your package.
Like, honestly, better than almost any other programming language I've seen.
But in addition to that, we go above and beyond,
and we try to make it a really good flow through reading that
and getting up and running with our package.
I thought it would be fun to talk about, like, what do make docs great?
What's the process for great docs?
What should package authors be thinking about for making their docs great?
How should users use that information to navigate docs?
And, you know, maybe whether those things are relevant
for documenting an application.
I think one of the reasons why we tend to have great docs in Elm
is because all the tools and packages that we enjoy using
have great docs.
So I think it's kind of a culture thing.
Like, in the Elm community, whenever we release something,
we write great docs.
And it's kind of the same as, like, oh, well,
every time we try to make something, we want it to be type-safe.
That's just the culture that we have.
And it's one that has been with Elm for a long time.
I don't know how long, but at least before my time with Elm.
And therefore, the expectation is that every time you write docs,
they have to be great.
And that culture at the start is very important
because now it's set in stone almost.
And whenever I see packages that are not great quality-wise or doc-wise,
it's usually by someone who has just learned Elm
and was just introduced to Elm
and doesn't have the expanded experience with Elm.
And that's fine, I guess.
They're going to learn at some point.
So when you say that you've grown accustomed to Elm packages with great docs,
can you dig in a little bit to what that means to you?
Like, as a consumer of documentation, what makes them great?
What are the things you notice about those packages?
Well, maybe let's start with the first thing that is obvious
but also quite not obvious.
The whole API of the package is documented.
If I remember my NPM days or JavaScript days,
you had a package and the documentation for that package was the readme.
And maybe like an additional website, but it's usually like the readme
and everything is in readme.
More on that later, I think.
But the API that would be in the readme,
sometimes it's just like an example like, oh, this is what you can do with this package.
You can do this, and they show a simplified example
because it's annoying to write a lot of documentation.
And then that's it.
Or they write a lot of options,
but they don't write all of the ways that you can use this package,
all the ways that are intended, nor the ones that are unintended,
which makes sense that they're unintended.
But I've seen a lot of packages where you would open an issue saying,
oh, I would love to be able to do this.
And they would answer, oh, well, you can import this submodule of the package,
and then you can do that.
But that's not documented.
And now, usually you would say, oh, now I'm using internals,
which is not good, and in practice, it's not good.
So yeah, the fact that we have to document everything in the API,
or just that they're shown in the package documentation,
is already a great point, I think, in the end.
Yeah, that definitely resonates with me.
I can think of so many times when I've been trying to figure out
how to do something with an npm package,
and I'm searching through and often end up digging through obscure issues
that I have to Google for and find some niche use of it that isn't documented
or isn't documented in a clear place.
Or I have to search through the source code and see what it's checking for.
If this object property exists, then do this.
If it doesn't, then do that.
So it's true.
By virtue of publishing an Elm package, those things are documented.
So that's the bare minimum for an Elm package.
So another part is every API is shown,
like all the custom types or the functions that are there,
all the type aliases.
But whenever you try to publish a package,
one of the things that the Elm compiler will tell you
is that some of the functions or some of the types are not documented,
and you need to.
So Elm forces you to write documentation for all those exposed APIs,
all those public APIs.
So even if you don't think you're going to write good docs for them,
you have to carefully or you have to make that decision purposefully.
But sometimes it's pretty easy to just write some documentation,
some explanation, oh, this function does this,
and then, oh, I might as well add one little example,
and now it's already very usable.
So that's like that little nudge that asks you to write some documentation,
even if it's empty, which I don't like, but we can talk about that later.
That little nudge already pushes you to write some documentation
and potentially to make it good.
So when you're consuming an Elm package,
what do those docs look like when they feel like they're really helping you?
What's your experience?
So that's one of the things I'm curious to dig into here too,
because I think in order to talk about great docs,
I think it's really good to put yourself in the user's shoes,
and we happen to be users of Elm packages
and consumers of docs for Elm packages.
So what is it, like, when we're looking at those beautiful Elm package docs
and they're not left blank and there's something written there,
what is it that makes it really good?
I think they always say, like,
this is a blazingly fast package to do something, or...
No, wait, sorry, that's NPM.
Sorry, I messed it up.
The logo really helps you understand what it does too.
Yeah, and the visual design and the 100 SCO score that you have.
No, yeah, so you usually want to look at a package.
The first thing that I see is the name of the package, who made it,
and then a description of what it does, like, what is the purpose of this,
or what does this package help you with,
then some examples of how to use it.
But also in some cases where you have, like,
for instance, if you have, like, a package to parse some format,
like, that's pretty obvious what it does.
So you don't have to explain it too much.
You have to explain how to use it, but not how it works under the hood
or what are the advantages of using this approach.
But whenever there are multiple alternatives to solution,
say, GraphQL packages,
what I often see is, like, the philosophy behind a package
or the trade-offs, or hopefully the trade-offs of the solution.
So I think explaining the philosophy or the trade-offs that are involved
in this solution versus other solutions,
sometimes even referencing the other solutions,
is a great thing to have in your docs.
Just saying it's blazingly fast is not necessarily helpful,
especially if all of them say that.
Right. Unless that's part of the philosophy, and it's a differentiator.
Yeah, but then you would need to say, like,
the reason why it's blazingly fast is either we try this new approach,
or there's a trade-off, like, this is simpler,
this doesn't handle all the edge cases,
but for the normal cases, it's going to be way faster.
And that's something that you would like to see.
Right. If it's a drop-in replacement for Dict, except faster,
that's the only thing that's different, then that's what you want to know.
And that's a good way to describe it.
And maybe in that case, the package can also handle the more complex approach.
And then it's just, like, having the best of both worlds, I guess.
Maybe in a somewhat convoluted way, maybe. Who knows?
Mm-hmm. Mm-hmm.
But yeah, I think I'm looking for all those.
So, description, examples, philosophy.
But yeah, description of the APIs, and how to use those,
and how to use them together through a guide or through examples.
That's the kind of things that I'm looking for.
Right. Yeah, that resonates with me as well.
I would say, when I go to open up the package documentation for an Elm package,
as you say, often there will be a few alternatives, and they have the same name.
They're all called ElmGraphQL, or they're all called, you know, ElmAnyDict,
or, you know, ElmMarkdown, or whatever.
And it's like, okay, well, so I want to know.
The first thing I'm trying to find out as fast as possible is, does this,
number one, can I use this for the problem I'm trying to solve?
Because if I can't, I don't even want to consider if I like it,
or if I like its trade-offs.
Just like, what problem can I use this to solve?
Is this a faster dict? No, this is ElmGraphQL.
Okay, well, then I'm just going to skip it.
Right. It better tell you that quickly.
And you don't have much of, I mean, I can say this from personal experience,
as a reader of docs, you do not have my attention for very long to tell me that.
And if it's like a very long-form description of things that doesn't just,
like, right at the top in a topic sentence tell me exactly what it's doing
and give me information to help me decide whether it's going to help solve my problem.
I might not stick around there.
It's certainly not going to be a good experience looking through that package documentation.
Because that's the first question I want to answer.
Yeah. The thing is, like, the Elm ecosystem is pretty small for good and bad.
And in this case, like, if you have two alternatives, or even like four,
like, you can spend a little more time digging whether a specific package works for you.
Whereas in JavaScript, like, you have 200 packages for the same thing.
And yeah, you really want to go to the one that strikes you the most.
Right. But if it's like a somewhat subtle thing, if it's like, oh, it's a form package,
and I look at the readme, and I don't understand, like, what are the limitations?
What are the, like, what are the things I can and can't do with this?
Then I'm not going to, and if I can't, like, find a live demo somewhere,
then I might not dig into that much further,
because I'm going to end up going to something else.
And it's going to have this mystique that I'm not going to want to approach it.
Yeah, what you say is that you want to know about the limitations of a package.
You want the package author to be honest about something, even if it sells not as well.
Yeah, totally.
Which is okay, because we're usually not getting paid for open source work anyway.
Yeah, I was just having some interesting discussions with Ryan,
the author of Elmland and Elm SPA,
and we were kind of talking about, you know, Elmland or Elm SPA.
Elmland is sort of a new version of Elm SPA versus Elm Pages,
because v3 is sort of blurring the lines a little bit,
because with Elm Pages v2, it was a much more narrowly scoped tool
that was generating static sites.
And with v3, you know, a site with authentication,
you know, a site with user-specific dynamic content
is now a use case that Elm Pages v3 supports.
So it starts to get blurry.
But so I tried really, it took a lot of thought actually,
to try to concisely summarize what is it that would make something not a good fit for Elm Pages v3.
And what I finally came up with is like,
well, there's this sort of architectural decision that you're making by using Elm Pages v3,
which is that I would like to build an application which communicates with an Elm backend.
That backend could be your build server as you generate your static pages,
or it could be a traditional server, or it could be a serverless function.
But there's some sort of Elm backend context,
and every time you navigate to a page,
it is communicating with that Elm backend to resolve data
and then sort of bootstrap the page with that data.
That, I believe, is a very compelling pattern,
but that's a big architectural decision.
And I think that it's a compelling story that is a very strong way to build an application.
But there are definitely certain clear-cut cases where it's not going to be a good fit.
For example, if you're building a one-screen game,
then that set of features isn't going to really help you with that.
And loading textures and things like that is not what the Elm Pages backend task abstraction is designed for.
It's not good for going off for 100 seconds and loading something.
You want that done asynchronously in the background.
It doesn't mean you can't use it, but it's not optimized for it,
it doesn't really need the features that Elm Pages v3 gives you.
Yes, exactly.
And similarly, you can use that rule of thumb,
is this feature set serving my needs for the use case I'm building for?
And do I want to make this architectural decision?
So that's what I finally realized is,
OK, a lot of the more dynamic apps with authentication and things like that,
you could choose Elm Land or Elm Pages.
But the differentiator is, do I want to use this architecture?
Because there are trade-offs that are associated with that.
Do you want to go all-in on using things that having a backend allows you to do?
Cookie-based authentication and being able to do server-level redirects
and being able to submit form data and handle that in the same context
using the form API.
If you want to use those patterns in that architecture,
then it's going to be a good fit.
But not everybody is going to want to use that architecture.
So that's the differentiator.
And it's not like one of them is good or bad,
but you need to tell people very clearly,
you need to help them make that decision.
Because you're the author of the tool, you have all of this context,
and somehow you need to distill that down to the relevant information
that someone needs to make a decision.
Because that's what they're trying to do.
They navigate to your tool, and the first thing they're trying to do
is make a decision, is this relevant to me?
Is this usable? And are there alternatives that would be better?
So you need to help them make that decision.
Absolutely. Yep. I think you're completely right.
And then once you've decided that, you probably want to figure out
how to use it.
And again, if you land on a tool, you go to the Elm Review package docs,
you understand what sort of use cases it helps you solve,
you decide you want to use it.
How do I actually do something, anything, right?
As quickly as possible.
I think of this actually very similarly to how I think about
the coding discipline of do the simplest thing that could possibly work,
or finding the shortest path to green,
and taking small steps to connect things where it's working in between.
You don't want a long feedback cycle where it's like,
okay, do these 100 steps, then you're all set.
Then you can evaluate whether you like this package or not.
Then you can evaluate whether you like it,
and then you'll know whether you've done those 100 steps correctly
at the end of the 100 steps.
Oh, yeah. There's that thing as well. Yeah.
I was still in the evaluating phase of the package,
where you want to know whether it fits your use case well,
and whether there are any gotchas with it.
Exactly. Yes.
That's going to be a super frustrating experience on both accounts,
where you're holding your breath, am I doing these steps correctly?
You're like, I don't have time to do 100 steps.
I haven't decided whether to commit to this yet.
I'm not like, I'm integrating this tool now.
You need to give an ELE demo.
You need to give a quick start guide.
I'm not sure if Elm Review has this right off the bat,
but the Elm Review dash dash template flag is a great way to try it.
You can say, here you go.
Here's a single command.
You npm install dash g, and then you run this one command.
You don't even need to do that.
You can just install it through npx.
You can just npx it.
Or you used to be able to. I don't remember.
Sure enough, you do have that first thing.
You've got it under a nice simple heading.
Let's look at Elm Review's docs.
The first thing I see is a topic sentence.
Elm Review analyzes Elm projects to help find mistakes before your users find them.
Screenshot. That's awesome. Now I know what it does.
I get a taste of the experience. It's a single sentence.
It's not like a paragraph that I'm like, oh, do I have to decide what's meaningful in this long paragraph?
It's a single sentence. Then there's a new heading.
What does Elm Review do?
It gives you a sense of what you're going to use it for.
And then there's a new heading. Try it out.
And it does indeed have the quick start command npx elmreview –template. Amazing.
I am a happy customer if I'm going through these docs and trying to evaluate whether you use this.
I'm glad to hear it.
But I'm sorry, I'm not actually going to pay you. I was a little misleading. Sorry, Jeroen.
It was worth the effort of doing all those years of work just to see whether you would pay me.
I would hypothetically pay if there were a way to do that.
As you say, you want the quick feedback. So there are multiple ways to do that.
So in the case of Elm Review, it's a bit of a peculiar story where you have to install the tool and then choose your configuration.
So I have this try it out section that tells you to use templates with a predefined configuration.
But that's like a pretty specific use case.
In most cases, let's imagine you just have a dict package.
Then just a simple example that you could copy paste into a REPL, that would be very sufficient.
You don't need anything more.
And that's great because it also makes a perfect example for your function and it makes it very clear what something does.
So that's great.
In some cases that are more complex, you could have a bigger code section.
But if you want people to play with it, like something visual or it's something a little bit more complex.
Let's say you want to parse a CSV, then you could write an LE.
The problem with the LE though is that sometimes I think it always uses the latest version of your package.
So older versions of your package will have documentation that is broken.
Maybe that will be fixed in the future.
But the essence of the idea still holds is you want something that people can just go to without having to install your package,
without having to create a small new project to try things out.
You don't even have to force people to use Elm Reactor or something.
And they can play with it and they can see, does this fit? Does this work?
Yes, no, let's go to the next package or let's try to fix my immediate problem that I was trying to solve.
In the case of something like Elm GraphQL, that is about code generation, that's a lot more complex.
So I think what you do, or I know what you do, is to have an example project where you have a GraphQL schema from which you generate a lot of code.
And if I remember correctly, you checked in the code so people can see it, can see the generated code.
And that I find to be very important.
I've seen a few places where you had to go in yourself and regenerate those files to see what they looked like.
But I wanted to see, if I'm curious or if that's important to me, how those generated files will look like.
So I think it's very important that you check them in so that they're available on GitHub and people can just see them.
So that's a very good thing that you've done.
But yeah, sometimes you need a project, sometimes you just need a code sample.
Whatever makes it very easy for people to try it out is good.
Right. Yeah, as you say, because it involves code generation.
If it's just a vanilla package, then if you can give an LE link, I really think you should.
Because if you can give a simple self-contained, like LE requires that it is self-contained.
So if you can give that, it forces you to write a very simple example.
And it gives people something shareable, they can play around with it.
And they can extract it into their own project from some working code.
Not some readme comment that they have to trust.
They can actually see it and even tweak it and then integrate it into their project.
I think that it's really good to have a variety of different types of content as well.
Because different people, and over the course of doing different types of tasks,
will want content presented in different ways.
So for example, the source code itself, even like you said, generated source code.
Perusing that, some types of users might want to say,
well what is this actually doing to understand it?
And someone else might never want to look at that.
They might want to look at an actual working example,
even if it's not an LE because it's using generated code.
They might want to see a deployed example that makes GraphQL requests
and inspects the network tab.
And someone else might want to play around with it locally and run the code generator.
Someone else might want to read a short guide.
And these are all different formats that people have different styles.
And some people read through the test folder.
I actually pretty commonly will go through a project and read through the test folder.
I consider that to be part of a project's documentation.
Yeah, I agree.
Part of the reason why I would like to see your generated code is because,
or for Elm GraphQL, is because a code generator will produce Elm modules,
and that's the API that I have to work with usually.
So whenever I choose to work with a package, I want to know the API.
And if your API is the generated code, then I kind of need to see it.
Otherwise, it's very blurry or very fuzzy for me.
Yes. Actually, that makes me think, like,
I probably could take that Elm.json file or the docs.json file
for the generated Elm GraphQL example that hits the GitHub API.
What is the docs.json file?
Docs.json file is an encapsulation of all of the exposed values,
types, and functions for a package along with their corresponding documentation.
So that is what the package docs present to us in a nice format on a site.
I could ship those.
I could ship that and then give people a link to documentation.
That would be another cool way to present that information for a generated API
using Elm Doc Preview.
Aha, Elm Doc Preview. What the hell is that?
Elm Doc Preview is a very good tool.
We're in the glossary section.
That's right. Exactly. Exactly.
So Elm Doc Preview is a really nice tool that lets you, on your local machine,
it lets you do a live reloading server that will show you your package documentation
for an Elm package or compiler errors if there are any.
It live reloads as you update your documentation.
It's an invaluable tool for package authors.
You can also use it for displaying documentation for application code as well.
You can define an Elm-application.json file,
and you can use it for in-house application documentation too,
which is kind of interesting.
Or you can use the Elm Doc Preview site to share links to people that will present your,
if you check in the docs.json that you get by running elm make dash dash docs equals docs.json,
that's how you generate the docs.json file for an Elm package.
If you share that, if you commit that docs.json,
then you can share a live preview link of pending documentation.
And that is a really valuable process for unpublished packages that you want to get feedback on, for example.
Absolutely. Yeah, it's a really valuable tool because often you're writing documentation
and you're not sure how it's going to look like,
and you write it and then you don't notice problems with it.
That will be obvious once you've published it.
So for instance, like if you indent some code in your documentation,
that will be displayed as Elm code.
But if you don't indent it correctly, then it might not be showing that way,
and then everything will be on one line and it will be very ugly.
So you don't want to notice that after you've published it.
You don't want to fix a documentation issue, republish,
see that it's still not looking okay, and then republish, and so on and so on.
There are also a few things that the Elm package website doesn't handle correctly,
like markdown tables.
Great idea in theory. In practice, it's not looking great at all.
So don't use those.
And if you use Elm doc preview, you will notice that it's not going to look okay.
There are a few issues, like if you have the add docs annotations
that are done in slightly incorrect ways,
then they're not going to show up okay in the package website.
So you also want to know about those early on.
So yeah, Elm doc preview, awesome tool to write your docs
and to be able to read through them in a much easier way,
like the way that your users would.
Instead of having to go through your docs that are spread out all over your Elm code,
which is not as great an experience.
It's also very motivating to see what needs documentation and what doesn't
and just read through a live preview of your docs
in that layout of a package documentation site.
And for me, just seeing something I don't like or that's missing
makes me want to go write it.
So I started to get that little kick to go do it.
There's one thing where you have to have a valid docs.json file
to be able to preview your docs.
And that means that you need to have written documentation
for every exposed thing.
In practice, if you want to see it really quickly
and without documenting some of the modules,
then you're going to add like empty documentation comments or to-dos,
which is probably a little bit better because it will be more obvious.
Yeah, that's a good idea.
I usually do empty ones, but yeah, to-dos is a good idea.
But I do it in tandem with your no missing documentation Elm review rule.
I think that's what it's called.
Yeah, docs.nomissing.
We can talk about those afterwards.
I kind of mentioned this before, and we got lost in thought, I guess.
But the one reason why we have good documentation in Elm
is because there are sections built in.
So every module that you expose is a different page or different section,
however you want to call those, on the package website,
meaning that not everything has to be in the README,
which I think is pretty good.
Not sure whether it's sufficient.
In a lot of cases, yes.
In some cases, maybe not.
But it's a pretty good thing to have at least.
So yeah, just the fact that you can separate some documentation from others
is good.
So for instance, if you have like one module,
which is a lot of tiny helpers with plenty of functions
that are not very important to the rest of the API,
then you can separate those into one module,
and they don't pollute, they don't waste space
for the more important modules of your API.
So I think that's a pretty important part of why docs are pretty good
as a starting point.
I totally agree.
And just to put a fine point on that mechanically,
what you're describing is so when you're writing an elm package,
you can write doc comments, which is just a special comment format.
You can write that for your top-level values,
and you can also write that for the module.
So the module has its docs, and each function and exposed value has its docs.
You use an annotation in the top-level module's docs
to list out all of the exposed values that you want to document.
So you can group them into sections.
So you can say, wiring up a form,
here's the init update view function for the form.
And then you can say, defining a field,
here's the checkbox and whatever functions for building a field.
And you can group those into sections using Markdown
because the docs use Markdown format.
So if you use the hash hash for an H2 heading,
it separates it into headings.
And you can even link to those headings.
So you can link somebody.
You have to sort of open up the inspector tool
and click to it because it's not linked at the moment,
but it gets the job done.
Yeah, you kind of have to guess also what the name of the link is
or what the href for the link is.
I find it with the inspector.
I click on it with the web inspector, yeah,
and then copy that.
You can try to guess it, but if you have something
with a few weird symbols, like, yeah,
just use the inspector.
That's going to have a better time.
Yeah, but that's invaluable.
And I would say, like, I notice when I'm reading documentation,
I will very frequently, like, often the only thing I read
in a whole page are the headings.
And that tells me whether or not something's relevant to me.
So, like, for example, like, you totally nailed this
for my preferences of docs, but I think this is
a pretty general best practice for docs in Elm Review.
So the review.fix module in Elm Review,
the top level describes what are fixes.
Like, what the heck is this thing, right?
Of course, there's a whole set of associated types
and functions and everything, but up front it gives you
a few things.
So it says guidelines, right?
Maybe that's the first thing you want to know.
Like, I'm writing a rule.
When would I write a rule?
But maybe I don't care about that right now.
Maybe I'm trying to, like, I know I want to write a rule.
I'm not looking for guidelines.
I want to mechanically do that.
Then you keep scrolling.
You don't look at that heading, right?
When, parentheses, not to provide an automatic fix.
If you are looking for the mechanics of it,
again, you keep scrolling.
Okay, now creating a fix.
Ah, maybe that's what I want to do.
Now you see the associated types and values
for creating a fix.
So, yeah, I think, like, I think when writing documentation,
it's important to keep in mind how people are going to consume it,
which is they're going to skim, they're going to skip over
headlines they don't care about, and that's okay.
That's not a personal failing of yours that people don't read
through top to bottom.
That's just not how people operate.
They don't read headlines.
They're there for a reason.
Put them in the top for a reason.
But it really is a choose your own adventure.
Actually, before we recorded, you and I were talking about
Zelda Breath of the Wild and Tears of the Kingdom and how cool
this sort of choose your own adventure style of, like,
an open world game is where you can bounce around.
You can, whatever, go straight to the final boss and skip all
the powerful items and upgrades, or you can do every possible
side quest and learn every mechanic of the game before
going to the final.
You can do it whatever order you want.
That's how people do docs.
They go in to do a job, and they're not going to do it in
the linear order you expect most of the time, or they might.
Yeah, first of all, like, I think, as you say, like,
you can skim through the headings, and I think it could
be valuable to have, like, a table of contents at the top
of every page or somewhere on the page.
That could be interesting.
But, yeah, so you describe the mechanics of how you write
documentation for a module.
So at the top of the module, you have a module documentation,
so curly braces, dash, pipe, some text, and at the end,
the closing, dash, curly brace.
And in between those, you write some text, some headings,
and references to functions.
And the thing that I really like about this is you decide,
based on how you write or where you write those add docs
things, how things will be presented.
So if you, the way that the docs are shown do not depend on
where they're defined in the page, because they could be,
because in Elm there's hoisting everywhere, so the Elm could
choose, well, this function is defined before this one,
so I'm going to show that one before.
But no, it's you define by saying add docs function A,
and then later add docs function B, that function A will be
displayed before function B.
And if you put it in a section or after a section header,
then it's going to show up in that section.
And you can write those, the things however you want.
So you can, as you say, you can write a story.
That's kind of like how I like to see it.
If you have a story to tell, if like maybe you don't need to
have a story for writing a dict package.
But in the case of Elm Review, there's a lot of things that I
need to explain, and some of them need to be first,
like guidelines, which I think when they're not details,
should be read before you read the rest.
Although you do whatever you want.
But like I'm expecting that by default people will read
things from top to bottom.
And you can, yeah, you can choose how things will be
displayed, and I found that to be very powerful.
And there's a cool thing that Elm format does as well
with add docs.
So whenever you add those add docs things,
it changes the exposing clause in your module,
the first line in your module declaration.
So module, module name, exposing,
and then plenty of things.
So if you have add docs for those,
it will reformat them in the same order.
So if you have add docs ABC and then next line add docs DEF,
then you will have two lines, ABC and then DEF,
in the exposing clause.
And I use that as well in my application code.
So if I want to make a, let's say, button module,
then I write the documentation so that the API is shown
as a table of contents in a way at the very top of my file.
So usually I have something like a section how to create,
and I have the type of the button,
so add docs button, and then also the init or the different
variants for init.
And then I have a section with all the with functions
if I choose to do so, and then a section on how to transform
that to HTML, something like that.
And then you can see in the exposing clause, well,
button, comma, init, next line, with something, with something,
with something, and at the end to HTML.
And you can even put those things onto more lines,
so you have with large size, with small size, with medium size,
and then on the next line with color red, with color blue.
These are terrible examples, but the way that I try to do those
is I write them so that every line deals with one thing.
So if you want to deal with the size of the button,
you have one line with all those.
If you want to talk about the color, you have one line
with all those, maybe also the color type or something.
And I find that just to have this thing is already very valuable,
so that's what I do in my application code,
and that's why I push at work to have some documentation,
even if it's just for that, because that makes things a lot simpler.
Cool. When you say some documentation,
are you mostly doing empty doc comments for internal things,
or do you actually write something for these internal APIs?
So if it's for application code, I usually don't write any documentation
because it's not mandatory there.
I try to have better names.
Usually things are pretty self-explanatory,
especially because at work we have this way of working,
we have several patterns that we use over and over.
So once you know it, you don't have to explain things again.
So it does not make sense to explain button.withColor
when you have so many other modules where you have withColor as well.
It makes a lot more sense in package code, I think,
because people might be new to the pattern,
and you also don't need to have as much of a great documentation experience
maybe at work, especially when you have colleagues that are right next to you,
and because for application code, time is more precious
than for open source maintainers.
I think it would depend if there's that one part of the code base
where new hires have to pair with the one person at the company
who really actually knows how to use this thing
in order to get up and running,
then maybe that is a case where it's good to do that.
I would say, because I am a big fan of self-documenting code,
and I will admit I very rarely write documentation for application code,
and I don't think that's a bad thing.
I would say, to me, I'm not a fan of writing documentation
when a good name would achieve the same thing.
I think it's sort of like going down the chain.
I think you should always prefer communicating something
through good naming and good API design when possible,
and also through a good, easy way to use something.
A command line interface and automation rather than a cookbook.
Here are five different steps, right?
That might be calling out to automate it.
So like, oh, I was a good boy and I wrote some documentation for this.
It's like, well, maybe you weren't such a good boy.
Maybe you should have automated that.
It's also because function names, that's what you see when you read code, right?
You can go to the documentation in your IDE,
sometimes on GitHub or through searching for yourself in the package website.
But it's much easier to see the function name,
and if that conveys the meaning, then that's all you need.
When I write bash code, or sorry, when I look for a command to do something
and someone on StackOverflow says,
oh, you should run this command, pipe this command, this slash pipe this command,
and I'm like, I know none of these commands,
or there are some of these flags that are new to me.
Well, now I need to read the documentation.
So hopefully it's going to be good, but like for bash commands,
everything is in like one manual.
It's one readme-ish document.
So it's hard to find the information you want sometimes.
And also, like, there's just a lot of things.
So yeah, good names is always preferable.
But if you can have both, go for both.
Right. And also, I mean, this is sort of maybe so obvious
that we wouldn't think to mention it here
because we sort of talk about this all the time.
But the API design is part of the documentation.
Where you look to find a function, how you group together,
and it's actually something I put a lot of thought into.
It's actually quite difficult to get right.
Do you have one big module with a bunch of things thrown in there?
Do you have a lot of small modules that have things clearly separated?
I think the way you organize things into Lmodules
is really a part of how you're presenting these concepts to users.
What should they think of together?
But it's also an ergonomic thing.
How many imports are they going to need?
And also, it's a sign.
If you have a lot of modules, is that a lot of concepts?
If you have a lot of modules,
but you end up having to import all of them every time,
that might not be a great experience.
So it's really a delicate process to slice things up into a meaningful way.
But you're presenting these ways of grouping things,
not just in terms of how you import them, but how you think of them,
and also how you read them.
Because Elm package documentation is organized by module,
it really is like the pages in your documentation.
And I think that's a brilliant part of it.
And again, back to your sort of opening sentiment
about the baseline for quality in Elm documentation.
I think that's part of it, is things are grouped
where a module is a page in your docs.
And so you import one thing,
you read the docs about that one thing you imported,
and it's all grouped together logically.
So that really shapes the way I write documentation.
So for example, like in Elm Pages v3,
back-end task is a big part of it.
So I have a back-end task.env module,
which is a pretty small module.
It's got a couple of things for expecting to get an environment variable
and errors that can come up when you do that.
Or back-end task.file, that is a module that is, you know,
knows how to read files that might have some front matter formatting in them,
or it might not, read a raw file, read a JSON file, things like that.
It's a pretty mechanical module, but it also sort of describes
that this is something that Elm Pages allows you to do, read from files.
But it also references back to the back-end task API,
because this is the sort of core abstraction.
And so this module has really, it breaks down the mental model.
It breaks down a conceptual guide,
and it links out to all these different ways you could use it.
So this is the opportunity to present these sort of key concepts
and the API for how to use this basic building block.
And then that in turn links out to all these more specific ways to use them,
like back-end task.custom is another huge piece, right?
It's allowing you to run arbitrary code in a JavaScript environment
that you can have access to and encode data to send to it, decode data to get back.
It calls an async JavaScript function, right?
That is, that's a big concept to chew off.
If that was part of the back-end task module's documentation,
it would be kind of overwhelming.
It would be bombarding the user with information to understand
when they might just be like, what is a back-end task?
I'm new to Elm pages.
But it's linked to, and there's a short set of bullet points at the top of back-end task.
So it describes what is a back-end task in a concise one-sentence way,
and then what are the ways you can use it?
And you can click to those and choose your own adventure from there,
or you can continue reading about back-end tasks.
So I think the way you group these and organize them is really key.
And again, it's like one module is one page in your documentation.
So the title of that module, the things that are grouped there, the scope of it,
how many concepts go there are all really important considerations.
Yeah, and it's really hard for us to choose what goes into each.
Like, should I split this thing into more modules?
I know, for instance, like Elm UI has asked themselves the same question,
like, oh, I have all these borders.
Should that be their own modules or module?
Or should it be in the same module as these other things?
And sometimes it's clear-cut, sometimes it's pretty hard, or it's arbitrary.
It actually requires some surprisingly nuanced technical tricks in the Elm code sometimes
to support that, like a common one being avoiding circular imports.
And so in Elm, the Elm compiler forbids things importing each other.
Modules can't import something that imports them.
And therefore, you often need to use this trick of sort of having something scoped
where it's exposed to the package.
What that means, we've talked about in previous episodes,
you have an Elm.json file, which for an Elm package gives you a key of exposed modules.
And that's where you list out the modules that will be part of your public API.
But you can have some non-exposed modules, and those non-exposed modules
allow you to have types and functions which are visible to your whole package,
but not visible through the exposed package.
But you can expose them through type aliases, which is why you'll sometimes see
type alias element equals element.
What does that mean?
Well, it's a package private one.
Yeah, it's a trick.
And so that means you have a lot of possibilities for where you can expose something.
Which module do you expose that type alias from?
It could be a lot of different ones.
But I think there is something really key.
Evan has talked about this in, I think, his talk, Growing Elm Modules.
Wait, no, sorry.
Life of a File.
Evan's talk, Life of a File, he talks about how there's something to how you
grow and expand an Elm module that is centered around a type.
And I think that's really true.
It should be usually one central type and some key things on how to use that thing.
I mean, something like a data structure would be a queue structure or a stack or something like that
would be an obvious example of that.
But also back-end tasks.
But back-end tasks, again, it's subtle because there are other ways to consume a back-end task,
like back-end-tasks.custom.
So that's a module associated with that same back-end-task type, but it's some helpers
that actually aren't built around a specific type.
So it's not always clear-cut.
While we're on the topic of things that are too basic to mention, but let's mention them anyway,
one reason why Elm docs are great is because they're simple, or because Elm code is simple.
Like, I'm not going to praise it even more than I usually do, but do I?
But the thing is, whenever you have simple code, like a function that adds to numbers,
well, you have the function, you have a code sample, so if you say add 5, 7,
you can show that it does 12, returns 12.
And that's it. There are no side effects, there are no mutations.
This is all that your function does.
So it's really easy to understand, whereas in languages like JavaScript,
if you look for the documentation for arrays, the list-like type, not the erasing something,
so if you do.sort on an array, it's going to mutate it.
And that's something that can be hard to convey without mentioning it explicitly,
and that can be surprising as well.
But you don't have those things in Elm, so things are pretty simple.
And also, every function shows the type annotation, and that's very valuable as well,
because a type annotation gives a lot of information, and if you don't have it,
which is, again, the case in JavaScript, often less so with TypeScript nowadays,
well, yeah, you have a lot more information that you can use to understand what it does.
So Elm is simple, and that has great benefits, basically.
Yeah, like if you're working with a different language, JavaScript, Ruby,
it's very common, like, is this nullable?
Which, of course, TypeScript somewhat helps with, but only somewhat.
How many arguments does this take?
If I pass in zero arguments, one argument, two arguments,
are there different variadic forms of this, different arities for this function?
Is it going to return different types based on the input I give it?
Is it going to mutate the input I give it or change some global thing or perform some side effect?
Is it going to do one thing if I pass in an object, and another thing if I pass in a string,
and another thing if I pass in a buffer, and another thing if I pass in a promise?
Does it return a promise or a non-promise?
Is there going to be any effect if I just import this file?
Right. Can it throw an exception?
Yeah, what are the possible crashes that this can have?
Right. All of these things, they are just non-existent in Elm,
so I couldn't agree more.
The explicitness already, your confidence in navigating an API is so much higher
from just the bare minimum documentation in an Elm package.
Also, I mentioned, if you have a code example, like add 5, 7,
and you add a line underneath that says, this returns 7,
there's a... often what you will see is dash, dash, greater than.
So it basically does an arrow, and then 7.
And there's a tool, which is called Elm Verify Examples,
that will turn those code examples into actual tests.
So you can be sure that your code sample is correct,
that it returns what you said it did.
So that's pretty powerful as well.
I really wish I could use this for Elm review in some way.
I know. I wish that it could verify that things compile,
but unfortunately it doesn't at the moment.
But it is a very valuable tool.
Also, on the topic of code snippets, I think that's also huge.
Just having code snippets, example, output,
like when you're navigating functions in an API,
again, you want to equip the user with what is the relevant information
they're going to need.
And really, even if it seems obvious from the type signature,
having an example is very useful.
It demystifies it, and it makes it feel real.
And it's also an opportunity to give an example use case.
So I think, in my opinion, I feel very strongly that every example
is an opportunity to demonstrate a meaningful use case,
which is why I try really hard to avoid toy examples,
foobar baz, and instead try to illustrate,
why would I use this feature with real-world use cases?
Because now you're using that opportunity to convey information
in a richer way.
People are taking the same amount of time to read through something,
but you've now conveyed the kinds of things this tool would be helpful for
and best practices for using it.
Yeah, this is something that I do purposefully in Elm Reviews documentation,
is for every function that you can use to define a visitor or something,
I add an example rule.
So that's a lot of code, potentially, but you can see,
oh, I can use this for this, and also this is how you use it.
But if you just go through the documentation, you can already
just copy-paste some rules and adapt them to your needs, potentially.
I find that to be pretty interesting.
Yeah, as an Elm Review user, I find that extremely valuable.
Also, just imagine looking at with simple declaration visitor,
and you're seeing, okay, it takes a function,
no declaration, returns, list of error,
and a module rule schema, and it returns a module rule schema
which has at least one visitor.
That's okay. Yes, that's telling me some information that's useful,
but without an example...
Really hard to use, yeah.
Yeah, like, okay, well, what do I do with node declaration?
Well, actually, what you're most probably going to do
with a node declaration is you're going to do
case node.value node of, and then you're going to enumerate
the different declaration types which are imported from this module.
Also, I'm a big fan of being explicit about the imports
because you want to make it clear.
And actually, Elm Verify examples does not require you
to qualify the module you're using,
but I prefer to be explicit about using unqualified module
or qualified module references,
so putting the module name before explicitly
and putting the import to the module itself,
even the one I'm documenting.
It just makes it more copy-pastable and more explicit.
Exactly, yeah.
And also, like, when you don't qualify something in your example,
it can be hard to know whether the function that you're referencing
is defined in this file, which in some cases it will be,
or if it's a value that is somewhere else in the code snippet
or that is just omitted because it's too complex to do for the example
or too annoying to do.
But if you qualify it, then it's a lot more explicit,
and as you say, copy-pastable, understandable.
And we usually in the Elm community advise for qualified imports anyway,
so let's use that in the examples anyway.
That's my take at least, and I know you agree with this.
Yes, and also it should be as idiomatic as possible in every way.
Now, of course, people do have different styles,
and there's nothing wrong with that.
Some people really like using unqualified imports in certain cases,
and that's totally fine, but I think that it's important
to try to write in an idiomatic format.
Like, for example, if you're going to use an import alias,
use that everywhere and use it consistently in your examples
because the thing is, everybody who uses your package
will probably use that same example that you use
because you're sort of defining a convention,
whether or not you realize it, and so you should put some thought
into how you want to import things, not just like,
well, for the case of this example, I'm going to use a one-letter import
or something like that.
People take it and run with it.
Also, so naming things is important.
I am personally a fan of giving meaningful names to type variables.
Now, in the case of, you know, dict comparable a,
that might be fine.
I don't know. What do you think about that?
I think that if it's for something generic, like purposefully generic
and extremely generic, then a is just fine.
If it's for functions like map, where you have a something a
that transforms it to something b, that's very fine as well
because people understand it.
If it's for anything else where you have more insights
about the type variable, what it will hold,
what it does represent, then I would use that instead when possible.
Right. Yeah, like in the Elm Review docs, I see you, for example...
You use schema state, I think.
Schema state, module context.
So that is... and you use the naming consistently.
So at least that gives people a sense of what name to use.
I'm trying to remember in ElmGraphQL, I made a change as well
at some point where I was not using a type variable.
I didn't have a good name for this type variable,
and it actually made it a lot harder for people to understand the context.
What did I name it? The scope.
Okay, so yeah, in ElmGraphQL, a selection set decodes to a value,
and it has a scope.
The scope is a phantom type.
This is a confusing concept to introduce to people, right?
Some people might not have used a phantom type.
Some people might have used a phantom type,
but might not understand how that applies to the concept of GraphQL.
So a selection set, you know, we've discussed in the past,
it is scoped so that you can't, you know,
if you are able to take a selection set that represents getting the first name
in the scope of a user,
you can't get the first name in the scope of a product.
So the scope phantom type variable defines the scope of your selection.
So I tried to use domain language there, and I think that's really key.
Yeah, the type variable is usually something a little bit more abstract.
So especially if it's used to represent some constraints,
then it's worth giving it a good name,
like even if it's just constraints, I don't know,
I use something somewhere maybe.
Yeah, and naming is also a process.
You can always iterate to get a better name.
You don't have to get everything around the first try.
I think it's really important to notice where do you find friction
as you're reading things?
Where are you finding that you're conflicted about what names to use?
You don't have a clear concept in your head of what term to use for something.
Where are people getting confused?
Where are you finding it difficult explaining a concept to other people?
I think it's important to deliberately invest time in giving names
to these abstractions and ideas and iterating on names there.
So those are things to look for.
Also getting feedback from users is key, of course.
You mentioned at the beginning that whenever you thought about
binding documentation, you were thinking about Varisblock.
So as you say, writing documentation is sometimes not the most fun part
and it can be very hard.
Also, how much should I detail?
What should I detail?
It's hard sometimes.
But in some cases, things are just super complex to explain.
I remember when preparing for Arm Review V2,
I had an API to merge module context into a project context or something.
The most complex part of Arm Review's API
is how you mingle project context and module context.
And I found that to be very hard to explain.
It took me plenty of paragraphs.
And I was like, this is still not amazing.
And at some point, I figured out a different API for it,
and it was much easier to explain.
So sometimes the solution to when you have trouble explaining something
is to change the API.
Like you're explaining something that is too complex
and you should try to find a simpler way.
So that was the pain points that I had once.
And the solution was do something different.
And then as soon as you see, oh, now it's much easier to explain,
well, that's a pretty good sign that it's a good API, probably.
Which for the same reason, that's why if you find yourself
writing documentation, it can be a crutch
because maybe you should have been documenting something.
Maybe a hard to explain concept,
maybe lots of documentation is a crutch
for making the concept easier to understand,
making the API simpler.
So it should always be documentation should support something
that's intuitive on its own, ideally.
And you're going to have the best time when you're always considering
changing something to make it simpler,
not just documenting what you already have.
And when you count, then documentation makes sense.
Like if something is done a specific way for performance
or because there's a bug in some package, some other package,
or the web browser doesn't work as you would expect,
then yeah, add the documentation comment that says,
this is the reason why something is this way.
Right, right.
Yeah, I always love the scene in the show Silicon Valley
when they're getting user feedback on the product
and then the CEO is watching through this blind mirror thing
and watches the users in frustration improperly using the product
and he goes in and corrects them and tells them how they're all using it wrong.
But of course, feedback is only as good as your willingness to listen to it.
So I think you, not that you should necessarily do directly
what everybody tells you you should do.
That's not the right answer either.
By the way, you should not listen to this episode.
We only have garbage information.
So see ya.
Right, exactly.
Yeah, but I mean, when you're getting feedback,
it's telling you something.
And that's the fine line there is like understanding that
people are telling you pain points and hearing that they have a pain point.
If some people are trying to write documentation,
I also have a few Elm Review rules that can be helpful for that.
So there's a package called slash elmreview documentation,
which has four rules, some more applicable to others than others.
So there's one that is called missing, which we mentioned before,
which reports missing or empty documentation.
So these rules can be used for application,
but they can also be used for packages.
And in this case, like for packages, it will report when it's missing,
just like the Elm Compiler would, but it also reports empty documentation,
which I think is something we should try to avoid as much as possible.
I think you disagree with it sometimes.
I personally disagree with it for map two, map three, map four, map five.
So my reasoning is that I see this quite often that people document map,
they document map two because it's more complex,
and then they don't document map three.
So the documentation is empty and same for map five, et cetera.
And the thing is like your documentation can be found in the package documentation,
but it can also be found in the ID.
And if you hover map two, you get a helpful documentation comment.
But if you hover map three, then you have nothing.
And that's not very helpful in my opinion.
So I would much rather have map three say refer to the documentation of map two.
Okay, nice.
Because now you can just, you know where to look at.
Potentially you can even click on it.
So that's why it does not, no missing reports, empty documentation.
Oh, cool.
Maybe I could even make an Elm review rule to automatically do that for my map and functions.
Potentially, yeah, that'd be fine.
There's also a add docs rule, which is a bit of a weird name.
Yeah, but I use it and it's saved me in some situations.
So thank you for that.
So we mentioned the add docs annotations,
and there are quite a few ways where you can use them
where the package documentation will not handle them correctly.
Like that the compiler will usually tell you, hey, you're missing add docs
or you're having add docs that are not expected.
But this rule will also report those for when you use it in applications
because the Elm compiler will not help you there.
But there's also cases where, for instance, if you indent add docs
or if you put it in at the very first line of your module documentation,
then your docs will not look as expected.
So if your entire module documentation is the curly braces, whatever, add docs something,
then that is literally what you will see, add docs something.
That's your entire documentation.
And I've seen this over and over again, so I've made an Elm review rule that reports about those.
And there's a few other gotchas that are similar to that.
Well, so like one of the ones that has saved me many times,
I actually wish that the IDE could do this for me automatically,
but when I rename a module, I sometimes, most times, honestly,
miss that in renaming my links within my docs.
And that Elm review rule catches it for me.
If it's pointing to a module which no longer exists, it catches that.
No, it doesn't. That's the third rule.
Okay, yes.
You're making a nice segue, at least.
So that's docs.reviewlinksinsections.
So that one is also very interesting.
As you say, you link from one module to another, from the README to another module.
And yeah, if you rename something, then things are broken.
Or if you make a typo, then things are broken,
and you don't want your users to let you know about that.
So this rule lets you know about it.
It also reports the ambiguous links.
So if you have a markdown section, so hashtag, hashtag, init,
and you have a function called init, then they will have the same href?
Yeah. Name. I think it's like the name under the hood.
The same anchor?
Name, attribute. Yeah.
So whenever you will link to that, it will go to the first one.
And you ideally want them to be different, like not ambiguous.
So this will also report that.
I think that's the one that reports it.
But yeah, something reports it.
And then there's another one that is docs.uptodate.readmelinks,
where the README is a bit of a peculiar file,
because you can see it on GitHub, and you can see it on the package website.
And depending on how you do the links, they will be up-to-date or not up-to-date.
Usually what people do is they link to the package website from the README,
and they use slash latest to specify the version, which works,
but not once you release a new version.
Now your previous version of your package,
the README will link to a function on the latest version,
and that function might not exist anymore.
So that's a problem.
So this helps replace those latest by the current version of your version
defined in your Elm.json.
And whenever you upgrade your version, it will auto-fix those.
So that's one reason that I have a very good time bumping the packages
and keeping these links up-to-date is I bump the version, I run this rule,
and all my links are correct again.
And that's just very nice.
It's definitely an example of something that is the correct thing to do,
but in practice, without automation, something that people just would not do.
It wouldn't be tenable. So it's great.
Yeah, and I remember that before I made this rule, it was like,
well, what should we do? And there was no clear cut answer.
So those are the four rules that are there.
If you can think of another one, let me know.
I definitely think there are more things to be done.
Yeah, and I mean, also somewhat relevant would be the forbidden keywords, is it?
For checking for things like to-dos.
To-do or string, is that what it is?
To-do? No, that's different.
Yeah, there's a package called sparksp.elmerview forbidden words
where you can tell it, please report all usages of to-do or replace me.
And this is whenever you make an ElmerView package,
you have this rule with replace me already in there.
So whenever you try to write replace me as a placeholder for some documentation,
for instance, this rule will at some point let you know,
hey, you should replace this.
So that's very useful as well.
Yeah, I also have a GitHub template called the ElmPackageStarter,
and this uses most of these ElmReview rules,
and I've found it pretty nice for bootstrapping a quick Elm package.
And it also links to a project of mine which we've talked about in the past,
the Idiomatic Elm Package Guide, which sort of lays out, I think,
a set of principles for how to organize your readme
and what format to manage a changelog and some boring but important things.
I think these are really important.
Having a changelog, if you create a new version of a package,
people are going to be wondering what changed.
And having an obvious format that is something you would expect across packages.
Same with an examples folder.
People in the Elm community, if they've kind of been around for a little while
and seen some packages, they're probably going to expect the GitHub project
to have an examples folder.
They're probably going to expect to have a link to the package from the GitHub readme.
They're probably going to expect to have a test suite with a badge
that shows whether the test suite is passing.
Some boring things like this that I think are worth having.
Yeah. By the way, the fact that you have the changelog
and sometimes the contributing instructions in the readme,
that's something I see sometimes and I don't think that's the place for the readme.
Especially because there are some standards where you have a file called changelog,
all caps, or usually all caps,.md, and one called contributing,
and there's also license and all those, plenty of other files like that.
Yes, and those are meaningful to GitHub and to Elm, to the Elm compiler too.
So, the license file, for example, is required by Elm.
Yeah, I wouldn't like it if the changelog was maybe also special,
at least was linked to in the package documentation.
Absolutely, yeah. And if you could, man, wouldn't that be cool
if you could kind of show the Elm diff, if you could have some syntax for the changelog
where you could say the breaking changes, because Elm diff,
when you do an Elm bump, it knows the breaking changes,
and if you could document how it changed or why it changed, that would be really cool.
Yeah, just being able to see the diff on the website, I guess,
on the package website between two versions, that would be nice as well.
Because often I see that a new package has been released on Slack
and I watch it, I open it on my phone, and I'm like,
oh, well, what has changed since the last version?
Well, let me open up my laptop and run Elm diff in a terminal.
Yeah, that is very useful, but it's not always a great experience.
I'm not always on a computer.
So another thing that is not always clear cut is like,
what belongs in the package docs and what belongs in some external place,
either the readme, a separate document in GitHub, or an external site?
Do you ever find reason for having something that's not included in the readme or the Elm package docs?
Yeah. So Elm review, for instance, has...
I say for instance, but all Elm packages are Elm review related.
Maybe one day I will have something else, but for now, they're all Elm review related.
After so many years of work, still only Elm review.
Well, it's focus.
Yeah, I guess. Well, and packages that I'm not maintaining.
So there is a document, for instance, for how to integrate Elm review into other places like IDs or bots or something.
So there's a documentation that describes how the CLI outputs some format that can be understood by computers.
And that is not relevant to the API.
So the Elm review package, the Elm package is meant to be used by people who write rules or who configure their Elm review configuration.
Everything else makes little sense.
There's still like all the how to get started, how to configure, how to install Elm review,
which is like an additional section because there's some NPM involved.
But yeah, I think there are some cases where you will need other things.
The main one that I can think of, and that does not necessarily fit the Elm package website very well, is a guide.
And that's something you hear sometimes as well on the Elm Slack, is that people see the Elm guide and then they wonder,
oh, well, how do I learn more or how do I know which functions exist?
And we kind of assume that they went to the package website and look at Elm core or they looked at other packages
and then know how to read that package website, which is a given once you've got some experience with it.
But like sometimes you want to know about the About tab or About page on a package and that has some relevant information.
But if you haven't browsed around, then you don't know about it.
So yeah, having like the Elm package websites with all the API plus a way to write a guide, that would be pretty nice.
So for instance, for Elm review, there's a lot of things that I try to teach that are not necessarily related to the API.
And that's why my documentation is really long.
Can you think of an example? Like would it be conceptually how an Elm review, how it traverses things with visitors or something like that?
Potentially, yeah, that's an implementation detail that would be worth explaining somewhere.
So for instance, if you want to explain like performance considerations, like you should use this function this way and not this way,
or you should have this architecture, then if that doesn't fit specifically with one function, then or in one module,
then it's a bit hard to know where to place it.
Also, just like guidelines on how to make great rules is at the beginning of the review the rule module, which is the main one.
But like it could be its own page, right? And also, for instance, like if you have a guide, then you can actually do a tutorial,
which I kind of try to do. And this is kind of why I say like, write your own story, because I'm in a way making a tutorial,
but maybe the way that the docs have rendered, it doesn't always make for the greatest experience.
But if I did not have to write all the API in the same page, then I would write it differently. And I would probably.
Yeah, I like the way you're framing that. I mean, I think like the example of performance tuning, a review rule or something like that is,
in that case, it gets to the point you were making earlier about who's the audience.
And, you know, the audience for very low level details of some JSON format of something is different than the audience of
how do I write a review rule? Maybe I want to make sure I'm using this internal API in our application in a particular way.
Well, I don't really care about performance, these performance considerations or this low level JSON format right now.
So it's a different audience. And you don't want to lump all those together. It's going to be very confusing.
I think maybe if the package websites just picked up on like all the markdown files in a documentation folder or something like that,
or something that is explicitly defined in the Elm JSON file, that could work very well.
Yeah, George Borges has talked about this idea with, you know, Elmbook and his inspiration from the Elixir documentation,
where they have both API documentation and sort of guides or markdown pages side by side.
So, yeah, I think you're right that there are certainly cases.
I mean, like you mentioned this example of, you know, Elm browser and then the Elm architecture in the Elm guide, right?
Like a user might go to the Elm browser docs or some core package and expect a guide, but actually that lives somewhere else.
But that does make sense. Like it would be too much for the scope of it to introduce the Elm programming language and the Elm architecture
all in the inline module docs. And it doesn't give you enough granularity.
So in that case, having these conceptual overviews doesn't fit there. Also tutorials like you might have a mini tutorial,
but you don't want to have a very ambitious tutorial that requires a lot of setup and a lot of steps and a lot of do it yourself exercises inline in your module docs.
It's just they should be very narrowly scoped. So something like that definitely belongs in an external tool,
which some package authors have external sites. Elm pages has an external doc site.
And, you know, it covers things like just a conceptual overview of Elm pages because the scope of it is very large and it wouldn't fit neatly in one module documentation page
or things that happen around generated code. What module do you put that in? It's describing a set of generated modules.
So there's like a file structure document that talks about navigating the file structure of an Elm pages application and what sort of modules are generated from that.
You know, certain things about the philosophy and the architecture and diagrams for the architecture, the adapter API,
which allows you to adapt to different deployment and hosting platforms.
So, yeah, so there's definitely I mean, my rule of thumb is if something fits neatly in a modules docs, put it there.
For example, back end task, I could put it in a separate, you know, it almost wants to be part of the Elm pages docs for slash docs.
Like I almost want it in the listing there, but it can fit in a module doc. So I put it there instead.
There's also like you can have a website, like a custom website where it says blazingly fast, you know, the obvious catchphrase,
where you can just make it look good. Just marketing wise, the package's website is not super handsome.
Like it's a good looking website. It's a very simple website, but you're not putting like marketing wise,
the JavaScript people will tell you, hey, this is shit. Like my Vite package looks the same as the Parcel package.
How can I make it obvious that this one is better without being able to choose my colors for my package documentation page?
So I think there's also a case where you want to have a dedicated website. I'm not sure how much overlap there is.
And when you want to have a additional, when you want to have a website and package documentation and a guide,
and how you mix and match them together. So I'm kind of used to have on the package websites.
And therefore, if new opportunities open up to me, like having the ability to write a guide,
I'm not sure how I will write the documentation yet.
There are definitely certain things that fit better in a custom site. Like you said, I mean, having more control over the styling,
but also if you want to have a showcase or certain community things like in the Elm Pages site, I have something that lets you submit.
There's a button to submit to the showcase. It uses Airtable and it pulls data from that API live or, you know, a blog.
Should you have a blog hosted on the Elm Package site? Probably not.
Yeah. You also maybe want to have like release notes that are displayed nicely, news, blogs.
And yeah, as you say, like things that are not relevant to the API, but that are relevant to the package,
like how many people use this, who is sponsoring this package or yeah, things like that.
Right. Yeah. Yeah. And in general, there are all these different rich formats. I think they all serve their needs.
Having a custom blog serves its need. Having Elm Package Docs does its job extremely well.
And having them both is really useful. Things like GitHub discussions, GitHub issues, poll requests, conference talks, podcast episodes.
All these things are great sources of information in different formats that serve different purposes.
A GitHub discussion is a great place to showcase ideas and community projects and get feedback on things and discuss specing things out
or making decisions or documenting history about something. Package Docs are not a good place for that.
But if there's some quirky thing that people are frequently asking about, go ahead and link to that GitHub discussion thread
that talks about the history and the spec and why that decision was made.
So I think this model of linking out to a lot of different resources, keeping things very concise and focused on one purpose.
But then if you're on this page and you're looking for something more, let me throw some links your way just in case.
That's hugely valuable. I don't think you can understate the power of just...
And even just linking to in between different module pages in an Elm Package is huge, I think.
Another great thing is that whenever you have a function, it has a type annotation and the type annotation has references, different types.
And whenever you have a type that is defined in your package, that becomes a link.
So you can click on it and now you can see where that was defined. And that is very valuable.
So if you can add more links for things that are not done that way, that's very valuable. I agree.
Yeah. Also, on a related note, I think we all have the curse of knowledge, especially as authors of packages,
where we have trouble understanding what it would be like navigating something because we know those things.
So it's hard to revert back to a state of not knowing those things and understand, not to mention different things will be confusing for different users,
coming from different avenues and different ways of thinking about things.
But I think it's a really good practice to get in the habit of understanding what assumptions you're making.
And there are certain habits that I've tried to build personally, where I'll try to notice when I'm making assumptions.
So like one easy one is an acronym. If you're mentioning an acronym, just spell it out.
Just say what the acronym stands for. You can use the acronym, but introduce at least what it stands for first.
Yeah. And you can even like if it's a concept like the CAP theorem, then you can link to a Wikipedia page or something that talks about that.
Or a blog post that has more information and simplifies, vulgarizes the concept.
Because sometimes Wikipedia is not very, very nice, especially for mathematical things.
Link to the Monad page.
Right, right. Yeah, but exactly. Any terms like that, that might require some specific domain knowledge, just go ahead and link to it.
You know, if you're talking about a type from a package, link to that external Elm package or NPM package or whatever it might be.
I also find that sometimes, like when I'm writing out docs, I will say, if I'm using the word it or that or this,
sometimes I'm like, wait a minute, are they going to know what I'm referring to?
Because I just covered a lot of things and sometimes instead of it, I'll say, you know, the backend task type.
And so now it's like, OK, backend tasks allow you to do that.
It gives you, you know, I'll just say backend task also, you know, because it's very subtle.
But I think that people can get tripped up on small details like this.
So be very explicit, show all of your imports, show your work, link to things, define your terms, just like go overboard with those things.
Yeah. Also, if you re-mention the backend task, you can make it a link.
So people don't have to go back to the very top of the page to find the link to backend task.
So that's also quite nice. And also like try to avoid words like just or simple or like they make sense in some cases.
But you have to be careful about those. Like what seems simple to you or easy to you is not necessarily the case for other people.
Although simple is objective, but you need to have watched Rich Hickey's talk about that.
Right. Yeah. I think also being straightforward and, you know, not saying like necessarily this is the best way to do this,
but saying these are the decisions. I think it's really good to be clear and explicit.
These are decisions. I believe these are pros of these, this set of decisions.
Perhaps here are some cons about this set of decisions, but these are the decisions. This is the philosophy.
I think we talked about this in our Elm Radio episode of how and when to write an Elm package.
But, you know, I think it's really valuable to just be up front with people and tell them because you can't you can't do everything.
You can't make every you know, I'll make every single trade off out that no, like a trade off.
You have to trade something. Which one do you want to choose? So just don't mince words.
Go make some bold decisions because and allow for different approaches to coexist that make different versions of those trade offs.
And so you don't have to take that responsibility of solving every problem.
Yeah. Or try to find a novel way that solves everything in a nice way, which is usually what we end up doing in the Elm world somehow.
Often with success and I guess the success, the failures are not published or.
It's true. It's true. There's a bias there. Definitely.
Yeah, it depends on the example. I guess like Elm Review does a great job sort of making the best of both worlds.
Whereas something like Elm GraphQL inherently there's a decision.
Do you have a query builder style or a generator style that spits out code for a specific GraphQL query?
Those are trade offs that there's you just have to pick one and say, we think there are merits to this approach,
but we can't make a perfect solution that makes everybody happy because there are trade offs.
Well, anything else we should point people to for writing great docs?
I think we've already given a lot of great tools and links, so you can see those in the show notes.
Try to do your best. Try to make it to make it understandable by people.
Ask for feedback and iterate like you're not going to get things right the first time you're going to have typos.
You can have things that are not clear and that's fine.
Just one thing. Try to avoid breaking changes as much as possible, but it's not a big deal either.
Yeah, and listen to your users.
I skimmed a little bit through a book that I had heard about called Docs for Developers,
and I quite liked what I read.
It was in a nice skimmable format, and they even talked about the importance of being skimmable,
which is near and dear to my heart as a reader of some portion of documentation.
I would definitely recommend checking that out.
Yeah, but you don't know why it's important for things to be skimmable because you've skimmed that, right?
That's right.
Okay, yeah, sure. Just want to be clear about that.
All right.
All right. Well, Jeroen, until next time.