Connascence: A Better Vocabulary for Code Coupling
9 types of dependency every developer should know. The word 'coupling' tells you nothing useful. Connascence tells you exactly what's wrong and how to fix it.
The word “coupling” is useless. It tells you two things are connected. It doesn’t tell you how, how badly, or what to do about it. Connascence does.
I spent years in code reviews saying things like “these modules are too tightly coupled.” Everyone nodded. Nothing changed. Because “coupled” is a direction, not a diagnosis. It’s like telling a patient they’re “sick” and sending them home.
Meilir Page-Jones introduced connascence in the 1990s. It should have replaced “coupling” in every developer’s vocabulary by now. It didn’t, because academics are terrible at marketing. So I built a tool that detects all nine types automatically, published it on PyPI, and ran it across production codebases. 74,237 violations later, I have opinions.
The Nine Types, Ranked
Connascence has a strength ordering. Weaker forms are cheaper to maintain. Stronger forms are more dangerous. This matters because it gives you a refactoring priority list — something “coupling” never could.
Static connascence is detectable without running the code. These are the ones your linter could catch if it knew what to look for.
Connascence of Name (CoN) is the weakest form. Two components must agree on a name. You call a function calculateTotal, the caller uses calculateTotal. Rename it, and every call site breaks. This is so fundamental that most developers don’t even think of it as coupling. It is. It’s just the cheapest kind.
Connascence of Type (CoT) means two components must agree on a data type. Your function expects a string, the caller passes a string. TypeScript and Python type hints exist specifically to make this form of connascence explicit. When it’s implicit — when you’re passing untyped dictionaries around and hoping the shapes match — you’re paying for type connascence without getting any of the tooling benefits.
Connascence of Meaning (CoM) is where things start hurting. Two components must agree on the meaning of a value. The classic example: status = 1 means “active” in module A and “pending” in module B. Magic numbers. Magic strings. Boolean flags where true means different things depending on context. Every time I see if (type === 3) in a codebase, that’s CoM. Use an enum. Name the concept.
Connascence of Position (CoP) means components must agree on the order of values. Function arguments are the obvious case. createUser("John", "Doe", 30) — swap the first two arguments and you get “Doe John” everywhere. Named parameters and builder patterns exist to eliminate positional connascence. Python got this right with keyword arguments. JavaScript still struggles with it.
Connascence of Algorithm (CoA) means two components must agree on an algorithm. Password hashing is the canonical example. The login system hashes with bcrypt, the registration system hashes with bcrypt. Switch one to argon2 without switching the other and nobody can log in. Serialization/deserialization is another common case. Both sides must agree on the encoding scheme.
Dynamic Connascence: The Expensive Stuff
Dynamic connascence requires running the code to observe. These are harder to detect, harder to fix, and more likely to cause production incidents.
Connascence of Execution (CoE) means components must execute in a particular order. Initialize the database connection before running queries. Load the config before starting the server. This sounds obvious, but in async systems with lazy initialization, execution order becomes a minefield. Race conditions are CoE violations.
Connascence of Timing (CoT2) means components must agree on when things happen. Timeouts, debounce intervals, cache TTLs. Your frontend waits 3 seconds for a response, your backend takes 4 seconds to process. You didn’t change any logic. You changed a timing constant on one side and broke the contract.
Connascence of Value (CoV) means multiple components must agree on specific values. Distributed systems hit this constantly. Two services maintain redundant copies of the same data, and both must update atomically. The CAP theorem is really a statement about the impossibility of eliminating connascence of value across network partitions.
Connascence of Identity (CoI) is the strongest form. Two components must reference the same object instance — not an equivalent one, the exact same one. Singleton patterns create CoI deliberately. Shared mutable state creates it accidentally. This is the most expensive form of connascence because it couples components at the identity level, which means you can’t substitute, mock, or distribute them independently.
Why This Ordering Matters
The strength ordering gives you something “coupling” never could: a refactoring roadmap.
Start with the strongest connascence in your codebase and work down. Convert CoI to CoV where possible. Replace CoV with CoA. Turn magic numbers (CoM) into named constants or enums. Replace positional arguments (CoP) with named parameters.
Each step down the ladder makes your code cheaper to change. Not in some abstract “good design” sense. In a measurable, “this refactoring reduces the blast radius of future changes” sense.
What 74,237 Violations Taught Me
I built connascence-analyzer and published it on PyPI. Then I pointed it at real codebases. Not toy projects — production systems with years of accumulated decisions.
The distribution is predictable. CoN and CoT dominate because they’re unavoidable. You can’t write code without agreeing on names and types. The interesting signal is in the middle of the spectrum.
CoM violations cluster around configuration handling and status codes. Every codebase I’ve analyzed has magic numbers that started as “temporary” and became permanent. The fix is almost always the same: extract a constant, give it a name, centralize the definition. Five minutes of work that saves hours of debugging later.
CoP violations correlate with function arity. Functions with more than four positional parameters almost always have position-dependent bugs somewhere in their call chain. The analyzer flags these, and the fix is usually a dataclass or options object.
CoA violations are the scariest because they’re invisible until they break. Two modules agreeing on a serialization format, a hash algorithm, a date parsing convention. The analyzer detects these by tracking shared algorithmic patterns across module boundaries. When it finds them, the fix is to extract the algorithm into a shared dependency — make the coupling explicit so it can be managed.
Connascence vs. “Clean Code” Heuristics
Most code quality advice is vibes-based. “Functions should do one thing.” “Keep classes small.” “Prefer composition over inheritance.” These are fine as intuitions. They’re useless as measurements.
Connascence gives you measurements. Not “this function feels too big” but “this function has CoP with 7 call sites and CoM with 3 magic constants.” You can count violations. You can track them over time. You can set thresholds.
The analyzer produces SARIF output, which means the results show up directly in GitHub PRs. A developer opens a pull request that introduces a new CoA violation — they see it inline before the review even starts. That’s the difference between a principle and a tool.
The Practical Takeaway
Next time you’re in a code review and something feels wrong, ask yourself: what type of connascence is this? Naming the specific form tells you three things the word “coupling” never will.
First, how dangerous is it? CoN is cheap. CoI is expensive. The strength ordering tells you whether to fix it now or file a ticket.
Second, where is it? Static connascence is in the source code. Dynamic connascence is in the runtime behavior. That tells you whether a linter can catch it or whether you need integration tests.
Third, what’s the fix? Each type has a standard refactoring. CoM becomes named constants. CoP becomes keyword arguments. CoA becomes shared libraries. The diagnosis implies the treatment.
I published connascence-analyzer on PyPI because this vocabulary should be standard equipment. The fact that most developers have never heard the word “connascence” is a failure of our field’s knowledge distribution, not a reflection of the concept’s value.
Want to understand your codebase’s hidden dependencies? Let’s analyze it: https://cal.com/davidyoussef