skimmable

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Skimmable

易读化(Skimmable)

Review the change as if a tired reviewer must understand it in one fast pass.
Prefer deleting code to explaining code.
Keep the pass narrow: improve readability, state modelling, and diff clarity without changing product behaviour unless the current behaviour is obviously dead, redundant, or inconsistent with types.
评审变更时,假设一位疲惫的评审者需要快速理解全部内容。
优先删除冗余代码,而非通过注释解释代码。
聚焦核心目标:在不改变产品行为的前提下提升可读性、状态建模质量与代码差异清晰度,除非当前行为明显无效、冗余或与类型定义不一致。

Checklist

检查清单

Apply these rules aggressively:
  1. Make the code obvious on first read.
  2. Reduce the number of states the code can represent.
  3. Reduce parameter count.
  4. Remove optional parameters unless they are truly required by callers.
  5. Replace boolean flags and loose option bags with discriminated unions when behaviour differs by mode.
  6. Handle every known variant exhaustively.
  7. Fail on unknown variants immediately.
  8. Trust static types inside the typed core; do not add defensive branches for impossible states.
  9. Assert at boundaries when loading data, parsing input, or reading external systems.
  10. Prefer early returns over nested conditionals.
  11. Keep functions cohesive; do not split logic into many tiny wrappers.
  12. Avoid clever helpers, indirection, and generic abstractions.
  13. Remove overrides, escape hatches, and customisation points unless there is a proven need.
  14. Remove dead paths, temporary compatibility layers, and unrelated edits.
  15. Bias toward fewer lines and fewer moving parts.
严格遵循以下规则:
  1. 确保代码一眼就能看懂。
  2. 减少代码可表示的状态数量。
  3. 减少参数数量。
  4. 移除可选参数,除非调用方确实需要。
  5. 当不同模式对应不同行为时,用discriminated unions替代布尔标记与松散配置对象。
  6. 全面处理所有已知变体。
  7. 遇到未知变体时立即报错。
  8. 在类型化核心逻辑中信任static types,不为不可能的状态添加防御性分支。
  9. 在加载数据、解析输入或读取外部系统的边界处添加assertions。
  10. 优先使用提前返回,而非嵌套条件判断。
  11. 保持函数内聚性,不要将逻辑拆分为大量微小的包装函数。
  12. 避免使用花哨的辅助函数、间接调用与通用抽象。
  13. 移除覆盖项、逃逸出口与自定义点,除非已证实有此需求。
  14. 移除无效代码路径、临时兼容层与无关修改。
  15. 尽量减少代码行数与可动部件。

Working method

实施步骤

Follow this order:
按照以下顺序执行:

1. Trim the diff

1. 精简代码差异

Remove changes that do not help the main goal of the PR.
Delete:
  • incidental renames
  • opportunistic refactors
  • styling churn with no behavioural value
  • compatibility code for callers that no longer exist
  • unused helpers, types, branches, and comments
If a simplification requires a separate behavioural decision, stop and call it out instead of smuggling it into the readability pass.
移除对PR核心目标无帮助的变更。
删除:
  • 无关的重命名
  • 随机的重构
  • 无行为价值的样式变更
  • 针对已不存在的调用方的兼容代码
  • 未使用的辅助函数、类型、分支与注释
如果某项简化需要单独的行为决策,请暂停并明确提出,不要将其混入本次可读性优化中。

2. Simplify data shape first

2. 先简化数据结构

Reduce complexity in types before touching control flow.
Prefer:
  • required fields over optional fields
  • one obvious input shape over many flexible ones
  • discriminated unions over booleans or loosely related fields
  • named domain types over generic maps and stringly typed objects
Ask of every argument:
  • Is it required?
  • Can it be derived?
  • Can two arguments become one domain object?
  • Is it just an override for an API that is too flexible?
在修改控制流之前,先降低类型复杂度。
优先选择:
  • 必填字段,而非可选字段
  • 单一明确的输入结构,而非多种灵活结构
  • discriminated unions,而非布尔值或松散关联的字段
  • 具名领域类型,而非通用映射与字符串类型对象
对每个参数提出以下问题:
  • 是否为必填项?
  • 是否可通过其他值推导得出?
  • 是否可将两个参数合并为一个领域对象?
  • 是否只是为了覆盖过于灵活的API?

3. Simplify control flow

3. 简化控制流

Make happy paths short and visible.
Prefer:
  • early returns
  • straight-line code
  • one level of indentation where possible
  • local branching near use
Avoid:
  • nested
    if
    pyramids
  • defensive
    else
    branches for impossible states
  • try/catch
    for expected values
  • hidden branching spread across helper functions
