Multiple ways to construct an object — overloaded constructors, factory methods, copy helpers — each repeat field assignments, defaults, and validation. Adding a new field forces edits in every construction path, and a field missed in one path silently produces partly-initialized objects. The team's verification cost compounds across paths no test inspects together.
Multiple construction paths the agent scans in parallel to confirm field initialization is consistent across them. Behavioral preservation on every field-related edit requires verifying every path, multiplying the agent's context-window load by path count and the retrieval cost of pulling each construction body into context.
A single canonical constructor owns all field assignment and validation. Every other construction path is a one-line delegation that supplies the variant-specific arguments. Adding a new field is a one-place change; the team's enhancement cost drops from N parallel edits to one, and separation of concerns between construction-as-orchestration and construction-as-initialization becomes structural.
One construction path the agent reads to know what a fully-initialized object looks like; all other paths are one-line delegations the agent skips past during reasoning. The agent's context window holds the canonical constructor; the reasoning step count drops because field-set drift is impossible by construction, and verifying any one path verifies the family.
Before the refactoring
class Loan {static newTermLoan(commitment, customer, maturity) {const loan = new Loan();if (commitment < 0) throw new Error('commitment must be non-negative');loan.commitment = commitment;loan.customer = customer;loan.maturity = maturity;loan.expiry = null;loan.unusedPercentage = 0.0;return loan;}static newRevolver(commitment, customer, expiry) {const loan = new Loan();if (commitment < 0) throw new Error('commitment must be non-negative');loan.commitment = commitment;loan.customer = customer;loan.maturity = null;loan.expiry = expiry;loan.unusedPercentage = 1.0;return loan;}static newAdvisedLine(commitment, customer, expiry) {const loan = new Loan();if (commitment < 0) throw new Error('commitment must be non-negative');loan.commitment = commitment;loan.customer = customer;loan.maturity = null;loan.expiry = expiry;loan.unusedPercentage = 0.5;return loan;}}
After the refactoring
class Loan {constructor(commitment, customer, expiry, maturity, unusedPercentage) {if (commitment < 0) throw new Error('commitment must be non-negative');this.commitment = commitment;this.customer = customer;this.expiry = expiry;this.maturity = maturity;this.unusedPercentage = unusedPercentage;}static newTermLoan(commitment, customer, maturity) {return new Loan(commitment, customer, null, maturity, 0.0);}static newRevolver(commitment, customer, expiry) {return new Loan(commitment, customer, expiry, null, 1.0);}static newAdvisedLine(commitment, customer, expiry) {return new Loan(commitment, customer, expiry, null, 0.5);}}
Constructor duplication is Shotgun Surgery on the field set of a class. Each new variant adds a parallel copy of the initialization rules; subtle divergence between paths is easy to miss in review and easy to ship to production unnoticed. The team's maintenance cost compounds across every variant that has its own way to forget a field.
N construction paths × M fields = N×M cells the agent verifies match on every field-related edit. Field additions cascade across all paths; per-path duplication consumes context budget that could be spent on the calling code. The agent's verification-surface cost multiplies with path count, and a partial update produces a half-initialized object that targeted tests may not exercise.
Chaining couples every construction path to the canonical constructor's parameter list, and that list grows as variants accumulate. Call sites pass null or default sentinels for parameters that don't apply to their variant, eroding intention-revealing names at the call site. The cognitive load of decoding which parameter applies to which variant rises with the signature's length.
A long canonical parameter list is itself a token cost tax — the agent remembers positional argument order on every reading of a delegating factory. Wrong-position bugs become subtler than missing-field bugs; the agent's completeness-check cost rises during the consolidation itself because every existing call site must be confirmed to route through the canonical constructor.
One place to read what 'constructing a Loan' means; new fields land in one constructor; each variant reads as the parameters it cares about. Validation runs once per construction, not once per path. The team's comprehension cost per construction site drops because the canonical body is the only authority, and review focuses on the variant-specific argument list.
Each variant constructor delegates to the canonical one; adding a new field touches the canonical constructor once and every variant inherits the change, and tests against the canonical body cover all variants transitively. The agent's reasoning-step cost per field edit collapses to one path, and verification surface contracts from N paths to one.
The canonical constructor grows into a Long Parameter List as variants accumulate. Readers no longer know which parameters matter for which variant, and the centralization stops paying — at that point Replace Constructors With Creation Methods or a parameter object becomes the next move. The cure adds accidental complexity when the canonical signature grows beyond what the variant set justifies.
The canonical constructor grows into a many-parameter signature where the agent loses track of which combinations are legal. Context cost moves from per-path duplication to per-parameter combination explosion; a parameter object or named-argument shape becomes overdue, and context-window load rises with each new parameter added without a name boundary.