agentskills.codes
HI

Evaluate code (especially LLM-generated) for structural simplicity using Rich Hickey's "Simple Made Easy" framework. Use this skill whenever reviewing a PR, diff, or code snippet for accidental complexity — particularly when the code was generated by an AI coding assistant and line-by-line review is

Install

mkdir -p .claude/skills/hickey && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14540" && unzip -o skill.zip -d .claude/skills/hickey && rm skill.zip

Installs to .claude/skills/hickey

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Evaluate code (especially LLM-generated) for structural simplicity using Rich Hickey's "Simple Made Easy" framework. Use this skill whenever reviewing a PR, diff, or code snippet for accidental complexity — particularly when the code was generated by an AI coding assistant and line-by-line review isn't feasible. Also use when the user asks about complecting, simplicity vs. easiness, structural coupling, or concept deduplication. Trigger on phrases like "is this simple", "does this complect", "review for complexity", "structural analysis", or any reference to Hickey, Simple Made Easy, or grey-box review.
610 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)

About this skill

Hickey: Structural Simplicity Evaluation

Evaluate code for structural simplicity using Rich Hickey's "Simple Made Easy" framework. This skill is designed for the world where AI generates code faster than humans can read it — where functional tests pass but accidental complexity accumulates silently.

The core premise: tests tell you code works; they tell you nothing about whether it's simple. Hickey: "What's true of every bug found in the field? It passed the type checker... it passed all the tests." Complected code can be perfectly correct today. The damage shows up when you try to change it, reason about it, or extend it.

Source: Full talk transcript (also in transcript.md relative to this skill)

Key Definitions

Simple (sim-plex, "one fold/braid"): One concern, one role, one concept. Simplicity is objective — count the interleaved concerns. Hickey: "What matters for simplicity is that there is no interleaving, not that there's only one thing."

Easy (adjacens, "nearby"): Familiar, at hand, within our skillset. Easy is relative. Hickey: "If you want everything to be familiar, you will never learn anything new."

Complect (com-plectere, "to braid together"): Interleave independent concerns so they cannot be reasoned about in isolation. Hickey: "Every time I think I pull out a new part of the software I need to comprehend, and it's attached to another thing, I had to pull that other thing into my mind because I can't think about the one without the other."

Compose (com-ponere, "to place together"): Combine independent things side by side, preserving isolation. Hickey: "I'd rather have more things hanging nice, straight down, not twisted together, than just a couple of things tied in a knot."

Scope of Review

The trigger — "review this for X", "extract Y", "look at file Z", a /do diff with N touched files — is a starting point, not a frame. The structural questions in this skill (concept multiplication, fragmentation, complecting) are most legible at module boundaries; reviewing only the lines the user (or upstream issue) pointed at is how recurring patterns in the same file get missed.

Default to whole-module scope. When the trigger lives inside a single file or component, read the whole file — not just the cited region. When invoked on a multi-file diff, each touched file is in scope, and cross-file structural patterns (concept multiplication across modules, fragmentation that spans files) are in scope too. Adjacent files in the same directory are fair game when the trigger's pattern recurs there — concept multiplication often lives across siblings.

Don't let the user's framing define the scope. A trigger that says "extract <ValueInputMode>" implies a UI extraction; if the surrounding code shows the same fragmentation pattern recurring, name that — even when the implied fix is elsewhere (e.g., a discriminated data-model change rather than a sub-component split). The reviewer's job is to surface what the evidence on disk says, not to confirm the trigger's framing.

Push back when the evidence contradicts the trigger. If the prompt narrows the question to one symptom but the file shows the symptom is one instance of a broader structural issue, the broader finding is the headline, not a footnote. "Issue #N described an extraction; the actual leverage is the data model" is a valid first finding, not an out-of-scope tangent. Anchoring on the trigger's framing is itself a Layer 2 silence — a finding the review never let form.

The Evaluation Process

Work through these layers in order. Every finding must survive /fact-check — after completing all layers, invoke the fact-check skill on your own evaluation to catch wishful justifications and bogus dismissals.

Layer 1: Identify the Concerns

Name the independent concerns the code addresses. Write them out explicitly. If you can't cleanly name distinct concerns, that is itself a finding.

Layer 2: Fragmentation Check

Hickey's "don't complect" has a dual the rest of this skill doesn't cover: don't fragment what belongs together. When one domain concept is split across multiple fields, state locations, signals, modules, or call sites, and their coherence depends on an unenforced rule, you have the same structural bug as complecting — you just arrived at it from the opposite direction. The fix for complecting is separation. The fix for fragmentation is reunification at whatever layer the one thing naturally lives: one type, one signal, one module, one function, one file.

