— Pixels Commander

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

[:en]Applying NASA coding standards to JavaScript[:ru]Применяем стандарты кодирования NASA к JavaScript[:]

[:en]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[:ru]Jet Propulsion Laboratory — научное учреждение выполняющее большой объем разработок и исследований для NASA. На счету JPL такие известные миссии, как марсоход Curiosity и недавно покинувший пределы солнечной системы зонд Voyager. JPL разрабатывала программное обеспечение для большинства беспилотных миссий по изучению дальнего космоса и других планет при этом высокая степень автоматизации зондов и длительность миссий обусловили бескомпромиссные требования к качеству программ. В результате сформировались рекомендации по написанию кода которые впитали фантастический опыт инженеров NASA и недавно были опубликованы. Так как требования к программам выполняемым на web — платформе постоянно растут и ей доверяются все более критические задачи давайте применим стандарты кодирования NASA для JavaScript / HTML приложений ради увеличения производительности, надежности и во имя лучшего мира.

 

Nasa coding JavaScript

1.Функции должны быть достаточно короткими что бы при печати уместиться на стандартном листе. Обычно это значит не более 60 строк на функцию.

Эта рекомендация отлично подходит и для JavaScript. Декомпозированный код гораздо проще воспринимать, тестировать и поддерживать.

2.Ограничьтесь простыми управляющими конструкциями. Не используйте goto setjmp, longjmp, а так же прямую или косвенную рекурсию.

Cовет из мира C заставляет задуматься. Мы определенно не будем использовать goto или setjmp в JS, но как быть с рекусией? Почему NASA выступает против простейшей техники преподаваемой еще в школе? Причина кроется в статических анализаторах применяемых NASA для уменьшения вероятности ошибок. Для этих анализаторов рекурсии делают код менее предсказуемым. В JS другая ситуация и существующие анализаторы не предписывают уклоняться от использования рекурсий.Что мы можем вынести из этого совета?

  • Используйте оправданно простые конструкции. Если хотите писать очень надежный код — бросьте писать очень хитрый код и начните писать предсказуемый. Установите стандарт кодирования и соблюдайте его;
  • Используйте анализаторы позволяющие уменьшить вероятность ошибок: JSHint/JSLint/Google Closure Tools;
  • Поддерживайте репозиторий в здоровом состоянии с помощью метрик: Plato;
  • Анализируйте типы с помощью Flow/Google Closure Tools.

3.Не используйте динамическое выделение памяти после инициализации модуля.

На первый взгляд JavaScript движок сам управляет памятью, а проходящий время от времени garbage collection решает все наши проблемы. Это не так. Память в JS имеет свойство течь, garbage collection не управляем и уменьшает производительность системы во время работы, а избалованные веб — программисты не имеют культуры управления памятью. В результате из этого правила можно выделить три рекомендации. Первые две будут вполне разумной практикой в любом проекте, третий только в проектах требующих предсказуемого расхода памяти и максимальной производительности.

  • С уважением относитесь к переменным, упорядочивайте их объявление. Если позволяет стандарт кодирования — объявляйте переменные в начале изолированного блока, это улучшит видимость использования.
  • Следите за утечками памяти, очищайте ссылки на переменные, убирайте слушатели, избегайте создания замыканий где это возможно. Классическая статья.
  • Можно заставить JS работать в режиме статического выделения памяти и сделать garbage collection управляемым (а значит избежать падения производительности и sawtooth pattern) с помощью пула объектов.

4.Все циклы должны иметь четко заданный верхний предел.

По объяснению JPL это делает статический анализ еще более эффективным и позволяет избежать бесконечного выполнения цикла. В случае если лимит на повторение превышен — функция возвращает ошибку и таким образом выводит систему из неожиданного состояния. Что же, вполне логично для программы с up-time 20+ лет! Проверка на превышение лимита осуществляется с помощью утверждений (assertions) подробнее о которых в пункте пятом. Если вы полностью примете парадигму assertions — ставьте уже и лимиты на циклы, вам это понравится.

