敏捷画卷:中国软件史的精彩侧影

1

如果把软件开发当成一个谜题,数代的软件人在过去的 50 年里前赴后继地尝试解决这个谜题,不过到今天为止,全世界不管是码农还是码神,我们仍在这个谜题当中痛苦挣扎。

1965 年 ~ 1985 年,软件危机逐步浮现,这让刚刚进入科学管理时代的人们极其不爽。1931 年建成的帝国大厦只花了 410 天,还是提前完工,写个软件还能复杂过盖摩天楼?那肯定是方法有问题。

供职于洛克希德软件技术中心的 Winston W. Royce,在其 1970 年的论文 “Managing the Development of Large Software Systems” 中提出了一个长得像瀑布的流程,业界似乎找到了一款灵丹妙药,虽然这位搞了多年航天器的 Royce 老兄并没有在他的文章中提到任何瀑布相关的字眼。之后以 1988 年 CMM 的发布为重大里程碑,剩下的似乎就是沿着既定的路线,细化,标准化,量化,优化,再优化……

直到一线干活的人们发现事情其实不是这样,于是生长出了各家敏捷流派,以期解决 Fred Brooks 在 “No Silver Bullet” 一文里提出的复杂性(Complexity)、配合性(Conformity)、隐蔽性(Invisibility)、易变性(Changeability)这些现代软件开发中本质性(Essence)的难度。

2

中国用 20 年的时间迈过了西方 50 年的软件工程发展史。《敏捷中国史》中一个个鲜活的故事和严谨的数据考证一起,描绘了敏捷方法在中国软件产业的土壤中一步步发芽、传播的画卷,构成了中国软件史一个精彩的侧影。

《敏捷中国史》不仅帮读者在宏观层面理清了中国软件工程领域在过去 20 年里发展的关键脉络;一系列从业者的经历巧妙串联,更让读者从个体视角体验历史,了解众多普通的软件人是如何参与着历史和创造着历史。

我个人的从业经历跟敏捷中国史的跨度大致重叠,因此格外有感触。阅读每篇内容,本以为早就遗忘的画面在脑海之中栩栩如生地一页页闪过。

3

我还记得 2001 年在新加坡的一个社区图书馆,第一次翻开 Kent Beck 的 Extreme Programming Explained 给我带来的冲击。

不过一番琢磨之后,我得出了几个轻率的结论:

  • 迭代开发玩不转,甲方的预算和立项流程根本不可能让乙方这么干(我当时在新加坡的一个系统集成商工作);
  • 结对编程太奢侈,没有老板会让团队这么干;
  • TDD 真是好东西,不过只要团队里有一个人不这么干,其他人也干不下去,让所有人都用 TDD 不现实。

所谓纸上得来终觉浅,直到四年之后,我自己卷起袖子,在全面采用敏捷实践的团队沉浸工作了几个月,才真正体会了那些理念和实践的价值和可操作性。让我很有共鸣的是,文中不少人和公司初步接触敏捷的经历和感受其实也是类似。

看到敏捷中国大会的举办,大型通信企业的敏捷转型,DevOps、设计思维、精益企业、精益创新的推广,ThoughtWorks 相关的记述把影像拉回我的记忆。那些熟悉的名字把他们的面容带回我的眼前,与他们合作中体验到的酸甜苦辣又从心中流过。虽然是文中很多事件的亲历者,我看到的也只是点点滴滴,从没想到有人能如此全局又生动地把握和呈现当时的脉动。

说到合作,我 2007 年加入 ThoughtWorks,那是我真正认识熊节的开始,不过我知道熊节却要更早一些。那时经常在 JavaEye 上津津有味地旁观一个叫熊节的人跟人吵架,觉得这人吵得很有见解,而且吵得很有文笔。于是,我有了无数的机会在现场和邮件里看熊节怼人,以及被熊节怼,从中学到很多。

为什么专门把怼人拿出来说?这其实跟 ThoughtWorks 的风格有关。不满足于现状,寻求更好的理念、方法和工具,追求软件卓越,这是 ThoughtWorks 的使命。ThoughtWorks 期望员工不盲从主流意见,要持怀疑挑战的态度,以求找到不一样的路径,做到比当前更好。熊节就是这种风格的典型代表。

4

20 年中国软件工程方法的变迁也是中国软件行业追赶国际先进水平的历史。巨大的国内市场已经让我们成为一个软件大国,但我们在工程方法领域并没能够取得匹配的领先地位。

我理解《敏捷中国史》不仅仅是对历史的记录和纪念,更是以史为鉴。文中一个个致力于改善工作成效的一线从业者,致力于推广新方法新工具的布道者,正是他们吸引了一批又一批热衷软件开发的人加入进来,一起推动行业的发展。


以上内容来自ThoughtWorks中国区总经理张松为《敏捷中国史》所作的序。在这一系列课程中,作者熊节也用部分章节叙述了在ThoughtWorks与敏捷的不解之缘。

于 2005 年进入中国以后,对中国的敏捷社区发展起到了极大的推动作用。面对行业环境对敏捷并不积极的状况,ThoughtWorks 选择了主动造势。“敏捷中国”开发者大会就是这样启动起来的。

ThoughtWorks 初入中国

从进入中国开始,ThoughtWorks 就在行业中扮演了敏捷先锋的角色。

2005 年,被西安丰富的高校资源和高新区政府的热情态度所吸引,ThoughtWorks 在西安软件园落户,目标是服务中国本土客户。

同年,ThoughtWorks 在国内获得了三个项目:与西安市高新区政府合作的单点登录系统建设项目,与河北省地税局合作的电子政务项目,以及与厦门好望角信息技术有限公司合作的网游物品交易平台项目。其中第三个项目是唯一来自私企的项目、唯一的互联网项目,客户对敏捷方法的配合程度很高。

ThoughtWorks 在这个项目上也投入了很大的资源,Martin Fowler、Fred George、Jim Webber、Perryn Fowler 等全球敏捷和开源社区的知名人物都曾参与过这个项目的架构与开发。在后来的几年中,好望角是 ThoughtWorks 在中国最重要的标杆项目。

Martin Fowler 的中国之行后,一批 BJUG 和 JavaEye 的网友(如徐昊、李默、熊节、陶文、钱安川等)陆续加入 ThoughtWorks,为 ThoughtWorks 在中国业务与影响力的初期发展做出了贡献。

除了在网络社区和《程序员》杂志为主的报刊发表言论之外, ThoughtWorks 开始积极参与国内的行业会议。

