透明思考


Transparent Thoughts


  1. 敏捷在中文期刊中的投影

    在中国推广敏捷软件开发方法十五年之后,我很想回头去看这段历史的全景。今天我用知网做了一些检索,看到了一些有趣的数据。

    我在所有期刊中检索主题中包含“敏捷”和“软件”的文章。其他的关键词检索会得出略有不同的结果。如果只用“敏捷”作为关键词,会得到13258条结果,但其中绝大多数与我想要谈论的软件开发方法无关。这是“敏捷”这个译名词的宿命:它是一个普遍被认可的、带有褒义的形容词,使用这个词让听众更容易接纳这些新千年以后发展起来的新方法学。但与此同时,这个译名也让听众产生更多的误解:很多人会以自己的想法去期望这些新方法学,而不去深究它们的内涵。

    我尝试的另一个检索条件是把文献类别限定在“计算机软件及计算机应用”,并检索主题中包含“敏捷”、且不包含“制造”的文章。排除“制造”的原因是“敏捷制造”也是一个相当热门、且与计算机技术很有关系的领域。这个检索的结果从统计上看与前一个相去不远。以下我主要讨论第一个检索的结果。从相关关键词来看,这个结果是比较准确的。

    首先,令我略有些惊讶且失望:检索到的文章只有972篇。作为对比,“云计算”检索到的文章有10880篇,“软件工程”有9890篇,“SOA”也有3992篇。回顾这十五年,不论在IT技术领域还是软件工程领域,敏捷仍然是一个非常小众的话题。

    从时间趋势来看,2001年及以前有一些涉及“敏捷”和“软件”主题的文章,虽然并不与现在意义上的“敏捷软件开发”直接关联,但在理念上有一定相通之处。除了敏捷制造、敏捷物流等领域的文章之外,向华和邹平的文章赵捧未和窦永香的文章段永强、张申生、高国军的文章都提及了“基于对象的软件代理”概念,这可能是“前敏捷时代”一个值得探寻的线索。

    “敏捷软件开发”这个名词的出现是在2001年,并于2002年正式进入了中文期刊的话语空间,从时间轴上可以清晰地看出这个趋势。从统计数据上看,敏捷的潮流在2007年达到高峰,从2011年开始有一次显著的下滑,随后又逐渐回暖。这里面有一个异常因素:《程序员》杂志2010年以后的数据没有被知网收录。但即使排除这个因素,大趋势估计相去不远。某种程度上,这个曲线和Gartner的“技术成熟度曲线”(或者叫吹牛逼曲线)有某种暗合之处。

    然后——我不知道该高兴还是悲伤——《程序员》杂志无疑地在这场潮流中扮演了领袖角色。在所有检索到的文章中,《程序员》发表的超过20%,与其他任何报刊杂志都不在同一个数量级上。排名第二的《电脑知识与技术》在十五年中发表了共计23篇相关文章,我觉得这就基本上可以解读为“毫不关心”。《程序员》在2002年3月的《极限编程》专题,经确认,的确是中国期刊中最早介绍敏捷方法的一组文章。(实际上,《程序员》2001年12期的《代码重构》专题还略早于此,但由于刊号变迁的缘故,知网没有正确收录《程序员》2001年的内容,而是收录了之前使用该刊号的《Internet信息世界》的内容。)最早引介敏捷方法,并在之后的几年中贡献了整个期刊领域最多的篇幅。我们可以无疑地确认,《程序员》是敏捷方法在中国期刊领域最重要的——并且也是唯一的——支持者。这一数据,从一个侧面映射出敏捷在中国传奇而又尴尬的境遇。


  2. 云原生软件开发九要素

    随着云计算逐渐成为主流的计算环境,我们提出这样一个问题:在云原生(Cloud Native)的环境下,软件开发应该是怎样的?在其他领域,我们看到云计算带来了弹性、标准化和更高的效率。当云成为软件开发团队的缺省环境,哪些实践能帮助他们兑现云计算的价值?各个组织、各个团队采用的软件开发方法论和流程各有不同,我们尝试找出一个最佳实践的最小集合:如果缺少其中的任一实践,就会在一定程度上妨碍团队享受云计算能带来的价值。从这个角度出发,我们识别出了下面这个列表:

    脚手架

    开发一个应用,采用什么基础框架、代码目录结构怎么安排、引入哪些测试工具、使用什么构建工具、遵循什么构建流程,这些问题在典型的应用类型(例如“Java微服务”或者“React前端应用”)下都有成熟的答案。大多数情况下,新的项目不应该从头新建这一套结构:一来是为了节省工作量,二来更重要的是应该尽量复用已有的最佳实践。建议的做法是使用标准的脚手架,例如JHipster或者其他基于Yeoman的生成器可以提供较为通用的脚手架,IT组织也可以设计自己的标准脚手架。

    自包含应用

    应用程序应该尽可能少依赖周边环境。对周边环境依赖越少,因为环境差异而带来的工作量和错误就会越少。以Java应用为例,基于Spring Boot的独立应用,就比依赖Tomcat甚至WebSphere应用服务器的应用更具自包含性,环境的配置和管理都会容易得多。其他例如npm、rvm等工具都意图让应用程序更具自包含性。

    统一构建

    每一次构建,不论是由开发者自行发起的,还是在持续集成环境执行的构建,都应该尽量、最好是完全一致。为了达到这种效果,不仅构建工具和构建流程本身统一,执行构建的外部环境也应该统一。最好的方式是把构建放在虚拟机或容器里进行,而不是在彼此不同的计算机上执行。为了避免个人电脑计算资源的局限,构建最好是在云上进行。

    镜像化

    为了保证构建的外部环境完全统一,执行构建的不应该是各自不同的计算机,而应该是虚拟机或容器。因此运行应用程序的机器应该被镜像化。Vagrant之类的工具提供了很好的版本化管理虚拟机镜像的能力,但VMWare和VirtualBox等虚拟机在性能上和资源占用上都并不鼓励频繁启停的使用方式,Docker等容器技术可能是更好的选择。构建产物也应该是可执行的容器镜像。

    基础设施即代码

    为了保证构建的外部环境完全统一,获得运行环境的过程应该完全自动化,不应该允许任何手动修改运行环境的情况。如果运行环境是不可变的,那么就可以随时抛弃并重建。因此环境的配置应该以代码形式记录并自动化,并且还能顺便得到环境可以版本管理、可以自动化测试等好处。

    构建产物仓库

    构建成功的产物应该被放入统一的仓库,并分版本管理。这样做的目的是按照《持续交付》的理念,整个构建发布流程中使用用同一个构建产物;并且统一且分版本管理的构建产物可以在组织内复用。Nexus可以用于管理各种构建产物,包括容器镜像。

    弹性运行环境

    运行应用程序的环境,不论是用于执行测试、还是用于试用、培训、用户验收测试、乃至生产,都应该是弹性的:需要时可以立即开通,不需要时可以理解销毁。对于生产系统,运行环境的弹性使系统可以随负载水平扩展,使各种路由技术成为可能;对于开发、测试、预发布等场景,运行环境的弹性使环境开通和使用变得简单且不会出错。

    服务质量透明

    诸如高吞吐量、高可用性、高安全性、双机热备等服务质量,由运维或数据库的专家配置好,在生产环境提供给应用程序使用时,只暴露IP地址和端口(甚至只有端口)。对于应用开发者而言,服务质量是透明的,不会感觉到生产环境与开发环境依赖的外部服务有任何差别。

    诊断信息服务

    当构建和运行都在容器中乃至云上发生,并且容器是为应用程序专门定制的,调试诊断必然不能像在本地开发机器一样直接。需要借助完善的日志、监控等方式(例如基于SplunkELK的方案)的来提供应用程序的诊断信息。诊断信息服务不仅作用于生产环境,测试、开发、构建等各个环境也使用同样的方式获得诊断信息。


  3. 人生赢家弗洛雷斯

    费尔南多·弗洛雷斯有着传奇的一生。他作为理工科的好学生留校,在智利社会主义的浪潮中涉足政坛,因为科技背景步步高升,陪着萨尔瓦多·阿连德总统走完政权和人生的最后一段路,作为政治犯坐了三年牢,然后摇身一变成为世界级学霸,顺便在硅谷搞了两家创业公司挣了一大把钱,终于衣锦还乡再次投身政治,支持的却是与阿连德政纲截然不同的总统。

    (本文绝大部分内容出自《控制论革命者》一书。)

    工科背景

    弗洛雷斯于1943年出生于塔尔卡市,位于智利首都圣地亚哥以南200多公里的一个城市。他的父亲是铁路工程师,母亲经营着一家小型的木材公司。在学校里,他是一个思维敏捷、尤其擅长数学的好学生。尽管还不知道未来的人生要干什么,弗洛雷斯已经意识到成为工程师是“一件大事”,所以他申请了天主教大学的工程学院,并被这所声名远播的大学接纳。在2003年的一次访谈中,他猜测自己可能是家族里第一个接受了大学教育的人。在正式的大学教育之外,一系列的人际关系、工作经历和政治变迁让弗洛雷斯发现了控制论和比尔。在大学里,弗洛雷斯跟随阿诺尔多·海克斯学习运筹学,后者在1963年至1964年间任天主教大学工程学院院长,后来接受了MIT斯隆管理学院的教授职位。

    因为弗洛雷斯受过运筹学训练,[斯塔福·比尔的公司]SIGMA雇佣他在智利铁路的项目上[作为实习学生]工作。在这个项目上,他接触到了《控制论与管理》,一本在他看来“脑洞大开”的书。弗洛雷斯于1968年毕业,获得工业工程学位。……1968年至1970年间,弗洛雷斯在天主教大学工程学院任教务主任,随后他的职责范围逐渐扩展到了大学的各项事务。

    初涉政坛

    这一时期大学正在进行改革,弗洛雷斯监督开展了很多工程课程的改革,包括增加课堂与社会的联系。和很多同时代人一样,弗洛雷斯在学术圈和政治圈都很活跃。1969年,弗洛雷斯等一群天主教大学的年轻知识分子脱离基督教民主党,成立了“统一人民行动运动”(简称“MAPU”)。这个由年轻知识分子组成的小型政党批评基督教民主党和总统爱德华多·弗雷·蒙塔尔瓦(1964年至1970年在位)采取的中间路线,与左翼的人民阵线中的共产主义者和社会主义者政见一致。MAPU加入人民阵线,再加上右翼和基督教民主党组成的获胜联盟内部的不稳定因素,最终促成社会主义者阿连德在1970年大选中以微弱优势获胜。

    作为对弗洛雷斯政治忠诚和技术能力的认可,阿连德政府将他任命为国家开发公司(简称“CORFO”)的技术总监,这家公司的使命是国有化智利的工业。弗洛雷斯成为了国家开发公司的三号人物,也是MAPU成员占据的最高位置。他最重要的职责就是管理已经国有化的工厂的日常调控。

    步步高升

    [Cybersyn]项目的资金部分来自国家科技学院,该学院的预算一直在增加;另一部分资金来自CORFO,这是全国资金最充裕的政府机关之一,毕竟它的使命是指导国家经济。弗洛雷斯在这两个机构都担任高管,既是发展机构CORFO的三号人物,也是国家科技学院的理事长。他动用了各种社会和组织关系来保障这个项目需要的财政、物资和人力资源,其中大部分是通过非正式渠道获得的。“我有很大的权力。”弗洛雷斯承认。不过CORFO实在太庞大了,弗洛雷斯与比尔提议的这个项目仅需要其整个预算中的一小部分。

    ……比尔见到了阿连德,获得了总统的许可继续开展他的项目。但比尔相信这次会见还有别的意义。他猜测弗洛雷斯想在智利政府中更广泛地应用控制论原理,而不仅是用于管理国有经济,这次会见则会帮弗洛雷斯未来的项目铺平道路。团队中最了解政治的施温伯提供了另一种解读。弗洛雷斯“有着更高级的大脑,”施温伯这样说道,“非常复杂,老于世故,精明能干,有时甚至是狡诈,但绝对精明。”弗洛雷斯很早就认识到了这个系统的科技价值和政治价值。照施温伯的看法,比尔与阿连德的会见一方面是为了解释正在开发的控制论管理系统,另一方面也是为了“让阿连德意识到有这么一个人,弗洛雷斯,掌管着这种力量”。弗洛雷斯希望给总统留下这样的印象:他是科技专家,并且他正在为政府做着有趣、大胆而且可能很有价值的工作。最重要的是,这次会见确认了弗洛雷斯与比尔之间的工作同盟,把阿连德也拉进了项目中。这次会见再次强调了在智利社会主义道路上正在发生的科技创新与政治创新之间的相互依赖关系。

    [1972年]10月初,弗洛雷斯升任经济部副部长。[发生于1972年的]十月罢工后,这位29岁的年轻人被总统任命为经济部长。……弗洛雷斯相信是他对科技的使用帮他赢得了这个内阁级别的位置,他认为继续发展自己作为科技专家的形象或许能给他带来政治上的优势。

    大厦将倾

    弗洛雷斯仍然是阿连德内阁的成员,但他在[1973年]新年当天离开了刚上任两个月的经济部长职位,转任财政部长。这个新的任命使弗洛雷斯离Cybersyn项目更远了。

    在[发生于1973年8月的]第二次卡车主罢工期间……阿连德任命弗洛雷斯担任政府的秘书长,负责政府对内对外的沟通。在风雨飘摇的智利政府中,弗洛雷斯已经占据了一个最高的位置,此时他才刚刚30岁。

    [1973年9月11日武装政变发生时]弗洛雷斯……已经是阿连德身边最贴近的助手之一,轰炸发生时他正与总统在一起,一直与军方保持着电话联系。他告知阿连德,军方要求总统立即、无条件投降。总统拒绝了这个要求,并派弗洛雷斯去和军方谈判。弗洛雷斯刚走出建筑即被逮捕,从此没有再见到总统。

    费尔南多·弗洛雷斯作为政治犯在监狱里呆了3年。……1976年,大赦国际的旧金山分部成功地通过谈判促成弗洛雷斯获释,并在斯坦福大学计算机科学系给他安排了为期一年的研究职位。

    变身学霸

    在斯坦福,他遇到了计算机科学家特里·维诺格拉德,两人在1970年代后期和1980年代紧密合作。

    弗洛雷斯在加州大学伯克利分校获得了博士学位,师从哲学家约翰·赛尔、休伯特·德莱弗斯和经济学家安·马库森。他于1982年完成的毕业论文讨论了如何在“未来的办公室”里用计算机改进管理和交流。……比尔在早年间对弗洛雷斯的想法有所影响,但在完成毕业论文的时候,弗洛雷斯的关注点已经从管理控制论转移到言语行为理论和海德格尔哲学,因为这些是他的博士委员会成员专擅的领域。

    1986年,弗洛雷斯发表了他的第一本书,与维诺格拉德合著的《理解计算机和认知》(Understanding Computers and Cognition)。这本书结合了计算机领域的问题与“生物本质、语言、人类行为本质等方面的理论”,着重讨论计算机能和不能从事哪些人类实践。……美国信息科学学会把《理解计算机和认知》评为1987年“最佳信息科学书籍”,这本书现在被认为是人机交互领域的关键文本之一。

    [注:约翰·赛尔和休伯特·德莱弗斯是人工智能哲学领域的大师;特里·维诺格拉德是人机交互领域的大师,1995年至1998年间指导了谷歌创始人拉里·佩奇的博士研究。]

    名利双收

    1980年代,弗洛雷斯还转型成了一名硅谷企业家。他创立了教育咨询公司Logonet,向商业社会传授本体论设计思想。他还跟维诺格拉德一起创立了Action科技公司,开发了名为“协调者工作组生产力系统”的软件包,他们自称是第一款用于计算机网络的工作组系统。……后来一位记者称“协调者”是“全世界第一款社交网络软件”。

    1989年,弗洛雷斯又成立了一家咨询公司,名叫“商业设计合伙人”,或者简称BDA。BDA向困境中的企业传授言语行为理论的原则,例如提出明确的请求、做出明确的承诺等,试图以这种方式改变这些企业。……在其巅峰时,BDA有150名员工,在3个大陆上开展工作,年收入5千万美元。……到2007年,弗洛雷斯的财富净值据估算达到了4千万美元。他的名气也随着财富一道蒸蒸日上。有人认为他唐突、脾气暴躁、说话直接甚至是粗鲁、惹人讨厌;但他对外传递的讯息,以及他在学界和商界的成功,让不少人把他视为偶像。

    2002年,弗洛雷斯以百万富商的身份回到智利,并作为国家最北端地区的代表被选为参议员,由此开启了他作为政治家的人生新篇章。2008年,他脱离支持他当选参议员的中左联盟,成立了自己的政党“智利第一党”。2009年,他又完全转变立场,公开支持右翼总统候选人、亿万富商塞巴斯蒂安·皮涅拉。最终皮涅拉赢得了大选。

    [2010年3月,弗洛雷斯被皮涅拉总统任命为智利国家竞争力创新委员会主席。]


  4. 技术领导者即服务

    八年前我写了一篇文章《Tech Lead的三重人格》。迄今为止为数众多的敏捷交付团队中,Tech Lead(技术领导者)对于交付的效能和质量起着至关重要的作用。我在那篇文章中指出,Tech Lead需要扮演三种重要的角色:技术决策者、流程监督人、干扰过滤器。一支团队能否有效采用架构最佳实践、交付流程最佳实践和项目运作最佳实践,很大程度上取决于Tech Lead把自己的工作完成得多好。

    如果更进一步把这篇文章中Tech Lead承担的责任做一个拆解,我们可以看到,一个称职的Tech Lead是这样去为项目的顺利交付做出贡献的:

    • 首先,他要制订适合该项目要求的技术方案。他要参与架构设计,了解平台和编程语言、主要的框架和库、集成点、部署策略、数据迁移策略,确认总体技术方案能够支撑系统的业务要求。
    • 随后,他要保障交付顺利开展。他要确保环境的一致性,搭建和管理持续集成流水线,指导并监督团队遵循持续集成的流程和实践。
    • 最后但绝非最不重要的,他还要管理和提升团队的能力。他需要确认团队是否熟悉用到的技术栈和工具,而且——虽然这一点在我写文章时的ThoughtWorks还不那么凸显——要帮助团队成员组织刻意练习来提升能力。

    正如当时那篇文章的一位读者非常正确地指出的,要一个人做这三方面的贡献很多时候是不切实际的。在很多组织里,这三件事是在三个环节中分别进行的,这三个环节的彼此割裂造成了很多问题:

    • 在方案环节,架构师根据客户的要求和痛点,基于自己的知识储备设计技术解决方案。他是如何分析客户的要求和痛点,他的知识储备是什么,组织里的其他人不一定知道,于是不同架构师提出的解决方案就很可能不一样。
    • 在交付环节,交付团队基于自己的知识储备来交付技术解决方案。方案背后隐含的知识储备,交付团队未必具备,所以屡屡会出现交付质量不佳的问题。不是他们没有能力,只是能力与方案的需要不符。
    • 组织感到团队的能力有不足,于是找来教练提升能力。然而教练基于的是一个标准的能力集来训练团队,这个能力集与项目实际需要的能力又不一定匹配。于是出现能力发展计划不对症、能力建设效果不明显的问题。

    由此可见,只有当方案、交付、能力三者有很好的协同,项目和团队才能健康成长。而这个协同之所以尤其困难,是因为它跨了三个非常不同的问题域(在很多组织是三个不同的功能部门),需要三种非常不同的能力,对这个居中协调者的要求非常高。

    所以,如果我们能用一个云上的平台来承载这个居中协调者的能力,对整个组织的交付质量和能力成长都会有帮助。这个平台的核心实际上就是技术栈管理:针对典型的应用场景(例如企业资源服务化、移动数字化渠道),制订组织统一的技术栈,并从技术栈推导出对应的能力评估模型和刻意练习课程。于是我们就得到了以技术栈为核心的IT能力三环联动模型:

    当提供技术方案的架构师选择一个技术栈,用这个技术栈交付软件的能力要求就被明确地传达到交付团队。交付团队不用自己去设置开发环境和持续交付流水线,用云原生的持续交付环境即可启动开发,并复用在技术栈上积累的交付最佳实践。通过云上的能力测评系统,能力教练可以清晰地知道哪些成员已经具备需要的能力、哪些成员能力还有差距,然后为有差距的成员提供针对性的刻意练习和指导。

    云计算已经成功地模糊了硬件与软件的界限,使IT的一大挑战——管理设备——极大简化。现在,对于IT的另一个大挑战:人才短缺,云计算的“XXX as a service”模式是否可以继续发挥作用?IT组织是否可能借助云计算获得优质IT人才的弹性和伸缩性?这是一个值得去探索的课题。在这个方向上,将对交付质量与效能起着重要影响的Tech Lead的能力以云平台服务的形式提供,有可能是触手可及的一个目标。


  5. 言说信息时代的奴隶制

    [本文系《再见i奴》(Goodbye iSlave)的书评,以《信息时代的三角贸易》为题同步发表于土逗公社。]

    毫不意外地,柯洁在三番棋中完败软硬件全面升级的AlphaGo。这场“人机大战”再次让人工智能成了街谈巷议的热点。在感叹科技进步一日千里的同时,大众也表达出了对人工智能飞速发展的担忧。不过,据笔者的观察,这种担忧大多数时候仍然是一种“终结者”式的想象,即担心机器拥有智能以后全面取代甚至统治、消灭人类。2015年6月,德国斯图加特的一家汽车工厂里发生致人死亡的工业事故,媒体在报道时竞相使用“机器人杀人”、“机器人把技术员按倒打死”这样耸人听闻的标题。类似这样的大众舆论反映出人们对智能技术的双重恐惧:不仅隐然感到科技的发展值得担心,而且隐然感到自己不知究竟应该担心什么。

    诚如维特根斯坦所说,“凡不可言说者必保持沉默”。在众多学者的话语中,对科技、尤其是信息技术的担忧具有两个明显的特征:它们通常具有强烈的未来主义色彩,多在谈论十余年、数十年后的“近未来”图景;而且它们多在强调“机器vs.人类”的关系。不论凯文·凯利还是博斯特罗姆,他们引人瞩目的作品都在谈论“超级智能”如何在不远的将来取代人类。然而这两个特征却有一种微妙而深远的影响:它们让读者觉得科技对人的威胁尽管存在,但毕竟尚未发生、而且对所有人的威胁是均等的。

    从这个意义上,邱林川的这本《再见i奴》(Goodbye iSlave,伊利诺伊大学出版社2016年)提出了一种新的言说方式,使“信息技术可能带来什么危害”这个问题呈现出一个全新的面向:信息技术的发展对人造成伤害,这件事无需等到未来,它早已在发生;而且这种伤害绝非均质的“机器伤害(所有)人类”,科技作为一种工具,再现与强化了人类社会古而有之的权力关系,使一些人受损、另一些人受益。在信息时代被高新科技再现与强化的关系究竟是什么?作者极富洞察力地指出:那就是奴隶制。

    尽管玫瑰的芬芳并不依赖于它的名字,但对于社会权力关系这种抽象事物,用什么语言谈论它,会直接影响受众的观感。当富士康、苹果和优步不约而同地声称为他们打工的劳动者是“自由的”,作者一针见血地指出了现代信息技术产业与十七世纪的奴隶制经济之间的共性:一方面,它们都建立在对劳动者的全面控制、甚至是对人身自由的控制基础上,富士康在连续跳楼事件之后安装的“防跳网”偶然而又必然地重现了十七世纪大西洋上运奴船上的设置,因为只有剥夺劳动者的人身自由乃至“自杀权”才能从他们身上榨取最大的剩余价值;另一方面,它们都着力生产“成瘾性消费品”,iPhone和微信就像白糖和咖啡一样让消费者深陷其中不能自拔,这才保障了廉价奴工生产出来的产品源源不断地被消费。在这一产业体系中,不仅在富士康生产iPhone的工人如同奴隶一般被控制,使用iPhone的消费者同样如同奴隶一般被规训——邱林川将前者称为“制造的奴隶”,后者则被称为“被制造的奴隶”。

    信息技术产业与奴隶制,这个或许令人吃惊、但绝非无中生有的类比,就像“玫瑰的名字”一样,让我们突然获得了一种能力,可以去谈论一些司空见惯、却又难以捕捉的问题。例如“低头族”:我们身边的年轻人(以及我们自己)随时随地都在低头玩手机,竟然到了城市专门开设“手机人行道”的地步,对此我们本能地感到不满,却不知该如何批评这一现象。直到邱林川说,这些人是“i奴”——信息时代的奴隶。于是我们恍然大悟:双手被无形的镣铐捆绑举在胸前,双脚只能沿着画好的路线前行,这可不就是奴隶的形象吗?为了让这个类比更加贴切,作者专门研究了与奴隶制相关的大量文献。当我们看到奴隶制的若干特征与当代信息产业的现状竟然高度吻合,这个类比给我们带来的冲击绝非仅仅是比喻意义上的。

    有了一个可以被谈论的问题,我们就能更有效地去发现潜在的解决办法。针对奴隶制对奴工的人身控制和成瘾性消费两大特征,作者将视野投向全球,并发现了一些可能的替代方案,例如劳动者共同拥有、民主治理的“平台合作主义”,例如重视供应链社会效应、以耐用性(而非消耗性)为卖点的“公平手机”等。尽管这些替代方案尚显稚嫩,但毕竟让我们看到了“另一种方案”的可能性;而为了去发现“另一种方案”,首先就必须有一套批判的话语来指出现状中的缺陷。从这个意义上,邱林川这本《再见i奴》可能是在看似牢不可破的资本与科技共同铸就的铁板上划开了第一道裂痕。

    博斯特罗姆在《超级智能》中的一个观点我个人非常赞同:信息技术的发展如此迅猛,我们很可能正处在一个决定人类命运的拐点上。然而当大众——在科技与资本精英的引导下——担心着几十年后人工智能是否会统治世界的时候,邱林川给我们指出了一幅更加现实也可能更加危险的图景:这个科技拐点将要决定的,可能是科技究竟让大多数人从此过上幸福的生活、还是把大多数人变成奴隶。这两者之中较为美好的那种前景,不会无缘无故自动发生的。拥有合适的语汇去谈论和批评,乃是我们能够对这次选择产生影响的第一步。


  6. 云原生平台:市场现状分析

    对于期望获得云原生能力的企业,在IaaS提供的云计算弹性与开发云原生应用之间,还有一个很大的空白地带:如何用云计算为软件交付的过程服务。Docker和Kubernetes等工具提供了容器化和容器编排的能力,但是企业需要解决“为了承载我(期望)的开发流程,我需要怎样的容器和编排”的问题。这就给云原生平台(Cloud Native Platforms)创造了市场空间。

    总体介绍

    为分布式云应用提供支撑的平台从2015年开始受到广泛的关注,围绕着Docker、容器编排等技术,开始出现各种商业模式。云原生平台可以视为下一代中间件,其用途是交付企业级分布式应用所需的各种支撑能力。以容器和容器编排为基础,云原生平台提供了日志、审计、安全、合规、权限管理、容器镜像仓、基础设施抽象、团队成员启动等支撑功能。

    测试和开发环境的随需可得不仅仅有助于优化基础设施资源,而且有助于统一实践和流程,能促进团队采用敏捷和DevOps的最佳实践。把抽象层次提高一点,云原生平台要支撑云原生应用所需的5项能力

    1. 云平台自动化。不仅提供基础设施开通与配置的自动化,而且内嵌测试、版本控制、文档、软件生命周期管理等方面的最佳实践,使整个开发-测试-部署的过程工作量最小化。
    2. 无状态函数。将软件以小型、目的单一的函数形式部署,通过事件驱动服务,从而避免维护运行时环境。
    3. 微服务架构模式。每个服务提供单一的业务能力,能够独立开发、测试和部署。
    4. DevOps流程和文化。自动化整个软件交付流水线,打破开发与运维的间隔,以全功能团队形式工作。
    5. 云可靠性工程。提高运维工作的自动化程度,把运维重心放在平台工程,从而提高系统可靠性。

    云原生平台可以大致分为“非结构化的/不带意见的”(unstructured/unopinionated)和“结构化的/带意见的”(structured/opinionated)两类。Pivotal声称Cloud Foundry作为一个“结构化的/带意见的”云原生平台,提供了下列能力:

    • 服务作为比基础设施更高层级的抽象。Cloud Foundry提供按需部署应用的机制。
    • 容器。所有应用程序都以容器形式运行。
    • 敏捷和自动化。Cloud Foundry可以作为CI/CD的一部分,用于随需开通环境和服务。
    • 微服务支撑。为松散耦合的服务架构提供集成和协作机制。
    • 云原生应用支撑。对应用程序的约束使其效率更高、更易管理、更可靠。

    从技术角度,云原生平台有5个重要的考量因素

    1. 如何加速应用开发的速度?
    2. 如何推动新技术趋势(移动、社交、实时、数据核心、IoT)的应用?
    3. 如何充分利用开源技术和社区?
    4. 如何提升运维响应变化的能力?
    5. 如何在平台中嵌入安全性?

    部分产品分析

    Apcera的定位是“支持云原生应用和遗留应用的容器管理平台”。主要的解决方案是“遗留应用现代化”,号称能在7天内把遗留系统迁移上云。

    Apprenda是基于Kubernetes的面向开发团队的云平台,既能支持云原生应用、也能支持传统应用的开发。通过集中的云政策和监管、共享的应用服务、基于微服务的架构标准化,Apprenda把开发组织变成成熟的软件工厂。

    HashiCorp Atlas在Vagrant的基础上规定了一个“开发-构建-产物管理-部署-上线”的标准流程。适用于以技术为核心、重视DevOps流程与文化的组织。

    Cloud Foundry是重视部署与运维的云原生平台。强调的卖点是(1)部署的速度;(2)云应用的伸缩能力;(3)缺省的安全性。

    OpenShift是基于Docker和Kubernetes的容器应用平台,支撑容器的开发、部署和管理。主要功能有(1)容器编排;(2)容器存储;(3)支持多种编程语言和框架;(4)自动化应用程序构建、部署、伸缩和健康管理;(5)基于项目的管理界面;(6)自服务平台。

    CodeEnvy在Docker基础上为开发团队提供云上的工作区。CodeEnvy提供.NET、Android、C++、Java、Go等技术栈,用Eclipse Che作为开发环境。主要卖点有(1)一键式Docker环境;(2)团队成员启动和协作;(3)DevOps工作区平台。

    Wercker是基于Docker的CI/CD自动化平台(类似于SnapCI),用Kubernetes提供容器编排,支持微服务部署,缺省支持GoLang、NodeJS、Ruby、Python。支持自定义的CI/CD流水线,通过流水线的级联和触发配置出复杂的工作流,支持并行测试。

    DaoCloud基于容器技术从交付件、运行环境、环境配置、权限协作等多个方面定义企业交付流程标准,通过标准化提升跨部门协作的沟通效率,推动软件在不同阶段的自动化高效流转。高度灵活的交付流程定义引擎,支持可视化定义企业完整的端到端交付流程。通过云平台的弹性,支撑企业交付流程中不同阶段对环境运行时的需求。

    数人云基于Mesos和docker,提供企业级持续集成和持续交付解决方案,从快速搭建持续集成环境,到改进和融合企业的开发、测试及持续发布流程,助力企业提升产品发布效率,规范产品开发流程。可以继承企业现有的版本控制工具,通过Jenkins实现自动构建和测试。


  7. 持续交付2.0:云原生持续交付

    持续交付》提出了一系列贯穿整个软件交付生命周期的最佳实践。但它成书的年代(2010年)云计算尚未得到广泛应用,尤其在软件开发过程中的应用非常有限。如果站在今天的技术水平和对云计算的理解水平基础上回顾《持续交付》的内容,我们有可能提出一组全新的、原生于云环境的持续交付实践。

    软件发布的反模式

    《持续交付》中列举了软件发布过程中的一些反模式,这些在行业中常见的不佳实践使软件发布过程容易出错,使软件发布的风险和压力增大。这些与可靠的发布过程相对应的常见的反模式包括:

    • 手工部署软件。靠详尽的发布文档来描述发布步骤及每个步骤中易出错的地方,靠手工测试来确认发布后的应用程序是否运行正确。不自动化的部署过程既不可重复也不可靠,会在调试部署错误的过程中浪费很多时间。
    • 开发完成之后才向类生产环境部署。开发团队认为“开发完成了”,才第一次把软件部署到类生产环境(比如试运行环境)。假如应用程序是全新开发的,第一次将它部署到试运行环境时可能会非常棘手。
    • 生产环境的手工配置管理。通过专门的运维团队来管理生产环境的配置,如果需要修改一些东西,就由这个团队登录到生产服务器上进行手工修改。经常导致部署到生产环境时就失败,尽管多次部署到试运行环境都非常成功。

    在云计算的背景下,我们可以看得更远一步:这些反模式如果在今天的研发团队中仍然出现,背后反映的是这支研发团队还不会利用云计算提供给他们的便利能力。

    • 手工部署软件 => 软件发布形态和流程不标准。因为软件的发布形态多种多样(JAR、WAR、RPM、DEB……),因为软件的功能与配置不解耦,所以才会需要手工部署。而发布形态和发布流程的不标准,背后的原因是计算资源稀缺,需要复用服务器。
    • 部署到类生产环境太晚 => 开发环境与生产环境不统一。因为开发和测试用的环境与生产环境有很大差异,才会出现部署到类生产环境时的种种困难。开发环境与生产环境的不统一,背后的原因也是计算资源稀缺,生产环境昂贵,无法做到随需可得。
    • 生产环境手工配置管理 => 环境管理情况复杂。因为环境需要长期使用且不断升级,才有了对环境进行管理的需求。环境需要长期使用和升级,背后的原因是计算资源缺乏弹性,不需要的时候不能随意丢弃。

    对于这些反模式,《持续交付》提出的解决办法是“将几乎所有事情自动化”。但在当时的技术水平下,由于软件发布的形态和流程不标准、开发/测试环境和生产环境不统一、环境管理情况复杂,“将发布流程自动化”在每个团队的具体做法都不同,因此持续交付的水平高度依赖于团队的能力与觉悟。《持续交付》也只能苦口婆心地劝说“如果需要执行这个流程数十次的话,就不是那么容易的事了”,而且“不需要把所有的东西一次性地全部自动化……随着时间的推移,最终你可以、也应该将所有环节全部自动化”。

    但如果在软件的开发过程中充分利用云计算的弹性能力,这些反模式有可能被根除,而不必由每个开发团队重复地尝试通过自动化来缓解。

    部署流水线

    《持续交付》提出了“部署流水线”的概念(如下图)。“随着某个构建逐步通过每个测试阶段,我们对它的信心也在不断提高。当然,我们在每个阶段上花在环境方面的资源也在不断增加,即越往后的阶段,其环境与生产环境越相似。”

    在充分利用了云计算的情况下,部署流水线会有两方面的改变:

    1. 不存在“所用环境与生产环境的相似度增加”的情况,从提交阶段开始(甚至在此之前的开发阶段),所有环境都与生产环境是一致的。
    2. 由于不需要根据项目拥有的计算资源来定制各个环境与生产环境的相似度,这个部署流水线不再是一个需要由开发团队来实现的概念模型。部署流水线可以是标准的、一致的,开发团队只需要定义自己这个软件的生产环境即可。

    《持续交付》中提倡整个部署流水线“只生成一次二进制包”,并且在各个验证步骤之间传递二进制包。只生成一次二进制包的实践是非常必要的,因为“出于审计的目的,确保从二进制包的创建到发布之间不会因失误或恶意攻击而引入任何变化是非常关键的”。但实际的项目中经常出现二进制包非常庞大、在各个步骤(及各个环境)之间传递二进制包很费时的情况,这也是导致一些项目最终仍然退回到每个步骤重新构建二进制包的原因:增量的编译和构建可能比通过网络传递整个二进制包还省时。

    如果构建的产物是容器镜像,所有运行时环境都从云上获得,那么实际上不存在传递二进制包的过程。每个验证步骤都用指定版本的容器镜像搭配对应的配置,启动一个新的运行时环境后,在云上的运行时环境中执行(自动的或手工的)验证即可。

    持续集成

    尽管《持续交付》说“选择并安装好持续集成工具之后,只要再花几分钟的时间配置一下就可以工作了”,但实际上很少有哪个项目的持续集成实施会如此顺利。例如当“发现在运行持续集成工具的机器上缺少一些必需的软件和设置”时,《持续交付》提出的建议是“将接下来你所做的工作全部记录下来,并放在自己项目的知识共享库中……并将重建全新环境的整个活动变成一个自动化的过程”。实际上,这是一件需要高度技能水平和纪律性的事,拥有这两者的技术领导者(Tech Lead)很罕见,希望每个开发团队都有这样一名技术领导者坐镇是个奢侈的梦想。

    而且,持续集成环境与开发环境仍然是有区别的,这个区别很可能是由于计算资源的限制。《持续交付》中说,“你可以很有把握地说:‘只要是在与持续集成一模一样的环境上,我的软件就可以工作。’”。然而问题就在于大多数情况下,开发环境与持续集成环境不是一模一样。这也是为什么持续集成必须集中式地进行,需要有“铃声和口哨”来及时发现构建失败,并且“要让持续集成能够发挥作用……整个开发团队就必须有高度的纪律性”。

    在充分利用云计算的情况下,开发一类软件(例如“Java微服务”或“ReactNative移动应用”)所需的环境和部署流水线可以由少数几名优秀的技术领导者来标准化,开发团队不需要再操心如何配置一个持续集成环境的问题。

    并且正如《持续集成将死》一文中所说,云的弹性能够使每个人、每次构建都使用标准的类生产环境,因此持续集成没有必要发生在一个中心化的“持续集成工具”上。由于持续集成的“集成”这个动作在代码进入团队代码库之前发生,很多的提醒和纪律变得不必要了:构建失败就不能提交代码,于是确保构建成功成了每个开发人员自己的事,不能把不成功的构建扔给团队去处理。


  8. 持续集成将死

    在思考“云时代的研发环境长什么样”这个问题的时候,我逐渐意识到一件很重要的事。2000年首次被提出、在过去十几年中我们习以为常的敏捷核心实践持续集成,很可能正在走到它生命周期的尾声。

    让我们来回顾一下Martin Fowler在他那篇著名的文章里如何描述持续集成这个过程:

    一旦完成了修改,我就会在自己的计算机上启动一个自动化build。……当我build成功后,我就可以考虑将改动提交到源码仓库。……然而,提交完代码不表示就完事大吉了。我们还要做一遍集成build,这次在集成计算机上并要基于mainline的代码。只有这次build成功了,我的修改才算告一段落。……在持续集成环境里,你必须尽可能快地修复每一个集成 build。好的团队应该每天都有多个成功的 build。错误的 build 可以出现,但必须尽快得到修复。

    从上面加粗的文字就能看出,过去的十多年里,在谈及持续集成这个实践时,我们已经预设了这个场景:有一个集中式的持续集成服务器在监听代码库的变化,每当有人提交代码时,持续集成服务器会自动取出最新的代码,执行整个构建和测试流程。围绕着这个场景,我们发展出了一整套的纪律来保障持续集成少失败、失败的时候能尽快修复。围绕着这个场景,我们发明了CruiseControl、GoCD、SnapCI等一代又一代的持续集成服务器,并在我们所有的项目中使用它们。这个场景在我们的脑海中如此根深蒂固以至于我们不再去询问:为什么需要这样做?

    实际上,我们需要一个集中式的持续集成服务器,这是有历史原因的。2000年代初期的技术时代背景,尤其是以下两个非常具体的约束条件,造成了今天我们看到的持续集成的形态:

    1. 计算资源短缺。这个约束条件决定了完整的、与生产环境相似的、能执行端到端验证的环境必定是稀缺品。典型的交付团队没有能力给每个成员配备整套环境,只能在他们各自的计算机上模拟一套尽可能接近于生产环境的开发环境。于是开发环境的验证结果不足为信,必须在一个标准的、更接近于生产环境的集成环境上通过验证,才能说软件达到了质量要求。
    2. 版本控制工具的局限性。Subversion(以及其他更早的版本控制工具)在pre-commit阶段通过服务器端回调钩子很难——如果不是完全不可能的话——得到完整的“提交后版本”,因此svn的pre-commit钩子基本只能用于检查提交信息是否符合规范,完整的验证则必须在代码已经合入代码库之后才能——在一台独立的“持续集成服务器”上——进行。

    云计算彻底改变了第一个约束条件。计算资源仍然不能说极大丰富,但企业应用开发所需的x86架构计算资源在云环境下已经不再短缺,结合各种基础设施自动化和配置自动化的技术,随时、按需提供整套环境已经不是难题。而且使用docker等容器技术开通出来的环境是抛弃型的、不可变更的,因此也就不存在环境不一致、验证结果不可信的问题:每个开发人员都可以从云上拿出一套环境,执行build,其过程与效果都与持续集成服务器的build完全一致。

    在过去的十多年里,持续集成之所以必须是一种“技能”、一门“手艺”,而不仅仅是一套工具的定制与实施,很大程度上正是由于计算资源短缺这个约束条件造成的。因为计算资源几乎总是短缺,所以每个团队、每个项目拥有的计算资源几乎总是有些这里那里的不同——这个项目可能有两套完整的测试环境,那个项目可能只有一套。这种资源的局限,逼迫每个项目的技术领导者们不得不根据手上能得到的环境,来微调持续集成的流水线乃至软件交付的流程。简言之,流程是依据环境来调整的。

    当计算资源短缺的约束条件不再存在,在考虑构建流水线时就可以有一个根本的观念转变:可以制订一套标准的构建流水线,并要求计算环境向这套流水线对齐。这时,持续集成就可以不必是每个团队的技术领导者都掌握的“技能”和“手艺”,它完全可以在一个组织范围内定制和大规模实施。因为环境可以弹性地适配流程,我们就能够为相同类型的项目定义统一的最佳流程。

    而git对svn的全面取代则带来一个细微而深远的影响:由于可以在pre-commit阶段直接获得完整的待提交快照、并在这个版本基础上执行测试,不能通过build的代码将直接被拒绝提交。换句话说,整个“持续集成纪律”尝试解决的问题——有缺陷的代码进入团队的代码仓库从而妨碍其他人不断提交没有缺陷的代码——将不复存在,有缺陷的代码将根本无法进入团队的代码仓库。

    综上所述,这两个要素的结合:

    1. 每个开发人员(以及自动构建)都可以在PaaS云上获得完整的技术栈运行时环境;以及,
    2. pre-commit阶段可以对待提交的代码进行完整的构建

    带来的是一个非常重要的影响:持续集成服务器这个东西,我们不再需要了。持续集成的“集成”这个动作,将在代码进入团队代码库之前发生。我们有办法(git的pre-commit钩子)确保这次集成发生,也有办法(云化、容器化的环境)确保这次集成是可信的。因此我们不再需要一个持续集成服务器来扮演团队的守门人。集中式的持续集成服务器将退化为团队研发行为的可视化仪表盘:它不再负责管理环境和构建软件,只负责采集所有构建中产生的数据、并以适当的形式展示,作为团队研发过程的可视化呈现。

    当持续集成服务器消亡,一个开发者的典型工作流程可能会是这样:

    • 从git仓库clone出代码,在自己的电脑上做修改;
    • 修改完成,从研发PaaS上获得一个运行环境,把刚写好的代码运行起来,用浏览器查看一下效果;
    • 执行构建,构建脚本自动从研发PaaS上获得一个运行环境,在其中执行编译、打包、代码检查和测试;
    • 构建通过,提交代码并push,git仓库的pre-commit钩子自动触发一次构建,过程与效果都与刚才手动执行的完全一致;
    • 如果没有手工执行构建就尝试提交,自动构建会失败,代码无法push到团队的代码仓库中,开发者自己去修复;
    • 如果自动构建成功,代码提交完成,最新版本的代码被构建成容器镜像;
    • 测试人员从研发PaaS上获得一个运行环境,把待测版本的容器镜像装载上去,执行测试,如果测试通过就将该版本标记为“发布候选”;
    • 运维人员从生产PaaS上获得一个运行环境,把发布候选版本的容器镜像装载上去,即完成上线。

    这个流程直接地实现了《持续交付》中描述的“两道门”结构。虽然每个项目运行的环境不同,但这个持续交付的结构可以是完全一致的,因为环境可以弹性地适配研发流程。

    与持续集成服务器同时消亡的,还有持续集成这个概念本身。由于对响应力(responsiveness)的要求是如此之高,现代的IT团队已经不能容忍有缺陷的代码先进入代码库、阻塞整个团队的工作、然后再来修复(甚至不修复、或者还需要说服某些团队成员去及时修复)。持续集成是如此重要,以致于它会变成团队的“空气和水”。它会被嵌入到日常的研发工具当中,成为程序员感知不到、而又不可妥协的质量要求——正如IntelliJ之类现代IDE把“通过编译”这项要求变成了程序员感知不到、而又不可妥协的质量要求。

    持续集成对于软件开发是如此重要,以至于不应该把它交给软件开发者自己去做。

    这就是为什么我认为持续集成工具、以及这些工具背后的持续集成概念在云计算深入研发之后将会消亡。取代持续集成的,将是更紧密地内嵌质量要求、更充分地利用云计算优势的云原生(Cloud Native)开发方法及支撑工具。


  9. 数字化企业的API架构治理

    前文中我们说到,传统企业在逐步建设自己的数字平台过程中,需要抓住交付基础设施、API和架构治理、数据自服务、创新实验基础设施和监控体系、用户触点技术这五个支柱。今天我们就来谈一谈API、架构治理这些听起来非常技术性的概念与企业的数字化战略之间有何关系。

    企业资源服务化

    从1990年代起,企业资源计划(ERP)一直是企业信息化的核心议题。植根于供应链管理,ERP通过对企业内部财务会计、制造、进销存等信息流的整合,提升企业的计划能力与控制能力。然而近年来,在互联网的冲击下,传统企业开始面临全新的挑战。尤其是在互联网的去中介化效应影响下,原本在供应链上下游各安其位的企业突然间都被压缩到了“生产-流通-消费”这个极度精简的价值链中。药品购销两票制就是这个极简价值模型的直观呈现。在这个模型中,掌握技术优势和消费者入口的互联网企业有可能形成一家独大的超级垄断,挤死传统的流通企业,把生产企业变成自己的OEM厂商,这是传统企业对来自互联网的竞争者恐惧的根源。

    为了对抗互联网企业的竞争,传统企业最好的办法不是硬拼互联网上的技术和流量,而是在自己擅长的领域开战:把自己多年积累的线下资源变成线上服务,构建起本行业的线上生态系统,不仅支撑本企业的线上经营,而且为上下游周边企业提供线上经营的平台,从而把线下优势转化为线上优势,以资源优势对抗技术优势。

    为了支撑企业资源的服务化,在设计在线服务的API和架构时需要考虑以下问题:

    • 平台架构和API的设计应该注重开发者体验
    • 在API的背后,应该从业务功能的角度出发划分合理的限界上下文和服务边界,对外提供高内聚低耦合的服务。
    • 在服务边界之间,应该考虑使用异步的事件机制实现服务之间的通信,来解耦领域模型,客观地描述运行时间比较长、甚至本质上不可能立即完成的操作。
    • 为了方便使用,应该提供API网关作为所有服务使用者的单一入口,在API网关背后去处理众多内部IT系统的复杂性。
    • 整个API架构应该以微服务的风格呈现,避免典型SOA架构中普遍存在的过于复杂的ESB编排逻辑。

    ERP之后是什么?

    进入2010年代以来,“后ERP时代”这个说法不断被提出。在谈到ERP的发展方向时,通常都会涉及业务与技术两个角度。例如一种观点认为,ERP需要从以流程为中心转变为以客户为中心,并且需要用好云计算、社交网络、大数据和移动化等新技术。

    ThoughtWorks认为,ERP在互联网时代的发展方向将是企业资源服务化(Enterprise Resource Servicification,ERS),通过数字平台的技术能力,把一家企业的资源融入一个行业的互联网生态,为企业铺下明确的数字化道路。

    API和架构治理解读

    下面我们来近距离看看,在“API和架构治理”这顶帽子下面,有哪些具体的问题需要被考虑到。

    开发者体验

    当企业资源以服务的形式对外提供,也就意味着不可能——像传统的IT系统建设那样——强迫别人使用这些服务。尤其是要把这些服务提供给第三方开发者、希望他们开发出形形色色的应用程序,那么服务的API是否易用就会很大程度上影响它能吸引到多少第三方开发者。ThoughtWorks第16期技术雷达还专门把开发者体验作为一个重要的技术主题。

    在讨论开发者体验时,可以从开发工具和开发环境的安装、配置、管理、使用、维护等角度来考量。具体而言,开发环境和测试环境是否能弹性地随需获得,开发/测试基础设施和持续交付流水线是否以源代码的形式提供并完全自动化,是否提供对主流开源软件的支持,是否提供可编程的、命令行友好的(而不仅仅是图形化的)工具界面,安全、数据访问权限等企业规章是否严重影响开发者的效率和感受,这些都是影响开发者体验的要素。

    服务边界

    和所有的面向对象设计一样,服务的设计应该是高内聚低耦合的:与一个业务相关的修改只在一个服务内部进行,并且一个服务的修改/部署不需要影响其他服务。和一个代码库内部的对象设计不同,每个服务通常有专属的代码库,并且由专人负责维护(而不是所有人拥有所有代码),因此服务边界的改变会带来更大的变更成本。所以,服务边界的划分需要投入精力认真对待。

    从设计原则上来说,服务的边界应该体现业务的边界,而不是单纯从技术角度出发划分服务边界。从业务功能的角度出发划分合理的限界上下文,以领域模型和领域事件的聚合为出发点来划分服务,更可能得出与业务边界一致的服务边界。随后再以业务目标驱动建设全功能一体化团队,就能做到业务、技术、团队三者对齐(康威定律再次起作用)。四色建模事件风暴等方法都能有效地实现领域驱动设计,从而建立起良好的领域模型及服务边界。

    事件驱动架构

    使用异步的事件机制实现服务之间的通信。对于运行时间比较长、甚至本质上不可能立即完成的操作(例如涉及人工操作),使用异步通信是合理的选择。即便不考虑响应的实时性,事件驱动的架构还表达了领域模型之间的松散耦合关系:跨领域的协作以事件而非方法调用的形式来表达,系统追求最终一致性而非强一致性。这一结构准确地映射了真实世界中多支相关但独立的团队之间的协作关系,避免了过度依赖其他服务的响应速度或可靠性等服务质量指标,使服务真正具有技术上的独立性。

    在设计系统时,借助事件风暴方法,可以通过领域事件识别出聚合根,进而划分微服务的限界上下文。当出现跨多个聚合根的事件时,可以很自然地将其实现为异步的领域事件,从而获得与领域设计高度吻合的实现。关于如何设计和实现领域事件,可以参阅ThoughtWorks咨询师滕云的文章

    在实现事件驱动的架构时,当然可以沿用传统的SOA架构中的消息中间件。但由于微服务架构中,业务逻辑都存在于各个服务内部,没有庞大臃肿的ESB(稍后我们还会详谈这个问题),因此消息机制也不需要强大的服务编排(orchestration)能力。RabbitMQ这样标准的消息代理当然很好,也有很多系统(例如Bahmni)采用更简单的做法:领域事件发生时,以ATOM格式发布;关心特定领域事件的其他领域模型则订阅特定的ATOM feed主题。这种基于HTTP的事件传播方式最大的好处就是简单,几乎不需要增加新的软件就可以实现。不过这个方案在处理低延迟的场景时表现不佳。

    公共网关

    微服务提供的API粒度与客户端的需求不同,所以客户端一个请求经常需要多个服务;服务端和客户端之间可能需要通信协议转换;不同的客户端对数据的需求不同,例如浏览器客户端需要的信息可能多于移动客户端;服务的终端信息(主机+端口)可能变化;不同数据片可能由不同的服务终端来提供——以上这些因素都指出:有必要对服务做一层门面封装,提供API网关作为所有服务使用者的单一入口点。

    API网关处理请求的方式有两种:一种是直接代理/路由给合适的服务;另一种是由一个请求扇出/分发给多个服务。API网关可能针对不同客户端提供不同的API,可能包含针对客户端的适配代码。横切需求(例如安全)也可能在API网关实现。

    当服务数量变多、API网关变大以后,维护一个通用的API网关会增加API网关层的复杂度,导致一个独立的“API团队”出现,协调和沟通的工作量加大。这时可以考虑引入公共网关的一个变体:为特定前端设计的后端(Backend For Frontend,BFF),即为每个前端应用提供一个单独的API网关,使对齐业务的一体化团队能够拉通前后端开发、而不必等待“API团队”完成他们的backlog。

    API网关可以实现为一个独立的服务端应用,其代价则是增加一层复杂度(和出错的可能性)。为了降低这一代价,可以考虑用Zuul等工具来实现API网关。

    微服务SOA拓扑

    与传统的SOA架构相比,所谓“微服务”最大的特点可能就在于没有一个重量级的ESB。重量级的ESB有其历史原因。在2000年代业界刚开始采用SOA时,很多企业尽管把业务系统包装成了web服务,但IT团队的组织结构并没有发生改变,仍然是由一组人集中式地掌管整个业务流程——只不过系统集成的方式不再是直接的方法调用,而是服务编排(orchestration)。原本存在于集成代码中的复杂逻辑,现在被转移到了ESB中。而这个“ESB团队”成了IT交付的瓶颈:不论发布事件的服务还是消费事件的服务、或是编排逻辑本身的改变,与事件相关的变更都需要通过ESB团队。这个团队的backlog堆积起来,使得每个服务、每个应用都无法提供快速响应。

    微服务架构更重视服务与业务的对齐。贝索斯所说的“两个pizza的团队”不仅负责一个IT系统的交付,而且要负责用这个IT系统来支撑一个业务的成功。为了做到单个服务能够独立开发、独立部署、独立运行,这支团队应该能够在很大程度上掌控自己的进度,而不依赖于一个集中式技术团队的进度。因此微服务应该通过服务注册与发现机制获得自己需要的依赖服务、自己判断是否要直接调用或订阅依赖服务的事件,每个服务包含与其业务对应的复杂度,而不是把整个系统的复杂度集中在ESB和编排逻辑上。整个系统的架构(以及团队的架构)应该呈现为若干个端到端拉通的、与业务对齐的纵切服务,而不是一个横切的大块(ESB)覆盖所有业务。

    小结

    为了激活企业线下资源、打造行业线上生态,IT需要一套有效的服务API和架构治理方法。首先从领域驱动设计入手,划分出合理的限界上下文和服务边界,然后用异步消息机制来描述领域事件。设计好的服务通过API网关或BFF暴露给前端应用,把依赖关系和集成逻辑约束在与业务对齐的一体化团队内部。在整个服务架构的设计中,需要保持对开发者体验的关注。顺畅地将企业资源服务化,这是企业数字化旅程的第二步。


  10. 数字化企业的交付基础设施

    前文中我们说到,传统企业在逐步建设自己的数字平台过程中,需要抓住交付基础设施、API和架构治理、数据自服务、创新实验基础设施和监控体系、用户触点技术这五个支柱。那么,当我们谈“交付基础设施”,我们究竟在谈什么?怎样的交付基础设施能加速数字化项目的交付?

    什么是交付基础设施

    云时代的研发环境应该以原生支持云计算的方式提供、管理和维护。在提供基础的弹性计算能力的IaaS平台之上,交付基础设施负责为交付团队提供便利的、最好是自助式的工作环境,让交付团队专注于交付软件的功能性需求,而不必操心软件功能之外的“脚手架”工作。按照ThoughtWorks数字平台战略的定义,这些脚手架包括:

    • 弹性基础设施,即交付团队使用底层云计算平台的方式,既包括各种虚拟机和镜像的管理,也包括生产环境的水平伸缩能力。
    • 持续交付流水线,交付团队编写的代码需要通过这条流水线最终变成可以上线运行的软件。
    • 部署运行时,软件在开发、测试、试运行、用户验收、培训、生产等各种环境需要部署的环境。
    • 监控,为交付团队提供生产环境(及其他环境)的可观测性,方便他们发现和解决问题。
    • 安全,把安全内建在软件的研发过程中,尽量避免因为人为失误造成安全隐患。

    从前这些交付基础设施脚手架通常是由每个交付团队的技术领导者(Tech Lead)来负责搭建和维护的。并且由于软硬件资源的稀缺和不灵活,团队经常需要微调自己的实践来适应不同的环境。所以,即使在同一家公司,各支团队所使用的交付基础设施也可能大相径庭。交付基础设施不一致、不规范的情况会迫使团队花费额外的精力去操心脚手架工作,并且使最佳实践不易推广普及。走上数字化道路的企业必定有大量的软件项目,尤其是微服务架构风格的引入会使企业拥有数量更多、单体规模更小的软件应用,此时交付基础设施不一致、不规范的情况就会对企业的数字化进程带来更大的阻力。

    云计算带来的弹性和灵活性让组织级的交付基础设施标准化、规范化成为可能。一个跨越项目团队的、组织级的交付基础设施团队现在可以在IaaS的基础上封装标准的脚手架,甚至把脚手架本身以PaaS的形式提供给交付团队。通过把整个企业优秀技术领导者的知识与经验内嵌在交付基础设施脚手架中,就降低了对单个交付团队的技术要求,帮助企业缓解优秀技术领导者难以获得的人才挑战。从这个意义上,以PaaS形式提供的交付基础设施本质上是技术领导者作为服务(Tech Lead as a Service)的云计算应用形式,它解决的是优秀技术人才的弹性和灵活性问题,让企业能够以一种创新的方式使用这些人才。

    架构师写代码吗?

    关于“架构师是否应该写代码”这个问题,业界有各种不同的声音。在敏捷的社区里,意见倾向于认为架构师需要写代码,因为这是他们获得关于技术决策的反馈和建立技术领导力的重要方式。将交付基础设施明确提出来,就给了架构师又一个清晰的编程目标——他们需要用代码的形式描述软件交付中的基础设施和最佳实践。除了培训、开会、代码评审等我们已经知道效率并不太高的方式以外,架构师对交付团队的指导和监管现在可以用实实在在的代码来承载。当交付团队不理解架构师说的某件事应该怎么做,现在他们更有理由要求架构师“show me the code”。

    交付基础设施解读

    下面我们来看看,在“交付基础设施”这顶帽子下面,架构师/技术领导者们究竟应该关心哪些问题,又有哪些最佳实践应该被纳入他们的视线。

    弹性基础设施

    允许交付随需获得计算能力。在微服务语境下,这种弹性有两层常见的含义:在生产环境下,服务可以随负载动态获得和释放计算资源,从而更高效地使用计算资源,更自动化地应对负载变化;在研发环境下,开发、测试、运维等不同角色可以随需动态获得完整的环境,从而统一环境、标准化研发实践、规范化研发能力,并且给研发提供体验更好的开发环境。

    为了实现弹性基础设施,一方面基础设施需要支持弹性,例如使用支持弹性计算的公有/私有云,并且有对生产环境的监控和自动化手段;另一方面应用本身需要有可扩展性,例如服务能分别独立部署、无状态化、容器化、有透明的前端负载均衡机制。有状态服务(比如数据库服务)的弹性伸缩问题是特别需要考虑的重要挑战。

    持续交付流水线

    用持续交付实践打通微服务的开发、构建、验证和部署流程。在数字化、服务化的背景下,众多互相依赖的微服务形成的系统架构,对构建、验证和部署造成更大的压力:各个服务有独立的代码库和构建流程,又需要随时能组合成可用的软件;构建产物需要有统一的存储管理;完整的运行时环境应该能按需获得;配置和部署应该能快速准确地完成。

    为了应对这些挑战,交付基础设施中应该包含完整的持续交付概念:流水线、环境管理、构建产物管理等。应该鼓励对服务虚拟化,最好是每个主机运行一个微服务,而不共享使用主机。应该包含配置自动化工具,例如Chef、Puppet等。在服务化的背景下,持续交付流水线需要体现服务间的依赖关系和团队间的协作关系,设计一个运转良好的流水线不是容易的任务。

    部署运行时

    交付基础设施应该包含生产系统所使用的运行时环境,并把生产环境前向拉通到验证和研发环节。为了在研发流程的出口得到服务化友好的交付物,最好是在整个开发过程中一直使用与生产环境近似的环境。例如开发人员应该使用全套环境随时验证,自动化测试和手工测试都基于全套环境开展。在这种情况下,环境的设置、管理、更新不可能由每个开发人员和测试人员自己进行,所以环境的管理更新必定是集中进行的,环境的设置必定是自动化的。

    在《技术栈管理:云时代的研发环境》一文中,我们已经介绍过“一个平台、两个PaaS服务、三个运行时环境”的技术栈管理理念。特别需要注意的是,如何将生产数据拉通到验证和研发环节。

    监控

    在微服务架构中,系统由多个小服务组成,且广泛使用异步通信,使问题和故障更难定位。因此交付基础设施需要提供全面可靠的监控机制,帮助交付团队了解系统的整体状况。

    监控的实现涉及日志、服务指标跟踪、业务语义综合监控等方式。在云环境下如何划分和管理监控的层级,监控系统如何无侵入的在各个微服务体系中收集故障和信息,如何有效管理监控的反馈环,如何在前后端分离和移动应用情况下收集和监控客户端日志,都是常见的挑战。

    安全

    当数字化、服务化IT系统的数量剧增,安全的设置会变得更加复杂。在微服务架构下,系统的安全性需要有一个整体的考虑。例如单点登录、服务间的身份验证和授权、各种防御措施等安全考量不应该下放到交付团队,而应该被涵盖在交付基础设施中统一提供、统一管理、统一更新。

    交付基础设施还应该鼓励安全实践内建(Build Security In),例如团队应该熟悉OWASP安全列表和测试框架、需求分析中应该包含安全需求和恶意用户需求、测试过程中应该包含安全性测试、应该进行自动化安全性测试并纳入持续交付流水线。这些流程与工作方法虽然不能完全以软件代码的形式承载,但它们同样是交付基础设施的重要组成部分。

    小结

    数字化、服务化的IT大背景会让企业开发和拥有的IT系统数量剧增。当企业IT交付更多地以“两个pizza团队”的形式组织,依赖于每个交付团队的技术领导者来搭建和维护一套完整高效的交付基础设施脚手架,这种期望即使不是完全不现实,也会对企业的人才积累提出非常高的要求。因此,企业应该集中优秀的技术人才(包括架构师们),打造一套标准的交付基础设施,充分考虑生产环境与研发环境的弹性、持续交付、部署运行时的统一、监控、安全等因素,并借助云计算的弹性和灵活性将其提供给交付团队。用便利的脚手架赋能一支能快速交付的团队,这是企业的数字化旅程的第一步。