3.6

查看英文版

3.6 遗留系统现代化

概述与动机

遗留系统是支撑着这个世界运转的系统。核心银行分类账、税务和福利引擎、空中交通和国防系统、保险保单管理,以及社会所依赖的政府登记系统,往往已经运行了数十年。其中许多是用 COBOL(面向商业的通用语言)或其他较老的技术编写的,而它们至今仍在处理着大多数关键交易。“遗留”并不是一个贬义词。它意味着这个系统有足够的价值才得以存活下来,重要到一旦失效就是灾难性的,也古老到无法安全地改动它。遗留系统现代化,是在不破坏这些系统所提供的关键服务的前提下,对它们进行改进、迁移或替换的一门学科。

这在很大程度上是企业和政府机构面临的问题,也正是那些规模最大、最广为人知的 IT 失败案例的发生地。全世界绝大部分重大交易,至今仍会触及大型机系统。大型机构生产环境中很大一部分代码,是用较老的语言编写的,由一批日益老龄化、不断萎缩的专业人才维护着。政府机构承担着最沉重的负担:数十年间沉淀下来的法定义务、比历届政府任期都更长久的采购和预算周期,以及不容中断的公民服务。最大的风险并不在于这些系统年代久远,因为其中许多运行得非常出色。真正的风险在于,维护这些系统所需的知识正随着人员退休而流失,所依赖的平台正变得日益昂贵和受限,而”干脆重写它”的诱惑,正是这个领域历史上一些代价最惨重的失败的根源。

本章涵盖真正有效的渐进式现代化模式(绞杀者无花果模式和抽象分支)、如何评估和优先排序遗留风险、对大型机和 COBOL 资产的管理维护、数据迁移和双系统并行运行的纪律,以及最重要的一点()如何抵御大规模重写的诱惑。核心信念是:成功的现代化改造几乎总是渐进式的、由证据驱动的,并且持续不断地交付价值。它绝不会是一场持续数年的大爆炸式行动。

关键原则

  • “遗留”意味着有价值、是基础性的,而不仅仅是年代久远。 在你动手之前,要尊重这个系统所做的事情;它承载着数十年来千辛万苦积累下来的业务规则。
  • 渐进式改造几乎总是优于大爆炸式改造。 在一个稳定的接口背后逐块替换;持续交付价值,让风险始终保持在较小的范围内。
  • 大规模重写是默认的失败模式。 完整重写通常会超支、交付不足,并最终被取消;对这种冲动要深怀警惕。
  • 你无法让自己不理解的东西实现现代化。 在替换之前,先逆向工程并记录下系统的行为(包括那些没有文档记录的规则)。
  • 数据迁移是这类项目最容易夭折的地方。 数据往往比任何人预期的都更陈旧、更混乱、更盘根错节;要把它当作头等大事来规划。
  • 让新旧系统并行运行,以建立信心。 双系统并行运行并进行比对,能在切换之前发现差异。
  • 按风险和价值排序,而不是按年龄排序。 优先让风险最高、价值最大的系统实现现代化,而不是单纯挑选最古老的那个。
  • 在更换引擎的同时,保持灯火不熄。 服务必须全程保持运行;对关键的公民服务系统或金融系统而言,没有可以接受的停机时间。

建议

用绞杀者无花果模式进行渐进式现代化改造

绞杀者无花果(以那种缠绕树木生长、并逐渐取而代之的藤蔓命名)是安全现代化改造中最主力的手法。在遗留系统前面放置一层路由层(API 网关、门面或代理)。然后,逐个能力地,在现代系统中构建替代实现,并把那一片流量重定向到新系统上,其余部分仍留在遗留系统上。随着时间推移,新系统不断壮大,旧系统不断缩小,直到最终可以被退役。这种方式持续交付价值,让每一次改动都保持微小且可逆,避免了高风险的一次性切换,并让你可以在任何时刻暂停或重新排定优先级。这与大爆炸式改造正好相反。在你围绕着遗留系统进行替换的同时,它依然在持续运行,持续发挥价值。

对内部接缝使用抽象分支

