coding-effectively

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Coding Effectively

高效编码

Required Sub-Skills

必备子技能

ALWAYS REQUIRED:
  • howto-functional-vs-imperative
    - Separate pure logic from side effects
  • defense-in-depth
    - Validate at every layer data passes through
CONDITIONAL: Use these sub-skills when applicable:
  • howto-code-in-typescript
    - TypeScript code
  • howto-develop-with-postgres
    - PostgreSQL database code
  • programming-in-react
    - React frontend code
  • writing-good-tests
    - Writing or reviewing tests
  • property-based-testing
    - Tests for serialization, validation, normalization, pure functions
必须掌握:
  • howto-functional-vs-imperative
    - 将纯逻辑与副作用分离
  • defense-in-depth
    - 在数据流经的每一层都进行验证
按需使用: 在适用场景下使用以下子技能:
  • howto-code-in-typescript
    - TypeScript 代码编写
  • howto-develop-with-postgres
    - PostgreSQL 数据库代码开发
  • programming-in-react
    - React 前端代码开发
  • writing-good-tests
    - 编写或评审测试用例
  • property-based-testing
    - 序列化、验证、归一化、纯函数相关测试

Property-Driven Design

属性驱动设计

When designing features, think about properties upfront. This surfaces design gaps early.
Discovery questions:
QuestionProperty TypeExample
Does it have an inverse operation?Roundtrip
decode(encode(x)) == x
Is applying it twice the same as once?Idempotence
f(f(x)) == f(x)
What quantities are preserved?InvariantsLength, sum, count unchanged
Is order of arguments irrelevant?Commutativity
f(a, b) == f(b, a)
Can operations be regrouped?Associativity
f(f(a,b), c) == f(a, f(b,c))
Is there a neutral element?Identity
f(x, 0) == x
Is there a reference implementation?Oracle
new(x) == old(x)
Can output be easily verified?Easy to verify
is_sorted(sort(x))
Common design questions these reveal:
  • "What about deleted/deactivated entities?"
  • "Case-sensitive or not?"
  • "Stable sort or not? Tie-breaking rules?"
  • "Which algorithm? Configurable?"
Surface these during design, not during debugging.
在设计功能时,提前考虑属性特性。这能尽早暴露设计缺陷。
探索性问题:
问题属性类型示例
是否存在逆运算?往返一致性
decode(encode(x)) == x
执行两次与执行一次结果是否相同?幂等性
f(f(x)) == f(x)
哪些量会被保留?不变量长度、总和、计数保持不变
参数顺序是否不影响结果?交换律
f(a, b) == f(b, a)
运算是否可重新组合?结合律
f(f(a,b), c) == f(a, f(b,c))
是否存在单位元?单位元特性
f(x, 0) == x
是否有参考实现?基准实现
new(x) == old(x)
输出是否易于验证?易验证性
is_sorted(sort(x))
这些问题能揭示的常见设计疑问:
  • "已删除/停用的实体该如何处理?"
  • "是否区分大小写?"
  • "是否为稳定排序?排序规则是什么?"
  • "使用哪种算法?是否可配置?"
在设计阶段就提出这些问题,而非等到调试时才发现。

Core Engineering Principles

核心工程原则

Correctness Over Convenience

正确性优先于便利性

Model the full error space. No shortcuts.
  • Handle all edge cases: race conditions, timing issues, partial failures
  • Use the type system to encode correctness constraints
  • Prefer compile-time guarantees over runtime checks where possible
  • When uncertain, explore and iterate rather than assume
Don't:
  • Simplify error handling to save time
  • Ignore edge cases because "they probably won't happen"
  • Use
    any
    or equivalent to bypass type checking
完整建模错误场景,绝不走捷径。
  • 处理所有边缘情况:竞态条件、时序问题、部分失败场景
  • 利用类型系统编码正确性约束
  • 尽可能优先选择编译时保障而非运行时检查
  • 不确定时,先探索迭代而非直接假设
禁止:
  • 为节省时间简化错误处理
  • 因“大概率不会发生”而忽略边缘情况
  • 使用
    any
    或等效方式绕过类型检查

Error Handling Philosophy

错误处理理念

Two-tier model:
  1. User-facing errors: Semantic exit codes, rich diagnostics, actionable messages
  2. Internal errors: Programming errors that may panic or use internal types
