Brains & Beards Show

BBS 19: Documentation in Software Projects

Episode Summary

The podcast discusses various types of documentation for software projects, including documenting code and external documentation. It explores what should be documented within code comments versus other approaches like TypeScript. Maintaining outdated documentation is an issue, so validation through tests or expected errors is preferable. External documentation like readmes should link to other sources rather than include details likely to become outdated. Architectural decision records are recommended to explain important design choices. The discussion also covers establishing a domain language to reduce ambiguity and standardizing terminology between teams. Overall, the podcast emphasizes writing documentation that remains useful over time rather than just explaining code.

Episode Notes

https://brainsandbeards.com/

Key Moments:

Episode Transcription

Hello, wonderful listener. Today, you're listening to a new episode of the Brains and Beards show, where Patrick and Wojciech discuss programming, building teams, workflows, and everything else that it takes to deliver great mobile application.

In this episode, we're gonna talk about documentation and how to make it useful to start with. Maybe let's talk about what types of documentation do we have. Maybe Patrick, you can kick us off. Hello, everybody. I like categorizing things. It helps me with processing of new information.

So this is why I wanted to start with some categories for the code to structure our talk a little bit better. So you can structure, of course, in many ways. You can have a boring documentation and fun documentation. You can have useful documentation and not useful documentation. To put the jokes aside, perhaps we can start with something which is documentation which you have inside of the code and perhaps inside of your repository between your code lines.

And the other thing, the other group would be the external documentation, which would be then stored, I don't know, in confluence in some other places. And perhaps let's start with the documenting code and what kind of documentation would be useful and good and helpful to have inside of the code base basically. So what would you suggest for it as a first thing? Nothing because a good code documents itself. The intention is clear, the naming is clear. But jokes aside, well, it was half serious joke because I do believe that good code documents itself.

And a lot of times, the way you'd classically think of documenting code is a lie waiting to happen. So you have a function and you write about the function at the comment that says, hey, this function takes this argument, it turns it into this result.

And then big chance is that somebody refactors the function, but they don't refactor the comment.

And then the comments is one thing, the code says other thing. And one year later when you revisit it, you're in a bit of a pickle. I think we should try to use other tools instead of comments to document our code. So good variable names, good function names. If you're using TypeScript, the function definitions are a great way to help you understand what the function does because you have good names of types. And you use a bunch of custom types instead of passing string and number everywhere, then it can help you make sense later of what the code is doing. But we always have some places where we come across strange code. That this looks wrong, but you wrote it and you know this is actually how it's supposed to work. Then it's very useful to document this line saying, hey, I know this looks strange, but that's why it works. And you explain the idea why it's written like this. And maybe somebody comes along in the future and has some knowledge that you did not have or knows that some requirements changed. And you actually don't need this word code anymore.

And it's much better than if you don't leave the comment and somebody comes in the future and sees this word code. And they think they're refactoring it, but they actually change it from working state to non-working state, but looking better. And then they get the bug report and they have to revert it back. So yeah, sometimes it's nice to leave a comment next to a line. But also it does not always have to be a simple comment. Because for example, if you're leaving information that there is a line that sleeps for a second, for example, and usually that would be a ridiculous thing to do. You could leave a simple comment explaining why this delay.

But if you have TSignore somewhere, it would be nice to explain why do you have TSignore there. But maybe Patrick can talk about it because he's much more of a TypeScript guru than I am. So it's about to have outdated documentation. So what if you could have documentation which could kind of validate itself and if the code would change, basically the documentation would throw an error.

We cannot do it for everything. But in this case, instead of using TSignore, you could use the TS expect error, which you would say, what error do you expect from the next line? And it would silence this error. In case the next line wouldn't throw this error, then the documentation is not valid anymore. And it would throw an error in that case. You know where to go and where to clean up your documentation or change your code to be correct again.

And the other things to touch on what you said,

