You might not think about this much when you are writing the world's next generation of modern software, but, as a support engineer, a large part of my role is sifting through a nasty amount of tech debt.
A long time ago, a guy told me that they style their code to be understandable by two people: himself, and himself in a few years.
It might not seem like much, but the benefits of documentation scale a lot, especially when it comes to technical support. I'm not saying it's the only thing good code needs--extracting functions where necessary is also great, but taking a look at your code and answering possible questions about it can do wonders for the next person that needs to understand WHY the engineer made their decisions.
For example, the other day I was investigating a case where millions of database records were clogging up a system, generated by what seemed to be a rather poorly-coded event handler. I observed that there was a threshold setting to prevent multiple events being generated by a single user, but the threshold's default was "1 second." To me, it seemed much more sane to change this to be 1 minute, or even 5 minutes.
The main concern that comes to mind when I see something like this is why? Why did the engineer choose 1 second? What is worse is that I saw in the git history that it used to be 5 seconds. So, why would it be lowered??
So, instead of understanding immediately why it might be low as a default, I have to spend more time understanding more of the module and understanding possible side effects of increasing the value. If only the engineer had documented why the value was defaulted to low—chances are they may have not chosen that default in the first place if they reviewed it.
Some things that I recommend after crawling through many rabbit holes of codebases for troubleshooting:
- Debug log uniqueness - do not make two areas of code print the exact same words. It makes it hard to find what execution branch the message is from.
- Plenty of debug logs. Don't flood the logs, but the more the better when it comes to problem solving. Include the stack traces, too!
- Function extractions - Naming functions is a great way to self-document your code. Seriously, keep your functions small and give more names to your code.
- Why and how, not just what. While "what" comments can be useful, you should avoid writing code that needs too much explanation. What I often see lacking in codebases is why decisions were made and how the code is supposed to be used.
For example (based on a real situation I had to deal with):
// This threshold controls the minimum time since the last
// event that we should run the process again.
throttle = fetch_property( "refresh.throttle", 1000 );
Could use improvement.
// We chose default 1 second even though that seems low
// because some companies have access grants on user
// action, and we want that to refresh within 1 second.
throttle = fetch_property( "refresh.throttle", 1000 )
Perfection. The code logic is pretty obvious, so this gives the "why." This still seems like duct tape to me, but at least I understand the decision. If I increase it, then it might mean that users have to wait up to the throttle time before their access is granted (after pressing a 'let me in' button). Ideally, this operation would be event-based for cases like this and not polling-based.