open atlas
↑ Back to track
Code patterns & craft CP · 07 · 01

Connascence

Connascence is a precise taxonomy of coupling — two components are connascent when changing one forces changing the other. Its two rules (prefer weaker, prefer more local) let you compare two designs' coupling objectively instead of by taste.

CP Senior ◷ 20 min
Level
FoundationsJuniorMiddleSenior

You’re in a design review and someone says “this is too coupled.” Everyone nods. But coupled how? Two reviewers point at different lines, both call it “coupling,” and the argument dissolves into taste — yours against theirs, with no way to settle it. “Coupling” as a single word is too blunt to argue with.

Connascence is the fix: a vocabulary that names the kind of coupling and ranks each kind by how much it hurts. Two pieces of code are connascent when changing one forces you to change the other to keep the program correct. Once you can name the kind — “that’s connascence of position, this refactor turns it into connascence of name” — coupling stops being a matter of opinion and becomes something you can measure and compare.

Goal

After this lesson you can define connascence and state its two rules (weaker is better than stronger; local is better than distant); name the common static forms (name, type, meaning, position, algorithm) and dynamic forms (execution order, timing, value, identity); refactor a positional-argument call into an options object and explain why that is objectively weaker coupling; and avoid the trap of treating connascence as trivia instead of a concrete refactoring compass.

1

Connascence is coupling with a precise definition: two components are connascent if changing one requires changing the other to stay correct. That’s it — but the precision matters. It gives you a binary test for any two pieces of code: if I change A, must I also change B to keep the program correct? If yes, they are connascent, and now you can ask the only two questions that matter — how strong is the connascence, and how far apart do the two pieces live.

// These two lines are connascent: the caller and the function
// agree that the SECOND argument is the timeout. Change the order
// in createConn, and this call is silently wrong but still compiles.
function createConn(host: string, timeoutMs: number) { /* ... */ }
createConn("db.internal", 5000);

Nothing is “wrong” here yet. The point is that a hidden agreement exists, and the rest of the lesson is about naming such agreements and ranking them so you can shrink them deliberately.

2

Static connascence — visible by reading the code — comes in a strength order you can memorize. From weakest to strongest: name (two places must agree on a name, e.g. a function and its caller share taxFor), type (must agree on a type), meaning/convention (must agree that 0 means “guest” and 1 means “admin” — a magic value), position (must agree on the order of things, e.g. positional arguments), and algorithm (must agree on a shared computation, e.g. both sides must hash a password the same way).

// Connascence of meaning: every site must "know" that 1 = admin.
if (user.role === 1) showAdminPanel();   // 1 means... what?
// Connascence of name (weaker): the agreement is a shared name.
if (user.role === Role.Admin) showAdminPanel();

The named version is weaker connascence than the magic 1, which is why “replace magic number with named constant” is a real improvement and not just decoration — it moves you down the strength ladder.

3

Dynamic connascence — only visible at runtime — is strictly worse, because the agreement is invisible in the source. The forms: execution order (B must run after A, e.g. open() before read()), timing (a race — correctness depends on when, not just order), value (several values must change together to stay consistent, e.g. a min/max invariant or a discount that must match a total), and identity (two references must point to the same object instance, not merely equal ones).

// Connascence of execution order: callers must call these in sequence,
// and nothing in the types says so. Reorder them and it compiles, runs,
// and corrupts state.
txn.begin();
txn.write(row);
txn.commit();   // must be last — but who enforces that?

Static connascence you can find by searching the code; dynamic connascence you find by debugging production. That’s why an order-dependency or a shared-mutable-value invariant is a louder smell than a positional argument — same family, worse locality of pain.

4

The two rules turn the taxonomy into a compass: prefer weaker connascence, and prefer more local connascence. Strength tells you which kind costs more to maintain; locality tells you that the same kind is far cheaper when both ends sit close together. Connascence of position between two arguments of one private function is trivial — both ends are on one line you control. The same connascence of position across a public API, consumed by twelve teams, is a migration project. Locality is often the bigger lever: it is usually fine to have strong connascence inside a small, cohesive module (that’s what cohesion is), and a disaster to have even weak connascence spanning module or service boundaries.