Error message format: Lowercase sentence fragments for "failed to {message}".
Good: failed to connect to database: connection refused
Bad:  Failed to Connect to Database: Connection Refused

Good: invalid configuration: missing required field 'apiKey'
Bad:  Invalid Configuration: Missing Required Field 'apiKey'
Lowercase fragments compose naturally:
"operation failed: " + error.message
reads correctly.
双层模型:
  1. 面向用户的错误:语义化退出码、丰富的诊断信息、可执行的提示消息
  2. 内部错误:可能触发 panic 或使用内部类型的编程错误
错误消息格式: 采用小写句子片段,格式为 "failed to {message}"。
正确示例: failed to connect to database: connection refused
错误示例:  Failed to Connect to Database: Connection Refused

正确示例: invalid configuration: missing required field 'apiKey'
错误示例:  Invalid Configuration: Missing Required Field 'apiKey'
小写片段可自然组合:
"operation failed: " + error.message
读起来更通顺。

Pragmatic Incrementalism

务实的增量式开发

  • Prefer specific, composable logic over abstract frameworks
  • Evolve design incrementally rather than perfect upfront architecture
  • Don't build for hypothetical future requirements
  • Document design decisions and trade-offs when making non-obvious choices
The rule of three applies to abstraction: Don't abstract until you've seen the pattern three times. Three similar lines of code is better than a premature abstraction.
  • 优先选择具体、可组合的逻辑而非抽象框架
  • 逐步演进设计,而非追求一蹴而就的完美架构
  • 不为假设性的未来需求提前构建功能
  • 做出非显而易见的选择时,记录设计决策和权衡点
抽象的三次原则: 不要过早抽象,直到你看到该模式出现三次。三段相似的代码比过早抽象更易维护。

File Organization

文件组织规范

Descriptive File Names Over Catch-All Files

使用描述性文件名,而非通用兜底文件

Name files by what they contain, not by generic categories.
Don't create:
  • utils.ts
    - Becomes a dumping ground for unrelated functions
  • helpers.ts
    - Same problem
  • common.ts
    - What isn't common?
  • misc.ts
    - Actively unhelpful
Do create:
  • string-formatting.ts
    - String manipulation utilities
  • date-arithmetic.ts
    - Date calculations
  • api-error-handling.ts
    - API error utilities
  • user-validation.ts
    - User input validation
Why this matters:
  • Discoverability: Developers find code by scanning file names
  • Cohesion: Related code stays together
  • Prevents bloat: Hard to add unrelated code to
    string-formatting.ts
  • Import clarity:
    import { formatDate } from './date-arithmetic'
    is self-documenting
When you're tempted to create utils.ts: Stop. Ask what the functions have in common. Name the file after that commonality.
根据文件内容命名,而非使用通用类别名称。
禁止创建:
  • utils.ts
    - 会沦为无关函数的“垃圾桶”
  • helpers.ts
    - 存在同样问题
  • common.ts
    - 什么内容不算“通用”?
  • misc.ts
    - 完全无帮助
建议创建:
  • string-formatting.ts
    - 字符串处理工具
  • date-arithmetic.ts
    - 日期计算工具
  • api-error-handling.ts
    - API 错误处理工具
  • user-validation.ts
    - 用户输入验证工具
为何重要:
  • 可发现性:开发者通过扫描文件名就能找到代码
  • 内聚性:相关代码集中在一起
  • 防止膨胀:很难将无关代码加入
    string-formatting.ts
  • 导入清晰:
    import { formatDate } from './date-arithmetic'
    自带文档属性
当你想创建utils.ts时: 停下来,思考这些函数的共性,根据共性命名文件。

Module Organization

模块组织

  • Keep module boundaries strict with restricted visibility
  • Platform-specific code in separate files:
    unix.ts
    ,
    windows.ts
    ,
    posix.ts
  • Use conditional compilation or runtime checks for platform branching
  • Test helpers in dedicated modules/files, not mixed with production code
  • 严格划分模块边界,限制可见性
  • 平台特定代码放在单独文件中:
    unix.ts
    windows.ts
    posix.ts
  • 使用条件编译或运行时检查处理平台分支逻辑
  • 测试辅助代码放在专用模块/文件中,不要与生产代码混合

