— Pixels Commander

[ In English, На русском ]

Applying NASA coding standards to JavaScript

Jet Propulsion Laboratory – scientific institution making a lot of research and development for NASA. JPL have been developing software for most of unmanned missions in the field of deep space and other planets exploration. Their portfolio includes such famous missions as Curiosity Mars rover and Voyager probe which left solar system after 25 years of flight and still providing scientific information. High level of automation and long duration of missions led to superior demands to software quality. As a result of JPL amazing experience a set of code guidelines was developed and published recently. Since demands to web-driven software constantly increase and more critical tasks are entrusted to JavaScript, lets apply NASA coding guidelines to JavaScript / HTML applications for higher performance, reliability and the better world.

Nasa coding JavaScript

1.No function should be longer than what can be printed on a single sheet of paper in a standard reference format with one line per statement and one line per declaration. Typically, this means no more than about 60 lines of code per function.

This fits perfectly for JavaScript. Decomposed code is better to understand, verify and maintain.

2.Restrict all code to very simple control flow constructs – do not use goto statements, setjmp or longjmp constructs, and direct or indirect recursion.

Rule from the C world makes wonder. We definitely will not use goto or setjmp in JS, but what is wrong with recursion? Why NASA guidelines prescribe to avoid simple technique we were studying as early as in school? The reason for that is static code analyzers NASA use to reduce the chance for error. Recursions make code less predictable for them. JavaScript tools do not have such a precept so what we can take out of this rule?

  • Use constructs which are justified by complexity. If you want to write reliable code – drop to write tricky code and write predictable. Define coding standard and follow it;
  • Use code analyzers to reduce chance for defect: JSHint/JSLint/Google Closure Tools;
  • Keep codebase by monitoring metrics: Plato;
  • Analyze types with Flow/Google Closure Tools.

3.Do not use dynamic memory allocation after initialization.

At first glance JavaScript manage memory itself and garbage collection cleaning memory from time to time solving the rest of problems for us. But it is not absolutely correct. Memory leaks often, spoiled JavaScript developers do not have a culture of managing memory, garbage collector decrease performance when run and it is hard to tame. Actually we can get three recommendations from this rule. Two of them are nice to follow in any project and last one fits for performance and reliability critical software.

  • Manage your variables with respect. Regularize variables declaration by putting them in the top of scope in order to increase visibility of their usage;
  • Watch for memory leaks, clean listeners and variables when not needed anymore. Classic article;
  • Switch JavaScript to static memory mode and have predictable garbage collection behaviour (means no accident performance regression and no sawtooth pattern) by using objects pool.

4.All loops must have a fixed upper-bound.

As JPL explains this makes static analysis more effective and helps to avoid infinite loops. If limit is exceeded function returns error and this takes system out of failure state. For sure, this is quite valuable for software with 20 years uptime! Checks for limit exceess are curried out by assertions. You may find more details on assertions in fifth rule. If you accept assertions practice use limits for loops as well, you will like it.

5.The assertion density of the code should average to a minimum of two assertions per function.

It is good to put few words here on what assertion is. The simplest parallel for them are unit tests which executes in run time.

if (!c_assert(altitude > MAX_POSSIBLE_ALTITUDE) == true) { 
	return ERROR; 
}

The rule literally says:

“Statistics for industrial coding efforts indicate that unit tests often find at least one defect per 10 to 100 lines of code written. The odds of intercepting defects increase with assertion density.”

Great, does this mean that we can treat rule as: “Write unit tests!“? Not exactly. Speciality of assertions is that they execute in run time and closest practice for JavaScript land is a combination of unit tests and run time checks for program state conformity with generating errors and errors handling.

  • Than higher is tests density than less defects you get. Minimal amount of tests is 2 per function;
  • Watch for anomalies in system state in run time. Generate and handle errors in case of failures.

6.Data objects must be declared at the smallest possible level of scope.

This rule have simple intention behind – to keep data in private scope and avoid unauthorized access. Sounds generic, smart and easy to follow.

7.The return value of non-void functions must be checked by each calling function, and the validity of parameters must be checked inside each function.

