作者归档:admin

kd

跨端架构的技术选型 2022

1. 跨端架构的意义

在《The Pragmatic Programmer》(中文翻译为《程序员修炼之道》)中,作者提了一个 DRY(Don’t Repeat Yourself)原则,主要指在程序设计以及计算中避免重复代码,因为这样会降低灵活性、简洁性。 把一切重复的代码抽象出来复用,当需要修改的时候只需要修改一次。 这里的复用有如下一些级别:函数级复用、对象级复用、模块级复用、类库级复用、框架级复用。 咱们今天聊的跨端是更高层面的复用,是端的复用。

现实中我们常见的工作方式是安卓的同学写安卓,iOS 的同学写 iOS,前端写 Web,小程序,H5 等等。 随着整体技术的进步,前端技术的发展,大前端的兴起,跨端实现越来多了,一些跨端的框架层出不穷。最终想要实现的跨端架构希望能做到:一次编写,多端运行

这里的端包括如下:

  • PC 端(苹果,Windows、Linux)
  • 移动端(安卓、iOS)
  • Web 应用、插件
  • 小程序(微信小程序,企业微信小程序、钉钉小程序、快手小程序)
  • H5

以上跨端的逻辑分为三个层面

  1. 硬件形态,或者说是设备,如 PC 电脑,移动设备、物联网设备
  2. 相同硬件形态下的不同平台,或者说是跨操作系统,如 PC 下的 Mac、Windows
  3. 相同平台下不同的应用或应用中的衍生应用,如 iOS 平台下浏览器应用(H5),微信小程序,支付宝小程序等

应用运行在某个操作系统之上,操作系统位于硬件设备与用户之间,是两者沟通的桥梁。不同的硬件有不同的架构和指令集,其对应也会有不同的操作系统。不同的操作系统其对应的执行程序结构不同,如 Windwos 下的 exe 结尾的程序不能在 Mac 下执行。这就是所谓的平台。

那么如何跨平台?

跨平台也是跨端的一部分,我们常见的跨平台应用,如浏览器,它所实现的跨平台是指运行在浏览器中的网页是跨平台的,而浏览器本身并不会跨平台,浏览器的生产厂商根据不同的平台,不同的操作系统分别实现了对应的版本,在应用层面抹平了平台和操作系统的差异,实现了跨端的目的。

浏览器在实现过程中会提供一个容器给到开发者,屏蔽平台差异,提供统一的 API 接口,让一份代码可以在不同的平台运行。 与浏览器类似的还有 Docker、JVM、Node 等。

在浏览器跨端的基础上,Electron 整合 Chromium 的渲染引擎、NodeJS 和用于调用系统本地功能的 API,使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序,其可构建出兼容 Mac、Windows 和 Linux 三个平台的应用程序。

在跨平台之外,我们还需要跨应用,如在不同的小程序、H5 之间,既然是应用,那就一定会包含界面,业务逻辑,如何只写一次业务逻辑,在不同的应用中执行,在代码重复层面追求更极致的体验,遵从 DRY 原则,让代码一次编写,多端运行。

我们在追求跨端的同时,也希望跨端的体验能趋近于原生的体验。

2. 跨端架构的本质

跨端架构的本质用稍微文艺一点的话来说就是:「世上本没有什么岁月静好,只不过是有人替你负重前行

一个跨端的架构至少包含渲染、逻辑和原生能力支撑、端的构建方式。一般会通过实现自己的容器来抹平(或者兼容)端的差异。

这个容器一般会提供两个能力:一个是渲染,另一个逻辑和原生能力支撑。 看我们常见的几种方案:

2.1 H5 hybrid 方案

浏览器自带跨端属性,通过各平台都有的浏览器,我们可以直接实现跨端,除了浏览器应用,我们也会把浏览器引擎嵌入到 APP 中,部分或全部使用浏览器渲染引擎,常见的如 Webkit、Blink 等。我们通过 Javascript 实现逻辑并且通过 JSBridge 调用 Native 的 API,透出的 API 需要应用在内部定制。我们一般用原生来实现要求高的界面,对于一些比较通用型,展示型的页面完全用 web 来实现,达到跨平台效果,各家的区别在于对于这块的定制和优化到了什么程度。

组成:渲染:WebView,逻辑:JS Engine,底层能力:JSBridge + 原生能力

优点:

  1. 接入成本极低,基本可以复用前端的技术栈和生态;
  2. 效率平衡性较好;
  3. 支持热更新,发版效率高;

缺点:

  1. 受限于 webview 等原因,动画复杂、无限流类的页面性能较差;
  2. 首开等对时间要求高的场景不能很好地做到极致;

如:网易云音乐,腾讯 QQ 中的大部分运营功能

2.2 框架 + 原生渲染

以 React Native 和 Weex 为代表的方案,通过结合 Web 的生态和 Native 的组件,尽可能地取长补短,让 JS 执行代码后用 Native 的组件进行渲染,实现了远超 webview 的效果。以 React Native 为例

组成:渲染:原生组件;逻辑:JS Engine + JSI;底层能力:原生组件;

优点:

  1. 开发成本均衡,大于 hybrid 模式,但是小于原生开发模式,一次开发,多端可用;
  2. 学习成本低,前端同学可以较快上手,上手速度快;
  3. 复用了前端生态,生态成熟,遇到问题,较容易解决;
  4. 有 Facebook 背书,发展相对靠谱;

缺点:

  1. 对于不同的平台特性的内容,需要有一些兼容的写法,甚至各平台各写一套;
  2. 交互复杂时,可能存在性能问题,新的架构已经大部分缓解了这个问题;
  3. 使用各端的原生组件渲染,相同代码渲染效果可能不一致性,已在尝试统一渲染。

使用 RN 的 APP: Facebook、youtube、discord、QQ、百度等等 使用 Weex 的 APP: 淘宝、天猫、饿了么等

2.3 框架 + 自渲染引擎

