Coding principles

Coding principles are essential no matter what you code, in what language or at which level. A coding principle can be thought of as a rule of thumb or a proven recommendation to help the programmer write readable, reusable and more error-free code. Coding principles cover everything from naming conventions to problem solving approaches and everything inbetween. 

I try to read alot. Being neurodivergant it's hard to read longer texts, especially novels. But shorter texts like blog posts, especially on a subject I'm very interested in, is easier. Over the years I've read alot about different coding principles. Some are clear and immediately useful and others are more abstract and not very helpful in the moment. I thought an early blog post here would be a perfect opportunity to share some of them along with explanations.

Hofstadter's Law

One of the best laws to keep in mind early on is "Hofstadter’s law". It states:

"It always takes longer than you expect, even when you take into account Hofstadter's law."

If you've developed a small program or a set of simple functions on several occasions you'll get a good idea of how long it takes you. For instance, setting up a fairly simple static webpage takes maybe a couple of hours for me, depending on functionality. But since most of what I code differs in several ways, and given increasing complexity, it's very difficult to estimate the time it will take to produce clean, readable and error-free code. Even if I take Hofstadter's law into account when doing the estimation. Now, this is not very useful in terms of giving you a clear answer on how to plan your time in a project, but I've found it is still a very good reminder of the state of things. Sometime's you'll surprise yourself in how fast you can accomplish something but more often than not it WILL take longer than you anticipated.

Rule of Three

To explain this one you first need to know about DRY and YAGNI:

  • DRY - Don't repeat yourself

This essentially mean that you should ot write duplicate code segments. You should always strive for keeping your code unique and reusable. If you write a function that you end up using in several different places, it makes less sense to actually write that piece of code every time you want to use it. Instead, write one function and call that one in every place you want that functionality. This will save time and make your code more readable (both to yourself, later on, and to anyone who inherits your codebase). The problem though is that you often don't know immediately if a piece of code will be reused, and that's where the Rule of Three helps out. 

  • YAGNI - You aren't gonna need it

This is a kind of warning against writing code that you think you might need further on in the development. This is likely a time-saver. The most important reason for this warning is that between now and the future point when you think you might need it, things will likely change in such a way that you probably won't need it. At least not in the version you made it. But it's the same problem here: it can be hard to determine if it will be needed or not.

So, how do these principles tie into the Rule of Three? Well, while they make sense and indicate actual problems, they don't really tell you when or how to act on them. With YAGNI you can't really be sure when to not implement some code that is not immediately usable. But with the Rule of Three you get very clear instructions on this. It basically says that you should only reorganize or optimize your code if you are about to write it for the third time. Let me explain that in more detail:

The first time you write a piece of code it's business as usual. You write it, test it and move on. The second time you realize you have written this piece of code once before. Now you just copy it and make small modifications if you need to. The idea here is that having the code duplicated only one time isn't a big deal in this stage of development. Starting to consolidate and optimizing your code too early will make it very time-consuming and will likely lead to making Hofstadter's law a reality.

But the third time is the charm! When you realize you have written basically the same code for the third time - that's when you start to consolidate it into one single reusable piece of code that can handle all three cases. The idea here is that once you write some functionality for the third time, only then are you able to understand what general functionality you really need.

Make it work, make it right, make it fast

Here's another clear applicable advice that you don't need a lot of context or deep thinking or planning to actually start using while coding. One of the biggest challenges when writing code is to write code fast, and at the same time make the code fast. This balance can be very hard to achieve from the start so the advice here is to split your coding up in three distinct phases.

First, your only focus should be to write code that works. It doesn't have to be perfect or do exactly everything right, it just have to work in some general meaning. Once you see that your code works in this sense you move into phase 2: Make it right. This phase comes from the knowledge that something that works isn't necessarily working in the right way. Lets take a brief example of the difference between code in these 2 phases:

Lets say you need a function that calculates the average of a set of numbers.

Function getNumberAverage(numbers) {
     return sumOf(numbers) / lengthOf(numbers);
}

This works as long as there are actual, simple numbers going into the function, but you can argue that it doesn't work right as it won't handle non-numbers, empty parameter or error handling. So you delve into phase 2 and make sure that your function is more robust so that it works in all the right ways. Then, and only then do you go into phase 3 - Make it fast.

Phase 3 lets you ask the question: "Is it fast?". If the answer is "No", now is the proper time to focus on making your code fast.

So why this particular order? Well it is considered a waste of time trying to optimize code that doesn't work yet. Because when you finally get the code to work, and work the right way, you will probable have rewritten the code to such extent that any optimization you've done has been written out of the codebase. The usefulness of this rule is that at any point during development, you can look at your code and see if it fulfills the three phases in order. And that will tell you exactly what you should be focusing on right now.

Here's an interesting article that highlights why it often is fast to just make it work in the first iteration and then optimize your code the second time around: "Coding faster: Make it work, then make it good (Medium, 2020)"

Single Responsibility Principle

This is a favourite of mine along with the Rule of Three and a couple of others. It is part of the SOLID principles in Object-oriented programming. The Single Responsibility Principle (or SRP) says that any coherent piece of of code - it can be a module, a class or a function - should have only one single job or responsibility. For instance, you might need a user registration process where the user is saved to the database, then a welcome email is sent out and finally an algorithm displaying new members is told to refresh. It would be logical to keep this abstract process view and code one single function to handle this part of the registration.

registerUser(approved-user) {
     saveUserToDatabase(approved-user);
     String email = getEmailFrom User(approved-user);
     sendWelcomeMessage(email);
     String name = getNameFromUser(approved-user);
     refreshNewUserBox(name);
}

But if the actual function ends up with a lot more lines of code than the pseudo-example above (and it most likely will), this will lead to code that is harder to read, understand, edit and update. No matter if this code is to be handled by another programmer later on, or if it's just you revisiting 6 months later, it will be much harder to find the right place to make edits or finding the faulty piece of code creating a nasty bug. So to combat this, you use the SRP to split the process into several functions - each responsible for doing one thing. This would look more like:

Function saveUserToDatabase(approved-user) {
     sendToDatabase(approved-user);
}

Function makeWelcomeMessage(approved-user) {
     String email = getEmailFrom User(approved-user);
     sendWelcomeMessage(email);
}

Function addToNewUserBox(approved-user) {
     String name = getNameFromUser(approved-user);
     refreshNewUserBox(name);
}

Each piece of code handles one thing and does so well. So if you later on want to change how you send out your welcoming messages, you only have that specific function to deal with and the other functions are unaffected.

A SRP tip! If you are unsure; try to describe the function or class. If you have to use the word 'and' - then it probably does too much and should be split up.

This is just some of many good principles or rules of thumb out there, so I will most likely revisit this subject in a second post some time later.