预优化是软件交付的杀手

2020 年 11 月 23 日

预优化是软件交付的杀手

本文最初发表于 Medium 博客,经原作者 Grace Ke 授权,InfoQ 中文站翻译并分享。


导读:本文作者用她的经验教训为我们娓娓道来:预优化是不是软件交付的杀手?相信她这篇文章能帮你避开软件交付的那些坑。


在我目前的岗位上,我有一个团队花了两年多的时间,试图让自己从前一个团队留下的坑“爬”出来,而这个坑是源于那个团队很差劲的设计,这些设计是一些处理平台服务的相当关键的应用,这些应用是许多其他微服务的上游功能。我认为问题的很大一部分在于,前一个团队是过度预优化的受害者。我知道,当事后诸葛亮是站着说话不腰疼,因为我们现在正承受着我们所继承的服务生态系统的后果和持续存在的问题。然而,我相信,我们不得不解决的一些问题本是可以避免的。

谬论


预优化是指,在遇到问题之前,我们必须一次性解决所有的问题。这几乎是在说,作为开发人员,我们应该有一个“水晶球”,可以预测我们在应用程序生命周期中将会遇到的所有问题。

例子与经验教训


我所见过的团队进行预优化的一些事情包括:


  1. 通过过早地扩展来预测自动扩展需求。我见过的最常见的问题之一是,团队会根据遗留应用程序在内部执行的情况,配置他们可以使用的最大的 CPU/mem EC2 组合。问题是,当我们监控这些新服务时,我们注意到,每个实例只使用了 CPU 的 2%。就操作成本而言,这些非常大的实例其实是非常昂贵的,但对服务的性能似乎并没有多少贡献。


经验教训:有时候,使用较小规模的实例进行扩展,要比过早地扩展实例要好。


  1. 与上述情况相反,我还发现团队只专注于一件事进行优化。我们继承的一个应用程序,它有一个扩展策略,根据遗留系统过去的数据去假设系统负载将是什么样的情况。然而不幸的是,尽管他们考虑了服务方面的可扩展性,但团队并没有考虑数据库方面的性能问题。在人们登陆到系统的几个小时内,数据库就变得不堪重负,我们花了几乎一整天的时间试图找出动态数据库扩展策略(在生产环境中)来解决这一问题。


经验教训:不要根据你正在建模的遗留系统的工作方式进行优化,要始终进行测试和验证。


  1. 我发现的另一个预优化活动是从一开始就计算出所有的数据模型,然后过早地对数据进行归一化。当我还是一名开发人员时,我记得在数据模型被布置时,我与所在的团队进行一番讨论。为适应未来的增长和存储他们认为用户会感兴趣的数据,团队花了近两次 sprint(在我看来,比我想象的长了 1.5 个 sprint)来讨论数据模型应该是什么样子的。作为一个从电商商店出身,有着多年后端服务开发经验的人,我知道我们正在艰难地挣扎、前行,因为我的经验告诉我们,我们永远不可能提前预测所有的数据需求,但我们可以使数据合同具有可扩展性,这样一来,我们就不会不断地进行违约更改。但是,我又知道些什么呢?不幸的是,我属于少数派,而团队中声音最大的那个人赢了。快进到我们交付的时候,数据模型是如此复杂且难以更改,以至于每当我们进行调整时,我们都不得不要求下游的所有人也要修改和重新部署他们的代码,以适应我们的更改。数据查询的性能也非常低效,因为表过于归一化而不是更扁平。


经验教训:有时候,简单的设计可能会更高效。归一化总是可以在以后进行,但它并不总是解决问题的办法。



  1. 我还见过有些团队在应用程序中加入了花哨的功能,不仅是因为它很酷,而且“非常前卫”,还因为他们认为我们的客户不知道他们想要什么,我们会给他们想要的东西。团队最终会构建“某一天的功能”。当公司的一个团队开始着手开发一个新的报告产品时,这就是团队前进的目标。然而,当他们交付的时候,顾客却很讨厌这个产品。他们觉得交付给他们的功能并不是他们要求的。遗憾的是,基本功能甚至都无法正常工作。今天,我们公司正在与第三个不同的团队一起开发该产品的第三次迭代。