以 Flutter 为代表,Flutter 将代码编译成原生代码,并且直接在各个平台中使用高效渲染引擎 Skia 进行渲染,没有桥接,不调用平台相关控件。Flutter 没有直接借用原生能力去渲染组件,而是利用了更底层的渲染能力,自己去渲染组件。这种方式的链路会比 RN 的链路跟短,性能也会更好,同时在保证多端渲染一致性上效果更优。

组成:渲染:Skia;逻辑:Dart VM;底层能力:原生组件

优点:

  1. 性能好,更接近原生;
  2. 跨平台体验优秀,跨多种平台,减少开发成本;
  3. UI 跨平台稳定;
  4. 同时支持 JIT 和 AOT 两种编译方式的特性,在不同场景下可以使用不同的编译方式。

缺点:

  1. UI 跨平台,但原生能力没有,脱离不开原生,开发人员需要具备原生(Android、iOS)基础开发能力,兼容适配性较差;
  2. 稳定性、可能会因为引入了 flutter 而导致线上的 crash 率增加;
  3. 代码可读性较差,Widget 的类型难以选择;
  4. 生态中的 SDK,各种第三方包鱼龙混杂,没有一个官方的标准

除了最开始的 Android 和 iOS 跨平台支持,最新已经开始支持 Web 和 MacOS,未来还会继续支持 Win 和 Linux 平台的。在 Web 场景下,目前 Flutter 只能说可以用,但是还有挺多需要优化的空间,比如编译后 Web 文件大小,特定场景下的性能以及不同浏览器内核的兼容等等。

使用 Flutter 的应用:如钉钉(定制了较多的功能)、美团外卖、马蜂窝等

2.4 DSL 编译 + 混合渲染

该方案提供自定义 DSL 静态编译转化成目标源代码,包括 iOS、Android,H5 以及中国特色的各小程序平台。主要包括 uni-app、taro、Chameleon、Rax 等等。但各家实现不同,支持的平台类型也不一致,以 uni-app 为例:

组成:渲染:混合渲染、weex原生渲染、webview渲染,小程序和 app-vue 页面属于混合渲染,app-nvue 页面全部是 weex 原生渲染,H5 全部为 webview 渲染;逻辑:JS Engine + VUE; 底层能力:原生组件、原生插件;

优点:

  1. 支持多种小程序,多端;
  2. 开发成本低、学习成本小,本质上就是在写前端;
  3. 插件多,但是以个人开发者居多,质量参差不齐,没有保证;

缺点:

  1. 什么都想要,而什么都没有到极致,如果只是做一个能用的应用,是合适的,如果对于性能要求高,或者有比较复杂的交互,需要谨慎调研考虑一下;
  2. 兼容性问题依然有很多小细节问题,存在多端同时上线,某一端存在 bug 的情况;
  3. 原生功能依赖于 nvue ,对于没有提供的原生功能,需要对应的原生开发同学来开发;

我们要做到应用的跨端,在 PC 端相对好确定一些,以前端为主,客户端辅助实现部分前端薄弱的部分,如安装过程和一些和操作系统打交道或对性能要求比较高的部分。我们常用的 PC 端架构可以基于 Electron 、Tauri,在多平台客户端和 Web 端实现跨端。

在移动端,则更复杂一些,不仅要跨 iOS 和安卓这种操作系统级,还要跨微信小程序、支付宝小程序这种应用的衍生应用。下面我们聊聊一些有人在用的方案。

3. 实际落地的几种方案

在考虑实际落地之前需要明确一下是在什么层面的跨端,最理想的是跨全端,即跨硬件平台,在 PC、移动端设备,另一种是分两种,大屏和小屏,大屏主要是针对 PC 端、小屏主要是针对移动端。

如果是跨全端,不仅仅要考虑技术实现,从产品、到设计都要考虑,产品要考虑针对不同的端的应用场景,设计要考虑不同的屏大小下的体验效果和交互方式。这里我们只考虑移动端的情况。

3.1 Qunar 的 React Native 优先的多端统一化方案

Qunar 方案的主要逻辑是基于 Qunar 已有的 RN 技术栈,已经解决了 iOS 和 Android 的跨端问题,在此条件下,其问题变成了如何将 RN 转换为 H5 和各小程序。业内没有现成的方案,只能曲线救国,分别处理:

  • 对于 RN 到 H5,选择使用 Twitter 开源的 react-native-web,将 RN 代码运行在 H5 上,这个把 RN 的组件和 API 都用 H5 实现适配一遍,适配其行为和默认样式,在打包的时候使用 webpack 的别名机制将用到的组件替换成 react-native-web 里的对应组件。react-native-web 对原项目没有侵入性,无需改动原来的代码,只需在项目中加入一些 webpack 构建配置即可构建出运行出和 React Native 应用一致效果的 Web 应用。
  • 对于 RN 到 小程序,选择使用 Remax 组件实现一套 RN 的组件库,借用 remax 来适配到多端。Remax 的运行时本质是一个通过 react-reconciler 实现的一个小程序端的渲染器。Remax 通过 react-reconciler 生成一份自定义的 VNode Tree,再遍历 Tree 递归模版(微信小程序模板不支持递归,Remax 会为微信小程序生成一个 20 层的模板调用)渲染出对应的小程序页面。在生成之前 Remax会 为每个组件生成一份模版,然后把这份模版写入每个 page 页面里。由于小程序本身 View 与 JS 分离,因此在拿到 Vnode 时,需要通过小程序自身的 setData 触发小程序渲染。

3.2 Flutter 全平台方案

Flutter 可以理解为使用 Dart 语言定义了一套和原生一样的图形系统,其底层使用和 Android 原生一样的 Skia 引擎,安卓下直接用系统引擎,苹果生态下用自带的 SKia,这样就完全避免了 RN 中 JS Core 和原生模块通信造成的各种开销。

Flutter 这种自实现的引擎能带来目前体验最好的两端一致性,同一套代码,在 Android 和 iOS 上执行,从业务逻辑到页面布局再到最终渲染,都是在 Flutter 内部完成,通过 Flutter 实现的功能,在不同系统手机上的呈现效果是高度一致的。