So the senior move when you spot coupling is two questions in order: can I make it weaker (move down the strength ladder), and if not, can I make it more local (pull both ends into the same module). Either one is a real, defensible improvement — and you can now say exactly which one you did.

Worked example

From connascence of position to connascence of name. A function with several positional arguments forces every caller into connascence of position: callers and the function must agree on the order, and that agreement is invisible at the call site.

// Connascence of POSITION (strong) across every call site.
function createUser(
  name: string,
  email: string,
  isAdmin: boolean,
  sendWelcome: boolean,
): User { /* ... */ }

// At the call site, the booleans are unreadable and order-fragile:
createUser("Ada", "ada@x.io", false, true);
// Swap the last two args and it still compiles — and silently
// makes an admin who gets no welcome email. A reorder of the
// signature breaks every caller, invisibly.

Refactor to an options object, which converts the coupling to connascence of name:

interface CreateUserOptions {
  name: string;
  email: string;
  isAdmin?: boolean;
  sendWelcome?: boolean;
}

function createUser(opts: CreateUserOptions): User { /* ... */ }

// Now the agreement is by NAME, not order:
createUser({ name: "Ada", email: "ada@x.io", sendWelcome: true });

Two things improved, and you can name both. Strength: position → name, a move down the ladder, because order no longer carries meaning — reordering the fields is now a no-op, and a typo in a field name is a compile error instead of a silent swap. Locality is unchanged, but you also shrank the agreement: optional fields can be omitted, so callers only commit to the names they actually use. The call reads as documentation. This is the entire point of connascence as a compass: you didn’t refactor “because options objects are nicer” — you refactored because you moved from a stronger named form of coupling to a weaker one, and you can defend that to a reviewer in one sentence.

Why this works

Why introduce a whole taxonomy when “reduce coupling” already exists as advice? Because “reduce coupling” gives you no way to compare two designs that are both coupled — and almost every design is. Connascence does. When two proposals both have coupling (they always do), you can say “proposal A has connascence of position across a public API; proposal B has connascence of name inside one module — B is both weaker and more local, so B wins,” and that is an objective ranking, not a preference. It also stops the over-correction: decoupling everything is impossible and usually harmful (you’d inline nothing and share nothing). The goal is never zero connascence; it is the weakest, most local connascence that still expresses the real relationship the code needs.

Common mistake

The failure mode is treating connascence as academic trivia — memorizing the nine forms to win arguments, then never using them. The taxonomy is worthless as a quiz answer and valuable only as a refactoring compass: you point it at real coupling and it tells you which direction is “better.” If you can recite “connascence of meaning” but it doesn’t change what you do in a code review, you’ve learned the words and missed the tool. The reverse mistake is just as costly: chasing every weak, local connascence to zero. Connascence of name between a function and its three callers in the same file is fine — that’s normal, healthy coupling. Spend the budget where strength and distance are both high. The compass points; it doesn’t command you to march everywhere.

Check yourself
Quiz

You replace a 4-positional-argument function with one that takes a single options object, used across many call sites. In connascence terms, what exactly improved, and why is it an objective win rather than a style choice?

Recap

Connascence is coupling with a definition sharp enough to argue with: two components are connascent when changing one forces changing the other to stay correct. Static forms are visible in the source and rank from weakest to strongest — name, type, meaning, position, algorithm; dynamic forms appear only at runtime — execution order, timing, value, identity — and are worse because the agreement is invisible until something breaks. Two rules turn this into a compass: prefer weaker connascence, and prefer more local connascence, with locality often the larger lever. The positional-args → options-object refactor is the canonical move: position becomes name, a defensible step down the ladder. Use connascence to compare two designs’ coupling objectively instead of by taste — and resist the twin failure modes of hoarding the vocabulary as trivia and of chasing every weak, local coupling to zero.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 4 done

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.