经验教训:在开发某个东西之前,一定要问清楚客户想要什么,千万不要仅仅因为它“很酷”。


  1. 我还看到过团队因为陷入“分析瘫痪”而永远无法推进开发。很多时候,由于团队试图设计这样的一个没有故障且不会发生任何问题的系统,但过去了这么长时间,还没有交付一个能够工作的产品。我在一个项目中就遇到了这种情况。团队花了很长时间来设计/预测潜在的问题,以至于我们延迟了产品的交付,而一旦交付到客户手中,我们似乎仍然没有抓住所有的问题。


经验教训:只需根据目前了解的需求进行构建即可。软件应该是灵活的,如果需要的话,应该能够容忍修改。


  1. 有时,我会查看我们继承的一些服务,对于某些服务,可能至少有 20 多个端点超出了必要的范围。例如,真的有必要有一个端点来公开所有可能的数据组合吗?也许这里的答案是,使用查询参数和一个端点即可。另一种观点是,也许没有人会使用或需要这些数据。我已经数不清我的团队对某些端点被调用的频率进行了多少次研究,而答案恰恰是零次。


经验教训:请记住,构建服务端点有多种方法,但请遵循最佳实践,并且仅在需要数据时才公开数据。


  1. 最后,我还见过一些团队在没有意义的情况下为工具构建 UI。但我要问的问题是,这个 UI 是为谁设计的?他们能否使用 Web 服务来获得这些功能?


