成为会带团队的技术人 - 前阿里本地生活研发总监 - 拉勾教育

上一讲我们以架构之名聊了一下理解业务这件事儿,这一讲我想进一步来聊一聊日常工作中架构工作的核心关注点是什么?

我是在接触分布式开发之后,才对 “架构” 有了概念,从三高(高可用、高性能、高可扩展)到 DevOps(集群、网关、复杂均衡等),从系统的功能模块设计到微服务的业务建模、领域设计。很长的时间里,架构好似包罗万象,可以装进与技术相关的所有内容,给人的感觉就是想做好架构就要无所不精无所不通。

但在真实的工作场景中,你不可能擅长所有技术,而且由于分工问题和精力有限,我们也不会所有事情一手抓,因为容易一地鸡毛,错失重点。

那围绕架构你的工作重点是什么呢?在我看来,就是系统复杂度的治理。因为像功能实现、性能优化、稳定性等方面存在共识标准,其他角色(如架构师、运维专家)会一起补位。但系统复杂度就有点儿如人饮水,冷暖自知的感觉,可能只有你自己,才会对此有足够的认识和把握。而足够清晰、健壮可以支撑业务发展的系统才有机会谈架构演进,所以今天这一讲,我想从系统复杂度治理以及演进两个维度来聊聊架构。

治理好系统复杂度才最务实

C.A.R. Hoare 曾说过:“软件设计有两种风格,一种是将软件设计得很复杂,以使其缺陷没那么明显;一种是把软件设计得很简单,以使其没有明显的缺陷”。

我非常喜欢这句话,可能是这几年见多了 “过于复杂” 的系统,我对简单和清晰异常执着。在我看来,业务复杂所以设计出复杂的系统不是本事,业务复杂,而你依旧能通过足够好的抽象与分层将系统做得简单才是本事。

不过在日常工作中,你可能没有机会从零去设计一个新系统,很多时候都是从接手历史系统,处理一个个问题开始的,比如系统某个接口响应变慢,需要解决性能问题。在解决问题的过程中,你会逐渐思考问题产生的原因、解决方案,这时你会发现这些问题是之前一些设计和迭代时导致的。很多问题源于在系统的结构性设计上就没有做好,比如服务的拆分、数据库的冗余、调用的链路或接口范式。

而它们在系统后期迭代中往往只能改善、难以根治,日积月累之后,我们只好重构系统。其实,随着业务与人员的发展和变化,你几乎无法避免系统重构的问题,“系统腐化——重构系统”的趋势近乎必然,背后的原因在于:日常系统的迭代以满足业务需求为主,其他精力都用来解决类似高性能、高可用这类明显影响系统 “生存” 的问题,而对高可扩展(延长系统的生命周期)的投入会比较少,因为系统的高可扩展是一个相对隐蔽的问题。有趣的是产品、运营乃至业务对技术在 “可扩展” 上的需求极为清晰且强烈,他们期望需求快点实现,并不关心实现得是否优雅、性能是否足够。

就算抛除产品和运营等诉求,从系统架构的角度来说,技术价值在商业环境(业务方)中最核心的体现就是让系统实现 “可持续的快速交付”。而在这方面表现好的系统也都有一些明显的特征:系统的结构清晰、即使整体繁杂但是每个局部都相对简单、链路干脆直接,没有不必要的冗余。 所以,我认为日常开发中,对架构最核心的关注点应该是复杂度的治理,如何在满足需求的前提下,尽可能地降低系统复杂度,保持系统灵活就是你应该关注的事情。

衡量复杂度

在实际工作中,我们虽然能直观感受到系统复杂与否,但却很难找到类似 QPS、响应时间等数据指标去准确地衡量它。所以很多时候技术 Leader 在衡量系统复杂度时,除了凭借主观感受外,还要参考系统和团队的一些表现特征(这些表现与技术债务有很多类似之处,毕竟产生技术债务也是系统复杂度的一种体现)。

  • 理解成本高:需要很长时间才能理解系统模块的组成及运作,比如新同学加入或系统交接时,老同学很难讲完整、新同学不容易听明白,要几周甚至 1~2 个月才能完全了解系统的实现和运作机理。
  • 变更牵连多:哪怕是实现一个小的需求都要改造系统的多个部分、甚至多个系统(上下游等),有的还需要协调其他团队或部门,结果导致迭代成本高,并可能引入更高的风险。
  • 一张图装不下:即你无法在一块白板上清晰且完整地画出系统主要功能场景的架构图,可能是牵连的系统、服务、组件过多或者链路设计不合理导致的。
  • 加人无法解决问题:即便你增加人员也难提高系统的交付速度和产出质量,比如原本 3 个人负责系统,增加到 6 个人的交付产出可能和 3 个人时所差无几,原因在于复杂度过高并且系统结构模糊,很难通过清晰的分工让生产力最大化。

