“打补丁”或“用拐杖支撑”——意味着创建一个临时解决方案来解决问题,它可以修复bug或添加功能,但不能消除根本原因,也不符合项目的架构标准。拐杖在任何开发中都不可避免:截止日期、对系统的不完全理解以及外部限制迫使人们做出妥协决定。根据Refactoring Guru,实用拐杖与技术债务之间的关键区别在于决策的自觉性以及是否有消除计划。正确使用临时解决方案需要纪律和文档记录。
要点
拐杖(crutch)——一种能工作但“仓促完成”的软件解决方案:它解决特定问题但不消除其原因,不遵循项目架构,并可能在环境发生最微小变化时崩溃。这个比喻很精确——就像真正的拐杖一样,这样的代码帮助“行走”,但不能治愈“腿”。
开发人员用拐杖“支撑”bug、版本不兼容、平台特性和紧急客户需求。典型的拐杖——拐杖条件:如果是iOS 15就添加间距,如果是华为就隐藏按钮。这样的检查会成倍增加,将代码变成由平台和版本分支构成的“千层饼”。
拐杖可能规模不同:从一行拐杖条件到整个中间层模块“修复”库的行为。重要的是要理解,拐杖并不总是坏事:在正确的人手中,它是允许按时发布产品的工具。问题始于拐杖永远留在代码中。
主要原因是理想解决方案与项目实际限制之间的冲突。开发人员知道如何正确地做,但时间、金钱或技术限制不允许。结果产生了“只是能用”的折衷方案。
让我们看看开发人员有意使用拐杖的四个主要原因。理解这些原因有助于将拐杖视为需要管理的实用工具,而不是错误。
最常见的原因。发布明天,bug仅在特定型号上重现,架构修复需要两周时间。拐杖条件需要一小时并关闭问题。发布后,团队承诺回来并正确重写。“没有什么比临时更永久的了”——正是关于这种拐杖。
库A需要Android 12,但您的应用支持Android 10。解决方案——编写中间层来检查操作系统版本并选择执行路径。这是拐杖,因为更新库时中间层需要重写。但替代方案——放弃库或旧设备支持——可能更糟。
// 用于API 29兼容性的拐杖
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
useModernApi()
} else {
useLegacyFallback()
}
项目依赖的库包含bug,但其更新可能需要数周(需要PR、代码审查、发布)。与其等待,团队编写包装器来动态修补库的行为。修复后的库版本发布后,包装器被删除。如果不删除——这已经是架构问题。
遗留项目中的新开发人员不理解为什么代码按这种方式工作。他们不是去弄清楚,而是在现有条件之上添加新条件。这是最危险的拐杖类型,因为作者没有意识到这是拐杖。唯一的解决办法——代码审查和团队新成员的结对编程。
并非每个拐杖都是坏事。在实际开发中,代码绝对纯净是不可实现的,而且通常不切实际。实用方法承认临时解决方案是过程的一部分,但要求自觉性、文档记录和消除规划。当拐杖比纯粹的架构解决方案更快地解决业务任务时,它就是合理的。
合理拐杖的标准:解决特定问题,有所有者(谁负责删除它),并且存在重构计划。如果三个条件中至少有一个不满足,拐杖就变成技术债务。像TODO注释配合跟踪器中的ticket这样的工具——是最小限度的文档记录方式。
发布分支中的关键bug,需要在明天部署前关闭。纯粹解决方案需要架构重构,耗时两周。拐杖——添加nil检查并将修复作为热修复发送。合理条件:在跟踪器中创建了重构ticket,指定了负责人,拐杖用注释标记。两周后团队回到任务。
// TODO: IT-1234 — 在AuthService重构后删除此拐杖
guard let userId = session.user?.id else {
return Result.failure(.notAuthenticated)
}
有意识的拐杖与架构问题(技术债务)之间的界限通过两个参数划分:决策的自觉性和消除计划的存在。拐杖总是具有已知寿命的临时解决方案。技术债务——许多被忽视的拐杖的后果。
| 参数 | 有意识的拐杖 | 技术债务 |
|---|---|---|
| 意识 | 团队知道这是临时解决方案 | 没人记得为什么代码是这样的 |
| 文档 | 有TODO、跟踪器中的ticket | 没有注释、链接、描述 |
| 消除计划 | 指定了重构的sprint | “总有一天我们会重写” |
| 影响 | 局部的,不妨碍新功能 | 阻塞变更,拖慢开发 |
当拐杖数量超过临界质量时,情况恶化。每个新拐杖都增加系统的“脆弱性”:一个地方的变更破坏另一个地方。结果开发变慢,bug成倍增加,新开发人员无法在没有作者帮助的情况下理解代码。此时拐杖不再是临时解决方案,而是架构问题。
如果代码中有五个嵌套检查针对操作系统版本、设备制造商和特定库的存在——这不是拐杖,这是架构问题。如果一个修复的添加导致相邻模块出现三个回归——拐杖不再是局部的。如果代码审查因“又一个拐杖”而被定期拒绝——是时候计划重构了。
重构拐杖——用架构上正确的方案替换临时解决方案的过程。这需要时间,因此需要优先级排序策略:并非所有拐杖都需要立即消除。好的策略——根据两个参数评估每个拐杖:该代码区域的变更频率和对用户的影响。
高优先级——经常变更的模块中的拐杖(业务逻辑、通用UI),它们拖慢开发并导致回归。中优先级——很少变更但可能影响用户的模块中的拐杖(支付处理、授权)。低优先级——遗留代码中的拐杖,稳定运行且不计划修改。
步骤1:清点——找到所有与拐杖相关的TODO和FIXME。步骤2:评估——确定哪些仍然相关。步骤3:规划——将拐杖重构安排到sprint中,从高优先级开始。步骤4:替换——实施纯粹解决方案,删除拐杖及其TODO注释。步骤5:验证——确保测试通过且没有回归。
# 查找项目中所有TODO拐杖
grep -r "TODO\|FIXME\|HACK\|XXX" --include="*.kt" --include="*.swift" .
对抗拐杖的最佳方法是不无故创建它们。在编写拐杖之前,问自己三个问题:能否在合理时间内做出纯粹解决方案?是否有不是拐杖的替代方案?团队会有时间回来重写吗?如果至少有一个问题的答案是“不”——在“支撑”代码之前再想想。
常见问题解答
打补丁——编写临时解决方案来关闭问题但不消除其原因。代码能工作,但不符合项目架构,可能在变更时崩溃。
拐杖——有消除计划的有意识临时解决方案。技术债务——许多被遗忘的拐杖的后果。拐杖是局部的,债务是系统性的并阻塞开发。
当截止日期紧迫、纯粹解决方案需要时间且拐杖有文档记录(TODO注释和跟踪器中的ticket)时。条件:拐杖在可预见的未来有删除计划。
添加TODO或FIXME,包含ticket编号和正确解决方案的简要描述。示例:// TODO: IT-567 — 使用Factory pattern重写。没有ticket,拐杖会被遗忘。
对所有TODO进行清点,评估优先级,从经常变更的模块开始。用纯粹解决方案替换拐杖,删除注释并用测试检查。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。