当你需要替换一个被系统中许多部分依赖的组件时,使用抽象分支(branch-by-abstraction)。在现有实现之上引入一层抽象(一个接口),把调用方迁移到依赖这层抽象上,在同一层抽象背后构建新的实现,切换过去(通常借助功能开关、逐步进行),最后移除旧的实现。这让一个大型组件可以在主开发分支上渐进式地被替换,而无需一个长期存在的分支,让系统全程保持可发布状态。它与绞杀者无花果模式天然搭配:门面处理外部接缝,抽象分支处理内部接缝。

有意识地评估和排序遗留系统的风险

在开始现代化改造之前,先对整个资产建立一份清醒的清单和风险评估。为每个系统在业务关键性、技术风险(过时程度、不再受支持的平台、安全暴露面)、改动频率,以及至关重要的知识风险(还有多少人能够维护它,他们距离退休有多近)等维度上打分。把各个系统绘制在一张风险与价值的网格图上。优先让那些同时具有高风险和高价值的系统实现现代化。对于那些稳定、很少改动、被充分理解的系统,即使它们年代久远,也可以考虑放任不管,因为一个没人需要去改动的、能正常运行的系统,并不是一个紧急事件。这种评估,能把”一切都又老又可怕”这种想法,转化为一条站得住脚、有先后顺序的路线图。

管理维护大型机和 COBOL 资产,而不只是替换它

并非每一个大型机或 COBOL 系统都应该、或者都能够安全地在短期内被替换。近期的优先事项往往是管理维护:在知识随人员退休而流失之前,先把它捕获下来。记录代码中所承载的业务规则(其中很多都没有文档记录,也无法复原),投资于能固定当前行为的自动化测试,从而让未来的改动更安全,招募并交叉培训维护人员,并对周边的交付实践(源代码控制、持续集成(CI)、自动化测试)进行现代化改造,即便核心部分保持不变。在确实需要现代化改造的地方,优先选择通过现代 API 暴露遗留能力(封装)作为第一步。对自动化的 COBOL 到现代语言的转换工具要保持谨慎,因为它产生的代码虽然能运行,却往往原封不动地复现了难以理解的逻辑。这里最稀缺的资源是理解力,而不是算力。

把数据迁移和双系统并行运行当作项目的核心

大多数现代化改造工作中最艰难、风险最高的部分是数据。它体量庞大、质量参差不齐、并且充满了数十年间积累下来的、没有文档记录的含义。对数据进行画像和清洗,把旧模式明确映射到新模式,构建可重复的自动化迁移,并配以完整的对账(记录数、校验和、业务汇总数),这样你就能证明没有任何数据被丢失或篡改。用双系统并行运行(parallel running)来降低切换的风险:让新旧系统在同样的输入下并排运行,并比较输出结果,直到新系统与旧系统的匹配程度达到你所信任的阈值。只有到那时才切换,并且始终保留回退的能力。对于真正关键的系统,要分批迁移和切换,而不是一次性全部完成。

应对大规模重写的诱惑

抛开那个混乱的旧系统、从零开始构建一个干净的新系统,这种冲动非常强烈,而对于大型关键系统来说,它几乎总是错误的。完整重写会低估藏在”丑陋”代码中的价值(边界情况、监管规则,以及真实用户所依赖的、与缺陷保持兼容的行为),耗时远超预期,在结束之前不交付任何价值,而且常常在耗费巨资之后被取消。默认选择渐进式现代化改造。把重写留给那些平台确实已经无法维持、渐进式路径也已被穷尽的情况。即便如此,也要通过绞杀者模式把重写分解成可以独立交付的部分,而不是一次大爆炸式发布。当领导层要求进行整体重写时,坚持问这个问题:头三个月能交付什么价值?如果项目进行到一半就被叫停,会发生什么?

权衡:优点与缺点

方法优点缺点
绞杀者无花果模式(渐进式)持续交付价值、风险低、可逆、服务保持运行整体周期更长、必须并行运行两套系统、集成开销
大爆炸式重写从零开始、新代码没有遗留约束失败率非常高、结束前没有任何价值、成本巨大、业务规则可能丢失
封装(用 API 包装)快速、低风险、无需触碰核心即可实现访问方式的现代化核心部分仍是遗留系统;只是推迟而非解决了根本风险
原样保留(管理维护)没有项目风险;短期内成本最低知识和平台风险持续累积;最终会被迫采取行动

