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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "code-architecture-wrong-abstraction" with this command: npx skills add flpbalada/my-opencode-config/flpbalada-my-opencode-config-code-architecture-wrong-abstraction

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.

// ✅ 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:

// 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
// 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

BenefitHidden Cost
Code reuseAccidental coupling between unrelated modules
Single source of truthLayers of indirection obscure bugs
DRY complianceOrganizational 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

// ❌ 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

// ✅ 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

// ✅ 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 WhenSkip Facade When
Hiding complex logic (APIs, state)Wrapping simple HTML elements
Enforcing design constraintsOne-off styling needs
Team needs consistent patternsJuniors need to learn the underlying tech
Behavior is stable and well-definedRequirements 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

ApproachMeaningWhen to Use
DRYDon't Repeat YourselfAfter patterns stabilize
WETWrite Everything TwiceDefault starting point
AHAAvoid Hasty AbstractionsGuiding principle

References

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

trust-psychology

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

social-proof-psychology

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

five-whys

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

cognitive-fluency-psychology

No summary provided by upstream source.

Repository SourceNeeds Review