yes, the code is great if it's self-explanatory and it's easy to read, understand and so on. The problem, only problem I have with this is that we don't, very rarely we program in this kind of clean sandbox. Usually you have to use APIs which you haven't wrote or perhaps SDKs or libraries which you haven't wrote, which have some strange dependencies or strange things which have to be done in a specific order or they have bugs. And you can overcome them, for example, by putting some slips or even other strange lines, which you wouldn't do if that would be everything in your perfect code. So sometimes you need to put strange lines, especially because you have to live with other libraries and so on. So in that case, as you said, adding a comment explaining why something weird was done is super useful. I don't think that documentation should serve as a teaching moment for the reader. If my team is fully React Native, which never attaches native parts, and I'm doing some feature which is written in Swift and it's a lot of Swift code and so on, I would expect that the user would, not the user, the decoder, the fellow coder would learn Swift, not from my documentation inside between my coding lines, but to go to documentation, tutorial and so on. So I shouldn't write explanations for people which don't know the domain to understand the code. Especially right now, because we have so much access to AI, which basically you can do things like mark function or some line of codes and they'll explain to me what this is doing. And it's going to be this way, it's going to be much more up to date than me trying to educate somebody via documentation between the lines of code. Yeah, so this is my take on it. Those are some very good points and reminds me of one more useful thing you could use comments in a code file for is to leave a counter of how much time has been wasted by unsuccessfully refactoring this part of code. I saw it on a couple of projects where somebody, when code looks really bad, but once you dig into it, it turns out that is the most concise way of implementing particular business requirements that you have there. And I've seen and I've contributed to those countries in the past. That's a cool idea.

It's good warning light for anybody who thinks they're smarter than the original author. Other than that, I wanted to also touch on a couple more things. One is commit messages. I still believe that writing a nice commit message is always valuable. And it's a forgotten skill, I very rarely see good commit messages. I think they're valuable. And I used to think that they're valuable as documentation of why code is the particular way. I don't believe that anymore, mostly because it's hard to get to it to them in the afterwards. The code lives on. So you wrote write the code a particular way at first, then it gets modified. And not always the modifications are meaningful because it can just be touched by some as lint rule or pretty rule and auto modified and somebody who does the good blame. It's not like they can go back one comment and see what the author wanted to achieve. But sometimes they have to go back one comment, second comment, third, fourth, fifth. And only then they actually get to some meaningful information.

Because usually there is no meaningful information in the commit message. Nobody goes digging into those, this stack of comments and this information is lost. So if you think it's important what you would write there, also ask the documentation, probably it makes sense to put it somewhere else as well. But a good commit message makes your commit more atomic or whatever you want to call it, more structured. That you don't throw too many things in one commit because then it's really hard to write a commit message for it. So it helps you with the working culture. Maybe that's why there are no good commit messages because for the code changes that we see it's hard to give a good message. Other than as we said I think in the previous episode, back fixes and performance improvements in the application change log. One interesting thing where you could put documentation as this executable documentation that basically renders itself obsolete is, as you mentioned with the TS expect error, is unit tests. If you have a regular expression which is surprisingly long and you would think it could be easier, all the business cases that it has to handle, you just put them as unit tests and if somebody goes in and if they want to see why it's like this, they can look up the test file and if they modify it because they want to make it simpler, the tests are going to break. And they will see the exact case that they are not handling. So this is also an interesting way of adding a test, not for the sake of testing but for the sake of documenting what your program does. So basically we both don't like the cases where the documentation is outdated and not useful anymore. So it's more valuable to have something which updates itself so you can make sure that it's still valid due to some automations. Yeah, basically I want to avoid the situation where the command says, hey, it's very important to start counting the indexes from one and then I see code that comes from zero. And then I don't know, is it not important anymore or is the code wrong, right? So perhaps another thing which often gets outdated is the readme because I saw you added to the list. So there are things which I like to have in readme but so my case is like this. We are in a team working for a company who on boards now a lot of new developers from the web teams to participate in React Native application development. They are being on board every three months or every two months or something like that. And what I found out it's never up to date is that like how to prepare your working environment.

