AgentSkillsCN

code-readability

代码可读性模式,涵盖注释、函数分解、嵌套结构、命名规范与代码组织方式。在评估代码是否易于理解、决定何时以及如何撰写注释、分解函数、降低嵌套深度、优化代码结构,或审查代码的可读性时使用此功能。涵盖注释策略、函数长度、抽象层级的混合使用、嵌套深度的缩减、匿名函数的可读性,以及让不良代码在视觉上一目了然。

SKILL.md
--- frontmatter
name: code-readability
description: Code readability patterns covering comments, function decomposition, nesting, naming conventions, and code structure. Use when evaluating whether code is easy to understand, deciding when and how to write comments, decomposing functions, reducing nesting depth, improving code structure, or reviewing code for readability. Covers comment strategy, function length, abstraction level mixing, nesting reduction, anonymous function readability, and making bad code visually obvious.

Code Readability

Readability is the foundation of code quality. Code is read far more often than it is written — every review, every debug session, every onboarding exercise is a read. If code is hard to read, it's hard to trust, hard to change, and hard to debug. Readability isn't about aesthetics; it's about reducing the cognitive effort required to understand what code does and why.

Quick Reference

The Readability Checklist

DimensionQuestionIf No...
NamesCan I understand what this does from the names alone?Rename variables, functions, classes
CommentsDo comments explain why, never what?Delete "what" comments; improve names instead
Function lengthDoes each function do one thing at one abstraction level?Extract methods
NestingIs nesting 2 levels or fewer?Use early returns, extract helper functions
ConsistencyDoes this code follow the same style as surrounding code?Adopt the project's conventions
Surprise factorWould another engineer expect this behavior from reading the signature?Rename or restructure to match expectations

The Comment Decision Table

SituationActionExample
Code does something non-obviousWrite a why comment// Retry limit set to 3 per SLA with payments team
Name doesn't convey intentFix the name, don't add a commentRename d to elapsedDays, delete // elapsed time in days
Complex algorithm or formulaComment the approach, not the steps// Uses Dijkstra's algorithm because graph is sparse
Workaround for a bug or limitationComment what and why// Safari doesn't support ResizeObserver in iframes (WebKit #219765)
Legal or licensing requirementKeep the comment// SPDX-License-Identifier: MIT
Commented-out codeDelete itVersion control remembers; dead code confuses readers
TODO or FIXMEInclude a ticket reference// TODO(CS-142): Replace with batch API when available
Section header in a long functionExtract a function insteadThe function name becomes the "header"

Naming and Structure

Names as Documentation

The most powerful readability tool is naming. A well-named function, variable, or class eliminates the need for comments explaining what code does. Names are read at every call site; comments are read only at the definition.

The hierarchy of documentation value:

LevelReachMaintenance CostReliability
Good namesEvery call site, every readerZero (name is the code)Always current
Type signaturesEvery caller, tooling, IDELow (compiler verifies)Enforced
CommentsReaders of this file onlyMedium (can drift from code)Trust but verify
External docsThose who find and read themHigh (often forgotten)Frequently stale

Invest naming effort proportional to scope. A loop counter i in a 3-line loop is fine. A variable used across 50 lines needs a full descriptive name.

(see code-naming -> Core Principles)

Consistent Style

Readability depends on predictability. When code follows consistent conventions, readers develop pattern recognition — they know where to look and what to expect.

What to keep consistent:

ElementConsistency Rule
Naming conventionsFollow language idioms (camelCase for JS/TS, snake_case for Python/Rust)
IndentationMatch the project (tabs vs. spaces, width)
Brace styleMatch the project (same-line vs. next-line)
Import orderingConsistent grouping (stdlib, third-party, internal)
Error handlingSame technique throughout the module

The golden rule: Match the code around you. Personal style preferences are less important than codebase consistency. A consistent codebase where every file follows the same conventions is more readable than one where each file reflects its author's preferences.

(see code-naming -> Language Convention Quick Reference)

Comment Strategy

When Comments Help

Comments should explain things that code cannot express: intent, constraints, decisions, and context.

Valuable comments explain:

What to CommentWhyExample
Business rulesCode can't express domain reasoning// Discount capped at 30% per finance policy Q3-2025
Non-obvious constraintsFuture maintainers need context// Must process before midnight UTC; downstream batch job depends on this
Performance decisionsOptimization rationale isn't self-evident// Using array instead of map: benchmarked 3x faster for n < 100
WorkaroundsWhy the code isn't "clean"// Workaround: API returns 200 with error body instead of 4xx
AssumptionsWhat the code relies on but doesn't verify// Assumes input is sorted; caller guarantees via OrderedCollection

When Comments Hurt

A comment that restates what the code does is noise. Worse, it creates a maintenance burden — when the code changes, the comment must change too. When it doesn't, the comment becomes a lie.

Comments that should be names instead:

code
// Bad: comment explains what the code does
int d; // elapsed time in days

// Good: name explains itself
int elapsedDays;
code
// Bad: comment describes the function
// Checks if the user has admin permission
function check(u) { ... }

