☰
前端精读周刊:维护好一个复杂项目的两个关键方案——主人翁心态与系统解耦
2026/10/1 2:14:12 网站建设 项目流程
  • 文档
  • 技术博客
  • 教程

【免费下载链接】weekly

前端精读周刊。帮你理解最前沿、实用的技术。

项目地址:https://gitcode.com/GitHub_Trending/we/weekly
点击查看免费下载

导读:国内互联网公司的项目往往已持续维护五年左右,而 IBM 这类老牌公司的项目甚至维护了十五年,但不同项目的维护成本却天差地别——有的多年迭代依然高效,有的却连改几个文案都会引发线上事故。本文以《前端精读周刊》264.精读《维护好一个复杂项目》 为核心骨架,拆解复杂项目难维护的根源,并结合本仓库「可视化搭建」系列文章中 BI 搭建引擎的真实设计实践,深入讲解"主人翁心态"与"解耦"这两套实战方案,读完你将掌握如何在设计之初就为复杂系统铺好可维护的底子。

复杂项目为什么会越维护越难

许多持续五年、甚至十五年的项目,维护成本却呈现出截然不同的走势:

  • 有的项目运作几年后,维护成本依然与初创期相差不大,可以保持较高效率的迭代速度;
  • 有的项目则越来越慢,研发效率持续下降,甚至改几个文案都会导致线上事故。

两种结果的差别,本质上不在项目规模,而在于设计之初是否考虑过可维护性。根据笔者经验,持续维护项目变得难以维护的原因,主要集中在两个层面,对应两套应对方案:

  1. 心态层面:开发者是否以主人翁心态对待项目,决定了代码的质量上限;
  2. 架构层面:系统是否做了解耦,决定了修改代码时会不会"牵一发而动全身"。

方案一:拥有主人翁心态,在设计之初就做好考量

为什么流程与 CodeReview 救不了场

作为项目管理者,一个项目一旦交给某位同学开发,就要完全信任这位同学的能力——因为实际上你已经不可能实质性地影响到开发细节了。

有人可能觉得好的流程或事后 CodeReview 能发现一些问题,但这永远是杯水车薪。以透视表开发为例:

小张接到任务研发透视表,要求这个透视表具有良好的开发体验并做好单测。

"怎么样做单测才算是有效的,如何同时保证开发体验呢?"——不同人会有完全不同的想法与结果,而流程与评审都难以穿透这个黑盒。

主人翁小张:把"隐患"录制成单测

对于一个有一定经验、又对项目真正上心的小张来说,开发过程是这样的:

  1. 先做主要功能:考虑数据模型、绘图技术方案后,决定采用图形语法方式定义数据结构,做了一系列高性能前置考虑后,快速做出原型,包含表格的渲染、操作、翻页、冻结等功能。
  2. 直面边界 case:随着需求深入,做下钻、排序时发现影响到了列冻结功能——代码架构没问题、抽象也良好,主要是细节的代码调用漏掉了,补上后功能恢复。但小张意识到,下次做树状展示结构时可能又会互相影响,这始终是隐患,于是决定先加单测再继续开发。
  3. 把操作 Action 化:由于出问题的场景有很小部分是大量操作后偶然引发的,普通函数式单测无法保证覆盖全面,小张决定做一个单测录制功能:
    • 先把对表格的所有操作Action 化,让一套 JSON 可以描述所有用户操作;
    • 在本地开发界面做一个单测录制功能,页面上对表格拖拽操作时,实时生成这套用户操作 JSON;
    • 把当时的页面结构与内部状态记录下来作为对比依据;
    • 单测就还原这套 JSON 并与基准状态做对比。
  4. 分层录制单测:
    • 录制大量原子操作单测:表格的各种空数据状态、单行单列渲染、列冻结行冻结;
    • 组合混合场景:列冻结时排序、翻页后进行下钻;
    • 构造随机复杂组合,形成日常容易出问题的特殊 case,例如"表格单页后突然清空数据,再强制冻结第二列,再灌入 3 列数据并对第 2 行做排序,再取消列冻结并翻到第 4 页"。
  5. 边界 case 闭环:每当遇到一个边界 case,都记录到单测,验证确实运行失败后再修复,直到包含该单测在内的所有单测验证通过,才算开发完成。

这套方案的核心价值在于:把"很难描述清楚"的偶发问题,转化成可复现、可回归的确定性资产——Action 化让操作可序列化,录制让单测成本趋近于零,组合场景让功能交叉地带也有保障。

打工人小张:单测只是用来应付指标

