Client code carries the recipe for constructing a complex object — instantiating sub-collaborators, configuring them, wiring them together, applying defaults. Every caller that needs the object repeats the recipe; recipe drift between callers produces subtly different objects.
Construction recipes duplicated across N caller sites the agent must verify match on every recipe change. Each call site assembles the object with its own subset of defaults; the agent cannot statically guarantee the assemblies stay consistent.
A Factory class owns the assembly recipe. Callers ask for the finished object by intent (`createOrder(...)`); the recipe lives in one place, evolves in one place, and is tested in one place.
One factory file the agent reads as the construction contract; callers are short delegations the agent treats opaquely. Per-edit budget for a recipe change collapses to one file.
Before the refactoring
// The caller knows the entire assembly recipe.function placeOrder(customerId, line1, city, postal, items, jurisdiction) {const customer = new Customer(customerId);const address = new Address(line1, city, postal);customer.setShippingAddress(address);customer.setBillingAddress(address);const order = new Order(customer);for (const item of items) {const lineItem = new LineItem(item.product, item.quantity);lineItem.applyDiscount(customer.discountRate);order.addLineItem(lineItem);}order.calculateTotal();order.applyTax(jurisdiction);return order;}
After the refactoring
// The factory owns the recipe; the caller asks for a finished order.class OrderFactory {constructor(taxJurisdiction) {this.taxJurisdiction = taxJurisdiction;}createOrder(customerId, line1, city, postal, items) {const customer = this.buildCustomer(customerId, line1, city, postal);const order = new Order(customer);items.forEach((item) => order.addLineItem(this.buildLineItem(item, customer)));order.calculateTotal();order.applyTax(this.taxJurisdiction);return order;}buildCustomer(customerId, line1, city, postal) {const customer = new Customer(customerId);const address = new Address(line1, city, postal);customer.setShippingAddress(address);customer.setBillingAddress(address);return customer;}buildLineItem(item, customer) {const lineItem = new LineItem(item.product, item.quantity);lineItem.applyDiscount(customer.discountRate);return lineItem;}}// Client:const factory = new OrderFactory(jurisdiction);const order = factory.createOrder(customerId, line1, city, postal, items);
Duplicated assembly is Shotgun Surgery for every recipe change — a new required field, a defaults update, a wiring change must land everywhere the object is built. Recipe knowledge leaking into clients couples them to the object's internals.
Recipe duplication × N callers × M recipe steps inflates the agent's context cost on every assembly-related edit. Subtle inconsistencies between caller-side recipes are invisible until they manifest as bugs; static reasoning cannot enforce 'every caller applies the discount'.
A Factory becomes the single point of coupling to the object's construction shape; signature changes there ripple to every caller. Over-aggressive factory extraction can hide which dependencies the constructed object actually has; debugging requires reading the factory to understand the resulting object.
A factory hides construction details from the call site; the agent must read the factory to know what the returned object actually carries. Static type information at the call site narrows to the return type, with construction-level invariants pushed inside the factory.
The recipe is named, tested, and editable in one file. Callers stop knowing what defaults apply, which fields are required, or how sub-collaborators wire. Object construction becomes a domain operation rather than a wiring chore.
The recipe lives at one factory the agent edits once; callers reach for the factory's named methods, and the recipe cannot drift across callers because callers do not hold a copy of the assembly steps.
A Factory whose `create` method is itself a long parameter list that mirrors the constructor's just adds an indirection layer. The pattern pays when the recipe involves real assembly decisions (sub-collaborators, defaults, configuration) — not when the factory is a shallow constructor pass-through.
A factory whose recipe is itself a long sequence of conditional steps can become as opaque as the duplicated callers were — the agent must trace the factory body line-by-line to know what came out. The pattern's gain materializes when the factory's recipe is itself decomposed into named build steps.