The rejected pattern: why the programming patterns your team didn't adopt deserve a decision record
A library that was rejected still leaves a trace — it isn't in package.json. A pattern that was rejected leaves nothing. The codebase that doesn't use decorators for dependency injection looks identical whether the team spent two weeks evaluating and rejecting them or whether decorators were never considered. There is no artifact to find, no absence that signals a deliberation happened. The reasoning exists only in AI chat sessions from eighteen months ago — and only if someone thought to extract it.
When a new engineer joins a codebase and finds it doesn't use an ORM, they face three possible readings of that absence: the team never considered an ORM and defaulted to raw SQL without deliberation; the team evaluated one or more ORMs and rejected them for specific reasons they couldn't articulate in the moment; or the team made a considered architectural decision to avoid ORMs and could articulate exactly why. The behavior that follows from each reading is different. The first reading prompts a proposal to add an ORM. The second reading also prompts a proposal, since there's no record to consult. The third reading prompts the new engineer to find the decision record, read the reasoning, and decide whether to accept the decision or argue that the specific constraints have changed.
The problem is that the codebase looks the same in all three cases. There is no file called no-orm-decision.md sitting in the repository to signal that a deliberation happened. There is no configuration entry, no commented-out import, no test fixture showing what the rejected alternative would have looked like. The absence of the ORM is the only artifact, and it is identical whether the absence was deliberate or accidental.
This is the most invisible category of architectural decision: not the decision to build something a particular way, which produces a codebase artifact that survives, but the decision not to use a pattern at all, which produces only the absence of something that was never present. The rejected dependency at least has a package.json entry to be absent from. The rejected pattern has no footprint in the codebase at any layer.
Why pattern rejections are harder to recover than dependency rejections
The rejected dependency has two recovery advantages over the rejected pattern. First, the library name provides a search anchor: an engineer who wonders why date-fns isn't in the codebase can search the AI chat export for "date-fns" and find the evaluation session. Second, the absence from package.json is a signal that the question might be worth asking — there's a definite place to look where something could have been added and wasn't.
The rejected pattern has neither advantage. The pattern doesn't have a canonical name that maps to a single AI chat search term. Was the evaluation framed as "should we use decorators?" or "how does NestJS dependency injection work?" or "constructor injection vs property injection in TypeScript?" or "metadata reflection for DI"? All of these could be the session that contains the pattern rejection reasoning, and none of them are the same search term. The engineer trying to recover the deliberation needs to know enough about what the team evaluated to know what to search for — which is precisely the knowledge they're missing.
The absence from the codebase is also a weaker signal for patterns than for dependencies. A new engineer looking at a codebase that uses TypeScript but doesn't use decorators may not register this as an absence worth questioning. Decorators are an opt-in feature that many TypeScript codebases don't use. The absence looks like a default rather than a decision. Contrast this with a codebase that doesn't have axios or node-fetch despite making HTTP requests: the absence of a commonly-used HTTP library is more obviously noteworthy. Pattern absences blend into the background of "coding style choices," which diffuses the signal that a deliberation may have happened.
The three pattern rejection categories
Pattern rejections fall into three categories, each with a different origin story and a different set of consequences when left undocumented.
The evaluated and explicitly rejected pattern. The team considered a pattern that was popular in adjacent codebases, evaluated it against the codebase's specific constraints, and chose not to adopt it. The evaluation was genuine — team members read about the pattern, prototyped with it, or discussed it at length — and produced a specific rejection reason: the pattern added too much "magic" for the team's debugging practices, the pattern required a compiler plugin that conflicted with the build pipeline, the pattern was correct for the framework context it came from but not for the team's framework, the pattern's performance characteristics were unsuitable for the specific hot path it would run on.
This is the category where the rejection record creates the most direct value, because the evaluation was expensive and would be expensive to repeat. A team that spent three days evaluating TypeORM before deciding to use raw SQL with a thin query builder has spent 24 person-hours that should never need to be spent again. Without a record, the next new engineer proposes TypeORM, the team must reconstruct the evaluation from memory, and the discussion runs long because the people who remember the evaluation don't agree on which details were decisive.
The deliberately excluded pattern as a standards decision. The team decided that a category of pattern is not acceptable in the codebase, not as a result of evaluating a specific instance but as an architectural position. "No global state." "No ORMs — all database access is raw SQL." "No magic — all behavior should be traceable without running code." "No class components in React." "No singleton services — all modules export stateless functions." These are categorical rejections: the team isn't saying "we evaluated Zustand and rejected it for store management," but "we've decided there will be no global state store at all."
Categorical rejections are more powerful than instance rejections because they don't require re-evaluation every time a new library in the category appears. They are also harder to enforce without documentation. A team that has a "no global state" position but hasn't documented it will find that position eroding gradually as each engineer adds "just one small global" for their feature, each addition seeming too small to justify a team discussion. The categorical rejection documented as an ADR is the instrument that converts a team culture agreement into a documented constraint that can be cited in code review.
The adopted and then de-standardized pattern. The team used a pattern, encountered problems specific to their codebase, and moved away from it — but the migration is partial. Some code uses the old pattern and some uses the new one. This category is the most urgent to document because the partial state looks like inconsistency without a record explaining the direction of travel.
A codebase that has some React class components and some function components, with no documented migration decision, presents a genuine question for a new engineer: are class components acceptable for new code? The honest answer may be "no, we're migrating away from them" — but without a record, the new engineer can only read the codebase as a signal, and the mixed state sends no clear signal. They might add a new class component because the adjacent code uses class components. They might write a function component and be uncertain whether they've violated a convention. The migration decision record — "we're moving from class components to function components; all new code should use functions; existing class components don't need to be migrated immediately but should be converted when the file is touched for other reasons" — resolves the ambiguity that the partial state creates.
The consistency compulsion
A new engineer joining a codebase brings the patterns they've used in their last three roles. This is the consistency compulsion: when a pattern is absent from the new codebase that was present in prior codebases, the new engineer's prior experience tells them the pattern belongs there. Their prior teams used it; it worked; the absence in the new codebase reads as an oversight or an opportunity.
This isn't a failure of judgment. It's the correct behavior given a codebase that has provided no evidence that the absent pattern was considered. The new engineer can't distinguish between "the team evaluated decorators and rejected them for a documented reason" and "the team never thought about decorators" from the codebase alone. The absence looks identical in both cases. Without a record, their prior experience is the strongest available signal — and their prior experience says the pattern works.
The consistency compulsion is strongest for patterns that have been adopted widely across the industry after the team made its rejection decision. A team that rejected ORMs in 2021 when Prisma was new and had rough edges will face more pressure to revisit that decision in 2026 when Prisma is mature and widely adopted. The rejection record serves two functions here: it names the specific constraints that drove the 2021 rejection (rough migration tooling, limited custom query support, performance overhead on bulk operations at the team's data volume), and it names a revisitation condition that distinguishes between "the constraints have changed" and "the ecosystem has matured." If the 2021 record said "revisit when Prisma's bulk upsert performance reaches parity with raw SQL for inserts above 10,000 rows" — a concrete, evaluatable condition — the 2026 proposal can be answered by running the benchmark rather than re-opening the entire philosophical debate about ORMs.
The "technical debt" misread
The most expensive failure mode of undocumented pattern rejections is the technical debt misread: a new engineer sees the absence of a popular pattern and reads it as an oversight to correct rather than a decision to respect. This misread is not a failure of engineering judgment — it is the rational response to a codebase that provides no evidence of deliberation.
The misread produces proposals that begin with "I notice we're not using X, which is the standard approach for this kind of problem. I'd like to migrate us toward X." This is phrased as a gap closure rather than a direction change, which is accurate from the proposer's perspective: without a record, the current state genuinely looks like a gap. The proposal doesn't need to overcome documented reasoning — because no documented reasoning exists.
The engineers who made the original rejection decision are now in a weak rhetorical position. They must make a case based on memory and intuition against a proposal that is backed by documentation (the library's docs, community adoption statistics, blog posts, conference talks). The asymmetry is structural: the proposing engineer has done research; the defending engineers are working from memory. Even if the original rejection was correct and well-reasoned, the team may capitulate to the proposal simply because the current reasoning is less articulable than the proposal's.
This asymmetry is sharpest when a new technical leader joins. A new CTO or engineering manager who sees a codebase full of raw SQL in 2026 and no ORM may read this as a legacy choice made before modern tooling was available, rather than a deliberate architectural stance. The architectural stance without a record looks indistinguishable from an architectural accident. The record converts the reading from "this team hasn't upgraded its tooling" to "this team evaluated the tooling and chose not to adopt it for these specific reasons." The same codebase, two completely different readings — and the record is the only thing that determines which reading is available.
Three specific examples in detail
Abstract categories are less useful than concrete instances. Three specific pattern rejections illustrate the shape that documentation takes for each category.
Decorator-based dependency injection in TypeScript. A team building a Node.js backend in TypeScript evaluates NestJS-style decorator-based DI (using @Injectable(), @Module(), reflect-metadata) against explicit constructor injection (passing dependencies as constructor parameters, no metadata reflection, no framework magic). The evaluation takes three days: the team builds a small prototype with both approaches, measures the cold start impact of reflect-metadata on their Lambda functions (the experimental decorator spec requires emitDecoratorMetadata, which adds approximately 20% to the compilation output for complex metadata graphs), discovers that decorator-based DI debugging requires understanding the inversion-of-control container to trace which implementation is being injected, and finds that their team's debugging practice relies on being able to read the code top-to-bottom to understand what runs. The team rejects decorator-based DI in favor of explicit injection.
Two years later, TypeScript 5.0 ships a new decorator spec that changes the metadata emission story. A new engineer joins and proposes introducing tsyringe for DI, noting that the new decorator spec addresses the metadata overhead concern. Without a record, the team must reconstruct the 2021 evaluation reasoning from memory to evaluate whether the new spec changes their position. With a record that names the three concerns (cold start overhead from metadata, container opacity for debugging, top-to-bottom readability constraint) and a revisitation condition ("revisit when TypeScript ships a stable decorator spec with documented metadata overhead for our Lambda use case"), the team can evaluate the proposal precisely: does the new spec address the cold start concern? What is the debugging story with the new container? The three-day evaluation becomes a one-day targeted assessment.
The ORM rejection as a categorical standards decision. A team building a SaaS product on PostgreSQL evaluates Prisma as the ORM. The evaluation is thorough: they build three representative data access patterns with Prisma and with raw SQL, measure the query plans generated by Prisma's query engine against the hand-written SQL, and find that Prisma's join handling produces suboptimal query plans for their most common access pattern (a join across four tables that Prisma constructs as multiple round trips to the database, which their raw SQL collapses into a single query with appropriate indexes). They reject Prisma for this product — but they also make a broader categorical decision: the team will not use ORMs for this codebase because the team's SQL fluency is high enough that the abstraction cost (query plan opacity, N+1 risk, migration friction for complex schema changes) exceeds the developer experience benefit.
The categorical decision is the more important record. It prevents not just a Prisma re-proposal but any ORM re-proposal — and it explains the constraint (query plan opacity for a team with high SQL fluency) in a way that establishes a clear re-evaluation condition: the categorical decision should be revisited if the team's SQL fluency profile changes significantly (e.g., through hiring engineers who come primarily from high-abstraction frameworks), or if the team's query plan tuning becomes a bottleneck that an ORM's automatic optimization could address. Without the categorical framing, each new engineer proposes their preferred ORM independently, and the team has a series of identical debates with no accumulating institutional memory.
The event emitter de-standardization. A team builds an early version of their product using Node.js EventEmitter for component communication — a pattern that was conventional in the pre-React era and works adequately for small component graphs. As the product grows, the team encounters the characteristic failure mode: event propagation is hard to trace in a debugger (events are emitted at one location and consumed at another, with no call stack connecting them), event handler registration order becomes load-order-dependent, and memory leaks from unremoved listeners start appearing in long-running sessions. The team decides to replace EventEmitter-based communication with direct prop passing and callback parameters — more verbose, but traceable and explicit.
The migration is partial. Twelve components still use EventEmitter. A new engineer joins and sees both patterns in the codebase. Without a migration record, they ask: which pattern is preferred for new code? The mixed state provides no answer. The migration record answers the question definitively: EventEmitter-based state communication is deprecated in this codebase; new code should use explicit props and callbacks; existing EventEmitter usage should be migrated when the component is touched for other reasons. The record also explains why — the traceability and memory leak concerns — which helps the new engineer understand that this isn't a style preference but a reliability decision driven by specific failure patterns that appeared in production.
Writing the rejected pattern record
The Nygard ADR format adapts directly for rejected patterns. The title follows the decision-statement convention with a comparison or categorical construction:
- "Chose explicit constructor injection over decorator-based DI — Lambda cold start and debugging constraints" — the evaluated-and-rejected instance
- "No ORM — raw SQL for all database access" — the categorical standards decision
- "Superseding EventEmitter state communication with explicit props — traceability and memory leak elimination" — the de-standardization migration record
The title must signal at list-scan level that the pattern was a decision, not a default. "We use explicit constructor injection" is a description of the current state. "Chose explicit constructor injection over decorator-based DI" signals that an evaluation happened and an alternative was considered. The comparison construction is the difference between a style note and a decision record.
Context. Name the codebase context that made the pattern relevant: why was this pattern under consideration, what problem was it being evaluated to solve, what was the trigger for the evaluation. "We were building the dependency injection layer for the core application services. The team evaluated decorator-based DI (NestJS-style, using reflect-metadata) as the standard approach for TypeScript dependency injection. The evaluation was triggered by a new team member joining from a NestJS background and proposing the pattern for the new service layer we were building."
Alternatives Considered. Name the rejected pattern with the specific concerns found during evaluation, and the chosen approach with why it fits the codebase's constraints. For categorical rejections, name what the category offers and why the category-level tradeoff doesn't fit. "Decorator-based DI via tsyringe: evaluated three-day prototype period. Concerns: (1) emitDecoratorMetadata added 18% to compiled output for our Lambda function bundle, increasing cold start by approximately 120ms at our function size; (2) debugging an injection failure requires understanding the container's resolution order, which is not readable from the code alone; (3) the experimental decorator spec at time of evaluation had known behavior differences from the TC39 Stage 3 proposal. Explicit constructor injection: chosen approach. All dependencies are passed as constructor parameters; no metadata reflection; full call stack traceability in any debugger."
Decision. The chosen approach with the constraint that drove it. The constraint should be specific enough to define a re-evaluation condition. "We chose explicit constructor injection over decorator-based DI. The decisive constraint was Lambda cold start budget: the team's target is sub-200ms cold starts for p99 at our current function size, and the metadata reflection overhead at time of evaluation brought us to 280ms. The secondary constraint was team debugging practice: the team reads code top-to-bottom to trace execution paths, and the inversion-of-control container breaks this practice for injection failures."
Consequences. What the team accepted. This is not the justification section — it names the real trade-offs of the path taken. "We accepted that new engineers joining from framework-heavy backgrounds will find explicit injection more verbose than they are accustomed to. We accepted that adding a new service requires manual wiring of its dependencies in each consumer. We accepted that the factory pattern for test doubles is more explicit than mock containers. These trade-offs are acceptable given the cold start and traceability constraints."
Revisitation condition. Named triggers under which the decision should be re-evaluated. "Re-evaluate this decision if: (1) TypeScript ships a stable decorator spec (post-Stage 3) with documented metadata emission behavior and our Lambda cold start measurement shows overhead below 50ms; (2) our Lambda bundle size constraint changes (e.g., we move to containerized deployment where cold start overhead is less sensitive to bundle size); (3) the team's size grows above 15 engineers and the manual wiring overhead becomes a documented productivity constraint. Re-evaluation should include a cold start benchmark on a current Lambda runtime at our function size, not rely on prior measurements."
The record does not need to be comprehensive. The discipline is in three places: naming the specific constraint (not "we preferred explicit injection" but "the metadata reflection overhead exceeded our cold start budget by 80ms at our function size at time of evaluation"), naming what was accepted rather than only what was gained, and naming a concrete revisitation condition rather than "revisit if circumstances change."
The categorical rejection as an anti-pattern ADR
Categorical pattern rejections produce a document type worth distinguishing: the anti-pattern ADR. Where a standard ADR documents what the team chose and why, the anti-pattern ADR documents what the team has decided not to allow and why. The distinction matters because the document's function is different: the standard ADR is a decision record for the engineers who implemented the choice, and the anti-pattern ADR is a constraint document for engineers who encounter the pattern in adjacent codebases and consider bringing it in.
The anti-pattern ADR has a specific audience: the engineer who comes from a team that used global state, decorators, ORMs, or singleton services and considers proposing the same approach here. That engineer needs to know not just that the approach isn't currently used, but that the absence is a decision rather than a gap, and what the specific constraint was that produced it.
The anti-pattern ADR also names the alternatives the team uses instead. "No global state" isn't a complete constraint without "we handle shared state through X instead." "No ORM" isn't complete without "all database access uses raw SQL with a thin query builder layer at the repository boundary." The alternative is what a new engineer needs to follow the standard — the constraint without the alternative leaves them in a negative space with no guidance on how to proceed.
The anti-pattern ADR connects to cross-team decisions in a specific way: when a team's categorical rejection affects what other teams can build on top of them. A platform team that has decided "no singleton services — all modules are stateless" has made a decision that affects every team consuming their modules. The modules can't carry state that accumulates across requests, which affects caching strategies, connection pooling behavior, and any feature that needs to track session-level context. The anti-pattern ADR communicates this constraint to downstream teams; without it, those teams discover the constraint when a feature they're building fails in production because it relied on state that the module layer doesn't preserve.
The de-standardization migration record in detail
The pattern that was adopted and then de-standardized produces the most urgent documentation need because it creates an inconsistency that every new engineer must resolve without guidance. The de-standardization decision record is not a rejection of a pattern that was never used — it is a rejection of a pattern that the codebase currently contains, with a migration plan for bringing the codebase to a consistent state.
The migration record has a section structure that differs from the standard ADR. Context explains the old pattern and why it was adopted initially — this is the charitable reading of the old choice, not a retrospective condemnation. "EventEmitter-based component communication was the conventional Node.js pattern for event-driven architectures at the time the product was built. It was the right choice for the component graph size at the time; the failure modes that prompted this migration were not visible at the initial scale."
Alternatives Considered names what triggered the re-evaluation: specific failure modes, performance problems, maintainability costs that emerged as the codebase grew. "The failure modes that triggered this evaluation: (1) three production memory leak incidents in 2025 Q3, each traced to EventEmitter listeners registered in component mount logic but not removed on unmount; (2) debugging session traces consistently showed that event propagation was invisible in standard debuggers — the handler was called but the call stack didn't show which emit triggered it; (3) event handler registration order caused two load-order-dependent bugs that took more than two days each to diagnose."
Decision names the new standard and the migration approach explicitly — not just "we're moving to prop passing" but "all new components use prop passing and callback parameters; the twelve components still using EventEmitter are listed in the migration tracker at docs/migrations/event-emitter-migration.md; the migration standard is: convert when the component is touched for any other reason, not as a dedicated migration sprint."
Consequences names the migration state explicitly: "The codebase will be in a mixed state for an indeterminate period. New code must not use EventEmitter for component communication. Code review should flag new EventEmitter usage as a migration blocker. The migration tracker lists the twelve remaining components; the last one should be converted as part of the next major refactoring cycle."
The mixed-state acknowledgment is important. A de-standardization record that pretends the old pattern no longer exists is less honest than one that says "we're in the process of migrating and here is the current status." The honest record is also more useful: a new engineer who understands that the codebase is in a deliberate migration away from EventEmitter will read old EventEmitter code differently than a new engineer who finds EventEmitter usage in what they believed to be a codebase that doesn't use it.
The pattern rejection log as a style guide supplement
A collection of pattern rejection records serves a function that a conventional style guide cannot: it shows not just what the team does, but what the team considered and decided not to do. A style guide that says "use explicit constructor injection" is a prescription. The pattern rejection record that explains why the team chose explicit injection over decorator-based DI converts the prescription into a constraint — one that makes sense of the current code to someone who has used the alternative.
Style guides accumulate rules without explanations. The rule "no ORMs" in a style guide is unevaluatable by a new engineer who doesn't know why the rule exists. Is it historical? Is it a performance constraint? Is it a maintenance philosophy? The style guide doesn't say. The rejection record says. An engineer who has read the rejection record and the style guide together understands not just what the rule is but what constraint it protects, which is the information they need to evaluate proposals for exceptions and to decide when the constraint might have changed.
The pattern rejection log is also the correct place to capture deliberate minimalism in pattern adoption. Some teams have a genuine preference for patterns that are explicit and traceable over patterns that are powerful and implicit. "We prefer boring patterns" is a team culture, not just a technical constraint. When that culture is visible as a series of rejection records — we evaluated decorators and rejected them for explicitness, we evaluated ORMs and rejected them for query plan control, we evaluated global state and rejected it for traceability — the culture is documented and explicit rather than invisible and inferential. A new engineer can read the pattern and understand the team's design philosophy, not just its current implementation choices.
Finding rejected patterns in AI chat
The WhyChose extractor finds pattern rejection decisions through evaluation session shapes that are characteristic but harder to anchor than dependency rejections. Pattern evaluation sessions begin with "how to" or "should we use" questions — "how does dependency injection work in TypeScript without a framework?" or "should we use an ORM or raw SQL for this kind of data access pattern?" — and conclude with a decision that doesn't result in a new file or dependency being added.
This session shape is distinct from the forward decision session ("let's implement X") and from the information-gathering session ("how does X work?"). The evaluation session has two phases: the investigation phase, where the engineer researches the pattern and evaluates it against their context, and the conclusion phase, where a decision is made. The conclusion phase is what the extractor looks for: markers like "we decided to keep it explicit," "the magic is too much to debug," "not worth the complexity at our current scale," "we'd rather write the SQL ourselves," "decorator support is too experimental for production."
The most valuable sessions are those where a specific alternative was evaluated. The session that compared TypeORM against raw SQL for a specific access pattern is more valuable than the session that simply asked "should we use an ORM?" — because the specific comparison contains the concrete reasoning that makes the rejection defensible. The quarterly decision review is the mechanism for surfacing these sessions systematically: a pass over 90 days of AI chat history looking for evaluation sessions that concluded negatively, across all domains including pattern decisions, is more reliable than trying to recover pattern rejections on demand when a proposal arrives.
The de-standardization sessions have a recognizable pattern: they're retrospective rather than prospective. "We've been using EventEmitter for component communication and we're running into problems" is a de-standardization trigger session. These sessions appear in the AI chat export near the dates of the production incidents or maintainability events that prompted them — making them locatable by correlating the chat export with the post-mortem or incident record dates from the same period.
What the rejected pattern record protects
The rejected pattern record protects three things that are otherwise lost when a deliberate pattern rejection is left undocumented.
It protects the evaluation work. The three days spent evaluating decorator-based DI, the week spent comparing ORM query plans against hand-written SQL, the two production incidents that triggered the EventEmitter de-standardization — this is expensive deliberation. Without a record, it must be partially repeated every time the pattern is proposed again. With a record, the prior work is the starting point for re-evaluation rather than the implicit baseline that the proposing engineer must reconstruct and overcome from scratch.
It protects the codebase consistency that the categorical rejection exists to maintain. A "no global state" constraint that isn't documented is a preference that erodes with each new engineer who adds one small global that seems obviously justified. A documented constraint is a standard that can be cited in code review, can be linked from the style guide, and can be applied consistently by engineers who weren't present when the constraint was established.
It protects the team's deliberate minimalism from being misread as accidental incompleteness. A codebase that doesn't use ORMs, decorators, or singleton services, and has no records explaining why, looks under-engineered to an engineer arriving from teams that use all three. A codebase with a rejection record for each absence looks like a codebase with considered opinions about its own patterns. The distinction is the difference between a team that hasn't gotten around to the standard tooling and a team that evaluated the standard tooling and decided against it. The records are the only evidence that distinguishes the two.
Further reading
- The rejected dependency decision record — the companion piece for libraries rather than patterns; rejected dependencies share the same "invisible absence" problem but have one recovery advantage: the library name is a single search anchor; rejected patterns may be framed under multiple vocabulary terms in AI chat, making extraction harder but the record more valuable
- Decisions that never get written down — the "not building this" decision type; the rejected pattern is the pattern-level version: the decision not to adopt a standard that would otherwise be visible in every file that touches a particular layer of the codebase
- Cross-team decisions and ADR constraints — categorical pattern rejections by a platform team constrain downstream teams; the "no singleton services" decision from the platform layer propagates a statelessness requirement that affects every consumer's caching and session management approach
- The ADR lifecycle: supersede and deprecate — de-standardization records are a form of supersession; the migration record supersedes the original adoption decision (which may never have been documented, making the migration record also the first documentation of what the old pattern was and why it was adopted)
- The ADR title convention — rejected pattern titles should use the comparison construction ("Chose X over Y") or categorical construction ("No X — Y instead") to signal at list-scan level that an evaluation happened; "We use explicit injection" is a description; "Chose explicit injection over decorator-based DI" is a decision signal
- The new-CTO onboarding problem — deliberate pattern minimalism is systematically misread by new technical leaders as accidental under-engineering; the rejected pattern record is the instrument that converts this misreading from a culture judgment into an engineering explanation with recoverable reasoning
- The ADR as a forcing function — opening the pattern rejection template before concluding a pattern evaluation surfaces whether the alternative was seriously evaluated; the blank Alternatives Considered section is diagnostic for evaluations that found the pattern unsuitable without examining what the team would use instead
- Nygard ADR template — the base format adapts directly; Context names the problem and pattern evaluated, Alternatives Considered names the pattern and its specific concerns alongside the chosen approach, Decision names the chosen alternative with the constraint that drove it, Consequences names what was accepted by not adopting the pattern
- How to document architecture decisions — the general ADR practice; rejected pattern records are among the most underspecified ADR types in standard guidance, typically covered only as a note that "not building something is also a decision"; the rejected pattern deserves its own record type with a specific structure and revisitation condition
- WhyChose extractor — pattern rejection sessions are harder to find than dependency rejection sessions because the pattern has no canonical search term; the quarterly review pass is the most reliable extraction mechanism; sessions concluding with "we'd rather keep it explicit" or "not worth the complexity" are high-value targets