对于"为了混口饭吃"的小张,开发过程则是另一番景象:

  • 做完各种表格功能后同样遇到边界 case 难题,本想 case by case 修复;
  • 但 leader 要求写单测,觉得倒也不坏,就创建了单测目录;
  • 先把遇到的问题修掉(毕竟谁也不希望自己手里的 bug 太多),但录制单测太麻烦,反正大家也不知道这个 case,修掉就不会再出现了吧;
  • 只把 leader 要求的几个基本功能单测加上,看下覆盖率达到硬性指标即可。

大团队代码总是容易走向混乱

假设你是 leader,你不知道自己的小张到底是哪一种,企图通过 CodeReview 统一提升团队代码质量——这实际上很难可行:

  • 打工人小张在 CodeReview 时展示的代码结构,就不是能做整体单测的抽象;
  • 你只能看着单测文件硬提一些"多加一些单测,多考虑一些情况"的建议,完全达不到主人翁小张的效果。

背后的原因是:影响代码质量的因素太多——Action 化、极端 case 的录入、全流程的单测形式,这些对代码来说都是质变。但 CodeReview 时看到的代码就是不够抽象、不够 Action 化的,不可能把代码推翻重写,只能基于现有代码提优化建议。而到这个时候,神仙也没法让打工人小张的代码优化成主人翁小张的,除非推翻重写。

这就是心态的影响力:能把项目做好的细节很多,且细节之间环环相扣。比如不把代码 Action 化就不方便做整体单测,但如果开发者打一开始就没想好好设计,CodeReview 时又有多少人能想到这一点呢?想到时再提可能为时已晚,一切都已成定局。

笔者看过不少久经历史的代码,大公司有大量开发者维护同一个项目,每个人心态各不相同,总能发现那些用打工人心态做出来的模块——想彻底优化就只能彻底重写,但碍于项目体量太大时间上不允许,只能沿着打工人思路继续写下去。所以,拥有一个良好、正面、积极的主人翁心态来写代码,一般来说就可以维护好复杂项目。

方案二:解耦——借鉴社会运作的底层认同机制

复杂 ≠ 功能多

复杂项目的"复杂"指的是什么?是功能多吗?其实不然。

如果仅从功能多就判定项目复杂,那我们身处的社会才是最复杂的系统,但社会中的每个玩家都没觉得吃穿住行很难——核心原因在于:了解我们用到的场景只需要少量知识,而做出一个行动要得到正确的结果,也不会造成太大的影响。比如出门买菜,只要坐公交到菜市场、扫码完成交易即可,不需要理解公交体系与菜市场背后的金融体系。

但代码世界就很有趣了:在代码世界买个菜可能会导致世界毁灭。这导致每一个项目开发人员,哪怕去买个菜,也要受过"总统级训练",对各种"国家级大事"做出正确的预案。

为什么?因为代码世界的逻辑是不同开发者码出来的,在实现底层逻辑时可能就埋下了耦合的种子,导致你不知道为什么买菜会触发那么严重的事情。举个例子:

改一个文案导致系统崩溃,原因可能是某处错误兜底逻辑用字面量判断了这个文案,而你把文案改了,这个判断就失效了。

在这种项目环境下生存,每一步修改都要小心翼翼。

解耦:几乎所有业务逻辑都可以解耦

解决办法就是解耦。我们不细说具体怎么解耦——因为每个场景的解耦方式都不同,只需要理解:几乎所有的业务逻辑都可以用解耦的方式做。只要按照这样的大思路去设计系统,不论路径是怎样的,最终都能设计出一个漂亮的系统级方案。

以 BI 系统为例,看似有各种复杂的模块可能相互影响:数据处理、仪表盘搭建、大屏搭建、图表、GIS 地图等。设计之初就要假装其他模块不存在,考虑每个模块必要的输入是哪些:

  • 布局系统:仅仅用于对画布进行布局。为了保证布局系统完全解耦,必须让项目支持在无布局的环境下运行。为此,布局必须真的"只做布局",而不存储当前画布结构——这样布局系统被移除时,不会影响组件的联动,因为组件联动需要利用画布结构 API。
  • 图层列表:可以和布局解耦。图层列表只关心画布的组件树结构,而不关心布局如何实现。画布的组件树结构就像生活中的金钱——大家都可以用它交易,而无需关心它流向了何方、被谁使用。
  • 数据逻辑:与画布结构无关。只需要关心表达式、用户对维度度量的配置、聚合方式以及图表本身的特性进行查询 SQL 拼接即可,唯一用到的通用资源是:当前组件实例信息修改后,需要更新到画布的组件树上。

一定要有一个大家都认可的底层概念

