🧬 Analogy
Connascence — literally ‘having been born together’ — is like the shared dependencies in a recipe. If two cooks merely agree on the name of an ingredient, either can swap brands freely. If they must agree on the exact order steps happen, or the precise timing, changing one forces the other to change too. The tighter the agreement, the harder either can move alone.
What connascence adds to coupling
Plain afferent/efferent coupling tells you that two components depend on each other; connascence (Meilir Page-Jones) tells you how. The definition: two components are connascent if a change in one would require the other to be modified to maintain overall correctness. It splits into two families.
graph TD C["Connascence"] --> S["Static<br/>(source-level, analysable)"] C --> D["Dynamic<br/>(runtime, harder to detect)"] S --> SN["Name (weakest)"] S --> ST["Type"] S --> SM["Meaning / Convention"] S --> SP["Position"] S --> SA["Algorithm (strongest static)"] D --> DE["Execution (order)"] D --> DT["Timing"] D --> DV["Value"] D --> DI["Identity"]
Static connascence (weakest to strongest)
Discoverable by static analysis, ordered from easiest to hardest to refactor:
- Name (CoN) — agree on a name (most desirable; renames are easy).
- Type (CoT) — agree on a type.
- Meaning / Convention (CoM) — agree on a value’s meaning (magic numbers).
- Position (CoP) — agree on order (parameter order).
- Algorithm (CoA) — agree on an algorithm (a client and server computing the same hash).
Dynamic connascence (runtime)
Harder to detect because it only appears when the system runs:
- Execution (CoE) — order matters (set fields before sending).
- Timing (CoT) — timing matters (race conditions).
- Value (CoV) — related values must change together (a value spread across databases; distributed transactions).
- Identity (CoI) — must reference the same entity (a shared distributed queue).
The three properties that guide refactoring
Connascence is judged on three axes:
- Strength — how easy it is to refactor. Prefer static over dynamic, and refactor toward weaker forms.
- Locality — how close the connascent code is. Proximal code tolerates stronger connascence; distant code should be weakly coupled.
- Degree — the size of the impact (a few modules vs many).
Weirich’s rules crystallise this: the Rule of Degree — convert strong connascence to weaker forms; the Rule of Locality — as distance increases, use weaker connascence. Page-Jones’s guidelines: minimise overall connascence by encapsulating, minimise connascence that crosses encapsulation boundaries, and maximise connascence within a boundary.
graph LR S["Strong & distant<br/>(Algorithm across services)"] -->|"Rule of Degree:<br/>weaken the form"| W["Weaker form<br/>(toward Name)"] S -->|"Rule of Locality:<br/>pull it closer"| L["Same module<br/>(local & encapsulated)"] W --> G["Weak & local = safe"] L --> G
⚠️ The 1990s blind spot
Classic connascence is low-level code hygiene and originally said nothing about the modern key decision: synchronous vs asynchronous communication in distributed systems. The Hard Parts and Building Evolutionary Architectures extend it with communication connascence (sync = stronger, async = weaker), which feeds directly into the architecture quantum.
Connascence and bounded contexts
Building Evolutionary Architectures notes that connascence locality (1993) and DDD’s bounded context (2003) express the same anti-brittleness principle: never expose implementation details — especially database schemas — across a boundary.
💡 Key insight
When you must couple two things, ask: can I make this Name connascence instead of Position or Algorithm, and can I keep it local? Weak-and-local beats strong-and-distant every time.
See also
- Components and coupling — the afferent/efferent foundation.
- Modularity and metrics — measuring the result.
- Evolutionary architecture — why appropriate coupling enables evolution.
When to use it — and when not
✅ Reach for it when
- When you need to reason precisely about why a change in one place forces a change elsewhere
- When refactoring to weaken coupling between modules or services
- When deciding what may safely cross an encapsulation boundary
⛔ Think twice when
- When the coupling is trivial and a simple afferent/efferent count suffices
- When you treat connascence as only a code-hygiene concern and ignore communication coupling
Related topics
Components as the architect's primary building block, and the afferent/efferent coupling that measures how they depend on one another.
fnd-componentsModularity and MetricsWhy modularity matters even when nobody asks for it, and the cohesion and coupling metrics — LCOM, abstractness, instability, distance from the main sequence — that make it measurable.
fnd-evolutionEvolutionary ArchitectureArchitecture that supports guided, incremental change across multiple dimensions — and why evolvability is the meta-characteristic that protects all the others.
Check your understanding
Score: 0 / 41. What is the definition of connascence?
Page-Jones: connascence describes when changing one component forces a change in another to preserve overall correctness.
2. Which is the most desirable (weakest) form of static connascence?
Name is the weakest and easiest to refactor; refactor strong forms toward Name where possible.
3. Dynamic connascence differs from static connascence because it…
Static connascence is discoverable by static analysis; dynamic forms (execution order, timing, values, identity) appear only at runtime.
4. What do Weirich's Rule of Degree and Rule of Locality advise?
Rule of Degree: convert strong to weak. Rule of Locality: the farther apart, the weaker the connascence should be.
Comments
Sign in with GitHub to join the discussion.