而你可以结合这 4 点表现特征以及自己的主观感受进一步判断系统的复杂度是否过高,如果系统复杂度过高,可能带来一系列问题:迭代压力大、经常延期、稳定性问题频发等。这时,你要着手治理复杂度,尽力不让问题扩大到难以解决只能重做系统的程度。

复杂度治理的思路

“复杂度的治理”这句话很宽泛,因为不同的系统和团队面临不同的环境上下文,很难一招鲜吃遍天。而且在现实业务场景中,系统发展一段时间后,往往要对抗业务发展的不确定性。所以不论是考虑不周到,还是为了赶时间的妥协,在架构上总是不完美的。这也是为什么你在公司很难找到 “干净” 的系统。

但你要注意,不要把 “简单清晰” 理解为 “干净完美”,复杂度治理并不是一个污水净化的过程。而是在限定 ROI 和考虑到业务不确定的情况下,通过架构的设计与实现,追求系统的可控与可持续。我非常认可“高内聚、低耦合” 架构理念,并认为在复杂度治理的思路上应遵照这一原则,对系统做简化和分治。

简化就是去掉不必要的复杂,让设计与实现保持简单。比如某个业务场景在系统上可能 A 调用 B 就够了,结果因为某些原因(团队职责、旧系统设计等),一定要 A 调用 C 再调用 B,甚至 C 里面还存在不必要的冗余。这种画蛇添足的设计与实现,就是让系统逐渐复杂化,类似的情形在技术组件使用、系统链路设计、数据库设计和接口实现上不胜枚举。

分治则是将原本难解决的问题,拆分到可解决的粒度,然后再逐一击破。当然,分治不是单纯的 “拆”,而是注重并管理拆分后,不同部分的关联关系。很多系统从早期单体应用过渡到分布式系统时,就是一个长期拆分的过程,所以你会觉得架构一直在拆分,并通过拆分解决了问题。如果说早前在软件设计领域,大家都说:“没有什么问题是加一个中间层解决不了的,如果有就再加一层”。演变到分布式和微服务时代,就变成:“没有什么问题是拆模块解决不了的,如果有就继续拆”。

这种说法有一定的道理,因为业务发展,必然会导致系统复杂度增加,而适当的拆分可以把复杂度均摊到多个部分,让人均维护的系统复杂度降低。但你要注意,复杂度在系统中只是被转移和分散了,并没有消失(消灭),你终究还是要处理那些拆分出来的复杂度,所以如果盲目地拆分,可能只解决眼下的问题,却在未来得到更糟的结果(比如到时候已经拆无可拆)。那具体怎么拆分呢?

常见的拆分方式是垂直拆分和水平分层: 垂直拆分把差异明确可以独立迭代的业务拆分开;水平分层把共性的能力下沉隔离。比如电商场景中,购物车和订单可以分成两个服务,它们虽然在业务流程上前后关联,但是各自具备独立完整的业务场景和生命周期,商品加入购物车未必会交易生成订单,可以各自独立存在;而库存和商品则是强依赖的关系,库存无法独立于商品存在。

但是拆分的原则并非一成不变,会随着业务和系统的发展而变化。比如评价一般是用户评价商品,所以早期的系统中,评价可能是耦合在商品模块内的。等到被评价的内容越来越多、评价的作用越来越大(用户画像、商品推荐度),可能会将评价作为一个垂直的系统独立出来。之后随着不同的业务展开,可能会出现多个独立的评价系统。它们的业务在早期会存在一定的差异,后面为了避免重复造轮子和打通数据,这些评价系统又会被合并成一个功能更强大的评价系统,这就是一个动态变化的过程。

所以拆分与合并不绝对,过度地拆分会导致系统无法高内聚,零散分离的系统,会增加稳定性风险和治理与迭代的代价,并且造成大量的协作成本。Linus 也曾说过:把复杂系统拆分成模块,似乎没有降低整个系统的复杂度,它降低的只是子系统的复杂度。而整个系统的复杂度,反而会由于拆分后的模块之间,不得不进行交互,变得更加复杂。

复杂度治理实践

