The Constitution: Technical Preference Universal Principles
《项目章程:技术偏好通用准则》
⚖️ CRITICAL GOVERNANCE NOTICE
CRITICAL: BEFORE doing ANYTHING else, Read and Comprehend this document COMPLETELY.
MANDATORY COMPLIANCE CHECK:
Before executing your task, you must perform a "Constitution Lookup":
- IDENTIFY the domain of your task (e.g., API, DB, Auth, logging, etc.).
- SEARCH the relevant sections (e.g., grep "API Design").
- REVIEW Critical Constraints table for domain
- VERIFY your plan against the discipline and constraints before proceeding.
⚖️ 重要治理通知
重要提示: 开展任何工作前,请先完整阅读并理解本文件。
1. The Supremacy Clause & Conflict Resolution
强制合规检查:
When directives conflict or seem impossible to satisfy simultaneously, follow this precedence:
-
Security Principles [Non-Negotiable]
- OWASP Top 10 compliance
- Input validation and sanitization
- Authentication and authorization
- Secrets management
- No information leakage
- Action: Security ALWAYS wins. Refactor other requirements to satisfy security.
-
Testability & Modularity [Structural Law]
- I/O isolation (dependencies mockable in tests)
- Pure business logic (no side effects in core logic)
- Clear module boundaries with contracts
- Action: If code cannot be unit tested without external dependencies, refactor to add abstraction layer.
-
Error Handling Standards [Reliability Law]
- No silent failures
- Correlation IDs required
- JSON envelope format
- Resource cleanup in all paths
- Action: All error paths must be explicit. No exceptions.
-
Language Idioms [Implementation Preference]
- Use native patterns (defer, RAII, context managers)
- Follow community conventions
- Leverage standard library
- Action: Implement above laws idiomatically, don't blindly copy other languages.
-
Performance Optimizations [Measure First]
- Appropriate data structures
- Profile before optimizing
- Caching strategies
- Action: Only optimize with measured bottlenecks. Correctness > speed
执行任务前,你必须完成「章程核查」流程:
- 明确任务所属领域(例如:API、数据库、鉴权、日志等)。
- 检索相关章节(例如:grep "API Design")。
- 查阅对应领域的关键约束表。
- 推进工作前,对照规则与约束校验你的方案是否合规。
Escalation Protocol
1. 最高效力条款与冲突解决规则
If following a directive is impossible:
- STOP coding immediately
- Document the conflict:
- Which directives conflict?
- Why is compliance impossible?
- What are the tradeoffs?
- Propose alternatives:
- Option A: [approach + which rules satisfied/violated]
- Option B: [approach + which rules satisfied/violated]
- ...
- ASK human for decision
- Present the precedence hierarchy
- Recommend least-harmful deviation option with justification based on hierarchy
- Wait for explicit approval
CRITICAL: NEVER silently violate, If following directive is impossible, STOP and ask human
当不同指令出现冲突或无法同时满足时,按以下优先级处理:
-
安全原则 [不可协商]
- 符合OWASP Top 10规范
- 输入校验与内容清洗
- 身份认证与权限控制
- 密钥管理
- 无信息泄露
- 处理方式: 安全优先级最高,调整其他需求以满足安全要求。
-
可测试性与模块化 [结构性规则]
- I/O隔离(依赖可在测试中被mock)
- 纯业务逻辑(核心逻辑无副作用)
- 带契约的清晰模块边界
- 处理方式: 如果代码脱离外部依赖就无法进行单元测试,需要重构增加抽象层。
-
错误处理标准 [可靠性规则]
- 无静默失败
- 必须携带关联ID
- JSON信封格式
- 所有路径下的资源清理
- 处理方式: 所有错误路径必须显式处理,无例外。
-
语言惯用规范 [实现偏好]
- 使用原生模式(defer、RAII、上下文管理器)
- 遵循社区约定
- 优先使用标准库
- 处理方式: 用符合语言习惯的方式实现上述规则,不要盲目照搬其他语言的写法。
-
性能优化 [先测量再优化]
- 选用合适的数据结构
- 优化前先做性能分析
- 缓存策略
- 处理方式: 仅针对已测量确认的瓶颈做优化,正确性优先于速度。
Scenario 1: Framework Requires Inheritance (Architecture vs Language Idiom)
- Conflict: Django ORM requires model inheritance, but domain must be pure
- Hierarchy: Architecture (Level 2) > Framework idiom (Level 4)
- Resolution: Create Database Model (inherits ORM) + Pure Domain Entity + Mapper pattern
- PROCEED: Architecture wins
Scenario 2: Performance vs Security
- Conflict: Caching would speed up response but contains PII
- Hierarchy: Security (Level 1) > Performance (Level 5)
- Resolution: Sanitize PII before caching OR skip caching for PII endpoints
- PROCEED: Security wins
Scenario 3: Testing Pure Functions with Time Dependencies
- Conflict: Domain needs current timestamp but should be pure
- Hierarchy: Architecture purity (Level 2) maintained via Time Port (Level 4 idiom)
- Resolution: Inject Clock/Time interface as driven port, mock in tests
- PROCEED: Both satisfied through proper port design
如果确实无法遵循某条指令:
- 立即停止编码
- 记录冲突信息:
- 哪些指令存在冲突?
- 为什么无法满足合规要求?
- 涉及的取舍是什么?
- 提出替代方案:
- 选项A:[实现思路 + 满足/违反的规则]
- 选项B:[实现思路 + 满足/违反的规则]
- ...
- 咨询人类开发者做决策
- 展示优先级层级
- 基于层级推荐危害最小的偏离方案并说明理由
- 等待明确审批
重要提示:绝对不要静默违反规则,如果无法遵循指令,请立即停止并咨询人类开发者。
2. Critical Constraints Manifest (Context Triggers)
示例:
Agent Note: If the user query touches on these specific topics, prioritize the following internal rules over general knowledge. These are the High-Priority Constraints. Violating these is an automatic failure.
| Topic | Critical Constraint (Summary) |
|---|
| Architecture | Testability-First Design. Domain is pure, All code must be independently testable. Feature-based packaging. |
| Essential Software Design | SOLID Principles, Essential Design Practices(DRY, YAGNI, KISS), Code Organization Principles. |
| Error Handling | JSON format only (, , ). No silent failures. No swallowing. |
| Testing | 70/20/10 Pyramid. Mock Ports, not Internals. Integration tests use real infrastructure (Testcontainers). |
| Concurrency | Avoid shared memory. Use message passing/channels. Timeout ALL I/O operations. |
| Config | Hybrid approach: YAML for structure, for secrets. Fail fast on missing config. |
| API Design | Resource-based URLs. Standard HTTP status codes. Envelope response format (, ). |
| Security (Auth) | Deny by default. Server-side checks EVERY request. RBAC/ABAC. MFA for sensitive ops. Rate limit (5/15min). Bcrypt/Argon2 only. |
| Security (Data) | TLS 1.2+. Encrypt at rest. No secrets in code. PII redacted in logs. |
| Security (Input) | Validation: Validated Zod/Pydantic Schemas at ALL boundaries. Sanitization: Parameterized queries ONLY. Sanitize output. |
场景1:框架要求继承(架构 vs 语言惯用规范)
- 冲突:Django ORM要求模型继承,但领域层需要保持纯逻辑
- 优先级:架构(第2级)> 框架惯用规范(第4级)
- 解决方案:创建数据库模型(继承ORM) + 纯领域实体 + 映射器模式
- 结论:按架构规则执行
场景2:性能 vs 安全
- 冲突:缓存可以提升响应速度,但缓存内容包含个人可识别信息(PII)
- 优先级:安全(第1级)> 性能(第5级)
- 解决方案:缓存前清洗PII信息,或者包含PII的接口不使用缓存
- 结论:按安全规则执行
场景3:测试带时间依赖的纯函数
- 冲突:领域逻辑需要获取当前时间戳,但需要保持纯逻辑特性
- 优先级:通过时间端口(第4级惯用规范)维持架构纯净性(第2级)
- 解决方案:注入时钟/时间接口作为驱动端口,测试时进行mock
- 结论:通过合理的端口设计同时满足两个要求
3. Table of Contents
2. 关键约束清单(上下文触发规则)
Agent Note: Refer to the sections below for detailed implementation rules on all 16 topics.
Agent注意:如果用户查询涉及以下特定主题,优先遵循以下内部规则,优先级高于通用知识。这些是最高优先级约束,违反将直接判定为失败。
| 主题 | 关键约束(摘要) |
|---|
| 架构 | 可测试性优先设计,领域层为纯逻辑,所有代码必须可独立测试,按功能打包。 |
| 基础软件设计 | SOLID原则、基础设计实践(DRY、YAGNI、KISS)、代码组织原则。 |
| 错误处理 | 仅使用JSON格式(、、),无静默失败,不允许吞掉异常。 |
| 测试 | 70/20/10测试金字塔,Mock端口而非内部逻辑,集成测试使用真实基础设施(Testcontainers)。 |
| 并发 | 避免共享内存,使用消息传递/通道,所有I/O操作必须设置超时。 |
| 配置 | 混合方案:YAML存储结构,存储密钥,配置缺失时快速失败。 |
| API设计 | 基于资源的URL,标准HTTP状态码,信封响应格式(、)。 |
| 安全(鉴权) | 默认拒绝,服务端校验所有请求,RBAC/ABAC权限模型,敏感操作要求MFA,限流规则(5次/15分钟),仅使用Bcrypt/Argon2加密。 |
| 安全(数据) | TLS 1.2+,静态数据加密,代码中不允许硬编码密钥,日志中PII信息需脱敏。 |
| 安全(输入) | 校验: 所有边界都使用Zod/Pydantic Schema做校验。清洗: 仅使用参数化查询,输出内容做清洗。 |
Architectural Patterns - Testability-First Design
3. 目录
All code must be independently testable without running the full application or external infrastructure.
Agent注意:以下章节包含16个主题的详细实现规则,可参考查阅。
Universal Architecture Rules
架构模式 - 可测试性优先设计
Rule 1: I/O Isolation
核心原则
Problem: Tightly coupled I/O makes tests slow, flaky, and environment-dependent.
Solution: Abstract all I/O behind interfaces/contracts:
- Database queries
- HTTP calls (to external APIs)
- File system operations
- Time/randomness (for determinism)
- Message queues
Implementation Discovery:
- Search for existing abstraction patterns: , ,
find_symbol("Repository")
- Match the style (interface in Go, Protocol in Python, interface in TypeScript)
- Implement production adapter AND test adapter
Example (Go):
Go
// Contract (port)
type UserStore interface {
Create(ctx context.Context, user User) error
GetByEmail(ctx context.Context, email string) (*User, error)
}
// Production adapter
type PostgresUserStore struct { /* ... */ }
// Test adapter
type MockUserStore struct { /* ... */ }
Example (TypeScript/Vue):
typescript
// Contract (service layer)
export interface TaskAPI {
createTask(title: string): Promise<Task>;
getTasks(): Promise<Task[]>;
}
// Production adapter
export class EncoreTaskAPI implements TaskAPI { /* ... */ }
// Test adapter (vi.mock or manual)
export class MockTaskAPI implements TaskAPI { /* ... */ }
所有代码必须可独立测试,无需运行完整应用或依赖外部基础设施。
Rule 2: Pure Business Logic
通用架构规则
Problem: Business rules mixed with I/O are impossible to test without infrastructure.
Solution: Extract calculations, validations, transformations into pure functions:
- Input → Output, no side effects
- Deterministic: same input = same output
- No I/O inside business rules
Examples:
// ✅ Pure function - easy to test
func calculateDiscount(items []Item, coupon Coupon) (float64, error) {
// Pure calculation, returns value
}
// ❌ Impure - database call inside
func calculateDiscount(ctx context.Context, items []Item, coupon Coupon) (float64, error) {
validCoupon, err := db.GetCoupon(ctx, coupon.ID) // NO!
}
Correct approach:
// 1. Fetch dependencies first (in handler/service)
validCoupon, err := store.GetCoupon(ctx, coupon.ID)
// 2. Pass to pure logic
discount, err := calculateDiscount(items, validCoupon)
// 3. Persist result
err = store.SaveOrder(ctx, order)
问题: 紧耦合的I/O会导致测试变慢、不稳定、依赖环境。
解决方案: 将所有I/O抽象到接口/契约之后:
- 数据库查询
- HTTP调用(外部API)
- 文件系统操作
- 时间/随机数(保证确定性)
- 消息队列
实现发现流程:
- 查找现有抽象模式:、、
find_symbol("Repository")
- 匹配现有风格(Go的interface、Python的Protocol、TypeScript的interface)
- 实现生产适配器 和 测试适配器
示例(Go):
Go
// 契约(端口)
type UserStore interface {
Create(ctx context.Context, user User) error
GetByEmail(ctx context.Context, email string) (*User, error)
}
// 生产适配器
type PostgresUserStore struct { /* ... */ }
// 测试适配器
type MockUserStore struct { /* ... */ }
示例(TypeScript/Vue):
typescript
// 契约(服务层)
export interface TaskAPI {
createTask(title: string): Promise<Task>;
getTasks(): Promise<Task[]>;
}
// 生产适配器
export class EncoreTaskAPI implements TaskAPI { /* ... */ }
// 测试适配器(vi.mock 或手动实现)
export class MockTaskAPI implements TaskAPI { /* ... */ }
Rule 3: Module Boundaries
规则2:纯业务逻辑
Problem: Cross-module coupling makes changes ripple across codebase.
Solution: Feature-based organization with clear public interfaces:
- One feature = one directory
- Each module exposes a public API (exported functions/classes)
- Internal implementation details are private
- Cross-module calls only through public API
Directory Structure (Language-Agnostic):
/task
- public_api.{ext} # Exported interface
- business.{ext} # Pure logic
- store.{ext} # I/O abstraction (interface)
- postgres.{ext} # I/O implementation
- mock.{ext} # Test implementation
- test.{ext} # Unit tests (mocked I/O)
- integration.test.{ext} # Integration tests (real I/O)
Go Example:
/apps/backend/task
- task.go # Encore API endpoints (public)
- business.go # Pure domain logic
- store.go # interface UserStore
- postgres.go # implements UserStore
- task_test.go # Unit tests with MockStore
- task_integration_test.go # Integration with real DB
Vue Example:
/apps/frontend/src/features/task
- index.ts # Public exports
- task.service.ts # Business logic
- task.api.ts # interface TaskAPI
- task.api.encore.ts # implements TaskAPI
- task.store.ts # Pinia store (uses TaskAPI)
- task.service.spec.ts # Unit tests (mock API)
问题: 业务规则与I/O混合在一起,脱离基础设施就无法测试。
解决方案: 将计算、校验、转换逻辑提取为纯函数:
- 输入 → 输出,无副作用
- 确定性:相同输入一定得到相同输出
- 业务规则内部无I/O操作
示例:
// ✅ 纯函数 - 易于测试
func calculateDiscount(items []Item, coupon Coupon) (float64, error) {
// 纯计算,返回结果
}
// ❌ 非纯函数 - 内部包含数据库调用
func calculateDiscount(ctx context.Context, items []Item, coupon Coupon) (float64, error) {
validCoupon, err := db.GetCoupon(ctx, coupon.ID) // 禁止!
}
正确实现方式:
// 1. 先获取依赖(在handler/服务层)
validCoupon, err := store.GetCoupon(ctx, coupon.ID)
// 2. 传入纯逻辑处理
discount, err := calculateDiscount(items, validCoupon)
// 3. 持久化结果
err = store.SaveOrder(ctx, order)
Rule 4: Dependency Direction
规则3:模块边界
Principle: Dependencies point inward toward business logic.
┌─────────────────────────────────────┐
│ Infrastructure Layer │
│ (DB, HTTP, Files, External APIs) │
│ │
│ Depends on ↓ │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Contracts/Interfaces Layer │
│ (Abstract ports - no implementation)│
│ │
│ Depends on ↓ │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Business Logic Layer │
│ (Pure functions, domain rules) │
│ NO dependencies on infrastructure │
└─────────────────────────────────────┘
Never:
- Business logic imports database driver
- Domain entities import HTTP framework
- Core calculations import config files
Always:
- Infrastructure implements interfaces defined by business layer
- Business logic receives dependencies via injection
Package Structure Philosophy:
- Organize by FEATURE, not by technical layer
- Each feature is a vertical slice
- Enables modular growth, clear boundaries, and independent deployability
Universal Rule: Context → Feature → Layer
1. Level 1: Repository Scope (Conditional)
- Scenario A (Monorepo/Full-Stack): Root contains grouping distinct applications (e.g., , ).
- Scenario B (Single Service): Root IS the application. Do not create wrapper. Start directly at Level 2.
2. Level 2: Feature Organization
- Rule: Divide application into vertical business slices (e.g., , , ).
- Anti-Pattern: Do NOT organize by technical layer (e.g., , , ) at the top level.
问题: 跨模块耦合会导致修改扩散到整个代码库。
解决方案: 按功能组织,具备清晰的公共接口:
- 一个功能对应一个目录
- 每个模块暴露公共API(导出的函数/类)
- 内部实现细节私有
- 跨模块调用只能通过公共API
目录结构(语言无关):
/task
- public_api.{ext} # 导出的接口
- business.{ext} # 纯逻辑
- store.{ext} # I/O抽象(接口)
- postgres.{ext} # I/O实现
- mock.{ext} # 测试实现
- test.{ext} # 单元测试(mock I/O)
- integration.test.{ext} # 集成测试(真实I/O)
Go示例:
/apps/backend/task
- task.go # Encore API端点(公共)
- business.go # 纯领域逻辑
- store.go # interface UserStore
- postgres.go # implements UserStore
- task_test.go # 单元测试(使用MockStore)
- task_integration_test.go # 真实数据库集成测试
Vue示例:
/apps/frontend/src/features/task
- index.ts # 公共导出
- task.service.ts # 业务逻辑
- task.api.ts # interface TaskAPI
- task.api.encore.ts # implements TaskAPI
- task.store.ts # Pinia store(使用TaskAPI)
- task.service.spec.ts # 单元测试(mock API)
A. Standard Single Service (Backend, Microservice or MVC)
apps/
task/ # Feature: Task management
task.go # API handlers (public interface)
task_test.go # Unit tests (mocked dependencies)
business.go # Pure business logic
business_test.go # Unit tests (pure functions)
store.go # interface TaskStore
postgres.go # implements TaskStore
postgres_integration_test.go # Integration tests (real DB)
mock_store.go # Test implementation
migrations/
001_create_tasks.up.sql
order/ # Feature: Order management
...
B. Monorepo Layout (Multi-Stack):
*Use this structure when managing monolithic full-stack applications with backend, frontend, mobile in a single repository.
Clear Boundaries: Backend business logic is isolated from Frontend UI logic, even if they share the same repo
apps/
backend/ # Backend application source code
task/ # Feature: Task management
task.go # API handlers (public interface)
...
order/ # Feature: Order management
...
frontend/ # Frontend application source code
assets/ # Fonts, Images
components/ # Shared Component (Buttons, Inputs) - Dumb UI, No Domain Logic
BaseButton.vue
BaseInput.vue
layouts/ # App shells (Sidebar, Navbar wrappers)
utils/ # Date formatting, validation helpers
features/ # Business Features (Vertical Slices)
task/ # Feature: Task management
TaskForm.vue # Feature-specific components
TaskListItem.vue
TaskFilters.vue
index.ts # Public exports
task.service.ts # Business logic
task.api.ts # interface TaskAPI
task.api.encore.ts # Production implementation
task.store.ts # Pinia store
order/
...
This Feature/Domain/UI/API structure is framework-agnostic. It applies equally to React, Vue, Svelte, and Mobile (React Native/Flutter). 'UI' always refers to the framework's native component format (.tsx, .vue, .svelte, .dart).
原则: 依赖向内指向业务逻辑。
┌─────────────────────────────────────┐
│ 基础设施层 │
│ (数据库、HTTP、文件、外部API) │
│ │
│ 依赖 ↓ │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 契约/接口层 │
│ (抽象端口 - 无实现) │
│ │
│ 依赖 ↓ │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 业务逻辑层 │
│ (纯函数、领域规则) │
│ 无基础设施依赖 │
└─────────────────────────────────────┘
禁止:
- 业务逻辑导入数据库驱动
- 领域实体导入HTTP框架
- 核心计算导入配置文件
必须:
- 基础设施实现业务层定义的接口
- 业务逻辑通过注入接收依赖
包结构设计理念:
- 按功能组织,而非按技术层组织
- 每个功能是一个垂直切片
- 支持模块化增长、清晰边界、独立部署
通用规则:上下文 → 功能 → 层级
1. 第一层:仓库范围(可选)
- 场景A(Monorepo/全栈): 根目录包含,存放不同应用(例如:、)。
- 场景B(单服务): 根目录 就是 应用,不要创建包装,直接从第二层开始。
2. 第二层:功能组织
- 规则: 将应用拆分为垂直业务切片(例如:、、)。
- 反模式: 顶层不要按技术层组织(例如:、、)。
Pattern Discovery Protocol
布局示例
Before implementing ANY feature:
- Search existing patterns (MANDATORY):
find_symbol("Interface") OR find_symbol("Repository") OR find_symbol("Service")
- Examine 3 existing modules for consistency:
- How do they handle database access?
- Where are pure functions vs I/O operations?
- What testing patterns exist?
- Document pattern (80%+ consistency required):
- "Following pattern from [task, user, auth] modules"
- "X/Y modules use interface-based stores"
- "All tests use [MockStore, vi.mock, TestingPinia] pattern"
- If consistency <80%: STOP and report fragmentation to human.
A. 标准单服务(后端、微服务或MVC)
apps/
task/ # 功能:任务管理
task.go # API handler(公共接口)
task_test.go # 单元测试(mock依赖)
business.go # 纯业务逻辑
business_test.go # 单元测试(纯函数)
store.go # interface TaskStore
postgres.go # implements TaskStore
postgres_integration_test.go # 集成测试(真实数据库)
mock_store.go # 测试实现
migrations/
001_create_tasks.up.sql
order/ # 功能:订单管理
...
B. Monorepo布局(多技术栈):
当在单个仓库中管理包含后端、前端、移动端的单体全栈应用时使用此结构。
边界清晰:后端业务逻辑与前端UI逻辑隔离,即使它们在同一个仓库中
apps/
backend/ # 后端应用源代码
task/ # 功能:任务管理
task.go # API handler(公共接口)
...
order/ # 功能:订单管理
...
frontend/ # 前端应用源代码
assets/ # 字体、图片
components/ # 共享组件(按钮、输入框)- 纯UI,无领域逻辑
BaseButton.vue
BaseInput.vue
layouts/ # 应用外壳(侧边栏、导航栏包装)
utils/ # 日期格式化、校验工具
features/ # 业务功能(垂直切片)
task/ # 功能:任务管理
TaskForm.vue # 功能专属组件
TaskListItem.vue
TaskFilters.vue
index.ts # 公共导出
task.service.ts # 业务逻辑
task.api.ts # interface TaskAPI
task.api.encore.ts # 生产实现
task.store.ts # Pinia store
order/
...
这种功能/领域/UI/API结构与框架无关,同样适用于React、Vue、Svelte和移动端(React Native/Flutter)。'UI'始终指代框架的原生组件格式(.tsx、.vue、.svelte、.dart)。
Testing Requirements
模式发现协议
Unit Tests (must run without infrastructure):
- Mock all I/O dependencies
- Test business logic in isolation
- Fast (<100ms per test)
- 85%+ coverage of business paths
Integration Tests (must test real infrastructure):
- Use real database (Testcontainers, Firebase emulator)
- Test adapter implementations
- Verify contracts work end-to-end
- Cover all I/O adapters
Test Organization:
- Unit/Integration tests: Co-located with implementation
- E2E tests: Separate directory
实现任何功能前:
- 查找现有模式(强制):
find_symbol("Interface") OR find_symbol("Repository") OR find_symbol("Service")
- 检查3个现有模块保证一致性:
- 它们如何处理数据库访问?
- 纯函数和I/O操作分别放在哪里?
- 现有测试模式是什么?
- 记录模式(需要80%+一致性):
- "遵循[task、user、auth]模块的模式"
- "X/Y模块使用基于接口的存储层"
- "所有测试使用[MockStore、vi.mock、TestingPinia]模式"
- 如果一致性<80%:停止并向人类开发者报告碎片化问题。
Language-Specific Idioms
测试要求
How to achieve testability in each ecosystem:
| Language/Framework | Abstraction Pattern | Test Strategy |
|---|
| Go | Interface types, dependency injection | Table-driven tests, mock implementations |
| TypeScript/Vue | Interface types, service layer, Pinia stores | Vitest with , |
| TypeScript/React | Interface types, service layer, Context/hooks | Jest with mock factories, React Testing Library |
| Python | or abstract base classes | pytest with fixtures, monkeypatch |
| Rust | Traits, dependency injection | Unit tests with mock implementations, |
| Flutter/Dart | Abstract classes, dependency injection | package, widget tests |
单元测试(无需基础设施即可运行):
- Mock所有I/O依赖
- 隔离测试业务逻辑
- 速度快(每个测试<100ms)
- 业务路径覆盖率85%+
集成测试(测试真实基础设施):
- 使用真实数据库(Testcontainers、Firebase模拟器)
- 测试适配器实现
- 端到端验证契约可用
- 覆盖所有I/O适配器
测试组织:
- 单元/集成测试:与实现代码放在一起
- E2E测试:单独的目录
Enforcement Checklist
语言专属惯用规范
Before marking code complete, verify:
不同生态下实现可测试性的方式:
| 语言/框架 | 抽象模式 | 测试策略 |
|---|
| Go | 接口类型、依赖注入 | 表驱动测试、mock实现 |
| TypeScript/Vue | 接口类型、服务层、Pinia stores | 基于、的Vitest测试 |
| TypeScript/React | 接口类型、服务层、Context/hooks | 基于mock工厂、React Testing Library的Jest测试 |
| Python | 或抽象基类 | 基于fixtures、monkeypatch的pytest测试 |
| Rust | Trait、依赖注入 | 带mock实现的单元测试、 |
| Flutter/Dart | 抽象类、依赖注入 | 包、组件测试 |
Core Design Principles
关联原则
Single Responsibility Principle (SRP):
- Each class, module, or function should have ONE and ONLY ONE reason to change
- Generate focused, cohesive units of functionality
- If explaining what something does requires "and", it likely violates SRP
Open/Closed Principle (OCP):
- Software entities should be open for extension but closed for modification
- Design abstractions (interfaces, ports) that allow behavior changes without modifying existing code
- Use composition and dependency injection to enable extensibility
Liskov Substitution Principle (LSP):
- Subtypes must be substitutable for their base types without altering program correctness
- Inheritance hierarchies must maintain behavioral consistency
- If substituting a subclass breaks functionality, LSP is violated
Interface Segregation Principle (ISP):
- Clients should not be forced to depend on interfaces they don't use
- Create focused, role-specific interfaces rather than monolithic ones
- Many small, cohesive interfaces > one large, general-purpose interface
Dependency Inversion Principle (DIP):
- Depend on abstractions (interfaces/ports), not concretions (implementations/adapters)
- High-level modules should not depend on low-level modules; both should depend on abstractions
- Core principle enabling Testability-First architecture
Essential Design Practices
核心设计原则
DRY (Don't Repeat Yourself):
- Eliminate code duplication through proper abstraction, shared utilities, composable functions
- Each piece of knowledge should have single, authoritative representation
- Don't duplicate logic, algorithms, or business rules
YAGNI (You Aren't Gonna Need It):
CRITICAL: Code maintainability always prevail
- Avoid implementing functionality before it's actually required
- Don't add features based on speculation about future needs
- Build for today's requirements, refactor when needs change
KISS (Keep It Simple, Stupid):
CRITICAL: Code maintainability always prevail
- Prefer simple(simple to maintain), straightforward solutions over complex, clever ones
- Complexity should be justified by actual requirements, not theoretical flexibility
- Simple code is easier to test, maintain, and debug
Separation of Concerns:
- Divide program functionality into distinct sections with minimal overlap
- Each concern should be isolated in its own module or layer
Composition Over Inheritance:
- Favor object composition and delegation over class inheritance for code reuse
- Composition is more flexible and easier to test
- Use interfaces/traits for polymorphism instead of deep inheritance hierarchies
Principle of Least Astonishment:
- Code should behave in ways that users and maintainers naturally expect
- Avoid surprising or counterintuitive behavior
- Follow established conventions and patterns
单一职责原则(SRP):
- 每个类、模块或函数应该只有一个变更原因
- 生成内聚的功能单元
- 如果描述某个东西的功能需要用「和」,那它很可能违反了SRP
开闭原则(OCP):
- 软件实体应该对扩展开放,对修改关闭
- 设计抽象(接口、端口),无需修改现有代码即可变更行为
- 使用组合和依赖注入实现可扩展性
里氏替换原则(LSP):
- 子类型必须可以替换其基类型,且不会改变程序正确性
- 继承层级必须保持行为一致性
- 如果替换子类会破坏功能,就违反了LSP
接口隔离原则(ISP):
- 不应该强迫客户端依赖它们不需要的接口
- 创建聚焦、面向角色的接口,而非大一统接口
- 多个小的内聚接口 > 一个大的通用接口
依赖倒置原则(DIP):
- 依赖抽象(接口/端口),而非具体实现(实现/适配器)
- 高层模块不应该依赖低层模块,两者都应该依赖抽象
- 是实现可测试性优先架构的核心原则
Code Organization Principles
基础设计实践
- Generate small, focused functions with clear single purposes (typically 10-50 lines)
- Keep cognitive complexity low (cyclomatic complexity < 10 for most functions)
- Maintain clear boundaries between different layers (presentation, business logic, data access)
- Design for testability from the start, avoiding tight coupling that prevents testing
- Apply consistent naming conventions that reveal intent without requiring comments
DRY(不要重复自己):
- 通过合理的抽象、共享工具、可组合函数消除代码重复
- 每一项知识都应该有单一的权威表示
- 不要重复逻辑、算法或业务规则
YAGNI(你不会需要它):
重要提示: 代码可维护性始终优先
- 避免实现实际还不需要的功能
- 不要基于对未来需求的猜测添加功能
- 为当前需求构建,需求变化时再重构
KISS(保持简单):
重要提示: 代码可维护性始终优先
- 优先选择简单(易于维护)、直接的解决方案,而非复杂、巧妙的方案
- 复杂性应该由实际需求 justify,而非理论上的灵活性
- 简单代码更易于测试、维护和调试
关注点分离:
- 将程序功能划分为不同的部分,重叠最小
- 每个关注点应该隔离在自己的模块或层中
组合优于继承:
- 优先使用对象组合和委托实现代码复用,而非类继承
- 组合更灵活,更易于测试
- 使用接口/trait实现多态,而非深继承层级
最小惊讶原则:
- 代码的行为应该符合用户和维护者的自然预期
- 避免意外或反直觉的行为
- 遵循已建立的约定和模式
Error Handling Principles
代码组织原则
1. Validation Errors (4xx):
- User input doesn't meet requirements (wrong format, missing fields, out of range)
- Examples: Invalid email, password too short, required field missing
- Response: 400 Bad Request with detailed field-level errors
- User can fix: Yes, by correcting input
2. Business Errors (4xx):
- Domain rule violations (insufficient balance, duplicate email, order already shipped)
- Examples: Can't delete user with active orders, can't process refund after 30 days
- Response: 400/409/422 with business rule explanation
- User can fix: Maybe, depends on business context
3. Authentication Errors (401):
- Identity verification failed (invalid credentials, expired token, missing token)
- Response: 401 Unauthorized
- User can fix: Yes, by providing valid credentials
4. Authorization Errors (403):
- Permission denied (user identified but lacks permission)
- Response: 403 Forbidden
- User can fix: No, requires admin intervention
5. Not Found Errors (404):
- Resource doesn't exist or user lacks permission to know it exists
- Response: 404 Not Found
- User can fix: No
6. Infrastructure Errors (5xx):
- Database down, network timeout, external service failure, out of memory
- Response: 500/502/503 with generic message
- User can fix: No, system issue
- 编写小的、聚焦的函数,具备清晰的单一职责(通常10-50行)
- 保持认知复杂度低(大多数函数的圈复杂度<10)
- 不同层(展示层、业务逻辑层、数据访问层)之间保持清晰边界
- 从一开始就为可测试性设计,避免导致无法测试的紧耦合
- 应用一致的命名约定,无需注释即可表达意图
Recoverable vs Non-Recoverable Errors
错误处理原则
Recoverable (4xx - User can fix):
- Invalid input, missing fields, wrong format
- Action: Allow retry with corrected input
- Response: Detailed error message with guidance
Non-Recoverable (5xx - System issue):
- Database down, disk full, out of memory
- Action: Log details, alert ops team, return safe generic error
- Response: Generic message, correlation ID for support
1. 校验错误(4xx):
- 用户输入不符合要求(格式错误、字段缺失、超出范围)
- 示例:无效邮箱、密码过短、必填字段缺失
- 响应:400 Bad Request,附带详细的字段级别错误
- 用户可修复:是,修正输入即可
2. 业务错误(4xx):
- 违反领域规则(余额不足、邮箱重复、订单已发货)
- 示例:无法删除有活跃订单的用户、30天后无法处理退款
- 响应:400/409/422,附带业务规则说明
- 用户可修复:视业务场景而定
3. 认证错误(401):
- 身份验证失败(无效凭证、token过期、token缺失)
- 响应:401 Unauthorized
- 用户可修复:是,提供有效凭证即可
4. 授权错误(403):
- 权限不足(用户已认证但没有对应权限)
- 响应:403 Forbidden
- 用户可修复:否,需要管理员介入
5. 未找到错误(404):
- 资源不存在,或用户没有权限知道资源存在
- 响应:404 Not Found
- 用户可修复:否
6. 基础设施错误(5xx):
- 数据库宕机、网络超时、外部服务故障、内存不足
- 响应:500/502/503,附带通用提示信息
- 用户可修复:否,属于系统问题
Universal Error Handling Principles
可恢复 vs 不可恢复错误
1. Never Fail Silently:
- All errors must be handled explicitly (no empty catch blocks)
- If you catch an error, do something with it (log, return, transform, retry)
2. Fail Fast:
- Detect and report errors as early as possible
- Validate at system boundaries before processing
- Don't process invalid data hoping it'll work out
3. Provide Context:
- Include error codes, correlation IDs, actionable messages
- Enough information for debugging without exposing sensitive details
- Example: "Database query failed (correlation-id: abc-123)" not "SELECT * FROM users WHERE..."
4. Separate Concerns:
- Different handlers for different error types
- Business errors ≠ technical errors ≠ security errors
5. Resource Cleanup:
- Always clean up in error scenarios (close files, release connections, unlock resources)
- Use language-appropriate patterns (defer, finally, RAII, context managers)
6. No Information Leakage:
- Sanitize error messages for external consumption
- Don't expose stack traces, SQL queries, file paths, internal structure to users
- Log full details internally, show generic message externally
可恢复(4xx - 用户可修复):
- 无效输入、字段缺失、格式错误
- 处理方式:允许用修正后的输入重试
- 响应:附带指导信息的详细错误消息
不可恢复(5xx - 系统问题):
- 数据库宕机、磁盘已满、内存不足
- 处理方式:记录详细日志、告警运维团队、返回安全的通用错误
- 响应:通用消息、用于支持排查的关联ID
Application Error Object (Internal/Log Format)
通用错误处理原则
{
"status": "error",
"code": "VALIDATION_ERROR",
"message": "User-friendly error message",
"correlationId": "uuid-for-tracking",
"details": {
"field": "email",
"issue": "Invalid email format",
"provided": "invalid-email",
"expected": "valid email format (user@example.com)"
}
}
1. 永不静默失败:
- 所有错误必须显式处理(无空catch块)
- 如果你捕获了错误,必须做相应处理(记录日志、返回、转换、重试)
2. 快速失败:
- 尽可能早地检测并报告错误
- 处理前在系统边界做校验
- 不要处理无效数据抱有侥幸心理
3. 提供上下文:
- 包含错误码、关联ID、可操作的消息
- 提供足够的调试信息,同时不暴露敏感细节
- 示例:"数据库查询失败(关联ID: abc-123)",而非"SELECT * FROM users WHERE..."
4. 关注点分离:
- 不同错误类型使用不同的handler
- 业务错误 ≠ 技术错误 ≠ 安全错误
5. 资源清理:
- 错误场景下始终要清理资源(关闭文件、释放连接、解锁资源)
- 使用语言适配的模式(defer、finally、RAII、上下文管理器)
6. 无信息泄露:
- 对外返回的错误消息做脱敏处理
- 不要向用户暴露栈跟踪、SQL查询、文件路径、内部结构
- 内部记录完整详情,对外展示通用消息
Error Handling Checklist
应用错误对象(内部/日志格式)
{
"status": "error",
"code": "VALIDATION_ERROR",
"message": "用户友好的错误消息",
"correlationId": "用于追踪的uuid",
"details": {
"field": "email",
"issue": "无效的邮箱格式",
"provided": "invalid-email",
"expected": "有效的邮箱格式(user@example.com)"
}
}
Related Principles
错误处理检查清单
Concurrency and Threading Principles
关联原则
I/O-Bound Operations (async/await, event loops):
- Network requests, file I/O, database queries
- Waiting for external responses dominates execution time
- Use: Asynchronous I/O, event-driven concurrency, coroutines
CPU-Bound Operations (threads, parallel processing):
- Heavy computation, data processing, video encoding
- CPU cycles dominate execution time
- Use: OS threads, thread pools, parallel workers
Don't Over-Use Concurrency:
- Adds significant complexity (race conditions, deadlocks, debugging difficulty)
- Use only when there's measurable performance benefit
- Profile first, optimize second
Universal Concurrency Principles
并发与多线程原则
1. Avoid Race Conditions
What is a race condition:
- Multiple threads access shared data concurrently
- At least one thread writes/modifies the data
- No synchronization mechanism in place
- Result depends on unpredictable thread execution timing
Prevention strategies:
- Synchronization: Locks, mutexes, semaphores
- Immutability: Immutable data is thread-safe by default
- Message passing: Send data between threads instead of sharing
- Thread-local storage: Each thread has its own copy
Detection:
- Go: Run with flag (race detector)
- Rust: Miri tool for undefined behavior detection
- C/C++: ThreadSanitizer (TSan)
- Java: JCStress, FindBugs
2. Prevent Deadlocks
What is a deadlock:
- Two or more threads waiting for each other indefinitely
- Example: Thread A holds Lock 1, waits for Lock 2; Thread B holds Lock 2, waits for Lock 1
Four conditions (ALL must be true for deadlock):
- Mutual exclusion: Resources held exclusively (locks)
- Hold and wait: Holding one resource while waiting for another
- No preemption: Can't force unlock
- Circular wait: A waits for B, B waits for A
Prevention (break any one condition):
- Lock ordering: Always acquire locks in same order
- Timeout: Use try_lock with timeout, back off and retry
- Avoid nested locks: Don't hold multiple locks simultaneously
- Use lock-free data structures when possible
3. Prefer Immutability
- Immutable data = thread-safe by default (no synchronization needed)
- Share immutable data freely between threads
- Use immutable data structures where possible (Rust default, functional languages)
- If data must change, use message passing instead of shared mutable state
4. Message Passing Over Shared Memory
- "Don't communicate by sharing memory; share memory by communicating" (Go proverb)
- Send data through channels/queues instead of accessing shared memory
- Reduces need for locks and synchronization
- Easier to reason about and test
5. Graceful Degradation
- Handle concurrency errors gracefully (timeouts, retries, circuit breakers)
- Don't crash entire application on one thread failure
- Use supervisors/monitors for fault tolerance (Erlang/Elixir actor model)
- Implement backpressure for producer-consumer scenarios
I/O密集型操作(async/await、事件循环):
- 网络请求、文件I/O、数据库查询
- 等待外部响应占主导执行时间
- 使用:异步I/O、事件驱动并发、协程
CPU密集型操作(线程、并行处理):
- 重型计算、数据处理、视频编码
- CPU周期占主导执行时间
- 使用:OS线程、线程池、并行工作器
不要过度使用并发:
- 会增加显著的复杂性(竞态条件、死锁、调试难度)
- 只有在有可测量的性能收益时才使用
- 先做性能分析,再优化
Concurrency Models by Use Case
通用并发原则
- I/O-bound: async/await, event loops, coroutines, green threads
- CPU-bound: OS threads, thread pools, parallel processing
- Actor model: Erlang/Elixir actors, Akka (message passing, isolated state)
- CSP (Communicating Sequential Processes): Go channels, Rust channels
1. 避免竞态条件
什么是竞态条件:
- 多个线程同时访问共享数据
- 至少有一个线程写入/修改数据
- 没有同步机制
- 结果取决于不可预测的线程执行时序
预防策略:
- 同步:锁、互斥量、信号量
- 不可变性:不可变数据默认是线程安全的
- 消息传递:在线程之间发送数据,而非共享
- 线程本地存储:每个线程有自己的副本
检测:
- Go:带 flag运行(竞态检测器)
- Rust:Miri工具检测未定义行为
- C/C++:ThreadSanitizer(TSan)
- Java:JCStress、FindBugs
2. 预防死锁
什么是死锁:
- 两个或多个线程无限期互相等待
- 示例:线程A持有锁1,等待锁2;线程B持有锁2,等待锁1
四个必要条件(死锁发生必须全部满足):
- 互斥:资源被独占持有(锁)
- 持有并等待:持有一个资源的同时等待另一个
- 不可抢占:不能强制解锁
- 循环等待:A等待B,B等待A
预防(打破任意一个条件即可):
- 锁顺序:始终按相同顺序获取锁
- 超时:使用带超时的try_lock,退避后重试
- 避免嵌套锁:不要同时持有多个锁
- 尽可能使用无锁数据结构
3. 优先使用不可变性
- 不可变数据 = 默认线程安全(无需同步)
- 可以在线程之间自由共享不可变数据
- 尽可能使用不可变数据结构(Rust默认、函数式语言)
- 如果数据必须变更,使用消息传递而非共享可变状态
4. 消息传递优于共享内存
- "不要通过共享内存来通信,要通过通信来共享内存"(Go谚语)
- 通过通道/队列发送数据,而非访问共享内存
- 减少对锁和同步的需求
- 更易于理解和测试
5. 优雅降级
- 优雅处理并发错误(超时、重试、断路器)
- 不要因为单个线程失败导致整个应用崩溃
- 使用监管器/监控器实现容错(Erlang/Elixir actor模型)
- 生产者-消费者场景下实现背压
Testing Concurrent Code
按场景选择并发模型
- Write unit tests with controlled concurrency (deterministic execution)
- Test timeout scenarios and resource exhaustion
- Test thread pool full, queue full scenarios
- I/O密集型: async/await、事件循环、协程、绿色线程
- CPU密集型: OS线程、线程池、并行处理
- Actor模型: Erlang/Elixir actors、Akka(消息传递、隔离状态)
- CSP(通信顺序进程): Go通道、Rust通道
- 编写可控并发的单元测试(确定性执行)
- 测试超时场景和资源耗尽场景
- 测试线程池满、队列满场景
Resource and Memory Management Principles
关联原则
Universal Resource Management Rules
1. Always Clean Up Resources
Resources requiring cleanup:
- Files, network connections, database connections
- Locks, semaphores, mutexes
- Memory allocations (in manual-memory languages)
- OS handles, GPU resources
Clean up in ALL paths:
- Success path: Normal completion
- Error path: Exception thrown, error returned
- Early return path: Guard clauses, validation failures
Use language-appropriate patterns:
- Go: defer statements
- Rust: Drop trait (RAII)
- Python: context managers (with statement)
- TypeScript: try/finally
- Java: try-with-resources
2. Timeout All I/O Operations
Why timeout:
- Network requests can hang indefinitely
- Prevents resource exhaustion (connections, threads)
- Provides predictable failure behavior
Timeout recommendations:
- Network requests: 30s default, shorter (5-10s) for interactive
- Database queries: 10s default, configure per query complexity
- File operations: Usually fast, but timeout on network filesystems
- Message queue operations: Configurable, avoid indefinite blocking
3. Pool Expensive Resources
Resources to pool:
- Database connections: Pool size 5-20 per app instance
- HTTP connections: Reuse with keep-alive
- Thread pools: Size based on CPU count (CPU-bound) or I/O wait (I/O-bound)
Benefits:
- Reduces latency (no connection setup overhead)
- Limits resource consumption (cap on max connections)
- Improves throughput (reuse vs create new)
Connection Pool Best Practices:
- Minimum connections: 5 (ensures pool is warm)
- Maximum connections: 20-50 (prevents overwhelming database)
- Idle timeout: Close connections idle >5-10 minutes
- Validation: Test connections before use (avoid broken connections)
- Monitoring: Track utilization, wait times, timeout rates
4. Avoid Resource Leaks
What is a leak:
- Acquire resource (open file, allocate memory, get connection)
- Never release it (forget to close, exception prevents cleanup)
- Eventually exhaust system resources (OOM, max connections, file descriptors)
Detection:
- Monitor open file descriptors, connection counts, memory usage over time
- Run long-duration tests, verify resource counts stay stable
- Use leak detection tools (valgrind, ASan, heap profilers)
Prevention:
- Use language patterns that guarantee cleanup (RAII, defer, context managers)
- Never rely on manual cleanup alone (use language features)
5. Handle Backpressure
Problem: Producer faster than consumer
- Queue grows unbounded → memory exhaustion
- System becomes unresponsive under load
Solutions:
- Bounded queues: Fixed size, block or reject when full
- Rate limiting: Limit incoming request rate
- Flow control: Consumer signals producer to slow down
- Circuit breakers: Stop accepting requests when overwhelmed
- Drop/reject: Fail fast when overloaded (better than crashing)
Memory Management by Language Type
资源与内存管理原则
Garbage Collected (Go, Java, Python, JavaScript, C#):
- Memory automatically freed by GC
- Still must release non-memory resources (files, connections, locks)
- Be aware of GC pauses in latency-sensitive applications
- Profile memory usage to find leaks (retained references preventing GC)
Manual Memory Management (C, C++):
- Explicit malloc/free or new/delete
- Use RAII pattern in C++ (Resource Acquisition Is Initialization)
- Avoid manual management in modern C++ (use smart pointers: unique_ptr, shared_ptr)
Ownership-Based (Rust):
- Compiler enforces memory safety at compile time
- No GC pauses, no manual management
- Ownership rules prevent leaks and use-after-free automatically
- Use reference counting (Arc, Rc) for shared ownership
1. 始终清理资源
需要清理的资源:
- 文件、网络连接、数据库连接
- 锁、信号量、互斥量
- 内存分配(手动内存管理语言)
- OS句柄、GPU资源
所有路径下都要清理:
- 成功路径:正常完成
- 错误路径:抛出异常、返回错误
- 提前返回路径:守卫子句、校验失败
使用语言适配的模式:
- Go:defer语句
- Rust:Drop trait(RAII)
- Python:上下文管理器(with语句)
- TypeScript:try/finally
- Java:try-with-resources
2. 所有I/O操作设置超时
为什么要超时:
- 网络请求可能无限期挂起
- 防止资源耗尽(连接、线程)
- 提供可预测的失败行为
超时建议:
- 网络请求:默认30s,交互场景更短(5-10s)
- 数据库查询:默认10s,根据查询复杂度配置
- 文件操作:通常很快,但网络文件系统需要设置超时
- 消息队列操作:可配置,避免无限阻塞
3. 池化昂贵资源
需要池化的资源:
- 数据库连接:每个应用实例池大小5-20
- HTTP连接:通过keep-alive复用
- 线程池:大小基于CPU核数(CPU密集型)或I/O等待(I/O密集型)
优势:
- 降低延迟(无连接建立开销)
- 限制资源消耗(最大连接数上限)
- 提升吞吐量(复用而非新建)
连接池最佳实践:
- 最小连接数:5(保证池预热)
- 最大连接数:20-50(避免压垮数据库)
- 空闲超时:关闭空闲超过5-10分钟的连接
- 校验:使用前测试连接(避免无效连接)
- 监控:追踪利用率、等待时间、超时率
4. 避免资源泄露
什么是泄露:
- 获取资源(打开文件、分配内存、获取连接)
- 从未释放(忘记关闭、异常阻止清理)
- 最终耗尽系统资源(OOM、最大连接数、文件描述符)
检测:
- 长期监控打开的文件描述符、连接数、内存使用
- 运行长时间测试,验证资源计数保持稳定
- 使用泄露检测工具(valgrind、ASan、堆分析器)
预防:
- 使用可保证清理的语言模式(RAII、defer、上下文管理器)
- 不要仅依赖手动清理(使用语言特性)
5. 处理背压
问题: 生产者速度快于消费者
解决方案:
- 有界队列:固定大小,满时阻塞或拒绝
- 限流:限制传入请求速率
- 流控:消费者通知生产者减速
- 断路器:过载时停止接收请求
- 丢弃/拒绝:过载时快速失败(好过崩溃)
Related Principles
按语言类型区分的内存管理
垃圾回收语言(Go、Java、Python、JavaScript、C#):
- GC自动释放内存
- 仍然需要释放非内存资源(文件、连接、锁)
- 延迟敏感应用需要注意GC暂停
- 分析内存使用以查找泄露(阻止GC的保留引用)
手动内存管理语言(C、C++):
- 显式malloc/free或new/delete
- C++中使用RAII模式(资源获取即初始化)
- 现代C++中避免手动管理(使用智能指针:unique_ptr、shared_ptr)
基于所有权的语言(Rust):
- 编译器在编译时强制内存安全
- 无GC暂停、无手动管理
- 所有权规则自动防止泄露和释放后使用
- 共享所有权使用引用计数(Arc、Rc)
API Design Principles
关联原则
Resource-Based URLs:
- Use plural nouns for resources: ,
- Hierarchical relationships:
/api/{version}/users/:userId/orders
- Avoid verbs in URLs: ❌ → ✅
HTTP Methods:
- GET: Read/retrieve resource (safe, idempotent, cacheable)
- POST: Create new resource (not idempotent)
- PUT: Replace entire resource (idempotent)
- PATCH: Partial update (idempotent)
- DELETE: Remove resource (idempotent)
HTTP Status Codes:
- 200 OK: Success (GET, PUT, PATCH)
- 201 Created: Resource created successfully (POST)
- 204 No Content: Success with no response body (DELETE)
- 400 Bad Request: Invalid input, validation failure
- 401 Unauthorized: Authentication required or failed
- 403 Forbidden: Authenticated but insufficient permissions
- 404 Not Found: Resource doesn't exist
- 409 Conflict: Conflict with current state (duplicate)
- 422 Unprocessable Entity: Validation errors
- 429 Too Many Requests: Rate limit exceeded
- 500 Internal Server Error: Unexpected server error
- 502 Bad Gateway: Upstream service failure
- 503 Service Unavailable: Temporary unavailability
Versioning:
- URL path versioning: e.g. (explicit, clear)
Pagination:
- Limit results per page (default 20, max 100)
- Cursor-based: (better for real-time data)
- Offset-based: (simpler, less accurate for changing data)
Filtering and Sorting:
- Filtering:
?status=active&role=admin
- Sorting:
?sort=created_at:desc,name:asc
- Searching:
Response Format:
{
"data": { /* resource or array of resources */ },
"meta": {
"total": 100,
"page": 1,
"perPage": 20
},
"links": {
"self": "/api/v1/users?page=1",
"next": "/api/v1/users?page=2",
"prev": null
}
}
API Error Response Format:
All API errors must follow a consistent envelope structure, matching the success format where possible or using a standard error envelope.
{
"status": "error", // Transport: Always "error" or "fail"
"code": 400, // Transport: Redundant HTTP Status
"error": { // Domain: The actual business problem
"code": "VALIDATION_ERROR", // Machine-readable business code (UPPER_SNAKE)
"message": "Invalid email format", // Human-readable message
"details": { // Optional: Structured context
"field": "email",
"reason": "Must be a valid address"
},
"correlationId": "req-1234567890", // Ops: Traceability
"doc_url": "https://..." // Optional: Help link
}
}
Related Principles
API设计原则
基于资源的URL:
- 资源使用复数名词:、
- 层级关系:
/api/{version}/users/:userId/orders
- URL中避免动词: ❌ → ✅
HTTP方法:
- GET:读取/检索资源(安全、幂等、可缓存)
- POST:创建新资源(非幂等)
- PUT:替换整个资源(幂等)
- PATCH:部分更新(幂等)
- DELETE:删除资源(幂等)
HTTP状态码:
- 200 OK:成功(GET、PUT、PATCH)
- 201 Created:资源创建成功(POST)
- 204 No Content:成功且无响应体(DELETE)
- 400 Bad Request:无效输入、校验失败
- 401 Unauthorized:需要认证或认证失败
- 403 Forbidden:已认证但权限不足
- 404 Not Found:资源不存在
- 409 Conflict:与当前状态冲突(重复)
- 422 Unprocessable Entity:校验错误
- 429 Too Many Requests:超出限流
- 500 Internal Server Error:意外服务端错误
- 502 Bad Gateway:上游服务故障
- 503 Service Unavailable:临时不可用
版本控制:
分页:
- 每页结果限制(默认20,最大100)
- 游标分页:(更适合实时数据)
- 偏移分页:(更简单,数据变化时准确性较低)
过滤与排序:
- 过滤:
?status=active&role=admin
- 排序:
?sort=created_at:desc,name:asc
- 搜索:
响应格式:
{
"data": { /* 资源或资源数组 */ },
"meta": {
"total": 100,
"page": 1,
"perPage": 20
},
"links": {
"self": "/api/v1/users?page=1",
"next": "/api/v1/users?page=2",
"prev": null
}
}
API错误响应格式:
所有API错误必须遵循一致的信封结构,尽可能匹配成功格式,或使用标准错误信封。
{
"status": "error", // 传输层:始终为"error"或"fail"
"code": 400, // 传输层:冗余HTTP状态
"error": { // 领域层:实际业务问题
"code": "VALIDATION_ERROR", // 机器可读业务码(大写下划线格式)
"message": "无效的邮箱格式", // 人类可读消息
"details": { // 可选:结构化上下文
"field": "email",
"reason": "必须是有效的地址"
},
"correlationId": "req-1234567890", // 运维层:可追溯性
"doc_url": "https://..." // 可选:帮助链接
}
}
Unit Tests (70% of tests):
- What: Test domain logic in isolation with mocked dependencies
- Speed: Fast (<100ms per test)
- Scope: Single function, class, or module
- Dependencies: All external dependencies mocked (repositories, APIs, time, random)
- Coverage Goal: >85% of domain logic
Integration Tests (20% of tests):
- What: Test adapters against real infrastructure
- Speed: Medium (100ms-5s per test)
- Scope: Component interaction with infrastructure (database, cache, message queue)
- Dependencies: Real infrastructure via Testcontainers
- Coverage Goal: All adapter implementations, critical integration points
End-to-End Tests (10% of tests):
- What: Test complete user journeys through all layers
- Speed: Slow (5s-30s per test)
- Scope: Full system from HTTP request to database and back
- Dependencies: Entire system running (or close approximation)
- Coverage Goal: Happy paths, critical business flows
Test-Driven Development (TDD)
测试策略
Red-Green-Refactor Cycle:
- Red: Write a failing test for next bit of functionality
- Green: Write minimal code to make test pass
- Refactor: Clean up code while keeping tests green
- Repeat: Next test
Benefits:
- Tests written first ensure testable design
- Comprehensive test coverage (code without test doesn't exist)
- Faster development (catch bugs immediately, not in QA)
- Better design (forces thinking about interfaces before implementation)
单元测试(70%的测试):
- 测试范围: 隔离测试领域逻辑,依赖全部mock
- 速度: 快(每个测试<100ms)
- 覆盖范围: 单个函数、类或模块
- 依赖: 所有外部依赖都被mock(仓库、API、时间、随机数)
- 覆盖率目标: 领域逻辑>85%
集成测试(20%的测试):
- 测试范围: 测试适配器与真实基础设施的交互
- 速度: 中等(每个测试100ms-5s)
- 覆盖范围: 组件与基础设施的交互(数据库、缓存、消息队列)
- 依赖: 通过Testcontainers使用真实基础设施
- 覆盖率目标: 所有适配器实现、关键集成点
端到端测试(10%的测试):
- 测试范围: 测试跨所有层的完整用户旅程
- 速度: 慢(每个测试5s-30s)
- 覆盖范围: 从HTTP请求到数据库再返回的完整系统
- 依赖: 整个系统运行(或近似运行)
- 覆盖率目标: happy path、关键业务流程
Test Doubles Strategy
测试驱动开发(TDD)
Unit Tests: Use mocks/stubs for all driven ports
- Mock repositories return pre-defined data
- Mock external APIs return successful responses
- Mock time/random for deterministic tests
- Control test environment completely
Integration Tests: Use real infrastructure
- Testcontainers spins up PostgreSQL, Redis, message queues
- Firebase emulator spins up Firebase Authentication, Cloud Firestore, Realtime Database, Cloud Storage for Firebase, Firebase Hosting, Cloud Functions, Pub/Sub, and Firebase Extensions
- Test actual database queries, connection handling, transactions
- Verify adapter implementations work with real services
Best Practice:
- Generate at least 2 implementations per driven port:
- Production adapter (PostgreSQL, GCP GCS, etc.)
- Test adapter (in-memory, fake implementation)
红-绿-重构循环:
- 红: 为下一个功能点编写失败的测试
- 绿: 编写最小化代码让测试通过
- 重构: 清理代码,保持测试通过
- 重复: 下一个测试
优势:
- 先写测试保证可测试的设计
- 全面的测试覆盖率(没有测试的代码不存在)
- 更快的开发速度(立即发现bug,而非到QA阶段才发现)
- 更好的设计(强制在实现前思考接口)
Universal Rule: Co-locate implementation tests; Separate system tests.
1. Unit & Integration Tests (Co-located)
- Rule: Place tests next to the file they test.
- Why: Keeps tests visible, encourages maintenance, and supports refactoring (moving a file moves its tests).
- Naming Convention Example:
- TS/JS: (Unit), (Integration)
- Go: (Unit), (Integration)
- Python: (Unit), (Integration)
- Java: (Unit), (Integration)
You must strictly follow the convention for the target language. Do not mix
and
suffixes in the same application context.
2. End-to-End Tests (Separate)
- Rule: Place in a dedicated folder
- Single Service: at project root
- Monorepo: subdivided by test scope:
- for full API flow E2E tests (HTTP → Database)
- for full-stack E2E tests (Browser → Backend → Database)
- Why: E2E tests cross boundaries and don't belong to a single feature.
- Naming: Follow
{feature}-{ui/api}.e2e.test.{ext}
(Universal - configure test runner to match this pattern )
- Example:
user-registration-api.e2e.test.ts
# Full API flow: HTTP → DB
user-registration-ui.e2e.test.ts
# Full-stack: Browser → Backend → DB
Directory Layout Example:
单元测试: 所有驱动端口使用mocks/stubs
- Mock仓库返回预定义数据
- Mock外部API返回成功响应
- Mock时间/随机数实现确定性测试
- 完全控制测试环境
集成测试: 使用真实基础设施
- Testcontainers启动PostgreSQL、Redis、消息队列
- Firebase模拟器启动Firebase Authentication、Cloud Firestore、Realtime Database、Cloud Storage for Firebase、Firebase Hosting、Cloud Functions、Pub/Sub和Firebase Extensions
- 测试真实的数据库查询、连接处理、事务
- 验证适配器实现可与真实服务正常工作
最佳实践:
- 每个驱动端口至少实现2个版本:
- 生产适配器(PostgreSQL、GCP GCS等)
- 测试适配器(内存、假实现)
A. Backend (Go - Feature-Based)
测试组织
apps/
backend/
task/
task.go # API handlers (public interface)
task_test.go # Unit tests (mocked dependencies)
business.go # Pure business logic
business_test.go # Unit tests (pure functions)
store.go # interface TaskStore
postgres.go # implements TaskStore
postgres_integration_test.go # Integration tests (real DB)
mock_store.go # Test implementation
migrations/
001_create_tasks.up.sql
user/
user.go
user_test.go
store.go
postgres.go
postgres_integration_test.go
e2e/
task_crud_api.e2e.test.go # Full API flow
通用规则: 实现测试与代码放在一起,系统测试单独存放。
1. 单元与集成测试(同目录存放)
- 规则: 测试放在它们测试的文件旁边。
- 原因: 保持测试可见,鼓励维护,支持重构(移动文件时同步移动测试)。
- 命名约定示例:
- TS/JS: (单元测试)、(集成测试)
- Go: (单元测试)、(集成测试)
- Python: (单元测试)、(集成测试)
- Java: (单元测试)、(集成测试)
必须严格遵循目标语言的约定,同一个应用上下文中不要混合使用
和
后缀。
2. 端到端测试(单独存放)
- 规则: 放在专用的文件夹
- 单服务: 项目根目录下的
- Monorepo: 按测试范围细分:
- 存放全API流E2E测试(HTTP → 数据库)
- 存放全栈E2E测试(浏览器 → 后端 → 数据库)
- 原因: E2E测试跨边界,不属于单个功能。
- 命名: 遵循
{feature}-{ui/api}.e2e.test.{ext}
(通用 - 配置测试运行器匹配模式)
- 示例:
user-registration-api.e2e.test.ts
# 全API流:HTTP → DB
user-registration-ui.e2e.test.ts
# 全栈:浏览器 → 后端 → DB
目录布局示例:
B. Frontend (Vue - Feature-Sliced)
A. 后端(Go - 按功能组织)
apps/
frontend/
src/
features/
task/
index.ts # Public exports
task.service.ts # Business logic
task.service.spec.ts # Unit tests
task.api.ts # interface TaskAPI
task.api.encore.ts # Production implementation
task.api.mock.ts # Test implementation
task.store.ts # Pinia store
task.store.spec.ts # Store unit tests
auth/
...
components/
TaskInput.vue
TaskInput.spec.ts # Component unit tests
e2e/
task-management-flow.e2e.test.ts # Full UI journey
apps/
backend/
task/
task.go # API handler(公共接口)
task_test.go # 单元测试(mock依赖)
business.go # 纯业务逻辑
business_test.go # 单元测试(纯函数)
store.go # interface TaskStore
postgres.go # implements TaskStore
postgres_integration_test.go # 集成测试(真实数据库)
mock_store.go # 测试实现
migrations/
001_create_tasks.up.sql
user/
user.go
user_test.go
store.go
postgres.go
postgres_integration_test.go
e2e/
task_crud_api.e2e.test.go # 全API流
C. Monorepo (Multi-Stack)
B. 前端(Vue - 按功能切片)
apps/
backend/
task/
...
frontend/
src/features/task/
...
e2e/ # Shared E2E suite
api/
task-crud-api.e2e.test.ts # Backend-only E2E
ui/
checkout-flow.e2e.test.ts # Full-stack E2E
Key Principles:
- Unit/Integration tests: Co-located with implementation
- E2E tests: Separate directory (crosses boundaries)
- Test doubles: Co-located with interface (mock_store.go, taskAPI.mock.ts)
- Pattern consistency: All features follow same structure
apps/
frontend/
src/
features/
task/
index.ts # 公共导出
task.service.ts # 业务逻辑
task.service.spec.ts # 单元测试
task.api.ts # interface TaskAPI
task.api.encore.ts # 生产实现
task.api.mock.ts # 测试实现
task.store.ts # Pinia store
task.store.spec.ts # Store单元测试
auth/
...
components/
TaskInput.vue
TaskInput.spec.ts # 组件单元测试
e2e/
task-management-flow.e2e.test.ts # 全UI流程
Test Quality Standards
C. Monorepo(多技术栈)
AAA Pattern (Arrange-Act-Assert):
// Arrange: Set up test data and mocks
const user = { id: '123', email: 'test@example.com' };
const mockRepo = createMockRepository();
// Act: Execute the code under test
const result = await userService.createUser(user);
// Assert: Verify expected outcome
expect(result.id).toBe('123');
expect(mockRepo.save).toHaveBeenCalledWith(user);
Test Naming:
- Descriptive:
should [expected behavior] when [condition]
- Examples:
should return 404 when user not found
should hash password before saving to database
should reject email with invalid format
Coverage Requirements:
- Unit tests: >85% code coverage
- Integration tests: All adapter implementations
- E2E tests: Critical user journeys
apps/
backend/
task/
...
frontend/
src/features/task/
...
e2e/ # 共享E2E套件
api/
task-crud-api.e2e.test.ts # 仅后端E2E
ui/
checkout-flow.e2e.test.ts # 全栈E2E
核心原则:
- 单元/集成测试:与实现代码放在一起
- E2E测试:单独目录(跨边界)
- 测试替身:与接口放在一起(mock_store.go、taskAPI.mock.ts)
- 模式一致性:所有功能遵循相同结构
AAA模式(安排-执行-断言):
// 安排:设置测试数据和mocks
const user = { id: '123', email: 'test@example.com' };
const mockRepo = createMockRepository();
// 执行:运行被测代码
const result = await userService.createUser(user);
// 断言:验证预期结果
expect(result.id).toBe('123');
expect(mockRepo.save).toHaveBeenCalledWith(user);
测试命名:
- 描述性:
- 示例:
should return 404 when user not found
should hash password before saving to database
should reject email with invalid format
覆盖率要求:
- 单元测试:代码覆盖率>85%
- 集成测试:覆盖所有适配器实现
- E2E测试:覆盖关键用户旅程
Configuration Management Principles
关联原则
Separation of Configuration and Code
Configuration:
- Environment-specific values (URLs, credentials, feature flags, timeouts)
- Changes between dev/staging/prod
- Can change without code deployment
Code:
- Business logic and application behavior
- Same across all environments
- Requires deployment to change
Never hardcode configuration in code:
- ❌
const DB_URL = "postgresql://prod-db:5432/myapp"
- ✅
const DB_URL = process.env.DATABASE_URL
Configuration Validation
配置管理原则
Validate at startup:
- Check all required configuration is present
- Fail fast if required config is missing or invalid
- Provide clear error messages for misconfiguration
- Example: "DATABASE_URL environment variable is required"
Validation checks:
- Type (string, number, boolean, enum)
- Format (URL, email, file path)
- Range (port numbers 1-65535)
- Dependencies (if feature X enabled, config Y required)
配置:
- 环境专属值(URL、凭证、功能开关、超时时间)
- 开发/ staging/生产环境不同
- 无需代码部署即可变更
代码:
- 业务逻辑和应用行为
- 所有环境相同
- 需要部署才能变更
禁止在代码中硬编码配置:
- ❌
const DB_URL = "postgresql://prod-db:5432/myapp"
- ✅
const DB_URL = process.env.DATABASE_URL
Configuration Hierarchy
配置校验
Precedence (highest to lowest):
- Command-line arguments: Override everything (for testing, debugging)
- Environment variables: Override config files
- Config files: Environment-specific (config.prod.yaml, config.dev.yaml)
- Defaults: Reasonable defaults in code (fallback)
Example:
Database port resolution:
-
Check CLI arg: --db-port=5433
-
Check env var: DB_PORT=5432
-
Check config file: database.port=5432
-
Use default: 5432
启动时校验:
- 检查所有必填配置是否存在
- 必填配置缺失或无效时快速失败
- 为配置错误提供清晰的错误消息
- 示例:"需要DATABASE_URL环境变量"
校验项:
- 类型(字符串、数字、布尔、枚举)
- 格式(URL、邮箱、文件路径)
- 范围(端口号1-65535)
- 依赖(如果启用功能X,需要配置Y)
Configuration Organization
配置优先级
Hybrid Approach (config files + .env files): define the structure of configuration in config files (e.g. config/database.yaml) and use .env files to inject the secret values.
.env files: Description: A file dedicated to a specific environment (development) for production these values comes from secrets/environment platfrom or manager not a physical
file on disk. When to Use: Use this only for secrets (API keys, passwords) and a few environment-specific values (like a server IP). These files except
should never be committed to version control (git).
- - Consist of credentials and secrets with blank value (SHOULD commit to git)
- - Local development credentials and secrets (SHOULD NOT commit to git)
DEV_DB_HOST=123.45.67.89
DEV_DB_USERNAME=prod_user
DEV_DB_PASSWORD=a_very_secure_production_password
Feature files: Description: Settings are grouped into files based on what they do (database, auth, etc.). This keeps your configuration organized. When to Use: Use this as your primary method for organizing non-secret settings. It’s the best way to keep your configuration clean and scalable as your application grows.
- - Database settings
- - Cache settings
- - Authentication settings
default: &default
adapter: postgresql
pool: 5
development:
<<: *default
host: localhost
database: myapp_dev
username: <%= ENV['DEV_DB_USERNAME'] %> # Placeholder for a secret
password: <%= ENV['DEV_DB_PASSWORD'] %>
production:
<<: *default
host: <%= ENV['PROD_DB_HOST'] %>
database: myapp_prod
username: <%= ENV['PROD_DB_USERNAME'] %>
password: <%= ENV['PROD_DB_PASSWORD'] %>
优先级(从高到低):
- 命令行参数: 覆盖所有配置(用于测试、调试)
- 环境变量: 覆盖配置文件
- 配置文件: 环境专属(config.prod.yaml、config.dev.yaml)
- 默认值: 代码中的合理默认值(兜底)
示例:
数据库端口解析:
- 检查CLI参数:--db-port=5433
- 检查环境变量:DB_PORT=5432
- 检查配置文件:database.port=5432
- 使用默认值:5432
混合方案(配置文件 + .env文件):在配置文件(例如config/database.yaml)中定义配置结构,使用.env文件注入密钥值。
.env文件: 描述:专用于特定环境(开发)的文件,生产环境的这些值来自密钥/环境平台或管理器,而非磁盘上的物理
文件。使用场景:仅用于密钥(API密钥、密码)和少量环境专属值(例如服务器IP)。除
外,这些文件永远不应该提交到版本控制(git)。
- - 包含占位符空白值的凭证和密钥清单(应该提交到git)
- - 本地开发凭证和密钥(不应该提交到git)
DEV_DB_HOST=123.45.67.89
DEV_DB_USERNAME=prod_user
DEV_DB_PASSWORD=a_very_secure_production_password
功能配置文件: 描述:按功能分组的设置文件(数据库、鉴权等),保持配置有序。使用场景:作为组织非机密设置的主要方式,是随着应用增长保持配置清晰可扩展的最佳方式。
default: &default
adapter: postgresql
pool: 5
development:
<<: *default
host: localhost
database: myapp_dev
username: <%= ENV['DEV_DB_USERNAME'] %> # 密钥占位符
password: <%= ENV['DEV_DB_PASSWORD'] %>
production:
<<: *default
host: <%= ENV['PROD_DB_HOST'] %>
database: myapp_prod
username: <%= ENV['PROD_DB_USERNAME'] %>
password: <%= ENV['PROD_DB_PASSWORD'] %>
Performance Optimization Principles
关联原则
Measure Before Optimizing
"Premature optimization is the root of all evil" - Donald Knuth
Process:
- Measure: Profile to find actual bottlenecks (don't guess)
- Identify: Find the 20% of code consuming 80% of resources
- Optimize: Improve that specific bottleneck
- Measure again: Verify improvement with benchmarks
- Repeat: Only if still not meeting performance goals
Don't optimize:
- Code that's "fast enough" for requirements
- Code that's rarely executed
- Without measurable performance problem
Choose Appropriate Data Structures
性能优化原则
Selection matters:
- Hash map: O(1) lookup, unordered
- Array/list: O(1) index access, O(n) search, ordered
- Binary tree: O(log n) operations, sorted order
- Set: O(1) membership testing, unique elements
Wrong choice causes performance degradation:
- Using array for lookups: O(n) when O(1) possible with hash map
- Using list for sorted data: O(n log n) sort vs O(log n) tree operations
"过早优化是万恶之源" - Donald Knuth
流程:
- 测量: 性能分析找到实际瓶颈(不要猜测)
- 识别: 找到消耗80%资源的20%代码
- 优化: 改进特定瓶颈
- 再次测量: 通过基准测试验证改进
- 重复: 仅在仍未满足性能目标时继续
不要优化:
- 已经满足性能要求的"足够快"的代码
- 很少执行的代码
- 没有可测量的性能问题的代码
Avoid Premature Abstraction
选择合适的数据结构
Abstraction has costs:
- Runtime overhead (indirection, virtual dispatch, dynamic resolution)
- Cognitive overhead (understanding layers of abstraction)
- Maintenance overhead (changes ripple through abstractions)
Start concrete, abstract when pattern emerges:
- Write straightforward code first
- Identify duplication and common patterns
- Abstract only when there's clear benefit
- Don't add "for future flexibility" without evidence
选择很重要:
- 哈希表:O(1)查找,无序
- 数组/列表:O(1)索引访问,O(n)搜索,有序
- 二叉树:O(log n)操作,有序
- 集合:O(1)成员检测,元素唯一
错误选择会导致性能下降:
- 用数组做查找:本可以用哈希表实现O(1),却变成O(n)
- 用列表存排序数据:本可以用树实现O(log n)操作,却变成O(n log n)排序
Optimization Techniques
避免过早抽象
Caching:
- Store expensive computation results
- Cache database queries, API responses, rendered templates
- Use appropriate cache invalidation strategy
- Set TTL (time-to-live) for cache entries
Lazy Loading:
- Compute only when needed
- Load data on-demand, not upfront
- Defer expensive operations until required
Batching:
- Process multiple items together
- Batch database queries (N queries → 1 query)
- Batch API requests where possible
Async I/O:
- Don't block on I/O operations
- Use async/await for concurrent I/O
- Process multiple I/O operations in parallel
Connection Pooling:
- Reuse expensive resources (database connections, HTTP connections)
- See "Resource and Memory Management Principles"
抽象有成本:
- 运行时开销(间接调用、虚拟分发、动态解析)
- 认知开销(理解抽象层)
- 维护开销(变更扩散到抽象层)
先写具体实现,模式出现后再抽象:
- 先写直接的代码
- 识别重复和通用模式
- 只有在有明确收益时才抽象
- 不要没有证据就添加「为了未来灵活性」的抽象
Data Serialization and Interchange Principles
优化技术
Validate at System Boundaries
All data entering system must be validated:
- API requests, file uploads, message queue messages
- Validate type, format, range, required fields
- Fail fast on invalid data (don't process partially valid data)
- Return clear validation errors to client
缓存:
- 存储昂贵的计算结果
- 缓存数据库查询、API响应、渲染模板
- 使用合适的缓存失效策略
- 为缓存条目设置TTL(生存时间)
懒加载:
- 需要时才计算
- 按需加载数据,而非提前加载
- 推迟昂贵操作到需要时再执行
批量处理:
- 一起处理多个条目
- 批量数据库查询(N次查询 → 1次查询)
- 尽可能批量API请求
异步I/O:
- I/O操作不阻塞
- 并发I/O使用async/await
- 并行处理多个I/O操作
连接池:
- 复用昂贵资源(数据库连接、HTTP连接)
- 参见「资源与内存管理原则」
Handle Encoding Explicitly
数据序列化与交换原则
Default to UTF-8:
- UTF-8 for all text data (API responses, file contents, database strings)
- Specify encoding explicitly when reading/writing files
- Handle encoding errors gracefully (replacement characters or error)
Encoding errors:
- Invalid UTF-8 sequences (malformed bytes)
- Mixing encodings (reading UTF-8 as ISO-8859-1)
- Always validate and normalize encoding
所有进入系统的数据必须校验:
- API请求、文件上传、消息队列消息
- 校验类型、格式、范围、必填字段
- 无效数据快速失败(不要处理部分有效数据)
- 向客户端返回清晰的校验错误
Serialization Format Selection
显式处理编码
JSON:
- Human-readable, widely supported, language-agnostic
- Good for: APIs, configuration files, web applications
- Limitations: No binary support, larger size than binary formats
Protocol Buffers:
- Compact binary format, fast serialization/deserialization
- Schema evolution (backward/forward compatibility)
- Good for: Internal microservices, high-throughput systems
- Limitations: Not human-readable, requires schema definition
MessagePack:
- Binary JSON-like format, faster and more compact than JSON
- Good for: Internal APIs, when JSON too slow but readability still desired
- Limitations: Less widely supported than JSON
XML:
- Verbose, legacy systems, document-oriented
- Good for: Enterprise systems, SOAP APIs, RSS/Atom feeds
- Limitations: Verbosity, complexity, security issues (XXE attacks)
YAML:
- Human-friendly, good for configuration files
- Good for: Config files, Infrastructure as Code (Kubernetes, CI/CD)
- Limitations: Complex parsing, performance, security issues (arbitrary code execution)
默认使用UTF-8:
- 所有文本数据使用UTF-8(API响应、文件内容、数据库字符串)
- 读写文件时显式指定编码
- 优雅处理编码错误(替换字符或报错)
编码错误:
- 无效UTF-8序列(格式错误的字节)
- 编码混合(将UTF-8当做ISO-8859-1读取)
- 始终校验并归一化编码
Security Considerations
序列化格式选择
Validate before deserialization:
- Prevent deserialization attacks (arbitrary code execution)
- Set size limits on payloads (prevent memory exhaustion)
- Whitelist allowed types/classes for deserialization
Disable dangerous features:
- XML: Disable external entity processing (XXE prevention)
- YAML: Disable unsafe constructors
- Python pickle: Never deserialize untrusted data
JSON:
- 人类可读、广泛支持、语言无关
- 适用场景:API、配置文件、Web应用
- 限制:无二进制支持,比二进制格式体积大
Protocol Buffers:
- 紧凑二进制格式,序列化/反序列化速度快
- schema演进(向后/向前兼容)
- 适用场景:内部微服务、高吞吐量系统
- 限制:非人类可读,需要schema定义
MessagePack:
- 类JSON的二进制格式,比JSON更快更紧凑
- 适用场景:内部API,JSON太慢但仍需要可读性的场景
- 限制:支持度不如JSON广泛
XML:
- 冗长、遗留系统、面向文档
- 适用场景:企业系统、SOAP API、RSS/Atom feed
- 限制:冗长、复杂、安全问题(XXE攻击)
YAML:
- 人类友好,适合配置文件
- 适用场景:配置文件、基础设施即代码(Kubernetes、CI/CD)
- 限制:解析复杂、性能低、安全问题(任意代码执行)
反序列化前校验:
- 防止反序列化攻击(任意代码执行)
- 设置 payload 大小限制(防止内存耗尽)
- 反序列化的类型/类使用白名单
禁用危险特性:
- XML:禁用外部实体处理(预防XXE)
- YAML:禁用不安全构造函数
- Python pickle:永远不要反序列化不受信任的数据
Logging and Observability Principles
关联原则
Log Levels (Standard Priority)
Use consistent log levels across all services:
| Level | When to Use | Examples |
|---|
| TRACE | Extremely detailed diagnostic info | Function entry/exit, variable states (dev only) |
| DEBUG | Detailed flow for debugging | Query execution, cache hits/misses, state transitions |
| INFO | General informational messages | Request started, task created, user logged in |
| WARN | Potentially harmful situations | Deprecated API usage, fallback triggered, retry attempt |
| ERROR | Error events that allow app to continue | Request failed, external API timeout, validation failure |
| FATAL | Severe errors causing shutdown | Database unreachable, critical config missing |
1. Every request/operation must log:
// Start of operation
log.Info("creating task",
"correlationId", correlationID,
"userId", userID,
"title", task.Title,
)
// Success
log.Info("task created successfully",
"correlationId", correlationID,
"taskId", task.ID,
"duration", time.Since(start),
)
// Error
log.Error("failed to create task",
"correlationId", correlationID,
"error", err,
"userId", userID,
)
2. Always include context:
- : Trace requests across services (UUID)
- : Who triggered the action
- : Operation timing (milliseconds)
- : Error details (if failed)
3. Structured logging only (no string formatting):
// ✅ Structured
log.Info("user login", "userId", userID, "ip", clientIP)
// ❌ String formatting
log.Info(fmt.Sprintf("User %s logged in from %s", userID, clientIP))
4. Security - Never log:
- Passwords or password hashes
- API keys or tokens
- Credit card numbers
- PII in production logs (email/phone only if necessary and sanitized)
- Full request/response bodies (unless DEBUG level in non-prod)
5. Performance - Never log in hot paths:
- Inside tight loops
- Per-item processing in batch operations (use summary instead)
- Synchronous logging in latency-critical paths
Best Practice: "Use logger middleware redaction (e.g., pino-redact, zap masking) rather than manual string manipulation."
所有服务使用一致的日志级别:
| 级别 | 使用场景 | 示例 |
|---|
| TRACE | 极详细的诊断信息 | 函数进入/退出、变量状态(仅开发环境) |
| DEBUG | 调试用的详细流程 | 查询执行、缓存命中/未命中、状态转换 |
| INFO | 通用信息消息 | 请求开始、任务创建、用户登录 |
| WARN | 潜在有害场景 | 废弃API使用、触发降级、重试尝试 |
| ERROR | 不影响应用继续运行的错误事件 | 请求失败、外部API超时、校验失败 |
| FATAL | 导致关闭的严重错误 | 数据库不可达、关键配置缺失 |
Language-Specific Implementations
日志规则
Go (using slog standard library)
import "log/slog"
// Configure logger
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo, // Production default
}))
// Usage
logger.Info("operation started",
"correlationId", correlationID,
"userId", userID,
)
logger.Error("operation failed",
"correlationId", correlationID,
"error", err,
"retryCount", retries,
)
1. 每个请求/操作必须记录:
// 操作开始
log.Info("creating task",
"correlationId", correlationID,
"userId", userID,
"title", task.Title,
)
// 成功
log.Info("task created successfully",
"correlationId", correlationID,
"taskId", task.ID,
"duration", time.Since(start),
)
// 错误
log.Error("failed to create task",
"correlationId", correlationID,
"error", err,
"userId", userID,
)
2. 始终包含上下文:
- :跨服务追踪请求(UUID)
- :触发操作的用户
- :操作耗时(毫秒)
- :错误详情(如果失败)
3. 仅使用结构化日志(无字符串格式化):
// ✅ 结构化
log.Info("user login", "userId", userID, "ip", clientIP)
// ❌ 字符串格式化
log.Info(fmt.Sprintf("User %s logged in from %s", userID, clientIP))
4. 安全 - 禁止记录:
- 密码或密码哈希
- API密钥或token
- 信用卡号
- 生产日志中的PII(仅在必要时记录邮箱/电话且需脱敏)
- 完整请求/响应体(非生产环境DEBUG级别除外)
5. 性能 - 热路径中不要打日志:
- 紧循环内部
- 批量操作的单条处理(使用摘要替代)
- 延迟敏感路径中的同步日志
最佳实践: "使用日志中间件脱敏(例如pino-redact、zap masking),而非手动字符串操作。"
TypeScript/Node.js (using pino)
语言专属实现
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
});
logger.info({
correlationId,
userId,
duration: Date.now() - startTime,
}, 'task created successfully');
logger.error({
correlationId,
error: err.message,
stack: err.stack,
}, 'failed to create task');
import "log/slog"
// 配置logger
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo, // 生产环境默认
}))
// 使用
logger.Info("operation started",
"correlationId", correlationID,
"userId", userID,
)
logger.Error("operation failed",
"correlationId", correlationID,
"error", err,
"retryCount", retries,
)
Python (using structlog)
TypeScript/Node.js(使用pino)
import structlog
logger = structlog.get_logger()
logger.info("task_created",
correlation_id=correlation_id,
user_id=user_id,
task_id=task.id,
)
logger.error("task_creation_failed",
correlation_id=correlation_id,
error=str(err),
user_id=user_id,
)
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
});
logger.info({
correlationId,
userId,
duration: Date.now() - startTime,
}, 'task created successfully');
logger.error({
correlationId,
error: err.message,
stack: err.stack,
}, 'failed to create task');
Log Patterns by Operation Type
Python(使用structlog)
// Request received
log.Info("request received",
"method", r.Method,
"path", r.URL.Path,
"correlationId", correlationID,
"userId", userID,
)
// Request completed
log.Info("request completed",
"correlationId", correlationID,
"status", statusCode,
"duration", duration,
)
import structlog
logger = structlog.get_logger()
logger.info("task_created",
correlation_id=correlation_id,
user_id=user_id,
task_id=task.id,
)
logger.error("task_creation_failed",
correlation_id=correlation_id,
error=str(err),
user_id=user_id,
)
Database Operations
按操作类型划分的日志模式
// Query start (DEBUG level)
log.Debug("executing query",
"correlationId", correlationID,
"query", "SELECT * FROM tasks WHERE user_id = $1",
)
// Query success (DEBUG level)
log.Debug("query completed",
"correlationId", correlationID,
"rowsReturned", len(results),
"duration", duration,
)
// Query error (ERROR level)
log.Error("query failed",
"correlationId", correlationID,
"error", err,
"query", "SELECT * FROM tasks WHERE user_id = $1",
)
// 收到请求
log.Info("request received",
"method", r.Method,
"path", r.URL.Path,
"correlationId", correlationID,
"userId", userID,
)
// 请求完成
log.Info("request completed",
"correlationId", correlationID,
"status", statusCode,
"duration", duration,
)
// Call start
log.Info("calling external API",
"correlationId", correlationID,
"service", "email-provider",
"endpoint", "/send",
)
// Retry (WARN level)
log.Warn("retrying external API call",
"correlationId", correlationID,
"service", "email-provider",
"attempt", retryCount,
"error", err,
)
// Circuit breaker open (WARN level)
log.Warn("circuit breaker opened",
"correlationId", correlationID,
"service", "email-provider",
"failureCount", failures,
)
// 查询开始(DEBUG级别)
log.Debug("executing query",
"correlationId", correlationID,
"query", "SELECT * FROM tasks WHERE user_id = $1",
)
// 查询成功(DEBUG级别)
log.Debug("query completed",
"correlationId", correlationID,
"rowsReturned", len(results),
"duration", duration,
)
// 查询错误(ERROR级别)
log.Error("query failed",
"correlationId", correlationID,
"error", err,
"query", "SELECT * FROM tasks WHERE user_id = $1",
)
// Job start
log.Info("job started",
"jobId", jobID,
"jobType", "email-digest",
)
// Progress (INFO level - periodic, not per-item)
log.Info("job progress",
"jobId", jobID,
"processed", 1000,
"total", 5000,
"percentComplete", 20,
)
// Job complete
log.Info("job completed",
"jobId", jobID,
"duration", duration,
"itemsProcessed", count,
)
// 调用开始
log.Info("calling external API",
"correlationId", correlationID,
"service", "email-provider",
"endpoint", "/send",
)
// 重试(WARN级别)
log.Warn("retrying external API call",
"correlationId", correlationID,
"service", "email-provider",
"attempt", retryCount,
"error", err,
)
// 断路器打开(WARN级别)
log.Warn("circuit breaker opened",
"correlationId", correlationID,
"service", "email-provider",
"failureCount", failures,
)
// Recoverable error (ERROR level)
log.Error("validation failed",
"correlationId", correlationID,
"userId", userID,
"error", "invalid email format",
"input", sanitizedInput, // Sanitized!
)
// Fatal error (FATAL level)
log.Fatal("critical dependency unavailable",
"error", err,
"dependency", "database",
"action", "shutting down",
)
// 任务开始
log.Info("job started",
"jobId", jobID,
"jobType", "email-digest",
)
// 进度(INFO级别 - 定期记录,而非每条都记)
log.Info("job progress",
"jobId", jobID,
"processed", 1000,
"total", 5000,
"percentComplete", 20,
)
// 任务完成
log.Info("job completed",
"jobId", jobID,
"duration", duration,
"itemsProcessed", count,
)
Environment-Specific Configuration
错误场景
| Environment | Level | Format | Destination |
|---|
| Development | DEBUG | Pretty (colored) | Console |
| Staging | INFO | JSON | Stdout → CloudWatch/GCP |
| Production | INFO | JSON | Stdout → CloudWatch/GCP |
Configuration (Go example):
func configureLogger() *slog.Logger {
var handler slog.Handler
level := slog.LevelInfo
if os.Getenv("ENV") == "development" {
level = slog.LevelDebug
handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
})
} else {
handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
})
}
return slog.New(handler)
}
// 可恢复错误(ERROR级别)
log.Error("validation failed",
"correlationId", correlationID,
"userId", userID,
"error", "invalid email format",
"input", sanitizedInput, // 已脱敏!
)
// 致命错误(FATAL级别)
log.Fatal("critical dependency unavailable",
"error", err,
"dependency", "database",
"action", "shutting down",
)
Unit tests: Capture and assert on log output
// Go example
func TestUserLogin(t *testing.T) {
var buf bytes.Buffer
logger := slog.New(slog.NewJSONHandler(&buf, nil))
// Test operation
service := NewUserService(logger, mockStore)
err := service.Login(ctx, email, password)
// Assert logs
require.NoError(t, err)
logs := buf.String()
assert.Contains(t, logs, "user login successful")
assert.Contains(t, logs, email)
}
| 环境 | 级别 | 格式 | 输出目标 |
|---|
| 开发环境 | DEBUG | 美化(彩色) | 控制台 |
| Staging环境 | INFO | JSON | Stdout → CloudWatch/GCP |
| 生产环境 | INFO | JSON | Stdout → CloudWatch/GCP |
配置(Go示例):
func configureLogger() *slog.Logger {
var handler slog.Handler
level := slog.LevelInfo
if os.Getenv("ENV") == "development" {
level = slog.LevelDebug
handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
})
} else {
handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
})
}
return slog.New(handler)
}
Monitoring Integration
日志测试
Correlation IDs:
- Generate at ingress (API gateway, first handler)
- Propagate through all services
- Include in all logs, errors, and traces
- Format: UUID v4
Log aggregation:
- Ship to centralized system (CloudWatch, GCP Logs, Datadog)
- Index by: correlationId, userId, level, timestamp
- Alert on ERROR/FATAL patterns
- Dashboard: request rates, error rates, latency
单元测试: 捕获并断言日志输出
// Go示例
func TestUserLogin(t *testing.T) {
var buf bytes.Buffer
logger := slog.New(slog.NewJSONHandler(&buf, nil))
// 测试操作
service := NewUserService(logger, mockStore)
err := service.Login(ctx, email, password)
// 断言日志
require.NoError(t, err)
logs := buf.String()
assert.Contains(t, logs, "user login successful")
assert.Contains(t, logs, email)
}
Checklist for Every Feature
监控集成
关联ID:
- 在入口处生成(API网关、第一个handler)
- 在所有服务中传递
- 包含在所有日志、错误和链路追踪中
- 格式:UUID v4
日志聚合:
- 传输到集中式系统(CloudWatch、GCP Logs、Datadog)
- 索引字段:correlationId、userId、level、timestamp
- 针对ERROR/FATAL模式告警
- 仪表盘:请求率、错误率、延迟
Observability Strategy
每个功能的检查清单
Three Pillars:
- Logs: What happened (events, errors, state changes)
- Metrics: How much/how many (quantitative measurements)
- Traces: How did it happen (request flow through system)
Key Metrics:
-
RED (for services):
- Rate: Requests per second
- Errors: Error rate/count
- Duration: Latency (p50, p95, p99)
-
USE (for resources):
- Utilization: % resource in use (CPU, memory, disk)
- Saturation: How full (queue depth, wait time)
- Errors: Error count
Health Checks:
- : Simple "am I alive?" (process health only)
- : "Am I ready to serve?" (includes dependencies)
三大支柱:
- 日志: 发生了什么(事件、错误、状态变更)
- 指标: 数量有多少(量化测量)
- 链路追踪: 是如何发生的(请求在系统中的流转路径)
关键指标:
-
RED(服务维度):
- 速率:每秒请求数
- 错误:错误率/计数
- 耗时:延迟(p50、p95、p99)
-
USE(资源维度):
- 利用率:资源使用百分比(CPU、内存、磁盘)
- 饱和度:已满程度(队列深度、等待时间)
- 错误:错误计数
健康检查:
- :简单的「是否存活」(仅进程健康)
- :「是否可提供服务」(包含依赖检查)
Code Idioms and Conventions
关联原则
Write idiomatic code for the target language:
- Code should look natural to developers familiar with that language
- Follow established community conventions, not personal preferences
- Use language built-ins and standard library effectively
- Apply language-appropriate patterns (don't force patterns from other languages)
Idiomatic Code Characteristics
代码惯用规范与约定
- Leverages language features (don't avoid features unnecessarily)
- Follows language naming conventions
- Uses appropriate error handling for language (exceptions vs Result types)
- Applies established community patterns
为目标语言编写符合惯用规范的代码:
- 代码对熟悉该语言的开发者来说应该是自然的
- 遵循已建立的社区约定,而非个人偏好
- 有效使用语言内置功能和标准库
- 应用语言适配的模式(不要强行套用其他语言的模式)
Avoid Cross-Language Anti-Patterns
符合惯用规范的代码特征
- ❌ Don't write "Java in Python" or "C in Go"
- ❌ Don't force OOP patterns in functional languages
- ❌ Don't avoid language features because they're "unfamiliar"
- ✅ Learn and apply language-specific idioms
- 利用语言特性(不要不必要地规避特性)
- 遵循语言命名约定
- 使用语言适配的错误处理(异常 vs Result类型)
- 应用已建立的社区模式
Dependency Management Principles
避免跨语言反模式
Production: Pin exact versions (1.2.3, not ^1.2.0)
- Prevents supply chain attacks
- Prevents unexpected breakage from patch updates
- Ensures reproducible builds
Use lock files:
- package-lock.json (Node.js)
- Cargo.lock (Rust)
- go.sum (Go)
- requirements.txt or poetry.lock (Python)
- ❌ 不要写「Python里的Java」或「Go里的C」
- ❌ 不要在函数式语言中强行使用OOP模式
- ❌ 不要因为「不熟悉」就规避语言特性
- ✅ 学习并应用语言专属的惯用规范
Minimize Dependencies
依赖管理原则
Every dependency is a liability:
- Potential security vulnerability
- Increased build time and artifact size
- Maintenance burden (updates, compatibility)
Ask before adding dependency:
- "Can I implement this in 50 lines?"
- "Is this functionality critical?"
- "Is this dependency actively maintained?"
- "Is this the latest stable version?"
生产环境: 锁定精确版本(1.2.3,而非^1.2.0)
- 预防供应链攻击
- 预防补丁更新导致的意外中断
- 保证可复现构建
使用锁文件:
- package-lock.json(Node.js)
- Cargo.lock(Rust)
- go.sum(Go)
- requirements.txt 或 poetry.lock(Python)
Grouping:
- Standard library
- External dependencies
- Internal modules
Sorting: Alphabetical within groups
Cleanup: Remove unused imports (use linter/formatter)
每个依赖都是一个负债:
- 潜在的安全漏洞
- 增加构建时间和制品大小
- 维护负担(更新、兼容性)
添加依赖前自问:
- "我能在50行代码内实现这个功能吗?"
- "这个功能是核心需求吗?"
- "这个依赖还在积极维护吗?"
- "这是最新的稳定版本吗?"
Avoid Circular Dependencies
导入组织
Problem: Module A imports B, B imports A
- Causes build failures, initialization issues
- Indicates poor module boundaries
Solution:
- Extract shared code to third module
- Restructure dependencies (A→C, B→C)
- Use dependency injection
分组:
- 标准库
- 外部依赖
- 内部模块
排序: 组内按字母顺序排序
清理: 移除未使用的导入(使用linter/格式化工具)
Command Execution Principles
避免循环依赖
Never execute user input directly:
- ❌
- ❌
- ✅ Use argument lists, not shell string concatenation
- ✅ Validate and sanitize all arguments
Run with minimum permissions:
- Never run commands as root/admin without explicit human approval. If elevated permissions are absolutely required, STOP and request authorization.
- Use least-privilege service accounts
问题: 模块A导入B,B导入A
解决方案:
- 将共享代码提取到第三个模块
- 重构依赖关系(A→C,B→C)
- 使用依赖注入
Use language standard library:
- Avoid shell commands when standard library provides functionality
- Example: Use file I/O APIs instead of , ,
Test on all target OS:
- Windows, Linux, macOS have different commands and behaviors
- Use path joining functions (don't concatenate with /)
永远不要直接执行用户输入:
- ❌
- ❌
- ✅ 使用参数列表,而非shell字符串拼接
- ✅ 校验并清洗所有参数
以最小权限运行:
- 没有明确的人类审批,永远不要以root/管理员身份运行命令。如果确实需要提升权限,停止并请求授权。
- 使用最小权限的服务账号
Check exit codes:
- Non-zero exit code = failure
- Capture and log stderr
- Set timeouts for long-running commands
- Handle "command not found" gracefully
使用语言标准库:
- 标准库提供对应功能时避免使用shell命令
- 示例:使用文件I/O API,而非、、
在所有目标OS上测试:
- Windows、Linux、macOS的命令和行为不同
- 使用路径拼接函数(不要用/拼接)
检查退出码:
- 非零退出码 = 失败
- 捕获并记录stderr
- 长时间运行的命令设置超时
- 优雅处理「命令未找到」场景
Documentation Principles
关联原则
Clear naming reduces need for comments:
- Code shows WHAT is happening
- Comments explain WHY it's done this way
When to comment:
- Complex business logic deserves explanation
- Non-obvious algorithms (explain approach)
- Workarounds for bugs (link to issue tracker)
- Performance optimizations (explain trade-offs)
- Inline comments: Explain WHY for complex code
- Function/method docs: API contract (parameters, returns, errors)
- Module/package docs: High-level purpose and usage
- README: Setup, usage, examples
- Architecture docs: System design, component interactions
清晰的命名可以减少注释需求:
何时需要注释:
- 复杂的业务逻辑需要解释
- 非直观的算法(说明实现思路)
- bug的临时解决方案(链接到issue跟踪系统)
- 性能优化(说明取舍)
- Broken Access Control: Deny by default. Validate permissions server-side for every request. Do not rely on UI state.
- Cryptographic Failures: Use TLS 1.2+ everywhere. Encrypt PII/Secrets at rest. Use standard algorithms (AES-256, RSA-2048, Ed25519). Never roll your own crypto.
- Injection: ZERO TOLERANCE for string concatenation in queries. Use Parameterized Queries (SQL) or ORM bindings. Sanitize all HTML/JS output.
- SSRF Prevention: Validate all user-provided URLs against an allowlist. Disable HTTP redirects in fetch clients. Block requests to internal IPs (metadata services, localhost).
- Insecure Design: Threat model every new feature. Fail securely (closed), not openly.
- Vulnerable Components: Pin dependency versions. Scan for CVEs in CI/CD.
- 内联注释: 解释复杂代码的实现原因
- 函数/方法文档: API契约(参数、返回值、错误)
- 模块/包文档: 高层用途和用法
- README: 搭建、用法、示例
- 架构文档: 系统设计、组件交互
Authentication & Authorization
安全原则
- Passwords: Hash with Argon2id or Bcrypt (min cost 12). Never plain text.
- Tokens:
- Access Tokens: Short-lived (15-30 mins). HS256 or RS256.
- Refresh Tokens: Long-lived (7-30 days). Rotate on use. Store in
HttpOnly; Secure; SameSite=Strict
cookies.
- Rate Limiting: Enforce strictly on public endpoints (Login, Register, Password Reset). Standard: 5 attempts / 15 mins.
- MFA: Required for Admin and Sensitive Data access.
- RBAC: Map permissions to Roles, not Users. Check permissions at the Route AND Resource level.
- 访问控制失效: 默认拒绝,服务端校验每个请求的权限,不要依赖UI状态。
- 加密失效: 所有场景使用TLS 1.2+,静态PII/密钥加密,使用标准算法(AES-256、RSA-2048、Ed25519),永远不要自己实现加密算法。
- 注入: 零容忍查询中的字符串拼接,使用参数化查询(SQL)或ORM绑定,清洗所有HTML/JS输出。
- SSRF防护: 校验所有用户提供的URL是否在白名单中,禁用fetch客户端的HTTP重定向,阻止访问内部IP(元数据服务、localhost)。
- 不安全设计: 每个新功能都要做威胁建模,安全失败(默认关闭),而非开放失败。
- 有漏洞的组件: 锁定依赖版本,CI/CD中扫描CVE。
Input Validation & Sanitization
身份认证与权限控制
- Principle: "All Input is Evil until Proven Good."
- Validation: Validate against a strict Schema (Zod/Pydantic) at the Controller/Port boundary.
- Allowlist: Check for "Good characters" (e.g., ), do not try to filter "Bad characters."
- Sanitization: Strip dangerous tags from rich text input using a proven library (e.g., DOMPurify equivalent).
- 密码: 使用Argon2id或Bcrypt哈希(最小成本12),永远不要明文存储。
- Token:
- 访问Token: 短有效期(15-30分钟),HS256或RS256算法。
- 刷新Token: 长有效期(7-30天),使用时轮换,存储在
HttpOnly; Secure; SameSite=Strict
cookie中。
- 限流: 公共端点严格执行限流(登录、注册、密码重置),标准规则:15分钟内5次尝试。
- MFA: 管理员和敏感数据访问必须启用。
- RBAC: 权限映射到角色,而非用户,在路由和资源层面都要校验权限。
Logging & Monitoring (Security Focus)
输入校验与清洗
- Redaction: SCRUB all PII, Secrets, Tokens, and Passwords from logs before writing.
- Events: Log all failed auth attempts, access denied events, and input validation failures.
- Format: JSON structured logs with , , and .
- Anti-Tamper: Logs should be write-only for the application.
- 原则: "所有输入都是恶意的,除非证明是安全的。"
- 校验: 在控制器/端口边界使用严格Schema(Zod/Pydantic)校验。
- 白名单: 检查「合法字符」(例如:),不要尝试过滤「非法字符」。
- 清洗: 使用成熟的库(例如DOMPurify等价库)剥离富文本输入中的危险标签。
Secrets Management
日志与监控(安全方向)
- Storage: Never commit secrets to git. Use (local) or Secret Managers (Prod - e.g., Vault/GSM).
- 脱敏: 写入日志前清除所有PII、密钥、Token和密码。
- 事件: 记录所有失败的鉴权尝试、拒绝访问事件、输入校验失败事件。
- 格式: JSON结构化日志,包含、和。
- 防篡改: 应用对日志只有写权限。
- 存储: 永远不要将密钥提交到git,本地使用,生产使用密钥管理器(例如Vault/GSM)。