Flutter 在 Android 和 iOS 上对跨端的支持较好,现在也有较多的业务在用 Flutter 完成这两端的跨端。对于 H5 而言,2019 年 2 月 Flutter1.2 版本和后面 5 月发布的 1.5 版本都主要支持了 Web ,但是到现在为止,Flutter 在 Web 端目前只支持 Dart–>JS 的转换,以及 UI 层的对齐,在工程化和性能优化方面做的工作并不多。Google 官方对 Flutter Web 性能优化所做的事项还比较少,编译输出的页面存在较大的性能问题,主要体现在以下两方面:

  • 首屏渲染时间长。即使使用了 FutureBuilder 把业务代码拆分成 xxx.part.js 之后,main.dart.js 体积依然维持在 1.1M。单一文件加载、解析时间过长,且静态资源缺少 CDN 化的支持,势必会影响首屏的渲染时间。
  • 滚动性能较差。 Flutter Web 自身实现了一套页面滚动机制,在页面滚动过程中,会频繁的创建 Canvas,最终导致滚动性能问题,甚至引起页面 Crash。

在阿里钉钉,基于 Flutter 构建的跨四端研发框架 Dutter,自己解决了数据通信问题、实现了自己的组件库,目前核心组件可以做到四端兼容,具体可以见:钉钉 Flutter 跨四端方案设计与技术实践

3.3 Hybrid 方案

这种反而在大厂是更常用的方案,其主要原因是大厂一般会有自己的框架团队,能够做比较多的定制,甚至有些把 Flutter 改吧改吧,自己实现一套,或者以容器化的方式实现自己的 Hybrid 方案。具体实现就不介绍了,这里主要介绍一下常用的一些性能优化的点,Hybrid 方案的性能优化的关键点就六个字:更早,更近、更快。

  • 更早:提前创建或初始化一些基础组件,如 WebView,减少用户体验层面等待的时间,让用户感觉更快了,也就是我们常说的预加载;
  • 更近:把资源提前缓存在本地,这里的资源可以是 Web 的资源,也可以是常用的配置数据等等,本地可以是客户端,也可以是移动端,如我们常见的「离线包」,特别是一些大流量的应用,一些资源的提前下发可以解决 CDN 等的突发流量等问题;
  • 更快:通过 Native 的方式替换一些跨端方案中的薄弱点,如一些网络控制的能力,一些视频播放或加载速度的优化等等

4. 小结

为什么标题中带一个 2022 呢? 因为技术是不断演进的,是不断发展的,今年的方案不一定适用于明年,期望有更好的方案出现。

写了这么多,把跨端的问题粗略的过了一遍,给自己温习一下,也分享一下。

任何跨端都是有成本的,当你选择跨端的时候,需要想的第一件事情,是否有必要这么做?ROI 如何?

跨端的问题很多最终都需要回归到当前端来解决,特别是一些对性能,对底层要求比较高的问题往往要在端来解决。

整体来看,跨端技术选型需要考虑如下 4 个问题:

  1. 战略和业务的问题,从公司产品战略和业务产品的角度探讨是否有必要跨端,如你的业务是否有强依赖的多端需求,各端的用户体量是否值得有如此投入?还是只要一端强就可以了?
  2. 人和组织的问题,在确认有跨端的强需求后,再看是否有合适的人和人才梯队来构建你想要的跨端架构,并且在确定跨端架构后考虑关于分工的问题,如一部分同学(如原移动端的同学)负责框架和能力,一部分的同学(如前端的同学)负责业务。在考虑现有人和组织的问题的时候,考虑一下后续人才招聘和团队人才密度的情况;
  3. 生态的问题,生态的问题会决定研发效率,是否有成熟的生态,是否有前从把坑都踩过了,当遇到某个场景是否有现成的解决方案或者类似的解决方案等等;
  4. 性能和体验的问题,随着业务的复杂,交互,场景也会越发的复杂,当遇到因复杂交互,或者复杂业务场景引起的性能问题时,是否有成熟的解决方案,或者退一步,是否可以解决?在多端一致性的问题上,是否能满足需求,或者兼容处理的成本有多高?

最后,祝大家国庆节快乐~

参考文档:

left

测试左移到底移了什么?

说起测试左移,得从瀑布模型开始。

软件工程瀑布模型(waterfall model)概念,起源于 Winston Royce 发表于 1970 年的著名文章 “Managing the Developmentof Large Software Systems” (Proc. Westcon, IEEE CS Press, 1970, pp.328-339)。

虽然这个模型可能是个误会,可以见 Craig Larman 和 Victor Basili 教授在 2003 年发表于 IEEE Computer 杂志的封面文章 《Iterative and Incremental Development: A Brief History》 中为我们讲解了一段非常精彩的有关瀑布模型的历史故事,这也可以说是世界软件工程史最大的误解之一。

他其实一直倡导的是迭代、递增和演进式开发,他在那篇文章中描述的瀑布模型其实只是一种最简单的情况,并不是普遍适用的,现在看也不是一种先进、最佳的方案。

瀑布模型的生命周期,包括需求分析阶段、设计阶段、实现阶段和测试阶段等等,其中测试又可以分为单元测试、功能测试、系统集成测试等。

测试左移是在瀑布模型的基础上,为弥补瀑布模型的不足,不让测试工作只成为产品交付前的最后一道屏障,而将测试往前提,将测试贯穿于整个软件研发生命周期中。

这里为什么是左移呢,是因为我们大多数的阅读习惯是从左到右,左在前。当把整个传统软件生命周期在一条直线上辅平,从左到右分为是从需求分析阶段、设计阶段、实现阶段到测试阶段,所以当我们想把测试提前的时候,在这条直线上,就是往左移了。

测试左移一词(shift-left testing)最早可能出现在 Arthur Hicken 的博客里,在他的博客中提到了对测试左移的看法。见这里:The Shift-Left Approach to Software Testing

其依据的核心逻辑是随着软件进入生命周期的后段,发现一个问题并解决的成本会急剧地增加,如下图所示:

成本增加的原因可能有如下几种:

  • 关联方多:越后期,关联的模块越多,定位一个问题,解决一个问题需要联动的各方更多,成本显著增加;
  • 影响面大:后期影响范围更大,修复一个问题需要考虑的问题更多;
  • 流程拉长:当到测试甚至线上再出现问题,整个处理问题的流程拉长,从开发阶段的开发自我闭环,到测试阶段,测试和开发互动,到线上用户、客服、测试、产品和开发都要介入,其流程长度完全不一样。

从图上看,当左移后这些成本会显著减少。不仅仅是减少成本,还可以减少当出现质量问题时,归责于测试团队的问题,以及关于质量的责任问题的扯皮过程。在我们传统的研发过程中,测试同学处于一个被动接受需求,被动接收开发完的功能进行测试,能主动改变的事情不多,而往往背锅的时候都会有测试团队。

测试左移的核心逻辑或原则个人认为有以下三点:

  1. 开发同学是质量的第一负责人,测试同学是共同责任人并辅助开发同学做好质量工作;
  2. 预防 BUG 比发现 BUG 更重要,工作的重心是预防而非发现;
  3. 测试同学以一个相对外部的视角来提供质量建议以辅助开发同学做好异常处理,以提升开发同学的开发质量和技术能力,从而提升整体研发的效能。

在测试左移的研发流程中,测试同学有以下职责:

  1. 测试同学主动参与整个研发过程,从产品阶段的质量需求,到设计阶段的方案设计(测试人员往往对全局更加了解)等;
  2. 测试同学通过手工或者自动化的方式,对 prod、stage、fat 等环境的应用进行频繁的测试,而不用困在流程中等提测后再进行,更主动进行测试;
  3. 在流程中负责主流程的质量验证;
  4. 测试同学负责线上问题的跟进和闭环;

我们理想中把这种模式严格落地后,线上质量会提升并且开发同学的能力会有极大的增强。

这里可能会有同学提出关于人力成本的考虑,觉得把测试工作转嫁到了开发同学,或者觉得测试同学的思维模式是找问题,开发同学潜意识不愿意找,不愿意把自己写出的东西弄崩溃,认为需要有一个测试环节等等问题。

但是当开发是质量的第一责任人,并作为一个独立的主体,对自己开发的代码负责,对自己负责的应用负责时,会想办法来预防 BUG,提升质量,那些思维模式的问题会随之改变。

在我的职业生涯中也经历了几年自己开发,自己测试,自己发布的时光,感觉很爽,就是一点,特别谨慎(害怕),因为此时你会是一个独立的主体来解决问题,你得为你自己的代码质量买单,此时会想尽一切办法不出 BUG,预防 BUG,包括极度严谨的多次的 Code Review,每次都要走的多级灰度部署,验证,日志查看,留守。其导致的结果是在一个超过十亿 PV 的应用(中间还有大版本升级、基础环境的升级等大范围的操作)上两年没有出现过大的事故(当然有灰度过程中的问题,但是及时发现并解决)。

那么,作为一个技术团队管理者在开始践行测试左移时需要考虑什么?

  1. 团队是否适合做测试左移,测试左移对于开发同学的要求会比较高?
  2. 是一把梭,还是先试点,需要评估一下这个改动的影响范围,考虑灰度一下?
  3. 业务需要快还是慢,对于慢业务用传统的瀑布是否更合适一些?
  4. 左移到什么程度?

回到主题,测试左移到底移了什么?

我的理解,测试左移,移的是角色职责,移的是责任主体,移的是质量意识,这些不移,其它移了都会是事倍功半。

management

技术管理者的 4 个基本思考点

技术团队管理者在日常工作中可能经常会遇到如下一些状况:

  1. 自测质量差
  2. 转测 BUG 多
  3. 项目延期
  4. 加班赶工
  5. 高强度加班后,小伙伴状态不好,导致更多的问题出现

从第 1 点状况演变成第 5 种状况,第 5 点状况继续推动第 1 种状态的持续加强,从而导致整个团队的状态极差,陷入 BUG 多 –> 延期 –> 加班 –> BUG 更多 –> 更多的延期 的死循环。

除了上面的死循环,可能还会有一些非功能性的问题,如性能、扩展性问题等等。

当团队大时,还可能遇到有小团队,各小团队各行其事,各为其主,心不往一处,力不出一孔。 这些问题让技术团队的管理者焦头烂额。

那么如何解决这些问题呢?个人认为可以从以下 4 个方面来逐一思考和优化,从而在一定程度上解决这些问题。

1. 把正确的人放到合适的岗位

所有的执行最终都是落到人身上,有了正确的人,事情会事半功倍。 说到人,我们往往会提起人的「选用育留」,这是一个很大的题目,我们不做详细的讲述,只关注选和用的一小部分。

管理上有一个在大部分场景适用的套路:选拔优先于培养。 这个套路背后有两层逻辑:

  1. 改变一个人太难,比如有些人就是懒,抽一鞭子动一下;又或者家境优渥,态度佛系,无欲无求,根本就是油盐不进;又或者玻璃心,安全感差,都很难搞;
  2. 成本太高,即使这个人具备可培养性,但从 0 到 1 把一个人培养起来,时间成本太高,而管理者的时间很贵,公司等不起。

所以我们这里是选人,从现有的人中找到合适的,从人才市场找到合适的,能直接用的。

本小节主要是回答两个问题:

  1. 正确的人是怎样的?
  2. 如何把正确的人放到合适的岗位上?

1.1 选人

在人的层面,主要包括两个部分,执行者和管理者,这里管理者包括整个团队负责人自己。 不同的部分,要求不同,选人的标准也不同。去掉专业技能部分,去掉历史经验部分,我们希望我们的伙伴是这样的:

  • 喜欢和投入:对于技术热爱,对工作有投入度,能够专注于自己手上的工作,找到成就感;
  • 知道自己想要什么:有一定长远的规划,知道自己走在什么样的路上,不限于一时一城之得失;
  • 一路人:认同企业的价值观,价值观认同其本质上是「人以群分」;
  • 宁缺毋滥:如果实在没有人,宁缺毋滥吗?这是一个好问题,严格来说是这样的,但实际中往往我们会妥协一部分。

