web 基础研发体系指的是, web 研发中一线工程师所直接操作的技术、工具,以及所属组织架构的总和。在过去提升企业研发效能的讨论中,围绕的主题基本都是——”通过云计算、云存储等方式将底层核心技术封装成基础设施“。而我们在实践中发现,在
- 互联网渗入到各行各业,业务爆发
- 企业竞争白热化,对速度和品质要求越来越高
- 一线工程师队伍越来越庞大,管理成本增高
这样的多重背景下,除了底层核心技术外,一线 web 研发效能的问题也逐渐成为决胜战场的重要因素。
然而在现实中我们看到,因为一线的研发工作可替换性强,所以并没有受到足够的重视,同时也缺少更统一、更有深度的规划和管理。实际上,将一线工程师所直接接触到的应用框架、测试、部署、监控等领域作为一个完整的体系来思考,并打造成一体化的基础设施,能为企业的业务研发带来巨大的效能提升。
在《月相》一章中,我们将介绍Web 基础研发体系有哪些构成部分,并且将深入到关键性的技术和问题中。《潮汐》一章将介绍如何配合这套研发体系,在组织结构上做出一些调整,通过管理手段进一步挖掘团队潜力,打造更高效的组织。
另外,希望这些内容也能为一线工程师提供一些职业规划上的引导。
2 月相我们将要讨论的研发体系,涵盖了”研发流程“和”系统“两个维度。可以用一张大图来描绘:
可以看到,将这些内容作为一个整体,符合目前互联网公司”核心技术“ + ”web 研发能力“ 的模式,能快速产出应用。其中,”用户“、”权限“、”流程“可以说是绝大部分系统的铁三角,因此我们也划入到了基础研发体系中。
接下来看每个部分。从流程的角度来说,提升效能的关键在于”工具化“和”自动化“,我们就以这两点来切入。
2.1 设计首先是设计,设计与编码的结合是目前业界想象空间最大,但也是最不成熟的领域。对于自动化的实现,目前的尝试大致可以归纳成两类:
第一类,与设计师约定规则,按规则转化设计稿。这种方式的关键在于,“既要规则简单易于被设计师接受,又要保证视觉上的关系能完整转化成程序中的关系“。我们举个例子来说明”视觉上的关系“和”常见的程序中的关系“: 网页上这样一个场景:
可以很容易地理解为一个 Tab 组件里面嵌着一个按钮,他们是”嵌套关系“,在程序中用 html 可能这样写:
<Tabs>
<Tabs.Pane title="tab1">
<Button/>
</Tabs.Pane>
<Tabs.Pane title="tab2" />
</Tab>
但是在现代的设计工具中,图层信息表示的仅仅是视觉上的前后关系。
这就出现了一种不匹配,设计师可以通过一万种方式来表达同样的视觉效果。因此,要保证正确识别关系,必须和设计师约定只能以某种方式来创建图层。但问题是这种约定本身对设计师来说没有实际意义,对他来说只是约束。除了嵌套关系以外,位置关系也是同样的问题——目前设计工具产出的设计稿只是某一种具体尺寸的视觉效果,而我们实际产品的尺寸会因设备不同而不同的,甚至可以随着浏览器窗口的缩放等功能动态变化:
怎么来表示这种变化对设计师来说也是额外的约束。乐观的是,从技术角度来说,总归是可实施的。
第二类尝试,像游戏一样做专用的设计工具,则从根本上解决了上述问题。
(React Studio)
思路很简单,既然设计师能有多种方式来表达,那么我们从工具角度来约束,是不是就不会有问题了?虽然同样有约束,但是对设计师来说负担要小多了,不需要额外记忆,按照工具的指引使用即可。我们甚至还可以提供一些高级功能防止出现一些人为错误,以此来吸引设计师。这种方式唯一的缺点是有一次性的学习成本。
虽然目前的自动化方案,都还只是从“视觉稿”到“程序静态视图”的自动化,并不包括交互逻辑的自动化,但已经有了巨大的意义。在前端程序员工作的统计中发现,他们有一半以上的时间都是在”调整大小、调整位置、对像素、对色值“,而且越是好的前端,这个时间比例越大。因为写逻辑是可以通过提升自身素质实现量级缩小的,而写样式这个工作本身很难实现量级的时间缩短。
如果在研发体系中,设计稿能自动转化成可用的代码,无疑对传统的 web 页面研发会有巨大的提升。虽然做专用工具看起来应该是最终的方向,但在目前的现实环境中,可能会因为加重设计师的使用负担而被抵制,所以通过在原有设计工具上做约定的方式来过渡可能更合适。在用约定的方案里,怎样让约定即不给设计师造成太大的负担,又能解决上述的规则转化问题,就成了重点。在实践中的解法是,通过工具的高级能力来补偿设计师。这部分细节已在《设计稿自动生成可用页面的展望》中详细描述过,这里不再赘述。
2.2 研发、测试和监控我们将这三个环节合在一起来讨论,是因为他们存在技术决策上的上下游关系。过去在大团队中规划研发体系,常常会出现一种现象,就是研发、测试、监控都是由不同团队规划的,而每个团队都想着做平台。后来慢慢发现这个思路是有问题的,因为做平台必然要考虑到不同端的接入,要花成本将自己的服务抽象得足够底层,花成本对不同的端做适配。而在这三个环节中,研发中的运行时框架(应用框架)是工具化和自动化的核心,只要对运行时框架多进行一点点投入,后面测试、反馈、监控的研发成本就能降到非常低!
举个前端的例子。在搭建可视化页面搭建平台时,我们设计了一个将“所有组件数据都统一到一棵树”上的方案。
在传统的 React 中所有组件的 state 和 props 合起来才能表达一个页面唯一的状态,state 和 props 分散在组件中,不易收集。而在这个设计中,全局的 state tree 即表达了页面的一个状态,如果将每一次变化后的state tree 都存起来,即可通过回放来展现页面动态变化的过程。更进一步的是,利用这个特性,我们在 200 行代码之内就实现了“录制即测试用例”的功能。用户无需写任何晦涩的用例代码,在调试自己写的页面时只要觉得没问题,就可以将刚才的调试过程保存成一个用例。
再举一个接口层的例子。我们运行时框架的接口采用了 GraphQL ,并且告别了手动写接口的形式,全部利用视图勾选生成。
这解决了两个研发中常见的问题:
- 杜绝了手工约定接口时可能出现的拼写等错误。
- 能自动统计到所有对某一接口进行消费的页面,一旦接口进行调整,可以自动通知到下游,甚至能自动生成适配代码,不影响下游。
这也就极大地减轻了测试环节的压力。之前的思路基本都是通过扫描代码去发现接口错误,要消耗大量资源,现在看起来没必要了。
这两个例子都是从研发的角度来思考所看到的收益,我们再单独从测试与监控的角度来看。
测试领域有一个热点,—— UI 自动化。目前的 UI 自动化有两种方案,图片对比 与 dom 树对比。
(Screenster)
这两种方案都有一个共同的缺点,即“无法正确地识别变化的类型”。例如现在有个需求,视图上的两个元素需要调换一下位置,但逻辑并没有变,希望测试平台不报警。除非人工干预,否则这两种方案都很难判断出来,因为他们是以”最后渲染结果“作为判断依据的。但是如果我们的测试是针对运行时框架来设计的话,就很容易实现。以上面研发时所讲的组件树方案为例,页面到底有没有逻辑性的变化只和 state tree 有关,因为页面的状态是 state tree,而逻辑操作的也是 state tree,所以我们只要认为 state tree 没变,就可以认为页面没有发生变化,不用触发报警。
除了能识别变化外,利用运行时框架的设计,我们还能实现更先进的功能,例如 B/S 架构中还原浏览器端出错现场的问题。在过去 debug 时,我们通常都要与测试交流,按照操作步骤手工还原到现场,如果能由程序自动化一次性达到出错现场,那无疑能给 debug 速度带来质的提升。要实现这种能力的关键点在于,任何表示页面状态的数据,都要能暴露到外部,也能由外部传入进行重置。一旦有一个决定页面状态的变量在函数中,不能取出,不能序列化后传给服务端,就无法做到。毫无疑问,这种能力也是需要应用框架来支持的。例子中 state tree 的设计,有一部分原因就是出于支持这种能力的考虑。
再来看监控领域的热点,“无埋点”监控,和自动化测试有异曲同工之妙。“无埋点”指的是无手工在代码中的埋点,通常是使用可视化的技术来进行“标记”。
(GrowingIO)
目前业界的一些方案中,遇到的问题同样是不能正确地识别变化。例如当页面上的元素改变了位置,埋点能不能不受影响?在可视化搭建平台这个项目里,我们同样是在应用框架这个层面设计了解决方案:
我们的页面使用一种类似于模板的方式来嵌套组件,这个结构我们称为 component tree,这个结构是静态可分析的,所以可以很容易地实现可视化。用户如果想要控制组件使其产生变化,那么必须给组件取个唯一的名字,在逻辑代码中使用这个名字对它的数据进行操作来实现改变。
有了这个前提,无需任何额外的投入,就已经实现了“无埋点”。因为“埋点”本身就是对逻辑功能的统计,所以埋点一定会埋在有逻辑相关的组件上,因此一定会有唯一的名字,那么无论组件怎么变化,只要没有被删除,我们的埋点信息就不会受到影响。同时,如果一个有埋点的组件出现了改名或者删除,我们还能自动提示报警。而这些功能,同样是在不到200行的代码就实现了。
(埋点取名)
综上,从测试和监控两个角度我们也可以看到,只要运行时框架提供一点点帮助,就能以极小的成本来实施。针对确定的上游研发框架来进行下游的开发,不用再考虑对各种框架的兼容问题,也可以让下游在能力上走得更远,实现更多先进的功能。
2.3 框架核心技术我们在线下交流中发现,很多团队对框架的投入只停留在写小工具和包装开源框架上。因为看不清方向,不知道如何投入,也不知道投入后有多少收益,所以不敢深入。其实方向和具体应该投入哪些技术,都是有迹可循的。这个踪迹的源头我们在 《理想的应用框架》中曾提到过的,就是程序的本质——数据和逻辑。
2.3.1 数据先具象一点来说数据。框架的数据就是框架运行时内部保存的对象等数据结构,只要回答好两个问题就能展现出强大的威力:
- 数据在哪?
- 数据的生命周期是怎样的?
知道数据在哪,是管理数据的基本条件。应用的任何状态,都可以看做是内部数据的一种表现。因此只有框架掌握了所有数据,才有可能实现例如还原现场之类的功能。这对我们的研发有两点指导意义:
一是在使用已经有控制反转和依赖注入的 web 框架时,应该完全遵循框架的约定,将服务等对象的管理完全由框架。有的框架语法写起来比较麻烦,可以通过命令行或者IDE工具来自动生成。
二是在我们改造或者创造框架时,应该把数据的统一管理作为最基本的底线,这是上下游实现自动化的基础。上一节中所提到的测试录制的能力,就是建立在统一数据源的基础上。再举个更有意思的例子,过去的前端的 ajax 请求基本都是独立调用 api,无中心的模式。这种模式可能会出现的问题是:
请求A发出后,由于网络等问题,一直未返回,这时用户有重新发送一次请求 A1。结果 A1 迅速返回在回调函数中提示成功。然后请求 A 超时返回,在回调中提示失败,导致最后用户看到的是失败的信息。
当引入 saga 之后,所有异步的操作都统一收归到通信管道中,就能进行跨请求的管理了。单个异步请求的取消、多个异步请求到底是独立、还是竞争、还是只保持最后一个,就都能很容易地实现了。基于中心化的请求管理,还能进行可视化:
(kuker)
继续讲到第二个问题,数据的生命周期是怎样的?生命周期通常是由外部事件来触发,或者自己运行到某一阶段自动触发的。对深度开发来说,有两个基础能力必须由框架来提供。即框架要支持:
- 手动驱动生命周期
- 内部数据的复制和置换
手动驱动生命周期对自动化测试之类的功能来说很重要,特别是在做一些基础性的测试时,有了这个能力就不用再完全模拟外部的触发条件。而内部数据的复制和置换则能为录制、还原现场、协同等高级功能提供基础。我们现在就在尝试基于这种能力,实现“用户可以将自己的出错场景一键发送给开发人员来复现”的功能。值得注意的是,有的语言中复制对象是非常昂贵的操作,这时可能就需要考虑,是否使用 immutable 的数据格式会更好?还是依靠一些约定和标记提供自身提供廉价的复制能力?限于篇幅,在此就不再展开。对框架中的数据问题感兴趣的读者可以去搜索 Single Source of Truth 和 Shared Mutable State 之类的话题,业界已经有非常多的精彩讨论。
2.3.2 逻辑聊完数据,终于来到最有意思的逻辑部分。框架从某种意义上来说,就是提供了一种逻辑表达的方式。要在逻辑表达上提升效能,有两个发展阶段:
- 提供一些模式或技术。让用户写出低耦合、易重用、易扩展的代码。在写代码时提高效能。例如 MVC、IOC 等等。
- 针对不同场景设计更合理的 DSL,能实现代码、图等表达方式的互相转换。在研发的整条链路上实现自动化。
我们看到大部分的框架都处于第一个阶段,不管是服务器端的 MVC 还是前端的 MVVM 。但也有少量第二阶段的尝试。例如 Flow Based Programming,试图完全用数据流向的角度来诠释业务中的逻辑。它的代码可以天然被分析成图,甚至能在运行时进行观察:
(noflo)
还有《理想的应用框架》中提到的基于事件来表示业务逻辑,也是一种 DSL。但这些尝试离最终的目标仍然有差距,最终理想的状态应该是能实现业务流程图、时序图、决策树等业务领域常用的表达方式与代码的互转。虽然有差距,而且看起来要走的路还很长,但是针对一些已经比较稳定的场景,已经有一些好的经验。CMS 框架 Drupal 就是一个很好的例子。它定义好了数据发布的整个流程和相应的钩子系统,让开发者用模块的方式在钩子里去修改或者增加自己的功能。曾经一度实现了一个非常繁荣的社区。更值得肯定的是,社区中很多模块都是可视化的,最终用户不需要写任何代码,按照模块的可视化指引就能完成相应的功能。这实际上就等同于 DSL 与代码的互转了。
不管哪一个阶段的关键技术,都离不开分析语义的能力。直白一点来说,就是“知道哪段代码是干什么”的能力。
在第一阶段的框架中,最影响效能的因素并不是”要写代码的多少“,而是写按框架概念写出来的代码是否易于理解、易于维护。这一点在越是大型、越是多人参与的项目中,越是明显。而代码的“语义”是否清晰在某种程度上直接决定了我们是否能通过技术手段来提升可维护性。例如,在使用依赖注入的系统中,如果所有代码的注入声明都清晰地表明了注入的到底是什么的话,那我们就很容通过语言层面的支持或者简单字符串匹配得到依赖关系图。反之,如果注入的信息模糊,既可能是函数也可能是 model,没有任何明显约束的话,那就可能得通过语法树,找到注入的入口才能分析出来,这样实现的成本就成倍增加了。
(Rekit Studio 依赖分析图)
相比于依赖分析,更重要的是“调用关系分析”,它对于帮助理解流程,特别是排查问题特别有用。举个简单的例子,在数据驱动的前端框架中,因为视图完全是数据的展现,发现视图不对了,如果能动态展示出修改了数据的业务堆栈(不是函数堆栈),就非常有用。不需要再一步一步断点。
当然实现起来也更难。难点有两个:一是依赖分析一般是运行前的,是静态的,而调用关系一般是运行时的,是动态的;二是依赖通常是声明出来,容易读出来,而调用通常是在主动式的语句中,会遇到条件判断、循环、甚至通过变量在不同函数、类作用域中传递,比较难分析。这种难,其实也就是语义不清。在框架的设计或者我们自己的改造中,可以通过三点来尽量提供明确的语义:
- 尽量转主动为被动
- 尽量片段化
- 消除用户代码中的副作用和对外部作用域中变量的依赖
前两点很好理解,被动声明式的代码结构更容易被分析。虽然在实践的时候需要大量经验,来保障设计出来的声明格式即能覆盖所有场景又要容易编写,但是它带来的收益也是最可观的。GraphQL 就是一个最好的例子,在服务器端用声明的结构将数据结构和关系表达出来,在客户端用声明的结构将要获取的数据结构表达出来,后端就可以使用统一的引擎来生成调用,省去的大量写接口的时间。第二点,尽量片段化是指我们在引导用户写代码的时候,应该把生命周期等等概念拆得尽可能小,使语义更细致。写起来繁琐的问题可以通过工具或者语法糖去解决。
第三点最重要,消除副作用指的是任何时候运行用户的代码片段应该都不会对外部环境产生影响。而消除对外部作用域中变量的依赖指的是对要用的数据、服务尽量都用参数的形式传入。这样做的好处有两个,对于一些复杂的,难以分析的调用的关系,可以将要观测的对象包装一下再传入,这样就能动态得到调用关系。在整体运行前,因为无副作用,也可以很容易地通过试运行这些片段来得到一些信息。虽然现在的语法树工具已经比较流行,但真的要完全通过语法分析来得到足够的语义仍然有很大的工作量。而上述的这三点,可以看做是快速、廉价的实现方式,并且实践中效果非常不错。
综合最后值得一提的是,上面所讲到的数据和逻辑中的原则与技术并不是相互独立的。在《前端服务化——页面搭建工具的死与生》和《通天塔之石——企业级前端组件库方案》这两篇文章中看到,我们所使用的很多技术,其实是混合支撑着数据修改追溯、组件属性可视化等功能。他们中间有的也有着依赖关系。但相比于“数据和逻辑”这两个源头,这些并不重要。只要掌握了这两个源头面对的问题,其他都是能推导出来的。
对开源框架如何使用也是同样的道理。对于严肃的企业生产来说,应该找到业务所面临场景的源头,吸收解决问题的先进的想法,但自己实现,就像编程语言各自实现语言特性一样。而不应该只是停留在包装开源框架这个层次。开源框架为了适应尽量广的场景,有更大的群众基础,给出的一定是普适性的方案,这种普适性在业务发展到一定程度,有了足够多的独特性之后就会变成巨大的包袱反噬研发效能。等到了这个时候再考虑自己研发,其迁移、适配、研发成本以及带来的风险可能会变得非常大。而我们从前文看到,掌握了框架研发的几个核心,从小的场景就开始投入,成本并不高。最重要的是长久积累形成体系后,所带来的“流程上自动化“、”降低下游实现成本”等能力能持续地帮助企业提升研发效能。
2.3 通用子系统与核心接口从流程的角度来看,提升效能的主要是靠自动化。而从系统的角度来说,则主要是靠能力的复用。”用户“、”流程“、”权限“几乎是任何业务系统中都存在的,因此将这三者也纳入到了基础研发体系的范围。在这里我们并不打算深入到每个系统所面临的具体问题中,只讲两点:
一、产品化或者子系统化,不要过早平台化,对将来系统整体打包有益。过去的互联网公司习惯将这些公用系统平台化,各个业务系统来接入。但这几年互联网业务进入的都是新领域,面临的市场、用户常常是需要隔离的。这个时候系统整体复制的能力就变得非常重要,所以一开始就将这三者以子系统或者子产品的方式来对待,能为之后的发展提供更多的灵活性。
二、三者不是并列关系,不用纠结于能力解耦,制定核心系统接入规范才是最重要的。权限系统无论是 RBAC 还是 DAC 都离不开用户系统的支持。流程则是既依赖于权限也依赖于用户。如果把运行时框架看做是主板,这三者应该是主板上的补充部分,一起为核心系统的接入提供针脚。同样是为了系统整体打包的能力,应该尽早制定核心系统接入的规范。
3 潮汐在《月相》一章我们探讨了 web 基础研发体系的构成及部分重难点。相比于具体技术本身,这套方案对组织成长和重塑的意义更大。在这一章中我们会先从大团队中两个有启发性的问题出发,逐步深入到如何打造一个更高效的研发组织方案中。虽然问题出现是在大团队的,但对小团队仍有两点借鉴意义:
- 小团队随着业务发展会长成大团队,也可能碰到一样的问题。
- 问题本身萌芽于成长过程之中,予以正确的指导能更加节约人力,助力公司发展。
首先注意,我们讨论的仍是 web 层的问题。这个现象在有多个不同业务的大公司中最常见。出现这种现象的原因有两个,一是公司到了万人规模,实际上就相当于上百个百人规模的小公司,必然想法很多,出现重复的自然也多。
另一个更重要是,在 web 这个领域,特别是前端,基础设施变化太快。
无论是浏览器底层所支持的 api,还是 javascript 语言本身变化都非常快。底层一变化,研发的各个环节必然出现基于新技术的空缺,很多框架和平台就是随着这些空缺出现的。现象本身存在即合理,但如果有更好的统一管理,是能从如下两个方面提取出巨大的效能的。
一、如果将研发流程中的各个环节中独立的平台统一,可以极大地加速业务开发。直接决定业务发展速度的是进行业务开发的工程师。他们最希望的就是只有一个平台将研发的所有流程都搞定,并且尽量自动化。平台一多,对业务开发工程师来说,学习、沟通、协作的成本就会陡增。这个成本有多大?在我们通过线下了解发现,这个成本很多时候甚至会超过业务开发本身。
二、加大对趋势和方向的研究,预防无序的框架和平台的投入,这样能尽量防止走偏和无效劳动,也是一种提升。越是大的团队,这一点越明显。“基础设施变化”这样一个滚滚巨轮这些年压碎了多少框架和平台,其中很多产生的效益都不足以覆盖其研发成本。更坏的是,出于个人利益等原因,有的平台过时了也不肯下线,阻挡了整个团队随基础设施一起进步的机遇,消耗的是更多的未来的人力。
我们在《月相》中提出的研发体系就是实现这两个提升的具体手段。
首先,有一个完整的体系来将研发流程当成一个整体对待,并且通过统一的平台来实现,能更好地实现流程的自动化。降低一线工程师沟通、学习成本。
其次,对运行时框架的投入,本身就包含着对趋势和新技术的研究,可以缓解被基础设施带着跑的问题。并且我们看到运行时框架研究到了一定程度,能够极大的减少后续测试、监控实现的成本,也降低了基础设施变化所产生的多米诺效应。这里对管理带来的反思是,我们应该谨慎“平台化”的思考。特别是下游环节,因为“平台化”的思维,必然要考虑对各种不同的端进行适配,必然要制定各种规范,这些都是人力成本。如《月相》中所示,上游理清楚并且统一后,下游是能有针对性地、很低成本地实现的,根本不需要“平台级”的成本投入。当然,在这里所说的只是减少技术上的投入,环节本身还是非常重要的。
平台林立从某一方面来说也表示着团队内部有着大量的无序的力量,这种力量在团队出现不同环节的分工时就产生了,尽早地建立研发体系就能尽早地将这些力量用起来,创造真正的价值。
3.2 资源池很多大公司的 web 研发团队都被当成资源池来用,哪个业务需要就投入到哪里。现象的直接原因有两个:一是从管理的角度来说,web 层的研发工作相对来说可替换性强,具备形成资源池的可能。二是 web 研发处于业务决策链底层,人员相对来说最紧张,因此通过资源池的方式能动态地支持业务开发,是最简单的解决方案。但这个方案其实是相当低效的。我们从对工程师个人的关注来切入这个话题。
首先从主观的角度出发,一个有激情,对职业未来充满憧憬的工程师与苦大仇深的工程师在效率上是有成倍差别的。除了其本身的性格外,造就这两种态度有很大一部分是职业上升渠道的问题。首先,和任何能被当成资源池来用的工作一样,因为可替换性强,所以不容易被重视。其次,目前的上升渠道不够。web 研发工程师的上升渠道要么是纵向的,当业务发展得足够好,重要性提高,成为系统负责人。要么是横向的,随着团队扩大,管理需要,成为管理者。这两者一个基于业务一个基于人力,和技术相关不大。所以有才华的工程师自然会想要造框架、造平台,努力成为公司技术中重要的一部分。但没有正确的引导,就会成为上面所说”无序的力量“,发展得不对反而会变成公司的消耗。而当他发现自己付出的努力得不到回报的时,就有可能变成苦大仇深的工程师。
最终影响的不只是工程师自身,公司也会要在管理成本上为其买单。我们在与一些资深 hr 交流时,他们进一步说明了这个问题。在公司工作了四、五年的员工,是已经融入了公司,理解公司文化也了解公司问题的人,算是中流砥柱。如果得不到好的上升,可能会有两种情况:其中有想法有行动力的,多半会选择离开。既会对组织稳定性会造成影响,也会让公司付出的培养成本付诸东流,对公司来说可算是很大的损失;另一种更不好的则是既不离开,也不像以前一样努力。他们在团队内有一定的话语权,却丧失了进去的激情,不再发挥积极的作用。当公司需要快速扩张、快速发生变革的时候,他们就会成为隐性的管理成本。
而解决方案,其实很简单。大公司内部通常都有”框架组“、”平台组“类似的部门,但基本都停留在”可用“阶段就完成任务了。这远远不够,如果将建立 web 基础研发体系作为目标,以产品化为衡量标准,加强重视和投入。 web 工程师的上升渠道就会得以扩张,公司内无序的力量就会进入到正确的领域。创造的价值又能进一步为研发提供能量,形成一个正循环,逐步缓解人力紧张的态势,资源池现象也就自然会消失。这很像我们身体受伤时,外部肿胀只是炎症的表现,消炎了,肿胀自然也就好了。重点是找到关键进行消炎,而不是围着外部现象思考。
建立体系还能带来的好处是,通过提升 web 研发的效率,降低业务研发所需的人数,能帮助大公司重新回到小步快跑的节奏。张小龙有一篇演讲《警惕KPI和复杂流程》,讲到了小团队的重要性,随着互联网公司的竞争越来越激烈,这种重要性会变得越来越高。我们从一些互联网的新巨头上,其实已经看见了“核心技术+基础web研发能力”来快速出产品,占领市场的趋势。对小公司来说,统一的 web 基础研发体系,是帮助实现超车的重要一环。对大公司来说,则是进一步挖掘效能,防止掉队的必修课了。
4 后记这篇文章笔者前后重写了三次,因为面对 web 一线工程师这样一个庞大的群体,效能提升已经不仅仅是技术或管理某一方面的事情,而是要综合各个方面来寻求个人和公司发展的共赢。 web 一线研发是很多工程师进入这个领域最开始做的事情,但是笔者看到了很多有才华的人困在了低效、重复的工作里面,努力了也因为方向不对而徒劳。这对个人和公司都是损失。在技术上找到方向,在管理上予以支持,多方发力,其实能提升的效能远超十倍。当然,一篇文章不足以实现任何改变,这篇文章最主要的目还是抛砖引玉,也希望对这个方向有想法,志同道合的朋友联系我,一起交流,一起推动。
邮箱: ariesate@outlook.com。
5 答读者问问:研发体系的统一规划不是破坏了竞争和创新吗?
答:统一规划并不是指不要竞争,不要创新。而是防止开倒车的情况出现,是给竞争和创新指定一个方向。例如我们在文中理清上下游关系,目的是指导研发力量应该往哪里投入,在投入的过程中仍然可以采取竞争的形式。
问:框架研发提升效能的趋势?
答:分两个方向。一是框架本身,会朝着“增强对用户代码的理解和控制力”这个方向前进。用户按框架写的代码,能不能通过工具分析出具体的语义?人工的低级错误能不能自动检测出来并自动修正?能不能转化成某种人类习惯的表达方式,比如图?当控制力达到一定程度后,这些能力都实现后,再往下应该就是代码的自动生成。
另一个值得一提的是框架能力与业务特有属性会一起沉淀到 IDE 上,会出现面向专有框架的 IDE,面向单个具体工程的 IDE。分析图,自动扫描工具都由 IDE 作为载体,就像很多游戏的专用编辑器一样。只有沉淀到 IDE 上才是最便捷、最有针对性的。对这个方向感兴趣的读者可以跟我邮件讨论,目前我们已经有一些初步的想法。
问:文章中讲到的问题都是大公司的,对小团队来说,什么时候应该开始建立自己的基础研发体系?
答:研发体系的建立可以分为规划和实施两个阶段。规划的话,从一开始就应该有所规划。在前期人力不够的情况下,不用强行追求打造自己的框架和工具,可以用开源的。但应该始终把开源的东西拿到自己的体系中,清楚地知道它在自己体系中的角色,也清晰地知道自己用了它的什么特性,未来还需要补充什么。什么时候开始实施其实有一个象征性的标准,也是我们前面在无序的力量中提到的——出现了不同环节的分工时。有的团队业务扩张太快,人力一直不够,那么建议按五分之一到三分之一的比例来抽出人力打造基础设施。磨刀不误砍柴工,何况投资得到的回报是电锯呢。