Cross-Platform Principles

跨平台开发原则

Use OS-Native Logic

使用操作系统原生逻辑

Don't emulate Unix on Windows or vice versa. Use each platform's native patterns.
Bad: Trying to make Windows paths behave like Unix paths everywhere.
Good: Accept platform differences, handle them explicitly.
typescript
// Platform-specific behavior
if (process.platform === 'win32') {
  // Windows-native approach
} else {
  // POSIX approach
}
不要在 Windows 上模拟 Unix 行为,反之亦然。使用各平台的原生模式。
错误做法: 试图让 Windows 路径在所有场景下都表现得像 Unix 路径。
正确做法: 接受平台差异,显式处理这些差异。
typescript
// 平台特定行为
if (process.platform === 'win32') {
  // Windows 原生实现
} else {
  // POSIX 实现
}

Platform-Specific Files

平台专属文件

When platform differences are significant, use separate files:
process-spawn.ts        // Shared interface and logic
process-spawn-unix.ts   // Unix-specific implementation
process-spawn-windows.ts // Windows-specific implementation
当平台差异较大时,使用单独文件:
process-spawn.ts        // 共享接口和逻辑
process-spawn-unix.ts   // Unix 专属实现
process-spawn-windows.ts // Windows 专属实现

Document Platform Differences

记录平台差异

When behavior differs by platform, document it in comments:
typescript
// On Windows, this returns CRLF line endings.
// On Unix, this returns LF line endings.
// Callers should normalize if consistent output is needed.
function readTextFile(path: string): string { ... }
当行为因平台不同而变化时,在注释中记录:
typescript
// 在 Windows 系统中,此函数返回 CRLF 换行符。
// 在 Unix 系统中,此函数返回 LF 换行符。
// 如果需要一致的输出,调用方应自行归一化处理。
function readTextFile(path: string): string { ... }

Test on All Target Platforms

在所有目标平台上测试

Don't assume Unix behavior works on Windows. Test explicitly:
  • CI should run on all supported platforms
  • Platform-specific code paths need platform-specific tests
  • Document which platforms are supported
不要假设 Unix 行为能在 Windows 上正常工作。需显式测试:
  • CI 应在所有支持的平台上运行
  • 平台特定代码路径需要对应平台的测试用例
  • 记录支持的平台列表

Common Mistakes

常见错误

MistakeRealityFix
"Just put it in utils for now"utils.ts becomes 2000 lines of unrelated codeName files by purpose from the start
"Edge cases are rare"Edge cases cause production incidentsHandle them. Model the full error space.
"We might need this abstraction later"Premature abstraction is harder to remove than addWait for the third use case
"It works on my Mac"It may not work on Windows or LinuxTest on target platforms
"The type system is too strict"Strictness catches bugs at compile timeFix the type error, don't bypass it
错误做法实际问题修复方案
“先暂时放到utils里”utils.ts 会膨胀到2000行无关代码从一开始就根据用途命名文件
“边缘情况很少见”边缘情况会引发生产事故处理这些情况,完整建模错误场景
“以后可能需要这个抽象”过早抽象比后期添加抽象更难移除等第三次遇到相同模式时再抽象
“在我的Mac上能正常运行”在 Windows 或 Linux 上可能无法运行在目标平台上测试
“类型系统太严格了”严格的类型检查能在编译时捕获bug修复类型错误,不要绕过类型系统

Red Flags

危险信号

Stop and refactor when you see:
  • A
    utils.ts
    or
    helpers.ts
    file growing beyond 200 lines
  • Error handling that swallows errors or uses generic messages
  • Platform-specific code mixed with cross-platform code
  • Abstractions created for single use cases
  • Type assertions (
    as any
    ) to bypass the type system
  • Code that "works on my machine" but isn't tested cross-platform
遇到以下情况时,请立即停止并重构:
  • utils.ts
    helpers.ts
    文件超过200行
  • 错误处理吞掉错误或使用通用提示消息
  • 平台特定代码与跨平台代码混合在一起
  • 为单一使用场景创建抽象
  • 使用类型断言 (
    as any
    ) 绕过类型系统
  • “在我机器上能运行”但未进行跨平台测试的代码