根本的权衡在于转型速度与失败风险之间,而遗留系统现代化正是这种权衡表现得最为失衡的领域。那种”快速、干净”的大爆炸式重写只是一种幻象,它反复带来的却是所有结果中最慢、最昂贵的那一种:一个被取消的项目,加上一个依然未被现代化的系统。渐进式方法感觉起来更慢,并且需要并行运行两套系统,但它们能全程交付价值,让风险保持微小且可逆,是经过实证检验的可靠路径。真正需要判断的抉择,是在继续管理维护一个稳定的遗留系统一段时间,和现在就开始渐进式替换之间做出取舍。应该让风险的演变轨迹(尤其是知识风险)来驱动这个决定,而不是对老旧技术的不适感。

与团队讨论的问题

  1. 你们有没有一个地方可以在遗留系统前面放置一层路由层?如果没有,要创建一个需要付出什么? 绞杀者无花果模式依赖于一道接缝:一个 API 网关、门面或代理,你可以通过它把某一项能力逐一重定向到新的实现上。许多老系统并没有这样的接缝,所以现代化改造的第一个增量,往往就是先构建这个拦截点,而这项工作很容易被低估。带上当前的集成关系图,讨论在不做大爆炸式切换的情况下,可以在哪些地方按能力拦截流量。如果哪里都没有,那么对某个内部接缝使用抽象分支,或许就是应该采取的第一步。没有路由层,你就没有渐进式路径,而这恰恰是组织最终被推回那条通常会失败的重写之路的原因。

  2. 你们是否真的把自己的资产绘制在一张风险与价值的网格图上,还是你们的路线图只是由”哪个系统感觉最老”来驱动的? 本章坚持认为,应该优先让高风险、高价值的系统实现现代化改造,并有意识地放任那些稳定、很少改动、被充分理解的系统不管,即便它们已经很古老。如果没有一张明确的网格图,注意力就会流向抱怨声音最大的地方,或者最不”时髦”的技术,而真正的定时炸弹(一个只有两名维护者、且都临近退休的关键系统)却在一旁静静等待。在业务关键性、技术风险、改动频率和知识风险这几个维度上给每个系统打分,然后从右上角开始依次排序。把这张网格图带到会议上,作为共同的地图。知识风险应该获得最高的权重,因为它是唯一一个只会不断恶化、而且一旦人员离开就无法挽回的因素。

  3. 切换的时候,你们要如何证明没有一条记录被丢失或篡改?谁来在这份证据上签字确认? 数据迁移正是这类项目最容易夭折的地方,而信心来自对账:新旧系统之间匹配的记录数、校验和以及业务控制总数,再加上双系统并行运行,在相同输入下比对输出,直到两者的一致程度达到一个很高的阈值。对于一个福利系统或分类账系统而言,一处差异就意味着一名公民被少发了福利,或者丢失了一分钱,所以这份证据必须能说服审计人员,而不仅仅是工程师。现在就决定你们要对账哪些总数、什么样的信心阈值才能触发切换,以及新旧系统要并行运行多久。全程保持回退能力,并分批而不是一次性完成切换。你们在双系统并行运行期间发现的差异,通常是必须予以保留的、没有文档记录的遗留规则,所以要把每一处差异都当作一次发现,而不仅仅是一个缺陷。

  4. 你们的哪些遗留系统是在被管理维护,哪些是在被主动替换?这个划分是谁做出的? 本章有意在”值得原地稳定维护的系统”(记录规则、增加特征化测试、交叉培训维护人员)和”值得渐进式替换的系统”之间划出一条界线,而这两者需要截然不同的资金和人力投入。对大型组织而言,危险在于渐变:一个被标记为”暂时维护”的系统,会在不知不觉中变成”永久维护”,直到最后一位维护人员退休,而这个选择最终会在危机之下被迫替你做出。相互竞争的考量,一边是维护成本和平台过时的问题,另一边是替换所带来的风险和干扰,而知识风险应该成为压倒天平的那个因素,因为它只会不断恶化。带上风险与价值网格图、每个系统的维护人员数量和退休时间表,以及一个明确负责”维护还是替换”这一决策的责任人。在企业和政府资产中,要为每个系统指定一个评审周期和一位负责人,因为一个从没人重新审视过的分类,本质上就是一个没有人真正做出的决定。

  5. 当领导层要求进行完整重写时,你们的标准回应是什么?你们能展示渐进式路径在头三个月能交付什么吗? 大爆炸式重写是默认的失败模式,然而它却不断获得资金支持,因为一张白纸很容易被推销,而绞杀者无花果模式却不容易。一个大型团队需要一套演练过的应对话术,这样论证才能靠证据取胜,而不是靠会议室里谁的职位最高。真正的张力在于,有些平台确实已经无法维持,重写确有必要,所以答案不能是一概拒绝:它必须权衡是否仍存在渐进式的接缝,以及维持旧平台运行的真实成本。带上一个渐进式第一增量能交付的价值、同类重写项目的历史失败率,以及把任何拟议中的重写分解成可独立交付部分的方案。在政府机构中,一个被取消的、持续多年的项目会在众目睽睽之下耗费公共资金,所以要坚持要求任何重写都能尽早交付价值,并且即便中途被叫停,也不会造成全盘损失。

  6. 在理解这些业务规则的人离开之前,你们要如何捕获那些锁在最古老代码中的业务规则? 一个遗留系统中的大部分价值,是数十年来各种边界情况、法规和与缺陷保持兼容的修复所积累下来的、没有文档记录的行为,它们存在于一小群即将退休的专家的头脑中,而不是任何书面记录里。对大型组织而言,这是唯一一种人员一旦离开就无法挽回的风险,所以它理应比那些更显眼的平台工作更早获得资金支持。与之相对的拉力是,知识捕获(文档记录、特征化测试、逆向工程、交叉培训)给人的感觉是一种不交付任何东西的开销,而这恰恰是它总被推迟的原因。带上一份清单,列出谁掌握着关键知识、他们距离离开有多近,以及今天有哪些测试覆盖能固定住当前的行为。在受监管和公共部门场景中,要把老代码中承载的法定规则当作一项合规资产:悄无声息地丢失它们,不是一笔技术债务,而是一种法律风险。

