跳到主要内容

2 篇博文 含有标签「software engineering」

查看所有标签

谷歌的软件工程:项目管理

· 阅读需 5 分钟

20% 时间

工程师被允许花费他们 20% 的工作时间给任何一个他们自己想要贡献的项目,不用寻求他们经理或者其他人的同意。这非常有价值,因为:

  1. 只要有好的想法,甭管一开始听起来有多烂,都有充足的时间去实现它到一个可以 demo 的状态。
  2. 给管理者看到他们原本看不到的活动,否则工程师会搞“skunkwork” 偷偷干活。
  3. 允许工程师干一些有趣的东西,防止他们 burn out,激励他们让他们更开心。有干劲的工程师和 burnt-out 的工程师在产出的差距是远超 20% 的。
  4. 鼓励创新,如果周围其他人都做 20% 项目,你也会受到感染去做。

OKRs

个人和团队都必须公开记录他们的目标和对目标的衡量。

  • Objectives 目标
    • 设置季度和年度的目标
    • 个人和小组的目标要和大组的目标看齐 (align )
  • Key Results 关键结果: 通过可测量的关键结果,可以算出达到目标的进度,范围是从 0 到 1。
  • OKR 尽量往高里设,最后一般达到 0.65 是一个不错的标准,如果你的执行结果常常低于它说明目标设置得太高,高于它说明目标设置得太低。
  • 好处
    • 大家互相知道在干什么,能够互相激励
    • 让执行有了目标,让目标更容易被执行
  • ORKs 与绩效考核不是直接相关的

项目继续做还是终止?

尽管对于重大的新发布的审查是流程化的,但是某个项目该不该做这种问题却没有定论,有些是自下而上的决定,有些是自上而下的决定。

重组 (re-org)

组的分拆以及合并是家常便饭,似乎这样做能够优化效率。

我的评价

20% 时间的结果是好的,比如孵化了 Gmail,AdSense 等等举足轻重的项目。在一个跑马圈地的年代,鼓励优秀的工程师花一些时间做一些新东西是非常合算的。在公司还小需要用非常好的福利措施来吸引人才的时候,宣扬 20% 时间也是一种特立独行的手段。我更倾向于 20% 时间是一种管理风格,而不是成功的必然原因。

ORKs 与绩效考核不直接相关,这种区分非常重要 —— 换句话说,就是愿景与执行分离,目标管理与绩效管理分离。举个例子,你开车从 A 点到 B 点,“你有没有到终点?”,对比,“你开的这辆车是不是好车?”,其实是两个不同的问题。同样的道理,产品最后卖的不好,与工程师有没有做出好的产品,是两件不同的事情。

对于普通的工程师来讲,在大公司里面跟其他组,包括跟你具体工作没关系的其他组,保持好的关系很重要,因为这相当于你在劳动力供求的市场上多出了一些需求方。这样一旦发生重组或者其他不利的事件的时候,你能够有更多的选择。

谷歌的软件工程:软件开发

· 阅读需 9 分钟

业界公认,谷歌是一家工程能力超强的公司。它有哪些好的工程实践?我们可以在里面得到哪些启发?其中又有哪些地方是被人诟病的?这些内容比较细致我们慢慢讲,本篇主要是讲开发。

Google Software Engineering - Software Development

代码库

  • 截止15年有 20 亿行代码存在少量的 Monorepo 单一代码库中,绝大部分代码对所有人是可见的。谷歌鼓励工程师见到有问题就可以改,只要所有人审核通过,就能进库。
  • 几乎所有的开发都是在代码库的头部 (head) 进行的,而不是在分枝上,避免 merge 时候遇到问题,安全修复也更方便。
  • 每个改动都会触发测试,有错几分钟内就能通知作者和审查者。
  • 代码库的每个子树至少有两个所有人,其他开发者可以提交修改,但是所有人批准才能进库。

构建系统

  • 分布式构建系统 Bazel 让编译、链接、测试轻松快速。
  • 成百上千台机器。
  • 可靠性高,确定的依赖输入导致确定的结果输出,不会出现奇怪的不确定的抖动。
  • 快。一个构建结果缓存了,依赖它的构建会直接采用缓存,不必重新勾结。只会重新构建改动的部分。
  • 提交前自检 (pre-submit checks)。一些快速的测试可以在提交前先执行。

代码审查

  • 有代码审查工具
  • 所有改动必须有审查
  • 发现 bug 之后可以去之前的那个审查上指出问题,相关人员会被邮件通知到
  • 实验性质的代码不用强制审查,但是生产环境下的代码一定会被审查
  • 鼓励每个改动尽量小。百行以内是“小”,三百行以内是“中”,一千行以内是“大”,超过一千行是“超tm大”。