2005 年 12 月,熊节代表 ThoughtWorks 出席了微软企业决策者峰会金融行业论坛,并做了题为《敏捷软件开发》的主题演讲。

2006 年 6 月,Martin Fowler 再次来华,出席第十届中国国际软件博览会暨中国软件产业发展高峰论坛,并做发言演讲。第十届软博会由信产部、发改委、科技部三部委主办,发言嘉宾包括时任信产部副部长娄勤俭、科技部部长徐冠华等政府官员,以及来自东软、用友、神州数码、CA 等知名企业的高管,是当时国内档次最高的 IT 行业会议。

但 ThoughtWorks 在这些行业会议上的亮相并不成功。熊节在微软企业决策者峰会上的演讲反响平平,几乎没有得到任何反馈。

Fowler 在软博会的演讲中介绍了 ThoughtWorks 给国外一家投资银行做的项目案例:这个原定计划 8 个月完成的项目,由于采用了迭代式的开发方法,在两个月的时候已经有部分功能上线,并给客户带来真正的经济效益,随后几个星期就收回了整个项目的投资。

当时台下的听众一片茫然。由于政府主导的重点行业信息化工程固有的特点,在当时绝大多数中国 IT 业者的概念中,软件项目就只有一次预算和一次交付(多发生在年初和年末)。一个项目中有多次交付、多次上线、项目还没结束软件已经开始赚钱,这样的事情对于很多人来说不是信不信的问题,而是根本无法理解。

对敏捷方法、迭代式交付基本理念和运作方式的缺乏了解,使得中国的 IT 同行一时无法认识到 ThoughtWorks 独特的价值。

在早期的三个本地项目中,ThoughtWorks 与西安高新区政府和河北省地税局的合作都出现了不愉快,为时不长即告结束,只有与厦门的私企好望角的合作持续了较长时间。Martin Fowler 在短暂的中国之行中已经看到,当时的中国市场并不特别重视软件的价值,行业更关注压缩项目成本,包括缩短项目周期和挤压人力成本,因此更倾向实施成型产品而非定制开发。

这种对软件独特价值的忽视和对成本的极度重视,导致 ThoughtWorks 在与北大方正等典型的本土 IT 企业谋求合作机会时屡屡遭遇尴尬。为此,ThoughtWorks 决定自行营造行业氛围,主办大型行业会议,倡导对软件价值的重视。

“你还不走吗?”熊节问郭晓。

此时已经是夜里 11 点多,熊节跟 CSDN 的一名工作人员正在调试会场的音响设备,猛然回头,发现郭晓坐在会场中间的座位上,两眼呆呆地望着天花板。

“噢,”被熊节问到,郭晓好像突然回过神似的,“再等一会儿。你们不是也没忙完嘛。”

说完,郭晓又进入了入神的状态。他低头看看一张纸卡片,然后又抬头望着天花板,过一会儿开始念念有词,手还不时挥舞两下。熊节好奇地走到郭晓身旁,探头看他手上的卡片写了什么。

“这是我明天的 cheat sheet,”郭晓主动拿起卡片给熊节看,只见卡片正反两面密密麻麻地写着英文小字,“明天不是我讲第一个吗?得抓紧时间练啊。”

“总共 40 分钟演讲还需要准备?”熊节诧异地问道,“你这种外企高管不是张口就来吗?”

“哪儿啊,”郭晓笑着说,“你可不知道,我最怕对着一大群人演讲了。紧张啊,紧张起来腿都会抖,跟筛糠似的。何况这是第一次在中国做这么大规模的演讲,更紧张。所以我得先练好,练得熟了就不那么紧张了。”

说完,郭晓又把注意力放回他的卡片上,一时抬头呆看天花板,一时念念有词手舞足蹈。直到其他人调好所有设备准备关灯,他才离开会议室回房间睡觉, 这时时间已过午夜。

首届敏捷中国开发者大会在 2006 年 6 月 3 日在北京新世纪日航饭店举行,大会的主题是“敏捷释放软件价值”。这次会议由 ThoughtWorks 和 CSDN 共同主办,JavaEye 等网上社区以协办单位身份帮助宣传。除 Martin Fowler 外,ThoughtWorks 还派出了来自澳大利亚的 Scott Shaw 和来自英国的 Liv Wild 作为演讲人,公司创始人 Roy Singham 也专程到中国参会。现场到会听众约 600 人。

在时任 ThoughtWorks 中国区副总经理郭晓的开场演讲中,他一方面迎合了行业对成本的重视,列举 Forrester 的数据说明采用敏捷开发方法可以大幅节省软件项目成本。然而他给出的数据中,产品总体缺陷率的大幅下降或许可以用测试驱动开发和持续集成等实践来解释,但项目速度(如果“速度”定义为项目完工的总体时间的话)的大幅提升是敏捷解释不了的,只能解释为实施敏捷的团队(即 ThoughtWorks 的团队)能力更强。

另一方面,他也指出“软件的功能不等于价值”,因为“实际上很多功能最终用户根本不会用”,反而造成软件的维护和扩展困难。敏捷方法借由充分的沟通避免开发不必要的功能、借助技术和管理手段保障软件的可维护性与可扩展性,从而释放软件的价值。

这个演讲用一种贴近中国市场现状的方式阐述了敏捷的价值:没有超越时代地谈论“迭代式发,而是从避免功能浪费和延长软件生命周期的角度提出论述。后来几年的实践证明,这个演讲的逻辑,比起 Fowler “原汁原味”的敏捷论述,在中国市场上更容易得到认可。 在随后的主题演讲中,来自英国的业务分析师 Liv Wild 具体介绍了如何进行“充分的沟通”。

当时的ThoughtWorks 在启动项目时会采用一套称为“QuickStart”(快速启动)的信息收集方法,以高互动、可视化的工作坊形式厘清项目的愿景、利益相关人、业务流程、功能范围、设计风格、技术架构,并形成明确的交付计划。当时典型的 QuickStart 需耗时 4 周,后来这套方法在中国被压缩到两周甚至一周。即使放下迭代式开发不谈,这套需求获取的方法本身也大大领先于当时国内 IT 企业普遍的水平。

