Avoid complex flow constructs, such as goto and recursion. These can make code harder to analyze and verify, especially in automated verification tools.
All loops must have fixed upper bounds. It must be trivially possible for a checking tool to prove statically that a preset upper bound on the number of iterations cannot be exceeded.
Avoid heap memory allocation after task initialization. Dynamic memory allocation can lead to unpredictable behavior and memory fragmentation in long-running systems.
Restrict functions to no more than 60 lines of text. Each function should be a logical unit that can be easily understood and verified.
Use a minimum of two runtime assertions per function. Assertions must always be side-effect free and should be used to check for anomalous conditions.
Restrict the scope of data to the smallest possible. Global data should be avoided, and when used, access should be carefully controlled and documented.
Check the return value of all non-void functions, or cast to void to indicate the return value is useless. Never ignore error conditions.
Use the preprocessor sparingly. Avoid complex macros and conditional compilation that can make code analysis difficult.
Limit pointer use to a single dereference, and do not use function pointers. This reduces complexity and makes static analysis more effective.
Compile with all possible warnings active; all warnings should then be addressed before release of the software.