本文是 Django 的共同创建者,同时担任工程主管的 Jacob Kaplan-Moss 所写。软件项目的估算是一个众所周知的难题,许多项目经常出现成本超支和进度延误的情况。然而,尽管估算困难,作者强调了估算的重要性,并认为放弃估算可能会限制职业发展。准确的估算是可以学习和掌握的技能,它能够帮助建立信任并促进职业的进一步发展。Jacob Kaplan-Moss 分享了一种软件项目时间估算的技术。他的方法的核心特点是同时捕捉时间和不确定性,以便为项目制定更准确的时间表。他的技术包括将工作分解为较小的任务、估算不确定性、计算预期和最坏情况的时间、必要时进行精细化,以及跟踪估算的准确性以便不断改进。

  1. 估算的挑战

    • 软件项目估算普遍困难,研究表明许多 IT 项目成本超支超过 200%,并且平均延误近 70%。
    • 大型软件项目尤其容易超支,预算超过 1500 万美元的项目平均超支 66%,进度延误 33%。
  2. “无估算”方法的局限性

    • 尽管有“无估算”方法和 Agile 方法试图避免时间单位的估算,但在某些情况下,准确的时间估算是不可避免的。例如,销售团队可能需要一个具体的时间表来完成交易,或者其他团队可能依赖于特定功能的交付时间。
  3. 估算的重要性

    • 估算是职业发展的关键技能,能够准确地给出时间表并按时交付任务有助于建立信任。对于希望在技术职业中更进一步的人来说,掌握估算技能至关重要。
  4. 估算是可以学习的技能

    • 估算是一项可以通过实践和反复校准来学习的技能。通过反复的项目分解和时间估算,工程师可以逐渐提高估算的准确性,并了解团队的工作节奏和代码库的复杂性。
  5. 我的估算技术

    • 任务分解:Kaplan-Moss 建议将工作分解为不同复杂度的小任务,并使用实际的日历时间(而非理想化的“程序员时间”)来估算每个任务的完成时间。任务复杂度分为小(1天)、中(3天)、大(5天)和超大(10天)四种。
    • 估算不确定性:在初步估算的基础上,他建议通过应用一个“如果事情出错”的乘数来捕捉不确定性。这个乘数根据不确定性水平(低、中、高、极高)从 1.1 到 5.0 不等。这样可以得到预期时间和最坏情况的时间。
    • 计算时间:通过任务复杂度和不确定性乘数,计算出每个任务的预期时间和最坏情况时间。例如,一个中等复杂度且高不确定性的任务预期需要 3 天,但最坏情况下可能需要 6 天。
    • 精细化估算:如果估算范围过大,他建议通过进一步分解大任务或减少不确定性来精细化估算。可以通过研究、短期试验(spikes)或直接动手完成部分任务来减少不确定性。
    • 跟踪准确性:最后,他强调在项目进行过程中跟踪实际用时,并与估算时间进行比较。这种反馈循环有助于不断改进未来的估算精度。
    • 其他估算技术:Kaplan-Moss 还提到了其他几种估算技术,如 PERT(程序评估与审查技术)、基于证据的调度(Evidence-based scheduling)和水果沙拉 Scrum(Fruit-salad scrum),并指出这些技术也捕捉了时间和不确定性,但采用了不同的方法。

软件项目估算

众所周知,估算软件项目的难度非常大。哈佛商业评论 (HBR) 的一项研究发现,每六个 IT 项目中就有一个成本超支超过 200%,且延迟几乎 70%。麦肯锡 (McKinsey) 的另一项研究发现,IT 项目的平均预算超支 45%,时间超出计划 7%。研究还指出,大型软件项目的问题尤为严重:预算超过 1500 万美元的软件项目,平均预算超支 66%,时间延迟 33%。

实际上,任何在软件行业工作过一段时间的人都经历过类似的情形。你可能曾自信地说“这应该只需要几天时间”,但一个月后却发现自己仍未完成。软件项目估算总是难免遇到霍夫施塔特定律 (Hofstadter’s Law):“事情总是比你预期的时间更长,即使你考虑了霍夫施塔特定律。”