社会也是建立在底层认同上才能如此解耦,所以复杂项目中一定要有一个大家都认可的底层概念,这个概念应该:

  • 尽可能通用化:想想金钱什么都能买,如果只能买蔬菜就麻烦了;
  • 贯穿整个业务逻辑:金钱是现代社会任何交易都必须的媒介。

许多项目被诟病难改,往往就是没有遵循这条逻辑,硬生生把可以不相关的概念耦合了。比如某个筛选器条件变化时,对某个组件做特殊操作——这个场景可以控制反转为:这个组件在接收到某些筛选条件时,自己做特定的操作。

因为对 BI 系统来说,筛选器的输出要作为图表绘图的输入,在这个底层框架下,就不要再开辟一条"筛选器关心到具体图表"的逻辑了。

仓库实践:可视化搭建系列如何落地"解耦"

本仓库的「可视化搭建」系列文章,正是这套解耦思想在 BI 搭建引擎(bi-designer)上的完整实践,可以作为上述原则的源码级佐证:

逻辑层抽象。在 268.如何抽象可视化搭建 中,可视化搭建被分层为:逻辑层(UI 无关,仅关心组件树结构、逻辑功能)、联动协议层、控件层、业务层。逻辑层统一提供组件树结构、组件元信息、递归渲染画布、布局/取数/联动/筛选/校验等拓展能力,业务层只需注册组件、对接定制逻辑。

数据流与 UI 解耦。270.画布与组件元信息数据流 展示了通过createDesigner创建上下文隔离的 Hooks API(useDesigner、selector),让画布、配置面板共用一套数据流;组件元信息通过selector(({ props }) => props.name)响应式取数,UI 通过useDesigner(state => ...)响应组件树变化,实现了业务 UI 与数据流逻辑的彻底解耦。

筛选能力与组件解耦。166.精读《BI 搭建 - 筛选条件》 明确论证了"不存在筛选组件这个概念,任何组件都具有筛选的能力":通过onFilterChange、filterFetch、filterReady、filterScope等声明式配置,把筛选动作从具体组件中解耦出来——输入类组件到展示类组件是基本筛选、展示类到展示类是图表联动、输入类到输入类是筛选联动、组件自身到自身是下钻。目标组件只写一个getFetchParam回调即可响应式处理筛选作用,无需关心是哪些配置导致了关联。

组件值抽象。273.组件值与联动 进一步证明"底层概念"的力量:抽象出唯一的组件值(getValue/setValue),并用valueRelates声明式定义多对多联动关系,任何组件都能参与联动链,甚至可以定义与自己无关的联动关系。

容器不设新类型。272.容器组件设计 则展示了"如无必要勿增实体"的克制:容器组件不是一种新 type,而只是"某个 prop 属性恰好是组件实例",通过children、treeLike 结构、propTypes三种约定自然实现。

这些实践共同印证了原文档的结论:一个大家都认可的底层概念(组件树、组件值、筛选能力),应当尽可能通用化、贯穿整个业务逻辑。当筛选、联动、下钻、布局这些看似不同的模块都收敛到同一套底层概念上时,新增功能不再需要开辟新的耦合路径,维护成本自然可控。

总结

维护好一个复杂项目很难,本文分享了两套实践中可用的方案:

  1. 抱有主人翁心态设计代码:要在设计之初就做好考量(Action 化、单测录制、边界 case 闭环),不要寄希望于对没有好好设计的系统做缝缝补补;
  2. 深入理解现代社会的运作机制,把代码架构映射到社会运作机制上:社会最适合代码借鉴的思路就是解耦,找到一个大家认可、尽可能通用、贯穿业务逻辑的底层概念(如同"金钱"),再利用庞大的分工协作网络完成单人无法完成的工作。

再结合仓库中 212.精读《可维护性思考》 与 254.精读《对前端架构的理解 - 分层与抽象》 的观点:设计模式与设计原则解决的是代码间、模块间的关系,而更上层的可维护性之道,在于给开发者提供"说明书"级别的接口、屏蔽不必要的复杂度,并让每个模块只暴露其所需的最小知识面。无论技术细节如何演进,只要把握住"心态"与"解耦"这两个支点,复杂项目就能在持续迭代中保持较高的研发效率与较低的事故率。

  • 文档
  • 技术博客
  • 教程

【免费下载链接】weekly

前端精读周刊。帮你理解最前沿、实用的技术。

项目地址:https://gitcode.com/GitHub_Trending/we/weekly
点击查看免费下载

相关推荐

上一篇:UMLet终极指南:简单快速的免费UML绘图完整教程
下一篇:GitHub-API认证机制全解析:从Token到JWT的安全实践

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询