@Lenciel

生产环境优先

最近在想技术团队的文化。

最后得出来二十四个字:

“生产优先,数据驱动。简明清晰,求真务实。使命必达,精益求精”。

先来说说“生产优先”。

因为在之前呆过的公司都是管理技术团队的同时还要管理产品和运营团队,我逐渐有了一个认知:生产环境中的效能才是最重要的效能。

我甚至觉得,对于创业公司,生产环境的效能是唯一重要的效能。

不是说选 Scrum 还是 Kanban 不重要,也不是说代码审核/静态扫描等等最佳实践不重要,更不是说 Stage/QA 等环境上的测试不重要。

但是,在资源有限的情况下,必须要明白,生产环境的代码是我们唯一的资产,其他的一切都是债务

这样的认知对我最大的帮助是确定工作的优先级。

特别是在刚刚进入一个团队的时候,可以干的事情实在是太多了,优先级的选择如果出了问题,后果非常严重。

在很长一段时间内,我们会把跟下面两个方面有关的事情定为优先级最高的任务:

  1. 如何能尽快把高质量的代码部署到生产环境。
  2. 如何能尽快对生产环境发生的问题进行响应并处置。

这样的认知和优先级的排列,会反应在我们技术团队的很多实践上,它们中的一部分是违反传统经验甚至违反直觉的,所以我想稍作讨论。

下面是我认为“面向生产”需要遵循的几个实践原则。

工程师为自己的系统负责

没有这种文化的团队谈不上什么实践 DevOps 。

工程师不仅仅是开发或运维,我们的测试、数据等等职能的小伙伴也是工程师。

“为自己的系统负责”的具体含义是:

  1. 对自己的线上系统都应该懂得如何进行部署、检测和监控。
  2. 能按照自己的不同职能对线上问题的发现和解决作出贡献,让生产环境故障的感知、响应和处置变得更快。

在具备这种主人翁意识的团队中,我们会逐渐把线上环境的部署、调试等权限开放给所有人。但权利和责任是对等的,这也意味着:

  1. 我们要求编写可观察性好且易于调试的代码,并且始终关心用户体验和用户价值。
  2. 我们要求对自己的系统在生产中的执行情况从有好奇心到有控制力。
  3. 最重要的是,我们要求工程师随时随地为他们的系统服务——包括值班甚至是轮岗。

目前我们已经有一些工程师不定期的在一线进行支持,我认为有计划的值班和轮岗是很好的。在技术团队内部我们还要推开发去运维部门轮岗:这是建立团队内同理心的机会,同时也让我们的工程师体验到自己还有哪些方面可以做得更好。

但最重要的是,我见过两种架构师,一种是只能在自己的机器上写代码最多画画组件交互图的,一种是知道一个业务系统跑什么硬件用什么操作系统能够扛多少请求需要多少带宽的,后面那种我认为才是合格的架构师。

关于这部分的内容有很多细节这里不展开,比如生产环境的告警应该怎么配置,通知到哪些人;如何减少告警疲劳以避免事故被漏掉;比如怎样通过 SLO 驱动来创建 Production Excellence 的文化等等。

多听用户的声音

使用系统的人是最了解系统的人

这适用于人类构建的几乎所有的系统:统治者设计的任何一个高明的制度,最终都会被使用它的人找到漏洞。

就软件系统而言,多听用户的声音包括很多层面,我举几个例子。

  1. 外部用户往往会找到我们测试用例覆盖不了的问题。因此我们需要建立多个层次的用户反馈和行为分析渠道,来解决外部用户的问题。
  2. 一线的工程师更了解时间计划如何制定或者 Bug 是什么风险级别。很多公司的管理者会忍不住参与这些活动,但我们通过一系列的办法来保障工作的优先级安排交给工程师自己来完成。
  3. 业务研发等团队是基础设施/公共服务等团队的用户,他们往往会发现公共平台/组件一些问题,并且知道要怎么绕过那些坑。要有渠道搜集这些反馈,来对我们的内部工具包括内部工具的 UX 进行改进。

我们构建任何系统的时候,都要把创造用户价值放在第一位。

无聊的技术往往是好的技术

和 Dan McKinley 一样,我也通常首选无聊的技术

系统本质上是不可预测的,特别是我们目前构建的各种分布式系统。如果你和我一样看过 Scala 编写的系统崩了之后乱七八糟的堆栈,你就会同意还是应该使用有更好工具链的 Java 。

