Install
$ agentstack add skill-lakr233-code-clarity-code-clarity ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →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:
hasElementsnot!isEmpty,isEnablednot!isDisabled - Class methods drop redundant type context:
line.length()notline.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
guardin Swift and earlyif err != nil { return }in Go are the same philosophy- Each early return is a complete thought: "this condition means we're done here"
- Avoid
elseafter areturn— it is always redundant and adds visual noise - Deeply nested
if/elseis 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()notgetUser()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; preferconnectionState - Avoid activity flag piles like
isBuffering,isPlaying,isSeeking; prefer oneactivity - Computed booleans such as
connectionState.isConnectedare fine when they are aliases for a real state, not independent storage - Store a
Taskonly when the owner must cancel, replace, or coalesce it later - Fire-and-forget async work should usually be a local
TaskorTask.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:
@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 switches on the tag:
// 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
- Source: Lakr233/code-clarity
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.