Mintnoii

返回

什么是前端架构?#

前端架构是应用程序代码的组织和结构。它描述了系统的模块如何相互交互,定义各个模块的接口,以及如何开发它们。

一个好的前端架构是保持代码库可维护且易于理解的关键。它大大减少了开发新功能、重构旧部件和避免意外错误所需的工作量。

理想的架构是由低耦合和高内聚的模块组成。

  • 耦合性 —— 指一个模块依赖于另一个模块的程度。我们希望具有尽可能低的耦合,这可以使更改或更新系统的某些部分变得更加容易。
  • 内聚性 —— 指模块内的元素协同工作的程度。我们希望模块内具有尽可能高的内聚力,以便所有元素紧密相关并共同努力实现共同目标。

Falling Into The Pit of Success#

这个标题来自《高效能程序员的修炼》(中文版)一书,这个原则翻译起来很奇怪:“掉进成功的陷阱”。但是如果你阅读完原标题的那篇文章之后,你会明白他想表达其实是:好的系统应该让开发变得容易,使得程序员很容易就能做正确的事情。

为什么“容易把事情做对”对项目非常重要:因为维护项目从来不是一个人而是一个团队的事情。

在一个团队中可能因为水平、经验等各种各样的原因导致不同人对框架理解不同,这种不同反馈在代码中就是大家都变得在用自己的方式,与众不同的方式做同一件事。但另一方面“程序”这种工业级的产品,我们要求的是稳定的输出,长期的可维护性。于是让每个人自己去思考问题,去实现一套自己的解决方案,无论对于效率还是质量而言都是有风险。

code review 的目标之一就是在消除这种变异性。一个好的团队的项目的代码库风格看上去因该是一致的,而不是迥异的。

举一个例子: Redux 框架,我不认为它是一个好的框架,问题不是出在技术方面,而是因为想让一个团队维护好一个 Redux 项目很难。假设 Redux 应用最好的状态是 10 分的话,那么代码层面的因素只能让你达到 5 分,另外的 5 分需要你学习所有代码以外的文档种的设计规范和模式来达成。这对个人不是问题,但是想让团队种的每一个人都把这些吃透并处于同一水平太难,这就是难以轻易的做对的事情。

我们需要类似于 Typescript 或者 tslint 这类东西尽可能的移除 “人” 这样的干扰因素,通过硬技术手段保证项目的稳定产出。

当然,技术上的约束无论如何还是有限,就像你永远无法叫醒一个装睡的人一样,你也永远无法阻止一个想破坏项目的人,不管他是无意还是无知造成的。找到,或者培养出优秀的人比一切都重要。

不要过度设计 — 活在当下#

“过度设计”的问题在于你以为你预测到了未来,但其实你并没有。 你只是在你的视野范围里一厢情愿的相信某件事情可能会发生,但还有千万种可能你没有看到。但现阶段的代码和精力其实无法涵盖这所有的可能性。

那么当我们希望前瞻性的解决问题时,我们究竟应该看多远?

  1. 不要尝试去预测未来,不要一直想“万一以后用上了呢?”
  2. 唯一不变的就是变化本身,只需要通过一些设计原则来保证目前的设计有能力应对未来的变化即可

第二点听起来有点像是个悖论,但其实不然。我们设计系统的目标就是让系统拥有足够的灵活性来应对变化。这种架构你也可以称之为演进式架构或者是持续性架构。在《演进式架构》一书中详细叙述了用来实施这样的架构的技巧和方法。

“过度设计”还会增加系统的复杂性,给项目的维护带来困恼。由此我们过渡到下一个原则:“简单和清晰”

简洁也并不可耻#

程序员天生有一种把简单问题搞复杂的能力:

  • “单用 React 解决问题怎么能体现出我的水平? Redux 全家桶走起”
  • “我们最好能做一个工具或者平台来解决这个问题,然后把平台推广到整个公司”
  • “最近 X 技术很火?最好能在项目里用一用,将来好写在我简历上”

“简历驱动开发”和“造轮子晋升”很常见,也可以理解。 但是,如果可以的话,还是请站在项目和团队的角度上考虑如何满足项目的非功能需求: 我们应该尽可能的降低项目维护和学习的门槛,降低它们的风险,而不是反向提高它们。

所谓非功能需求就是我们老生常谈的可拓展性,可维护性,可测试性等… 之所以叫非功能性需求的是因为它们和我们的业务需求没有任何关系。

  • 假说你确实只能通过复杂,理解成本较高的方式实现需求,那么退而求其次,至少应该把复杂性和风险控制在一定的范围内。
  • 如果你没法保证你的程序满足 SOLID 原则,没有套用各种模式和最佳实践,那么请至少保证系统的简洁和清晰。这依然可以提高代码的可维护性。比如把你的单个组件保持在 300 行以内。

越简洁的代码维护起来就越轻松这一点是毋庸置疑的。 你觉得是在一个一千行的面向过程有三层循环的函数内调试 bug 容易,还是在一个同样一千行但是拆分为五个每个文件不超过200行的组建代码内调试 bug 容易?这里的简洁是全方位的,小到变量的命名,函数的封装;大到框架的复杂程度,学习曲线。

参考资料: