Foreword — Dear beginner, dear not-so-beginner, dear reader. This article is a lot to take in. You’ll need perspective for it to make sense. Once in a while, take a step back and re-think about all the concepts explained here. They helped me a lot over the years, and I hope that they will help you too. This article is my interpretation of them for the work I do, which is mostly web-related development.
NASA’s JPL, which is responsible for some of the most awesomest science out there, is quite famous for its Power of 10 rules (see original paper). Indeed, if you are going to send a robot on Mars with a 40 minutes ping and no physical access to it then you pretty damn well should make sure that your code doesn’t have bugs.
That’s a question I have been considering for years and here is my interpretation of the 10 rules applied to interpreted languages and web development.
1. Avoid complex flow constructs, such as goto and recursion.
Original rule — Restrict all code to very simple control flow constructs – do not use goto statements, setjmp or longjmp constructs, and direct or indirect recursion.
When you use weird constructs then your code becomes difficult to analyze and to predict. The generations that came out after goto was considered harmful did indeed avoid using it. We’re at the stage where we’re debating if continue is goto and thus should be banned.
My take on this is that continue in a loop is exactly the same as return in a forEach() (especially now that JS has block scoping) so if you’re saying that continue is goto then you’re basically closing your eyes on the issue. But that’s a JS-specific implementation detail.
As a general rule you should avoid everything that is mind-bending or hard to spot because if your brain power is spent understanding the quirks of jumping around then you’re not spending it on the actual logic and then you might be hiding some bugs without your knowledge.
I’ll let you be the judge of what you put in that category but I would definitely put:
goto itself of course
PHP’s continue and break used in conjunction with numbers, which is just pure insanity
switch constructs, because they usually require a break to close the block and I guarantee you that there will be bugs. A series of if/else if will do the same job in a non-confusing manner
Besides this, avoid of course recursions, for several reasons:
As they build on the call stack, whose size is very limited, you can’t really control how deep your recursion can go. Even if your code is legit, it might fail because it recurses too much.
Do you get this feeling when doing recursions where you don’t really know if your code is ever going to stop? It’s very hard to imagine a recursion and to prove that it will stop correctly at the end.
It’s also more compatible with the following rules to use an iterative algorithm instead of a recursive one, because you have more control (again) on the size of the problem you’re dealing with
As a bonus, recursions can often come as an intuitive implementation of an algorithm but is usually also far from optimal. By example we often ask in job interviews to implement the factorial function using a recursive function but that’s far less efficient than an iterative implementation. Regular expressions too can be disastrous.
2. All loops must have fixed bounds. This prevents runaway code.
Original rule — All loops must have a fixed upper-bound. It must be trivially possible for a checking tool to prove statically that a preset upper-bound on the number of iterations of a loop cannot be exceeded. If the loop-bound cannot be proven statically, the rule is considered violated.
The idea with this rule is the same as with the interdiction of recursions: you want to prevent runaway code. The way you implement this is by making sure it’s trivial to prove statically that the loop won’t exceed a given number of iterations.
Let’s give an example in Python. You could do this:
def iter_max(it, max_iter):
cnt = 0
for x in it: