活动回顾 / T SALON

编辑内容

张泽亚谈前端工程化:从团队痛点到构建、发布与质量治理

张泽亚结合飞书文档的复杂工程实践,说明如何按业务阶段选择规范和工具,展开依赖缓存、项目拆分、私有 source map、测试灰度与平台整合,并区分当时已有措施与后续建设方向。

张泽亚把前端工程化理解为针对具体问题组织研发的过程。复杂业务里,写代码只是链路的一部分;需求、调试、构建、测试和发布之间的等待与协作,也决定团队能否稳定交付。

这段分享来自深圳“前端的挑战与机遇”录播第 3 段,所属稿件发布于 2022 年 5 月 9 日。文中是张泽亚当时在飞书文档及此前团队的经历,工具选择、指标和规划均保留历史语境。

工程化覆盖完整生命周期

前端承担的业务越来越复杂,工程化的目标,是把软件工程的方法用于开发、部署和维护,用规范、框架与工具解决效率、质量和性能问题。脚手架、构建优化和流水线都是手段,任何一项单独存在,都不等于完整的工程化体系。

张泽亚先向现场抛出“为什么会出现前端工程化”这个问题,再把答案落到业务复杂度:前端能承担的功能越来越多,协作人数增长,原先靠个人习惯支撑的小工程变成需要长期维护的产品。于是需求、技术方案评审、开发联调、测试、部署都进入同一条生命周期。一个小应用或许打包后上传就能交付;复杂文档不能只靠这一招。

团队可以先观察实际工作流,沉淀适合自己的规范,再通过工具落实;平台则把分散的能力连接起来,减少开发者在不同入口之间切换。脚手架解决初始化,SSR、国际化等解决方案处理特定业务需求,两者可以进一步组合成工程框架。但这些能力仍应尽可能可拆分,让团队按需要接入。

团队视角不同,收益与约束也不同

小型业务可能只需要顺畅的本地环境和发布链路,过多规范反而增加负担。复杂业务更重视统一治理;中台团队可能专注于抽象某一项公共能力,如云构建、检查和资源发布。

他把视角依次换成中小业务开发者、复杂业务负责人、中台能力提供者和一线工程师。前者想要快点启动开发服务并打通发布;复杂团队最担心多人改动相互踩踏;中台可以把构建等单项能力做成低成本服务;个人则要付出学习框架和遵守规则的时间。同一项规范在组织层面可能让整体效率大于成员简单相加,却让某个人眼前的一次改动变慢。这种代价必须在设计时承认。

个体开发者又有另一种感受:完整框架需要学习,规范会限制自由,短期操作甚至可能更繁琐。张泽亚没有回避这种矛盾。他认为设计时要尽量照顾使用体验,但评价最终应回到团队整体是否更有效,而不是只看某一次个人操作是否更快。

先确认业务阶段和可度量的痛点

他提出两个前提:没有适用所有团队的方案,也不能脱离产品所处阶段谈建设。业务基本能力还未完善时,过度投入基础设施可能得不偿失;但只顾眼前交付,也可能留下将来难以处理的债务。

实践起点是可观察的问题,例如启动时间、热更新时间、单元测试覆盖、构建与发布耗时。再看可用人力和长期目标,优先利用公司已有设施或合适的开源方案。自行造轮子需要理由,不能因为工具看起来先进就引入。

飞书文档当时一个明显痛点,是本地保存代码后,热更新可能需要十几到二十秒。这个可计时的等待比“工程很重”更适合作为改造起点。他同时要求团队想远一点:如果业务用户和代码体量继续增长,今天的结构是否还能让后来者迭代?提早考虑可扩展性,与在产品尚未打磨好时投入大量人力做平台,是两种不同的判断。

规范也不只有 lint 或 Git 工作流,还包括按业务还是文件类型组织目录、如何限制模块依赖,以及团队使用怎样的语言与接口约定。最终要通过低成本入口落实,最好能在 IDE、CLI 等日常环境完成常用操作,而不是让开发者记住一串分散的平台网址。

从规范到自动流转,而不是只增加流程

张泽亚说明,自己没有参与飞书文档最初的基础建设,因此先用此前团队的经历解释常见顺序:规范工作流、提供业务脚手架、接入工程框架能力,再沉淀组件与自动化流程。

到了多人参与的大工程,收益更多来自把调试、构建、发布等环节接起来。在约定时间触发检查、通知和流程推进,必要时由人确认,比反复手工协调更可控。片中对未来自动化的期待也有明确边界:流程可自动通知和流转,不代表代码冲突或业务判断能完全由机器解决。

他举飞书文档每周版本为例:几十乃至上百人可能跨团队参与,若灰度、上线、通知都靠人逐一切换平台、手工催办,协调本身便成为工作负担。机器人可以在时间节点拉起下一环节,让人负责审批和判断;合并冲突等需要理解代码语义的工作仍得由人解决。自动化的目标是减少重复流转,而不是假装没有责任人。

飞书文档面对的五类问题

他首先交代既有基础设施:代码托管、在线编译、部署、监控、机器人和开放 API 等能力已经存在,业务工程化很多时候是在这些能力之上整合,而不是从零再造全部平台。

问题主要来自业务本身:付费协作产品对稳定性要求高;工程体积增长使编译和本地更新变慢;跨团队功能可能互相影响;产品希望快速迭代,但大量代码集中进入版本又增加质量压力;复杂上下文让单个开发者很难独立定位所有问题。一次客户反馈可能需要不断拉入上游、下游同事,大家才逐渐拼齐线索。

跨团队冲突不仅是 CSS 相互覆盖,也可能是状态流转或内存泄漏。产品希望需求更快上线,研发却面对一周内大量代码合入后的质量风险。客户反馈一处问题时,A 找 B、B 找 C,群里人数越来越多,恰好说明问题的归属和上下文没有被工程结构充分表达。

依赖、编译与目录结构,分别减掉什么成本

依赖安装方面,团队当时迁移到 pnpm,并根据依赖描述与锁文件等信息判断能否复用已有安装结果,减少重复安装。构建侧则重视中间产物缓存,并尝试把相对稳定的依赖预先处理,降低每次需要重新编译的范围。

他的步骤分属不同开销:pnpm 与依赖快照针对安装,webpack 中间结果和较稳定库的预编译针对重复构建。本地则尝试用 esbuild 换取更短反馈时间;他强调这只是当时开发场景的取舍,生产构建还要看兼容、完整性与业务要求。目录改成按 feature 聚合,是为了让一个功能的组件、状态与逻辑更容易被找到,不是构建速度的同义词。

本地开发还尝试更快的构建工具。张泽亚明确区分了当时团队用于本地提速的试验与生产环境采用的方案;这不构成对某个工具普遍“不适合生产”的结论。

代码组织则从按文件类型集中,转向按 feature 聚合,以减少大量组件和状态文件混在同一目录带来的理解成本。它解决的是定位、职责和依赖问题,不会仅靠换目录名字就自动降低业务耦合。

微前端拆分为什么没有消除所有重量

在导航天然隔离的后台系统里,子应用边界相对容易确定;文档业务却不同。一篇文档可能插入表格、多维表格或其他内容,同一个能力又会出现在不同场景,模块之间有真实依赖。

这解释了为什么他前面用微前端来回应热更新慢,却在这里补充其代价。若各子应用都要依赖庞大的共享基座,单独开发其中一个功能仍需启动很重的一层;若模块之间原本缠在一起,拆边界前还要解耦。拆分减少一部分编译范围,但不是把复杂度消灭。

因此,拆分需要先重构和解耦。即使子应用可以独立运行,开发者仍可能依赖一个很重的基座。张泽亚坦陈,基座本身成为进一步提速的阻碍,继续简化与重构仍是当时的工作方向。微前端是应对巨石工程的手段,同时也带来新的运行和协作成本。

source map 要可用,也要控制访问

生产排查需要把错误映射回源码,但团队不希望 source map 随公开资源暴露。片中介绍的做法,是把生成与上传的一部分工作移出主要上线等待路径,另行处理并上传到受权限控制的平台。

构建流水线还会经过代码、包体和安全检查。source map 若直接跟静态资源一起公开,定位错误方便了,源码也可能随之泄露;若完全不保留,线上堆栈又难以还原。异步生成、私有上传和权限控制,是围绕这两种需求安排的一组措施。

这个方案同时关心两件事:能否在排查时取得匹配版本的信息,以及正常上线是否必须等待所有辅助产物。安全、调试和发布速度需要共同设计,不能只把生成 source map 当成一个孤立的构建开关。

多人改动合在一起,怎样逐层检查

每个需求通过自己的测试,并不意味着合到同一个版本后就不会互相影响。团队因此在不同阶段设置检查:低成本访问开发测试包,使用预发布环境,运行单元与端到端测试,再对即将进入发布流程的版本进行集成测试。

片中举了一个轻量测试入口:开发者构建后,在访问地址加查询参数即可打开对应测试包,省去每次都正式部署。预发环境则让固定用户群先“吃自己的狗粮”。进入每周版本前,测试人员围绕本次代码做数天集成检查;之后再灰度到团队、飞书内部、字节跳动内部等逐步扩大的用户群,并做上线前回归。每层都在回答不同问题,不能拿一层通过当作全部安全。

张泽亚强调,接入测试工具并不是最难的一步,更难的是针对具体业务写出有效测试。片中介绍当时每周版本在发布前还安排集成检查,不能把某个固定天数当作所有团队的标准。

通过之后,再逐步扩大使用范围:先是相关内部团队,再到更大的内部用户群,观察版本表现,配合回归测试推进。前端资源托管与配置服务负责按用户群下发对应版本。多地区、预发、线上和其他环境的部署,则通过自定义流水线减少手工操作。

能看见,不等于已经能使用

性能部分专门区分了 Time to View 与 Time to Use。SSR 可以让内容更早出现,但不代表复杂文档已经完成初始化、能够响应操作。工程体量、拆分后的加载以及弱网等因素,仍会影响可用时间。

片中提到团队观察过较长的等待与尾部场景,并以版本性能报告检查改善或退化。这里的数值是当时业务指标,不能作为当前飞书性能结论;更值得保留的是,性能定义要贴近用户能否真正完成操作,而不是只看页面什么时候有内容。

张泽亚举的当时例子是可用时间约四秒,在极端弱网或尾部场景可能达到十秒甚至更长。对打开重要文档的用户而言,先看到一点内容却不能编辑,并不算完成加载。因此团队希望每次版本发布都跑性能数据,明确本次改动让哪些指标变好或变差,并持续建设能稳定对比的性能实验室。

平台整合的三层结构

他把整合方式分为三层。最上层是开发者直接使用的环境,包括开发服务器、脚手架、IDE 插件、CLI、CI 和机器人;中间是业务定制 API,把公司平台和业务自建能力统一收口;底层则是构建、部署、版本灰度、框架与组件等设施。

中间层使团队可以围绕自己的流程组合能力,又不需要在每个客户端入口重复实现。微前端开发时使用线上基座还是本地基座,也可以在这种统一入口中安排。组件与设计语言则帮助不同端和产品保持一致。

他设想开发者主要停留在 VS Code、浏览器和飞书:IDE 插件与 CLI 处理本地操作,浏览器承接调试,机器人推进协作;业务定制 API 再去调用公司已有的构建、部署、代码和开放平台,也接到自建灰度与版本能力。这层整合不是另起一个包办一切的平台,而是减少开发者不断跳出工作环境寻找入口。

哪些问题当时仍没有完成

张泽亚把工程化比作先看症状再选择处理方式:有些问题能用现成工具缓解,有些需要影响更大的重构。他没有宣称前面五类问题已经全部解决。

后续方向包括深入编译环节提速,把更多操作收敛到 IDE、浏览器和 IM,自动协调审查、审批、测试与修复流程,以及建设更完整的性能实验室。团队当时已有性能报告的雏形,但仍希望增加独立稳定运行、与同类产品比较、按版本输出完整报告和支持事故前后分析的能力。把已落地部分与这些目标区分开,才能理解工程化是在持续建设,而不是一次完成。

查看原视频与分段信息:大前端时代的挑战与机遇:深圳场 →

主题
前端工程化团队协作性能优化