// Good: name IS the description
function hasAdminPermission(user) { ... }

Comments that should be functions instead:

code
// Bad: section headers in a long function
function processOrder(order) {
  // Validate the order
  if (!order.items) throw new Error("...");
  if (!order.customer) throw new Error("...");

  // Calculate totals
  let subtotal = 0;
  for (const item of order.items) { ... }

  // Apply discounts
  ...
}

// Good: functions replace section headers
function processOrder(order) {
  validateOrder(order);
  const subtotal = calculateSubtotal(order.items);
  const total = applyDiscounts(subtotal, order.customer);
  ...
}

Commented-Out Code

Delete it. Always. Commented-out code creates uncertainty ("Is this important? Was it left for a reason? Should I uncomment it?"). Version control preserves history — if the code is needed again, it can be retrieved.

Every line of commented-out code is a small lie: it looks like it might be relevant, but it isn't. It trains readers to skip over comments, reducing the value of real comments.

Function Decomposition

The One-Sentence Rule

A well-structured function can be described in a single sentence without using "and." If describing what a function does requires multiple independent clauses, it handles multiple responsibilities and should be split.

code
// One sentence: "Validates that the email address is well-formed"
function validateEmail(email: string): boolean { ... }

// Multiple sentences: "Validates the order AND calculates the total AND sends confirmation"
// → Split into three functions
function processOrder(order: Order): void { ... }

(see code-quality-foundations -> The One-Sentence Test)

Abstraction Level Consistency

A function should operate at a single level of abstraction. Mixing high-level orchestration with low-level detail forces readers to context-switch constantly.

Signs of mixed abstraction levels:

SignalProblem
Function both calls methods and manipulates data structures directlyHigh-level and low-level mixed
Some lines describe what to do, others describe howReader must switch between intent and mechanism
Function reads like a recipe with some steps being "crack an egg" and others being "synthesize albumin"Inconsistent granularity

The fix: Either call functions or do work — don't do both in the same function. High-level functions orchestrate by calling other functions. Low-level functions perform the actual computation.

code
// Bad: mixed abstraction levels
function sendNewsletter(users, template) {
  const smtp = net.createConnection(config.smtp.port, config.smtp.host);
  smtp.write(`EHLO ${config.domain}\r\n`);           // Low-level SMTP
  const filtered = users.filter(u => u.subscribed);   // Business logic
  for (const user of filtered) {
    const html = template.replace("{{name}}", user.name);  // String manipulation
    smtp.write(`MAIL FROM:<${config.sender}>\r\n`);        // Low-level SMTP again
    ...
  }
}

// Good: consistent abstraction level
function sendNewsletter(users, template) {
  const subscribers = filterSubscribed(users);
  const connection = connectToMailServer();
  for (const user of subscribers) {
    const message = renderTemplate(template, user);
    sendEmail(connection, user.email, message);
  }
}

Function Length

There is no universal line count rule — a 20-line function that does one thing clearly is better than five 4-line functions that fragment the logic. The real metric is conceptual unity: does the function handle exactly one responsibility?

Practical guidelines:

SituationGuidance
Function is hard to nameIt does too much — split it
Function requires scrolling to readConsider extraction if logic is separable
Function has sections separated by blank lines or commentsEach section is a candidate for extraction
Function has deeply nested conditionalsExtract the nested blocks
Every line contributes to one coherent operationLength is fine — don't split artificially

(see refactoring-patterns -> Extract Method)

Nesting and Control Flow

Reducing Nesting Depth

Deeply nested code is hard to follow because readers must maintain a mental stack of conditions. Each level of nesting adds cognitive load.

Maximum practical depth: 2 levels. Beyond that, readers lose track of which conditions are active.

Techniques for reducing nesting:

TechniqueBeforeAfter
Early return / guard clauseif (valid) { ... long body ... }if (!valid) return; ... long body ...
Extract methodNested block becomes a named functionprocessValidItem(item)
Invert conditionif (!error) { if (!empty) { ... } }if (error) return; if (empty) return; ...
Replace loop bodyComplex logic inside forExtract loop body into a function
code
// Bad: 4 levels of nesting
function processOrders(orders) {
  for (const order of orders) {
    if (order.status === "pending") {
      if (order.items.length > 0) {
        if (order.customer.isVerified) {
          // actual logic buried here
        }
      }
    }
  }
}

// Good: early returns flatten the structure
function processOrders(orders) {
  for (const order of orders) {
    if (order.status !== "pending") continue;
    if (order.items.length === 0) continue;
    if (!order.customer.isVerified) continue;
    // actual logic at top level
  }
}

Anonymous Functions and Readability

Anonymous functions (lambdas, closures, arrow functions) improve readability when they're short and their purpose is obvious from context. They harm readability when they're long, nested, or capture complex state.

When anonymous functions help:

ContextWhyExample
Short collection operationsIntent is clear from the method nameusers.filter(u => u.isActive)
Simple callbacksOne-liner with obvious purposebutton.onClick(() => setVisible(true))
Inline predicatesSelf-documentingorders.sort((a, b) => a.date - b.date)