我们选人一个常见的问题是注重人当下的表现和历史的成绩,然而我们选人是要解决未来的问题,更要关注其潜力。 那么如何看一个人的潜力呢?如果具备以下的特性,大概率是一个有潜力的人:

  1. 有意愿,什么叫有意愿,就是指一个人想变好,有内在的动机去追求更高的目标;这里扩大一些,还包含积极的态度、好奇心和进取心;
  2. 有静气,特别是遇大事时有静气,临危不乱;遇到繁琐的事情能一步一步慢慢做好;
  3. 有度,做事有度,知道做事的边界在哪,进展有度,但是并不是事事划边界,事事划边界显得格局太小,多数事情还是从大局着眼;
  4. 能扛事,遇到事情找方法,不找借口,执行力强;
  5. 有所为,天赋决定了能达到的上限,努力程度决定了能达到的下限。努力去做,有所为,并且 以现在绝大多数人的努力程度之低,还远远没有达到比拼天赋的程度。 这里的有所为不仅仅是做事,更多的是学习,不停地学习,提升自己。

1.2 用人

1.2.2 人才梯队

人才梯队从时间上看分为现在和将来。 现在是指盘点现有人才情况,梳理人才结构,对团队中的人进行分层,形成「梯状」。

当前状态的人才梯队分为两个层面,一个是分层,另一个是分层后的职责。 当团队大一些后,需要明确团队的组织分层,这里可能是正式认命的 Leader,也可能是虚拟的负责人,不管是实的还是虚的,最终都会有一个层存在。

分层本质上是一个分饼的行为,什么样的人拥有什么样的资源,承担什么样的责任,行使什么样的权利。

作为一个研发团队的负责人,需要梳理这些层,确定是否有合适的人,这些层的负责人形成了我们所说的「人才梯队」。

德鲁克曾说:「一个组织应该使每个人,特别是每个管理人员和每个专业人员(但也包括每个管理单位)都理解自身的任务」。 在我们做分层的过程中,需要关注每一层的职责和其解决的问题,如我们在软件架构设计的时候一样,每一个分层都有其作用,无用的分层只会增加性能的损耗。

对于将来,未雨绸缪。

当现在的人才正在发挥作用时,培养接班人,我们经常称之为 B 角。当有人才变动时可以快速补位上去。常用的方法有内部培养,跨团队轮岗,外部招聘等。

这块特别狠的可能要算宇宙厂了,在现有人员已经能满足工作需要的同时,会继续招聘,如果更优,可能会换人。

1.2.2 艰难的决定

在我们构建人才梯队的时候,想要做到知人善任是一件很难的事情,并且让每个人的表现都达到预期水平更难。当有些人并不能胜任他当前的工作时,我们应该怎么做?这里可能有以下两种常见的方式:

  1. 调岗,一般这种情况可能只是人岗不匹配,或者有些变化跟不上,但是对于人的部分能力还是比较认可。此时我们在公司内给他找一个匹配的岗位,更好地发挥其潜力。在技术团队最常见的例子是有些高 P 的技术同学,被推到管理岗,过了一段时间,发现适应不了,此时我们可以将其调到更能发挥其能力的岗位上来。又或者一些管理者因为团队扩大或者一些其它原因,管理的范围一下子增加了很多,一段时间后发现无法搞定这样的团队,此时可能将其调整到稍微低一级的职位上更合适一些。
  2. 离开,尽量让对方体面的离开,公司和个人都体面的分手,该赔偿的赔偿,该理解的理解,如果在外面有合适的机会,顺手推一把,也是个不错的善缘,不枉同事一场。这里经常出现的问题是犹豫不决,最终导致大家都不开心,不欢而散。管理上常说:「心要慈,刀要快」,就是要规避这种犹豫。

1.3 管理者自己

在用人中还包括管理者自己,一个技术管理者,不仅仅要注重技术,不仅仅要设定明确的目标并排出优先顺序,跟进过程发现问题解决问题,拿到结果,还要注意另一个重要的点:业务参与者。

一个优秀的管理者和一个不那么优秀的管理者的主要差别就是他们对业务的参与程度。事实证明,对所负责业务参与的程度越深,你就越能做出更加明智的决策。

什么叫业务参与度?

我认为它不是事无巨细地了解进展,应该到一线去,怎么到一线去,去写代码,去做代码评审?

到一线去,有两个层次,一个是业务的一线,用户或产品,看用户的反馈,产品的实现,二是工作的一线,看工作流程的卡点,系统化的情况,可视化的情况。不要自己去做这些事情,主要是收集信息,决策或者发现问题解决问题,这里确定做什么的原则是: 你做的事情的价值大小,而价值大小可以从时间杠杆,团队杠杆上来指数级扩大,如果做一件事只有短期的一件事的价值,那这些事情就不是你应该做的,应该停下来去思考要做的事情。

2. 组织

说到组织,你是想要一个「令行禁止,使命必达」的组织,还是一个「简单可依赖」的组织?

组织是为实现共同目标而采取的一种分工协作体系,是人与人之间的关系,组织结构往往会随着组织的重大战略调整而调整。

而企业在商场中求生,随着外部环境的变化,行业的变化,内部环境的演进而会不断进行迭代,不断调整组织结构,因此我们经常需要重新设计组织结构。

2.1 设计组织结构

我们在设计组织结构的时候通常要思考以下五个问题:

  1. 组织的目标是什么?因为组织是为了共同目标而存在的人与人的关系,目标不清楚,组织结构肯定也是不清楚的。
  2. 组织由哪些层,哪些单位组成?组织是一个体系,由不同的单元组成,需要明确各层或单元分别是什么,职责是什么,其作为一个子结构需要有自己的目标和使命。
  3. 组织各部分的规模和形式应该是怎样的?根据现实的情况,在设计组织结构时需要着重考虑规模和形式,多少层级?流程型?考虑管理者的有效管理幅度。
  4. 组织的哪些部分应该结合在一起,哪些部分应该分开?
  5. 组织内各单元之间的关系和协同应该是怎样的?

