Skip to content
aposd-simplifying-complexity logo

Skill: aposd-simplifying-complexity

aposd-simplifying-complexity

Transforms complex code by applying APOSD's pull-complexity-downward principle: resolves error hierarchies, collapses configuration, and moves caller-side logic into modules. Produces edited code, not just assessment.

ryanthedev/code-foundations0installs376stars

SKILL.md

Full skill instructions

Skill: aposd-simplifying-complexity

Error Reduction Hierarchy

The best way to deal with exceptions is to define errors out of existence.

Priority order: Define out → Mask → Aggregate → Crash (app-level only)


Pull Complexity Downward

Decision Procedure

Before adding complexity to an interface (new parameters, new exceptions, new caller responsibilities):

1. Is this complexity closely related to the module's existing functionality?
   NO  → Should it be pulled into a DIFFERENT module?
         YES → Identify correct module, pull there
         NO  → Leave in place (may be inherent to caller's domain)
   YES → Continue

2. Will pulling down simplify code elsewhere in the application?
   NO  → Do not pull down (no benefit)
   YES → Continue

3. Will pulling down simplify the module's interface?
   NO  → Do not pull down (risk of leakage)
   YES → Pull complexity down

All three conditions must be YES to pull down.

Critical Constraint: Pulling down UNRELATED complexity creates information leakage. If the complexity isn't intrinsic to the module's core abstraction, it doesn't belong there—find the right home or leave it with the caller.

Configuration Parameters

SituationWrong ApproachRight Approach
Uncertain what value to useExport parameterCompute automatically
Different contexts need different valuesExport parameterUse reasonable default, expose only for exceptions
Policy decision unclearLet user decideMake a decision and own it

Configuration parameters represent incomplete solutions. Every parameter pushes complexity to every user/​administrator. Prefer dynamic computation over static configuration.


Error Reduction Hierarchy

Apply in order of preference:

PriorityTechniqueHow It WorksExample
1Define outChange semantics so error is impossibleunset(x) = "ensure x doesn't exist" (not "delete existing x")
2MaskHandle at low level, hide from callersTCP retransmits lost packets internally
3AggregateSingle handler for multiple exceptionsOne catch block in dispatcher handles all NoSuchParameter
SpecialCrashPrint diagnostic and abort (app-level only)malloc failure in non-recoverable contexts

Note on "Crash": This is NOT level 4 of a hierarchy—it's a special case for truly unrecoverable errors in application code. Libraries should NEVER crash; they expose errors for callers to decide.

Error Reduction Decision Procedure

When facing an exception handling decision:

1. Can semantics be redefined to eliminate the error condition?
   YES → Define out of existence
   NO  → Continue

2. Can exception be handled at low level without exposing?
   YES → Mask
   NO  → Continue

3. Can multiple exceptions share the same handling?
   YES → Aggregate
   NO  → Continue

4. Is error rare, unrecoverable, and non-value-critical?
   YES → Just crash (app-level only)
   NO  → Must expose (exception information needed outside module)

When NOT to Apply Hierarchy

Exception CaseWhyWhat to Do Instead
Security-critical errorsAggregating auth errors loses security-relevant distinctionsKeep distinct types for audit/​logging
Retry-differentiated errorsCallers need different retry strategies per error typeExpose type info for retry decisions
Silent data loss riskDefine-out can mask user errors, complicate debuggingFail fast for essential data errors
Library codeCallers should decide crash policy, not libraryExpose errors; let app-level code crash

Validation Gates

TechniqueGate Question
Define outDoes anyone NEED to detect this error case?
MaskDoes the caller have ANY useful response to this error?
AggregateDo callers handle these errors identically?
CrashIs this (a) application-level code, (b) truly unrecoverable, AND (c) crash acceptable?

Define-Out Appropriateness Test

Before defining an error out of existence, verify it's an incidental error (safe) not an essential error (must fail fast):

QuestionIf YES →If NO →
Would this state occur in normal, correct operation?Safe to define outFail fast
Can the caller proceed meaningfully with the "defined out" state?SafeExpose error
Does the user/​system have another way to detect this condition if needed?SafeConsider exposing

Obviousness Techniques

Three Ways to Make Code Obvious

TechniqueHowWhen to Use
Reduce information neededAbstraction, eliminate special casesDesign-level changes
Leverage reader knowledgeFollow conventions, meet expectationsIncremental improvements
Present explicitlyGood names, strategic commentsWhen other techniques insufficient

Obviousness Test

If a code reviewer says your code is not obvious, it is not obvious—regardless of how clear it seems to you.

Common Obviousness Problems

ProblemWhy NonobviousFix
Generic containers (Pair, Tuple)getKey() obscures meaningDefine specific class with named fields
Event-driven handlersControl flow hiddenDocument invocation context
Type mismatchesList declared, ArrayList allocatedMatch declaration to allocation
Violated expectationsCode doesn't do what reader assumesDocument or refactor to meet expectations

Output Gate

Before presenting simplified code, produce a technique analysis table — this is the evidence that the hierarchy was applied:

| Error Condition | Technique | Gate Check | Reasoning |
|-----------------|-----------|------------|-----------|
| [each error]    | [1-4]     | [PASS/​FAIL]| [why]     |

Proceed only when the following are true:

  • Technique analysis table present for each error condition
  • Complexity moved to fewer places (not just relocated)
  • Interfaces are simpler than before
  • Callers do less work than before
  • Error handling is consolidated or eliminated
  • Reader needs less context to understand

If a criterion legitimately cannot be satisfied, present the code with the failed criterion and reason stated.


Principle Conflict Resolution

ConflictResolution Heuristic
Define Out vs Fail FastDefine out for incidental errors. Fail fast for essential errors.
Mask vs Explicit HandlingMask when caller has no useful response. Expose when caller's response differs.
Aggregate vs Specific MessagesAggregate the HANDLING, preserve specificity in the MESSAGE.
Pull Down vs Single ResponsibilityOnly pull down complexity RELATED to module's core purpose.
Obviousness vs BrevityWhen define-out creates non-obvious behavior, add explanatory comment.
Simplify vs PerformancePrefer simplicity unless profiling proves performance-critical.

Red Flags

Red FlagSymptomTransformation
Scattered exceptionsSame error handled in many placesAggregate to single handler
Configuration explosionMany parameters exportedCompute automatically, provide defaults
Caller doing module's workLogic outside that belongs insidePull complexity down
Over-defensive codeChecks for impossible conditionsDefine errors out
Generic containersPair<X,Y> obscures meaningCreate named structure
Comment-dependent understandingCode unreadable without commentsRefactor for obviousness
Masked error without observabilityApplying Mask or Define-out but no logging, metrics, or alternate signal when the error actually occursEvery masked error needs an observability escape hatch (log, metric, health check) so operators can detect when masking hides a real problem

Detailed checklists: Read(${CLAUDE_SKILL_DIR}/​checklists.md)


Chain

AfterNext
Simplification doneRead(${CLAUDE_PLUGIN_ROOT}/​skills/​aposd-verifying-correctness/​SKILL.md) — verify interface simplified