5.Устанавливайте не меньше двух проверок (assertions) на функцию.

Тут нужно немного сказать о проверках. Проще всего представить их, как юнит — тесты, выполняемые по ходу исполнения программы.

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

Этот раздел стандарта буквально гласит следующее:

«Статистически на каждые 10 — 100 строк кода приходится один дефект найденный с помощью юнит тестов. Чем больше плотность тестов — тем больше дефектов мы можем обнаружить автоматически.»

Отлично, значит мы можем расценивать это правило, как «Пишите юнит тесты!«? И да и нет.
Особенностью проверок и их приемуществом является постоянный контроль за состоянием системы по ходу выполнения. Самым близким аналогом этой практики для JavaScript будет совмещение юнит тестов и run-time контроля данных с генерацией ошибок при отклонениях.

  • Чем выше плотность тестов, тем меньше дефектов. Минимальное количество тестов — 2 на функцию;
  • Следите за состоянием системы в run-time, генерируйте ошибки в случае неполадок и обрабатывайте их.

6.Переменные должны располагаться в самом нижнем скоупе из возможных.

Правило рождено желанием поместить данные в приватное пространство и оградить их от записи извне. Разумная и простая практика.

7.Возвращаемое значение любой не — void функции должно проверяться вызывающей функцией. Все входящие аргументы функций должны проверяться.

По заверению авторов рекомендаций это правило нарушается чаще остальных, ведь в самой строгой интерпретации оно требует проверки результата даже встроенных функций.
Мое мнение — стоит проверять возвращаемые значения third — party библиотек, а так же входящие аргументы функций на существование и соответствие ожидаемому типу.

8.Использование препроцессора C должно быть ограничено включением заголовков и простыми определениями. Препроцессор — мощный инструмент, однако приводящий к уменьшению читабельности и дополнительным затратам на тестирование.

Вынужден согласиться — использование препроцессоров должно быть ограниченно в любых языках. Они не нужны пока есть стандартный, понятный и надежный синтаксис для ввода команд в машину, который к тому же активно развивается. Надежный и быстрый JavaScript стоит писать на JavaScript… Интересный материал о стоимости транспайлинга JS

9.Использование указателей должно быть ограниченно. Указатели на функции запрещены.

Это то правило из которого JavaScript разработчик не сможет вынести пользы.

10.С самого первого дня разработки код должен успешно проходить тесты и статический анализ с самыми строгими, педантичными параметрами проверки. При этом не должен вызывать преудпреждающих сообщений и тем более сообщений об ошибках.

Мы все это знаем… Не копите синтаксические замечания, не откладывайте фиксы. Держите код чистым, а перфекциониста в себе живым!

«Эти десять правил были экспериментально использованы в JPL при написании критического ПО с воодушевляющими результатами. После преодоления здорового сопротивления столь строгим правилам разработчики находят их полезными, приводящими к лучшей надежности, читабельности и тестируемости кода. Если сначала правила выглядят драконовскими, то вы должны понимать — они позволяют писать лучший код там, где от него могут зависеть жизни людей: ПО для контроля самолетов, ядерных электростанций находящихся в нескольких километрах от вашего дома или космических кораблей доставляющих космонавтов на орбиту. Правила работают, как ремень безопасности в вашей машине — сначала они мешают, но со временем становятся вторым Я которое невообразимо игнорировать.»(с) Gerard J. Holzmann, автор «The Power of Ten — Rules for Developing Safety Critical Code»

Заключение

Начиная писать эту статью, я не представлял как много мы можем перенять у NASA. Сравнение виделось нелепым сродни сравнению опытного инженера и школьника — самоучки, но это не так. Открытием стала зрелость web — платформы: существующий уровень проектной инфраструктуры, уже устоявшееся использование лучших практик из «старших» языков и изученность возможностей поражают. Хотя так много еще можно достичь…[:]

0 comments
Submit comment