Authors of guideline assure that this one is the most violated. And this is easy to believe because in it`s strictest form it means that even built-in functions should be verified. On my opinion it makes sense to verify resuts of third party libraries being returned to app code and function incoming parameters should be verified for existance and type accordance.

8.The use of the preprocessor must be limited to the inclusion of header files and simple macro definitions. The C preprocessor is a powerful obfuscation tool that can destroy code clarity and befuddle many text based checkers.

Using preprocessors should be limited in any language. They are not needed since we have standardized, clean and reliable syntax for putting commands into engine, it makes even less sense taking into account that JavaScript is constantly evolving. Reliable and fast JavaScript should be written in JavaScript. Nice research on determining the cost of JS transpilation

9.The use of pointers should be restricted. Specifically, no more than one level of dereferencing is allowed. Function pointers are not permitted.

This is the rule JavaScript developer can not get anything from.

10.All code must be compiled, from the first day of development, with allcompiler warnings enabled at the compiler’s most pedantic setting. All code must compile with these setting without any warnings.

We all know it… Do not hoard warnings, do not postpone fixes, keep code clean and perfectionist inside you alive.

“These ten rules are being used experimentally at JPL in the writing of mission critical software, with encouraging results. After overcoming a healthy initial reluctance to live within such strict confines, developers often find that compliance with the rules does tend to benefit code clarity, analyzability, and code safety. The rules lessen the burden on the developer and tester to establish key properties of the code (e.g., termination or boundedness, safe use of memory and stack, etc.) by other means. If the rules seem Draconian at first, bear in mind that they are meant to make it possible to check code where very literally your life may depend on its correctness: code that is used to control the airplane that you fly on, the nuclear power plant a few miles from where you live, or the spacecraft that carries astronauts into orbit. The rules act like the seat-belt in your car: initially they are perhaps a little uncomfortable, but after a while their use becomes second-nature and not using them becomes unimaginable.”(с) Gerard J. Holzmann, author of the “The Power of Ten – Rules for Developing Safety Critical Code”

Conclusion

When started to write this article I could not imagine how much web world could get from NASA and how much is similar. Comparsion was percepted for me as a ridiculus appliance of mature engineer rules to a self – studied scholar DIY, however it is not like that actually. The largest discovery is maturity of web – platform: existing level of automation, testability and projects infrastructure, amount of established best practices taken from maturer languages and amount of knowledge we already have on web development are astonishing. But still… It is a long way to go for us and a lot to challenge.

You may also be interested in First aircraft instrument built with HTML and first flight using it

  • rayC

    Restriction on recursion from NASA’s point of view is because of limited stack space. Every time you recurse, you put state information on the stack. Do you know how many times you can recurse before you run out of stack?

  • Anonymous

    Good point which was not completely covered in the rules. Actually I never saw a good publication on how call stacks impact JS performance or reliability. Should figure it out.

    As for maximum call stack in JS – there is good note on it http://www.2ality.com/2014/04/call-stack-size.html

  • Victor Homyakov

    Visualization of call stack depth for different stack frame sizes: http://jsfiddle.net/9YMDF/9/ and summary for different browsers: http://thinkjs.blogspot.com/2014/08/javascript-2.html

  • Fran

    Actually, this isn’t so hard to do… Especially if you know well design patterns…. Nice recommendations 🙂

  • Nat

    Rule 9 actually could be applied to JavaScript. I would ban nested data structures and passing functions to other functions. You could write JS this way, and it would make the code easier to reason about in some ways, but it would probably be a bad idea.

  • Anonymous

    Yes, there is so much buzz around functional programming nowadays that I would avoid making such a suggestion. Actually functions are a cornerstone of JavaScript development so this part of #9 is not applicable. But as for another part I`ve got a nice hint on http://en.wikipedia.org/wiki/Law_of_Demeter so still something to consider.

  • ryan

    You would? Why would you do that?

  • Ryan Scheel

    Nat doesn’t actually mean that their code is written that way. Rather, to truly follow rule #9, one would have to write that way. There’s an implicit “follow rule #9 by” following the word “would”, and the next sentence states that it’s a bad idea (probably). Which, given the constraints these rules impose, usage of JS at all would be a bad idea.

  • http://temporal.pr0.pl/devblog Jacek Złydach

    A lot of those rulese have to do with not just correctness, but predictable execution time. At NASA they’re writing a lot of real-time applications which have strict constraints on how long a given piece of code can run.

    In particular, function pointers will tend to make reasoning about execution time difficult, because you can’t be sure what code gets executed when you call a function indirectly.

    Also note that the rule about no function pointers pretty much kills any kind of polymorphism, if you were trying to do OOP in C.

  • Dilbert

    I disagree with your function pointer remark regarding fuzzy execution times. Replacing it with a large switch-case instead would hardly make the branch predictor any happier.

  • http://temporal.pr0.pl/devblog Jacek Złydach

    I didn’t mean this as a branch prediction issue but as a problem for programmer / static analysis tool. When you call a function through a function pointer, you basically have no idea what code will actually get executed. You’d have to manually inspect every place that sets the value of the function pointer, and even that might not help if some of those places get executed indirectly through another function pointer.

    And God forbid, if the function pointer is set to a value determined at runtime from external input…

  • http://www.dullroar.com/ Jim Lehmer

    Actually, in JS you could look at #9 as “Do not de-reference more than one ‘dot’ in a statement.” In other words, assuming you know foo is defined, then trying to dereference foo.bar is OK, but it is not OK to blindly do foo.bar.baz.xyzzy.plugh, for the possibility of hitting something undefined along the way.

  • Filipe Silva

    Was thinking the exact same thing!

  • Anton B.

    This is one of those things…
    The x = (y != null && y.z != null & y.z.w != null) ? y.z.w.value : null;

    In the latest version of C#, Microsoft has added a really nice short hand for this:

    x = y?.z?.w?.value ?? null;

  • http://www.dullroar.com/ Jim Lehmer

    Yeah, I am looking forward to the new null shortcuts in C#.

  • Anonymous

    this syntax is available in CoffeScript
    in JS one might use helper function to define namespaces, eg


    // something like this
    var w = defineNS( 'y.z.w', { ...object def } );
    // or this
    y.x.NEW = ensure( 'y.x' );
    // latter might throw proper exception if y.x is undefined

    but both approaches (cf & js strings) seem to be problematic, imho

  • Jon Green

    Rule 2: I agree with RayC about recursion: a point I was going to make myself. I would put a qualifier on that restriction: if you must use recursion, do so only when the recursion limit is strictly bounded, and minimal. But any recursion may be expressed as iteration, so for preference always iterate instead. Whilst recursion’s great for computational theory, it causes problems in the real world because stacks are limited. A classic example of: “In theory, theory and practice are the same thing. In practice, they differ.”

    Rule 3: not using dynamic memory at all (or, at most, only pre-allocating during initialisation) is a classic embedded development technique. Your JavaScript’s reliability will be considerably improved if you can follow this principle, as it will mean garbage collection is unnecessary or minimal, fragmentation does not occur (or to a far smaller extent), and memory use is strictly circumscribed. When you use static allocation all the time, you have an understanding of your memory use that you can never achieve using heap. If you’re using server-side JS, static allocation will definitely improve your long-term robustness.

    Rule 5: this can be rewritten as: “Test your assumptions”. Writing assertions is more than just creating a unit test: it’s a documentation of what you have assumed about entry conditions; of what you were thinking when the function was written. The act of writing them down helps clarify them in your mind, and explain them to developers who are approaching the code “cold”. So, whilst they provide a degree of code robustness testing, they also provide code clarity.

    Rule 7: in C or C++, it’s common (and good) practice to cast to (void) the result of a function you don’t need to test. This documents that you’re deliberately disregarding the result, and haven’t simply forgotten to check it. I forget if it’s possible to do the same in JS, but as an alternative I suppose you could write “function IGNORED(x) { }” and use that to wrap function invocations where you choose to discard a known return value.

    Rule 8: I slightly disagree with this rule. There is a principle in Smalltalk: “Code it once.” By putting a segment of frequently-used code in a macro, you can change all instances at a time if there’ a bug. However, in many cases it’s tidier to write it as a function, and (if relevant) use the “inline” qualifier. And it’s certainly true that preprocessor macros are non-trivial to debug, so they should be used carefully and sparingly.However, the relevant of this rule to JS is limited, so I’ll leave it there.

    Rule 9: this is relevant to JS. Function pointers can be created and assigned: “var foo = function () { return “A”); }”. However, whether there’s value (for JS) in limiting the use of (as in this case) lambda functions is debatable. I’d suggest that this rule – at least as regards function pointers – is probably more relevant to C/C++ than to JavaScript.

  • Dilbert

    Regarding #8, you can usually simply write a static inline function directly in the .h file and have it behave like a macro with added type safety.

  • Jon Green

    Um…that’s what I said. (And the only point of putting it in a header is if it’s shared, otherwise you’re just muddying your external interfaces.)

  • Dilbert

    NASA guidelines for embedded code don’t really apply too well to javascript.

    2. “Simple constructs” cannot possibly apply to javascript. It’s a prototypal, dynamic language. You don’t code microcontrollers in javascript, after all. Passing a function with a closure is so common in JS, and would probably make a plain C embedded programmer crazy.

    3. “Static allocation”? No way to eliminate completely. Object pools in JS are used for performance reasons – to avoid GC. The NASA article talks about plain C style memory allocation, where you want to avoid fragmenting your memory in limited memory long-running applications.

    6. “Scope declaration”? Did you know that there is no scope level variable declaration in javascript? Even jslint will warn against such declarations because they are misleading.

  • John Goodwin

    Just some notes on item #5.

    Styling wise, I disagree with checking booleans as === true. Additionally, the ! operator causes an implicit type conversion to boolean so the === to prevent accidental casting comparison anomalies will not be prevented.

    When possible, I also prefer people invert logic so that as few ! operators are required when it is reasonable to do. It is also my opinion that assert statements should throw when assertions fail, not return true/false.

    Revised, your assert should read like this:

    c_assert(altitude <= MAX_POSSIBLE_ALTITUDE);

    I think this is much easier to read and will result in fewer unintended side effects.

    John

  • David

    Great, do this mean that we can treat rule as: “Write unit tests!“?

    Correction: DOES

  • Anonymous

    Thanks, fixed