使用两个披萨原则提升团队水平

亚马逊CEO贝索斯曾经提出过一个很有名理论,被称作“两个披萨原则”,他认为如果两个披萨不足以喂饱一个项目团队,那么这个团队可能就显得太大了。因为人数过多的项目会议将不利于决策的形成,而让一个小团队在一起做项目、开会讨论,则更有利于达成共识,并能够有效促进企业内部的创新。

这个原则在系统研发团队中同样适用。因为较小的团队更加的自给自足,通常没有太多的依赖性,能有更好的沟通和更多的注意力去完成事情。而一旦出现依赖性,它们就像缠在一起的绳子一样,如果只是一两天绳子,那解开还不算太难,但如果是1000条绳子,梳理起来就非常难了。所以,消除依赖关系,可以让团队运转得更快。鼓励团队自主,往往就能够产生创造性的东西。

“奇妙清单”的CTO查德·福勒(Chad Fowler)一直在推广“消除依赖”的概念。他提倡,凡事都是为了避免相互依赖而写的,而且要刻意避免共享或重用代码。根据福勒的说法,如果一段代码已经存在很长时间了,而且改起来不非常麻烦,那么我们要考虑一下重构,因为这段代码成了一个风险点。

故意重构代码而不保留和重用确实是一种激进的做法。通常,大部分人会觉得重用是一件好事,有些企业甚至在内部组件了一支团队专门开发可重用的组件。但有些组件可能会让人琢磨不透而害怕用,这些组件就像是刻意地往库里放的,并要求每个人都要依赖它。

而按照福勒的想法,我们应该防止引入任何不理解的或无法理解的代码片段。如果有些代码已经让你感到不放心了的话,那么应该重写或者删除掉。这种做法会引起代码库的变动,但是也可能会让我们的代码更加健壮,因为变化+选择=改进。


依赖关系在项目中以各种方式存在

连锁反应

在提供者-消费者这种典型的IT结构中,通常存在着跨系统的依赖关系。比如,零售商的销售点系统(提供者)就是一个很好的例子,该系统将其输入到总帐系统(消费者)中。这两个系统之间需要按照协议进行数据交互,这种交互可以按照某种消息格式或者文件格式来进行。如果你在其中一个系统里做了修改,那么另一个系统也需要跟修改。

如果将其扩展到整个企业和那些相互依存的系统,则会产生连锁反应。比如,改动系统A中某些看似简单的功能,可能需要跟着改动没有关联的系统B,而系统B可能位于不同的管理链,由不同的团队维护,但会因为接口调用的关系而受到影响。

我们可能听到过这句话“只是在页面上加一个按钮,有那么困难吗?”。是的,为了能够在页面加上这个按钮,我们

必须把公司所有的后端系统都改一遍。

依赖的危害

随着时间的推移,我们可能建立了一个包含数十亿行代码的开源生态系统,所有这些代码都能免费使用。这个听起来很振奋人心。但是,这就是问题所在,你重用一段代码时,通常要引入一个库;当你重用一个库时,有时候可能要再引入几个库,然后再不断的引入其他库。

这种情况是大多数开发人员都会经历的。往往我们只需要使用开源库里的一个功能,最终却不知不觉得依赖了几十个Jar文件。这些文件中的任何一个修改都会导致你的应用程序出现bug。这就是依赖的危害。

另外一个依赖发生在运行时的组件之间。比如,如果一个组件调用了另一个组件,那么他们必须同时运行,并保持可用。如果是组件A和B共享一套协议,当组件A需要修改协议时,必须考虑组件B的修改,是否会引起依赖组件B的应用程序异常,会的话则不能修改。

那些维护公共库的团队,我们先简称为“基础团队”。已经组建基础团队的企业,在执行一段时间之后可能会拆分出数据字典团队或企业架构团队,尝试着搭建公共框架或者公共组件。但是这种做法恰恰是“退步的”。不断增加的依赖反而会拖慢开发速度和修改的灵活性,导致应用不够稳定,而不是减少代码量和工作量。

人与团队的依赖关系

如果我在一个开发团队,我可以自由的做着自己的工作,当工作完成后,我需要把结果交给不同的团队进行评审。比如,交给测试团队检查bug,或交给DBA团队检查数据库的修改是否合理。这就是我们个人和团队之间的依赖关系。

找DBA团队评审主要是因为开发人员在数据库领域的专业技能相对较弱。如果某些数据库表设计得不好可能会引起性能问题,全库异常,并破坏其他应用程序。当前有部分系统使用了微服务,主要是每个微服务通常会有自己的数据库,这样能够限制数据结构的调整只影响当前的服务,而不会影响所有使用了该库的应用程序。

人与团队的依赖关系也会引起时间安排和工作优先级的问题。每当有人说“我在等某某完成后功能,我这边才能开始写代码”,其实就说明了他跟团队存在着依赖关系,这样他的开发速度就会慢下来。如果你在写代码之前需要DBA在协助更改,则意味着你必须等到DBA完成其他任务后才来处理你的需求。那么你在DBA任务清单上的优先级就决定了DBA完成你的等待时间,然后到了另外一个评审流程,你又不得不按着另一个团队的任务清单继续等待了。

安全使团队规模自治

国外有个团队一直在倡导减少系统之间、个人和团队之间的依赖关系,他们列举了一些方法:

  • 致力于安全技术,使开发团队可以独立工作,而不会影响其他团队
  • 尽可能使用高内聚,低耦合的架构
  • 通过自动扩展构建云原生应用程序
  • 被用到的代码片段要保持强一致性
  • 上下文和元数据的边界和定义要准确
  • 可以隔离对架构中的异常部分

在编写代码的时候,我们要尝试避免对大量的库依赖。如果你认为写代码很麻烦,那么你潜意识里就想要重用了。但是你如果一味的追求重用,不想写重复地写业务逻辑,那你的代码里将出现很多的依赖关系。假设你需要一个之前写过的功能,如果只是一个函数,那其他可以不用引入整个Jar文件,只要把这个函数拷贝过来就可以了。

很多时候,大部分的功能都可以是一个函数。因此,我们没有必要把基类,工具类和整个框架都包含进来。这样,当我们管理应用程序的依赖关系时会更有把握,也不容易受到上游更改的影响。

“安全”还意味着一个团队调整代码当不会影响到其他团队,这需要仔细考虑协议和数据格式。如果你能使用元数据描述协议,那么你也可以找到某个逻辑来确保你所做的改动是安全的,比如:

  • 永远不需要以前不需要的东西
  • 永远不要拒绝以前接受过的事情
  • 现在的回报永远不少于以前的回报

这些就像Postel法则。

安全技术带给您更大的杠杆作用

消除系统之间,组件之间和团队之间的依赖性需要安全的技术和有力的工具。我们必须不断地解决这些依赖关系,以依赖关系引起的各种问题。在对架构的认知上,我们需要有一个新的认识,就是依赖数据,并以纯功能的风格进行系统开发,这样才能让团队的开发效率更高,同时要鼓励自主开发并推动创新。