For every group of related fields, state locations, or entities in the changed code, ask: does the domain model this as one thing?

If yes, is the code representing it as one thing, or has it been shattered into parts whose coherence depends on an unenforced rule? The rule can live anywhere — type shape, code convention, doc comment, "we remember to update both", runtime ordering, a reactive pipeline that rebuilds the unity at read time.

  1. Enumerate invariants. Write out every rule coupling the parts, in plain language: "if A then B", "all Xs agree on Y", "when kind=X then field F is present", "at most one of A/B/C is set", "every update of P must update Q".
  2. Check consumption sites, not just definitions. Fragmentation is most visible at the reader. If every consumer projects the same value out of a per-entity structure, the per-entity part is lying — the projection is the fingerprint. If multiple modules derive the same value from different sources, the derivation is the fingerprint.
  3. Watch for reconciliation machinery. A memo that lifts one value out of a collection; a callback that writes back into the collection; an effect that copies one entity's state onto others; a config loader that cross-validates two files; a convention comment that says "keep X in sync with Y". All are the shape of the state is fragmented and we're rebuilding the unity somewhere downstream. The machinery is the bug, not the fix.
  4. Collapse at the natural layer. Pick the representation layer where the one thing naturally lives. Sometimes that's a discriminated union in a type; sometimes it's a module-level signal; sometimes it's moving two files into one. The layer is wherever the unity stops needing a rule to hold.
  5. Silence is the bug. A review that skips this layer because "no invariants came to mind" is the same review that misses the fragmentation. When the review genuinely finds nothing, writing "no invariants found" explicitly is the required cheap output. The enumeration is what forces the check, not the outcome.

Layer 3: Check for Concept Multiplication

Hickey: "Be particularly careful not to be fooled by code organization. There are tons of libraries that look — oh, look, there's different classes; there's separate classes. They call each other in these nice ways."

For each new abstraction (component, module, signal, type) the code introduces:

  1. Name what it represents at the domain level, not the implementation level.
  2. Search for existing abstractions serving the same domain concept. If one exists, extending it is the default — not creating a parallel one.
  3. "Mirror existing pattern" is an easiness judgment, not a simplicity judgment. Creating ComponentB because ComponentA exists and looks similar adds a concept. Extending ComponentA keeps concept count flat.

Two abstractions serving one user-level concern = accidental concept multiplication, even if each is internally clean. The structural layers below won't catch this — they check within abstractions, not across them.

Concept Multiplication vs. Fragmentation: these are close cousins but distinct bugs. Concept Multiplication is about duplicated wholes (two classes for one domain concept → delete one). Fragmentation (Layer 2) is about split wholes (one domain concept shattered across multiple locations → collapse to one). A single finding can trigger both layers from different angles; that's redundancy, not muddling.

Layer 4: Check the Structural Pattern Catalog

Scan for known structural patterns plus any additional patterns the project has declared. Before evaluating, read .agency/hickey.md if it exists — its content is project-specific (inline patterns, or a pointer to another file). Treat any patterns found there as additions to the catalog below. The catalog has two halves: complecting (things braided together that should be separate) and fragmentation (things split apart that should be one). Both directions are "interleaved vs. not-interleaved" — Hickey's principle is bidirectional.

Complecting patterns (things-that-should-be-separate braided together)

ConstructWhat it complectsSimpler alternative
Mutable stateValue + time + identityImmutable values, controlled state containers
ObjectsState + identity + value + namespacePlain functions + data + namespaces
MethodsFunction + state; function + namespaceFree functions, interfaces
InheritanceTypes with typesComposition, interfaces, traits
Switch/case on typeWho + whatDynamic dispatch, visitor pattern
Mutable variablesValue + timeconst/final/let bindings, immutable data
Imperative loopsWhat + how + whenmap/filter/reduce, declarative transforms
ActorsWhat + whoQueues + stateless handlers
ORMObject identity + relational model + queryPlain data + declarative queries
Conditionals scattered across codeOne decision braided across many sitesRules, declarative policies, lookup tables
Callbacks/closures over mutable stateControl flow + state + timeStreams, queues, immutable values
Hand-rolled utility (tokenizer, parser, walker, normalizer, state machine, date/semver/URL helper, CLI arg parser) whe

Content truncated.

Search skills

Search the agent skills registry