In large systems there are often code paths that modify state or produce side effects that other code comes to depend on. This makes it hard to safely change code without understanding the whole system, because changing the order of function calls or operations could change the behavior. Is there a software quality metric that measures or correlates with this kind of issue?
I’ll give an example to illustrate.
Example: function modifies its inputs
function getTotalTripDistance(trip: Trip): number {
// This modifies its inputs
trip.waypoints.sort((w1, w2) => w1.time - w2.time);
const legDistances = computeLegDistances(trip.waypoints);
return sum(legDistances);
}
function formatWaypointLabels(trip: Trip): string {
const waypointLabels = trip.waypoints.map(w => w.prettyLabel());
return `(${waypointLabels.join(',')})`;
}
function summarizeTrip(trip: Trip): string {
const totalDistance = computeTripDistance(trip);
const waypointsFormatted = formatWaypointLabels(trip);
return `Trip to waypoints ${waypointsFormatted} takes distance ${totalDistance}`;
}
In this example, changing the order in which computeTripDistance()
and formatWaypointLabels()
are called will change the behavior. In order to safely change one part of the code (summarizeTrip
), one must understand a larger part of the system than otherwise necessary.
Other ways this issue can manifest:
- A class changes its internal state within a method call. Other code depends on this happening.
- A function modifies global state that other code depends on.
- State is updated via I/O, such as by writing to a database. Other code depends on this happening.
- Same problem, larger scale: one system depends on another system changing its state in response to a service call/message.
Obviously a useful system has to change state, since this is usually part of the system’s explicit requirements. Users expect their actions to produce changes. But most systems have some amount of “accidental” state changes that aren’t necessary, and that make the system harder to understand and to change.
It seems like it would be difficult to define a metric that measures something interesting here, because common patterns like memoization involve changes to state that usually don’t matter in practice. Existing code quality metrics like those on CISQ’s Coding Rules to Deliver Resilient and Scalable Software don’t mention changes to state or side effects. Is there any useful metric out there that can help highlight code that would benefit from removing side effects and state changes?