Picture this: You join a new team and you start to get to know the codebase. After some time you see some suggestions you would like to make to the system and some of the design principles make no sense at all.
You bring this up to your teammates and eventually get shot down with some generic phrases like, “it just works this way” or “we have looked at it before and it’s very difficult”. Maybe someone has implemented it a while ago, left the company and no one is brave enough to try and change it.
It seems like nobody quite knows why things are a certain way, but there is a reason.
Or in another scenario when you suggest improvements to the design people say that they have tried it already, it was hard, and it did not work so there’s no point wasting time on it again.
Have you ever found yourself in such situations? I would be surprised and somewhat jealous if you are a software developer and the answer is no.
Unfortunately, there is no easy way to get those historical answers. For the first scenario, your best bet is version control history, but even then trying to find answers may prove to be a challenge as this will depend on a number of factors. One of them being how well people communicated their intentions in their commit messages. Also, I have seen first-hand a migration of repository with all history lost.
There are plenty of things we can try and do to help our future self and future software engineers that will have to maintain the code we have written and systems we have designed. Let’s take a look.
Your commit messages are probably the most basic way to provide extra context as to why the code has changed and what you are trying to achieve with it. Most importantly, try to commit a little and often, which helps the reviewer understand the thinking process behind the change.
This is just an opinion but ideally, I would not squash my commits when merging pull requests. Mainly to have better historical reasoning as mentioned in the previous paragraph.
Most importantly try and make sure your commit messages are meaningful and useful. It is not an easy task and you may find it difficult at times, but like with most things the more you practice the easier it becomes.
Change-logs are great as they provide history in one wonderful document. There are numerous ways to automate them. Ideally you want this automated but there is nothing wrong with doing it manually if it makes sense to your team. It is highly likely at some point someone will get fed up with the manual task and automate it anyway.
One of the techniques you could do is use something like Conventional Commits to help you automate the process. Even if initially developers may find it frustrating, I would suggest that this helps them write better commit messages as it enforces some guidelines in doing it.
Now you don’t have to use conventional commits or follow semantic versioning as there are other ways to achieve the same result. Having an easy to access human-readable document with some sort of versioning can prove to be very useful especially when it comes to supporting your application in production and you need to easily understand what changes are live in front of your users.
You have to write documentation no matter your personal preferences, as the reason you do it is not for yourself but for other people. In the case with documentation, there are always at least two consumers of that documentation: The developers that may contribute to the codebase and may need some information around how the project is set up, and the consumers of your application or API.
Common topics to include in the documentation are things like: How to configure the application, what APIs does it provide, examples of how it can be configured, and most importantly examples of how can you test it if it’s relevant. The content and the shape your documentation will take will depend on the target reader, so you may need to provide two or more sets aimed at different audiences. Think how Facebook provides help pages for regular users and a whole separate site for developers. This of course all depends on the scale of your application.
Avoid putting low-level or implementation details in the documentation. Similarly to how you would not test implementation details in your tests as these can change and become obsolete.
This is a somewhat hot topic that is coming up more often where people finally realise that developers are also human beings that have a need for a good experience when using all sorts of tools for their job.
When it comes to developer experience, documentation is a big part of it. However, an often overlooked way to provide a good developer experience is to help and navigate the developer on the right path using meaningful error messages, examples and links to other relevant pieces of information in those error messages.
For documentation, think about examples like BrowserStack or Sentry that provide snippets with your secrets embedded into them when you are logged in to the website, so all you have to do is copy and paste it across. This saves an action and has always put a smile on my face and made me notice that extra little help.
When it comes to errors or warnings, do not just leave cryptic messages and instead spend some effort and give suggestions as to why things may not be working. A good example is the React team that have done a really good job with these over the last few years.
Good user experience is something that is widely accepted as a given these days. When it comes to developers the experience they have using your platform, API, module or any other piece of software can make a huge difference and most importantly may make life easier for new developers that come to our industry.
Something I have started doing recently, over the course of the last few years, is write decision logs when we are choosing something that we think someone may question in the future. For example, what state management library or front-end framework to use and why.
You have to add a list of pros and cons for each of the options considered. Also if anything was not a consideration ideally have a part that explains why.
Crucially this document needs to have a date of when this was written so that future teams can easily understand why things were done and when. For example, some show stoppers may have been resolved by the time a new member of the team has joined and it makes sense to actually move to another state management or testing library. In terms of where to keep this document I would suggest in your Wiki or your repository. Anything relating to specific code should live closer to it. Alternatively, you could have them in Confluence decision logs, it all depends on what works best for your team.
This may be a personal bias creeping in but I love diagrams. Nothing helps me understand how the system interacts better than pictures that explain how requests flow through your system.
Keep this someplace where you can version it so you can see the evolution of the system. Version numbers are good but things like dates are really important too.
In summary, you have to create some sort of documentation and historical audit trail for future teams. As well as spend some effort in helping any developers who will be working on the code after you have long left the company. You may not witness this directly but I am sure it will make some developer pretty happy someday.
Now I am not suggesting for you to necessarily do all the things I have mentioned, as it may not always make sense due to the size of the project and other factors, but at least try and do some of them. Unfortunately, there is little accountability in our industry. I have to work on some projects that were neglected and had to spend days and weeks working things out. Let’s try and be kinder to one another and to future developers.
My two parting thoughts that I think will help teach a little bit of empathy for those who have made decisions before us are:
“Always assume best intentions”
“Teams make decisions based on the best understanding they have at the time”
Cover photo by Juan Pablo Rodriguez