High Internal Quality Makes Software Cheaper, Not More Expensive
Martin Fowler argues that the familiar quality-versus-cost trade-off does not apply to the internal quality of software. He splits quality into external attributes users can perceive — UI, defects — and internal ones they cannot, such as architecture, naming, and modularity. Customers will pay more for a better interface but cannot judge internal structure, so it is usually treated as a cost to be cut. Fowler's claim runs the other way: internal quality lowers the cost of future change, making software cheaper to produce, not more expensive. A pseudo-graph of cumulative functionality against time shows low-quality projects starting fast and then stalling as cruft accumulates; the developers he canvassed report being slowed within weeks. Even the best teams create cruft — they hold it down with automated tests, frequent refactoring, and continuous integration. He concedes that output cannot be measured, so the crossing point rests on experience rather than data. Written for engineers who need an economic, not moralistic, case for quality.
A common debate in software development projects is between spending time on improving the quality of the software versus concentrating on releasing more valuable features. Usually the pressure to deliver functionality dominates the discussion, leading many developers to complain that they don't have time to work on architecture and code quality.
Betteridge's Law of headlines is an adage that says any article with a headline or title that ends in a question mark can be summarized by “no”. Those that know me would not doubt my desire to subvert such a law. But this article goes further than that - it subverts the question itself. The question assumes the common trade-off between quality and cost. With this article I'll explain that this trade-off does not apply to software - that high quality software is actually cheaper to produce.
软件开发项目里常见的争论是:把时间花在提升软件质量上,还是集中精力发布更有价值的功能。通常,交付功能的压力会主导讨论,结果许多开发者抱怨自己没时间做架构和代码质量。
贝特里奇头条定律(Betteridge's Law of headlines)有句老话:任何以问号结尾的标题,都可以用“不”来概括。认识我的人不会怀疑我颠覆这条定律的欲望。但这篇文章走得更远——它颠覆的是问题本身。这个问题预设了质量与成本之间常见的权衡关系。本文将说明,这种权衡并不适用于软件——高质量软件实际上生产成本更低。
Although most of my writing is aimed at professional software developers, for this article I'm not going to assume any knowledge of the mechanics of software development. My hope is that this is an article that can be valuable to anyone involved with thinking about software efforts, particularly those, such as business leaders, that act as customers of software development teams.
虽然我的大部分写作面向专业软件开发者,但本文不会预设读者了解软件开发的机制。我希望这篇文章对任何参与思考软件工作的人都有价值,尤其是那些作为软件开发团队客户的人,比如企业领导者。
As I mentioned in the opening, we are all used to a trade-off between quality and cost. When I replace my smart phone, I can choose a more expensive model with faster processor, better screen, and more memory. Or I can give up some of those qualities to pay less money. It's not an absolute rule, sometimes we can get bargains where a high quality good is cheaper. More often we have different values to quality - some people don't really notice how one screen is nicer than another. But the assumption is true most of the time, higher quality usually costs more.
正如开头所说,我们都习惯了质量与成本之间的权衡。换智能手机时,我可以选更贵的型号,处理器更快、屏幕更好、内存更大;也可以放弃一些品质,少花点钱。这不是绝对规律,有时我们能捡到便宜,用更低价格买到高质量商品。更常见的情况是,每个人对质量的看重程度不同——有些人根本注意不到一块屏幕比另一块好在哪里。但大多数时候这个假设成立:质量更高,通常成本也更高。
If I'm going to talk about quality for software, I need to explain what that is. Here lies the first complication - there are many things that can count as quality for software. I can consider the user-interface: does it easily lead me through the tasks I need to do, making me more efficient and removing frustrations? I can consider its reliability: does it contain defects that cause errors and frustration? Another aspect is its architecture: is the source code divided into clear modules, so that programmers can easily find and understand which bit of the code they need to work on this week?
如果我要谈软件质量,就得先说明它是什么。第一个复杂之处就在这里——软件质量可以指很多事。可以是用户界面:它能否顺畅引导我完成需要做的任务,让我更高效、少些挫败感?可以是可靠性:它是否包含会导致错误和沮丧的缺陷?另一个方面是架构:源代码是否划分成清晰的模块,让程序员能轻松找到并理解本周需要处理的那部分代码?
These three examples of quality are not an exhaustive list, but they are enough to illustrate an important point. If I'm a customer, or user, of the software, I don't appreciate some of the things we'd refer to as quality. A user can tell if the user-interface is good. An executive can tell if the software is making her staff more efficient at their work. Users and customers will notice defects, particularly should they corrupt data or render the system inoperative for a while. But customers and users cannot perceive the architecture of the software.
I thus divide software quality attributes into external (such as the UI and defects) and internal (architecture). The distinction is that users and customers can see what makes a software product have high external quality, but cannot tell the difference between higher or lower internal quality.
上述三个例子并不穷尽,但足以说明一个重要观点。如果我是软件的客户或用户,有些被我们称为质量的东西,我并不会直接感受到。用户能判断界面好不好;高管能判断软件是否让员工工作更高效。用户和客户会注意到缺陷,尤其是当缺陷破坏数据或让系统停摆一段时间时。但客户和用户无法感知软件的架构。
因此我把软件质量属性分为外部质量(如界面和缺陷)和内部质量(架构)。区别在于:用户和客户能看出一个软件产品为什么外部质量高,却无法分辨内部质量是高还是低。
Since internal quality isn't something that customers or users can see - does it matter? Let's imagine Rebecca and I write an application to track and predict flight delays. Both our applications do the same essential function, both have equally elegant user interfaces, and both have hardly any defects. The only difference is that her internal source code is neatly organized, while mine is a tangled mess. There is one other difference: I sell mine for $6 and she sells hers for $10.
Since a customer never sees this source code, and it doesn't affect the operation of the app, why would anyone pay an extra $4 for Rebecca's software? Put more generally this should mean that it isn't worth paying more money for higher internal quality.
Another way I put this is that it makes sense to trade cost for external quality but it makes no sense to trade cost for internal quality. A user can judge whether they want to pay more to get a better user interface, since they can assess whether the user interface is sufficiently nicer to be worth the extra money. But a user can't see the internal modular structure of the software, let alone judge that it's better. Why pay more for something that has no effect? Since that's the case - why should any software developer put their time and effort into improving the internal quality of their work?
既然客户或用户看不到内部质量,那它重要吗?假设我和 Rebecca 各写了一个追踪和预测航班延误的应用。两个应用核心功能相同,界面同样优雅,而且几乎都没有缺陷。唯一区别是她的内部源代码组织得井井有条,而我的乱成一团。还有一个区别:我的卖 6 美元,她的卖 10 美元。
既然客户从来看不到源代码,它也不影响应用运行,为什么会有人多花 4 美元买 Rebecca 的软件?更一般地说,这似乎意味着不值得为更高的内部质量多付钱。
换种说法:为外部质量牺牲成本说得通,为内部质量牺牲成本则没有道理。用户可以判断是否愿意多花钱获得更好的界面,因为他们能评估界面是否好到值得多花钱。但用户看不到软件的内部模块结构,更别说判断它更好。为什么要为没有影响的东西多付钱?既然如此——软件开发者又为什么要把时间和精力投入提升内部质量?
So why is it that software developers make an issue out of internal quality? Programmers spend most of their time modifying code. Even in a new system, almost all programming is done in the context of an existing code base. When I want to add a new feature to the software, my first task is to figure out how this feature fits into the flow of the existing application. I then need to change that flow to let my feature fit in. I often need to use data that's already in the application, so I need to understand what the data represents, how it relates to the data around it, and what data I may need to add for my new feature.
那么,软件开发者为什么会把内部质量当回事?程序员的大部分时间都在修改代码。即便是在新系统里,几乎所有编程也都是在既有代码库的背景下进行的。当我想给软件加一个新功能,第一件事是弄清楚这个功能如何融入现有应用的流程。接着我需要修改流程,让新功能放得进去。我还经常需要使用应用中已有的数据,因此必须理解这些数据代表什么、与周围数据有什么关系,以及为了新功能可能还要补充哪些数据。
All of this is about me understanding the existing code. But it's very easy for software to be hard to understand. Logic can get tangled, the data can be hard to follow, the names used to refer to things may have made sense to Tony six months ago, but are as mysterious to me as his reasons for leaving the company. All of these are forms of what developers refer to as cruft - the difference between the current code and how it would ideally be.
这一切都关乎我能否理解现有代码。但软件很容易变得难以理解。逻辑可能纠缠不清,数据可能难以追踪,指代事物的命名也许半年前对 Tony 还有意义,但对我来说,就像他离职的原因一样神秘。这些都是开发者所说的 cruft(代码锈蚀)——当前代码与理想代码之间的差距。
One of the primary features of internal quality is making it easier for me to figure out how the application works so I can see how to add things. If the software is nicely divided into separate modules, I don't have to read all 500,000 lines of code, I can quickly find a few hundred lines in a couple of modules. If we've put the effort into clear naming, I can quickly understand what the various part of the code does without having to puzzle through the details. If the data sensibly follows the language and structure of the underlying business, I can easily understand how it correlates to the request I'm getting from the customer service reps. Cruft adds to the time it take for me to understand how to make a change, and also increases the chance that I'll make a mistake. If I spot my mistakes, then there's more time lost as I have to understand what the fault is and how to fix it. If I don't spot them, then we get production defects, and more time spend fixing things later.
内部质量的主要作用之一,是让我更容易弄清楚应用如何工作,从而看到该怎么添加东西。如果软件很好地划分成独立模块,我就不必读完 50 万行代码,而能在几个模块中快速找到相关的那几百行。如果我们在命名上花了功夫,我就能迅速理解代码各个部分在做什么,不必费力琢磨细节。如果数据合理地遵循底层业务的语言和结构,我就能轻松理解它如何对应客服代表转来的请求。cruft 会增加我理解如何修改代码的时间,也提高我犯错的概率。如果我发现了错误,还要花更多时间理解问题出在哪里、怎么修;如果没发现,就会产生线上缺陷,之后又要花更多时间修补。
My changes also affect the future. I may see a quick way to put in this feature, but it's a route that goes against the modular structure of the program, adding cruft. If I take that path, I'll make it quicker for me today, but slow down everyone else who has to deal with this code in future weeks and months. Once other members of the team make the same decision, an easy to modify application can quickly accumulate cruft to the point where every little change takes many weeks of effort.
我的修改还会影响未来。我可能看到一条添加功能的捷径,但它违背程序的模块结构,会增加 cruft。如果我走这条路,今天我是快了,却会让未来几周、几个月里要处理这段代码的所有人变慢。一旦团队其他成员也做同样选择,一个原本容易修改的应用会迅速积累 cruft,直到每个小改动都要花上好几周。
Here we see a clue of why internal quality does matter to users and customers. Better internal quality makes adding new features easier, therefore quicker and cheaper. Rebecca and I may have the same application now, but in the next few months Rebecca's high internal quality allows her to add new features every week, while I'm stuck trying chop through the cruft to get just a single new feature out. I can't compete with Rebecca's speed, and soon her software is far more featureful than mine. Then all my customers delete my app, and get Rebecca's instead, even as she's able to increase her price.
这里就看出内部质量为何对用户和客户重要。更好的内部质量让添加新功能更容易,因此更快、更便宜。现在我和 Rebecca 的应用也许差不多,但接下来几个月,Rebecca 凭借高内部质量可以每周添加新功能,而我却困在 cruft 里挣扎,连推出一个功能都费劲。我拼不过她的速度,很快她的软件功能就远超我的。然后我的客户全都删掉我的应用,转用 Rebecca 的,哪怕她还能顺势涨价。

Visualizing the impact of internal quality

直观呈现内部质量的影响
The fundamental role of internal quality is that it lowers the cost of future change. But there is some extra effort required to write good software, which does impose some cost in the short term.
A way of visualizing this is with the following pseudo-graph, where I plot the cumulative functionality of software versus the time (and thus cost) to produce it. For most software efforts, the curve looks something like this.
内部质量的本质作用,是降低未来变更的成本。但写出好软件需要额外的努力,这在短期内确实会带来一些成本。
一种可视化的方式是下面这个伪曲线图:横轴是生产软件的时间(也就是成本),纵轴是累计功能量。对大多数软件工作来说,曲线大致是这样的。


This is what happens with poor internal quality. Progress is rapid initially, but as time goes on it gets harder to add new features. Even small changes require programmers to understand large areas of code, code that's difficult to understand. When they make changes, unexpected breakages occur, leading to long test times and defects that need to be fixed.
Concentrating on high internal quality is about reducing that drop off in productivity. Indeed some products see an opposite effect, where developers can accelerate as new features can be easily built by making use of prior work. This happy situation is a rarer case, as it requires a skilled and disciplined team to make it happen. But we do occasionally see it.
这就是内部质量差时会发生的情况。起初进展很快,但随着时间推移,添加新功能越来越难。即便很小的改动,也需要程序员理解大片难以看懂的代码。修改时会出现意料之外的破坏,导致测试时间变长,还需要修复缺陷。
专注高内部质量,就是为了减少这种生产力下滑。事实上,有些产品会出现相反效果:开发者能加速前进,因为可以借助先前的工作轻松构建新功能。这种理想状况比较少见,需要一支技术娴熟且纪律严明的团队才能实现。但我们偶尔确实能看到。


The subtlety here is that there is a period where the low internal quality is more productive than the high track. During this time there is some kind of trade-off between quality and cost. The question, of course, is: how long is that period before the lines cross?
At this point we run into why this is a pseudo-graph. There is no way of measuring the functionality delivered by a software team. This inability to measure output, and thus productivity, makes it impossible to put solid numbers on the consequences of low internal quality (which is also difficult to measure). An inability to measure output is pretty common among professional work - how do we measure the productivity of lawyers or doctors?
微妙之处在于,有一段时间,低内部质量的生产力高于高内部质量路径。在这段时间里,质量与成本之间确实存在某种权衡。当然,问题是:在两条线交叉之前,这段时间有多长?
说到这里,就遇到它为什么只是伪曲线图的问题。我们无法测量一个软件团队交付了多少功能。无法测量产出,也就无法测量生产力,因此不可能给低内部质量的后果标上可靠数字(内部质量本身也很难测量)。无法测量产出在专业工作中相当普遍——我们怎么衡量律师或医生的生产力呢?
The way I assess where lines cross is by canvassing the opinion of skilled developers that I know. And the answer surprises a lot of folks. Developers find poor quality code significantly slows them down within a few weeks. So there's not much runway where the trade-off between internal quality and cost applies. Even small software efforts benefit from attention to good software practices, certainly something I can attest from my experience.
我评估两条线在哪里交叉的方法,是询问我认识的技术娴熟的开发者。答案让很多人意外。开发者发现,低质量代码在几周内就会显著拖慢他们。因此,内部质量与成本之间的权衡适用的窗口并不长。即便是小规模软件工作,也能从关注良好软件实践中受益——这一点我可以用自己的经验作证。
Many non-developers tend to think of cruft as something that only occurs when development teams are careless and make errors, but even the finest teams will inevitably create some cruft as they work.
I like to illustrate this point with a tale of when I was chatting with one of our best technical team leads. He'd just finished a project that was widely considered to be a great success. The client was happy with the delivered system, both in terms of its capabilities and its construction time and cost. Our people were positive about the experience of working on the project. The tech lead was broadly happy but confessed that the architecture of the system wasn't that good. I reacted with “how could that be - you're one of our best architects?” His reply is one familiar to any experienced software architect: “we made good decisions, but only now do we understand how we should have built it”.
许多非开发者倾向于认为,cruft 只会在开发团队粗心犯错时出现;但即便是最优秀的团队,也会在工作中不可避免地产生一些 cruft。
我喜欢用一个故事说明这一点。有一次我和我们最好的技术团队负责人之一聊天。他刚完成一个被广泛认为非常成功的项目。客户对交付的系统很满意,无论是功能、建设时间还是成本。我们的人也对参与这个项目的体验评价积极。这位技术负责人总体满意,但承认系统的架构并不算好。我反问:“怎么可能——你可是我们最好的架构师之一?”他的回答,任何有经验的软件架构师都很熟悉:“我们当时做了好决定,但直到现在才明白当初应该怎么建。”
Many people, including more than a few in the software industry, liken building software to constructing cathedrals or skyscrapers - after all why do we use “architect” for senior programmers? But building software exists in a world of uncertainty unknown to the physical world. Software's customers have only a rough idea of what features they need in a product and learn more as the software is built - particularly once early versions are released to their users. The building blocks of software development - languages, libraries, and platforms - change significantly every few years. The equivalent in the physical world would be that customers usually add new floors and change the floor-plan once half the building is built and occupied, while the fundamental properties of concrete change every other year.
Given this level of change, software projects are always creating something novel. We hardly ever find ourselves working on a well-understood problem that's been solved before. Naturally we learn most about the problem as we're building the solution, so it's common for me to hear that teams only really best understand what the architecture of their software should be after they've spent a year or so building it. Even the best teams will have cruft in their software.
许多人,包括软件行业里不少从业者,都把构建软件比作建造大教堂或摩天大楼——毕竟我们为什么把资深程序员叫“架构师”呢?但构建软件身处一个物理世界未曾经历的不确定性世界。软件客户对产品需要哪些功能只有一个模糊想法,并在软件构建过程中逐渐了解得更清楚——尤其是早期版本发布给用户之后。软件开发的构建块——语言、库和平台——每隔几年就会发生重大变化。放在物理世界,这相当于:大楼建好一半并已入住时,客户还会加楼层、改户型图,而混凝土的基本特性每隔一年就变一次。
鉴于这种变化程度,软件项目总是在创造新东西。我们几乎不会遇到一个早已被解决、又被充分理解的问题。我们自然是在构建解决方案的过程中才最了解问题,因此我常听到团队说,只有在构建一年左右之后,他们才真正明白软件的架构应该是什么样。即便是最好的团队,软件里也会有 cruft。
The difference is that the best teams both create much less cruft but also remove enough of the cruft they do create that they can continue to add features quickly. They spend time creating automated tests so that they can surface problems quickly and spend less time removing bugs. They refactor frequently so that they can remove cruft before it builds up enough to get in the way. Continuous integration minimizes cruft building up due to team members working at cross-purposes. A common metaphor is that it's like cleaning up work surfaces and equipment in the kitchen. You can't not make things dirty when you cook, but if you don't clean things quickly, muck dries up, is harder to remove, and all the dirty stuff gets in the way of cooking the next dish.
区别在于,最好的团队既产生的 cruft 少得多,也能及时清除足够多的 cruft,从而继续快速添加功能。他们会花时间创建自动化测试,以便快速暴露问题,减少修 bug 的时间。他们频繁重构,在 cruft 积累到阻碍工作之前就清除掉。持续集成能减少团队成员各自为政导致的 cruft 堆积。一个常见的比喻是:这就像清理厨房的工作台和器具。做饭不可能不弄脏东西,但如果不及时清理,污垢会干结、更难去除,脏东西也会妨碍你做下一道菜。
Summing all of this up:
Neglecting internal quality leads to rapid build up of cruft
This cruft slows down feature development
Even a great team produces cruft, but by keeping internal quality high, is able to keep it under control
High internal quality keeps cruft to a minimum, allowing a team to add features with less effort, time, and cost.
总结一下:
忽视内部质量会导致 cruft 迅速积累
cruft 会拖慢功能开发
即使优秀团队也会产生 cruft,但通过保持高内部质量,能够将其控制住
高内部质量把 cruft 降到最低,让团队能用更少精力、时间和成本添加功能。
Sadly, software developers usually don't do a good job of explaining this situation. Countless times I've talked to development teams who say “they (management) won't let us write good quality code because it takes too long”. Developers often justify attention to quality by justifying through the need for proper professionalism. But this moralistic argument implies that this quality comes at a cost - dooming their argument. The annoying thing is that the resulting crufty code both makes developers' lives harder, and costs the customer money. When thinking about internal quality, I stress that we should only approach it as an economic argument. High internal quality reduces the cost of future features, meaning that putting the time into writing good code actually reduces cost.
This is why the question that heads this article misses the point. The “cost” of high internal quality software is negative. The usual trade-off between cost and quality, one that we are used to for most decisions in our life, does not make sense with the internal quality of software. (It does for external quality, such as a carefully crafted user-experience.) Because the relationship between cost and internal quality is an unusual and counter-intuitive relationship, it's usually hard to absorb. But understanding it is critical to developing software at maximum efficiency.
遗憾的是,软件开发者通常不擅长解释这种情况。无数次,我听到开发团队说:“他们(管理层)不让我们写高质量代码,因为太花时间。”开发者常常用职业精神来为关注质量辩护。但这种道德化论证暗示质量是有代价的——反而毁掉了自己的论点。恼人的是,由此产生的 cruft 代码既让开发者的日子更难过,也浪费客户的钱。谈到内部质量时,我强调只应把它当作经济论证。高内部质量降低未来功能的成本,也就是说,把时间投入写好代码,实际上是在降低成本。
这就是为什么本文标题里的问题没有抓住重点。高内部质量软件的“成本”是负的。我们生活中大多数决策都习惯的质量与成本权衡,在软件内部质量上并不成立。(对外部质量则成立,比如精心打磨的用户体验。)因为成本与内部质量之间的关系很不寻常、反直觉,所以通常很难让人消化。但理解它,对以最高效率开发软件至关重要。