遗憾的是,许多人看到这种模式后,觉得估算软件项目太困难,干脆选择放弃。有一种被称为“无估算” (No Estimates) 的立场认为我们应该完全停止对软件项目进行时间估算。很多敏捷方法采用了任意的评分系统——如故事点 (Story Points)、T 恤尺码等——这些系统的设计目的就是为了避免给出具体的时间估算。

其实,这些观点是有一定道理的。我并不是在批评故事点1。尤其是,“无估算”风格的项目管理常常能促成更有成效的时间规划讨论:这些系统更倾向于提出“接下来两周内我们能完成什么?”而不是“功能 X 需要多长时间?” 对于长期的软件项目来说,这种思考方式通常更为合理。

然而,总有一天你会被问到“功能 X 什么时候上线?”有些情况下,给出一个准确的答复是绝对必要的。比如,销售团队可能需要承诺某个新功能的时间才能达成重要交易。或者,这个功能可能是其他团队的关键依赖,他们需要知道如何安排工作计划。再比如,你的功能可能是一个新产品发布计划中的重要部分,而整个发布流程需要围绕它进行宣传推广,这就需要一个明确的时间表。我可以继续举更多例子,但重点是,很多场合下,估算是无法回避的。

在技术职业发展中,学会如何做出准确的估算是一个重要的“秘诀”。这对我个人来说确实如此:我从不回避给出时间线,并且我学会了如何做到足够准确,以至于人们信任我的估算。

如果你总是避免估算,并且在需要给出时间线时不学着去做,这可能会限制你的职业发展。能够告诉上级和同事什么时候能完成任务,并且按时达成,这种能力会非常显著地提升信任感。这对于个人贡献者和工程经理同样适用:你可以不擅长估算就成为高级工程师或工程经理,但如果想进一步发展,你很可能需要掌握这项技能。

而且这确实是一项技能:估算是可以学会的。你可以学习各种技巧,但说到底,方法并不是最重要的:无论你采用哪种方法,练习才是关键。如果你养成了将项目分解并进行时间估算的习惯,然后对这些估算与实际结果进行对比和校准,久而久之你会变得更加精准:你会了解团队在哪些方面容易受阻,在哪些方面进展迅速;你会发现代码库中哪些部分修改起来快速简便,哪些则需要更多时间;你会逐渐识别出自己的偏见,知道在哪些情况下你更可能高估或低估时间。

我的软件估算技术

虽然软件项目的时间估算很困难,但仍然值得一试,我想详细介绍一下我在制定项目时间表时所使用的技术。

关键特性:捕捉时间不确定性

在这个领域,我认为没有“唯一正确”的技术;这套方法对我很有效,我也成功教会了其他人使用。当然,其他方法也同样适用;我在后面列出了一些

不过,我的方法有一个关键特点,我认为任何有效的估算技术都应该具备:它同时捕捉了时间和不确定性。仅包含时间的估算往往意味着高度确定性:如果你告诉我“这需要10天”,而我们的截止日期是14天后,我会认为一切顺利无忧。反之,如果在相同的期限下你说“这需要10到15天”,那么我就知道可能会超期。

我的技术

我的方法如下:

  1. 将工作分解为更简单的任务
  2. 估算不确定性
  3. 计算预期值和最坏情况下的估算
  4. 必要时调整
  5. 跟踪准确性,以便逐步改进

详细内容请继续阅读:

1. 将工作分解为更简单的任务

几乎所有估算方法的第一步都是根据复杂性将工作分解为更小的任务。我使用以下几种规模:

复杂性时间
1天
3天
1周(5天)
超大2周(10天)

这些名称和时间在一定程度上是任意的(但你应选择一个标准并长期遵守,以便逐步提高准确性)。它们符合我对工作的理解,但其他时间尺度也可以使用。故事点代替标签也是可行的;名称本身不重要。关键是使用实际时间,这意味着:

  • 你必须将复杂性映射到具体时间单位。最终目标是获得一个日历时间估算(例如“4到6周”),并且你应尽量细化这种映射。

  • 使用实际的时钟小时和天数,而非理想化的“开发者工作日”,即假设工程师每天能写8小时代码。换句话说,在上述系统中,一个任务实际上大约需要4小时,考虑到工程师在工作日内的其他事务。

  • 尽量捕捉现实预期时间。不要过于乐观选择“最佳情况”:如果某件事通常需要3天,尽管有时可能1天就能完成,那就算作任务。反之,也不要过于保守:不要为了安全起见将任务升级为任务。以预期时间为准;下一步我们会估算不确定性

任务分解得越细,最终估算就越准确。如果做得好,大部分分解后的任务应为任务应较少,超大任务则最好没有。不过,不必一次性完成所有分解。我通常先做一个初步的粗略估算,其中可能包含一些过大的任务,然后稍后再调整

2. 估算不确定性

如果你现在停下来,仅仅加总所有任务时间,你会得到一个合理估算,但它无法反映确定性。正如我前面所说,这是关键:“20到30天”的估算和“5到45天”虽然中点相同(25天),但意义完全不同。

因此,下一步是捕捉不确定性。最初,你可能会想到捕捉最佳和最坏情况,但我认为这不太有用。我更倾向于捕捉预期情况和最坏情况。提前完成不是问题,根据我的经验,最佳情况很少发生。相反,捕捉如果事情出错可能需要多长时间更为重要。因此,我的不确定性系统从预期时间(上一步已确定)开始,然后应用一个“如果事情出错”的倍数:

不确定性级别倍数
1.1
1.5
2.0
极高5.0

“倍数”是一个缩放因子,用于计算悲观估算。例如,一个任务且不确定性,意味着“我预计这需要3天,但可能会拖到6天”(3 ⨉ 2.0)。或者,一个任务且不确定性,表示“预计需要5天,但可能会稍微延长到第6天。”

与时间估算类似,具体标签和倍数是任意的。我发现这些倍数效果不错,但其他倍数也可以(前提是:选择一个系统并坚持使用)。

和时间估算一样,你应尽量使不确定性低。如果极高不确定性过多,表明你可能需要迭代并调整估算。

3. 进行计算

现在只需根据复杂性和不确定性定义进行计算即可。最终你会得到一个分解后的估算,如下:

任务复杂性不确定性预期最坏情况
重构 doodad1天1.1天
调整列5天7.5天
调整样条曲线极高5天25天
反向歧管进气3天4.5天
部署1天1.1天
总计15天39天

因此,我可能会说这个虚拟项目:“预计大约需要3周。最坏情况下,可能长达8周。”(或者,如果与熟悉我估算方式的人沟通,我可能简化为“3到8周”)。

4. 必要时调整

这样的范围是否可接受?可能是:如果你只需要时间线设定预期,这可能足够了。但如果存在外部因素——例如,另一个项目在等待本项目完成,或市场团队需要提前知道一个会议在六周后能推广哪些内容——那么这个范围可能过于宽泛。

查看任务列表,很容易看出不确定性源自何处:“调整样条曲线”任务的极高不确定性几乎占据了所有的变动。这种情况很常见:你往往能将项目大部分拆解为低不确定性任务,但会留下少数高度不确定的任务。

另一个常见问题是,时间线过长且被几个高复杂度(超大)任务主导。如果这些任务风险较高,那么范围会变得非常宽泛。即便它们不具不确定性,包含太多庞大任务的项目计划也难以生成更精确的时间线(例如,你可能想知道一个六个月项目每月底的进展)。

在这些情况下,你需要通过拆解这些超大任务并降低极高不确定性任务的方式来调整估算。当然,如果这容易做到,你早就已经做了:那些庞大任务难以拆解,风险高的任务有时难以规避。

这里的窍门是投入一些时间——不是为了完成项目,而是为了明确范围或降低风险。我用的工具是时间限制:选择一个任意的时间(通常1-2周),并利用这段时间重新评估或降低风险。你可以通过以下方式度过这段时间:

  • 研究:如果这是他人已经做过的任务,你可能通过研究找到方法或明确不确定性。例如,将服务从 AWS 迁移到 Azure 可能起初看似一个高复杂度、高风险的任务,但这是有先例可循的;与有过类似经验的人交流,你能进一步拆解。如果你在小型公司,可能需要向外部求助,但这完全可行。

  • Spikes 是敏捷开发中的一个概念,专门针对这种情况。其思想是通过编写一些代码探索潜在解决方案。有时 spike 成为项目的一部分,但更多时候它是用来验证某种思路的临时代码。以迁移 AWS 到 Azure 为例:你可以给自己一周时间,尝试迁移一个更小、更简单的服务。然后根据这个 spike 的结果推断出更复杂迁移的更准确估算。

  • JFDI:有时候最好的方法就是直接做:给自己或团队一到两周时间,全力以赴。如果我们花五天时间处理这些样条曲线,可能会发现到结束时完成了一半,并且只需再一周就可以收尾。问题解决:不确定性消除。

没有“正确”的策略。关键在于,如果你面对一个不确定性太高或需要进一步分解的估算,投入一些前期时间换取更好的估算可能更划算。花一到两周时间生成更好的估算,比盲目推进要明智得多。

5. 跟踪准确性,以便逐步改进

最后,确保在项目进行中记录实际用时。这会形成一个反馈回路:你做出预测,观察其与现实的偏差,然后用这些经验逐步训练你的估算技巧。

例如,在项目结束时,我可能会得到如下表格:

任务复杂性不确定性预期最坏情况实际
重构 doodad1天1.1天2天
调整列5天7.5天3天
调整样条曲线极高5天25天10天
反向歧管进气3天4.5天3天
部署1天1.1天3天

从中我可能学到,未来的部署任务其实是复杂度的,调整列也属于,也许下次处理样条曲线时可以减少一些不确定性。

这就是为什么,虽然具体的复杂性和不确定性分类相对任意,坚持一个模型仍然很重要。如果你重新定义了的含义,就需要重新校准所有后续估算。

其他估算方法

正如我在开始时所说:没有“唯一正确”的估算系统。我介绍的方法是我常用且效果不错的,但也有其他方法。因此,最后我提供一些值得研究的其他系统。它们都具备我认为最重要的特性——捕捉时间和不确定性,但具体方式有所不同:

  • 计划评审技术 (Program Evaluation and Review Technique, PERT):PERT 是一个与我上面介绍的方法很相似的系统。它要求进行三类估算:悲观估算 (P),乐观估算 (O),和最有可能的估算 (M)。然后使用公式(O + 4M + P) / 6得出 PERT 估算。我没尝试过 PERT,因为如前所述,我不太注重最佳情况的估算,但它看起来很不错。

  • 基于证据的调度则采取了完全不同的方式。与其从估算入手,不如从工作入手:先跳过估算,直接跟踪任务/故事、其规模及实际用时。随着时间的推移,可以将之前的实际用时应用到剩余任务上,自然而然得出一个估算。

    当这种方法奏效时,效果非常好。它不需要任何前期估算且非常准确。然而,我发现这方法不易操作:它需要稳定的团队、较一致的工作以及足够长的项目周期来积累数据进行预测。我所在的许多团队较为临时,或项目变化频繁、周期短,所以我很少用这种方法。

  • 水果沙拉 Scrum 是一种有趣的故事点或 T-shirt 尺码替代方案。如果你不介意其中的幽默,它解决了故事点或尺码未解决的问题:它还能捕捉不确定性!这种方法通过一个单一轴表示复杂性和不确定性都随着水果大小增加(例如🍉西瓜表示一个既大又不确定的任务)。虽然是种简化处理,但经常有效。要从水果沙拉中推导时间估算,只需将每种水果映射到复杂性/不确定性组合,然后按前述方法计算。我试过这种方法,结果相当准确。此外,在估算这种枯燥的工作中加入些许幽默感也不失为一种好方法。