让主路径简短且清晰可见。
优先选择:
  • 提前返回
  • 线性代码
  • 尽可能保持单层缩进
  • 在使用位置附近进行局部分支判断
避免:
  • 嵌套
    if
    金字塔
  • 为不可能的状态添加防御性
    else
    分支
  • 对预期值使用
    try/catch
  • 分散在辅助函数中的隐藏分支

4. Make invalid states loud

4. 让无效状态立即报错

Assert at boundaries.
Use assertions when:
  • decoding external data
  • reading files, env vars, or request payloads
  • converting untyped data into typed domain values
  • checking invariants that must hold
After the boundary, write simple code that trusts the asserted shape.
在边界处添加assertions。
在以下场景使用assertions:
  • 解码外部数据
  • 读取文件、环境变量或请求负载
  • 将无类型数据转换为类型化领域值
  • 检查必须成立的不变量
越过边界后,编写信任断言后结构的简单代码。

5. Exhaust variants

5. 全面处理所有变体

When code depends on a variant, make the variant explicit and handle all cases.
Prefer a
switch
on a discriminant.
End with an exhaustive check or assertion if the language allows it.
Do not silently ignore unknown values.
当代码依赖变体时,需明确变体类型并处理所有情况。
优先使用基于判别符的
switch
语句。
如果语言支持,以全面检查或断言收尾。
不要静默忽略未知值。

6. Collapse unnecessary abstraction

6. 合并不必要的抽象

Inline small one-use wrappers when they hide the real logic.
Keep helper functions only when they:
  • remove real duplication
  • encode a domain concept
  • make the caller meaningfully clearer
Do not split a 20-line readable function into five 4-line helpers just to look tidy.
当小型一次性包装函数隐藏了真实逻辑时,将其内联。
仅在以下情况保留辅助函数:
  • 消除真正的重复代码
  • 封装领域概念
  • 让调用方代码更清晰易懂
不要为了看起来整洁,将一个20行的可读函数拆分为五个4行的辅助函数。

Preferred patterns

推荐模式

Keep arguments few and strict

保持参数少且严格

Bad signs:
  • more than 3 parameters
  • multiple booleans
  • optional callback plus optional overrides plus optional config
  • passing values already available on an object
Prefer:
  • one required object when the fields belong together
  • one discriminated union when there are modes
  • separate functions when the behaviours are actually different
不良信号:
  • 参数数量超过3个
  • 多个布尔参数
  • 可选回调+可选覆盖项+可选配置
  • 传递已存在于对象中的值
优先选择:
  • 当字段属于同一组时,使用单个必填对象
  • 存在多种模式时,使用单个discriminated union
  • 当行为确实不同时,使用独立函数

Prefer discriminated unions over booleans

优先使用discriminated unions而非布尔值

If a boolean changes behaviour, model the behaviour directly.
Use:
  • kind: "draft" | "published"
  • source: "cache" | "network"
  • status: "idle" | "loading" | "success" | "error"
Avoid:
  • isDraft
  • useCache
  • isLoading
    plus
    error
    plus
    data
    on the same object unless the state machine truly permits all combinations
如果布尔值会改变行为,请直接对行为进行建模。
使用:
  • kind: "draft" | "published"
  • source: "cache" | "network"
  • status: "idle" | "loading" | "success" | "error"
避免:
  • isDraft
  • useCache
  • 在同一对象上同时存在
    isLoading
    error
    data
    ,除非状态机确实允许所有组合

Prefer assertions over defensive code

优先使用assertions而非防御性代码

At boundaries, assert once. Inside, trust the assertion.
Avoid repetitive checks like:
  • if (!user) return
  • if (!user.id) return
  • if (typeof user.id !== "string") return
when types and boundary validation already guarantee the shape.
在边界处断言一次,内部逻辑信任断言结果。
避免重复检查,例如:
  • if (!user) return
  • if (!user.id) return
  • if (typeof user.id !== "string") return
当类型与边界验证已能保证结构时。

Prefer one obvious path

优先选择单一明确路径

If there is a default behaviour, encode it directly.
Do not add overrides like
sortOrder = "desc"
,
strategy = "auto"
,
separator = ","
,
shouldLog = false
unless callers truly need them.
Every option multiplies states.
如果存在默认行为,请直接编码实现。
不要添加
sortOrder = "desc"
strategy = "auto"
separator = ","
shouldLog = false
这类覆盖项,除非调用方确实需要。
每个可选配置都会成倍增加状态数量。

Review prompts

评审提示

