为何项目做到最后都成为“屎山”,代码无法改动

4,473 阅读10分钟

开头先来个小故事:张三是一个程序员,在金九银十的季节去了一家新公司就职。新的公司看起来前景不错,做的产品也属于行业前沿,张三发誓自己要在此一展鸿图,用自己精湛的开发技术为公司的上市添砖加瓦。

张三就职的第一天,公司就开放了代码的权限,他熟悉起公司的核心项目。结果却惊呆了,张三发现公司的项目就像是一个垃圾场,各种代码堆成山,而他要做的事情,就是去维护这些老项目,在那一刻,张三内心的幻想瞬间破灭。

但在维护了一段时间项目之后,事情出现了转机。有一天,新项目下来,老板任命张三为负责人,他内心狂喜,终于可以证明自己了。

"我一定要让代码显得优雅,跟烂代码说再见。"

刚开始,张三用了各种设计模式,疯狂抽象业务,越做越有劲,感觉一身的才学终于有了展示的机会。但项目是多人合作的,随着时间的流逝,和项目的不断迭代,很多新人不再按照预定的规范来开发代码。

为了方便,微服务如雨后春笋般一个个往外面冒,开发人员在代码里填充了一个又一个if语句。于是,张三开始手动地限制那些不符合规范的代码提交。可是项目时间的压力越来越大,张三最终迫于交付压力又放开了限制。虽然项目顺利的交付完成,但是奇怪的代码却永久的留在了项目中。

慢慢的,原始精美的设计开始变得越来越臃肿,逻辑变得复杂无比。没有人敢去重构,也不可能重构了。终于,张三实在无法忍受,辞职去了一家新公司就职,新的公司看起来前景不错,做的内容也是行业前沿的产品,张三再次发誓自己要在新公司一展鸿图,用自己精湛的开发技术为公司的上市添砖加瓦...

这样的故事在互联网行业内一遍一遍地上演,似乎任何项目随着时间演进,一个个补丁打到最后都会变成“屎山型”项目,充斥着各种历史遗留问题,新功能开发越来越慢,最终无法演进走向项目的终结。有些互联网公司会为延长项目的生命周期,在某个节点聘请资深的软件架构师,他们往往熟读《重构》,用各种各样的技术手段去延缓这个过程。

产品架构是啥

像软件架构一样,产品设计也是有架构的。

在做项目的时候,我们觉得从技术上,所有的问题都能得到解决,但是整个项目却在不知不觉间一步步的走向“屎山型”项目。按照研发的思维,问题的根源是抽象还不够,技术水平不行,需要更强大的架构师来重构。

聘请了资深的软件架构师后,一开始项目似乎变得好了一些,但最终结果并没有发生改变,只是延缓了而已,到了某个阶段也只能抛弃,重新开始。

复盘后会发现,我们的主要精力都在解决技术上的问题,而忽略了产品层面。按照网上的一种说法,产品架构就是在充分理解产品用户需求基础上对产品数据流转的逻辑梳理。

而我对产品架构的理解是从**"理念世界"**开始的。编程是用计算机能够理解的语言来描述现实事物,计算机能够理解的语言是什么?是逻辑。在编程的时候,研发人员需要在脑海中构思出事件的全貌,并且用逻辑的语言将它描述出来。

在西方的哲学里面有一个"理念世界"的说法,大概意思是人们做的任何事情,都是在脑海中预先构建出事物的本体,再到现实中对预先构建的内容进行模仿。这与我们编程的过程很相似,那么产品是否也遵循这个过程?

回想起以前做项目时的思考方式,发现确实如此,我们或多或少都需要在脑海中构建出产品的运行轨迹。但是在做项目的时候,作为研发的我们都在想如何用技术解决问题,却忽略了更重要的东西,任何产品都是为了解决客户的问题而存在,解决客户的问题是根本,无论用什么手段......

如何设计产品架构

在设计之前,先解决人的问题,什么样的人适合做产品架构?

产品架构就是在充分理解产品用户需求基础上对产品数据流转的逻辑梳理

所以这个人需要具备以下素质:

  • 能够调研并理解用户需求的能力
  • 能够知道产品的核心价值
  • 能够理解产品运行原理
  • 面向对象设计思维

有了这些素质,就可以开始构建"理念世界"了。

构建理念世界

构建"理念世界"其实就是将现实中将要发生的事情,用面向对象设计思维将事情转换成纯粹的逻辑描述,这个过程叫抽象

举个例子,如果我们需要做一个流量治理类型的产品,抽象过程大概是这样的:流量治理基本分为2个大的内容,流量监控和流量管理,这是核心价值。

而传统的流量治理工具基本都是采用SDK的方式实现,它们普遍具有以下的问题:

  • 侵入性强
  • 治理功能不全
  • 内容多、门槛高
  • 中间件演变困难
  • 版本碎片化严重
  • 升级成本高