设计组织结构最难的问题可能是分和合的问题,这里有一个原则: 凡是做出同样的贡献的活动可以结合在一个部门中统一管理,不论它们的技术专业是什么。那些不是做出同样贡献的活动则一般不应合在一起。

在考虑组织结构,特别是研发团队的组织结构的时候,千万不可忽略了康威定律,甚至我们有时需要「逆康威定律」,通过适配康威定律,在明确技术架构方向的基础上,以组织结构的调整来推动技术架构的演进。

2.2 组织的形态

每家企业都有自己的文化和组织形式,抽象出来大致可以分为权力型,流程型和交易型,落到研发团队内部,一般只有前两种。

现在许多人强调去中心化、去科层化、去权力化,实际上,在大多数情形下,权力连接还是最直接、最简单、最有效的机制。权力组织讲究的是执行。

组织中的权力主要有四种。除了决策权,还有建议权、审核权和知情权。

  • 决策权,对一个事情决定资源投在哪里,是不是可以做的权力;
  • 建议权是提出方案、预案的权力。注意,这是一种权力:建议权拥有者如果未提出建议,上级决策者不能替代或强压;
  • 审核权是对有关事项程序性、合规性的审查与核准。注意,这种权力不是对事项进行决策;只要有关事项符合程序、合乎标准和规范就予以通过放行。我们经常会看到一些企业的职能管理部门把审核权误当成了批准决定权,导致审批流程变长、决策效率降低,这是需要反思改进的。
  • 知情权是信息共享权,即获取信息的权力。

以上 4 个权限很容易让我们想到项目管理里面的 RACI 矩阵:

  • 谁负责:(R = Responsible),即负责执行任务的角色,他/她具体负责操控项目、解决问题。
  • 谁批准:(A = Accountable,决策权),即对任务负全责的角色,只有经他/她同意或签署之后,项目才能得以进行,是整个事情的决策者;
  • 咨询谁:(C = Consulted,建议权),拥有完成项目所需的信息或能力的人员,多提出建议。
  • 通知谁: (I =Informed,知情权),即应及时被通知结果的人员,却不必向他/她咨询、征求意见。

以上四种权力,决策权和建议权是权力主线,审核权和知情权是权力副线。主线是上下级逻辑,副线是平级或流程型逻辑,而我们往往理解的权力是主线权力。

在强权力主线的基础上,权力型组织的有以下 3 个缺点:

  1. 唯上,组织内的成员对决定其资源或发展的上级负责;
  2. 组织的发展由上级决定,当上级缺位、能力不足或脱离实际时可能会出现瞎指挥,乱指挥的情况;
  3. 层级过多,导致执行和决策的效率低下。

为规避这些缺点我们需要减少层级,增加连接和协同。

如果把管理层只分为三级,我们一般称之为高层、中级和基层。这三个层要解决的问题不同。高层解决长期发展的问题,中层解决系统效率的问题,基层解决的是事情本身的问题。

比如技术管理者常规上可以分为三个大层:

  • 高层管理:解决长期发展的问题,使企业/团队有前途,关注的是未来,不管是技术发展的未来,还是业务发展的未来;
  • 中层管理:解决系统效率的问题,使系统更有效率,要不断强化研发流程的统一性和适应性,确定并执行好标准和规范,提升协同的效率,标准化和系统化,同时为上层的选择和决策提供依据和保障;
  • 基层管理:解决事情本身的问题,而事情往往会落到一线的开发同学身上,于是经常我们基层管理者的主要工作是使一线开发同学工作有成就,培养一线开发同学,提高他们承担工作的能力和意愿,帮助他们解决问题。

我们期望是每个层能做好自己的事情,但是实际上往往是高层在做中层的事情,中层在做基层的事,基层在做一线的事情,错位了。于是,我们需要将管理层归位,各行其职,更专业的做事情。

在明确职责的基础上,尽量减少管理的层级,尽量不要出现 「副XX」 的岗位。

在协同方面我们用流程来解决协同合作的问题。流程本身的逻辑是对事情负责,对自身的任务及向下事项/环节负责。其驱动力是责任感和依赖。百度文化里面的「简单可依赖」能较好地说明流程的底层逻辑。当我们使用流程来解决协同的问题时,要经常迭代流程以优化协同的效率:

  • 注意流程是为了解决过程中的问题,不是为了设置卡点,每个人都有扩大自己权力的欲望,这是我们在流程建设中要特别注意的;
  • 尽量减少流程环节,聚合责任主体,我们经常遇到一个事情在某职能部门还需要经过两三道审核的情况,这种损耗可以通过聚合责任主体的方式来解决;
  • 控制流程时长,这里控制流程时长还包括流程环节的前置准备,就和我们开会一样,如果开会前准备充分,会议本身的时长就有极大可能减少。

用权力解决「力出一孔」的问题,用流程解决协同合作的问题。

简单来说,在权力体系的基础上,用流程连接各环节和负责主体,现在比较常见的矩阵式组织结构基本符合这个逻辑。当然这里有一个以流程为主还是以权力为主的问题,具体是哪种,还得看公司当前组织结构的逻辑。

2.3 分工的粗和细

Adam Smith 在 1776 年的《国富论》中提出了分工,分工对于手工业生产效率有较大的提高,其总结了三个优点:

  • 熟练程度的增加,当一个人专注于一块工作,不停的练习极大的增加了熟练度,熟练度的增加将导致质量和产量的增加;
  • 当熟练后,人们对于重复的操作进行机械化或自动化,从而更大的提高质量和产量;
  • 分工明确了输入和输出,在明确的分工下,从一个工序转为另一个工序的时长减少了。

自从分工提出来了,产生了大量的争论,有人提出了以下的一些缺点:

  • 一叶障目,不见泰山,分工越细,所关注的东西都小,当人陷于某个事情越来越小的部分时,其大局观往往受限,可能会导致局部提升而全局受损的情况;
  • 分工越细意味着沟通协作成本的增加,当分工获得的收益小于沟通协作的收益时,将产生极大的成本浪费;
  • 分工一定关系到组织结构的分块,当沟通不畅或没有沟通时,有可能出现组织上的「孤岛」