Use these prompts during the pass:
  • Can this function lose a parameter?
  • Can this object lose an optional field?
  • Can this branch become an early return?
  • Can these two booleans become one union?
  • Can this helper be inlined?
  • Can this error path become an assertion at the boundary?
  • Can this code fail loudly instead of guessing?
  • Can this diff lose files or lines?
  • If a reviewer skims this in 30 seconds, will the intent be obvious?
在优化过程中使用以下提示:
  • 该函数能否减少一个参数?
  • 该对象能否移除一个可选字段?
  • 该分支能否改为提前返回?
  • 这两个布尔值能否合并为一个联合类型?
  • 该辅助函数能否内联?
  • 该错误路径能否改为边界处的断言?
  • 该代码能否立即报错而非猜测处理?
  • 该代码差异能否删除一些文件或行?
  • 如果评审者用30秒快速浏览,能否明确代码意图?

Examples

示例

Reduce arguments and remove overrides

减少参数并移除覆盖项

Before:
ts
function createReport(
  userId: string,
  includeDrafts = false,
  sortBy: "date" | "name" = "date",
  sortDirection: "asc" | "desc" = "desc",
  limit?: number,
  overrides?: { timezone?: string; now?: Date }
) {
  const now = overrides?.now ?? new Date()
  const timezone = overrides?.timezone ?? "UTC"

  return buildReport({
    userId,
    includeDrafts,
    sortBy,
    sortDirection,
    limit,
    now,
    timezone,
  })
}
After:
ts
type ReportScope =
  | { kind: "published" }
  | { kind: "all" }

function createReport(userId: string, scope: ReportScope) {
  return buildReport({ userId, scope, now: new Date(), timezone: "UTC" })
}
Prefer a smaller API. Add configurability only when a real caller needs it.
优化前:
ts
function createReport(
  userId: string,
  includeDrafts = false,
  sortBy: "date" | "name" = "date",
  sortDirection: "asc" | "desc" = "desc",
  limit?: number,
  overrides?: { timezone?: string; now?: Date }
) {
  const now = overrides?.now ?? new Date()
  const timezone = overrides?.timezone ?? "UTC"

  return buildReport({
    userId,
    includeDrafts,
    sortBy,
    sortDirection,
    limit,
    now,
    timezone,
  })
}
优化后:
ts
type ReportScope =
  | { kind: "published" }
  | { kind: "all" }

function createReport(userId: string, scope: ReportScope) {
  return buildReport({ userId, scope, now: new Date(), timezone: "UTC" })
}
优先选择更精简的API。仅当真实调用方需要时再添加可配置项。

Replace booleans with a discriminated union

用discriminated union替代布尔值

Before:
ts
type SaveState = {
  isSaving: boolean
  error?: string
  receipt?: string
}
After:
ts
type SaveState =
  | { kind: "idle" }
  | { kind: "saving" }
  | { kind: "error"; message: string }
  | { kind: "saved"; receipt: string }
The second version prevents impossible combinations.
优化前:
ts
type SaveState = {
  isSaving: boolean
  error?: string
  receipt?: string
}
优化后:
ts
type SaveState =
  | { kind: "idle" }
  | { kind: "saving" }
  | { kind: "error"; message: string }
  | { kind: "saved"; receipt: string }
第二种版本避免了不可能的状态组合。

Handle variants exhaustively

全面处理所有变体

Before:
ts
function badgeColor(status: Status) {
  if (status === "success") return "green"
  if (status === "error") return "red"
  return "gray"
}
After:
ts
function badgeColor(status: Status) {
  switch (status) {
    case "success":
      return "green"
    case "error":
      return "red"
    case "pending":
      return "gray"
    default:
      return assertNever(status)
  }
}
Unknown variants should fail loudly during development.
优化前:
ts
function badgeColor(status: Status) {
  if (status === "success") return "green"
  if (status === "error") return "red"
  return "gray"
}
优化后:
ts
function badgeColor(status: Status) {
  switch (status) {
    case "success":
      return "green"
    case "error":
      return "red"
    case "pending":
      return "gray"
    default:
      return assertNever(status)
  }
}
开发过程中遇到未知变体应立即报错。

Assert at the boundary, trust inside

在边界处断言,内部逻辑信任断言结果

Before:
ts
async function loadAccount(accountId: string) {
  const raw = await db.accounts.find(accountId)

  if (!raw) {
    throw new Error("Account not found")
  }

  if (typeof raw.email !== "string") {
    throw new Error("Invalid account email")
  }

  return raw
}

function sendWelcomeEmail(account: any) {
  if (!account) return
  if (!account.email) return
  mailer.send(account.email)
}
After:
ts
async function loadAccount(accountId: string): Promise<Account> {
  const raw = await db.accounts.find(accountId)
  assert(raw, "Account not found")
  assert(typeof raw.email === "string", "Invalid account email")
  return raw as Account
}

