# Code Clarity

> Write readable, intention-revealing code with precise names, flat control flow, consistent abstraction levels, focused files, named constants, formatter-aligned style, repository conventions, and testable seams. Use when naming feels off, logic is hard to follow, functions do too much, nested conditionals obscure flow, files mix many exports, magic numbers are inline, formatting is inconsistent,…

- **Type:** Skill
- **Install:** `agentstack add skill-lakr233-code-clarity-code-clarity`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Lakr233](https://agentstack.voostack.com/s/lakr233)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Lakr233](https://github.com/Lakr233)
- **Source:** https://github.com/Lakr233/code-clarity

## Install

```sh
agentstack add skill-lakr233-code-clarity-code-clarity
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Code Clarity Framework

A practical framework for writing code that communicates intent clearly. The central thesis: **a developer reads code far more than they write it**, so every naming and structural decision is a communication decision. Code that requires a reader to reconstruct the author's mental model has failed at its primary job.

This framework focuses on the micro-level of software design — the decisions made at the function, method, class, and file level — and complements macro-level architecture thinking.

**Languages covered.** The framework is language-agnostic in principle, with first-class guidance for **Swift**, **TypeScript**, and **Electron** (the main/preload/renderer split), plus Go and Python equivalents where they sharpen a point. Swift examples carry the value-vs-identity and `guard` material; TypeScript and Electron examples carry the one-object-per-file, module-constant, discriminated-union, typed-IPC, and formatter-consistency material drawn from real production codebases.

## Core Principles

**Code is written once but read hundreds of times.** Every name, every function boundary, every conditional structure is a message to the next reader (usually yourself, six months later). Clarity is not a style preference — it is a correctness property: unclear code is one misunderstanding away from a bug.

**Clarity is partly local to a repository.** A refactor that ignores a codebase's existing naming, file organization, and control-flow habits can make the result less readable even when the individual function improves in isolation.

## Scoring

**Goal: 10/10.** When reviewing or writing code, rate it 0–10 on clarity. A 10/10 has names that read like prose, functions that do exactly one thing at one level of abstraction, guard clauses that eliminate nesting, structures whose responsibilities are obvious from their name alone, and changes that match the repository's established local conventions. Provide the current score and exactly what to change to reach 10/10.

## The Code Clarity Framework

Thirteen principles for writing code that communicates clearly. Principles 1–8 are structural and language-agnostic; principles 9–12 cover file organization, named constants, mechanical formatting consistency, and the Electron process boundary — the areas most often neglected in TypeScript and Electron codebases; principle 13 covers dependencies and seam design — one canonical implementation, seams only where behavior truly varies, and testing without drowning in mocks and dependency injection:

---

### 1. Naming: Names Are Your Primary API

**Core concept:** Every name — variable, function, method, class, parameter — is a contract with the reader. Names should reveal intent, not implementation. The reader should never need to read a function's body to understand what it does or what a variable contains.

**Why it works:** In most codebases, 70%+ of identifiers are custom names. If those names are precise, the code reads like a domain description. If they are vague or misleading, every reader carries extra cognitive load reconstructing what the author meant.

**Key insights:**
- Functions that *do something* use verbs: `fetchUser()`, `validateInput()`, `syncViews()`
- Functions that *return a value* describe what they return: `activeUsers()`, `formattedDate()`, `bytesReceived()`
- Booleans use `is/has/can/should`: `isVisible`, `hasChildren`, `canSubmit`, `shouldRetry`
- Avoid double negatives: `hasElements` not `!isEmpty`, `isEnabled` not `!isDisabled`
- Class methods drop redundant type context: `line.length()` not `line.getLineLength()`
- Names should be proportional to scope: loop variables can be `i`, module-level state needs full names
- Abbreviations are only valid if universally understood in the domain (`url`, `id`, `dto`)

**Code applications:**

| Context | Unclear | Clear |
|---------|---------|-------|
| **Action function** | `handle()`, `process()`, `manage()` | `submitOrder()`, `parseResponse()`, `invalidateCache()` |
| **Return-value function** | `get()`, `fetch()`, `data()` | `currentUser()`, `pendingRequests()`, `errorMessage()` |
| **Boolean** | `flag`, `check`, `status`, `valid` | `isAuthenticated`, `hasUnreadMessages`, `canRetry` |
| **Swift bool** | `!list.isEmpty` | `list.hasElements` (extension) |
| **TS bool** | `if (!user.disabled)` | `if (user.isEnabled)` |
| **Class name** | `Manager`, `Handler`, `Helper`, `Util` | `RequestThrottler`, `TokenRefresher`, `PayloadEncoder` |
| **TS type suffix** | `data`, `info`, `thing` | role suffixes: `*Service`, `*Store`, `*Schema`, `*Registry`, `*Queue` |
| **Parameter** | `func send(_ data: Data, _ b: Bool)` | `func send(_ payload: Data, encrypted: Bool)` |
| **TS interface** | prefix every interface with `I` reflexively | name the role: `Session`, `HandlerDeps`; reserve `I`-prefix for abstract contracts (`ISessionManager`) only if the repo already does |

See: [references/naming-conventions.md](references/naming-conventions.md) and [references/typescript-and-electron.md](references/typescript-and-electron.md)

---

### 2. Early Return: Flatten the Happy Path

**Core concept:** Handle preconditions, error cases, and guard conditions at the top of a function and return immediately. The main logic of a function should be at the lowest indentation level, unobstructed by nested conditionals.

**Why it works:** Nesting forces the reader to maintain a mental stack of conditions. Each level of indentation multiplies the cognitive load. Early returns collapse this stack: by the time the reader reaches the main logic, all edge cases have been disposed of and forgotten. This is idiomatic in both Go and Swift.

**Key insights:**
- Check preconditions first, return/throw immediately if they fail
- The "happy path" — the normal case — lives at the leftmost indentation level
- `guard` in Swift and early `if err != nil { return }` in Go are the same philosophy
- Each early return is a complete thought: "this condition means we're done here"
- Avoid `else` after a `return` — it is always redundant and adds visual noise
- Deeply nested `if/else` is a signal to invert and exit early
- Exception: don't force early return when the two branches are genuinely symmetric in importance

**Code applications:**

| Context | Nested (avoid) | Early Return (prefer) |
|---------|---------------|----------------------|
| **Swift guard** | `if let user = user { if user.isActive { ... } }` | `guard let user, user.isActive else { return }` |
| **Validation** | `if isValid { if hasPermission { doWork() } }` | `guard isValid else { return }; guard hasPermission else { return }; doWork()` |
| **Error handling** | `if error == nil { if result != nil { use(result!) } }` | `guard error == nil, let result else { handle(error); return }; use(result)` |
| **Go style** | `if err == nil { if data != nil { process(data) } }` | `if err != nil { return err }; if data == nil { return ErrEmpty }; process(data)` |
| **TS guard** | `if (input) { if (input.type === 'keyDown') { ... } }` | `if (!input \|\| input.type !== 'keyDown') return; ...` |
| **TS dependency guard** | wrap a whole handler body in `if (windowManager) { ... }` | `if (!windowManager) return; ...` at the top — keeps the body flat |

See: [references/early-return.md](references/early-return.md)

---

### 3. Function Design: One Thing, One Level

**Core concept:** A function should do exactly one thing, at exactly one level of abstraction. The name should make that one thing obvious. If you need "and" to describe what a function does, it is doing two things.

**Why it works:** Functions that mix abstraction levels force the reader to context-switch between strategy and implementation detail. A function that orchestrates a workflow should not also contain the bit-manipulation logic that implements one step. Keeping levels consistent lets the reader choose the depth they need.

**Key insights:**
- The "one level of abstraction per function" rule: orchestration and implementation should not coexist
- If a function's body requires a comment to explain a section, that section is probably a function
- Function length is a symptom, not a cause: a 50-line function doing one thing is fine; a 5-line function doing three things is not
- Parameters: 0–2 is good, 3 is a warning, 4+ usually means a struct/object is needed
- Avoid output parameters — return values instead
- Avoid boolean flags that change the function's behavior — split into two functions
- Side effects should be in the name: `saveUser()` not `getUser()` when it also persists

**Code applications:**

| Problem | Example | Fix |
|---------|---------|-----|
| **Mixed levels** | `submitForm()` validates, serializes, and also builds the HTTP multipart boundary | Extract `buildMultipartBody()` |
| **Boolean flag param** | `render(view, animated: Bool)` does two different things | Split into `render(view)` and `renderAnimated(view)` |
| **Too many params** | `createUser(name:email:age:role:team:avatar:)` | Accept `UserConfiguration` struct |
| **Hidden side effect** | `currentUser()` hits the network | Rename `fetchCurrentUser()` or make it async |
| **"And" function** | `validateAndSave()` | Two functions: `validate()`, `save()` |

See: [references/function-design.md](references/function-design.md)

---

### 4. State Modeling: Related Flags Are a Hidden State Machine

**Core concept:** When several booleans, optionals, or task properties describe the same lifecycle, they are usually a state machine written in the least clear form. Make the lifecycle explicit with an enum or a small value type so impossible combinations cannot be represented.

**Why it works:** Scattered flags force every caller to keep them synchronized. A reader has to ask whether `isConnected`, `isEnded`, `isBuffering`, `isPlaying`, `activeTask`, and `pendingTask` can overlap, and bugs appear when one branch updates three of them but forgets the fourth. A single state value makes the valid states visible and gives each transition one place to live.

**Key insights:**
- If states are mutually exclusive, use an enum: `.idle`, `.connecting`, `.connected`, `.ended`
- If values must update together, group them in the enum payload or a small state struct
- Avoid lifecycle pairs like `isConnected` + `isEnded`; prefer `connectionState`
- Avoid activity flag piles like `isBuffering`, `isPlaying`, `isSeeking`; prefer one `activity`
- Computed booleans such as `connectionState.isConnected` are fine when they are aliases for a real state, not independent storage
- Store a `Task` only when the owner must cancel, replace, or coalesce it later
- Fire-and-forget async work should usually be a local `Task` or `Task.detached`, not another stored optional property
- Do not use optional as a vague state marker when an enum case can name the state directly
- In SwiftUI apps, prefer one observable app/store object passed through the view tree over many tiny dependency-injection seams
- Put UI-facing snapshots in small context/configuration value types when several settings must be applied together
- Views should read state and send intent; lifecycle transitions belong in the store/controller that owns the state

**Code applications:**

| Problem | Unclear | Clear |
|---------|---------|-------|
| **Connection lifecycle** | `isConnected`, `isEnded`, `isReconnecting` | `enum ConnectionState { case disconnected, connecting, connected, reconnecting, ended }` |
| **Mutually exclusive activity** | `isBuffering`, `isPlaying`, `isPaused`, `isSeeking` | `enum PlaybackActivity { case idle, buffering, playing, paused, seeking }` |
| **Optional-as-state** | `currentRequest: Request?`, `isLoading: Bool`, `error: Error?` | `enum LoadState { case idle, loading(Request), failed(Error), loaded(Result) }` |
| **Saved one-shot task** | `var refreshTask: Task?` that is never cancelled | Create the task where the async work starts and let it finish |
| **Replaceable task** | Multiple optional task properties cancelled in bulk | Keep only tasks that must be cancelled/replaced, or wrap them in a named lifecycle state |

**SwiftUI data-flow check:**

```swift
@Observable final class AppStore {
  private(set) var connectionState: ConnectionState = .disconnected
  private(set) var playbackActivity: PlaybackActivity = .idle
  var configuration = AppConfiguration()

  var isConnected: Bool { connectionState == .connected }

  func connect() {
    guard case .disconnected = connectionState else { return }
    connectionState = .connecting
    Task { await runConnection() }
  }
}
```

The important part is not the exact names. The important part is that `connectionState`,
`playbackActivity`, and `configuration` each have one owner and each represent one coherent
piece of state. Do not spread the same lifecycle across independent `@State`, `@Binding`,
`@Published`, or optional properties.

**TypeScript: discriminated (tagged) unions are the same idea.** Where Swift uses an `enum` with
associated values, TypeScript uses a discriminated union with a literal `type`/`kind`/`status`
discriminant. The payload that only exists in one state lives only in that variant, so impossible
combinations are unrepresentable and every consumer `switch`es on the tag:

```ts
// Avoid — three booleans + an optional that must be kept in sync
interface LoadState { isLoading: boolean; isError: boolean; data?: Result; error?: Error }

// Prefer — one value, each variant carries exactly its own payload
type LoadState =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'loaded'; data: Result }
  | { status: 'failed'; error: Error }
