3 key principles of good code include being reusable, maintainable and easy to change. There are numerous ways to uphold and ensure these principles.
Steps to Writing Error-free Code
- Understand the difference between syntax errors and logic errors. Syntax involves the ‘spelling and grammar’ of programming, these are generally picked up by compilers. Check your variable names, brackets and punctuation. Autocomplete in modern IDEs helps reduce syntax errors.
- Syntax errors that are more like semantic errors are harder to spot and will not be picked up by a compiler, for example when you type the wrong variable name by mistake, you could then end up with the wrong answer. It is helpful to use meaningful names for your variables and functions.
- Logic errors often lead to unexpected results, and are seldom caught by a compiler. They can come about when statements are executed in the wrong order, incorrect boolean expression and decision points, wrong data types, etc. To eliminate logic errors, it helps to write out pseudocode before you start coding to help get the sequence right. Check that against the specification, are you doing what you’re supposed to be doing?
- Document code clearly, it helps keep code easy to read and understand, especially when it comes to debugging. It is easier to spot bugs in well organised code. When you come back to your code, it will be easier to know how it works. Using a coding convention helps get the balance of white space right. This helps with code readibility.
- Initialise variables, consider the boundary cases of data types chosen. Avoid mutable variables and consider the use of null values carefully. A method on call on null will result in a runtime exception. Consider array indices and use flexible data structures if required.
- Write modular code that can be tested in isolation so that the source of errors can be identified more easily. This is improved with abstraction and encapsulation which together enables unit testing.
- Error avoidance by checking preconditions/requirements are met on the client side, only execute the code if the precondition is true, isResourceAvailable(), if returns true then proceed. But this isn’t always reliable, the client can’t always be trusted.
Exceptions – defensive programming
Defensive programming assumes that the client can’t be trusted and checks argument values. Exceptions, used correctly help software be robust and recover from errors at run-time. Run-time errors are often related to the environment or resources, URL changed, network down, file deleted, printer switched off… These cannot be anticipated, but can be recovered from by handling the exceptions appropriately. It should not fail silently, outputting a message or code may not be seen or understood. Thowing an exception means that classes can be more cohesive and leave other classes to fix problems that are in their domain, e.g. leave I/O to classes concerned with I/O. Otherwise you bind functionality to technology, which may soon be out of date.
An Exception is a software signal which can be thrown or raised and also caught and handled, either in the same method or higher up. There are loosely three categories, Checked Exceptions (have to be caught or specify method may throw them, Unchecked Exceptions (often a bug in the program, not obliged to catch them) and Runtime Exceptions. Checked in this context means that the compiler checks to see that you catch or throw these exceptions, e.g. FileNotFoundException.