function sendWelcomeEmail(account: Account) {
  mailer.send(account.email)
}
Validate once. Keep the typed core simple.
优化前:
ts
async function loadAccount(accountId: string) {
  const raw = await db.accounts.find(accountId)

  if (!raw) {
    throw new Error("Account not found")
  }

  if (typeof raw.email !== "string") {
    throw new Error("Invalid account email")
  }

  return raw
}

function sendWelcomeEmail(account: any) {
  if (!account) return
  if (!account.email) return
  mailer.send(account.email)
}
优化后:
ts
async function loadAccount(accountId: string): Promise<Account> {
  const raw = await db.accounts.find(accountId)
  assert(raw, "Account not found")
  assert(typeof raw.email === "string", "Invalid account email")
  return raw as Account
}

function sendWelcomeEmail(account: Account) {
  mailer.send(account.email)
}
验证一次,保持类型化核心逻辑简单。

Prefer early returns over nesting

优先使用提前返回而非嵌套

Before:
ts
function visibleItems(items: Item[], user?: User) {
  if (user) {
    if (user.isAdmin) {
      return items
    } else {
      return items.filter((item) => !item.hidden)
    }
  } else {
    return []
  }
}
After:
ts
function visibleItems(items: Item[], user?: User) {
  if (!user) return []
  if (user.isAdmin) return items
  return items.filter((item) => !item.hidden)
}
Flatten the shape of the code.
优化前:
ts
function visibleItems(items: Item[], user?: User) {
  if (user) {
    if (user.isAdmin) {
      return items
    } else {
      return items.filter((item) => !item.hidden)
    }
  } else {
    return []
  }
}
优化后:
ts
function visibleItems(items: Item[], user?: User) {
  if (!user) return []
  if (user.isAdmin) return items
  return items.filter((item) => !item.hidden)
}
扁平化代码结构。

Do not split logic into too many tiny functions

不要将逻辑拆分为过多微小函数

Before:
ts
function checkout(cart: Cart) {
  validateCart(cart)
  const subtotal = calculateSubtotal(cart)
  const tax = calculateTax(subtotal)
  const total = calculateTotal(subtotal, tax)
  return finalizeCheckout(total)
}

function calculateTotal(subtotal: number, tax: number) {
  return subtotal + tax
}
After:
ts
function checkout(cart: Cart) {
  assert(cart.items.length > 0, "Cart is empty")

  const subtotal = sum(cart.items.map((item) => item.price))
  const tax = subtotal * TAX_RATE
  const total = subtotal + tax

  return finalizeCheckout(total)
}
Keep trivial logic close to use.
优化前:
ts
function checkout(cart: Cart) {
  validateCart(cart)
  const subtotal = calculateSubtotal(cart)
  const tax = calculateTax(subtotal)
  const total = calculateTotal(subtotal, tax)
  return finalizeCheckout(total)
}

function calculateTotal(subtotal: number, tax: number) {
  return subtotal + tax
}
优化后:
ts
function checkout(cart: Cart) {
  assert(cart.items.length > 0, "Cart is empty")

  const subtotal = sum(cart.items.map((item) => item.price))
  const tax = subtotal * TAX_RATE
  const total = subtotal + tax

  return finalizeCheckout(total)
}
将琐碎逻辑保留在使用位置附近。

PR gate

PR检查门限

Before opening or updating the PR, verify:
  • The diff contains only necessary changes.
  • Public APIs are stricter or simpler, not more configurable.
  • Modes are modelled explicitly.
  • Variants are handled exhaustively.
  • Assertions exist at boundaries.
  • The core logic trusts types instead of re-checking everything.
  • Functions read top-to-bottom with minimal nesting.
  • Argument counts are low.
  • Helpers exist only when they clarify.
  • The final version is shorter, plainer, and easier to skim.
If a choice is between clever and obvious, choose obvious.
If a choice is between flexible and simple, choose simple.
If a choice is between adding code and deleting code, prefer deleting code.
在创建或更新PR前,需验证以下内容:
  • 代码差异仅包含必要变更。
  • 公开API更严格或更精简,而非更具可配置性。
  • 模式已明确建模。
  • 所有变体已被全面处理。
  • 在边界处添加了assertions。
  • 核心逻辑信任类型,而非重复检查所有内容。
  • 函数从上到下可读,嵌套最少。
  • 参数数量较少。
  • 仅在辅助函数能提升清晰度时才保留。
  • 最终版本更简短、直白且易于快速浏览。
当需要在花哨与直白之间选择时,选择直白。
当需要在灵活与简单之间选择时,选择简单。
当需要在添加代码与删除代码之间选择时,优先删除代码。