When to extract a named function:

SignalAction
Lambda exceeds ~3 linesExtract to named function
Lambda is nested inside another lambdaExtract the inner one
Lambda captures variables from multiple scopesName it to clarify its role
Same lambda logic appears in multiple placesExtract and reuse
Lambda's purpose isn't obvious from contextGive it a name
code
// Hard to read: nested anonymous functions
fetchUsers()
  .then(users => users.filter(u => {
    const lastLogin = new Date(u.lastLoginAt);
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() - 30);
    return lastLogin > cutoff;
  }))
  .then(active => active.map(u => ({
    ...u,
    displayName: `${u.firstName} ${u.lastName}`,
    status: u.isVerified ? "verified" : "pending"
  })));

// Readable: named functions clarify intent
fetchUsers()
  .then(users => users.filter(wasActiveInLast30Days))
  .then(active => active.map(toDisplayUser));

Making Bad Code Look Bad

Visual Signals of Quality

Code structure should visually signal its quality. When bad code looks bad — long functions, deep nesting, inconsistent formatting — it creates natural pressure to fix it. When bad code is formatted to look clean, it hides problems.

Visual indicators of problematic code:

Visual SignalWhat It Suggests
Function won't fit on screenToo many responsibilities
Arrow-shaped indentation (deep nesting)Excessive branching, missing abstractions
Long parameter listsFunction depends on too much context
Inconsistent formatting within a fileMultiple authors, no shared conventions
Large blocks of commented-out codeUncertainty, deferred cleanup
Many // TODO or // FIXME without ticket refsAcknowledged but untracked technical debt

Formatting as Communication

Formatting isn't cosmetic — it communicates structure. Blank lines separate logical groups. Indentation shows scope. Alignment reveals patterns. Consistent formatting lets readers use visual scanning rather than character-by-character reading.

Use automated formatters (Prettier, Black, gofmt, rustfmt) to eliminate formatting debates. When formatting is automated, every diff contains only meaningful changes — no noise from whitespace adjustments.

Readability Antipatterns

Clever Code

Code that uses language tricks, bitwise hacks, or obscure syntax to save lines at the cost of clarity. "Clever" is not a compliment in code review.

CleverClear
!!valueBoolean(value)
x || (x = default)if (x === undefined) x = default;
arr.reduce((a,b) => ({...a, [b.id]: b}), {})Object.fromEntries(arr.map(b => [b.id, b])) or a named function

The test: If a junior engineer couldn't understand this code without looking up the syntax, simplify it.

Inconsistent Abstraction

Mixing raw operations with high-level abstractions in the same block. If one line calls fetchUser() and the next manually parses JSON with string splitting, the reader can't establish a mental model.

SignalProblemFix
High-level call next to low-level manipulationReader must context-switchExtract low-level work into a named function
Some lines describe what, others describe howInconsistent granularityMatch abstraction level throughout the function
Framework helpers mixed with raw API callsUnclear which layer you're operating inPick one abstraction level per function

Misleading Formatting

Formatting that suggests a different structure than what exists.

SignalProblemFix
Indented code outside a block scopeLooks like it's inside a conditional but isn'tFix indentation to match actual scope
Blank lines used inconsistentlySome logical groups are separated, others aren'tUse blank lines consistently to separate logical sections
Statement alignment suggesting relationshipsUnrelated code looks relatedAlign only genuinely related elements

Decision Tables

"Should I write a comment?"

The comment would explain...Write it?Instead...
What the code doesNoImprove the name or extract a function
Why the code does it this wayYes
What a variable holdsNoRename the variable
A workaround or hackYesInclude link to issue/ticket
The algorithm being usedYes (if non-obvious)
A section of a long functionNoExtract the section into a named function

"Is this function too long?"

SignalVerdict
Can describe in one sentence without "and"Length is fine
Has blank-line-separated sectionsExtract sections
Mixes abstraction levelsSplit by level
Requires scrolling and mental bookmarkingExtract for readability
Every line contributes to one operationLength is fine

Readability Checklist

Before committing code, verify:

  • Names reveal intent without needing comments
  • Comments explain why, not what
  • No commented-out code
  • Functions operate at a single abstraction level
  • Nesting is 2 levels or fewer
  • Anonymous functions are short and their purpose is obvious
  • Formatting follows project conventions
  • Code follows the same patterns as surrounding code

See Also

  • code-quality-foundations — Quality pillars, readability as the foundation pillar (see code-quality-foundations -> Make Code Readable)
  • code-naming — Naming decisions, naming as documentation, language conventions (see code-naming -> Core Principles)
  • refactoring-patterns — Extract Method, reducing nesting, function decomposition (see refactoring-patterns -> Extract Method)
  • software-tradeoffs — Readability as a quality to trade against performance or flexibility (see software-tradeoffs -> Core Tradeoffs)
  • code-antipatterns — Patterns that harm readability (see code-antipatterns -> Pattern Recognition)