Skip to content

Maintainability

Quality Attribute · Chapter 4

Maintainability is how easily a system can be changed after it goes into operation. Change is inevitable, so maintainability preserves the software’s value over time.

  • The cost ratio has shifted over the decades from development to maintenance — today most of a system’s lifetime cost is maintenance.
  • Easy-to-maintain code lets changes be completed faster, lowering cost.
  • Write for the future maintainer, not only the end user — even the original author forgets details or leaves the organization.
  • High modifiability also makes a system easier to understand and reverse-engineer (e.g. to migrate to newer technology).
TypePurpose
CorrectiveAnalyze and fix defects. Track by severity (impact: critical/high/medium/low) and priority (fix order; e.g. P0–P4, where P0 = blocker that holds a release). Maintainability is measured by time to analyze and fix.
PerfectiveImplement new or updated requirements (enhancements). Higher maintainability → lower effort/cost.
AdaptiveAdapt to a changed environment (new OS, new OS version, new DBMS). Measured by time to adapt.
PreventiveImprove quality to avoid future problems, e.g. refactoring a component to reduce complexity.
  • Modifiability — ease of change without introducing defects or lowering quality. Measured by the time from when a change is specified to when it is deployed. Especially important under agile, where each iteration changes existing code.
  • Extensibility — ease of adding new functionality (anticipating future growth).
  • Flexibility — ease of changing a capability to be used in a way not originally designed.
  • Scope of modifications — larger/more complex changes cost more; architecture-level changes raise effort and scale the most.

Designing for maintainability — reduce complexity

Section titled “Designing for maintainability — reduce complexity”

Complexity is a predictive measure of maintainability: more complex modules are harder to understand, test, and maintain, and are more likely to contain defects (which are then also harder to detect). Investing in lower complexity costs more up front but pays back over the system’s life. Three core techniques:

  • Reduce size — split large modules into smaller ones.
  • Increase cohesion — keep a module’s elements interrelated; avoid disparate responsibilities. High cohesion tends to correlate with loose coupling.
  • Reduce coupling — keep modules independent so a change to one is less likely to force changes in others.

These same principles appear throughout the book — see Minimizing complexity, Designing orthogonal software systems, and Following SOLID design principles.

  • Lines of code (LOC / SLOC) — proxy for size and complexity. Only meaningful across an order-of-magnitude gap (10k vs 100k tells you something; 48k vs 50k does not). Physical LOC counts source lines (excluding comments) and is easier but sensitive to formatting; logical LOC counts actual statements.

  • Cyclomatic complexity (McCabe) — number of linearly independent paths through a module. One common formula:

    CC = E − N + 2P

    where E = edges, N = nodes, P = connected components of the control-flow graph. Values greater than 10 typically flag complex, error-prone, hard-to-test modules that should be refactored.

  • Depth of inheritance tree (DIT) — OOP metric: max distance from a class to the root of its hierarchy. Higher DIT = more inherited attributes/methods (more reuse) but harder-to-predict behavior; lower DIT = less complexity but less reuse. Balance reuse against complexity; a DIT greater than 5 is worth analyzing and possibly refactoring.

  • Testability — the same complexity-reduction techniques (size, cohesion, coupling) also raise testability.
  • Software Architect’s Handbook (Packt, 2018), Ch.4 “Maintainability”, pp. 182-201.