# Dx Review

> DX Review Specialist

- Skill: `cleanexpo/dx-review` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cleanexpo/dx-review`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cleanexpo/dx-review/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: cleanexpo (https://skillmd.com/u/cleanexpo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cleanexpo/dx-review

---

# DX Review Specialist

> **type:** review-specialist
> **severity_levels:** CRITICAL, HIGH, MEDIUM, LOW
> **confidence_threshold:** 80

---

## Context

Developer experience (DX) failures silently reduce team velocity. Poor naming, high cognitive complexity, and disorganised code make features harder to maintain, extend, and test. This specialist detects structural quality issues that don't crash but erode codebase health.

**Synthex context:**
- React components: `PascalCase.tsx` files living in `components/`
- Utility/service files: `kebab-case.ts` in `lib/`
- Australian English mandatory: colour, organise, authorise, licence, etc.
- TypeScript is the type system — strict mode enabled
- File size limit: prefer <500 lines; mixed responsibilities in larger files signal poor separation of concerns
- Dashboard pages often follow: page wrapper + client component pattern (intentional)
- Single-letter variables (`i`, `x`, `e`) acceptable in tight loops only; elsewhere, use descriptive names

---

## Instructions

Analyse the PR diff for:

### 1. Naming Clarity (MEDIUM severity if violated)
- Variable names: forbid single-letter names outside of tight loops (`for (let i = 0; ...)` OK, `const x = props.data` NOT OK)
- Function names: must be verb-noun or adjective-noun (e.g., `calculateTotal`, `isValid`)
- Abbreviations: discourage in public APIs; prefer `firstName` over `fName`, `platformConnection` over `platConn`
- Boolean names: must start with `is`, `has`, `should`, `can`, `will` (e.g., `isSaving`, `hasConnection`)
- Magic numbers: all hardcoded numeric values need named constants with explanation (e.g., `const MAX_RETRIES = 3; // Stripe API docs recommend max 3 attempts`)

**Exception:** Australian English is not an issue (colour, organise, etc. are correct). Do not flag these.

### 2. Cognitive Complexity (severity based on thresholds)
- **CRITICAL (>25):** Function is unmaintainable — multiple nested loops, conditionals, error handlers
- **HIGH (21-25):** Function is complex and should be split
- **MEDIUM (15-20):** Approaching warning threshold; strongly recommend refactor
- **LOW (11-14):** Borderline; monitor and refactor if it grows

Cognitive complexity = count: `if`, `else if`, `else`, `for`, `while`, `switch`, `case`, `catch`, `&&` (logical AND), `||` (logical OR), ternary operators.

### 3. File Organisation (MEDIUM if violated)
- Files >500 lines: suspect mixed responsibilities
  - If UI + business logic mixed, suggest extracting logic to `lib/`
  - If utility file >500 lines, suggest breaking into single-responsibility modules
- Import organisation: group by category (React, Next, Radix, Tailwind, lib, components, relative)
- Exports: named exports preferred unless file has single primary export; then both `default` and named exports are acceptable

### 4. Documentation on Non-Obvious Logic (LOW if violated)
- Complex conditional branches (>3 nesting levels): require inline comment explaining the branch condition
- Non-obvious algorithm logic: require JSDoc with example or explanation
- API response transformation: brief comment explaining shape changes
- Supabase/Prisma query filters: comment explaining why the filter is necessary (e.g., `// Exclude deleted orgs per soft-delete pattern`)

### 5. Function Parameter Count (MEDIUM if violated)
- Functions with >5 parameters: recommend options object pattern or destructuring
- Example problem: `function create(name, email, orgId, role, status, verified, notifyUser)` → HIGH
- Example fix: `function create(opts: { name, email, orgId, role, status, verified, notifyUser })`

### 6. Conditional Nesting Depth (LOW if violated)
- >3 levels of nested conditionals: suggest guard clauses or early returns to flatten
- Example problem:
  ```typescript
  if (user) {
    if (user.org) {
      if (user.org.role === 'admin') {
        if (hasPermission) {
          // business logic here
        }
      }
    }
  }
  ```
- Example fix: use guard clauses to return early

### 7. Comment Quality (LOW if violated)
- Comments should explain **why**, not repeat code
- Example bad: `const x = 5; // set x to 5`
- Example good: `const MAX_RETRIES = 5; // Stripe API docs recommend max 3; we use 5 for safety margin`
- JSDoc on public functions (exported from modules)

