Files
skills/skills/solid/references/complexity.md
T
mathiasandClaude Opus 5 56a947b2a0 refactor(plugin)!: ship as a Claude Code plugin, one dir link per harness
Skills move to skills/<name>/SKILL.md and the repo gains both manifests, so
it is now a marketplace hosting exactly one plugin whose root is the repo
root (source: "."). Claude Code installs it from git; no symlinks involved.

Why: install.sh wired the library into every consuming repo as an ABSOLUTE
symlink to $CHECKOUT_DIR. The checkout moved three times, most recently when
local-dev@1966d5a dropped the embedded .skills/ copy, and nothing migrated
the links — 243 dangling links across 12 repos, hidden from git by the
**/.claude/skills/ ignore rule. Worse, ~/dev spans two path roots
(/Users/mathias on the iCloud-shared Macs, /home/mathias on koala), so an
absolute link stored there is broken on one side by construction. Re-running
the installer cannot fix that; only removing the mechanism can.

install.sh keeps the harnesses with no plugin system and drops from 42 links
per harness to one directory link, via the convergent ~/.agents/skills
convention that Pi and OpenCode both read:

  ~/.agents/skills          -> $CHECKOUT_DIR/skills
  ~/.config/crush/skills    -> ~/.agents/skills
  ~/.config/opencode/skills -> ~/.agents/skills

wire_claude_repo is deleted outright rather than gated behind a flag — on a
mixed-root fleet it cannot be made correct. Antigravity and Vibe targets go
too; both unused. CHECKOUT_DIR keeps its host-local default deliberately:
pointing it into ~/dev would put a git working tree inside iCloud with two
concurrent writers.

Taskfile drops five near-identical wiring loops (they had already drifted)
and delegates to install.sh. `task check` gains frontmatter validation, a
stray-root-entry gate, and manifest/tag version parity — the loose
secret-hygiene.md sat unloadable for months because nothing checked layout.

Verified: claude plugin validate . passes clean; a probe skill loaded as
skills:zzprobe through --plugin-dir; task check green at 42 skills.

Refs: #23
Bump-Type: minor

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-23 14:29:51 +02:00

6.3 KiB

Managing Complexity

The Two Types of Complexity

Essential Complexity

Inherent to the problem domain. Cannot be removed, only managed.

  • Business rules
  • Domain logic
  • User requirements

Accidental Complexity

Introduced by our solutions. CAN and SHOULD be minimized.

  • Poor abstractions
  • Unnecessary indirection
  • Framework ceremony
  • Technical debt

Goal: Minimize accidental complexity while clearly expressing essential complexity.


Detecting Complexity

1. Change Amplification

Small changes require touching many files.

Symptom: "To add this field, I need to update 15 files."

Cause: Scattered responsibilities, poor abstraction boundaries.

2. Cognitive Load

Code is hard to understand, requires holding too much in memory.

Symptom: "I need to understand 10 other classes to understand this one."

Cause: Tight coupling, hidden dependencies, unclear naming.

3. Unknown Unknowns

Behavior is surprising, side effects are hidden.

Symptom: "I changed this, and something completely unrelated broke."

Cause: Global state, hidden dependencies, implicit contracts.


The XP Values for Fighting Complexity

From Extreme Programming:

1. Communication

Code should communicate clearly. Names, structure, tests all contribute.

2. Simplicity

Do the simplest thing that could possibly work.

3. Feedback

Fast feedback loops catch complexity early. TDD, CI, code review.

4. Courage

Refactor aggressively. Don't let complexity accumulate.

5. Respect

Respect future readers (including yourself). Write for humans first.


KISS - Keep It Simple, Silly

"The simplest solution that works is usually the best."

How to Apply:

  1. Start with the obvious solution
  2. Only add complexity when REQUIRED
  3. Prefer boring, well-understood approaches
  4. Question every abstraction
// Over-engineered
class UserServiceFactoryProvider {
  private static instance: UserServiceFactoryProvider;

  static getInstance(): UserServiceFactoryProvider { ... }
  createFactory(): UserServiceFactory { ... }
}

