DEPENDABLE PROGRAMMING:
Good programming practices can be adopted that help reduce the incidence of program faults. These programming practices support fault avoidance, detection, and tolerance.
Limit the visibility of information in a program:
Program components should only be allowed access to data that they need for their implementation. This means that accidental corruption of parts of the program state by these components is impossible. You can control visibility by using abstract data types where the data representation is private and you only allow access to the data through predefined operations such as get () and put ().
Check all inputs for validity:
All program take inputs from their environment and make assumptions about these inputs. However, program specifications rarely define what to do if an input is not consistent with these assumptions. Consequently, many programs behave unpredictably when presented with unusual inputs and, sometimes, these are threats to the security of the system. Consequently, you should always check inputs before processing against the assumptions made about these inputs.
Provide a handler for all exceptions:
A program exception is an error or some unexpected event such as a power failure. Exception handling constructs allow for such events to be handled without the need for continual status checking to detect exceptions. Using normal control constructs to detect exceptions needs many additional statements to be added to the program. This adds a significant overhead and is potentially error-prone.
Minimize the use of error-prone constructs:
Program faults are usually a consequence of human error because programmers lose track of the relationships between the different parts of the system This is exacerbated by error-prone constructs in programming languages that are inherently complex or that don't check for mistakes when they could do so. Therefore, when programming, you should try to avoid or at least minimize the use of these error-prone constructs.
Error-prone constructs:
• Unconditional branch (goto) statements
• Floating-point numbers (inherently imprecise, which may lead to invalid comparisons)
• Pointers
• Dynamic memory allocation
• Parallelism (can result in subtle timing errors because of unforeseen interaction between parallel processes)
• Recursion (can cause memory overflow as the program stack fills up)
• Interrupts (can cause a critical operation to be terminated and make a program difficult to understand)
• Inheritance (code is not localized, which may result in unexpected behavior when changes are made and problems of understanding the code)
• Aliasing (using more than 1 name to refer to the same state variable)
• Unbounded arrays (may result in buffer overflow)
• Default input processing (if the default action is to transfer control elsewhere in the program, incorrect or deliberately malicious input can then trigger a program failure)
Provide restart capabilities:
For systems that involve long transactions or user interactions, you should always provide a restart capability that allows the system to restart after failure without users having to redo everything that they have done.
Check array bounds:
In some programming languages, such as C, it is possible to address a memory location outside of the range allowed for in an array declaration. This leads to the well-known 'bounded buffer' vulnerability where attackers write executable code into memory by deliberately writing beyond the top element in an array. If your language does not include bound checking, you should therefore always check that an array access is within the bounds of the array.
Include timeouts when calling external components:
In a distributed system, failure of a remote computer can be 'silent' so that programs expecting a service from that computer may never receive that service or any indication that there has been a failure. To avoid this, you should always include timeouts on all calls to external components. After a defined time period has elapsed without a response, your system should then assume failure and take whatever actions are required to recover from this.
Name all constants that represent real-world values:
Always give constants that reflect real-world values (such as tax rates) names rather than using their numeric values and always refer to them by name You are less likely to make mistakes and type the wrong value when you are using a name rather than a value. This means that when these 'constants' change (for sure, they are not really constant), then you only have to make the change in one place in your program.