寻找低垂的果实

11 阅读1分钟

寻找低垂的果实

原始链接

假设你的工作是在一个巨大的果园里摘水果。果园连绵好几个山头,走上一圈都要花几个星期。你首先应该做什么?

什么是“低垂的果实”?

作为一名优秀的工程师,你可能会把它简化为一个优化问题:为了摘到最多的水果,你需要让每棵树的产出最大化。你走到一棵树前,发现踮起脚尖能摘到 30% 的水果,但大部分水果太高够不着。于是你造了一把高梯子,把采摘率提高到了 90%。但有些水果不仅高,还长在边缘脆弱的树枝上,梯子靠不住。接着你又设计了一个能向侧边伸展的机械臂,采摘率升到了 95%。机械臂并不完美,有些角度很刁钻,有些果实长得太紧摘不下来。你不断微调机械臂,一点点提升产量,最终大功告成,达到了 98%。

与此同时,旁边有个聪明的家伙溜达到了附近的山谷里,那里的树上结着十倍多的水果。他只是闭着眼睛随便摘了摘自己触手可及的果实,最后收获的水果就是你的三倍。

这个道理很明显:你应该先去摘“低垂的果实”(即容易达成的高目标)。抛开比喻,这意味着你应该把时间花在用最少的精力交付最大价值的工作上。在科技公司,工作产生的价值差异极大。有时候,一位工程师苦干几周产出的价值,另一位工程师半小时就能搞定。这绝非夸张,我见过太多次了。

我这里主要拿性能优化举例(因为这是我的专业领域),但这套原则同样适用于其他工作。如果你想改善糟糕的设计、优化用户体验,或者修复简单的 Bug,只要你擅长发现“低垂的果实”,你就能获得十倍于努力的回报。

科技公司里“低垂的果实”

那具体该怎么做呢?最简单的方法是听从公司的安排。公司在任何阶段都只有少数几个核心优先级。围绕这些优先级工作往往能事半功倍,因为你有整个公司的资源在背后推着你走。举个具体的例子:在一个成熟的老功能上做些打磨,顶多带来一点渐进价值;但如果在备受瞩目的全新功能上做同样的打磨,可能就会直接决定这个功能的成败。

从技术角度来看,同样有大把“低垂的果实”。我的职业生涯中花了大量时间去提升 API 接口和页面的加载速度,这也是我最喜欢做的事情之一。这里的原则也是一样的:很多工程师会花大量时间去微调静态资源大小,或者把逻辑提取到缓存里,仅仅为了让页面加载快上 5 毫秒;但同时,另一个页面(甚至同一个页面)可能正因为一个没有索引的数据库查询,或者完全可以避免的 N+1 查询问题,白白浪费了 200 毫秒。

当你尝试优化时,视野要尽可能开阔。 别看到第一个慢的地方就停下脚步。先把整个流程扫视一遍,边看边做笔记,因为你可能会在后面发现更糟糕的问题。大多数代码路径都有好几个可以优化的地方。摘取低垂的果实,意味着要从最烂的那个部分开始动手。

性能分析与监控指标

如果条件允许,在优化前先进行性能分析 (Profile)。我喜欢尽可能使用 火焰图 (flamegraphs):它能将调用栈可视化,初始调用者在最底部,栈身垂直向上延伸。

cpu

这种方法的巨大优势在于,你能直观地看到哪些操作最耗时。有了火焰图,摘取“低垂的果实”几乎变成了一个机械化的过程:找出你自己写的代码(而不是第三方库或框架)中最长的那段耗时,看看能把它优化得多快,然后再找下一段,不断重复。

但要注意,火焰图有时会有误导性,因为它只提供单次请求的数据。有时候,最容易摘的果实只存在于部分特定的请求中(比如数据量很大的客户,或者特定内部 API 缓存未命中时)。为了抓住这些情况,你需要关注监控指标。特别是 p95 和 p99 的请求时长指标——也就是追踪你那最慢的 5% 或 1% 的请求到底有多慢。

如果只看平均值,你可能会忽略:虽然 API 平均响应只要 100 毫秒,但对部分用户来说却需要等上 5 秒。解决这个问题也许不会对平均时长产生明显影响(可能只是从 100 毫秒降到 99 毫秒),但对这部分用户来说却是质的飞跃。这比听起来更重要,因为这最慢的 1% 用户,往往就是你体量最大的 1% 用户,这意味着他们可能是付钱最多、最重要的核心客户。

去哪找“低垂的果实”?

假设你面对的不是单一的 API 接口,而是整个 API 集、整个服务,甚至整个公司。你该去哪找“低垂的果实”呢?我所知道的最好技巧是反向思考:哪些领域可能已经被狠狠地优化过了?

流量最高或最显眼的接口大概率属于这一类。如果我加入 Facebook,我不会指望能在 facebook.com 的页面加载上找到什么轻松的性能优化点。因为它太显眼、存在太久了——早就有无数工程师关注并解决过那些明显的问题了。同样,如果某个模块最近刚做过专项优化,那里大概率也不会有什么好摘的果实。最后,全新的代码通常也已经做过尚可的优化了。在一家技术过关的公司里,最糟糕的问题往往是随着时间推移,像结痂一样层层堆积起来的历史包袱。

另一个原则是,容易优化的代码通常早就被优化了。这就像晚上找钥匙,不要在路灯底下找——因为如果钥匙在亮处,别人早捡走了。你应该去那些其他寻找者不太会注意的黑暗、肮脏的角落找。那些易读、结构良好、且使用公司主流语言编写的代码,多半已经被优化过了。而那些错综复杂的遗留代码,或是用冷门语言写的代码,往往无人问津。如果修改这些代码还带有危险性(比如计费代码),就更是如此了。

所以,如果你想找“低垂的果实”,请寻找:

  1. 关注度处于第二或第三梯队的接口或页面——它们重要到值得优化,但又没重要到已经被彻底优化过。
  2. 老代码。越老越好。
  3. 难以阅读的代码,或者不是公司主要编程语言编写的代码。
  4. 让人不敢碰的高风险代码。

当然,在其他类型的代码里也能找到优化点。但在符合上述条件的代码中,你更容易找到那种“只需加一行代码,就能让 p95 延迟降低 500 毫秒”的轻松胜利。



如果你喜欢这篇文章,欢迎 订阅 邮件以获取我的新文章更新,或者 [在 Hacker News 上分享它](news.ycombinator.com/submitlink?… the low-hanging fruit)。以下是一篇带有相同标签的相关文章预览:

试图给不尊重的人留下好印象

在我职业生涯的头几年,我对于如何在科技公司做出好成绩几乎一无所知。和我共事的高级工程师及资深专家们就像魔术师:他们毫不费力地解决着我连看都看不懂的问题。当我还为了搞懂如何把数据存入数据库而焦头烂额时,他们却在处理牵动全公司命脉的系统扩容和架构设计工作。我当时唯一的愿望,就是做出能让那些工程师刮目相看的工作。
继续阅读...