1. What is a module, and what two things does its boundary define?
2. List the six types of connascence described in this chapter, ordered from weakest to strongest. Which type is hardest to detect and most expensive to violate?
3. What does a cycle in the import graph imply about the modules involved, and what is the consequence for testing and deployment?
Draw the import graph for the following module structure and
identify all cycles: auth imports users,
users imports billing, billing
imports auth, notifications imports
users. Which pairs of modules are entangled? What would you
need to extract to break the cycles?
A function signature is
processOrder(userId, accountId, orderId, productId, quantity).
Identify which type of connascence is highest risk here and explain why.
Propose a refactoring that reduces that risk.
A module declares a global CONFIG dictionary that
seven other modules read directly. What type of connascence does this
create? What failure mode does it produce? Propose an alternative
structure.
A complete answer will: (1) specify the tooling: a CI-enforced
import cycle detector (e.g., pydeps, madge, or
a custom script) that fails the build if new cycles are introduced —
this gate prevents regression while the existing cycles are addressed; a
dependency visualisation tool to identify the highest-fan-out nodes that
participate in the most cycles, (2) order cycle elimination by impact:
start with cycles involving the most files or the most frequently
changed modules (high churn cycles cause the most pain); within a cycle,
extract the shared dependency into a new module that both sides can
import, rather than reversing import direction, (3) name AT3
(Simplicity/Flexibility): extracting a shared module is simpler to
reason about but introduces a new module that must be maintained;
inlining dependencies eliminates the module boundary but increases file
size — state which is preferred and under what conditions, and FM8
(silent semantic drift) for cycles that embed implicit contracts between
files that are never documented, (4) describe the enforcement boundary:
define allowed import layers (e.g., api → services → domain →
infrastructure) and enforce directional imports only — any upward import
(domain importing from api) triggers a CI failure — specify the tool
configuration and the process for requesting exceptions.
A complete answer will: (1) make the cohesion argument using connascence vocabulary: authentication and profile management share connascence of identity (the user entity) and connascence of meaning (the concept of “logged-in user state”) — they change together when the user model changes, suggesting high cohesion that favours a single module, (2) make the coupling argument: authentication is consumed by every service in the platform (high fan-in), while profile management is consumed by a smaller set (settings, notifications) — placing them together creates connascence of execution (callers must import authentication logic to access profile operations) that spreads authentication’s blast radius to unrelated services, (3) state the conditions under which each argument wins: cohesion wins when the team is small, the user model is stable, and the overhead of a service boundary (network calls, separate deployment, schema migration coordination) is high; coupling wins when authentication scales independently of profile management, when separate SLAs are required (auth must be 99.99%, profile management can tolerate planned downtime), or when separate teams own each domain, and (4) name AT8 (Coupling/Cohesion) as the governing tradeoff and identify the connascence type that forces the decision: if profile management changes require authentication schema changes, the connascence of meaning is too tight for separation; if they can evolve independently, the coupling argument wins.