主题演讲之外,ThoughtWorks 的咨询师还在会场组织了一系列“敏捷游戏”,邀请现场听众参与。“折纸帽子”游戏阐述了在开发过程中与客户频繁沟通和反馈的重要性,“搬运气球”游戏阐述了迭代式交付对于消减项目风险的价值。 这两个游戏都是 ThoughtWorks 在印度举行的新员工入职训练营 “ThoughtWorks 大学”(简称 TWU)的课程内容,这种参与性强、寓教于乐的形式,在中国 IT 行业的专业会议中前所未见,令与会者耳目一新。

大会结束后,李默建立了“敏捷中国”邮件列表,并根据签到记录邀请与会者加入。在后来几年中,这个邮件列表中发生了一系列颇有深度的讨论,成为国内敏捷先行者们又一个重要的在线言论阵地。这个邮件列表随着一年一度的“敏捷中国”大会不断成长,正是中国敏捷社区在逆境中砥砺前行的剪影。

结语

在行业信息化项目的甲方与软件企业的高层领导都对敏捷缺乏兴趣的几年时间里,在一线打拼的一批敏捷实践者没有停下脚步。

他们在探寻轻量级开源架构方案的同时,也在各自的工作中采用敏捷方法,尤其是用户故事、迭代管理、持续集成和测试驱动开发等实践,在需求管理、项目管理、配置管理和质量保障等方面获得了扎实的能力提升。

他们在 JavaEye 等在线论坛交流心得、在 BJUG 等线下社区展开深入的探讨和分享。在交换信息、答疑解惑的过程中,他们也结识了同道的朋友,获得了并肩前行的动力。

在整个行业对敏捷缺乏认同的岁月,《程序员》杂志为这些“草根”实践者们保留了一个难得的言论阵地,使他们得以持续发声。当全球敏捷社区的领导者 ThoughtWorks 进入中国,这些敏捷实践者们迅速在其周围聚集,并以行业大会的形式喊出了响亮的宣言。

随后,一些对于软件能力有着最迫切诉求的大企业回应了这一号召,向着“敏捷中国”这面大旗靠拢。


作为中国敏捷十余年发展历程的亲历者与推动者,资深老程序员熊节从整个中国 IT 发展进程审视敏捷,通过本课程带你一起重新经历一代程序员的青葱热血岁月,与你一起梳理中国软件工程领域 20 年发展的关键脉络。

不止于敏捷,你会切实感受到整个中国 IT 行业、乃至中国经济的发展。

作者简介:

熊节,现任宝尊电商成都研发中心总经理,曾任 ThoughtWorks 总监咨询师、 CSDN 技术主编。

IT 行业打拼 18 年,在金融、零售、政府、电信、制造业、全球医疗等行业的信息化建设方面有着丰富经验。翻译了《重构》《软件工艺》《实现模式》等行业重要著作,是中国 IT 浪潮中敏捷发展的领航者之一。熊节拥有利物浦大学 MBA 学位。

插图提供:

虎头锤,旅居墨尔本的老程序员,北邮博士、北大硕士,15 年编程经验。目前从事支付系统相关业务,曾转战区块链、通信行业。敏捷倡导者、手绘爱好者。

Share

如何驱使行为改变

职权不是决定因素

绝大多数工程师对于变革有种无力感。这种无力感源于这样的想法:我不是管理人员,没有足够的职权,无法改变自己的组织。当这种感觉足够强烈的时候,它作带来的挫败感会使我们失去进一步行动的能力。

然而这种无力感无论是中层管理者、执行副总裁甚至首席执行官都会存在。论及变革,没人具有足够的权力。这是因为变革中最核心的问题不是改变组织的结构、战略或文化,而是改变人的行为与意愿——改变人的工作方式以及在工作中所关心的内容。 职权的确会让某些事情变得容易,有些大范围的变革还必须巧妙借助职权的力量——比如涉及组织结构调整的改变,但它不一定是变革成功的决定因素。

从2007年开始,国内很多软件企业开始尝试采用敏捷方法,其中不乏有中高层领导以行政命令推行敏捷方法的做法。我曾经在敏捷中国大会上碰到一位朋友,他跟我讲了他们公司的故事:他们公司里负责研发副总裁听说了敏捷方法之后,对于测试驱动开发非常感兴趣,认为这是提高软件质量的好方法。于是在公司内要求所有的研发人员都必须要开始采用测试驱动的方法进行编程,并制定了相应的考核制度。而最后的结果是,研发人员为了应对考核指标,写了很多无用的测试,花了额外的时间不说,对软件质量的提高并没能带来实质性的帮助。最后纵使在报告上有不错的测试覆盖率统计,绝大多数人——包括那位研发副总裁自己——都不认为这次变革是成功的,因为人们的工作方式和内容并没有真正地发生改变:他们并没有变得更注重质量,也没有采用自动化的方式来保证软件质量不受破坏。

比起职权我们更应该学会影响他人,驱动他们在行为上发生改变 。无论是否具有职权,成功地驱动变革都不是件容易的事情。拥有职权仍然需要小心使其发挥作用,没有它也不意味着我们完全被束缚了手脚,不能采取任何行动。关于职权在变革中的作用我们将在下一章讨论,在那之前首先需要讨论的是成功驱动变革的核心因素——如何驱使行为改变。

什么可以带来行为的改变?

有一种可以带来行为改变的方法,其主要模式可以表述为“分析—思考—改变”:

  • 针对特定问题向人们展示数据或者分析结果;
  • 通过数据或者分析的论证影响他们的思考方式;
  • 借由新的思维方式带来行为模式的改变。

这种方式天然受到工程师的喜爱。作为工程师,我们接受过严格的逻辑思维训练。我们相信数据以及理性的分析,并愿意根据分析结果调整自己的行为。我们理所当然地认为这是最合乎情理也是最客观理性的方法。不过令人意外的是,变革管理大师John P. Kotter的研究表明,“分析—思考—改变” 这种方式很少能真的发挥效果。他说对于“只对数据感兴趣的工程师”可能会有效果,但以我个人的经验来说,哪怕是对工程师,这招也没那么管用。

理智的局限

工程上的很多问题我们虽然知道优劣之分,但是难以量化和度量。比如Ruby到底比Java和.NET在研发效率上快多少?RESTful的架构风格比SOA(Service Oriented Architecture)在开放性和扩展性上好多少?采用结对编程的方式和不采用结对编程的方式在代码质量上有多少提高?我们明确地知道Ruby比Java和.NET的开发效率高、RESTful比SOA有更好的弹性、结对编程产出的代码质量更好,但是一旦论及多少和数量,却不是那么容易能够给出明确的答案。

那么我们要如何改变Java和.NET程序员的思维,让他们认为Ruby是值得尝试的?要如何改变具有多年SOA经验的架构师的思维,让他们相信RESTful是更好的选择?要如何改变从没有结对经验的项目经理,让他们理解结对并不是浪费时间和金钱呢?

此外,分析结果对人们思维的改变,远没有达到我们想象的那种程度。哈佛大学商学院Andrew McAfee教授富有洞鉴地指出:我们通常会高估新技术带来的效果和影响,大约三倍,而低估已有技术和方法的效能,大约也是三倍,只有在新旧技术相差十倍的时候,我们才会有明显的改变意愿。换句话说,如果我们希望发生的改变没有在某方面带来十倍以上的提升,人们的思考方式不会因为我们的分析而发生明显的变化。所以当你希望重构代码结构以获得更好的架构时,当你希望更换不同的数据库以获得更好的开发效率时,通常不会受到特别热烈的响应——这些改变虽好,但是远没有十倍以上的差距。

最后,也是最重要的,分析结果的确能够改变人的思维,但却很少能够有效地改变人的行为方式。比起思维,情感更能驱动人们作出行为的改变,而很少有分析结果能真正地打动人心,建立情感上的纽带。

几年前在推广Ruby的时候,我和几位同事组织过厦门Ruby用户组,期间我分享过一个主题:从面向对象技术发展的历史来看,为什么Ruby是更好的面对象语言。会后有位朋友私下找我沟通,他说:“从你提供的资料和分析,我或许相信Ruby这门语言有它的根源和发展;但是我实在无法想象在企业应用的开始中去使用它,因为我是企业级Java开发人员啊。”

这番话很有代表意义,Java对于这位朋友而言,与自我认知有关。他和Java有更深的感情联系——这是定义我的角色和身份的技术。所以他虽然能在思维上认可我给出的资料和分析,却很难真的作出改变。

感受带来改变

那么什么才是带来行为改变更有效的方法呢?答案是:目睹—感受—改变。

照John P.Kotter的话讲,这是“大多数在变革中取得成功的组织都会采用模式”。其模式可表述为:目睹是指通过一些戏剧性的、引人注意的情景或可视化展示(visualization)来帮助人们发现问题;在看到问题之后,引发人们情绪和感受上的冲击,让他们开始从内心深处作出反映,削弱那些阻碍变革的情绪,比如自满、愤怒、怀疑或恐惧。增加紧迫感、乐观或者信任等有益于变革的情绪;这些正向情绪开始改变原有的行为,或者强化新的行为模式。

这种模式常常会产生较深远的影响,除了能够带来行为改变之外,在这个过程中所产生的戏剧性的、引人注意的情景或是可视化展示的手段会被广为流传,对越来越多的人产生影响,它所产生的效果是非常惊人的。这里我有两个案例要和大家分享。

第一个例子是戏剧化的展示。

我曾经帮助某客户团队实施自动化测试。在这团队里,从部门领导到团队成员,普遍认为他们所处理的系统功能点杂、业务流程多变,实施自动化测试会有很高的脚本编写成本。此外,这家公司有独立的测试部门,但是由于这个团队做的是内部运营系统,测试部门并没有给予足够的重视和配合,反而是一再降低他们系统的测试优先级。从测试部门获得帮助几乎是不可能的奢望,所以所有人都认为自动化测试是不可行的, 但是他们愿意给我一个机会。

经过调研之后,我觉得数据驱动的功能测试非常适合这个团队的现状。经过了不到一周的准备,我迎来了第一次进度展示。为了帮助团队树立信心,并强调测试成本并没有想象中的那么高,我设计一个略有戏剧性的场景:在展示会当场把针对一个流程的测试,扩展为针对八个流程的测试——而这个改动仅仅涉及测试数据,没有任何测试代码的变化。也就是说,在2分钟内,测试案例个数提高八倍(当然,基数是一个自动化测试案例,这没什么可骄傲的)。没有行业数据、没有理论分析、没有特殊工具,就像变魔术一样提高八倍。

最终的结果也颇具戏剧效果,团队里除了产生了乐观的态度和对我的信任之外,我还收到一张速写。画了一个胖子站在投影幕布前面,幕布上整屏幕翻滚着测试执行日志和弹出的测试用浏览器。画的作者跟我说,当时他看到这一幕感到非常震撼,从来没有想过自动化测试可以凭空出现一般突然多出很多。此后我也在这个团队之外的地方听到了这个故事,与之相伴的是,很多实施自动化测试成功的案例。

第二个例子是没那么戏剧性的可视化展示。

很多年以前,那个时候微软刚刚推出WPF(Windows Presentation Foundation),我所在的一个团队就采用了这个技术,为某客户开发新一代客户关系管理系统。由于WPF在当时还是比较年轻的技术,很多实践都不是很成熟,我们做了很多的探索。一段时间以后,团队感觉到测试覆盖率偏低。不是测试报告上展示出来的数值低,而是根据缺陷的修复时间和缺陷的泄漏数量得到的一个直观感受。当时团队里有人有这样一个议论:因为WPF本身有很多自动生产的代码,同时很多交互功能以及样式渲染与WPF API绑定过死而难以测试。那么WPF最高测试覆盖率到底是多少?如果在WPF上最高测试覆盖率只能做到40%,那么我们的测试覆盖率还是很高的呢。毕竟我们使用了MVP(Model-View-Presenter)模式,对于可测试性还是有很大帮助的。

这是一种典型的自满情绪,这种情绪对于变革的推进是非常不利的。因为在这种情绪支配下,行为惯性很大且非常不容易接受其他建议。于是我们做了一个很简单的可视化展示,把当前团队的测试覆盖率写在一张卡片上,然后把这种卡片悬挂在团队最显眼的地方。如果我没有记错的话 ,初始数据是32%。然后有意思的事情就来了,这个数据当时是全办公室的最低值。毕竟其项目都是Web项目,以当时的能力要做到100%也不是不可能的。很多其他组的同事路过的时候,都会询问一下原因——毕竟这个数值实在太低了。可能是问得人多了终于有些同事受不了,开始反思32%是不是我们能做到的最好结果?答案当然是:不是,很多新的技术和技巧被发明出来用以改善WPF下的测试问题。几个月以后这个组的测试覆盖率接近60%。