Because that's always gets out to date and nobody updates this in the readme. Well the person who joins the company updates it. Well, they should probably. Yes, but this is like they make a documentation for themselves because they got the task.

Write down what doesn't work and they do it and they put the correct information. But in two months this is again outdated and the next person cannot follow this. So perhaps you can give the guidelines, you know, like go to React Native dev or whatever is the place and follow the instructions over there. Or like you will need an Android studio find out how to do it but not like giving line by line examples because this gets outdated very quickly. But there are a few good things to put in the readme. Well you're totally right and I think there is a fine line between different teams. The line is in a different place. What makes sense to put in the readme? And I totally agree that anything that is in the official documentation of the language or framework that you're using has no place in the readme. Just telling, hey those are the docs, go, educate yourself, come back, contribute to the project, right? But things that I would expect to see is how to deploy the app. Are we using Expo? Are we using Fastline? Do I have to run it manually on my machine? What do I have to configure to run it on my machine? Do we run it through work? So do I have to tag a specific way?

Usually I would not expect it directly in the readme but in the readme I would expect a reference to a docs folder where I have a markdown document just about deployment for example. The second is I would expect instructions how to run the development environment because usually it is not enough to go through the setup of like, hey I have Android Studio, I have Xcode, I have the Watchmen and whatever libraries I need.

But also I have to create this .env file with a link to the staging servers. Where do I get this link? Do I need some magic password? Where are they stored in my company? Is it like a one password vault? Who do I contact about it if I don't have access? Tough, that would be part of the onboarding and that is specific to this particular project. Yeah, in the example I just gave, I think it would be fine to say, hey the deployment key stores or credentials are in the one password vault but we expect the developer to know how to get the company's one password vault because we imagine that they have somebody to ask if they don't know. Also a useful place to put executable documentation is the package.json where we have scripts

and they sometimes do like you would put scripts for common things that you would do. I assume that anything that is in the code base is working because if we have a script that is no longer working and nobody else, nobody is using it anymore but the code is still there, it's a bug. This has to be removed because it's just confusing. Is there anything else you think would be important to put in the project's readme? When you were talking about it I was thinking about the decision, how much of the text you're putting in the readme versus how much your reference to other sources, perhaps that could be segway to the next point, the external documentation because sometimes it's a pain to change readme for some people especially if you like to be a little bit more nicer for the readers. If you would like to add images and so on, then it's kind of like writing a readme with images and so on is getting a little bit more convoluted. Perhaps there is some things which I prefer to have in the readme but as a link to confluence for example, even if I had confluence. Perhaps the changes, this is a little bit more dynamic, more people can improve your documentation because we're going to talk about it in the external documentation is that writing a good documentation is hard and perhaps the developers are not the best ones in doing this.

Perhaps having this early accessible in confluence where everybody can correct your sentences, make it more readable from different perspectives is a little bit easier than adding commits and pull requests to change readme. But I don't know. That's my point of view. There are different points. No, no, it's a great point because I think it's obvious that some of the documentation is going to be for the team that is internal and the other is going to be for the other people created by the team but for other people to read and this should not go to the readme. I think some kind of like business context probably should be in some kind of company database which would be notion confluence or whatever else you use.

Contributing to readme when you for example have a policy that you cannot push to master and you have to make the pull request to fix the typo in the readme.

And then imagine that you're doing some changes in the readme and you have random fail in the end-to-end tests or something like that. Yeah, exactly.

It's totally blocked. Happens. And you're just random designer who browse by the code to check something and they notice there was a typo. So there will be place for external docs. And I think it's kind of like a line between what we think about as documentation artifacts

and what is like inline documentation because what we talked about now is more like inline it documenting the code. It's in the context of a particular thing.

And this external documentation I think it's more related to the business, to team's decisions, stuff like this. And the audience for it is not developers who use the code base on a daily basis. And I think the classic example of such an artifact would be an architecture diagram.

