I recently stumbled on an interesting link regarding NASA and it’s version of the “Ten Commandments” of computer programming.
It’s a compiled list of software design principles and guidelines that, no doubt, came from hard won experience and mistakes that NASA computer programmers have learned over the years.
As I read over the list, I appreciated just how valuable it is, when you think about the kinds of things that NASA software programs must accomplish.
During the Apollo moon missions, the onboard guidance computers were crucial in ensuring that all critical phases of the mission: launch, booster rocket separations, mid-course corrections, LEM module docking procedures, the lunar landing and liftoff, redocking and return path to the Earth.
If you think about the disastrous consequences that could happen if any of the computer code needed for any of these critical maneuverings was faulty, you quickly realize just how important code quality is to NASA software development.
Even a single computer mistake in the code could have real tragic consequences when you’re talking about mission-critical software that could potentially affect human lives.
Most of us in software development don’t have to worry that our software could affect people’s lives if it fails. But if I was in a position where it was, I would certainly make code quality and craftsmanship the top thing on my mind, as if people’s lives depended on it.
During the Mars rover missions in 2003, the software onboard the robotic rover vehicles, Spirit and Opportunity, had to be bulletproof for different yet equally important reasons for the success of its mission.
The fact that there are 249 million miles between Earth and Mars means that you can’t walk over to the rovers, and fix or update any piece of the software running those rovers.
That means that the software running on those rovers needs to be practically FLAWLESS … there’s no practical or easy way to update the software, so you better make it as bulletproof as possible, because you really only have one shot to get it right.
Of course in life, there’s no such thing as perfection, but it’s not a bad goal to strive for it in your software.
Some of NASA’s golden rules were created at a time when C and C++ were the mainstream computer languages of the day. C and C++ required a developer to use compiler software to compile the raw C and C++ source code down into the raw, native binary language code which all computers (even to this day) can understand.
Of course, a lot of water has run under the bridge since then. C and C++ were primarily used by programmers to create fat, thick client software that needed to be manually installed to a computer.
With the internet revolution, the software development landscape has radically changed for developers.
Javascript has become the new “lingua franca” of computer programming languages, and the internet provides worldwide connectivity so that a software application can connect to any other application or data endpoint on the planet.
Some of NASA’s commandments aren’t as particularly relevant today. For instance, rules 8-10 are — regarding preprocessors, pointers and compiling of code — very specific to the C and C++ programming languages, which require a developer to manually compile raw source code into the binary code computers actually need to execute.
Languages like Javascript, Ruby, and Python, don’t require manual compilation of the source code… assuming the source code doesn’t contain any errors, they will just run automatically, as soon as you execute them, without the need for code compilation.
That said, it doesn’t invalidate some of these slightly outdated rules… they are still perfectly relevant for compilable languages like C and C++. The rest of the rules are less geared towards the nuances of specific computer languages and apply to general good practice software principles.
So … on to the commandments!
Rule No. 1 – Simple Control Flow
I can’t be sure whether NASA prioritized these commandments in terms of importance or not, but simple control flow is an important one.
Ask any software developer if they understand the term, “spaghetti code”, and I’d be surprised if you find ANYONE who is unfamiliar with the term.
Spaghetti code, like spaghetti on a plate, flies all over the place.
It’s the same with computer code. A computer could care less HOW you write your code. The code can be the ugliest, most godawful looking source code in the universe, but as long as the code can successfully execute and be understood by a computer, a computer could care less.
But rule 1 is more for the benefit of us humans than for computers.
Computer code needs to be HUMAN READABLE.
Simple control flow means no crazily nested if-then statements. Or switch statements with a million case branches. Or code that jumps all over the place.
It should flow the way a gripping and fast-paced novel should flow, DOWNWARDS towards the bottom of the screen.
Rule No. 2 – Fixed Upper Bound for Loops
The power of computers is their ability to do mundane tasks, very very quickly. Like adding numbers. Computers are essentially giant calculators that can perform math operations at an inhuman speed.
One of the most powerful things a computer can do is REPEAT ITSELF. That is, perform LOOPS.
Every computer language since the dawn of computers has the ability to loop. My very first BASIC computer program could loop
10 PRINT “HELLO!”
20 GOTO 10
A computer will cheerfully run a loop till the end of time.
Which is the problem that rule #2 tries to address.
A fixed upper bound for loops means you make sure a loop ends once the loop hits a certain condition to break out of the loop … otherwise, the loop will run on forever, which in most situations, is NOT what you want.
In a traditional for-next loop, the loop construct in pretty much any computer language requires an upper bound value.
ie.
int MAX_CEILING = 50, i;
for (i = 0; i < MAX_CEILING; i++) {…}
This loop will continuously run from the value 0 to 50 iterations. It’s also wise to avoid “magic numbers” in your code, and assign the number to a variable that conveys it’s intent … in this case, MAX_CEILING.
Programmers need to define their loops carefully or else you could end up with runaway loops or loops that don’t end at the right condition or point in time.
Rule No. 3 – No Dynamic Memory Allocation
Rule #3 is not as relevant today with the advent of newer programming languages that came out after the introduction of C and C++.
All software programs require a chunk of memory (the RAM storage inside a computer) set aside, in order to run on a computer.
In the days of C and C++ programming, you had to manually allocate and set aside a specific size of memory for custom data structures, if you didn’t know ahead of time how much memory the data structure would require.
For example, say you wanted to read the contents of a text file into memory and store it into a custom data structure. Since you had no way of knowing ahead of time exactly how much memory from your computer you needed to hold this text file data, you could call the malloc() function to make sure you grabbed more than enough ram memory to hold your data.
If the size of the data you need to manipulate could potentially change and grow at runtime, you could malloc() to allocate additional RAM from the computer to address the growing size of your data.
However, rule #3 states that caution is advised when performing this kind of dynamic memory allocation in your application, as memory leaks and other unwanted behavior can happen when dynamically manipulating memory like this.
The rule advises the programmer to use fixed size data structures that don’t change in size at runtime, whenever possible.
However, in this day and age, newer languages like Java and C# and Javascript don’t require you to worry about memory allocation … behind the scenes, the runtime environments in those newer languages automatically take care of allocating and freeing up computer memory for your application, when necessary.
Even so, it’s a good idea as a software developer to be aware of how much memory the objects and data structures can take up during runtime.
Rule No. 4 – No Large Functions
Rule 4 recommends that any function or method definition not contain code that exceeds the vertical height of a computer screen, or roughly a single sheet of paper.
If functions or methods run beyond a screen, the rule implies that the function is doing too much.
For example if a function is calculating the sum total of a customer’s purchase order, and then printing out the customer’s order, rule 4 states the function is doing too much … it needs to be split up into two separate functions, one for calculating the customer’s order and another for printing out the order to the printer or screen.
It makes the code easier to read, understand and debug when code is split up into many small and discrete chunks.
Rule No. 5 – Assertion Density
Assertions are a way of verifying things that happen in your code. For example, say you’re passing in an object into one of your functions that represents a data access library that allows you to connect to and query a database.
Before you actually try using the object, it’s a good idea to assert in your code that the object that as passed in, is not null, and in a valid state for use.
public void QueryDatabase(DataLibrary *library) {
assert(library != null);
// now we are safe to use the library object
// code continues here…
}
Rule 5 states that adding assertions to your code helps with the overall defensive coding strategy.
Rule No. 6 – Declare Data Objects at Smallest Level of Scope
Rule 6 states that the closer you declare an object to the time you actually need to actually use it, the less chance there is that other code can corrupt or misuse the object.
Rule 6 also implies that you need to avoid declaring objects that are global and visible by any other methods or classes in your application. By limiting the scope, that is the “visibility” of your objects by other code in your application, you are reducing the chance of unwanted side effects of your object by other code.
Rule No. 7 – Check Parameters and Return Value
Rule 7 is relevant for any programming language, old or new. It simply states that you need to check any incoming parameters passed into a method/function or whatever data or objects those same methods and functions return.
You check to make sure they are not null, that they are in a valid state you are expecting before you use it.
This is especially relevant in the age of the internet, where you often have to deal with data submitted from a web page.
You should ALWAYS assume the data passed in from a web page is suspect. The internet is literally a hacker’s paradise. You can unknowingly execute malicious code retrieved from a link or web page that can do bad things like query valuable data out of your enterprise database.
This isn’t hyperbole. Data breaches where MILLIONS of people’s credit card and identity information can be stolen is a very common occurrence in this day and age. Hackers take advantage of code that doesn’t properly verify the data submitted by a web link or page.
It’s even more important in today’s day and age to always verify data you need to use in your own application.
Rule No. 8 – Limited Use of Preprocessor
Rule 8 is really only relevant these days for C and C++ programming, but preprocessors let you do things BEFORE your code actually executes, hence the term PREprocessor.
#include <stdio.h>
#define message_for(a, b) \
printf(#a ” and ” #b “: We love you!\n”)
int main(void) {
message_for(Carole, Debra);
return 0;
}
The #define message_for macro allows you to replace the printf statement with the ‘message_for’ macro.
Rule 8 discourages this kind of macro development as it obscures actual source code with an additional level of indirection, requiring the developer to spend additional time tracing and understanding the code.
Most newer programming languages don’t have preprocessing capabilities which makes rule 8 less relevant in today’s programming landscape.
Rule No. 9 – Limited Use of Pointers
Pointers are also mostly a thing of the past, unless of course you continue to program in C or C++.
Essentially pointers are variables that store the memory address of another location in memory.
Passing pointers around in functions save more space in memory than having to pass around actual data structures and objects.
However, pointers can cause headaches and unforeseen bugs if you aren’t careful. Hackers often take advantage of the nature of pointers to insert malicious code in memory, which is often why C and C++ are the primary languages hackers like to use.
Newer programming languages don’t require you to directly manipulate memory addresses…they take of memory manipulation behind the scenes for you.
Rule No. 10 – Compile all Code
As mentioned before, the wave of the future seems to be Javascript development. Javascript itself doesn’t require manual compilation like traditional languages such as C and C++.
That said, this rule still has relevance even in today’s programming world.
Just because many newer languages like Javascript, Ruby, and Python don’t require manual code compilation, doesn’t mean the IDEA behind compilation isn’t still relevant.
What the spirit of rule 10 is saying is that our code should be in a state that is ready to execute and deploy in a production environment.
The spirit of rule 10 is really the concept of “continuous improvement”.
Even with languages like Javascript, that don’t require traditional code compilation, you can still create a development environment where once you save some new code, all your unit tests will automatically run, report any errors, continue to the deployment process if no errors are found, and any other necessary build/test/deployment tasks can happen.
I think this is the spirit of rule 10, to make sure your code is always in a good state which can be ready to deploy at a moment’s notice.
Conclusion
Some of these rules might be a little outdated or too geared towards certain programming languages, but in general, I still found them quite useful to strive for. Not to mention the credibility of real NASA software developers who came up with these hard-won lessons.
Sure, computer programming may not be rocket science … but it certainly can’t hurt that these rules came about with the help of rocket scientists?