分工落到一个研发团队,我们通常按编程语言分为 Java、PHP、C++、Javascript,或按端分为安卓端、iOS 端、前端、后端、算法、数据等,又或者大一点分为开发、测试、运维,架构师。

虽然「术业有专攻」,但多跨一步会让分工后的协同更高效一些,这里最常见的可能是测试左移和测试右移。

  • 测试左移是指在研发流程中,把测试的覆盖范围从传统的测试节点中释放出来,将其向左扩展,介入代码提测之前的部分,如开发阶段阶段,需求评审阶段,让研发人员在架构设计时就考虑产品的可测试性,并尽量进行开发自测,同时评估需求的质量,比如分析需求的合理性以及完整性等。
  • 测试右移是指把测试的覆盖范围从传统的测试环节中切出来,将其向右扩展,更多地融入代码部署、发布,甚至上线之后的步骤中。

更极端一些,走全栈路线,一个人从头到尾完成需求的所有工序。但是这种方式,对于人员素质的要求,对于团队组织的要求和常规不一样,且人的精力是有限的,能在每个方面都做到精通的,少之又少,除非所做的事情只需要略懂即可。

思考一下,你所在团队应该如何来做?

成本和效率?组织大小?技术发展?架构演进?

3. 机制

3.1 DRI 机制

3.1.1 DRI 的由来

DRI 是 Directly Responsible Individual 的简称,中文翻译为「直接负责人」。最开始是从苹果流传出来的内部管理概念。

DRI 不是流程、过程,也不是框架,而是一个负责人,对某部分的整体负责,小到 BUG,大到技术方向。 DRI 是为了解决责任主体的问题,其有助于避免责任分散。责任分散这个概念也被称为「旁观者效应」,也就是人们身处团队中时无法对某事负起责任,责任分散到了团队中的每个成员身上,而不是集中在真正有责任的人身上,因为每个人认为那个责任应该由其他人承担,表现得像一个旁观者。

3.1.2 DRI 的职责

  • 聚焦目标;
  • 督促、监督团队成员完成自己的任务;
  • 清楚团队中发生的一切;
  • 统筹策划,搞定所有的干系人,从头到尾负责到底,说简单点就是团队成员专注的做好手上的事儿, DRI 排除干扰,解决各种烦人的问题来,发现问题,解决问题;
  • 有一定的领导责任。

简单来说,当你是某个事情的 DRI 后,这个事情就是你自己的事情。特别是当职责不清或者突发问题时,DRI 就需要发挥主人翁的精神,拉起团队成员去分析问题,解决问题。

3.1.3 什么人适合做 DRI

DRI 和工作年限无关,和是否资深无关,和技术工种无关,你想你就是。

但是在操作过程中,我们会根据实际的场景做一些偏重。如果是一个后台的活儿居多的项目,其 DRI 大概率是后台的开发同学,如果是一个质量问题较多的项目,其 DRI 大概率是一个 QA 同学。

这里我们会充分考虑同学的主观意愿,有些同学想,有些同学不愿意,只想做好手上的工作,那么他做好他手上工作的 DRI 就可以了。

DRI 无关项目大小,无关职位高低,无关所在层级,每一件大事,小事都需要有一个 DRI,且只有一个 DRI

这玩意儿换成中文其实就是我们在标语里面经常看到的「责任到人」差不多,但是更强调责任主体的唯一性。

3.2 构建良好的协同机制

公元前 221 年,秦始皇用了十年的时间,先后灭了韩、赵、魏、楚、燕、齐六国,完成了统一中国的大业。在以后,他陆续颁布了多条律法,以稳固国家的统治,包括「书同文」、「车同轨」、「度同制」等。

一个国家,一个组织,要想成为一个高效执行的团队,一定要有标准,一定要有人告诉大家怎样做是对的。 落到我们团队管理,标准,流程是必须要做的,特别是当你的团队是由若干个团队整合或融合的时候。

3.2.1 统一标准规范

当你的团队是由原来多个业务的团队融合而成,大家原来都有做一些标准,现在我们需要按统一的标准达成共识并推行下去。又或者本来标准不完善,需要系统梳理标准来达到标准的统一。

行业标准一般是为了互联互通,而对于研发团队的研发过程来说,标准主要是为了减少过程中的认知成本,提升研发的效率,比如数据库规范,大家按统一的规范来设计数据库,当有其它同学接手你负责模块的时候,能减少在基本结构的认知成本,以及在一些模块间整合或数据迁移时,对于工作会比较友好一些。

标准是什么,标准是一件行为准则,其关注的是结果。

标准和规范一般是为了告诉人们什么是好的,关注的结果,而统一标准是为了让大家互联互通。

标准不是为了成功,而是为了让整个事情不至于太坏,尽量不出现重大的问题。 具体到研发团队,我们一般需要统一如下一些标准:

  • 研发过程
    • 代码风格规范
    • 数据库设计规范
    • 代码分干管理规范
    • 代码提交规范
    • 错误码规范
    • Code Review 标准
    • 代码权限管理规范
  • 沟通协同
    • 架构规范
    • 技术方案规范
    • 文档规范
    • 接口规范
  • 质量标准
    • 代码质量标准
    • 自动化测试标准
    • 测试质量标准
    • 线上质量标准
  • 性能标准
    • 服务端性能标准
    • 客户端性能标准
    • 前端性能标准
  • 安全标准
    • 信息安全标准
    • 代码安全标准
    • 数据安全标准
    • 线上安全标准

3.2.2 统一流程

流程是什么?

流程是基于时间线,有一定先后序列规则的完成一件事的过程。流程是线性的、连续的。

统一流程是什么?

统一流程就是把一些验证过的好的做事方式,好的经验通过流程的方式固化下来,防止大家重蹈覆辙,在一个坑里踩多次,并且为不熟悉的同学做好指导。

我们做任何一件事都是有流程的,有些是设计过的,有些是自然而然的,设计过的流程可能是别人的经验。并且流程需要持续迭代。