I used to absolutely hate the architecture diagrams because I think they fall into two possible camps. One it's so simple that it's useless because it's basically you could describe it in a single sentence and it would be much faster than making a picture.

And the second camp is it's so complicated that it's unreadable by anybody who's not really working on the particular team. Probably there is a third camp of actually useful diagrams but... You haven't been invited to this camp yet, right? So yeah, I guess those projects go well so they don't invite us to work on them. But all joking aside, I changed my mind a bit when I heard about the rule that every team in the company gets one diagram to describe their whole architecture.

So you're allowed one image, make it count. I think that was one paper because the image can be scaled indefinitely. I think the idea was A4 paper sheet or something like that. Could be. Also what helps is that if you only have one location, even if it's infinitely scaling image, it's one place where you change it so you can keep it up to date. Okay. That's one thing.

And when you look at it, you know that's the full picture. You know there's no another image that represents the other half of the architecture that you don't know about, like invoicing service. You know that everything is there. So I think that's a cool constraint, helps the teams not go overboard and not go too granular into way they're describing things because you can go as deep as you want with those diagrams. But at some point they are just valuable, understandable to the person who drew them. And yeah, we want them, people from other teams to be able to parse them quickly and understand them. But also I'm biased because right now we're working on mobile apps only. It would be probably different story if I would be working on back-end architecture. I would be loving diagrams there. But for us, like the, yeah, in the diagram it's only important which services are exposed to the outside world, which of our APIs that we can use, and which ones don't. That's usually the day-to-day. Okay. Patrick, maybe you can tell us about a documentation artifact that you particularly enjoy or not enjoy.

The problem of the solution perhaps. My problem is that some companies and some languages, especially let's say countries, really fun of abbreviations.

And they speak like, you know, like this, you go to HP, then you look at the PP and then you have SVG. Oh, SVG does. Okay. Random, some random letters put together.

And especially, you know, okay, perhaps not many people speak like this, but this is like my daily Jaira tickets experience.

That's abbreviation followed by abbreviation followed by abbreviation.

And this is really cumbersome because if you are new to the company, you cannot follow. And then you always have to be the one who is asking like, what do you mean? Or making mistakes because you thought, you know, what is something is meaning, but it's not really that. So one of the things which should be captured inside of the conference or somewhere else would be like the domain language. What is this abbreviation? But as well, like if we have, I don't know, like some kind of cryptocurrency application

which deals with tokens or perhaps it's not a token, perhaps it's everybody calls a coin or asset or whatever. Right. So if you have several teams like web team, mobile team and then backend team and everybody calls the same thing with a different name, then you're going to have a really, really messy code base basically. Right. And it would be hard to communicate between teams and the APIs will be even more confusing. So it's really cool to agree on the main things, how we call them and write them down in the conference. I think it's really helpful.

And this is one of the things you should start when you're starting a company. I like your solution, but there's a problem with it. Every team will have terms that they use every day that other people will not understand. Other people, including the new hires, they will not know what you're talking about. So it's super useful to have a dictionary of those terms. And it's very interesting that once you start building this dictionary and you invite other people to contribute, what kind of these agreements you're going to see there. So you will see that everybody was using this term, I know ABC, but you have three people claiming it means a different thing. That's why I make this remark when you start a company, that should be the first thing to do.

Because then you can start with this and then everybody can join in and use the same things. But if you have five years old components start to enforce a domain language, then perhaps you're out of luck. Yeah, I've seen this go sideways in two ways. One is that there are people who refuse to use it. They know it exists, they contributed to it. But for some of the definitions that the team decided on, they just have a different opinion and they keep using their different vocabulary. And for me, it's extremely annoying because I'm the kind of person who, if you have decided on the rules and you're then not following them, then they're like stuck overflowing my head. The second problem is that when you start a company, you could have one dictionary. But as people join, it gets super difficult for like, I don't know, 50 people to agree on a one single vocabulary. I think those vocabularies should be like, team based, feature based or domain based, something like this, because you will have like a particular subset of what the company does.

