
文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载导读国内互联网公司的项目往往已持续维护五年左右而 IBM 这类老牌公司的项目甚至维护了十五年但不同项目的维护成本却天差地别——有的多年迭代依然高效有的却连改几个文案都会引发线上事故。本文以《前端精读周刊》264.精读《维护好一个复杂项目》 为核心骨架拆解复杂项目难维护的根源并结合本仓库「可视化搭建」系列文章中 BI 搭建引擎的真实设计实践深入讲解主人翁心态与解耦这两套实战方案读完你将掌握如何在设计之初就为复杂系统铺好可维护的底子。复杂项目为什么会越维护越难许多持续五年、甚至十五年的项目维护成本却呈现出截然不同的走势有的项目运作几年后维护成本依然与初创期相差不大可以保持较高效率的迭代速度有的项目则越来越慢研发效率持续下降甚至改几个文案都会导致线上事故。两种结果的差别本质上不在项目规模而在于设计之初是否考虑过可维护性。根据笔者经验持续维护项目变得难以维护的原因主要集中在两个层面对应两套应对方案心态层面开发者是否以主人翁心态对待项目决定了代码的质量上限架构层面系统是否做了解耦决定了修改代码时会不会牵一发而动全身。方案一拥有主人翁心态在设计之初就做好考量为什么流程与 CodeReview 救不了场作为项目管理者一个项目一旦交给某位同学开发就要完全信任这位同学的能力——因为实际上你已经不可能实质性地影响到开发细节了。有人可能觉得好的流程或事后 CodeReview 能发现一些问题但这永远是杯水车薪。以透视表开发为例小张接到任务研发透视表要求这个透视表具有良好的开发体验并做好单测。怎么样做单测才算是有效的如何同时保证开发体验呢——不同人会有完全不同的想法与结果而流程与评审都难以穿透这个黑盒。主人翁小张把隐患录制成单测对于一个有一定经验、又对项目真正上心的小张来说开发过程是这样的先做主要功能考虑数据模型、绘图技术方案后决定采用图形语法方式定义数据结构做了一系列高性能前置考虑后快速做出原型包含表格的渲染、操作、翻页、冻结等功能。直面边界 case随着需求深入做下钻、排序时发现影响到了列冻结功能——代码架构没问题、抽象也良好主要是细节的代码调用漏掉了补上后功能恢复。但小张意识到下次做树状展示结构时可能又会互相影响这始终是隐患于是决定先加单测再继续开发。把操作 Action 化由于出问题的场景有很小部分是大量操作后偶然引发的普通函数式单测无法保证覆盖全面小张决定做一个单测录制功能先把对表格的所有操作Action 化让一套 JSON 可以描述所有用户操作在本地开发界面做一个单测录制功能页面上对表格拖拽操作时实时生成这套用户操作 JSON把当时的页面结构与内部状态记录下来作为对比依据单测就还原这套 JSON 并与基准状态做对比。分层录制单测录制大量原子操作单测表格的各种空数据状态、单行单列渲染、列冻结行冻结组合混合场景列冻结时排序、翻页后进行下钻构造随机复杂组合形成日常容易出问题的特殊 case例如表格单页后突然清空数据再强制冻结第二列再灌入 3 列数据并对第 2 行做排序再取消列冻结并翻到第 4 页。边界 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 APIuseDesigner、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三种约定自然实现。这些实践共同印证了原文档的结论一个大家都认可的底层概念组件树、组件值、筛选能力应当尽可能通用化、贯穿整个业务逻辑。当筛选、联动、下钻、布局这些看似不同的模块都收敛到同一套底层概念上时新增功能不再需要开辟新的耦合路径维护成本自然可控。总结维护好一个复杂项目很难本文分享了两套实践中可用的方案抱有主人翁心态设计代码要在设计之初就做好考量Action 化、单测录制、边界 case 闭环不要寄希望于对没有好好设计的系统做缝缝补补深入理解现代社会的运作机制把代码架构映射到社会运作机制上社会最适合代码借鉴的思路就是解耦找到一个大家认可、尽可能通用、贯穿业务逻辑的底层概念如同金钱再利用庞大的分工协作网络完成单人无法完成的工作。再结合仓库中 212.精读《可维护性思考》 与 254.精读《对前端架构的理解 - 分层与抽象》 的观点设计模式与设计原则解决的是代码间、模块间的关系而更上层的可维护性之道在于给开发者提供说明书级别的接口、屏蔽不必要的复杂度并让每个模块只暴露其所需的最小知识面。无论技术细节如何演进只要把握住心态与解耦这两个支点复杂项目就能在持续迭代中保持较高的研发效率与较低的事故率。赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐前端精读周刊Rest 与 Spread 语法辨析 —— 一个 ... 的两种身份与三个隐蔽坑前端精读周刊Rest 与 Spread 语法辨析 —— 一个 ... 的两种身份与三个隐蔽坑 JavaScript 用同一个符号 ... 同时承载了 Rest文档技术博客教程Claude Code 行为调优指南让 AI 少写冗余代码的 4 条守则Claude Code 行为调优指南让 AI 少写冗余代码的 4 条守则 同一个需求「写个函数算折扣」交给 Claude Code没引入 andrej kaAI 技能提示工程如何实现前端国际化完整指南与最佳实践如何实现前端国际化完整指南与最佳实践 前端国际化i18n是现代 Web 应用不可或缺的功能尤其在全球化背景下让产品支持多语言和多地区文化习惯已成为刚需文档技术博客教程上一篇UMLet终极指南简单快速的免费UML绘图完整教程下一篇GitHub-API认证机制全解析从Token到JWT的安全实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考