有意思的是很多年后Android开始流行,另外一个项目组再做Android项目的时候遇到了类似的问题,也是测试覆盖率较低。他们准备放弃的时候,有人给他们讲了这个故事,特别提到了当时我们组挂在外面的那个测试覆盖率卡片。只不过讲故事的人说:我记得看到的数字是百分之五十几,所以你们做到这个数字也该不难吧。

“分析—思考—改变”与“目睹—感受—改变”最大的差异就在于,前者着眼于具体问题的分析和解决,而后者注重建立情感上的联系。成功的变革最终都会解决一些具体问题,但单刀直入式地从解决问题开始,并不一定是驱动人们作出行为改变的最佳方式。而当人们在情感上建立联系之后,往往会作出更有效的分析,也更容易接受思维上的改变。

在这个过程中最核心的步骤是感受, 通过情感上的冲击消除不利于改变的情绪而提升变革的意愿。变革的意愿越强烈,成功改变行为的几率就越高。有两种感受与改变的意愿密切相关:信任感与紧迫感。所以驱动行为改变最重要的步骤是获取信任、确立愿景以及提升紧迫感。


更多精彩洞见,请关注微信公众号:ThoughtWorks洞见

Share

细说API – 文档和前后端协作

在上一篇文章——《细说API – 重新认识RESTful》中介绍了如何理解和设计RESTful风格的API,现在我们来聊聊如何有效的呈现API文档,以及前后端协作的方式。

我经历过一些没有文档的项目,前后端开发者坐到一起口口相传,或者有些团队用 word、pdf 来编写 API 文档。API 文档的缺乏给前后端协作带来困难,在缺乏专门工具的情况下,编写和维护文档是一件工作量巨大的事,人工处理也非常容易出错。

本文将会介绍三种方案来解决前后端协作的问题:

  1. 基于注释的 API 文档:这是一种通过代码中注释生成 API 文档的轻量级方案,它的好处是简单易用,基本与编程语言无关。因为基于注释,非常适合动态语言的文档输出,例如 Nodejs、PHP、Python。由于NPM包容易安装和使用,这里推荐 nodejs 平台下的 apidocjs。
  2. 基于反射的 API 文档:使用 swagger 这类通过反射来解析代码,只需要定义好 Model,可以实现自动输出 API 文档。这种方案适合强类型语言例如 Java、.Net,尤其是生成一份稳定、能在团队外使用的 API 文档。
  3. 使用契约进行前后端协作:在团队内部,前后端协作本质上需要的不是一份 API 文档,而是一个可以供前后端共同遵守的契约。前后端可以一起制定一份契约,使用这份契约共同开发,前端使用这份契约 mock API,后端则可以通过它简单的验证API是否正确输出。

基于注释的 API 文档

apidocjs 是生成文档最轻量的一种方式,apidocjs 作为 npm 包发布,运行在 nodejs 平台上。原理为解析方法前面的注释,使用方法非常类似 javadoc 等程序接口文档生成工具,配置和使用都非常简单。因为只是解析代码注释部分,理论上和编程语言无关。

安装:

npm install apidoc -g

在需要输出文档的源代码中添加一个一个注释示例:

最小化运行:

apidoc -i myapp/ -o apidoc

即可在 apidoc 中输出静态的 html 文档目录。如果指定配置文件 apidoc.json 可以定义更多的操作方式,也可以自定义一套 HTML 模板用于个性化显示你的 API 文档,另外在输出的 HTML 文档中附带有API请求的测试工具,可以在我们生成的文档中尝试调用 API。

使用 apidocjs 只需要添加几个例如 @api、@apiname、@apiParam 等几个必要的注释即可,值得一提是 @apiDefine 可以定义变量避免重复书写,@apiGroup 用来对 API 分组,@apiVersion 可以再生成不同版本的文档。

基于反射的 API 文档

apidoc 的缺点是需要维护一些注释,当修改源代码时需要注意注释是否同时被更新。不过如果你使用的是 Java、.Net 等强类型语言,就可以利用强类型语言的优势。

在这个领域最好用的文档工具当属 swagger,swagger 实际上是一整套关于 API 文档、代码生成、测试、文档共享的工具包,包括 :

  1. Swagger Editor 使用 swagger editor 编写文档定义 yml 文件,并生成 swagger 的 json 文件
  2. Swagger UI 解析 swagger 的 json 并生成 html 静态文档
  3. Swagger Codegen 可以通过 json 文档生成 Java 等语言里面的模板文件(模型文件)
  4. Swagger Inspector API 自动化测试
  5. Swagger Hub 共享 swagger 文档

通常我们提到 swagger 时,往往指的是 swagger ui。而在 Java 环境下,可以通过 Springfox 来完成对代码的解析,再利用 swagger 生成文档,下面我们给一个简单的例子看怎么给一个 Spring boot 项目生成文档。

首选加入依赖(gradle 同理):

全局配置:

我们的 controller,需要定义一些必要的注解来描述这个 API 的标题和解释,我们返回的 user 对象是一个简单 value object,swagger-annotations 包下面提供了很多注解可以满足更多的定制需求。

然后访问你的 context 下的 /{context}/swagger-ui.html 页面,你会看到一个漂亮的 API 在线文档。swagger 的文档上能看到具体的字段定义和 Model,如果修改了 Model,再次编译后则可以自动反应到文档上,这也是反应了强类型编程语言的优势之一。

基于契约的前后端协作

在过去的开发中,往往是后端开发者占主导,像上面的两种方案中,直接注释、反射通过生成 API 文档。

但前后端分离后让合作方式发生了变化。传统的方式往往是服务器开发者完成了 API 开发之后,前端开发者再开始工作,在项目管理中这样产生时间线的依赖。理想的情况下,在需求明确后,架构师设计,前后端应该能各自独立工作,并在最后进行集成测试即可。

后端开发者可以根据文档实现接口,最后按照文档联合调试即可,甚至通过契约生成 API 调用和数据承载的 VO (Value Object),减少工作量。如果 API 的提供者想做的更为完善一些,可以使用契约文件来验证实际 API 输出输出是否合理。

契约测试

当我们使用契约文件来提高前后端协作开发的体验,其中很重要的一部分就是契约测试,关于契约测试,我们一般指的是 Martin Fowler 提出的概念 —— “消费者驱动的契约”

简单来说,就是前后端开发者协定好后,由消费者驱动,通过编写 API 调用层相关的代码,可以直接生成契约文件。由于一个 API 可以被多处消费,所以消费者驱动可以更好的管理契约的变化(如果 API 验证契约时不能通过,说明契约被破坏了,可以在 CI 上马上反应出来)。

(Pact 契约测试模型)

写契约测试的博客非常多,就不展开赘述了。我把契约测试放到了前后端协作这个部分,是因为契约测试的前提是建立在前后端良好的协作下实现的。“契约测试”关注的是契约,而不是测试。

实际工作中,退一步说,制定好契约就可以完成基本的开发工作,对契约测试、验证可以让前后端协作变得更为可靠。如果你现在还没准备好使用契约测试的话,也不必焦虑,手动定义契约可以让前后端协作先运行起来。

而如果你恰好使用了 Spring boot 全家桶的话,不妨看看 Spring cloud contract。

使用 Swagger Yaml 契约

前面在讲 swagger 的时候,提到了Swagger Editor,使用这个工具可以通过编写 API 定义文件(Yaml格式),它提供线上版本,也可以本地使用。

后端通过生成 API 定义文件,就可以进一步完成生成 HTML 静态文档、模拟 API 数据等操作。

前端开发者可以通过 swagger 的 node 版本 swagger-node 自带的 mock 模式启动一个 Mock server,然后根据约定模拟自己想要的数据。 关于在前端使用的 mock server,实在太多,而且各有优劣,在附录中有一个清单以供参考,不再赘述。

使用 RAML 契约

使用 Swagger Yaml 契约或者 Pact 契约都能在一定程度上完成契约测试、生成文档、mock 等工作,但是我们在实际工作中发现这些工具和平台的契约规则并不相同。

Swagger 在生成文档上非常优秀,然而在契约测试上不及 Pact,反之亦然。

随着引入微服务和开放的互联网项目越来越多,前后端协作的问题越来越明显,而解决上述问题的工具和技术并不通用。好在业界早已认识到这个问题,于是一些组织提出了 RestFul API 统一建模语言 (RESTful API Modeling Language),也就是 RAML。

围绕着 RAML 这一标准,构建出 API 协作的工具链,设计、构建、测试、文档、共享。

其他前后端协作实践

中心文档服务器

在一个大型的团队中,可能会有几十个以上的项目同时提供了 API,这种情况下如果每个应用都各自提供API文档就会变得很难管理,如果将 API 文档绑定到应用服务上会带来一些无意义的损耗。可以使用一个集中地服务来存放这些文档,类似于 github 的私有仓库,swagger 同样也提供了类似的服务 – swaggerhub.com

即使不使用 swagger ,我们可以构建出 HTML 文档然后每一次输出部署到一台静态服务器,也是非常容易的事情。

  1. 如果是开源或者对外的 API,可以借用 GitHub Page 来创建我们的文档服务
  2. 针对团队内部,诸多云服务商均提供了静态服务器,例如 AWS 的 S3

管理契约文件

既然是契约文件,就不应该是API提供者或者消费者单独拥有的,即使只有一个调用方,至少是前端、后端共同拥有的。

那么契约文件应该怎么放呢?

我们之前一直放到API的代码仓库中,然后给所有的人添加了权限。后来发现这样做非常不方便,于是单独增加了一个管理契约文件的 git仓库,并使用 git 的submodule 来引用到各个涉及到了的代码仓库中。

将契约文件单独放置还有一个额外的好处,在构建契约测试时,可以方便的发送到一台中间服务器。一旦 API 契约发生变化,可以触发 API提供的契约验证测试。

附录:API 文档工具清单

  • 使用或调研过的,API 文档/契约生成工具
    1. apidoc
    2. swagger
    3. blue sprint
    4. RAML
  • 使用或调研过得 mock 工具清单
    1. wiremock
    2. json-server
    3. node-mock-server
    4. node-mocks-http
  • HTTP 请求拦截器
    1. axios-mock-adapter
    2. jquery-mockjax
  • 契约/API 测试工具
    1. Spring Cloud Contract
    2. Pact
    3. Rest-Assured

更多精彩洞见,请关注微信公众号:ThoughtWorks洞见

Share

挑战者银行的逻辑

[摘要]

自2009年始,欧美银行业出现了大量的挑战者银行(Challenger Bank)。它们改变了消费者对银行的认知和交互行为,并撬动传统银行机构进行数字转型,挑战者是如何发起挑战?被挑战者又是如何应对?当传统银行优势消弭,未来的银行路在何方?


2009年,30岁的澳大利亚青年Josh Reich在给投资人第一份提案的首页上写道「银行靠着让客户更糊涂挣钱」,而后他以「Simple」为名建立了堪称第一家的纯数字银行;次年,64岁美国人Vernon Hill在伦敦开张了Metro Bank 。上一次有店面的零售银行(High Street Bank)在英国开张还要追溯到100年前。这位靠连锁快餐店发家的美国大亨,面对媒体反复提到:「我们是一家零售商,只是恰好做银行而已」。这两位离经叛道者开启了挑战者银行(Challenger Bank)的序幕,越来越多数字和金融行业的从业者开始思考或行动,如何挑战甚至颠覆这个千年的古老行业。

(上一次有店面银行的开张要追溯到100年前,MetroBank是100年来的第一家)

银行的传统优势被撬动

挑战者撬动的正是,传统银行业的三大优势,它们是:

  1. 政策保护带来的行业准入门槛;
  2. 坐拥庞大的金融市场供需数据带来的信息不对称优势;
  3. 极低的客户流失率带来的极高获客成本。

首先,欧洲货币一体化的进程触发了对欧洲金融体系的重新塑造,从PSD1到PSD2再到「开放银行」的政策布局,显示着欧洲一体化的设计者对欧洲金融业僵化体制改造的决心,加上民粹主义抬头加深大众对于金融精英阶层的天然敌意,欧洲银行业经历着一个极具变化性的政策环境。

其次,专享客户数据、就能为客户提供独一无二的金融解决方案,这一银行长久以来的经营逻辑,正在被10年来崛起的互联网巨头以及获其背书的数字颠覆者打破,数据和人工智能的发展使得不需要全面的独享数据也可以提供精准的金融解决方案;同时,传统依靠信息垄断获得独享利润,在消费者中还落得一个「作恶」的名头。

最后,传统银行的极高客户粘性,其根本在于集中的服务提供者、繁琐的过程、传统的信任观念,极高的替代成本也推高了获客成本,对大银行而言,一个新客户的获客成本可能在1500至2500美元间,较高获客成本也为银行业获得了天然的门槛。而随着服务提供者的增多,更加便捷的过程、信任观念的改变使得新入市场的服务商可以通过更低廉的价格获客,再加上新互联网时代的病毒式营销方式,依靠高获客成本守住行业利润将会越来越难。

