DEV Community

Cover image for One Principle, One Proof: Why IVP-Compliant Modules Minimize Change Impact
Yannick Loth
Yannick Loth

Posted on

One Principle, One Proof: Why IVP-Compliant Modules Minimize Change Impact

You refactor authentication.
You touch UserController, LoginService, SessionManager, and APIGateway.

Four files for one concern.
You already know this is wrong — but can you prove it?

This article takes a single, universally understood quality metric — change impact — and proves that applying the Independent Variation Principle (IVP) reduces it to the theoretical minimum.
No hand-waving.
No "it depends."
A clean, short proof.


The metric: Change Impact

Every developer intuitively tracks change impact: how many modules do I have to touch when a requirement changes?

Let's make it precise.

A change driver is a single axis of anticipated change — a requirement, a business rule, a technology decision — that, when it changes, forces modifications to the code.
We write γ for a driver.

Change impact counts the modules affected when driver γ activates:

impact(γ, M) = |{ M ∈ M : γ ∈ Γ(M) }|
Enter fullscreen mode Exit fullscreen mode

where Γ(M) is the set of drivers whose elements live in module M.

This is the simplest quality metric you can write down, and arguably the one developers feel most viscerally.
When impact = 1, a change to one concern touches one module.
When impact = 4, you're hunting through four files, coordinating four PRs, risking four merge conflicts.


The principle: IVP in two directives

The Independent Variation Principle has several directives, but only two matter for this proof:

IVP-3 (Separation): Every module contains elements with the same driver assignment.
No module mixes concerns from unrelated drivers.

IVP-4 (Unification): All elements sharing the same driver assignment live in one module.
No driver is scattered across multiple modules.

Under the assumption that every element has exactly one driver (the "pure elements" condition — realistic for well-factored code), these two directives fully determine the modular structure: one module per driver, each module containing exactly the elements governed by that driver.


The proof

Claim: For an IVP-compliant modularization with pure elements, impact(γ, M_IVP) = 1 for every driver γ.
Moreover, IVP-4 is necessary and sufficient for achieving this.

Proof (⇒ IVP achieves impact 1):

By IVP-4, all elements with driver assignment {γ} reside in a single module M_γ.
So the set of modules containing γ is just {M_γ}, and:

impact(γ, M_IVP) = |{M_γ}| = 1
Enter fullscreen mode Exit fullscreen mode

That's it for the forward direction.
One directive, one line of algebra.

Proof (⇐ IVP-4 is necessary):

Suppose IVP-4 is violated: the elements of some driver γ are split across modules M_a and M_b (at least).
Then both M_a and M_b contain elements governed by γ, so:

impact(γ, M') ≥ 2
Enter fullscreen mode Exit fullscreen mode

Any violation of IVP-4 strictly increases change impact for the scattered driver. ∎


What about IVP-3?

IVP-3 (separation) prevents mixing drivers in a single module, but it doesn't affect per-driver impact.

Consider a module that consolidates authentication and payment logic.
If all authentication elements are still in that one module, then impact(γ_auth) = 1 — it's just that the same module also changes when payment logic changes.

IVP-3 violations don't increase change impact per driver.
They increase something else: how often a module is disturbed.
A module hosting 5 drivers changes whenever any of them activates — but that cost shows up in other metrics (cognitive load, test surface), not in per-driver impact.

This is why the proof is clean: change impact depends only on IVP-4.


The concrete example

Step 1: Identify the change driver

Let γ_auth be the authentication change driver.
It activates when any authentication-related requirement changes: password policy, MFA rules, token expiration strategy, or session validation logic.

The elements governed by γ_auth are:

  • validatePassword — enforces password policy
  • checkMFA — applies multi-factor rules
  • expireToken — controls session lifetime based on auth parameters
  • validateToken — verifies tokens against the auth scheme

All four elements exist because of authentication requirements, and all four must change together when those requirements change.
That's what makes them a single driver: they share the same reason to change.

Step 2: Non-IVP design — scattering γ_auth

A typical layered architecture distributes these elements by technical role rather than by driver:

// UserController.java — handles HTTP input
class UserController {
    void validatePassword(String pwd) {
        if (pwd.length() < 8) throw new ValidationException();
    }
}

// LoginService.java — orchestrates login flow
class LoginService {
    boolean checkMFA(User user) {
        return mfaRequired(user) && passwordStrong(user);
        //                          ^^^^^^^^^^^^^^^^
        // Duplicates password-strength knowledge from UserController.
        // When policy changes, this must change too.
    }
}

// SessionManager.java — manages sessions
class SessionManager {
    void expireToken(String token) {
        long ttl = computeTTL(getPasswordHash(token));
        //         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        // TTL derivation depends on the password hashing scheme.
        // When the auth scheme changes, this formula changes.
        store.expire(token, ttl);
    }
}

// APIGateway.java — validates incoming requests
class APIGateway {
    boolean validateToken(Request req) {
        return sessionManager.isValid(req.getToken());
        //     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        // Calls into SessionManager, which encodes auth assumptions.
        // When token format or validation rules change, this
        // call chain must be re-verified.
    }
}
Enter fullscreen mode Exit fullscreen mode

Driver analysis: γ_auth's elements are scattered across 4 modules.
Each module contains some authentication logic alongside other concerns.

Change scenario: Increase minimum password length from 8 to 12, and switch from bcrypt to argon2.

What changes:

  • UserController — the length check (< 8< 12)
  • LoginServicepasswordStrong() encodes strength criteria that just changed
  • SessionManagercomputeTTL(getPasswordHash(...)) depends on the hashing scheme, which just changed from bcrypt to argon2
  • APIGateway — token validation indirectly depends on the new session format; integration tests break, call chain needs re-verification

impact(γ_auth, M') = 4. Four modules, four PRs, four risk surfaces.

Why this is non-IVP: IVP-4 requires all elements with the same driver assignment to live in one module.
Here, the four γ_auth elements live in four different modules.
IVP-4 is violated.

Step 3: IVP-compliant design — unifying γ_auth

Group all elements governed by γ_auth into a single module:

// AuthenticationService.java — ALL γ_auth elements here
class AuthenticationService implements IAuthProvider {
    void validatePassword(String pwd) {
        if (pwd.length() < 8) throw new ValidationException();
    }
    boolean checkMFA(User user)      { /* MFA rules here     */ }
    void expireToken(String token)   { /* expiry logic here  */ }
    boolean validateToken(Request r) { /* validation here    */ }
}

// IAuthProvider.java — stable interface (not governed by γ_auth)
interface IAuthProvider {
    boolean authenticate(Credentials c);
    boolean validateSession(String token);
}
// UserController, LoginService, SessionManager, APIGateway
// all depend on IAuthProvider — never on auth internals.
Enter fullscreen mode Exit fullscreen mode

Driver analysis: Every element governed by γ_auth lives in AuthenticationService.
No other module contains authentication logic — they call through IAuthProvider, whose signature is stable across auth changes.

Same change scenario: Increase password length, switch hashing scheme.

What changes: AuthenticationService. Nothing else.
The interface IAuthProvider doesn't change (it exposes authenticate and validateSession, not password-length constants or hashing algorithms).
Callers are untouched.

impact(γ_auth, M_IVP) = 1.

Why this is IVP-compliant:

  • IVP-4 (unification): All γ_auth elements are in one module. ✓
  • IVP-3 (separation): AuthenticationService contains only γ_auth elements — no payment logic, no user profile logic. ✓

Why this matters more than you think

Change impact = 1 is not just "nice to have."
It is the theoretical minimum — you cannot do better than touching one module for a single-driver change (someone has to implement the change).

And the proof tells you something stronger: IVP-4 is not just sufficient for achieving this minimum, it is necessary.
There is no alternative modularization strategy that achieves universal impact = 1 without satisfying IVP-4.

This means every time you scatter a concern across modules — whether through "convenience," layered architecture conventions, or framework-imposed structure — you are provably increasing change impact.
Not probably. Not usually. Provably.


The meta-theorem (a teaser)

Change impact is just one metric.
In the book I'm currently writing, I prove a Quality Meta-Theorem showing that IVP simultaneously minimizes an entire family of quality metrics — change impact, cognitive load, test surface, defect localization scope — through a single structural argument.

The key insight: all these metrics can be expressed as functions of two module-level quantities:

  1. Driver count per module (|Γ(M)|) — minimized to 1 by IVP-3 and IVP-4 together (separation prevents mixing; unification ensures one module per driver)
  2. External dependency count (|D_ext(M)|) — minimized to the causal lower bound by IVP-4 (unification internalizes all same-driver dependencies)

Any metric that (a) decomposes as a sum over modules, (b) depends only on these two quantities per module, (c) is monotone in both, and (d) satisfies a superadditivity condition, is provably minimized by IVP.
The theorem calls such metrics driver-decomposable.
Change impact, cognitive load, test surface, and defect localization scope all qualify.

Change impact is the simplest member of this family.
It won't be the last one you care about.


References

  • Loth, Y. (2025). The Independent Variation Principle (IVP). Zenodo. https://zenodo.org/records/18024111
  • Loth, Y. (2026). Book to be published (Volume 1). Chapter 9: Quality Metrics Under IVP.

Top comments (0)