原型代码可以在保持清晰边界、可读命名和足够文档的同时,依然保持轻量化,为下一阶段做好准备。
原型悖论
在构建概念验证时,速度与结构之间始终存在张力。走得太快会积累技术债务,走得太慢会失去动力。
可扩展的实践
1. 清晰地命名
即使在临时性代码中,也要使用能表明意图的命名。calculateTotal() 比 calc() 更好。未来的你(或其他人)会感谢你的。
2. 让边界可见
即使在小原型中也要分离关注点:
- UI 逻辑与业务规则分离
- 数据获取与渲染分离
- 配置与实现分离
3. 记录决策,而非代码
添加简短注释解释为什么选择了某种方案,而非代码做了什么。代码展示的是”是什么”;注释应该解释”为什么”。
// 使用 localStorage 实现快速持久化
// 如果得到认可,将迁移到后端 API
const saveState = (data) => {
localStorage.setItem('prototype-state', JSON.stringify(data));
};
4. 留下迁移钩子
添加简单的扩展点,使演进更容易:
// TODO: 替换为适当的状态管理方案
const appState = { /* ... */ };
// TODO: 添加错误处理
const fetchData = async () => { /* ... */ };
5. 测试关键路径
即使只有一两个冒烟测试,也能在后期节省大量时间:
describe('核心工作流', () => {
it('处理输入并生成输出');
it('优雅地处理空输入');
});
何时重构 vs 重写
重构适用于:
- 核心架构合理
- 只有特定模块需要改进
- 团队理解代码库
重写适用于:
- 基本假设是错误的
- 技术债务阻碍进展
- 需求发生了重大变化
结论
原型代码不必是一次性的。通过最低限度的自律,它可以演进为生产级软件,而无需完全重写。
关键在于找到正确的平衡:足够的结构以支持演进,但不要多到拖慢实验速度。