approachability-audit

$npx mdskill add EpicenterHQ/epicenter/approachability-audit

Improves code readability for new TypeScript developers

  • Identifies hard-to-follow code patterns and unclear abstractions
  • Analyzes naming, type usage, and ownership boundaries in TypeScript
  • Focuses on first-read clarity and real domain boundaries
  • Provides targeted suggestions for simplifying code structure
SKILL.md
.github/skills/approachability-auditView on GitHub ↗
---
name: approachability-audit
description: Review code as a new TypeScript developer. Use when code feels indirect, clever, hard to follow, or needs a pass on abstractions, names, first-read clarity.
---

# Approachability Audit

Read the code like a smart but newly onboarded TypeScript developer.

Related skills: use `post-implementation-review` for the full second-read
ritual after implementation, `refactoring` for caller counting and inlining
mechanics, and `cohesive-clean-breaks` when a readability issue points to the
wrong public boundary.

The goal is not "make it shorter" or "make it more abstract." The goal is:

- make the first read cheaper
- make ownership boundaries obvious
- remove tricks that only exist for the type system
- keep real domain or validation boundaries intact

## What to Look For

- Names that imply a more important concept than the code actually represents
- Types that pretend to be runtime truths but are only inference shims
- Client-only hacks living in server packages, or server concerns leaking into browser/shared code
- Single-use helpers that hide simple control flow
- Tiny wrappers that add vocabulary but not clarity
- Multiple files for one concept when readers really need one place to look
- "Smart" generic helpers where direct code would be easier to trust
- Ad-hoc `as` assertions outside a clear parse or interop boundary
- JSDoc that restates code instead of explaining why the boundary exists

## What Not to "Fix"

- Real parse boundaries at JSON, file, or network edges
- Runtime validation that protects unsafe input
- Shared contracts that genuinely belong in one place
- Repetition that is cheaper than an extra abstraction

## Review Method

1. Start with the entrypoint a caller uses first.
2. Trace the minimum path needed to understand the behavior.
3. Count the hops:
   - if understanding one field or behavior requires jumping across multiple files, ask whether that indirection earns its keep
4. Mark each abstraction as one of:
   - `earns its keep`
   - `probably inlineable`
   - `wrong ownership boundary`
   - `misleading name`
   - `type-system workaround`
5. Prefer fixes that:
   - move code to the right owner
   - rename things honestly
   - collapse fake layers
   - replace cleverness with one explicit boundary

## Output Shape

When reporting findings, prioritize:

1. The one or two biggest readability or ownership smells
2. Why a new TypeScript developer would stumble there
3. The smallest fix that improves trust in the code

When editing code:

- keep commits surgical
- add or improve JSDoc on public boundaries
- do not add a new abstraction unless it reduces total cognitive load
- prefer explicit names like `*Bridge`, `*Contract`, `*Parser`, `*Factory`, `*State`

## Heuristics

### Good smell

```ts
export type SessionContract = { ... };
```

Portable type, honest name, obvious job.

### Bad smell

```ts
export type AppAuth = {
  options: { plugins: [...] }
};
```

If this is not the real auth type, do not name it like it is.

### Good workaround

```ts
export type CustomSessionClientBridge = { ... };
```

If a library forces a type trick, keep it local and name it as a bridge.

## Success Criteria

The code should leave a new teammate thinking:

- "I know which file owns this concept."
- "I know which type is a real contract and which one is just library glue."
- "I don't have to reverse-engineer the architecture from naming accidents."
More from EpicenterHQ/epicenter