不仅仅是系统崩溃的时候,你还需要考虑一些常规的操作,比如部署、更新和数据迁移。越无聊的技术在这些部分通常已经做得越成熟,比如 MySQL ,它可能不是一个数据库爱好者听到之后会兴奋的选择,但是在绝大部分时候,我们还是选择它。

这背后的逻辑是,很少有创业阶段的组织有解决特殊问题的带宽。数据持久化的过程,在集群中选择新的 leader,查询时间序列数据,垃圾回收等,这些部分出现的问题可以消耗掉一个小的团队。我们应该吝惜自己的精力,把它尽可能花在客户价值的创造上。

软件行业里有三种技术团队:

  1. 开路先锋:它们研发创造性的方案,解决还没有被很好解决的问题。
  2. 吃螃蟹的:它们使用开路先锋的各种还没有稳定的系统,并且做出自己的贡献
  3. 稳定后跟进的:等到螃蟹被反反复复吃得无聊的时候,根据自己的需要选择合适的方案和系统的。

前面两种类型的技术团队,除非公司的产品本身就是技术系统(类似 Kyligence 或者 PingCAP),否则就应该务实的当好第三类团队,让前面两类团队先去踩坑。

让部署变得简单

部署应该像呼吸一样,频繁而平淡

但在很多公司它就像心脏搭桥手术一样,紧张兮兮,流程繁琐,代价高昂。

部署变得简单意味着:

  1. 手动步骤很少
  2. 查看部署是否成功很容易
  3. 当部署不成功时回滚很方便

现在我们看技术团队的效率是要统计部署频率的。因为在 lean theory 里面有一个很重要的指标是 batch size。但是因为软件的 batch size 很难衡量,所以我们考察部署的频率。

要追求比较高的部署频率,部署的过程,无论是技术过程还是决策过程,都必须足够的轻量化:所有的变更只要经过代码审核等一系列手段,不需要挑时间,不需要管理人员的审批,就可以上线。

频繁部署意味着每个变更的范围更小,通常它们都更快也更安全。

部署不够“简单”的时候,我们会规定一些上线的时段,比如“周五不做部署”。要把这种情况当成需要解决的问题而不是“最佳实践”。部署是公司的心跳,我们不应该规定心脏什么时候可以跳。

唯一的例外是假期的封版,这也是因为在线教育这类业务,确实会有假期的流量峰值出现,这种时候让所有人加班并不现实,所以可以运行一个稳定的版本不做任何变更。如果业务类型是假期流量较少的,那么也就没有必要封版。

QA 不是为质量“把关”的人

让某个角色来验证服务是否达到线上质量要求是弊大于利的

我们现在暂时由 QA 来进行业务系统的生产环境部署,这是整个技术团队资源和能力未达到下一个阶段前的无奈之举。

首先,我们现在还需要在部署之前完成不少的手动工作,比如集成测试等等,这慢慢会造成瓶颈:如果部署变得容易并且经常部署小的更改,则没有任何 QA 团队能够满足对每个部署都进行测试。我们应该不断提高自动化率并将其构建到 CI 中(如果它们确实带来了价值)。

其次,QA 团队经常缺乏环境,并承受时间压力。他们可能最终只能测试“用例”而不是“用户路径”。例如,我们经常看到 QA 需要在 UI 层触发一个事件,然后检查数据库的变化。当开发重构了 UI 并同时修改了数据模型后,往往会出现变化没有同步给 QA 造成的测试失败:虽然整个功能是正常的,但是按照用例去触发 UI 事件然后检查数据库发现没有按照预期变化。再比如我们经常看到 CDN 带来的测试中无法遇到的问题。

那么 QA 应该负责什么呢?我认为以后的 QA 团队的发展方向将会是:

  1. 在生产环境中进行“持续测试”,而不是在虚拟的 QA 环境中运行手动和自动测试用例。
  2. 在生产环境中进行探索测试、混沌测试。
  3. 和基础设施团队合作,致力于 CI / CD 的建设。

另外,需要明确的是,线上事故的发生率也不能作为 QA 的绩效目标。

它是一个参考值,说明整个团队的表现。

但必须知道,完全避免系统故障是不可能的,甚至是不可取的。

正确的做法是接受分布式系统的复杂度,并专注于怎么应对故障

这意味着我们整个团队要一起努力不断改进的事件响应流程,做好 RCA ,以及事故分析。这是一个巨大的领域,具有许多有价值的工具和资源,可在发生(或不发生)故障时最大程度地提高投资回报率。