行业视角

初创企业。 你的”遗留系统”是你自己仓促搭建的最小可行产品(MVP),而不是什么大型机:一个如今承载着营收、人人都害怕触碰的原型。不要重写它。用一个干净的接口把最令人担心的模块包裹起来,增加特征化测试来固定它的行为,并渐进式地把功能一点点剥离出来,让每一次小规模发布都能交付价值、缩小风险范围。你没有从零重建的跑道,所以可选性比优雅更重要。

小型企业。 你没有现代化改造团队,预算也很紧张,所以务实的做法通常是让一个能正常运行的系统继续正常运行:把那唯一理解它的人所掌握的知识捕获下来,把它纳入源代码控制并配上一些自动化测试,并依靠供应商或成品产品,而不是定制化的重建。把这个决定看作是”买还是造”的问题,当某项能力已经商品化时,优先选择购买。把你有限的精力,花在那个一旦失效就会让整个业务停摆的唯一系统上,而不是那个单纯看起来最古老的系统。

企业。 问题在于投资组合的规模:数十个系统、众多团队,以及遍及整个资产的知识风险。开展一次共享的风险与价值评估,把渐进式模式(绞杀者无花果模式和抽象分支)标准化,并把数据迁移和双系统并行运行当作一等重要的、配有大家都信任的对账机制的实践来对待。把现代化改造当作一个针对风险演变轨迹持续管理的投资组合来治理,而不是一堆零散的英雄式项目,并为管理维护和知识捕获明确编列预算,这样就不会有任何关键系统只依赖于一位即将退休的维护人员。

政府机构。 数十年间沉淀下来的法定义务、采购规则,以及不容中断的公民服务,让大爆炸式替换变得格外危险。优先选择渐进式的绞杀者无花果迁移,分片切换,通过对账和长时间的并行运行来证明没有一条公民记录被丢失或算错,并全程保留回退能力。采购要求应该要求数据可移植性,并要求披露业务规则,而不是接受不透明的转换。任何持续多年的项目都必须尽早交付可审计的价值,并且即便中途被叫停,也要能经受住公众的审视。

示例

初创企业。 一家成立三年的初创公司,其最初的最小可行产品(MVP)如今已经变成了自己的一种”遗留系统”:一个仓促搭建、如今却承载着真实营收、人人都害怕触碰的原型。团队没有选择重写,而是用一个干净的接口把最糟糕的模块包裹起来,增加特征化测试来固定它当前的行为,并在几个月的时间里逐块把功能迁移出来。每一次小规模发布都能交付价值,并缩小那个令人担心的部分,因此这家初创公司获得了一个可维护的系统,而无需拿整个公司去赌一场自己负担不起的从零重建。