经验教训:虽然构建 UI 来实现我们的全栈开发能力很酷,但有时团队应该将精力集中在构建其他工具中(也许是监控,而这通常是事后才想到的)。要遵守 YAGNI(You Aren't Gonna Need It,你不需要它)原则。

荣誉奖


在预优化的另一面,还有一些工程团队应该做但没有做的活动,这些活动也会导致软件交付失败。


  1. 如果有意义的话,要进行“构建还是购买”的评估。我在团队中发现的一个问题是,他们太过专注于构建炫酷的解决方案,却从来不对他们交付的产品进行构建还是购买的分析。我的另一个团队,负责 DevOps 的实施,他们一直在研究不同的功能标记解决方案,以便在出现问题时,我们就可以引入新功能并鼓励更频繁的部署,而不会让我们的客户接触新代码。有一些开源和商业的解决方案可以很好的做到这一点。其他交付团队之一决定他们可以在内部构建一个。然而,一旦其他团队开始使用它,很明显,设计/构建它的团队并没有考虑到所有的用例,而且这个应用程序绝对不是为了支持数千个功能标志而构建的。实际上,可伸缩性就是一个如此严重的问题,以至于有一天,它导致了一次重大的停机事故,调用它的应用程序甚至都无法恢复到正常行为,因为它们没有内置任何中断和失败机制。


经验教训:不要为别人已经解决并且已经做得很好的问题构建东西。


  1. 不要相信高测试覆盖率就等同于高质量的产品。我们在工程部门有一个 QA 子组织,它有一个独立的报告结构。他们制定的一些规则有时是武断的。例如,他们要求团队在部署之前,所有的应用程序和服务的测试覆盖率必须达到 95%。然而,据我所见,人们为了达到该目标而对测试设置进行了调整,在我的职业生涯中,增加测试覆盖率(因为它可以被人为操纵)从来就不是必然意味着就没有 Bug。我真不知道这种幻觉从何而来。


经验教训:不要为了勾选一个复选框就过于专注那些毫无意义的指标。确定该指标试图实现的目标,然后将注意力集中在这个目标上。


  1. 一定要考虑监控。由于我的团队有 SRE,所以监控是我们促进并推动团队与我们的 DevOps 实践社区一起做的事情。对于团队来说,为他们的应用程序和服务创建监控和警报的重要性再怎么强调也不为过,这样我们的客户就不会在我们之前发现问题了。如果我们没有率先发现这些问题的话,那只会让我们看起来很糟糕。尽管围绕这一问题进行了多次沟通,但仍然令我感到震惊的是,我们几天生产的数百个应用程序/服务中,甚至没有进行综合测试或运行状况检查。


经验教训:创建监视器当然是一项时间投资,但与团队率先发现问题相比,客户发现问题的成本是很高的。“预防胜于治疗”这句格言在这里很适用。


  1. 保持安全第一的心态。我还经常看到团队将安全性视为“技术债务”,并推迟解决安全性违规问题。在云端环境中,这样的做法更危险,因为安全漏洞很容易变得非常公开(例如,在公共 S3 存储桶中意外泄漏 PII 数据),并且代价非常高昂。由于我的一个团队负责我们所有的 AWS 基础设施,因此我们亲眼目睹了安全漏洞的代价有多大。通常,我们最终会删除该账户,并不得不重新构建所有内容,特别是当它有关键的生产工作负载时。


经验教训:不要以为安全漏洞最终不会影响到你的团队。这往往是时间的问题,而不是会不会发生的问题。


  1. 不要创建循环依赖项。我甚至都不知道这是怎么发生的。我见过团队设计的服务必须按照非常特定的顺序进行部署才能正常工作。我还见过一些是先有鸡还是先有蛋的情况,其中服务 A(比如说身份验证服务)需要服务 B(比如说服务查找),但它需要来自服务 A 的某些内容(比如身份验证密钥)才能工作。这就违反了很多设计原则,并散发着“代码的味道”。我甚至都不知道它是怎么通过架构审批的。然而,这种模式我最近看得太多了,我开始怀疑自己是否错过了什么东西,因为软件设计已经发展了那么多年。


经验教训:不要忘记使用经过实践验证的设计模式。它们的存在是有原因的。如果有什么不对劲的地方,就对设计提出质疑,如果有必要的话,再从头开始。

爬,走,跑


在某些情况下,一些预优化还是有意义的(例如,对于代码可维护性或可移植性)。避免预优化可不是懒惰的借口。但是,如果我们为永远不会发生的事情进行预优化,那么它就是浪费了。预优化有时也会导致设计决策,让我们陷入无法摆脱的困境。我相信,有时候,预优化是反敏捷的。软件交付是一个迭代的过程,特别是对于复杂的项目。我看到过一些交付团队,特别是那些非常乐观和初级开发人员的团队,在项目的早期往往就做出超出他们能力范围的事情。我并不否认他们的热情,但有时候这才是经验的真正价值所在,有时候,磨练还是必要的。


在软件开发中,有这样一种“爬,走,跑”(crawl, walk, run)的原则。这意味着首先要构建所谓的最小可行产品(MVP,即“爬”阶段)。MVP 并非开发人员为了取悦产品所有者而胡乱拼凑的东西。这是满足客户基本需求的实际有用产品的最小版本。将 MVP 从足够好到更好的关键在于迭代:获得反馈,重构/修复,然后再次获得反馈,同时要牢记质量。创建 MVP 可以帮助团队能够快速失败,因为它可以快速识别问题,并在需要时进行调整。在“走”阶段,团队可以通过引入性能(例如,确定自动扩展策略)、成本优化(调整资源大小)和整体应用程序调优来“加速”,这有时可能包括重新架构(例如,从 EC2 托管的应用程序到使用无服务器解决方案的容器化应用程序)。最后,在“跑”阶段,团队现在可以加速到他们感到舒适的速度。也许这意味着一天要多次部署服务。这通常也是团队开始考虑灾备等更大问题,并进行“游戏日”,这是一项实践,以确定如果最糟糕的情况发生(又称“混沌工程”),可能会出现哪些潜在的问题。团队可以借鉴 Netflix 和 Amazon 的做法,看看他们是否可以利用这些公司从这些实践中学到的一些经验教训。

总结


预优化并不像有些人说的那样是“万恶之源”。有些东西你可以/应该进行预优化,但也有些东西你不能/不应该进行预优化。对错误的东西进行预优化可能会让团队蒙受很大的损失,而他们本可以交付高质量且可用的软件,让客户对此赞不绝口的。


作者介绍:


Grace Ke,在 IT 行业工作多年,沉迷于卡通爆炸(Toon Blast)游戏和网购。希望有朝一日能出版一本儿童读物。


原文链接:


https://medium.com/swlh/pre-optimization-is-a-killer-of-software-delivery-f04231f24c4e


2020 年 11 月 23 日 16:31796

评论

发布
暂无评论
发现更多内容

项目吐槽之需求分析二

Geek_XOXO

项目管理 pmp 项目实战

架构师训练营第一周作业

爱码士

架构设计

普通人如何站在时代风口学好AI?这是我看过最好的答案

华为云开发者社区

AI 算法

程序员喜欢的 5 款最佳最牛代码比较工具

程序员生活志

编程 工具

想自己写框架?不会写Java注解可不行

Java架构师迁哥

Docker架构

混沌畅想

Docker 容器 Docker架构

一文搞懂ReactNative生命周期的进化

凌宇之蓝

react.js 面试 大前端 React Native

Scikit-Learn中的特征排名与递归特征消除

计算机与AI

学习 数据科学 特征选择 降维 scikit-learn

一文带你读懂 Swift 社区最新开源的算法库

镜画者

ios swift 算法 apple

1024!奈学教育致敬程序员3+2战略发布会重磅来袭

奈学教育

1024 奈学教育

「架构师训练营」第 1 周作业 - 食堂就餐卡系统设计

小黄鱼

极客大学架构师训练营

在线EXCEL编辑器-Luckysheet

奇异石榴果

Java 开源 Excel bigtable js

发布3个月获得5K Star的Luckysheet - 基于MIT协议的开源电子表格

奇异石榴果

Java 前端 Excel 开源项目 html/css

spring-boot-route(二十三)开发微信公众号

Java旅途

Java Spring Boot

分布式文件存储数据库 MongoDB

哈喽沃德先生

数据库 nosql mongodb mongo 非关系型数据库

c++bind函数使用

良知犹存

c++

App场景下投放外链时如何做设备识别和采集(1)

DeeperMan

采集 流量

勾魂!在Github白嫖左程云1470页数据结构与算法+视频

996小迁

Java 架构 面试 面试数据结构与算法

项目吐槽之需求分析一

Geek_XOXO

项目管理 pmp

架构师训练营第2期 第1周 作业二:学习总结.md

老坛酸菜

MyBatis-技术专题-动态SQL

李浩宇/Alex

架构师训练营 1 期 - 第五周 - 技术选型

三板斧

极客大学架构师训练营

【得物技术】谈谈缓存的一二三四五

得物技术

缓存 架构 技术 缓存穿透 缓存击穿

6小时搞定云原生:从基础概念到上手实践

京东智联云开发者

云原生

iOS性能优化 — 二、卡顿监控及处理

iOSer

性能优化 编程语言 监控 ios开发 卡顿

MyBatis-技术专题-拦截器介绍

李浩宇/Alex

架构师训练营第2期 第1周 作业一:食堂就餐卡系统设计

老坛酸菜

一份超级完整实用的PyCharm图解教程,8K字赶紧收藏起来

计算机与AI

Python IDLE 开发环境

数字货币交易所开发功能方案,交易所平台搭建app

WX13823153201

数字货币交易所开发

1024!奈学教育致敬程序员3+2战略发布会重磅来袭

古月木易

程序员 奈学教育

【API进阶之路】研发需求突增3倍,测试团队集体闹离职

华为云开发者社区

软件开发 开发 开发测试

预优化是软件交付的杀手-InfoQ