arrow_back

前后端分裂

Rei 写于 07 Jan 2018

让我先讲一个真实故事。

有一天,我接到一个业务需求,给某个表单的下拉框增加一个选项。比较特别的是,这是一个前后端分离的项目,分离得非常彻底——维护前端的人甚至不跟我在同一个项目组,前端页面独立开发独立部署,我能够修改的只有 API,并且前端页面不会把内容直接提交到后端,而是经过一层 node.js 中转……

我试图搞明白这个表单怎么工作,发现当初设计这个表单的人非常“聪明”,已经预料到下拉框内容要改动的情况了,所以这个下拉框内容是动态的,根据后端另一个 API 的输出来渲染。那么看起来我只要修改这个 API 的输出,前端就能自动更新下拉框内容。于是我找到前端同事确认是不是能这么改,得到答复是:

“不行,前端这边加了缓存,你如果更改了 API 输出,需要通知我刷新缓存。”

好吧,为了性能考虑,加缓存是非常正确的选择。那么接下来我想先在测试环境修改联调一下,不要影响生产环境。这时候我才发现,测试环境是坏的!于是我又开始修测试环境。联调过程有点像乒乓球,我改一点东西,让前端刷新,然后发个请求测一下,如果有问题,再重复这个流程。

猜猜我完成这个需求花了多少时间?前前后后一共花了……3 天!这里面还有一个原因是前端同事并不是 100% 投入这个项目,有很多时间我都在等待他空出时间处理,而等待时间我也可以处理别的问题。但是,WTF 我居然改一个下拉框用了三天时间,这正常么?

从这故事可以得到的教训有:

  1. 不要把核心业务功能交给项目外的人负责。
  2. 不正确的分离会大大降低开发效率。

这篇博客主要想讲第二点。

分离狂热

现在对前后端分离的追捧已经到了狂热的程度。虽然“现代前端”拥护者会告诉你,什么样的场景用什么样的工具,前端框架是为复杂场景考虑的,并且给你展示一个复杂的、很适合用前端框架的场景。但是当你要编写一个简单应用的时候,这些拥护者这又会告诉你最好选用 Blabla 框架,因为这是市场“主流”,开发者众多,而你的应用总有一天会变得复杂的。

如果只是一昧鼓吹什么是主流和现代还不至于有很多人上当,真正戳中技术管理者痛点的地方是分工。当我们要处理一个很庞大的问题,以至于一个人、一个团队不能掌控的时候,自然会想到将系统拆解为几个模块进行分工。但问题是,前后端分离分错了方向。

人们很容易认为前后端是个不同的领域,因为一个在浏览器执行,一个在服务器执行,它们不是两个东西吗?

这是不对的,前端和后端都属于 Web 应用的一部分,不能脱离对方存在,前端代码需要通过后端发送。对于大多数内容型网站,如官网、电商、视频等等,为了 SEO,需要后端渲染。为了性能,需要后端缓存。为了安全,需要通盘考虑浏览器的安全机制,包括 CSRF、CROS、CSP 等。前后端分离的项目,都会由前端维护一个 node.js 中间层,就是证明了这一点。如果拍脑袋前后端分离,不协调就会出现在开发、测试、部署,功能开发的整个生命周期。

如果认为前后端分离可以增加并行投入的人力,从而加快开发进度,很遗憾,根据我的观察并不能达到这个目的。我曾经目睹一个功能用前后端分离的方案,轻易估算出了 150 人天的工作量,并且这又是合理的。前后端分离的项目一开始就要基于远程调用进行设计,而这是一般软件设计中极力避免的。开发过程中有很多逻辑是重复开发,前端实现一次,后端也实现一次,然后还要考虑远程调用的错误处理。最后,测试联调的时候也会出现大量问题。这个项目如果用传统集成式的方式开发,根据我的经验应该在 50 人天左右的工作量。

最后,还有一种观点是认为开发者是可以轻易替代的,并且职责分得越细,越容易替代。所以前后端分离描绘出一种梦幻情景:前端和后端只处理一部分,任何成员离开都可以轻易补上。事实上,分离后的项目变得更难理解了,前端不知道后端的处理,后端不知道前端的实现。调用关系越简单,层级越少,才会越容易理解。

正确的分割方法是按功能划分,一个人员负责某个功能的整个流程,前至用户如何填写表单,后至数据如何写入数据库和执行后台任务。如果单个项目过于巨大,就把一些业务独立的功能提取成项目。(很容易想到 Microservice,但这又做过头了,具体看 Martin Fowler 的 MonolithFirst,不在这里展开)

前端怪圈

“现代”前端已经成功把自己逼迫到非常窘迫的地步:前端框架层出不穷,曾经大热的 Angular 已经边缘化;为了处理请求不得不引入 Node.js Server,但是很谨慎将自己称为全栈,而是叫做“中间层”,因为前不久才用“全栈就是坏”的口号带歪了不少全栈程序员;为了 SEO 不得不引入服务端渲染,但又不能走传统模版渲染的老路,发展出来同构——把前端框架放到后端渲染,开发难度指数上升。

我已经听到有前端开发说不想碰 Node.js Server 了,他们想专注于处理交互(这也是前端职位的本意)。后端开发一开始很乐意把工作分出去,觉得工作量少了,实际开发才发现一点没少,以前一个变量传递就能完成的事情,现在要写一个 API。需求没完成,没有人会是轻松的。

现在这个境地,与其说是前后端分离,不如说是前后端分裂。大家都觉得不对,但是沟壑已经很深。一旦分开,融合就不是容易的事。

那怎么办……

出路其实每个人都看见过,但有的人选择视而不见,因为这会让人怀疑这几年是不是浪费掉了。

回想一下前后端分离被炒热之前的那个时代,我们是这样做 Web 应用:以服务端渲染为基础,加上少量 JavaScript 交互,对于特别复杂的组件使用前端框架,这就已经满足绝大部分 Web 应用的需求。

这个方案的问题是,不像前后端分离那样有浏览器/服务器这么明显的界限,没有一个简单标准判断哪一部分应该占多少比例,这取决于场景。另一个问题是,优秀的全栈工程师是稀缺品,培养起来需要时间。这就让人犯难了,一不小心就会掉入分离的陷阱。但是,能正确划分系统的人更是稀缺品……

现在是全栈开发备受质疑的日子,分离方案占据了制高点。但是技术潮流就像钟摆,说不定过两年又会摆回来。不要一直追着热点,而是要看清自己的需求。

软件开发没有银弹。