正在接洽新的产品合作

性能预算要在设计之前定下,而不是等到被投诉之后

把性能当作一个优化阶段,它永远会输给功能。把它当作设计开始前就已达成的约束,它会悄悄地、免费地带来更好的决策。

几乎每个团队都说性能重要。也几乎每个团队都把它排在接近尾声的某个阶段,在那里它和"按时发布"竞争,然后输掉。

最终做出快产品的团队做的是另一件事,而且不是什么英雄式的优化。他们在任何人打开设计工具之前,就先定义清楚"够快"是什么意思,然后像对待浏览器支持矩阵那样对待这个数字:把它当作约束,不是目标。

"以后再优化"为什么失败

三个原因,而第三个才是真正击垮你的那个。

  1. 没有东西强迫做取舍。 单独看,每个功能都值它的成本。没有预算就没有东西会说"不",加总起来就是慢。
  2. 便宜的修法会用完。 压缩、缓存和图片格式只能买给你一份固定而有限的额度。之后你就进入架构地带了。
  3. 昂贵的成因都属于架构。 过度取数的数据层、阻塞在网络上的渲染路径、位于关键路径上的第三方脚本。这些都在早期决定,到后期拆除代价极高。

等你能测量的时候,决定结果的那些决策已经是几个月前的事了。

预算长什么样

预算是少数几条具体的限制,一次构建可以通过或不通过。以我们给 KoiSpeak 查询功能定的为例:

指标预算理由
首个结果出现时间低于 150 毫秒人们在说话说到一半时打开词典。超过这个数就有等待感。
Largest Contentful Paint中端手机上低于 2.5 秒"良好"的常见阈值。
Interaction to Next Paint低于 200 毫秒打字绝不能有卡顿感。
发送的 JavaScript 体积硬上限,按路由分别设定唯一能预测其他所有数字的那个数字。

有两个性质让预算成为真的,而不是装饰:

  • 它是数字,不是形容词。 "快"没法让构建失败。150 毫秒可以。
  • 它在你用户真正拥有的设备上测量。 在开发者笔记本上通过办公室 wifi 测出来的预算,什么也没测。

在真实存在的硬件上测试

这是大多数性能工作在开始之前就跑偏的地方。

开发发生在快机器、快网络上。你的用户在几年前的中端安卓手机上,用着移动数据,后台还挂着十几个应用。这两种体验之间的差距不是 20%。在 CPU 密集的工作上,它经常是 5 到 10 倍。

实际的最低要求:

  • 桌上放一台真正的中端设备,每周都用。
  • 默认打开开发者工具里的 CPU 降频,而不是当成一次特别演练。
  • 采集真实会话的现场数据,而不只是实验室数字。实验室告诉你什么变了;现场告诉你用户实际拿到了什么。

预算如何改变决策

价值不在于测量,而在于它在任何东西被造出来之前,就把那些争论早早地、便宜地解决掉。

  • 一套字体变成一笔成本。 四个字重加两个字族,在移动端预算里是实打实的一块。你会在设计阶段就改成两个字重,毫无戏剧性。
  • 一个动画库必须自证其值, 与你本来会手写的那点 CSS 相比。
  • 统计脚本和客服挂件会被计入。 第三方脚本常常是最大的一项,而没人注意到,因为团队里没人写过它们。
  • 数据结构跟着预算走。 如果首个结果必须在 150 毫秒内落地,你就不能拉一大包数据再在客户端过滤。这会把你推向前缀索引,而那本来就是正确答案。

最后这条就是那个模式。预算不会拖慢你;它让正确的架构更早地显而易见。

发布之后再做的性能工作是一次救援行动。从一开始就被当作约束的性能,只不过是设计。

不搞仪式地执行它

没人检查的预算只是一个愿望。让执行保持廉价:

  • 体积超标就让构建失败。 一行 CI 配置,抓住大部分回退,维护成本为零。
  • 每个 pull request 跑一次合成审计, 只针对最重要的两三个路由。
  • 按周对现场指标告警, 而不是按每次提交。真实用户数据噪声大;看趋势。
  • 产品变化时重新审视预算。 预算不神圣。带着理由、有意识地把它调高是可以的。悄无声息地漂移过去则不行。

把预算花在哪里

预算讲的是分配,不是极简。有些东西配得上它们的重量:

  • 关键路径。 用户为之而来的东西必须排在最前,其余的可以等。词典是搜索框和结果。商店是产品图和价格。
  • 感知速度优先于测量速度。 立刻显示结果骨架,常常胜过把响应再快 40 毫秒。
  • 输入的即时反馈。 对一次按键在约 100 毫秒内作出反应会被感知为即时。超过大约 300 毫秒,人们就会注意到。

让人不舒服的那部分

真正的预算意味着要对那些单独看都很好的东西说不。这正是它奏效的原因,也是团队悄悄放弃预算的原因:当它第一次挡住某人想要的功能时,最省事的做法就是把预算调高。

纪律不在定下数字的那一刻,而在你守住这个数字的那场会议里。

性能 工程 产品