// KISS
class UserService {
  getUser(id: string): User { ... }
}

YAGNI - You Aren't Gonna Need It

"Don't build features until they're actually needed."

Warning Signs:

  • "We might need this later"
  • "It would be nice to have"
  • "Just in case"
  • "For future extensibility"

The Cost of YAGNI Violations:

  1. Development time - Building unused features
  2. Maintenance burden - Code that must be maintained
  3. Cognitive load - More to understand
  4. Wrong abstraction - Guessing future needs incorrectly
// YAGNI violation: Building for hypothetical needs
class User {
  // "We might need these someday"
  middleName?: string;
  secondaryEmail?: string;
  faxNumber?: string;
  linkedinProfile?: string;
  twitterHandle?: string;
}

// YAGNI: Only what's needed NOW
class User {
  name: string;
  email: Email;
}

DRY - Don't Repeat Yourself (with The Rule of Three)

"Every piece of knowledge should have a single, unambiguous representation."

BUT: The Rule of Three

Don't extract duplication until you see it THREE times.

Why? The wrong abstraction is worse than duplication.

Duplication #1 → Leave it
Duplication #2 → Note it, leave it
Duplication #3 → NOW extract it

Example:

// First time - leave it
function processUserOrder(order) {
  validate(order);
  calculateTax(order);
  save(order);
}

// Second time - note the similarity, but leave it
function processGuestOrder(order) {
  validate(order);
  calculateTax(order);
  save(order);
  sendGuestEmail(order);
}

// Third time - NOW extract
function processCorporateOrder(order) {
  validate(order);
  calculateTax(order);
  save(order);
  applyCorporateDiscount(order);
}

// After three, extract the common parts
function processOrder(order: Order, postProcessing: (o: Order) => void) {
  validate(order);
  calculateTax(order);
  save(order);
  postProcessing(order);
}

Separation of Concerns

"Each module should address a single concern."

Concerns to Separate:

  • Business logic vs Infrastructure
  • What (policy) vs How (mechanism)
  • Input vs Processing vs Output
  • Data vs Behavior

Example:

// BAD: Mixed concerns
class OrderProcessor {
  process(order: Order) {
    // Validation
    if (!order.items.length) throw new Error('Empty');

    // Business logic
    let total = 0;
    for (const item of order.items) {
      total += item.price * item.quantity;
    }

    // Persistence
    const db = new Database();
    db.query(`INSERT INTO orders...`);

    // Notification
    const email = new EmailClient();
    email.send(order.customer.email, 'Order confirmed');
  }
}

// GOOD: Separated concerns
class OrderProcessor {
  constructor(
    private validator: OrderValidator,
    private calculator: OrderCalculator,
    private repository: OrderRepository,
    private notifier: OrderNotifier
  ) {}

  process(order: Order): ProcessResult {
    this.validator.validate(order);
    const total = this.calculator.calculateTotal(order);
    const savedOrder = this.repository.save(order);
    this.notifier.notifyConfirmation(savedOrder);
    return ProcessResult.success(savedOrder);
  }
}

Managing Technical Debt

Types of Technical Debt:

  1. Deliberate - Conscious trade-off for speed
  2. Accidental - Mistakes, lack of knowledge
  3. Bit rot - Code degrades over time

The Boy Scout Rule:

"Leave the code better than you found it."

Every time you touch code:

  • Improve one small thing
  • Fix one naming issue
  • Extract one method
  • Add one missing test

When to Pay Down Debt:

  • When it's in your path (you're already there)
  • When it's blocking new features
  • When it's causing bugs
  • During dedicated refactoring time

When NOT to Refactor:

  • Code that works and won't change
  • Code being replaced soon
  • When you don't have tests

The Four Elements of Simple Design

In priority order (from XP):

  1. Runs all the tests

    • If it doesn't work, nothing else matters
  2. Expresses intent

    • Clear names, obvious structure
    • Code tells the story
  3. No duplication

    • DRY (but Rule of Three)
    • Single source of truth
  4. Minimal

    • Fewest classes and methods possible
    • Remove anything unnecessary

If these four are true, the design is simple enough.