155 lines
4.4 KiB
Markdown
155 lines
4.4 KiB
Markdown
# Design Patterns — 从实际任务提取
|
||
|
||
> 来源: reflections/ + 日常开发经验
|
||
> 已验证可跨任务复用的模式
|
||
> 新增: Anti-Patterns (≥2 occurrences → promoted from reflections)
|
||
|
||
---
|
||
|
||
## Positive Patterns
|
||
|
||
## Pattern 1: Phase Isolation (阶段隔离)
|
||
|
||
**问题**: 大任务中分析、设计、实现三阶段互相干扰,导致返工
|
||
|
||
**方案**:
|
||
```
|
||
Phase 1: Analyze → 输出物: analysis/*.md (不可变)
|
||
Phase 2: Design → 输出物: plan.md (不可变)
|
||
Phase 3: Implement → 输出物: src/*.js (只读 plan.md, 不改)
|
||
```
|
||
|
||
**关键规则**: 每阶段输出物一旦完成,即标记为不可变。下一阶段只能读取,不能修改。如果发现设计缺陷,完成当前 Phase 后回头修改 plan.md。
|
||
|
||
**适用场景**: 任何需要"理解外部系统 → 设计增强方案 → 编码实现"的任务
|
||
|
||
**反模式**: 在分析源码时就开始写代码 → 分析不完整 → 后期返工
|
||
|
||
---
|
||
|
||
## Pattern 2: Self-Contained Module (自包含模块)
|
||
|
||
**问题**: 模块依赖外部库/环境,部署和测试困难
|
||
|
||
**方案**: 每个模块满足三个条件:
|
||
1. `node module.js` — CLI 独立运行
|
||
2. `require('./module')` — 库接口可被其他模块调用
|
||
3. 零必须依赖(外部依赖标记为 optional,降级到自实现)
|
||
|
||
**模板**:
|
||
```javascript
|
||
// ... implementation ...
|
||
|
||
// 库接口
|
||
module.exports = { ... };
|
||
|
||
// CLI 入口
|
||
if (require.main === module) {
|
||
main();
|
||
}
|
||
```
|
||
|
||
**实例**:
|
||
- `risk-scorer.js`: CLI + require() 双接口,零依赖
|
||
- `toml-parser.js`: 自实现,零依赖
|
||
- `ctx-compress.js`: CLI + require(),子模块均自包含
|
||
|
||
---
|
||
|
||
## Pattern 3: Strategy Dispatch (策略分发)
|
||
|
||
**问题**: 同一操作有多种策略,如何选择和扩展
|
||
|
||
**方案**:
|
||
```
|
||
1. Detect → 检测特征
|
||
2. Analyze → 分析数据
|
||
3. Recommend → 推荐策略
|
||
4. Plan → 制定执行计划
|
||
5. Execute → 执行
|
||
6. Mark → 标记结果 (CCR hash / metadata)
|
||
```
|
||
|
||
**实例**:
|
||
- SmartCrusher: 5 种压缩策略 (SmartSample/TopN/ClusterSample/TimeSeries/lossless)
|
||
- SessionRouter: 4 种路由策略 (Spawned/ReusedIdle/ReusedActive/Deferred)
|
||
- ctx_compress: 4 种压缩器 (json/log/search/diff)
|
||
|
||
**扩展方式**: 新增策略只需在策略注册表中加一个条目,不碰分发逻辑
|
||
|
||
---
|
||
|
||
## Pattern 4: Graceful Degradation (降级优先)
|
||
|
||
**问题**: 最优路径可能不可用(环境限制、数据格式不匹配)
|
||
|
||
**方案**: 永远提供降级路径
|
||
```
|
||
最优路径 → 备选路径 → 通用路径 → passthrough
|
||
```
|
||
|
||
**实例**:
|
||
- CCR Store: Redis → SQLite → InMemory → (不存, hash 仍生成)
|
||
- SmartCrusher: lossless:csv → SmartSample → passthrough:small_array
|
||
- CodeGraph 搜索: FTS5 → LIKE → 空结果
|
||
|
||
**关键**: 降级时 WARN 日志 + 明确策略名 (如 `passthrough:not_json`)
|
||
|
||
---
|
||
|
||
## Pattern 5: Three-Tier Cache (三层缓存)
|
||
|
||
**问题**: 内存缓存快但小,持久化慢但大,分布式慢但共享
|
||
|
||
**方案**:
|
||
```
|
||
L1: InMemory (500 项, 1min TTL) ← 热数据
|
||
L2: SQLite (10K 项, 5min TTL) ← 温数据
|
||
L3: Redis (可选, 分布式共享) ← 跨进程共享
|
||
```
|
||
|
||
**查询顺序**: L1 → (promote to L1) → L2 → (promote) → L3
|
||
|
||
**实例**: `src/compression/ccr-backends.js` — HybridCcrStore
|
||
|
||
---
|
||
|
||
## Anti-Patterns (from failures, ≥2 occurrences)
|
||
|
||
### AP-1: Operator Coercion Trap (2 occurrences)
|
||
|
||
**问题**: JavaScript `||` 把 `0`/`""`/`false` 当 falsy,导致排序/评分/计数逻辑出错
|
||
|
||
**症状**:
|
||
- 排序结果反转(healthy=0 排到最后)
|
||
- FK 索引计数字段被跳过
|
||
|
||
**方案**: 数值/布尔默认值用 `??`(nullish coalescing),`||` 仅用于字符串默认值。
|
||
Grep `||` in scoring/ranking code.
|
||
|
||
**来源**:
|
||
- 2026-06-03: Agent Evolution FK 索引 `count || 0`
|
||
- 2026-06-05: PR-30 Cross-Agent Certification `order[status] || 6`
|
||
|
||
---
|
||
|
||
### AP-2: Generator Path Assumption (1 occurrence)
|
||
|
||
**问题**: Builder/Generator 有硬编码路径假设(如 `src/app/`),与实际 framework convention 不一致
|
||
|
||
**方案**: 生成器完成后立即跑 build smoke test,验证输出目录与 runtime 预期一致
|
||
|
||
**来源**:
|
||
- 2026-06-05: Domain Benchmark — frontend-builder 输出 `src/app/`,Next.js 期望 `app/`
|
||
|
||
---
|
||
|
||
### AP-3: DDL Flat Mapping (1 occurrence)
|
||
|
||
**问题**: SQL DDL CONSTRAINT/KEY 行被解析为数据列
|
||
|
||
**方案**: DDL parser 必须有 column / constraint / index 三层分离
|
||
|
||
**来源**:
|
||
- 2026-06-05: Backend Builder — PRIMARY KEY 行泄露进 TS interface
|