Connascence is evaluated along three dimensions — strength, locality, and degree. Define each, and state the refactoring rules that follow from them.
answer
- Three axes: strength, locality, degree
- Strength ↓, keep strong ones local, degree ↓
- Static always weaker than dynamic
- Rule: maximize connascence inside a boundary (= cohesion)
- Strong + remote + high degree = fix first
basics
~20 sStrength = how hard the coupling is to detect and change (name is weak, timing/identity strong). Locality = how far apart the coupled parts are (same function vs different services). Degree = how many places share it. Rules: reduce strength, keep strong forms local, and shrink degree.
solid answer
~60 s**Strength** ranks the kinds: weaker connascence is easier to discover and safer to change — name < type < meaning < position < algorithm (static) < execution order < timing < value < identity (dynamic). Prefer weaker forms; the standard refactoring converts a stronger form into a weaker one (magic number → named constant demotes meaning to name; positional args → named parameters demotes position to name; typestate demotes execution order to type). **Locality** is the distance between the connascent elements: same block, same class, same module, same component, separate deployables. Strong connascence inside one small, cohesive unit is cheap because one person can see all of it and change it atomically; the same strength across a service boundary is expensive because it needs coordinated releases. **Degree** is how many elements share the connascence — two callers versus two hundred. Page-Jones's rules: (1) minimize overall connascence by breaking the system into encapsulated units; (2) minimize connascence *across* encapsulation boundaries; (3) maximize connascence *within* a boundary — which is exactly cohesion. Practically: high strength + low locality + high degree = fix first; high strength but tightly local and low degree = usually fine.
code
text · 12 linesSame connascence kind, three verdicts:
(a) Connascence of Position, local, degree 2:
private fun midpoint(a: Point, b: Point) = ... // fine, ignore
(b) Connascence of Position, cross-module, degree 12:
reportRow = [id, name, region, total] parsed by index in 12 places
-> demote to Connascence of Name: emit a keyed record
(c) Connascence of Timing, cross-service, degree many:
consumer sleeps 200ms assuming the producer has committed
-> highest priority: replace with an explicit event/ack (or idempotent retry)go deeper
Name the three dimensions and give one example each; know that weaker and more local is better.
Add the strength ordering and two concrete demotion refactorings, and explain why the same kind can be fine or fatal depending on locality.
Apply the triage matrix, connect rule 3 to cohesion, and describe guardrails (contract tests, schema registries) that hold the line.
Use connascence mapping to place service and team boundaries, argue against splits that cut through strong connascence, and encode the invariants as automated fitness functions and contract testing policy.
### Why three dimensions, not one Saying "this is connascence of position" is only half a judgement. Positional arguments in a two-parameter private helper used once are irrelevant; the same positional coupling in a public wire format used by forty consumers is a migration project. The three dimensions turn the taxonomy into a **priority function**. ### 1. Strength Strength = how difficult the connascence is to **discover** and how risky it is to **change**. The conventional ordering, weakest first: **Static (readable in the code):** Name → Type → Meaning/Convention → Position → Algorithm **Dynamic (only at run time):** Execution Order → Timing → Value → Identity Every static form is weaker than every dynamic form, because static forms are visible to compilers, linters, greps, and IDE refactorings, whereas dynamic forms require running the system — often under specific concurrency or load — to expose. The operational meaning of "weaker": if I change one side, how likely am I to be *told* about the other side, and how mechanical is the fix? Renaming a method: the IDE finds every site. Changing a hash algorithm on one side of a wire protocol: nothing tells you, and production breaks. **Demotion refactorings** (the practical payoff): - meaning → name: magic value → named constant or enum. - position → name: positional parameters → named parameters / parameter object / keyed map. - algorithm → name+type: extract the algorithm into a single shared, versioned library or service call. - execution order → type: return the next-stage object from the previous step, so illegal sequences don't type-check (builder / typestate / fluent stage interfaces). - timing → execution order or type: replace sleeps with explicit signals, handshakes, or idempotent retries. - value → single owner: put co-varying values inside one object/aggregate that enforces the invariant atomically, or derive instead of duplicating. - identity → explicit injection: pass the one shared instance in rather than having each side look it up. ### 2. Locality Locality = the **distance** between the connascent elements, measured in encapsulation boundaries crossed: same expression/block → same method → same class → same package/module → same component → same deployable → separate deployables/teams/organizations. The key claim: **as locality decreases (distance grows), acceptable strength drops sharply.** Strong connascence within a five-line function is fine — you see it all at once and change it in one commit. The same strength between two services owned by different teams means coordinated releases, versioned contracts, backward-compatibility windows, and cross-team meetings. This is why the same construct can be good or terrible depending on where it lives: positional tuple destructuring inside one function is idiomatic; a positional CSV contract between two organizations is a liability. ### 3. Degree Degree = the **number of elements** entangled by this instance of connascence. A magic status code understood in 2 places vs 200. A shared date format used by 3 modules vs every service. High degree amplifies everything: the cost of change scales with degree, and the chance of a missed site scales with degree. It is also the dimension most amenable to incremental improvement — you can shrink degree by funnelling access through a single adapter or facade before you attempt to change strength. ### Page-Jones's rules 1. **Minimize overall connascence** by decomposing the system into encapsulated elements. 2. **Minimize connascence that crosses encapsulation boundaries.** 3. **Maximize connascence within an encapsulation boundary.** Rule 3 surprises people, but it is precisely **cohesion**: things that must change together should live together. Splitting strongly connascent code across modules is how you create the worst kind of coupling — strong *and* remote. Conversely, if two modules turn out to be strongly connascent, the honest fix is often to *merge* them, not to add an interface between them. Note the direct link to the modular monolith / microservice debate: a service boundary drawn through strong connascence produces a distributed monolith. "Where are the strong connascences?" is a better boundary-finding question than "what are the nouns?". ### The triage matrix in practice | Strength | Locality | Degree | Action | |---|---|---|---| | strong (dynamic) | remote (cross-service) | high | Top priority — this is what causes outages and lock-step deploys | | strong | remote | low | Fix or document explicitly (deployment ordering, contract test) | | strong | local (one class) | any | Usually fine — leave it, but keep the unit small | | weak (name/type) | remote | high | Acceptable; this is what a published API *is* | | weak | local | low | Ignore | Guardrails that operationalize it: contract tests and schema registries convert remote connascence of meaning into checked connascence of name/type; consumer-driven contracts limit degree by making consumers explicit; fitness functions can fail a build when a strong form crosses a named boundary.
- Rule 3 says to maximize connascence within a boundary. Doesn't that contradict 'reduce coupling'?No — it is the cohesion half of the same idea. Elements that must change together belong in the same encapsulated unit. Maximizing internal connascence means you have concentrated the entangled parts where they are cheap, leaving only weak connascence crossing the boundary.
- How would you use connascence to decide where to split a monolith into services?Map the strong connascences (value, identity, execution order, algorithm) and refuse to draw a boundary through them. A boundary drawn across strong connascence produces a distributed monolith with lock-step deploys and distributed transactions; boundaries should fall where only connascence of name and type crosses.
- You find strong connascence of meaning between two services (an integer status code with implicit semantics). What is the concrete remedy?Demote it toward name/type and get it checked: publish an explicit enumerated schema in a registry, validate both sides against it, and add consumer-driven contract tests so the agreement is machine-verified rather than tribal knowledge.
Think of shared secrets among people. A secret two people in the same room share (strong but local, degree 2) is manageable. The same secret shared by two hundred people across three continents who can never meet is unmanageable — same 'strength', catastrophic locality and degree. Reducing strength is writing it down publicly; reducing degree is telling fewer people; improving locality is putting everyone who needs it in one room.
saying these in an interview costs you the question
- Judging connascence by kind alone, ignoring how far apart and how widespread it is
- Claiming all strong connascence must be eliminated — locally scoped strong connascence is normal and cheap
- Reading 'maximize connascence within a boundary' as advice to increase coupling
- Adding an interface between two strongly connascent modules instead of considering merging them
- Assuming splitting code into more modules automatically lowers coupling; it often converts local connascence into remote connascence