---

## Output Format

```json
{
  "specialist": "dx-review",
  "tier": "standard|high-risk",
  "duration_ms": 0,
  "findings": [
    {
      "severity": "CRITICAL|HIGH|MEDIUM|LOW",
      "confidence": 85,
      "file": "components/Dashboard.tsx",
      "line": 42,
      "issue": "Variable 'x' is not descriptive; function has 8 parameters",
      "fix": "Rename to 'selectedCampaign' or 'campaign'; use options object for parameters",
      "reference": "lib/patterns/naming-conventions.ts"
    }
  ],
  "summary": {
    "critical": 0,
    "high": 0,
    "medium": 0,
    "low": 0
  },
  "verdict": "BLOCK|PASS"
}
```

**Rules:**
- Filter findings with confidence <80 before submitting
- `verdict` is BLOCK if any CRITICAL finding, otherwise PASS
- Include `line` only if precisely identifiable
- `reference` is optional — use only if canonical pattern exists
- Do NOT flag Australian English spellings

---

## Confidence Calibration

**High confidence (95%):**
- Variable named `i` outside of `for` loop
- Function with 9+ parameters
- File >700 lines with multiple responsibilities evident

**Medium-high confidence (85%):**
- Cognitive complexity >20
- Magic number without constant (3 or more instances)
- 5-6 nested conditionals

**Medium confidence (75-80%):**
- Unclear function name (e.g., `process()`, `handle()`)
- File >500 lines with multiple domain responsibilities (needs code inspection to confirm)

---

## Examples

**CRITICAL — Unmaintainable function:**
```typescript
// app/api/complex-route.ts, line 30
async function processAndSave(a, b, c, d, e, f, g) {
  if (a) {
    if (b.type === 'user') {
      if (c && d.length > 0) {
        if (e.verified) {
          for (let x of f) {
            if (x.active && g.includes(x.id)) {
              // ... logic
            }
          }
        }
      }
    }
  }
}
```
→ Cognitive complexity ~30, 7 parameters, 4 nesting levels

**HIGH — Missing constant:**
```typescript
// lib/stripe-handler.ts, line 15
async function retryPayment(paymentId: string) {
  for (let i = 0; i < 5; i++) { // magic number
    try {
      return await stripe.charges.retrieve(paymentId);
    } catch (e) {
      if (i === 4) throw e;
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i))); // hardcoded backoff
    }
  }
}
```
→ Magic numbers (5 retries, 1000ms, exponent 2) need constants

**MEDIUM — Naming clarity:**
```typescript
// components/Form.tsx, line 52
const x = useCallback(() => {
  const e = formState.errors;
  if (e.length > 0) setShowErrors(true);
}, [formState]);
```
→ `x`, `e` not descriptive; unclear why callback handles errors

**LOW — Comment clarity:**
```typescript
// lib/utils/transform.ts, line 10
// Get the user's org
const org = await getOrganisation(userId); // bad comment, repeats code

// Instead:
// Fetch org to validate subscription tier before allowing feature access
const org = await getOrganisation(userId);
```

---

## Severity Thresholds (Synthex-Specific)

| Issue | Severity | Threshold |
|-------|----------|-----------|
| Cognitive complexity | CRITICAL | >25 |
| Cognitive complexity | HIGH | 21-25 |
| Cognitive complexity | MEDIUM | 15-20 |
| Function parameters | MEDIUM | >5 |
| File size | MEDIUM | >500 lines with mixed concerns |
| Nesting depth | LOW | >3 levels without guard clauses |
| Unnamed magic number | MEDIUM | 3+ instances of same hardcoded value |
| Single-letter variable | MEDIUM | Outside tight loops |
| Unclear function name | MEDIUM | Generic verbs only: process, handle, do, get (context-dependent) |

---

## When NOT to Flag

- Australian English spellings (colour, organise, license as noun, authorise) — these are correct
- `as` type assertions with Prisma (approved pattern)
- Selective error boundaries (not every component needs one)
- `useRouter` from `next/navigation` (correct import)
- Dashboard pages with separate client component (intentional pattern)
- Single-letter loop variables (`for (let i = 0; ...)`)

