.config/opencode/skills/code-architecture-wrong-abstraction/SKILL.md
Guides when to abstract vs duplicate code. Use this skill when creating shared utilities, deciding between DRY/WET approaches, or refactoring existing abstractions.
npx skillsauth add klen/dotfiles code-architecture-wrong-abstractionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
Don't abstract until code appears in at least 3 places. This provides enough context to identify genuine patterns vs coincidental similarities.
// ✅ 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);
This is how wrong abstractions evolve:
// 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
The fastest way forward is back:
// 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
| 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 |
Facades wrap complex subsystems behind a simple interface. They're useful but often become wrong abstractions when overused.
// ❌ 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
};
// ✅ 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
// ✅ 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
| 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 |
If a junior must:
...the facade adds friction, not value.
Sometimes ctrl+f and manual updates across files is simpler than maintaining a leaky abstraction.
| 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 |
tools
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.
development
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.
development
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.
development
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.