Module-owned mutable state and representations stay encapsulated

arch-005

Intent

Do not expose module-owned mutable state directly or turn internal state layout into a public contract.

Applicability

Applies when the diff changes exported/shared APIs, getters, mutable globals, or storage of shared state.

What to inspect

Returned collections, mutable globals, singleton state containers, direct field exposure, and APIs widened only to let tests or callers manipulate internals.

Pass criteria

Shared APIs expose focused accessors, immutable snapshots, or read-only views, and mutable state remains owned by one module.

Fail criteria

The diff returns internal mutable state directly, adds mutable ambient global state, or widens a production API so callers can manipulate internals.

Do not flag

Immutable value objects and intentionally public compatibility APIs that are already part of the supported contract.

Confidence guidance

HIGH when the API visibly returns internal mutable storage or adds a mutable global. MEDIUM when mutability is indirect. LOW when ownership is unclear.

Remediation

Hide the representation behind focused accessors and keep state privately owned.

Pass example

String getParameter(String name) { return params.get(name); }

Fail example

Map<String, String> getParams() { return params; }

Sources

  • A Philosophy of Software Design — John Ousterhout book
  • C++ Core Guidelines standard
  • Effective Kotlin book
  • Refactoring — Martin Fowler book
  • Unit Testing: Principles, Practices, and Patterns — Vladimir Khorikov book