因此,当政策不再成为保护者反而成为塑造者、依靠信息不对称获利既难以实现也不道德的情况下,获客成本可能大幅下降,银行业可能面临前所未有的变化。

在这一背景下,近十年的时间里,银行业在国际范围内发生了一系列有趣的变化:

挑战者银行崛起

在Josh Reich之后,特别是2009年以后的英国,出现了大量挑战者银行。

欧洲传统银行成本收入比(Cost-to-income Ratio)维持在64%,因复杂度更低的IT系统、更加简单的产品组合(相对的合规要求降低)、和更低租金,使得一个纯数字的小型挑战者银行的成本收入比可能达到48.5%,这也成为挑战者银行创业者的基本逻辑——尽可能低降低业务的复杂度,以换取更低的IT和运营成本

考虑到传统银行业极高的获客成本,挑战者银行通常从对金融需求更短期和及时、更容易接受新服务的年轻消费者开始,并选择更加频繁、门槛更低的金融场景,例如:短期借贷款、信用卡、日常支出管理、支付、小额存款等。

针对这些轻量级的金融场景,挑战者银行们更是利用病毒营销,在短时间内积累大量的客户,然后依此从监管者手中获得更全面的银行业务执照。在法规允许的范围内,开辟更多挑战现状的金融产品,要么承担更大的运营风险,要么提供更高额的客户回报,本质都是用较低的运营成本换取客户量

当业务模式和客户量趋于稳定,挑战者银行通常面临两个选择,其一为被传统大银行收购,成为其数字战略的重要部分;其二为获得金融机构或互联网巨头的注资,开始独立的国际化进程,期待演进成全新的银行业态,保持颠覆者成色。

概括来说,以下逻辑支撑着以欧洲为主的挑战者银行们崛起和演进:

  1. 以纯数字银行开始,以获得更低的运营成本;
  2. 专注年轻客群,以信息集成者或中间人的身份快速获得客户;
  3. 通过积累的客户量,从监管者手中获得银行业务执照;
  4. 用低运营成本交换更高回报的金融产品大规模获得客户;
  5. 逐渐建立稳定的金融业务,开始选择性并入传统大银行、或考虑多市场拓展。

2014年2月,数字银行领域的明星Simple以1.17亿美元的价格加入欧洲银行巨头BBVA,这间成立于2009年的纯数字银行在成立仅仅五年后选择加入已经有150年历史的BBVA,既显示出大部分以Simple为代表的挑战者对规模化的无奈,也揭示出传统银行业不甘被颠覆的决心。

事实上,直到2016年底,BBVA在美国的业务Compass才完成了与Simple系统的整合——经过近三年后,BBVA在北美的Simple客户才可以真正并入其业务系统中。而保证其能够顺利完成Simple和Compass的系统集成的基础来自于2016年初开始的对OpenAPI平台的投入。

(过去10多年来,在欧美,从单纯的银行线上渠道,到无数挑战者银行以颠覆的姿态出现)

传统银行的数字转型

挑战者银行并未对传统银行业产生系统性的颠覆,但其对消费者行为的重新塑造确实触发了传统银行大规模的重构,其转型策略有如下三个阶段:

  1. 抱持开放的心态,持续不断进行遗留系统改造,建设中台能力,培育金融基础设施的建设能力;
  2. 对金融生态的基础设施进行投资,通过合作网络降低获客成本,同时倒逼核心银行系统演进;
  3. 孵化全新的业务组合和渠道,并开始寻找新的商业模式。

以BBVA数字转型的进程为例,「开放」从数字转型的一开始就成为其核心关键字,2016年2月,BBVA宣布上线其在后来有深远意义的Open API平台,为互联网金融创业公司提供API接口,最初开放的四个中台能力接口包括:

  1. PayStates:聚合BBVA卡支付信息方便第三方数据分析或商业情报;
  2. Connect:为第三方应用授权访问BBVA服务;
  3. Accounts:为第三方应用授权访问BBVA账户信息;
  4. Card:为电商网站绑定BBVA支付方式。

(BBVA的API市场,为金融创新者提供身份验证、转账、存款账户、以及多卡管理的标准能力)

在OpenAPI平台建设的同时,BBVA继续投资技术基础设施,例如:2016年5月,BBVA宣布与Red Hat合作,在IaaS(Infrastructure-as-a-Service )、PaaS(Platform-as-a-Service)、以及云管理平台等领域进行深入合作;10月,BBVA宣布与Amazon Web Services合作,以获得处理每天5亿4200万笔交易的云计算能力。

更加现代和健壮的基础设施,使得BBVA能够更容易接入外部合作伙伴或内部的独立业务,形成规模效应的同时减低运营成本——2016年11月,BBVA与CRM领域巨头Salesforce合作,在西班牙完美实现全手机开户;同时西班牙银行业务系统的现代化帮助其完成北美Compass业务和Simple的整合。

以云和数据技术为依托的基础设施,也使得BBVA有能力引入完成收购或整合更多的解决方案或产品,甚至孵化出全新的业务模式。

2016年底,BBVA继续向英国移动银行Atom追加投资,结合自有数据和开放能力,成为在2018年前具备交付开放银行能力的欧洲少数金融机构之一。

其旗下的投资机构Propel Venture Partners管理着17家不同类型创业公司的股份,类型覆盖区块链、数字签名、保险、理财、员工福利等,为BBVA提供技术、客户体验、和解决方案的可能性。

(BBVA旗下投资管理平台所管理的解决方案)

在业务创新领域,BBVA已经开始探索将数据作为企业服务的新模式——2016年11月,「Commerce 360」上线,为中小型企业提供交易(来源于丰富的POS数据)分析能力。

(BBVA的企业交易信息分析业务)

2018年底,BBVA十个主要市场国家中的6个都以实现「数字化拐点」——超过50%的客户交互在数字渠道上完成,这间150年历史的西班牙银行正在有条不紊地进行其数字化演进。

银行的未来

纵观欧美银行业过去10年的发展,前五年挑战者银行蓬勃兴起、后五年传统银行大规模转型,挑战者更多是催化剂而非颠覆者,我们最终迎来怎样一个银行的未来?

讨论银行未来的角度,是先假设一个极端的场景,当现有银行所有的竞争优势荡然无存,那么谁建立了全新的竞争优势,谁就建立了未来的银行。

