Restrict Mutability of State
What appears at first to be a trivial observation turns out to be a subtly important one: a large number software defects arise from the (incorrect) modification of state. It follows from this that if there is less opportunity for code to change state, there will be fewer defects that arise from state change.
Perhaps the most obvious example of restricting mutability is its most complete realization: immutability. A moratorium on state change is an idea carried to its logical conclusion in pure functional programming languages such as Haskell, but even its modest application in the context of other programming models has a simplifying effect. If an object is immutable it can be shared freely across different parts of a program without concern for aliasing or synchronization problems. An object that does not change state is inherently thread-safe — there is no need to synchronize state change if there is no state change. An immutable object does not need locking or any other palliative workaround to achieve safety.
Depending on the language and the idiom, immutability can be expressed in the definition of a type or through the declaration of a variable. For example, Java's
String class represents objects that are essentially immutable — if you want another string value, you use another string object. Immutability is particularly suitable for value objects in languages that favor predominantly reference-based semantics. The
const qualifier found in C and C++ and the
immutable qualifier in D constrain mutability through declaration. In C++
const qualification restricts mutability in terms of access rights, typically expressing the notion of read-only access and not necessarily full immutability.
Perhaps a little counterintuitively, copying offers a complementary technique for restricting mutability. In languages offering a transparent syntax for passing by copy, such as C#'s
struct objects and C++, copying value objects can greatly improve encapsulation and reduce opportunities for unnecessary and unintended state change. Passing or returning a copy of a value object ensures that the caller and callee cannot interfere with one another's view a value. But note that to be effective for values, copying requires a transparent passing syntax: if the programmer has to make special efforts to remember to make the copy, such as explicitly call a clone method, they are also being given the opportunity to forget to do it. It becomes a complication that is easy to overlook rather than a simplification.
Another way to restrict the mutability of state is to reduce the scope in which it is accessed. Declare a variable as close to its point of use as possible. For local variables, declare as late as possible, only when a variable can be sensibly initialised. For variables with longer lifetimes, improving locality reduces the scope for change. Global data has the broadest scope possible. Keep in mind that public class-wide variables —
statics in many languages — are simply globals with scope etiquette.
Of course, this is not some kind of silver bullet with which to shoot down all defects, but restricting the mutability of state offers a guideline that can help to reduce the incidence of defects through improved encapsulation and code simplification.
This work is licensed under a Creative Commons Attribution 3
Back to 97 Things Every Programmer Should Know home page