测试

  • 单元测试
  • 集成测试、回归测试
  • 提交前检查
  • 自动生成测试覆盖率
  • 部署之前做压力测试,并产生相应的关键指标,尤其是延迟、错误率随着负载的变化

Bug 追踪工具

Bugs, feature requests, customer issues, process 等等都记录起来,需要时常 triage 以确认优先级然后分配给相应的工程师。

编程语言

  • 限制有五个官方语言 C++, Java, Python, Go, JavaScript,以便代码重用和开发协作。每种语言有风格指南。
  • 工程师要经历代码可读性培训。
  • 当然某些场合的 DSL (Domain-specific language) 也不可避免。
  • 这些语言之间的数据交互主要是通过 protocol buffers。
  • 通用流程很重要,不同的语言,同样的工作流程

Debug 和分析工具

  • 服务器崩溃时,崩溃信息会自动记录下来。
  • 内存泄漏会附带上当前的 heap 对象。
  • 有大量的 web 工具帮你检测 RPC 的请求、改变设置、资源消耗等等。

发布

  • 大多数的发布工作是普通的工程师自己做的
  • 及时发布很重要,因为快速的发布节奏能够极大地激励工程师多干活、更快地得到反馈
  • 典型的发布过程:
    1. 找最新的稳定 build ,做一个 release branch,可能再附带 cherry-pick 一些小改动
    2. 跑测试与构建、打包
    3. 发布到 staging 服务器上内部测试,这时候可以 shadow 一下线上的流量,看看有没有问题
    4. 发布到 canary 上承接少量的流量公开测试
    5. 逐渐发布给所有的用户

对发布的审查

用户可见的、或者是重大的发布必须有相关的法务、隐私、安全、可靠性、业务需求相关的审查批准,确保相关人员被通知到。有专门的工具来辅助这个流程。

尸检报告

有重大的 outage 事故发生之后,相关责任人必须写尸检报告,内容包括

  1. 事件标题
  2. 总结
  3. 影响:发生了多久、影响了多少流量、损害了多少利润
  4. 时间轴:记录时间的发生、诊断和消弭
  5. root causes
  6. 做的好的地方,做的不好的地方:有什么经验能够帮助他人在下一次更快更准地找到并解决问题?
  7. 接下来的可行动作:有什么接下来可以做的事情能够避免将来类似的事情发生?

对事不对人,这里的关键是理解问题本身、以及将来如何避免类似问题。

重写代码

大量的软件每隔几年就会被重写一次。坏处是确实成本高,好处是

  1. 保持敏捷。市场在变,软件的需求也一直在变,代码也需要随之变化以应对不时之需。
  2. 降低复杂度。
  3. 传承知识给新人,让他们有拥有感。
  4. 提高工程师的移动性,促进跨领域创新。
  5. 采用最新的技术栈和方法论。

我的评论

谷歌的单一代码库和强大的构建系统是小公司不可学的,毕竟小公司没有资源和能力让构建系统快到敏捷可用;保持小、简单、快速会让小公司跑得更顺畅,更加专注于核心业务逻辑。

构建系统通常是定制化的,你的知识无法迁移和衍生。强大的构建系统对新手甚至是有害的,因为提高了新手俯瞰全局的成本。

知识无法迁移和衍生也是完善的内部工具 (in-house tools) 的问题。我在职业生涯中会尽量避免使用不会开源的内部工具,比如 Uber 的 Schemaless,只针对特定的场景且没打开放出来算做大做强 ;而相反的, Linkedin 的 Kafka 则是一个有开放性和衍生性的知识的好产品。

在公开市场,这整个开发、测试、集成、发布的流程都有非常好的工具帮你来做,举个 JS 社区的例子:

流程工具
代码库Github, Gitlab, Bitbucket, gitolite
代码审查Github Pull Requests, Phabricator
提交前自检、测试与 Linthusky, ava, istanbul, eslint, prettier
Bug TrackingGithub Issues, Phabricator
测试与持续集成CircleCI, TravisCI, TeamCity
部署发布在线服务的 Heroku, Netifly, 发布移动 App 的 Fastlane, 发布库的 NPM

最后,我可能有一个洞见,不注重这些工程流程自动化的细节的公司,在工程上会损失巨大的竞争力。我甚至为了良好的工程实践自己配了一个 JS 全栈开发框架 OneFx。快节奏与慢节奏、高质量与低质量差别,通常是指数级别的,因为 —— 通常,快会让你更快更多,差会让你更差更少。