如果能解决此类型的问题,将会使得服务网格在流量治理解决方案上会优于SDK的方式,这是比较优势。

有了上面的分析,可以得出:如果希望产品可以快速升级且侵入性弱,势必不能与客户的代码进行强绑定,需要一种与语言无关,又能够做到流量监控,流量管理的手段,只能去操作系统上面找。

如果能操纵操作系统上的某些功能,使得流量在进入服务之前,先进入编写的程序中,就做到了语言无关。

思路大概是这样:一个请求进来,先被操作系统转发到编写的程序中,然后我们自己编写的程序会将数据上报到某个东西保存起来,最后程序转发到真正的服务中,流量监控就做完了。同时,还需要一个用于存储上报数据的东西,它需要具备持久化的能力。

在这个流程当中,使用了一种设计模式,叫sidecar模式,就用sidecar称呼我们自己编写的程序好了,存储的那个就叫数据库。

接下来推导一下流量控制的逻辑,要做到流量控制,需要用到sidecar,毕竟流量已经被操作系统转发到了sidecar上面,我们可以做过处理之后再转发到真正的服务当中。那么就需要一个另外的东西,它来告诉sidecar要怎么对流量进行处理,毕竟每个sidecar需要处理的东西是不同的,而且策略内容需要持久化,不然找不到与sidecar的对应关系,这个东西叫做控制端。

所以流程大概变成:控制端配置流量控制策略,控制端将策略下发到sidecar上,一个流量请求过来,被操纵系统转发到sidecar,sidecar先上报流量数据到数据库,然后根据流量策略对流量进行处理,最后将请求转发到真正的服务中。

在完全理想的状态下,通过这样一个流程,基本实现了流量治理的核心能力,并且这个流程相比SDK的方式更具有优势,这就是所谓"理念世界"的构建过程,纯粹的逻辑描述。

验证理念世界

"理念世界"是否正确的唯一衡量标准是它是否完全正确的描述了现实,就像面向对象思维里面的类和对象的关系,"理念世界"是对现实的抽象。

要想知道抽象的好不好,有一个非常有效的办法。随便找几个人,把你的逻辑说一遍,如果表述的过程是顺畅的,对方能够理解并且没有感觉到逻辑上的遗漏,这个抽象就是比较好的。

在做项目的时候,可以把这个过程尽量的描述给不同岗位的人,一边加深团队成员对产品的理解,一边又通过不断的表述来完善这个流程没有考虑完善的内容。

如何落地

当"理念世界"被建立,就需要在现实中对它进行模仿,在软件工程学当中,这一步就要开始脱离设计,到实操的阶段。一般项目分2类,一类是从零开始,一类是存量项目。

从零开始的项目

从零开始的项目在动手之前,一般会面临2个问题,从哪开始?要模仿到什么程度?

通过流程可以发现以下是一切的基石。

1.操纵操作系统上的某些功能,使得流量在进入服务之前,先进入我们编写的sidecar中

2.sidecar

3.数据库

所以在没有外部特殊情况干扰的时候流量监控功能肯定要优先于流量控制功能做,这是从哪开始。

至于做到什么程度主要依托于时间,人员的关系,看实际情况把握。但无论做多少,只要方向是对的,对整个 产品的迭代都是具有正向的意义。

通过这张图,服务划分变得很清楚,在交给研发时,每个服务的边界也有明确的规定。就算研发人员出现新入职或者水平良莠不齐的现象,在这个框架内所能造成的破坏力也有限,不会对整个产品造成结构性的问题,在未来的某个时候只需要找一个足够强的研发重新写一遍,问题就都解决了。

存量项目

存量项目的落地就十分的复杂,历史遗留害人不浅......

大的思路应该是先用"抽象"映照现实,看看到底现在的代码是否完全符合"抽象"的"流程",先搞清楚现状再想办法修枝剪叶。

如果有新的需求到来时,还是要先抽象,任何正当的需求都是符合逻辑的,如果抽象出来不符合逻辑,那肯定是需求挖掘的不够,不能盲目的加代码。

还是那个例子,如果需要添加一个对sidecar的监控,那流程就会变成这样。

但是监控单独拎出来是否有必要?如果没有必要的话流程可以变成这样。

这2种形态没有优劣之分,可以结合具体情况做选择。

总结

如果有了解到服务网格领域的朋友应该很早就发现,上诉说的例子就是一个弱鸡版Istio的诞生。

我尝试着从需求出发,纯粹的逻辑推导Istio的架构。很多有名的开源项目为什么历经这么多年,反而越来越精炼,这里面一定有值得探讨的地方。

本文只是提供一种解决问题的方法,解决问题的核心还得看人,一群大佬,无论用什么方法做出来的东西都精美而优雅。