TDD 已经过时了吗?
一系列关于 Kent Beck、David Heinemeier Hansson 和我之间的对话,主题是 测试驱动开发 (TDD) 及其对软件设计的影响。
起源
一个具有挑衅性的演讲和博客文章引发了一场对话,我们旨在了解彼此的观点和经验。
更多…这场对话源于 David 在 RailsConf 上的主题演讲,他在演讲中表达了对 Rails 社区中 TDD 和单元测试的不满。他随后发表了几篇博客文章,其中第一篇宣称 “TDD 已死”。
几天后,我给他发送了一条关于后续文章的错别字更正,他说他欢迎我对他的演讲和博客文章的看法。然后我们在 Skype 上进行了愉快的、富有成效的讨论,持续了一个小时。David 与 Kent 也进行了类似的讨论,Kent 建议我们三人继续讨论,并将对话公开。David 在推特上发布了这个想法,并获得了许多积极的反馈。
我们对这个系列没有太多计划,只是粗略地设想进行六次 30 分钟的对话。我们之所以这样做,主要是因为我们想了解彼此的经验和观点——我们自恋到认为许多其他人也希望观看我们的对话。我们在早期剧集中没有回答问题,但在最后一集回答了几个问题。在进行这些对话的过程中,我们很享受在博客和推特上继续进行的讨论。
我们要感谢 Thoughtworks,特别是 Wesley Clock,感谢他们帮助我们组织视频设置和安排 hangout 时间。在每集结束后,我都会写下讨论的记录,供那些喜欢阅读而不是观看的人阅读。
1: TDD 和信心
2014 年 5 月 9 日
我们谈论了我们在 TDD 流程中的不同体验,以及 TDD 和自测试代码经常被混淆的方式。
更多…
记录
David 通过提出他对 TDD 和单元测试的三个主要问题来开启了讨论:对 TDD 和单元测试定义的混淆, 通过使用模拟来驱动架构而造成的测试诱导的损害, 以及 TDD 的红/绿/重构循环对他来说从未奏效。 我评论说,要理解 TDD 等的起源,了解其历史很有用,所以 Kent 解释了 TDD 的起源。他从在 Smalltalk 中尝试开始, 发现 TDD 对他的性格来说很有效。
我评论说,当我们第一次在 C3 合作时,我们并没有开始使用 TDD,而是确保每个编程环节都同时交付代码和测试。 Kent 说,程序员应该有信心他们的代码有效,TDD 是一种(并非唯一)方法来实现这一点。 David 喜欢 Ruby 的设计目标——程序员的幸福,并且同意你只有在拥有测试时才算完成——但他不喜欢 TDD 作为实现这一目标的方法。 他认为人们的大脑不同,因此喜欢不同的技术和语言, 他不喜欢 TDD 与你从自测试代码中获得的信心混为一谈。
Kent 谈到了最近在 Facebook 举办的一次黑客马拉松,其中大约一半可以使用 TDD,而另一半不适合。 在可以使用 TDD 的代码中,他发现自己处于一种愉快的流程中,但发现另一部分更棘手。但在非 TDD 部分,他仍然使用回归测试和短反馈循环。 他并不反对混合使用两种风格,就像演奏古典音乐和爵士乐一样。 TDD 让他想起了他在学校学习数学的方式——总是需要例子。
David 曾经遇到过 TDD 流畅的情况,但他的大部分工作并非如此—— 他的问题是,你愿意为了获得这种流畅性而牺牲什么? 许多人做出了糟糕的权衡,尤其是在大量使用模拟的情况下。 Kent 认为这是关于权衡的:是否值得让中间结果可测试? 他以编译器为例,编译器中的中间解析树是一个很好的测试点,也是一个更好的设计。 但针对 David 关于模拟的问题,他说自己很少使用模拟, 他担心那些使用模拟的人经常发现重构很困难,而他发现测试使重构更容易。
我评论说,术语存在两个问题,导致不同的东西被混淆在一起:首先,DHH 对 TDD 的批评是基于一个假设,即你必须在 TDD 中使用大量模拟,而事实并非如此; 其次,自测试代码和 TDD 之间存在差异。TDD 是实现自测试代码的一种方法。 David 说,他的反应是看到人们将 TDD 描述为一种道德上的事情,这种方式依赖于大量模拟, 结果是许多代码由于其对隔离单元测试的渴望而设计得很糟糕。
我最后扮演了时间警察的角色,说在下一次会议中,我们将探讨 TDD 如何可能导致损害,如果这确实是损害,以及我们如何判断这是否是损害。
2: 测试诱导的设计损害
2014 年 5 月 16 日
David 认为,使用 TDD 会导致诸如六边形 Rails 之类的做法,由于过度间接的复杂性,这是测试诱导的设计损害。Kent 认为这与 TDD 关系不大,更多的是与设计决策的质量有关。
在观看这场对话之前,你应该查看 David 准备的 gist,它展示了他所担心的设计损害的示例。你也可以观看视频,其中备受怀念的 Jim Weirich 探索了这种六边形架构方法。
更多…
记录
我从提出问题开始。TDD 会导致设计损害吗?由此产生的损害真的是损害吗?我们如何判断设计是否被损害了? David 描述了 他之前发布的 gist。它是一个示例,展示了人们使用 TDD 和大量模拟时所达成的架构类型。应用程序的每一层都被隔离,例如,控制器可以在不与真实模型、数据库或请求/响应循环交互的情况下进行测试。 对 David 来说,重要的是不是具体的示例,而是为了更容易地进行隔离测试而需要的无谓间接和复杂性。
Kent 说,将测试诱导的损害归咎于 TDD,就像开车去一个糟糕的地方,然后责怪汽车一样。 David 展示的设计并非源于 TDD,真正的问题是,这些间接方法在某些情况下都是很好的技巧,我们需要了解它们是否值得付出代价。 David 不认同,他说,一旦你骑上 TDD 的马(或汽车),它就会鼓励你走上一条特定的道路——它会导致一个一次测试就产生的怪物。 Kent 反驳说,这更像是 一次设计决策。TDD 对设计施加了一种进化压力,人们对测试覆盖范围的粒度有不同的偏好。
Kent 问 David,他希望用这个 gist 做些什么,而它的结构使得这些事情变得困难。 (“如果它只是在那里,谁在乎呢——当我想要改变它时,设计才真正重要”)。 David 回答说,代码的大小与改变它的难易程度之间存在直接关系。所有这些间接方法都必须保持同步, 10 行代码比 60 行代码更容易理解和改变。 每一层间接都会带来很高的成本。 David 继续说,TDD 的红/绿/重构流程非常上瘾 (Kent 观察到,他是世界上最差劲的毒品贩子),这种上瘾导致人们做出这些糟糕的决定。 我不同意这种说法,我认为这不是由于 TDD 造成的,而是由于对隔离的渴望, 六边形架构的本质就是从其环境(在本例中是 Rails)中隔离。
David 说,人们想要隔离的原因是由于 TDD, 他听到过各种关于隔离的论点,但只有测试论点是有道理的。 他认为,有人想要将 Rails 应用程序变成命令行应用程序的想法非常荒谬, 同样地,你不能简单地用对 Web 服务的调用来替换内存存储,因为它们具有不同的操作特性。 这些可替换的梦想并非真正目标——真正目标是隔离测试。 Kent 同意,你不能将内存存储和 Web 服务视为相同(“你可能认为你已经解耦了,但实际上你并没有”),因为它们的失败情况不同。 元素之间的边界会在一定程度上泄漏“问题是我们愿意花多少钱来实现元素之间的多少解耦”。
肯特认为,10 行代码和 60 行代码之间的区别是一个凝聚力问题。 大卫同意,但认为凝聚力和耦合通常是相互对立的。更高的耦合通常值得付出代价以获得更好的凝聚力。 肯特观察到,还有其他方法可以消除外部依赖,你也可以使用中间结果,这就是编译器的工作原理。 难以测试的东西表明你需要一个设计洞察力,站起来走走通常很有用,可以找到那些导致更好设计并更易于测试的洞察力。 大卫同意测试可以带来更好的设计,但表示他的经验通常相反,没有一个好的可测试设计。 肯特指责大卫缺乏自信,也许你今天看不到洞察力,所以你必须在同时取得进展,但他乐观地认为他最终会找到它们。 大卫驳斥了这一点,称之为“基于信仰的 TDD”——他过去也有这种感觉,但当他找不到理想的解决方案时,就陷入了令人沮丧的循环。 肯特澄清说他不是在谈论 TDD,而是在谈论软件设计, 这与 TDD 无关,而是关于如何获得反馈。 思考软件设计才是关键,因为当你获得良好的设计洞察力时,它会带来巨大的回报。 获得这些洞察力与你的工作流程无关,而是与诸如何时工作何时休息、从其他地方收集影响、与他人合作等事情有关。
我们最后说,下次的主题将是探讨在编程过程中如何寻求反馈的权衡。
进一步阅读
- 大卫的 gist 展示了他所担心的设计损害的示例。该 gist 基于对 Jim Weirich 的演讲 中给出的六边形 Rails 架构的探索。
- 大卫的博客文章 介绍了测试诱发的设计损害问题
- 术语“六边形架构”最初由 Alistair Cockburn 提出,它也称为“端口和适配器”。
- 在这次闲聊之后,我和我的同事 Badri Janakiraman 又进行了一次关于 六边形 Rails 架构的本质及其使用权衡 的闲聊。
3: 反馈和质量保证
2014 年 5 月 20 日
我们讨论了我们在编程过程中获得反馈的各种方式以及质量保证在向开发人员提供反馈方面所起的作用。
更多…
记录
肯特首先说,涉及 TDD 的决策是关于权衡的: “在理想的世界里,我们会对我们的编程决策获得即时、无误的反馈”…… “我敲下的每一个键,如果代码已准备好部署,它就会立即部署。” 但目前这种理想状态是不可能的,所以问题是我们应该从这种理想状态退缩多少。 他列举了权衡中的几个约束。
- 频率:我们希望以多快的速度获得反馈 ?
- 保真度:我们希望红绿信号有多准确?
- 开销:我们准备付出多少代价?
- 生命周期:这个软件将存在多久,这既是概率也是时间。
他认为,我们需要比较这四个约束。 “我们这次闲聊不是为了达成一致——我的个人目标只是通过向那些准备以建设性方式拆解我想法的人阐述这些权衡来理解权衡的集合。”
我概述了我们希望获得反馈的三件事。
- 软件是否为软件用户做了一些有用的事情? 有时测试对此有帮助(例如工资计算),有时没有帮助(例如 html 渲染)。
- 我是否破坏了任何东西?“这就是自测试代码……如此有用的地方。” 我希望看到每个测试至少失败一次。
- 我的代码库是否健康? 这样我才能继续快速构建东西。当你不确定谁会接手代码时,这个元素就变得更加棘手。
大卫提出了一个话题,即 TDD 的成功导致了对质量保证的忽视。 许多公司采用了 TDD 并取消了质量保证, Basecamp 直到几年前才有了质量保证。 他认为 TDD 让程序员“变得过于自信,认为他们不需要质量保证”。 虽然旧模式已经过时,但钟摆摆得太远了:“我认为你不能在任何有质量的东西上工作并生产出优秀的软件,除非有人来测试它,而这个人不是你。” “这令人失望,因为我看到了质量保证人员参与进来是多么强大。”
另一个问题是,要理解权衡,你必须理解成本,所有关于 TDD 的讨论都集中在益处上。 这种对成本的忽视是人们无法理解测试诱发的损害的原因。 权衡的连续体适用于其他事物。考虑可靠性的成本:从 99% 到 99.999% 的成本比达到 99% 的成本要高得多。 我们还必须考虑关键性。高可靠性对于航天飞机和起搏器很重要,但对于探索性网站来说并不重要。 在没有测试的情况下不编写一行生产代码的规则不符合关于关键性的权衡。
肯特想回到质量保证的问题,他认为与质量保证的旧关系是功能失调的。 他办公室里唯一一件 Facebook 的纪念品是一张海报,上面写着“Facebook 的任何事情都不是别人的问题”,他觉得 Facebook 对于一家如此规模的公司来说,非常遵循这一点。 Facebook 直到最近才有了质量保证,程序员承担了这种责任。 “这是一个‘与什么相比’的问题?”与拥有有效的质量保证相比,没有质量保证更糟糕,但没有质量保证比旧的功能失调的关系要好。 我补充说,在 Thoughtworks,我们几乎总是会在项目中加入质量保证。 我也觉得,从 90 年代开始的重大转变不仅仅是摆脱了功能失调的对抗关系,还摆脱了手动脚本测试。 而且,初创公司可以在没有质量保证的情况下运营,这是令人振奋的。 大卫同意,有意识地用质量保证换取初始速度是件好事,但有些人把程序员测试做得太过火,没有看到探索性测试的价值。 如果开发人员认为他们可以在没有质量保证的情况下创建足够高质量的软件,那他们就错了,你的测试可能是绿色的,但当它投入生产时,用户会做一些你意想不到的事情。
大卫说,最糟糕的是,开发人员不参与客户服务。 许多程序员不想轮流值班,因为这是一件苦差事,但它也是一个反馈循环。 带有绿色测试的代码可能是一个低于你想要达到的水平的平台。 肯特认为,我们应该在绿色条中添加一些红色像素,以提醒我们这些限制。 “轮流值班是教你哪些测试你没有编写的反馈循环。” Facebook 的程序员必须轮流值班,每个人都抱怨,但他们不可能放弃它。 一旦你认为你不再犯错误,那就是一个错误,你就停止成长。最终,“世界不会让你假装你不再犯错误。” 他宁愿付出代价,通过凌晨 2 点的电话尽早发现错误。
我最后观察到,我们没有时间更多地讨论测试的成本(大卫之前提到的第二点),所以我建议我们下次再讨论这个话题。我还提到,碰巧 今天我的网站上发表了一篇由 Mike Bland 撰写的文章,它正好讨论了这个话题。
4: 测试的成本
2014 年 5 月 27 日
我们讨论了测试和 TDD 的一些缺点:你能做太多测试吗?团队是否会过分重视测试而忽视功能代码?
更多…
记录
大卫首先说,“要谈论权衡,你真的必须理解缺点,因为如果没有缺点,就没有权衡。” 他接着说,TDD 不会强迫你做任何事,但它会把你推向某些方向。 他想要提出的第一个问题是过度测试。人们常说,你不应该在没有失败测试的情况下编写一行代码,起初这似乎很合理,但它会导致过度测试,例如,每行生产代码有四行测试代码。这意味着,当你需要更改行为时,你需要更改更多代码。 肯特曾经说过,“你没有被付钱来编写测试,你只需要编写足够的测试来让你有信心”——所以他问我和肯特是否在每行生产代码之前都编写测试?
肯特回答说,“这取决于情况,这将是我对任何有趣问题的答案的开头。” 使用 JUnit 时,他们对测试优先非常严格,并且对结果非常满意——所以他不认为在使用 TDD 时总是会过度测试。 Herb Derby 提出了增量覆盖的概念——这个测试提供了哪些独特的覆盖率?增量覆盖率为零的测试应该被删除,除非它们提供某种沟通目的。 他说,他经常会编写一个系统级测试,编写一些代码来实现它,进行一些重构,最后会扔掉最初的测试。许多人对扔掉测试感到恐慌,但如果你没有从中得到任何好处,你应该扔掉它。 如果同一件事以多种方式进行测试,那就是耦合,而耦合是有成本的。
我说,我确信存在过度测试的代码,事实上,如果有人这样做,那一定是 Thoughtworks,因为我们有强大的测试文化。很难找到合适的数量,有时你会过度测试,有时你会测试不足。我预计会不时地过度测试,这不是什么需要担心的事情,除非它太大。 关于测试每一行代码的观点,我提出了一个问题:“如果我搞砸了这行代码,测试会失败吗?” 我有时会故意注释掉一行代码或反转一个条件,然后运行测试,以确保其中一个测试失败。 我的另一个心理测试(来自肯特)是只测试可能出现故障的东西。 我假设库是有效的(除非它们真的很奇怪)。我问自己,我是否会搞砸我对库的使用,以及错误的后果有多严重。
肯特宣称,测试代码行数与生产代码行数的比率是一个虚假的指标。 对他来说,一次很有意义的经历是观看 Christopher Glaeser 编写编译器,他每行编译器代码有 4 行测试代码——但这是因为编译器有很多耦合。一个更简单的系统将有更小的比率。 大卫说,检测注释掉一行代码意味着 100% 的测试覆盖率。思考哪些可能会出现故障是值得探索的,Rails 的声明式语句不会导致足够的故障来值得测试,所以他对远低于 100% 的覆盖率感到满意。
我回答说,“如果你不能自信地修改代码,那么你的测试就不够多(或者不够好),” 以及 “当你修改代码时,如果你觉得修改测试比修改代码花费更多精力,那么就说明测试太多了。” 你想要处于“刚刚好”的区域,但这需要你积累经验,知道你和你的团队容易犯哪些错误,以及哪些错误不会造成问题。 我说我喜欢“我可以注释掉一行代码”的方法,当我不确定自己的方向时,这是一个起点,但随着我在一个环境中工作得越多,我可以想出更好的启发式方法。 David 认为这种调整在稳定的产品团队和将代码交给未知团队的咨询团队之间是不同的,因此咨询团队需要更多测试。 Kent 说学习测试先行的方法很有好处,就像开发中棘手部分的四轮驱动低速档。
David 提出了下一个问题:许多人过去认为文档比代码更重要。现在他担心人们认为测试比功能代码更重要。与之相关的是,对 TDD 循环中的重构部分重视不足。所有这些都导致了对重构和保持代码清晰的能量不足。 Kent 描述了他最近经历的一件事,他扔掉了部分生产代码,但保留了测试并重新实现它。他非常喜欢这种方法,因为测试告诉他新代码是否正常工作。 这引出了一个有趣的问题:你宁愿扔掉代码保留测试,还是反之?在不同的情况下,你会给出不同的答案。
我说我发现了一些情况,阅读测试帮助我理解代码在做什么。我认为两者没有哪个更重要——关键是双重检查,如果它们出现不匹配,就会出现错误。 我同意 David 的观点,我有时会感觉到团队在测试环境上投入的精力比在支持用户上投入的精力更多,测试应该是达到目的的手段。 我发现当我澄清代码时,我会得到多巴胺的刺激,但我最大的兴奋点是当我需要添加一个功能时,我认为它会很棘手,但结果却很简单。这是由于代码清晰造成的,但清理代码和获得多巴胺的刺激之间存在距离。 Kent 展示了 Jeff Eastman 的一个比喻,这个比喻用文字很难描述。他从大型设计简化中获得了快感。 他觉得解释一个新测试工作的好处很容易,但很难说明清理设计的好处。
David 说我们经常关注可以量化的事情,但你不能将设计质量简化为一个数字——所以人们优先考虑列表中排名较低的事情,比如测试速度、覆盖率和比率。这些东西是蜜罐,我们需要意识到它们的诱惑。 Cucumber 真的让他很生气——对测试环境的赞美,而不是对生产代码的赞美。只有在与非技术利益相关者编写测试的虚构甜蜜点中才有用。 过去,推销 TDD 很重要,但现在它已经征服了一切,我们需要探索它的缺点。 我不同意 TDD 占主导地位的说法,我听说过很多地方还没有采用 TDD。
进一步阅读
- 关于误用度量的问题,请查看 Pat Kua 关于 度量适当使用 的文章。
- 我已经写了更多关于 测试覆盖率的使用和误用 的内容。
5: 回答问题
2014 年 6 月 4 日
我们回答观众的问题:TDD 的开源示例有哪些,什么变化会让我们改变对 TDD 的使用,以及它对没有经验的开发人员的效果如何。最后,我们总结了我们对 TDD 健康状况的看法。
更多…
记录
我首先说我们将通过选择和回答观众提交的一些问题来构建这次聚会。 David 从选择 Mike Harris 的一个问题开始,他询问了使用过 TDD 的开源项目的示例,这些项目要么产生了测试诱导的设计损坏,要么很好地使用了 TDD。David 回答说没有好的例子,这是这类辩论的问题之一。 我们通常没有好的应用示例,因为开源贡献者通常在私有应用程序和开源通用框架和库上工作。 因此,我们带着不同的背景进入这些讨论, 这往往让人觉得分歧比实际情况更多。当你有真实的代码而不是哲学原则时,人们就会走到一起。 我们拥有的代码示例是次要示例,而不是人们实际正在使用的示例, 因此你必须通过演示来了解人们——这就是他使用 Jim Weirich 的示例来解释设计损坏的原因。对于好的 rails 代码,他建议使用展示标准测试方法的 Rails 书籍。
我评论说,理解真实的代码需要花费很多精力。我深入研究了一些代码库,但这需要很多时间,即使这样,它也不像与团队一起工作那样理解。 Kent 说 JUnit 是一个严格使用 TDD 并取得成功的项目的例子。 但它不是本次讨论的良好例子,因为它具有清晰的接口,为 TDD 提供了一个甜蜜点。我们在这里讨论的是不同类型的应用程序。 如果有人有好的例子,他们应该写出来。
David 说我的评论说明我们不能将编程视为科学——我们不能客观地评估技术。 这并不意味着它不值得讨论。我们无法得到明确的答案,你需要弄清楚什么对你来说是合理的。 Kent 同意我们无法复制实验,但他说我们仍然可以以科学的心态个人地看待事物。我们可以亲自尝试一些东西,但你无法得到普遍的答案。
Kent 选择了下一个问题(由 Graham Lee 提出):关于我们编写软件的方式,什么变化会使 TDD 对 Kent 和 Martin 变得多余或过时,以及关于 TDD 的执行方式,什么变化会使它对 David 变得有用? Kent 回答说他的 RIP TDD 帖子 指出了他的立场(虽然有点讽刺)。TDD 解决了许多问题,从信心开始。 TDD 还允许他将问题分解成部分,解决特定情况,而不必一次性解决一般情况。 他还没有准备好放弃 TDD,仅仅因为它很难。
我说对我来说,这不是关于会使 TDD 过时的变化,而是关于 TDD 在不同环境中的适用性。我经历过以机械的方式遵循 TDD,以一种平静的“快速不慌不忙”的方式,我偶然发现了好的设计。 我最近的大部分编程都是我的网站工具链,虽然我确实在逐步进行,并且有一个很好的回归测试套件——但我没有发现 TDD 适用。但当我构建 infodeck 代码时,有很多应用行为 TDD 是有效的。 “有些环境非常适合 TDD,有些环境不太适合”。人们将自己的个性带入到这个环境中。
David 说他的经历类似。他最初接触测试是通过 TDD,他很喜欢它,并试图将其应用于所有事情,但慢慢地意识到 MVC Web 应用程序中的许多领域并不适合 TDD。 这并不意味着 TDD 对某些情况无效,只是这些情况只占他工作的一小部分。 但放弃 TDD 并不意味着他想要放弃自测试代码——这对他来说一直是 TDD 的价值所在。
我说这正是人们应该采用 TDD(或任何技术)的方式。尝试一下,过度使用它,找到适合你的模式。然后也深入研究一下:“tdd 是通往自测试代码的入门毒品”。 Kent 说他不介意人们通过这种过程最终得到什么样的工作流程。他使用 TDD 的经验与 David 的不同。他发现开发中有一些时刻,有些事情很难,他想到一个具有特定协议的对象,可以简化事情。 TDD 为他提供了一种快速获得这种想法反馈的机制,尝试和使用 API 的示例并实现它。 他也发现了一些 TDD 不适合的情况。然后他发现 command-R 是获得反馈的好方法, 但他希望能够看到简化对象的那一刻,并用 TDD 尝试一下。
我选择了下一个问题(来自 Tudor Pavel):TDD 如何与经验不足的开发人员一起工作。我回答说 TDD 强制人们做小部分工作,并帮助他们将接口与实现分离。它不能保证取得良好的结果,因为你不能在没有经验的情况下进行良好的设计。 当经验不足的人使用 TDD 时,他们通常不会进行足够的重构,导致次优设计。你不能将一个没有经验的开发人员的输出与一个有经验的开发人员的输出进行比较,你必须将其与该没有经验的开发人员在没有 TDD 的情况下会做的事情进行比较。虽然我们无法衡量这一点,但它似乎是有利的,并且创建了一个自测试代码库,该代码库更容易通过重构来改进。因此,TDD 为你提供了一个良好的起点。
David 说这就是他从 TDD 中获得的价值。他从 TDD 开始,发现它是一个很棒的训练轮,但他觉得讨论没有足够地进行下去。 当人们说你必须给新人简单、直接、夸张的建议,否则他们不会去做时,他持怀疑态度。这表明你对所教内容缺乏信心。 我同意这种对教条式陈述的厌恶。如果我找不到反对我所描述内容的论据,我会感到怀疑。然而,一个转折是,我们确实必须不断地为新人重复入门内容:有些人抵制重复基本内容。
David 认为这就是我们现在进行这次谈话的原因。当人们描述 TDD 时,他们会添加自己对基本内容的理解,经过十年的这种理解,你最终会从起点走得很远,而且不是一个好地方。 你需要按下重置按钮,这是一种粗糙但有效的方法。当他说 TDD 已经死了时,他指的是当前的变异——我们必须回到第一性原理。 Kent 说他对 David 最初的主题演讲的直觉反应就在那个层面上。程序员经常会做很多次同样的事情,把事情弄得太复杂,并在工作中坚持使用功能失调的系统。他很乐意重新回到第一性原理,但他不想失去人们在过去十年对编程的期望的演变。你应该能够感到自信,指出进步,进行富有成效的技术合作。他觉得他现在可以在工作中做真实的自己,而这在他开始职业生涯时是不可能的。
David 认为,几件事同时发生了:TDD、XP 和 Ruby。人们嘲笑编程应该很有趣的想法,但他希望在开发 Rails 时继续坚持这个想法。现在他认为 Ruby 世界认为这种快乐是理所当然的——这些东西已经赢了——敏捷也一样。 我不同意敏捷已经赢了——这个标签赢了,但很多人说他们做敏捷,但实际上并没有。对于像这样的事情来说,这是很典型的,我称之为 语义扩散 的过程。最大的胜利是,现在我们能够在客户面前公开地进行敏捷开发。 David 观察到其他事物也存在这种重启问题——十年后,你会得到很多垃圾。他以 Pinkberry 为例,它最初只有两种口味,但后来就像其他冰淇淋一样,有了复杂的口味范围—— “大多数人无法放过好主意。” TDD 和敏捷现在都是非常宽泛的概念。说他们正在做敏捷的人却做着相反的事情。 Kent 说,他还没有看到 David 在 TDD 中遇到的那些问题。他在工作中始终从第一原则应用 TDD。
Kent 感谢 David 提出了 TDD 积累了一些藤壶,需要清理一下。 David 说,他在 Rails 中也看到了类似的问题。他以基本形式使用 Rails,并对看到的一些 Rails 使用方式感到震惊。 Kent 回忆起第一次 OOPSLA 时,XP 受到关注,Jim Rumbaugh 说,十年后你不会认出 XP 会变成什么样,他是对的。 我说,这就是成功的表现,另一种情况是,事情不会起飞。总是很难判断,不好的事情是由于技术本身的缺陷造成的,还是由于技术的误用造成的。我们所能做的就是不断重复基本原则和好的教训。 David 同意:“你要么成为英雄而死,要么成为恶棍。” Ruby 是对编程中好想法的重启。函数式编程是另一种重启。这些重启是有益的。 他对 Rails 和 TDD 持续了这么长时间印象深刻。 在 Rails 之前,他使用 PHP 工作。如果你能很好地使用 PHP,你可以用它编写出好的代码,他认为其他语言在鼓励良好使用方面做得更好。他认为,在 MVC Web 应用程序中很好地使用 TDD 比编写干净的 PHP 更难。
Kent 还没有发现这一点——只要他能够将问题的一部分分解成有用的抽象,他就可以使用 TDD。 他想探索其他途径来实现这些将自己全部投入编程的大目标。他将继续通过实验来探索这一点:做得太多,做得不够,找到最佳区域,并理解原因。 他坚定地反驳了 David:TDD 并没有死,但很高兴 David 给它点燃了火焰,让它像凤凰一样重生。
David 说,他之所以开始这场讨论,是因为人们不愿意谈论 TDD 不起作用的情况。他们感觉不好或没有信心,但被告知必须使用 TDD。他想打开可接受反应的范围,以便我们能够讨论 TDD 何时适用,何时不适用。 互联网上很多人都在谈论 TDD 很好,但人们害怕说它对他们不起作用。对于 David 来说,婴儿是自测试代码,我们不想在质疑 TDD 时失去它。
我最后说(正如我们在开始之前所怀疑的那样),我们有很多共识。我们都非常重视自测试代码,我们都同意 TDD 在某些情况下很有价值,我们可能在多少情况下存在分歧(尽管很难真正确定)。 所有的一切仍然归结为一点,如果你参与了软件开发,你必须认真思考它,你必须建立适合你和你的团队的实践,你不能盲目地接受任何技术。你需要尝试它,使用它,过度使用它,并找到适合你和你的团队的方法。我们不是在进行编码科学,所以我们必须依靠自己的经验。
要加入这场对话,请使用 #isTDDDead 标签 推文。
我们已将所有这些讨论的音频添加到 Thoughtworks 播客提要中,您可以通过 itunes 或 soundcloud 获取。
如果您喜欢这段视频,您可能还会喜欢 我和 Badri Janakiraman 的这次对话,我们深入探讨了六边形架构、是否使用 Active Record 或 Data Mapper,以及是否将 Rails 视为平台或组件套件。
这些页面,包括我们对话的记录,已被翻译成 韩语