Inside this space, we agree on this vocabulary to one thing limit the amount of people who need to agree to use it and make that you can police into using this vocabulary. And the second thing is that for me as a developer, invoice means one thing, like that's a bill that you send to the user, right?

But for the people in accounting, ah, that will be a different story, right? Because then you would have an invoice that I guess would go to a company customer and there will be a bill that goes to a private customer. And then you have distinction between them. So a lot of those teams would go very granular on those terms that other half of the company like doesn't care because it's not their problem to solve. And the way to manage those dictionaries would be to have them like scoped by the team. Of course, this will suck for people who work across teams like higher level managers. Yeah, of course, if you don't have to share this knowledge, then it doesn't have to be in this domain language. But if you agree that something is going to be shared, then not communicated the name of it, it's going to end up in even more mess.

So if you want to communicate, you have to define the interface and the naming is one part of the interface, which is super important. That's why I think this part should be inside of the domain language and cannot be that one team calls differently.

Yeah, there is some kind of high level part that will have to be common across the whole company. Yes. But if you make it too big, then I mean, some documents are too big to be read and understand. So any documentation should be kept readable. If it's just page after page after page after page of abbreviations, then nobody's going to pay attention to it. Yeah, we talked on the last podcast that Half an Hour is our sweet spot. We're past it.

But there's one more amazing thing I want to share around documentation, which I recently has become a fan of. There is this concept of ADR, which is another acronym that Patrick doesn't like for architectural decision record.

And actually, you could just call it decision record. It's a document that explains why you decided to do something a particular way. Example from React Native World. Let's say you're deciding which navigation library to use, which I thought we don't have to decide about it anymore. But now with X for router, because again, become a thing to decide which navigation library to use. At some point, your team will have to decide that probably there's going to be some discussion.

And there will be some points raised. And in the end, you make a decision. And then Half a Year down the road, you would like to maybe revisit this. It would be great to have a written down account on why you decided on it this way. So what you would put in such a document is the problem or question you're trying to answer, the decision that you made, the context in which you made it, and possible alternatives that you considered. And the context could be business requirements, could be the team structure. So for example, if you would be deciding on building a new mobile app, and you could build it.

So the business context could be, for example, hey, this needs to run on both iOS and Android. So we already know that if you choose it to do it native, then you would have to build two apps, right? Team constraint could be, hey, we only have people who know JavaScript.

Here it might explain why the team chose to build it in React Native and not Kotlin. Those documents are great for two purposes. One is making decisions clearly. So somebody has to sit down and write down all the options and think through them and basically suggest a decision. Then you would have a meeting, but then you would only discuss. We assume that everybody knows what's written in the document. Is there anything they missed? Or is it just a decision making time to weigh the pros and cons?

And later, when a new hire joins in and he says, hey, why is this API REST and not GraphQL and reopens the old can of worms, you just send them a link to your repo or you have this document sitting and you say, like, that's why. Like, do you think the context of the decision has changed? Like there is a wish to revisit it, because if the context didn't change, then there is no reason to talk about it again.

Those are two reasons I like them. And probably they are kind of like bridge between the two worlds, between like team documentation and external documentation, because some of the decisions will influence other teams. And then those ADRs should be in confluence, for example, visible to other teams like out in Dopan. So the example of something that influences other teams is you decide to use web sockets to communicate with the backend. So like, obviously, it influences the backend. Now, probably it would even be the backend which makes this decision.