```

Real Electron/React codebases push this far: a domain event type or route state becomes one union of
many tagged variants, with small type-guard helpers (`isLoaded(state)`) instead of scattered
booleans. Validate the boundary with a schema (zod `z.discriminatedUnion('type', [...])`) so the
runtime shape and the compile-time type cannot drift. See
[references/typescript-and-electron.md](references/typescript-and-electron.md).

---

### 5. Abstraction Levels: Hierarchy Must Be Consistent

**Core concept:** Every scope — module, class, function — should operate at a single, coherent level of abstraction. High-level code manages strategy and orchestration. Low-level code handles mechanism and detail. Mixing these two in the same scope is one of the most common and most damaging readability failures.

**Why it works:** When a reader encounters a high-level function, they expect to understand the overall flow without knowing implementation details. When they encounter a low-level function, they expect to see a specific, contained operation. Mixing the two forces the reader to maintain both strategic and tactical context simultaneously.

**Key insights:**
- Abstraction level corresponds to position in the call tree: higher up = more abstract
- Red flag: a function that calls other named functions AND also does raw data manipulation
- Red flag: a class that manages business logic AND also formats strings for display
- Naming reveals level: `orchestrateCheckout()` is high-level; `appendQueryParameter(_:to:)` is low-level
- The "step-down rule": reading a file top-to-bottom, each function should be followed by the functions it calls at the next level down
- In Swift: a ViewModel should not contain URL construction logic; a NetworkLayer should not contain business rules

**Code applications:**

| Context | Mixed Levels (avoid) | Consistent Levels (prefer) |
|---------|---------------------|---------------------------|
| **Checkout flow** | `checkout()` calls `applyDiscount()` then also does `price * (1 - rate)` math inline | `checkout()` calls `applyDiscount()` which internally computes the math |
| **ViewModel** | `loadData()` builds URLRequest, parses JSON, and updates `@Published` state | `loadData()` calls `repository.fetchItems()` and maps to display models |
| **Class responsibilities** | `OrderProcessor` manages order state A

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Lakr233](https://github.com/Lakr233)
- **Source:** [Lakr233/code-clarity](https://github.com/Lakr233/code-clarity)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-lakr233-code-clarity-code-clarity
- Seller: https://agentstack.voostack.com/s/lakr233
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