先搞好生产环境

这一节我很想写下的标题是“只搞好生产环境”

随着工作年限的增加,我们都会经历很多的测试环境, Stage 环境从无到有被搭建起来。在每个项目和团队开始成长的初期,它们确实有意义。但是只要业务和团队真的长起来了,这些环境和生产环境一定会漂移。

非生产环境不会有流量,不会有复杂的架构,甚至不会用跟生产环境一样的数据库。所以通常情况下,花费大量的时间维护这些环境并骗自己它们和生产环境”非常一致“,还不如先搞好生产环境:

  1. 有没有很好的灰度策略支持
  2. 有没有很完善的熔断和降级
  3. 有没有很智能的流控和切换
  4. 有没有很完备的监控和告警

总结

“生产优先”作为我们宣扬的技术团队做事风格,体现着我们对西瓜价值观的思考:用户第一,客户第二,创造用户/客户价值的事情,才是第一优先级的。同时,它也表达了对系统复杂度达到目前阶段一些技术架构上的取舍和对各个职能的工作内容的调整。它包括了眼下的安排,也包含了未来的计划。

用 textlint 自动排版

作为一名工程师,平时写的大部分文档都会有中英文的混排。

中文文档的排版本来就有很多的讲究

当中英文混排的时候,一个主要却挺有门槛的讲究是要用空格对中英文进行隔断。

正确:

我们接下来会使用 SLI/SLO 进行关键路径的指标梳理。

错误:

我们接下来会使用SLI/SLO进行关键路径的指标梳理。

很多人会嫌这样的要求太龟毛,但 pangu.js 的作者 vinta 有句话说得好:

「有研究顯示,打字的時候不喜歡在中文和英文之間加空格的人,感情路都走得很辛苦,有七成的比例會在 34 歲的時候跟自己不愛的人結婚,而其餘三成的人最後只能把遺產留給自己的貓。畢竟愛情跟書寫都需要適時地留白。」

打算好好做人了吧?

但确实挺有门槛,即使强迫症如本座,每次输入英文的时候要手工隔断也觉得很烦。

一方面,写一篇文档得多敲几百次空格;一方面,思路也感觉遭到了隔断。

考虑到这个普遍存在的问题,国产输入法大多数都提供自动敲空格的功能。

但国产输入法我都不太敢用,哪怕是 MAC 版。

最近吐槽这个问题的时候稍微研究了一下,发现有个日本友人写了一个 npm 包叫 textlint。

安装之后,你就可以安装并配置一系列的规则包

目前主要的规则包都是日语和英文的,非常复杂,简单用用可以先从下面的入手:

  • 检查感叹号使用
  • 检查空格使用
  • 检查句子太长(需要配置阈值)
  • 检查顿号、逗号太多(需要配置阈值)
  • 检查错别字(需要配字典)
  • 检查错拼术语(需要自己建术语库)

我先安装了空格检查的规则:

1
npm install textlint-rule-ja-space-between-half-and-full-width --global

然后在文档的根目录做一下初始化:

1
textlint --init

这会生成一个textlint.rc的文件,修改它的内容为:

1
2
3
4
5
6
7
8
{
  "filters": {},
      "rules": {
          "ja-space-between-half-and-full-width": {
          "space": "always"
        }
      }
}

这样,对目录下的一些或者特定文档就可以运行textlint命令来进行扫描了。

1
2
3
4
5
6
7
8
9
textlint .

_posts/2020-01-02-adding-whitespace-automatically.markdown
  26:66  ✓ error  原則として、全角文字と半角文字の間にスペースを入れます。  ja-space-between-half-and-full-width
  26:71  ✓ error  原則として、全角文字と半角文字の間にスペースを入れます。  ja-space-between-half-and-full-width

✖ 2 problems (2 errors, 0 warnings)
✓ 2 fixable problems.
Try to run: $ textlint --fix [file]

正如末尾一行的友情提示所说,textlint 妙就妙在提供了fix开关进行自动修正。

因为是用 iA Writer 作为 Markdown 编辑器,所以本座自定义了一个快捷键,对当前 iA Writer 应用里最前台(其实就是正在编辑)的文档,调用 textlint 扫描然后自动修复:

1
2
3
source_file=$(osascript -e 'tell application "iA Writer" to set filepath to file of document 1' -e 'POSIX path of filepath')
textlint $source_file
textlint --fix $source_file