Coupling is often treated as a vague notion: “it’s too coupled” says nothing about what to actually change. Connascence provides a precise vocabulary to name the different forms of coupling, compare them, and decide which ones to reduce first.

The concept is documented in detail at connascence.io.

Three axes to evaluate connascence

Every instance of connascence can be analyzed along three axes:

  • Strength: the stronger a connascence, the harder it is to detect and refactor.
  • Degree: an entity coupled to hundreds of others is more problematic than one coupled to two.
  • Locality: two components that are close together (same class, same module) can tolerate stronger forms. At a distance, those same forms become dangerous.

The 9 types of connascence

Connascence of Name (CoN)

Multiple components agree on the name of an entity. This is the weakest and most unavoidable form.

class PaymentService:
    def charge(self, amount: int) -> None:
        ...

service = PaymentService()
service.charge(100)  # depends on the name "charge"

Renaming charge breaks all callers. Easy to fix, but the risk grows at scale.

Connascence of Type (CoT)

Multiple components agree on the type of an entity.

def send_invoice(amount: int) -> None:
    ...

send_invoice("100")  # runtime error: str instead of int

Python does not catch these errors at compile time, but mypy or Pyright will.

Connascence of Meaning (CoM)

Multiple components agree on the meaning of specific values. Magic values are the classic symptom.

# Fragile: what does 1 mean?
def get_card_type(card_number: str) -> int:
    if card_number.startswith("4"):
        return 1

# Better: named constants
VISA = 1
MASTERCARD = 2

def get_card_type(card_number: str) -> int:
    if card_number.startswith("4"):
        return VISA

Moving from magic values to named constants upgrades the coupling form (CoM to CoN).

Connascence of Position (CoP)

Multiple components agree on the order of values.

# Fragile: argument order matters
def create_user(first_name, last_name, year_of_birth, is_admin):
    ...

create_user("Thomas", "Richards", 1984, True)

# Better: dataclass
from dataclasses import dataclass

@dataclass
class UserData:
    first_name: str
    last_name: str
    year_of_birth: int
    is_admin: bool = False

A dataclass eliminates the risk of confusion from argument order.

Connascence of Algorithm (CoA)

Multiple components agree on a specific algorithm. If both sides must implement the same computation, a change in one must be replicated in the other.

import hmac
import hashlib

def sign_payload(data: str, secret: str) -> str:
    return hmac.new(secret.encode(), data.encode(), hashlib.sha256).hexdigest()

def verify_payload(data: str, secret: str, signature: str) -> bool:
    expected = hmac.new(secret.encode(), data.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

If you change the signing algorithm in sign_payload, verify_payload must be updated at the same time.

Connascence of Execution (CoE)

The order of execution between components matters.

import threading

lock = threading.Lock()

lock.acquire()
try:
    process_critical_section()
finally:
    lock.release()

A context manager (with lock:) encapsulates this order and reduces the risk of forgetting.

Connascence of Timing (CoT)

The timing of execution matters. Typical in concurrent systems.

import threading
import time

result = None

def fetch_data():
    global result
    time.sleep(0.5)
    result = {"status": "ok"}

t = threading.Thread(target=fetch_data)
t.start()

# Race condition: result may still be None here
print(result)

Connascence of Value (CoV)

Multiple values must change together. Common between production code and tests.

class Article:
    DRAFT = "draft"
    PUBLISHED = "published"

    def __init__(self, content: str):
        self.content = content
        self.status = self.DRAFT

# Test coupled to the initial value
def test_initial_status():
    article = Article("content")
    # Referencing Article.DRAFT instead of the raw string reduces this coupling
    assert article.status == Article.DRAFT

Connascence of Identity (CoI)

Multiple components must reference the same instance of an object.

shared_cache = {}

def writer(cache: dict):
    cache["key"] = "value"

def reader(cache: dict):
    return cache.get("key")

writer(shared_cache)
assert reader(shared_cache) == "value"

This is the strongest form. At a distance, it becomes very hard to reason about.

The practical rule

Connascence does not require eliminating all coupling. It helps you arbitrate: a CoA within a single class is acceptable, a CoV between two distant services is a warning sign.

The rule: the greater the distance between components, the weaker the connascence form should be. Reducing the strength or bringing components closer together is a concrete way to improve maintainability.

To explore all types in detail: connascence.io.