--- name: code-architecture-wrong-abstraction description: Guides when to abstract vs duplicate code. Use this skill when creating shared utilities, deciding between DRY/WET approaches, or refactoring existing abstractions. --- # 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 Hello // What if you need or ? // Now you must extend the facade first: Hello // Added prop Hello // 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 // Hides: localization, calendar rendering, keyboard nav, accessibility // ✅ Good: Facade enforces design system constraints // Ensures consistent styling, no arbitrary colors ``` ### When to Skip the Facade ```tsx // ✅ Sometimes native HTML is clearer Fine print Highlighted text // vs forcing everything through a facade: ... // ❌ 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)