Other thing would be what kind of navigation library or localization library or animation framework we use for Dop. And this would just sit somewhere in the docs directory and be just for the internal usage of your team. So yeah, I think it's my favorite documentation artifact. Very useful, because even if you are part of the team, perhaps even if you are taking part in the meeting where something is discussed, many people are there, but not mentally present. And it's very likely that in two weeks, somebody is going to ask you again the same question, like what was this? Why did we decide on this meeting? Or why are we going this way, not the other way? And having it written down, it's very cool. But as well as you said that you came prepared with something written down already. It's a great way of preparing yourself and making the best possible preparation and going in deep into the topic. So the meeting is much more useful for everybody, but as well give you more arguments for your proposition because if you came prepared with something written, usually what it means, this guy knows something. Yeah, it's a proof of stake, no?

Exactly, a proof of stake. Yeah, it's a great way to box through some decisions. Also one thing I wanted to touch on here is that I heard this phrase recently that writing is thinking. And the point there is that writing something down forces you to think about it because well, you have to know what to write.

And the other way around, that thinking without writing it down in some actionable way is kind of pointless because you think it and then it goes away unless you write it down for somebody else for the future. It's an actionable task that touches on something you said before that developers maybe shouldn't be expected to write long pieces of text because they might not be the best people to do it nicely. I disagree because I think it's part of the developer's job. Like we write stuff the whole day. We just write a bit differently. And I think writing is communication and communication we know is crucial for software projects and you have to communicate intent in your code. You have to communicate intent if near wards on Slack in Confluence as well. If you want to be a good developer, you need to write well, communicate well and just like part of the job. If somebody chooses not to do it, then it's like choosing not to learn a programming language. Just for the record, I never said that developers shouldn't write. I said that perhaps for some developers it's not easy. But I definitely encourage everybody to write more and to get better in writing stuff because it helps you, as you said, it helps you think, it helps you to process what you're learning because you have to restructure it in another way. And if you cannot restructure what you learned, then you haven't learned it. Because if you can write something the way that somebody else can learn from it, then this is when you're really a kind of expert in that what you wrote. Otherwise, you just rewrite somebody else's words. So writing down, it's really crucial for learning experience. So yeah, everybody should write down things. So maybe we should go back to the beginning of the podcast and record the whole thing. The more you document, the better because writing is good in the end. Yes, but you don't have to commit everything to the repository. That's the trick. You should write it down, but you don't have to put it in the repo. That's the solution. What I try to do sometimes when I have a hard problem is to write down a documentation of what I want these things to do. So I write my in-depth audio code. I write, "Okay, I need this, then this should do this. And when I have this, that should be the outcome." And I replace the free text with the code. That is something which I sometimes do, and that's helpful. I don't know if that's connected with writing and learning, but sometimes, you know, like just writing down something, it forces you to rethink or triggers different paths in your mind and it helps out with solving problems. I think that's what's advertised as the main benefit of test-driven development, except you don't, you write it in like your own syntax and not in an executable fashion and you throw it away in the end. Cool. Those are some actionable steps in the end. Write more when you need to think about the problem. If you're not documenting anything, maybe start with the ADRs that will help you make decisions. Yes, start with that.

From the technical tips, we have Patrick's great tip on the TS expect error. I've seen in the wild like 90% of the time TS ignore and very rarely TS expect, so I think a lot of people would benefit from knowing it exists. You can try to replace some of them on your code. And then you'll see that you had an empty TS ignore, which actually does not trigger any errors anymore. Yeah, then you delete something. That's good. Less code. Less code is better code. Cool. Thank you, Patrick. Thank you. As usual, it's been a pleasure and I hope our listeners enjoyed listening to this episode as much as I did recording it. Thank you for listening to the show. We're happy. We're providing value to you. And if you have any suggestions for future episodes, just drop us an email or get in touch in any other way that you think it's going to work.

Yeah, have a great day, afternoon. Have a great tomorrow, like Patrick.

Enjoy everybody. Bye bye. Thank you for listening to the episode. Please subscribe if you haven't yet. And if you like our show, consider sharing it with your friends. You will find notes to this episode on our page, brainsandbeards.com slash podcast, where you can as well give us feedback or suggest a topic for the future episodes.

We would be very happy hearing back from you. Stay safe and curious. Till the next one.