name: 规划框架 description: “在编码前应用结构化思考。适用于:开始新功能、进行架构决策、重构大型组件或评估实施方案。包括马斯克的5步算法和ICE评分框架。”
规划框架
何时使用
在开始任何重要的编码任务之前:
- 新功能(> 50行代码)
- 重构现有组件
- 架构变更
- 调试复杂问题
马斯克的5步算法
第1步:让需求不那么愚蠢
质疑一切:
- 谁请求了这个功能?为什么?
- 它实际上解决了什么问题?
- 是否有更简单的方法实现相同的结果?
- 如果我们不构建这个会怎样?
Portfolio Buddy 2 示例:
请求: “为指标表添加可排序列”
问题:
- 为什么?用户希望快速找到表现最佳/最差的策略
- 更简单的解决方案?只需默认按夏普比率(最重要的指标)排序
- 替代方案?添加"前三名"和"后三名"高亮部分
决策: 通过
useSorting钩子实现了完整的多列排序,因为:
- 不同用户关心不同的指标(夏普 vs 索提诺 vs 最大回撤)
- 排序是 O(n log n) - 对于 <100 个策略可忽略不计
- 可重用的钩子可用于未来的表格
第2步:删除部分或流程
我们可以消除什么?
- 移除没有明确目的的功能
- 简化工作流程中不必要的步骤
- 简化数据结构
- 删除未使用的依赖项
规则: 如果你没有加回10%你删除的内容,那就是删除得不够。
Portfolio Buddy 2 示例:
发现: 安装了 Recharts 库(11.5KB)但从未导入
问题:
- 它在哪里使用了吗?没有 - 搜索显示零导入
- 为什么安装它?可能是初始计划,后来换成了 Chart.js
- 我们可以删除它吗?可以 - 没有东西依赖它
操作:
npm uninstall recharts(节省了 11.5KB 的捆绑包大小)结果: 更清晰的依赖树,更快的安装,更小的捆绑包
第3步:简化或优化
只有在删除之后:
- 简化剩余代码
- 提取可重用函数
- 提高类型安全性
- 降低复杂度
Portfolio Buddy 2 示例:
问题: PortfolioSection.tsx 有 591 行(是 200 行限制的 3 倍)
优化前:
function PortfolioSection() { // 50 行合约乘数逻辑 // 40 行日期过滤逻辑 // 100 行 Chart.js 配置 // 80 行统计计算 // 300+ 行 JSX 渲染 }简化后:
// 提取钩子 const portfolio = usePortfolio(files, dateRange) const contracts = useContractMultipliers(strategies) // 提取组件 <ContractControls {...contracts} /> <EquityChartSection data={portfolio.equity} /> <PortfolioStats metrics={portfolio.metrics} />结果: 主组件 < 100 行,逻辑封装,可重用
第4步:加速周期时间
- 减少构建/测试时间
- 改善开发者体验
- 添加有用的错误信息
- 优化反馈循环
Portfolio Buddy 2 示例:
之前: Create React App 构建时间:~30 秒
操作: 迁移到 Vite
之后: Vite 构建时间:~2 秒(快 15 倍)
影响: 开发者每小时可以迭代 15 倍以上
第5步:自动化
最后一步 - 自动化那些被证明是必要的部分。
Portfolio Buddy 2 示例:
先不要自动化: CI/CD 流水线
- 手动 Cloudflare 部署目前工作正常
- 每月只部署 2-3 次
- 设置 GitHub Actions 需要 2-4 小时
- 等到部署频率增加再说
应该自动化: 提交时的 TypeScript 检查
- 可以在合并前捕获
any类型违规- Git 预提交钩子:
tsc --noEmit- 节省后续调试时间
ICE 评分框架
使用以下标准评估解决方案:
- 影响: 这能带来多大改变?(1-10)
- 信心: 我们有多大把握这会成功?(1-10)
- 简易性: 实施起来有多简单?(1-10)
ICE 分数 = (影响 × 信心 × 简易性) / 10
Portfolio Buddy 2 示例
示例1:错误边界(高优先级)
功能: 在风险组件周围添加 React 错误边界
- 影响: 7(防止组件错误导致整个应用崩溃)
- 信心: 9(标准的 React 模式,文档齐全)
- 简易性: 8(包装组件 + 后备 UI,约 50 行)
- ICE 分数: (7 × 9 × 8) / 10 = 50.4 → 高优先级
决策: 应该尽快实施。快速见效,影响大。
示例2:导出到 Excel(中高优先级)
功能: 为指标表添加 Excel 导出按钮
- 影响: 8(用户明确请求过)
- 信心: 8(存在像 xlsx 这样的库,经过验证的解决方案)
- 简易性: 7(格式化数据,生成文件,触发下载)
- ICE 分数: (8 × 8 × 7) / 10 = 44.8 → 高优先级
决策: 值得实施。明确的用户价值,合理的努力。
示例3:重构 PortfolioSection(中优先级)
功能: 将 591 行的组件拆分成更小的部分
- 影响: 6(提高可维护性,没有面向用户的变更)
- 信心: 8(已确定清晰的提取点)
- 简易性: 4(繁琐,4-6 小时,有破坏现有功能的风险)
- ICE 分数: (6 × 8 × 4) / 10 = 19.2 → 中优先级
决策: 对代码健康很重要,但不紧急。在面向用户的功能之后再做。
示例4:通过 WebSocket 实现实时价格更新(低优先级)
功能: 图表中的实时市场数据更新
- 影响: 6(锦上添花,但应用分析的是历史数据)
- 信心: 5(集成复杂,需要数据源)
- 简易性: 3(WebSocket 设置,状态管理,错误处理)
- ICE 分数: (6 × 5 × 3) / 10 = 9 → 低优先级
决策: 目前跳过。不符合核心用例(历史分析)。
示例5:移除 Recharts 依赖(快速见效)
功能: 卸载未使用的 Recharts 库
- 影响: 3(减少少量捆绑包大小,清理依赖)
- 信心: 10(确认从未在任何地方导入)
- 简易性: 10(一个命令:
npm uninstall recharts) - ICE 分数: (3 × 10 × 10) / 10 = 30 → 中优先级
决策: 容易快速见效。下次处理 package.json 时做。
示例6:索提诺比率计算(已完成)
功能: 添加索提诺比率指标(已完成)
- 影响: 8(对交易者来说重要的风险调整后指标)
- 信心: 9(定义明确的公式,类似于夏普)
- 简易性: 7(计算 + UI + 无风险利率输入)
- ICE 分数: (8 × 9 × 7) / 10 = 50.4 → 高优先级
结果: 在提交 258ba3a 和 9f25040 中成功实施。
ICE 分数解读
| ICE 分数范围 | 优先级 | 行动 |
|---|---|---|
| 40+ | 高 | 尽快做,在 1-2 个冲刺内 |
| 25-39 | 中高 | 计划在接下来的 2-3 个冲刺内 |
| 15-24 | 中 | 待办事项,有容量时再做 |
| 10-14 | 低 | 如果非常容易或具有战略性则考虑 |
| < 10 | 非常低 | 除非需求改变,否则可能跳过 |
规划清单
编码前:
- [ ] 应用第1步:彻底质疑需求
- [ ] 应用第2步:确定可以删除/简化的内容
- [ ] 计算 ICE 分数(针对 > 100 行的功能)
- [ ] 确认不存在更简单的解决方案
- [ ] 确定哪些组件/钩子将受到影响
- [ ] 检查代码库中是否存在类似功能
- [ ] 查看相关代码以了解上下文
规划后:
- [ ] 用 3-5 个要点写下方法
- [ ] 确定潜在问题/边缘情况
- [ ] 现实地估计时间(将初始猜测乘以 2)
- [ ] 确认已计划 TypeScript 类型(不使用
any) - [ ] 验证组件不会超过 200 行
Portfolio Buddy 2 规划示例
示例:添加索提诺比率(已完成功能)
第1步 - 需求:
- 用户需要在夏普比率旁边有索提诺比率
- 索提诺关注下行风险(对交易者更相关)
- 需要无风险利率输入
第2步 - 删除:
- 不需要单独的页面来展示高级指标
- 不需要教程/帮助文本(公式是标准的)
第3步 - 简化:
- 添加单一的无风险利率输入(不是每个策略)
- 在现有的
calculateMetrics()函数中计算 - 在现有的 MetricsTable 中添加列
ICE 分数: 50.4(高优先级)
方法:
- 在 dataUtils.ts 中添加
calculateSortino() - 在指标计算中包含索提诺
- 在 PortfolioSection 中添加无风险利率输入
- 在 MetricsTable 中添加索提诺列
- 用样本数据测试
时间估计: 3-4 小时
结果: 成功完成,但发现了计算错误(提交 9f25040 修复了它)。
实施说明: 索提诺是在 PortfolioSection.tsx 中内联实现的(第 133-158 行),而不是在 dataUtils.ts 中。做出这个决定是因为:
- 索提诺需要投资组合级别的日收益率(不是交易级别的指标)
- 需要用户输入状态(无风险利率)
- 与胜率、盈利因子等的计算上下文不同
- 团队应该讨论是否为了保持一致性而提取到 dataUtils
示例:重构 PortfolioSection(未来工作)
第1步 - 需求:
- 组件有 591 行(是限制的 3 倍)
- 难以维护和理解
- 希望遵循编码标准
第2步 - 删除:
- 检查重复逻辑
- 移除注释掉的代码
- 合并类似的状态处理程序
第3步 - 简化:
- 提取
ContractControls.tsx(合约乘数 UI) - 提取
EquityChartSection.tsx(Chart.js 配置) - 提取
PortfolioStats.tsx(统计数据显示) - 在主组件中只保留编排逻辑
ICE 分数: 19.2(中优先级)
方法:
- 创建 EquityChartSection 组件(150 行)
- 创建 PortfolioStats 组件(80 行)
- 创建 ContractControls 组件(100 行)
- 将主 PortfolioSection 重构到 <100 行
- 测试所有功能是否仍然有效
时间估计: 4-6 小时(繁琐但直接)
风险:
- 破坏现有功能
- 如果不小心,会出现属性传递过多
- 测试所有边缘情况
决策: 中优先级。在更紧急的面向用户的功能之后再做。
示例:Vitest 测试设置(未来工作)
第1步 - 需求:
- 目前没有测试
- 关键计算需要测试(夏普、索提诺、相关性)
- 防止指标计算中的回归
第2步 - 删除:
- 不需要 100% 覆盖率
- 暂时跳过 UI/组件测试
- 只关注计算函数
第3步 - 简化:
- 只测试 dataUtils.ts 函数
- 使用 Vitest(Vite 内置)
- 从真实的 CSV 文件中模拟数据
ICE 分数: 24(中优先级)
方法:
- 安装 Vitest:
npm install -D vitest - 在 package.json 中添加测试脚本
- 创建
dataUtils.test.ts - 为关键计算编写测试
- 最终在 CI 中运行测试
时间估计: 6-8 小时(学习曲线 + 编写测试)
要编写的测试:
- 使用已知数据的
calculateSharpe() - 使用已知数据的
calculateSortino() calculateCorrelation()的边缘情况parseCSV()的错误处理
需要注意的红旗
规划期间
- ❌ “这只需要 30 分钟”(通常需要 2-3 小时)
- ❌ “我现在先用
any类型”(技术债务会累积) - ❌ “我们以后可能需要这个”(YAGNI - 你不会需要它)
- ❌ “每个人都这样做”(验证它是否适合你的用例)
实施期间
- ❌ 组件接近 200 行(现在重构,不要等到以后)
- ❌ 从另一个文件复制逻辑(提取到共享工具函数)
- ❌ 添加依赖项而不检查大小/替代方案
- ❌ 没有错误处理(“以后再加” = 永远不会发生)
Portfolio Buddy 2 经验教训
- ✅ 做得好: 质疑是否需要 Redux,转而使用普通的 React 钩子
- ✅ 做得好: 测试两者后选择了 Chart.js 而不是 Recharts
- ❌ 需要改进: 让 PortfolioSection 增长到 591 行
- ❌ 需要改进: 累积了 14 个
any类型违规
框架实践
当你准备开始编码时,问:
- 我要构建什么?(明确的需求)
- 我为什么要构建它?(要解决的实际问题)
- 我可以删除什么?(移除不必要的部分)
- 最简单的版本是什么?(MVP 方法)
- 我有多大信心?(ICE 评分)
- 时间估计是多少?(现实一点,乘以 2)
然后将你的计划写成:
- 3-5 个要点的方法
- 时间估计
- 将更改的文件
- 潜在问题
最后:
- 如果工作量 >4 小时,获取反馈
- 从最小的可测试增量开始
- 频繁提交