企业。 一家大型保险公司在一个大型机 COBOL 系统上运行保单管理业务,这个系统可靠,但改动成本高昂,且由一小群临近退休的工程师维护。这家保险公司没有选择重写,而是用现代 API 包装大型机,并采用绞杀者无花果模式:新的报价和购买以及自助服务能力,被构建在一个现代平台上,并通过一个门面进行路由,而核心保单记录则继续留在大型机上。与此同时,团队为这套 COBOL 系统记录业务规则,并增加特征化测试。经过数年时间,能力一项接一项地从大型机上迁移出去,每一次发布都在交付价值,直到剩余的核心部分能够按照这家保险公司自己的节奏、而不是在危机之下被退役。

政府机构。 一家社会保障机构必须对一个已有数十年历史、向数百万公民发放福利的计算系统进行现代化改造,这个系统既不能被中断,也不能出现错误的发放。在研究了同类的失败项目之后,该机构否决了大爆炸式替换方案。取而代之的是,他们对数据进行画像和清洗,构建配有完整对账机制(对照控制总数)的自动化迁移,并让新的福利计算引擎与旧系统并行运行长达数月,把相同的申领案件同时输入两个系统并比较每一次计算结果,调查每一处差异(这往往会发现必须予以保留的、没有文档记录的遗留规则)。只有在新系统与旧系统的匹配程度达到非常高的信心水平之后,他们才按福利类型逐一切换,并全程保留回退能力。这个绞杀者门面让公民在整个过渡期间,感受到的始终是一项连续不断的服务。

业务论证:动机、投资回报率与总拥有成本

遗留系统现代化有一个不同寻常的业务论证,因为最大的成本往往是不作为的成本,而最大的风险恰恰是现代化改造项目本身。不进行现代化改造所不断累积的成本是具体可见的:在过时平台上不断上涨的维护和许可成本、日益稀缺且昂贵的专业人才、无法快速满足新的监管或服务需求,以及随着理解这个系统的人越来越少,灾难性故障的风险也在不断扩大。相比之下,现代化改造的成本很高,而如果以大爆炸方式进行,其失败概率确实很高。这正是渐进式方法对投资回报率如此重要的原因:它把一次巨大的赌注,转化为一系列各自都能带来回报、并且可以随时叫停的小赌注。

要向领导层证明这一点,需要重新构建这个选择。问题不是”要不要进行现代化改造”,而是”现在就渐进式地进行现代化改造,还是承担不断攀升的维护成本,日后在危机之下被迫接受一场风险更高的现代化改造”。量化维持现状的总拥有成本(平台和许可成本、稀缺技能的溢价、一次无法恢复的中断按风险加权后的成本),并将其与一个分阶段的项目相比较()后者随着每一个增量而降低风险和成本,同时让服务持续运行。至关重要的是,要坚持要求任何拟议中的重写都必须被设计成能够尽早、频繁地交付价值。一个三年内什么都不交付、并且一旦被取消就会造成全盘损失的项目,不是一项投资,而是一场赌博。支持绞杀者方法最有力的投资回报率论据,是可选性:价值持续不断地交付,而组织可以在任何时候调整方向。

反模式与陷阱

  • 大爆炸式重写。 持续数年、要么全部要么全无的替换方式,在结束之前不交付任何价值,而且常常在耗费巨资之后被取消。
  • 在不理解的情况下重写。 替换那些业务规则从未被记录下来的代码,悄无声息地丢掉真实用户和法律所依赖的边界情况。
  • 低估数据。 把数据迁移当作事后才考虑的事情,而它恰恰是整个项目中最艰难、风险最高的部分。
  • 跳过双系统并行运行。 在没有并行比对的情况下就切换到新系统,直到差异影响到真实的人之后才被发现。
  • 把自动化转换当作解决方案。 用机器把 COBOL 翻译成一种现代语言,就以为大功告成,结果产生了逐字复现旧逻辑、却让人无法理解的代码。
  • 按年龄而不是按风险来推进现代化改造。 把精力花在古老但稳定的系统上,而高风险、高变动的系统却在一旁等待。
  • 丢失知识。 让最后的维护人员退休,却没有捕获业务规则,也没有增加特征化测试。
  • 没有回退方案。 在没有退路的情况下切换,结果新系统在真实负载和真实数据下出现问题。