复杂度治理的思路更多是帮你如何理解和思考,除此之外在实际工作中我们还需要一些更具体的行动,要重视代码的实现和每一次需求的迭代,“千里之堤溃于蚁穴”,在这里我分享几点简单的实践。

  • 相比 coding 更重视设计:与其花时间去 review 代码,不如用更多时间思考如何设计并实现某一个功能。“重视设计” 这点听起来简单到让人忽视,但据我观察,如果一个团队重视设计,那么其负责的系统肯定也不会差。我的第一任 Leader 和我强调过,写代码之前要先用笔和纸把编码思路写下来,捋清楚了再干。这一点我延续至今,我也建议你将这样的习惯推广下去(未必是笔和纸的形式)。
  • 永远做 2 套以上的方案:很多时候可行方案未必是最优方案,局部最优未必是全局最优,多几套方案不光能促使你多思考利与弊,也能让你对未来多一些考量。系统变得复杂,与我们选择最容易的实现方案有关,这就容易出现 “为了实现某个需求,只能对系统做复杂改造” 的情况,所以不要习惯于选择最容易的实现方案,也不要满足能 Work 就好。
  • 从 MVP 的视角考虑设计:从 MVP (最小完整业务的角度)去考虑系统要如何设计与实现,先做减法再做加法,比如有些设计去掉之后,系统也可以很好地实现,说明这些设计不必要,因为架构本身也需要 “断舍离”。
  • 关注上下游的实现:站得高才能看得远,重视系统之间的相互作用。从实操的角度来说,上下游的系统就是你负责系统的背景环境,了解它们的实现不仅会帮你进一步了解业务,也会让你避免出现更多的问题(比如 API 的共识契约、上下游的超时、降级等)。
  • 坚持 “日拱一卒”:尽可能在每次迭代中修复之前的问题,逐渐完善系统的实现。如果日常不能逐渐完善系统那么即使经过重写或重构,也只是短时间内 “刷新” 了系统状态,之后系统依然会出现各种问题,不会有根本性的改变,所以,不要把复杂度的治理当作一次性动作,它应该成为长久的习惯。

总的来说,架构层面的复杂度治理是为了让系统更简单、更轻便,可以更灵活地跟上业务的变化。而这是一个两手都要抓的过程:先满足业务,然后从技术的视角去前瞻业务,通过架构的演进让技术走在业务前面,为业务赋能。那么有意思的问题来了:很多人在说中台是系统演进的终态,那么从系统演进的角度来说,你是不是一定要把系统做成中台呢?

没必要一定把系统做成中台

最近两年,我在本地生活负责技术中的业务中台,最初组建时我的团队就叫 “中台研发部”,也与很多公司内外的同学交流过中台,其中很多同学都会问到类似的问题:“不做中台是不是就跟不上时代了?我们打算这样做中台你觉得怎么样?”。先抛出我的观点:没必要一定把系统做成中台,不做中台就会落后更是无稽之谈,不过,你可以借鉴中台的思路作为系统设计与演进上的形态参考。

07 | 架构设计:治理好系统复杂度才最务实 - 图1

我曾经看过逍遥子的一个访谈,他说:“第一天就想做平台的人,基本没有做成的”。这句话我深有感触,系统的演进本质上是公司业务发展在技术上的映射,适合永远比最好要重要。比如饿了么早期不管系统还是业务,都是 ALL IN ONE 的,在快速发展的过程中,业务逐渐形成更多形态,技术也以 DDD 的思路拆分并逐渐微服务化,过程中大量的业务模块拆分为独立系统和基础中间件的分层下沉。伴随着业务发展同时也推动了技术演进,对应的多活、中台、全面云也就自然地落地了。

而你的系统是否适合往中台的结构上演化,可以从下面三个方面,结合你的实际情况去考虑。

是否有土壤基础

宏观上看,中台优先侧重在组织和分工的重定义上,优先解决生产关系的问题,所以会有 “小前台,大中台” 的说法。可以说,新组织结构的设计是中台落地的土壤和前提,技术团队想要做好中台,单纯靠理念与共识是不够的,往往要通过组织自上而下地推进,能自下而上实现变革的公司少之又少。

很多同学看过 SuperCell 的中台奇迹后觉得热血沸腾,不过当你看到 SuperCell 的公司文化和工作机制后,才能知道它创造奇迹的根因,比如庆祝失败、最大程度授权(CEO 倒金字塔)、追求最好玩(而非最赚钱)。从 SuperCell 的宣传中可以看到,他们追求小而精的业务开发团队(前台)与提供强大工具与服务的中台。前者追求麻雀虽小五脏俱全,小到足够灵活并且沟通成本最低,后者追求的是足够强,而非足够广(并不是什么都统一做到中台)。

所以如果你的公司(大部门)没有中台的战略方向或者对应的组织落地动作,那么单纯的技术中台是非常难落地的。

清楚中台并不完美