如果监管者的目标是为了建立更加持续的金融体系,而非稳定现有金融体系,那么谁能够帮助监管者打造这一目标,谁就获得了新的竞争优势;

如果利润来自于与客户的金融共赢,而信息不对称不再可能获得利润,那么谁能够帮助客户在每一个金融场景下获得成功,谁就获得了新的竞争优势;

如果高获客成本变得极低,而银行难以依靠垄断客户获得利润,那么谁能不断发现新的价值点,通过规模效应和生态系统获益,谁就获得了新的竞争优势。

那么未来银行的佼佼者多半拥有以下特点:

  1. 她一定能够更快地响应政策的变化,甚至参与和引导监管者的决策
  2. 她一定不再主要从信息不对称中获得利润,而成为跨行业金融场景的服务提供商,从交易双方的金融成功中获利;
  3. 她一定坦然面对获客成本和客户忠诚的持续下降,不断建立新的价值链,在更广阔的金融生态中保持独特的竞争优势。
Share

十年来我所经历的技术碎片

时间回退十年,我还在职业中的第二家雇主——IBM CSDL。虽然不能完全记清楚每个工作的细节,但从现在看来,不管是当时的行业形态,还是工作环境,都跟十年后的今天不可同日而语。那时,微信恐怕都还在策划中吧(事实上2011年才推出),朋友同事间的沟通,更多是依靠那个叫MSN的聊天工具,自然如今它也没了踪影。

作为一直晃荡在这个行业的从业者,我也有趣地看到这十年来,我所接触的工具和技术上的变化,有见地的深层理解无从谈起,权当是对一些碎片的记忆吧。

工作协同的系统是企业内不可或缺的部分,Lotus Notes在十年前虽然已经进入陈暮晚年,但仍然是IBM以及它的客户组织所依赖的协同系统,邮件,存档,甚至一些复杂的自动化流程,都会基于那个沉重的文档数据库(自然不是后来出现NoSQL文档数据库)展开。

我清晰地记得,当时作为QA的我,每天都不得不打开数据库服务器位于加拿大的测试案例库,然后切换到另外的待测系统界面,运行测试,然后再把测试结果一一记录到数据库中,在此期间我有相当的视角,只能盯着保存中的沙漏,以及那个标记了远端服务器地址的图标,它对我来说是个陌生的异域名字,但仅此而已,唯一能做的就是长时间的等待,直到几十秒钟后,它告诉我保存成功。

在我进入ThoughtWorks时,已经是Google Suite的天下(事实上ThoughtWorks也曾经是使用Lotus Notes的,你能想象那些ThoughtWorker怎样在谩骂中忍受?),即使没有条件的公司,也会逐渐享受到一些本土化的系统产品的优待,如日中天的钉钉自不必说,企业QQ,企业微信,Worktile,Teambition,Slack这些工具或平台,都在尝试无缝的链接企业和员工之间的工作流程和信息连通,以期求在效率上和数据共享上达到前所未有的高度。

在我从QA转向Dev之后,另外两个技术在这十年间的变迁,对我来说印象同样深刻不已。

一是前端技术。如今虽然已经有了现象级甚至垄断的框架——Angular、Vue和React,但在十年前,我们明显看到的是前端技术方兴未艾,百家争鸣。借着AJAX概念的提出,智慧的开发者在短短几年内发明不同的工具框架,你追我赶,各领风骚。IBM是Dojo.js的Sponsor,jQuery则是社区的宠儿,后来微软加入对其官方的支持,ExtJs则是独立的一方存在,作为具有优雅而丰富的UI类库的代表。其他还有很多,ThoughtWorks早期员工陈金洲的Buffalo以及OPOA(One Page One Application)也是在那期间出现。

这些十年前的竞争者们,以新鲜的视角,把企业以及开发者的目标聚焦在前端这个领域,当然是应了中国互联网的发展。关注用户体验,尊重标准是那个时期时髦的关键词。而今现象级的框架,则更像是把开发的理念做了影响深远的革新,大幅度提升了开发者的体验。

第二个就是虚拟化和云技术。我记得不到十年前,IBM还在实验室里尝试用虚拟化技术帮助创建大规模(性能)测试所需要的机器资源(事实上ThoughtWorks也做出过类似尝试),业界还在争论公有云的安全问题而更看好私有云,以及评价云服务提供商应该是IBM和微软更能胜出,是因为只有它们两家公司做到了从基础设施硬件,到操作系统,数据库,到应用软件一应俱全,更容易提供完备的SaaS,PaaS,XaaS。

而今天看来,结果怎样已经是不言自明,Amazon的AWS和IaaS一家独大了。当然更重要的是,Amazon做到了对开发人员体验的关注和尊重,开发体验远远胜过其他厂商。我们很容易看到,至少现在不尊重开发人员体验的第三方服务提供商的赢面没有那么大。

很难想象,这十年会是我所经历的。技术的剧烈变迁,一如时代的变迁,超出了大多数人的认知。很难讲清楚技术和时代之间谁影响了谁,但就十年而言,我们多少可以看出一些印记,以及理清一些脉络,比如固守技术投资的企业多半会逐渐失去市场竞争的态势,比如对外部技术跃迁的无视多少是体现了企业内臃肿的组织结构和随处可见的官僚气味,比如开发者体验这个毋庸置疑的已经既定的存在。

作为个人,我也很庆幸,在这十年间,即使不是亲身经历,也是耳闻目睹了这些技术上的变化,相比较新进入这个行业的人来说,我也许更能明白从历史纸背透过来的必然性,以及在面对又一新鲜炫目的技术变化时,以更快的速度理解它,以及它可能的命运。

就今天而言,即使在开发者的世界,分工的细致化已经势不可挡,全栈即便不是奢望,也需要时间的沉淀,当越来越多的人止步于好用,够用,会用,是不是就足以理解要解决的问题,并且可以完美的解决,我抱有怀疑。

当然,更进一步,我觉得永远都不会错的,就是每个开发者都不应该局限于用最新最酷口碑最好的技术来解决所有遇到的问题,满足于尝鲜和随波逐流,毕竟,问题才是最重要的。


亲临现场聆听Martin Fowler、Neal Ford、Rebecca Parsons等国际软件巨匠的十年回顾。现在购票可享最后五天七折优惠!

更多精彩洞见,请关注微信公众号:ThoughtWorks洞见

Share