在研发管理中我们常常会构建的流程如下:

  • 敏捷流程
    • 需求迭代流程
    • 紧急需求流程
    • 值班需求流程
  • 研发流程
    • 代码审核流程
    • 代码发布流程
    • 紧急发布流程
  • 协同
    • 对接流程
    • 资源申请流程
    • 线上问题/告警处理流程
    • 事故处理流程
    • 安全问题处理流程

3.2.3 统一工具

以上说了要统一标准,统一流程,这些第一步是要把这些标准和流程做出来,形成文档,落到知识库中。 如果只做到这一步,这些标准和流程可能就真的只是一个文档,情况好一点,有人来推进重视,可能会落实一些,但一旦这个推进人不在了,或者不再关注了,很多形式就不了了之。

要解决这个问题只能通过工具或系统,以工具或系统的形式固化标准和流程,把这此好的经验和方式以更物理的方式沉淀下来。再以这些工具或系统为杠杆,提升整体研发的效率,创造增量的价值。

4. 系统

这里的系统不仅仅是指使用某个系统,在使用系统的基础上整合,实现我们高效执行的目的。 前面我们有了机制,但是事情太多,不能让所有的事情用人来去解决,需要用系统来解决。

机制解决规模化的问题,系统解决规模固化的问题。 系统解决的问题有两个层面,一个是过程跟进,一个是结果度量。

4.1 工作流和过程跟进系统

一个研发部门可以看作一个系统,需求从一端进入,经历各种正确的工序,才能变成产品,如期从另一边离开。 当系统内部存在冲突,或者不和,或者互相针对,那么就会发生各种想象不到的问题,从而让整个系统的产出变少,甚至没有。

着眼于整个工作流,确认瓶颈点在哪,尽可能的运用各种技术和流程来确保工作在计划内有效的执行。因为我们知道:在代码投产之前,实际上并未产生任何价值,因为那只是困在系统里的半成品。

在实际中我们如何让大家更好的协同,更好的让这个工作流运转起来呢?以前可能是 Excel、Word 或者 todolist,再加上邮件或 IM 传来传去,现在更进一步有在线的表格协同,还有更完整的项目管理系统,如 Jira 、Trello、腾讯的 TAPD、阿里的 Teambition、禅道等。工具不同,但是目标是相同的,都是希望做到对项目执行的管控、对团队事务(问题)的跟踪,对需要多人协作任务的快速流转和处理。

除此之外,还需要文档管理,即过程中的物料也需要跟进起来,关联起来。至于是一个系统,还是多个系统,都是可以的。

系统虽有,但用得怎么样不好说,一个好的系统用不起来也是白搭,这里作为管理者需要推动起来的。

4.2 工作可视化

项目管理的系统用起来后,我们的工作流转就会落到系统里面,此时根据系统的数据,我们可以让工作可视化,透明化,能够清晰的观察工作流动的情况,从而发现瓶颈。发现问题是最难的,很多时候我们不知道有什么问题,包括自己。发现问题,才能解决问题,方法总是有的。 在资源有限的情况下,对非约束点的改进看起来很正确,但实质上毫无帮助,甚至会消耗宝贵资源拖累真正需要解决的问题。

我们可以构建一些看板,看一个产品从产品到设计、到研发实现,到测试完成,上线发布,再到线上问题跟进等等的情况。看效率,看一个需求从出现想法到用户看到需要多少时间,每个环节需要多少时间,哪些需求在哪些环节停留太久?不同的需求,不同的人,不同的产品做多个层次的对比,从而发现问题解决问题,让一切都在阳光下进行。

同时,我们可以让系统和数据告诉我们,整体团队的投入如何,有多少同学的工作是可以追溯的,有多少人力是隐藏在不为人知的地方的,能看到我们的时间都去哪了。

基于这样的看板,我们从两个角度优化整个系统:

  • 从左到右的流动,看从产品、设计、研发到运维的工作情况。为了最大程度地优化工作流,需要将工作可视化,减小每批次大小和等待间隔,通过内建质量杜绝向下游传递缺陷,并持续地优化全局目标;
  • 从右向左的反馈,每个阶段中,应用持续、快速的工作反馈机制。通过放大反馈环防止问题复发,并缩短问题发现时间,实现快速修复。通过这种方式,我们能从源头控制质量,并在流程中嵌入相关的知识。这样不仅能创造出更安全的工作系统,还可以在灾难性事故发生前就检测到并解决它。

总体来说,先透明出来,再优化,打开黑盒,问题会简单很多

4.3 其它

以上的两个是从任务跟进和结果度量的角度,或者说从项目管理的角度来看整个团队的运转。 换一个角度,从研发同学工作本身,有没有需要系统化的地方?

代码管理是否系统化,Code Review、接口文档、接口自动化测试、Mock 数据、测试数据集管理、用户数据自动脱敏重放,代码从写完提交到代码库之后到上线,线上巡查等等这些是否有系统化?

这并不是今天我们要讲的话题,但是就系统化来说,这些都是必不可少的关键点。不管是哪方面,我们的原则是尽量减少人工介入,把人的经验变成代码和系统。

5. 后记

这篇文章务虚居多,也比较散,但是确实是技术管理者日常工作中要不停思考的点。 思考这些是用来帮助厘清思路,并不具备实操性,也就是不能实际的解决问题。 不同的公司,不同团队,问题不同,解决的方法也不同,欢迎一起探讨。

打开「黑盒」,问题会简单很多。常思考人、组织、机制和系统,这 4 个方面,发现其中的问题,并厘清解决问题的思路,一步一步,有节奏的去解决。

你好,我是潘锦,超过 10 年的研发管理和技术架构经历,出过书,创过业,带过百人团队,也在腾讯,A 股上市公司呆过一些年头,现在在一家 C 轮的公司负责一些技术方面的管理工作。早年做过 NOI 和 ACM,对前端架构、跨端、后端架构、云原生、DevOps 等技术始终保持着浓厚的兴趣,平时喜欢读书、思考,终身学习实践者,欢迎一起交流学习。微信公众号:架构和远方,博客: www.phppan.com