过去两年我可以说既是中台的打造者(饿了么)也是中台的使用者(阿里),不管从哪个角度来说我都不认为中台已经是一个完善到可以开箱即用的解决方案,甚至如果你仔细观察会发现大部分说中台很好、很赞的往往是中台的打造者,很少有中台的使用者站出来说点什么。听起来中台只有好处没有坏处,这明显不正常,中台在落地过程中存在很多困难,我分享给你 3 个深刻感受。

  • 中台离业务较远

如果说技术中间件的底层复用能力可以解决系统 10% ~20% 的问题,那么中台要解决的问题其实已经进入了业务的深水区。可做中台的同学远离一线业务,想对业务做足够的抽象和沉淀非常困难,所以中台的抽象会有一定的滞后性,对业务快速迭代和开发起到的作用有限。

拿阿里来说,中台最早要解决的也不是业务快速支持的问题,而是不同业务的打通问题,只不过通过中台打通业务之后,中台本身也会变成一个单点瓶颈,然后才考虑如何解决瓶颈带来的开发效率和对新业务的快速支持与赋能的问题。很多时候最初要解决的问题,决定了出发点和终点的方向,所以如果考虑的是能力复用的需求,那么中台更适合沉淀已有的能力,对于全新业务如果和之前沉淀的业务关联不大,中台支持并不好(中台也不是万能的)。

  • 中台资源有限

通俗点儿说,中台的团队也是有 KPI 的,从服务业务的结果看,肯定会优先支持大业务和关键业务,所以如果老板不指定小业务或创新业务,它们很难得到足够的资源支持,即使得到支持,也会因为中台要进行很多定制开发,使协作周期变长,参与人员变多。所以,很可能你本来的开发成本转移到中台一部分,但协作成本上升得更多,整体的效果与周期并没有很理想。

  • 中台的灵活度有限

中台本身对业务的抽象和开放定制是有限的,比如订单中台的状态机有不同的开放程度,如果任一状态都可以配置编排,就会因为太灵活,使相关联的业务能力做得很碎才能与之配合。所以中台更多是有限开放,比如支付是固定的不能跳过,但是交付可以是快递交付、也可以是到店交付等方式。但是不管如何,都要在中台定义的框架内去设计,这就导致有些业务其实是无法 100% 适配的,很多时候会有各种小问题。

演进思路可以先行

前面提到中台存在一些问题,但我并不是完全否定中台,中台体现了很强的技术属性(抽象、沉淀、复用、打通),可以说是进一步把技术上的这些追求延展到了业务上,当企业体量到一定程度总是利大于弊的,如果说原来我们抽象沉淀的是技术组件,那么中台抽象沉淀的就是业务组件。但你也要认识到企业数字化的需求和场景千差万别,很难有一招鲜吃遍天。做业务关注差异,做系统关注共性,能不能做出技术中台很大程度上受业务形态、阶段和组织结构的影响。

如果环境和业务发展没到那个阶段,我认为大张旗鼓做中台的 ROI 比较低,技术人员很难准确预测业务的形态。实际一点的做法是: 借鉴中台的系统演化思路,对业务的实现多做抽象和共性的沉淀,尽可能保持系统灵活同时多支撑相似的业务形态。比如做订单就考虑如何支持多种类商品或营销、做支付就考虑除了微信与支付宝未来要支持银行卡怎么办,每次做设计时都假想下如果又多一个业务形态你怎么支持。

于你而言未必公司从上至下定一个叫 “中台” 的战略之后,再大刀阔斧地去做中台,完全可以先从一个很小的系统下手。另外,不要借着做中台的契机,不管什么业务、什么系统都往所谓的中台里合并,如果一个中台系统又大、又厚重、响应又慢,我真的找不出它有什么存在的价值。想清楚你要的是什么效果,如果你追求的是一个足够灵活的系统,叫不叫中台又有什么关系?

小结

这一节的主题是架构,但是我没有和大家讲分布式的三高,也没有聊微服务的 DDD,这些内容对我们做架构当然是非常重要的,不过正因为特别重要所以不管是大量的文章书籍还是现实工作都已经给予它们足够的关注点,做不好大部分是资源或者能力的问题,属于已知问题。

而复杂度在我看来是一个隐蔽杀手,虽然不会立刻让系统崩溃,但是会逐渐腐蚀系统,更像是慢性病,需要长期的治疗(开发和架构习惯)才能克服,并且对很多同学而言属于未知问题,你都没有察觉到它的严重性和影响又谈何解决呢,所以建议你在系统设计时多从人和系统两个方面去考虑复杂度的变化,尽量让现在做的事对未来有帮助。

07 | 架构设计:治理好系统复杂度才最务实 - 图2

作业:针对自己负责的系统你是如何在复杂度上进行治理的?从目前你对业务的了解来看,这个系统的终态应该是怎样的,为什么?