成熟度模型

  • 第 1 级,初始阶段。 遗留系统令人畏惧,处于冻结状态;改动被刻意回避。没有清单,也没有风险评估。即便尝试过现代化改造,也是由挫败感驱动的、随意的”要么全部要么全无”式重写。知识存在于少数即将退休的人的头脑中,没有任何书面记录。
  • 第 2 级,发展阶段。 一些团队有一份清单和对风险的大致了解,少数遗留系统被 API 包装起来以便于访问。渐进式模式为人所知,但应用得并不一致,思路仍然倾向于大爆炸式重写。数据迁移有被尝试,但被低估,各团队之间的实践差异很大。
  • 第 3 级,标准化阶段。 系统依据一套有文档记录、覆盖全组织的方法,按风险和价值排定优先级。渐进式模式(绞杀者无花果模式、抽象分支)是被强制执行的默认选择,每一次现代化改造都遵循一套标准的操作手册。数据迁移是一项经过规划、配有对账机制的工作,并在切换前进行双系统并行运行,知识捕获和特征化测试是必须遵守的实践,而不是可选项。
  • 第 4 级,管理阶段。 现代化改造依据数据被度量和控制。整个资产建立了基准:每个系统的维护人员数量和退休时间表、特征化测试覆盖率、迁移对账通过率、双系统并行运行的差异数量,以及每个增量交付的价值,所有这些都对照目标持续被追踪。维护还是替换的决定,以及切换的”通过”或”不通过”的判断,都基于这些证据做出,一个知识风险超出阈值的系统会触发行动,而不是等待危机来临。
  • 第 5 级,编排阶段。 现代化改造是持续进行的,与业务和风险规划相整合,并且能够自我调整。投资组合会随着风险演变轨迹(尤其是知识风险)的变化而不断再平衡,渐进式替换成为一种常规的、平淡无奇的日常工作,每一个增量都在交付价值,并且是可逆的,组织有意识地掌控着改造的节奏。每一次迁移中获得的经验教训,都会反馈回共享的操作手册中,让整个资产随时间不断改进。

讨论想法

  1. 对你们最关键的遗留系统,还有多少人能够维护它?他们距离离开有多近?
  2. 在哪些地方,你们受到了大爆炸式重写的诱惑?一种渐进式方法在头三个月能交付什么价值来替代它?
  3. 你们最古老的系统中,业务规则的文档记录程度如何?如果这段代码被替换,这些规则会怎样?
  4. 你们是否已经对需要迁移的数据做过画像分析?你们是否知道它究竟有多混乱、多盘根错节?
  5. 你们正在把现代化改造的精力,花在哪些古老但稳定、其实可以安全放任不管的系统上?
  6. 你们能否让新系统与旧系统并行运行,并在切换之前证明两者结果一致?

关键要点

  • “遗留”意味着有价值、是基础性的;在改动一个系统之前,先尊重并理解它。
  • 用绞杀者无花果模式和抽象分支进行渐进式现代化改造,持续交付价值,让每一次改动都保持微小且可逆。
  • 把大爆炸式重写当作默认的失败模式;把它留给那些真正无法维持的平台,即便如此也要将其分解。
  • 按风险和价值(尤其是知识风险)排序,而不是按年龄排序;有些古老的系统最好被管理维护,而不是被替换。
  • 数据迁移和双系统并行运行是这项工作的核心;进行画像分析、对账、并行运行,并保留回退能力。
  • 最有力的业务论证是可选性:渐进式现代化改造把一次巨大而危险的赌注,转化为许多各自都能带来回报的小赌注。

参考文献与延伸阅读

  • Michael Feathers, Working Effectively with Legacy Code
  • Martin Fowler, “StranglerFigApplication” and “BranchByAbstraction”
  • Sam Newman, Monolith to Microservices
  • Nicholas Carr / industry studies on mainframe and COBOL dependency (context on the scale of legacy estates)
  • Robert Annett, Working with Legacy Systems
  • Eric Evans, Domain-Driven Design (anti-corruption layer)
  • Gregor Hohpe, Enterprise Integration Patterns and The Software Architect Elevator
  • Standish Group CHAOS Report (evidence on large project and rewrite failure rates)