 All right. Hello. Welcome to MPEX LA. I'm really psyched to be here. I know a lot of other people in the room are really psyched to be here. First, I want to give it up for all of the speakers so far today. Seriously, raise a round of applause for them. Any bozo can hire a pianist, but it's the talks that really make MPEX what it is. And if you ask me, it's the best little conference going. So thanks for that. My name's Pete, and I am here to talk about docs. So let's talk about what we're trying to get out of this talk today. On a technical note, it's a guide to xDoc, which is the documentation system that's built into Elixir. Having it as a built-in part of the language has a number of advantages. The number one perhaps being that there's a really good system that nobody even has to think about choosing because it's just there. But I'm also sort of, I'm just going to expound on how nice it is to have good documentation for a project. How many people in here write programs? Yeah, we've been hurt before. Bad documentation is a brick wall that we will sometimes climb over if we have to. But it's really nice when you don't put it there anyway. So this talk is essentially about making people happy. Specifically, the people who interact with the software that you write. And I'm giving this talk here today because as it turns out, Elixir has one of the best sets of tools that I've ever seen for generating documentation in a really low friction way. So let's start with a couple of thought exercises. These are tough questions. Do you want to have to fully understand somebody else's code in order to use it? I'm not going to be playing PowerPoint karaoke all night, so don't worry about that. I said understand there. I didn't say read. Reading code is far-fetched enough, but expecting somebody to understand your code, or really anyone's code, that's really asking a lot. That's a big time commitment, depending on the complexity of the project especially. It's a lot to ask. All right, another question. If somebody cannot describe their code in English, do you trust them to have been writing it? This is a valid question. My head works a little bit differently from a lot of people as far as this is concerned, but I personally find if I cannot explain the code that I'm writing, I will roughly never get it right. And if I can't explain the code that I'm writing, it's almost a mechanical process because I've done the hard thinking. So this is a question, again, from the point of view of a coder, a consumer of libraries, as opposed to a project leader. So I'm just going to pivot a little bit there. If you write some code, do you want to restrict your audience to geniuses? Because I bet you don't. If you put up barriers to being able to understand the code that you write, you are excluding all but the most dedicated folks from using it. And I say dedicated, not entirely as a compliment. There are lots of ways to contribute to a project, and being a difficult genius is only one of them. So you really want to open the doors for everyone you possibly can. And here's the big question. Do you want other people to fix the stuff that you wrote that is crap at no cost to you? It sounds too good to be true. Who in this room has any open source projects that they've started or contributed to? Do you remember your first contribution? The first time somebody sent you a patch, we were like, oh, my code was broken. And somebody across the world fixed it for me. And they just sort of dropped that fix on my doorstep. Or I wrote code that didn't do enough things, and somebody made it do another thing that's very useful to my project, and they just gave it to me for free. It's been a while since my first time, but boy, you never forget it. It's just amazing when you realize that you are part of, you're really part of a community, and a community of people that in many cases you don't even know. You throw a project up onto the internet, and sometimes if you wrote something that other people want to use, sometimes it has reach, and it will find users. Docs are one of the ways to get you there. All right, so we all had a break. We're hopefully a little bit more energetic. I want you to shout at me out of turn, name some docs that you really like. Ecto, Phoenix, both very good examples. I actually really like Ecto's docs. Phoenix, it's all right. But Ecto, I really like their docs. More, doesn't have to be in the Elixir ecosystem at all. Stripe, everyone loves the Stripe API docs. That's arguably what landed Stripe where they are. Is just having an incredibly easy API to use that upon looking at the documentation, you didn't even have to read it very closely. You just gaze at it, and you pretty much get the gestalt of the whole thing. Rails. Rails, Rails has. The guides, I find the guides. Okay, sure, the Rails guides are decent. Yeah, it's a little more pros than kind of more project docs or module docs. All right, so any other, closure and atomic, I've never looked at those. They always have a rationale and they explain what problem they're solving. That sounds fantastic. I'm actually gonna look that up probably tomorrow morning. I'm not gonna do it tonight, I don't think. All right, so I wanna point something out. Nobody in the room mentioned tests. This is because, and I want to be fully clear about this, tests are not documentation. And if your documentation says read the tests, you're being a jerk, that is a user surly attitude. Docs are there for a reason, tests are there for a reason. Elixir has pretty good systems in both places. I'm here to talk about docs and tests ain't docs. All right, so Elixir has really good docs. Nobody mentioned it because hopefully they were leaving room for me to say it and didn't wanna just stomp on me. They look really good. We've all read them. I think it was, I wanna say Elixir 1.2 is when they redid the style of the documentation, like the HTML page, and it looks very, very modern. A lot of doc systems in other languages tend to look a little bit web 0.9, whereas Elixir did a really good job of just looking like a really handsome reference material. All of the modules that are documented explain their purpose. That's a big thing because if you're a beginner and you see something called enum, maybe you know what that does, maybe you don't, maybe you have a background in C, so you're thinking in enums rather innumerables. The module docs are there to kind of just orient you. Functions are there to explain their semantics and Elixir does a very good job of that as well, and there is sample code everywhere. Can't tell you how much I love that. So let's take a look at the GenServer documentation. I picked GenServer because pretty much all of us have stared at this page a lot, and it's actually describing something that is fairly complex. And so we see the sidebar for the Elixir project docs has now been rearranged so that it's no longer, it's no longer in alphabetical order, it's now grouped by categories, which I gotta say it broke some muscle memory for me, but that's okay. Progress always comes at a bit of a price. So you see a short description of what this module is about. You see an example section, there's code samples, everything's nice, it's syntax highlighted. Now this is the way that most of us consume the docs, but this isn't the only way to get to them. It's not just a web page generator. The docs are written in markdown, which means you can do fun things like from IEX, you can just type H and then the module name that you want the docs for, and it puts it right in front of you. Rendered pretty nicely in text. So it's really, it's Xdoc mostly cranks out HTML and EPUB, but the doc system is kind of a little bit more pervasive in Elixir than that. So right now I'm gonna give you a very whirlwind tour of how to use Xdoc, and it's very simple. You write your docs in markdown. Everyone here written markdown, hopefully. If not, treat yourself. It's really, despite being poorly specified, I'll say that, common things are really easy to do. You indent sample code four spaces in from the rest of what you're writing. That is how you stick sample code in there, and I'll talk a bit more about that in a bit. You add Xdoc to your project dependencies and mix.exe pretty much the same way you would anything. Generally, you will install this only for your dev environment, but it doesn't really hurt to throw it in prod as far as I know. And then you type mixdocs to generate HTML docs locally, and it'll say, I just spit out docs slash index.html, and if you have a hex project, which is, of course, the Elixir package manager, you type hex.publish docs and the docs get pushed to hexdocs.pm, which is where all Elixir and Elixir project docs live. Automatically, it's really nice that you don't have to think about setting up a web server for this, and if you know the name of a project, you just go to hexdocs.pm and then add the project name to that URL, and there you are. So as a little demonstration, I'm gonna use a library I wrote probably about six months ago. It's called Strip.js. I'm gonna use that to demonstrate it. I warn you, Strip.js sounds like a hip new framework for the front end. It's not, it's something I wrote to Strip executable JavaScript from HTML and CSS. Now why would I wanna do that? So writing docs in Markdown, you can see right here, the pretty simple example of a module doc. Now, how many people have seen these at signs within Elixir code? They're called module attributes. I don't fully understand where their functionality ends, but boy, you can really do a lot of stuff with them. I use them for module constants all the time, but it's also used by the Elixir doc system to delimit where you have the documentation. So you simply put at module doc and then open a multi-line string. Start typing. As you can see. Sorry, as you can see, it's Markdown, so you've got your back text to put a code formatting, you've got your bulleted list, you've got your section heading with the double pound sign, you've got some sample installation code that is indented for spaces. Let's see how this looks, even though I'm pretty sure everyone in the room already knows, damn, it looks good. I don't know, it still fills me with glee that I didn't have to design this webpage, because I remember when I did have to design the HTML for every project that I had because there was no better alternative. It's one of the reasons that makes me, it's I'm very happy to be in the Elixir community. We got syntax highlighting, Markdown formatting, it's great. Let's scroll down a little bit to our usage section. Now, I wanna say, I've never actually heard this as a term, I love what I call docs driven development. I like to write the sample code for the module first. That's pretty much like when I'm thinking of something I wanna write, the best way, at least for me to be thinking about it, I write the code, I basically, I write the sample code I want to see in the world. And then it's just a trivial matter of making it compile and execute properly. And generally, I'll go back and forth over that sample code a bunch of times because I'll just be thinking, like, oh, well, if I do this, then it's gonna kinda suck to use it this way and I'll write a little more sample code and oh yes, it actually does look cockeyed that way. I'm gonna rearrange things a little bit. And it's good because I spend a little bit of time writing and rewriting that part as opposed to writing and rewriting the whole damn library. It's a good time saver for me. Your mileage may vary. So onto a little further description of module docs, explaining the purpose of a module is, to me, the number one thing that you wanna have in a module doc. People need to know, if you want to include people in your project, they need to know what the hell the damn thing is at least trying to do. What is its raison d'etre? Like, why does it exist? You wanna have sample code that illustrates pretty much the happy path through your library. You don't have to really pollute your module doc with a whole lot of sample code, but you do wanna give people a very top-level introduction to one or two of the most common things that you do with this library and how it looks. I like to stick my installation instructions in module docs. And I'll go over why that is in the next slide. In lesser languages, we would use a read me for it, but all right, here's a little slice of life. I have like a handful of elixir libraries that I treat with varying levels of respect in terms of maintenance. So, imagine you add a feature, or actually, just imagine you fix a bug because that's often what I'm doing. And you increase the version number of your project, but oh man, you forgot to, well, first you forget to update it in the read me. I've done that, I don't know how many times. The read me, I write it, and then it basically sits in a corner and I'm actually working on the module so I see the module doc, but I'm kind of rarely going on the front page of my GitHub or opening up read me.md. So, let's say you forget to change it in the module doc. Well, that kind of sucks because now you republish your docs and you're telling people to install an old and possibly broken version of your software depending on whether you bumped the patch level or the minor or the major version. And so your docs are wrong, they're lying, don't lie. So the solution that I like to this is to use variable interpolation in your module doc. Now, in most of these docs, you'll see the twiddle capital S sigil, which does not interpolate variables. I like to have the module doc use lowercase s so I can interpolate in particular the mix file project version. Now, you don't have your mix file at runtime so you wouldn't be able to write code like that as part of one of your functions. However, this is all evaluated at build time and you do have the mix file there during that. So what that means is that the interpolation happens at build time, you will then just end up with a static string that gets compiled into your module. There are your docs, everything works. And here's how it looks. Yep, that's how it looks. It's really not very complicated at all. And you do this, all of a sudden, your module docs are not out of date in terms of what version you're telling people to install. Pretty nifty, it's really not rocket science. Pretty nifty, though. So on to function documentation. A lot of people go back and forth on this. If I hear the phrase self-documenting code again, I might poke my eyes out. There's stuff that code documents itself and there's stuff that it doesn't. Code is really a great way to describe procedure. In fact, you can't beat it. If you're describing the procedure of something in documentation or in comments and then code, that is arguably redundant. But here is something that the code cannot express and that is intent. What did the human behind the keyboard intend to do when they wrote this code? It might actually be different from what the code is doing. And you want people to have the idea of what it's trying to do and not what it is actually doing because if there's a discrepancy between the two of them, you want them to contribute a patch to your project. So I like the intent and specification. Those are two things that the code can stray from. The code will never stray if you're reading it to figure out exactly what is going on. But why? That's something that's best left to humans. And it's a specification, so be specific. And I especially mean around your input and output argument types. Elixir is a weekly type language or dynamically typed if you will. Just like Erlang, this is one of the trade-offs that Erlang made early on in order to enable things like hot code reloading without having an absolute shit show of a compiler. So we have to work a little bit harder in order to express our intent in terms of the types that we expect to take in and emit. And much like ModuleDoc where you stick ModuleDoc directly after your DEF module, you stick an app doc before your function definition. So we can look at an example of that. This is the clean CSS function out of Strip.js. And you can see directly before DEF or clean CSS we have an at spec. So this is called a type spec. I'm gonna get into that just a teeny bit later. So you see that code example with the IEX. It's called a doc test. And this is solving another problem where documentation goes stale. Sample code is great. You really wanna present it to people but sometimes your refactor something and your code changes very subtly or maybe you have some extra output that you didn't use to have. It's not that you broke any specifications by doing this but you do want the code sample to reflect what the user is actually gonna see at their terminal if they paste that in. So Elixir solution and there are several other languages that do this too, Rust is a notable example is the concept of a doc test where as part of your test suite you are running these pieces of sample code. Now there's kind of limitations into how clever or how complex your doc test can be. And they generally follow the same limitations of how much work do you really want to do for an illustrative code example. If you're really trying to test a wacky situation rather than illustrate a common thing you put that in your tests. The sample code is there to instruct people and not just to enforce a contract. The rules are really simple. You start with IEX on every line that you're typing into Elixir. You don't start with IEX on the lines that Elixir is typing back to you. No blank lines allowed or rather when you have blank lines what you're doing is starting an entirely separate test with its entirely separate scope. And after that you just put doc test and then your module name in one of your test files. I'm using the word just very deliberately. You will hear more on that in a short while. Might not be for me though. So a tip on doc tests. I've done this a couple of times being somewhat of a fumbling idiot sometimes. I'll write the doc tests and then I will forget to invoke them and I'll think they're running and they're not running at all and the tests will go stale because they're not running at all. So what I try to do is start by adding doc tests. Every time I write a module I try to put the line doc test and then the module name in one of my tests. Even if the module does not have its own test file yet I realize I'm not practicing good TDD there. But it's harmless to have no doc tests. You avoid the situation where you slap yourself in the forehead because you thought you'd been running tests for the last couple of weeks and you haven't. But also and this is something I did not expect. If you're using a code coverage tool and measuring the code coverage of your tests for some reason running doc tests will scoop a few more no op lines than not doing a doc test. Your code coverage percentage will go up. Have you changed anything of import? Not at all. Your code's exactly the same. You're not testing anything real but the number did go up and everyone does like that number to go up. So a bit more on data types. Sometimes you luck out. Sometimes just by visual inspection of the module name, the function name and the arguments you can figure out exactly what's going on. Lots of functions are not in this category and you need to go a little bit out of your way in order to describe exactly what you expect to see and exactly what you are admitting. Like I said before in a static typing language this is, it's just baked right into the language and in general your IDE will complain at you before you ever do something so silly as even hitting save or committing code. We're not in one of those languages so we have to work a little bit harder and Erlang and Elixir have a solution called TypeSpecs. So an example of the TypeSpec is that at spec line and what I'm saying is the first argument is a string in Elixir there is the general convention that if you have a module name that represents a data type you call that data type T. So string.t is a string which I think is technically just a UTF-8 compatible binary. Ops I actually have defined elsewhere I'll show you that in a moment. That's nice because you can specify okay here are the exact options I'm expecting to get. It's a nice piece of documentation just saying these are the ones I support anything else that's not in there I didn't really expect it so you probably shouldn't be passing it. And after the double colon is what it emits. So the clean CSS takes in a string emits a string. Something nice about these TypeSpecs they can kind of be some documentation in and of themselves. It works recursively which is very nice so you can have recursive data structures like for instance this describes a very very simple HTML document. Describes it poorly but it does describe it. I show you Ops. Ops is just a keyword list which we can call keyword T. Something else in here you notice the single pipe that just means it's gonna be this or that. So an HTML tree is one node or an array of nodes a list of nodes I should say. And then once you've got your TypeSpecs in place I think a lot of people in the room I've heard mention this word dialyzer. Erlang's discrepancy analyzer. It's pretty great. Essentially it takes a long time to run the first time because Erlang is building up a lookup table. But after you grind that out for a few minutes you can reuse that lookup table. It works really well on your own machine it actually works kinda sucky in CI. Like if you wanna have that as part of your testing suite you actually have to jump through some hoops in order to cache those lookup tables but on your own machine it'll generally work out fine. And in a nutshell you write TypeSpecs you run mixed dialyzer and then Erlang either tells you you're lying or not. And it's really good to have a static analyzer be able to say hey you're expecting to take type A or type B here but it looks like type C could also come in here. Or it'll tell you about unreachable sections of code. It's pretty neat. And speaking of code, speaking of lying really the best docs can they can just wallpaper over a huge pack of lies. And sometimes if you wanna be sure you just gotta read the source. No substitute for it if you want to know the procedure. And so you generally would go on to Google get to the project GitHub, go into lib, find the module. No one wants to do that. No one has to do that in Elixir. There is an option that you can pass to mixed docs called dash dash source URL. I have a little illustration of it here. Sorry for the weird vertical part but I noticed sitting down there you don't see the bottom of the screen all that well. So I'm using a MixAlias which by the way I love MixAlias, they're pretty neat. And so every time you run mixed docs it's actually just sticking in this extra argument. And what that has the effect of. You see at the, so you see at the top of the page there is that little kind of like empty HTML tag looking thing. You click on that tag and like magic you get to your source code. There's kind of nothing to it there. That is something that you will find and you'll find that alongside types and functions and really like everything that you have specifically documented in your docs you'll be able to get to on GitHub. Which is, I mean I use it with some of my own projects even when I have the source code in front of me just because it's often the easiest way to get from a description to the actual code that implements it. So I described a lot of stuff over the last few minutes taking a step back from that, what happened? We have good docs too. We just like, most of what I just told you is kind of a mechanical process other than writing the docs in the first place which is something you want to do but it's really just typing the right thing in the right place. And suddenly you have module docs that explain purpose. You have functions that explain really what they are doing. All of this is organized very nicely. They have code examples that you can trust. Dializer if you're running that will add another layer of trust to the whole thing. And it all looks really good. It makes me feel good that any Joe Schmo can publish an Elixir project and they are on equal footing with the Elixir docs themselves which I consider to be very high quality. A few resources if you want to read more about this stuff. X docs, X docs has a pretty good doc site. Elixir has a good page on type specs if you want to dig deeper into that. I recommend it, if it's not, I'll just put it this way. It's a really good tool to have in your bill. Even when I'm not running Dializer all the time I generally type specs as just a way to remember what the hell I was thinking three weeks later. That's about all I got. I definitely will love to take some questions. The executable specs, can you execute anything that is not Elixir? I don't believe so unless you implement that thing in Elixir. Yeah, I believe it is only useful for Elixir stuff. Something that I've had pretty good luck with if I want to express tests in a way that is kind of like more agnostic. Documenting a CLI, that's not something that I've done in Elixir. Honestly, I personally steer away from Elixir for command line projects, if that makes me a bad person. Don't know what to say about that. In that, okay, so the question was this doc stuff makes a lot of sense for external projects, public projects, but how can you guide the process of documentation internally on a team? I personally, I'm leading an engineering team. I try to lead by example there. I also will give a developer a huge rash of shit if they hand in something that has no documentation. And generally, I don't know, I'm lucky because I have the people who are contributing to the Elixir projects I have at work are kind of conscientious in that way. In general, it just so happens that we have some of the more senior developers working on Elixir stuff. And I think one of the identifying marks of a senior developer is just having good discipline around that sort of stuff because we get old and our mind goes and we don't know what we did a couple of weeks ago. I got that young because I used to be a Pearl developer. I wrote Pearl and Fortran in kind of a weird yin and yang formation for a while. And in either of those languages, if you are not documenting the bejesus out of what you're doing, you are sunk a few months from then. So the question is, I put installation instructions in my module docs. Do I also echo that information in the readme? What I try to do with readmes is make them as short as possible and have an extremely prominent link to the docs on hexdocs.pm. And so I'll usually have a one line description of what the repo is all about, what the project is all about. I'll have a sample code snippet so people can look and say, okay, well, that's what it looks like to use it. And then I will say full documentation is available on hexdocs.pm. Do I know of any open source projects seeking contributions to their documentation? I would venture a guess that a lot of open source projects would love documentation help, even if they haven't mentioned it. There are open source projects that have a very high emphasis on documentation and just developer experience in general. I think that's one of the hallmarks of a lot of the new languages coming out. I'll mention Rust again, because they take their documentation very, very seriously. I mean, they have people employed to be writers. But yeah, I can't think of any offhand, I'm sure there are some, but I can't think of any offhand that are just outwardly begging for docs. The question is some code is self-documenting. Do I document every function I write? The answer to that is I document every public function that I write. Sometimes there is, I'll have helper functions that I'm calling from the internal function. And in those situations, often the name will be enough because you know the greater context in which it's operating. But yeah, usually. And again, often it's to help me. Well, thank you everybody. Thanks a lot, Pete Kamesh everybody.