code-architecture-wrong-abstraction
Guides when to abstract vs duplicate code. Use this skill when creating shared utilities, deciding between DRY/WET approaches, or refactoring existing abstractions.
Best use case
code-architecture-wrong-abstraction is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Guides when to abstract vs duplicate code. Use this skill when creating shared utilities, deciding between DRY/WET approaches, or refactoring existing abstractions.
Teams using code-architecture-wrong-abstraction should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/code-architecture-wrong-abstraction/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How code-architecture-wrong-abstraction Compares
| Feature / Agent | code-architecture-wrong-abstraction | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Guides when to abstract vs duplicate code. Use this skill when creating shared utilities, deciding between DRY/WET approaches, or refactoring existing abstractions.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
AI Agents for Coding
Browse AI agent skills for coding, debugging, testing, refactoring, code review, and developer workflows across Claude, Cursor, and Codex.
Best AI Skills for Claude
Explore the best AI skills for Claude and Claude Code across coding, research, workflow automation, documentation, and agent operations.
Cursor vs Codex for AI Workflows
Compare Cursor and Codex for AI coding workflows, repository assistance, debugging, refactoring, and reusable developer skills.
SKILL.md Source
# Code Architecture: Avoiding Wrong Abstractions
## Core Principle
**Prefer duplication over the wrong abstraction. Wait for patterns to emerge before abstracting.**
Premature abstraction creates confusing, hard-to-maintain code. Duplication is far cheaper to fix than unwinding a wrong abstraction.
## The Rule of Three
Don't abstract until code appears in **at least 3 places**. This provides enough context to identify genuine patterns vs coincidental similarities.
```jsx
// ✅ Correct: Wait for the pattern to emerge
// First occurrence - just write it
const userTotal = items.reduce((sum, item) => sum + item.price, 0);
// Second occurrence - still duplicate
const cartTotal = products.reduce((sum, p) => sum + p.price, 0);
// Third occurrence - NOW consider abstraction
const calculateTotal = (items, priceKey = 'price') =>
items.reduce((sum, item) => sum + item[priceKey], 0);
```
## When to Abstract
### ✅ Abstract When
- Same code appears in **3+ places**
- Pattern has **stabilized** (requirements are clear)
- Abstraction **simplifies** understanding
- Use cases share **identical behavior**, not just similar structure
### ❌ Don't Abstract When
- Code only appears in 1-2 places
- Requirements are still evolving
- Use cases need **different behaviors** (even if structure looks similar)
- Abstraction would require parameters/conditionals for variations
## The Wrong Abstraction Pattern
This is how wrong abstractions evolve:
```jsx
// 1️⃣ Developer A spots duplication and extracts it
function processData(data) {
return data.map(transform).filter(validate);
}
// 2️⃣ New requirement is "almost" compatible
function processData(data, options = {}) {
let result = data.map(options.customTransform || transform);
if (options.skipValidation) return result;
return result.filter(options.customValidate || validate);
}
// 3️⃣ More variations pile up...
function processData(data, options = {}) {
let result = data;
if (options.preProcess) result = options.preProcess(result);
result = result.map(options.customTransform || transform);
if (!options.skipValidation) {
result = result.filter(options.customValidate || validate);
}
if (options.postProcess) result = options.postProcess(result);
if (options.sort) result = result.sort(options.sortFn);
return options.limit ? result.slice(0, options.limit) : result;
}
// ❌ Now it's incomprehensible spaghetti
```
## How to Fix Wrong Abstractions
The fastest way forward is **back**:
1. **Inline** the abstraction back into each caller
2. **Delete** the portions each caller doesn't need
3. **Accept** temporary duplication for clarity
4. **Re-extract** proper abstractions based on current understanding
```jsx
// Before: One bloated function trying to do everything
processData(users, { customTransform: formatUser, skipValidation: true });
processData(orders, { sort: true, sortFn: byDate, limit: 10 });
// After: Inline and simplify each use case
const formattedUsers = users.map(formatUser);
const recentOrders = orders.sort(byDate).slice(0, 10);
// Later: If true patterns emerge, abstract properly
```
## Hidden Costs of Abstraction
| Benefit | Hidden Cost |
|---------|-------------|
| Code reuse | **Accidental coupling** between unrelated modules |
| Single source of truth | **Layers of indirection** obscure bugs |
| DRY compliance | **Organizational inertia** makes refactoring painful |
## Facade Pattern: When It Becomes a Wrong Abstraction
Facades wrap complex subsystems behind a simple interface. They're useful but often become wrong abstractions when overused.
### The Typography Component Trap
```tsx
// ❌ Facade that becomes limiting
<Typography variant="body" size="sm">Hello</Typography>
// What if you need <small> or <mark>?
// Now you must extend the facade first:
<Typography variant="body" size="sm" as="small">Hello</Typography> // Added prop
<Typography variant="body" size="sm" as="mark">Hello</Typography> // Another prop
// ❌ Facade keeps growing with every edge case
type TypographyProps = {
variant: 'h1' | 'h2' | 'body' | 'caption';
size: 'sm' | 'md' | 'lg';
as?: 'p' | 'span' | 'small' | 'mark' | 'strong' | 'em'; // Growing...
weight?: 'normal' | 'bold';
color?: 'primary' | 'secondary' | 'muted';
// ... more props for every HTML text feature
};
```
### When Facade Works
```tsx
// ✅ Good: Facade encapsulates complex logic
<DatePicker
value={date}
onChange={setDate}
minDate={today}
/>
// Hides: localization, calendar rendering, keyboard nav, accessibility
// ✅ Good: Facade enforces design system constraints
<Button variant="primary" size="md">Submit</Button>
// Ensures consistent styling, no arbitrary colors
```
### When to Skip the Facade
```tsx
// ✅ Sometimes native HTML is clearer
<small className="text-muted">Fine print</small>
<mark>Highlighted text</mark>
// vs forcing everything through a facade:
<Typography variant="small" highlight>...</Typography> // ❌ Overengineered
```
### Facade Trade-offs
| Use Facade When | Skip Facade When |
|-----------------|------------------|
| Hiding **complex logic** (APIs, state) | Wrapping **simple HTML elements** |
| Enforcing **design constraints** | One-off styling needs |
| Team needs **consistent patterns** | Juniors need to learn the underlying tech |
| Behavior is **stable and well-defined** | Requirements are still evolving |
### The Junior Developer Test
If a junior must:
1. Learn the facade API
2. Then learn the underlying technology anyway
3. Then extend the facade for edge cases
...the facade adds friction, not value. Sometimes `ctrl+f` and manual updates across files is simpler than maintaining a leaky abstraction.
## Quick Reference
### DO
- Wait for **3+ occurrences** before abstracting
- Let patterns **emerge naturally**
- Optimize for **changeability**, not DRY compliance
- Test **concrete features**, not abstractions
- **Inline bad abstractions** and start fresh
### DON'T
- Abstract based on **structural similarity** alone
- Add parameters/conditionals to **force fit** new use cases
- Preserve abstractions due to **sunk cost fallacy**
- Fear **temporary duplication**
## Key Philosophies
| Approach | Meaning | When to Use |
|----------|---------|-------------|
| **DRY** | Don't Repeat Yourself | After patterns stabilize |
| **WET** | Write Everything Twice | Default starting point |
| **AHA** | Avoid Hasty Abstractions | Guiding principle |
## References
- [The Wrong Abstraction - Sandi Metz](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction)
- [The Wet Codebase - Dan Abramov](https://www.deconstructconf.com/2019/dan-abramov-the-wet-codebase)
- [AHA Programming - Kent C. Dodds](https://kentcdodds.com/blog/aha-programming)Related Skills
code-architecture-tailwind-v4-best-practices
Guides Tailwind CSS v4 patterns for buttons and components. Use this skill when creating components with variants, choosing between CVA/tailwind-variants, or configuring Tailwind v4's CSS-first approach.
what-not-to-do-as-product-manager
Anti-patterns and mistakes to avoid as a product manager. Use when evaluating leadership behaviors, improving team dynamics, reflecting on management practices, or onboarding new product managers.
visual-cues-cta-psychology
Design effective CTAs using visual attention and gaze psychology principles. Use when designing landing pages, button hierarchies, conversion elements, or optimizing user attention flow through interfaces.
vercel-sandbox
Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include "Vercel Sandbox browser", "microVM Chrome", "agent-browser in sandbox", "browser automation on Vercel", or any task requiring Chrome in a Vercel Sandbox.
value-realization
Analyze if end users discover clear value. Use when evaluating product concepts, analyzing adoption, or uncertain about direction.
user-story-fundamentals
Capture requirements from user perspective with structured user stories. Use when writing backlog items, defining acceptance criteria, prioritizing features, or communicating requirements between product and development.
typescript-satisfies-operator
Guides proper usage of TypeScript's satisfies operator vs type annotations. Use this skill when deciding between type annotations (colon) and satisfies, validating object shapes while preserving literal types, or troubleshooting type inference issues.
typescript-interface-vs-type
Guides when to use interface vs type in TypeScript. Use this skill when defining object types, extending types, or choosing between interface and type aliases.
typescript-best-practices
Guides TypeScript best practices for type safety, code organization, and maintainability. Use this skill when configuring TypeScript projects, deciding on typing strategies, writing async code, or reviewing TypeScript code quality.
typescript-advanced-types
Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.
trust-psychology
Build trust signals that reduce perceived risk and enable user action. Use when designing landing pages, checkout flows, onboarding experiences, or any conversion point where user hesitation is a barrier.
theme-epic-story
Structure product work hierarchically using themes, epics, and stories. Use when organizing backlogs, planning releases, communicating with stakeholders, or breaking down large initiatives into manageable work.