Restrict Mutability of State

From WikiContent

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 functional programming, 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. Immutability is particularly suitable for value objects in languages that favor predominantly reference-based semantics. For example, Java's String class represents objects that are essentially immutable — if you want another string value, you use another string object.

A related approach to immutability is through qualification, such as the const feature found in C++ and D. Object usage can be constrained with respect to the mutability declared for accessing the object. The set of operations on a qualified object is restricted to those that are similarly qualified. In C++ and D const qualification typically expresses the notion of read-only, which is clearly a restriction on mutability, but D also offers immutable for declaring 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 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 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.