43-软件工程之美

开篇词 | 你为什么应该学好软件工程？

你好，我是宝玉，欢迎加入我的专栏，和我一起开始软件工程的学习之旅。

和很多人一样，我的职业生涯是从一个自学编程的“野路子”程序员开始的。1999 年，我考入西北工业大学工程力学专业，但是却对编程很感兴趣。大一的时候自学网页编程，大二开始去学校网络中心兼职，同时在外面接了很多做网站的私活。

那时，虽然我的编程水平提升特别快，但是因为完全是自学，跟其他计算机科班的程序员一比，多少有点自卑感，觉得好像差点啥！在实际工作中，遇到具体的问题，我只能见招拆招，一个一个地解决。

当然，因为一开始我无法从系统层面整体看事情，所以虽然问题解决了，但也总有一种疲于奔命的感觉。我曾经遇到的问题，你肯定也不陌生，比如：

开发时没有分析没有设计，上手就写，后期难维护，加班熬夜去填“坑”；

缺少理论指导，遇到新项目不能举一反三，工作很平庸；

遇到需求变更这种事，除了抱怨两句客户，只能闷头做，无力反抗；

做项目没计划性，想到哪做到哪，总是延期，比其他同事做的慢；

不知道如何与团队协作，职业发展遇到瓶颈，无法得到晋升。

那时候我不知道啥是正规做法，主要靠自己摸索。也特别困惑：科班出身的程序员是否与我有同样问题？像微软、阿里等这些大厂的程序员，他们又是怎样协调完成好那么庞大的项目？我这个“野路子”程序员面临的问题，他们又是怎么分工协作解决的？

2002 年初，我有幸转了专业，成为了中国第一批软件工程专业的学生，有机会系统地学习软件工程的理论知识，这解开了我的很多困惑。

软件工程学让我知道，软件项目的开发其实是一个工程，整个开发过程是可以有效组织起来的；对于开发过程的各个阶段，已经有很多解决问题的最佳实践，有很多方法来帮助我们高效完成任务；我们还可以借助工具来协助管理，提升开发效率。

如果说以前自学编程时，我还是停留在学习各种编程方法（术）上面，那软件工程开始让我主动去思考这些“术”后面的“道”，去思考软件项目中各种问题背后的原因，以及各种方法后面的理论指导。

这种对“道”的思考，逐步影响了我思维方式，让我从单一的程序思维上升到系统的工程思维去看日常的问题；同时让我形成了一套自己对于软件开发和项目管理的方法论，能举一反三，指导我去灵活运用各种方法，或者根据项目特点创造合适的解决方法。

当然，软件工程学的价值不仅于此。有人说程序员是吃青春饭的，因为计算机技术更新太快，年纪大了学习能力下降，就很难跟得上了。于是就有人很焦虑，会关心未来技术发展趋势如何？我怎么才能跟得上这些技术变化？

亚马逊的创始人杰夫·贝索斯（Jeff Bezos）曾经在一次演讲中说：“人们经常问我，未来 10 年什么会被改变？我觉得这个问题很有意思，但也很普通。从来没有人问我，未来 10 年，什么不会变？”

这个回答同样适用于软件开发领域。在软件开发领域，有哪些知识十年前很重要，现在仍然重要，未来可能同样重要？

其实仔细分析，这些知识不外乎：数据结构、算法、面向对象思想、设计模式、软件工程。如果范围不局限于程序开发，还要算上测试、产品设计、项目管理、运维这些岗位。

你会发现，无论你是什么岗位，只要你从事软件开发相关领域，都绕不开“软件工程”，因为现代软件项目开发，多多少少都离不开软件工程知识的应用。

想象下在日常工作中，不管你用什么开发语言，不管是前端和后端：

你接到一个开发任务，如果想开发出客户想要的功能，你是不是先要做需求分析；

你接手一个复杂的、大的功能模块，是不是先要做设计，才能把复杂的拆成简单的，才能让大家一起分工去开发；

你完成一个功能模块，如果要保证质量，是不是需要写一些测试代码，还要做一些功能测试；

还有日常用的那些工具，像源代码管理、Bug 跟踪。

而这些内容，都是软件工程相关的知识，和你用什么语言无关。十几年前我开始工作时就在用这些知识，现在还是在用这些知识，未来这些知识还不会过时。

换言之，这就是经典的价值，为什么说我们要学经典，因为经典就是这个行业最为本质的东西。你顺着这个逻辑想，就知道为什么大学的计算机专业要设计数据结构、算法、操作系统、软件工程这样的课程了。

技术更新迭代速度确实很快，难以把握，更难以预测，但是软件开发背后的逻辑却万变不离其宗。

你只有掌握了这些逻辑，才能步步为营，不被快速发展的软件开发行业所淘汰。因为你脑袋里装有软件开发的战略，相对于赤手空拳、盲打莽撞的人来说，你更能在未来获得先机。

我经常会跟身边的朋友“安利”软件工程的重要性，但是往往都没有下文。究其原因，主要是传统的软件工程教学方法出了问题，各个知识点过于偏理论，难以和实际项目的应用联系起来，理解起来生涩乏味。导致有人误以为软件工程是枯燥、无用的。

回想当初我在学习软件工程课程时，并没有觉得特别枯燥，主要归功于三点：

我学习前已经有项目实践经验，所以学习时，很容易能将理论和项目经历串起来；

我在以前项目中有很多困惑，带着问题再去学习，这样效率更高；

即学即用，获得正反馈。我不仅会把软件工程的知识应用在工作中，还会把日常生活中的问题当成一个项目去思考，不停练习和获得正反馈。

我一直在思索，怎么让软件工程的学习，既不那么枯燥无味，同时，也具有实用性，即学即用，可以用来指引帮助我们来解决问题。

这样一直到 2015 年，我到美国攻读计算机的硕士学位，发现美国的计算机教育确实有可取之处，例如学校会聘请企业的专家作为兼职讲师，让学生有机会了解业界最前沿的技术趋势。

这些有丰富项目经验的企业专家讲师在讲课时，总能把一些知识点和鲜活的案例结合起来，和学生一起探讨这些知识点背后的历史和逻辑，让软件工程学变得易学、实用。

在美国读书的经历给了我很大启发，软件工程的学习，也可以不那么枯燥。恰好我的经历也比较特殊：

从自学编程的程序员到软件工程专业科班毕业；从技术开发到在微软飞信做项目管理；从程序员到技术总监；从几个人小团队到几千人的大厂；从国内公司到美国公司；从个人小项目到几千万用户的大项目；从传统瀑布模型到最新的敏捷开发。

这些丰富的经历，帮助我更好地理解了软件工程的知识，也知道如何应用它，可以发挥最大的效用。

因此，在这个专栏中，我会结合自身在软件开发中的经历，将软件工程中的知识点和我所看到的国内外前沿的、典型的项目案例结合起来讲解，也会和你一起分享我对这些知识背后的思考。和你一起去软件工程学中，寻找软件项目中问题的答案。

我希望最终，你能把软件工程知识和项目经验有机地结合起来，转换成你自身能力的一部分。

另外，在实际软件项目开发中，离不开各种工具的使用，像源代码管理、持续集成、看板、监控报警等，帮助我们更好地协作、规范项目流程、上线维护。

在本专栏，我也会在穿插着介绍各种工具的用法，有哪些价值，让你在了解后能很快应用到项目中，达到即学即用的效果，提高项目开发效率、规范项目流程。

我们的专栏会从“道、术、器”三个维度来讲解软件工程的知识内容。

“器”就是软件工程中的各种工具。

“术”就是软件工程中的各种方法。例如如何做需求分析？如何对需求变更做变更管理？

“道”就是软件工程知识的核心思想、本质规律。例如为什么要有需求分析？需求变更产生的深层次原因是什么？项目中决策的依据是什么？

在专栏的模块设置上，我将它分成了三大部分。

1. 基础理论

从宏观的角度建立起软件工程的知识结构，展现软件工程学的全景图，让你掌握从软件工程的基础概念到主流的软件过程方法论。我会帮你开始思维上的转变，去尝试用工程化的思维模式，去分析和解决工作和生活中的问题。

2. 项目过程

我会按照软件生命周期，把知识点拆成：规划、需求分析、设计、编码、测试、运行维护这六个阶段，然后带着你一起去了解每个阶段要侧重做哪些事；分析每个阶段常见的问题，找到解决方法；了解各个阶段有哪些工具可以对项目有帮助，从而学会应用它们。

3. 案例分析

在这个模块中，我会带你一起去看看这些大公司是怎么应用软件工程的。之前你可能会有疑惑，认为软件工程学很虚，我们小公司用不着，或者不知道怎么在实际项目中应用软件工程。

其实软件工程的思想是润物细无声，包括微软、谷歌、华为、阿里巴巴这样的大公司早已经深得其精髓，把它用得炉火纯青了。

你的公司，你遇到的大部分项目问题，都可以回到软件工程的逻辑里来解决。我会给你分享我看到的经典的软件工程案例，让你能够通过综合案例，把前面的知识融会贯通，并逐步内化为自己的基础能力。

简单来说，我希望通过这个专栏，你可以从知到行，打好基本功，掌握软件工程学中涉及的方法和工具，学会举一反三，在软件项目的开发和管理过程中，能运用自如；也希望软件工程的思维，可以让你脱离技术的拘泥，有更高的格局和视角去看待工作和生活中的问题。

最后，也希望软件工程学这门基础学科，真正成为武装你职业上升的盔甲。无论你想走技术路线，还是转向做管理，都能从赤身肉搏、苦钻技术却不得法的“野路子”，变得行有章法，在未来软件的快速革新稳步前行。

如果你在专栏的学习过程中，遇到任何问题，或者有什么想法，欢迎留言与我交流。相信这段学习之旅，你我都将收获满满。

好，那就让我们开始吧！

特别放送 | 从软件工程的角度解读任正非的新年公开信

你好，我是宝玉。

2019 年 1 月，任正非的那封《全面提升软件工程能力与实践，打造可信的高质量产品》公开信在朋友圈刷屏了。作为软件工程专业出身的程序员，这封公开信自然是引起了我的好奇，仔细阅读之下，确实让我大吃一惊。

于是，我从软件工程的角度对这封公开信进行了解读。在我们专栏内容正式更新前，我将它作为特别放送分享给你，希望可以帮助你更好地理解软件工程。

这封信看似像八股文一般，但细看之下，可以发现作者对于软件工程的理解确实非常深刻，各种专业术语信手拈来，比喻恰到好处。

我对华为的研发其实一直挺好奇的，从传统的硬件公司，到现在软硬件齐头并进，华为手机销量都已经超过了苹果，可见华为的软硬件研发实力早已处于全球领先水平。信中提到：

二十年前的 IPD 变革，重构了我们的研发模式，实现了从依赖个人、偶然性推出成功产品，到制度化、持续地推出高质量产品的转变。

这一句话，也揭示了华为的软件研发能做到全球领先水平的原因。

华为是在 1999 年开始从 IBM 引进 IPD 的，到今年 2019 年正好 20 年，在过去的 20 年里，IPD 帮助华为从游击队变成了正规军，研发队伍从几千人到几万人，软件产品也覆盖到手机操作系统、应用、云服务。

我对 IPD 是不甚了解的，只知道 IPD（Integrated Product Development，集成产品开发）是一种产品开发方法，但如果说软件产品的开发方法，我是比较熟悉的，那就是软件工程。

任正非发出的这封信的大背景也很特殊，2018 年中美贸易战开始，中兴、华为首当其冲成为美国开刀的对象，跟风站队的澳大利亚、新西兰、英国也跳出来抵制华为，说华为不安全，可能含有间谍软件，窃听国家机密。这帽子一扣是很难扯清的，这就是为什么整封信从标题开始，一共 17 次提到两个关键字：可信。

只有让客户觉得华为的产品“可信”，华为才能尽快走出这场危机，那么怎么才能做到可信呢？

如果你是餐厅老板，有人造谣你的厨房脏乱差，员工上完厕所不洗手，你怎么办？最好的办法自然是用先进的管理流程，并且让整个做菜的过程尽可能公开透明。

所以信中有这样一句话：

我们要转变观念，追求打造可信的高质量产品，不仅仅是功能、特性的高质量，也包括产品开发到交付过程的高质量。

要转变观念，不再只认结果的质量，还要追求过程质量了！而如何追求过程质量呢？那就是要“全面提升软件工程能力和实践”。

如果信到此为止，也就是个普通官方“八股文”。领导们嘛，可不就是喜欢指个大方向，说你们要用软件工程，要实施软件工程，至于怎么用，那是你们的事情，毕竟做领导的哪有几个真的懂软件工程，难得的是这封信居然有很多具体怎么做的细节内容。

以下，我带你看看这封信都提到了哪些具体到操作层面的问题。

1. 软件项目管理金三角

先看这一句：

我们各级管理者和全体员工都不得以进度、功能、特性等为理由来降低可信的要求，确保可信的要求在执行过程中不变形。

振聋发聩呀同志们，热泪盈眶呀！生活中有多少次这样的情况发生：三个月的项目，老板说你一个月就要给我做完；做到一半的项目，PM 说这个功能很重要，我们要加上去。最终怎么办？牺牲质量呗！又想要马儿跑得快，又想要马儿不吃草，天底下哪有那么好的事情。

软件工程里面早就告诉我们了：时间（多久可以完成）、范围（需要实现多少功能）、成本（花多少钱）这三个要素直接决定了产品的质量（产品的质量，客户的满意度）。

希望各位老板别光学乔布斯，也学学任正非！想员工天天加班，还想着少发工资，还要产品质量好做得快，这不现实呀！

首先得明白一个最浅显的道理：想要“多、快、好、省”都占着，这是不存在的，你只能选两样。想要便宜还想要质量好，那就得等；想要快还想要质量好，那就得多花钱；要想便宜做的又快，那就得接受质量差！

2. 自我精进

2018 年底，程序员被企业裁掉的不少，很多程序员开始担忧起前景来。关于这一点，这封信中也提到了一些指导意见，如果你能达到以下要求，应该是不必担心裁员的。

我们要从最基础的编码质量做起，视高质量代码为尊严和个人声誉。代码就像是高楼大厦的一砖一瓦，没有高质量的代码，可信的产品就是空中楼阁。我们要优化并遵循公司各种编程规范，遵从架构与设计原则，熟练使用各种编程库和 API，编写出简洁、规范、可读性强、健壮安全的代码。

这一段是说给我们程序员看的，这其实也是对程序员的基本要求，大家看看自己，看看身边，真能做到的有多少？像我一样觉得自己还做的不够好的，咱还是努力学习吧，多练练，多用点心肯定更没问题的。

3. 关于架构

讲完了程序员的自我精进，信中又开始说架构师了：

我们要深刻理解架构的核心要素，基于可信导向来进行架构与设计。

看到没有，又提到可信了，架构设计的时候，别再天马行空，啥新酷用啥，啥流行用啥，一定要有“可信导向”，架构设计目标先搞清楚。

然后是细节：

在确保可信的前提下，要在性能、功能、扩展性等方面做好权衡；慎重地定义我们的模块与接口，真正做到高内聚与低耦合；我们要遵循权限和攻击面最小化等安全设计原则，科学设计模块之间的隔离与接口，提升安全性；低阶架构与设计要遵循高阶的架构与设计原则，在充分理解原有架构与设计的情况下，持续优化；我们要熟悉各种设计模式，重用公共成熟组件和服务，避免重复劳动。

“高内聚与低耦合”，这是讲架构设计时，让模块之间做到高内聚与低耦合。

“权限和攻击面最小化”，这是讲架构设计时，要注意对安全的防范，权限要控制好，暴露出来的入口要小，做好防范。

“重用公共成熟组件和服务”，不要浪费时间在造轮子上了，多重用现有的组件和服务，甚至要提取公共的组件和服务。减少重复劳动，提高效率。

你看，多么浅显的道理，一看就明白，但要做到可不容易！

4. 技术债务

华为这些年高速发展，早些年为了追求速度肯定也没少走捷径，这些年下来，也肯定没少欠技术债务，现在也是一个从追求速度到追求质量转型的契机。

所以信中说完架构开始讲技术债务了：

我们要重构腐化的架构及不符合软件工程规范和质量要求的历史代码。我们知道，再好的架构，其生命力也是有限的。随着时间的推移、环境的变化以及新技术、新功能特性的引入，架构也会腐化。面对腐化了的架构，要毫不犹豫地去重构它。同时主动以可信设计原则为导向，去重构不符合软件工程规范和质量要求的历史代码，提升软件架构的生命力。

我们都知道，没有万能的架构，只有适合当时需求、当时技术条件和人员的架构。时间推移了，很多架构就满足不了要求了，就需要重构。

作为 80 后，小时候其实生活挺艰苦的，那时候我们穿衣服都讲究的是：“新三年，旧三年，缝缝补补又三年。”架构也一样嘛，不满足需求，我们先修修补补，真要重构挑战还是不小的，但是不去做的话，它会一直成为发展的障碍。

这封信也算是推了一把：“面对腐化了的架构，要毫不犹豫地去重构它。”当然你重构，也不要忘记“可信”这个根本目标，“同时主动以可信设计原则为导向。”

其实 Google 在这方面已经走在前面了，一直鼓励重写代码，任何软件每隔几年就重写一遍，这样可以优化代码，采用最新技术，去掉一些没有价值的功能，最重要的是让新员工得到锻炼，保持高昂的斗志。不知道关于这点，华为是不是在像 Google 学习。

5. 安全性

这些年，互联网发展很快，但是安全事故却层出不穷：开房记录被泄漏、密码被泄漏、比特币被盗……这暴露出业界的普遍问题，对安全的重视度不够。

所以信中也不止一次提到安全问题：

公司已经明确，把网络安全和隐私保护作为公司的最高纲领。

我们要深入钻研软件技术，尤其是安全技术。

我们要遵循权限和攻击面最小化等安全设计原则，科学设计模块之间的隔离与接口，提升安全性

“编写出简洁、规范、可读性强、健壮安全的代码。

要打造一个安全的软件，就是首先要有安全意识，然后要懂安全技术，在整个开发过程中要从架构设计、代码等方方面面去注意。

6. 技术是工具

这些年，开发界一直有些不好的风气，就是都认为自己的技术是最牛的，写后端的看不上前端的，用 Angular 的看不上 Vue，写 PHP 的认为自己的语言是全世界最好的，做开发的还看不上做测试的。

但是这封信中有一句话，大家不要忽视，“软件技术是我们打造产品的基本工具”，技术只是工具，只是我们用来打造产品的工具！

技术是否先进，技术选择是否合理，将决定我们软件的高度。

技术的选型，不仅要看技术是不是先进，还要看它是不是适合当前的产品项目。并不是什么技术很新酷，就用什么！

我们要深入学习架构与设计、编码、测试、安全、可用性、性能、维护性、体验等技术，并科学运用这些技术。

既然技术只是工具，那么我们就没必要给自己设置各种技术壁垒障碍。

如果开发就只学编码，测试就只学测试，认为安全问题，那应该是搞安全的人的事，这样的话是非常不利于团体协作的。

每个人都能在一个领域深入地钻研，同时对其他领域有一定了解，对个人、对团队都是非常有利的一件事。这样的话，也不需要 DevOps 这种为了兼顾开发、测试、运维三种角色而存在的工种。

7. 一致性

我们做软件开发工作的人都知道，一致性很重要，然而现实中这样不一致的例子却比比皆是：

从客户的需求，到最终的实现，总是差别很大；

我们良好的设计，在编码实现的时候，因为赶进度、开发人员偷懒等各种原因绕开设计，抄近路，最后设计和编码无法一致；

我们在项目初始的时候制定了很多规范，却总是不了了之，难以执行；

……

通常是一步错步步错，就像下面的秋千图：客户想要一个给三个孩子玩的秋千；产品经理以为就是一个板子加两绳子就行；架构师发现除非把树截开，否则秋千没法荡起来的；程序员以为用绳子和板子连一起就完事了；而真正满足客户需求的，也就只要在绳子上挂个轮胎而已！

一致性在软件开发领域，一直都是理想美好而现实却很残酷，信中也提到：

我们要遵守过程的一致性。遵守适用的法律法规、遵循业界共识的标准、规范，确保规范到实现的一致性、代码到二进制的一致性。架构要符合架构原则，设计要遵循设计模式，代码要符合编程规范，最终做到需求与实现一致，达成各项对客户的承诺。我们只有脚踏实地做好每一步，才能真正打造出可信的高质量产品。

无论这个目标有多难，但是从“遵守过程的一致性”开始，在每个阶段都去做到一致性，“脚踏实地做好每一步”，还是有希望能做到“真正打造出可信的高质量产品”。

8. 改变习惯

在实施软件工程的过程中，有两个难题，一个就是转变思想，另一个就是改变习惯了，这种改变的过程也一定是很痛苦的。

为此，我们要改变行为习惯，追求精品。我们要开放透明、积极和勇于揭示问题并主动推动改进。软件开发是一种创造性和艺术性的工作，需要充分发挥我们的聪明才智和潜力。我们要改变只重视功能结果、不重视代码质量的行为习惯，要严格遵守软件工程规范；改变被动的修修补补；改变碎片化知识获取，主动去学习提升并贡献经验、代码，形成共享知识库。我们需要改变的行为和习惯还有很多，对绝大多数人来讲都将是一个痛苦的转变过程，会脱一层皮，但我相信大家能够迎接这种挑战。

从事软件开发工作越久，恐怕养成的坏习惯就越多，信中列的几条都很有代表性：

“只重视功能结果、不重视代码质量。”

功能实现完了就完事了，质量那是 QA 的事。这种坏习惯不改, 质量是很难有保障的。

“不遵守软件工程规范。”

软件工程的各种规范不是约束，也不是摆设，而是实实在在为了团队整体更好地协作。对于定好的规范，要严格执行，不合理的规范，也要提出来一起改进。

“被动的修修补补。”

为了能继续凑合，继续修修补补，而没有考虑重构改进，也是一个不好的习惯。

“碎片化知识获取，不主动去学习提升。”

在现在的信息时代，碎片化的知识获取是容易的，但是像软件工程这种知识，仅仅通过碎片化的学习还是不够的，必须主动的，系统的去学习，虽然这个过程会很辛苦，但是非常有必要。

“不愿意贡献经验、代码，不去形成共享知识库。”

很多人不愿意去分享知识和经验，有的是因为太懒，有的是觉得没什么好处。但是分享本身就是学习和提升的最好手段。知识库这种事不仅是对别人有帮助，对自己也是一个特别好的学习精进的过程。

想象下你新加入一个团队，如果这个团队有很好的知识库，你可以通过知识库，很快上手工作。同样的，如果你把你的经验写到知识库，后面的新人也可以从你的分享中受益。

9. “软件工程”和“质量工程”需要依靠架构技术

“软件工程”和“质量工程”需要依靠架构技术，而不是依靠 CMM 和 QA 管理流程。一切工程问题，首先要思考能否通过技术解决，当前技术无法解决的问题，暂时由管理手段代劳，同时不停止寻找技术手段。

所有的涉及到的管理问题，最终都要归结到人管理还是制度管理的问题上，软件项目管理也不例外。如果过多的依赖于人的管理，那么项目经理的职责就太重了，优秀的项目经理本身就是稀缺资源，最终会变成瓶颈。

所以通过架构技术和工具，把管理流程落实下来是一个非常好的方式。有两个例子可以很好地说明这点。

早些年软件服务规模庞大、模块耦合度紧密，所以需要一个庞大的开发团队，团队一大，沟通成本就高，进而管理成本很高。后来微服务这种架构提出后，将大的服务拆成小的服务，整个组织也从大项目部门拆分成各个小组，各小组可以独立更新维护。

另一个例子是，以前单元测试和代码审查还有自动部署很难执行，后来借助源代码管理工具和 CI（Continuous integration，持续集成）工具，就可以很容易地进行代码审查，并且可以确保单元测试跑通过后才进行部署。

这一点其实信中也有体现：

我们将全面强化以 Committer 角色为核心的代码审核和提交机制，代码经过更加严格和系统的审核才能合入版本。为此我们将建立一支更高水平的 Committer 角色群体，负责软件架构的看护、代码的审核和提交，整体保障合入代码的高质量。我们要变革考核机制，要让架构设计好、代码写得好的人脱颖而出，对编程能力不满足要求的人给予帮助和培训。但任何人如果编写的代码长时间不能合入版本，将会被团队抛弃。

10. 软件工程就像一个国家的农业

软件工程就像一个国家的农业，是最基础的设施！

看到这句时，我很感动。这些年软件工程被提起的其实不多，大家关注更多的是各种新酷的技术，而对于这种软件开发最基础的理论视而不见。

还有人一提到软件工程，就马上说软件工程不是银弹。软件工程从来不说自己是银弹，就像现代医学，也不会号称自己包治百病，它只会不断改进，对症下药。

好，这就是我对这封信的全部解读，我希望它能帮助你更好地理解软件工程，认识到软件工程的重要性。

如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

学习攻略 | 怎样学好软件工程？

你好，我是宝玉。

关于什么是软件工程和为什么要学软件工程，我在开篇词中已经简单介绍过了。总结来说：软件工程是软件行业知识体系的内核。无论你想走技术路线，还是转向做管理，想要走的更快更稳，那就绕不开软件工程。

在正式开始学习前，我们先来聊聊应该如何学习软件工程。你要先知道，软件工程学科的“知识树”结构是什么样的，才能更好地理解每个知识点和彼此间的联系。

软件工程知识架构全景图

首先你要明确，当我们谈软件工程学时，究竟在讲些什么呢？

在《软件工程——实践者的研究方法》这本经典软件工程教材中，作者 Roger S.Pressman 画了一张图，高度概括了整个软件工程的核心知识。

由图可见，“质量焦点”在最底层，这不难理解软件工程是为了应对软件危机诞生的学科，其目标就是为了要聚焦于质量，构建和维护高质量的软件。可以说，聚焦于质量就是软件工程的基石。

那“过程”指的是什么呢？

要构建高质量软件，则要解决软件过程中的混乱，将软件开发过程中的沟通、计划、建模、构建和部署等活动有效地组织起来。而软件过程，就是在软件项目的生命周期内，也就是软件从诞生到结束这期间，在开发与构建系统时要遵循的步骤。

有两种过程框架你一定经常听到，那就是瀑布模型和敏捷开发。这是在软件工程多年的发展中，逐步形成的两种主流的软件过程指导框架。

那么，何为“方法”？

方法是指在整个过程中，如何构建系统的方法学。比如说，如何分析用户需求；如何对产品进行测试验收；如何进行系统架构设计等。

知道了过程，掌握了方法，那么具体落到操作层面，就会涉及到工具的使用。

我们需要工具来辅助方法的执行，提高效率。通过工具，可以把一些手动的工作自动化，比如自动化测试工具，自动构建部署工具；通过工具，可以帮助把一些流程规范起来，比如 Bug 跟踪、源代码管理；还可以通过工具，帮助提高编码效率，比如各种编辑器 IDE、各种高级语言。

如果现在再回头总结一下，软件工程的核心知识点，就是围绕软件开发过程，产生的方法学和工具。

你可以用一个简单的公式来理解软件工程，那就是：软件工程 = 工具 + 方法 + 过程。

根据这个公式，我将软件工程的知识结构做成了思维导图，方便你对知识点有更好地理解，高效学习。

如何学习软件工程？

我给了你软件工程学的公式，也对软件工程有了更为全面的了解，看起来软件工程学很简单，但这些内容一下子要吃透也不容易。在开篇词中，我介绍了会从“道、术和器”三个维度去讲这个专栏，这其实对应了学习软件工程的四重境界。

学习软件工程的四重境界

第一重：用器

“器”就是工具，工具规则简单，上手就可以用，也很快就能看到效果。比如，原型设计工具可以帮助你确定需求，持续集成工具可以帮助你简化测试和部署的流程。对工具的学习是最为简单的，也是最基础的。

第二重：学术

“术”就是方法，学会方法，你就能应用方法去完成一个任务，例如用需求分析的方法，你去搞清楚用户想要什么，用 Scrum 去组织项目开发过程。

掌握了术，甚至是可以脱离器的，例如你没用原型设计工具，你用纸和笔，用白板，一样可以去沟通确认需求。

第三重：悟道

“道”就是本源，软件工程知识的核心思想和本质规律。就像敏捷开发，本身并不是一种方法，而是一套价值观和原则，领悟了这个道，就可以成为你在处理项目过程中各种问题决策的依据。道是可以产生术的，你掌握了敏捷开发的道，你就可以领悟出 Scrum、极限编程这样的术。

第四重： 传道

当你能把复杂的知识通过浅显易懂的方式传授给别人，那就说明你对知识的领悟已经到了更高的境界。同时，教学也是最好的学习方式，通过传授别人知识，可以让你对知识本身有更深入的理解。

做中学和教中学

你可能会问，怎样学，才能到达以上这四重境界？我在做技术管理的工作中，经常要做一些培训的工作，在这过程中我总结了两套行之有效的方法：“做中学”和“教中学”。

“做中学”，是一种自下而上的学习方法，通过实践，从使用工具到学习方法，再从方法中提炼出道。

在学习本专栏的时候，你可以采用“做中学”的方式，把专栏中的知识应用起来，在实践的过程中去巩固你学到的知识，去思考背后的道。把已经积累的项目经验和软件工程的知识点关联起来，这样才能加深你的理解，学以致用，把经验和知识转化为能力。

“教中学”，是一种自上而下的学习方法，通过教学，去进一步深入领会别人总结出来的道，去模仿推导方法，去学习如何使用工具。

比如，你学习完一篇专栏文章后，把学到的知识进行输出，写成微博或博客分享出去；在公司内部讲给你的同事们听等。在教学分享的过程中，去进一步深化吸收知识内容，构建你的知识体系。

“做中学”和“教中学”，这两种方法你可以配合起来使用。

参考书目

另外，在学习软件工程的过程中，我看过一些不错的相关书籍，在这里列个书单，供大家参考。

《构建之法》

作者邹欣是微软的研发总监，同时在多所高校进行了软件工程的教学实践，在此基础上对软件工程的各个知识点和技能要求进行了系统性整理，形成教材。也是本专栏很多很好的补充。

《人月神话》

这是软件工程历史上的经典著作，内容发人深省，40 年来一直畅销不衰，里面的观点即使到现在也不过时。这本书即使你以前看过，隔一段时间再翻看一遍，可能都会有新的感悟。

《人件》

如果说《人月神话》关注“软件开发”本身，《人件》则关注软件开发中的“人”。作者指出知识型企业的核心是人，而不是技术。

《知行合一： 实现价值驱动的敏捷和精益开发》

作者丛斌有二十多年从事软件工程教学、咨询和研究的经验，所以书写的特别接地气，文章有很多真实案例，对敏捷开发和 CMMI 都有很深入描述。

《软件工程——实践者的研究方法》

这是大部分高校采用的软件工程标准教材，可以作为一个参考。

《持续交付》

讲述如何实现更快、更可靠、低成本的自动化软件交付，描述了如何通过增加反馈，并改进开发人员、测试人员、运维人员和项目经理之间的协作来达到这个目标。

《走出软件作坊》

这本书生动的描述了国内小型 IT 企业在发展过程中遇到的一系列项目管理问题，以及作者是如何去解决这些问题的。

总结

今天，我带你浏览了软件工程的全景图，也为你讲解了学习软件工程的四重境界。同时，我也介绍了“做中学”和“教中学”这两套行之有效，并且特别适合软件工程学科的学习方法，所以希望你在后面的学习中，可以付诸行动。

分享你学到的知识。将你从专栏学习到的知识写成微博或博客等，分享给大家。写作是一种特别好的总结和学习方式，在你写的过程中，很多不清楚的问题就想明白了。

做几次内部分享或培训。如果你从来没做过公司内部的分享或培训，不妨迈出第一步，把你学到的知识，写成 PPT，小范围地讲给你的同事或朋友。如果你已经做过类似的分享，那么就再做几次软件工程相关的。准备 PPT 的过程，就是你最好的学习过程。

把你学习的知识应用起来。学到的知识只有用起来才能变成你自己的经验，尝试着把在专栏中学到的知识应用到你的项目中去。多问多思考。有疑问就提出来；看到其他人问的问题，也可以去思考为什么，一起探讨问题的答案。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

01 | 到底应该怎样理解软件工程？

你好，我是宝玉，我今天分享的主题是：什么是软件工程？

当有人问你什么是软件工程，你会想到什么？

也许你会想到软件，会想到建筑工程，也许对此还没有什么概念。不要紧，在回答这个问题前，我们先来看看软件工程和我们的日常生活有哪些关系。

作为一个程序员，你的日常大致是这样的：早上被手机的闹钟叫醒，然后坐地铁去上班。在路上你会打开微信和朋友闲聊几句，刷刷微博，还会用 Youtube 看看视频充充电；上班的时候，你打开 Mac 电脑，用 VS Code 写程序，用 Chrome 看网页，下班后再玩几局守望先锋游戏。

你发现了没？在这一天的日常生活里面，软件无处不在。手机操作系统软件、闹钟 App、微信、微博、Youtube、Mac OS、VS Code、Chrome、守望先锋，这些都是软件，你的日常生活已经和这些软件密不可分。

软件是怎么被创造出来的？

那么你有没有想过，这些软件是怎么来的呢？

首先，它们的诞生都是有人想要造一个东西。这其中的很多故事你都耳熟能详：张小龙创造微信、乔布斯创造 iOS、暴雪想做一款不一样的射击游戏……他们首先有着这样的意图，然后，他们立项做这样的产品。

每一款软件项目背后，都有很多人在参与。你无法想象微信这样复杂的 App 靠张小龙自己可以开发出来；你也不会相信 iOS 是乔布斯一个人设计出来的；像守望先锋这种游戏的背后，有成百上千的游戏策划、美术设计、程序开发人员。

从立项到第一个版本的发布，每个成功的软件都需要有计划、有步骤地进行，什么时候发布第一个版本、第一个版本有什么样的功能、什么时候发布第二个版本、第二个版本有哪些地方要改进，这些都是研发过程中需要考虑的问题。

比如，守望先锋是在 2013 年立项，他们计划做一个基于职业的 MMO 游戏，他们先花了几个月的时间做了第一个 Demo 演示，只有四个英雄和一个张地图。

（图片来源：The treasures of the Overwatch Archives panel at Blizzcon）

然后，他们要在 2014 年的“暴雪嘉年华”发布这个游戏的试玩版，包含 12 个英雄和 4 张地图。

（图片来源：The treasures of the Overwatch Archives panel at Blizzcon）

最终这个游戏在 2016 年 5 月正式发布了。这样从立项一步步到最终发布，历时三年，中间经过了诸多环节。

类似的例子还有微信，2010 年 11 月 20 日立项，2011 年 1 月 21 日第一版上线，当时的功能还非常简陋，只能发送文本消息和照片。之后才是一个个版本的迭代，直到你现在看到的微信。

像这种有人参与、有计划、有步骤地造一件产品，我们通常称为“工程”。

所有工程的本质，就是要做出有用的产品，比如造房子的建筑工程、造火箭的航天工程。像网红“手工耿”一样专搞无用发明的情况，我们是不能称为“工程”的。

在软件领域，对应的就是“软件工程”，这些我们日常使用的软件背后，都是基于软件工程的方法在开发、运行和维护的。

如何摆脱“软件危机”？

也许有人会认为，不用软件工程，我一样可以开发软件出来。这确实没有错，因为如果一个人没有学过建筑工程，他也是可以造一个房子出来，只是造出来大概会是这个样子：

我们知道，不按照建筑工程造房子，是会出事故甚至死人的。而在软件工程的历史上，也是真的有造成过很大损失、甚至还有人为之丧命的事件存在。

OS/360 操作系统是上世纪 60 年代最复杂的软件系统之一，也是第一个超大型的软件项目，一共有 1000 名左右的程序员参与了项目的研发，花费了 5000 个人年，最终无法运行。项目负责人佛瑞德·布鲁克斯后来写了一本软件工程的经典书籍《人月神话》，承认在他管理这个项目的时候，犯了很多错误，造成了价值数百万美元的损失。

如果是说 OS/360 还只是造成了经济损失的话，Therac-25 事件就是真的导致了人员死亡。Therac-25 是加拿大原子能有限公司（AECL）所生产的放射线疗法机器，在 1985 年到 1987 年之间，在美国及加拿大，至少有六起和 Therac-25 相关的医疗事故是因为程序 bug，导致部分病患受到比正常剂量高一百倍的辐射，因而造成患者重伤甚至死亡。

发生这些惨痛的事，原因却并不难理解。

在计算机刚发明出来的时候，计算机的能力非常有限，只能接收简单的指令和运算，不需要软件工程也可以开发出简单的软件。

但是，当软件的规模越来越大，复杂度不断增加，软件项目开发维护过程中的问题就逐步暴露出来：软件产品质量低劣、软件维护工作量大、成本不断上升、进度不可控、程序人员无限度地增加。所以在 60 年代，“软件危机”的概念被提出来。

为了摆脱软件危机，1968 年秋季，北大西洋公约组织的科技委员会召集了近 50 名一流的编程人员、计算机科学家和工业界巨头，讨论和制定对策。在那次会议上第一次提出了“软件工程”（software engineering）这个概念。

从此诞生了一门新兴的工程学科：软件工程，它是为研究和克服软件危机而生。

在这次会议上，同时也提出了“软件工程”的定义：

为了经济地获得在真实机器上可靠工作的软件而制定和使用的合理工程原则。
（Software engineering is the establishment and use of sound engineering principles in order to obtain economically software that is reliable and works efficiently on real machines.）

1993 年，电气电子工程师学会（IEEE）给出了一个更加综合的定义：

将系统化的、规范的、可度量的方法用于软件的开发、运行和维护的过程，即将工程化应用于软件开发中。
（Software Engineering: (1) The application of a systematic, disciplined, quantifiable approach to the development, operation, and maintenance of software; that is, the application of engineering to software. (2) The study of approaches as in (1).）

如果你去搜索一下“软件工程定义”，你还能找到很多其他定义，这里就不一一列举。我们没必要花太多时间在这些字面解释上，关键是要抓住这些定义的本质：就是要用工程化方法去规范软件开发，让项目可以按时完成、成本可控、质量有保证。

软件工程的演化史

对比传统的工程学科，和软件工程最接近的就是建筑工程了。设想一下建一座房子：首先要先立项、设定预算，然后画设计图，再是施工，施工完成后，有专业人士进行质量检查，质检合格后入住。

开发软件本质上也是像盖房子一样，是从无到有创造的过程。工程化的方式，就是你分步骤（过程），采用科学的方法，借助工具来做产品。

于是参考建筑工程，整个软件开发过程也被分成了几个阶段：需求定义与分析、设计、实现、测试、交付和维护，这也就是我们常说的软件项目生命周期。

当然，各个阶段都会有人的参与，于是产生了软件项目里的各种角色：项目经理、产品经理、架构师、程序员、测试工程师、运维工程师。而对这整个过程的管理，我们通常称之为“项目管理”。

同时，也很自然就衍生出一套最基础的过程模型：瀑布模型。

瀑布模型的诞生，在当时是有非常重大的意义的，让软件开发从无序到有序，让大家更好的分工协作，同时每个阶段又衍生出各自的方法学和工具，例如需求分析、软件测试等等。

然而瀑布的特性决定了它只能从上往下流，而且从上到下走完整个周期很长，所以一旦出现了需求的变更，将会非常痛苦，很多事情需要重头再来。

于是基于瀑布模型，又衍生出 V 模型、原型设计、增量模型、螺旋模型等模型，试图改善瀑布模型存在的一些缺陷。这些改进模型的发展趋势上就是缩短项目周期，快速迭代。

这样到了 90 年代，各种轻量级开发方法例如 Scrum、极限编程等也不断被提出。到了 2001 年，这些轻量级开发方法一起组成了敏捷联盟，其后敏捷开发如同星星之火，逐渐形成燎原之势。

近些年，云计算、微服务这些新技术的产生，也对软件工程产生了影响。云服务让分工更细，很多企业可以将运维、服务器维护、DBA、甚至某些独立服务交给云服务商；微服务让大团队变成小团队，每个小团队可以更专注于细分领域，减少相互之间的依赖。 ​

一个公式

当你大致了解整个软件工程的演变发展史，你会发现，软件工程的知识，都是建立在软件项目的过程，或者说软件项目生命周期之上的。

基于软件过程，我们有了角色分工，有了对过程的管理和工具，对过程中每个阶段细分的方法学和工具。

现在，如果再回头看看我们的问题“什么是软件工程？”其实可以总结为：软件工程就是用工程化的方法来开发维护软件。也可以说软件工程就是用一定的过程，采用科学的方法，借助工具来开发软件。

如果用一个简单的公式表达，那就是：软件工程 = 过程 + 方法 + 工具。

总结

今天，我和你一起追溯了一下软件工程的起源，你知道了软件工程学科的产生，就是为了解决软件危机。也见证了整个软件工程的演变历史，从瀑布模型到敏捷开发。

从 1968 年提出软件工程到现在，正好是 50 年。在 2002 年，我最开始学软件工程专业的时候，还只有瀑布模型、需求分析、系统设计等这些传统软件工程内容，但是经过十几年的发展，在软件项目中，敏捷开发、持续集成、微服务等这些新兴内容已经开始在软件项目中占据越来越重要的位置。

可以预见，未来软件工程领域还会有新的概念、新的知识诞生。但是万变不离其宗，只要你抓住软件工程的本质，无论将来如何变化，你总能很快掌握新的知识内容。

而软件工程的核心，就是围绕软件项目开发，对开发过程的组织，对方法的运用，对工具的使用。

课后思考

当然，每个人对于软件工程的理解也是不同的，你理解的软件工程是什么样子的呢？欢迎在留言区留言讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

02 | 工程思维：把每件事都当作一个项目来推进

你好，我是宝玉。我今天分享的主题是：掌握工程思维，把每件事都当作一个工程项目来推进。

我大学学的是软件工程专业，毕业十多年后再回顾当年学的专业课，好多专业概念已经记忆模糊，唯有对一位老师的教诲记忆深刻，对我毕业后的职业生涯影响深远：

软件工程是一门用工程化方法解决软件项目问题的学科，其本质也是一门工程学科，这门课的知识在学完后，不仅可以应用在软件项目中，还可以应用于日常生活中遇到的一些问题，Everything is a project。

这句话对我影响很大。我真的开始在日常生活中尝试应用“Everything is a project”的概念，小到做作业，大到完成工作中的复杂项目。

解决这些问题的方式，就是参考软件生命周期和瀑布模型，把一件事情分成几个阶段：分析、设计、实施、测试、完成，然后制定相应的计划。这种方法不仅非常有效，让我的做事效率大幅提高，而且让我在看待事情上，能够更全面地、站在更高的角度去思考。

2010 年在上海的时候，我机缘巧合参加了一个关于产品设计与用户体验的线下活动，我可能是与会人员中，为数不多的非专业产品设计的同学。

在活动中组织者安排了一个游戏环节，每 5 个或 6 个人分成一个小组，来设计一个给老年人使用的手机，限时 30 分钟。完成后，每组选一个人上台花 5 分钟展示作品，最后投票选出做得最好的一组。

我的第一反应就是把它当作一个项目，于是快速地拟定了如下计划。

0~10 分钟（分析）：头脑风暴，收集想法。

11~15 分钟（设计）：根据头脑风暴结果，确定最终设计。

16~25 分钟（开发）：将想法画在纸上。

26~30 分钟（发布）：完善结果，准备展示。

这个计划小组成员都很认可，于是我们严格按照这个计划进行手机的设计。同时我观察了一下其他组的情况，大家都在热火朝天地讨论各种想法，似乎没有意识到时间其实是有限的。

轮到演示的时候，我们组毫无争议地拿到了第一，因为我们不仅准备充分，而且设计的手机功能完整，而其他很多组甚至还没来得及把想法完整地画下来。

什么是工程方法？

后来我才了解到，这种有目的、有计划、有步骤地解决问题的方法就是工程方法。工程方法不是软件工程独有的，几乎所有工程类别都可能会应用，例如建筑工程、电子工程等，只不过步骤可能略有不同。

(图片参考：Engineering Method）

工程方法通常会分成六个阶段：想法、概念、计划、设计、开发和发布。

想法：想法阶段通常是想要解决问题。最开始问题通常是模糊的，所以需要清晰地定义好问题，研究其可行性，检查是否有可行的解决方案。

概念：概念阶段就是用图纸、草图、模型等方式，提出一些概念性的解决方案。这些方案可能有多个，最终会确定一个解决方案。

计划：计划阶段是关于如何实施的计划，通常会包含人员、任务、任务持续时间、任务的依赖关系，以及完成项目所需要的预算。

设计：设计阶段就是要针对产品需求，将解决方案进一步细化，设计整体架构和划分功能模块，作为分工合作和开发实施的一个依据和参考。

开发：开发阶段就是根据设计方案，将解决方案构建实施。开发阶段通常是一个迭代的过程，这个阶段通常会有构建、测试、调试和重新设计的迭代。

发布：将最终结果包括文档发布。

如果你用这六个或者其中几个阶段对照日常工作和生活中遇到的问题，会发现绝大部分问题都可以看成一个项目，并且拆分成几个阶段，按照计划一步步完成。

站在整体而非局部去看问题

可能会有人说：“我不用这种工程方法去做事，一样可以做成呀，并没有什么区别。”确实，做一件事有很多种方式，但用工程方法去处理事情，有两点好处：

有一个被有效论证过的方法论指导你，可以帮助你提高成功概率，也可以提高效率。

当你用工程方法去思考的时候，你会更多的站在整体而非局部去思考，更有大局观。

前面提到的“设计一个老年机”的游戏就是个很好的例子，后来我在不同场合、不同人群中都组织过这个游戏，无论我如何强调时间限制（30 分钟）和产出（必须要演示结果），绝大部分人还是会把时间和注意力放在各种稀奇古怪的想法上，并沉浸其中。等到时间快到了，他们才发现还来不及把方案画到纸上，甚至还没确定该选哪个方案。

这种现象其实很常见，我们在日常处理事务时，天然地会选择自己感兴趣的、擅长的那部分，而容易无视整体和其他部分。

所以问题的核心并不在于是不是用工程方法，而是有没有把这件事当作一个项目，是不是能看到这件事的全貌，而不是只看到局部。

在工作分工越来越细致的今天，一个项目里面有产品设计、开发、测试、运维等诸多岗位，每个岗位都有自己的价值追求，测试人员关注找出更多 Bug、开发人员关注技术和高效开发功能、运维关心系统稳定。

分工带来的好处，就是复杂的任务可以分给不同的人来做，这也有助于技能的专业化，提高工作效率。但如果只站在自己的立场去考虑问题，没有人关注整体价值，就容易相互误解，产生矛盾、增加成本。

以下这些工作场景，估计你不会陌生。

产品经理提出一些天马行空、不切实际的需求，而技术上不可行或者实现成本很高，导致最后返工，造成资源浪费和进度延迟；

架构师为了满足开发上的成就感，更愿意自己“造轮子”，而不愿意采用现有开源程序或者购买合适的组件；

开发工程师喜欢在代码中使用各种设计模式或者最新技术，导致项目进度延迟，代码难以维护；

测试工程师不愿意学习自动化测试技术，导致测试周期较长，且容易出现疏漏；

除非产品经理特别注明，开发工程师和测试工程师不会注意用户体验上的细节。

这样的场景问题还有很多，为什么会出现这种情况呢？事实上，这在很大程度上都归因于大家只是站在自己岗位的角度来看问题，没有站在项目的整体角度来看。

如果能站在项目整体来看问题，你就会去关注项目的质量、项目的进度、项目的成本、项目的最终用户，那么上面这些场景将变成：

为了项目整体的效率和避免返工浪费，产品经理会及早和开发人员确认技术可行性，并对产品设计先行验证；

为了节约项目开发成本，提高开发效率，架构师选择成熟的架构，合理购买商业组件和使用开源程序；

为了提升开发效率，不影响项目开发进度，开发工程师尽可能采用成熟的技术，高效简洁地落实项目；

为了项目质量和效率，测试工程师学习自动化测试技术，将大部分测试变成自动化运行，极大地提高了测试效率和质量；

为了让用户有好的体验，不仅产品经理，每个人都会仔细体验用户界面，对于不合理的地方提出改进意见。

看起来很理想化对不对？但如果大家真能从自己做起，这样的结果并不是太难达到。

肯定有人会想，我又不是项目经理，干嘛要操这心呀？在这个问题上，我的看法是：每个项目成员，如果能多站在项目的角度去考虑，那么这样不仅对项目有利，更对自己有好处。

项目做成了，大家脸上都有光，也得到了更多的锻炼；项目没做成，不仅脸上无光，甚至可能面临丢工作的危险。很多人都有技术升管理的理想，能多站在项目整体角度去考虑的人，在日常工作中，也一定会有更多的锻炼机会，自然会多一些提升的空间。

我把这种思维方式称为“工程思维”。如果给一个定义的话，工程思维，本质上是一种思考问题的方式，在解决日常遇到的问题时，尝试从一个项目的角度去看待问题、尝试用工程方法去解决问题、站在一个整体而不是局部的角度去看问题。

在我的职业生涯中，一直习惯于用“工程思维”去思考问题，遇到问题，会尽可能把它当成一个项目，用工程方法有计划、有步骤地去解决它，这让我积累了不少的工程方法实践经验。

同时，我也更多站在整体的角度思考，这让我在项目中能更好地和其他同事合作，有更多的晋升机会。我还记得，我第一次开始管项目的时候，并没有慌张，而是把项目任务按阶段一拆分，然后按阶段制定好计划，再按照计划一点点执行、调整，很快就上手了项目管理的工作。

总结

改变，最有效的是方式是改变思想，这往往也是最难的部分。

当你开始学习这个软件工程专栏，我希望你不仅仅学到软件工程的理论知识，更希望你能用“工程思维”来思考你遇到的各类问题。

你不需要现在是一个项目经理或者管理者，也一样可以在日常生活中应用“工程思维”。比如学习这个专栏，你会制订一个什么样的计划？每个阶段达到一个什么样的成果？比如你今年有没有去旅行的计划？你会怎么制订你的旅行计划？

如果有兴趣的话，你还可以看看我以前写过的一篇文章“记录下两个孩子在 MineCraft 里面还原公寓的经历”。

这也是一个很有意思的工程思维实践，帮助孩子们在游戏里面还原公寓。这本质上也是一个项目，需要制订计划，需要设计、实现。我希望他们从小就有工程思维，能在未来有目的、有计划、有步骤地去解决日常生活的问题。

课后思考

最后，我希望你思考一下，你在日常生活中，会不会习惯上把一些事情看作项目？或者你看完这篇文章后，有没有觉得有一些事情可以看作项目？你会怎么应用工程方法来实践？

此外，在你参与的项目里面，你会站在项目整体的角度来考虑一些问题吗？你觉得如果站在项目整体的角度看，是不是可以对你的工作方式有所调整？欢迎你在留言区分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，欢迎把它分享给你的朋友。

03 | 瀑布模型：像工厂流水线一样把软件开发分层化

你好，我是宝玉，我今天分享的主题是：瀑布模型，像工厂流水线一样把软件开发分层化。

可以这么说：瀑布模型算是现代软件工程的起源，软件工程的发展，很大部分都是构建于瀑布模型的基础之上的。我们后面所学的软件工程的很多内容，都是源自瀑布模型的衍生，或者其中某个阶段的细分。

我在上大学期间，还并不懂软件工程瀑布模型这些知识。当时我自学了点编程知识，然后开始在外面接点做网站的小活，开发模式非常简单，接到活直接写代码，有问题就改。这样下来居然也做了不少小网站，但是大一点的网站项目就搞不定了，甚至手头的小网站项目，找个同学帮忙都不知道大家该怎么分工。

所以当时我也很好奇，大的软件系统是如何开发出来的？那么多人一起开发一个软件，系统是如何分工协作的？

后来到大三的时候，开始系统学习软件工程课程，我才开始了解到一些理论知识，包括我做小网站的这种开发模式，都有一个专业术语，叫边写边改（Code And Fix）模型。

这不是我的发明。在 1960 年初，软件开发刚开始起步，这时的软件开发是混沌无序的，那时候编程语言还是汇编语言为主，开发模式就是边写边改模型。如果程序员水平高，功能简单，还是可行的。

后来软件开发需求越来越多，功能越来越复杂，从事软件开发的人员水平也参差不齐，这种落后的软件生产方式已经无法满足迅速增长的计算机软件需求，从而导致软件开发与维护过程中出现一系列严重问题，这个现象也被称之为“软件危机”。

像这种边写边改的开发模式，为什么说不能满足复杂软件项目的需要呢？主要是有几方面的原因：

整个开发过程不可控，想基于这种开发模式做项目计划太难；

项目的人数多了后，无法有效分工协作；

项目开始的时候对需求几乎没有进行有效分析，对需求的理解容易出现偏差，后期导致很多返工；

项目编码完成后，没有有效测试，运行时 Bug 非常多。

瀑布模型的诞生

为了解决软件危机中的这些问题，在 1970 年，Winston Royce 博士借鉴了其他工程领域的思想，比如建筑工程，提出了瀑布开发模型，指出软件开发应有完整之周期，并将软件开发过程分成了若干阶段。像瀑布一样，从上往下，完成一个阶段继续下一个阶段。

瀑布模型把整个项目过程分成了六个主要阶段：

一、问题的定义及规划
这个阶段是需求方和开发方共同确定软件开发目标，同时还要做可行性研究，以确定项目可行。这个阶段会产生需求文档和可行性研究报告。

二、需求分析
对需求方提出的所有需求，进行详细的分析。这个阶段一般需要和客户反复确认，以保证能充分理解客户需求。最终会形成需求分析文档。

三、软件设计
根据需求分析的结果，对整个软件系统进行抽象和设计，如系统框架设计，数据库设计等等。最后会形成架构设计文档。

四、程序编码
将架构设计和界面设计的结果转换成计算机能运行的程序代码。

五、软件测试
在编码完成后，对可运行的结果对照需求分析文档进行严密的测试。如果测试发现问题，需要修复。最终测试完成后，形成测试报告。

六、运行维护
在软件开发完成，正式运行投入使用。后续需要继续维护，修复错误和增加功能。交付时需要提供使用说明文档。

瀑布模型在提出后，因为其简单可行，切实有效，马上就在很多软件项目中应用起来，一直到 2000 年前后，都是最主流的软件开发模型，即使到现在，你也能在很多软件项目中看到它的影子。

也是从那时开始，有了“软件生命周期”(Software Life Cycle,SLC) 的概念。

软件生命周期是软件的产生直到报废或停止使用的生命周期。而像瀑布模型这样，通过把整个软件生命周期划分为若干阶段来管理软件开发过程的方法，叫软件生命周期模型。

虽然现在瀑布模型已经不是最主流的开发模式，那为什么我们现在还要学习瀑布模型呢？

因为不管什么软件项目，不管采用什么开发模式，有四种活动是必不可少的，那就是需求、设计、编码和测试。而这四项活动，都是起源自瀑布模型，也是瀑布模型中核心的部分。

学好瀑布模型，才可以帮助你更好的理解这些内容。

如何用瀑布模型开发项目？

如果单纯看这些阶段的概念介绍，还是有点难以直观地理解整个软件开发过程，在这里拿我经历过的一个网站开发项目作为案例，来看一下如何使用瀑布模型来开发一个软件项目。

问题的定义及规划的阶段

大概在 2009 年的时候，Web2.0 还正火，公司老板打算做一个游戏领域的社交网站。

问题很明确，就是要做一个社交网站，并且用户能按照游戏来交友。至于可行性分析嘛，按照当时 Web2.0 的热度，这个似乎是可行的。那么就立项了。

然后老板问项目经理，这么样一个网站，你大概得多久做出来？项目经理一看，这么复杂一个网站，怎么也得半年才能做出来一个版本，于是说半年。老板说半年太久了，给你三个月吧，项目经理心中叫苦，最后讨价还价，决定四个月上线。

于是，项目经理按照四个月开始倒推项目计划：

需求分析——2 周；
软件设计——4 周；
程序编码——6 周；
软件测试——4 周。

需求分析的阶段

在项目立项后，产品经理首先和老板充分的沟通，了解老板的想法是什么，要做一个什么样的网站。在了解老板的想法后，产品经理对市场上同类的社交网站进行了调研，然后用原型工具设计了网站的原型。原型虽然很简陋，但是从原型可以看出来，项目要做成什么样子，便于确认需求。

原型拿给老板看后，老板再根据自己的想法提一些反馈，这样反复沟通确认，在原型设计确认清楚后，产品经理开始撰写产品设计文档，将原型设计落实到文档，将整个网站划分成不同的功能模块，例如用户注册、登录、添加好友等，确定每个功能模块需要哪些功能。

这个阶段产品经理是最忙的，那这时候其他人在干嘛呢？其他人都还挺轻松的，架构师研究网上流行的社交网站都采用什么架构，程序员、测试看看技术文档。

虽然最终确定了产品设计文档，但是因为中间反复确认的时间过长，原定 2 周能完成的需求分析，最后拖到了 3 周。项目经理一看，最终上线时间点没法延，那就只好压缩编码时间了，不行加加班！

项目计划变成了：

需求分析——3 周；
软件设计——4 周；
程序编码——5 周；
软件测试——4 周；

软件设计

产品经理的产品设计文档确定后，架构师开始做架构设计，UI 设计师开始设计 UI，测试经理开始针对产品设计文档写测试用例，产品经理还要进一步设计交互。

由于前期原型设计工作做的好，所以 UI 设计还是很顺利的，主风格定下来以后，各个界面就是细节的确认了。

因为产品设计文档写的详细，输入输出很清楚，测试用例也进展顺利。

至于架构设计这边，架构师很有经验，先把整体架构确定，写了个技术方案文档，和大家一起开会讨论，几次后确认了整体技术方案。按照功能模块一拆分，把其中一个功能模块做了一个样板，然后把各个子模块分给开发人员，大家一起协助做详细设计，然后再分别确认。

大家都如火如荼地忙起来了。如果一切顺利的话，软件设计 4 周应该能完成，可以进入编码阶段了。但是软件设计进行到第 3 周的时候，老板的想法发生了一些变化。

因为市场上已经有了游戏社交的网站，而且运营结果不算太好，而网页游戏正流行，如果我们的平台能接入网页游戏，这会是个不错的机会。

于是需求变更了，我们要能和其他网页游戏的用户系统对接，这个需求最开始是没有提出来，也没有考虑的。

项目经理考虑再三，决定还是接受这个需求变更，但是希望能多一些时间，老板没同意，认为时间点很重要，哪怕砍一点功能，牺牲一点质量也要如期上线。但就算这时候砍功能，设计工作还是少不了多少。

于是产品经理重新修改相应原型，再确认，再重新修改产品设计文档。变更完后，UI 设计的相关页面重新修改设计、测试人员修改测试用例，最苦的是架构师，当初没有考虑到要和其他用户系统对接，现在用户系统的设计都要重新考虑了。

于是为了赶进度，项目组开始加班，即使如此，软件设计阶段也推迟到了第 5 周才勉强完成。

项目计划又变了：

需求分析——3 周；
软件设计——5 周；
程序编码——5 周；
软件测试——3 周。

程序编码

终于进入编码阶段了，为了保证进度，加班还在继续，哪怕前期做了大量的设计，真到编码的时候还是有好多没有考虑到的，同时各个模块之间还存在相互依赖，有时候虽然自己功能开发完成，还需要等待其他人的功能完成才能调试，所以 5 周时间很快就过去了，而程序还不能完整地跑起来。

其实中间还有个小插曲，老板觉得还要加上支付的功能，但是项目经理觉得这个阶段改需求已经不可能了，以辞职为威胁总算顶回去了，打算放在下个版本加上。

终于到第 6 周的时候，有了一个勉强可以测试的版本。

项目计划现在变成了：

需求分析——3 周
软件设计——5 周
程序编码——6 周
软件测试——2 周

软件测试

留给测试的时间只有两周了，但是前期实在 bug 太多，两周测试时间过去，软件质量还是很糟糕，完全无法正常使用，于是项目不得不延期，一直延期了 4 周后，才算具备上线条件。

所以最终的项目计划差不多是：

需求分析——3 周
软件设计——5 周
程序编码——6 周
软件测试——6 周
和原定计划已经延迟了 4 周。

运行维护

网站上线后，好在前期并没有多少用户，但是线上 Bug 还是不少，需要继续修复线上发现的 Bug。

瀑布模型的优缺点

以上案例是我参与过的、用瀑布模型开发的软件项目的一个缩影，你会发现瀑布模型其实跟我们传统的建筑建造方式非常类似。我们拿盖房子的过程来看看瀑布模型。

客户想要盖一栋房子（初步的想法）。

客户一开始可能没想清楚想要什么样子的房子。（客户对需求还不清楚）

施工方开始找客户确认：用途是什么，要个几层的房子，什么建筑风格，希望什么时间完工，预算多少。（问题定义）

施工方根据客户提的需求，对比工期和预算，评估是不是值得做。（可行性研究）

施工方评估后觉得可行，于是和客户签订合同，约定价钱和工期。（立项，制定项目计划）

施工方开始跟客户沟通确认需求，例如每层户型如何，将来的装修风格等。（需求分析）

确认完需求后，施工方开始出建筑施工图，还画了漂亮的建筑效果图。（系统设计和 UI 设计）

施工方按照设计图开始施工。（程序编码）

这期间如果客户去参观施工情况，客户只能看到毛胚，只有最后施工完成才能看到最终样子。（在中间客户看不到结果，只有最后能看到结果）

原定二层是两个卧室，在房子施工过程中，突然客户说两个卧室不够，要改成三个卧室。这意味着施工方要对施工图重新设计，很多已经建好的房间要拆掉重建。（瀑布模型是很难响应需求变更的，而且越到后期代价越大）

工程质量检查人员对施工结果进行质量检测，如果不满足质量要求，需要修改。（测试）

最后验收通过后，客户入住。（上线）

所以你看，用瀑布模型开发软件，就像建筑工程里，盖房子一样简单和自然。每个阶段都有侧重的事情，就像需求阶段专注于搞清楚需求，编码阶段专注于实现。

最重要的是，这种编码前先设计、编码后测试、整个过程重视文档的方式，开发出来的产品，质量相对是有保障的。

但用瀑布模式开发，也存在一些问题。

最大的问题就是不能及时响应需求变更，越到后期变更代价越大。另外，通常要到最后阶段才能看到结果是什么样子。

我以前参与过的用瀑布模型方式开发的项目中，在开发和测试阶段加班是常态，原因就在于需求分析和系统设计可能会有延误，从而延迟了编码阶段的开始时间，压缩了编码实现的时间。

而在编码阶段，通常会发现很多设计时没有考虑清楚的问题，或者遇到需求变更，导致编码阶段即使加班加点也会大大延期，最后留给测试阶段的时间就不够多了。

鉴于瀑布模型存在的这些问题，后来又有很多人提出了其他的软件生命周期模型，比如快速原型开发模型、增量模型、迭代模型，以期保留瀑布模型的这些优点，克服瀑布模型中存在的问题。我们将会在后面的章节中，详细介绍瀑布模型衍生出的其他开发模型。

总结

从瀑布模型提出将近 50 年过去了，虽然现在大家一提起瀑布模型，似乎已经成了落后的代名词，但在当时是有划时代意义的。如果类比一下，我觉得瀑布模型的价值相当于工业界第一次提出流水线作业。

1769 年，英国人乔赛亚·韦奇伍德开办埃特鲁利亚陶瓷工厂。以前制作陶瓷只有“制陶工”一个工种，一个人从挖泥、制胚到最后烧制，要求很高。但是乔赛亚把原本的制陶流程从开始到结束分成了若干阶段，每个阶段可以由不同的人完成，从单一的制陶工分成了挖泥工、运泥工、扮土工、制坯工等，这样就大大提高了生产效率，也降低对工人的要求。

同理，瀑布模型的出现，也解决了软件项目开发中的几个重要问题。

让软件开发过程有序可控。瀑布模型的每个阶段都有明确的任务，每个阶段都有明确的交付产物，都有相应的里程碑。这些让整个过程更可控，而且能及早发现问题。

让分工协作变成可能。瀑布模型的六个阶段，也让软件开发产生相应的基础分工：项目经理、产品经理、架构师、软件工程师、测试工程师、运维工程师。

质量有保障。瀑布模型每个阶段都需要交付相应的文档，而文档的撰写和评审，可以帮助在动手之前把问题沟通清楚，想清楚。瀑布模型在编码结束后，会有严密的测试，只有测试验收通过后，才能上线发布。这些措施都让软件的质量更有保障。

课后思考

通过今天的学习，你可以对照一下你工作中的软件项目开发是不是采用的瀑布模型？和瀑布模型有哪些不同？你认为瀑布模型有哪些好或不好的地方？欢迎你在留言区留言，和我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

04 | 瀑布模型之外，还有哪些开发模型？

你好，我是宝玉，我今天分享的主题是：瀑布模型的衍生模型都有哪些，你该如何选择？

在上一篇文章中，我重点介绍了瀑布模型。你现在知道了，瀑布模型简单易行，对于软件质量是有比较高保障的。但是瀑布模型对于前期需求不明确的项目，很难开展需求分析，后续如果有需求变更，瀑布模型便很难响应。

而且，每个软件项目的情况各不相同，都有自己的特点。比如说：

有的项目风险很高，客户可能随时不给你钱了，得要做好准备，随时止损；

有的项目客户自己没想清楚要想的是什么，做出来后再提各种修改意见，必须得想办法降低变更成本；

有的项目客户希望能很快就能上线。

如果选用瀑布模型做这些项目，就会导致成本过高或者周期过长等问题出现。所以，并不是所有的项目都适合使用瀑布开发模型，你需要针对不同的情况做一些调整。

实际上，为了应对瀑布模型的不足，已经衍生出了很多其他的开发模型。今天，我将为你分享一些有代表性的瀑布模型的衍生模型，你可以了解到这些衍生模型的本质，在接手不同类型的项目时，可以灵活地进行选择。

快速开发快速改

快速原型模型

我刚毕业时参加了一个项目的开发，项目经理跟我说，这个项目怎么快就怎么写，不要在意代码质量、架构、性能这些，当时我表示很不能理解，哪有这样做项目的？

我还偷摸着花了很多时间想把代码写好，结果发现，这个快速做好的简单版本，主要目的就是为了给客户演示，跟客户确认需求，然后把客户的反馈记录下来，再去优化。这样几个小版本下来，基本上就把需求确定了，而我当时写的好多代码根本就用不上。

后来我才知道，这就是快速原型模型。

快速原型模型，就是为了要解决客户的需求不明确和需求多变的问题。

先迅速建造一个可以运行的软件原型，然后收集用户反馈，再反复修改确认，使开发出的软件能真正反映用户需求，这种开发模型就叫快速原型模型，也叫原型模型。

这就好比客户想要盖房子，但是他没想好要盖成什么样子，于是施工方就先搭了一栋彩钢房（就像工地里面搭的临时房子），让客户先用起来，然后再给反馈调整。

因为彩钢房搭建简单快速，改起来也相对容易。等到客户确定好需求，再在已经搭好的彩钢房的基础上完善，或者直接重新按照确定好的需求造房子。

不过，这样做也有一个问题，用彩钢房这种方式盖房子虽然快，但是房子质量不会太好，住的不算舒服，想有点个性化的风格也难。

同样的道理，也适用于软件项目。彩钢房就像是软件原型，重点是反映软件核心功能和交互，功能可以是不完整的，可靠性和性能要求不高，但开发速度可以很快。

原型模型因为能快速修改，所以能快速对用户的反馈和变更作出响应，同时原型模型注重和客户的沟通，所以最终开发出来的软件能够真正反映用户的需求。

但这种快速原型开发往往是以牺牲质量为代价的。

在原型开发过程中，没有经过严谨的系统设计和规划，可靠性和性能都难以保障。所以在实际的软件项目中，针对原型模型的这种快速、低质量的特点，通常有两种处理策略：抛弃策略和附加策略。

抛弃策略是将原型只应用于需求分析阶段，在确认完需求后，原型会被抛弃，实际开发时，将重新开发所有功能。类似于用彩钢房盖房子，确认完客户需求后，拆掉重新建。

附加策略则是将原型应用于整个开发过程，原型一直在完善，不断增加新功能新需求，直到满足客户所有需求，最终将原型变成交付客户的软件。类似于用彩钢房盖房子，最后还要做一番精装修，交付客户。

采用哪种策略来应用原型模型，还是要看项目特点，包括所采用原型开发工具和技术的成熟度。举例来说，如果客户对可靠性、性能要求高，那么就最好是抛弃策略，如果客户对质量要求不高，有简单功能就够了，那么可以试试附加策略。

快速原型模型即使到现在还一直有在用，用于低成本快速的确认需求。如果你将来遇到这种项目，就没必要花太长时间在代码质量上，赶紧做出来才是王道。

另外，原型制作并不一定要像传统代码一样进行设计编码，有很多原型工具，像 Axure、墨刀等，简单的拖拽就可以实现简单的界面和交互，同样可以达到确认需求的目的。现在原型设计已经成为产品经理确认需求的一个非常重要手段。

大瀑布拆小瀑布

瀑布模型的很多问题，根源都是周期太长。周期长所以中间难以响应变更，周期长所以客户很久才能看到结果，周期太长所以风险不好控制。如果能将周期变短，那么很多问题就迎刃而解了。

基于这种思路，产生了很多开发模型，比较典型的主要是：增量模型 和 迭代模型。

增量模型——按模块分批次交付

增量模型是把待开发的软件系统模块化，然后在每个小模块的开发过程中，应用一个小瀑布模型，对这个模块进行需求分析、设计、编码和测试。相对瀑布模型而言，增量模型周期更短，不需要一次性把整个软件产品交付给客户，而是分批次交付。

如果拿盖房子来比喻的话，就是先盖卫生间，然后盖厨房，再是卧室。

盖卫生间的时候，也要先分析需求，然后设计，再实施，最后验收。有时候也可以多模块并行，例如同时盖卫生间和厨房，前提是模块之间不能有依赖关系，比如，你不可能先盖二楼再盖一楼。

你会发现，增量模型将整个系统进行模块化处理，所以你可以分批次交付软件产品，使用户及时了解软件项目进展。如果一个模块有问题，或者需要做需求变更，对整体影响也有限。在开发的时候，也可以灵活地按照模块来分工，多个模块并行开发提升效率。

因为增量模型的根基是模块化，所以，如果系统不能模块化，那么将很难采用增量模型的模式来开发。另外，对模块的划分很抽象，这本身对于系统架构的水平是要求很高的。

基于这样的特点，增量模型主要适用于：需求比较清楚，能模块化的软件系统，并且可以按模块分批次交付。

迭代模型——每次迭代都有一个可用的版本

迭代模型每次只设计和实现产品的一部分，然后逐步完成更多功能。每次设计和实现一个阶段叫做一个迭代。

我们还是继续拿盖房子来举例：如果用迭代模型的方式盖房子，第一个迭代要先盖一个茅草屋，快速满足客户对房子的核心需求；第二个迭代再盖一个小木屋，比茅草房更大更舒适；第三个迭代再盖成一个豪华别墅，满足客户所有需求。

你要注意，无论是造小木屋还是大别墅，整个过程都会像一个完整的项目一样，包括需求分析、设计、实现与测试验收。

在迭代模型中，整个项目被拆分成一系列小的迭代。通常一个迭代的时间都是固定的，不会太长，例如 2-4 周。每次迭代只实现一部分功能，做能在这个周期内完成的功能。

在一个迭代中都会包括需求分析、设计、实现和测试，类似于一个小瀑布模型。迭代结束时要完成一个可以运行的交付版本。

迭代模型和增量模型很容易混淆，因为都是把大瀑布拆成小瀑布。这两种模型的主要差别在于如何拆分项目功能上。

增量模型是按照功能模块来拆分；而迭代模型则是按照时间来拆分，看单位时间内能完成多少功能。

还是用盖房子来理解，增量模型则是先盖厨房，再是卧室，这样一个个模块来完成。而迭代模型则是先盖一个简单的茅草房，有简易的土灶和土床，然后再升级成小木屋，有更好的灶和更好的卧室，这样一步步迭代成最终的房子。

我原来参与过的瀑布模型开发的项目，因为要很长时间才能看到最终结果，而且结果通常跟最初描述的结果相差较多，客户看到后多少会有些心理落差。

而后来改用迭代模型后，因为每次迭代完成后都有可以运行的版本，这样客户可以直观感受软件的进展，及时调整心理预期。尤其是当客户见证了一个软件从简陋到完善的过程，往往满意度是比较高的。

迭代模型最难的部分，在于规划每次迭代的内容和要达到的目标。多了可能完不成，少了可能造成每次迭代工作量不饱和，这需要在实践中去摸索，一个迭代一个迭代的去调整。

迭代模型由于在初始迭代时，只清楚当前迭代的需求，而不知道后续需求，设计可能会考虑不周全。这样的话，迭代一多，系统会有不少冗余，一段时间后就需要对系统进行重构。

另外每次迭代，用户可能会增加新的需求和对现有需求进行更改，因此开发时间上可能会比预期要长。如果你做的是小项目的话，并不建议使用迭代模型来开发。

我该选择什么过程模型？

除了上面提到的这几种模型，还有很多其他开发模型，要记住所有的开发模型很难。你搞透了瀑布模型，搞清楚了其阶段划分，结合一些应用场景，你就可以举一反三，了解绝大部分衍生模型。

我在这里给你列举几个常见的项目场景，我们可以一起来分析下，看看用什么模型适合。

场景一：外包项目，需要阶段验收

假如你现在是一家外包公司，你可以采用瀑布模型开发，但是甲方需要对你项目的每个阶段进行验收测试，以确认你是不是达到要求。

针对从需求定义一直到编码阶段，每个阶段都有对应的测试验收。如果画成图，就是下面这个样子的。

这个模型就是 V 模型，本质上它还是瀑布模型，只不过它是更重视对每个阶段验收测试的过程模型。

场景二：项目风险高，随时可能会中断

如果你现在要做一个风险很高的项目，客户可能随时不给你钱了。这种情况下，如果采用传统瀑布模型，无疑风险很高，可能做完的时候才发现客户给不了钱，损失就很大了！

这种情况，基于增量模型或者迭代模型进行开发，就可以有效降低风险。你需要注意的是，在每次交付的时候，要同时做一个风险评估，如果风险过大就不继续后续开发了，及时止损。

(图片来源：WikiPedia)

这种强调风险，以风险驱动的方式完善项目的开发模型就是螺旋模型。

场景三：山寨一款软件产品，希望能快速上线发布

其实软件行业山寨的案例不少，山寨项目的特点是，项目需求是明确的，不会有什么变化，这时候就可以选择增量模型，划分好模块，先实现核心模块，发布可运行版本，再增量发布其他模块。多模块可以同步开发。

场景四：客户都没想清楚想要什么，但是个大单子

很多项目，客户一开始都没想清楚想要的是什么，需要花很长时间去分析定义需求，但是单子很大，值得认真去做好。

那么这样的项目，你可以考虑拆分成四个阶段：

1. 初始阶段

主要是确定需求边界和主要风险，几乎没有什么开发工作。

2. 细化阶段

这个阶段主要是确定需求，可以采用快速原型模型开发，和客户对需求反复确认，需要辅助一定量的开发和测试工作。对代码质量可以要求比较低，重点是确认需求。可能需要一个或多个版本迭代。

3. 构造阶段

在需求确认清楚后，现在可以使用迭代模型来开发，逐步交付产品。这个阶段的重点是开发和测试。如果迭代中，有新的需求加入或者需求变更，也可以在新的迭代中加入。

4. 交付阶段

在开发和测试完成后，产品可以交付客户，根据线上运行情况还需要修复一些 Bug。这个阶段重点是测试和部署。也会有多个迭代。

整个过程看起来就像下图这样。

（图片来源：《构建之法》）

上面这种开发方式来源自统一软件开发过程（Rational Unified Process，RUP），适用于复杂和需求不明确的软件系统。

场景五：我的产品已经上线，但是需要持续更新维护

很多产品在上线后，还在保持不停的更新维护，修复 Bug、增加新功能，每个月甚至每周更新。

在这种情况下，迭代模型是比较合适的。固定时间周期，在固定的周期内选择适合的需求开发任务和 Bug 修复任务去完成，按时发布。

另外还可以尝试敏捷开发，也是基于迭代的开发模型，它也强调快速交付，每次交付系统的部分功能，来保证客户满意度。在敏捷开发中，系统交付的周期称之为冲刺（Sprint）。

严格来说，敏捷开发并不算是一种开发模型，更像是框架或指南。有各种开发模型来实现敏捷开发，比如说极限编程（Extreme programming），看板（Kanban）和 Scrum。有关敏捷开发，我将在下一篇中向你详细讲解。

总结

现在的软件项目，各种类型都有，根据项目特点，选择好合适的开发模型，可以让你事半功倍，降低项目风险，提高项目开发效率，控制项目成本。比如说：

一个以确认需求为主要目的的项目，就可以不用花太多时间在代码质量上面，低成本、高效做出来才是最重要的；

一个高风险的项目，则可以采用螺旋模型，出现问题及时止损；

一个很长时间加班加点，却一直没法上线，导致士气低落的项目，可以改成增量模型，先上线一个小模块，让大家看到成绩提升士气，然后再迭代，逐步上线其他模块。

同时，你也不必拘泥于这几种开发模型，还可以借鉴其他模型做的好的地方，甚至创造自己的开发模型，比如说你觉得敏捷的“站立会议”适合你的项目，那也可以借鉴过来。

课后思考

如果一个项目，需求不明确，后期可能会有比较大的变化，你觉得应该采用哪种开发模式或者哪几种开发模式组合比较好？你现在所参与的项目开发中，是采用的什么开发模式？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

05 | 敏捷开发到底是想解决什么问题？

你好，我是宝玉，我今天想跟你聊聊“敏捷开发”。

关于敏捷开发的实际应用，现在无外乎有以下几种常见的情形：

很多团队想敏捷开发，但不知道该怎么上手；

有的团队已经应用了一些敏捷开发的实践，然而效果不理想，不知道是敏捷开发的问题，还是自己实践方式不得当；

有的团队听说了敏捷开发，但是并不知道它是什么。

为什么会这样呢？今天我们就围绕敏捷开发来谈一谈，看看敏捷开发是什么，能帮助我们解决哪些问题，要不要实施敏捷开发，以及怎么能应用好敏捷开发。

什么是敏捷开发？

那什么是敏捷开发呢？有人认为：

敏捷开发就是 Scrum、极限编程；

敏捷开发就是每天站立会议、每两周一个 Sprint（字面意思是冲刺，可以理解为迭代）；

敏捷开发就是把需求变成故事，把故事写在便签上贴到白板，然后根据状态移动到不同的列；

敏捷开发就是用看板软件来管理项目。

然而，这些是敏捷开发的真正含义吗？

要理解敏捷开发，我们先要了解其诞生背景。在 2001 年那会，瀑布模型还是主流，我们知道，瀑布模型是一种“重型”的开发模式，整个流程走完通常周期很长，少则数月，多则数年。长周期导致风险增加、难以响应变化。

于是由瀑布模型衍生出很多模型，试图去改善瀑布模型存在的问题，我已经在上一篇文章中给你介绍了一些。不过除了介绍的那些以外，在当时还有一些不怎么有名，而现在却如雷贯耳的轻量级开发方法，例如极限编程（Extreme Programming，XP）、Scrum 等。

2001 初，17 位代表上述各种轻量级软件开发过程流派的领军人物聚集在一起，讨论替代瀑布模型这种重量级软件开发过程的新方法。

但是没能达成一致，所以退而求其次，把大家都认同的理念整理出来，也就是后来的敏捷宣言。这些人还一起成立了敏捷联盟。

（图片来源：敏捷开发宣言）

我们再回头来看前面大家对敏捷的定义，其实都是在从方法论、工具等方面解释敏捷开发。而敏捷宣言指出：

敏捷不是一种方法论，也不是一种软件开发的具体方法，更不是一个框架或过程，而是一套价值观和原则。

现实中关于敏捷的讨论，更多的是在讨论各种方法论和工具。不可否认，这些方法论和工具，能帮助团队“敏捷”起来，但它们和敏捷开发之间的关系，更像是“术”和“道”的关系。

各种敏捷框架、方法论和工具，就像是“术”，告诉你敏捷开发的方式，而敏捷则是“道”，是一套价值观和原则，指导你在软件项目开发中做决策。

这么说还是比较抽象，我给你举个例子。

敏捷开发中流行的站立会议，主要目的是为了保证团队成员充分的沟通，遇到困难可以及时寻求帮助。但是如果每天的站立会议流于形式，并不能起到有效的目的，则应该减少频度，甚至取消换成其他方式。

要不要在你的项目开发中使用站立会议，判断的依据就在于这样做是不是符合敏捷的价值观和原则。

也就是说，当你开发做决策的时候，遵守了敏捷开发的价值观和原则，不管你是不是用 Scrum 或者极限编程，那么都可以算是敏捷开发。

敏捷开发想解决什么问题？

如果你读仔细读了敏捷宣言，你会发现，宣言中右边的内容其实都是瀑布模型核心的内容：流程和工具、详尽的文档、合同谈判、遵循计划。

虽然敏捷开发并未对瀑布模型的价值进行否定，但也表明了瀑布模型做的还不够好，同时提出了一套自己的价值观。

比如说，我们开始做一个新项目，需要从客户那里收集整理需求，如果按照传统的软件开发模式，我们需要在开发前获得所有需求，然后和客户签订合同，在发布前都不会轻易修改需求。

但是如果我们采用敏捷开发模式来开发项目，那这样做显然违背敏捷的价值观：“客户合作高于合同谈判”。

所以如果是敏捷开发，在每个迭代后，都应该向客户收集反馈，然后在后面的迭代中，酌情加入客户反馈修改的内容。

结合敏捷开发提出的背景，你其实不难发现，敏捷开发就是想解决瀑布模型这样的重型软件开发方法存在的问题，用一种轻量的、敏捷的方法来改善甚至是替代它。

这些年敏捷开发也是一直这么做的。瀑布模型的典型问题就是周期长、发布烦、变更难，敏捷开发就是快速迭代、持续集成、拥抱变化。

如果用敏捷的方式盖房子

在讲瀑布模型的时候，我拿盖房子举了个例子，如果改成用敏捷开发的模式盖房子，则会是这样子的：

客户想要盖一栋房子（初步的想法）。

产品经理和客户进行了初步的沟通，把用户的需求写成了一个个用户故事（用简单的用户故事代替繁重的需求文档），例如：

作为一个上班族，我想要一个卧室，以便于休息；
作为一个家庭主妇，我想要一个厨房，以便于做饭。

施工人员根据用户故事和客户进一步沟通（客户合作高于合同谈判），然后对用户故事进行设计和实现；

每个用户故事开发时，还要给一个测试机器人编写测试脚本，让机器人可以自动测试（大量采用自动化测试），并且做好的用户故事可以随时被测试验收（随时发布，持续集成）；

每个 Sprint 四个星期时间（时间盒子，迭代时间固定）；

第一个 Sprint 搭了个草棚，一张床就是卧室，厕所就挖了一个坑，厨房还来不及搭建（每个 Sprint 会选择高优先级的用户故事），屋顶还在漏水（每个 Sprint 会定期发布，客户可以随时看到可用版本，即使还不完整）；

第二个 Sprint 有了简易厨房，同时修复了屋顶漏水的毛病（每个 Sprint 不仅完成用户故事，还会修复 Bug）；

第三个 Sprint 升级成了小木屋，但是忘记加上窗户（敏捷推崇自动化测试，但可能会测试不完备）；

第四个 Sprint 升级成了砖瓦房，窗户也开好了，客户可以入住。但是这时候客户发现一家三口的话，完全不够用，需要扩建到 3 个卧室。于是决定下个迭代改成 3 个卧室（响应变化高于遵循计划）；

第五个 Sprint，升级成了 3 个卧室，升级过程中把厨房下水道弄坏了（迭代过程中可能会导致质量不稳定）；

第六个 Sprint，修复了下水道的问题，房子也装修好了（迭代中不断完善）；

客户验收使用（上线）。

用敏捷开发的方式，不再像瀑布模型那样有严格的阶段划分，会在迭代中不断完善；不再写很多文档，而是和客户一起紧密合作；不再抵制需求变更，而是即时响应变更；不再等到测试阶段才发布，而是随时发布，客户随时可以看到东西。

当然，采用敏捷开发的模式也存在一些问题，例如全程需要客户参与，由于测试相对少一些 ，问题也会相应多一些。

敏捷开发和瀑布模型的差异

由于我大学时学软件工程，那时学的就是瀑布模型，毕业后很多年的项目开发都是以瀑布模型为主的，所以我在刚开始去看敏捷开发，总会以瀑布模型的方式类比敏捷开发，实践的时候也难以摆脱瀑布模型的影响。

直到近些年，我完整的在日常项目中反复实践敏捷开发，才逐步领会到瀑布模型和敏捷开发的一些差别。

这些年敏捷开发，已经逐步发展出一套 “Scrum + 极限编程 + 看板” 的最佳实践，Scrum 主要用来管理项目过程，极限编程重点在工程实践，而看板将工作流可视化。

我将基于 Scrum 和极限编程的实践，来对比一下敏捷开发模型和瀑布模型的差异。

1. 敏捷开发是怎么做需求分析的？

瀑布模型的一个重要阶段就是需求分析，要有严谨的需求分析，产生详尽的需求分析文档。而敏捷开发的需求，主要是来源于一个个小的用户故事，用户故事通常是写在卡片上的一句话，在 Sprint 的开发中，再去确认需求的细节。

比如一个用户登录网站的需求，在用户故事里面就是一句话：

作为用户，我想登录网站，这样可以方便浏览。

好处是减少了大量需求文档的撰写，可以早些进入开发。但这个对开发人员在需求理解和沟通的能力上要求更高了。

2. 敏捷开发是怎么做架构设计的？

瀑布模型在需求分析完了以后，就需要根据需求做架构设计。而在敏捷开发中，并不是基于完整的用户需求开发，每个 Sprint 只做一部分需求，所以是一种渐进式的架构设计，当前 Sprint 只做适合当前需求的架构设计。

这种渐进式的架构设计，迭代次数一多，就会出现架构满足不了需求的现象，产生不少冗余代码，通常我们叫它技术债务，需要定期对系统架构进行重构。

3. 敏捷开发怎么保证项目质量的？

瀑布模型在编码完成后，会有专门的阶段进行测试，以保证质量。在敏捷开发的 Sprint 中，并没有专门的测试阶段，这就依赖于开发功能的同时，要编写单元测试和集成测试代码，用自动化的方式辅助完成测试。

相对来说，这种以自动化测试为主的方式，质量确实是要有些影响的。

微软的 Windows 就是个很好的例子，在 Windows 10 之前，Windows 的开发模式是传统的类瀑布模型，有很长一段测试的时间，质量有很好的保障，Windows 10 开始，采用的是敏捷开发的模式，每月发布更新，稳定性要稍微差一些。

4. 敏捷开发是怎么发布部署的？

瀑布模型通常在编码结束后，开始部署测试环境，然后在测试阶段定期部署测试环境。测试验收通过后，发布部署到生产环境。

在敏捷开发中，这种持续构建、持续发布的概念叫持续集成，因为整个过程都是全自动化的，每次完成一个任务，提交代码后都可以触发一次构建部署操作，脚本会拿最新的代码做一次全新的构建，然后运行所有的单元测试和集成测试代码，测试通过后部署到测试环境。

持续集成是一个非常好的实践，极大的缩短和简化了部署的流程，而且自动化测试的加入也很好的保证了部署产品的质量。前期搭建整个持续集成环境需要一定技术要求。

5. 敏捷开发的 Sprint 和迭代模型的迭代有什么区别？

在上一章我介绍了增量模型和迭代模型，这两种也是一种快速迭代的方式，那么敏捷开发和迭代模型的区别是什么呢？

我们假设有两个团队，都要实现一个简单的用户系统，一个团队用迭代模型，一个团队用敏捷开发（Scrum），一个迭代 /Sprint 的时间周期都是 2 周（10 个工作日）。

迭代模型所在的团队，产品经理会先花 2 天时间去分析需求，写成需求分析文档，架构师会花 3 天时间来做设计，程序员会花 3 天时间编码，测试再花 2 天时间去测试，最后上线用户系统。

再看敏捷开发的团队，Product Owner（类似于产品经理）会把需求拆分成了几个简单的用户故事：用户登录、用户注册、找回密码、修改资料，然后放到当前 Sprint 的 Backlog（任务清单），Team（开发团队）成员开始从 Backlog 选择用户故事。

程序员 A 选了“用户登录”这个用户故事，他会去找 Product Owner 确认需求细节，之后动手实现这个用户故事。

功能完成后，同时程序员 A 还写了单元测试代码和集成测试代码，对登录的功能写了自动化测试。完成后，通过持续集成工具测试和部署到测试环境。部署完成后，用户登录功能就可以进行使用了。

这个过程，程序员 A 可能花了 4 天时间，做完“用户登录”这个用户故事之后，他又开始继续选取“找回密码”的用户故事来做，4 天时间也完成了。

其他程序员也和程序员 A 一样，他们也会从 Backlog 选择一些用户故事来做。

当团队中第 1 个用户故事部署完之后，测试人员就开始帮助测试，发现的 Bug 都提交到了 Backlog，程序员们在完成用户故事后，开始着手修复这些 Bug，正好在最后 2 天都修复完成。

从上面的例子，你可以看出，迭代模型本质上是一个小瀑布模型，所以在一个迭代里面，需要完整的经历从需求分析，到设计、编码、测试这几个完整的阶段。

所以像瀑布模型一样，刚开始测试的时候是不稳定的，到测试后期才逐步稳定下来，一般迭代前期也会相对轻松一点，而后期测试阶段可能会时间很紧张。

敏捷开发的 Sprint 中，没有严格的阶段划分，每个 Sprint 周期里面会完成多个用户故事。在用户故事的开发时，会针对用户故事有编码、自动化测试编码。当一个用户故事开发完成，即可通过持续集成自动发布到测试环境。

相对来收，敏捷开发中，整个 Sprint 的节奏是比较恒定，产品也是相对稳定的，即使用户故事没有完成，也不影响版本的发布。

因此，敏捷开发更注重软件开发中人的作用，需要团队成员以及客户之间的紧密协作。

该不该选择敏捷开发？

该不该选择敏捷开发，是很多团队纠结的问题。毕竟关于敏捷，有很多在中国落地失败的例子，是不是这种方法在国内水土不服？

其实，敏捷开发无论国内还是国外，大厂还是小厂，都已经有无数成功案例。这些年，软件工程中一些好的实践，像持续集成、测试驱动开发、结对编程、看板等都来自于敏捷开发。可以肯定，敏捷开发是一种非常好的软件开发模式。

但在应用上，也确实需要一些满足一些条件才能用好，例如：

团队要小，人数超过一定规模就要分拆；

团队成员之间要紧密协作，客户也要自始至终深度配合；

领导们得支持。敏捷需要扁平化的组织结构，更少的控制，更多的发挥项目组成员的主动性；

写代码时要有一定比例的自动化测试代码，要花时间搭建好源码管理和持续集成环境。

所以在选择敏捷开发这个问题上，你先要参考上面这些条件。

因为敏捷开发对项目成员综合素质要求更高，做计划要相对难一些。如果团队大、客户不配合、领导不支持，再好的敏捷方法也很难有效实践起来。

如果你要实践敏捷开发，建议先找个小项目进行试点，能证明可行了，再进一步推广。有条件的话，可以和一些顾问公司合作，请人做专门的培训和指导。

如果不具备条件，应该考虑先把其中一些好的实践用起来，比如说持续集成、每日站会、自动化测试等。

总结

我们今天一起学习了什么是敏捷开发，也就是敏捷开发是一套价值观和原则。也对比了瀑布模型和敏捷开发，其中的差异还是很大的。

瀑布模型面向的是过程，而敏捷开发面向的是人。敏捷开发要解决的，恰恰是瀑布模型中存在的一些问题。

最后，在要不要用敏捷开发这个问题上，不用过于纠结，看好敏捷开发，那就放心去用，觉得时机还不成熟、还不够了解，就先试点或者只是先借鉴其好的实践。

软件开发，最核心的是人，而不是用什么方法，以前没有敏捷开发只有瀑布模型的时候，也一样诞生了大量伟大的软件，像 Windows、Office。现在有敏捷开发，更多的是让我们多了一些选择。

在下一篇文章，还会再从大厂如何应用敏捷开发的角度，继续讲一讲敏捷开发的应用。

另外，敏捷开发涉及内容还是比较多，如果想有更多了解，可以阅读一些书籍作为专栏的补充。

除了“学习攻略”中推荐的一些书，还有像《用户故事与敏捷方法》、《敏捷武士︰看敏捷高手交付卓越软件》等这些敏捷实践的书籍也可以辅助看看。

课后思考

实施敏捷开发能给你的项目带来哪些好处？如果要实施，你打算从什么地方入手？如果已经实施了敏捷开发，你觉得用法对吗？有哪些做的好的或者不好的地方？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

06 | 大厂都在用哪些敏捷方法？（上）

你好，我是宝玉，我今天分享的主题是：大厂都在用哪些敏捷方法？我将分为上下两篇，来与你一起讨论这个话题。

在我还是一个野路子程序员，到处接私活做网站时，就开始好奇：大厂都是怎么开发软件项目的？直到毕业后，我前前后后加入了若干大中小型企业，包括这些年在美国高校、公司的一些经历，对大厂的项目开发有了比较多的了解。

其实大厂做项目也没有什么特别的，无非就是工程中常见的“分而治之”的策略：大项目拆成小项目，大服务拆成小服务，大团队拆成小团队。

服务之间通过商定好的标准协议进行通信，架构上将大的服务拆分隔离成微服务，大团队按照业务或者服务拆分成小组，按照一定的流程规范保障协作。最终，各个小组要负责的内容其实就不多了。

就像淘宝这种网站，不需要一个庞大的项目组，通过逐级分拆，一个小组可能就只需要负责一个页面中的一个小模块。

所以，也要归功于现在微服务、容器等新技术，可以将复杂的业务逐级拆分，让很多公司能真正敏捷起来。

在上一篇文章中，我有提到，团队要实施敏捷，不仅要小，还要组织扁平化。相对来说，美国的互联网大企业做的还是很不错的，组织架构都很扁平，工程师地位很高。

这些年，国内工程师地位应该也有很大提升，组织也在向扁平化发展。前些天我也看到阿里工程师写的一篇文章《敏捷开发的根本矛盾是什么？从业十余年的工程师在思考》，对这个问题有精彩的论述。

下面，我就带你一起看看，大厂具体是怎么应用敏捷方法的。

和敏捷开发相关的主要流程规范

大厂里流程规范很多，最开始你会不喜欢它们，后来会离不开它们。

这些墙很有趣。刚入狱的时候，你痛恨周围的高墙；慢慢地，你习惯了生活在其中；最终你会发现自己不得不依靠它而生存。这就叫体制化。——《肖申克的救赎》

这里，我简单将其中和敏捷开发相关的流程介绍一下。

一切工作任务围绕 Ticket 开展

早些年的项目开发，都是围绕着项目计划开展的，把甘特图打印贴在墙上，方便团队成员看项目进展到什么地步了。自从敏捷化后，开始变成了看板。

所谓的看板，就是把白板分成几个栏，每一栏为一类，分别写着“To Do（待选取）”、“In Progress（进行中）”、“Done（完成）”等，再把工作任务变成一个个五颜六色的即时贴，根据状态贴在不同的栏下面。

慢慢的物理的看板变成了电子看板，通过各种项目管理软件来管理跟踪这些任务，即时贴也变成了 Ticket（也有叫 Issue 的）。逐渐的，所有与开发相关的任务也都和 Ticket 挂钩了：

报一个 Bug，提交一个 Ticket ；

提一条需求，提交一个 Ticket ；

要重构一下代码，提交一个 Ticket 。

看板这种基于 Ticket 来管理跟踪任务的方式，看起来繁琐，但确实是很高效的一种方式。

每一个任务的状态都可以被跟踪起来：什么时候开始做的，谁在做，做完没有。

整个团队在做什么一目了然。

Ticket 和敏捷开发中的 Backlog（任务清单）正好结合起来，通过 Ticket 可以收集管理整个项目的 Backlog 和当前 Sprint（迭代）的 Backlog。

有了看板后，大家每天上班第一件事就是打开看板，看看当前 Sprint 还有哪些 Ticket 没有完成，哪些已经完成，哪些正在进行中，非常直观。

作为项目成员来说，做完手头的事情也不用去问项目经理该干什么事情了，直接从 To Do 栏选一条 Ticket 做就是了；对于项目经理，看看 To Do 栏还有多少没有被选取，就知道还剩多少 Ticket 没完成，看看 In Progress 栏就知道哪些 Ticket 正在进行中。

如果有 Ticket 在这一栏待太久或者这一栏 Ticket 太多，那可能就有风险了，就可以及时介入。

对于项目管理软件和 Ticket，我在后面章节中还会有进一步介绍。

基于 Git 和 CI 的开发流程

如果你的团队应用瀑布模型来开发，大概会有两大烦恼：代码不稳定和部署太麻烦。

早些年虽然也用源代码管理，但是大家都是在 master（主干）上开发的，所以 master 的代码特别不稳定，一不小心就可能被人签入了不稳定的代码。所以在上线前，有一段时间叫“代码冻结期”，意思就是这期间，除非是紧急修复，否则谁都不能往上面提交代码。

还有测试环境的部署也是个老大难问题，尤其是服务一多，编译时要注意各种依赖，注意各种环境的配置。所以更新测试环境是个大工程，以至于当年我在飞信的时候，专门有人负责部署测试环境。

上面的“代码冻结”和“专人部署”方案，可一点都不敏捷。所以团队想要敏捷起来，一定要解决代码不稳定和部署太麻烦这两个大问题。

好在基于 Git 的开发流程结合 CI 的自动测试部署，很完美的解决了这两大问题。

Git 本来只是源代码管理工具，但是其强大的分支管理和灵活的权限控制，结合一定的开发流程，却可以帮助你很好的控制代码质量。

我们假设现在 master 的代码是稳定的，那么怎么保证新加入的代码也稳定呢？

答案就是代码审查（Code Review）和自动化测试。如果代码有严格的审查，并且所有自动化测试代码都能测试通过，那么可以认为代码质量是可靠的。当然前提是自动化测试代码要有一定的覆盖比率。

关于这点，对于大厂来说倒不是什么问题，正规的项目组对于代码审查和自动测试代码的覆盖率都有严格的要求。现在还有一个问题，就是如何在合并到 master 之前把代码审查和自动化测试做好呢？

简单来说，就是每次要往 master 添加内容，不是直接提交代码到 master，而是先基于当前稳定的 master，克隆一个 branch（分支）出来，基于 branch 去开发，开发完成后提交一个 PR（Pull Request，合并请求）。

（图片来源：VSCode 项目 PR ）

PR 提交后，就可以清楚的看出来代码做了哪些改动，其他人就可以针对每一行代码写评论提出修改意见。如果确认代码没问题了，就可以通过代码审查。

接下来还剩下自动化测试的问题。这时候该 CI （持续集成）出场了。

如果你不了解 CI 是什么，可以把它想象成一个机器人，每次你提交一个 PR（严格来说是 Commit，这里略作简化）到源代码服务器，这个机器人马上就知道了。

然后它创建一个干净的运行环境，把你提交的代码下载下来，再下载安装所有依赖项，然后运行你的所有测试代码，运行完后，把测试结果报告给你。测试结果直观的反馈在 PR 上，绿色表示通过，红色表示不通过。

（图片来源：Video-React 项目 PR ）

关于 Git 和 CI，我在之后的文章中会展开讲解，这里只是为了展现敏捷开发方法的流程。另外，阮一峰老师写过两篇文章，《Git 工作流程》《持续集成是什么？》，你也可以先行阅读了解。

至此，代码审查和自动测试的问题都解决了。当一个 PR 代码审查通过，以及 CI 通过了所有自动化测试，就可以合并到 master 了，而且我们也可以认为合并到 master 后的代码也是稳定的。

至于自动部署测试环境，反倒是简单，就是 CI 这个机器人，在你代码合并到 master 的时候，再次运行自动化测试代码，测试通过后直接运行自动部署的脚本，把 master 代码部署到开发环境或测试环境上。

在这里以一个开发任务为例，大致讲解一下应用敏捷开发方法的基本开发流程：

把要开发的 Ticket 从“To Do”栏移动到“In Progress”栏；

从主干（master）创建一个分支（branch），基于分支去开发功能或修复 Bug；

编写实现代码和测试代码（单元测试和集成测试），是不是测试驱动不重要，看个人偏好或团队要求；

持续提交代码更新到分支，直到完成；

创建 PR（Pull Request，合并请求），邀请其他人帮忙 Review 代码，根据 Review 的结果，可能还需要更新几次；

CI 在每一次提交代码到代码库后都会自动运行，运行后主要做这些工作：
– 检查代码格式是不是符合规范；
– 运行单元测试代码；
– 运行集成测试。

最终这些检查都完成后，CI 会把执行结果显示在 PR 上。通常绿色表示通过，红色表示失败；

PR 能合并需要满足两个条件：CI 变绿 + 代码 Review 通过；

PR 合并后，CI 会自动构建 Docker Image，将 Image 部署到开发环境；

将相应的 Ticket 从看板上的“In Progress”栏移动到“Done”栏。

（图片来源：Jira）

正常来讲，你是需要严格遵守开发流程的，但偶尔肯定也有紧急任务，来不及写测试代码，这种情况下，一定要再创建一条 Ticket 跟踪，以确保
后续完成测试代码。

部署上线流程

最早的时候，程序员都是自己管服务器，但是由于这样过于随意，就会导致很多问题出现。

于是后来有专门的运维团队，将开发好的程序，编译好，数据生成脚本写好，然后写成部署文档，交给运维去手动部署。这个过程无比繁琐、无比慎重，通常几周才部署一次，遇上打补丁才隔几天部署。

这些年随着容器化、微服务、DevOps 这些技术或概念的兴起，部署已经变得越来越高效，大厂已经开始在部署流程上融合这些理念。

以前是运维人员按照文档部署，现在已经变成了 DevOps 写自动化部署工具，然后开发人员自己去部署生产环境。

现在大厂的部署也都实现了自动化，但是流程上还是有一些控制。

首先，部署的不再是程序代码，而是 Docker 的 Image，每次代码合并后 CI 都会自动生成新的 Image，测试也是基于 Image 测试。

部署生产环境之前，先在内部的测试环境充分测试。

部署生产环境前，需要审批确认，有 Ticket 跟踪。

部署时，先部署一部分，监测正常后再全量部署。

整个过程都有监控报警，出现问题及时回滚。

如果一切顺利的话，整个生产环境的服务部署过程通常几分钟就完成了，这在以前简直是不敢想象的事。

每日站立会议

在敏捷开发中，每日站会是非常有名的。在大厂，但凡实施敏捷开发的小组，上班第一件事，就是一起开一个站会，沟通一下项目的基本情况，这也导致会议室越发紧张起来。

虽然站立会议什么时间开都可以，但是早上无疑是最好的时机，一天工作的开始，开完会全身心去干活。

是不是站着开会其实不重要，重点是要高效沟通反馈。开会时间控制在半小时以内，半小时内不能完成的应该另外组织会议。

谁来主持站立会议呢？在敏捷的 Scrum 中，有一个角色叫 Scrum Master（敏捷教练、敏捷大师），主要任务就是保证各种敏捷流程的。

所以通常是由 Scrum Master 主持会议，也可以采用轮班制，每个星期换一名团队成员主持。负责主持会议的人，主要职责是组织会议，一个一个环节开展，控制好会议节奏。

开会都干什么呢？主要有三个话题：

1. 成员轮流发言

每个人轮流介绍一下，昨天干了什么事情，今天计划做什么事情，工作上有没有障碍无法推进。

一个成员的发言可能是这样的：“昨天我实现了用户登录模块的前端输入框，今天打算完成后端 API 调用，在实现后端的时候需要 API 组的支持，昨天发现他们文档有问题，不知道该找谁。”

要注意的是，这过程中很容易偏离主题，比如突然有人提了一句：“我们好久没团建了，是不是该出去玩玩了。”很可能大家都很 high 的讨论起来了，这时候会议主持者要及时打断，记录到“问题停车场”，让下一个人继续，先保证大家能高效完成这一环节。

问题停车场（Parking lot question），把需要进一步讨论的问题临时放到这里，一会儿再讨论。

通过这样的形式，项目成员可以相互了解任务进展，有困难也可以互相支援，及时发现问题和风险。还有一个重要因素，就是每个人对于自己提出的目标，也会信守承诺，努力完成。

2. 检查最新的 Ticket

前面提到所有日常工作都是基于 Ticket 来开展的，这些 Ticket 可能是测试报的 Bug，也可能是产品经理提交的需求，也可能是其他。

所以每天例会都需要检查一下新增的 Ticket，并且要甄别一下优先级，然后决定是放到当前 Sprint，还是放到 Backlog（任务清单）。

这个阶段同样要注意不能发散，不要针对 Ticket 的细节展开过多讨论，有需要讨论的同样可以先收集到“问题停车场”，会议组织者需要做好控制。

3. 停车场问题

在这个环节，大家可以针对之前来不及讨论的问题进行讨论，能在会议时间内解决的问题，就马上解决，不能解决的会后再私下讨论或者再组织会议。

当然，大厂的流程规范还有很多，在这里我仅列出与敏捷相关的主要开发流程。

总结

我们知道，在敏捷开发中有很多概念，像 Backlog、持续交付、每日站会等，这些概念最终要变成实践的话，就必须要通过一定的流程规范来保障这些概念的实施。

这就是为什么很多公司写代码要求你写自动化测试代码，为什么要用一些像 Jira、禅道这样的项目管理软件来管理任务，为什么要每天开站立会议，为什么要有代码审查。这些都不过是为了保障敏捷的实施。

如果你在实施敏捷开发的项目工作，就可以多去观察平时工作中这些和敏捷有关的流程规范，再结合敏捷开发中的知识点，就能很好的帮助你理解敏捷开发，理解这些流程规范背后的理论依据。

如果你工作中不是用的敏捷开发，也可以参考本文中提到的一些实践，尝试着试用起来。

在下一篇里，我还会以一个具体的项目小组对敏捷的应用为例，继续给你讲讲大厂都在用的那些敏捷方法。

课后思考

你的项目中，有哪些跟敏捷开发相关的实践？你觉得哪些做的好的地方，哪些做的不够好的？或者哪些是你疑惑的地方，都可以留言讨论。另外，你可以再思考一个问题：一个每周一个 Sprint 的敏捷项目，怎么保证每周都有交付，还能保证产品质量？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

07 | 大厂都在用哪些敏捷方法？（下）

你好，我是宝玉，我今天继续与你分享大厂的敏捷方法应用。

在上一篇文章中，我们一起看了一下大厂和敏捷相关的一些流程规范，同时也为你留了一道思考题：

如果每周一个 Sprint，怎么保证每周都有交付，还能保证产品质量？

所以在这一篇中，我们就以每周一个 Sprint 的小项目组为例，看看它的日常是怎么应用敏捷开发的。

一个应用敏捷开发的小组日常

这个小组是做网站开发的，基于微服务负责网站的某一个小模块。标准配置 7 人，4 个程序员（至少有一个资深程序员，有架构能力），1 个产品经理（Scrum 里面叫 Product Owner），1 个测试，1 个项目经理（Scrum 里面叫 Scrum Master）。主要负责网站某模块的日常维护。

在分工上：

产品经理：写需求设计文档，将需求整理成 Ticket，随时和项目成员沟通确认需求；

开发人员：每天从看板上按照优先级从高到低领取 Ticket，完成日常开发任务；

测试人员：测试已经部署到测试环境的程序，如果发现 Bug，提交 Ticket；

项目经理：保障日常工作流程正常执行，让团队成员可以专注工作，提供必要的帮助，解决问题。

在敏捷开发框架下，已经形成了一些很好的敏捷实践，这个小组也是基于 Scrum 方法做过程管理，基于极限编程做工程实践，看板可视化。每周一个 Sprint。

如何完成需求和修复 Bug？

这个小组的日常工作，也是围绕 Ticket 来开展的。所有的需求、Bug、任务都作为 Ticket 提交到项目的 Backlog，每个 Sprint 的任务都以看板的形式展现出来。

每个人手头事情忙完后，就可以去看板上的“To Do”栏，按照优先级从高到低选取新的 Ticket。选取后移动到“In Progress”栏。

每周一部署生产环境

没有人愿意星期五部署，那意味着如果部署后发现故障，可能周末都没法好好休息了。所以即使程序早已经测试好了，除非特别紧急，否则都会留在下一周再部署。所以部署放在上半周，这样后面遇到问题还有足够的时间去应对。

部署很简单，按照流程执行几个命令就可以完成生产环境部署。部署完成后，需要对线上监控的图表进行观察，如果有问题需要及时甄别，必要的话对部署进行回滚操作。但轻易不会打补丁马上重新上线，因为仓促之间的修复可能会导致更大的问题。

像敏捷开发这样一周一个 Sprint 的好处之一就是，即使这一周的部署回滚了，下周再一起部署也不会有太大影响。

每周二开迭代回顾会议，总结上个 Sprint

每周二的早上，这个小组一般还会预留一个小时的时间，因为常规的站会完成后，还有一个迭代回顾会议 (Sprint Retrospective)会议，目的是回顾一下在迭代中，团队有哪些做的好的地方，有哪些做的不好的地方。

对于需要后续改进的，需要创建相应的 Ticket，加入到 Backlog 中，在后续迭代中改进完善。

例如会议上，测试人员反馈说，上一个 Sprint，开发人员上线前几个小时还往预部署的分支里面更新代码，导致测试需要重新做回归测试，但因为时间不够了，没来得及测试完整，导致上线后不稳定，建议以后不要随意在上线前，在部署分支更新代码。

对于这样的问题，可能不止一次发生，说明流程上还是存在问题。所以最后大家商定，以后如果不是紧急的修复，就不要在预部署的分支上更新，确实要加，需要和测试先确认。

如果会议中要形成涉及项目的决策，最好是通过集体表决的方式决策，尽可能避免独裁式决策。因为敏捷的原则之一是要善于激励项目人员，给他们以所需要的环境和支持，并相信他们能够完成任务。

每周四迭代规划会，计划下周工作

每周四早上，也需要一个小时来组织会议。因为常规站会完成后，还有一个迭代规划会（Sprint Planning Meeting）。这个会议是要大家一起讨论下一个 Sprint 的内容。

在开会之前，产品经理和项目经理会商量好 Ticket 的优先级，会议上，大家一起按优先级高到低从 Backlog 中选出下个 Sprint 的内容。

团队每个成员都要对候选的下个 Sprint Backlog 中的 Ticket 从 1-5 分进行打分，1 分表示容易 1 天以内可以完成的工作量，2 分表示 2 天内可以完成的工作，5 分表示非常复杂，需要 5 天以上的工作量。

这里需要注意，打分时，要大家一起亮分，而不是挨个表态，不然结果很容易被前面亮分的人影响。

评估每条 Ticket 工作量的大概流程如下：

会议组织者阅读一条 Ticket，可能是用户故事，可能是 Bug，可能是优化任务。同时会询问大家对内容有没有疑问。

大家一起讨论这个 Ticket，确保充分理解这个 Ticket。

每个团队成员在心中对 Ticket 进行工作量估算。

会议组织者确认大家是否都已经确定估算结果，确认后，开始倒数：“3，2，1”，大家一起伸出一只手，亮出代表分数的手指头。

如果估算结果存在分歧，出分最高的和最低的各自说明理由，讨论后达成一致。

这种估算工作量的方法有个名字叫估算扑克，因为亮分时用扑克牌亮分而得名，但并非一定要用扑克牌。

用这种方式评估工作量有几点很明显的好处：

大家积极参与，详细了解需求。相比以前，可能只有当某个功能模块分配到自己头上的时候，才会去详细了解那部分需求，而其他开发人员可能都不了解这部分需求。

工作量是由实际参与开发的成员作出评估，往往更准确也更容易被接受。以前项目经理代为估算的模式，很容易不准确，或者让开发人员抵触。

促进成员的交流和经验分享。我们知道一般经验浅的新手估算工作量都会偏乐观，而经验丰富的老手则会更准确，通过这种方式，新手可以向老手学习到很多工作量估算甚至技术实现的经验。

所以，在经过几个 Sprint 的磨合后，一般一个团队在每个 Sprint 的产出是比较稳定的。比如说这样一个 7 人的小团队，一个 Sprint 预计可以完成 20-30 分的 Ticket。

每周五分支切割

周五标志着一周的工作要结束了，所以下班之前（4 点左右），要做 branch cut（分支切割），也就是要把当前主干上的代码，克隆到一个分支（branch）上。

为什么要做分支切割这一步操作呢？

经过一周的开发，master （主干）已经合并了不少新的 PR（Pull Request，合并请求），但是如果你直接把 master 的代码部署到生产环境，肯定还是不放心，毕竟自动化测试还是不能完全代替专业测试人员的测试。

所以我们需要把 master 上的代码部署到测试环境进行测试，并且对测试出来的 Bug 进行修复，直到稳定下来为止。由于 master 还需要一直合并新的功能，所以最好的方式就是每次 Sprint 结束，从 master 创建一个分支版本出来，然后基于这个分支部署和修复 Bug。

所以需要基于主干做一个 branch cut，创建一个预部署的分支，将预部署分支的代码部署到测试环境，这样下周测试人员就可以测试新的版本。测试验收通过后，预部署分支的代码会部署到生产环境。

每周轮值

小组里面除了日常开发工作以外，其实还有不少琐碎的事情，比如每周部署生产环境，每天部署测试环境，每周的 branch cut（分支切割），回答其他小组的问题，主持每日会议（不一定需要项目经理），这些事情如果都是一个人做难免会有些枯燥。

在敏捷开发中，鼓励发挥每个成员的主动性，所以每周轮值是一个不错的方式，可以让每个人都有机会去体验一下，帮助团队完成这些事情，更有集体荣誉感和责任感。

一些问题解答

上面只是选取的一个项目小组的日常，所以估计你看完还会有些疑问，在这里我把可能的问题列一下，先行解答一下。

1. 基于这种敏捷开发的方式加班多吗？

其实加不加班，绝大部分时候和是不是敏捷开发没关系的，还是看项目组的情况。

通常来说，基于敏捷开发一个 Sprint、一个 Sprint 迭代，节奏还是比较稳定的，这个 Sprint 做不完的任务也可以顺延到下个 Sprint，不影响发布。不像瀑布模型那样前松后紧，后期加班可能性大一些。

2. 一周一个迭代怎么保证质量？

以前我在使用迭代模型开发时，一般是 4 周左右的迭代周期，2 周就是极限了，所以最开始看敏捷开发用 1 周的迭代周期，心中也有疑惑，1 周时间又要开发又要测试，怎么保证质量？

实际实践下来，发现 1 周一个 Sprint 确实可行，而且质量也可以有保障，这里面有几个因素：

（a） 有足够比例的自动化测试代码，可以很好地保证质量。当用户的主要功能都通过自动化测试覆盖时，基本可以保证主要功能流程不会出问题。

（b） 一个 Sprint 开发完成后，并不马上部署生产环境，而是先部署到测试环境，会有 1 周时间测试。

（c） 有专业的测试人员进行测试，并非完全依赖自动化测试。有时候一些大的功能更新，甚至会组织全组成员一起测试，以弥补测试人员不足的情况。

在一个 Sprint 开发结束后，并不马上部署生产环境，而是先部署测试环境测试。

也就是说，虽然是 1 周的 Sprint，但是其实还有 1 周的时间进行测试。每个 Sprint 不仅开发新功能，同步还要修复以前版本的 Bug。

这样基本上可以保证有好的质量。而且这种 1 周的迭代，可以保持每周都有内容更新，还有个好处就是每周更新的内容不多，出现问题的话，很容易就定位到是什么地方导致的问题。

3. 基于敏捷开发如何做计划？

大厂里面通常会在上一年底确定第二年整年的大的开发计划，并确定上线的时间范围，每个季度再根据情况做一些调整。

这些大的计划最终会变成具体的开发任务，一个大的开发任务，会分拆到各个部门，各部门再将任务分拆到各个项目组。基于敏捷开发的话，主要就是看把这些开发任务放到哪几个 Sprint 去做，并且确保在规定的时间范围内完成。

至于工期的估算，在迭代规划会上会对每个 Ticket 进行打分，根据分数可以预估有多少工作量，要花多少时间。

4. 如何沟通协作？

组和组之间的沟通协作，主要通过邮件、会议、内部沟通工具，最终任务会以 Ticket 的形式体现。

团队内部的话，因为都在一起，所以沟通起来很方便，每天站立会议都是很好的沟通方式。

在敏捷开发中，有一种实践叫结对编程，就是两个程序员在一台电脑上一起工作。这个一直争议比较大，但是如果用来两人一起排查一些问题，或者是资深程序员带新手程序员，则是一种非常好的协作方式。

5. 上面介绍的实践案例和标准 Scrum 有什么不同？

我上面介绍的内容，确实和标准的 Scrum 有不少不一样的地方。

首先是角色名称不一样，在 Scrum 里面是分 Product Owner、Scrum Master 和 Team 三种角色，而在这个案例中是产品经理、项目经理和团队成员，但其实只是名字叫法不一样。

还有要注意一点，就是传统的项目经理，会是偏控制型角色，Scrum Master 则更多是一种服务型的角色，主要职责是保障敏捷流程的执行，以及提供必要的帮助，很多团队的决策就是采用集体决策的方式。

另外，Scrum 有四种会议，除了前面介绍的三种：每日站会（Daily Scrum）、Sprint 计划会（Sprint Planning）和 Sprint 回顾会议（Sprint Retrospective），其实还有一种会议是 Sprint 评审会（Sprint Review）。

Sprint 评审会主要是客户审查 Sprint 的完成结果。因为上面这个小组并没有直接的客户，都是完成产品经理提交的需求，而且沟通紧密，所以没有安排专门会议。

这个小组的站立会议并不是“标准”的站立会议，Scrum 的站立会议通常只有 15 分钟，并且只有轮流发言环节。

这里增加的每天审查 Ticket 环节，主要是为了将优先级高的 Bug 修复之类的 Ticket 放到当前 Sprint，及时响应，及时处理。有的项目组没有这个环节，是由测试人员或者 Scrum Master 直接将 Ticket 放到看板。

这个小组并没有使用用户故事来开发需求，而是由产品经理事先写好需求文档。在上一篇文章里面，提到了 Scrum 采用用户故事的方式，分拆需求，减少繁重的需求文档，在实现的过程中再沟通确认需求。

这是 Scrum 推荐的一种方式，也是一种高效的方式，但并不代表这是唯一的方式。如果有产品经理，可以提前几个 Sprint 就将需求文档写详细，一样可以达到高效的理解需求的效果。

那么这样还算敏捷开发么？

其实在《05 | 敏捷开发到底是想解决什么问题？》就有讲过，是不是敏捷开发，核心并不是应用了哪个方法，而是应用的时候，是否遵循了敏捷开发的价值观和原则。

比如说非标准的站立会议效率更优，那么就应该采用非标准的站立会议；如果有专业产品经理事先做好需求分析，可以达到解释清楚需求的效果，就没必要一定要用用户故事来理解需求。

总结

上一篇文章我们讲了大厂里和敏捷相关的一些流程规范，这一篇又讲了一个小组是怎么应用敏捷开发来开发项目的。

现在看上一篇文章中我留的思考题：如果每周一个 Sprint，怎么保证每周都有交付，还能保证产品质量？想必你已经有了答案。

要保障质量，还是离不开充分的测试，不仅要有自动化测试，还要辅助一定量的人工测试。敏捷开发虽然求快，但是不代表应该牺牲质量。

其实，大厂的敏捷实践并不神秘，关键是分而治之，最终团队小，项目小，所以才可以敏捷起来。大厂会注重流程和工具的应用，通过 Ticket 的方式来管理和跟踪开发任务，通过自动化的方式来部署。

大厂的敏捷实践，一般是基于 Scrum、极限编程和看板，针对各自项目组的特点，会有所侧重有所调整，在遵循敏捷的价值观和原则的前提下，做到高效使用。

希望上面介绍的敏捷应用，能对你理解敏捷开发有所启发，帮助你优化改进日常项目流程。还有要注意的一点就是，没有万能的开发模式，只有适合项目的开发模式，最重要的还是要摸索出一套适合你自己项目特色的开发模式。

限于篇幅，对于 Scrum、极限编程和看板，我并没有展开细讲，还需要大家自己辅助看看书，我在《学习攻略 | 怎样学好软件工程？》和《05 | 敏捷开发到底是想解决什么问题？》文章中也列了一些参考书籍。

留言区有同学推荐的文章《天下武功，唯快不破—新时代敏捷项目管理之道》对敏捷开发也有很不错的讲解，推荐阅读。

课后思考

看完本篇内容，你可以将上面介绍的开发模式和你现在的项目开发模式对比，你觉得有哪些好的地方可以借鉴？你觉得有哪些做的不够好，可以改进的地方？

另外，你也思考一下，为什么文章中，这个项目没有在一个 Sprint 里面同时完成开发和测试，而是把测试放在下一个 Sprint，这样做有什么优缺点？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

08 | 怎样平衡软件质量与时间成本范围的关系？

你好，我是宝玉，我今天与你分享的主题是：怎样平衡软件质量与时间、成本、范围的关系。

在《从软件工程的角度解读任正非的新年公开信》这篇文章中，我已经提到了“软件项目管理金三角”的概念。由于这个内容对于软件工程来说，非常之重要，所以我今天特别展开再讲一下。

你会发现，在实际的软件项目中不乏这样的例子：

一个项目，正常估算，要三个月才能完成，但是老板或客户要压缩到一个月完成，而你不知道如何说服他们；

项目开发一半，产品经理告诉你，有一个非常紧急的功能，要增加到这个版本中，你不知道该不该拒绝，或者如何拒绝；

听说迭代模型很好，你也尝试使用迭代模型，但是每次迭代时间到了还是完不成，只能把迭代时间延长，最后又做回传统的瀑布模型了；

你们组用瀑布模型开发，一到项目后期总免不了加班加点赶进度，为什么他们用敏捷开发的加班要少一些？

其实，这些日常项目中涉及时间、成本和范围的问题，都离不开“软件项目管理金三角”的概念。

掌握好这个知识点，学会平衡软件质量与时间成本范围的关系，可以帮助你更好的驾驭项目中的各种问题，也可以帮助你更好地理解软件工程中各个模型，尤其是瀑布模型和敏捷开发。

什么是软件项目管理金三角？

在现实生活中，我们都知道，做产品想“多、快、好、省”都占着，是不可能的，最多只能选两样。

想要便宜和质量好，就要花时间等；想要快还要质量好，那就得多花钱；想要又便宜又快，那就得接受难用、质量差。

而在软件项目中，也有一个类似的平衡关系，就是软件质量（产品的质量，客户的满意度）与范围（需要实现多少功能）、时间（多久可以完成）、成本（花多少钱）四个要素之间的平衡。

上面这个图就是著名的项目管理金三角（以下简称“金三角”），三条边分别是时间、成本和范围，中间是质量。

为什么四个要素，是“质量”放在三角形的中间？

因为软件工程的目标就是要构建和维护高质量的软件，所以项目的质量是高于一切的。也就是说，“质量”这个因素一般不会妥协，因此把“质量”放在三角形中间，然后在时间、成本、范围这三条边之间寻求平衡。

质量往往也是其他三个因素平衡后结果的体现，想要做的快、成本低、功能多，最后一定是个质量很差的产品。

如何应用“管理金三角”做决策？

我在专栏中常用“道术器”来比喻软件工程中的各个知识点，“金三角”无疑就是“道”级别的。

项目管理其实就是项目中一系列问题的平衡和妥协，而“金三角”理论则为我们的平衡提供了理论指导，了解这三个因素分别对项目其他方面产生的影响，可以帮助你在做决策时进行权衡取舍。

当你接手一个项目，项目的进度、成本和范围指标很容易可以跟踪到。有了这些信息，你就可以及时发现问题，调整“金三角”的边，及时解决，以防止这些小问题发展成大问题。

我来举两个例子，看看“金三角”是如何应用的。

老板要压缩项目时间怎么办？

当项目经理，常遇到的问题之一就是时间被压缩，比如文章中开头举的例子，老板问我一个项目多久能完成，我按照经验，觉得要三个月，老板觉得三个月太久了，要砍到一个月就上线。

最开始的时候，我就是据理力争，说这不科学，肯定不行呀。老板说时间点很重要，必须要一个月上线。结果就是大家吵得不欢而散，最后还得加班加点做，质量也不好。

后来我学乖了，先用“金三角”知识分析了一下：老板希望时间是 1 个月，也就是说时间这条边被缩短了，那么结果就是会影响到另两条边：范围和成本，如果另外两条边可以调整，也不是不可以。

于是再遇到这种问题，我就换了一种方式跟老板沟通：“一个月也不是不行，就是我们得需求调整一下，第一个版本只能做一些核心功能，剩下的后面版本再加上（调整范围）。另外还得给我加两人，不然真做不完！（增加成本）”

这样的方案一提出来，就好沟通多了，最后重点就变成了砍多少功能和加多少人的事情了。

产品经理要临时加需求怎么办？

在文章开篇我提到一种情况，项目开发一半，产品经理告诉你，有一个非常紧急的功能，要增加到这个版本中，怎么办？我们拿“金三角”知识先套用一下。

增加需求，也就是范围这条边要增加，那就必然对成本和时间这两条边造成影响，要么延期，要么增加成本。

面对这种临时加需求的情况，我们也不需要直接说不能加，而是清楚的让产品经理认识到这样做的后果：进度延期，需要更多的成本。如果这个功能真的太重要，可以接受延期，也不是不可以接受，那就重新制定新的项目计划好了。

所以你看，如果我们能应用好“金三角”的知识，很多软件项目中问题，一下子就多了很多方案可以选择了。

瀑布模型和敏捷开发如何平衡时间成本范围的关系？

除了可以将“金三角”的知识应用在软件项目中，还可以应用它来理解和应用软件工程中的开发模式，尤其是瀑布模型和敏捷开发这两种典型的开发模式。

瀑布模型有严格的阶段划分，有需求分析、系统设计、开发和测试等阶段，通常在开发过程中不接受需求变更，也就是说，我们可以认为瀑布模型的范围是固定的，其他两条边时间和成本是变量。

所以使用瀑布模型开发，如果中间发现不能如期完成进度，通常选择的方案就是延期（加班），或者往项目中加人。

我们再来看敏捷开发，敏捷开发中，是采用固定时间周期的开发模式，例如每两周一个 Sprint，团队人数也比较少。所以，在敏捷开发中，时间和成本两条边是固定，就只有范围这条边是变量。

这就是为什么在敏捷开发中，每个 Sprint 开始前都要开 Sprint 计划会，大家一起选择下个 Sprint 能做完的任务，甚至于在 Sprint 结束时，没能完成的任务会放到下个 Sprint 再做。

这时候再想想文章开头我们提到的问题：

听说迭代模型很好，你也尝试使用迭代模型，但是每次迭代时间到了还是完不成，只能把迭代时间延长，最后又做回传统的瀑布模型了。

你现在是不是就明白了：如果不能固定“时间”这条边，就会导致时间也成了变量，迭代自然无法正常推进。

如何平衡好软件质量与时间成本范围的关系？

那么怎么样才能平衡好软件质量与时间成本范围的关系呢？

前面我们说日常生活中“多、快、好、省”最多只能选两样，其实如何平衡好软件质量与时间成本范围的关系也是一样的道理，我们只能最多选择两样，然后在另一边或者另两条边去寻找平衡。

所以第一件事就是：从时间、成本和范围这三条边中找出来固定的一条或者两条边，再去调整另一条边。

下面，我来分析一些案例，帮助你更好地理解。

1. 淘宝网站第一个版本是怎么做到一个月上线的？

这个故事其实我是从极客时间《从 0 开始学架构》专栏看来的，李运华老师在《架构设计原则案例》一文中举了淘宝网站的例子：

2003 年 4 月 7 日马云提出成立淘宝，2003 年 5 月 10 日淘宝就上线了，中间只用了一个月时间。

好，如果你是当时的淘宝网站负责人，马云要你一个月上线淘宝网站，功能还不能少，你怎么办？

第一件事当然是先应用“金三角”分析一下：时间这条边被固定了，只能一个月；功能也不能少，范围这条边也限制住了，那就只能在成本上想办法了。要么一下子雇很多牛人，要么直接买一个现成的电子商务网站，然后修改。

显然，直接买一个网站，再雇一堆牛人的方案最好，所以淘宝网站就这样在一个买来的网站基础上，由一堆牛人快速搭建起来了。归功于淘宝网站的快速上线，刚推出后，正好赶上“非典”，网购需求增大，淘宝网一下子就火爆起来了。

从成本角度我们还有可以去做的，比如说有同学在看完《06 | 大厂都在用哪些敏捷方法？（上）》这篇文章后，也想在团队里面推行代码审查和 CI，但是苦于搭建这一套 git+CI 的系统没有经验，不知道该如何下手，怎么办呢？

我的建议就是刚开始就没必要自己去折腾了，买一套 GitHub 的企业版，加上支持 GitHub 的商业 CI 系统，花不了多少钱，而且可以节约大量搭建这种系统的时间。

2. 极限编程是怎么做到“极限”的？

前面在介绍敏捷开发的时候，也提到了极限编程（eXtreme Programming，XP），是目前敏捷开发主流的工程实践方法，极限编程的“极限”（Extreme），意思就是如果某个实践好，就将其做到极限。比如：

如果做测试好，就让每个开发人员都做测试 ;

如果集成测试重要，就每天都做几次测试和集成 ;

如果简单的就是好，那么我们就尽可能的选择简单的方法实现系统功能 ;

……

极限编程的“极限”理念，产生了很多优秀的实践方法，例如持续集成、自动化测试、重构等。

这些实践帮助我们可以在短时间的迭代中，产生高质量的代码。我们用“金三角”的理论来分析一下极限编程在 Sprint 中的应用。

在一个 Sprint 中，计划好了当前 Sprint 要做的工作内容后，那么极限编程怎么帮助我们提高代码质量呢？

一个 Sprint 要做的内容是确定的，相当于成本和范围这两条边都固定了，时间这条边就成为变量了。要么通过加班延长工作时间，要么通过提升效率、减少浪费帮助我们提升时间利用率。

极限编程，就是通过帮助我们提升效率和减少浪费这方面来做的。比如说：

持续集成，通过自动化的方式帮助我们部署，节约了大量需要人去手动部署的时间；

自动化测试，通过自动化测试，节约测试时间，另外，有了自动化测试，可以避免后面修改代码产生 Bug，减少了大量的浪费；

只做刚好的设计，避免设计时考虑了太多不必要的可能，造成浪费。

其实我们在项目中也有很多地方可以借鉴这种思路，比如说写代码的时候，少自己造轮子，多使用成熟的开源或者商业组件，可以提升效率；比如把需求想清楚搞清楚再去开发，可以减少很多返工的时间成本！

3. MVP 模式是怎么诞生的？
这些年流行的 MVP（minimum viable product，最小化的可行性产品）模式，是一种快速推出产品的模式：一开始只推出最核心的功能，满足用户最核心的需求，然后在用户的使用过程中收集反馈，进一步升级迭代。

这种模式怎么诞生的呢？还是应用“金三角”理论，要快速推出产品，还想成本不用太高，那就意味着时间和成本这两条边是固定的，剩下范围这个变量。

所以最简单有效的办法就是砍掉一些重要性不那么高的功能需求，只保留最核心的需求。通过缩小范围的方式，达到快速推出高质量产品的效果。

类似的道理，我们程序员，在遇到很多功能忙不过来的时候，可以主动的去和项目经理协商，砍掉一些不那么重要的需求，把精力放在核心需求上，保证项目可以如期上线。

总结

其实，要平衡好软件质量与时间成本范围的关系并不难，你只需要记住，最重要的是根据“金三角”的三条边，找出来固定的一条或两条边，然后去调整剩下的边，达到平衡。

软件项目的“金三角”很多人都知道，主要是不知道如何应用到实际的项目中，希望这篇文章能为你提供一些思路，帮助你在项目中真正应用好这个非常实用的知识。

课后思考

关于今天的内容，邹欣老师在《构建之法》书中，提出了一个很好的问题。我也在这里列出来，希望你可以思考一下。

顾客对于要交付的软件和服务，都是有很多美好的需求的， 用户希望软件开发的又快， 又便宜 （人工便宜），质量又好， 最好是免费的。 那么，如果只满足部分的需求， 我们会得到什么样的软件呢？

例如，上图的 ① 说明， 如果希望软件做得又快，又低成本（人工便宜）， 不考虑其他要求， 那么，我们会得到大致什么样的软件呢？

例如，上图的 ⑤ 说明， 如果希望软件是免费的，而且要很快交付，越快越好， 那么，这样的软件有什么特点呢？

请把 ① 到 ⑦ 的需求组合会导致什么样的软件， 会出现什么样的问题， 都列出来。

另外，对于质量和时间成本范围的平衡，你有没有什么应用的案例？你对你当前项目的时间、范围和成本都清晰吗？有没有什么可以做的更好的地方？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

“一问一答”第1期 | 30个软件开发常见问题解决策略

你好，我是宝玉。专栏已经上线更新一段时间，看到同学们对软件工程有了更深刻的理解和思考，我很受感触。

有些人说走了很多弯路，日常搬砖，增删改查；也有的同学跟我一样“野路子”程序员出身，非常困惑希望建立自信；还有的同学发表上千字的学习心得，可以说非常用心。

编码的最终目的还是为了实现一整个软件的开发，在程序员的晋升之路上，总有单独挑大梁负责项目的时候。你会发现软件开发中的很多问题，都是可以通过软件工程的知识来解决的。

在已经更新的文章中，同学们经过思考，结合自己的工作实践场景，提出了非常好的问题。我们专栏的留言内容成为了专栏最好的补充。于是我就将留言板中的答疑和精彩留言进行汇总，方便你更好的查阅和理解专栏内容。

一问一答

No.1
hua168：学这个专栏需要哪些基础为前提的？开发都要学哪些基础东西？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：学习这个专栏，不需要你有特别的基础，当然有一些项目经验可以帮助你更好的理解。至于要学什么基础的东西，其实你可以从另一个角度思考一下：开发的价值是体现在哪的？

开发的价值是通过在项目中创造价值体现的，所以你要考虑学什么能帮助到你更好的在项目中创造价值。比如说除了具体的编程技能外，还可以从这些方面思考：

提升对需求分析和理解的能力，这样你就知道要做的是什么，减少返工；

提升架构和抽象的能力，能把需求抽象成架构设计，能把复杂的问题通过架构分解成简单的问题；

高效率的编码，完成需求，等等。

No.2
hua168：软件工程在游戏项目上，是不是也一样呢？
zhxilin℃：对游戏行业的过程模型有什么理解？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：万变不离其宗。游戏项目一样离不开软件工程，游戏开发本身也是软件开发，只是有些名字换了，比如产品经理变成了游戏策划，产品设计变成了游戏策划案。游戏开发一样要有需求分析、架构设计、编码、测试等关键活动。只是游戏项目的需求变更频繁、节奏快，用增量或者迭代要好很多！另外也可以试试敏捷开发。

No.3
于欣磊：现在已经进入云计算时代，基本上大中小企业都在上云，复杂逻辑都在云端处理，真的还需要软件工程里讲的开发要搞这么多流程么？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：是的，云计算的兴起可以减少很多劳动，但不代表你就什么都不用做了，还是要做需求分析，再去做架构设计，做完架构设计你才能清楚哪些可以用云计算，那些需要自己去实现。最后编码完了，一样还要测试的。

No.4
hua168：现在运维的前景怎么样？感觉竞争很理解，很多小公司都不招，开发兼职了运维，各大云出了一些维护，监控工具，对他们来说够用了，感觉发展空间变小了，运维开发招也少了……也有人提到运维职业会消失，难道要转开发？那运维开发能力也争不过真正的开发啊。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：你这个问题很有代表性，现在云服务兴起后，传统运维的职位在减少，所以 DevOps 在兴起。DevOps 和运维的主要差别就是 DevOps 不仅有运维能力，还有开发能力，可以站在运维和开发更高的角度去看问题，帮助自动化的，稳定的交付部署产品。你不用完全转开发，但是应该要学习一些开发知识，尤其是自动化脚本相关的。

No.5
行者无疆：传统瀑布模型前期进行了完整的需求评估，在技术选型，系统架构，实施路径上可以做好全面的规划，虽然周期长，不必要的反复工作会少很多，目标也更容易控制。

那敏捷模型的迭代方式并不会把需求都考虑全面，未来的迭代可能会造成前面的技术架构或者实施细节等都不能满足新需求的要求。所有工作都要重来的问题，会存在大量的重复工作和资源浪费。 敏捷模型是如何有效地规避这些问题的呢？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：你说的问题确实存在，导致常说的技术债务问题，所以需要定期去重构，改进这些问题。迭代过程中的重复工作确实存在，但是软件开发中的浪费其实主要不是在于迭代过程中的重复工作，而是在于需求不明确和需求变更导致的返工或失败。

敏捷开发持续发布稳定版本的理念还是利大于弊。有一些项目其实是瀑布模型和敏捷开发的结合，需求分析和系统设计的时候用瀑布模型，开发和测试阶段用敏捷，也是个不错的选择。

No.6
Geek_85f782：如果说软件工程 = 过程 + 方法 + 工具，其中过程是否就是具体指软件的生命周期？方法是指选用的生命周期模型，比如瀑布、螺旋、迭代、敏捷？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：我认为过程应该包含过程模型采用的方法。而方法是指基于过程模型之下的方法。因为过程模型决定了软件开发过程是什么样的，进而决定了采用什么开发方法。

比如你选择了瀑布模型，整个软件开发过程就是按照瀑布模型的分阶段来进行，对应的方法就是瀑布模型中的方法，例如需求分析、架构设计；如果你选择了敏捷开发，则整个开发过程就是一种敏捷迭代方式，后面的方法对应的就是敏捷开发的一套方法体系，例如 Scrum、用户故事、持续集成等。

No.7
hua168：中小公司，开发人员流失严重，如果像工厂流水线那样，即使核心开发人员全部走了，新招的开发在没有人带的情况下，能继续接上开发……除了制定规范，开发文档之外还有那些措施？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：软件开发，核心就是人，如果没有人，规范和文档都没意义的。要留住人，一个么得舍得给钱，另一个得有个好的环境，还有就是要有梯队，能把新人培养上去。饭店里只有一个大厨，大厨当然敢乱提要求，如果大厨多几个，就不担心了。还是得要舍得下本钱招优秀的人。

No.8
白发青年：如果是外包项目，作为项目的乙方，如果采用敏捷开发，最初的工作量就很难完整估计，不利于双方的合同签订。不知老师是否有好的建议？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：这个问题通常有两种解决方案供参考：

你按照瀑布模型的方式去估算工作量，然后签订合同。开发的时候你需求分析和架构设计还是用瀑布模型的方式，但是编码和测试用敏捷开发。这是一种不错的折中方案；

你把所有需求拆分成用户故事，对用户故事进行打分（了解下计划扑克之类的打分方案），然后可以算出来一个总分数。另外按照你以前敏捷开发的经验，可以知道每个 Sprint 大概能完成多少分，这样你就能大致推算出来工期。

No.9
Charles：瀑布模型非常考验人的能力，会造成互相扯皮推卸责任，上线以后有什么问题，还会互相推锅背，这种情况下管理者有啥好的方式去解决？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：虽然我觉得甩锅不是什么好事，但是如果你真要甩锅，最简单有效就是设置流程去划分责任。上线后有问题其实很正常的，重要的是要有合理的机制：

及时发现问题，监控报警、用户投诉反馈等；

马上解决问题，对线上版本有专门的代码分支，可以随时打补丁修复，测试上线；

避免后续再犯同样的错误。要分析原因，看什么导致问题，然后改进流程。

No.10
Linuxer：小公司有很多项目就是一两个人，没有那么多角色，怎么做到按这种流程去开发项目呢? 比如常常在代码编写过程中发现很多问题都没考虑全面又感觉在交流需求的时候根本就没想到，要怎么在之后的项目中不再犯这种问题呢？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：即使只有一个人，建议也要做简单的需求分析和设计，做完后，形成简单的文档，找人评审一下，提一些意见。因为你写文档的过程，给别人讲的过程，其实是在帮助你思考，帮助你梳理清楚逻辑，避免在实现的时候发现好多问题没想清楚。

还有一个思路就是快一点迭代，每一个迭代解决优先级最高的问题，然后下一个迭代中改进上一个迭代的问题。项目中犯错误其实很正常的，重要的时候要总结，看看通过什么方式能改进，避免犯类似的错误。

No.11
bearlu：是不是每种模型，有其应用场景，不能只追求最新，要适用才行？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：你说的太对了！举个例子来说，敏捷开发肯定又新又好，但是如果成员没一个懂敏捷开发，强行照葫芦画瓢，可能结果还不如用瀑布模型。

No.12
clever_P：瀑布模型难以快速响应需求变化，那有没有可能通过对业务领域的深入研究，对业务的发展和变化做出一些前瞻性预测，在软件设计的时候将这些预测考虑进去，以此来减小后期需求变化对整个项目的影响呢？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：你说的是一个方向，但是要预测其实是很难的，结果很可能是过度设计，设计了很多可能最终完全用不上的架构，反倒不如快速迭代快速响应来得实际。

No.13
一年：开发一款测试市场反应的产品，也应该使用快速原型模型是不是好点呢？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：恐怕不行，因为快速原型模型是牺牲质量的。质量差的软件，去测试市场，你不知道是因为质量问题不行还是需求没抓住不行。这种情况，可以考虑用迭代模型，先开发核心需求，然后再逐步迭代。

No.14
凯纳软件：感觉自己之前做任何事情都没有章法，觉得只要做了就可以。通篇学完之后，知道自己哪里欠缺，应该怎样去学习及工作。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：谋定而后动。还有一点经验就是：如果你想更有章法，更有大局观，做一件事情前先做个计划，可以帮助你更好的思考，也更容易执行。

No.15
Joey：我们公司是比较大的国企（多个业务部门对一个开发部门），对质量要求较高，现在业务条线也比较多，业务部门基本都嫌我们开发部门效率低，对于我们研发部门，组织架构还是按照瀑布模型设计的，开发模型基本是迭代 + 增量，如果想推行敏捷，肯定需要调整组织架构，一旦调整，就会触发一些利益关系，在这种背景下，有没有什么好的招数，既可以提高研发效率，又可以保证质量？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：如果你想推行敏捷，可以先找个小项目，组个小团队试点，成了可以作为一个参考，领导可以去邀功，以后可以更大规模尝试；失败了也损失不大，领导也不用担责任。

不管用不用敏捷开发，你都可以学习其中好的实践，例如持续集成用起来，帮助你高效的集成部署；自动化测试代码写起来，帮助你提高项目质量；迭代快起来，以前 3 个月变成 1 个月，以前 1 个月的变 2 周。有些事情即使只是程序员都是可控范围内的，做着做着其实你就“敏捷”起来了。

No.16
一步：最小可行性产品 MVP 应该就是迭代开发了？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：MVP 更多的是需求定义上的概念，和开发模型并没有关系。但是你使用迭代开发或者敏捷开发，必然要优先选择最核心最重要的功能需求先开发。所以通常 MVP 的方式选择核心需求，用迭代模型或敏捷开发开发需求。

No.17
龙哥：有依赖交叉的用户故事应该怎么做，比如用户系统的数据库该由谁搭建。毕竟注册、登录、修改这些都可能基于一个数据表。表字段这些需要统一，不能一个程序员改一次字段名吧

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：敏捷开发中有一个迭代 0，也就是第一个迭代，就是做这些准备工作、基础架构搭建的。敏捷团队小，有个好处就在于遇到你说的这种情况，在做之前，大家都在一起开个小会一商量就可以定下来了。

No.18
阿神：敏捷开发里开发也要写集成测试用例吗，那么测试人员主要做手工测试？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：对，开发不仅要写单元测试，还要写集成测试。但开发都是用模拟数据，假的 API。而测试的自动化测试会用真实的数据，调用真实的 API，而且也要做一部分手动测试。至于比例多少，还得看项目特点。

No.19
holylin：如果合同金额一开始就是根据商务阶段了解的情况评估的工作量而确定的，那么在合同执行过程中，如果按敏捷开发的思路，客户不断改需求我们不断地响应，然后工作量甚至已经超过了原先合同的金额，这个时候要如何处理？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：这是个好问题，我对这个问题上没有什么经验，但我可以试着帮你分析一下。

你的合同是按照当时的需求签订的，如果后期客户变更需求或者增加新需求，那相当于需要重新签订变更这部分的补充合同。

应用敏捷开发的时候，你也可以让产品经理或者项目经理充当客户的角色，这样他们会更偏重产品需求的解读，而不是重新提出新的需求。还有一点，合同执行的时候，这时候你不需要太过于纠结是不是用敏捷还是迭代还是瀑布，而是哪一种开发模式，可以让你高质量高效率的完成，那就是最好的最适合你的开发模式。

No.20
长眉 _ 张永：作为一个电商 ERP 服务商，既要关注产品的研发进度，又要对产品做维护。人员一旦离职，发现没有较为详细的文档，就需要去猜测，之前的业务了。敏捷后上线，留下的技术债务应该归谁负责呢？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：敏捷还是要写必要的文档，只是会简化。尤其是这种涉及交接的、维护的，文档不能省。技术债务应该团队成员集体负责，大家在迭代计划会上应该将技术重构列入后续的 Sprint。

No.21
刘晓林：敏捷开发这么强调扁平化，这么重视人，这么强调开放而弱化约束，那和最初没有软件工程时期的开发主要区别是啥呀？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：好问题，你难倒我了。前面介绍过，没有软件工程的时候呢，开发就是边写边改模式，没有需求分析、没有架构设计、没有测试，就导致很多问题。

No.22
邢爱明：对于企业管理的软件，核心需求涉及多个部门，需要反复沟通确认周期很长，这种情况下是否还适合使用用户故事的方式做需求分析呢？

另外，我按照瀑布开发模式的习惯分析，开发人员和 po 沟通需求后，如果没有文档作为输出物，在开发和测试的时候就没有标准，反而会造成工作返工。这是否意味着，团队成员需要高度的协同和配合? 以完成任务为导向，而不是强调各自的分工？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：好问题！敏捷开发这种方式，需要客户紧密配合，也就是可以方便确认需求，否则还是少不了要写需求文档。另外我在文章中描述用户故事，有些描写不清楚或者歧义的地方，其实用户故事还应该包括验收标准，这样可以解决你说的开发和测试没有标准的问题。

团队成员需要高度的协同和配合那是一定的，尤其是架构和需求两部分。需求简化后，就意味着开发过程中需要反复沟通确认；没有专门的设计阶段，也就意味着每个 Sprint 开始前，团队要商量有没有要设计或者修改架构的，有就需要有个简单可行的方案对架构进行修改。如果各自分工，这样的目标就很难达到。

No.23
D：在敏捷开发过程中如何保证业务的传承？当有新同事加进来，如何让他快速的熟悉整个业务。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：这个是个好问题，也是个大问题！新人的传承，通常我的经验是：

团队要有自己的知识库或 WIKI，常用的知识要花时间整理上去，这样新人来了可以自己查；

先给他简单的任务，再慢慢稍微复杂一点，给予必要的指导，做中学是最快速有效的；

遇到一些典型的问题可以通过结对编程的方式带着一起做。
仅供参考。

No.24
dancer：对比瀑布模型来说，敏捷开发在需求分析和软件设计上要薄弱一些，这会导致越向后迭代，软件越难以变更和维护，请问老师有什么好的方法和建议吗？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：需求分析是在 Sprint 进行中同步进行，也就是开发具体的用户故事之前要和客户或产品经理充分沟通了解需求。如果用户故事不是特别大，并不是很大的问题。另外并非只能用用户故事，也可以用传统的产品设计文档代替用户故事，也一样是很不错的实践。

对于架构设计，架构只设计当前迭代的，所以迭代到一定阶段，是要考虑重构的。通常重构代码也是 Sprint 的工作任务的一部分。

No.25
Dora：瀑布对人员要求不高 (各自负责各自的工作，比如需求只管需求)，而敏捷流程，一个人什么都要过一遍。这样理解，对吗？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：瀑布对人员也不说要求不高，但分工确实更细一点，比如像你说的，需求只管需求；开发一般就不操心怎么测试，写完等着测试报 bug；敏捷开发里面，分工没那么细，需求不仅要写需求文档或者用户故事，还要和团队成员紧密合作，及时讲解需求；开发也要自己写很多自动化测试代码；敏捷团队也不是没有测试，但是会用自动化测试分担一部分测试任务。

No.26
Tiger：在敏捷里面，开发写自动化脚本测试，那是不是就不需要测试这个角色了啊？感觉在敏捷里面，只需要开发这一个角色就可以了啊？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：在《07|大厂都在用哪些敏捷方法？（下）》我有谈到这个问题。自动化测试是辅助的，还是离不开人工的测试。而且开发写的集成测试和测试写的自动化测试还是有一点差别，一个是用程序模拟的操作的模拟的固定数据，而测试则用的是真实的数据真实的环境。举个例子来说，网页的自动化测试，开发只会用 Chrome Headless，数据都是事先写好的模拟数据；测试的话会用主流的 Chrome、Safari、Firefox、Edge 分别测试（自动化或手动），数据都是测试环境的真实数据。

No.27
一路向北：对于小公司小团队的项目，因为项目经理，产品经理都是身兼数职，是否有更好的实施方式呢？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：项目经理、产品经理兼多个项目是正常的，也没大问题。但是让程序员同时兼做开发和项目经理工作就很不好，因为项目经理需要更多全局掌控，而一旦要花精力在开发上，很难跳出具体的开发工作，会极大影响项目管理工作；项目管理工作也会频繁打断开发，造成进度延迟。

所以我建议应该有专职的项目经理，不应该让程序员兼职项目管理。新旧项目交织并不是问题，可以放在一个项目一个 Sprint 里面一起管理，也就是同一个 Sprint 里面有维护的 Ticket，也有新需求的 Ticket，只要保证开发人员同一时间只是做一件事，而不要几件事并行，就可以最大化发挥敏捷优势。

No.28
天之大舒：怎样培养团队成员？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：有一些建议参考仅供参考：

招人和开人都很重要，招优秀的，开掉没有责任心，没能力的。这两点都不容易做到，不过得坚持做；

设置合理的流程，配合一定的奖惩制度；你奖励什么，团队就会往哪方面发展；

团队要有梯队，不能都是资历浅的也不能都是资深的，保持一个合适的比例是比较健康的；

实战中锻炼，实战中磨合；给他们有挑战的任务，给予合适的指导。（这就是有梯队的原因，需要高一级别的待低一级别的。）

No.29
星星童鞋：请问老师，对于需求更新极快，基本上每周都需要迭代更新上线的项目，在架构设计和项目部署上会不会有什么特殊的要求？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：架构设计上，一定要定期需要重构，优化设计，不然后续新需求效率会降低，包括代码上也会越来越臃肿。比如我现在所在项目组，每 1-2 年会有一次大的架构升级调整，日常每隔几周会有小的架构优化，这样基本上可以保证快速迭代不会受太大影响。

部署的话，一个是要自动化，可以快速方便的部署，另外一个部署后，需要有配套的数据监控和高于阈值报警的机制，因为上线后可能会有严重问题，需要及时发现，及时处理。

No.30
alva_xu：如果一个迭代里没有评审会，怎么知道我上线的系统是符合要求的？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：没有评审会，但是有专职测试针对最初提的需求进行测试，另外产品经理也会验收，如果验收不合格会提交 Ticket。也就是说是有验收，只是没有专门的会议。

精选笔记

阿杜：
软件过程不是搞科研，不是搞艺术，而是解决多人合作将一个想法落地的学科，其中包括严谨的过程步骤、规范，用于提高效率或防范风险的工具。软件工程的主体是工程，这就要求我们具备基本的工程思维：模块化思维、抽象思维；具备一些关键的意识：质量意识、风险意识、交付意识。

相关阅读：01 | 到底应该怎样理解软件工程？

alva_xu：
对于大型系统的建设，可否用敏捷方法来实现，一直是个问题。
敏捷方法，适合于小团队（比如两个披萨团队）、小架构。对于大型单体应用的开发，至少在架构设计上是不适合用敏捷迭代方式的。

为了解决大型系统建设的迭代开发、快速交付问题，业内不断在探索。随着微服务架构的提出，以及容器技术的成熟，和 cicd 的实现，单体巨石应用被拆解成分布式的微服务应用，此时，敏捷方法也就开始真正大行其到了。

所以，微服务、容器、devops 这三剑客和敏捷方法一起，互为依存、互相促进，成为了软件工程中最有生命力的技术工具和流程，使软件开发在质量和效率上得到极大提升。

相关阅读：01 | 到底应该怎样理解软件工程？

老张：
在今天没有不可替代的硬件，却有无数不可替代的软件。硬件早已不是共享的壁垒，而曾经被认为有很强可塑性的却已经是最硬的壁垒。一台服务器、一块磁盘、一根内存以及交换机、防火墙等网络设备，更遑论鼠标、键盘、显示器，在冗余、复用、虚拟化等等技术之下，更换、替代、扩容如此之方便，经过简单培训的工人就可以轻松完成。

可是即便是美国国会图书馆，依然认为纸质是保存资料最好的方式，因为大量资料电子化后存放在不同介质，需要当时定制的软件才能读取这些格式。今天的软件就是这么硬。也许有一天，有人会写写如何开发真正的软件。

相关阅读：01 | 到底应该怎样理解软件工程？

阿银：
软件工程的本质在于工程。利用工程理论来保证高质量软件产品的产出。工程讲究效率，成本，质量，除此之外，容易忽略的是工作量与效益的权衡，这一点尤为关键。

相关阅读：01 | 到底应该怎样理解软件工程？

hyeebeen：
软件工程的产生源自于对高效产出可靠稳定的软件产品的需求，在各种“+”的现实生活中，不掌握合格的软件工程管理技巧，日常的项目工作会很容易有瓶颈。

学习对应的工程技巧，内化为自身素质，在项目过程中既能预防工程风险，也能建设面对风险的反应机制。这种人，各个企业都喜欢。

相关阅读：01 | 到底应该怎样理解软件工程？

阿杜：
1. 做任何事情都要按照一定的理论指导来，例如，依靠系统化、结构化的“工程思维”，将生活和工作中的每个事情都看做一个项目，可以提高做事的成功率和效率，虽然不用这些理论指导也能做成事情，但是相对来说是偶然性的，不是常规性的。这就是常说的认知（意识）先行，持有高级的认知去跟低认知的人竞争，是一种降维打击。

2. 工程思维的核心有两点：系统化，也就是全局观，要从站在整个项目的高度去看问题，不能做井底之蛙；结构化，也就是有步骤、有节奏得做事情的意识。

3.《软件工程之美》这个专栏，我给自己定了一个小目标：全部跟完，并且坚持留言跟老师交流想法。我制定了简单的阅读步骤：（1）文章至少阅读两次，第一次通读，第二次做笔记摘抄、整理文章的思维导图、提出自己的想法；（2）整理自己的学习心得，形成阅读笔记发到自己的博客（公众号）上。

4. 课后思考：我今年年初开始运营自己的公众号，我把它当做一个项目，就从下面几个方面进行了思考：我要提供的内容和定位是什么样的、我的用户是谁、我应该如何去运营；这些东西想好后，我就将要做的事情拆分为：公众号设置、文章内容输出、运营推广三块，然后按照一定的步骤去执行，现在公众号的设置已经基本完成，整个项目进入内容输出和运营推广的循环中了。站在项目的角度去看这个问题，可以让我在动手执行的时候更有方向感和节奏感，也会对自己清楚自己某个小的点做的改动会对全局产生什么影响；在没有使用这个角度去看问题之前，我只是简单得主张内容才是核心，但是不懂运营和推广，没什么章法。

相关阅读：02 | 工程思维：把每件事都当作一个项目来推进

起而行：
项目思维的两个关键在于：

主意局部任务与总体时间的关系；

用熟悉的办法解决问题。

以留学为例。

1. 在距离留学申请还有两年的时间，可以先做不确定性强，见效慢的事情，不是不重要，而且短时间内会来不及。比如未来职业规划，兴趣的培养，长期的科研与项目。

等到了申请还有半年的时候 要注意局部的任务与总体时间的关系。那么确定性不强的任务效果将不会特别好，而刷语言考试的成绩，这种确定性强，时间可控，反馈见效相对快的事情就要提上日程。

2. 用熟悉的办法解决问题。在留学，这种高度信息不对称的领域，可以自己试着了解前人经验，但我认为，专业的事情给专业的人去做，那么找中介辅助申请就是个好的主意。

相关阅读：02 | 工程思维：把每件事都当作一个项目来推进

alva_xu：
工程方法就是有目的、有计划、有步骤地解决问题的方法，而工程思维就是用工程方法解决问题的思维模式。这种思维模式，首先要求有全局观。

而事实上，由于工程中的不同职责分工，导致各个角色有可能只从自己的分工角度去考虑问题，这实际上是软件工程中最大的障碍，也是传统的 CMMI（过程域的规范化）、现在的 DevOps 和敏捷方法想要去解决的问题。

正如《凤凰项目》中的观点，既要有自左向右的工作流，又要有自右向左的反馈流。通过人员和组织（自组织）的调整、工具和技术（CICD 工具）的使用以及流程（Scrum 等敏捷工作流程）的推广，确保整个工程项目不会被不同角色割裂开来，从而确保工程的实现。

相关阅读：02 | 工程思维：把每件事都当作一个项目来推进

纯洁的憎恶：
工程方法不仅给团队提供了一系列成熟的理论范式与实践工具，提高效率与成功率。更重要的是把团队的视角“强行”抬到全局高度，避免我们紧盯着自己关心局部问题，更好的统一思想、形成合力。

相关阅读：02 | 工程思维：把每件事都当作一个项目来推进

hyeebeen：
非常赞同“一切即项目”的思考模式，作者经历的培训活动我也经历过，确实在时间意识上会比没受过工程训练的人强一些，更注意过程控制。

ps：学习自动化测试其实不等于一定能缩短测试周期，“测试周期”的定义如果是测试独占的项目时间段的话，可以通过测试前移，加强自测，契约优先的接口自动化测试等来缩短独占时间。没有系统或者不够工程化的自动化测试脚本，反而会增加测试时间。

相关阅读：02 | 工程思维：把每件事都当作一个项目来推进

纯洁的憎恶：
我对瀑布模型感触颇多啊！

瀑布模型把复杂的软件生产过程，按照时间线索，切分为若干较为独立和专业的部分，条理清晰。

在每个阶段内只需要集中精力与阶段任务即可，不用胡子眉毛一把抓。

每个节点有交付件，过程可控、权责清楚明白。

瀑布模型特别符合我所在的大型央企的性格。但是我经手好几个项目，也被瀑布模型折腾的死去活来。比如我现在正在处理的项目。

首先，从可行性分析、立项、批预算、采购建设单位就花了一年多的时间。可行性分析做了 1 个月，立项流程走了不到 1 个月，批预算的时候，主管业务的领导变卦了，要求重新做可行性分析，于是我们又花了 1 个月。

二次立项的时候，主管信息的大领导突然决定要把区块链和人工智能等热门技术加进去，于是又要求重新立项。但是我们要做的事情实在和区块链八竿子打不着，死去活来的找了个理由扯上关系了，来来回回又花了 2 个月，这就半年了。

再次走到批预算的环节，主管领导发现原来的预算干不了这么多事情，又不同意增补预算，于是继续扯皮。经过多番协调，总算解决了，这时候叶子已经黄了。

我们立刻开展需求调研，但各个需求部门和最终用户都借口工作太忙不搭理我们，我们只好自己憋需求，简直是闭门造车。等需求憋出来了，大领导把需求部门都叫来议一议，结果被集体口诛笔伐。

大领导怒了，强令需求部门专门抽时间参与需求调研，各部门也是不情不愿啊，效果可想而知。需求总算审查通过了，就在我们准备采购实施单位的时候，国资委红头文件一直下来，公司的采购流程发生重大调整，项目被硬生生搁置下来。眼看年根了。

第一年就这么过去了。等来年采购流程也屡的差不多了，预算又出问题了。去年批的预算只能去年用，不允许跨年。只好等到年中调整预算，又小半年过去了。采购流程走完，实施单位也很够意思，不等合同签订就投入工作。我们用极短的时间完成了软件设计，并且开始如火如荼的开发工作，此时又到了金秋。

就在这时，新的纪检书记上任了，他对我们的系统设计很不满意，要求相关部门限期整改，于是需求大调整，可是这会儿编码已经进行了 1/3 啦…之后就是上线日期一推再推，从 10 月初推到 10 月底，再推到 11 月、12 月，眼看又要跨年了。我们和实施单位连续半年 997，总算看到上线的曙光，这时候公司一把手退休了…

新领导上任后对整个流程极不满意，否定了纪检书记的指示，于是我们又开始第 2 伦大调整。现在已经是项目的第三年了，我们依旧没能上线。整个团队都要累趴下了，全公司一点成果也没看见。

相关阅读：03 | 瀑布模型：像工厂流水线一样把软件开发分层化

纯洁的憎恶：
稳定、可靠、一步到位的瀑布模型，不太适用于违约风险大、需求不明确、快速见效的场景。

快速原型模型：不见兔子不撒鹰。期初不考虑质量、架构，用最快的速度见效，并向用户确认需求。经过几轮直观、快速的反馈，把需求确定下来。接下来，既可以抛弃原型用瀑布精密重构，也可以在模型基础上完善。优点是快速有效地确认需求。不足难以有效应对后续的需求变更。

增量模型：分而治之。将大系统横向拆分成相对独立的若干小模块，每个模块采用瀑布模式分批次交付。优点是较快见到成果，且能够及时了解项目进展。不足是存在需求明确、系统可拆分、交付可分批等适用条件。

迭代模型：罗马不是一天建成。把软件项目纵向划分成若干阶段，从核心功能入手，逐渐深化、细化，直到满足用户的全部需求。每个阶段都是一个瀑布，都要在前一阶段成果基础上加工、打磨。优点是快速满足基本需要，并体会软件演进的快感。不足是需求演化具有不确定性，会导致代码冗余、系统重构风险、项目周期不可控。

我做甲方管过不少外包项目，大 V 模型再熟悉不过了。整个过程冗长繁琐，走流程比建软件更累心。而且等项目结束的时候，需求早就变得面目全非了。乙方只能硬着头皮做，不然连业绩都没有，真是血本无归。在增量或迭代模型的每次交付后都做一次风险评估，演进为螺旋模型，可以及时止损。

项目做成这样，更深远的原因是业务都是在摸着石头过河，需求不变更才怪呢。但每年几个亿的信息化预算还是非常诱人的，投标单位络绎不绝。RUB 看起来不错，但需求快速演化会依然带来无法回避的系统重构压力，终归还要具体问题具体分析。

相关阅读：04 | 瀑布模型之外，还有哪些开发模型？

西西弗与卡夫卡：
当前不够明确、后期可能有较大变化的需求，准确说首先要考虑的不是用哪种开发方法，而是最好避免一开始就投入开发资源。开发的代价非常高，推倒重新开发的代价更高。最好是先想别的办法，验证需求是否真实存在之后再动手写代码。

相关阅读：04 | 瀑布模型之外，还有哪些开发模型？

alva_xu：
对于增量或迭代开发，大型企业需要考虑这些不适应点：

大型官僚机构的办事程序和敏捷过程不匹配。比如开发想敏捷，但财务采购等都不敏捷。代码敏捷了，基础环境不敏捷等。

伴随增量的添加，系统结构会逐渐退化。特别是对于大型系统，其系统结构的建设，就需要提前制定计划，而不是增量开发。

与开发方的合同问题，需要新形式的合同。旧形式的合同是固定合同，做多少事拿多少钱都在合同时谈好了，不适应工作量的变更。

相关阅读：04 | 瀑布模型之外，还有哪些开发模型？

纯洁的憎恶：
流程、工具、文档、合同、计划都是工业化的标志。它们带来了稳定的质量、惊人的效率、超大规模的协作，对于软件工业也是如此。

然而软件工业具备轻资产、知识密集型、从业人员素质高等特点，充分发挥人的创造力和价值，是其相较传统工业更高阶的要求。加之软件工程面对的不确定性与复杂度更显著。于是“个体和互动高于流程和工具，工作的软件高于详尽的文档，客户合作高于合同谈判，响应变化高于遵循计划”的敏捷思想应运而生。

通过用户故事，理解用户需求；在迭代中采用渐进的架构设计；定期重构解决技术债务；功能开发的同时编写自动测试代码；自动化持续构建。

由于淡化了部分工业思维中兼顾稳定、质量、效率、成本的传统手段，敏捷思想的最终落地，需要素质极高的从业人员参与其中，且数量不宜过多，以此来弥补流程上的缺失。同时要团队与客户紧密协作，上级的充分信任，才能够有效发挥其灵活应变，又万变不离其宗的优势。

这是大胆的返璞归真，好似回到了瀑布模型前的蛮荒时代，实则是更高级的打法，就像独孤九剑一般。所以，敏捷开发“道”的属性更浓。

敏捷开发具有快速迭代、持续集成、拥抱变化等诱人的特点，但也有苛刻的条件要求。不过，即使无法推行完整的敏捷开发，依旧可以在传统模式下，有针对性的应用敏捷开发的实践方法。

相关阅读：05 | 敏捷开发打底是想解决什么问题？

alva_xu：
我们现在着手的一个项目，是一个软件框架建设项目，外包给供应商做的。在签合同时，基本需求已经梳理得差不多了。所以按理是可以采用瀑布式开发来进行的。但由于以下原因，所以我们结合了增量开发和 Scrum 项目管理的模式进行系统建设。

基本需求是可以分模块来实现的。

我们这个项目所依赖的其他部门提供的基础平台也不是一次性可以交付我们使用的。

我们的使用方 (另外一个应用项目）对我们项目的时间要求很急，但可以接受我们分批次交付的模块。

基于以上原因，我们设立了几个大的增量阶段，每个增量阶段我们有分几个 sprint 来进行开发管理。到目前为止，进展还比较顺利。

但由于我们这个框架建设项目的外部干系人比较多，所以在协调上游平台和下游应用系统的时候，确实遇到了许多沟通方面的问题。由于其他项目没有进行看板管理，所以需要进行例会形式的沟通来确保关键节点的功能实现。

所以，我认为，开发模式和项目管理模式不可以拘泥于一种形式，关键还是要看是否真正达到了整体的敏捷和精益。对于文中老师提及的 scrum 管理和极限开发，确实是小团队内部协同作战的比较好的实践。但对于多团队协同作战，就要考虑综合运用各种方法了。

另外，对于文中提及的站会形式，从“道”的角度来说，当然是可以视实际需求来确定是否要开，但往往一种文化的培养，需要有仪式感，需要不断锻炼。所以对于我们来说，我们还是坚持开 Scrum 中要求的四个重要会议。

相关阅读：05 | 敏捷开发打底是想解决什么问题？

纯洁的憎恶：
分治策略是应对庞大复杂系统的惯用思路，但它的难点或精髓在于如何确保形散神聚。

详细计划（甘特图）VS 任务状态（Ticket）。

代码不稳定 & 环境部署麻烦 VS 代码审查 & 自动测试 & 自动部署（GIT、CI、DevOps）。

上传下达 VS 频繁沟通、提醒、分享。

大厂的敏捷开发实践，把枯燥的编码变得跟玩游戏一样。借助有效的流程与工具，能够有效节约团队成员的精力，聚焦于任务或角色，不会因频繁“统一思想”导致“技术动作变形”。

而另一面，在大厂里每个人通常都是螺丝钉，长此以往也许会养成不谋全局的习惯。如果能从自己的角色中跳出来，俯瞰整个组织协作的全过程，并站在这个视角上思考问题，一定会有更喜人的收获。

相关阅读：06 | 大厂都在用哪些敏捷方法？（上）

alva_xu：
在一个以 Scrum 为方法的敏捷团队里，首先，Scrum master 是呵护 develop team 的保护神，他的其中一个职责是保护每一次迭代的工作量是 dev team 能按时完成的，而且保护 dev team 能专注于现有 sprint back log 的实现，不会被其他干系人的新需求所打断。

其次，Dev team 是一个 T 型团队，技术比较全面，许多事情多能自助搞定，比如，开发人员同时又有测试技能，同时如果结合结对开发，测试驱动开发，那么，交付物的质量就更有保障。

再者，在一个敏捷团队里，人数比较少，dev team 的沟通能力都比较强，沟通可以比较充分，所以解决问题的能力就比较强，工作效率比较高

最后，敏捷模式的开展，也依赖于工具的使用，目前常用的 CICD 工具，与 jira/confluence 需求沟通管理工具的打通，部署次数的提高，无疑大大提高了开发发布效率，同时也提高了发布质量。

综上所述，只要在人员组织架构、工具产品、流程这三个方面都达到了敏捷的要求，那么发布质量就有了保证。

相关阅读：06 | 大厂都在用哪些敏捷方法？（上）

Felix：
Git 方面也要求团队 Master 中的代码必须通过 Merge Request(Pull Request) 来，也作为 Code Review 的最后一道关卡。持续集成方面大部分通过 Jenkins、几个微服务是通过 Gitlab CI，我们的终极目标是基于镜像部署发布，屏蔽环境影响。

相关阅读：06 | 大厂都在用哪些敏捷方法？（上）

alva_xu：
我觉得在计划会上，有几个事情必须要做好.

第一是需要定义 DOR 和 DOD，Define of Ready 和 Define of Done, 如果没有这两个定义，那么扑克牌可能会玩不起来。

第二 需求（用户故事分解成的 task) 一定要尽量明确。不管扑克估算还是其他估算方式，如果第一轮估算偏差过大，说明大家对需求不明确，需要产品经理进行更详细的说明。通过几轮估算，如果大家能达成比较一致的估算，那么工作量的估算就比较靠谱了，这也是 Scrum 这种工作方法带来的好处，能让需求得到合理的资源安排。

不管怎么说，在 Scrum 里，要重视估算，有了好的估算，速率才真正有意义，才能真正保证交付质量。

相关阅读：07 | 大厂都在用哪些敏捷方法？（下）

纯洁的憎恶：
“多、快、好、省”，软件工程的四难选择问题。由于质量是软件工程压倒一切的要素，因此“好”必须留在“盘子”里。剩下的要素都是可以根据具体情况权衡取舍的。四难选择变成了三难选择。于是，工程师在实践中面对不确定时，也能够有底气做到“不抵触，讲条件”了。

延长时间的另一面是提高效率。借助工具、优化流程、节约资源等方法，可以在一定程度上“冲销掉”延长的时间。

非常欣赏 MVP 模型，既可以快速见效，又降低了大量返工的可能。在瀑布模型中，通过会有过度设计的现象。一开始想了很多，结果发现恨不能 80% 都是瞎想。先拿出核心功能，再根据用户使用的情况，有指向性的完善，步步迭代演进，十分靠谱。

唯一令人担忧的是，在外包模式中，如果没有明确的需求，就难以估算出较为准确、合理的预算，进而无法立项、采购。如果先做一版需求申请下来预算再说，再用 MVP 模型步步试探。那么最后做出来的东西可能与需求文档严重不一致，存在较大的审计、内控风险。也许企业大了、规矩多了，做起事来确实别扭。

作者回复：迭代模型和 MVP 是非常好的组合，因为迭代的时候，会优先选取最重要的功能，慢慢的那些不重要的功能甚至永远不会被加入迭代中，就因为不需要浪费时间在上面了

相关阅读：08 | 怎样平衡软件质量与时间成本范围的关系？

alva_xu：
传统的大企业（不是指 BAT 这类大企业），比如我们企业，IT 项目牵涉到三个部门，一个是业务需求部门，一个是 IT 部门，一个是财务预算审批部门，采取的形式一般都是采用外包方式，而且往往是固定合同，也就是合同价格是确定的，需求范围也是确定的，这样的话，金三角的两条边就定下来了，剩下来的就是时间和质量的关系问题了。

按照金三角的理论，我们就可以知道前面所述的场景下项目组该重点抓什么了：作为甲方项目经理，重点抓的就是质量和时间了。如何通过提高效率，使单位时间的产出比原来的多（相当于增加了时间），来提高项目的交付质量，是我们甲方 IT 项目经理最关心的事。

所以这时候，我们的方法是建立统一软件框架、提供公共服务组件、制定代码和测试规范、培训乙方团队、搭建 CICD 平台和自动化测试平台、sonarqube 自动代码检测平台等，使原来几周一次测试变成一周几次测试，使原来低质量的代码快速变成高质量的代码…

反正是采用各种方法，提高工作效率，用于抵消业务部门不时提出的变更导致的项目进度的风险。当然在开发模式上，也会衡量敏捷的开发模式（特别是 scrum 的管理模式）和传统瀑布及衍生模式哪种模式更高效。

当然，理解了金三角，对于前期申请项目预算也是有帮助的，比如，可以和预算部门谈判，如果要砍预算，在时间一定的情况下，就只能减少项目范围，这是我们业务需求部门所不能接受的。这样，就可以使 IT 项目经理名正言顺地把预算部门和 IT 部门的矛盾转嫁到预算部门和业务需求部门去。

当然，最合理的做法应该是向 BAT 公司看齐，IT 部门转变为利润中心，自己管预算、自己有开发团队，那么金三角的三条边就都可以进行调优了。

相关阅读：08 | 怎样平衡软件质量与时间成本范围的关系？

最佳思辨

林云：
文中提出可以借鉴软件开发模型中的特点，这一点并不是普通软件开发成员可以使用的。任何一个软件开发模式都有对应的主要问题。就像你把飞机的引擎放在拖拉机上一样。需要对模型进行总体考虑。而且不同的软件开发模式都有对交付团队有能力的要求。

举个不恰当的例子，组合软件开发模式的特点就像让一个摩托车驾驶员开着安装了飞机引擎的拖拉机。这并不是软件工程想达到的结果。希望作者对组合研发模式的前提和应用过程进行描述，以减少软件工程方法使用的随意性。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：
谢谢指正，结合最近波音 747Max 的案例，确实不能乱用，不能说飞机的软件也用敏捷这种快速上线快速迭代的模式。我觉得组合研发模式的前提还是质量，软件工程的目标就是要构建和维护高质量的软件，无论怎么组合开发模式，都不能牺牲质量。

这里我也列一些我觉得好的实践：

瀑布模型可以参考敏捷开发，引入持续集成、自动化测试，提升效率；

敏捷开发可以参考瀑布模型，开发前多设计，开发后多测试，尤其是要辅助人工测试。

相关阅读：04 | 瀑布模型之外，还有哪些开发模型？

好，今天的加餐就到这里，非常感谢同学们用心的留言，也希望我们专栏的同学都能每日精进，学有所成！

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

09 | 可行性研究： 一个从一开始就注定失败的跨平台项目

你好，我是宝玉，我今天分享的主题是：可行性研究， 一个从一开始就注定失败的跨平台项目。

如果你随手拿起本软件工程教材翻翻，第一章一般都是讲“可行性研究”的，呈现顺序仅次于“绪论”，可见其重要性。

“可行性研究”通常讲的是如何科学地论证项目的可行性，以及这个项目是不是值得做。这个知识点比较简单，落实到期末考试的题目上，一般只是一道像这样的选择题或填空题：

“可行性研究主要从哪几个方面进行？”

这个题目要回答的话也不难，记住答案即可。

对于软件项目的可行性研究，主要从以下几个方面入手：

经济可行性；

技术可行性；

社会可行性。

看上去这么简单的知识点，到底重要在哪里呢？我们先来看一个真实的案例。

2015 年的时候，Facebook 推出了一个跨平台的移动端解决方案 React Native，只要用 JavaScript 一门语言就可以将写好的代码运行于 iOS、Android 移动平台。

所以在 2016 年的时候，某著名大型互联网公司的移动部门负责人非常看好这个技术，专门成立了项目组，用了不少人力，花了大半年时间将移动端 iOS、Android 产品迁移到 React Native 技术框架上。

就在项目快要上线的时候，法务部门却发现 React Native 的开源许可协议是“BSD+ 专利”。那这个“BSD+ 专利”的许可协议是什么呢？

BSD 的许可协议本身是开放的、没有限制的，但 Facebook 在此基础增加了一个“专利”协议。也就是说，如果对 Facebook 及其子公司提出专利诉讼，不管诉讼的项目是否与该协议有关，用户的所有专利权利也都会自动终止。

也就是说，如果未来该公司因为专利问题与 Facebook 产生纠纷，那么该公司将会无条件输了官司。

而目前该公司和 Facebook 是有竞争关系的，所以法务部门为了避免未来可能的纠纷，不得不叫停这个项目。而此时，他们在这个项目上投入的大量人力财力，相当于全打水漂了。

即使后来 2018 年的时候，Facebook 把 React Native 的开源协议修改为友好的 MIT 协议，也为时晚矣。

你看，如果在立项之前，就让法务部门帮助评估一下社会可行性（具体到这里，也就是法律方面的可行性），该公司就可以避免很大一笔损失。

类似的案例其实不在少数。许多公司老板或者部门负责人不是很懂技术，天马行空想到一个点子，或者看到某个热门技术（比如说团购、共享经济、人工智能、区块链），不做可行性研究就直接立项去做，耗费了不少人力、物力和时间成本不说，最后项目也不得不以失败告终。

为什么软件项目很少做可行性研究？

可行性研究不是软件项目的专利，在很多其他工程领域，项目正式启动前，都会有可行性研究这一环节，而且一般都会请一家甚至多家专业的评估机构帮助做可行性分析，并出具可行性研究报告，然后项目方来决定是不是立项。

拿建筑工程来说，你要在某条街上盖房子，却不做可行性研究，那么如果这条街两年后要拆迁，那就意味着你的房子也会面临被拆掉的命运，那损失就大了。

为什么在软件工程领域，可行性研究就不是很灵了？如果你也经历过或者听说过一些失败的软件项目，不知道你有没有想过，为什么这些软件项目很少有做可行性研究的？如果你有机会就这个问题去做一下调查，很可能会得到下面这些答案。

1. “因为我们是软件项目，所以我们很特殊。”

“我们很特殊”，这句话听着有没有很熟悉？软件项目确实有和其他工程项目不一样的地方。

比如说软件项目很抽象，以至于在立项之前对于问题的描述（需求）和解决方案（技术方案）通常都是模糊不清的，只有随着项目的推进，才能逐步搞清楚需求。

而可行性研究是基于问题和解决方案来分析的，因此这有点像“先有鸡还是先有蛋”的问题：你得先立项才能慢慢搞明白需求是什么，然后才能有解决方案；而你只有搞明白需求是什么，以及解决方案是什么，才能去做可行性研究。

但“我们很特殊”，不能成为不做可行性分析的借口，可能项目需求最开始是模糊不清的，还不具备可行性研究的条件，那么等到项目有了一定的进展，需求逐步明确后，要继续对可行性做研究。

如果发现方案不具备可行性，也应及时调整方案或停止项目以止损。

2.“老板拍板的项目，明知道不可行也得硬着头皮干呀！”

这个问题要分类讨论，有两种情况。

第一种情况，多半是由于老板或者项目负责人控制决策权，且对于不同意见容忍度较低。底下人不敢提不同意见，明知道不对也只能执行。

如果你是项目执行人员，不能参与决策，但觉得项目明显不可行，我仍然建议你尽可能站在专业的角度给出科学的分析，通过合理的方式反馈意见。毕竟，项目如果失败了，你也一样可能遭受损失。

如果你就是老板或者项目负责人，则应该建立可行性研究的意识，并理性听取不同意见，科学客观地进行可行性分析，以便有效降低项目失败概率。

第二种情况，老板或者项目负责人能接触到的信息更多、更全面，同时还有战略上的一些考虑，所以下面执行的人觉得不靠谱，并不代表真的不靠谱。

举个例子，2009 年阿里巴巴决定做阿里云的时候，公司反对者占绝大多数，只有马云和王坚等少数人觉得这个项目可行，而且必须做。最后，事实证明他们是对的。

所以有时候，也不要着急下结论，可以换个角度思考下，也许是你因为条件限制还没想清楚。

3.“软件项目是鼓励创新、鼓励试错的，可行性研究会阻碍创新！”

这也是一种很典型的错误观点，认为创新就可以不做可行性研究，否则会阻碍创新。实际上可行性研究和创新从来就不是矛盾的，它反而可以帮助你提前过滤掉那些不靠谱的创新想法，提前发现可能的风险。

想一想文章开头关于 React Native 开源协议冲突的案例，虽然是一个创新性的项目，却未绕过开源协议引起的法律纠纷。如果当初有法律方面的可行性研究，完全可以改用开源协议更友好的同类开源技术，避免项目的失败。

如何做好可行性研究？

前面，我们讲了可行性研究在软件工程中的重要性，也帮你厘清了几个常见的困惑，接下来我们来看看“如何做”的问题。

其实，当你决定要做可行性研究，你就已经成功一半了，怎么做反而是相对简单的部分！

软件工程的教材里面，通常会讲如何写可行性研究报告，很烦琐，要撰写诸如引言、背景、定义等内容。在这里，我们关注的重点是，软件工程中是如何去做可行性研究的。如文章开头所说的，通常从三个方面着手做：

经济可行性。从成本和收益角度分析，看投入产出比。不仅要分析短期利益，还要分析长期利益，看是不是值得做。

技术可行性。软件项目最终是需要人通过技术来实现的，所以要分析技术上是不是可行，如果有技术上解决不了的问题又能否规避。

社会可行性。社会可行性涉及法律、道德、社会影响等社会因素。比如，触犯国家法律的事情肯定不能做；产品如若不符合道德标准，可能带来较大的社会负面影响，那么也要慎重考虑。

仍然以文章开头提到的 React Native 项目为例，我们从这三个方面出发，来做一个简单的可行性研究。

先来看看经济可行性。按照投入成本和收益估算，我们在此仅做一些简单假设：

这个项目要投入 10 个人，每个人的人力成本预计是 10000 元 / 月，预计要花半年时间上线；

每个人在项目实施过程中，所需要的硬件和软件成本预计在 1000 元 / 月；

每年该部门预计完成 10 个项目，每个项目收益预计 1000000 元；

该项目导致 2 个项目延期半年；

该项目预计可以节约项目成本，所以每个项目收益可以提高 50000 元；

该项目可以让每年部门完成的项目提升到 12 个。

预计投入 1660000 元 / 半年，投入使用后每年可以产生约 2600000 元的收益，不到一年可以收回成本。

以前我写程序的时候并没有多少成本意识，觉得就改改代码而已，后来发现真要把工资和用的时间算一下，其实成本还是不低的。

就像上面这样一个 10 个人的项目，半年下来就要花一百多万。当然如果项目成功的话，不到一年就可以收回成本，而且后面还会持续创造价值。所以从经济可行性分析，还是可行的。

然后再来看看技术可行性。

从技术本身来说，经过一年多的发展，技术已经成熟稳定，并且已经有了几个成功案例。

从人员储备来说，部门已经有 5 名成员有 React Native 项目经验，其他人员可以通过 3 个月左右的培训上手。

从风险角度看，部分老的安卓机型无法支持，但是这部分机型占有率非常低，可以不予考虑。另外，部分视频组件需要自己实现，技术上可行，需要把这部分的开发任务放入项目计划中。

技术可行不可行，关键还是在人。就算技术成熟，如果短时间内找不到人来做，也是有很大风险的。同时也要评估可能存在的技术风险，像本例中的设备兼容问题，如果不兼容设备很多，那技术就不可行了。

像这个项目，已经有一定的人才储备，不会成为技术上的瓶颈，另外不支持的设备只占极少数，可以忽略不计，所以总体上技术还是可行的。

最后再来看看社会可行性。

道德可行性是没有问题的，不会有任何不良道德行为。

社会影响方面，也没有负面影响。

法律可行性上，项目本身不违反国家法律法规。版权上，React Native 采用 BSD+ 专利开源协议，存在法律风险！

至此，我们可以得出结论，这个项目从经济可行性和技术可行性上来说，都没问题，但是社会可行性方面存在很大风险。于是，接下来我们和公司法务部门进一步沟通，确认并达成一致，最好的结果是暂时冻结该项目。

就这样，我们通过项目启动前的可行性研究，及时冻结项目，为公司避免了人力、物力和时间上的浪费。而且，这样一来，我们还有了及时寻找其他解决方案的时间和机会。

总结

可行性研究是项目启动前很关键的一步，可能最早帮你发现风险，甚至避免损失，千万要重视起来。就如我前面所说的：

哪怕你做的可行性研究不能改变决策，最后项目结束的时候，和当初做的可行性研究做一下对比，也都是非常宝贵的项目经验积累。

结合《工程思维：把每件事都当作一个项目来推进》一文的内容，我建议你把每件事都当作一个项目来看，因此每次决定做一件事前，不妨先做一个“可行性研究”。

比如说，你打算要做一个功能模块的性能优化，不妨先列一下成本和收益（经济可行性），看看你投入的时间精力，再看看最终带来的性能提升效果，来判断下是不是值得做。

比如说，你要换工作，那就列一下你工资提升带来的收益（经济可行性）。最好换算成时薪，看看长期是不是真的更合算，因为有时候虽然工资多了一点，但加班太多反而得不偿失。

最重要的，你要关注一下法律上的风险（社会可行性），想一想你有没有签竞业协议，新工作会不会因违反竞业协议给你带来巨额赔偿问题。

最后，我想给你一个小建议：如果可行性研究并不能给你一个很明确的结果，也可以考虑小范围试点，先实现一个最小化可行产品，等验证了可行性，再逐步加大投入。

课后思考

除了本文列出的几个，你觉得还有哪些原因导致了可行性研究形同虚设？你身边有没有关于可行性研究的案例，以及你是怎么做可行性研究的？欢迎你在留言区留言，和我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

10 | 如果你想技术转管理，先来试试管好一个项目

你好，我是宝玉，我今天与你分享的主题是：如果你想技术转管理，先来试试管好一个项目。

技术转管理，是很多技术人员的梦想，所以经常有人问我，怎么样才能转型管理？

项目管理，是最基础的管理，既要管理一个项目，又要协调整个团队一起，完成共同的目标。

我的管理转型就是从项目管理开始的，在从技术转型项目管理的过程中，让我从以前专注于局部技术实现，逐步转向关注项目整体；从个人的单打独斗，到借助整个团队的力量一起完成一个项目。

一直到后来做开发总监要去管理整个开发部门，发现还是一样绕不开要管理项目，只是从直接管项目变成了间接管项目而已。

所以我一般会建议：如果你想技术转管理，先试试管好一个项目。项目管理通常是技术人员转型管理的第一步，也是非常关键的一步！

技术人员转型管理的障碍是什么？

很多人认为技术人员是不适合做管理的，包括网上也有很多对程序员的刻板印象，比如说：极客、木纳、不善交际、头发少、穿格子衫……

而我了解的程序员却不是这样子的，他们都很聪明，学习能力强，而情商这些其实和其他职业群体是没有区别的。

那么为什么程序员会给人这种刻板印象呢？

一方面原因是这个群体勇于自黑，不介意这些印象；另一方面则是他们过于专注技术实现，沉浸于细节中，而忽视了其他事情。

程序员总是想着如何技术实现、用什么语言框架、怎么提高效率……要钻研技术，这些是非常好的优点，但是要转管理，这反而会是一种障碍。

因为管理，最重要的一点就是大局观，要能从整个项目的角度，从整个团队的角度去思考，去确定方向，去发现问题，对问题及时解决及时调整。

但是当你把注意力都放在技术细节上，就容易忽视其他事情，例如和其他人之间的沟通、不关心当前项目进展。

就像有人说的：

关注细节的，是工程师；
关注过程的，是项目经理；
关注结果的，是老板。

所以，如果你要技术转管理，可以先从管好一个项目开始。这也是为什么我在专栏一开始，就建议你要逐步转变思维，从技术思维到工程思维，不要仅仅局限于自己负责的那一个小模块，而是要多从项目的整体去思考。

怎么样去管理一个软件项目？

软件项目管理涉及知识不少，既有传统的项目管理知识，又需要掌握软件工程的知识，所以很多人一谈到项目管理就觉得很难很复杂。

我在专栏中一直强调“道、术、器”，对于很多知识，如果我们能总结出其中的“道”，再去看很多问题，其实就没那么复杂了。

就软件项目管理来说，“道”就是管好人、管好事。如果从这两个维度去看如何管理项目，就会发现其实并不难，有很多“术”可以为我们所用。

怎样管好软件项目中的人？

软件项目管理的一个维度是管人。项目管理中的人，主要涉及两类：客户和项目成员。

1. 管理好客户的预期

客户，就是会使用你软件产品的人，通常也是给你项目出钱的人。

对于客户的管理，就是对于客户期望值的管理，如果你项目的结果高于客户的期望，那么就可以说你的项目就是成功的，如果没有达到客户的期望，可能就是不成功的。

想要满足客户预期，通常来说，就是你能在项目的质量、范围、时间和成本上达到要求。

质量达标：交付产品是高质量的，满足客户需要的。

完整交付：按照约定的功能范围交付最终产品。

按时交付：项目按照客户认可的进度完成。

预算之内：在预算内完成项目。

这四个要素，并不是说必须都要满足，其实很多时候是可以协商的，重点是要达到一个平衡，怎么达到平衡？具体你可以参考《08 | 怎样平衡软件质量与时间成本范围的关系？》，我已经在这篇文章中进行了详细的解答。

2. 用流程和规范让项目成员一起紧密协作

项目成员，也就是帮助你一起完成项目的人。

对于项目成员的管理，不需要过多依赖人的管理，否则项目经理就会成为项目管理的瓶颈。所以更多要落实到流程和工具上。

好的项目管理，不需要直接去管人，而是管理好流程规范；项目成员不需要按照项目经理的指令做事，而是遵循流程规范。

合适的项目管理工具，也可以简化流程，保障流程的执行，提高效率。

关于具体怎样制定流程规范，我会后续更新的文章《11 | 流程和规范：红绿灯不是约束，而是用来提高效率》中有更多介绍。

关于项目管理的工具，也会在《13 | 项目管理工具：一切管理问题，都应思考能否通过工具解决》中有详细介绍。另外，你也可以先参考我在《06 | 大厂都是如何应用敏捷开发的？（上）》中我提到的部分案例。

怎样管好软件项目中的事？

软件项目管理的另一个维度就是管事。软件项目中的事，是指要完成项目目标，在整个开发过程中所产生的一系列任务。对项目中事情的管理，本质上就是对软件开发过程的管理。

1. 选择适合项目的开发模式
软件项目的过程管理，和其他工程项目完全不一样，有其独特性，好在软件工程对这些过程的开发模式都已经有了很好的总结，我们直接借用就可以了。

选择好开发模式，才好确定后续的一系列问题，例如流程规范、使用什么工具，如何制定项目计划等。

所以对软件项目过程的管理，首先就是要根据项目特点选取合适的开发模式，是敏捷开发还是瀑布模型或者瀑布模型的衍生模型？是一步到位还是逐步迭代？

对于开发模式的选择，可以参考《03 | 瀑布模型：像工厂流水线一样把软件开发分层化》、《04 | 瀑布模型之外，还有哪些开发模型？》和《05 | 敏捷开发到底是想解决什么问题？》的内容。

当然，开发模式选好了后，还需要配套的流程规范，以及合适的工具，以保障开发模式的执行。

2. 制定好项目计划

凡事预则立不预则废，在选择好开发模式后，紧接着就是要做好项目计划，有了项目计划，才能有计划有目的地去推动项目进展，出现问题也能及时发现、及时调整。

对于如何制定计划，我将在下一篇更新的文章《11 | 项目计划：代码未动，计划先行》中进行详细讲解。

3. 对计划进行跟踪和控制，同时做好风险管理

计划制定后，并不是说事情就会完全按照我们设想的进行，实际执行难免会和计划有些出入，所以还需要对计划进行跟踪和控制。当项目的推进过程中，如果计划有出入时，需要分析原因，对计划做出调整。

同时，也不能盲目乐观，对于项目过程中可能存在的风险要进行识别，做好 B 计划，这样一旦风险发生变成问题，可以及时应对，减少风险导致的损失。有关风险管理的内容，可以参考《14 | 风险管理：不能盲目乐观，凡事都应该有 B 计划》。

管好人、管好事，你就能管好软件项目。除了上面介绍的一些项目管理知识，涉及软件项目管理的知识内容还有很多。这里并不是说其他知识内容不重要，而是在刚开始的时候，先把这些事情做好，可以保证项目管理不会出现大的偏差，然后逐步拓展到其他知识领域。

在这里，我把前面说的内容做了个简单的思维导图，希望可以对你的项目管理转型起到一定的帮助作用。

技术转管理的一些经验教训分享

技术转管理的路上肯定不会是一帆风顺的，要自己踩过很多坑才能成长，我这里也给你分享一点经验教训，希望能帮助你少走一点弯路。

控制你想写代码的冲动

我给每一个刚从技术转型管理的同学的第一个建议都是一样的，那就是：“不要写代码，不要写代码，不要写代码，控制你想自己动手写代码的冲动。”

前面我说过技术人员转型管理的最大障碍是什么，那就是过于关注技术，而忽略了其他事情。从技术转型管理，是个巨大的转变，这种思维的转变是很难一蹴而就的。

对于程序员来说，写代码是自己的“舒适区”，而管理则是“学习区”或“恐慌区”，在转型的过程中，特别容易回到舒适区。

比如你看某个接手你的程序员代码写的实在是不够好，那是你最熟悉的，你只要一小时就写完了，而他要一整天的时间，还没有你写的质量好，你会很有冲动去帮他完成。

比如说在项目进度吃紧的时候，你可能第一想法就是自己去写代码帮助团队赶上进度。

但是，你要知道，当你转型管理后，你的主要职责就管理，而不是写程序。如果你还是把大部分时间用在写程序上，那么你就很容易忽略项目中的问题。比如没有去关注项目的进展、目前项目的瓶颈、和客户以及其他项目组之间的沟通协调等。

这就是为什么你第一步是要控制自己写代码的冲动。作为一个项目管理者，你的第一要务是管理好项目，而不是去写代码。当你控制住不去写代码以后，你才能把注意力放到团队和项目上去，去领导团队。团队出现问题时，你能及时解决、及时调整。

所以，如果你带的项目进度吃紧时，你要做的不是去写代码，而是去帮助团队从其他角度想办法。具体怎么做，你可以参考我在《08 | 怎样平衡软件质量与时间成本范围的关系？》这篇文章里介绍的一些方法，看是不是可以用这些办法缓解进度压力。

团队的成功，才是你的成功

我刚转型做管理的时候，问过老板一个问题：“是不是我把上级的工作做了，我就能升职了？”老板的回答很出乎我意料：“并不是你把上级的工作做了就能升职，而是你的下级都成长了，能替代你的位置了，你就可以升职了。”

这让我明白一个道理：作为一个管理者，团队的成功，才是你的成功。做程序员的时候，把代码写好就很成功了，但是转型做管理后，团队的成功和项目的成功，才是你的成功。

形成自己的管理风格

我在刚开始工作的时候，当时的项目经理很厉害，对我们要求非常严厉，做错了可能就要挨批评，项目管理的很好。那段时间我也进步很大，所以我觉得他是一个很好的项目经理，我就想着自己以后也要像他一样去管理项目。

等到我开始管理项目时，我也想像他一样去严厉的对待下属，但我的性格是比较温和的，我没有办法去做到动不动就去责骂、批评下属，这也让我有了很大的困惑。

后来我尝试着结合自己的性格特点，更多地去激励、帮助下属。在这种管理风格下，整个团队的氛围很融洽，大家做事情也积极主动，一样达到了很好的管理目标。

所以说管理这种事，并不是只有一种风格一种方法，你完全可以根据自己的特点，找到适合自己的管理风格。

坚持就是胜利

技术转型管理的过程，一定不会是一帆风顺的，你会面临很多挑战，会有非常大的压力。这时候最容易产生的冲动行为就是：“算了，还是回去写程序吧！”

我在转型的过程中也遭遇过非常大的压力，遇到过各种困难，掉了好多头发。我有过好多次想放弃的念头，最终还是咬咬牙，坚持了下来。

这样过了几年后，我再回头看当初觉得特别难、压力特别大的事情，现在看起来根本不算什么。如果我当初真的放弃了，恐怕再难迈过那道坎，完成转型。

一旦你已经下定决心要转型，就不要轻言放弃，坚持就是胜利。

总结

想要技术转型管理，首先从转变思维方式开始，从技术思维到管理思维，从关注细节到关注整体。然后去改变习惯，控制自己想写代码的冲动，多去从其他角度想办法。

要管理好一个项目，关键是要管理好项目中的人和事。对客户要管理好期望，对项目成员则通过合理的流程规范更好的一起协作；对于项目中事的管理就是对软件开发过程的管理，选择好开发模型很重要，然后就是制定好计划，按照计划推进，过程中不断的调整，并且管理好项目中的风险。

课后思考

你是否有想法从技术转型管理，打算怎么做？如果你正在准备转型或者转型中，有没有遇到什么困难，打算怎么去解决？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

11 | 项目计划：代码未动，计划先行

你好，我是宝玉，我今天想与你聊一聊“项目计划”的问题。

若干年前，我接手一个陷入困境的项目，当时的项目经理刚从技术高手转型项目管理，还是没有摆脱技术思维，项目没有什么计划。

他把关键模块分给了自己开发，同时还要兼顾项目管理，导致自己的工作遇到瓶颈，其他人的进度也受影响，大家加班加点也没什么进展，士气低落。

我接手后，第一件事是重新制定项目计划，在排任务时，避免了对某个人的过度依赖，设置了几个关键里程碑。我还特地把第一个里程碑设置的相对容易一点，只需要运行核心功能。

这样大家重整旗鼓，很快就完成了第一个里程碑。达到第一个里程碑的目标后，团队成员很受鼓舞，士气很快就上来了，后面按照新的计划，并没有太多加班加点，就完成了一个个的里程碑，最后顺利完成项目。

你看，如果没有计划，你的项目可能会陷入一种无序和混乱中。

计划，就像我们出行用的导航，你可以清楚地看到项目整体的安排，同时它还时刻提醒我们目标是什么，不要偏离方向。

执行计划的项目成员，就像使用导航的司机，可以知道什么时间做什么事情，保证任务得以执行。执行计划的过程，就像我们沿着导航前进，可以了解是不是项目过程中出现了偏差，及时的调整。

做技术的就不用关心计划吗？

很多程序员对计划有误解，也不愿意做计划，他们通常都会用一些原因来说做计划是没必要的。

一种典型观点是：“既然计划总是在变，干嘛还要做计划？还不如上手就是干来的爽快！”

这就好比我看过的一个段子：“既然飞机老是晚点，还要时间表干吗？”“没有时间表，你怎么知道飞机晚点了呢？” 计划也是这样，给你提供一个基准线，让你知道后面在执行的时候，是不是出现了偏差，可以根据计划不断地修正。

还有的人说，做计划那是项目经理的事，我是程序员，项目计划与我无关。

我在专栏中常说你要有大局观，不要将自己局限在程序员的身份中。试着做计划就是一个非常好的培养大局观的方式。比如说，你在制定计划的过程中，需要去综合考虑各种因素：有哪些任务要做，可能存在什么风险，任务之间的依赖关系是什么，等等。

参与做计划的过程，可以让你对项目的各种事情了然于胸，这就相当于扩大了你的上下文，让你有更高的视角看待当前工作遇到的问题。

另外，我还见过很多人抱怨项目经理制定的项目计划有问题，却很少看到会有人愿意主动参与制定项目计划。如果你不主动参与计划的制定，最终就只能按照项目经理制定的计划执行了。出现计划不合理的地方，你也只能接受，工作就会一直很被动。

当然，有时候你可能确实是没机会参与到当前的项目计划中。不过，万事皆项目，你一样要学会做计划，因为学会做计划，会对你工作生活的方方面面起到积极的作用。

比如很多人都有一些目标：要转型做管理、要移民、要写一个业余项目，然而很多目标都无疾而终了。这是因为光有目标还不够的，必须得要付诸行动。而要行动，就需要对目标进行分解，进而变成可以执行的计划。

如何制定计划？

如果有一天，你接手了一个项目，通常第一件事就是得去制定一个项目计划。那么怎么制定计划呢？

制定项目计划，通常有三个基本步骤：

第一步：任务分解；

第二步：估算时间；

第三步：排任务路径。

以前我在飞信时，有一个项目叫“留言飞语”，就是飞信用户可以在网站或者 PC 客户端，互相留言，当时我负责这个项目的服务端，正好我还留着当年制定的计划，虽然不算一个很好的计划，但好在它是一个真实项目的计划，正好可以用它来说明一下如何制定计划。

（备注：图片较大，需要点击查看大图）

你看到的这个计划其实不是第一版，可能也不是最后一版，因为制定计划本身是一个反复迭代的过程，尤其是一开始在需求并不够明确的时候，只能比较粗粒度的分解任务和估算，在项目推进的过程中再逐步细化和完善。

第一步：任务分解

我们写程序的时候都有经验，就是要把复杂的问题要拆分成简单的问题，大的模块拆成小的模块，在工程里面这个叫“分而治之”。做计划也是一样，第一步就是要对任务进行分解。

在项目管理中，对任务分解有个专业的词汇叫 WBS，它意思是工作分解结构（Work Breakdown Structure, WBS)。就是把要做的事情，按照一个树形结构去组织，逐级分解，分割成小而具体的可交付结果，直到不能再拆分为止。

下图就是“留言飞语”项目按照 WBS 拆分的结果。

可以看的出，整个过程是按照瀑布模型来划分的，大的阶段分成技术方案设计、编码和测试，然后每一个大的阶段下面再进一步细分。

例如技术方案设计下面再有需求分析、技术方案设计和评审等；而编码阶段则是按照功能模块再进一步拆分。拆分之后，都是小而具体、可交付结果的任务，且不能再进一步拆分。

这里需要注意的是，在制定计划时，除了要拆分任务，还需要反复思考各种可能存在的问题。

比如，这个项目不仅是网站可以访问，还需要在 PC 客户端能发留言，所以还需要考虑和 PC 客户端的通信协议、什么时间可以让 PC 客户端可以测试协议等。如果上手就写，没有良好的计划，就可能会忽略这些问题，最后导致 PC 客户端都不知道怎么去调用服务端接口，也不知道什么时候可以和客户端联调。

如果项目经理对技术细节不熟悉，可以邀请架构师或者技术负责人协助进行任务的分解。

第二步：估算时间

任务分解完之后，你就需要对每一个任务估算时间。就像下面这样。

估算时间这事，有很多方法可以参考，主要还是得依靠以前的经验。要想估算准确，需要从两个方面入手：

任务拆分的越细致，想的越清楚，就能估算的越准确。

要让负责这个任务的人员参与估算。

举例来说，让你直接给出一个“留言飞语”这样项目的估算时间，是很难的，但对于某个具体功能模块的实现，就可以比较准确了。当把“留言飞语”这样大的项目拆分成足够小的任务时，你就可以很容易的对小的任务进行准确的估算，从而让整体的时间估算变得准确起来。

为什么要让开发人员参与估算呢？

我们来对比一下。假如说，一个任务，项目经理估计需要 3 天，但是实际执行的时候，这个任务可能要 5 天，结果导致开发人员加班。这时候开发人员心中肯定会有不满的情绪，认为是项目经理的错误估算导致了他的加班。

如果这个任务所需的时间，是由项目经理和开发人员一起估算出来的，结果最终发现错误估算了任务的难度，这时候开发人员多半会主动加班加点，努力在 3 天之内完成，也不会轻易怪罪到项目经理头上。

但这不意味着项目经理对估算不需要控制，通常来说，项目经理需要自己有一个估算，然后再请开发人员一起评估。如果结果和自己的估算差不多，那就可以达成一致，如果估算不一致，那怎么办呢？

其实很简单，就是要双方一起沟通，消除偏差。特别要注意的是，开发人员预估工作量通常会很乐观，所以最后时间会偏紧，这种情况一样要去沟通消除偏差。估算的主要目的是尽可能得到准确的时间。

但是在沟通中也要注意技巧，不要采用质问的方式：“这么简单一个模块居然要 5 天？”这只会让听者产生逆反心理，无法有效的沟通。可以恰当的提一些问题来达到有效沟通的目的，比如我通常会问两个问题：

“能不能把你这个任务再细化一下？”

“能不能简单介绍一下这个模块你是打算如何实现的？”

估算出现偏差，可能是由于开发人员没想清楚，或者是项目经理自己低估了其难度。提问可以帮助双方搞清楚真实的情况是什么样的，而且也不会招致反感。同时项目经理还可以给予一些建议和支持。

沟通最好的方式就是倾听和恰当的提问。

如果任务的粒度太粗，就需要进行细化，细化后就能更准确的知道结果。

对于估算的结果，通常还要考虑增加一些余量，因为实际项目执行过程中，并没办法保证是 100% 投入，有可能并行还有其他事情，或者一些突发事情、事先没有考虑到的任务都有可能影响进度。至于加多少余量，还是要根据项目的情况和经验来判断。

第三步：排路径

我们知道，项目中有些任务是可以并行做的，而有些任务之间则是有依赖关系的。比如说“留言飞语”项目中，编码和测试方案是可以同时进行的，而 Code Review，要在编码完成后进行。

所以，排路径就是要根据任务之间的关系，资源的占用情况，排出合适的顺序。例如下图。

排路径是一个相对比较复杂的任务，比如要注意任务的依赖关系，要注意路径的长度，尽可能让几个任务可以并行的进行，避免相互等待。如果借助像 Project 这种工具会让这个过程相对容易些，可以直观的看出来哪些任务是相互依赖的，哪些是同时进行的。没有 MS Project 这类软件，也可以用一些替代手段，例如 Excel 上画表格。

制定计划时不要担心不够准确，先有一个基本的计划，可以粒度比较粗，不那么准确，让事情先推进起来。

设置里程碑

不知道你有没有参加过那种周期很长的项目，一直看不到结果，时间一长会很疲惫。所以有经验的项目经理会在项目启动后，根据制订好的初步计划，确定几个关键的里程碑。

里程碑的时间点确定后，计划可以灵活调整，但里程碑一般不会轻易改变，因为里程碑代表着一份承诺。这对于项目成员来说，有两个重要的影响，一方面，成员会有很明显的来自 DeadLine 的进度压力，自古 DeadLine 就是第一生产力；另一方面，就是在里程碑完成后，大家会获得一种正面激励。

里程碑的设置，并没有特别的规则，可以是项目生命周期的特定主要时间，也可以是一些关键的时间点。拿“留言飞语”这个项目来说，有三个时间点非常关键：

第一个时间点就是确定和 PC 客户端的通信协议，这样 PC 客户端可以根据这个协议开始开发功能了；

第二个时间点就是服务端开发完成，PC 客户端可以服务端联调了；

第三个时间点就是测试验收通过，可以上线了。

最终这三个时间点被定义为里程碑。

在项目的推进过程中，根据里程碑完成的情况，你就可以很直观地知道项目的进展如何。如果发现不能如期完成里程碑，就需要进行适当的调整了，例如加班，或者砍掉一些功能需求。

当然，设置好的里程碑也不是不能调整，但是要注意调整次数不宜过多，不然就会变成“狼来了”，以后就没有人相信你的时间点了。

计划需要跟踪和调整

项目管理中，并不是计划制定好了就完事了，还需要跟踪和调整。就好比你要开车去什么地方，设置好导航还不够，还需要沿着导航前进，如果遇到障碍或者走错路了，得要及时调整。

项目的跟踪是很必要的，可以了解计划的执行情况，了解成员的工作情况，是否能按时完成，需要什么样的帮助。

跟踪进度的方式主要有两种，一种是项目经理定期收集跟踪，一种是项目成员主动汇报。项目经理挨个收集的话，会有一个沟通确认的过程，对进度会了解的更准确；项目成员主动汇报，可以减少项目经理的收集工作，但有可能不准确。

在这方面，我觉得敏捷开发的两个实践特别值得借鉴和推广。

第一个就是每日站立会议，在每天的站立会议上，每个项目成员都需要说一下自己昨天做了什么，明天计划做什么，有没有什么阻碍。通过这种方式，可以非常好的了解每个人的任务进展情况，同时对于成员遇到的困难，其他人也可以及时给予支持。

第二个就是看板，通过看板，可以非常直观的看到每个人在干什么，进展如何。

通过对项目计划的跟踪，可以很容易的看出来执行的情况，也会发现偏差，计划出现偏差是很常见的，所以需要定期进行调整，也不需要太频繁，例如可以每周一对计划做一次调整。

总结

项目计划是保障软件项目成功非常重要的手段，制定计划的过程，可以让你对项目有全面的了解，跟踪计划让你知道项目进展情况，出现问题也可以及时调整。

将任务分解、估算时间、排路径，三步就可以制定出一个项目计划，制定计划不要追求完美，制定好一个初步计划后，就可以先按照计划推进起来，进行过程中还可以继续调整细化。设置里程碑可以有效的保证项目的按时交付。

最后，并不需要当项目经理才能去制定计划，生活中每件事都可以当作一个项目，都可以去制定计划来帮助你实现目标。

课后思考

你现在项目的计划制定的如何？如果你是项目经理，你会如何改进？你日常生活中，会有制定计划的习惯吗？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

12 | 流程和规范：红绿灯不是约束，而是用来提高效率

你好，我是宝玉，我今天想与你讨论流程和规范的价值，以及如何参与制定好的流程规范。

不知道你所在的软件项目中是不是也有各种流程规范，例如：

开发人员不能直接在生产环境修改代码操作数据库，必须在本地先测试验证后，由运维操作；

代码需要 Review 通过才能合并主分支；

代码需要遵守各种规范，像命名、格式，还有缩进用几个空格还是 tab 的细节问题；

遇到 Bug，先提交到 Bug 跟踪系统。

在我经历的项目中，或多或少都会有各种各样的流程规范，而且越是大的、正规的项目团队，流程规范越是多。

然而很多人对于流程规范并不是很理解，甚至觉得是一种约束。

为什么要有流程规范？

从某种程度上来说，流程规范确实是一种约束：约束了我们如何做一件事，约束了我们用什么标准做事，约束了我们用特定的顺序做事。

既然如此约束我们，为什么还要有流程规范呢？

提升团队效率

从个体来看，因为流程规范的存在，确实可能存在效率降低的情况，但从团队的角度来看，好的流程规范反而是提升效率的。

这其实很像我们生活中的红绿灯，用一个简单的规则：红灯停绿灯行，来约束车辆行人按照指示灯行进。

从单个车辆来看，看似是因为红绿灯的存在而影响了效率，但是从整体来看，因为红绿灯的存在，有效避免了拥堵，反而是提升了大家出行的效率。

其实红绿灯除了能提高效率，还有其他好处：

红绿灯这样好的管理交通的经验，形成流程规范后，就可以全世界共享这种先进的经验；

红绿灯不再到处依赖于人指挥交通，而变成了让红绿灯的规则来指挥交通。

软件项目中的流程规范是不是也有这样的效果呢？

以代码审查的规范为例，对于技术高的程序员来说，代码审查可能会耽误一点时间，但对整个团队来讲：

即使是水平高的程序员，也可能会被发现有错误，代码审查可以降低出错的概率，保障质量；

对于水平低的程序员，可以通过代码审查学习和成长，代码被高水平程序员审查后，可以有效提高质量。

软件项目中这样的例子还有很多，类似的还有像遇到 Bug 要提交到 Bug 跟踪系统，还需要配合重现步骤说明，看起来繁琐，但是却让 Bug 可以有效跟踪，让开发人员可以重现和定位，从而高效的修复 Bug。

将好的实践标准化流程化，让大家可以共享经验

我们知道，在运动项目上，有些运动员特别有天分，总能拿好的成绩，而这些运动员的动作，会被反复的研究学习，最终形成标准化动作。而其他天分一般的运动员，按照研究出来的标准动作练习，也能取得非常好的成绩。

软件工程也是这样，早些年的软件项目，就是个人英雄主义盛行的时代，项目的成败极其依赖于个别厉害的项目经理或者技术高手，而这种牛人，总是稀缺的存在。

所以后来很多编程高手写代码的方式，甚至写代码的格式，也会被研究，最终形成一套套的代码规范。其他水平一般的程序员，按照代码规范，也能写出不错的代码。

代码规范还有个好处，就是大家写出来的代码看起来差不多，换个人接手别人的代码，也能很快上手。

如果我们站在流程规范的角度看软件工程的开发模式，它也是源自实践过程中，有些厉害的项目经理发现了好的、可以提升软件质量的开发实践，不断总结改进，最后变成了流程，让普通的项目经理按照这一套流程，也能做出不错的软件。

你看瀑布模型也好，敏捷开发也好，最后落实下来，不就是开发过程中一个个的流程规范么？所以瀑布模型我们需要各种阶段评审，敏捷开发需要每天开站立会议，需要每个 Sprint 有计划会、评审会。

借助流程规范，让项目管理从人治到“法治”

在《10 | 如果你想技术转管理，先来试试管好一个项目》这篇文章中我就提到过，管理就是管人和管事，而管人，就要借助流程规范来管理。

因为如果在项目管理中，过于依赖人的管理，项目经理就会成为瓶颈，大事小事都需要项目经理来决策。再说项目经理也不能保证每次决策的正确性，如果决策失误，会很可能导致一些冲突。

而好的项目管理，不需要直接管人管事，而是管理好计划和流程规范；项目成员不需要按照项目经理的指令做事，而是遵循计划和流程规范。

我以前工作过的一个项目组，一个项目持续了好多年，中间人换了一批又一批，甚至有时候连项目经理都空缺，而项目一直井然有序的进行着，没有出什么问题，靠的就是多年积累下来的适合项目组的流程规范。

就像在《06 | 大厂都在用哪些敏捷方法？（上）》这篇文章中描述的那样：项目成员日常从看板就可以知道要做什么任务，代码审查、自动化测试可以有效保证质量，项目文档可以保证新人加入时能快速上手，结对编程可以保证新人遇到问题可以得到直接的帮助。

还有一个常见场景就是需求变更，产品经理想加一个紧急需求，这通常是让项目经理为难的事情：加吧，影响项目进度，开发人员有意见；不加呢，可能客户或者产品经理有意见。一个不小心就两边都得罪了。

如果你有一个大家认可的需求变更流程，就不再需要靠项目经理一个人决定该不该加需求，而是通过流程，来大家一起决策是不是要加这个流程。

所以你看，流程规范，看起来是约束，实际上你用的好的话，不仅可以提高团队效率，还可以将好的实践标准化流程化，让大家可以共享经验，还可以有效的管理项目。

如何制定好流程规范？

在项目管理中，难免要去制定流程规范。即使你不是管理者，也可以提出合理的流程规范，帮助把项目管理好。

有一个科学的制定流程规范的方法，可以让你更好地制定出好的流程规范。

制定流程规范的四个步骤

对于流程规范的制定，可以通过四个步骤来开展。

第一步：明确要解决的问题

要制定一个流程规范，第一步就是明确你是要解决什么样的问题。项目中很多问题，都可以思考是不是能通过流程解决。

比如说有程序员在生产环境操作，误删了数据表，造成了严重问题。如果只是对程序员进行处罚，寄希望于小心谨慎避免类似问题，那么下一次还有可能会有类似的事情发生。

如果说在流程上规范起来，例如：数据库操作之前先备份数据库，事先写好 SQL 语句，需要有人审查，测试环境先测试通过，最后再生产环境执行，那么就可以避免以后再出现不小心删除数据表的事情发生。

第二步：提出解决方案

对于问题，也不用着急马上就想着用流程规范，可以先思考解决的方法，有了方法后再进一步思考是否能提炼流程规范。

那么方法和流程规范有什么区别呢？

相对来说，方法更有针对性，可能只适用于特定场景或者特定人，而要将方法上升到流程规范，则需要有一定的普适性，能变成具体的步骤或者标准，让每个人都能执行。

比如说服务器部署后出现问题，高手可能就直接上服务器操作，直接修改代码编译解决，这是一个解决方法，但这不能成为一个流程规范，因为换一个水平不行或者对代码不熟悉的人来做，可能会搞出更大的问题。这时候回滚操作就是一个相对普适的方法，可以变成一个部署后出现问题的流程。

在提出解决方案，制定开发流程时，可以参考借鉴软件工程中，大家公认的好的实践。比如说：

敏捷开发的流程：虽然你的项目不一定采用敏捷开发的方式，但是敏捷开发中一些好的流程是可以借鉴的，例如参考我之前文章提到的像看板、站立会议、持续集成，这些好的工作流程，都可以借鉴。

代码规范：其实很多公司都公开了他们的代码规范，可以直接基于这些规范制定团队的规范。例如说前端的有 Airbnb 的代码规范 Airbnb JavaScript Style Guide，Java 的有 Google Java Style Guide ，.Net 的有.NET Guide，等等。

源代码管理流程：现在的源代码主流是 git，而基于 Git 的代码管理已经有很多成熟的流程规范可以参考。例如阮一峰老师写过的《 Git 使用规范流程 》、《Git 工作流程》和《Git 分支管理策略》，或者 Github 官方出品的《Understanding the GitHub flow》，Gitlab 官方推荐的《Introduction to GitLab Flow》。

部署流程：十年前，每日定时构建还是很时髦的部署流程，而现在，主流的部署流程已经变成了持续部署，每次代码合并到主分支都可以触发一次自动部署，这样一有问题，就能马上知道发生在哪个环节。

像这样的好的流程还有很多，在我们专栏会介绍一些。如果平时多留心，你也可以学到很多。

第三步：达成共识，推广执行

在流程规范提出后，还需要得到大家认可，只有大家认可，达成共识，才能共同遵守，保障制度的执行。

对于大家都认可、很重要的流程规范，一定要让大家严格遵守，必要的时候需要配合一些奖惩制度，以保障其执行。

比如说流程规范的执行和绩效考评挂钩，对于没有执行的需要私下沟通提醒，严重的需要批评教育。否则流程规范会形同虚设，没有太大的意义。

第四步： 持续优化，不断改进

流程制定后，在实际执行的时候，难免发现一些不合理或者不科学的地方，这时候就需要对其进行调整。

还有一些流程规范，随着时间推移，可能已经不能适合要求了，也需要考虑改进甚至抛弃，不然反而会成为一种阻碍。

比如说以前采用瀑布模型开发时，项目经理因为需要了解进度，所以每个项目成员要写日报，如果有站立会议了，日报这种形式就可以完全被站立会议替代，没有再存在的必要。

通过以上四个步骤，你就可以将日常项目中遇到的一些问题，用流程规范的方式逐步管理起来，在实施的过程中再不断优化改进，淘汰不合适的流程规范。

将流程规范工具化

如果说，以前我还是人为去推动一些流程规范的执行，近些年，我越来越感觉到，应该尽可能借助技术手段来推动甚至替代流程规范。

例如说代码规范，以前代码规范的执行，主要靠反复的教育宣传和代码审查中一个个去检查。而现在，借助 VSCode 这种强大的 IDE，以及 ESLint 这种代码检查工具，可以方便的检测出不符合规范的代码，甚至于可以帮你直接格式化成满足代码规范的格式。

还有像保证代码质量的问题，早些年必须依赖测试人员大量手工的测试，而现在借助 CI（Continuous Integration，持续集成）、自动化测试和 Git，可以保证代码必须在通过测试以后，才会合并到主分支，从而很好的保证了代码的质量。

就像任正非的《全面提升软件工程能力与实践，打造可信的高质量产品》公开信中题记的这一段话说的：

“软件工程”和“质量工程”需要依靠架构技术，而不是依靠 CMM 和 QA 管理流程。一切工程问题，首先要思考能否通过技术解决，当前技术无法解决的问题，暂时由管理手段代劳，同时不停止寻找技术手段。

“软件工程”不要过于依赖流程和管理手段，要思考怎么通过技术手段去解决问题。

总结

流程和规范，就像红绿灯一样，不是一种约束，而是牺牲一点个体利益，提高团队效率；流程和规范将好的实践标准化流程化，让大家可以共享经验；流程和规范，让项目管理从人治变成“法治”。

要制定好项目规范，先明确要解决的问题，然后提出解决方案，看是否可以通过流程规范来解决，有了方案后需要团队成员一起达成一致，最后再推广执行。在执行过程中需要持续的优化，不断改进。

对于需要手动操作的流程，可以思考是不是能采用技术手段自动化，通过技术手段去解决。

课后思考

你所在项目中有没有不合理的流程规范？或者欠缺流程规范导致混乱的情况？如果有，你觉得可以制定什么样的流程规范来改善？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

13 | 白天开会，加班写代码的节奏怎么破？

你好，我是宝玉，我今天想与你讨论让很多程序员头疼的话题：开会。

说到开会，是很多人心中的痛，每天白天忙于参加各种会议，压缩了本来就少的可怜的工作时间。最可气的，有的人开完会工作就算完成了，而像我们写程序的，还得下班后加班加点赶进度！

所以一说到开会，程序员们往往如遇洪水猛兽一般，避之不及。

开会是有价值的

但我想说的第一个问题是：开会是有价值的。软件项目中有不少会议，其实有其价值所在，给你简单举例分析一下。

像评审会议，通过会议，可以让产品设计或架构设计在确定前，收集大家的意见，及时发现问题。

像每日站立会议，可以及时了解项目的进展，了解当前的困难和瓶颈，及时调整计划，解决问题。另外在会议上，每个人都要当众讲一下做过的事情和计划要做的事情，这也是一种无形的监督和约束。

像项目立项会议，可以创建一种仪式感，让每个人都知道项目的关键信息：

项目目标：这项目是要干什么的，达到一个什么目标；

项目里程碑：项目的开始结束时间，项目的阶段划分，以及各个阶段的时间点；

角色分工：项目成员的分工和角色是什么，每个人知道自己的任务是什么，知道遇到问题该找谁；

流程规范：项目开发的主要流程是什么，基于瀑布还是敏捷。

像项目总结会议，团队成员可以一起总结一下项目的得失，把经验总结下来，帮助团队在下一次做的更好。

还有很多会议我就不一一列举。从上面可以看出，这些会议都是可以创造价值的。

开会是有成本的

开会的成本，就像房间里的大象，显而易见而很多人却有意无意无视它的存在。

我们做个简单的数学计算，假设一个程序员月薪一万元，如果他每天四分之一的时间在开会，那就相当于公司每月花了 2500 元在会议上。

这还只是一个人的成本，如果开会的人多，那么仔细算算，整体的会议成本其实很夸张的。所以我想说的第二个问题就是：开会其实是有成本的，而且还不低。

什么样的会议是有效率的？

其实软件项目中有些会议我是愿意参加的，因为是有价值的，高效的。比如说前面提到的每日站立会议，时间不长，但是收获很大。

再比如隔壁郑晔老师的《10x 程序员工作法》专栏中提到的“轻量级沟通”，其中建议的会议方式：人少，面对面沟通。这种小会通常也让我觉得很高效，经常能产生有价值的方案。

还有一些会议则让我觉得没什么价值，比如领导冗长的讲话，比如一堆人在偏离会议主题的讨论，比如跟我没什么关系却被迫参加的会议。

那么为什么这些会议给我的感觉完全不一样？这其实就是我想讲的第三个问题：会议是不是有效率，取决于它创造的价值是不是高于其成本。

我觉得像每日站立会这样的会议更有效率，其时间短、人数少，所以成本低，创造的价值高于其成本。而人数多，又偏离会议主题的讨论会则没有价值，这是因为人数多时间长，导致会议成本高，而其创造的价值远远不及成本。

那为什么还有那么多低效率的会议？因为有的会议，就不是为了创造价值。

比如说有的会议，花的成本不是组织者的，对他来说，得到他的会议价值就可以了。

你是砍柴的，他是放羊的，你和他聊了一天，他的羊吃饱了，你的柴呢？

还有很多会议，是因为组织者和参与者，都没有意识到开会其实是有成本的，所以浪费了成本还不自知。不过这种情况还好，还是可以想办法改进的。

接下来，我们来看看如何提高开会效率，破除避免白天开会，晚上还要加班写代码的节奏。

如何提高开会效率？

我们专栏有一篇文章《08 | 怎样平衡软件质量与时间成本范围的关系？》，很多同学看过里面讲的软件项目金三角理论后，直呼“醍醐灌顶”、“终于找到一套可以说服老板的说辞了”、“能够提高与产品经理打太极的水准”。

其实提高开会效率、提升开会价值的方法，就跟软件项目金三角的理论一样，只要从两个角度去想办法：减少开会的成本，增加开会创造的价值！

在具体探讨这两类方法之前，我们先要认识到一个前提：那就是要让大家意识到开会是有成本的，如果开会创造的价值不能大于其成本，就是浪费。

就像金三角理论，你得先让老板、项目经理明白三条边不可能都占，才好去沟通讨论。要提高开会效率，也需要大家先有这个意识，才能在具体措施上达成一致。

那么，有哪些方法可以减少开会的成本呢？

1. 砍掉一些没价值的会议

在日常工作中，还有很多会议其实并没有什么价值的。如果一个会议符合这些标准，你就要慎重考虑参加了：

没有目标的会议。大家都在随意发散，完全没有主题；

不能形成决策，没有会后行动。如果一场会议看完后都没有什么结果，那跟没开都没啥差别；

你属于可有可无的角色。如果一个会议，跟你其实没什么关系，你无法提供有效的反馈，对你也没什么价值，只不过是被人拉过去开会的，那不如把这个时间用来做一点对项目更有价值的事。

所以，你可以在每次要接受一个会议邀请之前，先问自己两个问题再做决定：这个会议我真的有必要参加吗？以及，有其他方式可以替代吗？

其实，很多问题并不是非要通过“会议”的方式解决。

以前我负责的一个服务，其他组需要调用，所以有一个组的同事想跟我组织一个会议，让我介绍一下服务，以及如何调用。我思考了一下，觉得准备这个会议我也要写一个 PPT，开会还得要时间，这时间我足够写一个详细的说明文档出来了，而且以后其他组再要用，也只要看文档就可以了。

于是我就跟他们说：我们不用开会，我一会发一个文档链接给你们，如果有问题我们可以在聊天工具上沟通。后来他们看完文档后，有几处不清楚的地方，在咨询过我以后，我将文档更新好，就没什么问题了，而且后面再不需要为这件事开会了。

2. 减少参与会议的人

会议的成本和两个因素相关：一个是人数，一个是时间。如果减少人数，就能减少成本。

减少人数好处还在于，人一少，每个人都会更投入，也更有效率，所以往往时间反而会少产出会高。而且，如果会议上要形成一些决议，人越多越难做决策，人越少越容易达成一致。要想有决议的话，先开几个小会，达成一致后再开大会，大会更多只是宣布一个结果。

像谷歌和 Facebook，他们对于会议的态度就是能不开就不开，无关的人不参与。Amazon 的规则也很简单：一场会议的人数，最多订两份批萨，如果超出这个规模，说明这个会议的人数太多了。

3. 缩短开会时间

减少开会成本的另一个方法就是缩短开会时间。缩短开会时间有很多成熟可靠的方案可以选择。

比如说站立会议，通过站立的方式逼着大家快点结束。

另外，麦肯锡开会上有些做法也值得借鉴：

每个成员有一张黄牌，用于喊停其他人会议中发散讨论无意义的话题；

有人控制节奏，大家快速发言；

PPT 不超过 3 张，鼓励大家预先准备，多讨论。

还有比如我们在前面敏捷开发介绍的例子，会议有人主持，当话题开始发散的时候，果断制止，放到“停车场问题”环节，也就是会议的最后专门讨论。

类似这样的缩短开会时间的办法，确实可以有效减少会议成本，这类提升效率的方法还有很多，你可以从这个角度多思考尝试一下。

4. 提升会议所创造的价值

如果能有效提升会议产出，也一样可以达到很好的效果。

比如说，每个会议要有明确的目的和主题，所有的讨论都要围绕会议目的展开。当你发现会议上一些问题的讨论偏离了会议的主题，例如一个需求评审会，结果架构师在讨论技术细节，这就完全偏离了主题。

你就应该站出来提醒一句：“现在既然是讨论需求，不如先不讨论技术上的问题，等到需求确定了，我们后面再慢慢讨论技术问题。”或者说：“不如这个问题我们另外组织一个会议讨论。”

还有开会后，要有明确的结论，有后续的待办事项，落实到个人，对待办事项有跟踪。

偷偷说一下，有时候一些没什么价值的会议，又必须要参加，我一般会参会前，用一个本子把一个技术难题、或者一篇博客主题，写下来。

开会的时候，把这个难题理清楚思路，把博客的提纲写出来，这样一个会议开完，我的问题也解决了，或者文章提纲也有了。同样也是收获满满，没有浪费太多时间。

这些都是提升会议价值的方式，相信你对会议成本有了概念以后，也可以找到很多可以帮助你提高开会效率、更好创造会议价值的方法。

总结

今天带你一起学习了解了开会的“道”，那就是开会是有价值的，开会是有成本，会议是不是高效，就看它创造的价值是不是高于其成本。

如果你想破除白天开会，加班写代码的节奏，就需要从缩减开会成本和提升开会价值的方向上去想办法，还需要让你的老板、项目经理都有“会议是有很高成本”的意识。

砍掉一些没价值的会议，减少开会的人数，缩短会议的时间，提高会议创造的价值。

课后思考

你在项目中，有哪些会议其实是可以不参加的？哪些会议是可以缩减人数的？哪些会议是可以缩短时间的？哪些会议是可以更好的提升价值的？或者你对上面的观点有哪些补充？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

14 | 项目管理工具：一切管理问题，都应思考能否通过工具解决

你好，我是宝玉，我今天想与你分享的主题是：一切管理问题，都应思考能否通过工具解决。

早些年我在做项目管理工作的时候，除了制订计划外，还要花不少时间去跟踪计划的执行情况。

项目管理上出了问题，管理者总是喜欢从流程规范的角度去想办法，于是为此设定了不少流程规范，例如每天要写日报，根据日报更新项目进度，每周要开周例会，看看项目有没有执行上的问题。

对任务进度的量化也是个很困扰项目经理的事情，需要频繁的去问程序员：“你这个任务进展如何，大概完成比例多少？”，从程序员那得到的答复通常都是个很乐观的数字，例如 80%。第二天以为他能做完，结果一问是 90%，就这样要持续好多天才真的算做完。

所以后来我得出来一个结论：一个任务，只有 0% 和 100% 两种状态是准确的，中间状态都是不靠谱的。

除此之外，还有个问题就是，项目的进展并不太直观，除了项目经理每天看计划表，对计划有一个大概了解以外，其他人可能只有在到了计划设置的“里程碑”时，才对进度有比较直观的感觉。

项目成员手头事情做完，如果和计划有出入，也不知道自己接下来该干嘛，都要跑去问项目经理，所以项目经理对于很多事情都要从中协调，日常有很多繁重的任务管理工作。

后来我发现其实很多管理者都有类似的困惑：任务不好量化难以估算，项目成员对当前项目进度缺少直观感受，管理者要花大量时间在任务管理上。

这些年，随着软件项目管理工具的发展进化，发现当年困扰我的这些问题已经不再是一个主要问题，因为通过工具就能很好的解决这些问题。

这也是我这些年项目管理和技术管理的一点感悟：

一切管理问题，都应思考能否通过工具或技术解决，如果当前工具或技术无法解决，暂时由流程规范代替，同时不停止寻找工具和技术。

下面的微博即是一例，当遇到问题时，不仅从流程上思考有没有问题，更要考虑是不是可以用工具或技术手段来解决。

在这里，我还是先带你看一下项目管理工具软件发展史，通过工具的演化，你可以更深入的了解到工具是怎么解决这些管理问题的。

项目管理工具软件发展史

在没有项目管理工具的年代，都是怎么管理项目的？

早些年，我除了好奇过大厂是怎么开发大型软件项目以外，还好奇过像登月这种超大型项目是如何做项目管理的。正好前不久看了余晟老师写的一篇文章《“阿波罗“登月中的工程管理一瞥》，让我有机会一窥究竟。

其实这种大项目的项目管理并不神秘，就是像我们专栏《11 | 项目计划：代码未动，计划先行》那一篇讲的，这种大项目也是采用 WBS（工作分解结构）把所有任务一级级分解，再排成计划，按照计划有序进行。

但阿波罗项目是个超大型项目，所有的任务分成了 A、B、C 三级，到 C 级已经有超过 4 万个任务。要给这四万多任务排出项目计划就太不容易了，一共要几十名分析人员来协调和跟踪所有的任务。最终列计划的图表贴在墙上超过 100 平米。

（阿波罗登月项目巨型计划图。来源：NASA）

在没有项目管理工具的年代，要制订一个项目计划非常之不容易，需要专业人士花大量时间，而且每次修改调整，都要再花费大量时间精力。

最初的项目管理软件：项目计划工具

直到后来像微软的 MS Project 这样的项目计划工具软件普及，才让制订计划变成了一个相对容易的事情，可以方便的对分解好的任务排出计划。

（图片来源：MS Project 官网）

早些年软件项目的开发以瀑布模型为主，瀑布模型的这种按阶段划分的开发模式，和 WBS （工作分解结构）这种将任务层层分解的理念不谋而合，MS Project 这种软件可以非常好的将所有任务分解、制订计划，按照计划跟踪执行。所以那时候会使用 MS Project 就是项目经理的标配。

MS Projec 虽然解决了计划制订的问题，但还是有些不足之处。例如不方便跟踪任务进度，进度不直观等。

再加上后来敏捷开发开始兴起，很多项目都开始采用 Scrum 的方式来进行项目管理，开发变成了迭代的方式，以前单纯的项目计划工具，就不能很好的满足项目管理需要了。

基于 Ticket 的任务跟踪系统

传统的项目计划软件还有很多问题无法解决。比如，很多人都有过以下类似的项目经历：

产品经理口头让开发对产品做一点小改动，开发也答应了，后来就把这事忘了，或者测试都不知道还有这事，也不记得要测试这个模块；

代码审查的时候，发现组内某个同事的代码没有写单元测试，但是因为任务紧，只能先上线，于是叮嘱他后面一定要把单元测试代码补上，结果还是忘了。

日常项目中像这样的小事情不少，如果不记下来很容易忘记，如果用传统的项目计划软件排进去又很麻烦，直到后面有了基于 Ticket 的任务跟踪系统，才很好的解决了这个问题。

Ticket 跟踪最早源于客服的工单（Ticket）系统，每次客户接到一个问题，就创建一个工单，后续和客户的每一次交流和处理，都要更新工单内容和状态，直到结束。

最早在软件项目中，应用 Ticket 跟踪系统的领域是测试领域，用来追踪 Bug，后来逐步衍生到整个项目管理领域，不仅跟踪 Bug，还用来跟踪需求、开发任务等。

也有很多系统用 Issue 来表示 Ticket 的概念，无论 Ticket 还是 Issue，表示的都是一个工作任务，可以包括软件的 Bug、功能需求、某个模块的开发、系统的重构任务等。

那一个 Ticket 应该包含哪些主要信息呢？

一个 Ticket，应该包含：

标题：摘要性的描述 Ticket 内容；

类型：属于什么类型的 Ticket：Bug、需求、任务；

内容：Ticket 的详细内容，例如，如果是 Bug 的话，除了要写清楚 Bug 内容，还需要重现步骤。如果是需求的话，要有需求的描述，可能还需要额外的文档链接辅助说明；

创建人：谁创建的这条 Ticket；

优先级：这个 Ticket 的优先级高还是低；

状态：Ticket 的状态，例如：未开始、处理中、已解决、重新打开、关闭等；

指派给谁：这个 Ticket 被指派给谁了，谁来负责；

历史记录：整个 Ticket 改变的历史信息，用以跟踪；

当然除了这些外，还有一些其他信息，例如创建时间、附件、标签、版本等。另外现在的 Ticket 跟踪软件都有强大的定制功能，可以增加额外的辅助信息，例如你是基于敏捷开发，还可以加上 Sprint、故事分数等信息。

Ticket 的这些内容，基本上可以包含一个工作任务所需要的所有内容。有了 Ticket 之后，无论大到一个功能需求，还是小到一个 Bug，从它创建，一直到完成，整个过程都可以方便的被跟踪起来了。再也不担心像任务被忘记等前面提到的这些情况了。

基于 Ticket 去跟踪任务，不再需要通过日报、一对一会议的方式来收集任务执行情况，负责 Ticket 的项目成员在完成任务后，会直接修改 Ticket 的状态，这样其他人就可以看到 Ticket 是否已经完成。

Ticket 通过各种不同状态，例如未开始、开发中、完成等，可以很直观的了解任务的进展，这就避免了任务难以量化的问题。

Ticket 跟踪系统和敏捷开发也是很好的搭档。在敏捷开发中，产品 Backlog（产品待办任务列表）是一个用来放所有产品的待办任务的清单，在每个 Sprint 开始前的迭代计划会议上，从产品待办任务清单里面选取一部分任务到 Sprint 的待办任务清单（Sprint Backlog）中。

当使用 Ticket 跟踪系统后，就可以把所有产品的待办任务用 Ticket 都记录起来，当我们在迭代计划会议上选取好任务后，就标记为要在当前 Sprint 完成，这样后面就可以方便的筛选出属于当前 Sprint 的所有 Ticket，这样大家就可以从 Ticket 跟踪系统知道我们这个 Sprint 有哪些 Ticket 需要完成、进展如何。

如果将当前 Sprint 中，从开始到结束，每天记录一下 Sprint Backlog 中未完成 Ticket 的数量，绘制成一张图表，横轴表示时间，纵轴表示剩余 Ticket 数量，就可以通过图表直观地看到还剩下多少工作。

这种用于表示剩余工作量的工作图表也叫燃尽图（burn down chart），可以直观的预测工作将在何时全部完成。

（图片来源：维基百科）

基于 Ticket 的任务跟踪系统，很好的弥补了项目计划工具的不足，让项目中大大小小的各种开发任务都可以方便的记录跟踪起来。燃尽图也可以直观的了解剩余工作情况。

如果说美中不足的话，就是整体的 Ticket 状态还不是很直观，例如不能清楚的看到哪些任务在进行中，哪些任务待领取。

基于看板的可视化任务管理

看板本来是在 1940 年由“丰田汽车”发明的生产管理系统，其中一些理念被借鉴到软件开发中，尤其是其可视化的任务管理方式，很好地解决了早期 Ticket 跟踪系统不直观的问题。

所以现在的 Ticket 任务跟踪系统几乎都会有看板视图，通过看板可以很直观的看到当前任务进展情况。

参考上图，可以看出，在看板视图上的所有 Ticket，可以很直观的看出哪些还没开始，哪些进行中，哪些已经完成。

这种可视化的任务视图，不仅是对项目经理，可以很直观看到进展，对于普通项目成员也是很方便。

从“待选取”栏选择一个 Ticket，拖动到“开发中”栏，表示这个 Ticket 已经选取，开始开发了。

手头上的 Ticket 开发完成后，就可以将 Ticket 拖动到下一栏——“测试”栏。

测试人员看到新加入“测试”栏就可以从测试栏选取 Ticket 进行测试。

如果测试没通过，Ticket 就会被拖动到“待选取”栏。

如果测试通过，Ticket 就会被拖动到下一栏——“待部署”栏。

部署完成后，所有“待部署”栏的 Ticket 就会被拖动到“完成”栏。

整个过程完全不需要项目经理从中协调太多，尤其是结合每日站立会议，可以让项目成员自发有序地按照看板开展日常工作。

借助 Ticket 跟踪和看板可视化，项目经理可以从繁重的任务管理中解放出来，可以抽出来时间做一些其他更重要的事情。

以上就是项目管理工具的一个演化简史，可以看到，每一次工具的发展进化，相应的很多项目管理工作就可以得到简化，很多早期的项目管理问题，也就不再是问题了。

有哪些项目管理软件可以选择的？

在了解完项目管理工具的发展历史后，再给你介绍一些目前国内国外主流的项目管理软件，帮助你根据自己项目需要进行选择。

如果单纯是项目计划工具，功能最好、最全的应该是微软的MS Project，但遗憾的是只能运行在 Window 上，不支持 Mac 平台。如果要在 Mac 上使用项目计划工具，可选的有OmniPlan和Merlin Project。

而且这些项目计划工具，现在也都支持了看板视图。不过如果只是单机支持的话，意义并没有那么大，需要在线版的 Ticket 跟踪结合看板视图，才能让整个团队可以一起浏览操作，发挥其最大效用。

基于 Ticket 的任务跟踪系统，最有名的应该是Atlassian公司出品的Jira软件，功能全面，体验很好。Jira 主要是在海外比较流行，因为访问速度和使用习惯等原因，国内用户要相对少一些。

同类产品也很多，微软的Azure DevOps （以前叫 TFS, Team Foundation Server），和微软系的产品如 Visual Studio、Azure 可以很好的整合。

代码托管平台GitHub本身也集成了一套 Issue 跟踪管理系统，虽然没有 Jira 那么强大，但是对于普通项目来说，足够用了。尤其是对于开源项目，完全可以基于 GitHub 的 Issue 进行日常的项目管理。

国内同类的软件有：

禅道：为数不多提供开源版本可以自己搭建的；

Worktile：集成了即时消息软件；

TAPD：腾讯出品，可以和腾讯的服务很好整合，例如企业微信和腾讯云；

云效：阿里巴巴出品，可以和阿里的服务很好整合，例如阿里云和钉钉；

DevCloud：华为出品，和华为云有很好的整合。

还有一些其他很多产品，这里就不一一列举。

那么该如何选择适合的工具呢？

从功能上来说，基本上，上面提到的每一款产品都能满足日常项目管理的基本需求，建议从项目特色、团队成员和价格服务等因素综合考虑。

例如说你的项目完全是微软技术栈，就可以考虑使用 TFS；如果你深度使用阿里云和钉钉，那么就可以考虑阿里的云效；如果你想自己搭建，那么就可以考虑 Jira 或者禅道。

这些产品都有免费版本，可以先试用，你可以仔细对比后，根据自身的情况再最终决定。

总结

今天我带你一起了解了软件项目管理工具的发展历史：从完全手工方式管理项目，到借助计划工具分解安排计划，到基于 Ticket 跟踪管理任务，再到基于看板的任务可视化。每一次工具的升级，都是对项目管理工作的一次简化。

合理的使用项目管理工具，可以帮你极大提高管理效率，起到事半功倍的效果。我也列举了一些目前国内外主流的项目管理工具，希望可以帮助你做出选择。

最后，对于日常项目管理的问题，你也可以多思考是不是可以由工具或者技术手段来解决的。

课后思考

你在日常项目中，有哪些应用工具或者技术解决项目问题的例子？或者你觉得可以用工具或技术解决的问题？你现在项目中用的是什么项目管理工具，有什么优缺点？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

15 | 风险管理：不能盲目乐观，凡事都应该有B计划

你好，我是宝玉，我今天想与你分享的主题是：风险管理，凡事都应该有 B 计划。

说到风险，很多人都模模糊糊有一些了解，然而在软件项目中，有风险意识的却不多。比如说：

估算一个模块工作量，程序员总是会给出一个乐观的进度，而最终实现这个模块的时候，却发现总是有些其他的事情发生影响了进度；

一个关键的程序员，突然离职了，导致项目进度停滞。其实早前就有一些迹象，而项目经理没引起重视；

技术负责人很激进的采用了一个最近很流行的新技术，结果做的过程中，发现这个技术还不太成熟，很多坑没法填，导致项目最终失败；

服务器突然挂了，才发现硬盘坏了而数据没有备份，造成巨大的损失。

这些问题其实都和风险相关，如果没有及时发现这些潜在的风险，没有应对方案，轻则导致项目进度延迟，重则导致项目失败，造成重大损失。

在软件工程里面，针对这些可能造成风险的问题，对风险进行提前识别和管理，就可以有效地应对。

什么是风险管理？

风险是指不确定的事件，一旦发生，将会造成消极的影响。风险包含两个方面的内容：

发生后，会造成什么样的损失？

发生的概率有多大？

所以也有人认为：风险 = 损失 x 发生概率。

比如说，有一次我负责一个小项目，激进的采用了刚开始流行的 React 框架，如果使用熟悉的 Angularjs 框架，正常来说一个月就能完成，当时很乐观的觉得 React 和 Angularjs 也差不多，时间应该不会多出来太多，就按照一个月时间来做项目计划。

最后到实际开发的时候，发现 React 和 Angularjs 有很多不一样的地方，必须要现学现用，原本计划一个月完成的，最后加班加点拖到一个半月时间才完成。

在这个项目中，使用了新技术就是一个风险：

造成的损失就是导致了进度延误；

发生延误的概率，如果项目开始前估算，大概在 60% 左右，项目进行中这个概率就上升到 80% 了。

像软件项目中这样的风险，如果发生后就会变成问题，问题如果没有及时解决，就会影响到项目计划。如果我们能有效的对风险加以识别，加以管理，就能减少对项目的负面影响。

风险管理就是指在项目进行过程中，识别可能的风险，对风险进行评估，并加以监控，从而减少风险对项目的负面影响。

风险管理重要吗？

对于风险管理，现在软件项目中提起的不多，应用的更少。

主要原因还在于大家都比较乐观，多少有一定侥幸心理，都觉得自己不会运气那么差，风险事件正好就发生了。还有就是因为如果要做风险管理，需要额外多做一些工作。

这就跟你买保险的道理一样，风险事件没发生，相当于你买保险的钱白花了，但是一旦发生了，你才知道买保险的钱花的值得。

对软件项目风险的管理，才是体现项目管理水平的地方。我们对比下面几种应对风险的层次来看：

被动应对：风险已经发生，造成了问题才被动应对；

有备无患：事先制定好风险发生后的补救方案，但没有任何防范措施；

防患未然：对可能的风险做出防范，并把风险防范作为项目任务的一部分。

哪一种层次更体现项目管理水平，相信你心中已经有了答案。

拿前不久发生的拼多多被“薅羊毛”事件来举例。

2019 年 1 月 20 日凌晨，拼多多平台出现系统漏洞，用户可以领取 100 元无门槛券。因此开始出现大批用户借此“薅羊毛”，利用无门槛券下单虚拟商品，例如充话费或 Q 币等等，并一度有消息传出，拼多多将因此损失 200 亿元。

这个事件追究原因的话，当然可以说是开发代码没写好，可以说是测试没测好，也可以说运维没有监控好。

但另一个角度讲，如果这个项目的负责人有一点风险意识，做了风险管理，即使出现这样的问题，损失也一定不会这么大。

如何做好风险管理？

培养风险意识

风险管理其实最大的问题不是如何做，而是项目成员缺少风险意识，有了风险意识，才能去识别出来项目中可能的风险，进而去管理风险。

对于培养风险意识，我的经验就是：项目中的任务，不能盲目乐观，都思考一下它最坏的结果是什么，如果最坏的结果不能接受，就说明要有个 B 计划，考虑风险管理了。

比如说拼多多的无门槛券，最坏的情况是被无限刷，造成巨额经济损失，那这种结果是不能接受的，就需要考虑风险管理。

如何对风险进行管理？

软件项目风险管理，通常分四步来做。

第一步：风险识别，识别可能的风险

风险识别，就是看项目中有哪些可能的风险，因为只有找出来有可能存在的风险，才会有后续的步骤。

识别风险这种事，经验很重要，因为大部分风险其实都是相似的。以前看 CSDN 总裁蒋涛发过一条微博，内容引发了很多人的共鸣，每一条无不应对着软件项目中的常见风险。

10 个项目死亡的信号：

第一版做太多功能 ;

太依赖新技术平台；

与公司另一个有份量的产品竞争；

团队人手不足；

复杂的问题，需要复杂的解法；

成员开始隐藏进度落后的事实和原因；

不断更改、增加的需求 ；

2.0 症候群 - 非要更大、更强、更美 ；

产品没有市场立足点；

你根本无法解决的大问题。

一个识别风险的方法叫检查表法，就是可以把类似于上面这些常见风险收集整理起来，分类列成清单，按照清单去检查对照。

软件项目的风险主要分成以下几类：

项目风险：项目预算、进度、用户和需求等方面的问题；

人员风险：人员离职、人手不足等问题；

技术风险：采用的技术所可能带来的风险；

商业风险：与市场、产品策略等有关的商业风险。

你也可以按照上面的分类整理出自己的风险检查表。

另外你还可以借助集体的智慧，定期针对风险问题开一些头脑风暴会议，一起发现可能的风险。另外，要有合适的渠道，让项目成员可以反馈可能发生的风险问题。

第二步：风险量化，对风险进行评估量化

在风险识别出来以后，需要从两个方面去评估：

发生的概率多大？

发生后，后果多严重？

对于概率大，后果严重的风险，需要高优先级重点考虑；对于概率不高但后果严重的问题也要考虑，不过优先级略低；对于概率高但后果不严重的风险事件，可以优先级很低或者不考虑；对于概率低后果不严重的，则可以不予考虑。

拿拼多多的“无门槛券”来说，这就属于一个高风险、高概率的事，需要重点考虑。

第三步：应对计划，对风险制定应对策略

在评估后，需要后续进一步考虑的，就要制定好应对的计划。针对风险，主要分成以下几个策略。

回避风险——更改导致风险的方案

回避风险很好理解，就是要对可能发生的风险，放弃或者修改导致风险的方案。这样就从根源上消除了风险，简单而彻底。

就像我前面举的因为使用 React 技术导致风险的例子，可以直接放弃使用 React，用回熟悉的 Angularjs 技术，这样就可以避免技术风险的发生。

但这种方案不一定适合所有情况，例如拼多多“无门槛券”的风险，就无法采用这种方案。

转移风险——将损失转嫁出去

以前玩大富翁游戏，最开心就是有一张“嫁祸卡”，万一遇到倒霉事就可以转嫁到其他玩家身上。转移风险也是这个思路，就是为了避免承担风险损失，将损失转嫁出去的方法。

日常生活中买保险就是一个例子，发生意外，保险公司会帮助赔付。

在软件项目中，举例来说，如果你的团队对于服务器管理不是很在行，有可能会遇到服务器宕机或数据库丢失数据等风险，就可以考虑购买云服务，这样云服务商会帮你解决服务器宕机或数据库丢失的问题，而且万一宕机或丢数据了他们也会承担一定的责任。

缓解风险——降低风险发生概率或减少可能造成的损失

缓解风险就是在风险发生前采取一定措施，降低风险发生的概率，或者减少风险可能造成的损失。

比如你要担心数据库数据丢失的风险，就需要定期备份数据库；比如你担心核心成员要离职，那就涨点工资，避免人才流失。

拼多多的无门槛券也是个典型的例子，如果对券的消费增加一定的限制，比如说每个用户有领取的上限，一个月不能超过 100 张，每张券不超过 10 元，不能用于购买 Q 币手机话费等虚拟物品，这样即使出现问题，也不会造成很大损失。

我们在《04 | 瀑布模型之外，还有哪些开发模型？》中学到了“螺旋模型”，也是这种策略：每个迭代都要做一下风险评估，再决定项目是不是继续。

接受风险——明知山有虎偏向虎山行

还有一些风险本身很难避免，或者去应对这个风险的成本超过风险发生后造成的损失，那么就没必要应对，直接选择承担风险后果就好了。

比如说前面说的采用新技术 React 导致进度延迟的案例，这个风险虽然有很大概率发生，但是进度延迟的影响可以接受，并且让团队今后在技术栈上多了新的选择，长远来看对项目更有利，那么这个风险就是可以接受的，还是可以继续做下去。

回避风险、转移风险、缓解风险、接受风险，以上就是针对风险提前准备的一些应对策略，实际项目中，可以根据实际情况来灵活运用以上策略，有效应对风险，减少可能损失。

第四步：风险监控，对风险进行监控预警

风险在没发生的时候并不会变成问题也不会造成损失，如果风险可以监控，可以预知风险即将发生，或者可以在风险发生后，第一时间知道，那么就可以马上对风险进行干预，避免变成更大的问题。

要做好监控，第一要能对监控的内容量化，第二要设置阈值，第三就是要有后续的报警和处理机制。

很多公司都已经建立了自己的监控系统，将关键数值量化，并设置阈值，超过阈值后自动触发报警机制。

一个简单的例子就是服务器宕机了，监控系统发现机器没响应了，自动通过邮件、短信、电话等方式通知正在值班的人员。

还有稍复杂一点的方式，像网络服务，可以监控每一次请求结果的状态码，统计请求的成功率。如果单位时间内，服务出错的比例低于阈值，那说明服务是正常的；如果错误比例超过阈值，那说明是出现了问题，需要报警通知相关人员，马上处理。

再回到拼多多“无门槛优惠券”的例子，其实有很多数据可以监控到，比如说单位时间内优惠券使用数量，商品销售数量等，如果结合一些可视化视图，应该可以直观的看到当时有大量虚拟商品销售，大量的优惠款被使用。

如果针对优惠款设置了阈值，例如每分钟使用超过 1000 张就触发报警，那么也可以及时发现无限刷的问题，避免造成更大损失。

以上四个风险管理的步骤是一个连续循环的过程，在整个项目期间，都要持续的对风险进行识别，对风险量化，对于风险采取应对计划，对风险进行监控。

(注：项目风险管理过程)

总结

今天带你一起学习了软件项目管理中的风险管理知识。软件项目中的风险就是指那些不确定的但是可能会造成消极影响的事件，通过对风险的管理，可以有效降低风险发生的概率，减少风险发生后的损失。

软件项目风险管理包括风险识别、风险量化、应对计划和风险监控四个过程，这四个过程是一个循环的过程，需要在项目中持续进行。

希望你在学习后，能提高风险意识，不能盲目乐观，凡事都应该有 B 计划。并且能将学到的风险管理知识应用到项目中，做到对可能的风险了然于胸，未雨绸缪，运筹帷幄。

课后思考

按照风险管理的知识，可以对你目前项目中，按照发生概率或造成后果列出 1-3 条风险，并思考如何能管理这些风险。欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

16 | 为什么你不爱写项目文档？

你好，我是宝玉，我今天分享的主题是：为什么你不爱写项目文档？

我以前看过一个投票，盘点程序员不喜欢的事，有两条和文档相关：

不喜欢写文档；
不喜欢项目文档太少。

看起来很矛盾，却很现实。基本上大家都认同：“项目文档很重要”，然而我们在项目中总是短期高估文档的重要性，而长期低估文档的重要性。

结果就是口号喊的很响：要重视文档、要写好文档、要多写文档，然而随着项目的推进，总有比文档优先级更重要的任务，文档的优先级总是被有意无意推迟，导致项目的文档缺失、老旧、无人维护。

那么为什么程序员都不爱写文档呢？我总结了一下大概有下面这些原因。

不知道怎么写

不知道怎么写文档的应该占很大一部分比例。

太忙没时间写或者懒得写

程序员确实很忙，但总有不那么忙的时候，却也很少见有人利用这时间去写文档。包括我自己也这样，有时候没那么忙的时候，宁可去想想怎么重构下代码，却很少会愿意去写文档，主要还是太懒。

敏捷开发所以不用写文档？

对于这个问题，我其实反驳过多次，敏捷宣言最后一句话明确指出：“尽管右项有其价值，我们更重视左项的价值。”也就是说敏捷从来没有否认文档的价值，只是更重视“工作的软件”罢了。

为什么要写文档？

写文档，其实对个人、对项目、对团队，都是非常重要的事情。

帮助写文档的人理清楚思路

我想你应该有这样的感受：写作的过程，就是一个思考的过程。

写文档，可以让你在写代码之前，梳理清楚思路，想清楚整体结构，比如说有哪些工作是重点难点；哪些要依赖其他人，需要及早协商的；哪些是要考虑安全性的。

如果上手就写代码，就很容易陷入到某个技术细节中，而忽略了整体结构。写的时候才发现一个技术难点无法解决，或者已经在某个不重要的细节上浪费了很多时间；或是发现有些依赖其他人提供的服务还没准备好；又或者是上线后才发现有安全漏洞。

先写文档，就会抛开代码细节，去站在全局思考。写的时候，各个模块之间的依赖关系、各种可能的安全隐患、各种可能需要其他人配合的地方，就都冒出来了，必须要去查资料，去找人讨论，反复缜密的思考后最终写出来。

有人觉得自己写作不行，所以不会写文档。写作不行，只是让你在用词遣句上会有所欠缺，而这不是写文档的真正障碍。

真正的障碍是没想清楚，在心中只有一些未成型的混乱的想法和概念，必须要努力把这些模糊的想法确定化和具体化，才能写出来。

换个角度来说，如果你连文档都写不出来，那又怎么能指望代码写得好呢？

便于未来的维护和交接

“好记性不如烂笔头”，存在脑子里的内容是不可靠的，一个正常的项目组，如果需要长期维护，就需要一定的文档，把设计、操作流程、环境配置等内容记录下来，而不仅仅依赖于口口相传。

我有一个习惯，每到一个新项目组，就会把日常工作中遇到的问题、各种环境配置、一些操作的步骤等，所有以后可能还会用上的都记录下来，其中一些还会整理到团队的 WIKI 上。

一段时间后，这些随手记下来内容都会发挥大作用，对于我来说，很多问题就不需要问第二遍了。对于团队来说，随着人员更替，我记录的这些内容都是最好的一手资料，有新人过来，按照我当初记录的内容，就可以快速上手。

团队更好的协作沟通

在一个项目组中，大家都有不同的分工，有人负责产品设计，有人负责架构设计，有人负责测试。而文档，就成为了团队成员很好的沟通工具。

比如说产品设计有雏型的时候，会有一个产品设计的评审会议，基于文档，项目成员可以一起参与其中，提出自己的意见和看法。这样就不至于等到产品设计出来之后，大家才对于设计有改进想法或意见，造成无法更改的结果。

当然，写文档还有很多好处，在这里我就不一一列举了。

如何写好文档？

其实文档的重要性真不用多说，很多人也不是不爱写项目文档，而是不知道该如何写好文档。所以在这里我来介绍一下该如何写软件项目文档。

很多人对于写文档是有心理压力，觉得自己写作水平不高，不知道该如何下手。首先你要对文档有一个正确的认识：文档写作，关键是通过文档把你的想法表达出来，至于用词、格式相对都是其次的。

打个比方，我们如果是大厨给餐馆做菜，得追求个宽油大火、色香味俱全，自己在家做饭，就没那么多讲究了，填饱肚子是第一要素，在这个基础上味道好一点就很好了。

我们写文档就像是在家做饭，是不需要追求太多华丽的词藻，也不需要追求字数，只要用简单的文字、图表把想法表达出来，最终在讲解的时候，配合一些口头说明就可以啦，其实比我们上学时写作文容易多了。

下面给你介绍一些具体可行的文档写作方式。

1. 从模仿开始

前面有提到，我其实一开始是不知道如何写文档的，直到毕业两年后，我在飞信项目组，领导让我写一个新项目的技术方案文档，我两眼一抹黑说不会写呀，然后领导给了我另一个项目的技术方案文档，说你就“照葫芦画瓢”照着写吧！

“依葫芦画瓢”就简单多了，同时又学习了一下如何画线框图、时序图等图形，很快就完成了一份技术方案文档，再反复修改几次，质量就还可以了。

后来我带团队时，让团队成员写文档，就把当时我写的文档给他们参考，很快他们也能写了。包括后来我写开源项目（angular-ui-tree, react-video），要写英文文档，也是去找了几个同类的开源项目的文档，参照他们的内容和格式，就把文档拼出来了。

模仿就是最好的写文档方式，尤其是现在网上可以参考的例子也很多，当你写文档不知道该如何下手的时候，不妨去找一个类似的文档，模仿着写试试。

2. 从小文档开始

一开始写文档，内容不需要很多，可以从小的文档开始。就像前面我提到的，记一些笔记，不要在意格式，一两句话，一些截图，就是不错的笔记。

有一次和同事一起去开会，会上他给另一个组的人介绍了如何调用一个服务，介绍的很详细。我就建议他把刚才介绍的内容写成个小文档，这样下次再有类似会议就可以基于文档来说。

于是他就整理了一个简单的文档，再为别人讲解的时候就基于文档介绍，容易很多。同时，他每次还会再完善一点内容进去。之后再有同类问题时，他直接把文档发给人家就好了，都不需要再专门开会。

项目中很多文档都可以从这样小的内容开始：别人给你讲一个问题的时候记录下来；你给别人讲一个问题的时候记录下来；解决一个技术难题时记录下来方案……

这些记录下来的笔记，稍加整理，就可以是很不错的项目文档。

3. 从粗到细，迭代更新

小时候写作文，老师给的最多的建议就是要列提纲，这个建议我小时候当耳边风没怎么听，后来要写项目文档的时候用起来反倒觉得非常实用。

我写一个大一点的文档，都是从脑图开始的，先基于脑图，把基本结构梳理清楚。然后第二步就是写 PPT，PPT 有个好处就是不用太多文字，列个一二三，画几张图，就是个简单的文档，PPT 还有个好处就是可以用来给别人讲解，收集反馈。

写完 PPT，也收集好了反馈，再写正式的文档。先按照脑图列的提纲把主要章节放上去，然后把 PPT 上的内容和画的图放到文档中，一篇文档的骨架就搭好了，剩下的就是对细节的补充了。

为什么我不一开始就写很细的文档呢？

一个原因是太难写，要花很多时间精力，甚至可能写不下去；另一个原因就是在收集反馈的过程中，会有很多修改。写得越细则无用功越多，最后，你甚至会因为不想改文档而抵触不同的意见。

而从粗到细逐步迭代的方式就好多了，一开始的目的是为了梳理清楚思路，只要脑图这种级别的内容就好了，然后进行调整。因为文档很粗，调整也方便，等到基本确定后再写细节，就不会有大的反复。

4. 一些基本的画图的技巧

有人说：“字不如表，表不如图，一图胜千言”。这个观点我非常认同，好的图能帮助你简单而直观地把问题说明清楚。

画图其实不复杂，不需要多专业的绘画技巧，也有很多工具软件可以帮助我们简化操作，像 Visio、PowerPoint、Keynote、OmniGraffle 等都是很好的画图软件。平时看到好的图也要注意收集整理，以后自己写的时候，也可以直接参考，可以帮你少走弯路。

写文档的时候，主要有几种图比较常用：线框图、流程图、时序图、各种格式的截图。

线框图

线框图是最常用也最实用的一种图形，用简单的方框代替功能、模块、服务等，再用箭头表示关系或者数据流向，非常简单直接。

要画好线框图并不难，主要是要理清楚有哪些模块，以及模块之间的关系是什么。用方框配上文字表示模块，方框之间的连线和箭头表示关系。

看几个例子：

例：Twitter 当年的缓存方案。

（图片来源：InfoQ）

例：Netflix 的账单系统架构图。

(图片来源：Netflix 技术博客)

流程图

流程图是软件项目文档中一种常用图形，可以方便的表示各种不同条件下的逻辑路径。要画好流程图不难，重点是要理清楚逻辑关系，各个关键节点在不同条件下的走向。

例：重置密码流程图。

时序图

时序图也是软件项目所特有的一种图形，可以表示不同对象之间发送消息的时间顺序，尤其在涉及网络通信的文档中特别常用。

画好时序图，关键是要列清楚所有涉及的对象或者服务，以及消息发送的先后顺序。

例：注销登录过程的时序图。

各种格式截图

截图也是个非常简单直接的方式，把软件的 UI、交互设计的效果、数据趋势图、数据统计图等直接截图，必要的话配上一些箭头、文字，也可以很好的说明清楚问题。尤其是产品设计文档，经常用到。

上面就是如何写文档的一些具体建议，按照上面说的方法做，写好项目文档不会是多难的事情，你还可以在日后的工作中，不断学习不断改进。

一些关于文档的其他建议

有时候我也看到一些比较极端的情况，就是过于追求文档，项目中要花大量的时间写文档，而很多文档是形式化的，并没有太大意义，可能写完了不会用来讨论，也不会有人看。

所以我是比较认同敏捷宣言的观点的：文档很重要，但是工作的软件高于详尽的文档。这里面的平衡很重要。

不需要为代码写很多文档，好的代码格式，良好的注释、完善的单元测试可以很大程度上代替针对代码而写的文档。

Markdown 是一种非常好的文档格式，可以让你更专注于内容上，而不是文档格式上面。

在线文档工具优于离线文档工具，在线文档有很好的版本管理，也更方便多人协作。像 Github WIKI、石墨文档、Google Docs、Evernote 等都是非常好的在线文档工具。

对于文档的撰写，要作为一个正规的项目任务进行，安排人、安排时间，放到项目计划中去。就像前面说的“懒得写”文档的情况，一旦把文档当成一个与开发同等重要的任务去执行，就没有借口去犯懒了。

重要的是，文档的写作一样需要多练习，写的越多，就越熟练。

总结

今天，带你一起分析了为什么不爱写项目文档的原因，也解释了为什么写文档很重要。

没时间写或者懒，不能成为不写文档的理由。对于重要的项目文档，就应该加入到日常的开发任务中，把写文档，摆在和设计、开发同等重要的位置。从某种角度来说，写不好文档，代码也很难写好。

针对程序员不爱写项目文档的情况，我也提出了切实可行的写文档的方法。比如说不会写，就可以从模仿别人写的文档开始，然后从粗到细，不断迭代，配合一些图表，就可以写出不错的项目文档。

课后思考

你所在的项目组，项目文档情况如何？你写文档吗？有没有什么写文档的经验和大家一起分享的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

17 | 需求分析到底要分析什么？怎么分析？

你好，我是宝玉，我今天想与你分享的主题是“需求分析”。

通过前面的学习，我们知道在瀑布模型中，第二个阶段就是需求分析阶段，同时需求分析的结果也决定了后续的系统设计、开发、测试等阶段能否顺利如期进行。即使是用敏捷开发，同样也少不了对需求的分析整理。

可以说需求就是整个产品的源头，所以需求分析的结果往往决定了产品的成败。如果没有正确把握客户需求，可能就会一步错，步步错！

就像我在《特别放送 | 从软件工程的角度解读任正非的新年公开信》提到的秋千的案例：

客户想要一个给三个孩子玩的秋千；产品经理以为就是一个板子加两绳子就行；架构师发现除非把树截开，否则秋千没法荡起来的；程序员以为用绳子和板子连一起就完事了；而真正满足客户需求的，也就只要在绳子上挂个轮胎而已！

所以在本篇文章中，我将带你去了解：需求分析到底要分析什么？以及我们怎么样才能做好需求分析，抓住用户的真实需求，做出来客户想要的软件产品，避免失败或浪费。

什么是需求？

我们日常在项目中，经常会听到“需求”这个词，比如说：

项目经理对产品经理说：用户给我们提了一个需求，想要一个给三个孩子玩的秋千，你分析一下；

产品经理对架构师说：我们现在有一个需求，在树上栓两绳子，再吊一块板子，你做一下设计。

很明显，这两个需求的意思不一样，前面这个需求是用户需求，后面这个需求是产品需求。

用户需求是由用户提出来的，期望满足自身一定需要的要求，例如用户说：“想要一个给三个孩子玩的秋千。”这种原始的用户需求通常是不能直接做成产品的，需要对其进行分析提炼，最终形成产品需求。

产品需求就是在分析提炼用户真实需求后，提出的符合产品定位的解决方案。就像上面“在树上栓两绳子，再吊一块板子”，就是产品经理针对用户需求提出的解决方案。

需求分析是要分析什么？

其实对用户需求的分析，不是一个动作，而是一个过程。需求分析，就是对用户需求进行提炼分析，最终形成产品需求的过程。

而针对每个用户需求的需求分析过程，需要经过三个步骤。

第一步：挖掘真实需求

大部分用户提的需求，都不见得是其真实的需求，需要透过现象看本质，去挖掘其背后真实的需求。就像福特汽车创始人亨利福特说过的：

如果我最初是问消费者他们想要什么，他们应该是会告诉我，“要一辆更快的马车！”

这里“要一辆更快的马车”就是一个典型的用户需求，但这并非是用户的真实需求，用户的真实需求，需要通过分析才能得到。

要分析用户的真实需求，可以从三个角度入手。

目标用户：用户不同，诉求也不一样；

使用场景：使用场景不一样，解决方案也会有所不同；

想要解决的问题：用户背后想要解决的问题是什么。

我们假设目标用户是普通乘客，使用场景是日常出行，那么用户想要解决的问题其实并不简单是“要一辆更快的马车”，想要更快的马车只是用户自己能想到的解决方案，而他想解决的问题是“更快更舒适的出行方式”。

而现实项目中，大多数人需求分析的不正确，就是因为没有挖掘出用户的真实需求。

我们再看之前的秋千项目，目标用户是三个孩子，使用场景是一起户外玩耍，想解决的问题其实是能有一起玩的娱乐设施。

第二步：提出解决方案

我们知道了目标用户，其使用场景和想要解决的问题，就可以结合产品定位，提出相应的解决方案。

比如针对想要“更快更舒适的出行方式”日常出行的乘客，我们就可以提出汽车的解决方案，而不一定要局限于马车，汽车能更好的满足用户需求。

针对三个孩子想有一个在户外一起玩的娱乐设施这个需求，我们可以提供一个轮胎式的秋千，就可以很好的满足他们的需求，我们甚至可以建一个小型游乐园。

第三步：筛选和验证方案

在提出方案后，我们需要对方案进行筛选，比如对于秋千项目，建小型游乐园的方案虽然能满足需求，但是成本太高，需要排除掉。

在选好方案后，还需要对方案进行验证，以确保方案能解决用户需求。

在传统瀑布模型中，选定方案后，会写成产品设计文档，走相应的评审流程，评审完成后再进行设计、开发和测试，测试完成后会让客户再进行验收。而敏捷开发，在整个开发过程中，每个迭代或者关键的里程碑，也一样需要客户进行验收。

通过以上三步，就可以对用户需求进行提炼分析，最终形成产品需求。

所以在需求分析过程中，分析的就是一个个用户的需求，找出背后的真实诉求，再有针对性的提出解决方案。

对于解决方案，要进行筛选和验证，有些不可行的用户需求不会变成产品需求，可行的用户需求会按照优先级进入实施阶段，最终变成产品。

怎样做需求分析？

前面我介绍了对单个用户需求的分析，主要经过三个步骤：

第一步：挖掘真实需求；

第二步：提出解决方案；

第三步：筛选和验证方案。

而软件项目的用户需求，从来就不是单一的，而是一系列需求，所以对于软件项目的需求分析，还需要增加收集整理的步骤。整个过程是迭代进行的，如下所示：

收集需求：对用户需求进行收集整理；

分析需求：对需求进行分析，挖掘用户真实需求；

需求评估：筛选过滤掉不可行的需求；

需求设计：针对用户需求提出解决方案，设计成产品方案；

验证需求：验证方案是否可行。

我在美国 DePaul 大学读书的时候，曾在学校兼职，当时接到一个项目，要为计算机学院的网络教学系统做一个网页版的播放器。

我们知道现在的课堂里面，老师上课的时候，会用电脑放 PPT 或者课件，同时还要在黑板（也有的是白板）上写写画画辅助说明。

DePaul 大学的网络教学系统，就是在老师上课的时候，用摄像头把老师讲课的整个过程都录制成视频，同时也会用特殊的软件，把当时电脑屏幕上显示的内容，和白板上写的内容，都录制下来。

这样选网络课程的同学可以通过网络直接观看，既不会漏了老师讲的内容，也不会错过老师在电脑上放的和白板上书写的内容。播放器要做的就是要播放录制的教学视频、电脑屏幕和白板。

我将以这个项目为例，讲讲如何做需求分析。

1. 收集需求

这个项目的原始需求是老师给我的，只是告诉我要做这样一个播放器，让学生能看教学内容。而这个需求还不够，我还需要继续收集用户需求。

收集用户需求有很多方法，这里列举部分：

头脑风暴：就是大家一起开会头脑风暴讨论；

用户调研：通过调查问卷或者访谈，通过问用户一些问题收集反馈；

竞品分析：通过分析其他同类产品的功能获得需求；

快速原型：通过原型来收集反馈，收集确认需求。

拿播放器的项目来说，头脑风暴没有足够的项目成员，也没有同类产品可以做竞品分析，做原型的话，成本有点高，所以用户调研就是最适合的收集需求的方法。它不仅简单，而且能收集到真实的用户反馈。于是我通过微信群、邮件、用户访谈从老师、领导和学生那分别收集了一些反馈。

老师们没有什么有效反馈，因为他们基本不需要用到这个软件，领导们有个需求就是希望能播放字幕，而很多学生希望能有 2 倍速快进功能。

2. 分析需求

收集了需求，就要分析用户的真实需求，这是最难的部分，也是最体现产品经理需求分析水平的地方。

用户需求背后的真实需求有三个层次：

表层需求：用户对解决问题的期望，例如马车更快；

深层需求：用户的深层次动机，诉求产生的原因，例如乘客对出行速度的要求；

底层需求：人性本能的需求，例如对安全感对舒适的追求。

要分析好用户需求背后的真实需求，就是要结合“目标用户”和“使用场景”，按照上面三个层次去思考。

我们拿刚才播放器为例，目标用户是学生，使用场景是学校机房或者家里，希望解决以下问题。

字幕的问题：

表层需求：显示字幕；

深层需求：语言不好，跟不上老师节奏；

底层需求：聋哑学生无法听到声音，只能通过字幕学习。

快进的问题：

表层需求：能快进播放；

深层需求：可以节约学习的时间，提高效率；

底层需求：取得好的学习成绩

经过这么一分析，基本上就对于用户的真实需求心里有数了。

3. 需求评估

需求收集分析完了后，还需要进一步评估，以决定做还是不做，优先级如何，先做哪些再做哪些。

需求评估考虑的因素有：

可行性：技术能否实现；

成本：人力成本、时间成本；

商业风险和收益：有没有商业上的风险，收益是否合理；

紧急性与重要性：是不是用户迫切的需求。

如果确定可行，还需要评估其优先级。评估优先级一个简单的方案就是用“紧急重要四象限”的方法来区分：

复杂一点的有 KANO 模型，如下图所示。

红色曲线，是用户认为必须要有的功能；

绿色曲线，就是用户明确提出的需求；

黄色曲线，属于兴奋型需求，就是用户自己没想到，超出预期的功能。

回到我们播放器的例子：

红色曲线（必须要有的功能）：能播放视频、播放电脑屏幕，播放白板；

绿色曲线（用户明确提出的功能）：字幕、2 倍速快进；

黄色曲线（超出预期功能）：10 秒快进、10 秒快退、在时间轴上记录笔记。

4. 需求设计

在分析和评估完需求后，还需要提出解决方案，也就是对需求进行设计，做出来有效的产品设计方案。最终的产品设计，会落实到人机交互上面，用户可以通过软件界面交互。

现在产品设计方面，各个平台都有一套比较成熟的界面标准控件，大部分产品设计都可以基于标准界面控件，组合成满足需求的用户界面，在满足功能的前提尽可能做的易用和美观。

在需求设计的时候，可以用草图、原型设计工具、界面设计工具进行设计。

在需求设计阶段，可以参考其他成熟的产品。比如我在设计播放器时，也是通过借鉴其他软件的设计来完成的，比如说向 Youtube 借鉴了视频播放器的设计，向 Skype 的电话会议系统借鉴了其播放区域切换的交互，最终完成了产品设计。

5. 验证需求

在需求设计好后，还需要进行验证，看解决方案是否能满足用户的需求。

对需求的验证方式其实是贯穿整个软件项目生命周期的，在需求分析阶段，会反复验证确认设计好的需求是否满足用户的真实需求，例如各种设计评审。

在产品开发完成后，也需要有需求的验收，以确保开发出来的软件产品是客户想要的，满足客户需求的。

现在很多互联网产品，还有一种基于数据的验证需求方式，也就是 A/B 测试。

设计好一个功能上线后，并不直接让所有用户使用，而是先给一小部分用户使用，然后分析数据，看使用这个功能的用户群和不使用这个功能的用户群，在营收、访问量、活跃度等关键数据上是更好还是更坏。如果好，就加大比例，如果数据不好，可能就会调整甚至取消这个功能。

我在设计播放器的时候，首先用 PPT 做了一个简单的草图，拿去给老师确认，收集一些反馈后，写了一个 PC 版的软件原型，拿给一部分同学试用。在收集反馈后，做了一些修改和调整，最终确认了产品的设计。

在需求分析完成后，就可以基于需求分析形成的文档，进行设计和开发了。（DePaul 大学的网络教学系统产品演示）

总结

今天带你一起学习了软件工程中一个非常重要的知识点：需求分析。

需求分析，就是一个将用户需求变成产品需求的过程。要做好用户需求的分析，需要找出来隐藏在用户需求背后的真实需求，还要针对用户的真实需求提出解决方案，最终验证方案是不是能满足好用户需求。

需求是整个产品的源头，很多软件项目失败的原因就在于没有做好需求分析，软件中很多浪费也来源于需求没想清楚导致的返工。做好需求分析对于软件项目来说非常的重要。

要做好软件项目的需求分析，需要做好需求的收集整理工作，然后对收集好的需求进行科学的分析，评估是不是可行以及划分优先级，对可行的需求项进行设计，最后还要验证设计出来的结果是不是满足需求。

希望你通过这节课的学习，能科学地运用好需求分析的知识，对项目的需求分析把好关，保证最终产品能满足用户需求，超出用户预期。

课后思考

你工作中有没有遇到因为需求分析没做好导致项目失败的案例？你了解 AB 测试吗，有在项目中用过 AB 测试吗？极客时间的课程为什么要支持音频？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

18 | 原型设计：如何用最小的代价完成产品特性？

你好，我是宝玉，我今天想与你分享的主题是“原型设计”。

我们都知道，软件项目中很多问题都和需求相关，比如说需求不明确，需求变更。这些问题轻则导致返工造成浪费，重则导致项目失败带来巨大损失。所以在软件工程中，搞明白需求是一件至关重要的事。

上一篇我带你学习了如何分析需求的方法，而分析需求，同样也离不开工具的支持。所以这一篇，我将带你学习需求分析中原型设计的用法，借助原型设计，用最小的代价完成产品特性。

什么是原型设计？

对于原型设计，很多程序员可能比较陌生，但是对于产品经理来说，原型设计却是日常工作中最常用的技能之一。因为原型设计，是产品经理确认需求、设计产品最重要的沟通工具。

其实最早的原型设计，并不是作为一个需求沟通工具存在的。

原型设计的发展历史

早在上世纪 70 年代，在瀑布模型提出后，很大程度上改进了软件项目的开发。但是需求不明确、需求多变的问题从那时候开始就是一大难题。

《人月神话》的作者弗雷德里克·布鲁克斯（Frederick P. Brooks, Jr.）在《没有银弹 - 软件工程中的根本和次要问题》中第一次提出了：“在获取和制订软件需求时，将快速原型开发作为迭代计划的一部分”。

后来快速原型就逐步发展成为一个开发模型，叫快速原型模型，我在《04 | 瀑布模型之外，还有哪些开发模型？》这一篇里也有介绍。这种模型的特点就是快速开发，快速修改。目的是为了解决客户的需求不明确和需求多变的问题。

注意，这里的快速原型模型，是开发软件项目的一种模式，还不是工具。

给你举个例子，如果用快速原型模型开发网站，大概分三个阶段。

第一个阶段就是纯静态 HTML 页面，能看到页面什么样子，没有后台，无法保存数据。这种静态页面开发成本不高，速度很快，改起来也方便，做好了就可以拿去跟客户确认需求。

客户看了后就能知道产品长什么样子，是不是满足要求，会提出明确的反馈意见。根据反馈，开发方会继续修改静态页面。

第二个阶段就是模拟一个后台服务，没有数据库，数据直接保存内存中。但是可以让网站有真正的交互了：从网站添加内容就能显示出来，修改网站内容，网站显示的内容也会跟着修改。

这个阶段客户可以在网站上体验交互，也能完整的体验操作的流程，可以进一步针对交互再提出反馈，开发方根据反馈继续修改。

第三个阶段就是完成最终的后台服务，接入真正的数据库或者其他后台服务，完成整个网站的开发。由于前面两个阶段，产品经理已经把需求和交互确认清楚，所以这个阶段的开发，就没有太多需求上的反复和修改，可以高效的完成设计和开发。

简单来说快速原型模型就是，第一阶段确认界面布局和内容，第二阶段确认交互，第三阶段实现。

通过快速原型模型来开发，可以低成本、快速地确认好需求。但也有一个问题：整个过程单靠产品经理是无法完成的，必须要有开发人员配合才能完成。而对产品经理来说，要开发人员配合还是一件高成本的事情。

低保真原型设计

于是有产品经理用线框图来代替第一阶段。线框图画起来简单，纸和笔就可以，展示效果不错，通过线框图可以直观地看到界面上有什么，布局是什么样的，一样可以用来和客户确认需求。

（图片来源：WikiPedia）

线框图简单方便，可以起到沟通需求的效果。但缺点也很明显，就是看起来不够真实，不方便反映界面之间的关系，另外也不能反映界面交互。所以线框图这种模式也叫低保真原型。

中等保真原型设计

再后来就有像 Axure 这样专业的原型设计软件产生，不仅可以反映界面上的布局和内容，还可以展示网站的整体结构和交互。也就是说，借助原型设计工具，可以达到前面快速原型开发前两个阶段同等的效果。

（图片来源：我参与过的某项目原型设计截图）

这种原型设计，可以很好的用来确认需求和界面交互，虽然制作难度上比线框图要复杂一点，但是不需要开发人员介入，产品经理完全可以自己搞定。

但这样制作出来的原型，也不能做到 100% 真实，因为它在界面的真实度、色彩上要比最终产品差一些，所以也被称之为中等保真原型。

高保真原型设计

近些年移动端快速发展，对于移动端来说，因为界面比较小，布局和内容上已经没法玩出什么花样。所以客户更追求界面的美观和交互的炫酷，对原型的保真度要求也就越来越高。

所以很多原型工具就在高保真方面狠下功夫，让你简单操作就可以做出漂亮的界面和炫酷的交互，甚至完成后都不需要再做 UI 设计了。

当然，高保真原型的学习成本和制作成本都要高于低保真原型，所以变更成本更高，而且也很容易导致产品经理花大量时间在细节的调整上，影响整体的进度。所以通常高保真都会和低保真原型设计配合使用，先用低保真原型快速确认清楚需求，再用高保真原型确认最终的交互和 UI 设计。

就这样，原型设计从最开始的一种快速开发模式，逐步演进成了今天的原型设计工具。让产品经理不需要会编程知识，也可以做出很酷的软件原型，从而可以低成本、高效率的确认清楚产品需求。

怎么做好原型设计？

虽然说现在原型设计工具已经让制作原型越来越简单，但即便如此，原型设计也可以算得上是一个小项目了。因为要做好原型设计，不仅要考虑单个界面怎么设计，还要考虑这个产品整体有多少个界面，各个界面的关系和流程是什么样的。

要做好原型设计，可以借鉴《02 | 工程思维：把每件事都当作一个项目来推进》中讲的内容，用工程方法来完成。

参考工程方法，我们可以将每次原型设计过程分成四个部分：分析、设计、实施和验证。

这里，我以极客时间 App 为例，假设我们需要制作一个极客时间的 iPad 版，应该如何制作原型呢？

第一步：分析

在原型设计时，通常属于需求的最初阶段，需求还是很模糊、不具体的。所以这个阶段首先要做的，就是要对用户的需求有个初步的了解，分析清楚原型设计的目标是什么。

比如说，我们要设计极客时间的 iPad 版，其实在内容上，完全可以基于 iPhone 版本，只是在布局上，交互要重新设计，充分发挥 iPad 大屏幕的优势，展现更多有价值的信息。

第二步：设计

在对需求进行初步分析后，需要开始对原型进行整体设计。在设计阶段，主要从两个维度来考虑：

从信息架构的维度，考虑清楚整个产品的信息架构，划分出模块；

从使用流程的维度，考虑清楚界面之间的流程。

画产品的信息结构图

产品的信息结构，就像一本书的目录，整体描述了架构信息。

在做原型设计前，先梳理清楚整体结构，有助于帮你想清楚产品有哪些功能模块，模块之间的关系如何，哪些模块是公共的，哪些模块是面向不同用户显示不同内容的。

参考极客时间 iPhone 的结构，我们可以把主体的信息架构梳理出来，如下图所示：

在考虑清楚主体结构后，可以进一步细化。例如其中“讲堂”下面虽然分成了“专栏”、“视频课程”、“每日一课”和“微课”，但其实点进去都是一样的，我们可以称之为“课程模块”，对于课程模块，我们可以进一步细化。如下图所示：

这样你就可以一步步将整体信息结构从粗到细，一点点整理清楚。

画产品使用流程图

用户在使用产品时，会在不同的模块之间跳转，比如说你从极客时间进入到一个没订阅过的专栏，还可以点击订阅按钮进入订阅界面，订阅成功又可以返回专栏界面。

所以，需要用流程图把这些界面之间跳转的逻辑梳理清楚。在设计流程图的时候，要重点考虑用户的使用场景，结合使用场景设计好流程。

举例来说，如果当前用户进入到专栏首页，如果用户没有订阅，最重要的就是让用户可以方便的订阅，然后继续阅读；如果用户已经订阅，就没必要显示订阅相关内容，直接可以看到文章列表，选取想看的文章直接阅读。

需要注意，画产品使用流程图时，不仅要考虑正常使用的流程，也同时要考虑清楚异常的情况。比如说用户留言输入错误，网络失败，怎么处理？如果把用户辛辛苦苦输入的消息弄丢了，将会让用户体验大打折扣。

我们以进入专栏，阅读专栏文章这个流程为例，画一个简单的流程图，来说明各种不同情况下的流程和跳转关系。

通过这样的流程图，可以考虑清楚界面和界面之间的跳转逻辑。

第三步：实施

在设计好整体的信息架构和使用流程图后，就可以开始对每个界面画流程图了。

在具体到界面时，要优先考虑满足产品需求，然后是让界面好看好用。

比如说阅读专栏文章这个界面，在 iPhone 上，屏幕很小，显示的信息有限，到 iPad 上，有了更大的屏幕，就可以增加更多的内容。但是注意不能造成太多的信息干扰，要突出重点，增强体验。

所以我们可以保留 Tab 导航的设计，让用户可以随时回到一级界面，并且针对 iPad 界面特点，对位置做一些针对性调整。

（该原型由墨刀制作完成，以下同）

另外在专栏阅读时，一个常见的场景就是看完一篇后，想切换到目录查看其他文章。可以针对 iPad 的交互特点，点击显示目录，方便在文章之间切换跳转。

这样一个界面基本上就初步完成了。接下来还可以对一些可以点击的界面元素，例如按钮，增加跳转操作，按照前面产品使用流程图的设计，将界面之间连接起来，让用户可以方便的从一个界面跳转到另一个界面。

第四步：验证

原型设计完成后，还需要一个很重要的环节就是验证，产品经理自己反复验证几遍，如果发现有流程上走不通或者使用不方便的地方先自己调整。调整好了交给其他人去体验，让他们提出反馈意见。

一般在正式的项目中，针对原型设计，需要有相应的评审会议，让大家提出反馈，根据反馈再作出调整。

比如说我前面设计的文章阅读界面，在交给朋友体验后，他给我的建议是：这样的设计看起来文章之间跳转方便了，但是要返回专栏页面反而是不方便了。而相对来说，返回专栏是一个更常见的操作。所以对竖版界面来说，这样的设计可能会让用户不知道如何回去，不如还是改回传统的返回式导航。

我觉得他说的很有道理，所以最终界面变成了这样：

而这样的调整，在原型设计工具中，几分钟就完成了，非常小的代价就完成了一个产品设计的确认。

经过分析、设计、实施、验证这四个阶段，再反复的修改和确认几次，基本上就可以做出来不错的原型设计了。

如何选择合适的原型设计工具？

原型设计工具，选择非常多。我建议你选择的时候，可以从几个维度考虑：

面向的平台：Web、桌面、手机；

保真度：中等保真度还是高保真度；

功能：是否满足你的要求；

成本：价钱是否可以接受。

这里推荐几款主要的原型设计工具，供参考。

Axure RP：Axure RP 曾一度是原型设计工具的代名词，历史悠久功能强大，可以制作网站、桌面软件、移动 App 的原型。 缺点是专业度较高，价格高。

墨刀：墨刀 是一款优秀的国产原型设计工具，可以制作网站、桌面软件、移动 App 的原型。上手相对容易，价钱也较 Axure 便宜很多。

Adobe XD：Adobe XD 是 Adebe 出的一款设计兼原型设计工具，可以制作出高保真原型，对于设计师尤其容易上手。

ProtoPie：ProtoPie 是一款高保真原型设计工具，不需要编程基础，可以做出逼真强大的交互效果。

Framer X：Framer X是一款高保真的原型设计工具，功能很强大，但是需要一定的编程基础，尤其适合程序员使用。

关于原型设计工具更多的资料，可以到“人人都是产品经理”网站的原型设计分类下，可以找到很多有价值的资料。

总结

今天带你一起了解了原型开发的演变历史。原型开发，从一个软件开发模型，逐步演变成了一个需求设计工具，让产品经理不用依赖程序员就可以作出逼真的产品原型，也大大降低了项目成员了解需求的难度。

原型设计，让产品经理可以用最小的代价完成产品特性，逐步成为产品经理确认需求、设计产品最重要的沟通工具。原型设计工具有很多可以选择的，建议从面向的平台、保真度、功能和价格等多方面因素综合考虑。

要做好原型设计，可以结合工程方法，分成四个阶段：分析、设计、实施和验证。

分析阶段，搞清楚用户的需求，原型设计的目标；

设计阶段，划分好产品的信息架构，设计好产品操作的流程；

实施阶段，按照设计的结果，对每个界面制作原型，并做好界面之间的链接；

验证阶段，和项目成员、客户进行确认，收集意见反馈，根据反馈进行修改。

如果你的项目还没有把原型设计作为确认需求、设计产品的沟通工具，可以考虑推广应用起来，不仅上手容易，而且可以帮你降低确认清楚需求的成本。

如果你打算做自己的产品，先不要着急动手写代码，不妨先做一个原型出来。

课后思考

如果你给极客时间设计一个 iPad 原型，你打算做成什么样？你所在项目中，在做产品设计时，会用到原型设计工具吗？用的是什么设计工具？你对原型设计是怎么看的呢？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

19 | 作为程序员，你应该有产品意识

你好，我是宝玉，我今天分享的主题是：作为程序员，你应该有产品意识。

最近电视剧《都挺好》热播，没想到其中一段台词却引发了很多程序员的集体焦虑。台词说的是：“作为一个程序员，你的年龄已经很大了！我问你，你学新东西有年轻人快吗？”

是呀，年纪越来越大，而新技术却层出不穷，是难免会焦虑。但如果你真的每个新的热点技术都去跟，都去学，就可以不焦虑了吗？我看也未必，因为新技术一直会有，学习也都是有成本的，只要你不能一直跟上新技术的步伐，你就会一直焦虑。

那焦虑是怎么产生的呢？

在我看来，焦虑通常来源于压力，压力来源于对未来的不确定，对未来的不确定来源于不知道自己的价值在哪里，不知道未来是不是还能持续创造价值，会不会失业。

会不会失业，取决于你创造的价值是否高于你的工资水平，否则确实是有失业的风险。所以要想不焦虑，我们就要考虑如何提升自身价值，只要自己创造的价值够大，就不担心自己会失业，减少很多不必要的焦虑。

程序员的价值

虽然通常来说，技术水平越高，工资越高，但并不都是这样。你的工资，通常是和你创造的价值正相关的。而程序员的价值通常体现在两个方面。

第一，你的价值体现在你所做的产品之上。

也就是说，你所做的产品越有价值，你的价值就越大，相应的工资就也会高。

这也解释了为什么同一个公司内，负责热门产品的部门，奖金都能多分一点；在效益好的公司，不但不担心裁员，反而钱也拿的多。这些年程序员的待遇相对于其他行业要高，也主要是因为软件和互联网行业的产品估值高。

所以说，程序员的价值，并不完全是体现在技术上的，而在于用技术做成了产品，产品创造了价值，再回过头来成就了程序员的价值。

第二，你的价值体现在团队中的稀缺性。

很多时候程序员其实没机会去选择产品的。但即使在同一个产品中，技术水平相当的程序员，价值也有差别。那些价值高的程序员通常在技术上或者技术之外都有一技之长：

有的程序员能搞定别人搞不定的技术难题；

有的程序员擅长培训新人；

有的程序员擅长和业务部门沟通；

有的程序员能高质量地完成功能模块；

有的程序员能按照需求设计好的架构，可以让团队高效率低成本地完成需求。

这些有一技之长的程序员，能帮助团队创造更高的价值，也因为其独特性，难以被取代，具有稀缺性，所以价值也更大。

那怎样来提升价值呢？努力提升自己技术水平，让自己成为技术大牛，这肯定是每个程序员都坚持在做的事。但技术水平提升到一定程度后，会有瓶颈的，进展会非常缓慢。

这时如果也在其他领域同步发展，就会起到事半功倍的效果。比如说有的程序员会发展写作能力，写很多好的技术文章，在业界具有影响力；有的培养产品意识，让自己在技术之外，还能更好理解产品需求，能很好地和产品经理沟通，根据业务需求做出好的设计，写出高质量代码，帮助团队在项目过程上做的更好。

写作固然是提升个人价值很好的方式，但要在写作上有成就，需要建立在长时间不断练习的基础上。而产品意识，是程序员的固有思维中比较欠缺、正好可以互补的，相对比较容易掌握，也能取得明显的效果。

什么是产品意识

产品意识，本质就是一种思维方式，一种站在产品角度思考问题的方式。如果细分一下，产品意识包含：商业意识、用户意识和数据意识。

商业意识

所谓商业意识，就是所做的产品是要有商业价值的。比如说成功的商业产品有 Windows、iPhone、Google 等，这些产品不仅满足用户需求，同时也能创造商业价值，让这些公司变成成功的商业公司，雇用了大批优秀的程序员，从而可以继续研究更多产生商业价值的产品。

其实很多程序员也有做产品的梦想，而且也有人付诸行动，业余时间做了不少产品，但是鲜有成功的。其中一个根本的原因就是，他们做的产品其实没有什么商业价值。

比如说程序员热衷于做个 Github 客户端、博客系统，虽然说确实有用，但是却没什么商业价值，没有用户愿意付钱，导致难以持续。

商业意识的另一方面其实是成本，成本意识也是程序员容易忽视的。比如说：

有时候为了炫技，采用了更难更酷的技术方案，而忽视了所采用的方案会导致很高的开发成本；

花了太长时间去开会而忽略了开会的成本；

有时候又为了省钱，舍不得买一些成熟的商业组件或服务，反而是浪费了更多成本。

如果程序员有商业意识，就可以在项目中有更好的成本意识，为项目节约时间、经济等成本，帮助团队打造更有价值的产品。

用户意识

所谓用户意识，就是说做产品时，你要能挖掘出用户的真实需求，让产品有好的用户体验。这需要你要有同理心，能站在用户的角度去思考和体验产品。

大部分程序员可能更多专注于程序上，所以在用户意识上确实有所欠缺。举例来说：

一个产品功能，产品经理在细节上没有定义清楚，程序员可能并不会主动提出，最终做出来的产品会不好用；

在做技术方案时，更追求技术炫酷，而不是用户体验更好；

在设计接口时，并没有考虑调用者的便利性。

如果程序员能跳出纯技术的局限，多一点用户意识，想到的问题将会多了很多维度，比如说：

能让自己的负责的模块有更好的体验；

让自己的技术方案更好地满足用户需求，用户更满意；

让自己设计的接口、API 更好用，与同事愉快合作。

做到这样，无论对产品还是对自身，都是价值的提升。

数据意识

所谓数据意识，就是在产品设计、产品运营时，通过数据来发现问题、证实结果。

典型的有 A/B 测试，通过数据来发现用户更喜欢哪个功能，哪个功能带来更多的收入。像微博的“时间乱序”功能，虽然很多大 v 吐槽，但是数据证明了这是一个好的产品设计，最终还是一样上线。上线后新浪根据数据不断优化，到现在反倒是很多人喜欢这个功能。

程序员虽然逻辑很好，但是大多对数据倒是不敏感，对编译警告、测试覆盖率、程序 Crash 的比例、API 错误率、一个函数内上千行代码、性能指标等等这些数据经常选择性忽略。

还有个典型的例子就是语言框架之争，程序员经常为某些语言或者框架争论不休，其实不妨基于数据分析，讨论上会更加客观。比如，从数据你就可以明显看到 jQuery 和 React 近些年在前端的发展趋势。

其实产品意识，并不难理解，只是需要你往前更迈一步，在商业意识、用户意识和数据意识上去多思考，就可以帮你在项目中做的更好。

如何培养产品意识？

那么程序员要怎样培养产品意识呢？要培养产品意识，其实和程序员转管理的类似，首先要解放思想，然后要改变习惯，最后要多实践。这么说可能比较抽象，我们逐条展开来看。

首先要解放思想

解放思想，其实就是说，对于程序员，不要总是单纯的用技术眼光看问题，也可以从产品的角度看问题。这两者有什么区别呢？

举个例子，办公聊天软件 Slack 可能很多人都知道，是一款在线沟通协作软件。在国内可能知名度要低一些，但是在海外有大量企业用户，非常的火。

这款软件在刚出来的时候我就知道，不过那时候我觉得这不就是一个聊天室么，我都能写一个！我站在技术角度也做了不少分析：

这个软件前端还是用的 jQuery，如果用 React 应该可以做的更好；

这个软件跨平台是基于 HTML5，如果是原生代码也许性能可以更好；

还是 REST API，如果用 GraphQL 那 API 请求效率会更好；

从国内访问的话，速度太慢了，应该架设一些国内的服务器或者 CDN。

而现在，我会同时也从产品角度分析 Slack：

它的商业价值，在于它把工作的沟通，变得高效又好玩；

消息都在云端，检索也方便，也不担心像微信一样换设备消息就没了；

其开放 API 的设计，让它和很多其他办公软件可以无缝集成，极大提升了效率；

Slack 需要付费才能查看到 10000 条之前的消息，这是个很有意思的设计，当你已经有 10000 条消息时，说明已经有足够的意愿去付费了。

其实这两个角度也代表了两种不同的思维方式，一种是很多程序员熟悉的技术思维，一种是产品思维。

技术思维会关注用什么技术，关注技术细节，关注功能“如何”实现；产品思维会关注用户体验，关注一个功能所创造的价值，会去思考为什么要或者不要一个功能。

这两种思维不同，也很容易导致沟通上的误解。比如程序员会更多考虑技术实现，产品经理会更多考虑产品设计。如果都能往前迈一步，程序员有产品意识、产品思维，产品经理能有一点技术思维、工程思维，那么相互沟通起来就会更通畅。

这两种思维之间的差别，其实也正是要培养产品意识的关键点。要想培养产品意识，就是要从纯粹的技术思维，有意识地培养产品思维。从关注技术、技术细节，到关注用户体验，关注产品创造的价值。

然后要改变习惯

改变习惯是是指在日常使用产品、开发产品的时候，多站在产品的角度思考，去思考它的商业价值、用户体验、使用场景等等。

比如你学习专栏用的极客时间 App，你聊天用的微信。使用一些具体功能时，可以思考一下这些问题：

这个产品的商业价值是什么？

为什么要有这个功能？是为了满足用户哪方面需求的？

这个产品目标用户是谁？

这个功能的使用场景是什么？

这个功能的体验好不好？有没有更好的方式提升体验？

也许你没法马上有清楚的答案，但寻找答案的过程也是一个很好的学习的过程。

如果你是程序员，在开发功能、设计架构的时候，也不妨跳出技术之外，从产品角度思考一下：

这个功能的需求是什么？我是否完全理解了需求？

如果你是这个功能的用户，你觉得还有哪些地方值得改进？

哪些技术可以帮助提升用户体验？

这个 API 用起来是不是好用？有没有更好的设计？

除了对产品的思考，日常工作中，遇到一些问题，也可以从产品思维的角度去想想。

一个常见的场景就是，产品经理一下子提交了一堆新的需求任务，影响了正常的开发进度，这时候你不一定要拒绝他，你就可以和他一起把需求的优先级梳理一下。你就知道哪些要优先做，哪些其实没有那么着急，方便更好的安排你的工作。

还有像产品经理提交了一个技术很复杂的需求，你可以不用着急马上拒绝或者说要很长时间，而是跟他探讨一下这个需求背后要解决什么问题，是不是可以有替代的解决方案，既能降低技术难度又可以满足需求。

自己开发的功能模块完成后，可以把自己当成用户试试，如果觉得体验不好或者有更好的建议，都可以反馈给产品经理。

最后要多实践

光有理论还不够的，最好能自己实践一下。

你不妨在业余时间做个小应用程序，或者设计一个原型，做完了再找你的朋友试用一下，让他们提提意见。在做产品的过程中，你自然会去站在产品的角度去思考，这会让你对产品方面有更多感悟。

其实不用担心没有什么好的想法，可以从日常生活中，自己的需求、家人和朋友的需求中，去找到合适的产品需求。我当初做过很多产品都是这样的来的：

给孩子照的照片太多，写了个工具批量生成缩略图；

老婆工作上需要经常对网页截取整张图片，设计一个帮助截图的工具；

帮父亲建了个家谱应用；

给校友们建了一个网上交流的论坛，写过一个论坛系统。

用心观察，类似的需求你也可以找到很多。

总结

今天，我们一起分析了程序员的价值体现，主要体现在两方面：所创造产品的价值和自身的稀缺性。程序员有产品的意识，可以帮助产品和自身提升价值。

产品意识，主要包括商业意识、用户意识和数据意识。要提升产品意识，首先要解放思想，然后要改变习惯，最后要多实践。

当你慢慢培养了产品意识，不仅可以通过技术来打造更高价值的产品，也可以让你在技术之外有一技之长，能在项目中创造更大价值，减少技术快速革新带来的焦虑感。

课后思考

请你也从技术角度和产品角度分析你常用的一款产品，同时也欢迎你分享一下你对于产品意识的思考或故事。欢迎在留言区与我分享讨论。

感谢阅读如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

20 | 如何应对让人头疼的需求变更问题？

你好，我是宝玉，我今天分享的主题是：如何应对让人头疼的需求变更问题？

我以前在国内做开发的时候，加班加点是家常便饭。这几年在美国工作，极少加班，但是产出却并没有下降，所以我一直在思索其背后的原因。这里面涉及因素很多，包括大环境、管理水平、配套设施等，但是有一个因素至关重要，那就是需求变更。

在国内很多软件公司，需求变更是常态，开发到一半，很多原始需求就发生了变化，这时候当初的设计已经不能满足要求了，很多代码需要修改，不得不加班加点来赶上进度。

反观不加班的美国公司，需求确定后很少变更。这样开发人员就可以针对需求有良好的架构设计，而不用考虑太多可能的变更，从容地在项目计划的时间内完成任务，而不需要加班加点。

在需求变更这个事情上，没有赢家，每个人都是受害者。

程序员自己辛苦的工作白费了，得不到成就感，因为频繁变更的需求，不得不在设计的时候考虑很多可能的变更，导致代码臃肿，代码质量降低，加班加点成了常态。甚至有人说：“杀一个程序员不需要用枪，改三次需求就可以了！”

产品经理也觉得很委屈：“客户要改，我有什么办法？”程序员和产品经理似乎变成了两个对立的岗位，程序员怪产品经理乱改需求，产品经理觉得是客户造成的，人人都觉得自己委屈。客户同样不满意，觉得做出来的软件不是他想要的，进度总是在延后，还想加钱？

既然大家都不满意，那么我们就需要想办法去改善，这也是软件工程这门学科存在的目的和意义。

目前也已经有很多管理需求变更的解决方案，比如这两个常见的解决方案。

方案一：增强需求变更流程，让需求变更规范起来。
这个方案简单来说，就是通过严格的流程，来避免一些没有意义的变更，从而达到管理需求变更的目的。

方案二：快速迭代，缩短版本周期。
这个方案是另一个思路，就是将大的功能拆分，每个版本周期仅实现一部分功能需求，周期较短，这样需求发生变更时，就可以快速响应。

不过，在看到这两个方案后，我还是希望你不满足于当前的答案，自己停下来思考一下这两个问题：

这些方案是否解决了你当前项目的问题？

如果换一个项目环境，当前方案是否依然适用？

之所以要思考这样的问题，是因为对于像软件工程这样偏理论知识的学习，你一定不要仅仅停留在了解有什么样的解决方案上，而是要追本溯源，研究问题背后的原因，研究理论背后的来龙去脉。

因为，就算你记住了再多的解决方案，换个项目环境可能就不适用了。所以我们要多去思考和分析逻辑，这样未来遇到类似的问题时，才可以做到对症下药，选择合适的解决方案，甚至在没有现成解决方案的情况下，能自己创造合适的解决方案。

为什么建筑工程中少有需求变更？

要解决需求变更的问题，你首先要知道，软件开发行业中的需求变更是怎么来的。

我很喜欢拿软件工程和建筑工程进行对比，你可以思考一下，同样是工程，建筑项目也是有需求变更的，但却不会像软件项目这么频繁和失控。为什么呢？

我总结一下，这里有两个主要原因：需求的确定性和需求变更的成本。

原因一：需求的确定性

建筑需求是很具象的，而软件工程的需求是抽象的。所以建筑项目里面，无论是提出需求还是变更需求，客户和施工方都明确地知道他们想要什么。

软件需求则经常是抽象、模糊、不精确的，模糊不清的需求导致在软件开发有了雏形后，才慢慢想清楚真正的需求是什么，从而导致需求变更。

举个例子，客户最开始对软件界面的颜色是没有任何要求的，当第一版本的软件给客户看的时候，客户觉得白色背景太难看了，希望换成蓝色的；第二版本换成蓝色后，客户现在已经觉得黄色更好看，希望改成黄色背景；第三版本的时候，产品经理担心客户还想换颜色，就直接做成了换皮肤功能，用户可以自己选择颜色，客户还是不满意，问能不能把背景换成图片……

是不是很熟悉？类似的事情其实经常发生在我们日常的工作场景里。

原因二：需求变更的成本

建筑项目里面的需求变更，我们都很容易和成本挂钩，因为这些东西已经是生活常识了。而与此相对的是，很多人，包括很多老板都对软件项目需求变更导致的成本增加缺少系统认识。

举个例子，装修房子的时候，如果墙面已经刷成白色了，但是客户想都刷成蓝色，那么他会很清楚，这涉及一系列成本：需要重新购买涂料、需要找人重新粉刷。

但换成一个软件项目，客户想把界面的白色背景换成蓝色的，他会觉得这是很简单也是理所当然的，甚至产品经理也会这么想，他会对程序员这么说：“不就是换个颜色吗？几行代码的事，客户让换就换了嘛！”

但是实际上，软件项目的需求变更，哪怕是换一个背景颜色，同样是要涉及成本的：需要修改所有涉及背景颜色的代码，需要更新相关测试代码，还需要对涉及的界面重新测试。

你可以说这成本是架构设计水平不到家导致的，但是如果设计时就考虑到要有支持换背景颜色的功能，那么开发的工作量从一开始就上去了，成本同样是提升了。

回到文章开始时我们提到的美国程序员不加班的问题，为什么美国的产品经理不敢随意更改需求？因为在美国很多 IT 公司都是工程师文化，工程师相对比较有话语权，正常情况下是不会加班加点的，所以产品经理变更需求的成本很高，在确定需求时，必须慎之又慎。

如何解决需求变更问题？

说完了原因，咱们再来看看解决方案。

首先，你需要意识到，在软件项目开发中，需求变更其实是不可避免的，一味抵制需求变更也是不可取的。你能做的就是利用软件工程的知识，理解需求变更背后深层次的原因，找到合适的方案来改善，积极拥抱合理的需求变化，减少不必要的需求变更。这是大的前提条件，也是共识的基础。

好，既然引起需求频繁变更的原因我们已经清楚了，那么，怎样有针对性地想解决方案呢？这里你也不妨停下来思考一下，你会想到哪些办法？

我的经验是从源头着手，既然需求变更的原因是需求不确定和需求变更成本太低，那么我们就针对性地提出相应的解决方案：

提升需求确定性，把需求分析做好，减少需求变更；

提高需求变更的成本，让客户或者产品经理不能太容易就变更需求，这样就可以达到减少需求变更的目的。

但在实施的时候，我们会发现一个问题，假如一味提高需求变更的成本，会让客户满意度下降，也造成了产品经理和开发人员之间的对立，不利于项目协作。

所以我们从另一个角度思考：需求变更之所以让你痛苦不堪，也是因为需求变更让项目成员付出了高昂的代价，例如返工、加班，如果我们可以低成本地响应需求变更，那么一样可以达到管理需求变更的效果。

所以解决方案上可以再加上一条：

降低响应需求变更的成本，可以方便快捷地响应需求变更。

接下来，我来举一个在实际项目遇到的案例，我们一起来分析一下，通过对这个需求变更场景的分析，来解决以上提到的这些问题。

案例分析

我有个大学同学叫加龙，毕业后自己开了个公司，早些年企业建站火的时候专门接企业网站的活。刚开始的时候很艰苦，也没几个人，甚至都没有专门的产品经理。

开发流程比较简单，就是先把项目谈下来，客户提一个建站的需求，然后他们去开发网站，开发好了拿给客户演示。而客户在看到网站演示后，几乎每次都会提出很多变更的需求，例如说颜色变一下、布局变一下、给留言功能加上关键字过滤功能等等。客户还喜欢直接在 QQ 上找负责开发的程序员，让给改一下。

创业初期，加龙同学真的是不容易，每天和几个程序员一起加班加点，就是为了应对客户这种频繁变更的需求。如果你是加龙，参考前面总结的几种解决方案，你会怎么做？

加龙作为软件工程专业毕业的学生，我觉得他当时运用软件工程知识去改善需求变更问题上是做得非常好的。他其实并没有采用一个单一的解决方案，而是分阶段逐步改进。

第一步：规范变更流程，提升客户变更成本。

加龙其实也知道，通过提升需求确定性，做好需求分析，和客户多沟通确认，是可以有效减少需求变更的。但是他当时确实人手太有限，也没有专业的产品经理，不能短时间内去提升需求分析、产品设计的水平，所以他第一步选择提升客户变更需求的成本，这样可以马上产生效果。

于是在后面的项目中，在和客户签订合同时，他会和客户约定，如果有需求变更，先统一提交到他那里，然后他甄别后再决定是否做，什么时候做，是否要重新签订新的附加合同（增加额外费用）。通过制定一系列标准，让双方合作的流程变得更规范。

这样，程序员就可以专注于开发，也不会因为频繁的需求变更影响进度，大家不用那么累，收入也在稳步上升。但是需求变更的情况还是时有发生。

第二步：用原型设计低成本响应需求变更；做好需求分析和确认，减少需求变更。

加龙在挺过最艰难的创业初期后，雇佣了一个全职的产品经理，专门去和客户确认需求。这个产品经理很专业，每次在了解完客户的需求后，不急于让让程序员马上去写代码，而是自己先用 Axure 这样的原型设计工具，做一个简单的交互原型，给客户演示。

于是客户会针对原型的效果提出一些修改意见，他再快速地修改原型，这样反复确认，等到客户没有什么修改意见后，再让开发着手实现。

通过原型设计的方式，不仅可以方便地与客户沟通需求，还可以灵活响应需求变更。

通过提升需求确定性，加龙的公司进一步降低了需求变更的情况发生，营收又上了一个台阶，又增加了几个程序员和产品经理。

第三步：通过灵活的架构和强大的配置，低成本响应客户需求变更。

加龙公司经过两年的发展后，敏锐地发现其实大部分企业网站的功能都是很相似的，主要差别还是在界面样式上。

这些年大部分网站的开发其实都是把前一个网站复制一份，修修改改，但是这样还是效率太低，如果可以做到定制化，就可以更高效地定制网站。

于是加龙从公司抽调了几名骨干程序员，成立了一个专门的项目组，把这两年做的网站类型做了分析，做了一套建站系统，有点类似于后来流行的像 WordPress 这样的博客系统，可以通过换皮肤的的方式来定制界面，通过插件的方式增加功能。

由于前期积累充分，大约半年后他们就开始使用这套系统去建站，一下子就把建站的成本大大降低啦。而且当客户的需求有变化时，只要后台做简单的配置就可以马上支持需求变更。

但是这个模式也有问题，就是有些特别个性化的定制需求，还是满足不了。不过这也没关系，对于需要个性化的客户，要么增收额外的费用，要么就直接放弃掉。

如果咱们对他这三步采取的方案做一个总结，还就是我前面说的三个方案：

提升需求确定性；

提高需求变更的成本；

降低响应需求变更的成本。

只不过他根据公司不同阶段的特点，来灵活运用。这就是我的同学加龙在处理需求变更时分阶段采取的方案，是不是跟你想的一样？

总结

今天我通过对比建筑工程中的需求变更，和你一起分析了软件工程中需求变更产生的原因。需求频繁变更，主要是由于需求不确定和变更成本过低导致的。并由此提出了三种不同的解决方案。

提升需求确定性，来减少需求的变更。这种方案的优势就是对需求理解透彻，后期返工少，缺点是对产品经理的需求分析能力要求很高。

提高需求变更的成本，规范需求变更流程，减少需求变更。这种方案的优势就是可以马上起到效果，缺点就是过于繁琐的流程不利于项目协作。

降低响应需求变更的成本，积极应对需求变更。这种方案的优势在于可以快速响应需求变更，能快速试错尽快调整，缺点在于对软件架构和项目管理要求比较高。

就像我的同学加龙那样，你可以根据项目的实际情况，对比这些方案的优缺点，选择适合你的解决方案。

例如你是做企业外包项目的，客户不懂又喜欢瞎掺和，那么就可以适当提高变更成本，甚至每次变更都可以计入项目成本中；如果你是做互联网项目，需要快速推出产品，那么就可以选择降低响应变更成本，快速迭代，快速试错，尽快调整。

如果你是项目经理，希望你通过这次对需求变更的学习，能针对项目特点，制定合适的需求变更流程，选择适合项目的开发模式，管理好需求变更，从而提升整个项目的开发效率，避免重复返工导致的浪费。

如果你是产品经理，希望你通过这次学习，对需求变更导致的成本有很高的重视，努力提升专业水平，勤于和客户、开发测试人员沟通，勤总结，尽可能将需求变更的可能降到最低。

如果你是开发或者测试，希望你在以后遇到需求变更时，不要置身事外，抱怨产品经理不专业，因为需求并不是产品经理一方的事情，项目参与者都有责任一起把需求分析做好。

在一些需求分析和变更的关键阶段，要主动参与其中，从开发和测试的角度提供一些专业的建议，去思考需求产生的背景，避免对立情绪。

课后思考

在你参与的软件项目中，需求变更多吗？你们是怎么管理需求变更的？你觉得哪些地方可以做得更好？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

“一问一答”第2期 | 30个软件开发常见问题解决策略

你好，我是宝玉。我们专栏已经完成了项目管理和需求分析这两个模块的学习。这两个模块看起来都和技术没什么关系，但却是项目中至关重要的部分。

项目管理贯穿项目始终，需求是项目的源头。希望通过对这两个模块的学习，能加深你对项目管理和需求分析知识的理解，能应用其中一些方法，帮助你个人能力更上一层，项目越做越好。

这个过程中一如既往的收到很多同学的精彩留言。其中有一些是提问，一些是针对文章主题自己独到的思考，这些内容都是对专栏内容最好的补充，可以加深你对软件工程的理解和学习。

一问一答

No.1
Charles：可行性分析形同虚设，小公司岗位职责不清晰，互相照顾面子怕得罪人，谁都怕犯错背锅，感觉谁都对，最终就导致谁是“老板”谁拍板！我感觉这个问题挺严重的，很影响决策正确性，只能等所谓的市场反馈。也用类似项目成员“扑克牌”打分的方式可以解决吗？核心问题出在哪里？

宝玉：这个问题已经不是可行性研究的问题了！核心问题在于没有一套合理的类似于扑克牌打分的机制和流程。

扑克牌为什么是个好机制：

公平合理，每个人都有机会不受他人影响的表达

不用背锅，估错了也没关系，意见不一致还可以讨论

可行性研究是不是也可以形成类似机制？有专门会议，大家提前准备，会议上一起讨论结果，不用背锅，根据讨论结果形成最终决议。项目结束后在回顾对比当初的分析，作为下一次的参考。

No.2
川杰：架构师是否也属于管理者的范畴？因为他需要对产品的整个框架的负责，进而涉及到对每个人的代码的管理，必要时还要给带领团队成员去做重难点问题的攻坚。那么对于架构师而言，是更偏向技术还是管理呢？

宝玉：我觉得架构师和管理有相通的也有不同的，简单说一下我的观点：

相同之处：

都需要大局观；

都需要好的沟通能力，让团队清晰的理解自己的意图；

都需要用好流程和工具；

都要善于“分而治之”，把复杂的问题拆分成小的具体的问题。

不同之处：

项目经理更多的是跟人打交道，对项目负责；

架构设计更多是专注技术，对架构负责。

两者互为补充，架构师有项目管理能力、项目经理有架构能力，都是非常好的！

No.3
天之大舒：目标的一致性是遇到的困难，公司没有激励制度，导致项目经理和组员目标不一致，如何解决这个问题很挠头。

宝玉：解决目标一致性问题，一个方法是多一对一沟通，你了解组员想法，组员知道你的期望；另一个方法就是不必依赖于公司现有制度，自己创造激励制度，激励制度并不一定要花钱或者花很多钱，有时候正式的表扬比钱还有价值。

No.4
titan：小公司如何进行技术管理的问题？我所在的公司，开发人员多的 40、50 人，少的 10 多个人，这个阶段，是用制度来进行管理，还是人来管理比较合适？

宝玉：我觉得无论大小公司，一定都要多用合理制度流程，多用工具，摆脱对人的过度依赖，只是在设计流程规范时，要充分结合公司特点、项目特点。

比如说小公司老板权力很大，有些流程普通员工有效，老板直接无视了，你还得做好隔离措施，让他不要破坏流程。比如说大公司很多工具、系统都是自建，小公司就不如买来的合算。

大公司各种会议和文档相对多很多，小公司这方面就可以多精简，但必要的也不能少；大公司用瀑布模型开发，一个项目几年耗得起，小公司还是敏捷一点，早点能看到产出更好。将来有一天，小公司也会变成大公司，如果你之前没有做好制度建设，将来团队壮大，项目多了，可能就会成为你的管理瓶颈。

No.5
风翱：“团队的成功，才是你的成功“，以前也坚信这个观点，但自身的例子，让我有些动摇。把下级培养起来了，结果不是升职，而是上级越来越把我边沿化。对于这种情况，怎么调整自己呢？

宝玉：心情完全能理解，但建议还是看长远些。人生不只是一个下属，不只是一个老板，也不只是一个项目。以前我也纠结过这问题，现在不纠结了。因为我不止能培养好一个下属还能培养更多的下属，我能做好一个项目还能做好更多项目，我不需要靠一个老板的赏识与否来证明自己。

No.6
冰封血影：针对一些曾经贡献大的技术怎么管理呢？然而传统思维模式和产品迭代模式遗留的一些诟病，很难用新环境、新模式让他们去做改变。（这里并不是否定以前的模式）
比如：对新推出的 KPI 这类漠不关心、对整个团队表现不出积极的面，反而带来了一些不好的点和面，但是做东西质量相比其他又高；这类怎么去处理和更好的提升整个团队的战斗力、协作力？

宝玉：几点建议：

多一对一沟通，了解他的诉求，让他了解你的期望；

尝试安排一些有挑战的任务；

充分发挥其优势；

“鲶鱼效应”，招聘技术相当的或者更高的，减少其不可替代性。

如果负面因素较多，可考虑隔离到某个项目中，避免对其他人造成负面效果；如果负面影响大于正向贡献，劝退也是个方案。

No.7
Dora：技术人员呢一般很傲，所以做项目管理，可能要面对被技术人员心里瞧不起，甚至不听话。怎么办？

宝玉：几点建议吧：

如果管理者技术牛，或者懂一点技术，那么就容易很多；

让项目成功，就是最好的证明你实力让他们服气的方式；

多换位思考，尊重技术，平等沟通；

多帮助他们：帮助成长、帮助涨工资、帮助他们少无谓加班；

多用流程规范来管理，少基于人管理。

No.8
tcny：如果因为开发不紧不慢耽误了时间，如何处理呢。应该设置什么样的奖惩制度呢？

宝玉：这是个好问题！计划恰恰就是为了预防类似于开发不紧不慢耽误了时间的问题。具体例子，一个模块，正常估算（开发和 PM 都认可）需要 5 天，但是如果你的计划粒度是 5 天，那么你到最后一天才能知道是不是会延迟，这时候补救已经晚了。

如果你能把粒度设置到半天一天，那么第二或第三天你大概就能知道进度是不是有问题，然后马上作出调整，要么加班，要么找人帮忙，要么换人，要么改计划。这样才可以做到防患未然！至于奖惩制度，只是手段，而不是目的！

No.9
一路向北：计划是否也会有一个迭代的过程呢？

宝玉：计划一定是个迭代的过程，计划也是个粗到细的过程。一开始不建议做特别细的计划，整体粗一点，定好大的时间节点，也就是里程碑，然后对于下一阶段的计划细化。

细化过程中要拉上具体参与的人一起制定，这样结果才科学也不会导致抵触。里程碑定了后不要轻易变，不然就失去了 DeadLine 的意义，即使变也不能过于随意和频繁。

No.10
Geek_85f782：如果是采用敏捷方法的项目，项目计划是否应该就是迭代计划？在这种情况下 WBS 的结构其实就是一轮接着一轮的“规划 - 分析 - 编码 - 测试 - 集成发布 - 与敏捷配套的一系列总结”？每一轮迭代的成果就是项目的里程碑？

宝玉：敏捷的项目计划确实有些不一样，WBS 分解后会变成 backlog，backlog 的项会被打分（参考扑克牌打分），根据分数大致可以算出来需要多少 Sprint。因为敏捷开发磨合好后，每个 Sprint 能做的任务分数大致相当。算出来多少 Sprint，就能大概知道需要多少时间。通常里程碑不会那么密集的，一般会几个 Sprint 一个里程碑。

No.11
纯洁的憎恶：制定计划最好能让项目相关各方充分参与，这样计划更可行，偏差低，结果更可控、可预期。但我的经历却是需求、开发、运营、用户等角色几乎不参与制定计划，就连需求分析、功能设计、测试、验收也以工作忙为借口很少介入。项目管理人员主动拉他们，也遭到厌恶与不配合。在观念与体制不支持的环境里，如何能更好的调动各方充分参与、支持项目呢？

宝玉：你这种性质的单位我确实没经历过，缺少经验。不过我可以帮你从另一个角度分析下，就是如果我不愿意参与计划可能有这些方面原因：

跟我利益不相关，做了没好处，不做没损失；

你已经做的够好够细了，没什么好发挥的；

就算参与了提了想法和意见也没用，最后还是项目经理说的算，那我还掺和个啥劲。

所以你可以看看能不能让这事变成一个跟大家利益相关的事，跟绩效考评啥的扯上关系，必要的话拉上领导狐假虎威一番。拉他们参与时不用太细，让他们有机会参与制定，制定时能平衡好他们的利益关系。尤其是里程碑的确定，我觉得应该是和大部分人利益相关的，至少这个点得让他们参与进去。

No.12
bearlu：我一直想自己私下做一个项目，但是不知道如何开始，是不是第一步要确定做个什么软件？

宝玉：对的，第一步先想好做什么。给你的建议是：

做个小的；

做个实用的，最好自己能用或者身边人能用；

迭代开发，第一版本只做核心功能。

No.13
哥本：在做里程碑的时候需要花时间整合做集成测试吗？就比如向您说的，服务端开发完成后需要与 pc 客户端联调，那这就涉及到发布，环境搭建，部署…做 WBS 时要把这些时间也算进去吗？

宝玉：做里程碑要不要整合做集成测试，取决于里程碑的目标，比如说如果目标是具备测试条件可以联调，只要能调就可以；也可以定义目标是要测试验收通过，这就需要做集成测试的。
这种需要人需要时间去做的事情，都应该放到计划里面。文章中的计划表没有放，是考虑不周。

No.14
alva_xu：需求文档和测试用例怎么验收？对于性能测试是否合格问题，你们是怎么解决测试环境和生产环境可比性问题的？

宝玉：需求文档验收可以通过需求评审会议，评审时开发和测试都要有代表参加，一个是提出反馈，另一个是及早了解需求。评审会议通常要开几次才能最终定下来。测试用例通常是产品经理协助验收或者辅助确认。

原来我们在飞信时，会有一个模拟生产环境的压力测试环节，从生产环境同步真实数据过去，规模按生产环境比例缩放。还有的压力测试是直接在生产环境做的，在半夜人流量少的时候。

No.15
张驰：在日常工作中，流程应该由谁来制定呢？普通开发人员还是领导者，亦或者是公司有这种专职专岗的人？往往很多人都能够发现问题，甚至也有一些自己解决问题的方式方法，但是要想具体流程化对公司整体产生作用，往往感觉是有力无门，没有一个好的渠道。

宝玉：一个好的流程经常是跟问题切实相关的人员提出来的，或者把问题反馈出来，大家一起想办法，最后由项目经理或者部门负责人帮助落实推广。

其实像敏捷开发每次迭代结束后的 Sprint 回顾会议就是一个很好的讨论问题的方式。可以考虑参考 Sprint 回顾会议的做法，定期有专门的会议讨论这样的问题。另外如果有组员之间的“1-1”会议，也是个不错的讨论这样问题和解决方案的途径。也可以通过邮件、聊天工具讨论解决。

No.16
bearlu：能不能说说开会要留意些什么内容，我是个新手，每次开完会议，到开发的时候又找产品确认具体功能。

宝玉：我想你说的应该是需求评审会议或者需求讲解会议，对于这类会议，建议你会议前读一下文档，这样心中有数，同时对于文档中觉得不清楚或者有疑惑的地方记录下来，在会议中提出。不同的会议重点不一样，开会之前你都可以实现了解下这次会议的主要目的是什么，然后事先准备一下，这样开会就会更有效率一些。

No.17
hua168：整个项目开始前到项目完全结束，一般都要开那些会议呀？目的是什么？

宝玉：整理如下。

项目启动会议：

通常在项目启动后，会有一个正式项目启动会议，俗称 Kick off meeting，通过这个项目，你可以了解几个关键信息：

项目目标：这项目是要干什么的，达到一个什么目标；

项目里程碑：项目的开始结束时间，项目的阶段划分，以及各个阶段的时间点；

角色分工：项目成员的分工和角色是什么，每个人知道自己的任务是什么，知道遇到问题该找谁；

流程规范：项目开发的主要流程是什么，基于瀑布还是敏捷。

瀑布模型各个阶段的评审会议

瀑布模型因为阶段划分清楚，每个阶段都有明确的产出，所以通常每个阶段都有评审会议，典型的像需求评审会议和架构设计评审会议。这种会议主要目的是用来收集意见。

瀑布模型各阶段的说明会议

在评审会议结束后，需求设计、架构设计最终确定后，通常还会组织会议对需求设计和架构设计做说明，所以会有：需求设计说明会和架构设计说明会。

进度报告会议

无论是采用瀑布模型还是敏捷开发，通常都少不了进度报告会议。只是瀑布模型通常是以周为单位的周例会，而敏捷开发是以天为单位的每日站会。可以通过会议，了解项目的进展，了解当前的困难和瓶颈，及时调整计划，解决问题。另外在会议上，每个人都要当众讲一下做过的事情和计划要做的事情，也是一种无形的监督和约束。

项目计划会

在敏捷开发中，每个 Sprint 开始前都会有一个 Sprint 计划会（Sprint Planning），决定当前 Sprint 要做哪些内容。在瀑布模型中，每个版本开始之前也会有项目计划会。有所不同的是，瀑布模型通常是项目经理和开发经理、测试经理等少数几个人决定的，而敏捷开发中则是全体成员一起针对 Backlog 的内容进行选取和打分。

产品演示验收会

在瀑布模型中，项目在测试通过后，会对客户有一个产品演示验收的会议，向客户展示工作成功。敏捷开发中也有 Sprint 评审会（Sprint Review），在每个 Sprint 结束后，像客户演示当前 Sprint 成果。

项目总结会议

在项目结束，通常项目经理需要组织一个总结会议，希望大家能在会议上总结一下项目的得失，把经验总结下来，帮助下一次做的更好。在敏捷开发中，更是每个 Sprint 都会有一个 Sprint 回顾会议（Sprint Retrospective）。

一对一会议

虽然项目中有每日站会或者周例会这种让项目成员可以反馈问题的方式，但是对于很多人来说，并不愿意在很多人面前说太多，但是如果是一对一的私人对话，则更愿意反馈一些更实质性的内容，从而项目经理或者管理者能了解到更真实更准确的信息。

No.18
kirogiyi：能否把项目管理每个阶段用到的典型工具分享一下？

宝玉：我们专栏每个阶段都有关于工具的章节。

需求分析篇的工具要讲原型设计，需求阶段还有需求收集管理工具，通常可以用 Ticket 管理系统（如 Jira）、源代码管理（如 git）或文档管理工具（如 Google Docs/ 石墨文档）来做。

设计阶段其实主要用文档工具，用 MS Visio/PPT 画图。

编码阶段主要是源代码管理工具、各种 IDE、持续集成平台（Jenkins）的搭建。

测试阶段主要是有测试用例管理系统（例如 TestRail），有 Bug 跟踪系统（基本上和项目管理工具一起的，例如 Jira）。

运维监控有日志管理系统（例如 ELK），监控（例如 Wavefront），报警（例如 PagerDuty）。

No.19
纯洁的憎恶：我看燃尽图好像是根据 ticket 数量的历史变化情况，线性的预测未来的工作进展。但工作真实进展很可能不是线性的，这是否说明燃尽图的剩余工作预测存在天然偏差呢？

宝玉：燃尽图是有天然偏差的，因为任务的复杂度其实不一样的，有的几小时就完了，有的得好几天，有时候你看只剩下一个任务了，但这个可能是最难耗时最长的。所以我个人更喜欢看板视图，可以直观看到当前 Sprint 具体什么任务还没完成。

No.20
busyStone：请问新的这些工具还能看到并方便的编辑任务依赖么？有没有工具可以直接通过修改状态就自动换看板的? 另外，像同一个需求需要多端，安卓，苹果，PC 同时开发的，请问有没有好的方法来建立任务? 之前都是一样建一个，有点烦。

宝玉：以 Jira 为例：

Ticket 之间是可以建立关联的，好像不是强依赖。

修改 Sprint 属性就可以切换看板，修改状态就可以切换看板的泳道。

Ticket 可以克隆（Clone），同一个需求可以克隆多份，然后稍作修改。

No.21
oldlee：请问前后端开发分离工具有没有好产品推荐？现在遇到的问题是，客户端经常需要等待服务端开发完，才能调用接口联调。

宝玉：这个问题有几种解决方案：
服务端先实现一个模拟的接口；
客户端自己模拟接口；
第三方服务。例如：
https://github.com/easy-mock/easy-mock
https://getman.cn/mock/
https://apizza.net/

No.22
alva_xu：项目的不同时间节点，项目风险及其处理手段也是不一样的。所以，在讲风险管理的时候，还要加一个时间维度。老师能不能就这个维度来谈谈风险及管控处理方法？

宝玉：其实要考虑时间维度，你只要把时间范围成本三要素的约束加上就好了。因为时间变了，这三要素的约束也在变。

给你举个例子：一个创业公司，人少缺钱，这时候人就是个很大的风险，有人离职项目就很危险，这其实本质就是三要素的成本；等到熬过这阶段，进入发展阶段，活下来有钱了，人也多了，相对来说，成本就不是最大的约束了，人的风险就没那么大了，这时候就是求快，时间会变成约束，所以如果你的技术和架构跟不上开发的效率，就会成为新的风险。

No.23
Bo：已经写好项目文档，但想更另一步优化文档，老师可以分享一下项目中需求规格说明书、概要设计、详细设计、代码规范文档、测试文档、部署文档等的优秀具体案例吗？

宝玉：有些内部文档不方便分享。我在文中附了一个开源项目的链接：https://video-react.js.org/

这个是一个组件使用文档，其实类似的有很多开源项目的文档都写得很好。比如：
Vue: https://cn.vuejs.org/v2/guide/
Redux: https://redux.js.org/

还有可以网上搜索一些，例如：
产品需求文档模板：https://www.jianshu.com/p/e89e97858be1
微服务，从设计到部署：
https://docshome.gitbooks.io/microservices/content/2-using-an-api-gateway.html

No.24
hua168：老师能简单说一下项目前–> 项目中–> 项目完成，一般都需要哪些文档呀，有没有示例或链接或搜索关键词？

宝玉：我大致列一下，可能有遗漏的。

项目立项：

原始需求文档；

可行性分析报告；

立项说明书。

需求相关的：

原型设计文档；

产品设计文档。

系统设计相关的：

技术方案文档；

详细设计文档。

开发相关的：

代码规范文档。

测试相关的：

测试用例；

测试验收报告。

运维相关的：

部署文档；

故障报告。

No.25
邢爱明：对于详细设计文档的颗粒度一直有点疑问。是写到类图或者时序图这种级别，说明不同类和方法之间的关系？还是要细化到类似于伪代码级别，需要写操作哪个数据库表，和调用哪个 api 接口？

宝玉：我们 2002 年学软件工程的时候，推荐的写设计文档就是你说的这种细化到为伪代码级别，当时初衷是学习建筑行业，把写代码变成像搬砖砌墙一样，招一堆蓝翔培训出来就可以写代码。据说当年日本软件产业就是这样的。

实际上这些年下来，这种方法是不可行的（至少我没看到过成功案例），一个是设计文档写得太细，其实成本上已经跟写代码没差别了，不利于分工协作；另一个是写代码本身是一种创造性的劳动，当你把文档写到伪代码那么细，具体负责代码实现的没什么好发挥的空间了，都变成体力劳动了。

推荐的做法是写设计文档时不要太细，同时应该把具体模块的设计交给负责这个模块开发的人去做，指导他完成设计。这样既可以更好地分工协作，也可以让程序员有机会成长和充分发挥其主观能动性。

No.26
晓伟呢。☀：需求分析之后是不是应该还有产品需求分析文档（PRD）和产品需求规格说明书？

宝玉：其实不必困惑这个问题，因为这本身没有特别的标准的。如果用瀑布模型开发，确实会有你说的文档，但如果是敏捷开发，可能会是另外的形式存在，例如每个小功能一个独立的用户故事，或者是独立的产品设计文档，只是讲清楚一个功能。

虽然形式不一样，但其目的都是一样：让大家可以讨论需求，可以理解需求。之前我有回复过有哪些文档的问题：

这件事需要讨论需要评审，要有文档作为讨论的依据，以及记录讨论的结果。比如各种设计文档；

这件事要有规范，要有文档保证规范统一，比如各种规范文档；

这件事要记录下来，作为以后的一个参考。比如各种报告、环境配置、操作手册、API 文档等。

No.27
hua168：什么是模块呀？模块有哪些分类？一般说的模块是业务模块？模块间“高内聚低耦合”，如果模块之间要进行通讯是不是用接口实现？如果有依赖关系越多的话的话那不是独立性越差？要实现“高内聚低耦合”，如果复杂一点的项目有难度吧？

宝玉：这个话题其实属于架构下面的。在技术里面，模块其实是对某一种类型需求的抽象。举个例子来说，一个博客系统，博客的帖子是一个模块，评论是一个模块，用户是一个模块，帖子和评论又可以进一步抽象成内容模块。

模块的分类看你是从架构层面看还是从业务层面看。比如说从架构层面看，一个普通的博客网站可以看成三层：UI 层、业务逻辑层和数据访问层。其中 UI 层包含帖子列表模块和博客文章阅读模块；业务逻辑层则是帖子业务模块、用户业务模块。

如果从业务层面看，包含博客阅读模块和后台模块，更偏向功能的分类。

高内聚低耦合是架构里面的概念。因为架构设计，需要把大的系统拆分成小的模块，拆分后，还要通过约定的协议通信。典型的有前后端分离然后通过 REST API 通信，还有像类库之间直接通过公开的方法调用。

低耦合意思是项目没有什么依赖，改动互相不受影响。比如说前端和后端之间通过 API 通信，只要 API 不变，无论你后端用什么语言，跟前端都没关系。

高内聚指的是一个模块都是关系很紧密的代码，比如用户模块，所有用户操作的功能都在用户模块里面，关系紧密，也不需要依赖于其他模块。

No.28
kirogiyi：在敏捷开发中产品部门怎样参与产品设计会更好，或者以什么方式参与会更好？

宝玉：你问的是产品部门参与产品设计，还是开发部门？

首先从技术角度，好的产品设计要让产品设计在技术上实现不要成本太高，所以在一些技术难度高的设计上两边要多沟通确认，共同制定出技术难度适中，又满足好产品需求的设计。

然后从产品角度，开发必须充分理解产品设计才能设计出符合产品需求的架构，才能开发出满足需求和好的用户体验的产品。所以开发需要多和产品设计反复沟通需求，然后将确认后结果体现在文档上。

至于参与方式，主要还是看什么形式比较好。比如可以安排需求评审，关键节点让主要开发人员参与确认技术可行性和成本以及建议。比如有分批次的需求讲解会议想开发讲解产品设计，回答理解不清楚的问题。每个 Sprint 产品经理都要参与其中，及时和开发沟通确认需求不明确的问题！

No.29
alva_xu：在目前前后端分离、Restful 风格的应用架构下，是否更容易实现原型设计时的代码的重用率，以提高开发速度？具体是怎么做的？

宝玉：以前在讨论开发模型的时候有介绍，快速原型开发模型有两种模式，一种是抛弃型的，就是用工具开发的这种；一种是演化型原型，就是类似于 MVP，先做简单核心功能，然后不断演化，变成最终产品。

如果你要提升代码的重复率和开发速度，这种前后端分离的呀，我给你的建议是用一些第三方 API 云服务：
https://www.apollographql.com/
https://firebase.google.com/products/firestore/

这样你就完全不用考虑后端开发了，直接用它们定制就好了。等到产品开发出来，你再考虑后端迁移。

No.30
LDxy：Windows 系统已开始就是作为一个产品开发的，最初的项目团队应该是很有产品意识的；而 Linux 系统的开发者最初好像并不是把它作为产品开发的，这是不是也是造成如今 Linux 和 Windows 相比对大多数用户的易用性差别很大的原因？这是不是也是产品意识差异导致的结果？能不能作为一个说明产品意识的例子？

宝玉：我觉得 Windows 和 Linux 产生的差别还是因为产品定位的不同导致的。前者是商业产品，面向普通用户；后者是开源产品，面向专业用户。

精选留言：

西西弗与卡夫卡：
最近有个项目延期，原因之一就是用到的第三方库需要 https 绑定域名，测试环境因为用 http 所以没有发现该问题。事先的可行性研究，目的就是消除或者平衡项目中的技术风险、能力风险、协作成本、法律、部署等风险。

总结里给出了一个可行方法，即尽早上线部署，不对外公开服务即可。像法律问题，靠及早软件部署没法解决，可以有个检查清单，每类风险都给出适当评估意见。

相关阅读：09 | 可行性研究： 一个从一开始就注定失败的跨平台项目

Felix：
机缘巧合转管理已经快两年了，以下说说我的看法：

1. 大局观，我十分赞同老师的观点，我领导经常耳濡目染地这么教我们，我也觉得这是我转管理后的最大收获，不能像以前看着自己的一亩三分地，站在全局考虑问题；正如张一鸣说的那句，“工作时不分哪些是我该做的，哪些是我不该做的”，这句话对我影响很大，做事不设边界，才能我有更大的成长，而这些对管理来说尤为如此

2. 流程规范，接手管理后，发现虽然我们有一大堆流程规范的 wiki，但很多形同虚设，我个人总结有以下几点问题：（1）不能很容易找到对应规范 wiki； (2) 很多规范冗长复杂，不知所云； (3) 流程规范太多，没时间看。

于是我第一步就是整理杂草丛生的 wiki，先保证目录清晰，让大家按目录写 wiki，对号入座，查阅起来方便有条理，接下来挑出重点的流程规范进行了简化微调，每周会重点强调 1-2 个，在本周工作中重点关注，并适当提醒，渐渐大家一起走入流程的正规，并也体会到了按流程规范走所带来的便捷。

3. 管理该不该写代码，我觉的这事不能一棒子打死，我的观点有点像党的一句老话：从群众中来，到群众中去，在项目关键时刻负责一个小的 Ticket，我觉的有以下几点好处：

因为平时的 code review 不可能面面俱到，这么做更加深入了解系统底层结构和代码，更加容易指导员工的技术细节问题 ；

让自己不是光说不练，纸上谈兵的领导，我觉得从我本身而言，可能中高层确实不必要，但我认为这对于一线管理还是很有必要的，能够树立威信，合作沟通更加顺畅。

4. 自己的管理风格，关于大棒还是胡萝卜，确实不同的公司、团队应该有不同的管理风格，这里没有对与错，但我认为对于扁平化的互联网公司，各种大牛，严厉的风格是我不提倡的，像老师说的激励帮助下属，团队氛围融洽，大家自驱地做事情我认为更可取。

相关阅读：10 | 如果你想技术转管理，先来试试管好一个项目

alva_xu：
关于技术转管理，先从项目管理开始。这个观点我极其赞同。以下我谈谈自己的想法。

1. 老师举的是软件开发项目管理的例子，假定的项目经理是有开发技术的，所以需要克制自己不要有写代码的冲动，这一点我极其赞同。但假如项目经理以前并不是写代码的，这时候怎么办？我倒是觉得，应该学点代码，尝试写点代码，深入理解软件开发框架，培养点软件架构思想，才能充分理解开发人员的境况，更容易和自己团队甚至客户进行交流。

同时无论你过去是开发大牛、还是应用架构师、领域专家、还是基础架构师，除非人员安排如此，否则，千万不要越俎代庖，把这些事情交给负责这些事情的人去做，你可以做的就是帮助指导，而且尽量要从方法上去指导，“授人以鱼不如授人以渔”。特别是一个比较固定的团队，培养一个人的成长比样样事必躬亲要好。

2. 管理牵涉到“人”“工具”“流程”三个部分的使用。项目经理首先需要学一些管理学的知识，如何激发”人“的潜力已完成目标是管理的最主要目的，所以一些管理理念，比如 MBO，管理方法（沟通技巧）都得学一点。

对于“工具”，好的工具和差的工具效果不同，但更主要的是要用好工具，比如敏捷模式中，像 Jira，或者 VSTS 等都是很好的管理工具，也就是老师讲的 ticket 工具，但怎么用好它，需要项目经理在团队内外进行培训推广，常抓不懈。还要考虑怎么把“流程”固化到工具中，那么项目管理就可以行云流水了，所谓子在川上曰，逝者如斯夫！

3. 当“人”“工具”“流程”都发挥了它们的作用的时候，项目经理就需要凭借自己的知识和经验、善于发现风险，管控风险。这时候，我觉得风险管理是项目经理最大的责任。特别是控制好“范围”（防止项目过程中范围扩大或者变小），“成本”和“时间”，以最终达到合理成本下按时交付完整的达到质量要求的项目交付物。

以上几点，也是我从基础架构规划实施、然后做基础架构项目，现在管理软件开发项目好多年来的对于项目管理的一些经验，和大家共享，也请老师点评。

相关阅读：10 | 如果你想技术转管理，先来试试管好一个项目

javaadu：
1. 不同的岗位有不同的职责，基层管理者的职责并不是单纯的管理，要兼具技术深度、技术视野、项目管理、团队管理等技能。

2. 关于“写不写代码”的讨论，作者说这句话的意思是，项目管理者要明白，写代码并不是万能药，不能过分得关注细节，要跳出来，看全局，要明白自己的职责——管理项目过程、控制风险，拿到结果。

至于说是不是要写代码，那是另外一个问题，阿里现在已经取消了技术线的纯粹的管理岗位，就是希望技术线的基层领导者都不要把技术丢掉，要能跳出来看全局，同时也能带领团队打硬仗。

3. 我有转型管理的计划，我希望自己能够实现从个人的成功到团队的成功，原因是：个人的成功，影响力有限，团队的成功才能完成更大的成就。

我计划按照老师说的，从项目管理入手。遇到的困难就是自己的大局观不够，一冲动就喜欢自己上，这样的情况很不好：自己累的要死，还没什么成就感，然后团队其他成员也得不到充分的的锻炼。希望在后面的工作中，如果有项目管理的机会，自己能够改善自己的大局观。

相关阅读：10 | 如果你想技术转管理，先来试试管好一个项目

纯洁的憎恶 ：
进步的关键是角色转换，级别越高离具体工作越远，对人和资源的驾驭能力越强。项目管理就是要管好人和事。管好人就是正确引导客户的期待，用流程和规范管理团队。管好事就是选择适当的模式，制定计划，防控风险。持续成长是勇于跳出舒适区进入学习区。

相关阅读：10 | 如果你想技术转管理，先来试试管好一个项目

MiracleWong：
根据自己的经验写一下。

1. 很多的时候，我们不愿意制定计划的原因，简单地说是“懒”，深层次的原因是不愿意“思考”，因为这需要做很多准备工作，并消耗很脑细胞，是对自己认知上的一个考验。再往深处挖则是“不愿意承担责任”（工作中由其会遇到类似的同事，我拿多少钱就做多少工作，偶尔自己也会成为这种人)，因为要介入制定计划、以及后续的调整，就觉得这不应该是自己的工作量。

2. 就是制定计划的颗粒度粗细的问题。自己经常会遇到类似的困扰，就是一次计划，分解的太过详细，导致行动的时候因为繁琐反而拖延或直接不做。（也明白这是一种心理上的自我欺骗，认为做了计划就等于行动了，类似于买个课程就等于学习了，收藏了就等于看了。）

这就会导致下一次的计划时遭到“反噬”——上一次那么详细也没什么用，还不是不做或者稍微写一下呢。等到自己没有什么目标或者虚耗摸鱼时，又记起“详细计划”的好，一次次的循环。

3. 对于宝玉老师说的是否有制定计划的习惯，我经常是每个月的最后两天，制定下周的目标（类似工作计划）。将目标和自己的工作生活学习联系起来并进行分解，分散到每个月的四周里。每周做个小结，月底再做月总结和下月目标。目前还是在尝试练习中，在逐步形成自己做目标和总结的固定模板，省去部分重复性的工作。

相关阅读：11 | 项目计划：代码未动，计划先行

alva_xu：
计划就是为了把项目的各种资源（人力资源，软件资源，硬件资源等）有序组织起来，以便及时识别变化、应对变化。所以做计划的时候，一要考虑如何使计划更加准，二要考虑一旦有变化、计划如何能更加容易调准。

方法可能就是：
1. 尽量把任务拆解，和任务执行者一起确定故事点（scrum 里的说法，这里借用一下）。这样的话即使计划变化，并不是每个任务都变化，计划调整就快；

2. 对任务进行合理排序，找出关键路径和关键节点，项目的风险就比较容易识别。计划调整就能更加及时有效；

3. 通过设立指标和看板，利用项目管理工具及各种形式的会议、报告，及时收集监控项目情况，适时发现问题，及时调整计划。

相关阅读：11 | 项目计划：代码未动，计划先行

alva_xu：
我来谈谈对老师讲的几个点的个人看法和实践。

1. 方法和流程规范的区别

老师讲的很对，流程规范是在很多经验总结后形成的。从 ITIL 流程来说，这里的方法实际上可以理解为事件管理的范畴，就是发现了一个 incident ，想办法去解决，甚至用 work around 的方法去解决。当相同的 incident 发现次数多了，在 review 的时候，事件就上升成为问题。问题管理就是用来彻底避免相同事件重复发生的。

而规范流程是问题管理的一种手段。问题管理会带来变更管理，规范流程的制定和修改，是可以纳入到变更管理中的，只要纳入到变更管理，就自然会考虑到沟通机制、回退计划等事情。

我们也碰到过类似老师提到的该数据库的问题。刚开始数据库该出问题了，我们就处理数据库问题，后来，总结下来，需要严格改数据库的流程，比如增加业务运维和基础运维的经理审批才允许修改数据库，改数据库的流程我们也花了很多时间进行优化才真正固定下来。

2. 流程规范工具化

我觉得，除了工具化，还要尽量自动化。举个例子，我们这边最早采用 checkstyle 和 findbug 嵌入到 IDE 的方式进行代码检查，然后规定每个项目必须用这两个工具。但后来发现，这个规定执行的很不好，许多项目组没有自觉执行，增加了 QA 团队的检查工作量。后来我们采用 sonarqube, 并把它集成到 ci 里，就不怕项目组不执行了。

3. 推广执行的问题

除了前面两个方法，纳入变更管理和纳入自动化流水线之外，还有一个特别重要，那就是考核问题。但这个有很大的难度。有些规范的执行力度很难量化考核 。举个简单的例子，测试用例和需求文档的匹配问题，还有比如压力测试的性能指标问题，如果没有工具和环境，这简直会把 QA 愁死。所以，流程执行的好坏，还是与人和工具技术有关，三者互相关联，缺一不可。

相关阅读：12 | 流程和规范：红绿灯不是约束，而是用来提高效率

青石：
组织会议，一定要有会议时间、地点、人员、主题，会前要有准备、会中讨论要有结果（指定干系人）、会后要跟踪。没有主题、没有讨论结果、没有跟踪的会议，都属于无效会议。

相关阅读：13 | 白天开会，加班写代码的节奏怎么破？

kirogiyi：
完全手工方式管理的优点在于自由空间大、项目结构松散，比如临时添加需求、临时添加人员、临时改变策略等。一旦管理者没有足够的能力去驾驭项目的整体架构，随着项目时间的推移，项目不是越做越简单，而是越做越难，可能到处都是窟窿，根本没法持续下去，并且责任和义务大部分集中于项目管理者。

尽量采用软件工具管理的优点在于对需求、人员、进度、里程碑等可以进行事无巨细的分解或者组合，明确每个人的职责，明确每件事完成的要求，既可以让参与人员看到长期目标，也可以让他们看到短期目标，而不是遥遥无期。可以这样讲，没有路标的 100 公里总是比有路标的 100 公里来得费尽得多，还有就是很容易让参与者失去信心，丧失斗志。

相关阅读：14 | 项目管理工具：一切管理问题，都应思考能否通过工具解决

刘晓林：
我觉得辅助计划工具是从项目规划和任务分解出发，以任务之间内在逻辑关系为依据组织任务，优点是能够清晰地看到整个项目的蓝图，缺点是结构化程度太高，不够灵活，不能适应项目执行期间遇到的变化。

基于 tickt 的管理跟踪系统是从项目执行的角度出发，以执行周期为依据组织任务 (如一个 sprint)，注重任务的状态跟踪，优点是灵活；缺点是缺乏结构化，各任务之间的关系不明确，容易只见树木不见森林，因此不适合做项目规划和任务分解。

因此，需要将二者结合起来用，在规划和任务分解阶段，用项目规划工具，生成蓝图，最后把分解后的任务做成一个个 tickt，做项目跟踪。

相关阅读：14 | 项目管理工具：一切管理问题，都应思考能否通过工具解决

alva_xu ：
ms-project 这样的计划工具，适合于项目整体计划的把控，人财物的协调。ticket 系统适合于每个阶段任务的安排、变更和任务跟踪。

两者一个全局一个局部，在敏捷项目里应该结合起来使用会比较好。项目整体计划抓大的 WBS ，不作过度深入的 WBS，而 ticket 系统可以跟踪管理局部的变更，是计划管理的子集。

所以我的经验往往是先做一个全面的迭代计划（用甘特图）, 基于此做人员安排和工作安排，并拿此作为汇报的依据向领导汇报。当然，这种模式适用于项目整体目标清晰，时间节点容易规划、每一阶段工作都容易估算的项目。

相关阅读：14 | 项目管理工具：一切管理问题，都应思考能否通过工具解决

青石 ：
领导常说“大脑是用来计算的，并不是用来记忆的。”工作年头越多，越习惯将平时操作的过程整理成文档，分享给内部成员。

学的东西越多，记住的内容往往越少，将重复或可整理的内容写成文字，保存起来，使用的时候知道去哪里找就好。这也正是索引 / 缓存的妙处，利用大脑有限的 Cache 资源缓存常用的内容索引，将不常用的内容存盘，需要的时候再次加载。

相关阅读：16 | 为什么你不爱写项目文档？

一路向北：
我们的项目文档基本上是以协议和流程为主，写这类文档的时候，实际上已经把项目的每一个细节都考虑清楚了，经过几次的 review 之后，后面的项目实现就是根据文档的内容再继续细化，一旦遇到不太清楚的地方，再回头翻阅文档，也很容易知道当初设计的时候是怎么一回事。

套用格式确实是一个比较好的方式，填空总是比直接写作文要简单的多，而一旦整个空都填满之后，再继续润色，细化那又会比一开始简单些。写文档，记笔记等，用对工具还是很重要的。

相关阅读：16 | 为什么你不爱写项目文档？

青石：
赞同老师的“价值体现在产品之上”。技术能力越强，增长曲线越缓慢。实际开发过程过程又大多是满足需求，而不关注质量。企业雇佣关系也更倾向于成本低、增长曲线高的程序员（大不了用你的薪水雇佣两个），所以就出现老程序员的无奈。那么技术在达到一定程度后（增长曲线减慢，收益比下降），同时横向扩展，丰富自己的知识体系结构，不失为一种保值方式。

技术通过努力都可以达到差不多的水平，不同的是思维方式和所处的高度。不断学习的过程，其实就是让自己了解的更多思考的越多，思考的越多站的高度自然更高。

入门时写代码是为了实现功能，深入下去会想了解它的实现方式，接着尝试举一反三将思想运用到其他地方。培养产品意识也是从全局看问题，站的越高，望的越远。

相关阅读：19 | 作为程序员，你应该有产品意识

kirogiyi ：
程序员的焦虑是自己吓着了自己，一些负面词汇听多了，潜意识里难以平息内心的恐慌（码农、大龄程序猿、996、ICU 等等），只想着能赚钱的时候赶紧赚一笔，至于技术进步和长远打算，都只是锦上添花而已，愿意做出一些无价值的付出。越临近这些负面词汇的边缘越是心急如焚，要么逃命去吧，要么留在原地观望，要么综合培养自身硬实力和软实力，前两种会逐渐淘汰，后一种会有顽强的生命力。

宝玉老师这里讲到产品意识，我认为这是一种软实力的培养，它能辅助你的硬实力做出更好的项目，也能拓展自身在技术以外的视野。其实，有时观察下来，技术能力强的人一直忙个不停，可以解决很多问题，问题却好像没玩没了似的；技术能力一般，喜欢沟通，具有一定产品意识的人，上午一杯咖啡，下午一杯茶，安安心心按时下班，轻轻松松交出项目成果。我认为，程序员在一定的阶段，不能只关注自身技术实力的成长，忽略了其他方面的成长。这就像一个偏科的人，永远拿不到第一名，而那些各科成绩均衡，没有一科成绩第一的人却成为了第一的道理是一样的。

有句话，一直记得很清楚：吾生有崖，而知无崖。学新技术也是一样的，不一定死搬硬套要去学会，这样学习成本会很高，但一定要去关注，知道什么时候、什么地方可以用得上，一般有经验的技术人都能在短时间内学会，尤其对大龄技术人员。一旦时间久了，关注的点就不一样了。思维开始转换，然后从更高、更深的层次去考虑问题，才能真正体会到“技术是工具”这句话的深刻含义：工具可以换，思维可以变，灵活多变最重要。

相关阅读：19 | 作为程序员，你应该有产品意识

alva_xu：
InfoQ 上有篇文章供参考： 35 岁的程序员是“都挺好”还是“都挺惨”？
https://mp.weixin.qq.com/s/1q82RO4gRAXtuFeDGV4qRw

实际上，和年轻人相比，在学习能力上，总会有瓶颈。不拼体力、不拼脑力，我们拼经验，拼沉淀，拼吃的盐比你多。所以，我们在成长过程中，一定要注重学习、消化和沉淀，从表层易变部分向底层基础部分转移，从程序员向架构师产品经理转型。持续学习、多学方法论，不断扬弃，顺势而为！

相关阅读：19 | 作为程序员，你应该有产品意识

果然如此 ：
1. 提升需求确定性：设计高保真的原型，如用 axure，不仅仅是画框图，还要加各种响应事件等；
2. 提高需求变更的成本：提前约定变更制度，签字画押；
3. 降低响应需求变更的成本：提高技术框架水平。

以上是通过产品、流程、技术三个方面解决需求变更问题。

相关阅读：20 | 如何应对让人头疼的需求变更问题？

思辨时刻

dancer ：
管人和管事，言简意赅，受教了! 但是对是否写代码，我个人的看法是，对于一个一线技术管理，比如不到十人技术团队的 leader，我觉得时刻保持学习新技术，写写代码还是有必要的。好处一是做技术选型或者评审设计的时候，不会把团队带跑；好处二是做技术决策的时候，更有说服力。总儿言之，就是要有一定的技术领导力。

宝玉：
你这个补充很好，我在文中说的有点绝对了，客观一点说法应该是尽可能保持一个合适的比例！但管理的团队越大，职责越多，那么要写的代码比例就要越少。

相关阅读：10 | 如果你想技术转管理，先来试试管好一个项目

好，今天的加餐就到这里，非常感谢同学们用心的留言，也希望我们专栏的同学都能每日精进，学有所成！

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

21 | 架构设计：普通程序员也能实现复杂系统？

你好，我是宝玉，我们已经正式进入到“系统设计”这个主题模块，今天我们先来聊一聊“架构设计”。

早些年，软件很简单的时候，不需要需求分析和架构设计，直接采用边写边改（Code And Fix）模型，也能做出来。后来软件复杂了，就对程序员要求特别高了，所以早些年的软件开发，都是个人英雄主义盛行。比如张小龙一个人完成了 Foxmail，求伯君完成 WPS，王江民写 KV 杀毒软件……

不过，那时候对于普通程序员来说，去写这样复杂的系统，也是可望而不可及的。再后来软件产品越发复杂后，靠高手的开发模式也不可行了。

软件需求越来越多，而高手又是稀缺资源，所以要解决的一个问题就是：让普通程序员也能参与其中，一起实现复杂系统，而不必依赖于很多精英。

为什么软件项目需要架构设计？

要想实现让普通程序员也能实现复杂的软件系统，我们先要看看什么样的是复杂的软件项目。复杂的软件项目，通常有两个特点：需求不确定和技术复杂。

关于需求不确定，我在前面的文章已经讲了很多，我们主要来看看技术的复杂性。技术的复杂性，主要体现在四个方面。

1. 需求让技术变复杂

如果需求本身很复杂，那么对应的技术也会很复杂。比如说你做一个个人博客网站，和做一个淘宝这样的网站，技术复杂度是有天壤之别的。

要响应需求的变化，也会让技术变复杂。对于明确的需求，相对来说技术实现是容易的。但是，随着需求地不断变化，新的需求可能会破坏原有的代码架构，导致系统越来越臃肿复杂，维护也越来越难。

2. 人员会让技术变复杂

现在软件开发通常不是一个人，而是一个团队。团队成员水平不一样，擅长的技术方向也不一样，让这一群人有效地协作也是很大的考验，简单的技术问题也会变成复杂的技术问题。

3. 技术本身也是复杂的

现在软件项目中选择编程语言、框架、技术组件、数据库等技术或工具，还可能需要应用像微服务、大数据、人工智能技术，这些技术本身就是复杂的，普通人都需要通过一定的学习才能掌握。

4. 要让软件稳定运行是复杂的

软件在开发完成后，要发布运行，但运行时也充满了各种不确定性。比如说明星发布八卦可能会导致微博宕机；阿里云宕机导致很多基于阿里云的系统也跟着一起无法提供服务。

因为技术的这些复杂性，会导致软件开发变得很复杂，开发成本很高。而架构设计恰恰可以在这些方面很好地解决技术复杂的问题。

首先，架构设计可以降低满足需求和需求变化的开发成本。

对于复杂的需求，架构设计通过对系统抽象和分解，把复杂系统拆分成若干简单的。就像淘宝这样复杂的网站，最终拆分成一个个小的微服务后，单个微服务开发的难度，其实和个人博客网站的难度已经差不太多了，普通程序员都可以完成，降低了人力成本。

对于需求的变化，已经有一些成熟的架构实践，比如说像分层架构这样把 UI 界面和业务逻辑分离，可以让 UI 上的改动，不会影响业务逻辑的代码；像 Wordpress 这样基于插件和定制化的设计，可以满足绝大部分内容类网站的需求，降低了时间成本。

其次，架构设计可以帮助组织人员一起高效协作。

通过对系统抽象，再拆分，可以把复杂的系统分拆。分拆后，开发人员可以各自独立完成功能模块，最后通过约定好的接口协议集成。

比如说前后端分拆后，有的开发人员就负责前端 UI 相关的开发，有的开发人员就负责后端服务的开发。根据团队规模还可以进一步细分，比如说前端可以有的程序员负责 iOS，有的程序员负责网站，这样最终各个开发小组规模都不大，既能有效协作，又能各自保证战斗力。

再次，架构设计可以帮助组织好各种技术。

架构设计可以用合适的编程语言和协议，把框架、技术组件、数据库等技术或者工具有效的组织起来，一起实现需求目标。

比如说经典的分层架构，UI 层通过选择合适的前端框架，例如 React/Vue 实现复杂的界面逻辑，服务层利用 Web 框架提供稳定的网络服务，数据访问层通过数据库接口读写数据库，数据库则负责记录数据结果。

最后，架构设计可以保障服务稳定运行。

现在有很多成熟的架构设计方案，可以保障服务的稳定运行。比如说分布式的架构，可以把高访问量分摊到不同的服务器，这样即使流量很大，分流到单台服务器的压力并不大；还有像异地多活这样的架构方案可以保证即使一个机房宕机，还可以继续提供服务。

其实，满足需求和需求变化、满足软件稳定运行是架构的目标，对人员和技术的组织是手段。架构设计，就是要控制这些技术不确定问题。

我们也可以说：架构设计，就是通过组织人员和技术，低成本满足需求以及需求的变化，保障软件稳定高效运行。

什么是架构设计？

你现在已经知道了架构设计的价值，那么究竟什么是架构设计呢？要说清楚这点，我们可以分别从目标和方法两个角度来看。

最开始我以为架构设计的目标是满足业务需求，保证软件能正常工作。后来发现这其实只是最基本的要求，因为很多糟糕的架构设计，也能满足业务需求，让系统正常运行。

比如说有人把一个小网站拆分成几十个微服务运行，也是一种架构设计，但是这样，无论是开发成本还是运行成本都很高。

所以架构设计的目标，是用最小的人力成本来满足需求的开发和响应需求的变化，用最小的运行成本来保障软件的运行。

架构设计，已经有很多成熟的方法。比如说：

使用微服务这样的架构，把复杂系统拆分成一系列小的服务，服务再拆成功能模块，让人员更好地分工协作；

通过前后端分离，让程序员更专注于某个知识领域，降低开发难度；

用分层设计来隔离业务逻辑，减少需求变更带来的影响。

这些架构设计的方法，其实都是基于工程领域分而治之的策略，本质上就是将系统分拆，将人员分拆。但是光拆还不够，拆完了还得能拼回来，所以你要清楚架构设计的“道”。

架构设计的道，就是组织人员和技术把系统和团队拆分，并安排好切分后的排列关系，让拆分后的部分能通过约定好的协议相互通信，共同实现最终的结果。

这很像乐高玩具，将一个个小的模块通过接口拼接在一起，搭成一个大的模型。只不过在程序中，换成了其他形式的接口，比如前后端通过 REST 这种协议交互，内部组件之间通过方法调用；在软件项目中，人员从大的开发团队被分拆成小组后，小组之间通过流程规范协作。

如何做好架构设计？

架构设计，总给人感觉技术很高深。所以很多新手一提到架构设计，就有些畏足不前，不知道该从什么地方下手。

架构设计要做好，确实不是一个容易的事，需要大量的经验积累。但业界已经有了很多成熟的架构设计模式，我们不需要闭门造车，可以在理解清楚业务需求后，找到相近的架构设计，然后基于成熟的架构设计方案，进行改造，变成适合自己业务需求的架构。

接下来我就以极客时间的服务端为例，来简要说明一下如何做架构设计。假设现在你要设计第一版本极客时间服务端的架构，只有专栏课程一个核心功能，目标用户访问量是日 PV 上万，峰值每秒 10 个左右访问，对稳定性要求高。那么，你该如何做呢？

第一步：分析需求

架构设计，最基本的就是要能满足业务需求，所以搞清楚需求是至关重要一步。而产品需求，只有功能的描述，界面的交互，还需要进一步进行抽象。

一个常用的分析方法就是分析用例，也就是了解主要用户角色和其使用的场景。

我们如果把极客时间的专栏课程功能画成用例图，大概如下所示：

从图上可以看出，有四种角色：编辑、专栏作者、未付费用户和付费用户。每个角色有其独特的功能，有些角色之间还有通用的功能。还需要注意的一点是，每个用户，都可能会通过不同的设备终端来使用这些功能：网站、安卓手机、iPhone 手机。

第二步：选择相似的成熟的架构设计方案

在了解清楚需求后，就可以从业界成熟的架构设计模式中选取一个或几个。当然，具体选择哪些架构设计模式，需要你根据平时的学习积累来做判断。到这个阶段，同时还要考虑使用的语言和框架。

极客时间服务端，主要包含两部分内容，一个是给手机客户端提供的 API 服务，还有就是网站需要的 Web 服务。第一个版本其实访问量并不大，我们完全可以把 API 服务和网站服务合并成一个服务。另外专栏的内容，文字内容涉及数据库的存储，同时音频涉及文件存储。

这其实是一个典型的网站架构，可以基于传统的分层架构来实现。分层架构按照水平方向将系统拆分成几个层，每层都有清晰的角色和分工，不需要关心其他层的细节。

（图片来源：《软件架构入门》）

在选择好架构方案后，还需要考虑选择什么语言和开发框架。这部分选择需要根据团队情况和项目情况来综合评定。

比如说团队以 PHP 程序员为主，就没必要贸然选择 Java 作为开发语言；如果以 js 程序员为主，就可以考虑使用 Nodejs。因为这样的选择，能让团队不需要太多的学习成本。

第三步：自顶向下层层细化

其实我们专栏从工程思维开始，到后面提到的写文档、原型设计，都是建议能从整体到局部，这样更具有大局观，不容易过早陷入技术细节中。架构设计也是如此，好的实践是自顶向下层层细化。

在选择好成熟的架构设计方案后，可以基于方案，层层细化，逐步完善、调整和优化。

部署架构

这类分层架构网站，部署也比较简单。为了减少运维成本，我们可以基于云服务设计部署架构，选购云数据库和文件存储，选购虚机作为网站服务器。

那么部署架构可以比较简单，如下图所示：

基本上这个架构就可以基本满足运行需求。但要做到稳定性高还不够，万一数据库挂了或者网站服务器挂了，都可能会让服务中断一段时间。

所以我们可以增加一台异地网站服务器和一个异地云数据库实例作为备份，这样一旦网站宕机或者数据库有问题，可以切换到备机，马上恢复访问。所以调整后架构如下：

这样我们就很好的满足了对运行稳定性的要求。

分层和分模块

按照分层架构的思路，我们可以把系统分成四层。

用户界面层：用户界面，负责展现功能以及和用户交互。
服务层：API 服务和 Web 网站服务。
业务逻辑层：实现业务逻辑，比如说如何读取用户订阅的专栏列表。
数据访问层：对数据库的访问。
数据存储：用数据库保存数据，文件库保存音频文件。

分层分好后，还需要基于前面的用例图，把相同的功能抽象出来，设计成模块，比如说留言相关的都放到留言的模块，文稿相关的都放到文稿模块。

最终的设计图大概会是这个样子：

在分层和分模块之后，就可以很好的对人员进行分工，可以把具体工作细分到某一层的某个模块。

API 设计、数据库设计、模块的设计

在分层和分模块的设计完成后，就可以对 API 进行设计，对数据库进行表设计。这部分就不展开细讲了。

还有一些模块的设计，还可以让负责开发该模块的程序员参与一起设计，这样一方面让他提前熟悉设计，另一方面也可以让他锻炼设计能力，提高参与积极性。

第四步：验证和优化架构设计方案

在技术方案完成后，还需要去验证方案是不是满足设计的目标，能否满足需求和未来需求的变化，能否保障软件有效地运行。

方案的验证是贯穿整个设计始终的，一个完整的架构设计方案，需要有多次的评审会议，充分收集各方面的反馈，反复修改后才能最终确定下来。

在第二、三步，可能会生成几个技术方案，这时候就需要做出一些技术上的决策。决策时，需要考虑清楚方案是否能低成本的完成软件需求的开发，同时能低成本的运行和维护该软件。还有你要考虑架构预期要满足多长时间的业务增长，比如说半年还是一年还是三年。

在架构设计确定后，就可以基于架构设计的结果大家一起分工协作了。架构设计并不是确定后就不修改了，在实际开发的过程中，还需要根据情况对架构进行优化和调整。

比如说实际运行的时候，发现 API 访问量很大，会拖慢网站访问速度，那我们就可以考虑把 API 和网站分拆开来，各自做成单独的服务，避免相互干扰。

推荐学习材料

O’Reilly 出版过一本免费的《Software Architecture Patterns》， 介绍了五种最常见的软件架构。阮一峰老师有在软件架构入门中对其各种模式进行介绍。

还有像《架构师之路》这样的开源电子书，对于服务端架构中常用的设计，有很不错的总结。《图解：从单个服务器扩展到百万用户的系统》这篇文章对单服务器到大用户系统的演变也有通俗易懂的总结。《以“前浪微博”场景为例，谈谈架构设计流程四步曲》这篇文章也介绍了一种非常好的架构设计方法。

最后，推荐一本书：《架构整洁之道》。我觉得是架构设计书籍中写的最透彻最浅显易懂的一本。

总结

今天，我们一起学习了软件工程中一个非常重要的知识，那就是架构设计。

架构设计，是为了控制软件项目中技术复杂的问题。架构设计，通过组织人员和技术，低成本满足需求以及需求的变化，保障软件稳定高效运行。

架构设计可以通过四个基本步骤：

第一步：分析需求；

第二步：选择相似的成熟的架构设计方案；

第三步：自顶向下层层细化；

第四步：验证和优化架构设计方案。

通过良好的架构设计，可以有效降低开发难度和成本，让普通程序员也能实现复杂系统。

课后思考

请你思考一下，为什么淘宝这样的网站需要用微服务这样的架构，而一个个人博客网站不需要微服务？你认为极客时间这样的网站用的什么样的架构？也欢迎分享你觉得不错的架构书籍和文章。欢迎在留言区与我分享讨论。

感谢阅读如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

22 | 如何为项目做好技术选型？

你好，我是宝玉，我今天分享的主题是：如何为项目做好技术选型？

在架构设计过程中，肯定绕不开技术选型这个话题，大到架构、框架、语言选择，小到用什么组件、设计模式。

这也是最容易引起争议的话题，无论是现实中还是网上，到处有各种语言、框架的争论：Java 好还是 C# 好？前端框架是 Vue 好还是 React 好？跨平台手机开发，该选 React Native 还是 Flutter……

虽然这种争论从来没什么结果，但当你做技术选型时，却很容易受到这些信息的干扰，尤其是你身边有几个某种语言或者框架的狂热粉丝的话，他们会不停地在你旁边吹风，说他喜欢的语言或框架的各种好处。

包括我们自己做技术选型时，也会有很多个人偏好在里面。比如我以前对微软技术栈特别熟悉，也特别喜欢，做技术方案就会偏向微软技术栈；我喜欢 React，做前端技术选型，也会偏向 React 的方案。

通过上一篇架构设计的学习，我们知道，架构设计的主要目标，是要能低成本地满足需求和需求变化，低成本地保障软件运行。然而对技术的个人偏好，很可能让你在技术选型时，忽略架构设计的目标，导致满足需求的成本变高，或者运行成本居高不下。

所以今天，我们一起来探讨一下，在软件工程中，怎么样才能避免这种选型的倾向性，科学客观地做好技术选型。

技术选型就是项目决策

技术选型，就是在两个或者多个技术方案中选择适合当时项目情况的方案。技术选型看起来是个技术的选择，但其实是一个和项目情况密切相关的项目决策。

在项目中，除了技术上的选型，类似的选择也有很多，比如说产品设计中：某个功能该不该加？该选哪种动画效果？比如制定测试方案的时候，选择哪一种压力测试工具？选择哪个测试框架？这些选择，本质上就是一种项目决策。

要做好技术选型，就是要做好项目决策。那么怎样从做项目决策的角度来选择合适的技术选型呢？

受制于时间、范围和成本的约束

我们在《08 | 怎样平衡软件质量与时间成本范围的关系？》中学习了项目金三角的理论，也就是项目受制于三个因素：时间、范围和成本。

技术决策作为一种项目决策，也要受制于时间、范围和成本，在决策时不能超出这三者的边界。

比如说在项目时间紧时，决策上就要偏向能提升开发速度的技术；在成本吃紧的情况下，要多用成熟的免费的框架、工具，避免用贵的商业软件或者自己造轮子提升成本；在范围大、需求多的情况下，架构就要考虑如何能简单快速完成需求。

还要注意一个问题就是随着项目的推进，其实制约项目的三个因素一直在动态变化，需要及时根据情况调整技术决策。

举个例子来说，2004 年飞信 PC 客户端做第一个版本的时候，那时候主要约束是成本，只有一个 C++ 程序员，这个程序员会用什么技术就用什么技术，谈不上选型。

到 2005 年做第二版本时，有了几个人，但是时间上要求快，所以就选择了能提升开发速度的 C# Winform 技术方案。到 2008 年做第三版时，人手充裕了，也没有进度上的压力，这时候主要就追求用户体验、性能，所以又选择了 C++ 的技术方案重新开发。

要分析可行性和风险

我们在专栏前面的内容中学习了可行性研究和风险管理的知识。如果在项目决策时，不考虑可行性，不预估风险，就极有可能导致决策失败。

就像在《09 | 可行性研究： 一个从一开始就注定失败的跨平台项目》那篇文章中的案例，技术选型时，没有考虑到 License 的法律问题，导致项目失败。还有在《14 | 风险管理：不能盲目乐观，凡事都应该有 B 计划》那篇文章中的案例，选择 React 时，没有考虑到可能导致的风险，导致项目延迟。

当然，换个角度说，如果在项目中，选择新技术的风险可以接受，也能满足时间、成本和范围的约束，还可以达到丰富团队技术栈的目的，那也是可以的。

要考虑利益相关人

在做项目的决策时，如果决策时没有人代表利益相关的人，就可能会做出不考虑他们利益的决策。

选择适合的技术选型时，也要考虑到这一点。比如说光顾着选用新酷的技术，而没有考虑客户的利益，导致成本增加，进度延迟；比如在选择 UI 组件时，只想着哪个调用方便，而不考虑产品经理的利益，导致产品体验不好。

项目决策中常见的坑

无论是技术选型也好，还是其他项目决策，经常会遇到一些坑，一不小心就会踩上去。

把听到的观点当事实

现在网上充斥着各种观点：一个 React 的粉丝会给你描述 React 的各种优点，而不会告诉你学习曲线有多陡峭；一个不喜欢微软技术的程序员会把.Net 贬低的一文不值；一篇鼓吹 Mongodb 多好的文章可能是收了钱的软文。

每个人都有自己的观点没有问题，但是不能把观点当成事实，尤其是在做决策之前，至少需要验证一下。

先入为主，有了结论再找证据

在做技术选型或者项目决策时，还有一个问题就是可能心中已经有了答案，后面所谓的决策，不过是寻找有利于自己答案的证据。比如说我特别喜欢 React，在做技术选型时，就会拼命寻找对 React 有利的数据作为证据，这其实可能会导致结论并不客观。

所以当你选择技术选型的时候，要像做项目决策一样思考分析。要想你的决策能正确，就要注意项目中范围、时间和成本的约束，要分析可行性和风险，要考虑利益相关人，最后还得要避开常见的一些坑。

如何做好技术选型？

现在我们知道了要像做项目决策一样，去选择适合自己项目的技术选型，那么具体该怎样做呢？

我们在《02 | 工程思维：把每件事都当作一个项目来推进》中学习了工程思维和工程方法，在《12 | 流程和规范：红绿灯不是约束，而是用来提高效率》中学习了流程规范。对于技术选型问题，我们一样也可以考虑借鉴工程方法设计一套流程，基于流程去做技术选型或项目决策，来保证整个过程能科学可行，充分考虑项目决策的特点，避开常见的坑。

对于技术选型包括项目决策类的问题，我们可以分成：问题定义、调研、验证、决策这几个阶段。

问题定义

在问题定义阶段，需要搞清楚两个问题：为什么需要技术选型？技术选型的目标是什么？

以前看过一个技术漫画《The problem is not the tool itself》。

“我想我们已经达到 MySql 的极限，非常慢…我们得面对数据巨大的事实”，“我们应该迁移到 NoSQL，我已经准备了一个 3 个月的战斗方案，瞧瞧这”，“框架重构、培训、内部工具升级”，“等等，让我检查一下”，“你忘了建索引，加上索引可以让速度提升 20 倍”，“忘记索引？你可真行。” ​

（图片来源：Commitstrip）

这种事情在软件项目中可不少见，很多时候为了解决问题引入一个新技术，然而真的需要吗？也许我们可以基于现有技术方案进行优化，根本就不需要引入一个新的技术或新的框架。

还有一个就是技术选型的目标需要明确，你的技术选型目标是为了使用新酷技术呢？还是为了提升开发效率？还是为了降低开发成本？

只有明确了技术选型的目标，才能有一个标准可以来评判该选择哪一个方案。

就像上面例子中提到的第二版的飞信 PC 客户端，目标就是要提升开发速度，所以就选开发效率高的 C#。

调研

在明确技术选型的目标后，就可以去调研，看有哪些技术选型可以满足目标，包括开源的方案和商业的方案。

在调研时，可以参考前面“项目决策的特点”中的内容，从几个方面去分析：

满足技术选型目标吗？

满足范围、时间和成本的约束吗？

是不是可行？

有什么样的风险？风险是不是可控？

优缺点是什么？

在调研结束后，可以筛选掉明显不合适的，最终保留 2-3 种方案留待验证。必要的话，可以一起讨论，最终确认。

验证

一个技术是不是合适，如果不够了解，没有应用过的话，实际用一下是很有必要的。可以通过一个小型的快速原型项目，用候选的技术方案快速做一个原型出来，做的过程中才能知道，你选择的技术选型是不是真的能满足技术选型的目标。

就像前面举的飞信 PC 客户端的例子，在决定第二版本是否使用 C# 开发时，其实做了大量验证工作。当时.Net Framework 还不普及，要打包整个.Net Framework 到安装包里面，这体积就太大了，这是一个很大的问题。

后来发现有一个产品叫 Salamander，它可以只打包程序所需要的 dll 库文件，这样体积就可以控制在 20mb 以内，最后在制作安装包时，用 7zip 压缩，就可以让安装包控制在 10mb 左右。在验证阶段，证明了安装包体积是可以缩小的，基于 C# 开发是可行的，才最终选定了 C# 的技术方案。

决策

在调研和验证完成后，就可以召集所有利益相关人一起，就选择的方案有一个调研结果评审的会议，让大家提出自己的意见，做出最终的决策。

必须要承认，对于技术选型来说，是有不确定性的。即使通过上面的流程，也一样可能会做出错误的决策。但有一个科学的流程，至少可以保证提升做出正确决策的概率。

如果遇到很纠结的情况，就需要负责决策的人来拍板了，这时候其实并不一定有对错，重点的就是做出一个选择，然后按照选择去执行。有时候迟迟不选择、不拍板才是最坏的结果。

在项目结束后，也要对之前技术选型和项目决策做总结，不断的完善技术选型和项目决策的机制，帮助未来更好的进行决策。

总结

今天，我带你一起探讨了技术选型的问题。技术选型，本质上是项目决策的一种，也符合项目决策的一些特点。也就是说，技术选型的选择要受制于范围、时间和成本的约束，要分析可行性和风险，要考虑利益相关人。还有一些坑要小心避开，比如要避免把听到的观点当事实，要验证；要避免先入为主，不要有了结论再找证据。

要做好技术选项，要有一个科学的流程，首先要明确技术选型的目标，避免没必要的引入新技术；然后要充分调研；还要对备选的方案进行验证；最终和利益相关人一起决策。

技术选型，也不要太过于纠结，要勇于决策，选定了就坚定的去执行。

课后思考

你所在项目中是如何做技术选型的？有哪些不错的方法和原则？你在技术选型时，通常是选择保守的还是新酷的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

23 | 架构师：不想当架构师的程序员不是好程序员

你好，我是宝玉，今天我想与你讨论一下要想成为架构师，你需要具备哪些能力。

很多程序员的梦想，就是将来能成为一名架构师。包括我刚学编程那时候，也是以当架构师为目标，觉得不想当架构师的程序员不是好程序员，希望将来能成为一个优秀的架构师。就像拿破仑那句名言：“不想当将军的士兵不是好士兵。”

随着工作经历的增多，我也开始参与到架构设计中。对架构设计了解的越多，我越发觉，其实做架构设计，并不代表一定要有一个架构师的头衔。

拿破仑那句名言，原句是“Every French soldier carries a marshal’s baton in his knapsack”，意思是“每个士兵背包里都应该装有元帅的权杖”。

元帅的权杖，意味着大局观，元帅的思维方式。当士兵背包里装有元帅的权杖，就意味着士兵也能胸中有大局观，能有元帅的思维，理解元帅在特定战场上想什么，这样能更好的执行命令，提升整体的战斗力。

其实拿破仑的本意是激励每一名上战场的士兵都要有大局观，有元帅的思维，并不需要每一个人都一定去当将军、当元帅。

这也适用于技术领域，对于程序员来说，并不代表一定要有一个架构师的头衔，而是心中有大局观，有架构师的思维。从而能理解架构设计，能写出好的程序。

什么是架构师思维？

通过上一篇的学习，我们知道架构设计，是要控制技术的复杂性。对于架构师来说，要控制技术复杂性，有几种有效的方式：抽象、分治、复用和迭代。

架构师思维，其实就是这几种思维的集合。

抽象思维

抽象思维可以说是整个架构设计的基础。因为对于架构设计来说，是要为了满足业务需求的，而业务需求都是一些文字性的描述、原型、UI 设计图，这些需求要最终变成代码让机器执行，就必须先进行抽象，抽象成计算机能识别的模型。

其实抽象思维我们不陌生，因为我们从小学习的数学，就有很多抽象思维的训练。举例来说，我们小时候做的鸡兔同笼问题，看起来很复杂，但是如果我们会二元一次方程，把鸡抽象成 x，兔子抽象成 y，就可以用二元一次方程列出相应的方程式，从而求出解。

在软件项目中，遇到类似的场景，就会考虑抽象出来，总结一个规则和方法。有时候即使场景不同，也可以把其中有共性的内容抽象出来，可以更方便的使用。

举个例子，我们在之前文章中有对极客时间专栏做用例分析，其中有四个角色：编辑、作者、未订阅用户和订阅用户。其实这四种角色，都可以抽象成“用户”模型，然后通过对用户设置不同的角色属性，来应用成不同的角色。

还有像极客时间专栏的一篇文稿、视频课程的一节视频课，都有标题、内容、作者、留言等信息，所以可以抽象成“文章”模型，通过文章的类型、内容来区分专栏文稿还是视频课。

在架构设计中，对需求进行抽象建模后，可以帮助我们隐藏很多无关紧要的细节，我们在高层次的架构设计时，可以关注在几个主要的模型上，而不必关心模型内的细节实现。

分治思维

架构设计的一个重点，就是要对复杂系统分而治之，分解成小的、简单的部分。但光分解还是不够的，同时还需要保证分解后的部分能够通过约定好的协议集成在一起。

分治思维在架构设计中有多经典的应用。比如说上一篇介绍的分层架构，把 UI 部分与其业务逻辑部分隔离，这样这两部分就既可以各自进行变更，又互不影响。比如说 UI 交互修改，不需要修改业务逻辑代码，业务逻辑部分对性能进行优化，不需要修改 UI 界面。而每层之间，可以通过约定好的方法或者 API 进行交互。

还有像我们平时说的大数据，高并发这些复杂问题，也是通过分治来解决的。要知道单台机器，无论你性能如何优化，都是有其极限的。而像“双十一”这种高峰时刻，瞬间的流量可能是几百、几千万，就需要通过设计合理的策略，分化到不同的服务器，让每个服务器的流量不至于太大。参考：《秒杀系统优化思路》。

这种分治的思维其实不仅适用于架构上，也适用于平时程序员写代码。比如说有些程序员写代码，喜欢把大量的逻辑放在一个方法或者一个类里面，最后极其难以理解和维护，如果能分拆成几个小的方法或者小的类，不仅结构更清晰，也更容易理解和维护。

复用思维

复用是一种非常简单有效的提升开发效率的方法，通过对相同内容的抽象，让其能复用于不同的场景。

举例来说，我们前面提到极客时间的专栏和视频课程，可以作为两个不同的模块进行开发，但是实际上内容差不多，如果能抽象成同一个“课程”模块，这样专栏和视频课程的模块就可以复用“课程”模块，不需要维护两份相似的代码，进而提升开发和代码维护的效率。后面如果要增加每日一课和微课，也不需要重新开发，只要复用之前的“课程”模块即可。

以前我在 DePaul 读书时，要给学校做一个教学播放的软件，由于当时技术框架选的是 React，而 React 没有合适的视频播放组件，于是我只好自己实现了一个。实现完成之后，我觉得这个视频播放功能肯定有很多人也需要，如果能复用的话会很实用。于是我把它封装后放到 GitHub 上，解决了很多人需要在 React 中播放视频的需求。到现在已经有超过 1000 个 Star。

复用思维在日常写程序的时候也很常用，比如有的程序员喜欢复制粘贴代码，所以经常看到很多重复的代码，如果要修改，得修改好几个地方。如果能把这些重复的代码提取成公共的类或者方法，就可以减少很多重复，让代码更简洁和易于维护。

迭代思维

好的架构设计，通常不是一步到位，而是先满足好当前业务需求，然后随着业务的变化而逐步演进。

就像淘宝这样的业务，它背后的架构设计也不是一步到位成现在这样，拆分成好多微服务。最开始，它也只是个普通的分层架构，后来随着业务不断扩展，逐步迭代成今天这样复杂的架构。

这种迭代的思维，在写程序时也很重要。因为很多程序员喜欢追求完美，期望能一步到位，然而这样带来的问题是开发成本会大量增加，导致进度延误。另一方面，如果对需求的变化预测不正确，就会有很多冗余的代码，后面难以维护。

其实，开发人员对以上提到的这些思维模式都不陌生，只是在实践的时候，总是有意无意地忽略了。

好的架构师什么样？

对于程序员来说，培养架构师思维，并不是很难的事情。然而要成为好的架构师，光有架构师思维还不够。

一个好的架构师，不仅技术要好，还要懂业务；能从整体设计架构，也能在局部实现功能。

比如说一个做互联网软件架构设计有丰富经验的架构师，要去做建筑行业软件的架构设计，短时间内一定是很难设计出好的架构，因为他需要先熟悉建筑行业软件的业务，才能设计出符合业务特点的架构。

有一种架构师叫“PPT 架构师”，也就是说擅长写 PPT，画架构图。对各种热门的名词如数家珍。但是脱离一线开发，对业务和底层基础知识知之甚少。这样的架构师设计出来的架构，通常是不接地气的，实现起来会非常困难，成本也高。

因为作为架构师，如果不写代码，是不能体会出设计不好带来的问题，无法及时地对架构中的问题做出调整。

所以好的架构师，一定要是程序员出身，并且能坚持做一线程序员。也许他不需要写大量的业务代码，但至少要参与一部分编码工作，以及代码审查工作，以保证架构的正确执行。

好的架构师，不仅要有技术深度，还要有一定的技术广度。因为技术的选型，通常不能局限于一种技术，需要根据业务特点和团队特点灵活地选择。

好的架构师还有一个能力就是沟通能力。作为程序员，可能把自己的模块开发好就不错了，相对不需要太多的沟通工作。但是架构师就不一样，除了架构设计，还有大量沟通工作。

首先架构师要经常和产品经理打交道，反复确认需求，了解需求细节，只有这样才能分析清楚需求，了解各种用户场景。

然后架构师设计出来的架构，要通过文档、会议来讲给其他人听，能让其他人理解架构，用好架构。

所以要成为好的架构师，需要具备几个条件。

有架构师思维：具备良好的抽象思维、分治思维、复用思维和迭代思维；

懂业务需求：能很好地理解业务需求，能针对业务特点设计好的架构；

有丰富的编码经验：像抽象、分治、复用这些能力，都需要大量的编码练习才能掌握；另外保持一定量的编码经验也有助于验证架构设计；

良好的沟通能力：架构师需要沟通确认需求，需要让团队理解架构设计。

具备了这些条件，就可以成为很好的架构师，设计出好的架构，组织好人员和技术，低成本的满足好需求和需求变化，以及系统的运行。

如何成为好的架构师？

想要成为好的架构师，没有什么捷径，需要比普通程序员更多的努力才行。如果你有志向成为架构师的话，我的建议是：

要成为一个优秀的程序员

技术好是成为架构师的基础条件。需要让你的代码容易读，容易扩展，能重用。这样通过大量的编码实践，才能逐步地培养出好的架构师思维。

多模仿多学习

在刚开始的时候，不用想着闭门造车，想出一个特别牛的架构。反倒不如先把业界成熟的流行的架构吃透，用好。

现在网络上也有很多好的开源项目，这些开源项目都有良好的架构设计，可以找几个跟你研究方向相关的项目，本地搭建一下，然后自己试一下，最好能弄一个自己的项目二次开发或者模仿一遍，做中学，是最简单有效的。

我以前在用 Asp.Net 的时候，就基于一个开源的 Asp.Net 项目 Community Server 做了大量的二次开发工作，这对我后来做架构设计帮助非常大，因为我从里面学习和实践了很多非常好的架构设计思想。

选择好行业和平台

软件其实下面细分了很多行业领域，大类有像互联网应用、企业应用、游戏应用，大类下面又有细分的小类。比如说企业应用又和各行各业的业务结合在一起的，像建筑行业软件，就需要有建筑行业的专业知识。

前面我说过，架构师要同时懂业务和技术，而这些行业知识，也不是短时间内能积累起来的。所以如果想当架构师，最好能选择一个合适的行业，能在一个行业里面早点积累足够的行业知识，后面做架构设计的时候，就能更好地设计出符合业务特点的架构。

同时，这些行业领域的业务经验，和技术结合的架构经验，也会成为你个人独特的优势，不容易被替代。

还有平台也很重要，好的平台，能给你更多的实践机会。所以你看极客时间上那些开课讲架构、微服务的，无一例外都是大厂出来的，因为只有大厂，才有机会去实践这种高并发大数据的架构设计。

如果你有志成为架构师，不能光埋头写程序，也要早做打算，选择适合你自己的行业和平台，少走弯路。

总结

今天，我们谈了“不想当架构师的程序员不是好程序员”这个话题。其实对于程序员来说，并不代表一定要有一个架构师的头衔，而是心中有大局观，有架构师的思维。从而能理解架构设计，能写出好的程序。

架构师思维，指的是要具备良好的抽象思维、分治思维、复用思维和迭代思维。

另外没有架构师的头衔，也一样可以做架构设计，只要你有架构师的能力就可以了。而好的架构师，需要具备：

有架构师思维；

懂业务需求；

有丰富的编码经验；

良好的沟通能力。

要想成为好的架构师，没有什么捷径可以走，首先需要要成为一个优秀的程序员，然后多模仿、多学习好的架构设计，最后还要早点选择好行业和平台，积累好行业的业务知识，借助平台获得大量的实践机会。

课后思考

互联网架构师和企业架构师有什么不同？你有没有成为架构师的梦想，有什么打算？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

24 | 技术债务：是继续修修补补凑合着用，还是推翻重来？

你好，我是宝玉，今天我想与你讨论一下关于技术债务的问题。

做开发的同学对以下场景应该不会陌生：

为了赶项目进度，单元测试代码就来不及写了，打算以后再补；

随着需求的变化，原本的架构设计已经不能很好地满足新的需求，但是又不想对架构做改动，于是就绕开架构设计增加了很多代码；

一个旧的系统，没有文档没有注释，技术老旧，难以维护。

这些问题，如果没有及时修正，就会导致代码臃肿、系统效率低下，难以维护，也难以新增功能。

有一个很形象的名词叫“技术债务”，用来形容上面这些架构或代码上的质量问题。

所以今天的课程，我将带你一起来了解一下什么是技术债务，它形成的原因是什么，以及怎么来管理技术债务。

什么是技术债务？

我们在学项目管理金三角时，有一张表示软件质量与时间、成本、范围关系的三角形图，也特别解释了为什么质量要放在三角形中间，因为质量往往是其他三个因素平衡后结果的体现。

范围不减，成本不增加，还想节约时间走捷径，就会影响到质量。这个“质量”，不止是产品质量，还有架构质量和代码质量。这种对质量的透支，就是一种债务。而技术债务，就是软件项目中对架构质量和代码质量的透支。

技术债务确实是个形象生动的比喻，让你意识到它是和成本挂钩的，而且技术债务也有金融债务的一些特点，比如有利息，再比如技术债务也有好的一面。

技术债务是有利息的

债务的“利息”，就是在后面对软件做修改的时候，需要额外的时间成本。

假设我们做一个项目，在刚开始时，架构良好代码整洁，添加一个功能可能需要 4 天时间。随着项目不断维护，因为走捷径积累了一些技术债务，这时候再开发一个同样复杂度的功能就需要 5 天时间了。

这多出来的 1 天，就是技术债务造成的利息。因为你需要时间去梳理现在臃肿的代码，以找到合适的位置添加代码；修改代码后还可能导致原有系统不稳定，需要额外的时间去修复系统不稳定的问题。

技术债务不一定都是坏的

现实中，如果是贷款买辆豪车，一方面要支付利息，一方面车子一直在贬值，这不一定是个良性的债务；但如果你贷款买房子，虽然支付了利息，但如果房子升值，这个债务其实是良性的。

在软件项目中，也经常会刻意的欠一些技术债务，提升短期的开发速度，让软件能尽快推出，从而抢占市场；还有像快速原型开发模型，通过欠技术债务的方式快速开发快速验证，如果验证不可行，甚至这笔技术债务都不需要偿还了。

但技术借债也一样不能是无限制的，因为借债越多，利息越大，当收益抵不过利息时，就会陷入恶性循环，导致开发效率低下，进度难以保障。

所以对于项目中的债务，我们要清楚的知道有哪些技术债务，以及它给项目带来的收益和产生利息，这样才能帮助我们管理好这些债务。

技术债务产生的原因

如果现实中有人负债累累，那么多半有几个原因：这人对债务没有规划、生活所迫不得不借债、为了长远利益而临时借债、不知情的情况下欠了债务。

其实技术债务产生的原因也类似，所以《重构》一书的作者 Martin Fowler 把技术债务产生的原因分成了两个维度：

轻率（reckless）还是谨慎（prudent）；

有意（deliberate）还是无意（inadvertent）。

这两个维度正好可以划分成四个象限，如下图所示：

（图片来源：Technical Debt Quadrant）

轻率 / 有意的债务

这个象限，反映的是团队因为成本、时间的原因，故意走捷径没有设计、不遵守好的开发实践，对于债务没有后续的改进计划的情况。

例如不做设计直接编码，后期也没有打算重构代码。或者是团队成员以新手程序员为主，没有足够的资深程序员指导和审查代码。

这样产生的债务，短期可能还好，但是因为技术债务会一直积累，会导致利息越来越多，最终带来的负面效果会越来越大。

谨慎 / 有意的债务

这个象限，则反映的是团队清楚知道技术债务的收益和后果，并且也制定了后续的计划去完善架构和提升代码质量的情况。

比如说为了尽快发布产品，先采用“快猛糙”的方式开发，后续再对代码进行重构。

这样产生的债务，因为能及时偿还，所以既可以短期有一定时间上的收益，长期也不会造成负面影响。

轻率 / 无意的债务

这个象限，反映了团队不知道技术债务，也不知道要后续要偿还技术债务的情况。

比如说一些开发团队对于什么是架构设计，什么是好的开发实践一无所知，代码一团糟。

这样产生的债务是最危险的，因为既没得到技术债务的收益，还要偿还其产生的利息。

谨慎 / 无意的债务
这个象限反映了团队其实很重视架构设计和技术债务，但因为业务的变化，或者其他客观因素的原因，造成技术债务的产生。

比如说最初设计的时候，无法准确预测后面业务的发展，随着业务的发展，设计无法满足好新的需求。

这样产生的债务难以避免，但如果能及时的对架构升级、重构，就能保证不会造成严重的影响。

以上就是软件项目中的四种技术债务，每一种技术债务产生的原因都不尽相同，对其处理的策略不同，也会造成不同的影响。

如何管理技术债务？

既然技术债务有利息也有收益，那么我们怎么能保证软件项目中的收益大于支付的利息呢？

Martin Fowler 画过一张图，来形象的描述了设计、时间和开发速度的关系。没有设计直接写代码，从短期看确实是节约时间的，但是跨过一个临界点后，开发速度会急剧下降。

（图片来源：Is it worth the effort to design software well?）

技术债务的收益和利息也是类似的道理，最初的时候，利息低收益高，欠一些技术债务是会节约时间的，但是超过一个临界点后，利息高收益低，就会大大降低开发效率。

所以最好能让技术债务控制在临界点之下，这就要求我们能充分了解目前项目中的债务情况，然后才好制定相应的策略，从而达到控制债务的目的。

识别技术债务

如果是现实中的债务，查查银行账户就很容易知道是不是欠债了，而技术债务却没那么直观，但识别技术债务是很关键一步，只有发现系统中的技术债务，才能去找到合适的方案解决它。

你要是细心观察，还是可以通过很多指标来发现软件项目存在的技术债务。比如说：

开发速度降低：通常项目正常情况下，在相同的时间间隔下，完成的任务是接近的。尤其是使用敏捷开发的团队，每个任务会评估故事分数，每个 Sprint 能完成的故事分数是接近的。但是如果单位时间内能完成的任务数明显下降，那很可能是技术债务太多导致的。

单元测试代码覆盖率低：现在大部分语言都有单元测试覆盖率的检测工具，通过工具可以很容易知道当前项目单元测试覆盖率如何，如果覆盖率太低或者下降厉害，就说明存在技术债务了。

代码规范检查的错误率高：现在主流的语言也有各种规范和错误检查工具，也叫 lint 工具，比如 Javascript 就有 eslint，Swift 有 SwiftLint，python 有 pylint。通过各种 lint 工具，可以有效发现代码中潜在的错误和不规范之处，如果错误率高，则说明代码质量不够好。

Bug 数量越来越多：正常情况下，如果没有新功能开发，Bug 数量会越来越少。但是如果 Bug 数量下降很慢，甚至有增多的迹象，那说明代码质量或者架构可能存在比较大问题。

除了上面这些指标，其实你还能找到一些其他指标，比如你用的语言或者框架的版本是不是太老，早已无人更新维护了；比如开发人员总是需要加班加点才能赶上进度，如果架构良好、代码质量良好，这些加班本是可以避免的。

选择处理技术债务策略

在识别出来技术债务后，就需要考虑如何来解决这些技术债务了。解决技术债务有三种策略。

重写：推翻重来，一次还清

将老系统推翻重写是很多程序员最热衷干的事情之一了。重写系统是一种优缺点都很明显的策略，这有点像你试图把债务一次性还清。

优点是你可以针对当前的需求和业务发展特点，重新进行良好的设计，精简掉不需要的功能和代码。缺点就是重写通常工作量很大，在新系统还没完成之前，同时还要对旧系统维护增加新功能，压力会非常大；另外新写的系统，重新稳定下来也需要一段时间。

维持：修修补补，只还利息

维持现状，只对严重问题修修补补，这其实是常见的一种策略，就跟还债的时候只还利息一样。

修修补补相对成本低，不用投入太大精力，如果项目不需要新增功能，只需要维护还好，如果项目还持续要新增功能，越到后面，维护的成本就越高了。

重构：新旧交替，分期付款

重构相对是一种比较折中的策略，就跟我们采用分期付款的方式偿还贷款一样。

每次只是改进系统其中一部分功能，在不改变功能的情况下，只对内部结构和代码进行重新整理，不断调整优化系统的结构，最终完全偿还技术债务。这种方式优点很多，例如不会导致系统不稳定，对业务影响很小。缺点就是整个过程耗时相对更久。

这三种策略并没有绝对好坏，需要根据当前项目场景灵活选择。有个简单原则可以帮助你选择，那就是看哪一种策略投入产出比更好。

无论选择哪种策略，都是要有投入的，也就是要有人、要花时间，而人和时间就是成本；同样，对于选择的策略，也是有收益的，比如带来开发效率的提升，节约了人和时间，这就是收益。

如果收益高于投入，那这事可以考虑做，否则就要慎重考虑。对一个生命周期不会太久，或者没有什么新功能开发的系统，花大力气去重构、重写是不合算的，不如维持现状。而如果有新技术新产品出现，可以以极低的成本替代原有系统，这样重写就是个好方案。

比如说我们项目中有个很老的自己写的 CMS 系统，问题很多也没法维护，于是最近找了一个开源的 CMS 系统，把原有的数据一导入，马上就很好用了，也没有花多少时间。

通常，如果你纠结于不知道该选择哪一种策略时，那就选择重构的策略，因为这是相对最稳妥有效的。

实施策略

当你选择好用哪种策略处理技术债务之后，就可以实施你的策略了。不同的策略可能实施方式上略有不同。

对于重写的策略，要当作一个正式的项目来立项，按照项目流程推进；

对于重构的策略，要把整个重构任务拆分成一个个小任务，放到项目计划中，创建成 Ticket，放到任务跟踪系统中跟踪起来；

对于维持的策略，也要把需要做的修补工作作为任务，放到计划中，放到任务跟踪系统中。

实施策略的关键就在于要落实成开发任务，做为项目计划的一部分。

预防才是最好的方法

前面说的方法策略，都是针对已经存在的技术债务而言的。其实最好的方法是预防技术债务的产生。像下面这些方法，都是行之有效的预防措施：

预先投资：好的架构设计、高质量代码就像一种技术投资，能有效减少技术债务的发生；

不走捷径：大部分技术债务的来源都是因为走捷径，如果日常能做好代码审查、保障单元测试代码覆盖率，这些行之有效的措施都可以帮助你预防技术债务；

及时还债：有时候项目中，因为进度时间紧等客观原因，导致不得不走捷径，那么就应该把欠下的技术债务记下来，放到任务跟踪系统中，安排在后续的开发任务中，及时还债及时解决，就可以避免债务越来越多。

如果团队能提高对技术债务的认识，防患于未然，就能让技术债务保持在一个合理的水平，不会影响到开发效率。

总结

今天，我带你一起了解了软件项目中技术债务的知识。解释了技术债务的概念，技术债务，就是软件项目中架构质量和代码质量的透支。

技术债务，也并不都是坏事，如果合理利用，就可以在短期内缩短时间，但是后期如果不偿还技术债务，也会对项目及个人造成不好的后果。

技术债务产生的原因有四个方面：轻率 / 有意的债务、谨慎 / 有意的债务、轻率 / 无意的债务和谨慎 / 无意的债务。

可以分三个步骤来管理技术债务：识别技术债务、选择处理策略和实施策略。处理策略有三种：推翻重写、修修补补和重构。

对于技术债务，是继续修修补补凑合着用，还是推翻重来？其实取决于哪一种策略的投入产出比更好，如果推翻重来代价太大，那么就应该谨慎考虑，不如先修修补补或者局部重构；如果修修补补难以维持，就要考虑重写或者重构。

对于技术债务，还是要在日常开发中有好的意识，不走捷径，防患未然，预防技术债务的发生。

课后思考

你现在的项目中，你能识别出来哪些技术债务？你打算采用哪种策略来管理你的技术债务呢？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

25 | 有哪些方法可以提高开发效率？

你好，我是宝玉，今天我想与你讨论一个每个开发人员和项目管理者都关心的话题：如何提高开发效率。

我其实也一直很关注这个话题，收集了很多方法让自己工作变得卓有成效。通过对这些方法的应用，我也可以算得上是一个高效的程序员：曾一个人在很短时间完成了飞信 Web 版客户端；在 DePaul 上学之余，帮学校完成了在线教学播放器系统的改造；三个月时间帮公司完成了主站从 jQuery 到 React 的迁移。

如果让我对学过的这些方法做个整理和总结，再进一步精选提炼，我觉得对我影响最大的是“积极主动”、“以终为始”和“要事第一”这几条看似简单的工作原则。

积极主动，行动起来改变自己

相信你也跟我有过相同的经历。成为一个高效程序员，最大的阻力不是来自于不知道方法，而是自己的消极心态。遇到进度延迟、效率低下之类的问题，你就会下意识觉得：

时间进度太紧了；

我已经尽力了；

最近加班太多了没精神；

产品经理太不靠谱了，需求没想清楚，害的我瞎忙活。

是的，你也知道这些答案都很消极负面，可是怎么控制自己不这么想呢？首先你要知道，无论这些事情的本质责任在于环境还是个人，抱怨排斥的心态对于实际工作的改进是没有任何帮助的。

当然，很多人也知道抱怨没用，但具体怎样才能做到不抱怨，并且积极主动呢？史蒂芬・柯维写在他的《高效能人士的七个习惯》书中，对这个问题提到了两个行之有效的建议，我们可以结合着软件开发来分析一下。

想想再回应

我还记得第一次有人给我介绍单元测试很好用，能让你效率更高、代码质量更好时，我的第一反应是不可能，这样明明要多写很多代码，怎么可能会高效？

于是我有一段时间是很排斥的，直到后来参与一个已经有单元测试的项目，尤其是在重构代码的时候，我发现修改了大量代码后，程序还是很稳定，当时便觉得应了网友的那句话，“真香”！

每个人对于外界的刺激都会做出反应，本能的或者习惯性的，就像我前面举的例子，遇到事情会本能的觉得都是外部原因。如果一直这样，那就会进入恶性循环，变得更加消极麻木。

但如果在回应之前，给自己一点时间想想，站在积极的方面理性思考一下，就可以去控制你的本能反应。

所以很多次，就在我脱口而出“不可能”或者“不行”的时候，我提醒自己再想想。于是我会改口说：“我试试”、“我再想想”。这样很多次提醒自己以后，会一点点由“不可能”的本能变成“我想想”的习惯。

后来有人跟我说 CI（持续集成）很好，我思考过了之后进行了尝试，在 Github 上建了一个项目，把 CI 搭起来试了一下，觉得真的是很好。如果我还是秉持着以前的消极心态，不知道又要晚多久才能去尝试。

减少关注圈，扩大影响圈

我关注很多事情，比如编程语言、明星八卦、国家大事，这些都是“关注圈”。而这其中，要区分哪些事，是我可以影响和掌控的，这些事则是“影响圈”。

不要总盯着自己无法改变的部分，你需要要多花时间精力在影响圈上。

比如说，我不能改变 996，至少我可以利用这时间多学习一点，找机会换一个更好的环境；我不能要求每个人都写单元测试，但是我自己的代码写了单元测试，这样项目质量更好了我也更有价值；我不能决定跟什么样的人一起共事，但是我愿意跟他们分享我的经验，他们成长了我也受益。

工作一段时间后，你也可以尝试去扩大自己的影响圈。

比如说，很多程序员像我一样，有过不少因为产品经理需求没想清楚导致返工的经历，后来我就格外关注产品设计相关的知识，业余时间自己学习了不少，这就相当于扩大了我的影响圈。

所以后来产品经理给我一个需求，我不需要在开发完成后才抱怨他不靠谱，而是在给我需求的时候就去跟他讨论，是不是有可能没想清楚。

当你不仅仅局限于程序员的角色思维，扩大了影响圈之后，你就可以试着向产品经理提出很多有价值的建议，比如：

这个布局在文字很长的情况下会有什么变化？

如果网络很慢，加载数据的时候应该显示什么？加载失败显示什么？

如果数据为空的时候这个列表应该显示什么？

其实“减少关注圈，扩大影响圈”这个道理也很简单：接受不能改变的，改变能改变的，尽量扩大可改变项的范围。

以终为始，想清楚再开工

如果对比一下我在十几年前作为一个新手程序员，和现在作为一个老手写代码有什么不同的话，我认为在新手阶段，我是拿到需求就开始写，写到一半发现好像不对，马上修改，好像还不太对，就这样反反复复，效率很低下。

而现在拿到一个需求，我会先仔细的分析需求文档，反复和产品经理确认各种细节，然后做个简单的设计，考虑清楚模块怎么设计，他们之间是什么关系，然后再写，写完还要加上测试代码。

你知道最大的区别是什么吗？我现在做一件事之前，会先想清楚“终”，然后才知道怎么“始”。所以我先搞清楚需求这个“终”，然后再设计规划出这个从“始”通向“终”的路线，最后从“始”出发写代码，一气呵成，不仅快，而且质量好。这就是“以终为始”。

要做到“以终为始”，就是在做事情的时候注意三点：目标、原则和计划。

经常停下来想想目标

我刚毕业参加工作的时候，要开发一个内容管理系统，其中涉及有数据库访问，这就需要把数据表的字段和类对应起来，觉得太体力活了，于是我开始写数据库生成代码工具。而要想写代码生成工具，我还得学习 Winform 知识……就这样几个月过去了，关于这个系统的代码还是最开始的几行！

我的目标是写一个内容管理系统，结果却跑去写代码生成工具，这样怎么能做到高效呢？正确的做法应该是手动完成这几个类的生成，其实用不了几分钟，或者用一个现成的工具。如果觉得代码生成工具是个有意义的项目，应该另外立项，而不应该影响当前的项目。

这样的事情在我身上还发生过几次，所以我后来就逼着自己隔一段时间要停下来想想：我的原始目标是什么？我正在做的事是我的目标吗？如果不是，那么马上回到自己的原始目标去。

制定原则

其实大部分很好的编程方法都是需要坚持做才有效果的，比如说自动化测试代码，有时候时间进度一紧，就会来不及写，时间一长，就会欠下技术债务。

所以我给自己定了一个原则：增加一个功能，就要写自动化测试，如果来不及写，就给自己写一条 Ticket。

这条原则我坚持得很好，所以我的自动化测试代码得以坚持，从而真正帮助我做到高效开发。

你也可以给自己定一些原则，比如：

“先运行再优化 (Make it Work Make It Right Make It Fast)”——也就是在优化代码之前，先用简单的方法实现，再考虑怎么优化，这样可以保证设计的简单，也可以避免你陷入技术细节中而忽视了原始目标。

“不复制粘贴代码 (Don’t repeat yourself)”——复制粘贴会导致代码臃肿，不便于维护，提取抽象可以保持简洁。

“每个 Pull Request 要尽可能小”——这有助于把复杂的任务分解成几个简单的任务，简单的任务更容易高效完成。

有原则了，你才能不忘初心，有始有终。

公开自己的计划

那么有了原则就够了吗？显然不是，有了原则，你还要坚定不移地去执行。如何执行呢？做计划。

刚开始工作时，我是害怕做计划的，怕计划了完不成，问到我工作的时间安排时，我会给一个模凌两可的答复，这其实导致了我在实际开发时，缺少进度压力，从而迷失在细节中导致延误进度。

后来我尝试做出了一些改变，把任务细化，做个简单计划，主动给出一个明确的时间点。有了计划指引和时间点的压力，会倒逼着自己时刻专注于目标是什么，“终”在哪里，还有多少没有完成，这样下来工作效率自然而然就会高起来。

通过在做事时，围绕着目标、原则和计划这三个点，反复地刻意地练习，也可以让你慢慢养成“以终为始”的好习惯。

要事第一，把时间用在刀刃上

作为程序员，其实大部分时间并不能专注写程序，总有各种各样的事情打断我们，比如，一会产品经理找你过去开个会确认个需求；一会测试过来找你重现一个 Bug；一会还有同事过来请教你一个问题；微信上老朋友找你叙叙旧；突然生产环境出故障了，需要马上去解决。

就这样一天下去，感觉一直在忙忙碌碌，其实并没有多少时间在写程序。这时候怎么办呢，对手头的事情进行优先级管理。

时间四象限也许你不陌生，就是把事情分成重要紧急、重要不紧急、紧急不重要、不紧急不重要四个象限，不同的事情有不同的应对策略。

重要紧急的事情马上处理

比如说，生产环境出故障了，测试环境部署失败了，这些都是重要并且紧急的事情，只能是马上处理。

重要不紧急的要事，要花最多的时间在上面

对代码重构、写自动化测试代码、确认清楚需求文档，这些事情都属于重要不紧急的事情，但是如果不及时处理，就有可能变成重要紧急的事情，比如不偿还技术债务，就可能会变成生产环境故障。

所以这部分事情我会多花时间，重点做。通常我会每段时间只专注做一两件重要的事，其他事情尽可能忽略，比如前一个阶段我主要的工作就是重构前端代码，这个阶段我就在忙排查性能隐患，至于其他事情，就先放一放。

紧急不重要的事凑一起集中做

像微信的消息通知，无关紧要的会议，请教一个不算很急的技术问题，这些都是紧急不重要的问题，然而却会占用我们大量时间。如果时间都用在这些事情上面，必然会占用在重要事情上所需的时间。

所以我有些小技巧也许你可以参考。比如我在专注干活时，会全屏幕、关掉所有消息通知，保证没有消息干扰，忙完一段后再去集中处理。

还有如果有人找我时我正在忙，如果他的事情不是重要紧急的，我会礼貌地告诉他我正好手头有点事情，大约多少时间后我主动去找他。相应的我也会尊重别人的时间，找别人的时候会先问一下：“你什么时候有 10 分钟左右时间，我想请教你一个问题？”

不重要不紧急的事情能不做就不做

不紧急不重要的事也很多，比如说我的 Mac 电脑突然提示我要更新系统了。我有点强迫症，以前系统一有要升级，我就迫不及待要升级到最新，结果一升级系统，半天就不能干活了。所以后来这种事情，就放在不重要的时间去做，比如周末、睡觉之前让它慢慢升级。

其实我在做开发的时候，觉得很多很杂的事情也不算太多，真正到后来转型做管理的时候，才真正体会到什么叫事情多而杂。但正是源于开发时期就形成的时间四象限方法的运用，让我可以在繁忙的时候，保证时间用在有价值的事情上。

要事第一，就是要保证你有限的时间用在最有价值的事情上。

总结

积极主动、 以终为始和要事第一，这三个原则以及其衍生出来的方法，正是帮助我逐步变成一个高效程序员的关键所在，希望也能对你有所帮助。

如果你已经学习了很多类似的原则或者方法，而觉得没什么效果，那也许只是因为没有尝试把它们变成习惯。你可以像我一样，把认同的好的原则或方法，通过反复的刻意练习，反复地提醒自己，训练成习惯，然后用习惯指导你的日常开发。

当然，这样的改变不会是一天两天就能完成，但也不用着急，因为习惯的养成需要时间的积累，才能变成条件反射。当你把好的原则或方法变成了直觉反应，自然就会成为一个高效的程序员。

课后思考

你知道的有哪些提升开发效率的方法？你身边那些高效程序员都是如何做到高效开发的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

26 | 持续交付：如何做到随时发布新版本到生产环境？

你好，我是宝玉。到今天为止，持续交付已经成为一种公认的好的开发实践，越来越多的开发团队都已经应用持续交付，它通过自动化的方式，让你的代码在每一次提交后，都能自动化地走完编译、测试流程，完成后即可随时准备好部署发布。

持续交付如果细分，其实可以分成持续集成、持续交付和持续部署三个概念，这三个概念很相近，但又有所不同。

今天我将带你了解什么是持续集成、持续交付和持续部署？以及我们该如何用好它们，在项目中最大程度上发挥其效用。

集成、部署和交付的发展史

要想更好地理解并应用好持续集成、持续交付和持续部署，你需要了解它们的演变史。持续集成、持续交付和持续部署的历史还不算太长，但是集成、部署和交付却是伴随着软件工程一起发展的。

集成的发展演变

在多人软件项目开发的时候，每个人都会负责一部分功能模块的开发，集成指的就是每个人把自己开发的分支代码，合并到主干上，以便测试打包。

集成的原始阶段

早在瀑布开发的年代，在开发阶段，一般是不集成的。大家各自开发，等到开发阶段差不多快结束了，再一起提交代码到源代码管理工具，让代码集成在一起，再编译、部署发布到测试环境。

由于长时间都是在各自的开发环境运行，每次集成都是很痛苦的过程，会遇到各种问题，比如说编译无法通过、hard code 了开发环境地址、类库版本不一致、API 格式不一致等，通常要持续几天甚至几周才能逐步有一个相对稳定的版本。

图片来源：Understanding the Difference Between CI and CD

从手动集成到自动化的持续集成

《重构》的作者 Martin Fowler 说过：“如果一件事很痛苦，那么就更频繁的做（if it hurts, do it more often. ）”，持续集成本质上也是把集成这件让人痛苦的事情，更加频繁地去做。

图片来源：Understanding the Difference Between CI and CD

瀑布模型开发的集成，或者说传统的集成，都是在开发阶段整体完成的差不多了，才开始集成。而持续集成的做法，则是每次有代码合并入主干之前，都进行集成，持续的集成。代码集成到主干之前，必须通过自动化测试，只要有一个测试用例失败，就不能集成。

持续集成的好处很明显：

配合自动化测试，这样可以保证主干的代码是稳定的；

频繁集成可以让开发人员总能从主干及时获得最新的代码，不至于像类库、API 不一致等问题到最后测试的阶段才暴露出来。

持续集成主要的问题就是搭建整个持续集成环境，要稍微麻烦一点，另外需要配合一些流程规范来辅助执行，比如要求有一定自动化测试代码的覆盖率，要求测试通过才能合并到主干。

部署和交付的发展史

部署指的是将代码发布到各种环境，比如部署测试环境以供测试。交付则指的是软件产品在测试验收通过后，具备发布到生产环境交付给客户使用的条件。

部署和交付的原始阶段

在早些年，部署是一件很麻烦的事情。需要手动获取最新源代码、编译、再需要针对环境修改很多配置。

这事我确实深有体会，当年在飞信时就这样，几十个服务，一个服务有十几个项目，光挨个编译一遍就要好久，然后每个服务还有自己的配置。所以当年专门有一个人，就负责每天部署各种服务到测试环境。

生产环境就更麻烦了，因为出错了会导致服务中断。最初部署生产环境是开发人员自己做的，根据自己的经验把程序部署，然后手动修改很多配置，以保证正常运行。但这样经常会遗漏一些配置，导致程序无法正常运行，出问题后程序员很可能会直接在线上环境修复 Bug，导致更大问题。

随着分工的进一步细化，逐步发展成有专门的运维岗位，由运维人员负责部署。而开发人员上线前要写专门的部署文档和检查表，运维人员按照部署文档和检查表一步步部署生产环境。

这样确实有效减少了配置错误等问题，但整个部署过程还是很繁琐，尤其是服务器一多，耗时很长，仍然可能会因为人工操作错误导致失败。

所以为了避免部署出问题，会尽量避免进行生产环境部署，几周甚至几个月才会部署一次。

从手动部署到脚本自动化部署

对于程序员来说，如果一件事能自动化解决，迟早会有人找出自动化的解决方案，部署也由原来的手动部署发展成为自动化部署。

早期的自动化部署解决方案是每日构建（Daily Build），简单来说，就是大家在每天晚上下班后，每日构建程序自动从源代码管理器下载最新代码，编译、部署程序到测试环境。这样第二天测试人员就可以拿到最新的程序，对前一天修复的 Bug 进行测试。

每日构建是个很大的进步，因为初步实现了自动化对代码进行编译、部署测试环境。但也有一些不完善的地方，比如说如果有开发人员提交的代码有问题，可能会导致当天的编译或部署失败，那么第二天开发人员上班后，还需要手动解决。

你会发现，自动化逐步应用到运维领域，确实是让部署过程更容易，但也只是让部署过程更容易，还是无法解决发布版本的质量问题，还是可能会因为配置错误导致失败，测试环境正常的功能到生产环境就不工作了。

从脚本部署到持续交付

其实在理解了持续集成后，再理解持续交付就要容易多了。持续交付，就是在持续集成的基础上，再进一步，在功能合并到主干后，不仅会进行自动化测试，还会打包，并部署到测试环境中。

理论上来说也可以直接部署到生产环境，但是这个环节需要人工确认。参考下图，红色部分表示需要手动确认。

图片来源：Understanding the Difference Between CI and CD

持续交付本质上也是把部署和交付这件让人痛苦的事情，更加频繁地去做，从而让部署和发布变得不但不痛苦，反而越来越简单。

把持续交付的工作做好后，部署生产环境会变得非常简单，只需要点一下按钮或者运行一个命令，就可以很快完成，不需要人为地去修改配置等手动操作，也将因为配置错误或者环境不一致导致的问题的可能性降到了最低。

从持续交付到持续部署

持续交付，对于生产环境的部署，依然需要有手动确认的环节。而持续部署，和持续交付唯一的不同，就是手动确认的环节都没有了，每次代码从分支合并到主干，在自动化测试通过后，会直接自动部署生产环境，不需要人工确认。

但是，持续部署要想做好，还是很有挑战的一件事，毕竟从代码合并到生产环境的部署，如果没有人工干预仅仅依赖于自动化测试，这对自动化测试的覆盖率和稳定性要求非常高。尤其在开发新功能时，还需要引入新的自动化测试代码，可能会导致测试不全面。

当然对于新功能可能导致的不稳定问题也有解决策略，就是把新功能用功能开关（Feature flag）隐藏起来，设置特定的 Cookie 或者 Header 才打开，到生产环境后人工再测试一遍，通过后再打开，如果没通过，就继续修复继续持续部署。

该不该应用持续交付？

经常会有人问我类似的问题：我们是瀑布模型开发，该不该应用持续交付？

我的答案是：持续集成和用什么开发模型是没有关系的，瀑布模型也可以应用持续集成，应该尽快将持续集成的环境和相应的开发流程搭建起来，可以马上看到好处。

尽快暴露问题：Martin Fowler 说过，“持续集成并不能消除 Bug，而是让它们非常容易发现和改正。”自动化测试，可以保证很多问题在合并到分支之前就能被发现；每次合并后就部署到测试环境，也能让测试人员尽早介入，及时发现问题。

极大提升效率：持续交付让开发过程中从代码合并，一直到最终部署，都实现了自动化，能极大程度上提高效率。

提升质量：每次合并之前都需要通过自动化测试，因此错误会少很多。

降低项目成本：在最初搭建持续交付环境的时候，是要投入一定成本的，但是从长远看，开发效率提升了，代码质量提高了，反而是对降低项目的整体成本有帮助的。

虽然现在持续交付还不够普及，但未来就像源代码管理一样，成为开发团队的标配。现在大厂都已经普及了持续交付，还会有专门的团队负责持续交付工具的开发和维护。对于中小厂，一般不需要自己开发持续交付工具，可以基于开源工具搭建，或者购买托管的持续交付工具，一样可以很好满足持续交付的需求。

如果你所在团队还没有开始用起来持续交付，那么不如现在开始应用起来，能有效提升团队的开发效率和代码质量。当然很多团队没有推行，主要问题还是不知道如何搭建一套持续交付的环境。接下来，我就给你介绍一下如何搭建自己的持续交付环境。

如何搭建持续交付环境？

要搭建好自己的持续交付环境，其实并不算太难，已经有很多持续集成工具和教程帮助我们做这件事。

准备工作

根据前面对持续交付的说明，要想搭建自己的持续交付环境，并不是简单找一个持续集成工具一搭就可以工作了，而是还需要做一些准备工作。

我们先来看持续集成部分，持续集成相对要求简单：

需要有源代码管理工具，比如说 git、svn，因为持续集成工具需要从统一的源代码仓库获取代码；

需要写自动化测试代码，因为持续集成有一个很重要的条件，就是自动测试必须通过。

第一个条件其实好满足的，现在源代码管理工具已经是标配，无论是免费的还是收费的，都有很多选择。第二个条件其实也不是太大的问题，因为自动化测试覆盖率，可以逐步提升，不要求一步到位。所以可以先把自动化测试写起来，然后在开发过程中逐步增加覆盖率。

持续交付相对比持续集成要求更高，因为整个过程需要高度的自动化。要实现持续交付，你的项目需要满足以下条件：

对代码构建的过程可以反复进行，并且每次构建的结果是一致的、稳定的；

所有环境的配置都存在于源代码管理工具中，不仅仅是代码；

需要自动创建针对于不同环境的发布包；

所有环境的部署发布步骤都必须是自动化的。

上面这些要求，最难的部分其实就是自动化打包和自动化部署到各种环境，因为每套程序都不一样，每个服务器环境也不一样，这是必须要各个团队针对自己的项目情况去解决的问题。

选择合适的持续集成工具

持续集成工具现在已经有很多选择，有开源的、商业的，有线上托管的，还有自己搭建的。

主要的持续集成工具有这些：

Jenkins

Jenkins应该是目前最好的开源持续集成工具，可以自己搭建，插件非常丰富，可以满足绝大部分项目的需要。相对使用难度要高一些，需要花一点时间学习。

Go CD

Go CD是 ThoughtWorks 公司出品的持续集成工具，可以免费使用。

Travis CI

Travis CI是一个老牌的托管的商业 CI 系统，和 Github 集成的非常好，尤其是开源项目，可以免费使用。

GitLab CI

GitLab CI是 Gitlab 推出的持续集成工具，可以自己搭建也可以使用它的在线托管，价钱便宜。

Azure Pipelines

Azure Pipelines是微软的持续集成平台，可以自己搭建也可以使用它的在线托管，和微软的开发语言和服务集成很好。

根据选择的工具实施

在选好你要用的持续集成工具后，就需要根据工具的说明去搭建。这部分相对简单，网上也有比较多的教程，限于篇幅，这里我就不一一介绍啦，相信你通过它们的官方网站或者是搜索，很容易能找到很多相关的使用教程。

总结

今天我带你一起学习了与持续交付相关的一些概念：

持续集成，就是持续频繁地将代码从分支集成到主干，并且要保证在合并到主干之前，必须要通过所有的自动化测试。

持续交付，则是基于持续集成，在自动化测试完成后，同时构建生成各个环境的发布包，部署到测试环境，但生产环境的部署需要手动确认。

持续部署，是在持续交付的基础上，对生产环境的部署也采用自动化。

要搭建持续交付环境，首先需要做好准备工作，例如自动化测试代码和自动部署脚本；然后要选择好持续集成工具；最后按照选择的持续集成工具来实施。

最后，推荐你配合阅读《持续交付 : 发布可靠软件的系统方法》，这本书很系统地讲述了持续交付的概念和如何去实施的过程。

课后思考

你的项目中应用了持续交付吗？如果应用了，你觉得有哪些优缺点？如果没有应用，你觉得主要的障碍是什么？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

27 | 软件工程师的核心竞争力是什么？（上）

你好，我是宝玉。软件工程师，是软件项目重要的人员组成，从设计到编码实现以及上线后的运行维护，都需要软件工程师的参与。所以大家都很关心的一个问题是：软件工程师的核心竞争力是什么？

软件工程师的核心竞争力

其实我在微博上问过“软件工程师的核心竞争力是什么?”这个问题，得到很多答案：技术、代码、天赋和激情、经验、想象力……

我思考过这些答案，觉得很多答案挺好，但是不够突出核心竞争力，或者没有突出软件工程师的特点。

比如说技术和代码的能力，属于基础能力，而不属于核心竞争力；天赋和激情其实放哪个职业都可以这么说，何况天赋也不是决定性因素，聪明人不是好程序员的例子也很多；

经验是很重要，但是不能说明太多问题，有些号称有三年开发经验的程序员不过是把一年的经验重复了三次罢了；想象力是很重要，但我觉得更适合科学家或者艺术家，因为他们要通过想象力在未知的领域去探索，而工程师更多是利用已有的知识去打造产品，想象力算不上核心竞争力。

我觉得软件工程师的核心竞争力，不是单一能力的体现，而是几种能力和价值的合集。学习能力、解决问题能力和影响力构成了软件工程师的核心竞争力。

学习能力

对于软件工程师来说，最基本的要求是技术和代码。如果你看招聘网站上招聘软件工程师的要求，都需要能掌握一门或者多门编程语言，会熟练使用工具、框架。

但熟练掌握一门编程语言、框架，还不能构成核心竞争力。因为现在技术更新迭代很快，现在你熟悉的语言或者框架，可能过几年就没有太大的市场了，需要学习新的技术。

这点做前端的同学一定深有体会，比如说前几年 jQuery 很火，你好不容易精通了，过两年又要改用 Angularjs，而现在则是 React、Vue 的天下了。而且光会这些框架还不够，还要学习使用配套的打包工具，而这些工具也一直在推陈出新，每次更新升级，都需要重新学习。

就算是编程语言，时间周期能稍微长一点，但也没有谁能保证一门语言就可以一直很火。我早些年对 Asp.Net 的掌握程度已经到了技术专家级别，后来公司要转型做移动应用，就必须要去学习 Objective-C，否则就得换个公司才行。

出国留学后要重新找工作，这时候 Asp.Net 和 Objective-C 的工作机会很少，但是前端工作机会多，我就需要去学习前端，才能有更多的工作机会。

所以我觉得，编程语言、框架、工具，这些都不是软件工程师的核心竞争力，能快速学习掌握编程语言、框架、工具的学习能力才是软件工程师最基础的核心竞争力。

解决问题的能力

为什么我说学习能力只能算是基础的核心竞争力呢？因为技术学了是要用才能产生价值的。学的好不代表用的好，所以你看很多 Leetcode 上刷题分很高的同学，让他去实现一个需求，代码可能还是会写的一团糟。

那么软件工程师怎么运用学到的技术呢？我们可以先看看软件工程师的日常工作，有哪些主要工作以及需要什么样的能力去解决：

实现功能需求——需要先分析需求，然后抽象设计，最后实现；

修复 Bug——改 Bug 最大的挑战其实是重现问题，也就是发现问题，然后再分析问题，最后解决问题；

重构代码、优化性能——对代码重构，优化性能，最难的地方其实在于发现代码问题在哪，发现性能的瓶颈，后面再去寻找解决方案，最后再解决。

也就是说，软件工程师这些日常开发工作的核心还是在发现问题、分析问题和解决问题，在这里我统称为解决问题的能力。这几个能力看起来没什么稀奇，但是要仔细分析，其实软件工程师的水平高低，恰恰就体现在解决问题的能力上面。

发现问题

若干年前我们要做一个抽奖系统，负责这个任务的程序员设计了技术方案让我审查，他的方案从功能方面是没问题的，但在安全方面却欠考虑。

我就问他如果有人用个工具模拟用户操作反复点，岂不是可以不停参加抽奖，极大提高中奖概率？于是他说那可以在前端增加限制重复点击。我说如果通过抓包工具反复直接发送数据包，不还是一样可以绕过你前端的检查吗？

同样一个抽奖系统需求，新手程序员看到的是如何实现功能，而有经验的程序员，会发现可能存在的安全隐患，未雨绸缪，把可能的安全问题消除掉。这就是发现问题的能力体现。

分析问题

软件工程师经常遇到的一个问题就是生产环境发生故障，新手程序员通过分析也能把问题修复，但修复完了就觉得没事了。而有经验的程序员，会在恢复后还会进一步分析故障产生的深层次原因，以及以后可以怎么预防类似的故障再次发生。

分析问题，不仅是分析表面的问题，还需要去分析问题深层次的原因，以及思考预防同类问题的机制。

解决问题

在发现问题和分析问题后，解决问题相对会容易一些。但有时候明明知道问题在哪，但不知道怎么解决也是很苦恼的事情。

在工作中，就经常有新手程序员跑来找我请教问题，比如说遇到一个 API 不会用。这时候我通常不会直接告诉他答案，因为我可能也不知道某个 API 的用法，就算我知道，告诉他这一次了，他下一次还是不会自己去解决。

所以我会反问他：

“查过官方文档吗？”——基本上大部分 API 在官方文档都有说明的；

“有没有去搜索过？”——很多问题已经有人遇到过，而且还把解决方案都贴出来了，比如 StackOverflow 就是个很好的地方；

“用的什么关键字？”——很多时候没找到结果就是因为关键字没选好，比如用中文关键字很难搜索出匹配的技术帖子，改成英文使用谷歌就可以找到更多更合适的结果；

还有的问题确实不是通过搜索能解决的，需要去寻求其他组或者外部专业人士的帮助。就算是问问题这种小事，一样都能体现程序员的水平。

比如我就遇到过很多次有程序员去请教别人问题，但是都没人回复的，并不是没有人愿意帮忙他，而是他基本的问题都没有描述清楚，从他的问题很难知道表达的意思和要解决的问题。这时候我通常会建议他先去看看《提问的智慧（How To Ask Questions The Smart Way）》这篇文章。

这样的发现问题、分析问题和解决问题的能力，就是软件工程师的进阶核心竞争力。

影响力

解决问题的能力，确实是软件工程师很重要的一种能力体现，但是还不能完全体现出软件工程师的核心竞争力。

为什么大部分程序员的技术水平、解决问题能力差不多，而有的升职加薪的机会更多？有的不需要去找工作，总有工作来找他们？这其中的差别，就在于他们的影响力不同。

有些程序员，做事情认真靠谱，做出来的结果让人放心，这样久而久之，就在公司形成了口碑和影响力，大家愿意与之共事。

有些程序员，乐于帮助其他人，分享自己的经验，跟一些新手程序员是亦师亦友的关系，在团队里有很高的威望。

有些程序员，有独特的项目、公司或者行业经历，比如在阿里巴巴这样的大厂有多年架构师经验，在业界有一定知名度。

有些程序员，写自己的技术博客，出去做技术讲座，成了技术大 v，在技术圈子里面有一定的名气和影响力。

这样的影响力不是一朝一夕能形成的，但却是一个软件工程师最核心的价值体现。

你需要通过一点点技术成长的积累，需要通过一个个成功项目的积累，需要通过一篇篇技术文章分享的积累，需要通过一次次帮助其他人成长的积累。而一旦形成足够的影响力，就会变成软件工程师职场发展最牢固的护城河。

所以综合来说，软件工程师最核心的竞争力其实分三层：

最底层、最基础的就是学习能力，通过学习能力，快速学习掌握新技术；

中间一层就是解决问题的能力，充分利用学到的技术，去发现问题、分析问题和解决问题；

最上一层就是影响力，是核心竞争力的综合体现。

学习能力、解决问题能力和影响力一起构成了软件工程师的核心竞争力，就像下面这个金字塔图一样，我称之为软件工程师竞争力金字塔。

但必须要注意，这三层缺一不可，你不能光去追求影响力而不踏踏实实学习和做事，也不能光去做事而不学习。必须要踏实地、一层层地打好基础，这样才能让你的竞争力是持久的，而不是昙花一现。

软件工程师竞争力金字塔的意义

如果你是软件工程师，了解了软件工程师竞争力金字塔，你就可以自下而上，更有针对性地培养、提高软件工程师的核心竞争力。

不是把自己绑死在一门技术或者一个框架上，而是训练自己的学习能力，让自己可以拥有快速学习掌握新技术的能力。

日常工作不仅仅是实现一个个的功能，做一个个的项目，而是在这个过程中，去锻炼和提升你发现问题、分析问题和解决问题的能力。这样才能最大化的你的工作经验，而不是机械重复没有积累。

在工作中，不仅是把事情做成，还要把事情做好；不仅是自己成长，还要帮助其他人成长；最大化的利用好所在平台和行业的经历，转变成你的经验和影响力。工作之外，也多分享，打造自己的品牌。

这样你就可以一点点搭建出来属于你的竞争力金字塔，构建出自己的核心竞争力。

如果你是要招聘优秀的软件工程师，了解了软件工程师竞争力金字塔，你可以自上而下，更容易筛选出来优秀的软件工程师。

首先你可以通过内部推荐，优先找那些口碑好，做事情认真靠谱的软件工程师。然后你可以考察他解决问题的能力。比如说你可以问他：

“项目中遇过的哪些复杂的问题？”

“是怎么样去解决的？”

通过对问题的答复，再深入问一些细节问题，例如：“你当时是怎么考虑安全性问题的？”“如果访问量增加一倍，会有什么影响？”等等。你就可以对他解决问题的能力有个初步了解了。

最后你还可以考察一下学习能力。比如你可以问他：

“最喜欢的编程语言是什么？”

“你是如何学习这门语言的？”

“如果让你学一门新的语言，你打算怎么学？”

这样你也可以对他的学习能力有一个大致了解。

总结

由于篇幅限制，我将文章分成了上下两篇。上篇主要分析了软件工程师的核心竞争力，也就是学习能力、解决问题能力和影响力。我会在下篇中，为你讲具体怎么做，才能有效提升软件工程师的核心竞争力。

学习能力，就是你学习并掌握一门技术、框架和工具的能力。好的学习能力，能帮助软件工程师在技术日新月异的今天，快速跟上技术发展的步伐。

解决问题的能力，就是发现问题、分析问题和解决问题的能力。解决问题的能力，是软件工程师非常重要的能力，帮助软件工程师在日常工作中高效完成工作，创造价值。影响力则是软件工程师价值的综合体现。

如果你是软件工程师，那么就需要自下而上，训练自己的学习能力，积累解决问题的经验，工作的过程中形成影响力，一点点去构建自己的核心竞争力。如果你要招聘或筛选优秀的软件工程师，则需要自上而下，选择那些口碑好有影响力的，能帮你解决问题的，有很好学习能力的人。

课后思考

你对于软件工程师的核心竞争力是怎么看的？有哪些是你认为有价值的能力？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

28 | 软件工程师的核心竞争力是什么？（下）

你好，我是宝玉。在上一篇中，我们讨论了什么是软件工程师的核心竞争力，也就是学习能力、解决问题的能力和影响力。

今天我就来跟你谈一谈，如何提升软件工程师的核心竞争力，也就是说，如何分别提升你的学习能力、解决问题能力和影响力。

如何提升学习能力？

学习能力是软件工程师最基础的能力，学习能力直接决定了你掌握技术的速度。现实中，有的程序员掌握新技术就是比别人要快，似乎有什么秘籍，可以让他们快速学习和掌握新技术。真有这样的秘籍吗？如果有，那是什么样的内容呢？

前不久我要写一个家谱的微信小程序，要实现列表、检索等功能。此前我没有任何关于小程序的开发经验，于是我粗略地看了下开发文档，搭建了开发环境，下载了几个示例程序，前后花了一天时间完成了一个不算简单的小程序开发。

为什么我能在一天时间就可以学习掌握小程序的开发呢？其实只是因为我已经构建了自己的开发知识体系，而小程序开发所需要的知识，绝大部分在我的知识体系里面已经有了存储。这些部分我就不需要重新再学习，我只要去学习小程序所独有的知识就好了，而这部分知识只有很小比例，所以很快就可以掌握。

那么你可能会问，怎样可以构建自己的知识体系呢？

首先需要在一个技术领域深耕

每个人精力其实很有限的，一开始专注在一个技术领域容易在短时间取得成绩，同时也相当于建立起了最初的知识体系，在未来的知识森林里种下的第一棵大树，这样当你开始学习新的技术的时候，已有的知识就可以直接借用，相当于这棵大树可以帮助新的知识树的成长提供很好的养分，快速培养出新的大树。

如果一开始就同时涉猎多个领域，每个领域的知识又没有掌握好，这样的知识是没法共享的，就相当于你种的只是一片知识的灌木，最终只能收获像灌木丛的知识体系。

只有一个领域的知识你真正吃透，才能有效地共享到其他领域，构成一个知识领域的森林。

我在毕业之后，整整有 6 年时间，是一直专注于 Asp.Net 技术领域，让我能成为这个领域的专家，也帮我构建了最基础的知识体系。所以在后来我去学 iOS 开发时，发现像面向对象、网络协议、数据存储、MVC 开发模式这些知识，我就不需要再去学习，因为我的知识体系已经有了这部分知识。

换个角度说，如果我当时 Asp.Net 学的不够深入，那么面向对象、数据存储、MVC 开发模式这些我还是要再学一遍，自然也无法快速掌握 iOS 开发。

知识体系建立之初，确实是痛苦的，感觉什么都不懂，有很多要学习的。市面上有新技术出来也会觉得焦虑，觉得应该去学新的技术。但如果初期不能够专注在某个领域深入的话，你学了再多技术，结果也没有一门能深入，这就很难构建出有深度的知识树。

要在某一个领域的技术达到一定深度，通常需要三年以上的时间。当你熬过这个阶段，在一个技术领域取得了一定成就，不仅会收获你的知识树，还能收获技术上的自信，让你有信心在其他技术领域也同样取得成就。

然后往相近的领域逐步横向拓展

当在一个技术领域达到一定深度后，可以开始横向扩展。最好是往相近的领域扩展，因为这样你之前的知识有很多是可以共享的，容易快速取得成绩。

这也是为什么我后来选择了前端，因为对我来说，前端跟我以前掌握的 Asp.Net 的经验是有很多重叠的，我只需要去学习框架和工具那部分知识。

当然横向构建知识体系，也一样不是一个轻松的过程，因为以前你在某个领域取得的成就和经验，反过来也会成为一种阻力。因为以前你熟悉的知识，已经变成了你的舒适区，你会天然地不愿意走出舒适区，不愿意到挑战区或恐慌区去学习新的知识。

这其实就是我当初学 React 时候的感受，我觉得 jQuery 已经用的很好了，为什么要学这个？我老想着用以前 MVC 的经验去套用 React 的编程模式，反而更难理解。

但是，当你迈过去，掌握了新领域的知识，就会感觉整个知识体系一下子扩展了很大一块。相当于让你的知识体系，从一棵树，逐步变成了一个小树林，最终会成为一个森林。

快速掌握新技术的秘籍，就是要构建属于你的知识体系，让你在学习新知识时，能借用已有的知识，加快学习速度。

你可以现在思考一下你的知识体系是什么样子的，是一片灌木丛还是已经有大树了？如果只是一片灌木丛，你打算在哪个技术领域先打造你的知识树呢？

如何提高解决问题的能力？

解决问题的能力是软件工程师进阶的核心竞争力，你看现实中，那些解决问题能力强的程序员，遇到问题总是有办法，都能有条不紊地给解决了。

有一次我们组负责的一个网络服务出现异常，大约 1% 的请求会出现异常导致服务报警，当时值班的同事正好是一个新手程序员，他也试着分析日志，但是没有找到明显的错误日志，完全不知道如何应对，只好让我帮忙。这个问题我以前也从来没遇到过，但我有一套应对这类问题的思维方式和逻辑。

我第一步就是回滚上一次的部署，看是否恢复正常，结果没效果。于是我再从日志找出所有有问题的请求，寻找规律，从中选取几个有问题的请求，去跟踪它们整个请求过程，结果在某一个路由环节发现指向到了一台错误的服务器，最终定位是某个路由程序出现的问题。

我们将这个问题反馈到相关组，问题马上就得以解决。然后我建议他在这个路由环节增加监控，这样以后再出问题就能第一时间报警通知。

为什么我有这样解决问题的能力呢？是因为我在多年的开发经验中，形成了一套解决问题的方法论，即使遇到没有解决过的问题，也能借助一套方法论去解决。

这其实很像我在专栏开头就提到的工程思维，遇到一个项目，哪怕不是软件项目，我也可以借鉴瀑布模型，用工程方法去解决。所以你要提高解决问题的能力，就要形成自己的一套解决问题的方法论。

在这里分享我解决问题的一套方法论，其实很简单，只有三步。

第一步：明确问题

解决问题，最重要的一步就是要明确问题是什么，这其实就跟做项目需要先需求分析一样，搞清楚目标是什么，才能做到有的放矢。

同时这一步也要透过现象看本质，去明确问题背后是不是还有其他问题。就像我在前一篇文章中举例的抽奖项目，不能光看到功能需求，还需要看到安全上的需求；网络异常的问题，不能光想着应用程序错误，还要看看网络是不是有问题。

第二步：拆分和定位问题

前面我们学习架构思维的时候，也提到了，一个复杂的问题，只有经过拆分，才好找到本质的问题。

就像上面举的解决网络异常的例子，我首先拆分成程序问题和网络问题，通过回滚观察，我就可以基本上断定不是程序问题，那就说明是网络问题。然后对于网络问题，将整个请求过程拆分，最终就可以定位到有问题的环节。

第三步：提出解决方案并总结

发现并分析完问题后，找到解决方案是容易的，但很有必要总结一下。总结要做的就是两点：

下次有这种问题怎么解决，是不是可以做的更好？

这种问题是不是可以预防？如果可以，应该怎么做？

通过总结，就可以进一步提升解决问题的经验。如果你对于解决技术上的问题还没有总结出来自己的方法论，不妨可以先参考这套方法，一步步去发现问题，分析问题和解决问题。

尤其是在解决完问题后，再想一想如何预防以后类似问题的发生。如果每次解决完问题，你还能提出一个预防问题发生的方案，一定会让大家印象深刻的。

如何提升影响力？

刻意地去提升自己的影响力，是很多软件工程师忽略去做的事情。但影响力，却是程序员核心竞争力的最顶层，也是一个软件工程师能力的综合体现。培养影响力也是我职业生涯中受益良多的一件事。

其实当你意识到影响力是有价值有意义的事，怎么去做反而没那么难，比如这些方面可以去考虑。

在某个领域做出了足够牛的成绩

有些程序员能在某一个技术领域做到一定深度，做出了常人难以达到的成绩，比如说 PHP 开发组核心成员的鸟哥惠新宸，写 Vue 框架的尤雨溪，前端的 Winter。做到他们这样，基本上就不用担心影响力的问题了。

要取得这样的成绩，要实力、要机缘、还要坚持。

做事情超出预期

在软件项目中，你作为一个程序员，每个人都会对你有预期，项目经理希望你如期完成项目，产品经理希望你完成需求，其他程序员希望你代码质量好。如果你是初级程序员，则大家期望你代码不要有太多问题就好，如果你是高级程序员，大家不仅期望你要写好程序，还要能带带新人。

如果你做事情的结果能超出预期，就会让人对你刮目相看，进而会形成口碑。就像我前面举的例子，如果你解决完一个问题，还能想到怎么预防问题再次发生的方案，这通常就超出他人对你的预期了。

要让自己的表现超出其他人的预期，除了要付出很多努力外，也要学会管理好其他人的预期，我以前有写过一篇文章《程序员也可以懂一点期望值管理》，谈如何去了解别人对你的期望，降低别人对你的期望，最后做出高于期望的事。

帮助其他人就是在帮助自己

我有遇到过很多程序员不愿意教别人，认为教会徒弟饿死师傅。其实这完全搞反了，帮助别人、教别人收获最大的恰恰是自己。

程序员的经验，很大部分来自于解决问题时积累的经验。你自己在工作中遇到的问题其实是很有限的，但如果帮助其他人解决问题，相当于增加了你解决问题的样本，这些样本能帮助放大你的工作经验。

帮助其他人，还是形成影响力最简单有效的途径。就像我微软最有价值的称号，就是因为我当年在社区热心组织活动，网上帮助其他人解决技术问题而获得的。

分享就是学习和打造影响力

刘未鹏老师写过的一篇博客《为什么你应该（从现在开始就）写博客》影响了很多人，这是一篇值得反复阅读的好文章，讲了写博客的好处，例如交朋友、帮助思考、学习。

我的观点也类似，包括在学习路径中我就建议大家可以通过分享，在教中学。其实，在分享形式上也可以更多样化，除了写博客还有很多其他方式，比如公司内部的讲座就是很好的分享途径。

我在内部也会经常做培训分享，也会写博客分享技术经验，每一次分享都会帮助我学习巩固很多知识点，也是在帮我打造自己的影响力。

写东西这方面我做的不够好，写博客断断续续的，但还是在坚持，也收获很多，包括《软件工程之美》专栏的诞生，其实也是因为极客时间看到我写过的一篇《记在美国的一次校园招聘》，所以才邀请我开的专栏。

希望你也能思考一下影响力这个问题，想想你还可以在哪些方面，用哪些方法去打造你的影响力，和学习能力、解决问题能力一起，形成你的核心竞争力。

总结

最后简单总结一下。软件工程师的核心竞争力，体现在学习能力、解决问题能力和影响力三个方面。

要提升学习能力，要构建好自己的知识体系，首先需要在一个技术领域深耕然后往相近的领域逐步横向拓展。

要提升解决问题的能力，要形成自己的方法论，去发现问题，分析问题和解决问题。

要提升自己的影响力，可以在一个领域深入打造自己独特的有价值的能力，让自己做事情能超出别人的预期，同时乐于分享和帮助他人。

课后思考

你的知识体系是什么样子的，你是怎么打造你的知识体系的？你自己或者你周围哪些擅长解决问题的软件工程师都有哪些独特的方法？你是怎么提升影响力的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

29 | 自动化测试：如何把Bug杀死在摇篮里？

你好，我是宝玉。前不久我所在项目组完成了一个大项目，把一个网站前端的 jQuery 代码全部换成 React 代码，涉及改动的项目源代码文件有一百多个，变动的代码有几千行，最终上线后出乎意料的稳定，只有几个不算太严重的 Bug。

能做到重构还这么稳定，是因为我们技术水平特别高吗？当然不是。还是同样一组人，一年前做一个比这还简单的项目，上线后却跟噩梦一样，频繁出各种问题，导致上线后不停打补丁，一段时间才逐步稳定下来。

这其中的差别，只是因为在那次失败的上线后，我们总结经验，逐步增加了自动化测试代码的覆盖率。等我们再做大的重构时，这些自动化测试代码就能帮助我们发现很多问题。

当我们确保每一个以前写好的测试用例能正常通过后，就相当于把 Bug 杀死在摇篮里，再配合少量的人工手动测试，就可以保证上线后的系统是稳定的。

其实对于自动化测试，我们专栏已经多次提及，它是敏捷开发能快速迭代很重要的质量保障，是持续交付的基础前提。

所以今天我将带你一起了解什么是自动化测试，以及如何在项目中应用自动化测试。

为什么自动化测试能保障质量？

自动化测试并不难理解，你可以想想人是怎么做测试的：首先根据需求写成测试用例，设计好输入值和期望的输出，然后按照测试用例一个个操作，输入一些内容，做一些操作，观察是不是和期望的结果一致，一致就通过，不一致就不通过。

自动化测试，就是把这些操作，用程序脚本来完成的，本质上还是要输入和操作，要检查输出是不是和期望值一致。只要能按照测试用例操作和检查，其实是人来做还是程序来做，结果都是一样的。

不过，自动化测试有一个手工测试没有的优势，那就是可以直接绕过界面，对程序内部的类、函数进行直接测试，如果有一定量的自动化测试代码覆盖，相对来说软件质量是更有保障的。

而且，一旦实现了自动化，每测试一次的成本其实大幅降低了的，几百个测试用例可能几分钟就跑完了。尤其是每次修改完代码，合并到主干之前，把这几百个测试用例跑一遍，可以有效地预防“修复一个 Bug 而产生新 Bug”的情况发生。

但现阶段，自动化测试还是不能完全代替手工测试的，有些测试，自动化测试成本比手工测试成本要高，比如说测试界面布局、颜色等，还是需要一定量的手工测试配合。

有哪些类型的自动化测试？

现在说到自动化测试，已经有很多的概念，除了大家熟悉的单元测试，还有像集成测试、UI 测试、端到端测试、契约测试、组件测试等。而很多时候同一个名字还有不同的解读，很容易混淆。

在对自动化测试类型的定义方面，Google 的分类方法我觉得比较科学：根据数据做出决策，而不仅仅是依靠直觉或无法衡量和评估的内容。Google 将自动化测试分成了三大类：小型测试、中型测试和大型测试。

假设我们有一个网站，是基于三层架构（如下图所示），业务逻辑层的类叫 UserService 类，数据访问层的类叫 UserDA，我们将以用户注册的功能来说明几种测试的区别 。

小型测试

小型测试是为了验证一个代码单元的功能，例如针对一个函数或者一个类的测试。我们平时说的单元测试就是一个典型的小型测试。

比如说 UserService 这个类，有一个注册用户的函数，现在要对它写一个单元测试代码，那么看起来就像下面这样：

通过这样的测试代码，就可以清楚的知道 UserService 类的 create 这个函数是不是能正常工作。

小型测试的运行，不需要依赖外部。如果有外部服务（比如文件操作、网络服务、数据库等），必须使用一个模拟的外部服务。比如上面例子中我们就使用了 FakeUserDA 这个模拟的数据库访问类，实际上它不会访问真实的数据库。这样可以保证小型测试在很短时间内就可以完成。

（小型测试，图片来源：《Google 软件测试之道》）

中型测试

中型测试是验证两个或多个模块应用之间的交互，通常也叫集成测试。

如果说要对用户注册的功能写集成测试，那么就会同时测试业务逻辑层的 UserService 类和数据访问层的 UserDA 类。如下所示：

对于中型测试，可以使用外部服务（比如文件操作、网络服务、数据库等），可以模拟也可以使用真实的服务。比如上面这个例子，就是真实的数据库访问类，但是用的内存数据库，这样可以提高性能，也可以减少依赖。

至于中型测试要不要使用模拟的服务，有个简单的标准，就是看能不能在单机情况下完成集成测试，如果可以就不需要模拟，如果不能，则要模拟避免外部依赖。

（中型测试，图片来源：《Google 软件测试之道》）

大型测试

大型测试则是从较高的层次运行，把系统作为一个整体验证。会验证系统的一个或者所有子系统，从前端一直到后端数据存储。大型测试也叫系统测试或者端对端测试。

如果说要对用户注册写一个端对端测试的例子，那么看起来会像这样：

对于大型测试，通常会直接使用外部服务（比如文件操作、网络服务、数据库等），而不会去模拟。比如上面这个例子，就是直接访问测试环境的地址，通过测试库提供的 API 操作浏览器界面，输入测试的用户名密码，点击注册按钮，最后检查输出的结果是不是符合预期。

（大型测试，图片来源：《Google 软件测试之道》）

区分测试类型的依据是什么？

以上就是主要的自动测试类型了。捎带着补充一个测试类型，那就是契约测试，这个测试最近出现的频度比较高，主要是针对微服务的。其实就是让微服务在测试时，不需要依赖于引用的外部的微服务，在本地就可以模拟运行，同时又可以保证外部微服务的接口更新时，本地模拟的接口（契约）也能同步更新。对契约服务更多的说明可以参考这篇文章：《 聊一聊契约测试 》

那么契约测试，属于大型测试还是中型测试呢？

Google 针对这几种测试类型列了一张表，根据数据给出了明确区分：

（图片来源：Google Testing Blog）

结合上面的表格其实就很好区分了：

小型测试，没有外部服务的依赖，都是要模拟的；

中型测试，所有的测试几乎都不需要依赖其他服务器的资源，如果有涉及其他机器的服务，则本地模拟，这样本机就可以完成测试；

大型测试，几乎不模拟，直接访问相关的外部服务。

所以现在你应该就知道契约测试，也是中型测试的一种了，因为它不需要依赖外部服务，本机就可以完成测试。

为什么中型测试这么看重能单机运行呢？因为这样才方便在持续集成上跑中型测试，不用担心外部服务不稳定而导致测试失败的问题。

上面的表中还反应出一个事实：越是小型测试，执行速度越快，越是大型测试，执行速度越慢。通常一个项目的小型测试，不超过一分钟就能全部跑完，一个中型测试，包括一些环境准备的时间，可能要几分钟甚至更久，而大型测试就更久了。

另外越是大型测试，写起来的成本也相应的会更高，所以一般项目中，小型测试最多，中型测试次之，大型测试最少。就像下面这张金字塔图一样。所以我们也常用测试金字塔来区分不同类型的测试粒度。

（测试金字塔，图片来源： TestPyramid ）

如果你对测试类型很感兴趣，可以参考《测试金字塔实战》这篇文章作为补充。

怎么写好自动化测试代码？

很多人认为写自动化测试很复杂，其实测试代码其实写起来不难，包含四部分内容即可，也就是：准备、执行、断言和清理，我再把第一段代码示例贴一下：

第一步就是准备，例如创建实例，创建模拟对象；第二步就是执行要测试的方法，传入要测试的参数；第三步断言就是检查结果对不对，如果不对测试会失败；第四步还要对数据进行清理，这样不影响下一次测试。

上面还有几个测试代码示例，都是这样的四部分内容。

这是针对写一个自动化测试的代码结构。对于同一个功能，通常需要写几个自动化测试才完整。

一个完整的自动化测试要包括三个部分的测试：

验证功能是不是正确：例如说输入正确的用户名和密码，要能正常注册账号；

覆盖边界条件： 比如说如果用户名或密码为空，应该不允许注册成功；

异常和错误处理：比如说使用一个已经用过的用户名，应该提示用户名被使用。

所以你看，写一个测试代码并没有你想的那么复杂，那还有什么理由不去写测试呢？

如何为你的项目实施自动化测试？

现在你了解了有哪些类型的测试，如何写自动化测试代码，也许迫不及待想在项目中实施自动化测试。

选择好自动化测试框架

要写好自动化测试代码，首先要找对自动测试化框架。不同的语言，不同的平台，测试的框架都不一样。好在现在搜索引擎很方便，根据“你的语言 + 自动测试框架”的关键字，就能找到很多的结果。这里我也帮你找了一些，供参考。

Web 前端

Jest： Facebook 的前端测试框架；
Mocha：历史悠久的一个 JS 测试框架；
Nighwatch: 一个 API 很简单，但是功能很强大，可以直接操作浏览器的自动测试框架。

iOS 开发

可以参考这篇文章《iOS 自动化测试框架对比》。

安卓开发

可以参考这篇文章《Android 谈谈自动化测试》。

在持续集成环境上跑你的自动化测试

选好自动化测试框架后，你的自动化测试代码，其中的小型测试和中型测试，最好要能在持续集成环境上运行起来。

让自动化测试在持续集成上运行非常重要，只有这样才能最大化地发挥自动化测试的作用。

因为持续集成，会强制测试通过才能合并代码，在合并代码之前就能知道测试是不是都通过了，可以帮助程序员获得最直观的反馈，知道哪里可能存在问题，这样才能真正做到防患于未然，把 Bug 杀死在摇篮里。

下图描述的就是自动测试配合持续集成的一个标准流程：

在提交代码前，先本地跑一遍单元测试，这个过程很快的，失败了需要继续修改；

单元测试成功后就可以提交到源代码管理中心，提交后持续集成服务会自动运行完整的自动化测试，不仅包括小型测试，还有中型测试；

通过所有的测试后，就可以合并到主分支，如果失败，需要本地修改后再次提交，直到通过所有的测试为止。

（图片来源： Microservice Testing: Unit Tests ）

新项目和老项目的不同策略

如果是新项目，那么可以在一开始就保持一定的自动化测试代码的覆盖率，你甚至还可以试试测试驱动（TDD）的开发模式，也就是先写测试代码，再写实现代码，保证测试通过，最后对代码进行重构。

（图片来源：郑晔 《10x 程序员工作法》专栏）

如果是老项目，短期内要让自动化测试代码有覆盖是有难度的，可以先把主要的功能场景的中型测试写起来，这样可以保证这些主要功能不会轻易出问题。

后面在维护的过程中：

增加新功能的时候，同步对新功能增加自动化测试代码；

修复 Bug 的时候，针对 Bug 补写自动化测试代码。

这样一点一点，把自动化测试代码覆盖率加上去。

如果时间紧任务重，来不及写自动化测试怎么办？

确实遇到时间紧的情况，我建议你要优先保证中型测试代码的覆盖，因为这样至少可以保证主要的用户使用场景是正常的。然后把来不及完成的部分，创建一个 Ticket，放到任务跟踪系统里面，后面补上。

总结

今天我带你一起学习了关于自动化测试有关的知识。自动化测试，分为三类：

小型测试，主要针对函数或者类进行验证，不调用外部服务，执行速度快；

中型测试，主要验证两个或多个模块应用之间的交互，可能会调用外部服务，尽可能让所有测试能在本机即可完成，执行速度比较快；

大型测试，对服务整体进行验证，执行速度慢。

写好单元测试代码，基本结构就是：准备、执行、断言和清理；基本原则就是：

要验证正确性；

覆盖边界条件；

验证是否有异常和错误的处理。

自动化测试，一定要配合好持续集成，才能最大化发挥其效用。

对于自动化测试的实施，开头是最难的，因为需要花时间选择自动化测试框架，需要针对自动化测试框架搭建环境，甚至要去搭持续集成环境。但搭建持续集成和搭建自动化测试环境，并且保证持续更新维护自动测试代码，这个技术投资，一定是你在项目中最有价值的投资之一。

搭建持续集成环境和集成自动化测试框架的事情，要作为一个正式的项目任务去做，当作一个很重要的任务去推进。

课后思考

你所在项目中，自动化测试代码覆盖如何？保持高覆盖率的主要阻力或者障碍是什么？打算怎么改善项目中自动化测试代码的覆盖？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

30 | 用好源代码管理工具，让你的协作更高效

你好，我是宝玉。在今天，源代码管理工具在软件项目中已经是标准配置了，几乎每个软件项目都会应用到，可以说是最基础的项目开发工具。选择也很多，可以自己搭建源代码管理服务，也可以直接用网上托管的服务，例如 Github、Gitlab、BitBucket 等。

但同样是应用源代码管理工具，为什么有的团队就能做到代码质量高，随时能发布新版本，高效开发？而有的团队却不能做到高效开发，拿到的代码也不稳定，合并时冲突很多？

今天，我将带你了解一下源代码管理工具，以及如何才能应用好源代码管理工具，以保证代码质量稳定，协作高效。

源代码管理工具发展简史

源代码管理工具也叫版本控制系统，是保存文件多个版本的一种机制。每一次有人提交了修改，这个修改历史都会被版本控制系统记录下来。如下图所示，每一次对内容的修改，都会形成一个当前项目完整内容的快照。

（图片来源：《什么是版本控制？》）

源代码管理工具从诞生到现在已经有 40 多年的历史了，经历了四个阶段。

没有源代码管理工具的时代

早些年开发软件可没有我们这么幸运，在 1972 年之前都没有任何工具可以帮助我们做源代码管理。

这就意味着，当你开发时，必须要告知团队里的其他人，你正在对哪些文件进行编辑，好让他们不要操作相同的文件。当你花了很长时间完成你的修改后，可能这些文件早已经被团队里的其他开发成员修改或者删除了。

除了协作的问题，还有一个问题就是版本问题。没有源代码管理，你得经常性对项目的文件保存备份，很麻烦，而且还是一样有不少问题：

很难知道做了哪些修改，你可能需要挨个目录去查看文件修改时间；

对版本命名是一个很麻烦的事情，每次备份都得有一个名字；

很难知道两次备份之间，做了哪些修改。

本地版本管理

最早的版本控制系统是 SCCS（Source Code Control System），诞生于 1972 年，它实现了对单个文件保留多个版本，这就意味着你可以看到每一个文件的修改历史了。

后来又有了 RCS （Revision Control System），它具有更好的文件比较算法，通过登录同一台中央大型机，可以实现每个人签出自己的拷贝。

但这个阶段只能本机使用，而且一次只能修改一个文件，无法满足好多人协作的问题。

集中式版本管理

1986 年问世的 CVS（Concurrent Versions System）是第一个采用集中式的服务器来进行版本库的管理工作，所有文件和版本历史都放在服务端，每个用户通过客户端获取最新的代码，可以多个人编辑一个文件，并且能提交到服务器合并在一起。

再后来的 SVN（Subversion）则对 CVS 进行了很多优化，比如支持文件改名移动、全局版本号等，这些优化很大部分程度上解决了 CVS 存在的一些缺陷，所以在 2000 年后逐步取代了 CVS 成为主流的源代码管理工具。

(图片来源：How Version Control Systems Work)

不过，这类集中式源代码管理工具，过于依赖服务器，如果服务器出问题或者连不上，就没法用了，如果服务器损坏，所有的版本历史也会丢失。

分布式版本管理

分布式版本管理工具的典型代表就是 Git，分布式版本控制系统的整个代码库的副本都可以存储在用户的本地系统上，这样文件和版本控制操作变得非常容易，离线也可以操作，如果主存储库关闭或者删除，可以很容易从本地存储库恢复。

现在 Git 已经逐步替代了 SVN、CVS 等源代码管理工具，成为最主流的源代码管理工具。

Git 的主要问题是学习成本要稍微高一点，要花点时间理解它的工作原理和记住主要的命令。

(图片来源：How Version Control Systems Work)

如何选择合适的源代码管理系统

现在源代码管理系统已经有很多的选择，你可以选择网上托管的代码管理服务，或者是自己搭建。

自己搭建的好处就是可以有更多的控制，但是需要有自己的服务器，自己搭建环境，还要后续的维护。用网上的托管平台，可以减少运维成本，功能也很强大，但对平台有一定的依赖。

我的建议是如果项目规模不大，隐私要求不高，完全没必要自己搭建，可以直接选择网上的托管平台。这样可以节约很多时间成本，而且还可以方便和一些第三方服务，例如持续集成等进行整合。

如果希望对源代码管理有更多控制，也能接受运行维护上的投入，就可以选择自己搭建。

自己搭建源代码管理系统

Git

Git 本身是开源免费的，所以每个人都可以搭建自己的 Git Server，具体的操作执行我就不在此处展开了，网上有很多教程。例如：《 服务器上的 Git - 在服务器上搭建 Git 》、《搭建自己的 Git 服务器》

Gitlab

Git 自带的 Server 默认是没有 Web 界面进行管理的，只能用命令行操作交互，这在操作上有很多不便利性，尤其是不方便做代码审查，所以可以安装GitLab 的社区版本，开源免费的，有 Web 操作界面，可以像 GitHub 一样提交 Pull Request，并且和 CI （持续集成）系统例如 GitLab CI、Jenkins 都有很好的集成。

网上也有很多安装教程：《在自己的服务器上部署 GitLab 社区版》、《GitLab 的安装及使用教程》。

Gerrit

Gerrit是由 Google 开发的，用于管理 Google Android 项目源代码的一个系统。它支持 Git 和代码评审。参考教程：《Gerrit 代码审核服务器搭建全过程》

网上的代码托管平台

GitHub

GitHub现在已经是全球最流行的代码托管平台，功能强大，和第三方服务集成非常好。而且私有的代码库如果不超过 3 个人都是免费的，我自己很多个人项目就都是放在 GitHub 上托管。

GitHub的 Web UI 非常强，尤其是代码浏览和审查，在网站上就可以提交 Pull Request 和进行代码审查。不过 GitHub 不提供 CI 服务，需要和第三方 CI 服务集成。

Gitlab

Gitlab的网上托管服务很多地方和 GitHub 都很类似，但是价格更便宜。例如免费用户可以支持无限的私有项目，也内置了 CI 的支持。

Coding

Coding是国内一个不错的代码托管平台，5 人以下的私有库免费，内置了 CI 支持，同时还有项目管理工具支持。

其他的服务还有像：码云、阿里云 Code、百度效率云、腾讯 Git 代码托管、华为云 CodeHub，这里就不一一介绍了。

如何用好源代码管理工具？

用好源代码管理工具，有三个简单可行的原则：

原则一：要频繁的提交

很多开发人员不愿意轻易提交代码到源代码管理中心，喜欢“憋个大招”，本地做了大量修改，希望代码能“完美”。但这样做却没能享受到频繁提交带来的好处。

频繁提交，这意味着你每次提交的代码变更是比较少的，便于 Code Review，同时如果出现问题，也可以迅速定位或者直接回滚。

频繁地提交，也让团队成员可以及时同步最新代码，不至于在最后合并时，产生有大量的合并冲突。

频繁提交，不意味着提交不完整的内容，而是将要提交的内容分拆，并且保证完整性。

比如说，有一个涉及前后端的功能，可以拆分成前端和后端两次提交，各自有独立的代码和测试；比如说你开发新功能的时候发现有代码需要重构，那么对于重构的代码单独一次提交，不要和新功能的代码提交放一起。

原则二：每次提交后要跑自动化测试

在前面章节我们学习了自动化测试和持续集成，自动化测试是非常有效的质量保障手段，而持续集成工具，可以让每次提交代码后，能自动地运行相应的自动化测试代码，有效保障提交代码的质量。

源代码管理的第二个原则，就是每次提交，必须要运行自动化测试代码，如果测试不通过就不能合并，要对问题进行甄别和修复，确保提交的代码质量是没问题的。

原则三：提交的代码要有人审查

代码审查是自动化测试之外，一种非常行之有效的提高质量的手段，通过代码审查，可以发现代码中潜在的问题。通过代码审查，也可以加强团队的技术交流，让水平高的开发人员 Review，可以帮助提升整体代码水平；Review 高水平的代码也是一种非常有效的学习方法。

怎么做好代码审查呢？

我的经验是，在审查别人代码的时候，先了解清楚这个提交的代码要解决的是什么问题，想象一下如何是自己来写这个代码会怎么写。这样在审查的时候，就能发现一些和自己不一样的地方，别人好的地方我们可以学习，不对的地方应该指出。

对于审查出来的问题，可以分成三个类型：

问题：如果对代码有不清楚的地方，可以作为问题提出，进一步澄清确认；

建议：原来的实现没有太大问题，但是可以有不同的或者更好的实现；

阻塞：代码有明显问题，必须要修改。

这样对于被审查的人可以针对你的问题进行针对性修改。

这三个原则很简单，可以有效提升代码质量，减少合并冲突，及时发现问题，从而让你的协作更高效。

我该选择什么样的开发流程？

现在基于源代码管理有三种主要的开发流程：

Git flow

Github flow

Gitlab flow

阮一峰老师有一篇文章《Git 工作流程》对它们有详细介绍。在这里我重点介绍一下 GitHub flow，因为它简单易懂，另外，它也对上面我提到的三个原则有很好的支持。

当然，我并不是说你一定要选择 GitHub flow，而是在理解它后，可以基于它的流程衍生出适合自己项目特点的开发流程。

GitHub 开发流程

GitHub 开发流程的关键在于两点：

有一个稳定的分支，例如 master；

每次创建新功能或者修复 Bug，必须创建一个分支。最后通过代码审查和自动化测试后，才能合并回稳定分支。

通过这样的开发流程，就相当于把自动化测试和代码审查作为一种强制性要求了，所有的修改必须要通过代码审查和自动化测试通过才能合并，从而保证有一个可以随时部署发布的稳定分支。

我们具体看看基于 Github flow 是如何开发的。

第一步：创建一个分支

分支是 Git 中的核心概念，整个 GitHub 流程都是基于分支展开的，master 分支是要一直保持稳定的，不能直接在 master 上开发。

无论你是要开发一个新功能还是修复一个 Bug，第一件事永远是从 master 创建一个分支出来。

（图片来源：Github 网站截图）

第二步：提交更新

当创建好分支后，就可以基于分支开始工作了，这时候就可以按照前面建议的原则，频繁的提交更新。注意每次提交的时候，要加上说明性的信息，让其他人明确知道你这次提交的内容是什么，如果开发过程中，发现错误了，还可以随时回滚之前的更改。

（图片来源：Github 网站截图）

第三步：创建一个 Pull Request

在开发完成后，创建一个 Pull Request（合并请求，简称 PR，Gitlab 中叫 Merge Request），创建 PR 时，通常要附上描述性的信息，关联上相应的 Ticket 连接，让其他人知道你这个 PR 要完成什么任务。创建好 PR 后，其他人就可以直观的看到你所有的修改。

（图片来源：Github 网站截图）

第四步：讨论和代码审查

当你的 PR 提交后，团队的其他人就可以对 PR 中的代码修改进行评论。比如说代码风格不符合规范、缺少单元测试、或者很好没有问题。PR 的主要目的就是为了方便大家做代码审查。

根据代码审查的结果，你可能要做一些修改，那么只要继续提交更新到这个分支就可以了，提交更新后，PR 就会自动更新，其他人可以基于你的更新进一步的讨论和审查，直到通过代码审查。

（图片来源：Github 网站截图）

第五步：部署测试

在合并前，还需要把分支的修改进行测试。理论上来说，需要将修改的内容部署到测试环境测试，但这样效率太低了，所以通常的做法是借助持续集成工具，在每次提交代码后，就运行自动化测试代码，自动化测试代码全部通过后，就可以认为质量是可靠的。

这也意味着你需要让项目中的自动化测试代码保持一定的测试覆盖率，否则质量还是难以保障的。

（图片来源：Github 网站截图）

第六步：合并

当你的代码通过了代码审查和自动化测试，就可以将代码合并到 master 分支了。合并后，之前的分支就可以删除，但你之前所有的提交记录在 master 都可以看到，所以完全不用担心丢失历史版本记录。

（图片来源：Github 网站截图）

以上就是 GitHub 开发流程的主要步骤，通过分支开发新功能或者修复 Bug，强制通过代码审查和自动化测试才能合并 master，从而保证 master 的稳定。

GitHub 开发流程的几个常见问题

基于这个流程我再补充几个常见问题：

怎么发布版本？

要发布版本的话，从 master 上创建一个 Tag，例如 v1.0，然后将 Tag v1.0 上的内容部署到生产环境。

怎么给线上版本打补丁？

如果线上发布的版本（例如 v1.0）发现 Bug，需要修复，那么基于之前的 Tag 创建一个分支（例如 hotfix-v1.0-xxx）出去，在分支上修复，然后提交 PR，代码审查和自动化测试通过后，从分支上创建一个新的 Tag （例如 v1.0.1），将新的 Tag 发布部署到生产环境，最后再把修改合并回 master。

如果我经常需要打补丁，有没有比 Tag 更好的办法？

每次发布后，可以创建一个发布版本的分支，例如 release-v1.0，每次打补丁，都直接从发布分支 release-v1.0 而不是 master 创建新的分支（例如 hotfix-release-v1.0-xxx），修复后提交 PR，代码审查和自动化测试通过后，合并回分支 release-v1.0，然后基于 release-v1.0 分支发布补丁。

最后将合并的 PR，借助 git 的 cherry-pick 命令再同步合并回 master。

上面的例子其实主要是说明一下，GitHub Flow 只是一种基础的开发流程，你完全可以基于 GitHub Flow，衍生出适合你自己项目特点的开发流程。

无论你基于哪一种开发流程，最好能做到这两点：

有一个稳定的代码分支；

在合并分支之前，对代码有审查，自动化测试要能通过。

这样你才能做到可以随时发布，质量稳定，高效协作。

总结

源代码管理工具也叫版本控制系统，是保存文件多个版本的一种机制，可以记录文件的历史版本。

用好源代码管理工具，有三个简单可行的原则：

原则一：要频繁的提交；

原则二：每次提交后要跑自动化测试；

原则三：提交的代码要有人审查。

基于源代码的开发流程，要保证好两点：

有一个稳定的代码分支；

在合并分支之前，对代码有审查，自动化测试要能通过。

用好源代码管理工具，设计好开发流程，保证好代码质量，你的协作才能更高效。

课后思考

你所在项目用的是什么源代码管理工具？你觉得有什么优缺点？你基于源代码管理工具的开发流程是怎么样的？有哪些可以改进的地方？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

“一问一答”第3期 | 18个软件开发常见问题解决策略

你好，我是宝玉。我们专栏已经完成了架构设计和开发这两个模块的学习。这两个模块都是和技术有很大关系，也是很多人关心的内容。

希望你通过对架构设计内容的学习，能控制好软件项目中技术复杂的问题。遇到技术选型，能心中有数，通过一个科学的过程去完成选型；对于项目中的技术债务，能及早识别，及早应对。

通过对开发模块的学习，可以帮助你在项目中搭建持续集成环境，推行自动化测试，改进基于源代码管理工具的开发流程。借助工具和流程，让你项目的开发质量更上一个台阶。

本篇继续分享同学们的精彩留言，这些问答和同学们的分享都是对专栏内容的最好补充，希望可以帮助你更好地学习和理解软件工程知识。

一问一答

No.1
一路向北：每次看这些架构的思想方法的时候，总是和实际的应用没能很好的结合起来，原因是不是架构设计的实践不够？或者是对各种实现的分析和思考太少？

宝玉：我觉得不仅要有架构实践，还要有不同场景的实践。

举个例子来说，你平时做企业应用架构，没什么流量，没多少数据，复杂的地方都在业务逻辑，这时候你去看那些讲大数据、讲高并发的文章，很难带入到场景去。

还有就是一些架构，不自己搭一遍是很难了解其中的优缺点的，这也是另一个原因。

可以考虑有机会自己尝试，把看到的一些好的架构用一个原型程序搭一遍，造一点数据出来，用工具压测一下，这样会更有感觉。

和实际应用想结合的问题，一方面说明你现有的架构可能并没有什么大问题，没有那么迫切的需求要改造；另一方面可能还是因为缺少实践经验，心里没底，不知道真用上了有没有用。

No.2
小伟：比较规范的文档有哪些，他们功能分别是什么？

宝玉：对于瀑布模型，每个阶段结束后，都有相应的验收文档，而敏捷开发则没有那么多硬性的要求，而是根据项目需要，写必要的文档。

有些团队对于测试阶段，会有测试用例文档、测试验收报告，发布前还会有部署文档、维护手册，但现在这类文档基本上被测试工具、部署脚本替代了，也没有什么存在必要。

我觉得项目中必要的文档，主要包括这几类：

设计类文档
这类文档主要用来说明、讨论需求设计、架构设计，可以用来了解、讨论和评审，以及记录后续结果。

说明类文档
这类文档用来对规范、API、配置、操作等做说明，便于规范和统一。

报告类文档
对事情结果的报告和说明，比如说验收报告、故障报告、调研等。

而这些文档的价值，在于帮助成员了解设计、参与讨论，记录项目成果，减少沟通成本。重要的不是文档多丰富，而是这些文档有没有价值，你能不能及时通过这些文档得到想要的答案。

所以你也可以对照一下你的项目中，现在的文档有哪些地方是可以简化的，哪些地方是要增强的。

比如说，概要设计 / 接口设计 / 详细设计是不是可以适当合并，减轻文档工作？PRD 是不是够详细？会不会引起歧义不容易理解，要不要增加原型设计文档辅助？

No.3
邢爱明：项目团队的开发人员，基本都是从外包公司临时找的，水平参差不齐，稳定性差，因此技术选型更多考虑技术的普及度的和是否容易学习掌握，从这方面看基本不太可能选择比较小众、但在特定领域很高效的技术。

加上是企业内部管理的系统，数据量和用户数量可控，因此存在技术瓶颈的可能性很小，综合下来看，最好的选择就是最成熟和通用的技术，比如说选择 java 技术栈，web 开发的 ssm 框架等，但这样长远看团队和个人的技术能力很难提升，请问老师在这方面有什么建议？

宝玉：我觉得团队的技术提升和项目的技术选型要分开，不要总想着两个都兼顾，优先保证好项目稳定、低成本运行。

技术提升这种事，需要让一部分人先成长起来，然后带动其他人。我自己工作之外会做一些业余项目，然后在这些项目中体验新的技术，体会其中优缺点，然后再逐步应用到工作的项目中，传授给同事们。

我也鼓励其他同事这么做，去做一点自己的项目。但工作中的项目，我是很保守的。

No.4
alva_xu：对于开源技术方面，老师有没有什么经验来指导选型？

宝玉：开源技术选型，我的经验一般是这样的。

先找朋友推荐，少走一点弯路。

没有推荐的话，就去网上搜索，找几个满足需求的备选。

对比以下几个指标：

代码质量、有无测试；

文档健全度；

看 Issue 处理情况、最后更新时间（无人维护的项目后续恐怕有问题都没法解决）；

看 Star 数量，通过 Google 和 StackOverflow 看使用情况。

4. 自己按照说明试试看。

No.5
alva_xu：有没有什么大的原则可以指导技术选型？比如技术成熟度等？

宝玉：我认为在满足设计目标的前提下，大的原则还是在于项目约束，尤其是成本和时间，然后就是看技术可行性和风险是不是可控，其他看团队风格，有的偏保守有的追新。

比如说我自己的原则：

成熟的好过新酷的；

流行的好过小众的；

团队熟悉的好过陌生的；

简单的好过复杂的；

开源的好过商业的（有时候也视情况而定）。

No.6
Charles：有着正常职位或头衔的架构师，对一个全新的项目理解产品需求后进行架构设计，一般会产出哪些“东西”，来满足后续的架构讲解和项目开发过程中的沟通？

宝玉：互联网产品特点是用户多，企业产品特点是业务复杂，所以架构的侧重点不一样。

架构师在架构设计后，产出首先是架构设计文档，让大家理解架构。然后还要写架构开发的文档，比如如何基于这个架构开发功能模块，有哪些公共 API 可以调用，怎么样是最佳实践，要遵守哪些规范等。

再要帮助搭脚手架和基础模块或示例项目，也就是要搭建一个最基础的可运行项目，通过这个项目，大家可以直观地理解你的架构是怎么落地的，通过基础模块或者示例项目，可以知道如何基于框架开发，后面就也可以照葫芦画瓢照着实现。

还有就是在开发过程中，要答疑、解决架构中存在的问题，对架构做优化，还要做代码审查，对于不符合架构规范的地方要指出和修正。

No.7
Dora：互联网架构，要考虑互联网很快的迭代速度，所以对于扩展等特别注意。企业架构，内部 IT 系统相对稳定，对比互联网架构，更简单？

宝玉：挺好的分析。帮你补充几点：互联网架构不仅迭代会快一些，用户规模通常更大，但业务也会单一些；企业应用通常业务比较复杂，尤其是和行业会有一些结合，但是用户规模要小很多。这些特点，都会影响架构设计的选择。

No.8
WL：老师能不能具体讲讲重构有哪些原则和要注意的地方，感觉一直得不到要领。

宝玉：重构的要领我觉得两点。

第一：你要先写一部分自动化测试代码，保证重构后这些测试代码能帮助你检测出来问题；

第二：在重构模块的时候，老的代码先保留，写新的代码，然后指向新代码，或者用特定开关控制新旧代码的指向（这样上线后可以自己先测试，有问题也可以及时关闭），然后让自动化测试通过，再部署测试，新代码没问题了，删除旧代码。

No.9
bearlu：有没有事情管理的工具？因为如果不记录下来，一会儿就忘记了。

宝玉：留言区 McCree 同学推荐了滴答清单。我个人的话，一般就用系统自带的记事本记一下，或者贴一个便签纸在显示器。如果时间跨度长，我就记到 Calendars 上，加上提醒。工作中的任务，我则会创建成 Ticket。

No.10
W.T：现在还有一种说法：提倡基于主分支开发，效率更高；而不是您提到的每人基于自己的分支开发完再合并回主分支。您怎么看待这个问题？

宝玉：我认为对于软件工程来说，很多问题，并不是只有唯一解，即使是最佳实践，也得看适用的场景和团队。

无论是基于主干还是分支开发，有两点需要注意的：

就是一定要有一个稳定的分支，可以随时发布的那种，至于是叫 master 还是叫 release 并不重要。

合并之前要有代码审查和自动化测试（配合 CI）。

上面两点才是核心。

No.11
hua168：如果一个项目有 5 个开发做，持续集成怎么保证不乱？比如开发 A 刚刚修复的 bug1，开发 B 把自己修复的 bug2 上传，之前的代码 bug1 没修复，怎么办？如果采用分支怎么合并？如果是直接更新 master 分支，那 A 不是白做了？

宝玉：要注意是“合并”而不是“覆盖”。比如说 bug1 涉及 file1 和 file3 的修改，那么开发 A 合并的时候只合并 file1 和 file3。

等到开发 B 修复了 bug2，修改了 file1 和 file2，file2 直接合并，file1 需要手动去修复合并冲突才能合并。

每个人开发之前，都会从 master 获取最新版本，合并的时候，如果出现冲突，要先解决冲突才能合并进去。这些其实应该自己去动手试试，会体会更深刻。

No.12
dancer：在微服务架构中，一个服务在测试环境的交付验证，往往还依赖于其他相关服务的新版本，导致新的 feature 很难独立的交付。对于这种情况，有什么好的方法吗？

宝玉：我觉得对于大部分时候，微服务之间应该是独立的，而不是依赖过于紧密，如果每一个新功能都会这样，那架构设计一定是有问题的，需要重新思考服务划分的合理性。

但你需要有更多上线或者场景我才能针对性提出一些意见。对于有一些确实需要跨服务合作的大 Feature，这样也是正常的，就是需要一起协作，实现商量好通信协议，分头开发，再联调。

No.13
Gao：老师所讲排查生产问题的案例，首先回滚版本，再看日志。这会引发更多的系统功能不可用吧，两个版本之间的功能差异尚不清楚就直接回滚，系统风险是否被进一步扩大？

宝玉：这个确实要具体情况具体看，因为我日常的系统上线，都会有回滚方案，回滚也是自动化的很方便。有些跟数据库相关的，如果数据库结构发生变化又产生了新数据，确实没法直接回滚。

No.14
kirogiyi：团队成员的能力和素质参差不齐，如何有效的去组织和管理项目的自动化测试，自动化集成？

宝玉：首先，你要先搭建好自动化测试环境，让自动化测试代码能跑起来，最好要和 CI（持续集成工具）整合在一起，每次提交代码 CI 都会跑自动测试，然后能看到运行结果。

然后，把自动化测试作为开发流程的一部分，比如说要代码审查和自动化测试通过后才能合并代码。这部分工作如果和 CI 集成会容易很多。

再有就是要培训，比如遇到不会写的，开始先带着他写几个，确保他学会了自己能写，然后下次代码审查的时候，看到缺了就要求补上，还不会就继续教，来不及写的就创建个 Ticket 跟踪起来。

简单来说，就是代码审查 +CI+ 培训。

No.15
探索无止境：各种类型的测试覆盖率你们一般采用什么指标？个人感觉在理想的情况下最好是做到百分百覆盖率。

宝玉：100% 覆盖，这个我觉得可以作为一种理想追求，但是没必要追求极致，还是要在进度和质量之间有个平衡比较好，毕竟进度也很重要。

另外对于前端业务，我更重视集成测试的覆盖，对于主要业务场景集成测试覆盖到位后，单元测试也就有比较多的覆盖，相对性价比更高，然后再逐步补充单元测试的覆盖率。

No.16
起而行：持续集成怎么理解呢？我看知乎上说，有的团队成员在一天内多次进行编译，发布或自动化测试。

宝玉：狭义的持续集成不包括发布，主要指集成，持续的（每次提交代码变更都触发，频繁地提交）对代码进行集成（合并到主干），但集成前要确保自动化测试通过。广义的持续集成还包括部署，也就是集成后自动部署测试环境 (持续交付) 或者生产环境（持续部署）。

No.17
小小：请问下有没有介绍开发如何写好测试不错的书？

宝玉：推荐：《how we test software at microsoft》中文版《微软的软件测试之道》。不过没有书其实你也可以找到很多资料的。比如我平时写前端程序，那么我会去 GitHub 或者 Google，通过关键字、语言找跟我项目类似的开源项目，然后看其中有没有自动化测试写得好的。

找到了 (例如：reactstrap、electron-react-boilerplate、kitematic) 就照葫芦画瓢好了，因为都是真实项目，所以特别简单有效，建议你也可以试试。

另外耐心一点，你也可以看到很多关于测试知识分享的技术文章，多看一看也有收获。

No.18
hua168：代码审核是纯手工做的吗？没有好的工具？

宝玉：代码审查可以参考 GitHub 上一些开源项目的 PR Review，通常网页上可以清楚地标记出代码修改，针对代码行可以写 Review 的评论，这就已经很方便了。

其他工具主要是 Lint 检查代码规范、语法错误等，这个一般在 CI 里面就集成了。

精选留言

陈珙 ：
在没有特殊要求的情况下，项目中更加倾向选择更为熟悉的技术，因为我们需要对项目的质量与交付时间负责，可以做到可控的。而新技术有着新的设计思想与强大的功能，同时也伴随着无法预知的“坑”。在后续产品迭代的时间里，有针对性的升级或者选择更换同类技术里更优的。

相关阅读： 22 | 如何为项目做好技术选型？

Y024：
Appfuse（一个 Web 开发基础平台）的作者 Matt Raible 曾总结了选择 Web 框架的 20 条标准。

同时，他也整理了一份表格，你可以根据自己的权重进行调整，产生自己的分析。

但是现实情况，大家可能更遵循的是“经济适用原则”，比如：很多人提到的，负责人会啥就用啥，或者大公司者业界流行什么就用什么。

有位大佬说过，“这个世界是，你认为有很多选择，其实只是幻觉，大多数人只有很少的选项。技术研讨会，搞一个选型：hadoop + mysql + xx 时髦技术。架构师唾沫四溅吹一下午，结果老老实实上 Oracle 单例。”

相关阅读： 22 | 如何为项目做好技术选型？

kirogiyi ：
架构师是一个概念性职位，没有明确的界定，需要具备的能力和素质也是千差万别，每个开发人员心目中的架构师画像也都不一样，神秘的 IT 牛人，高级的保姆，无休的恶魔…

在我看来，一名优秀的架构师应该具备良好的技术思维、产品思维和项目管理思维。技术思维是基础，评估技术难度、分析技术复杂度、准确把握技术方向，这些都是架构师在设计架构时面临的技术决策。

产品思维是骨架，在产品思维上构建起来的整体全面的产品意识，可以对业务、功能、模块进行明确的抽象、分治、迭代等等。

项目管理思维是方向，无论是敏捷管理模型还是瀑布管理模型，都需要在不同的时间、不同的环境条件下去关注金三角理论的取舍所带来的影响，降低技术以外对项目带来的局限性。

不过，架构师也不是想象中的那么神秘。开发人员和架构师的差别，最主要是层次和格局上的差别，导致最终产生了不同的结果而已。

试想，两个能力相同的开发人员，一个的目标是每年涨工资（80% 开发人员），他会去努力多做事，拓展技术的深度；一个的目标是 CTO（20% 开发人员），他会去努力多做事，多思考，多学习，多交流，尽力做到面面俱到。几年以后的结果就不言而喻，至少坚定的目标能够推动过程的发展。

相关阅读：23 | 架构师：不想当架构师的程序员不是好程序员

alva_xu :
讲到架构，我想先得谈一下康威定律。康威在 1967 年曾说过：

Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations.

通俗地说，就是组织形式等同于系统设计。所以系统架构设计的进化，是和组织形式的变化结合的。

从 ITIL 来说，BIA，（business IT alignment）是 IT 的核心，所以充分认识组织的业务模式和运营方式，才能让架构师设计出适合于企业的系统架构，架构设计的最高境界就是适合企业业务的运营。

从单体架构到微服务架构，从前后端分离到中台，都是架构适应业务（功能与非功能需求）的体现。所以架构师首先必须要有业务思维、产品思维。TOGAF 把企业架构分成业务架构、应用架构、数据架构、技术架构四个子域，我觉得相当全面。

从程序员开始，如果能培养好老师讲的架构师能力模型中的四个思维和三个能力，我们可以给自己规划出一个架构师的成长路径，从单个业务应用开始，然后扩展到一个业务领域，最终到达企业架构师，甚至成为跨企业应用架构师的境界。

相关阅读：23 | 架构师：不想当架构师的程序员不是好程序员

纯洁的憎恶：
技术债务不全坏，与金融债务一样，需要具体问题具体分析。轻率 & 有意的债务要避免。谨慎 & 有意的债务有收益。

轻率 & 无意的债务要警惕。谨慎 & 无意的债务要改变。识别债务防患于未然。根据成本收益分析，决定重写（一次性还款）、维持（只还利息）还是重构（分期付款）。

相关阅读：24 | 技术债务：是继续修修补补凑合着用，还是推翻重来？

kirogiyi ：
在研发过程中，产生技术债务的时候，稍微有点技术功底的人，或多或少都会有感觉的。

比如：有重复代码的时候，会意识到好像已经写过了；函数命名的时候，会意识到好像有个相似的名称已经命名过；函数行数过多的时候，自己心里会感觉不舒服等等。

更有甚者，你去整理这些问题还会被同事标上“强迫症”患者的称号，还是放弃吧。技术债务就这样在外部和内部双重压力下自然而然的产生了。

那么如何产生有利的技术债务呢？我觉得应该从公司制度、研发流程、个人素质培养三方面入手。

公司制度实际上是为领导层准备的，领导层以身作则去影响下面的员工，员工就没有冒犯的理由，比如合理的奖惩制度，要做到公平合理，一视同仁；

研发流程主要是让团队成员知道自己什么时候该做什么事情，如何去按照指定的约束去做好自己的事情，除此之外，还应该给予明确的成长上升空间；

员工素质的培养则需要从一个人的职业素质，技能优化，团队协作方面着手，让他们拥有积极努力的心态参与到工作中去，这基本上就能解决最基础的技术债务问题（领导决策错误产生的技术债务另当别论）。

在我遇到过的技术债务中，主要由领导决策、产品业务逻辑、技术最初选型、技术更新换代、团队综合素质中的一种或几种导致。对此，我只能说个人能力达到什么层次就应该去解决什么层次的技术债务，不能去推诿和落井下石，在你手中的技术债务就应该当成自己欠下的技术债务来解决，这样才能持续性的做好自己分内和分外的事情，工作起来才能得心应手。

相关阅读：24 | 技术债务：是继续修修补补凑合着用，还是推翻重来？

kirogiyi：
我觉得高效，意味着自律，自律的好坏是可以通过你散发出来的气息让周围的人感受到的，比如：说话有没有条理，做事拖不拖延等等。

生活自律，你会发现每一分每一秒都充满了希望和力量，用积极乐观的心态去完成每一件事，知道自己上一步做好什么，下一步才能做好什么。

工作也是一样，要想高效完成任务，需要利用前辈们总结的思想和方法，去长期实际应用，在使用的过程中就会体现出你的高效，不能说我知道单元测试，我知道 CI…，很少有人讲我一直在用。

如果我们注意观察，会看到身边的同事，有的很少加班（活蹦乱跳），有的经常加班（蔫头耷脑），做了一样的事情用了不一样的时间，此时就能真正的体会到高效做事的魅力了。

我不提倡加班的原因就在于此，但那是针对高效人士的，低效人士不加班，老板是不会答应的。而一般对自己的时间把握比较好的人，在估算工作时间或工作量的时候，都比较果断，不会支支吾吾，还会主动给出具体时间点和阶段性成果，让人觉得这才是真正做事的人应该有的态度。

我的看法是，积极、主动、自律是高效人士的必备素质。

相关阅读： 25 | 有哪些方法可以提高开发效率？

nigel：
就学习能力而言，“祭海先河，尤务本原之学”，重要的是对基础知识的掌握。就像侯捷先生说的“基础的东西不易变，不易变的可重用”。

相关阅读：27 | 软件工程师的核心竞争力是什么？（上）

_CountingStars：
之前看到过一个关于 code review 的观点：在让别人 review 你的代码的之前，你要确保你的代码没有基础的问题，比如单元测试要通过，不能有代码风格问题，首先你要确保你的代码是能正常工作并解决需求的。当然这些基本都可以通过自动化来操作，比如提交 PR 的时候，自动化的检查代码风格，运行单元测试。保证邀请别人 review 你代码的时候，不要为这些小事费精力，提高 review 效率和积极性。

相关阅读：30 | 用好源代码管理工具，让你的协作更高效

bearlu：
其实我觉得用什么源代码管理工具都没关系，最重要是要了解工具，形成流程，按流程走，然后纠正流程。

相关阅读：30 | 用好源代码管理工具，让你的协作更高效

思辨时刻

Charles：
三四线城市，技术选型前期主要考虑：当地市场什么人才比较充足，比如后端 PHP 人多，那就 PHP，学习成本也低，几人团队协作起来也不是大问题，而且前期扩充人员也比较好招人；另外前期应该也不会在语言层面出现性能问题。

然后数据库基本就选 MySQL，够熟悉够成熟。前端的话，web、小程序、ios、android 之类的都统一 MVVM 思想，进行前后端分离开发，这样各个用户端都可以统一 API 提升效率，这个也会从产品角度思考。

如果产品经理就只是需要一个 PC 网站，而且短期也没升级计划，就选择传统的后端渲染 web 页面方案。可能会站在目前项目或经历过的项目经验去思考问题，期待老师回复指正。

宝玉：
我觉得你的选型思路在项目发展阶段，包括没有很大规模之前都是没有问题的。选最熟悉的、流行的往往也是风险比较低的。包括如果就是一个 PC 站也不做 SPA（单页应用），也没有必要前后端分离。还是看是不是能低成本满足好项目需求和业务发展。

有一点补充的，就是前端除了 MVVM，像 React 的 Flux 和 Redux 的架构模式，也是一种很好的架构模式，但在非 Rect/Vue 的项目中应用不多。

相关阅读：22 | 如何为项目做好技术选型？

好，今天的加餐就到这里，非常感谢同学们的用心留言，希望大家都能持续思考与总结，不断精进！

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

31 | 软件测试要为产品质量负责吗？

你好，我是宝玉。从这一篇开始，我们将进入软件工程中的测试模块的学习。

说到软件测试，你一定不会陌生，尤其是如果你做开发相关岗位的话，一定是对测试又爱又恨，一方面测试从你的程序找出 Bug，然后你还要费心去修复；另一方面测试帮你发现 Bug，修复后能很好的提升质量。

正因为测试能发现软件中的质量问题，通过测试能有效提升软件质量，慢慢的大家就觉得软件测试能保障质量，所以测试要对质量负责。开发也会对测试产生依赖心理，很多功能模块实现后，就扔给测试人员去测试。

上线后，如果因为有测试漏测导致的 Bug，测试人员还要为质量问题背锅，受到责备。上面这样的场景到现在也还在很多软件项目中上演。但这对测试人员其实是不公平的。

因为软件开发是多个环节组成的，从最开始的需求，到后面的设计、开发，每个环节都可能会导致质量问题，而测试只能对已经开发完成的软件产品进行检测，并不能干预整个过程。

比如说测试是无法对开发写的代码直接测试的，只能基于软件功能去测试，也就是说对于代码的质量，测试人员其实是没有什么办法的。

那到底谁应该为产品质量负责呢？在回答这个问题之前，你不妨先思考一个更本质的问题：什么是软件产品质量？

什么是软件产品质量？

我以前以为，软件质量就是由 Bug 数量、性能高低、安全性等指标决定的，现在看来这样划分其实并不全面。

因为不同的人对软件质量好坏的评判角度是不同的。比如对用户来说，更看重产品是不是满足需求，是不是美观好用；对开发来说，看重的是代码质量是不是高，是不是好维护；对于软件测试人员而言，看重的是 Bug 数量、安全、性能等指标；对于项目负责人，看重的是整个开发过程的质量，是不是成本可控、如期完成。

在这个问题上，我比较认同《The Three Aspects of Software Quality: Functional, Structural, and Process》这篇文章作者 David Chappell 的观点，他把软件质量分成了三个考量方面：功能、结构和流程。对于他提的“结构质量”，我认为定义为“代码质量”更贴切，也就是说，功能质量、代码质量和过程质量这三个方面组合在一起，很好地概括了软件质量。

所有的软件开发都是从一个想法开始的，用户需要一个软件，有人出钱，然后开发团队实施，把想法变成需求，需求变成设计，设计变成代码，代码变成软件。

功能质量

最终用户得到是软件，体验的是软件的功能，功能的质量直接决定了产品的质量。

满足用户需求，是对功能质量最基础的要求。在这个基础上，Bug 数量、性能、UI/UX 都是很重要的质量指标。如果你的软件 Bug 太多、性能差，用户不会满意；界面难看，操作体验也很差，这些因素都决定了你产品的功能质量。

代码质量

构成软件最重要的部分是代码，代码质量指的是实现软件功能的架构和代码的质量。代码的质量主要体现在以下这些方面：

代码的可维护性，也就是在不影响稳定性的前提下，是否能方便地添加或者修改现有的代码；

代码的可读性，代码是否容易理解，是否能快速上手；

代码的执行效率，代码执行效率直接影响了软件性能；

代码的安全性，是否有安全漏洞，安全性是代码质量很重要的一个指标；

代码的可测试性，代码是否能使用单元测试、集成测试进行测试验证。

虽然用户不能直接感知到代码，但是代码质量高低会直接影响功能质量，同时代码质量低也会影响后续的维护升级。

过程质量

软件的开发离不开软件工程，离不开项目管理。软件开发过程的质量决定了你的项目是否能如期完成，开发成本是否在预算之内。

过程质量虽然也是用户不能直接感知的，但是过程质量会直接影响代码质量和功能质量，甚至是产品的成败。

以上就是软件质量的三个方面，软件质量从来不是单方面质量决定的，通常是几方面质量因素相互影响，共同决定的。

比如说改进流程，增加了自动化测试的覆盖，引用了持续集成，这样可以提高代码质量和功能质量。或者说对代码质量过于追求，又可能会影响过程质量，例如时间延期，成本超标。

谁该为产品质量负责？

在梳理清楚产品质量的问题后，我们就可以来讨论谁该为产品质量负责的话题了。

既然产品质量是由功能质量、代码质量和过程质量共同决定的，那么对产品质量负责，意味着要对这三方面共同负责。

在说到责任之前，我想补充一下权责对等的问题。责任和权力是需要对等的，比如说你让开发人员对软件开发过程负责，那么前提是他必须有权力去影响和控制开发过程，否则离开权力谈责任就是耍流氓了。

然后，我们再一起看看项目中的主要角色，谁最应该为产品质量负责？

软件测试，可以对功能质量负责，对软件产品进行测试验收，以确保产品满足功能需求，有好的功能质量。但是通常不能对代码质量和过程质量负责。

开发人员，可以对代码质量负责，也可以写测试代码，通过自动化的方式做功能测试，虽然还不能完全替代手工测试的作用，所以也可以算得上对功能质量负责。但开发人员通常对过程质量影响有限。

项目负责人，可以对过程质量负责，而且过程质量的水平高低，会间接影响代码质量和功能质量。但因为项目负责人不直接编码和测试，所以无法直接影响代码质量和功能质量。

所以综上，我觉得如果要排序的话，软件质量的首要负责人是项目负责人，其次是开发人员，然后才是软件测试。

虽然从权责的角度看，项目负责人是最应该对项目质量负责的，但是从效果来说，却是开发人员对项目质量负责最有利。

首先，开发人员是唯一能直接影响代码质量、能对代码质量负责的人。开发人员能更容易地找到代码中的 Bug，更容易通过架构设计、自动化测试代码等手段保证好代码质量，提升测试效率。

现在软件开发的发展趋势也是如此，软件测试的很大一部分手工测试工作已经被自动化代替。

所以很多公司就让开发负责产品质量，甚至都不设测试岗位，典型代表就是 Facebook。开发人员自己写代码实现功能，然后写自动化测试代码对功能进行测试，最后上线。这样不仅自己测试能保证功能的质量，又能通过自己写单元测试、集成测试来保证代码的质量。

当然，开发人员对功能质量负责，意味着必须在实现功能的同时，还要考虑如何去测试这个功能，这样让代码更具有可测试性，这就对开发人员的要求更高了。

就像 Facebook 强调的“Be there from start to ship”，就是让每个工程师能自始至终地负责产品。从想法到原型设计、到产品开发、上线和维护，全部是工程师自己完成。

我们不需要做到 Facebook 那样，从头到尾都一个人搞定，但至少，作为开发人员，我们可以对代码质量有更高要求，让项目有更多自动化代码的覆盖；可以在交付测试之前自己先测试一遍。

这样的话，开发就可以真正做到对代码质量和功能质量负责。如果你还想对过程质量也能负责，那么敏捷开发中一些理念是有可取之处的。

敏捷开发中强调的是：项目的所有人一起为产品质量负责，人人为产品质量负责。

但人人为质量负责，很容易变成一句口号而很难落实。就像三个和尚没水喝的故事里面那样，当质量变成每个人的责任时，就没有人真正为质量负责了。所以我们不止是要学习敏捷开发中的理念，还要学习它一些具体的方法。

如何做到“人人为产品质量负责”？

只有真正在团队中建立了一种重视产品质量的文化，每个人才会确确实实地对质量负责。那么有哪些方法可以帮助团队建立这种“人人都重视产品质量”的文化呢？

首先，可以参考敏捷开发中的扁平化管理。在敏捷开发中没有项目经理，只有产品负责人，而产品负责人更多是充当一种服务型的角色。大家都很平等，也就是说每个人都有权力去影响到项目过程，实现权责对等，大家才会为过程质量负责。

其次，可以选择将团队拆小。敏捷开发中的团队规模都不大，大的开发团队拆分成了小的开发小组，每个组人数都不多。人数多的时候容易推诿扯皮，但如果人少，每个人就必须要承担更多的责任，这有助于形成人人重视产品质量的文化。

另外，也可以鼓励工种之间的融合，例如开发人员多写自动化测试代码；测试人员在开发人员写自动化测试时，提供帮助，例如设计测试用例。这样不只是局限于各自负责的质量领域，也同时关注其他质量领域。

最后就是制定相应的制度，鼓励大家重视质量。比如说：

每个 Sprint 都有项目回顾会议，每个人都可以针对质量提出有效的建议，最终将这些建议落到实处；

出现质量问题，不是推卸责任，而是分析原因，及时修复，避免以后出现类似问题。

要做到“人人为产品质量负责”，还是要像上面提到的一样，要落到到行动而不是口号上，组织上扁平化、小型化，分工上打破岗位墙，制度上鼓励大家重视质量，才能真正建立重视产品质量的文化，一起把产品的质量提升上去。

总结

今天我带你一起探讨了一个在软件项目中的常见问题：软件测试要为产品质量负责吗？

保证软件高质量，并非只是测试人员的责任。软件质量体现在功能质量、代码质量和过程质量这三个方面，对产品质量负责，也意味着要对这三方面共同负责。

软件测试，不能影响代码质量和过程质量，所以并不需要为产品质量负责，项目负责人能直接影响过程质量，也能间接影响代码质量和功能质量，应该为产品质量负责。对于开发人员而言，不应只是局限于对代码质量负责，还应该注意功能质量。

对产品质量，最理想的状态还是能做到人人都为产品质量负责，而达到这样的目标，还是需要建立一种重视质量的文化，每个人才会确确实实地对质量负责。

课后思考

你所在项目组中，谁为产品质量负责？你觉得应该怎么样在团队中建立一种好的重视质量的文化？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

32 | 软件测试：什么样的公司需要专职测试？

你好，我是宝玉，我今天想与你讨论一下，什么样的公司需要专职测试这个问题。

若干年前，网络上对于软件开发是否需要专职测试有过一次讨论，代表文章有：左耳朵耗子老师写的《我们需要专职的 QA 吗？》，然后邹欣老师对此回复《测试 QA 的角色和分工》。

从这些年业界发展趋势来看，看起来很多公司都不需要专职测试了，只需要开发兼任测试工作就可以了。比如，Facebook 号称自己没有专职测试工程师，Google 和 Amazon 虽然有专职的测试工程师，但都是开发人员对质量负责，开发人员写大量的自动化测试代码。但这样真的可行吗？

在回答这个问题之前，我们还是先来看看，软件测试的主要工作是什么？只有搞清楚软件测试的工作，才能搞清楚这部分工作是否可以由开发来替代，是否需要专职测试。

软件测试的主要工作是什么？

在前面开发篇内容的学习中，我们对开发的工作已经比较了解了：在需求确定后，开发人员开始针对需求进行架构设计，然后编码，最后对发现的 Bug 进行修复，保障线上稳定运行。

而软件测试也类似，也是从需求开始，在需求确定后要去对需求进行分析，然后做测试设计。

如果说架构设计是对业务需求在技术方面的抽象，那么测试设计更像是对业务需求的具象，把业务需求分解成一个个具体的用户操作步骤，也就是测试用例。然后在开发完成后，按照设计好的测试用例进行逐一的测试验证，将发现的 Bug 报告给开发人员，并跟踪 Bug 的修复。

如果对软件测试的工作简单总结一下，就是发现 Bug，报告 Bug，跟踪 Bug。

软件测试怎么发现 Bug？

这里面最难的就是发现 Bug，尤其是如何尽早、尽可能全面地发现 Bug。

举个例子来说，如果现在你需要开发一个用户登录的功能，你在开发完成后会怎么测试？

一个普通的程序员通常只会简单测试一下以下用例：

输入正确的用户名、密码，能登录；

输入错误的用户名密码，提示错误，不能登录。

而一个有经验的程序员还会测试一下其他情况，例如：

用户名或者密码为空，是否提示错误；

没有注册的用户名和密码，是否会提示错误。

但如果是一个专业的测试人员来测试，是不是只做上面的测试就够了呢？专业测试还会测试哪些内容呢？

对于专业测试人员来说，上面这些肯定是不够的，还需要有以下这些情况的功能性测试：

用户名密码是否大小写敏感；

用户名或密码如果是用特殊字符，会不会导致程序异常；

用户名或密码如果特别长，是不是会有异常；

是不是所有主流浏览器和终端设备都能使用。

这就完了吗？并没有。除了功能性的测试，还需要进行非功能性的测试，也就是像性能、安全性和用户体验等方面的测试。比如以下测试用例：

是否可以通过发送数据包反复登录，暴力破解密码；

会不会有 Sql 注入的风险；

大量用户同时登录，页面会不会崩溃；

用键盘 tab、回车键是否可以操作。

当然还会有其他测试用例，这里就不一一列举。为什么专业测试人员和开发人员的测试用例会差这么多？

因为开发人员的重点，是放在如何实现功能上，就拿上面用户登录的例子，开发人员会想着如何能校验用户输入的用户名密码，并给出相应的提示，对于异常流程和场景会相对考虑较少。

而对于测试人员来说，重点是在检测，也就是会考虑所有可能的用户使用场景，正常的、异常的，甚至各种极端情况，例如大量并发访问、黑客恶意破解，所以他们能想到更多、更全面的测试用例。

测试人员设计测试用例，就是要尽可能做到覆盖所有用户操作的可能，但理论上来说这是不可能的，因为组合是有无限多个的。不过从测试的角度看，没有必要每一个可能都去测试，可以通过一些科学的方法来通过有限的测试用例，保证尽可能多的测试覆盖。

有哪些方法呢？我给你举几个例子：

等价类划分

就是把所有用户可能输入的数据分类，如果一类数据对于发现 Bug 的效果是一样的，那么这类数据就是一个等价类，测试的时候只要从里面任意选取一个值就好了。比如一个输入框要求只能输入 1-100 之间的整数，那么 1 到 100 之间都是等价的，0 和任意负数也是等价的，101 和之上的整数也是等价的。

因为分类是有限的，这样就可以用有限的测试用例实现尽可能多的测试覆盖。

边界值分析

边界值是对等价类的补充，因为输入输出的边界是非常容易出错的一个地方。比如说上面输入框的例子，0，1，100，101 都是边界值，可以设计用例来测试是否会有可能出错。

探索性测试

探索性测试就是根据前面的测试结果，通过有效的策略进行测试。

举例来说，如果你玩过 RPG 游戏的话，里面通常会有走迷宫的地图，各种分叉，不同的分叉可能有不用的宝箱，如果你想打开所有的宝箱，那么就可以在遇到分叉的时候，每次优先走右边的分叉，除非右边已经去过了。

那么这里“右边的分叉是不是走过了”，就属于已经测试过的结果，策略就是优先走右边的分叉。关于探索性测试的介绍可以参考这篇文章《探索性测试揭秘》。

当然除了以上这几个主要的策略，还有很多其他的策略，比如说：

场景设计；

因果图；

错误推测法。

这里我就不一一介绍，推荐阅读《微软的软件测试之道》，这本书上有很多具体的测试方法的详细介绍。

借助这些方法，测试人员就可以对需求功能设计出完整的、有较高覆盖率的测试用例。

所以，有时候测试人员的工作看起来不过是用鼠标点点测试，但他们在拿到需求后，其实花了很多时间和精力分析需求，然后根据需求设计测试用例，准备测试数据。等到开发人员完成软件开发后，就按照设计好的测试用例逐一测试，这样就可以做到及时发现 Bug。

软件测试怎么报告 Bug？

在测试的过程中，如果测试人员发现了 Bug，就会通过 Bug 跟踪系统提交 Bug 给开发人员。Bug 跟踪系统其实跟我们在《14 | 项目管理工具：一切管理问题，都应思考能否通过工具解决》中介绍的任务跟踪系统是一样的，它可以方便地提交和跟踪 Bug。

测试人员要做的就是创建一个新的 Ticket，在 Ticket 的描述中，详细说明 Bug 是什么，具体的重现步骤，必要的话还要附上截图、日志等辅助信息。这样开发人员在收到 Bug 后就能快速定位问题，按照优先级对 Bug 进行修复。

软件测试怎么跟踪 Bug？

Bug 的跟踪，并不仅仅是要跟踪开发人员什么时候修复了这个 Bug，通常还包括对 Bug 修复的验证。

开发人员修复完一个 Bug 后，测试人员首先会验证这个 Bug 是不是真的被修复了，然后还要对整体功能做一个回归测试，确保不会因为修复 Bug 而引起其他功能出现问题。

回归测试是指修改了旧代码后重新进行测试，以确认修改没有引入新的错误或导致其他代码产生错误。

回归测试这一步很重要，因为通常开发人员在修复完 Bug 后，只会验证其修复的 Bug，而不会验证其他功能是不是会有影响。但实际上，软件项目中经常会出现修复一个 Bug，而导致系统其他功能出现问题的情况。回归测试，则能有效、及时地发现修复 Bug 导致系统不稳定的情况。

什么样的公司需要专职测试？

了解了测试的主要工作内容之后，我们再回过头来看看今天要讨论的问题：什么样的公司需要专职测试。

如果一个公司不需要专职测试，那么意味着专职测试的工作可以被其他工种替代，比如说由开发人员一起完成软件测试的工作。

想象一下，如果你是一名开发工程师，然后你要兼职做测试，那么你需要额外做好哪些工作？

首先，你在拿到需求后，除了做技术上的设计外，还需要做测试上的设计，借助测试方法设计测试用例。

这样做好处很明显，可以在写程序时，让程序更易于测试，设计时会对需求考虑更全面。缺点也显而易见，你不止要学习编程知识、了解业务，还要学习测试方法。也许对你来说可以做到，但是对于绝大多数开发人员来说，这是一个很高的要求。

然后在开发完成后，要对自己写的程序进行测试。这里可能存在一个问题，也就是如果你在程序实现的时候，漏掉了一个逻辑处理，比如说漏了检测 Sql 注入，那么你在测试的时候也不会想到要去测试这部分。

测试自己的程序还要克服一个心理障碍，就是要对自己的程序进行破坏性测试，才可能找到潜在的问题，但去“破坏”自己完美的程序，对大多数开发人员来说也是很难接受的一件事。

如果上面两个问题都能克服，你还得再考虑一个问题：如果项目进度比较吃紧，作为开发人员你会压缩哪部分时间？

正常来讲，测试时间必然要被压缩的，因为你首先得保证代码实现。这可能就导致只要项目进度一紧张，测试就被严重压缩了，进而会严重影响质量。

这样看来，完全由开发人员兼职测试，还是很有难度的，不仅对开发人员要求非常高，而且需要开发人员承担所有的开发和测试的压力。

为什么 Facebook 可以做到没有专职测试呢？

首先 Facebook 的工程师水平确实是高于业界平均水平的，有能力同时做好开发和部分测试工作；

其次，Facebook 的产品周期相对宽松，可以有时间完成自动化测试代码；

Facebook 在功能发布之前，先发布新功能到内部环境，几千内部员工先测试，部分充当了测试人员角色；

Facebook 的发布和监控也比较完善，有问题能通过监控及时发现，并且可以随时快速回滚或者发布补丁；

最后就是用户对这类社交产品的 Bug 相对容忍度比较高，想想看如果是波音飞机上的软件能这么做吗？

至于 Google 和 Amazon 这些公司，他们也是类似的情况：

大量优秀的工程师，可以同时兼任开发和测试；

有大量的自动化测试代码覆盖；

强大的发布和监控系统；

时间进度比较宽松；

用户对 Bug 容忍度较高。

对于不能满足上面条件的公司，有专职的测试是更有利于软件项目开发和质量保障的。

大厂不设专职测试的启示

虽然对于大部分公司来说，要做到完全没有专职测试还不现实，但这些大厂不设专职测试的实践还是有值得借鉴和思考的地方。

首先，用自动化测试代替重复性的手工测试是必然趋势。随着自动化测试技术的成熟，写自动化测试代码的成本逐步降低，而自动化测试，可以极大提高测试效率，尤其是像回归测试这种需要频繁进行的。

其次，测试设计是软件测试人员的核心竞争力。无论是自动化测试还是手工测试，测试用例是核心。无效的测试用例，用任何方法去测试，都不会达到良好的测试目的，只有测试用例设计好了，真正做到有效高覆盖，测试才是高质量的。

最后，开发人员和测试人员的更多融合是一种双赢。比如说测试人员可以给开发人员提供测试用例作为测试参考，开发人员可以写更多自动化测试代码，这些方式都能有效保障产品质量。

总结

今天我带你一起分析了什么样的公司需要专职测试。同时也学习了软件测试的一些基本知识，简单来说软件测试的工作，就是发现 Bug，报告 Bug，跟踪 Bug。

要能及时发现 Bug，需要针对需求进行分析和测试设计，把需求具象成一个个用户操作步骤的测试用例。通过一些科学的测试方法，像等价类划分、边界值分析、探索性测试，能有效提升测试的覆盖率。

公司是否需要专职测试，还是取决于公司的具体情况，例如是否有大量优秀的工程师可以同时兼任开发和测试，有大量的自动化测试代码覆盖，有强大的发布和监控系统，时间进度宽松，用户对 Bug 容忍度较高。

课后思考

你们公司有专职测试吗？你觉得是否应该保留专职测试？为什么？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

33 | 测试工具：为什么不应该通过QQ/微信/邮件报Bug？

你好，我是宝玉。十多年前，当我还是个野路子程序员时，我在外面接私活做项目，客户在使用过程中遇到了 Bug，直接就截个图，或者是用 Word 文档整理在一起，从 QQ 或者邮件上把 Bug 信息发送给我，我收到后再修复更新上线。

而现在正规的软件项目已经不会再用这种原始的方式来报 Bug 了，而是会借助测试工具来帮助报告和跟踪 Bug，即使你偶尔能看到有项目还在采用原始方式报 Bug，你肯定也会觉得这样做不专业。

但不知道你有没有仔细想过这个问题，为什么现在不通过 QQ/ 微信 / 邮件报 Bug，又有哪些测试工具可以帮助你更好地发现、报告和跟踪软件中的 Bug 呢？今天我们会展开讨论这个问题。

Bug 跟踪工具

我想你对与 Bug 这个词一定不陌生，它是我们软件中的缺陷或错误。这个词的诞生也很有意思。

1947 年 9 月 9 日，一只小飞蛾钻进了哈佛大学的一台计算机电路里，导致系统无法工作，操作员把飞蛾贴在计算机日志上，写下了“首个发现 Bug 的实际案例”。

（图片来源：WikiPedia《Software bug》）

虽然 Bug 的历史已经有 60 多年了，然而 Bug 跟踪工具却没有出现太久。软件项目中最早也是通过邮件、即时通讯等原始方式报告 Bug，直到 1992 年才有第一个专业的 Bug 跟踪软件GNATS。

在这之后才逐步有了像 Bugzilla、Jira、MantisBT 等专业的 Bug 跟踪工具。而现在，Bug 跟踪工具已经成为软件项目中必不可少的工具之一。

那么，Bug 跟踪工具是怎么逐步替代 QQ、邮件等方式来处理 Bug 的呢？

为什么要使用 Bug 跟踪工具？

我们在上一篇学习了软件测试相关的理论知识，软件测试的主要工作就是发现 Bug、报告 Bug 和跟踪 Bug。测试人员发现 Bug 只是第一步，还需要报告 Bug 让开发人员可以知晓和定位，并且跟踪整个 Bug 修复的过程。

用 QQ 或者邮件报 Bug 的这种方式，看起来快捷简单，但是问题很多：

Bug 不能有效被跟踪，不知道一个 Bug 是不是已经被修复了；

效率很低，开发人员频繁的被这样的报 Bug 的消息打断，不得不停下手头的工作去甄别 Bug；

不能直观的了解当前项目的 Bug 状态，比如说：修复了多少，还有多少没有修复，近期 Bug 数量是增加了还是减少了。

不难看出，通过 QQ 等方式报告的 Bug，都是文字配合图片等信息，很难检索和分类，而 Bug 跟踪工具，采用结构化的数据来定义 Bug，每一个 Bug 都有一些关键的信息可以对 Bug 进行分类和检索。

在 Bug 跟踪工具使用中，一个基本的 Bug 信息包括：

标题；

描述（包括期望结果、实际结果和重现步骤等关键信息）；

优先级；

指派人；

状态（New、Open、 Rejected、Fixed 等）；

其他。

那这样的话，就很容易的对 Bug 进行分类和检索，比如说：

张三想查看所有分配给他的 Bug，那只要列出所有指派人是张三的 Bug；

想列出所有未解决的 Bug，只要列出所有状态不是 Close 或 Rejected 的 Bug 即可。

这样对于开发人员来说，可以直观的看到自己有哪些 Bug 需要处理，Bug 的描述信息也可以帮助重现 Bug、快速定位到 Bug 的原因；对于项目经理或者测试人员来说，可以直观的看到哪些 Bug 还没解决，及时了解项目进展。

另外，我在《12 | 流程和规范：红绿灯不是约束，而是用来提高效率》这篇文章中提到了项目中的流程和规范，在软件项目中，要把好的实践流程化，把好的流程工具化。Bug 跟踪工具则很好的贯彻了这一点，将 Bug 的解决过程流程化。

你平时在 Bug 跟踪系统中看到的 Bug 状态，看起来只是一个有限的状态列表，但背后其实是一套解决 Bug 的流程。就像下面这张图表示的这样，一个 Bug 从创建到最后结束，其实是有一个完整的流程的。

通过这样的流程，开发人员就可以集中对 Bug 进行分配、按照优先级分别解决，而测试人员则可以第一时间知道 Bug 处理的状态变化，及时验证，方便跟踪整个过程。

使用 Bug 跟踪工具的注意事项

报告 Bug 的目的是为了能跟踪 Bug，以及帮助开发人员重现直到解决问题。要想做到测试和开发高效协作，这里面有一些需要注意的事项。

首先，所有的 Bug 都应该通过 Bug 跟踪系统管理和跟踪，不应该再通过 QQ/ 微信 / 邮件的方式跟踪 Bug。如果客户、同事通过 Bug 跟踪系统之外的其他途径反馈 Bug，应该统一提交到 Bug 跟踪系统管理跟踪起来。

然后，不能把多条 Bug 合并成一条，一个 Bug 创建一个独立的 Ticket。我遇到过有些测试为了省事，把几条 Bug 合并成一个 Ticket 来报，导致的问题就是，必须这几条 Bug 都修复了，这个 Ticket 才能改变状态，如果其中一个 Bug 没有验证通过，需要 Reopen 整个 Ticket。

再有，描述清楚如何重现 Bug 非常重要。一个 Bug 如果无法重现，也没有日志、截图等辅助信息，那是非常难以定位的，会浪费很多开发人员定位 Bug 的时间。

最后，不要把 Bug 跟踪系统当成讨论板用。在项目中一个常见的场景是，一个 Ticket 下面，跟讨论版一样添加了很多留言，开发认为不是 Bug，测试认为是一个 Bug，开发又觉得是产品设计没定义清楚，应该让产品经理来讲清楚，皮球踢来踢去，最后问题还没解决。

Bug 跟踪系统的主要功能是用来跟踪 Bug 的，不是用来讨论和扯皮的。遇到上面的情况，其中一方就应该主动一点，拉上相关人面对面讨论，当面确认清楚这个 Bug 到底是什么问题，然后马上解决掉。

自动化测试工具

除了 Bug 跟踪工具，软件测试中还有很重要的一个工具就是自动化测试工具，虽然我在《29 | 自动化测试：如何把 Bug 杀死在摇篮里？》中已经有了较多篇幅说明，但这里还是想继续提一下，因为我觉得,未来自动化测试会占据越来越多的比例，很多手工测试的工作会逐步被自动化测试代替。

像美国 Facebook、Google、Amazon 这些大厂，单纯的手工测试职位在减少，一些手工执行测试用例检查的工作外包到了人力成本更低的像中国、印度、罗马尼亚等国家，而美国本土主要招聘的都是能写自动化测试的软件测试人员，或者直接就是开发人员来写这些自动化测试代码。

这就意味着对于软件测试人员来说，要求越来越高了，不仅要会设计测试用例，还要能写自动化测试脚本。同时对于开发人员来说，不仅要写功能代码，还需要实现一定量的自动化测试代码。

这些年自动化测试工具的快速发展，也降低了自动化测试的实现难度，可以方便地搭建自动化测试环境，通过简单的脚本语言就可以模拟人工操作。

但很多团队还是不愿意投入在自动化测试的开发上面，宁可雇佣更多的初级测试人员手工测试。

其实这个问题还是要整体来看，这就像修路，如果你从一个地方到另一个地方（类比测试所有用例），偶尔走几次，那么可以不修路（手动测试），如果你未来一段时间需要频繁的在两个地方通行（反复测试），那么最好现在就开始修建高速公路（自动化测试），这样可以节约你大量通行的时间 (测试时间)。

当然更多的情况其实是团队不知道该如何实施自动化测试，比如说测试人员不会写程序，开发人员太忙，或者开发人员不会写测试用例，或者不知道该选择什么样的自动化测试工具。

对于这种情况，我的建议是：

测试人员可以学习一些基本的编程知识，尝试自己实现自动化测试。自动化测试所需要的技术，主要是对 API 的调用，并不需要复杂的逻辑，其实学习门槛并不高，而且这种技术在工作效率、薪资、个人职业发展等方面的投资回报都是巨大的。

从项目的角度，应该加大对自动化测试的投入，让开发人员参与到自动化测试代码的开发中。增加自动化测试代码的覆盖，对于提升软件质量是有明显好处的，通过自动化测试可以提升测试效率，及时发现软件质量问题。

对于开发人员来说，如果已经有了测试用例，完成自动化测试并不复杂，这个投入其实比做一些重要性不高的功能回报更高。

自动化测试工具的选择，需要根据你的软件的特点，去找出来适合你软件自动化测试的几款，然后自己搭建环境试用一下。在本文后面的附录中，我会列出一些自动化测试工具供参考。

其他帮助发现 Bug 的测试工具

软件测试的一个主要工作就是发现 Bug，而要发现 Bug，就需要对软件的各个领域进行测试，比如说有性能、安全性、兼容性等领域。

这些不同领域的测试，要求也不一样，比如说性能测试要求能测试出软件是否有性能瓶颈，能达到多少用户的访问量，需要模拟大量用户并发访问；安全性测试则要求对软件可能存在的安全漏洞进行扫描、验证；兼容性测试则要针对不用环境不同设备，对软件进行测试，以确保不会因为环境不一致导致功能不正常。

这些测试要么人工很难完成，例如模拟大量用户并发访问；要么需要很深的专业知识，例如安全性测试；要么需要大量的设备和巨大的工作量，比如做兼容性测试。所以这些领域的测试，就需要借助工具的帮助才能进行测试，从而发现问题。

应用这些测试工具其实并不难，毕竟都有很成熟的 API，网上也有很多教程，真正需要的是去执行。另外如果想要最大化工具的价值，及时发现问题，还要考虑将测试工具的应用自动化，加入到你的持续集成流程中去。

以压力测试为例，你用 Jmeter 完成了压力测试脚本后，还可以考虑和 CI 集成，在每次构建时，运行一遍压力测试代码，可以在构建完成后看到直观的图表，还可以设置性能数据的阈值，如果性能指标低于阈值，会导致构建失败，这样就可以第一时间发现性能问题，缩小问题范围，并及时解决。

在这里，我也帮助搜集了一些相关的测试工具供参考，具体可以查看附录。

附录

Bug 跟踪工具

在项目管理工具那一篇文章中，我已经给你介绍了一些任务跟踪系统，比如说Jira、禅道、TAPD、云效等，都可以用来跟踪 Bug。

Bugzilla

Bugzilla 是由 Mazilla 公司提供的一款开源免费的 bug 跟踪系统。这是一款历史很悠久的产品。

MantisBT

MantisBT 是一个简单但功能强大的开源 bug 跟踪系统，可以通过各种插件来扩展其功能。

Redmine

Redmine 是一款开源的综合性的项目管理工具，不仅可以用于 Bug 跟踪，还可以用来跟踪项目进度。

自动化测试工具

除了传统的桌面应用外，现在移动设备的普及，要测试的终端也越来越多。借助一些自动化测试工具，可以帮助简化多设备的测试。下面简单介绍几个自动化测试工具。

Selenium

Selenium 是一个 Web 端的自动化测试工具，直接运行在浏览器中，用来模拟用户操作。类似的还有WebDriverIO 和 Nightwatch.js ，支持 Javascript，API 更简单更方便。

Appium

Appium 是一个开源、跨平台的自动化测试工具，用于测试移动原生应用，支持 iOS, Android 系统。

Macaca

Macaca 是阿里巴巴开源的一款面向多端的自动化测试工具，支持桌面端、Web、移动端、真实设备和模拟器。

更多自动化测试工具可以参考：《Best Automation Testing Tools for 2019 (Top 10 reviews)》(中文版)。

压力测试工具

很多软件在上线后，需要面对巨大的用户访问量，但如果等到上线后才发现程序性能不行，访问量一大就会导致服务崩溃，那就太晚了。所以最好是在测试阶段，就能测试出来程序的性能如何，瓶颈在哪里，然后在发布前对程序进行优化，确保能满足性能要求。

对程序性能的测试，就需要借助压力测试工具来模拟大量用户并发访问的场景。下面简单介绍一下几款常用的性能测试工具。

Apache JMeter

JMeter 是一款开源的压力测试工具，纯 Java 应用程序。

LoadRunner

LoadRunner 是惠普旗下的一款商业自动负载测试工具，可以通过录制的方式制作测试脚本，上手容易功能强大，可以方便的监控和分析性能测试结果。

阿里云性能测试 PTS

阿里云性能测试 PTS 是基于云端的压力测试服务，可以模拟从全国各地域运营商网络发起的流量，报告真实反应用户体验情况。

WebPageTest

WebPageTest 是一个可以用来测试和分析网页性能的在线工具，支持不同浏览器，支持 API。可参考《WebPagetest H5 性能测试工具入门详解》。

更多性能测试工具介绍可以参考：《10 大主流压力 / 负载 / 性能测试工具推荐》。

安全性测试工具

软件的安全性是非常重要的指标，有时候开发人员缺乏安全意识，就可能会导致程序存在安全漏洞。安全领域也是开发和测试之外的一个技术领域，中小公司一般不会有自己专业的安全团队，就需要借助一些安全性测试工具来帮助对软件进行安全性检测。

HP Fortify On Demand

Fortify On Demand 是惠普旗下的一款安全检测工具，可以通过分析源代码、二进制程序或者应用程序 URL 检测程序安全漏洞。

Sqlmap

Sqlmap是一款开源免费的检测 Sql 注入的工具。

IBM Application Security APPScan

APPScan 是 IBM 旗下的一款漏洞扫描工具，支持网站和移动 App。

更多安全性测试工具介绍可以参考：《 11 款常用的安全测试工具 》、《安全测试工具篇（开源 & 商业）》、《最受欢迎的软件安全性测试工具有哪些？》。

浏览器兼容性测试工具

网站开发最苦恼的问题之一就是浏览器兼容问题，不仅要兼容 Chrome、IE/Edge、Firefox 三大主流浏览器，还得考虑桌面设备和移动设备上的不同表现。如果人工对所有浏览器做兼容性测试，工作量比较大。好在也有一些不错的工具可以帮助做兼容性测试。

Browsera

Browsera 可以对不同浏览器下的布局提供报告，包括截图和 Javascript 错误。

Browslering

Browslering 可以针对不同浏览器进行测试，它在虚拟机中运行真实桌面浏览器，还可以人工进行交互。

更多浏览器兼容性测试工具可参考《10 个免费的顶级跨浏览器测试工具》

测试用例管理工具

我们在上一篇里面已经学习了，设计测试用例是软件测试很重要的工作，有专业的工具帮助管理测试用例，也可以起到事半功倍的效果。

TestRail

TestRail 是 TestRail 是一个专注于管理测试用例的工具，可以用它来创建测试用例和用例集，跟踪测试用例的执行和生成报告。

飞蛾

飞蛾 是 Coding 旗下的测试管理工具，对中文支持好，界面美观。

更多测试用例管理工具可以参考：《有哪些比较好的测试用例管理工具？》

总结

今天，我带你一起学习了软件测试工具的相关知识。软件测试，主要工作就是发现 Bug、报告 Bug 和跟踪 Bug。软件测试工具，也是围绕这三方面来帮助我们提高效率的。

Bug 跟踪工具，不仅可以方便的报告 Bug 和跟踪 Bug，更可以帮助开发人员将 Bug 的解决过程流程化。

自动化测试工具是发展趋势，未来自动化测试会占据越来越多的比例，很多手工测试的工作会逐步被自动化测试代替。

除了 Bug 跟踪工具和自动化测试工具，软件测试中还有性能测试工具、安全性测试工具、兼容性测试工具等，这些工具都可以更好的帮我们发现软件中的质量问题。

如果想要最大化工具的价值，及时发现问题，还要考虑将测试工具的应用自动化，加入到你的持续集成流程中去。

课后思考

你的项目中应用了哪些软件测试工具？看完本文，你觉得还可以在哪些方面加强对软件测试工具的应用？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

34 | 账号密码泄漏成灾，应该怎样预防？

你好，我是宝玉。我们日常总能看到各种与黑客和网络安全相关的新闻，而这其中大部分安全问题都和软件程序有关系。比如说像 CSDN 数据库泄漏事件、携程泄漏用户银行卡信息事件、有些电商网站用户可以篡改支付购买金额等等。

在软件项目开发时，安全是一个很容易被忽略的问题，但又可能会造成严重损失。所以我们在软件开发时有必要对安全问题引起重视，防患未然，构建安全软件。

今天，我将带你了解一下软件开发中的安全问题，学习如何构建安全的软件，以及出现了安全问题之后该怎么办。

安全问题本质是技术风险

如果你还记得《15 | 风险管理：不能盲目乐观，凡事都应该有 B 计划》这篇文章中的内容，我在其中提到，风险是指不确定的事件，一旦发生，将会造成消极的影响。

安全问题，本质上也是一种技术风险，没发生问题的时候一切都好，一旦发生就会有严重的影响。在对安全问题的应对上，你也可以借鉴对风险管理的方法来改进软件的安全问题，也就是风险识别、风险量化、应对计划和风险监控。

在做风险管理时，首先要做的就是识别风险和对风险量化，对于安全问题，你也可以先思考一下：软件项目中安全问题的主要来源是什么？搞清楚安全问题的来源，以及造成的后果，你就可以对软件中导致安全问题的情况有一个基本的识别和量化。

软件中的安全问题来源主要可以分为以下三大类。

第一类：恶意输入

很多我们熟知的软件安全问题都属于此类型，就是黑客通过恶意输入，然后绕过软件限制对系统进行攻击和破坏。

像 SQL 注入，就是黑客把 SQL 命令输入到软件的输入框或网页的 URL 查询参数，欺骗服务器，执行恶意的 SQL 命令。这样可以绕过密码验证，登录管理员账号，或者删除数据库数据，甚至控制服务器。

还有像 XSS 攻击，将恶意代码通过外部参数或者用户输入的方式植入网页中，获取用户的 Cookie 等敏感信息、盗用管理员权限，甚至非法转账。

这类问题都是由于恶意输入导致的，应对恶意输入的问题，最简单有效的方式就是对用户输入的数据，做严格的校验，格式化。

第二类：假冒身份

很多程序对于用户身份的校验比较弱，可能会导致黑客假冒用户身份做出超越权限的事情。

比如说，我见过有的网站，把后台入口隐藏起来，而没有做权限控制，导致黑客猜到地址后就可以进入后台操作。

还有的游戏后台不做验证，直接接收传入的数据，导致可以伪造游戏用户发送数据，破坏游戏平衡。

如果对用户的身份不做严格的验证，很可能就会导致假冒身份的安全问题。应对策略就是要对用户的身份做验证，尤其是涉及敏感权限的操作，甚至要做两重验证。

第三类：数据泄露

很多软件都储存了用户的敏感信息，比如用户帐号密码信用卡记录，或者服务器的敏感信息，比如数据库连接字符串、登录帐号密码，而这些数据是有被泄漏风险的。

一些软件会把服务器上的敏感信息打包在程序中，而程序可能会被反编译导致敏感数据泄漏。携程泄漏用户银行卡信息事件中，就是因为把用户信用卡信息记录在日志中，日志泄漏导致用户信用卡信息也被泄漏，造成盗刷等严重问题。

还有 CSDN，对用户密码明文存储到数据库中，数据库泄漏后，用户密码也跟着一起泄漏了，而大多数用户习惯于在不同的网站也用相同的密码，导致在其他网站的密码也一起泄漏了。

对于软件来说， 我们不能假设数据存储是安全的，而是要考虑到数据是有泄漏的可能，提前做好预防措施，对敏感数据进行加密。

在了解了这些常见的安全问题来源和可能带来的后果之后，我们在软件开发时，就可以对薄弱环节、重点问题进行提前预防，在开发时就考虑到可能的安全漏洞，做出科学的应对方案。

如何预防软件中的安全问题？

预防软件中的安全问题，也可以参考对风险管理的策略。在风险管理中，对风险识别和量化后，接下来就是要制定应对计划了。

很多开发人员觉得安全问题，只要在软件开发完成之后，测试阶段做一个安全测试就可以了，但这样做等于把安全问题留到了最后环节，是很难达到对安全问题进行高质量管控的。

一方面，对于安全测试来说，很难覆盖到所有可能存在的场景，可能会出现疏漏，导致安全漏洞被利用。另一方面，如果测试阶段发现安全问题，可能需要修改大量代码，甚至于要重新设计，这时候成本就太高了。

所以应对安全问题，最好的方式就是在整个生命周期中都做到重视安全问题，各个阶段都考虑到安全方面的问题，才能真正做到防患于未然，构建出安全的软件。

那么在软件开发的各个阶段，都需要如何考虑好安全方面的问题呢？

需求阶段

需求是软件项目的源头，在确定需求，做产品设计的时候，不仅要考虑到功能上的需求，还要同时考虑到安全方面的要求。这样当你在架构设计的时候，就不会轻易遗漏安全方面的架构设计。

尤其是对于一些外包项目，如果在需求中没有提出安全需求，大概率外包公司是不会帮你考虑这些需求的。

需求阶段，涉及用户输入的内容，需要考虑到可能的恶意输入，做出针对性预防措施；对于涉及用户权限的，要求有身份的验证，一些对安全要求极高的，可以在需求上就要求做双重验证；对于有敏感数据的，可以在需求上就要求对数据进行加密。

举个简单例子，比如说用户登录功能，如果让你提出安全方面的需求，你会考虑到哪些需求？这里我简单列几个功能参考：

登录网页使用 Https 或者在传输密码时加密；

增加图形校验码，避免恶意攻击；

密码失败次数过多，应该锁定用户一段时间；

记录用户登录 IP。

当你在需求阶段就提出了安全性的需求，设计、实现和测试时自然不会遗漏掉安全方面的内容，从源头上就让大家有了安全方面的意识。

设计阶段
在做设计架构时，最重要的事就是要把安全加入到设计目标，有了安全方面的设计目标，自然能找到很多安全相关的解决方案。

为了保障在设计时就考虑好安全方面的问题，在做架构设计方案的评审时，也需要增加安全方面的评审，确保有安全方面的考虑，确保技术方案切实可行。

现在架构设计领域，也有了一些业界公认的好的安全相关的设计原则，比如说攻击面最小化、权限最小化、纵深防御等。

攻击面最小化

攻击面就是指程序被用户直接访问到的部分，比如 API、网站等，这些暴露给用户的地方也是最可能被黑客攻击的地方。

暴露的面越多则风险越高，攻击面最小化的设计原则，就是说尽量减少暴露黑客可能发现并试图利用的攻击面数量。

举例来说，你的数据库应该关闭外网访问，避免黑客直接攻击数据库导致数据泄漏。还有像对于一些复杂的多网站业务系统，实行单点认证，就可以让所有业务都在一个地方登录，你可以在这一个地方做到做到足够安全，这样所有网站的登录都是相对安全的。

权限最小化

权限最小化的设计原则就是对于系统的用户、文件访问、进程运行等，都只给予其能拥有的最小权限，这样可以保证一个应用程序或者网站被攻击、破解，能将损害降到最低。

举例来说，以前在部署 Asp.Net 程序的时候，运行 Asp.Net 的程序是单独的一个用户，这个用户所拥有的权限是只能运行程序所在目录，不能超出其目录范围，这样即使用户上传了恶意木马文件，那么也只能控制这一个目录，避免了进一步的损失。

纵深防御

纵深防御的设计原则，指的是从不同的维度去实施安全保护措施，从而缓解被攻击的风险。纵深防御并不是同一个安全方案要做两遍或多遍，而是要从不同的层面、不同的角度对系统做出整体的解决方案。

这里我给你举一个电商网站的例子（摘自：《代码未写，漏洞已出—架构和设计的安全》）。

国内中小电商，一半以上在早年都犯过这个错误，现在基本都修复了。电商的交易和支付系统之间流程是这样，一个人过来说老板我要买一台冰箱，多少钱？两千。OK，你把钱付给支付系统。因为支付请求也是在用户侧的浏览器里提交的。这个过程对用户是不可见的，但攻击者实际上可以修改这个数据。攻击者可以修改浏览器提交的数据，本来交易系统让他提交 2000 元，攻击者改为提交 1 元，然后支付系统就返回 OK，说我收到钱了。这个 OK 到交易系统那里，交易系统一看支付成功了，那就安排发货，1 元钱就把冰箱买到了。

你看，单独从支付系统和交易系统来看，设计上都没有问题，都对数据输入做了校验，但问题是没有站在一个系统的整体角度去考虑，没有考虑到不仅要校验交易有没有成功，还要校验交易的金额是不是匹配。

当然解决方案其实也很简单：

不要只反馈是否 OK，同时也把支付的金额和 OK 一起返回过去。是支付 2000 元 OK 还是 1 元 OK。这就解决了问题，现在的电商都改成这个设计了。

通过这样一些好的安全设计原则，就可以在设计阶段把很多安全问题预防好。

开发阶段
只是设计阶段做好安全相关的设计还不好，很多安全问题其实都是编码阶段时，没有好的编码习惯、没有良好的安全意识导致的。

对于开发阶段的安全问题预防，需要从以下几个方面入手。

编码规范中加入安全相关内容

对于用户输入的数据，需要有校验，防止恶意输入；对于涉及权限的操作，要检查用户权限；对于敏感数据要进行加密处理；对于用户的操作，要有日志记录；不能在日志中记录敏感信息等等。

要有代码审查

代码审查其实在我们专栏提过很多次，这也是预防安全问题一个行之有效的手段，通过代码审查，及时发现代码中的安全问题。

增加安全相关的自动化测试

现在有些安全工具，可以帮助对代码做安全检查，甚至可以和 CI 集成，如果增加相应的自动化安全测试代码，也可以第一时间对代码中的安全问题进行反馈。

测试阶段

在测试阶段，除了功能测试以外，增加对安全性方面的测试。除了增加相应的测试用例，也可以借助一些安全测试工具（可参考上一篇“软件测试工具”这篇文章）来进行测试。

上线维护

上线部署时，不部署源代码，只对编译后程序部署；删除 Debug 文件。

对服务器进行安全设置，比如说严格限制端口，只保留必须的端口；只对少数服务器开发外放服务；开启操作日志；对访问目录设置最小的权限。

通过对整个软件生命周期都做好安全方面的考虑，并落实到位，才能真正保证好软件的安全。

如果真的出现安全问题怎么办？

是不是我们在整个软件生命周期都做好安全方面的考虑，也落实到位，就完全不会有安全问题了？

安全问题就像程序的 Bug 一样，没有谁能保证绝对的安全，就像风险管理的最后一步风险监控那样，我们必须做好 Plan B，万一出现安全问题，马上应对，将损失降到最低。

首先，要设立应急的流程。当出现安全问题了，根据流程，知道该找谁，应该怎么去第一时间恢复生产，避免进一步损失。

其次，要分析程序的漏洞在哪里。通过分析日志，找出漏洞在哪里，才能针对性去修补漏洞。

最后，要总结原因。从错误中吸取教训，看问题是在哪个环节导致的，必要的话，就改进开发流程，避免类似的安全问题再次发生。

总结

今天我带你一起学习了如何构建安全的软件，软件的安全问题本质也是一种技术风险，可以借用风险管理的知识来帮助构建高安全性的软件。

软件安全问题主要来源是：恶意输入、假冒身份和数据泄漏，要注意对输入数据的校验、对用户身份的校验和对敏感数据的加密。

构建高安全性软件，最好的方式就是在整个生命周期中都做到重视安全问题，各个阶段都考虑到安全方面的问题，才能真正做到防患于未然，构建出安全的软件。

安全问题就像程序的 Bug 一样，不能绝对避免，同时也要做好应对措施，在出现安全问题后，将损失降到最低，并且避免以后发生类似问题。

课后思考

通过对这篇文章的学习，你觉得你所在项目中，在安全方面有哪些可以改进的地方？你有哪些提升安全性的经验可以和大家一起分享的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

35 | 版本发布：软件上线只是新的开始

你好，我是宝玉。上一章我们学习了软件测试篇，今天，我们将从版本发布这个话题开始，进入到运行维护篇的学习。

说到版本发布，对于很多开发人员来说，觉得是很简单的一个事情，就是将程序编译打包部署，但实际发布的时候，却经常出现发布错版本的问题，或者是发布前修改了一点代码导致上线出现 Bug 的情况发生。

而版本发布对于很多项目管理者来说，又是一个很纠结的事情，觉得还有很多功能没完成，很多 Bug 还没改完，害怕用户负面评价，结果时间一拖再拖，迟迟无法上线。

所以今天我将带你一起学习一下如何做好版本发布，保障好发布产品的质量。

关于软件版本

在讨论这个话题之前，我们需要了解“版本”的含义。也许你已经知道版本的含义，但是这里还是有必要明确一下，因为在不同的语境下，版本的含义也有所不同。比如产品经理会对开发人员说：“这个功能我们会放到下个版本中实现”，开发人员会对测试人员说：“这个 Bug 在昨天的版本中已经修复了”。

这里产品经理说的“版本”是指特定功能集合，开发人员说的“版本”是指某一次程序的构建结果。也就是说对软件版本来说，包含两部分含义，一部分代表特定功能集合，一部分代表某一次特定的代码构建结果。

为了明确标识软件版本，需要对版本进行编号。目前业界在软件版本的命名上，通常会采用以下方式：

主版本号 . 子版本号.[. 修正版本号.[构建版本号]]

比如说：1.2.1、2.0、3.0.1 build-123。

其中主版本号和子版本号用来标识功能变化，小的功能变化增加子版本号，大的功能变化增加主版本号。修正版本号则表示功能不变化的情况下修复 Bug，而构建版本号表示一次新的构建，这个通常由编译程序自动生成。

团队中对版本有了清晰的定义和良好的版本编号，在讨论版本时，你就可以根据版本号清楚地知道应该有哪些功能，属于哪一次的构建结果。在修复 Bug 或者增加功能时，开发人员也能清楚地知道代码应该加入哪个版本。在验证 Bug 时，测试人员就可以知道应该在哪个版本中验证 Bug 有没有被修复。

版本发布前，做好版本发布的规划

回到前面的问题，为什么有的项目管理者会在发布前感觉没准备好，害怕上线发布呢？根源上，他们还是对于功能和质量没有信心，担心发布后获得负面的评价。

而实际上，并不代表你需要完成所有的功能，或者没有任何 Bug，有一个完美的版本才能上线。毕竟追求完美是没有止境的，这世界上也不存在完美的软件，很多著名的软件，比如 Windows、Office、iOS 都免不了在发布后还要打补丁。

这里的关键在于，要在用户（或客户）的心理预期和你软件的实际情况之间，达到一种平衡，让软件的功能和质量，满足好用户的预期。

要合理管理好用户的预期，达到好的发布效果，就需要在版本发布前先做好版本发布的规划。

那么，版本的发布规划，是指规划哪些内容呢？

首先是规划好要发布的功能。在发布前，搞清楚哪些是用户必须要有的功能，哪些是用户可以没有的功能。对于必须要有的功能，那么要保证软件中有这个功能才能发布，对于不是必需的功能，可以以后再逐步完善。

有一个经典的案例就是第一代的 iPhone，复制粘贴的功能都没有，但是在用户界面和触屏设计上做的非常好，一样取得了极大的成功。

然后是定义好发布的质量标准。在发布前，搞清楚你的用户对质量的容忍度如何，对哪些功能的质量要求高，对哪些功能的质量要求没那么高。对于那些用户在意的功能，要具有较高的发布标准，反之，则可以有较低的质量标准。

举例来说，像微博这种社交类应用软件，界面布局存在一点问题，用户是可以接受的，但是如果撰写内容的时候，因程序异常导致辛辛苦苦写的内容丢失，用户是比较难以忍受的。

再有就是要设计好发布的策略。考虑好是直接发布给所有用户？还是先让一部分用户试用？比如说可以先让内部用户使用，内部用户对软件质量问题容忍度是很高的，还可以帮助发现很多问题。

让一部分用户使用 Beta 版也是一个好的发布策略，当用户知道你的软件还是 Beta 版的时候，要求会比较低一点，可以接受一些不那么严重的 Bug。 还有就是采用灰度测试的发布策略，让一小部分用户先用新功能，如果没发现什么问题，再继续扩大使用的用户规模，如果有问题，也只是影响少量用户。

像 Gmail 在最初的几年，一直是 Beta 版本测试。还有像苹果的 iOS，用户也可以选择安装最新的 Beta 版本，可以先体验新功能，但是必须忍受系统的不稳定。

最后，就是有一个综合性的版本发布计划。在确定了要发布的功能、定义好了质量标准、设计好了发布策略，就可以制定一个综合性的版本发布计划了，确定好发布的时间点。

这个发布计划，不止是项目内部成员，还需要和项目之外利益相关方，比如客户、市场运营人员，大家一起确定最终的发布计划。

在对版本的发布做好规划后，就不用再纠结于该不该发布，该什么时候发布的问题。

有功能没完成没关系，关键要看这个功能是不是必须要有；有 Bug 没有修复完成，是不是影响发布，要看这些 Bug 是不是影响发布的质量标准；还可以采用一些像 Beta 版、小规模用户试用的发布策略，降低用户对功能和质量的预期。

规范好发布流程，保障发布质量

在规划好发布的版本后，要发布版本似乎是一件很简单的事，就是将源代码编码、部署。

但发布版本，可能并不是像你想的那么容易，这其中有几个需要注意的问题。

首先是必须保证要编译部署的是正确的版本。虽然一般来说，开发人员不会犯这样的错误，但是如果发布了错误的版本，后果可能很严重，所以要引起足够重视。比如说当年拼多多造成了几千万损失的薅羊毛事件，就是错误发布了一个测试用的无门槛券，导致被大量滥用。

然后要保证版本稳定可靠。如果你有开发经验的话，应该知道开发软件，一个常识就是每一次对代码的修改，都可能导致新的 Bug 产生。如果你的代码库在发布之前还一直在增加新的功能或者是不停地修复 Bug，那么质量是难以稳定下来的。

再就是要在发布失败后能回滚。没有谁能保证程序发布后没有严重问题，所以最保险的办法就是要在部署后，如果发现发布的版本出现严重问题，就应该对程序进行回滚操作，恢复到部署之前的状态。即使有些不可逆的升级，也需要事先做好应对措施，比如发布公告，停止服务，尽快修复。

针对这些问题，已经有些好的实践，比如说代码冻结、Bug 分级、回归测试等可以降低发布风险，保障发布产品的质量。在《12 | 流程和规范：红绿灯不是约束，而是用来提高效率》这篇文章中，我也提到了：流程和规范能将好的实践标准化流程化，让大家可以共享经验。所以在版本发布上，我们也可以制定合理的流程，来应用这些好的实践，保证发布的质量。

那么一般大厂都是什么样的发布流程呢？下面这个流程可以作为一个参考。

在发布之前要做代码冻结。

什么是代码冻结呢？就是在发布之前，对于要发布的版本，在源代码管理工具中，专门创建一个 release 分支，然后对于这个分支的代码，冻结功能的修改，不接受新功能的增加，甚至重要性不高的 Bug 都不修改，只修复重要的 Bug。

由于严格的控制代码的修改，这样可以让版本的质量逐步趋于稳定。

对代码冻结后发现的 Bug 要分级

在代码冻结后，可能还存在一些 Bug，测试的过程中也会新增一些 Bug。代码冻结的原则就是尽可能减少代码的修改，避免引起不稳定。所以对于这些 Bug，要有一个简单的分级：是否在发布前修改，还是留在发布后再修改。

至于如何对一个 Bug 分级，这需要项目负责人和产品负责人一起确认。

每次修复 Bug 后，发布新的候选版本

进入代码冻结后，开发人员还需要对一些 Bug 进行修复，每一次修复完 Bug 后，就要生成一个新的候选发布版本，比如说 1.1 RC1、1.1 RC2。

关于生成发布版本，现在比较流行的做法是和持续集成系统整合，完全自动化。也就是在自动化测试通过之后，会自动构建，生成各个环境的发布版本。这样好处是，可以避免人为失误导致的错误，另外程序的配置管理做好了的话，只要测试环境的版本在测试环境测试没问题，那么就可以认为在生产环境的版本也是正常的。

自动化构建，生成发布版本并不复杂，各个语言都有成熟的方案，如果你还不了解的话，可以通过搜索引擎搜索关键字：“[对应平台] 自动打包”，例如搜索“iOS 自动打包”、“iOS build automation”这样的关键字。

其中稍微有点麻烦的就是如何应用不同环境下的不同配置，比如说测试环境连测试环境服务器，生产环境连生产环境服务器。有关程序配置管理部分，可以参考这篇文章：《大型项目程序配置管理演化之路》

每次部署新的候选发布版本后，要做回归测试

在每次开发人员部署新的候选发布版本到测试环境后，还需要做一次回归测试。也就是说在 Bug 修复完，对主要流程要重新测试一遍，同时还要对之前确认过的 Bug 再确认一遍，以确保 Bug 确实修复了，并且没有引入新的 Bug。

如果当前候选发布版本达到版本发布的质量标准后，就可以准备发布了。

申请上线发布

上线发布是一件很严谨的事，所以在正式上线发布前，通常还需要有一个申请和审批的流程。审批的主要目的是要有人或者有部门统筹对所有的上线发布有一个全面的了解和控制，避免上线过于随意导致问题，避免和其他部门的上线冲突。

部署发布

如果已经实现了自动化，部署发布应该是非常简单的一步。如果还没有自动化部署发布，也需要事先将详细的操作步骤写下来，避免部署发布时发生纰漏，这样在实际部署发布时，按照事先写好的步骤操作就不容易出现错误。

上线后的测试

项目上线后，测试人员需要马上对已经上线的版本做一个主要功能的测试，以确保线上运行正常。如果做好了数据监控，还同时要对一些关键数据进行监控，例如服务器 CPU 利用率、内存占用、服务出错率等数据。

如果万一发现版本上线后出现问题，需要考虑按照事先准备好的回滚方案进行回滚操作，尽量将损失降到最低。通常不到万不得已，不建议马上对问题打补丁进行修复。因为哪怕很小的代码修改，都可能会引入新的 Bug。而重新做一遍回归测试，耗时会比较长。

以上就是版本发布的一个常见流程，你也可以基于这个流程制定适合你项目的流程，让你的版本发布更加稳定可靠。

软件上线只是新的开始

当你的软件上线后，这不代表你的项目就结束了，可能这才只是新的开始。

用户在使用你的产品的时候，可能会遇到一些 Bug 或者是有一些建议，所以需要给用户反馈的渠道，让用户可以有途径对于 Bug 或者功能去反馈。通过收集用户的反馈，可以进一步完善你的软件产品。

只是靠用户主动反馈问题还是不够的，需要主动的对发布的版本进行监控，比如说要收集 App Crash 的 Log、监控服务器资源占用情况、监控 API 出错的比例、监控网页响应的速度等数据。当发现数据异常时，很可能说明发布的版本是有问题的，需要及时的应对，回滚版本或者发布新的更新补丁。

有关线上监控和报警的内容，将会在后续我们专栏《监控和日志分析：如何借助工具快速发现和定位产品问题 ？》中继续带你一起学习。

不管怎么样，软件成功上线了都是一件值得祝贺的事情，同时也是时候回顾总结一下整个项目过程了，关于这一点，我也会在后续专栏文章《项目总结：做好项目复盘，把经验变成能力》中跟你一起探讨如何做好项目总结复盘，把经验变成能力。

总结

今天带你一起学习了版本发布的相关知识。做好版本发布，关键在于版本发布前做好版本发布的规划，以及采用一个科学的发布流程。

版本规划，其实就是通过合理的规划，尽可能的让软件的功能和质量，满足好用户的预期。所以一方面要尽可能提供应有的功能和保证质量，另一方面也可以通过合理的发布策略，例如 beta 测试，降低用户预期。

通过规范的发布流程，可以确保要发布的版本正确以及发布质量的稳定。流程的关键在于发布前要对代码冻结，避免发布前频繁修改代码引入新的 Bug，同时在每次修复 Bug 后，要做回归测试保证 Bug 被修复以及没有引入新的 Bug，上线后还要对线上版本再一次测试，确保没有问题。整个流程中一些手工部署发布的操作应该尽可能自动化。

最后，软件上线只是新的开始，还需要收集用户的反馈，对线上服务进行监控和预警，对整个版本的开发过程进行总结回顾。

课后思考

你在发布版本前，会做哪些准备工作？你的发布流程是什么样的？通过对这篇文章的学习，你觉得有哪些可以改进的地方？或者你觉得有什么更好的方案可以分享的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

36 | DevOps工程师到底要做什么事情？

你好，我是宝玉。这些年，有关 DevOps 的概念很火，大家都在讨论 DevOps，有人说 DevOps 就是自动化运维，有人说 DevOps 是流程和管理，还有人说 DevOps 是一种文化。以前的运维工程师也纷纷变成了 DevOps 工程师。

今天，我将带你一起了解一下，究竟什么是 DevOps？DevOps 到底要做什么事情？

传统的运维模式以及面临的挑战

在传统的瀑布模型开发中，软件生命周期中的运行维护这部分工作通常是交给运维工程师来完成的。

当开发人员完成编码，测试人员测试验收通过后，到了要发布的时候，就会将程序交给运维人员部署发布到生产环境。

（图片来源：The Product Managers’ Guide to Continuous Delivery and DevOps）

除了程序的部署更新，传统运维工程师最重要的职责就是保障线上服务的稳定运行。对服务器 24 小时监控，有意外情况发生时需要及时处理和解决。

除此之外，还有日常的更新维护，比如说安装升级操作系统、安装更新应用软件，更新数据库、配置文件等。

早些年这种运维模式运行的很好，但随着这些年互联网发展，有两个主要的因素对传统的运维模式产生了很大挑战。

第一，服务器规模快速增长和虚拟化技术的高速发展。

早些年，一般的企业服务器数量都不会太多，运维工作以手工为主，自动化为辅。几个人几十台服务器，即使用手动方式管理，也不会太困难。但随着这些年技术的快速发展，大型互联网公司的服务器数量越来越庞大，而中小公司都开始往云服务上迁移，基于 Docker 这样的虚拟化技术来搭建在线服务的基础架构。

服务器规模的增加和虚拟化技术的使用，就意味着以前的手动方式或者半自动的方式难以为继，需要更多的自动化和基于容器技术或者相关工具的二次开发。对于运维的工作来说，运维人员也需要更多的开发能力。

第二，高频的部署发布。

传统的软件部署频率不高，一般几天甚至几个月才部署发布一次，同时每一次的部署发布，也可能会导致系统的不稳定。而敏捷开发和持续交付的概念兴起后，更新的频率越来越高，每周甚至每天都会有若干次的更新部署。

高频部署带来的挑战，首先就是会引起开发和运维之间的冲突，因为开发想要快速更新部署，而对于运维来说，每次更新部署会导致系统不稳定，最好是不更新，可以让系统维持在稳定的状态。另一个挑战就是想要快速的部署发布，也意味着运维要有更高的自动化能力。

为了解决这些挑战，DevOps 出现了，它帮助解决开发和运维之间的沟通协作问题，提升运维开发和自动化能力。

什么是 DevOps？

DevOps 可以理解为一种开发（Development）和运维（Operations）一起紧密协作的工作方式，从而可以更快更可靠的构建、测试和发布软件。

DevOps 并不意味着开发一定要懂运维技术，运维要懂开发技术，而是说两个工种要更紧密的协作，有共同的目标：更快更可靠的构建、测试和发布软件。

这就意味着，对于运维来说，不再抵触开发的频繁更新部署，会帮助搭建自动化部署平台，提供自动化部署工具；对于开发来说，不再认为运维的工作和开发没关系，开发人员会邀请运维人员参与架构设计，帮助运维实现自动化脚本开发。

那么当你的团队采用 DevOps 的方式工作的话，会带来哪些好处呢？

整个软件的构建、测试和发布过程高度自动化

DevOps 一个很重要的基础就是自动化，通过对自动化的应用，是最简单有效的打破开发和运维之间壁垒的方式。

因为应用自动化后，对于运维人员来说，自动化的交付流程，减少了繁重的手工操作，自动化测试可以有效对产品质量提供很好的保障。对于开发人员来说，可以方便高频率地进行部署。

如果你的团队还没有开始实施自动化，可以先从持续交付开始，具体可以参考我们专栏在《26 | 持续交付：如何做到随时发布新版本到生产环境？》中的介绍。

信息更加透明和易于测量

在传统的开发和运维合作模式中，开发和运维之间的信息不是那么的透明。对于开发来说，不了解程序在服务器上运行的情况，对于运维来说，程序就是个黑盒子，无法对程序内部进行监控，出现问题只能重启或者回滚。

当采用 DevOps 的工作方式，信息更加透明，通过日志和工具，数据也可以被更好测量。比如说：

可以直观看到开发到部署需要多少时间，哪个环节可以改进？

当前服务运行情况如何，每分钟访问数多少，API 出错率多少？

当前用户数多少，有多少新增用户？

这些数据，不仅可以帮助运维更好地预警，或者是帮助开发更好地优化程序，还可以帮助业务团队更好地了解服务的运营情况。

培养跨职能协作的文化

DevOps 的核心文化是不同职能工种之间的紧密协作的文化。其实不仅限于开发和运维之间，就像我们之前在《32 | 软件测试：什么样的公司需要专职测试？》）中讨论的，开发和测试之间也一样离不开紧密的协作。

如果你的团队是在真正地实践 DevOps 的工作方式，就会积极拥抱这样的跨职能协作的文化，在日常工作中包容错误、对事不对人，能对项目的开发流程持续改进，鼓励创新。

有关 DevOps，也有一些不错的文章，有兴趣的话可以进一步阅读：《DevOps 前世今生 | mPaaS 线上直播 CodeHub #1 回顾》、《孙宇聪：来自 Google 的 DevOps 理念及实践》和《关于 DevOps ，咱们聊的可能不是一回事》。

DevOps 看起来很美好，也许你迫不及待想去实施，但 DevOps 这种工作方式的建立，也不是一下子能完成的，上面提到的这些带来的好处，相应的也是你要去遵守的 DevOps 原则：自动化、信息透明可测量、构建协作文化。

这也意味着：

你需要去构建自动化部署的系统，从构建、测试到部署实现高度的自动化；

建立数据监控的系统，让信息透明可测量；

最后要形成跨职能协作的文化。

看起来很难，但也不需要有压力，因为要实践 DevOps，不需要你改变开发模式，瀑布模型或者敏捷开发都可以实施；不需要靠管理层推动；也不一定要让开发人员去学习运维知识或者运维去学习开发知识。而是通过了解 DevOps 的核心价值，也就是跨职能之间紧密协作，更快更可靠地构建、测试和发布软件，一点一点地做出改变。

DevOps 工程师到底要做什么事情？

在了解了什么是 DevOps 后，我们再来看看基于 DevOps 的实践，DevOps 工程师到底要做什么事情？

对于 DevOps 工程师的定义其实是有争议的，因为有人认为 DevOps 是一种团队工作的方式，而不是一种职业。也有人认为 DevOps 工程师是一种职位，用来帮助团队形成 DevOps 工作方式的职位。

在这里我们没必要陷入这种争论，而是从 DevOps 实践的角度，来看看 DevOps 工程师，要做什么事情，可以帮助团队来实践 DevOps 的工作方式。至于是 Dev 来做这些事情，还是 Ops 来做这些事情，还是一起协作来做这些事情，并不是最重要的。

首先，DevOps 工程师要帮助团队建立基于持续集成和持续交付工作流程。

关于持续集成和持续交付，不仅仅是工具的使用，同时还是基于工具之上的一整套的交付工作流程。

这套工作流程已经是业界公认的好的实践，但在很多中小团队普及率还不高，主要的难点之一是搭建比较复杂，可能还涉及二次开发；另一个是不知道该怎么建立这样的流程。

对于这样的工具和流程的建设，最初的时候，就是需要有专门的人，专门的时间去建立，也是 DevOps 工程师首先要去解决的问题。

其次，要建立一套基于日志的监控报警的系统，以及故障响应的流程。

对于线上系统，应急响应非常重要，要在故障发生后，第一时间作出响应，及时恢复生产，避免更大损失。而要做到这一点，同样离不开工具和流程的支持。

需要能建立一套基于日志的监控报警的系统，将应用程序还有运行环境的各项数据监控起来，设置报警的阈值。当数据异常，超出阈值，就马上触发报警，然后进入应急响应的流程。

对于应急响应流程，首先应该能第一时间通知最合适的人去处理，比如负责这个服务值班的开发人员，然后对于怎么第一时间恢复应该有准备，涉及跨部门协作也应该有相应的配合流程；最后对于故障应该有总结，避免类似情况再次发生。

有关监控和日志分析，我还会在我们专栏后续文章《 监控和日志分析：如何借助工具快速发现和定位产品问题 ？》中有更多介绍。

然后，要构建基于云计算和虚拟化技术的基础设施。

虽然并非每一个软件项目都是基于云计算或虚拟化技术来搭建的，但云计算和虚拟化技术方面的技术，其实是横跨开发和运维的，可能对于大部分开发和运维来说，都只了解其中一部分知识，这就需要有人能同时懂软件开发和云计算或虚拟化技术，或者一起协作，才能搭建出真正适合云计算或虚拟化技术的架构。

构建出来基于云计算和虚拟化技术的基础设施后，对于开发人员来说，只要通过 API 或脚本即可搭建应用，对于运维来说，也只要通过脚本和工具即可管理。

这其实也是 DevOps 中的“基础设施即代码”的概念。

最后，要形成 DevOps 的文化。

DevOps 最核心本质的就是工作方式和协作的文化，而这样的文化需要有人引领，一点点去形成。

DevOps 工程师要帮助开发和运维相互理解对方的工作，帮助开发和运维在一起协作时多沟通，相互学习。出现问题不指责，而是分析原因，共同承担责任，找出改进的方案。

这些就是 DevOps 工程师要做的事情，本质上还是 DevOps 的几条基本原则：自动化、信息透明可测量、构建协作文化。不需要有 DevOps 工程师的头衔，基于 DevOps 的原则去做事情，就可以算的上是 DevOps 工程师。

总结

今天我带你一起学习了当前热门的 DevOps 概念，DevOps 可以理解为一种开发和运维一起紧密协作的工作方式，从而可以更快更可靠地构建、测试和发布软件。DevOps 的主要原则就是自动化、信息透明可测量、构建协作文化。

DevOps 工程师，要做的事情就是帮助团队来实践 DevOps 的工作方式。具体可以帮助团队：

建立基于持续集成和持续交付工作流程；

建立基于日志的监控报警的系统，以及故障响应的流程；

构建基于云计算和虚拟化技术的基础设施；

形成 DevOps 的文化。

DevOps 工程师做的事情，就是帮助团队基于 DevOps 原则来做事，让团队形成紧密协作的工作方式，更快更可靠的构建、测试和发布软件。

课后思考

你所在团队是否是 DevOps 的工作方式一起紧密协作，有没有什么值得改进的地方？可以怎么改进？你认为的 DevOps 团队应该是什么样子的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

37 | 遇到线上故障，你和高手的差距在哪里？

你好，我是宝玉。在软件上线后，发生线上故障是一个常见的问题，但怎样对线上的故障进行处理，却很能反映出新手和高手程序员的差距。对于团队来说，如何应对线上故障，也同样能反映出线上运维水平的高低。

今天，我将带你一起分析一下，新手和高手在应对故障时有什么不同？大厂在处理线上故障时，有哪些可以学习借鉴的地方。

遇到线上故障，新手和高手的差距在哪里？

在这里，我把新手在处理线上故障遇到的一些常见问题列一下，同时，我们也一起分析下，高手是怎么处理这些问题的。

新手遇到复杂的线上故障，不知道该怎么下手

对于线上故障，有的很简单，从界面或者错误日志上可以直观地看到问题在哪，从而也好找到方法去解决。但有的故障，却没办法直观地看出原因，比如说内存一直在涨，CPU 居高不下，遇到这种复杂的故障，通常新手就不知道该怎么下手了。

而对高手来说，会在实践中总结一套自己解决问题的步骤，遇到问题，会按照解决问题的步骤有条不紊地去分析和解决。（比如说 caoz 的这篇《出了 bug 怎么办》，就充分体现了一个高手的水平。）通常通过下面这样的步骤：

第一步，评估影响范围；

第二步，试图重现问题；

第三步，临时方案和终极方案；

第四步，风险评估及持续优化。

如果你还记得专栏文章《28 | 软件工程师的核心竞争力是什么？（下）》里的内容，就会发现，其实这本质就是一种解决问题的能力。一步步分析、解决和预防问题。

从新手到高手，可以从借鉴像这样的方法开始，然后在实践中不断总结经验，形成一套自己的分析问题、解决问题的方法。

新手遇到线上故障，会想着马上修复 Bug

当发现 Bug 后，尤其是自己的 Bug，很多开发人员马上就想到了 Bug 的修复方案，迫不及待就要去写代码打补丁了。然而这样做的问题就是，匆忙之间打补丁，如果没有经过充分的测试，可能会引入新的 Bug，甚至是更严重的 Bug。如果要充分测试，那么意味着时间会比较长，而线上故障的时间越长，可能意味着损失也越大。

而对于高手来说，会首先对故障进行评级，看对用户的影响范围，如果是核心业务，大面积影响用户，那么当务之急是恢复生产，然后再考虑如何去修复 Bug。

恢复生产并不一定需要修复 Bug，可以用一些临时性的方案，比如说回滚系统到上一个稳定的版本；重启服务看是否能恢复正常。当然在恢复之前，还要尽可能保留当时的日志、故障场景的截图、内存的 Dump（把当前内存数据保存的静态文件）等信息，用来后续查找故障原因使用。

遇到线上故障，新手需要时刻牢记：恢复生产、降低损失是第一要务，修复 Bug 是其次的。

新手遇到线上故障，不知道如何快速定位到 Bug 在哪

在临时恢复业务后，还是需要找到 Bug 在哪，后续才能从根本上解决。对于比较复杂的线上故障，新手通常不知道从哪里下手，看日志看代码都看不出所以然，而高手却总能快速地定位到 Bug 在哪。

高手快速定位 Bug 在哪，关键在于通过有效的手段，逐步缩小问题范围，直到找到 Bug 在哪里。

比如说，一种常见手段就是先重现 Bug，因为有了重现的步骤，就等于将问题的范围，缩小到重现 Bug 的这几步操作相关的代码上，就很容易发现问题在哪。

还有一种手段就是分析错误日志，通过错误日志，可以马上定位到错误在哪里。所以对于平时写程序，无论是客户端还是服务端，注意收集错误日志是非常重要的，可以帮助你在排查问题的时候节约不少时间。

还有一些不能重现的 Bug，则不是那么容易发现，其实也可以按照缩小问题范围的思路来定位。

比如说，Bug 是在最近一次部署后发现的，并且回滚部署后就恢复了正常，那么就说明问题很可能是由于这一次部署和上一次部署之间的代码变更导致的，如果代码变更不多，就可以通过分析变更的代码来定位。

像内存泄漏或者 CPU 高的问题，一般就可以通过分析内存 Dump 文件，分析当前是哪些线程占用资源多，线程运行的代码是什么；哪些变量占用资源多。从而可以缩小范围，快速发现问题在哪。

排除法也是一种缩小范围的方法，尤其是在架构比较复杂的情况，一次用户请求的操作可能经过多个服务，如果配合日志，那么可以对一个请求经过的每一个服务都进行日志分析，对于正常的服务可以逐一排除，直到找到出问题的环节，从而可以缩小问题范围。

所以下一次你在遇到难以定位的 Bug 的时候，也可以想想怎么样可以逐步缩小问题的范围，直到发现问题。

新手解决完线上故障后，下次可能还会发生类似故障

新手在遇到线上故障后，采用一些临时解决方案，比如说重启服务，发现恢复了，然后就把这事忘记了。对于线上的故障，如果不找到产生的原因，那么下一次还会发生类似的故障，甚至比以前还更严重。

高手对于线上故障，会仔细分析 Bug 产生的原因，从根本上解决，避免类似的故障再次发生。

比如说，我以前所在的项目组，采用敏捷开发，每周一个 Sprint，每周会部署上线。但上线后经常会出现一些故障，导致在部署后就要回滚，或者再打补丁。

虽然每一次上线后的故障可能都不一样，但是如果仔细分析背后的深层次原因，还是因为上线前没有充分测试导致的。每周 3-4 天时间开发，1-2 天测试，来不及对程序进行充分的测试。所以我们后来从流程上改进，将一个 Sprint 内开发好的程序，在测试环境测试一周后再上线，这样调整后，极少出现线上故障。

有关上面案例中流程改进结果，可以参考专栏文章《07 | 大厂都在用哪些敏捷方法？（下）》中“一周一个迭代怎么保证质量？”的说明。

对于新手来说，每一次解决线上故障，同时也是一次学习和总结的机会，不仅是学习如何解决一个线上故障，还要学习解决一类的线上故障，避免类似的故障再次发生。

大厂都是怎么处理线上故障的？

在处理故障方面，可以看到新手和高手的差距。同样，如果你留心观察，会发现各个大厂，也都有一套自己线上故障处理的流程。（比如：《SRE：Google 运维解密》《阿里如何应对电商故障？》《滴滴是如何高效率处理线上故障的？》）

通过看这些大厂的故障处理流程，你会发现，大厂其实是把高手解决故障的方式，变成故障处理的流程和操作手册，并且通过反复地故障演习。不断练习和强化对故障处理的流程，让系统更健壮，让新手也可以快速上手，做到高效处理线上故障。

至于具体的处理流程，其实大同小异。

首先，对故障进行评级。

根据故障影响的范围，对故障进行评级，从而决定后续的处理方案。比如说 P0 是最严重最紧急的，可能是大面积服务瘫痪，影响大量用户，需要紧急处理；如果是 P5，可能只是用户体验相关的，晚一点处理也没关系。

其次，要马上恢复生产，避免进一步损失。

使用临时方案，恢复生产减少损失是第一位的。可以采用部署回滚、服务降级等处理手段。

另外，要分析故障原因，修复故障。

最后，记录故障发生处理全过程，分析故障原因，提出后续改进方案。

大厂处理线上故障处理机制有哪些值得借鉴的地方？

从流程看，大厂处理线上故障的机制似乎并没有什么特别的，那么有没有值得学习借鉴的地方呢？答案是肯定有的。

故障报警和轮值机制

你可以先思考一个问题：如果你所在项目组的系统出现线上故障，要多长时间可以恢复正常？怎么样可以做到最快的速度恢复？

要做到最快速度处理线上故障，关键就是要让正确人的第一时间就可以去响应。正确的人就是对故障服务最熟悉的人，通常就是这个服务的开发人员。

但让所有开发人员 7x24 小时随时待命也不现实，所以一般大厂会采用轮值的机制，比如说对于每个服务，每周要安排两个人值班，一个是主要的，出现故障第一时间响应；另一个人准备着，以防万一联系不上主要值班人员时可以顶替值班。

大厂都有一个报警系统，值班的那一周，值班人员手机要 24 小时开机，笔记本要随身携带，如果负责的服务出现故障，那么会在第一时间被报警系统呼叫。如果 15 分钟没有人响应，就会层层往上传递，值班开发人员没响应就呼叫经理，再是总监，VP，直到 CEO。

这套机制虽然被很多开发人员诟病良多，毕竟值班期间要随时待命，但确实是一套非常简单有效的机制，让最熟悉服务的开发人员第一时间去处理，可以帮助线上系统以最快的速度恢复服务。

实战演习

在我工作经历中，不止一次出现过数据丢失的情况，其实丢失数据前，都有完善的备份恢复方案和日常备份，然而这些备份恢复方案却从来没执行过，等到真正出问题，才发现这个方案完全是不可行的，日常备份也早已被破坏无法恢复，最终导致数据丢失。

如果日常对这些方案有演习，去实际测试一下，就不至于这么狼狈。实战演习就是频繁地对故障进行演练，来测试平时做的这些方案是不是真的可行，这样遇到真正的故障，才不至于手忙脚乱不知道如何应对。

其中最有名的就是 Netflix 的混乱猴子军团，Netflix 在亚马逊云上建立了一个叫做 Chaos Monkey（混乱猴子）的系统，这些猴子会在工作日期间随机杀死一些服务，制造混乱，来测试生产环境下的稳定性。

也有人把这样的实战演习叫“混沌工程”。

混沌工程就像“疫苗”：注射少量潜在有害的异物以预防疾病，这种人为的“破坏”其实是有帮助的。混沌工程通过在技术系统中注入危害 (如延迟、CPU 故障或网络黑洞) 来建立这种免疫力，从而发现和修正潜在的弱点。《以毒攻毒： Google、Amazon、Netflix 如何用混沌工程控制系统风险 》

日志记录和分析工具

对于软件来说，线上出现问题，分析日志记录是最简单有效的定位问题方式。这就要求平时在开发的时候，就要注意对关键日志信息的记录，同时还要搭建像 ELK 或 Splunk 这样的日志分析系统，方便查询日志。

举个例子：一个 API 请求，出现了随机无法访问的故障，而这个 API 可能会经过 5-10 个服务，怎么快速定位是哪一个服务出现问题？

一个好的实践是这样的：

对于每一个请求，都会分配一个唯一的请求编号（requestId），在经过每一个服务的时候，都带上这个请求编号，每个服务都把这个请求的输入和输出记录下来，输入的 url 参数是什么？http 的 header 是什么？输出的状态码是什么，输出内容的大小是什么？如果出错，异常信息包括错误堆栈是什么？

当出现故障的时候，找到一个有问题的 requestId，根据这个 requestId 去日志分析系统查询相关的所有服务的日志，这样马上就可以看出来哪一个服务返回的结果是有问题的。

当然还有一些其他好的实践，例如说新功能上线时，灰度发布的策略。通过开关控制，先让一小部分用户使用，如果出现故障，马上关闭开关，避免影响。

大厂的这些线上故障处理预防的实践都是公开的，通过网上的一些文章或者他们技术人员在技术大会上的分享，你也可以从中了解和学习到很多。重要的是看这些实践的好处是什么，哪些是可借鉴到你的项目中的。

总结

今天带你一起学习了线上故障的处理。对于线上故障的处理，基本原则就是要先尽快恢复生产减少损失，然后再去查找原因，最后不要忘记总结复盘。

要做到最快速度处理线上故障，关键就是要让正确人的第一时间就可以去响应。正确的人就是对故障服务最熟悉的人，通常就是这个服务的开发人员。

要让你的故障响应流程在真正遇到故障时能起到作用，需要经常做故障演习，测试你的故障响应流程，测试你的系统在故障下的稳健性。

线上故障的分析，少不了对日志的记录和分析，平时在开发阶段就应该要注意对日志的记录，同时也可以搭建一套适合你项目的日志分析系统，在遇到故障时，能及时的通过日志定位到问题所在。

最后，保持学习大厂对这些线上故障处理的好的实践，应用到你的项目中。

课后思考

你平时对于线上故障是如何处理的？有哪些可以改进的地方？你的团队对于线上故障是怎么样的处理流程？有哪些可以改进的地方？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

38 | 日志管理：如何借助工具快速发现和定位产品问题 ？

你好，我是宝玉。在开始学习之前我想先问你几个问题：

如果你的网站或者服务出现故障，是谁第一时间发现问题的？用户还是运维人员？

假设你的服务架构是由若干微服务组成的，其中一个微服务的异常导致了你的某个 API 请求异常，你是否能快速定位到是哪个微服务出了问题？

在部署系统后，你是否能观察出来系统的性能是上升了还是下降了？

如果你自己对这些问题的答案不是很满意，那么就可以来看看，如何借助监控和日志分析工具，或者说日志管理工具，第一时间发现线上问题，以及快速定位产品问题。

什么是日志管理？

要理解上面提到的这些问题，首先你要清楚，什么是日志管理。

日志就是操作系统和应用软件自动生成的事件说明或者消息记录，包含了时间、日志信息。举例来说，下面就是一个典型的 Web 请求日志：

10.0.1.22 – – [15/Oct/2018:13:46:46 -0700] “GET /favicon.ico HTTP/1.1” 404
10.0.1.22 – – [15/Oct/2018:13:46:58 -0700] “GET / HTTP/1.1” 200

从上面的日志中，可以看出来，日志包含两次 http 请求，它们发生的时间、请求的 URL、请求的 IP 地址、最后返回的状态码等信息。

在日志数量不多的时候，凭借肉眼或者借助文本编辑器，还能大概看出日志的内容，但是当日志数量一多，从日志里面查找需要的信息就变得很困难了。

现在的应用程序越来越复杂了，尤其是像微服务这样的架构，一个系统需要由若干微服务组成，每个微服务可能还会部署在若干容器上，那么意味着如果你要根据日志去排查故障的话，需要从几十、上百个地方去收集日志，再逐个去分析。

要解决这样的问题，就需要对日志进行统一管理。日志管理就是指对系统和应用程序产生的日志进行处理的方法，包括对日志进行统一收集，对日志数据进行筛选和解析，统一存储，还要让它们可以方便被检索。

当然你不需要自己去从头实现这样的日志管理系统，现在已经有很多成熟的日志管理工具可以帮助你对日志进行管理，你只要去了解这些工具可以帮助你做什么，以及如何基于它们来搭建适合你项目的日志管理系统即可。

如何快速发现和定位问题？

也许你会问，为什么说搭建了日志管理系统，就可以帮助快速发现和定位问题呢？

首先，日志集中式管理后，就可以方便地对所有日志进行统一的检索。当所有日志都可以放在一起检索了，自然就能高效地定位到问题，而不再需要到各个应用程序的日志里面去分别检索。

同时在检索的方式上，可以用类似于 Sql 语句的方式来检索，高效地对结果进行查询和归类。

（图片来源：Splunk）

然后，对日志进行集中式管理后，可以通过图表直观的看到应用运行情况。当所有的应用实时将日志传输到一起，日志管理系统就可以根据应用日志中记录的信息，动态地生成图表，实时看到应用运行的情况。

举例来说，某一个 API 服务，日志信息记录了每一次 Http 请求的状态、耗费时间等信息。

127.0.0.1 [10/Oct/2018:13:55:36 -0700] "GET /api HTTP/1.1” 200 2326 0.038

那么把这些信息统一收集、实时统计的话，就可以随时看到单位时间内，这个 API 错误率有多少，平均耗时多久，从而可以根据这样的信息生成实时的图表，方便查看当前 API 服务的运行情况。

（图片来源：WaveFront）

最后，可以根据日志的数值设置规则自动报警。对于这些从日志中实时分析出来的数据结果，如果设置好相应的阈值，在超过阈值后，比如说 API 错误率超过 10%，或者 90% 的 API 请求时间超过 1 秒，就会自动触发报警，通知相关的开发人员进行维护。

所以你看，当你搭建好一整套日志管理系统后，不仅可以帮助你快速地对日志进行检索，你也可以根据图表看数据走势，还可以通过对日志分析结果的监控，设置自动报警的规则，第一时间了解系统故障。

大厂的日志管理系统的架构是什么样子？

现在对于像阿里、新浪这样的大厂来说，对日志管理系统的应用已经是标配了，比如说阿里云：《基于 ELK 实时日志分析的最佳实践》、新浪：《ELK Stack 在新浪微博的最佳实践》、《新浪是如何分析处理 32 亿条实时日志的？》，七牛：《如何快速搭建智能化的统一日志管理系统》。

可以看的出，很多大厂是基于 ELK 搭建的自己的日志管理系统，而 ELK 的架构也是一套经典的日志管理的架构，所以这里我就以 ELK 为例来说明日志管理系统的基本架构。

先解释一下 ELK：

ELK 是 Elasticsearch+Logstash+Kibana 的缩写。
ElasticSearch 是一套搜索框架，提供了方便的接口，可以方便地做全文检索，可以用来对日志进行检索。
Logstash 是一个数据收集工具，可以用来收集日志数据。
Kibana 是一套可以和 ElasticSearch 交互的界面，通过 Kibana 可以方便的检索 ElasticSearch 内的所有数据，还可以用图形化的方式展示数据结果。

基于 ELK 搭建的日志管理系统基本架构是这样的：

（图片来源：Openstack log analysis using ELK）

这套架构有几个重要的模块：日志采集和解析、存储和搜索、结果可视化、监控和报警。

日志采集和解析

要想对日志进行统一管理，就必须要从各个应用系统收集日志。Logstash 就可以帮助实现对日志的采集。

如果日志文件只是一行行带时间戳的文本，那其实是无法有效检索的，必须将其解析成结构化的数据，才能方便地检索。

另外，一套系统可能由不同的应用类型组成，有的是 Java 写的，有的是 Go 写的，日志格式可能完全是不一样的，所以还有必要在对日志解析后，提取公共元素，比如时间、IP 地址、主机名、应用名称等。

Logstash 不仅可以对日志数据进行收集，还能对日志数据进行过滤和解析，解析完成后再将解析好的数据发送给 ElasticSearch。

存储和搜索

当所有的日志数据都被集中存储后，可以想象这个日志数据库是相当庞大的，直接查询效率是比较低下的，这就意味着还需要对日志数据进行索引和分析，从而让你可以快速地检索出来结果。

ElasticSearch 就是一套专业的全文检索和数据存储系统，同时还有一套类似于 SQL 的查询语句，这样你就可以基于它，方便对收集好的日志数据进行检索了。

但 ElasticSearch 本身类似于数据库，没有图形化界面。

结果可视化

可视化是日志管理的另一项重要功能。通过可视化的图表，可以直观地看到数据的走势，以及方便地和历史数据进行对比。

比如说通过观察交易数据的走势曲线，就能看出来这周的交易数据比上周是增长还是下降；根据 API 响应速度的走势，可以看得出新版本部署后，性能是提升了还是下降了。

像 Kibana 就是一套专门针对 ElasticSearch 的图形化操作工具，可以方便对 ElasticSearch 数据进行检索，也可以对结果用图表的方式展现。

监控和报警

ELK 本身只是提供了一套基础的日志管理框架，但是基于它之上还可以有很多扩展，比如说自动报警就是一个非常典型的场景，可以基于已经存储和索引好的日志数据，制定相应的自动报警规则，当线上服务发生异常时，可以自动地触发报警，通知相关值班人员及时处理。

ELK 可以通过插件的方式，安装像 ElastAlert 或Watcher这样的自动报警插件，实现自动报警功能。

（图片来源：Build your own error monitoring tool）

怎样搭建一套日志管理系统？

在了解了整个日志管理系统的基础架构后，再要去搭建这样一套日志管理系统，就可以做到心中有数了。你可以基于这套架构去寻找合适的工具，或者直接基于 ELK 去搭建一套日志管理系统。

关于 ELK 网上已经有很多安装使用教程，比如这一本电子教程《ELK 教程》就写的很详细。

ELK 本身是一套开源免费的工具，除了 ELK，还有一些类似的工具可以选择，可以和 ELK 配合使用。

Splunk

Splunk 是一套商业的日志管理系统，搜索功能非常强大，操作方便，就目前来说，要比 ELK 好用，但价钱很高。

Grafana

Grafana 是一套开源的数据监测和可视化工具，可以和 ELK 或 Splunk 配合使用，展示效果比 Kibana 要更好。同时可以支持自动报警功能。

Wavefront

Wavefront 是 VMware 旗下的一款商业的图形化监控和分析工具，可以从 ELK 或 Splunk 等数据源收集数据，在此基础上分析应用的性能瓶颈所在，排除故障。也支持自动报警。

PagerDuty

PagerDuty 是一套报警服务，不仅可以和手机、邮件、Slack 等方便的集成，还可以和企业的轮值安排结合，按照排班顺序呼叫当值人员。

以上就是一些常用日志管理系统以及配套系统工具，基本上可以很好地满足你对日志管理的需求，通过搜索引擎你也可以找到更多类似的服务。

总结

今天我带你一起学习了日志管理工具相关的内容。通过日志管理工具，可以集中的管理所有系统的日志，方便对日志进行检索，图形化的展示结果，还可以做到根据设置的规则进行自动报警。

如果你想搭建属于自己的日志管理系统，可以基于 ELK 或者 Splunk 这样的日志管理工具，配合一些插件，实现你自己的日志监控和分析工具。

在搭建好日志管理系统后，如果我们再回头看文章开头那几个问题，你会发现：

如果你的网站或者服务出现故障，可以通过你设置好的自动报警规则第一时间通知值班人员，及时解决；

假如你的某一个微服务出现异常，你可以从你的日志管理系统中直接对所有微服务的日志进行查询，快速定位到问题所在；

在部署系统后，通过对 API 响应时间等数据指标的图形化显示，你可以直观的看到性能是上升了还是下降了。

总的来说，借助日志管理工具，可以帮助你快速发现和定位产品问题。

课后思考

你的项目中是否有应用日志管理工具？你觉得这样的工具的应用，对你的日常项目可以带来哪些好处？如果还没有应用，主要阻力是什么？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

39 | 项目总结：做好项目复盘，把经验变成能力

你好，我是宝玉。相信大家都有这种体验，经历了无数个“996”加班，项目终于成功上线了，也进入了稳定运行阶段，你终于可以松一口气，准备迎接下一个项目的挑战了。

然而，这时还有一件事不要忘记了，那就是对项目复盘，全面总结一下项目过程中的得与失。

什么是项目复盘？

“复盘”本来是围棋术语，表示对弈之后，棋手把下棋的过程重演一遍，看看哪些地方下的好，哪些地方下的不好，有哪些更好的走法。把下棋的过程还原，并且分析、讨论的过程就是复盘。

软件项目中的复盘，也是通过分析、讨论开发中出现的问题，进而总结成功经验，吸取失败教训，提升团队能力。

一次项目过程，自然会有一些做的好的地方，也会犯一些错误，复盘就是要分辨出哪些是好的实践，继续保持；哪些是做的不够好的，找出原因，针对性改进，避免再犯同样的错误。

如果没有这样的项目复盘，那下一次做项目，你还会是用同样的方式来做事情，那恐怕踩过的坑可能还得再踩一遍。

也许你会认为，你们团队也开过项目复盘总结会议，但是似乎没有什么效果，是不是项目复盘并没那么有价值？

并不是项目复盘没有价值，多数情况下，是因为没有做好项目的复盘，反而相当于浪费了一次学习提升的机会。比如说一些常见的存在问题的项目复盘情形：

总结不出来有效的结论

有些团队流水账的回顾了一遍项目过程，感觉似乎有做的不好的地方，但说不上是什么地方做不好，所以也无法进一步的总结。

没做好是客观原因导致的

有些团队在复盘后，将结论归结为是客观原因导致的，比如说：“虽然这次做的不好，只是客户不靠谱，但是下一次遇到一个好客户肯定能做的更好！”，这相当于没有想清楚：是哪里做的不好？为什么做的不好？下一次遇到这样的客户怎么才能做的更好？

知道什么原因，但不知道该怎么办

有些经过分析总结，能找到原因，但不知道如何应对。比如说发现主要原因是客户老变需求导致项目延迟，但是不知道如何应对需求变更。

类似于这样的项目复盘，确实达不到好的总结效果，也难有提升。那么怎么样才能做好项目复盘呢？

如何做好项目复盘？

项目复盘，首先就是知道项目中哪些是做的好的地方，哪些是做的不好的地方，这样才能把做的好的地方继续发扬光大，做的不好的地方进行改进修正。

那怎么样才能知道哪些地方做的好，哪些地方做的不好呢？

只要对比一下你当初制定的项目目标和最终的项目结果，就可以发现差异，通过这些差异，就可以清楚地知道哪些地方是变好了、哪些地方变糟了，比如说项目延期了，功能被砍了，软件质量相比以前的项目质量提升了。

但光知道差异还不够，需要思考背后的原因，比如说为什么会导致项目延期？做了什么事情让软件质量提升了？也就是说，要从这些事情中能总结出来规律，从而知道哪些做法是真正有效的，值得继承或者推广？哪些做法是无效的？

这里就需要结合软件工程的知识来分析，把实践经验概括为普适的理论或者原则。

最后就是要用这些从经验中学到的理论或原则，指导后续的项目开发，决定要停止做什么，开始做出怎样的改变，以及继续做哪些事。

联想公司对于项目的复盘总结了四个步骤，同样适用于软件项目，我们可以借鉴它的做法，采用四个基本的步骤来进行：

回顾项目目标；

评估项目结果；

分析原因；

总结规律，落实行动。

接下来我就这四个步骤来分别讲一下，如何对项目进行复盘。

第一步：回顾项目目标

每个项目在最开始的时候都会确定项目的目标，所以复盘的第一步，就是要回顾最初的项目目标，方便对最终结果进行评估。

在这个环节，需要你描述清楚当初定的项目目标是什么？项目计划中制定的里程碑是什么？其中的关键就在于，对目标的描述要尽可能准确和客观。

因为只有做到准确和客观，在后续你才能对目标的完成情况进行准确地评估。

比如说：“我们的目标是做一款伟大的产品”，就不算是准确客观，因为“伟大”是一个根据主观评判的形容词，每个人对伟大的理解是不同的。

你需要将这类形容词换成具体可考核的检查项，比如，可以总结出类似于这样的目标：“三个月时间完成一款在线学习网站产品，包括登录、在线学习、留言等主要功能模块，上线后的 Bug 比例低于上一款产品。”

最后再加上最初定的里程碑，比如说：“两个月开始内部测试，三个月正式上线。”这样，大家就可以对目标的完成情况有清晰地认识。

第二步：评估项目结果

在对项目的目标进行回顾后，就可以来看看项目的实际结果和当初的目标有多少差异了。这里需要列出两方面的差异：好的差异和坏的差异。

比如说项目的结果是：我们花了四个月时间完成整体项目，三个月才开始内部测试。原有功能作出了调整，学生留言老师回复的功能改成了类似于讨论版，大家一起讨论的功能，上线后质量稳定，Bug 比例低于上一款产品。

好的差异：

上线后质量很稳定，严重 Bug 很少；

没有出现需求遗漏，开发和测试能及时同步需求的变更。

坏的差异：

功能发生了变化，中间有比较多的需求变更；

项目发生了延期。

可以鼓励团队成员一起列出项目中好的差异和坏的差异。需要注意的是，在这一步，只需要客观描述结果就好了，不需要去分析原因，不然大家很容易思维发散，过早陷入对细节的讨论。

第三步：分析原因

在结果评估完了后，就可以来分析原因了，分析的时候也可以主要从两方面着手：是什么原因导致了好的差异，什么原因导致了坏的差异。

比如说，导致好的差异的原因：

增加了自动化测试代码的比例，改进了开发流程，代码合并之前有代码审核，并且要通过自动化测试；

增加了工具的使用，比如持续集成系统的搭建，每次提交后可以清楚的看到测试结果；

改进了项目流程，对于所有的需求细分后，都创建成了 Ticket，基于任务跟踪系统记录了起来，这样可以及时了解任务进程，有需求变更的情况，相关人员也能及时了解。

比如说，导致坏的差异的原因：

老板对于产品干预过多，导致需求变更频繁；

项目周期过长，难以响应需求的变化；

设计时没有考虑到需求的变更，导致需求变更发生后，很多设计需要修改，最终导致延期。

在分析的时候，可以营造一个宽松的氛围，让团队成员能畅所欲言，讨论时要做到对事不对人，尽可能客观地分析清楚成功和失败的原因。只有分析清楚原因，才能总结出规律。

第四步：总结规律，落实行动

分析出原因后还不够，最重要的是，还需要去总结背后的规律，才能真正把成功或失败的经验变成个人和团队的能力。这里也可以充分运用你在《软件工程之美》专栏中学习到的知识，去帮助你总结规律。

比如说，接着上面的案例你可以继续总结规律：

需求变更是导致项目延期的主要源头，需要在后续项目中控制好需求的变更；

自动化测试加上代码审查，再配合持续集成工具，可以有效提升产品质量；

任务跟踪系统可以方便地跟踪需求的执行情况，也能保证项目成员能及时同步需求的变更。

总结出来规律后，还需要落实成行动，才能真正做出有效的改变，帮助你在以后的项目中做的更好。落实行动的关键就是：对于好的实践，继续保持；对于不好的实践，停止并寻求改变。

就上面的案例来说，针对上面总结出来的规律，你可以继续整理出需要在后续项目中落实成行动的事项：

参考专栏文章《20 | 如何应对让人头疼的需求变更问题？》中的解决方案，针对需求变更，我们将缩短项目周期，采用快速迭代的开发模式，及时响应需求变更，同时在一个迭代中，没有特殊情况，不做需求上的变更，有变更放到下一个迭代中；

继续增加自动化测试代码的比例，代码在合并前要对代码进行审查，用好持续集成工具；

继续使用任务跟踪系统，对需求任务进行跟踪，并且可以尝试对于一些临时性的任务也用任务跟踪系统跟踪起来。

通过分析目标、评估结果、分析原因和总结规律这四个步骤对项目复盘，能有效帮助你发现项目中做的好的地方和做的不好的地方，找出背后的原因，最终总结出来规律，落实成行动，做出积极的改变，把经验变成个人和团队的能力。

总结

项目复盘，可以帮助你从刚刚经历过的软件项目中，总结成功经验，吸取失败教训。为什么在同样的工作时间内，有的人就是成长的比较快，收获更多的经验能力？其实他们就像优秀的棋手，通过不断地对做过的事情进行总结复盘，来快速提升自己的能力。

项目复盘主要通过四个步骤进行：回顾项目目标、评估项目结果、分析原因、总结规律落实行动。

另外你需要注意的是，对于项目的复盘，并不是说只有项目快结束了才要去做，日常项目中遇到一些特殊的事情，比如线上故障，也可以及时总结复盘，预防类似的事情再次发生；在每一个迭代结束之后，都可以阶段性的复盘，比如说敏捷开发中每个 Sprint 的项目回顾会议；在整个项目结束的时候进行全面的项目复盘。

在项目复盘的形式上，可以通过团队会议的形式来进行，但是要想做到会议有效率，还需要在会议之前就做好准备工作，事先收集内容；会议进行中要有人组织引导大家积极发言讨论，避免陷入细节的争吵中，更要避免互相甩锅、人身攻击等极端情况发生；会议后，要落实到行动。

关于项目复盘会议，我觉得阿里的这篇文章写的非常好：《开会 = 浪费时间？阿里技术团队这样开项目复盘会》，你可以作为参考。

希望你也可以不断地对做的过事、参与的项目进行总结复盘，把经验变成能力。

课后思考

你平时都是如何对项目进行复盘总结的？你觉得哪些地方可以做的更好？你有哪些好的经验可以和大家一起分享讨论的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

“一问一答”第4期 | 14个软件开发常见问题解决策略

你好，我是宝玉。恭喜你完成了软件测试和线上维护这两个模块的学习。

软件测试是保障软件质量的重要一环，但也不能过于依赖软件测试，毕竟软件质量体现在功能质量、代码质量和过程质量三个方面，而软件测试只能帮助保证功能质量，代码质量和过程质量还需要团队一起努力。

现在大厂已经很少有手工测试的岗位了，大部分测试工作已经转移到开发上，同时自动化测试的比例越来越高，但这相应的对开发的要求更高了，不仅要写功能代码，还要写自动化测试代码。

软件测试也离不开对工具的使用，通过 Bug 跟踪工具报 Bug 和跟踪 Bug，使用测试管理工具管理测试用例，基于自动化测试框架写自动化测试代码，借助性能测试工具对软件性能进行压力测试。测试工具还可以和持续集成一起整合，最大化发挥测试工具的效应。

软件测试做好了不代表你的软件就是安全的，不会导致账号密码泄漏，要想构建安全的软件，需要在整个软件生命周期中都重视安全问题，各个阶段考虑到安全方面的问题，防患于未然，构建出安全的软件。

当你的软件已经测试通过，准备上线发布了，不要忘记在软件发布前做好版本规划，尽可能的让软件的功能质量，满足好用户的预期。一方面要尽可能提供应有的功能和保证质量，另一方面也可以通过合理的发布策略来降低用户的预期。

传统软件发布上线后，就是运维负责保障线上运行了，但这种分工也导致了开发和运维之间在沟通协作上不够紧密，甚至有冲突，于是 DevOps 出现了，它帮助解决开发和运维之间的沟通协作问题，提升运维开发和自动化能力。通过自动化、信息透明可测量、共同协作的原则，达到更快更可靠的构建、测试和发布软件的目的。

软件发布后不代表项目就结束了，还需要处理线上故障，保障运行。遇到线上故障不用慌，要先恢复生产减少损失，然后再去找原因，最后再去总结复盘。日常还要对故障多演习，这样真的有故障发生了才能做到从容应对。日志管理工具是线上故障排查的好帮手，能帮助你快速定位线上故障，还可以对日志的数据进行监控，有问题提前预警。

最后，项目结束后不要忘记对故障进行复盘，总结成功经验，吸取失败教训。把好的实践继续发扬光大，对于不好的实践马上停止做出积极有效的改变。

以上就是两个模块内容的总结，希望你在学习这些知识后，能应用到实际的项目中去，帮助你项目的质量再上一个台阶。

今天加餐继续分享我们专栏的精彩问答和留言，这些问答和同学们的分享都是对专栏内容的最好补充，希望可以帮助你更好地学习和理解软件工程知识。

一问一答

No.1
hua168：质量是怎么打分的？算进 KPI 考核吧？直接用代码质量管理软件（如 sonar）实现自动检查可以吧？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：很遗憾，都不好量化，软件检查只是辅助，可以作为一个参考。代码质量要看满足需求，是否设计良好，代码简洁逻辑清晰，可维护、可测试、安全高性能；过程质量要看开发过程对软件工程和项目管理知识的应用；功能质量要看客户满意度。

No.2
砍你一刀：能分享一个比较好的测试用例模板吗？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：我建议你试试 testrail，它的测试用例模板非常专业。

对于测试用例：

几个关键的字段是：标题、描述、优先级、分类。

测试类型：功能测试、性能测试、回归测试、冒烟测试…

自动化状态：没有自动化、只能手动测试、只能自动化、集成 CI…

先决条件：这个用例需要满足好什么条件。

测试步骤：写清楚一步步的执行步骤。

期望结果：操作完成后结果应该是什么样的。

No.3
kirogiyi：最近发现一种现象，开发人员面对测试人员的时候，会展现出一种职业选手遇到业余选手的姿态，傲慢、理所当然，我觉得这是一种不正常的心理状态，应该怎么去管理？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：这确实是常见的现象，核心还是多一起合作多相互了解吧，让开发人员看到测试的核心价值，就是对测试方案的设计。

我对测试人员敬佩的地方不在于他们会写自动化测试，毕竟这个我写起来还更好，而是他们总能从我没想到的角度测试出来 Bug，从而帮助我提升程序质量。

可以安排一些开发人员和测试人员一起合作的事情，比如测试人员提供测试方案测试用例，开发人员按照测试用例去实现自动化测试，让开发人员明白，做好测试其实不是他们想的那么容易。

No.4
和：我的单位不是软件公司，但是企业内部也会开发一些业务类软件。可是从需求分析到设计，开发，测试，部署，运维，都是我一个人的工作。这样的情况，如何工作效果会更好一些？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：对于这个问题，我觉得你可以自己先分析一下，你觉得目前哪些地方做的好，哪些地方可以有改进？可以从几个方面分析，比如：

工具：

有没有用源代码管理工具？
有没有用持续集成跑单元测试？
有没有用 Bug/ 任务跟踪系统？

开发流程：

多长时间一个项目周期，一个功能从开发到上线要多长时间？质量如何？
有没有做需求分析和确认？会不会做出来东西不是业务部门想要的？
有没有开发前先做简单的技术设计？
有没有写一些基础的测试用例？
有没有开发后自己测试和找业务部门帮忙测试？
线上的故障有没有一个合适的流程去处理？
有没有写一些基本的文档，如果来一个新人了能否接手？

技术实践：

有没有写自动化测试？
有没有用好的技术框架或开源组件？
有没有自动化部署？

分析出问题，知道哪些地方做的不够好以后，就可以有一个改进的计划 ，看如何将改进方案落实下来。

还有就是一个人开发，缺少向其他人合作和学习的机会，可以有意识地创造更多这样的机会，比如内部多和其他部门合作。外面可以参与一些开源项目。

No.5
hua168：现在不是流行测试驱动开发吗？先写测试代码再写实现代码，那写完再让专门的测试去测？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：测试驱动是一种很好的开发实践，但普及率也不算很高。可以看到自动化测试那一篇，测试驱动写的是单元测试，并不能保证不出 Bug，只是说能有效提升代码质量。还有就是开发人员测试自己写代码，很容易遗漏编码时就没考虑到的逻辑。

No.6
果然如此：对于自动化测试一直有些疑问，如果每次发布都对所有方法自动化测试，那么：

一定会耗费很多时间；

数据库产生很多测试历史数据；

写测试用例能达到覆盖率高的写代码技巧，如边界测试代码、幂等测试代码如何实现。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：
1. 自动化测试确实会耗费很多时间。自动化测试代码通常是金字塔结构：

单元测试（小型测试）代码最多，执行也最快，占总比例的 70% 左右，通常 1 分钟内；

集成测试（中型测试）代码其次，执行比较快，占比 20% 左右，控制在 10 分钟以内；

端对端测试（大型测试）最少，执行慢，占比 10% 左右。

一般 CI 里面跑单元测试和集成测试，耗时 10-15 分钟左右，其实还可以接收。

2. 跑自动化测试，数据库有不同策略。单元测试不访问数据库，完全模拟。集成测试只访问本机数据库，或者模拟的内存数据库，每次创建新数据库，或者使用完清空数据库。端对端测试，每次创建唯一数据（例如增加固定数据 + 时间戳），连接真实的测试环境，可以不清理数据。

3. 高覆盖率的关键在于，在写代码时就注意让代码方便地被测试。也不必过于追求 100% 覆盖，70% 以上我觉得就不错了。

No.7
宝宝太喜欢极客时间了：对测试这块一直很疑惑，测试脚本、测试用例、测试数据这三者如何配合一起通过 CI 进行自动化测试？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：是这样的，CI 本质上只是一个像流水线传送带，你的代码提交了，流水线传送带开始工作，你可以在传送带上面添加任务。

简单来说，你可以想象成 CI 的一个任务启动后，给你一个干净的虚机（实际是运行 Docker Container），然后帮你把当前代码下载下来，帮助你配置好运行环境，然后你就可以在里面安装任何软件、服务和运行任何脚本。

举例来说，你可以在传送带上增加以下任务：

安装所有的依赖包；

运行模拟服务（比如一个内存数据库）；

运行单元测试；

运行集成测试。

如果上面所有任务都成功了，那么这一次的 CI 任务就成功了，其中一个失败，这一次的 CI 任务就失败了，然后你就要检查什么原因导致的，然后修复再重新执行，保障 CI 任务成功执行位置。

No.8
一步：问一个有关 code review 的问题，code review 是指把代码放到大屏幕上大家一起看呢？还是类似 github 上的合并代码的时候发个 pr，然后另一个人对需要合并代码进行检查，检查通过后才同意合并请求？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：我觉得两种都算 Code Review，只是形式不一样。

通常 PR 这种 Code Review 应该是贯穿到日常开发过程中的，每个人都可以去 Review，有 1-2 个人 Review 通过就可以。合并的话不止是 Code Review 通过，还需要自动测试通过。

而大屏幕这种参与人多，成本高，属于偶尔针对特殊故障分析、学习研讨等目的才做的。

No.9
yellowcloud：我们目前有一个项目是做实时数据采集的，对方将实时数据推送给我们，基本上每天每个时刻都可能有数据推送过来。这样就导致一个问题，我们部署新的版本时，他们的数据还在推送，这样就不可避免地丢失了部署过程中的数据，对方也没有重新推送的机制。请问，这种问题有没有比较好的解决方案，以解决更新版本时数据丢失的问题？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：这个问题其实不复杂，你可以将服务分拆，独立出来一个专门接受数据的服务，这个服务极其简单，只做一件事：接收数据，并存储到数据库或消息队列。

你原有的服务，改从数据库或者消息队列读取即可。更新部署的时候，接受数据的服务就不要轻易更新了，这样就不担心会丢数据了。真要更新，只要和对方协商一下，暂停推送就好了。

No.10
Charles：如果是瀑布流带点敏捷部分实践的开发方式，总觉得 UI 这个岗位工作量不怎么饱和，一个版本过来特别是小版本迭代，周期可能是两周，UI 可能 1 天就搞定了，其他岗位都差不多要全程跟下来，这个问题出现在哪里？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：这个问题有点不好回答，毕竟对你项目情况不够了解。

我觉得，如果 UI 这个岗位对你的团队来说是必须的，并且 UI 设计师很好地完成了他 / 她的工作，那么就很好，没有任何问题。毕竟有的人效率就是比较高，好过故意磨洋工看起来很忙。

如果 UI 这个岗位是可有可无，那么就可以考虑不设置这岗位，将工作外包出去，或者尽可能用一些标准的 UI，或者让前端工程师兼职 UI 设计工作。

No.11
yellowcloud：宝玉老师能不能介绍一整套可以简易部署使用的 devOps 的工具，方便小公司快速部署、实现，在实践中感受 devOps 的魅力。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：如果你要部署持续集成环境，可以先试试 Jenkins 或者 Gitlab CI。如果你要部署日志和监控系统，可以试试 ELK，也就是 Elasticsearch、Logstash、Kibana 三者的结合。网上可以找到很多安装使用教程。

No.12
一步：搭建自动化测试，自动化部署，自动化监控系统，都自动化了，开发都做了，是不是就不需要运维和测试了？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：自动化只是把重复的体力活做了。自动化测试的话，还是需要测试人员写测试用例才能有更好的测试效果；自动化部署和监控，也离不开专业运维人员的设计和搭建。

但是可以预见的是，以后低端的手工测试和运维岗位会被挤压的很厉害。如果你看大厂的招聘岗位，这些低端手工岗位都极少或者根本就没有。

No.13
邢爱明：我在传统的制造业甲方公司，系统大部分是内部管理系统，对于线上故障处理，一直有几个疑问:

谁来主导线上故障处理的过程？

故障排查是不是应该有一个标准的分析过程，让运维、开发、安全各方能更好的协作？

便利性和安全如何平衡？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：

谁主导线上故障，我觉得有两个指标要考虑：一个是这个人或者角色要懂技术懂业务，这样出现故障，能对故障进行评级；另一个是要能调动开发和运维去协调处理，这样出现故障能找到合适的人去处理，不然也只能干着急。

故障排查上：如果是操作系统、数据库、网络等非应用程序故障，应该是运维负责；如果是应用服务故障，应该是开发去负责，即使开发最近没有去做发布也应该是开发去查。因为只有开发对于应用程序的结构最清楚，才能找出来问题。排查过程中，运维要给予配合。

应该搭建起来像 ELK 这样的日志管理系统（可参考《38 | 日志管理：如何借助工具快速发现和定位产品问题 ？》），将应用程序日志也放上去，这样正常情况下就不需要去登录服务器了，直接就可以通过日志工具查看到异常信息。另外，一些特殊情况应该允许开发人员登录服务器排查定位。

No.14
Charles：目前只放了 nginx 日志到日志服务做一些简单的分析，还有其他什么日志是应该放到日志服务里的？有什么比较好的实践吗？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：我觉得应用程序的日志也应该考虑放进去，对排查问题很有帮助。应用程序的异常信息、错误堆栈非常有用，必须确保记录下来了。

举个例子来说，你的一个手机 App，一些特定场景下，某个 API 请求出错，而这个 API 可能背后会连接多个服务或者数据库，这样的场景下，光靠 nginx 日志是不够的，必须要有应用程序的日志配合才好定位。

你可以参考我在《37 | 遇到线上故障，你和高手的差距在哪里？》提到了一个 requestId 的实践。

1716143665 拼课微信

kirogiyi:
产品设计、软件开发、软件测试都应该对产品质量负责。

产品设计要重视产品需求的完整性，提升用户的操作舒适感，展现流畅的页面逻辑设计，这是产生良好软件质量的开端。在进行产品设计评审的时候，除了评审人员外，相应的软件开发团队和软件测试团队一定要派人员参加，不能坐等任务分配。有的开发团队和测试团队不去了解需求和产品设计情况，只是一味等待产品的 UI 设计，久而久之，就形成了少交流多看文档的习惯，于是大家就开始机械般各顾各的，做完了扔出去就好，就很难在产品质量上达成共识。

软件开发是核心，开发人员对产品的理解程度和自身的技术水平决定了产品的质量和迭代周期。如果开发团队不去与产品团队交流，不去与测试团队核对测试用例，那么在开发过程中大多只会去关注是否实现和能否实现，至于产品质量出现的问题，就不是他们关注的重点。然后就会自私地认为产品设计需要增删改是产品团队的原因，产品上线出现 Bug 是测试团队没有覆盖到，部门之间的战争就开始酝酿直至爆发。

软件测试更多的是发现问题、监督问题和约束行为。发现问题的重点在于通过完善的测试覆盖，去找到开发过程中的盲点，而不是去为别人的疏忽大意导致的错误埋单，比如：开发提交的产品有错别字什么的，这种错误就是开发负责人应该承担的。

在明确发现问题后，测试团队有权利去监督开发团队解决问题，直至问题得以彻底解决。除此之外，测试团队可以对开发团队行为进行约束，双方协作完成自动化测试体系和流程的构建，共同遵守规则：开发团队负责单元测试、集成测试和系统测试的代码编写，测试团队负责查缺补漏和必要的人工测试。

因此，个人认为，产品、开发、测试的紧密合作是保障产品质量的必备条件。

相关阅读：31 | 软件测试要为产品质量负责吗？

纯洁的憎恶：
解铃还须系铃人，要想提高软件质量，就要着眼于整个生产链条，每一个环节都要为提高质量出力，而绝不能仅仅依靠质量监控岗位或部门。相反，很多企业设置了类似的部门或岗位，并把质量、安全的重担压在他们肩上，但又没有赋予足够的权力去介入、影响整个链条，结果可想而知。不谋全局者不足以谋一域啊。

把整体质量按照生产链条或链条上的不同角色，划分为若干子部分。通过有机的把控各个子部分质量，形成合力，达到提高整体质量的目的。

相关阅读：31 | 软件测试要为产品质量负责吗？

纯洁的憎恶：
看来是否需要专职测试人员，在一定程度上需要视具体业务情境而定。不同的情境会有不同的异常情况和极端情况，需要有针对性的设计出完备的测试用例。而且在 Bug 修复后，也要保证修复本身没有 Bug。

所以测试也是一个系统性的工作，如果取消专职测试人员，不仅对开发业务水平要求更高，还需要项目自身的不确定性低一些。感觉有测试思维的发开人员，更有可能写出健壮的代码。

相关阅读：32 | 软件测试：什么样的公司需要专职测试？

yasuoyuhao：
代码就像程序员的名片，要对写出来的代码负责，最好的负责方式就是写测试代码，让每次代码变动，都不会影响到其他代码的运行，避免所谓的改 A 坏 B，节省迂回的时间浪费。也为 CI/ CD 做好准备，无论目前有没有。

程序员不想写测试在我们公司的原因大多是，不知道怎么开始写，不知道重点应该测试什么。先写测试的开发模式让他们觉得不习惯，但这些都是过程，培养良好的撰写测试代码习惯后，开发品质更有保证，提升开发效率，提升个人能力，我想都是有帮助的。

程序难免有 Bug，透过追踪软件，良好的管控 Bug 数量与修复进度，并且补足测试。

相关阅读：33 | 测试工具：为什么不应该通过 QQ/ 微信 / 邮件报 Bug？

纯洁的憎恶：
新手用野路子解决问题，高手用模型解决问题：
1. 给问题评级。紧迫的调动优势资源先解决，一般的往后放放。
2. 尽快恢复生产。生产是企业的首要职责，遇到问题优先恢复生产，减少直接损失，然后再正式地解决问题。
3. 找到问题出现的位置。“搜集证据”，通过“粗调”在时空上缩小包围圈，再用“精调”明确问题点，运用排除法最终锁定问题。
4. 分析原因，修复问题。
5. 提出解决方案。钥匙不一定插在锁眼里，要沿着问题的线索不停“倒带”找到根源。再针对根源，站在系统和流程的高度制定解决方案，避免问题复现。

重点：
1. 通过故障报警 + 业务骨干轮值机制，让正确的人第一时间响应问题。
2. 通过实战演习，确保应急预案稳定可行。
3. 通过使用日志记录和分析工具，积累、整理日常生产信息，出现问题才有得分析，否则重现问题也无济于事。

相关阅读：35 | 版本发布：软件上线只是新的开始

alva_xu：
线上故障，这是 ITIL 要解决的问题。我觉得最主要还是从三个方面来看。一是从流程上，对于事件管理、问题管理、变更管理、服务等级管理等，要有明确的流程。二是要有合适的工具，比如 ticket 系统，CMDB，监控工具、日志平台等。三是从人员组织来看，要有一线、二线和三线团队的支持，根据所创建的 ticket 的严重性和紧急性，给予不同 level 的支持。当然这也是目前流行的 devops 要解决的问题。

相关阅读：37 | 遇到线上故障，你和高手的差距在哪里？

alva_xu：
我觉得 scrum 方法中提到的两个会，可以作为项目复盘会内容的参考。Sprint 评审会议（Sprint Review Meeting）和 Sprint 回顾会议（Sprint Retrospective Meeting）。Sprint 评审会议在 Sprint 快结束时举行 ，用以检视所交付的产品增量并按需调整产品待办列表，是对工作成果的评审。Sprint 回顾会议是 Scrum 团队检视自身并创建下一个 Sprint 改进计划的机会。是对方法论的回顾和提高。项目复盘会也应该从这两个角度去做总结提高。

相关阅读：39 | 项目总结：做好项目复盘，把经验变成能力

邢爱明：
回想一下，项目复盘想要效果好，需要做一些准备工作:

复盘会前，要求项目组核心人员对项目的情况先自己进行总结，包括做得好和做的不好的方面，有书面文件输出。先要有思考，复盘会上大家才有可讨论的内容，否则会议上大家可能就是随便说说，复盘会成了走形势。

复盘会的会议主持人，需要有比较强的会议主导能力，尤其是参加会议的人来自多个部门的时候。因为大家总结项目中做的不好的地方，难免会涉及到多个部门或团队配合的情况，且每个人的描述也不可能做到百分之百的客观和公正。

如果有人认为总结的内容有问责的含义或需要自己承担责任，复盘会就很容易变成了甩锅会。这时候就需要会议主持人正面介入和引导，让大家讨论解决方案和改进措施，确保按照预定的议程开复盘会议。

相关阅读：39 | 项目总结：做好项目复盘，把经验变成能力

思辨时刻

成：
我们公司团队小，每次 app 开发完成后，要求测试人员组织开发全体测试 2 次，用于保证质量。团队小测试人员技术有限，性能，安全等一般难以保证。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：
其实即使是小团队，也应该加大对自动化测试对投入，绝对是磨刀不误砍柴工，这样 App 开发完成后，很多测试就可以自动化完成，节约时间和人力。当然在没有自动化测试的覆盖的话，这也是很好的一种测试方式。

相关阅读：31 | 软件测试要为产品质量负责吗？

毅：
项目负责人为软件质量总责任人。功能，代码，过程都要关注，并不一定要亲力亲为，因为除了质量他还要兼顾范围、时间和成本。提升质量意识最理想状态是组员有质量人人有责的意识与行动，但实际上这很难。如果自下而上做不到，就自上而下用制度强推，有奖有罚。

最后补充一点就是推行质量保障是需要公司层面作为支持的，否则在推行过程中会有不少阻力，也许在强人项目经理的推动下，个别项目能做的很好，但心会很累。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：
确实还要考虑金三角的因素。软件项目，也并非一定要有强人项目经理，其实只要按照软件工程，踏踏实实做好每一个环节，质量就不会差到哪去。

比如说在需求上多花点时间精力，把需求确认清楚，这就成本一半了，然后再基于确定的需求做好架构设计再开发，最后开发后做好测试，那么质量就有了基本保障了。

相关阅读：31 | 软件测试要为产品质量负责吗？

邢爱明：
谁来做测试工作，这也是我一直比较疑惑的地方。我现在是甲方，主要做的是企业管理软件，业务逻辑和流程控制都比较复杂，部分系统是需要一些领域的专业知识。

软件开发的时候，基本采用的是瀑布模式。首先由专门的人员做需求澄清，分析和设计，一般我们称为业务分析师，他们这些人会输出软件的需求规格说明书，包括软件原型、详细说明 word 文件，然后交给开发团队进行功能设计、开发和交付。

在这种模式下，是否需要专职的软件测试人员，有两种意见。

第一种，不需要测试人员，原因是业务逻辑复杂性，找一个普通的外部测试人员进来，还需要花较长的时间去了解需求，学习如何操作一些专业的应用软件，还需业务分析师花费宝贵的时间去做辅导学习。如果不让测试人员学习，就只能测试一些很简单的功能，对把控整个软件交付的质量作用非常小。解决方案就是让业务分析师兼职做测试工作，因为对需求本身非常清楚，学一点测试基础知识，在付出点加班时间，也是能完成功能测试工作的。

第二种，需要专职的测试人员，因为上一种方案中，需求分析师大部分做的还是正向测试，即按照自己设计的功能和流程，判断软件交付是否合格。但是对异常场景的测试还是比较少的，会导致软件上线后，在用户实际操作过程和设想的不同的时候，往往会出现一些功能异常，给用户的直接感受就是软件不稳定。所以说希望通过专业的测试人员，多采用一些探索式的测试方法，尽量多地发现软件中存在的缺陷，提升交付质量。

哪种方案更加合理一点？

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：
我的观点是这种情况下需要专职测试的，业务分析师的重点应该是把需求文档写清楚。

业务复杂不能成为一个借口，想想看开发人员是怎么理解需求的，难道也是业务分析师代劳？肯定也是由业务分析师写成需求文档，然后开发人员基于文档开发，当然中间少不了很多确认环节。

测试也是类似，应该专业的人来做比较好，可以有更好的测试覆盖。一开始肯定是难一点，但是一段时间业务熟悉后，会极大提升整个团队的测试效率，而你也不需要再为这个问题纠结了。

相关阅读：32 | 软件测试：什么样的公司需要专职测试？

kirogiyi：
对于 Devops 我只是听说过，并没有具体的去了解过它的使用和应用场景。根据宝玉老师的讲述，Devops 的基础是自动化，那么自动化之外好像更多的是一种概念，可以因环境而产生各种不同的方式和方法，并没有比较明确的定论。感觉就像敏捷开发一样，满足敏捷宣言思想的操作都可以是敏捷开发，最终适合自己或团队的才是最好的。

宝玉（加微信：642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。）：
自动化确实没有明确的定论，重要的是得要有应用自动化的意识，让自动化变成你项目开发流程的一部分，从而提升效率、改进质量。

应用自动化本质就是应用工具和基于工具二次开发，常用的自动化工具比如说自动测试框架、持续集成、日志监控报警。这些都是基础工具，还需要针对自己项目的环境，基于工具提供的 API，去定制化的写配置脚本，让它可以适合你的项目。

最重要的还是要把这些工具整合到你的开发流程中，比如说：

当你提交代码的时候，持续集成能帮你自动运行自动化测试脚本，可以直观看到测试结果，根据结果再决定是否合并或者继续修改；

当你合并代码后，持续集成能帮你自动化部署到测试环境，并且构建生成生产环境的部署包，甚至帮你自动部署生产环境。

当你要部署的时候，通过自动化的脚本直接部署到生产环境，而不需要手工去干预太多，避免人为因素的失误。

当你部署上线后，通过日志监控报警系统能实时看到部署后的数据变化，及时发现问题。

这样的流程其实是比较通用的、大部分项目都是适用的，只是前期需要投入一定的时间精力去研究和搭建，但是搭建好了这样的整套自动化环境和建设好了相应的开发流程，相应的从效率和质量上的回报也是很大的。

相关阅读：36 | DevOps 工程师到底要做什么事情？

好，今天的加餐就到这里，非常感谢同学们用心的留言，也希望我们专栏的同学都能每日精进，学有所成。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

40 | 最佳实践：小团队如何应用软件工程？

你好，我是宝玉。经过前期理论知识的学习，你可以开始尝试在实践中去应用所学到的软件工程知识了。

想象一下，现在你要加入一家创业公司，从头组建一个开发团队，一开始只有三五个人，你会怎么去应用软件工程的知识，让你的团队能高效率、高质量地开发软件产品？

或者说，你现在就是在一个小团队，各种流程不规范，开发效率低，软件产品质量不高，你打算怎么去应用学到的知识去改善现状呢？

在这一篇里，我将带你一起运用学过的软件工程知识，看如何在小团队中应用软件工程？（在这里我补充说明一下：本文讨论的小团队，不是指大厂的一个小组，而是小公司或者三五个人的小开发团队）

小团队在软件开发中存在的常见问题

不知道你有没有在小团队工作的经历，如果有的话，建议你可以自己先总结一下：小团队在软件开发中存在的一些常见问题是什么？

为什么说你需要先自己去发现问题呢？因为在学习完软件工程的理论知识后，并不是说你把所有知识点一股脑儿全应用上就解决问题了，而是要先去发现问题在哪，然后针对这些问题，再去应用软件工程的知识去寻找问题的解决方案。

就像小团队如何应用软件工程这个问题，你首先要先找出来小团队的问题在什么地方，然后去分析这些问题可以应用软件工程的哪些知识，从而找到适合你的解决方案。

我个人有过一段时间的小团队工作经历，也见过很多类似的小团队的开发，就我的经验来说，小团队在软件项目开发上，主要问题体现在以下几个方面。

1. 小团队成本敏感

首先，小团队对成本都很敏感，成本是小团队很多问题的根源，对成本的控制也衍生出一系列大公司可能感受不到的问题。

因为控制成本，所以开不出好的薪水，难招到优秀的程序员；因为控制成本，所以进度都催的紧，毕竟多干一天就要多发一天工资；因为控制成本，舍不得在工具上的投入，都得要尽量用免费的、开源的；因为控制成本，通常几个项目并行，一个人可能要同时在几个项目中切换。

2. 小团队人少活多

小团队人一般不会多，但是活不一定少。

从分工上来说，通常在大厂前端后端几个人合作完成的事，在小团队就得一个人从前端写到后端了，可能甚至都不会有专业的产品设计和功能测试人员，都是开发兼任。

从人员构成来说，大厂在组建技术团队时会注意梯队的搭配，整个团队像金字塔的结构，顶部有几个特别资深的开发人员，中间有一些丰富经验的，底部的是有潜力但经验比较少的。而小团队就算是运气好，也可能只有一两个技术大牛，更多的是水平一般、经验比较少的。

这样的分工协作和人员构成，导致的问题就是大家每天都很忙，但是感觉技术上积累有限。对个别技术大牛的依赖性强，他们一旦离职，影响非常大。

3. 小团队缺少流程规范

在流程规范方面，恐怕是大家对小团队吐槽最多的地方，也是很多从大厂跳槽到小公司的程序员特别不适应的地方。

项目开发比较随意，拿到需求可能就开始直接写代码了，没有严格的需求分析、架构设计，写完了后简单测试一下就上线了，上线后再修修补补；需求变更是家常便饭；多个项目并行的时候，每个项目的负责人都觉得自己的项目是最重要的，希望你能把他的项目进度往前赶一赶；老板权力很大、想法多变，经常会直接干预项目。

这样不规范的开发流程，导致的结果通常就是开发效率低下，软件产品质量不高，项目计划难以遵守甚至没有计划。

小团队如何应用软件工程？

成本敏感、人少活多、缺少流程规范，这几个是小团队在项目开发中存在的主要问题。那么在小团队应用软件工程的时候，我们就需要去解决好这些问题。

成本敏感的问题，如果这个是客观存在的，就没有太好的办法去解决，只能说我们在做一些决策、制定流程的时候，需要充分考虑好成本因素，减少浪费。

人少活多，那么我们就相应地提升个人和团队的整体水平和效率。缺少流程规范，那么我们就建立适合小团队特色的流程规范，让开发流程规范起来。

所以接下来，我就从团队建设、流程建设这两个维度来谈谈如何应用软件工程。

1. 团队建设

也许你会觉得好奇，软件工程的各个知识点，都是在讲过程、方法、工具，似乎都没有讲人的，但为什么在实践的时候，反而最先考虑的却是团队建设？

但你要换个角度想就很容易理解了：软件工程上讲的所有的过程、方法和工具，最终还是落实在人身上，需要人去基于开发过程去制定流程遵守流程；需要人去应用软件工程中总结好的方法；需要人去使用工具。如果团队对软件工程缺少认识，那再好的方法和工具也无法落地。

所以要实施好软件工程，也要同步做好团队建设，让你的团队有一点基础的软件工程知识积累，有几个技术骨干可以帮助一起推广和实施。如果对软件工程知识的推广能扩大到团队之外，比如你的老板和业务部门，那么在后续推进一些流程规范，会起到事半功倍的效果。

团队建设，绕不开几件事：招人、培养人、管理人和开除人。

小团队如何招人

小团队招人，难点在于成本有限，开不出很高的工资，品牌也不够吸引人，招人的时候相对选择有限，能否直接招到技术大牛就得看运气了。但这不意味着就要大幅降低标准，比较现实的方法就是招有潜力的程序员培养。

那么怎么知道候选人是不是有培养潜力呢？可以参考我们专栏《27 | 软件工程师的核心竞争力是什么？（上）》这篇文章，考察候选人的学习能力、解决问题能力。

我以前在创业团队时，每年会招不少实习生，然后对实习生进行培训，参与实际项目，最后留下来一批优秀的有潜力的实习生，在一两年后，就能成长的不错，能独立完成小型的项目。

但我在这种方式上也犯过错误，就是新人的比例太高，中间断层，日常的技术指导和代码审查一度跟不上，导致代码质量低下。所以在招人时，也不能一味节约成本，还要注意梯队的建设，中间要有几个有经验的技术骨干帮助把控好代码质量。

小团队如何培养人

在培养人方面，相对来说，小团队不像大公司有完善的培训制度，资源也有限，难以请到外面的人来讲课，所以培养人主要还是要靠内部形成好的学习分享的机制。

在大厂，新人加入，通常会指定一个 Mentor，也就是导师或者师傅，可以帮助新人快速融入环境，新人有问题也可以随时请教。这种师傅带新人的机制其实对小团队一样适用，对新人来说可以快速融入，及时获得指导，对于师傅来说，通过带人，也能促进自身的成长。

除了有师傅带，新人的技术成长，更多还是来源于在工作过程中不断实践和总结，在这个过程中，及时准确的反馈很重要。软件工程中，像代码审查、自动化测试、持续集成都可以帮助对工作结果进行及时的反馈。

代码审查，可以帮助团队及时的发现代码问题，也能促进团队相互学习，代码风格统一；自动化测试，可以对代码结果马上有直观的反馈，有问题早发现修正；持续集成也是通过频繁的集成频繁的给出有效反馈，及早发现代码问题。

在小团队推行这样好的开发实践，让团队获得及时准确的反馈，有助于整个团队的成长。

另外，内部的技术分享也是很好的共同提升的方式，对于听的人来说可以学习到一些新鲜的知识，对于分享的人来说，准备一个技术分享，本身就是最好的学习总结方式。我以前在团队会定期组织这样的技术分享，不止我自己，每个团队成员都会去分享，整个团队分享讨论的技术氛围形成的很好。

还有在分工方面，不要因为一两个技术大牛能干，就把大部分工作都让他们做了，这其实对团队整体是不利的，“大牛”的发展也遇到瓶颈，而其他人缺少锻炼。所以最好是让“大牛”一半的精力负责一些重要的像架构设计、框架开发的工作任务，同时还要有一半的精力在代码审查、带新人等方面，帮助其他人一起成长，整个团队的发展才能更健康。

小团队如何管理人

因为小团队人数不多，对人的管理上，可以不需要像大公司一样用复杂的组织结构，用复杂的管理制度。小团队的管理，核心在于营造好的氛围，鼓励成员自我驱动去做事。

其实这个理念和敏捷开发的理念是吻合的。在专栏文章《05 | 敏捷开发到底是想解决什么问题？》中，我也提到了：敏捷开发的实施，离不开扁平化的组织结构，更少的控制，更多的发挥项目组成员的主动性。

要鼓励团队自驱动，具体做法上也可以参考敏捷开发的一些做法，比如说通过任务管理系统和看板，让团队成员自己领取开发任务；在制定一个迭代的计划的时候，让团队成员一起参与对任务的打分，参与计划的制定。

除了这些鼓励成员自驱动，发挥主动性的做法，在营造好的团队氛围上，还要注意的就是遇到线上故障、进度延迟这些不太顺利的情况，更多的是提供帮助，一起总结复盘，而不是甩锅问责。

有关开除人

在应用软件工程的时候，团队中可能有些人会成为障碍，要么是能力不足无法落实，要么是态度有问题抵触软件工程的实施。

在这种情况下，首先对于有问题的成员肯定是要努力挽救，如果是能力不足，就给予帮助，给时间成长，对于态度有问题的，明确指出其问题，限期改正。但如果最终结果还是达不到预期的话，那就必须要果断地将这些成员淘汰。

2. 流程建设

小团队被人诟病较多的地方就是在于流程规范的缺失，但像大公司，流程规范繁多，也容易造成效率低下，人浮于事的情况，这也就是为什么现在大公司的开发团队也在分拆，从大团队拆分成小组，精简流程规范。

对于小团队，一开始也不宜有太多的流程规范，不然，如果流程不合适反而会成为一种束缚，最好只是先设置最基本的流程规范，然后在实践过程中针对团队特点和业务特点去逐步完善。

那么哪些流程是软件开发中最基本的流程规范呢？

选择适合你的软件开发模型

现在的软件开发，已经不再像以前那样采用原始边修边改的开发模型，而是应该采用科学的开发模型。我们专栏一开始就有大量的篇幅介绍各种开发模型，大的方面有瀑布模型和敏捷开发，基于瀑布模型还有很多衍生模型。

那么小团队应该采用哪种开发模型比较合适呢？

也许你会认为应该采用敏捷开发。敏捷开发确实是一种非常适合小团队的开发模型，整个开发过程非常有效率。如果能采用敏捷开发是最好的。

但需要注意的是，如果你的团队是以瀑布模型为主，大家都有丰富的瀑布模型开发经验，但是对敏捷开发都没有实践过，对于敏捷开发的各项活动还不熟悉，还没能充分理解敏捷的价值观和原则，那么最好不好贸然直接换成敏捷开发。

因为这样做的话，团队在一段时间内，都需要去摸索如何用敏捷开发，可能反而会降低开发效率。

对于团队只熟悉瀑布模型这种情况，有条件的话，聘请外部的敏捷顾问帮助实施敏捷开发是个不错的选择。如果条件有限，可以先尝试逐步借鉴敏捷开发中好的实践。

敏捷开发中哪些实践是适合小团队借鉴的呢？

首先在开发周期上，应该缩短交付的时间，使用快速迭代的开发模型。因为小团队的一个特点是需求变化快，要求交付的速度快，那么快速迭代或敏捷开发就是一个合适的开发方式。即使团队习惯了瀑布模型开发，切换到快速迭代也会比较容易，只需要把大瀑布拆分变成小瀑布。

具体在实施上，可以缩短并固定开发周期，比如说每 2～4 周可以发布一个版本。在做迭代的规划时，优先选择当前最核心最重要的功能；在一个版本内，不轻易接受新的需求变更，有需求变更放到下一个迭代中；在迭代时间结束了，无论新功能是否开发完成，都按时发布新版本，没完成的放入下一个迭代。

通过这样的变化，可以保证在一个迭代中整个团队的开发状态是稳定的，不需要受到需求变更的干扰，也可以慢慢形成适合团队的迭代节奏。

另外在会议上，敏捷 Scrum 的几个会议也可以借鉴，像每日站立会议，可以帮助团队及时了解项目进展，解决进度上的障碍；每个迭代的计划会议，可以让大家一起参与到计划的制定中；每个迭代的验收会议，可以让业务部门、老板及时的验收工作成果，看到大家的工作进展；每个迭代的回顾会议，可以帮助阶段性复盘总结，不断优化开发流程。

还有基于看板的任务可视化，也可以帮助团队直观的看到当前迭代中的任务进度，可以主动选取任务，而不需要去问项目经理下一步该做什么。

以上这些内容，也可以参阅专栏文章《06 | 大厂都在用哪些敏捷方法？（上）》，里面有更详细的解释。

构建基于源代码管理工具的开发流程

很多小团队开发质量低，开发混乱的一个原因就是没有使用源代码管理，也没有一套基于源代码管理的开发流程。在专栏文章《26 | 持续交付：如何做到随时发布新版本到生产环境？》和《30 | 用好源代码管理工具，让你的协作更高效》中，对于如何基于源代码管理工具构建和开发已经有了非常详细的介绍，这些开发流程一样适用于小型团队。

有一点要注意的是，小型团队完全没有必要自己去从头搭建自己的源代码管理工具、持续集成工具，应该尽可能采用在线托管的服务，这样可以节约大量搭建、维护工具的人力和时间成本。

类似的策略也应体现在技术选型上，小团队应该尽可能使用现成的工具、框架，而避免自己造轮子，把主要精力放在业务功能的开发上面。

建立外部提交需求和任务的流程

小团队在流程规范上混乱的一个体现是，业务部门包括老板对于提交开发任务非常随意，可能直接找某个开发人员私下让改一个需求，增加一个功能，导致开发人员不能专注于任务开发，经常被打断。还有多个项目并行而资源又紧缺的情况下，每个项目负责人都觉得自己的业务是最重要的，希望能尽快完成。

如果你有过在火车站售票口排队买火车票的经历，你会发现，无论人有多少，只要大家有序排队，售票窗口就能按照先后顺序为大家服务，如果大家一窝蜂挤上去买，就会乱成一团，如果有人插队，那么其他人的进度就会受影响。

其实软件项目开发也是类似的，对于开发团队来说就像是售票窗口，买票的人就相当于一个个的开发任务，无论开发任务有多少，只要你将这些开发任务排成队列，就可以有序地解决。如果一个业务团队的开发任务特别紧急要插队，那么意味着其他业务团队的任务就必须要受影响，那么就需要大家一起去协调。如果你不去通过流程规范任务，那么任务一多，必然就会乱成一团，无论是开发团队内部还是外部，都不会满意。

建立外部提交需求和任务的流程，可以参考专栏《14 | 项目管理工具：一切管理问题，都应思考能否通过工具解决》的内容，让所有人都基于任务跟踪系统去提交需求和开发任务，所有任务都先进入 Backlog（任务清单），然后在每个开发迭代中，去按照优先级选择当前迭代的任务，如果有优先级的冲突，应该需要事先沟通解决。对于提交需求和任务的人，也能通过任务跟踪系统，及时的了解到任务的进展。

在团队之外推行这样的流程是会有一定阻力的，最好是能事先去找几个关键的业务负责人私下沟通，取得理解和支持，让他们知道这样做对他们的好处，比如说可以更好地跟踪任务的进展，让开发效率更高，更好地为他们完成任务。

以上这几个流程，就是在小团队的软件开发中应用软件工程，需要建立几个最主要的的流程，把这几个基础流程建立起来后，就可以帮助小团队的开发，从无序逐步进入有序。

总结

今天，我带你一起分析了小团队在软件项目开发上的主要问题是：对成本敏感、人少活多和缺少流程规范。相应的，我们就需要从团队建设和流程建设两个地方入手，去解决这些问题。

在团队建设方面，需要从四个方面入手：招人、培养人、管理人和开人。

招人的时候，找一些有潜力的培养，也要注意梯队建设，中间有技术骨干补充；

对团队的人才要悉心培养，通过给新人安排师傅的方式培养新人，日常注意代码审查，内部技术分享是个不错的共同提高的方式，技术高手要注意不只是闷头干活，也要承担一定的带人的工作；

管理人核心在于营造好的氛围，鼓励成员自我驱动去做事；

对于不适合团队的人也不要手软，及时的淘汰。

在流程建设方面，要着重建设好三个方面的流程：

选择合适的软件开发模型，建立项目开发流程；

构建基于源代码管理工具的开发流程；

建立外部提交需求和任务的流程。

团队建设和流程建设是在小团队中应用软件工程的关键，通过团队建设让团队成员有共同的软件工程意识，有实施软件工程的基础，通过流程建设让软件工程好的实践流程化、工具化。

课后思考

你有小团队的项目经历吗？你觉得小团队面临的主要问题是什么？你觉得可以从哪些方面在小团队中应用软件工程？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

41 | 为什么程序员的业余项目大多都死了？

你好，我是宝玉。对于不会写程序的人来说，想做一个软件项目，就得找程序员帮忙写程序。而对于程序员来说，想做一个软件项目，写程序不是问题，产品设计自己也能做一点。所以对于很多程序员来说，一旦有了一个想法，可能就会利用工作之外的时间，做点自己的业余项目（也叫 Side Project）。

然而大部分项目，都是怀着美好的期望开始，结果做到一半就无疾而终，就算少数坚持到了上线发布，最终还是因为少人问津而不得不放弃。

所以今天将带你一起分析一下，为什么程序员的业余项目大多都死了？怎么样可以借助学习到的软件工程知识提升业余项目成功的概率？

为什么程序员的业余项目大多都死了？

作为程序员，我也很热衷于做业余项目，周围的程序员朋友们也有不少做业余项目的案例，通过对这些案例的观察和分析，我觉得程序员做的业余项目，主要死于以下这些情况。

1. 想法大，时间少

我有个朋友，前一段突然有了一个想法，想做一个类似于 Excel 的基于网页的在线电子表格程序，这是个很大的想法，毕竟微软和谷歌都是有一个团队在完成这样的项目。

但他觉得如果只是实现最核心功能还是可行的，于是激情满满地找资料，写原型代码。然而现实还是很残酷的，他上班就忙，经常加班，下班还要带娃，留给自己的时间其实不算多，一段时间看不到成果后，慢慢的激情就消逝了，这个项目也就不了了之了，而现在他已经又在尝试其他项目了。

这是一个常见的现象，很多程序员在业余做项目开始之前激情满满，经过一段时间没有进展，没有正向反馈，很容易就激情消逝，不想再继续了。尤其是一段时间后，可能又有新的项目想法了，于是就又开始了一个新的循环。

2. 过于追求技术，缺少约束

在公司的项目中，我还是比较保守的，毕竟要受限于项目的成本、时间和范围的限制，而且有 Dead Line 的强约束，所以不会太激进，能稳定上线运行是第一位的。而我一旦去做业余项目，就会陷入过于追求技术的困境。

我前年的时候，想对自己的一个网站进行升级，如果用传统的熟悉的技术方案应该不需要太长时间，但我想体验当时比较新的 React Universal 技术，后端 API 要使用当时时髦的 GraphQL，要考虑多数据库的支持，还要用上 WebSocket 保持内容及时更新。结果有些知识还需要边学边用，虽然在学习这些知识的时候收获不小，但是项目进度确是惨不忍睹，到今天还只有一点雏形。

程序员的业余项目，因为缺少成本、时间和范围的限制，没有设置 Dead Line 约束，所以经常会天马行空，只为了追求技术上的兴奋点，恨不得把新酷技术都用上。

但如果看看项目的定义：

项目是指一系列独特的、复杂的并相互关联的活动，这些活动有着一个明确的目标或目的，必须在特定的时间、预算、资源限定内，依据规范完成。（摘自百度百科）

你就可以发现，项目是要有目标要有约束的。一个缺少目标和约束的项目，是难以成功的。

3. 缺少产品能力和运营能力

有一些程序员，是有做产品的梦想的，希望能打造一款好的产品，解决工作生活中的一些问题，或者就是想通过做产品去赚点钱。其实这样的不乏成功案例，比如像业余时间打造出知名的 iOS 应用 Pin 和 JSBox 的钟颖，还有像图拉鼎这样的人，他们先是程序员，后来因为有了成功的产品而做独立开发者的例子。

但这样的例子并不多，普遍存在的情况是，当程序员们真正要去打造产品的时候，却发现要做一个产品并不是那么容易的事情，缺少产品能力就无法设计出好的产品，缺少运营能力就算产品做出来也鲜有人问津。而那些真正成功的独立开发者，无不是能兼顾产品设计能力和产品运营能力，既能设计出真正解决用户需求的产品，又能通过一定的运营让用户了解产品，为之买单付钱的人。

怎样提升业余项目成功的概率？

想法大，时间少；过于追求技术，缺少约束；缺少产品能力和运营能力。这几点是程序员业余项目失败的主要原因。如果要应用软件工程知识来寻找这些问题的解决方案，你会想到什么方案呢？

1. 怎么样让项目不至于半途而废？

想法大，时间少怎么办？怎么样让项目不至于半途而废呢？

回想一下专栏文章《08 | 怎样平衡软件质量与时间成本范围的关系？》中的金三角理论，在这一篇中，我解释了如何去平衡软件质量与时间成本范围的关系，今后你会发现项目中很多问题都能应用到金三角的理论。

比如说，想法大，其实就是范围大，按照金三角的理论，你要去固定一条边或者两条边，然后去调整剩下的边。

对于业余项目来讲，其实时间是很难去控制的，也就是你不可能像每天上班那样投入大量的时间在上面，所以这条边要被固定起来。

然后看成本这条边，虽然理论上来说你可以花钱请人帮你，也可以花钱买成熟的商业组件，但作为一个业余项目，一般来说前期不会投入大成本的。你也可以假定它是固定的。

那么最适合调整的边就是范围这条边，毕竟作为一个业余项目，你可以先实现最核心的功能。可以采用 MVP（minimum viable product，最小化的可行性产品）的模式，一开始只推出最核心的功能，满足用户最核心的需求，然后在用户的使用过程中收集反馈，进一步升级迭代。

前不久一个朋友做了一款播客的应用，他就是采用的 MVP 的开发模式，先快速发布了一个只有核心功能的版本，甚至还很多 Bug。发布后邀请了几个朋友试用，收集了反馈，并且也把发现的 Bug 修复了，再逐步增加新功能。这样几个迭代后，他的 App 已经登上了新闻分类的排行榜。如果一开始他就想的是要做一个很大的项目，也许到现在还在开发中呢。

即使程序员做的是业余项目，还有必要补充的一点就是：在决定做什么项目之前，一样要充分考虑项目的可行性研究。关于这一点，你可以参考专栏文章《09 | 可行性研究： 一个从一开始就注定失败的跨平台项目》去从经济可行性、技术可行性还有社会可行性方面，去分析一下项目是不是真的可行，再动手不迟。

2. 怎么避免陷入过于追求技术，项目难以交付的困境？

过于追求技术，缺少约束是程序员业余项目失败的另一个主要原因。

程序员追求技术是天性，这一点其实也不是坏事，重点是要有所约束，毫无约束的结果就是迷失在技术中，而忘记了项目的整体。

这其实也是我在专栏一开始就写的《02 | 工程思维：把每件事都当作一个项目来推进》中提到的，要把业余项目也当作一个正式的项目，做你的业余项目时，也要站在项目的整体去思考项目的进展，而不是沉迷于局部的技术实现。

所以你有业余项目的话，也要像专栏文章《11 | 项目计划：代码未动，计划先行》中提到的那样，去做项目计划，去设置里程碑。还要敢于把计划和里程碑分享给你的家人和朋友们，公开的做出里程碑的承诺，让他们帮助监督你的计划执行。

当你有了一个可行的计划，有了真正的 Dead Line，你的项目交付就有了基本的保障。

我前些年运营过网站，一个针对我的母校西北工业大学校友们的论坛网站叫开放实验室，我需要负责这个网站的日常运营和程序开发，所以每次升级之前，我都会在论坛发帖子公布我的升级计划，设定一个上线时间，这样网站的用户会监督我的项目进度。有了进度的压力，就会逼着我必须按时完成，而不是老想着用什么新酷的技术。

在你的业余项目难以交付的时候，记住一句话：Dead Line 就是第一生产力。

3. 怎么弥补你的短板？

产品能力和运营能力是大部分程序员的短板。

关于产品设计，我们专栏需求分析篇的几篇文章可以参考。《17 | 需求分析到底要分析什么？怎么分析？》可以帮助你去分析用户真正的需求是什么，从而让你可以做出来用户想要的，而不是自己凭空想象出来的用户需求。

产品能力的锻炼不是一朝一夕就能炼成的，但日常多模仿，多实践，还是能做出来不错的产品设计，我在专栏文章《19 | 作为程序员，你应该有产品意识》中如何培养产品能力上，给出了一些具体的建议。

比如说你可以从解决自己的需求，解决家人朋友的需求开始，设定一个小的产品目标，然后借鉴类似的产品，模仿它们的产品设计、交互设计，就能做出来一个基本可用的产品。

像 UI 设计，其实现在无论是网站的 UI 设计还是 App 的 UI 设计，都趋向于标准化，对于一个业余项目，使用一些标准模板，或者花点钱购买一套漂亮的界面模板，都是不错的选择。

对于产品的运营，这一点很遗憾，软件工程重点是讲如何做项目的，并没有太多运营相关的知识。我个人的一点经验就是，如果你要运营一款产品，你需要想清楚以下几个问题：

想清楚你的产品能给用户带来什么样的价值？帮助用户解决什么问题？

商业模式是什么？也就是用户是不是会为你的产品付钱？或者你的产品通过什么方式赚钱？

如何让用户知道你的产品？如何让用户知道你产品所能带来的价值？

只有想清楚了你的产品的核心价值是什么，才好去针对性的运营你的产品。具体产品的运营上，可以找你的朋友作为第一批用户，然后去像Product Hunt这样的网站发帖子自荐，还可以通过微博、Twitter 这样的社交媒体宣传。

除了自己去学习产品知识和运营知识之外，其实还有一种方式，就是组建一个小团队，找到志同道合的人一起，你写程序，有人做产品设计，有人负责运营推广，大家取长补短，一起把产品做好！

总结

今天带你一起分析了程序员的业余项目失败的原因。想法大，时间少；过于追求技术，缺少约束；缺少产品能力和运营能力。这几点是程序员业余项目失败的主要原因。

针对想法大、时间少的问题，可以借助软件项目金三角的理论，去缩小范围，在做项目时，可以采用 MVP 的开发模式，先实现核心需求，再逐步增加功能。

针对过于追求技术、缺少约束的问题，应该要对你的项目制定计划，设定里程碑，把时间点告诉你的家人和朋友，让他们监督你执行，通过 Dead Line 来保障项目的进度。

针对缺少产品能力和运营能力的问题，需要有针对性地去学习相关知识，也可以去组建小团队，弥补这些方面能力的不足。

最后，即使程序员们的业余项目很可能会是以失败告终，我做过很多失败的业余项目，但我还是强烈的建议你多尝试做一做业余项目。因为做业余项目，即使项目失败了，一样可以让你收获很多：

通过业余项目，你可以学习和使用工作中不会使用的技术。你工作中做后端开发，你业余项目完全可以体验 iOS App 开发。

通过业余项目，你有机会去按照自己的想法去实现。很多时候在工作中，因为你无法去做决策，无法改变架构的设计或产品的设计，而在自己的业余项目中，你可以完全按照自己的想法去尝试，去证明自己。

通过业余项目，可以锻炼你的大局观和工程思维。当你真的去自己负责一个项目时，就会更多地去站在项目的整体去思考一个项目，而不是局限于专业领域。

通过业余项目，帮助你更好地在项目中沟通。在做过业余项目后，在工作中，和产品经理、测试沟通，你会更懂他们，因为他们的工作你也体验过了，你会体会到他们的工作其实不像你最初想的那么容易。

课后思考

你有做过业余项目吗？你有经历过哪些成功的和失败的业余项目经历？你从中收获的经验教训是什么？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

42 | 反面案例：盘点那些失败的软件项目

你好，我是宝玉。我想你日常一定看到过很多项目失败的案例，有些失败项目的案例甚至超出我们的想象，比如说我的朋友圈就被两个项目刷过屏，一个是号称史上最烂的开发项目，开发 12 年，六百万行代码；一个是美国联邦调查局的一个软件项目，花了 1.7 亿美元，最后变成了豆腐渣工程。

也许大多数人看完这类文章后，会当作一个有趣的故事，觉得他们软件工程水平太差了，居然会把项目做成这样。当你学习完软件工程知识后，再看到这些项目失败的案例，不妨从软件工程的角度来分析一下，这些项目失败的真正原因是什么？你能从中有什么启发？

什么样的软件项目算是失败的项目？

如果我们说一个项目是失败的项目，那么怎么算是一个失败的项目呢？

项目管理协会（PMI）认为成功的项目必须满足六个条件：

按时交付。

成本在预算范围内。

能按照当初的设计正常运行。

有人使用。

满足项目最初的目标。

项目出资方对项目满意。

相应的，如果上面有一个或者多个条件没有满足，那么项目就有可能是失败的，比如说：

没能按时交付。

成本超出预算。

Bug 太多，无法按照当初的设计正常运行。

产品没有得到市场认可，没有人使用。

产品偏移了最初的目标。

项目出资方不满意。

而那些特别失败的项目，往往是多个条件甚至所有条件都不能满足，并且时间、成本、交付结果跟最初目标都相差很大，无疑都造成了巨大的损失。

IEEE（电气和电子工程师协会）有一个专门的网页，把过去十年间，那些著名的失败软件项目，做了一个墓碑来展示，墓碑里的这些项目加起来的损失大约 700 亿美元。WikiPedia 上也有一个网页（List of failed and overbudget custom software projects）列出来那些损失严重的软件项目，也是惊人的数字。

（图片来源：Monument to Failure）

而这些软件项目的失败，很大程度上是可以预测和避免的。如果把问题简简单单归结为软件工程水平太差了，或者是项目实施者的水平太差了，那么我们就无法真正的从这些失败中吸取教训，在下一次还会再犯同样的错误。

分析失败软件项目的原因

在航空业，如果一架飞机坠毁，会有专业的调查小组去对飞机失事原因进行详细调查，比如分析说当时的天气情况、飞机的维护记录、飞行员的性格特点，平时受到的培训是怎样的，航空公司的文化，对安全的重视程度等等，从而找到事故的根源，并且提出相应的改进方案，避免类似的灾难再次发生。

软件项目其实也是类似的，对于一个失败的软件项目案例，要去分析：外部环境、技术管理、项目管理和组织文化，这样才能帮助你找到项目失败的根源。

外部环境

在调查员去调查飞机失事原因的时候，首先会看的是不是外部环境导致的，例如恶劣的天气环境。分析软件项目失败原因，也可以首先看看外部环境。

如果你去看看历史上那些有名的失败的项目案例，其中政府主导的项目占大多数，而且通常主要因素不是成本，而是各种政治因素导致的不切实际的项目进度，或者是频繁变更的需求，从而严重的影响了成本和质量。

而对于商业软件项目，很多是由于缩减成本导致的。因为商业竞争的大环境，企业为了节约成本，总是希望用更少的人做更多的事情。

还有一些常见的场景就是在一个项目开始之前，销售为了拿下项目，通常会过度夸大项目的成果，而又会相应的压缩项目预算、时间，并且也可能低估了技术实现的难度，最终项目要开发的时候，开发人员才发现根本无法如期完成当初承诺的项目目标，最终导致项目失败。

技术管理

在调查飞机失事原因时，调查完外部环境，还要分析是不是飞机本身设计原因导致的，比如前不久的波音 737 MAX 飞机事故，就是因为软件故障导致的。类似的，分析软件项目失败原因，也一样要去分析技术管理上的问题，很多软件项目失败的原因也是技术原因导致的。

比如说在项目中使用了不成熟或不熟悉的技术，最终导致技术不可控，或者浪费大量的时间在技术的学习上。

项目的规模也会导致技术复杂度直线上升，想象一下，做一个普通的个人网站和做一个淘宝这样的网站，复杂度不可同日而语。通常越大的项目，技术越复杂，需要考虑各种软件硬件的交互，服务之间的耦合。也就是说，项目规模越大，失败的概率也更大。

项目管理

调查飞机失事，飞行员是重点调查对象，因为飞行员直接决定了飞机是否能安全行驶。对于软件项目来说，项目经理在软件项目中起着至关重要的作用。很多项目失败不是因为外部环境导致的，也不是技术原因，而是因为糟糕的项目管理。

在一个软件项目中，项目经理掌握了资源的分配，还要制定项目的计划，对任务进行分配，组织分工协作，管理风险，项目成员的日常沟通等等。而这些决策通常很难量化，需要基于当时的情况进行权衡，一旦这些决策出现大的失误，就会导致项目的失败。

组织文化

在飞机失事后，调查人员调查的最后一个领域就是所属航空公司的文化环境，看航空公司是不是足够重视安全。在软件项目中，一个开放、平等、注重沟通协作的团队或组织更容易及早发现和解决问题。

就像文章开头提到的美国联邦调查局的项目，当有雇员指出来项目中的问题，最后的结果竟然是被扫地出门。

当然，我们在分析盘点那些失败的软件项目时，从多个方面去分析，就是为了能找出这些项目失败的根本原因，从而避免类似的错误再次发生。

盘点那些失败的软件项目

接下来，我们来一起盘点几个著名的失败的软件项目，看看这些案例可以给我们的日常开发带来哪些启示。

在分析这些案例时，我会先分别从外部环境、技术管理、项目管理和组织文化这几个方面去分析问题和原因，最后一起总结从这些案例中收获的经验教训。

案例 1. 来自地狱的项目

案例描述：

这个案例来自法国政府，当时参与项目的一名项目成员专门为这个项目开了一个博客叫ProjectFailures，将这个项目描述为来自地狱的项目。原计划 2-3 年开发，结果干了十几年都没有完成，最终以项目负责人被以欺诈罪关进监狱而告终。详细内容可以查看中文版本：《开发 12 年 整整 6 百万行代码：史上最烂的开发项目长这样》。

案例分析：

外部环境：法国政府官员腐败，对于项目进度并没有施加压力；

技术管理：没有好的开发实践，完全 C++ 开发，600 万行代码，版本控制一团糟；

项目管理：糟糕的项目管理，团队成员 55 人，35 名经理，20 名开发人员，管理人员比开发人员还多；不断开会，只是展示 PPT；

组织文化：禁止超过 9 点打卡，禁止喝咖啡等奇葩要求。

案例 2. 美国联邦调查局虚拟案件文档系统

案例描述：

FBI（美国联邦调查局）虚拟案件文档系统的项目开始与 2001 年，项目初始目标是 3 年内将原有的 FBI 案件文档管理系统升级，但因为 911 恐怖袭击事件爆发，项目目标从升级变成了重写。最终 2005 年项目宣布废弃，而此时已经在这个项目上花费了 1.7 亿美元。有关项目的细节可以参考：《著名豆腐渣软件项目：美国联邦调查局虚拟案件文档系统》。

案例分析：

外部环境：FBI 没有真正懂技术的负责人领导和管控项目，对承包商缺少控制；

技术管理：无法解决项目的复杂性，系统在设计上不完整，不充分，不到位，以至于在现实场景中完全无法使用，上线前没有测试；

项目管理：开发方和客户之间沟通不畅；频繁需求变更，项目管理混乱，外行领导内行；

组织文化：指出问题的雇员反而被调查和开除。

案例 3. 微软 Vista 项目

案例描述：

微软的 Windows Vista 项目开始与 2001 年 7 月，预计 2003 年发布。比尔盖茨为 Vista 提出了三大目标：1. 完全使用 C# 提升开发效率；2. 使用数据库作为新的文件系统 WinFS；3. 使用全新的显示技术 Avalon （后来改名为 WPF），打破桌面软件和网站的用户界面界限，提升微软竞争力。

目标非常好，但技术难度非常大，结果三年后也未能开发完成，不得不在 2004 年对目标进行调整：不用 C#、取消 WinFS、删改 Avalon ，一开始的三大目标就这样被完全否决，最终 2007 年才发布 Vista。参考文章：《五年磨砺: 微软 Vista 开发过程全记录》。

案例分析：

外部环境：在目标的设定上，主要不是为了满足用户需求，而是为了商业上的竞争需要；

技术管理：技术上难度过大，超出团队控制范围，无法完成任务；

项目管理：比尔盖兹对项目直接干预较多，项目周期太长；

组织文化：盖茨制定目标后，核心团队明知困难，却不敢也没有反对，当看到任务无法完成时，他们不再努力工作，只想着如何推卸责任。

通过对这些项目的分析，再结合我们之前学习过的软件工程知识，其实软件工程对这些问题都有方案可以应对。

在设定项目目标的时候，如果真正的将可行性分析落到实处，那么像 Vista 这样的技术不可行的项目目标，也许一开始就可以进行调整，而避免后续更大的损失。

如果在项目开始的时候，有认真的对需求进行分析，和客户有很好的沟通，对于需求的随意变更有管理和控制，那么像 FBI 这样的项目，就有机会做出来满足用户需求的软件项目。

在项目开发之前，如果做了架构设计，做了技术选型，那么像法国政府项目、FBI 项目，也许可以有更简单可行的技术方案，要知道架构设计就是控制技术复杂的最好手段。

在项目开发的时候，如果做好版本控制，持续集成，自动化测试，那么像法国政府项目、FBI 项目，质量上就更有保障，不至于一测试全是问题。

在设置项目周期的时候，如果能缩短版本发布周期，尽快发布第一个版本，那么很多延期本可以避免或者不至于那么严重。想想看法国政府项目花了 12 年，如果他们在第一年内能先发布一个简单的版本，后续再逐步迭代，也许结果会完全不一样。

缩短项目周期也是微软在 Vista 项目上收获的一大教训，在 Vista 之后，微软的项目周期都大幅缩短，而且发布频率也大幅提高，每天都有内部测试版本发布。缩短周期后，可以尽早发布，尽早验证项目的可行性，也让测试可以尽早介入。

在团队的文化上，如果日常营造平等的、沟通协作的氛围，让项目成员敢于提出不同的意见，那么像 FBI、Vista 这样的错误也许可以早点被修正。

类似于这样的项目还有很多，比如有一本书叫《梦断代码》，讲述了一堆优秀程序员，一起开发一个大型的开源项目，最终如何走向失败的过程，有兴趣可以看看。邹欣老师对这本书也有非常独到的点评：《梦醒时分 - 梦断代码读后感》

以后你遇到类似的案例，也可以尝试去对它们进行盘点分析，找出它们失败的根本原因，能从中吸取教训，避免类似错误发生。

总结

今天我带你一起学习了如何从软件工程的角度分析失败的软件项目。

通过借鉴航空业对飞机坠毁原因的调查，也可以从四个方面去分析软件项目失败的原因，那就是外部环境、技术管理、项目管理和组织文化。

如果细化一下，还可以总结出一些具体的常见的失败原因：

不切实际或者不明确的项目目标；

对项目所需要的资源估算不准确；

需求不明确或者频繁变更；

没有对风险进行有效管理；

和客户之间沟通不畅；

无法解决项目的复杂性；

没有好的开发实践；

糟糕的项目管理；

上层的政治斗争；

商业压力。

其实软件项目失败并不可怕，最重要的还是在失败后，总结原因，吸取教训。就像微软在 Vista 项目失败后，总结经验，改进了开发流程，加快了发布周期，在 Windows 7 项目上重新取得了巨大的成功。还有像暴雪，在泰坦项目失败后，基于泰坦项目开发出了大受欢迎的守望先锋游戏。

课后思考

你有经历过或者听说过印象深刻的失败的软件项目吗？你觉得原因是什么？有哪些经验教训？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

43 | 以VS Code为例，看大型开源项目是如何应用软件工程的？

你好，我是宝玉。如果你所在的团队在日常的软件项目开发中，能科学地应用软件工程的知识，让你的项目能持续取得进展，最终交付的产品也有很好的质量，那么是一件非常幸运的事情。

然而现实中，很多人并没有机会去参与或观察一个好的项目是什么样子的，也没机会去分析一个好的项目是如何科学应用软件工程的。

好在现在有很多优秀的开源项目，不仅代码是公开的，它们整个项目的开发过程都是公开的。通过研究这些开源项目的开发，你能从中学习到一个优秀项目对软件工程的应用，加深你对软件工程知识的理解，进而应用到你自己的项目实践中。

我想你对 VS Code 应该不陌生，它是一个非常优秀的编辑器，很多程序员包括我非常喜欢它。VS Code 也是一个大型的开源项目，整个开发过程非常透明，所以今天我将带你一起看一下 VS Code 是如何应用软件工程的，为什么它能构建出这么高质量的软件。

如何从 VS Code 的开发中学习软件工程？

也许你会很好奇，平时也去看过 VS Code 的网站，但并没有提到软件工程的呀？

是的，VS Code 的网站并没有特别突出这些信息，但是如果你有心，可以找到很多有价值的信息，它的整个开发过程都是公开透明的。

比如通过它项目的WIKI和博客栏目，可以看到项目的计划、项目开发流程、测试流程、发布流程等信息。通过它的GitHub网站，你可以看到团队是如何基于分支开发，开发完成后提交 Pull Request，团队成员如何对代码进行审核，合并代码后如何通过持续集成运行自动化测试。

除此之外，团队成员在网上也有一些对于 VS Code 开发的分享，比如说 VS Code 主要负责人 Erich Gamma 2016 年在 GOTO 技术大会上有一个专门关于 VS Code 的主题演讲。

也许你还会想问：这些信息我也知道，也能从网上看到，但怎么通过这些信息去观察和学习它跟软件工程相关的部分呢？

不知道你是否还记得，在我们专栏的第一篇文章《01 | 到底应该怎样理解软件工程？》中提到了：软件工程的核心，就是围绕软件项目开发，对开发过程的组织，对方法的运用，对工具的使用。所以当我们去观察一个软件项目，我们就可以去看它的开发过程是怎么被组织的？运用了哪些软件工程的方法？使用了哪些工具？

接下来，我就带你一起从以下几个方面分析 VS Code 对软件工程的应用：

VS Code 的开发过程；

团队的分工角色；

各个阶段如何进行；

使用了哪些工具。

VS Code 的开发迭代过程

如果你是 VS Code 的用户，你会发现 VS Code 每个月都会有新版本的更新，每次更新都会有很多新酷的功能。这是因为 VS Code 每个版本的开发周期是 4 周，每四周都会发布一个新的版本。

从开发模式来说，VS Code 采用的是快速迭代的开发模式，每四周一个迭代。那么这四周的迭代的工作都是如何进行的呢？

第一周

每个版本的第一周，通常是起着承上启下的作用，一方面要准备新版本，一方面还要对上一个版本的工作进行收尾。

在这一周里，开发团队要去做一些偿还技术债务的事情，比如说重构代码，优化性能。所以如果你的团队抱怨说没有时间做偿还技术债务的事情，不妨也去学习 VS Code 团队，定期留出专门的时间，做偿还技术债务的事情。

另一个主要工作就是一起讨论下一个迭代要做的功能。其实这有点类似于敏捷开发中，每个 Sprint 开始之前的项目计划会议。

如果上一个版本开发完成的功能，发现了严重 Bug，第一周还要去修复这些紧急 Bug。

第二周和第三周

第二周和第三周主要工作就是按照计划去开发，一部分是开发新功能，一部分是修复 Bug，所有的 Bug 都是通过 GitHub 的 Issue 来分配和跟踪的。

团队成员每天还要先检查一下分配给自己的 Issue，如果遇到线上版本紧急的 Bug，要优先修复。

第四周

VS Code 团队把最后一周叫 End game，你可以理解为测试周，因为这一周只做测试和修复 Bug。

这一周要测试所有新的 Feature 和验证已经修复的 Bug，确保被修复。同时还要更新文档和写 Release Notes。

测试完成后就发布预发布版本，这个预发布版本会先邀请一部分人使用，比如说微软内部员工、热心网友。

下一个迭代第一周

每个迭代开发测试完成的版本，会放在下一个迭代的第一周发布。如果在预发布版本中发现严重 Bug，需要在第一周中修复。

如果没有发现影响发布的 Bug，那么第一周的周三左右就会正式发布上一个迭代完成的版本。

前面我在专栏文章《40 | 最佳实践：小团队如何应用软件工程？》中，建议小团队可以缩短迭代周期到 2-4 周，有同学担心不可行，但你看 VS Code 这样稳定的 4 周迭代，不但可行，而且还是 VS Code 能保持每月发布一个新版本的关键所在。

VS Code 团队的角色和分工

VS Code 的开发团队现在大约 20 人左右，一半在苏黎世，一半在西雅图。整个团队基本上都是开发人员，结构很扁平。

从分工上来说，在开发新功能和修复 Bug 的时候，会有一些侧重，比如有人侧重做 Git 相关的功能，有人侧重做编辑器部分功能。这样有侧重的分工对于提升开发效率是有好处的。

从角色上来说，除了开发，还有主要有两种角色：Inbox Tracker和Endgame Master。这两种角色在每个迭代的时候是轮值的，每个人都有机会去担任这两个角色。

Inbox Tracker

Inbox Tracker 的主要任务就是收集、验证、跟踪 Bug。但这个工作对于 VS Code 团队来说可不轻松，现在 Issue 的总量已经超过了 5000，每天提交的新的 Issue 的量大概有 100 左右。所以 VS Code 团队写了一个机器人叫VSCodeBot，可以帮助对 Issue 先自动处理，打标签或回复，然后 Inbox Tracker 再对剩下的 Issue 进行人工处理。

Inbox Tracker 要检查新提交的 Issue 是不是一个真正的 Bug，如果是提问，建议到 StackOverflow 去问，如果是 Bug，打上 Bug 的标签，并指派给相应模块的负责人。

Endgame Master

VS Code 团队是没有专职的测试人员的，所有的测试工作都是开发人员自己完成。在每一个迭代中。Endgame Master 在这里就很重要，要组织管理整个迭代的测试和发布工作。

Endgame Master 在每个迭代测试之前，根据迭代的开发计划制定相应的测试计划，生成 Check List，确保每一个新的功能都有在 Check List 中列出来。

因为 VS Code 团队没有专职测试，为了避免开发人员自己测试自己的代码会存在盲区，所以自己写的功能都是让其他人帮忙测试。Endgame Master 一个主要工作就是要将这些测试项分配给团队成员。

最后整个测试计划会作为一条 GitHub Issue 发出来给大家审查。比如说这是某一个月的Endgame 计划。

团队的日常沟通是通过 Slack，在测试期间，Endgame Master 需要每天把当前测试进展同步给所有人，比如说总共有多少需要测试的项，哪些已经验证通过，哪些还没验证。

VS Code 的各个阶段

接下来，我们来按照整个开发生命周期，从需求收集和版本计划、设计开发、测试到发布，来观察 VS Code 各个阶段是如何运作的。

1. VS Code 的需求收集和版本计划

VS Code 每次版本发布，都能为我们带来很多新酷的功能体验，那么这些功能需求是怎么产生的呢？又是怎么加入到一个个版本中的呢？

VS Code 的需求，一部分是团队内部产生的；一部分是从社区收集的，比如 GitHub、Twitter、StackOverflow 的反馈。最终这些收集上的需求，都会通过 GitHub 的 Issue 管理起来。如果你在它的 GitHub Issue 中按照feature-request的标签去搜索，可以看到所有请求的需求列表。

VS Code 每半年或一年会对下一个阶段做一个Roadmap，规划下一个半年或一年的计划，并公布在 GitHub 的 WIKI 上，这样用户可以及时了解 VS Code 的发展，还可以根据 Roadmap 上的内容提出自己的意见。

大的 RoadMap 确定后，就是基于大的 RoadMap 制定每个迭代具体的开发计划了。前面已经提到了，在每个迭代的第一周，团队会有专门的会议讨论下一个迭代的开发计划。在 VS Code 的 WIKI 上，也同样会公布所有确定了的迭代计划。

那么，有了功能需求和 Bug 的 Issue，也有了迭代的计划，怎么将 Issue 和迭代关联起来呢？

GitHub 的 Issue 管理有一个 Milestone 的功能，VS Code 有四个主要的 Milestone。

当前迭代：当前正在开发中的 Milestone；

On Deck：下一个迭代对应的 Milestone；

Backlog：还没开始，表示未来要做的；

Recovery：已经完成的迭代，但是可能要打一些补丁。

（图片来源：VSCode Milestones）

2. VS Code 的设计和开发

VS Code 的架构设计现在基本上已经定型，你在它的 WIKI 和博客上还能看到很多 VS Code 架构和技术实现的分享。

在每个迭代开发的时候，一般小的功能不需要做特别的架构设计，基于现有架构增加功能就好了。如果要做的是大的功能改造，也需要有设计，负责这个模块开发的成员会先写设计文档，然后邀请其他项目成员进行 Review，并给出反馈。

VS Code 的开发流程也是用的GitHub Flow，要开发一个新功能或者修复一个 Bug，都创建一个新的分支，开发完成之后提交 PR。PR 合并之前，必须要有核心成员的代码审查通过，并且要确保所有的自动化测试通过。

对于 GitHub Flow 的开发流程，我在专栏文章《30 | 用好源代码管理工具，让你的协作更高效》中有详细的介绍。你也可以在 VSCode 的Pull requests中看到所有提交的 PR，去看看这些 PR 是怎么被 Review 的，每个 PR 的自动化测试的结果是什么样的。通过自己的观察，去印证专栏相关内容的介绍，同时思考是否有可以借鉴到你自己项目中的地方。

VS Code 对自动化测试代码也是非常重视，在实现功能代码的时候，还要加上自动化测试代码。如果你还记得专栏文章《29 | 自动化测试：如何把 Bug 杀死在摇篮里？》中的内容：自动化测试有小型测试、中型测试和大型测试。VS Code 的自动化测试也分为单元测试、集成测试和冒烟测试。

VS Code 的CI（持续集成）用的是微软自己的 Azure DevOps，每一次提交代码到 GitHub，CI 都会运行单元测试和集成测试代码，对 Windows/Linux/macOS 三个操作系统分别运行测试。在持续集成上可以直观地看到测试的结果，VS Code 现在有大约 4581 个单元测试用例，运行一次 1 分钟多；集成测试 466 个，运行一次大约 3 分钟。

（图片来源：VSCode 的持续集成工具 Azure DevOps）

如果你的团队还没有开始相应的开发流程，没有使用持续集成工具，不妨学习 VS Code，使用类似于 GitHub Flow 的开发流程，使用像 Azure DevOps 这样现成的持续集成工具。

3. VS Code 的测试

前面提到了，迭代的最后一周是 End game，这一周就是专门用来测试的，并且有轮值的 Endgame Master 负责整个测试过程的组织。

具体测试的时候，大家就是遵循 Endgame Master 制定好的测试计划，各自按照 Check List 逐一去检查验证，确保所有的新功能都通过了测试，标记为修复的 Bug 真的被修复了。对于验证通过的 Bug，在对应的 Issue 上打上 verified 的标签。

在人工测试结束后，Endgame Master 就需要跑冒烟测试，确保这个迭代的改动不会导致严重的 Bug 发生。

如果你的团队也没有专职测试，可以学习 VS Code 这样的做法：留出专门的测试阶段，事先制定出详细的测试计划，把所有要测试的项都通过测试跟踪工具跟踪起来，开发人员按照测试计划逐一测试。

4. VS Code 的发布流程

在 Endgame 测试后，就要从 master 创建一个 release 分支出去，比如说 release/1.10 ，后面的预发布版本和正式版本包括补丁版本都将从这个 release 分支发布。

如果在创建 release 分支后发现了新的 Bug，那么对 Bug 修复的代码，要同时合并到 master 和 release 分支。每一次对 Release 的代码有任何改动，都需要重新跑冒烟测试。

在 Release 分支的代码修改后的 24 小时之内，都不能发布正式版本。每次 Release 代码修改后，都会发布一个新的预发布版本，邀请大约两万的内部用户进行试用，然后看反馈，试用 24 小时后没有什么问题就可以准备发布正式版本。

发布正式版本之前，还要做的一件事，就是 Endgame master 要写 Release Notes，也就是你每次升级 VS Code 后看到的更新说明，详细说明这个版本新增了哪些功能，修复了哪些 Bug。

如果版本发布后，发现了严重的线上 Bug，那么就要在 Release 分支进行修复，重新生成补丁版本。

除此之外，VS Code 每天都会将最新的代码编译一个最新的版本供内部测试，这个版本跟我们使用的稳定版 Logo 颜色不一样，是绿色的 Logo。VS Code 内部有“吃自己狗粮”（eat your own dog food）的传统，也就是团队成员自己会使用每天更新的测试版本 VS Code 进行开发，这样可以在使用过程中及时发现代码中的问题。

（图片来源：The Journey of Visual Studio Code）

像 VS Code 这样的发布流程，通过创建 Release 分支可以保障有一个稳定的、可以具备发布条件的代码分支；通过预发布内部试用的机制，有问题可以及时发现，避免造成严重的影响。

关于发布流程的内容，你也可以将 VS Code 的发布流程 对照我们专栏文章《35 | 版本发布：软件上线只是新的开始》中的介绍，加深理解。

VS Code 使用的工具

VS Code 的源代码管理工具就是基于 GitHub，整个开发流程也完全是基于 GitHub 来进行的。

它的任务跟踪系统是用的 GitHub 的 Issue 系统，用来收集需求、跟踪 Bug。通过标记不同的 Label 来区分Issue 的类型和状态，比如 bug 表示 Bug，feature-request 表示功能请求，debt 表示技术债务。通过 Issue 的 Milestone 来标注版本。

VS Code 的持续集成工具最早用的是Travis CI和AppVeyor，最近换成了微软的Azure Pipelines，在他们的 Blog 上有一篇文章《Visual Studio Code using Azure Pipelines》专门解释了为什么要迁移过去。

VS Code 的文档一部分是用的 GitHub 的 WIKI 系统，一部分是它网站的博客系统。WIKI 主要是日常项目开发、维护的操作说明，博客上更多的是一些技术分享。

另外 VS Code 团队还自己开发了一些小工具，比如说帮助对 Issue 进行自动处理回复的 GitHub 机器人 VSCodeBot。

通过这些工具的使用，基本上就可以满足像 VS Code 这样一个项目的日常运作。像这些源代码管理、任务跟踪系统、持续集成工具的使用，在我们专栏也都有相应的文章介绍，你也可以对照着文章的内容和 VS Code 的使用情况加以印证，从而加深对这些工具的理解，更好把这些工具应用在你的项目中。

总结

当你日常在看一个开源项目的时候，不仅可以去看它的代码，还可以去观察它是怎么应用软件工程的，不仅可以加深你对软件工程知识的理解，还能从中学习到好的实践。

比如观察一个软件项目的开发过程是怎么被组织的，团队如何分工协作的，运用了哪些软件工程的方法，以及使用了哪些工具。

VS Code 使用的是快速迭代的开发模式，每四周一个迭代：

第一周：偿还技术债务，修复上个版本的 Bug，制定下一个版本的计划；

第二、三周：按照计划开发和修复 Bug；

第四周：测试开发完成的版本；

下一迭代第一周：发布新版本。

在团队分工上，VS Code 的团队很扁平，没有专职测试，通过轮值的 Inbox Tracker 和 Endgame Master 来帮助团队处理日常 Issue 和推动测试和发布工作的进行。

在工具的使用方面，VS Code 使用的是 GitHub 托管代码，基于 GitHub Flow 的开发流程使用的。还有使用 Azure DevOps 作为它的持续集成系统。

通过观察对 VS Code 对软件工程知识点的应用，再对照专栏中相关文章的介绍，可以帮助你更好的理解这些知识点，也可以借鉴它们好的实践到你的项目开发中。

课后思考

你可以尝试自己去观察一遍 VS Code 项目对软件工程知识的应用，得出自己的结论。你也可以应用这样的观察分析方法，去观察其他你熟悉的优秀开源项目，比如像 Vue、React，看它们是怎么应用软件工程的。欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

44 | 微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的？

你好，我是宝玉。在上一篇文章里，我带你一起了解了像 VS Code 这样的开源项目对软件工程的应用，以及如何学习借鉴优秀的项目对软件工程的应用。今天我将带你去看看像微软、谷歌、阿里巴巴这些大厂是怎么应用软件工程的，以及我们应该如何学习和借鉴他们对软件工程的实践。

我想无论你现在是否在大厂工作，都有很多途径了解到大厂是如何应用软件工程的，网上已经有很多他们员工的分享。你可能更想知道的是： 从大厂应用软件工程的实践中，你能学习什么，又该如何学习借鉴。

每个公司，都有自己的历史和文化，他们的文化又影响了各自的软件开发模式。

比如说谷歌，谷歌崇尚工程师文化，请来的工程师都是万里挑一的，开发也没有太大的进度压力，所以 Google 的工程师做项目就会不紧不慢，质量优先，有统一的代码规范，严格的代码审查和严谨的自动化测试。还会频繁地重写系统，每隔几年，就把软件重写一遍。

再比如说 Facebook，Facebook 有一种黑客精神，创始人马克·扎克伯格有句名言是“Move Fast and Break Things”，也就是说快速做出产品，不要怕犯错。所以 Facebook 的工程师做软件开发的时候不会想太多，先实现再说，做出来就发布，哪怕可能有 Bug。发布后根据用户的反馈再不断完善，真的把线上功能弄坏了，再打补丁去修复。

然而这些带有各自文化特色的部分，却是我们很难学习借鉴的，因为这样的文化都只适合各自的公司。假设让微软去学习 Facebook 的黑客精神，发布带有很多 Bug 的 Windows 系统，那么用户是不能忍受的；而让普通公司去学 Google 的工程师文化，项目没有严格的 Dead Line，系统隔几年重写一遍，那公司恐怕都要撑不住了。

所以，要学习大厂，你要多去关注大厂们对软件工程实践共通的地方，可以应用在你自己项目的地方，另外还要去看大厂对软件工程实践的变化趋势，在朝什么方向发展。通常这些大厂的很多实践都是业界的风向标，一旦一些实践大厂都在应用，那么很多中小厂就会跟风，最终变成行业标准。

在上一篇《43 | 以 VS Code 为例，看大型开源项目是如何应用软件工程的？》中，我从项目的开发迭代过程，团队的角色分工和项目开发各个阶段来分析了 VS Code 对软件工程的应用。类似的，我也将从大厂的开发团队组成、开发工具的使用、项目开发流程这几个方面来分析一下大厂对软件工程的应用中，有哪些共同点？有哪些变化趋势？有什么地方可以借鉴？

软件项目开发团队组成

软件项目开发，最终要落实到“人”上面。大厂在招人方面一向舍得投入，不仅花很多人力财力在招聘上面，同样对于员工待遇上也很大方，只为了能招到最优秀的人才。

对于普通的公司和团队来说，很难像大厂那样有一群行业内顶尖的人才，但是它在软件团队的一些管理和实践方面，还是有一些共通的地方，有值得普通公司学习借鉴之处。

1. 软件开发团队规模小

网上曾有一张流传甚广的关于各大公司的组织结构图。

（图片来源：HOW YOUR STARTUP’S ORG CHART CHANGES YOUR PRODUCT）

这张图形象生动的描述了各大公司的组织结构，各具特色。然而这些大厂的组织结构具体细分到软件项目开发团队的时候，却惊人的相似：那就是一个软件项目开发团队都不会太大，一般不会超过 10 个人，如果超过就会被分拆。最著名的就是亚马逊的“两个披萨原则”，也就是团队的人数不应该多到让两个披萨不够吃。

其实大厂的软件项目都采用小团队的原因很好理解，那就是团队规模越大，交流就越复杂，成本也越高！要想沟通更高效，那么就要求团队的规模必须足够小。

组织架构的小型化也会对软件架构有影响，通过架构的隔离，让各个不同的团队可以在一起高效地协作。有在谷歌 YouTube 工作的朋友跟我说，YouTube 的 App，其中一个导航菜单，都是一个专门的小团队在维护。

如果你所在团队规模大，沟通效率不高，那么可以考虑向大厂学习，分拆成小团队，可以有效提高沟通协作的效率。

2. 没有专职测试

在我们专栏文章《32 | 软件测试：什么样的公司需要专职测试？》中，探讨了专职测试这个话题，而现在像微软、谷歌、Facebook、阿里巴巴这些大厂，都没有专职的测试人员。

但没有专职测试人员不代表他们不重视质量，只是他们在用更高效的方式来代替人工“点点点”的手工测试。就像专栏文章中介绍的，Facebook 能做到没有专职测试人员，是因为他们有大量的自动化测试；另外，Facebook 在功能发布之前，先在内部使用，上线之后能做到有效监控，出现问题能随时回滚或者打补丁。

大厂替代专职测试的这些手段，对于普通公司来说，可能现阶段去实施是有难度的，但是随着这些发布、监控工具的不断普及，自动化测试的普及，开发团队不设置专职测试会逐步变成一种趋势，现在的手工测试将来也许会被逐步淘汰。

3. DevOps 文化

在我们专栏文章《36 | DevOps 工程师到底要做什么事情？》中，有过对 DevOps 进行探讨，DevOps，本质上就是一种紧密协作的工作方式。

早些年像微软这样的大厂，工程师团队有三种角色：项目经理，开发人员和测试人员，而运维团队则是工程师团队的另一组人。虽然好处是分工更明确，但是久而久之也造成了不同工种之间的隔离，尤其是各自目标不一致导致的利益冲突。

所以微软也在前些年进行了转型，将运维团队合并到了工程师团队，运维人员和开发人员协作更加紧密了，有效提高了编码效率，质量和产量。

除了微软，其他大厂也纷纷采用了类似的 DevOps 转型和实践。这里有两篇关于谷歌和阿里巴巴的 DevOps 实践文章可以参考：《孙宇聪：来自 Google 的 DevOps 理念及实践》《阿里研究员毕玄谈应用运维体系的变迁，DevOPS 是大势所趋》。

如果你的团队也存在不同工种之间协作的矛盾和冲突，不妨借鉴一下大厂对 DevOps 的实践。

开发工具的使用

大厂都爱自己造轮子，对开发工具也是如此，都有一个专门的部门去做内部工具的开发和维护。

如果你有幸在一个大厂工作，那么你会很幸福，基本上开发过程中，各种像编译、部署、持续集成等等都有好用的工具可以帮助你自动化，提升效率，你只要专注于写代码就好了。然而一旦离开大厂，你会发现这些日常工具都要自己去搭建，甚至得自己去写。

但好在大厂用的这些主要工具，你在网上几乎都能找到开源的或商业的替代品。只是没有那么好用罢了。

比如说谷歌一名前员工在 GitHub 上分享了他在谷歌工作时，日常会使用的一些工具，以及外界对应的替代方案（工具和替代方案）。再比如微软和阿里巴巴都将自己的工具（Azure DevOps 和 阿里云 DevOps）做成了服务供第三方使用。

有一点倒是可以看得出：这些大厂舍得在工具上投入。应用工具，也确实可以有效地提升效率，改进软件项目质量。

关于工具，我们专栏在各个章节也都有介绍，建议可以学习下大厂，把这些工具用起来，帮助你更好地完成项目。

项目开发流程

各个大厂基本上都没有规定必须要用什么开发模型或者不允许用什么开发模型，各个开发团队都可以自行决定采用的开发模型。

所以你会看到有的团队是敏捷开发，有的团队是快速迭代，甚至有的团队还用的是瀑布模型。但他们在项目开发中有很多共通之处。

1. 迭代周期短

即使是像微软这样，以前要几年才发布一个版本软件的公司，现在也加快了迭代。现在 Windows 10，每半年就会更新一个大的版本，每天都会发布可以测试的版本。

上一篇介绍的 VS Code 的开发，也是每个月就会有一个大的版本发布。还有像谷歌的 Chrome 浏览器，也是每 6 周发布一个新版本，如果某个新功能还没准备好，那么放到下个版本发布。

（图片来源：Chrome 发布周期）

如果你的项目需要半年以上的开发周期，也要考虑一下，是否可以缩短开发周期，快速迭代起来。

2. 严格的开发流程

其实我在专栏已经反复地、苦口婆心地讲了很多开发的流程，比如说基于分支开发、代码审查、自动化测试、持续集成等等，希望大家能在实践中去应用这些好的实践。

然而在大厂，这些开发流程基本上都是硬性要求：

要基于分支进行开发新功能或者修复 Bug；

要遵守公司或者团队的代码规范；

合并之前要有至少一个人 Review 通过；

要写自动化测试代码，并且保证所有测试用例通过。

在谷歌的卫生间里面，甚至会张贴着有关 Testing on the Toilet 的贴纸，让你在去卫生间的时候还能学学怎么写测试，好让你的 PR 能早点通过审查。

（图片来源：Testing on the Toilet）

3. 严谨的测试流程

虽然大厂都没有专职测试，但是测试可不含糊，都有一套严谨的，并且行之有效的测试流程。

以谷歌的 Chrome 浏览器为例，除了自动化测试以外，每个 Chrome 的版本发布之前，都要经历以下几个版本。

金丝雀版本（Canary Channel）： 过去煤矿工人要下井会带着带着金丝雀，这种鸟对危险气体的敏感度超过人。如果金丝雀死了，矿工便知道井下有危险气体，需要撤离。金丝雀版本会频繁发布，但并不太可靠，就像金丝雀一样用来第一时间发现严重的问题。

开发版本（Dev Channel）：工程师日常使用的版本，一边开发一边使用，让工程师可以第一时间验证自己开发的功能。

测试版本（Test Channel）：给内部员工的版本，就像上一篇 VS Code 介绍的 Eat your own food，自己人先试用。

Beta 版本或发布版本（The Beta Channel or Release Channel）：是给外部用户使用的测试版本，并不保证稳定，但是用户可以提前体验新功能，也能帮助开发团队及时发现 Bug。

类似的，如果你看 Windows 10 的发布流程，也是这样一个一个的测试版本的测试流程，最后正式发布的版本已经是经过千锤百炼，反复测试过的。

（图片来源：微软邹欣：Hit refresh 背后的软件工程革新）

4. 完善的发布和监控流程

就算经过完整的测试，也不能保证质量就是可靠的。所以大厂们还会配合一套完善的发布和监控流程。

发布前，先评估风险，增加相应的监控数据和设置报警的阈值。制定出现问题的应对方案。

上线后，先推送一小部分用户，并同时进行线上数据的监控，如果没有发现异常，自动加大比例，直到完整覆盖；如果发现异常，自动报警通知相关负责人，上线处理，并直接关闭新功能。

有关上线发布和数据监控的内容，你也可以参考专栏文章《35 | 版本发布：软件上线只是新的开始》和《38 | 日志管理：如何借助工具快速发现和定位产品问题 ？》中的更多介绍。

5. 事后总结，不断改进

在专栏文章《39 | 项目总结：做好项目复盘，把经验变成能力》中，提到了项目复盘的重要性，以及如何做好项目复盘。

对于大厂来说，复盘也是整个项目开发过程中很重要的一部分，正是因为有这样一次次的“事后诸葛亮”会议，才让团队成员能从中总结成功经验，吸取失败教训。

（图片来源：微软邹欣：Hit refresh 背后的软件工程革新）

参考阅读

其实，大厂的软件工程实践，网上有很多相关的文章，这里我将收集的一些内容在这里分享一下，供参考阅读：

《Google 公司的软件工程之道》

《软件工程在微软的演化——邹欣》

《解密 Facebook 产品的开发流程》

《微软、谷歌、Facebook、Amazon 软件质量控制实践》

《微软开发团队的 DevOps 实践启示》

《The Facebook Mobile Release Process》

《敏捷开发，你真的做对了吗？阿里文娱广告团队敏捷实践总结》

《如何在 2 周内交付 85% 以上需求？阿里工程师这么做》

总结

现在业界顶级的互联网或者软件公司，他们都对软件工程都有非常好的应用和实践，这也是他们能跻身成为顶级互联网的一个不可或缺的前提。通过学习和观察大厂的软件工程实践，能帮助我们拓宽视野，提升软件工程知识水平。

学习大厂，要多去关注大厂们对软件工程实践共通的地方，以及可以应用在你自己项目的地方。

在团队管理方面，大厂的软件项目团队规模都被拆的比较小，这有助于团队成员之间的沟通协作；没有专职的测试人员，测试工作被自动化测试代替；有很好的 DevOps 文化，各个工种之间紧密协作。

在开发工具方面，大厂都很重视工具的开发和使用，很多工具我们也能找到替代品，或者直接使用大厂提供的工具服务。

在项目开发流程上，大厂有严格的开发流程，代码必须写自动化测试代码，自动化测试通过，并且有人 Review 通过的 PR 才能被合并；大厂虽然没有专职测试人员，但是整个测试过程很严谨，在发布前都经过了充分的测试；大厂对于软件发布也都有完善的发布和监控流程，不仅可以快速发布，还可以及时发现线上问题；大厂也会有上线后的复盘总结，总结成功经验，吸取失败教训，将项目经验变成团队能力。

从大厂对软件工程实践中，你可以学习到一个优秀的公司是如何来应用软件工程，打造出高质量产品的，也可以借鉴其中好的实践到你自己的项目中。

最后你要清楚，即便是大厂，对软件工程的应用也不是一成不变的，会随着技术的发展、软件工程的发展不断改进。比如说微软早些年就使用的类似于瀑布模型的开发模式，现在也变成了敏捷的快速迭代的开发模式。这种不断学习改进的方式，也值得我们大家学习和思考。

课后思考

这些大厂里面，你最喜欢哪几家？你觉得他们对于软件工程的实践哪些做的比较好？哪些是你可以学习借鉴的？欢迎你在留言区分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，欢迎把它分享给你的朋友。

45 | 从软件工程的角度看微服务、云计算、人工智能这些新技术

你好，我是宝玉。这些年来，新技术新概念层出不穷，比如说微服务、云计算、人工智能等。你有没有去学习和了解这些新技术呢？又是怎么去理解这些新技术的呢？

也许你会从技术的角度，去学习和理解这些新技术，去看如何把服务分拆，看如何应用虚拟化、容器技术，如何用人工智能切页面。

这些新技术可能会让你很兴奋，毕竟又有很多新知识可以学习和应用的；但另一方面也可能会增加一些困惑，比如说：

我该不该在项目中使用微服务？

在设计微服务架构的时候，服务拆分的粒度该多细？该拆成 10 个服务还是 100 个？

云计算对我的项目会带来什么影响？我应该怎么应用？

人工智能会代替我写程序吗？

如果只是从技术角度思考这些问题，难免会陷入技术之中，反而不容易看清楚这些问题。在我们专栏一开始《02 | 工程思维：把每件事都当作一个项目来推进》这篇文章中，我就提到了工程思维的概念：

工程思维，本质上是一种思考问题的方式，在解决日常遇到的问题时，尝试从一个项目的角度去看待问题、尝试用工程方法去解决问题、站在一个整体而不是局部的角度去看问题。

在学习使用这些新技术的时候，你不妨从项目的整体，从软件工程的角度来理解这些技术，这能给你带来不同的视角。那么怎么从软件工程的角度去理解呢？

我想你应该对我们专栏上两篇文章有印象，我分别从团队、项目过程、工具这几个维度，分析了开源项目和优秀公司对软件工程的应用。类似的，你也可以跳出技术之外，从软件工程的角度来理解微服务、云计算、人工智能这些新技术和概念。

软件工程中技术架构和组织架构的关系

首先我们来看看微服务，你可能第一反应就是：它是一种架构技术。没错，从技术角度来看，微服务就是一种架构技术。经过对我们专栏的学习，我相信你对架构应该不会陌生，比如：前后端分离架构、微服务架构。

不知道你有没有观察过：通常系统架构和组织架构是相似的。比如说前后端分离的架构，那么在组织上一般也会分前端组和后端组；而微服务架构，则分组是和服务相关的，可能一个组就是负责一个微服务。

其实组织架构和技术架构相似这个现象不是偶然的，这个现象背后有个定律叫康威定律 (Conway’s Law)。康威（Melvin Conway）博士在 1967 年提交的一篇论文《How Do Committees Invent?》中最有名的一句话是：

Organizations which design systems are constrained to produce systems which are copies of the communication structures of these organizations. — Melvin Conway

如果对这句话翻译一下，它的意思是：

你设计的软件系统架构，会以某种方式反映出构建软件背后团队的组织架构，你在设计软件的系统架构时，同时也在设计你的组织架构，反之亦然。也可以简单理解为：组织架构的设计等同于系统架构的设计。

如果你拿康威定律去验证你现在的团队组织架构，或者你熟悉的其他团队的组织结构，你会发现运行良好的项目，都很好地符合这条定律。那些大型复杂的单体软件系统，背后也对应着一个庞大的开发团队，那些应用微服务的项目，背后都是一个个的小组。

（图片来源：Conway’s Law）

看完康威定律再回过头来看微服务，你会发现，微服务架构的设计，不仅仅是一个对服务拆分的架构设计，同时也是对组织架构拆分的设计。

当你在做架构设计，在考虑你的微服务拆分粒度的时候，不妨先想一想：你团队的组织结构是什么样的？真的大到需要用微服务了吗？你能按照微服务的设计去重新设计和调整你的组织结构吗？

当你在设计系统架构的同时，把组织架构的设计也考虑进去，很多问题也就迎刃而解了。比如说你开发团队 30 个人，要使用微服务的架构，那么拆成 3～5 个微服务是比较合适的。因为每个小组 10 个人左右，每个小组维护 1～3 个微服务，是相对比较合适的配比。

然后你再看那些应用微服务失败的案例，比如说一个小开发团队，做出 100 多个微服务的架构，那团队维护这些服务的成本一定是相当高的，最终会难以维持。就像这篇文章：《再见微服务，从 100 多个问题儿童到一个超级明星》，虽然文章里面没有提到团队的组织结构，但是可以想象，这 140 多个微服务背后一定没有 140 个小团队，哪怕每个团队只有 10 个微服务，对团队来说也是有极大维护成本的。

还有一些传统大型企业，团队构成是按工种划分成不同团队的，开发一个团队、测试一个团队、运维一个团队，那么推行微服务阻力会非常大，因为这样的组织结构和微服务的组织结构是不兼容的。

对于微服务的组织结构，需要按服务划分团队，团队成员有开发、测试和运维，一起组成一个小团队，围绕着服务不断迭代，这样效率是最高的。

（图片来源：微服务写的最全的一篇文章）

如果以后又出来什么新的概念和技术，你不妨从软件工程的角度，去看看它和组织结构的关系。比如说这些天网上开始流传一个新的概念叫：微前端（Micro Frontends），在我看来，这说明现在开发的重心在往前端倾斜，前端团队越来越庞大，需要被分拆了。

(图片来源：Micro Frontends)

新技术改变了软件工程中的分工协作

我们再来看看像云计算和人工智能这类技术，也许你会觉得它们代表了很多激动人心的高端技术，比如虚拟化、自动化、智能等等。但这些新技术不仅是技术上有突破，更是对软件工程的开发过程，对分工协作都产生了深远的影响。云计算通过标准化的服务简化了开发的难度，人工智能和自动化在逐步替代项目中的一些手工操作。

在我大学上软件工程课的时候，老师跟我们说：“在建筑工程，用一些标准的模块，比如各种建筑材料组合在一起，就可以完成复杂的结构。这种标准化的结构可以极大地降低建造成本。希望未来软件工程也能像建筑工程一样，用一些标准的模块和组件，也可以构建出复杂的软件。”然而在十多年前，这个目标还是挺遥远的。

十多年前我办过网站，自己写程序，程序写完后要自己去买服务器放到机房托管，每年要给机房交托管费，还要定期数据备份，给服务器杀毒，装防火墙防止 DDos 攻击这种事情。万一服务器宕机了，需要给机房打电话，会有人帮忙重启一下，如果不行就得自己去机房了。

那时候的网络访问还有南北隔离的问题，也就是说你服务器放在南方电信，而用户是北方网通的，那么访问速度就特别慢，想要速度快就要用 CDN（Content Delivery Network，即内容分发网络），以保证访问速度。但当时 CDN 的价钱不是普通用户能承受的了的。

可以说我办个网站是操碎了心。这也是很多中小企业早些年自己开发运行软件系统的写照：需要兼顾开发、测试和线上运维，什么事情都需要自己做。

但是现在，如果我再要去办一个网站，我不会再自己去买服务器托管，而会选一家云计算服务商，将我的程序放在云服务商运行，出问题了就重新部署或者再新开一个虚拟服务器。数据库我也不会自己去安装维护，直接用云数据库，这样省去了数据备份的烦恼。

就算是程序开发，我也不会所有功能自己实现，比如文件存储我会直接用七牛云之类的云存储服务，还能使用 CDN 服务。网站内容的搜索我也会考虑阿里云的文档检索服务，如果有手机 App 消息推送，直接用云厂商的推送服务。

早些年像语音识别、图像识别、地图导航这些高精尖的技术，普通中小公司是没有机会去使用的，或者要付出昂贵的成本。而现在这些高端技术，都有服务提供。如果你有一个好的想法，不用担心技术会限制你的想象力，借助这些服务，你可以实现你的想法。

早些年的开发团队，服务端比前端人数要多，因为那时候界面简单，而后端需要实现很多数据库增删改查的逻辑。现在的趋势是，界面越来越复杂，而后端服务越来越强大，借助一些云服务甚至不需要去写程序，就能实现服务端 API 供前端调用。比如我曾开发过一个微信小程序，后端用的LeanCloud的服务，不需要写后端代码就有一个不错的后端 API 服务。

如果你从软件工程的角度去看云计算，它本质上是在将那些与业务无关的，而又很重要的基础设施、技术，作为一种标准服务提供，让你在软件开发时，只需要专注于业务所独有的部分，从而可以极大地减少开发工作，提升开发效率。

随着云计算的普及，软件工程的标准化、模块化也慢慢出现了一线曙光，希望未来构建软件系统，也能像盖房子一样，通过标准化降低开发成本和难度。

人工智能是另一个现在很火的技术，Alpha Go 在围棋上战胜了人类，无人驾驶也有了突破。另外，人工智能在软件工程领域，也有了一些应用和尝试，比如说微软开源了一个人工智能的项目叫Sketch2Code，可以把 UI 设计草图转成 HTML 代码，也就是一部分开发工作未来也许可以被人工智能替代了。

阿里巴巴在尝试智能化运维：《阿里智能运维平台的演进：从自动化到无人化》，也就是将人工智能应用在运维领域，从而让人工智能去替代人工的很多操作。在测试领域，虽然还没有见到有成功的用人工智能替代人工测试的案例，但是自动化测试替代了大量手工测试是一个可见的趋势。

现阶段，这些应用只是一个开始，但不会是结束，未来会有更多云服务、人工智能在软件工程领域的应用，会对软件开发的分工协作产生更多影响。

但云服务、人工智能再强大，也难以替代那些创造性的劳动，也就是那些你业务和项目所独有的东西，比如说你对业务的抽象和设计，测试用例的设计，对整个项目过程的组织。

在软件工程中，技术是工具

对于像微服务、云计算、人工智能这些新技术，如果站在技术角度看，技术人员永远有两种态度：拥抱新技术和抵触新技术。

但如果你站在软件工程的角度去看技术：技术服务于架构设计，架构设计服务于业务，业务服务于商业。也就是本质上来说，技术是为项目服务的工具。

做一个项目，首先是要去解决一个商业问题，比如说你要网上卖东西。然后基于这个商业问题，你要设计一个业务，比如做一个在线商城系统。当你确定了你的业务，你再去设计出适合这个业务的架构，比如设计一个三层架构。最后架构设计好了，你再去选择适合这个架构的技术，比如 PHP+MySql。

但现实中常常不是这样的，开发人员学会了微服务的技术，就像有了一个锤子满世界找钉子，所以当你需要一个在线商城系统，他会给你按照微服务搭一个架构出来，也许你只要一个简单的 PHP+MySql 系统就足够了。

或者说最开始你的架构就是简单的三层架构，能很好地满足当时的需求，然后业务不断壮大，于是服务越来越大，团队也越来越大，沟通成本非常高，非常有必要对团队进行分拆。那么这时候微服务就是适合你的架构，而你的技术负责人不懂微服务，也很抵触微服务，就不太可能推动这样的转变。

对于这些新技术，如果只是从技术角度去看，就会更多考虑这个技术喜不喜欢，酷不酷，难不难学，而不容易考虑到如何更好地去为架构服务。

但要是从软件工程的角度，就会把技术当做工具，去学习了解这些新技术，然后进一步思考：这个技术能解决什么问题？应用在项目中有什么样的优缺点？

当你不仅仅是从技术角度去看这些新技术，而是能同时站在软件工程角度看这些新技术时，就能真正的让技术去为架构服务，让架构去为业务服务，从而帮助业务产生好商业价值。

总结

不管是现在还是将来，你总是免不了要去面对新技术。从技术角度去看新技术，也许你会兴奋，也许你会抵触，但是如果你跳出技术角度之外，站在软件工程的角度去看新兴技术，你会有不一样的收获。

技术架构等同于组织架构，当你在设计系统架构，你同时也在设计你的组织架构，反之亦然。当你纠结微服务的拆分粒度，不妨看看你的组织架构是不是能和微服务架构匹配。

云计算、人工智能这些新兴技术也逐步改变了分工协作，云计算这样的基础服务，可以降低开发成本，让你可以专注于业务开发；人工智能和自动化技术的发展，也逐步替代了原有的像手工测试、手工运维的工作。但对于创造性的劳动，例如业务的设计和抽象，测试用例的设计和项目过程的组织，是不太可能会被替代的。

最后，技术是工具。技术服务于架构设计，架构设计服务于业务，业务服务于商业。对新技术，保持学习和了解，知道新技术能为你解决项目中什么问题，就像工具一样，选择合适的技术，让技术为架构服务。

课后思考

你是怎么看这些新技术的？你有没有应用这些技术在你的项目中？有没有经验教训可以分享的？欢迎在留言区与我分享讨论。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

“一问一答”第5期（内含彩蛋） | 22个软件开发常见问题解决策略

你好，我是宝玉。恭喜你完成了经典案例解析篇的学习，这也意味着你坚持到最后，完成了我们专栏所有内容的学习。

学习软件工程的知识，最终还是为了要能去应用学到的知识。案例解析就是帮助你结合日常生活中一些常见的现象，去站在软件工程的角度思考和分析。

也许你所在的是一个小团队，日常并没有注意对软件工程的应用，但团队小不是拒绝应用软件工程的借口，小团队一样要做好团队的建设，基于软件工程做好流程建设。

也许你团队的软件工程已经很好了，但你做个人业余项目却总是失败告终，不妨用学到的软件工程知识去分析一下，看问题在什么地方，又该如何去改进，才能做出成功的项目。

也许你也经常在网上关注一些软件开发的信息，当你再看到一些失败的软件项目案例时，不仅仅是当作一个有趣的故事，不妨去站在软件工程的角度，看看它失败的原因是什么，从别人的失败案例中吸取经验教训。

也许你日常也关注开源项目，但只是关注技术层面，以后不妨也站在软件工程的角度去看看这些开源项目是怎么运作的？有哪些可以学习借鉴的地方？

也许你身在大厂或者关注大厂的开发实践，那么不妨多学习和观察大厂的软件工程实践。因为大厂之所以成为大厂，对软件工程的应用自然是有其独到之处，学习大厂能帮助你拓宽视野，提升软件工程知识水平。

也许你为微服务、云计算、人工智能这些新技术兴奋或者焦虑，跳出技术角度之外，站在软件工程的角度去看新兴技术，你能有不一样的收获。

我对这些案例的分析，都只是为你提供一种不同的视角，帮助你从软件工程的角度看工作中遇到的问题。相信你在学习之后，也可以利用学到的知识，自己去观察软件工程实践，去应用软件工程知识，发现软件工程之美。

今天加餐，继续分享我们专栏的精彩问答和留言，这些问答和同学们的分享都是对专栏内容的最好补充，希望可以帮助你更好地学习和理解软件工程知识。

一问一答

No.1
hua168：小团队比较乱的话，最好是规范哪些关键的地方？比如我们小团队开发，首先看这个功能有没有开发过，如果是开发过，就直接基于以前开发过的代码改。这就导致运维有问题，有些路径没有替换完，手工输入命令可以运行，用 shell 脚本监控，发现程序异常，就重启，结果就报错了，用脚本死活启动不起来。然后发现没有路径及文件，叫开发改，要一拖再拖，都不愿意改。

宝玉：流程规范的建立是一个逐步的过程，发现单个的问题，首先解决问题，解决完后就需要思考一下：是不是可以通过流程规范规避类似问题。

就拿你这个例子来说，可以先把 CI 持续集成环境搭起来，然后在发现这个问题后，就针对这个路径的问题，提一个 Ticket，要求补上这部分的自动化测试代码。这样以后每次提交代码，CI 都会自动运行这个测试，出问题了就能及时发现，不至于到了生产环境再发现。

开发人员任务多可以理解，但是你需要把这些任务通过任务跟踪系统统一管理起来，写一个 Ticket 给他，排上优先级。等其他任务忙完，就该把这个任务给做了。

所以小团队乱，任务跟踪管理、开发规范，这都是需要优先建立的流程规范。

No.2
Joey：研发过程文档，是否有必要进行统一模版，比如方案设计文档、功能测试报告等。我们公司的现状是，整个研发部门 1000 人左右，如果不设置模版，大家五花八门，别人不好检查；如果设置模版，研发人员又说限制他们的想象力。

宝玉：我倒是觉得有模板的文档好写一点，填空就好了。对于文档模板，我没有什么建议，毕竟每个公司情况不一样。我经历过的公司没有强制规定要模板的，但会提供两种模板，一种是风格样式的，字体颜色等都采用公司品牌的风格；一种是基于内容的模板，把大标题小标题都列出来，写的时候填内容就好了。文档审查重点是检查内容，而不是格式。

No.3
Charles：小团队可能就 10 来个人，每个岗位可能就 1～2 个人，这种情况下做内部分享，希望大家都来参与，那么分享内容不好把控；如果太局限于本岗位知识，其他岗位人员参与度不高，效果也不明显。如果只是本岗位的知识分享，那么就 2、3 个人讨论下就行了，有什么好办法解决这个问题？

宝玉：可以设定一些学习的课题分享，比如说最近有什么新技术很火，但是大家都不知道是什么，也很想了解，可以让一个人去学习研究，然后跟大家一起分享，分享的过程其实以讨论为主，分享的人也不需要太多压力，自己也能学到东西，其他听的人在讨论的过程中也能学到东西，共同学习提高。你可以从中做好主持的作用，最好提前也学习准备一些。

No.4
yellowcloud：我们目前项目使用的管理工具是 TFS，它好像也自带 CI 和 CD 功能，我想请问一下，它和文中介绍的 Azure DevOps，哪个好用呢？

宝玉：Azure DevOps 应该是 TFS 的升级版，如果在线托管的话，你应该考虑用 Azure DevOps。

No.5
乐爽：详细的需求分析是放在迭代内进行的，但此时的需求是一个很小的点，所以不会占据整个迭代太多的时间，是吗？如果在迭代内发现需求方案不合理，放入到下一个迭代，这是否合理呢？

宝玉：是的，因为一个迭代内的需求不多，所以需求分析相对时间较短。如果一个需求在一个迭代内做不完，可以延到下一个迭代。如果一个需求不合理，那么需要重新讨论，讨论清楚了再决定是放当前迭代还是后续迭代。

No.6
hua168：小公司复盘，是这个弄好了，那个又变差了，也不想着怎么改进，强制执行大家都很抵触，怎么办？

宝玉：如果是解决一个问题又导致了新的问题，按下葫芦起了瓢这种情况，需要多在整体思考一下原因，尤其是项目的整体流程和开发计划方面。推广开发流程导致反感，觉得时间紧还搞其他这个问题，需要两方面入手：

首先要反省项目计划，如果只是加要求而不给相应时间计划，比如说要求写自动化测试，而不留出写自动化测试时间，那当然会抵触。所以相应的要制定出更好的项目计划，避免为了砍时间而砍时间，给开发留出时间去设计、去写测试代码，不然就算你制定一个很紧的计划，还是要花很多时间修 Bug，最终花的其实时间差不多。

提升大家的认识，不仅是团队内部，还包括团队外部，你的老板和业务部门，获得他们的支持。让大家知道磨刀不误砍柴工：前期投入时间在开发质量上面，后期会节约大量修改 Bug 的时间。

No.7
浮生：目前执行过程中发现，因为不是自己负责的功能，团队成员在审查其他人代码的积极性并不高，再加上各自任务都很紧，即使审查也是匆匆过去，有时并未起到应有的效果，请问在流程机制中有方法可以提高审查的效果吗？

宝玉：很抱歉我暂时没有好的建议。可以尝试的是：

首先强制 Review 才能合并是必须的；

让 PR 小一点，减少 Review 的难度；

时间进度上，考虑上代码审查的时间，毕竟代码审查是磨刀不误砍柴工；

鼓励资深的程序员做好带头作用，可以把 Review 代码参与度和 Review 代码质量作为绩效的一部分；

你可以每天检查一遍审查通过的代码，对于明显有问题的私下可以谈一谈。

No.8
胡鹏：我现在遇到一些情况，需求出来了，估时的时候，通常有两种心理。第一种， 尽量压缩自己的时间，当然领导也会压缩时间， 这时心里想的是要好好表现，把时间压短一点；第二种:，尽量多一点充裕时间，当出现问题能有足够的时间来解决，不至于延期对于这种估时，取一还是取二还是在一和二之间平衡？

宝玉：太紧和太松的时间估算都不可取，应该是尽可能准确地接近实际情况的时间，并且留有一点富裕应对意外情况。时间太紧了要加班加点还要被质疑能力；时间太松了会影响以后估算时间的真实性。

准确的估算时间是程序员能力的一种，做好不容易，一些建议供参考：

充分理解清楚需求，知道要做什么，这是基本前提，不然做着做着发现需求没搞清楚，那一定是要多出很多额外时间。

非功能性的需求，比如说写自动化测试、搭环境、重构代码这些任务也应该作为计划的一部分，要把时间算进去。

拿到任务后，将任务要分解到尽可能细，越小的任务力度估算越准确，而且在跟领导说时间进度的时候也有理有据，底气足扛得住。

综合考虑任务并行的情况，给线上版本修 bug、开会这些时间也要算进去，想想每天真正有效的工作时间是多少。

计划保持及时更新，当出现延迟或者有延迟风险的时候，或者进度提前，需要及时和项目负责人沟通，作出调整，避免影响整体项目进度。

留一点余量，应对突发情况。

反过来，如果你是领导，在下属估算时间的时候，也要参考上面的一些建议，让计划尽可能地接近真实情况，而不是下属给一个很紧的时间就按照这个时间执行，最后得加班加点，加班是为了应对突发情况的，而不是正常情况。

No.9
Joey：1. 如何更好地推广 SonarLint 白盒扫描工具？2. 如何要求各开发团队更好地，有效地做代码走查，而不流于形式？（我们现在使用 Gerrit）3. 如何要求开发人员有效实施单元测试？

我们一个研发部门人数 1000 左右，由于历史原因，大家根本不重视单元测试、代码走查以及白盒扫描，或者说这只是留于形式，走流程。久而久之，开发人员觉得我不关心这些东西，也没有出现很严重的事情啊，反正有测试人员对质量把最后一道关，我为什么要浪费时间呢？但是现在随着业务越来越多，系统越来越复杂，要从研发过程上加强质量，面临以上三个很突出的问题，请老师支支招。

宝玉：因为我对你的情况不是很了解，先简单答一下。这种开发流程问题肯定还是要自上而下推才能推得动。我觉得首先应该先找一两个小项目组试点，摸索出一套适合你们的最佳实践，形成流程规范，比如说基于 Github Flow，把 CI（持续集成）环境搭建起来（如果没有的话），把你说的 SonarLint、自动化测试加入到 CI 流程中。再就是逐步扩大范围，在更多项目组推行最佳实践和流程规范，并且改进流程规范。最后就必须要借助行政手段强制推行了。

No.10
Liber：我们专栏之前的文章中，以本文注册用户为例，分别写了小、中、大型测试用例，但实际开发过程中，如何权衡对一个场景，是该小、中、大测试都写，还是只写部分？

宝玉：实际开发中，理论上来说，是一个场景大中小测试都要写的。通常情况，开发写小型测试和中型测试，测试写大型测试，或者开发帮助写大型测试。小型测试：中型测试：大型测试比例大约为 7:2:1。小型测试尽可能多覆盖，不要求 100%，谷歌是 85%。中型测试覆盖大部分用户使用场景，小型测试覆盖主要用户场景。

No.11
OnRoad：客户需求频繁变更，大领导迫于客户压力全盘答应，导致开发节奏被打乱，除了量化风险上报之外，还有什么好办法？

宝玉：需要和你的领导私下协商，需要在他的帮助下一起作出一些调整：

要设立流程提高客户变更需求的成本，可以需求变更，但不能太过于频繁随意；

缩短开发周期，采用迭代模型或者敏捷开发，2～4 周发布一个版本，每个版本实现当前已经确定的最重要的需求，在一个版本内不接受需求变化，变化的需求放在下一个迭代中实现。

No.12
探索无止境：对于专栏中提到的“测试验收通过后，预部署分支的代码会部署到生产环境。”我的理解是，部署的分支的代码，上线测试没问题之后，再把这个代码合并回主分支，这样理解对不对？

宝玉：这里有两种策略：

每次线上 Bug，修复后只合并到预部署分支，最后统一把预部署分支合并会主分支。优点是简单，缺点是合并时可能会有很多冲突；

每次线上 Bug，修复后同时合并预部署分支和主分支。优点是以后就不用再合并回去，还有可以及时同步 Bug 修复，缺点是麻烦，每次要 cherry pick。

我们项目中选的是后一种策略，因为能及时同步 Bug 修复到主干对我们很重要。

No.13
maomaostyle：在敏捷开发中，如何结合标准的项目管理方法呢？比如 wbs 任务拆解，风险识别，因为这两点相对于项目的整体情况已经应该拿到了足够多的输入，但是在敏捷的背景下需求等细节都是不清晰的。另外比如最小化原型产品更难以结合大而全的项目管理方法了吧？

宝玉：敏捷开发中，wbs 一样可以帮助分解任务，然后把任务拆分到 Sprint，还可以设置里程碑。风险识别应该和用什么开发模型没太大关系，关键还是识别和确定应对策略。最小化原型法可以是小瀑布开发模型也可以是敏捷开发，关键在于需求要定义清楚，要小。

No.14
宝宝太喜欢极客时间了：方法论、方法、模型这些名词具体怎么理解？敏捷开发属于哪一种？实施敏捷软件架构设计等文档都可以省略吗？如果文档都省略了那如果开发的人员离职后新接手人员怎么快速的熟悉项目呢？公司的知识积累怎么体现？

宝玉：敏捷宣言说的：“工作的软件 高于 详尽的文档。尽管右项有其价值，我们更重视左项的价值。”没有否认文档的价值，也不代表实施敏捷软件架构设计可以省略文档，只是没有必要写过多繁重的、没有价值的文档。

另一个角度来说，也不要过分夸大文档的作用，离职交接，光文档还不够，还离不开人和人之间的互动，交流；公司的知识积累更多靠的是人、代码、文档、流程规范、文化等多方面因素综合的结果，而不光是文档。

No.15
Tiger：我们做的项目外包，项目组的人数是固定的，每次都是项目组要离职一个才会再招一个人进来补充，这种情况无法培养技术后备，人员风险怎么把控？

宝玉：这种确实有点困难，有两种策略你可以考虑：

减少对人的依赖，让人来了跟流水线工人一样可以马上上手。如果你的项目类型比较类似，其实可以考虑将相同部分通过架构简化，通过配置或者定制化适用于不同项目。

培养现有的人，提升现有人能力，归属感，减少离职率。都不容易做到，但都可以试试，或者你也可以想到更好的办法。

No.16
ailei：除了《人月神话》《人件》，还有哪些偏管理的软件工程的书？

宝玉：有几本项目管理的书可以看看：
《项目管理修炼之道》
《项目管理 - 计划、进度和控制的系统方法》
《软件项目成功之道》
《做项目，就得这么干!》

No.17
成：如果一周开发，一周测试，测试的时候，开发人员开始下个迭代，那 Bug 啥时候修改呢？如果下一个迭代期间也要修改 Bug，那本次迭代工作也进度也难以保证样，不是很理解如何操作？

宝玉：是这样的，开发当前 Sprint 新功能的时候，同时要修改上个 Sprint 的 bug。比如说这周是 Sprint 1.2，那么同时要修改 Sprint1.1 的 Bug。而且 Sprint 1.1 的 Bug 的优先级要高于 Sprint 1.2 新功能的开发。

其实改 Bug 通常不需要花太多时间，所以一般影响不大。如果偶尔 Bug 修改时间过长，不能如期完成的，需要推迟上线。如果团队不适应这种节奏，那么应该延长 Sprint 周期，例如两周一个 Sprint。

文章的例子只是一个参考，并不是说一定要这样做。

No.18
E：软件开发的过程和方法之间的关系是什么？

宝玉：软件开发过程就是指开发软件时整个过程的开发模式，比如说瀑布模型还是敏捷开发。选择了开发过程，你就需要有具体方法来执行。比如你选择了瀑布模型，整个软件开发过程就是按照瀑布模型的分阶段来进行，对应的方法就是瀑布模型中的方法，例如需求分析、架构设计；如果你选择了敏捷开发，则整个开发过程就是一种敏捷迭代方式，后面的方法对应的就是敏捷开发的一套方法体系，例如 Scrum、用户故事、持续集成等。

No.19
刘晓林：关于 Ticket 工期估算我有个疑问。团队中一般都是一两个人负责一个小模块，之所以这样做是为了提高工作效率，避免同一段代码每次迭代都由不同的人去修改，因为大家对自己的小模块很熟悉，所以工作效率很高。但这样带来的问题是，团队成员对其他人负责的模块不熟，所以工期估算只能由模块负责人自己完成，别人很难帮上忙。这种情况怎么解决啊

宝玉：这是个好问题。我的建议是模块要换着做，宁可慢一点，不然的话不仅仅是其他人不能帮忙不能估算，万一有人离开团队了，麻烦更多的。如果团队不大，甚至于做的时候，分工都不要太细，都不要太局限前端后端，这样其实对整个团队来讲是最好的，互相能替换。当然，也不要着急，慢慢来，不要一下子改变很大。

No.20
谢禾急文：我想到一个想法，就是通过用一个工具记录我自己开发过程中遇到的所有 Bug，通过记录、分析、反思这些 Bug，能够有助于提升我的编程能力，有助于避免犯同样的错误。我觉得你上面说的那些工具，能够满足我的需求。如果有一个网站，能够提供 Bug 记录、分享、解答的工能，是不是能够满足某些用户的需求？(好像 stackoverflow 就是这样的工具)

宝玉：我觉得是有帮助，但这个问题的关键在于分析反思 Bug。自己对自己 Bug 的反思才是价值最大的，其他人看过之后不一定能有那么大的共鸣，因为一个 Bug 都有复杂的业务背景，是很难被记录，缺少上下文也很难理解。StackOverflow 是很有价值的，因为它是从问题切入，而问题是有很多共性的，很容易引起共鸣。

No.21
纯洁的憎恶：提高学习能力，用知识体系对抗复杂多变的问题。提高解决问题能力，明确问题，拆分与定位问题，提出解决方案并总结。塑造影响力，深耕业务能力成为专家，做事超出预期，尽可能多的输出价值，分享是施加影响力的有效途径。

我很早就知道知识体系的重要性，我也比较重视构建知识体系，但并没有什么亲测有效的方法，且对知识体系是个什么样的存在缺乏体感认识。可能还是学得太浅，用的太少。

宝玉：方法不是最主要的，最多让你学习提升一点速度。关键还是坚持，多练习多实践。

从知识转变成技能，一定需要通过反复的刻意的练习，才能形成条件反射，最终掌握。没有任何学习方法能替代练习，最多有像催化剂，可以加速练习效果的学习方法。

还有就是对技术的学习，不能太依赖于工作上的输入，工作上如果项目好用户多，那还是很有挑战的，但大多数时候没有那么多挑战，可能就是个增删改查，那么几年的工作经验可能只是简单的重复，不能达到刻意练习的效果。那还是要在工作之外寻找一些练习的途径，比如上次我建议的：自己做一点项目、参与一些开源项目。

要想对知识体系有体感认识，还是建议先在一个领域有深度，有一棵树了才能想像出来森林是什么样子的，不然只能看到一片灌木丛。这过程难免要踩很多的坑，经历很多次的失败和挫折，反复的思考、总结和重试。

No.22
titan：敏捷开发在一些小公司落地是比较难的，原因我认为主要是人的综合素质达不到，敏捷的一些思想和原则不能落地，比如团队成员人人平等的价值观，在小公司，牛人比较少，大部分都是比较弱的人，你让牛人跟他们强调平等，似乎是不太可能的事情。

宝玉：平等和牛人，这其实不矛盾的。就像蜘蛛侠的叔叔说的：能力越大责任越大。牛人担负的责任会更大，贡献多，收入也多。一个健康的开发团队，无论大小，都应该是有梯队的，有资深的，有新人，资深的（牛人）负责架构、模块划分、实现核心模块，新手则基于架构实现具体模块。不然单靠个别牛人完成功能也是不现实的。敏捷开发在小公司落地，最根本还是真的懂敏捷，能应用好敏捷的原则和实践，不要追求形式化，不要走捷径。

精选留言

alva_xu ：
就“选择适合你的软件开发模型”，这一点，我谈谈想法。软件开发模型是瀑布还是敏捷对于软件开发管理来说有很大的不同；但即使采用瀑布模型，对于团队管理来说，我们是可以借鉴敏捷模型的。

比如，我们可以采用看板管理来提高任务管理的效率和透明度，可以通过站会来加快问题的沟通，对于“建立外部提交需求和任务的流程”，我们也可以借助敏捷管理的思路，通过每天站会或者周例会的时候，一起做个新需求评估。对于技术交流，也可以像敏捷团队里说的培养 T 型技能的人员为目的来开展。

而且，先通过团队管理方式的转变，培养大家的敏捷文化，然后再切到敏捷开发模式，就会更加顺畅。我觉得，小团队管理，一定要培养自主自治合作分享的文化和能力，通过用轮值 Scrum master 的办法，一点点提高这方面的文化和能力。

相关阅读：40 | 最佳实践：小团队如何应用软件工程？

纯洁的憎恶：
抛弃妄念，脚踏实地。切忌追求过于宏大的目标、过于新奇的技术，而最终难以落地。做事要有边界和约束，向死而生才有效率。专业短板可以尝试自行补齐，也可以求助他人取长补短。

相关阅读：41 | 为什么程序员的业余项目大多都死了？

思辨时刻

幻想：
我觉得两周为一个发布周期，很有可能导致代码质量低下。例如：两周一迭代里，我们可能没有时间在上个迭代里就做好下个迭代需求的分析，只能遗留到当前迭代，这个时候，需求分析、代码设计、接口设计就要花好几天。好了，由于限制死了两周要发布一次，导致测试人员死盯着发布日期，进行倒推，让开发人员尽量在某个时间点提测，不然迭代上线就风险很大，这样就导致了开发这边压力很大很大，开发时间短，代码质量低，提测后，又各种 bug，也进而阻碍测试进度，整条线都非常疲惫紧张。

最惨的是，由于测试人员急着测试，也未能做到详细测试，就上线了。又是各种线上 Bug。因此这种两周一上线，会容易让人死盯着上线日期，给全部人员带来很大的压力，相当于是给自己挖坑和约束了，很不应该的。

我觉得软件工程里，开发阶段是最关键的阶段，得给到合理的时间，不然这个阶段被动了，乱了之后，就会产生一系列的不好级联反应。因此，我觉得应该有开发人员来把控节奏，给出工作量，给出哪些可以优先测试。

宝玉：
好问题！你说的担忧完全合理，也确实可能会出现这样的情况。

我来解释一下为什么 2～4 周是可行的。我们假设你现在的项目是三个月周期，一共是 12 周，然后你大约 2～3 周在需求，2～3 周架构设计，4 周左右在编码，2～3 周测试。

那也就是说需求分析期间，其实开发、测试做不了啥事，架构设计的时候，主要是架构师在忙，编码的时候，主要是程序员在忙，测试的时候，开发和测试在忙。

再假设你大概要完成 10 个功能，也就是这 10 个功能从设计到开发预计花了 10 周时间，平均每周一个功能。

如果换成 2 周一个迭代，那么我们可以考虑每个迭代只选取 2 个功能，但是在这 2 周，整个团队的运作可能是这样的：

迭代 v1.1（2 周）
产品设计，准备下一个迭代 v1.2 的产品设计；
开发，设计和开发这个迭代 v1.1 的功能，同步修复发现的 v1.0 的 Bug；
测试，测试上一个迭代 v1.0 开发好的功能；
开发完成后，部署开发完成的 v1.1 到测试环境；
发布测试验收完的迭代 v1.0。

迭代 v1.2（2 周）
产品设计，准备下一个迭代 v1.3 的产品设计；
开发，设计和开发这个迭代 v1.2 的功能，同步修复发现的 v1.1 的 Bug；
测试，测试上一个迭代 v1.0 开发好的功能；
开发完成后，部署开发完成的 v1.2 到测试环境；
发布测试验收完的迭代 v1.1。

也就是你差不多还是有两周时间开发新功能，两周时间测试，但是每两周可以发布一个小版本，而且整体节奏比较平缓。如果到时间内完不成所有功能，那么就发布完成的，没完成的放到下一个迭代，这样可以保证每周都可以发布。配合代码审查和自动化测试以及基于分支开发的流程，可以保证合并后代码质量相对是可靠的。

如果这样操作有难度的，那么采用 4 周一个迭代，但是每个迭代功能减少，还是一样可行的。还有每个迭代结束后的上线发布，可以有两种类型，小迭代可以不发布生产环境，只是测试环境，几个小迭代后再发布生产环境。也就是说，方法其实是有的，观念上可以先调整，因为这样的迭代周期肯定是可行的。

相关阅读：40 | 最佳实践：小团队如何应用软件工程？

纯洁的憎恶 ：
深深地感受到，软件工程不是为了创造最伟大的软件项目而存在，却是为了保障每一个项目的成本、质量、工期、目标等等可控而存在的。

果然如此：
软件工程是过程控制的方法论，而产品设计才是保证伟大的产品，两者应该结合。

相关阅读： 42 | 反面案例：盘点那些失败的软件项目

kirogiyi ：
软件工程方式的使用，或多或少会受到最高领导层管理理念的影响，这从各大公司的组织架构图可以看出一些端倪，比如：Amzon 的组织架构图，领导力准则得以全面体现，精确而清晰；Facebook 的组织架构图，更利于信息的快速传递和响应，管理方式相对其他公司更加扁平；Google 的组织架构图，上层倾向于层级管理，下层倾向于扁平管理，适合于公司指令的上传下达，也适合于不同层级之间的工程师进行沟通交流进步成长。

如果领导层倾向于规范化流程化，那么采用 Amazon 的开发方式，明确的分工，明确的目标，这使得贝佐斯的领导力、执行力、远见力得以全面实施。

如果领导层倾向于激进和冒险，那么采用 Facebook 的开发方式，只要你够积极，不断创新，即使犯错也是一种进步，不得不说这种方式在小公司开发团队中实施起来更可行，毕竟小公司需要快速响应，快速迭代，快速决策，不可预料的事情比较多。

如果领导层倾向于人性的发挥，那么采用 Google 的开发方式（个人认为适合资金比较雄厚的公司），它能让工程师在舒适的环境中充分发挥所长，并去尝试开拓自己感兴趣的新的技术领域，各自都对自己的领域精雕细琢，质量无形中就得到了一定程度上的保证。

从上面来看，我算是一个激进和冒险的人，更喜欢 Facebook 的开发方式，使我能够在不断的创新和错误中成长。

宝玉：你这个角度也很新颖！一个公司的文化和创始人的性格是有很大关系的，这些文化都没有绝对的好坏，都成就了伟大的公司，合适的就是最好的

相关阅读： 44 | 微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的？

传说中的胖子：
我以前学习技术，就是看怎么实现，或者说是怎么用；现在学习技术，是学习技术在什么情况下产生的，适合解决什么场景下的问题，需要的资源是什么。多学习一些技术以及使用场景、然后在出现问题的时候可以结合实际情况做多种选择，根据其他因素原则一个比较合适的方案，方案确定了，技术实现就会方便很多。因为在 IT 行业边缘化的三线城市，也不知道这种想法有没有什么遗漏，希望老师帮着补充。

宝玉：
我觉得从思路上是没问题的，从实践的角度一点建议：技术只有通过实践才能真正清楚其优缺点和使用场景。建议有些新的流行的技术，哪怕项目中不使用，业余时间也可以自己去试试，这样能给你未来的项目实践有更好的指导。当然也不要走偏，学了一个新技术就要应用到实际项目中，如你所说：学技术的目的是为了帮助你更好的选择，选择了合适的之后才是应用。

相关阅读： 45 | 从软件工程的角度看微服务、云计算、人工智能这些新技术

文末彩蛋

至此，我们专栏的全部加餐内容已经更新完毕。非常感谢为“一问一答”专题贡献精彩留言的同学们，是你们为专栏提供了精彩的问答、新鲜的案例、行之有效的学习方法，感谢你们将自己的获得分享给大家。

在此，极客时间团队为入选专题“精选留言”和“思辨时刻”的同学给予奖励。

留言贡献率最高前 3 名：

alva_xu

纯洁的憎恶

kirogiyi

以上 3 位同学，每人将获得极客时间“Hello，World，超大防水鼠标垫”一个，极客时间团队将在一周内发货。

热心留言贡献者：

阿杜（javaadu）、老张、阿银、hyeebeen、起而行、西西弗与卡夫卡、Felix、MiracleWong、青石、刘晓林、一路向北、果然如此、dancer 、陈珙、Y024、nigel、_CountingStars、bearlu、Charles、林云、邢爱明、成、毅、yasuoyuhao、幻想、传说中的胖子。

以上 26 位同学，每人将获得极客时间15 元电子优惠券一张，优惠券将于 6 月 18 日 18 点前发放到以上同学的极客时间账户，优惠券有效期截止为7 月 15 日。

感谢阅读，如果你觉得这篇文章对你有一些启发，也欢迎把它分享给你的朋友。

结束语 | 万事皆项目，软件工程无处不在

你好，我是宝玉。不知不觉，我们专栏就接近尾声，也该告一段落啦。

在专栏更新之初我就提到，把每件事都当作一个项目来推进，现在我也用“万事皆项目”来作为我们专栏的结束。

我学习软件工程最大的收获，就是在看问题的时候，不再局限于从技术层面或者是一个局部去思考问题，而是站在整体，用软件工程的方法去指导自己的思考和决策。

所以在整个专栏的讲述中，我也希望能给你带来这样的转变：在做一件事情之前你可以先考虑一下，这是不是可以当作一个项目来推进，站在整体思考，有目的、有计划、有步骤地解决问题。

万事皆项目

实际上，日常工作生活中，不仅是软件项目可以应用软件工程的知识，很多事情都可以应用软件工程的知识。就拿我们《软件工程之美》的专栏来说，这也是一个项目，从专栏诞生到完成，你从中也可以感受到对软件工程知识的应用。

项目都是从一个想法开始的

在写这个专栏之前，我曾在微博上多次建议从事软件开发的开发人员学习一下软件工程，这是一门非常有价值的学科，所以后来极客时间邀请我开专栏，我首先想到的就是写一个软件工程相关的专栏，而极客时间还没有这样的专栏，所以我们初步达成了做软件工程专栏的意向。

项目开始之前不要忘记可行性研究

在有想法后，并不是直接就立项开始做，在软件工程中会有可行性研究。在专栏开始之初，也是有一个磨合的过程，我会试写几篇稿子与极客时间内容团队一起打磨，双方都需要通过磨合来确认是不是可能继续合作，当磨合好后，双方觉得没问题了才会最终立项。

如果没有经过可行性研究就贸然立项，到项目开始才发现不适合，那对双方都是不负责任的。所以项目开始之前，先进行可行性研究，是避免损失的有效方式。

项目未动，计划先行

一个项目开始之前，制定计划是保障项目正常推进的关键。一个专栏 40 多篇文章，每周更新 3 篇，如果没有一个科学的计划，没有 Dead Line，那是很难保证项目进度的。所以要做到项目未动，计划先行。

在制定计划时，也不能盲目乐观，还要考虑到项目风险。比如说作者临时有事可能就会有断更的风险，这时就需要对风险进行控制。

在具体实现一个项目之前，先进行分析和设计

在做一个软件项目的时候，首先要做的就是分析需求，然后根据需求分析的结果设计架构。对于一个专栏来说也是这样的，首先要考虑清楚专栏的主题是什么？专栏的受众是谁？他们想要什么样的内容，然后再去设计专栏的目录。

可以说专栏的目录对于一个专栏的成功至关重要，如果没有目录直接天马行空去写，可能会导致写出来的内容偏离主题，用户也不容易理解。当然目录也不是一成不变的，在写作的过程中，根据大家的反馈，我也会做一些微小调整。

从瀑布模型到敏捷开发，从集中交付到持续交付

传统书籍的出版，类似于瀑布模型，作者统一完成书稿，然后交给编辑修订，最后出版。而现在的专栏写作，更像敏捷开发，作者每完成一篇内容，就交给编辑审阅，反复修订后才可以上线发布。《软件工程之美》专栏采用的是持续交付模式，甚至于提纲写好了就先交付，编辑给出反馈意见后，进一步补充完善细节，最终完成上线。

测试和线上维护也很重要

我平时在网上也会写一些文章，通常写完就发出去了，甚至都没有仔细检查，结果经常是有读者留言才发现很多错误。但专栏文章的发布，每一篇除了自己检查，编辑还会对内容进行校对，避免出现错别字或者语法错误，从而保证大家阅读专栏时的体验。

软件发布后还会对线上的版本维护，保障稳定运行。专栏每一篇文章上线后，都会有同学留言，对同学的留言进行回复也是很重要的工作，可以帮助同学解答一些文章中的疑惑，也可以从同学们的留言中收获很多有价值的反馈。

项目完成后，不忘总结复盘

如果说软件工程之美专栏是一个项目，现在也到了总结复盘的时候了。

首先从做的不太完善的地方开始，总结经验教训。我平时在对于知识的理解和学习，更多是停留在思考上，日常并没有养成习惯去把这些思考写成文字。写作专栏的过程，是把这些知识系统思考梳理的过程，如果日常只是零散的思考，写作的时候就需要对很多细节进行学习和补充。

这其实也是我在《学习攻略 | 怎样学好软件工程？》中给你的建议：在学习软件工程时，尝试把学到的知识写下来，去教给其他人，这样你的收获也会是最大的。

接下来，我们复盘下哪些地方做的比较好。

软件工程的知识虽然不像一些编程语言或者框架更新的那样快，但也一直在演进。就像十多年前还是瀑布模型为主流，现在越来越多的是敏捷开发和快速迭代的开发模式；十多年前每日构建还是当时最好的实践之一，而现在持续集成已经取代了每日构建成为新的最佳实践。

我觉得我还算比较幸运，有很多不同的项目和团队的经历，有小创业团队也有大厂。因为大学转专业学习了软件工程的原因，也一直在留心观察工作中的软件工程实践，所以有机会去学习和实践很多软件工程知识，并在这个专栏中，将这些知识输出交付给你。

在专栏更新的过程中，确实引起了不少同学的思考，提出了很多有价值的问题。通过留言，让我有机会去解答一些文章中没有涵盖的内容，通过留言也诞生了很多对专栏精彩的补充分享。通过这些留言，我也切实地感受到很多同学从专栏学习中有收获、有进步，这也是我认为在专栏项目中做的还不错的地方。

埋下一颗种子

日常生活中很多事情，就像去写一个专栏，并不是一个软件项目，但是你应用软件工程的知识去指导去推进，一样可以帮助你有计划、有步骤地完成它，一样可以让你有很多机会去实践软件工程的知识。

你对软件工程知识实践的越多，你对它的理解也会越深刻，这样当你在做软件项目时，你就能更好地应用这些知识，帮助你高质量地完成项目。

通过对大家留言的观察，我也发现那些已经有丰富的项目经验的同学收获是最大的，因为他们很容易将丰富的项目经验和软件工程的知识串起来，把零散的知识点借助专栏的学习一点点构建成了完整的软件工程知识体系。

如果你现在觉得对这些软件工程知识的吸收有限也没关系，当你从第一天开始学习软件工程，就相当于埋下了一颗种子，当今后再遇到项目中的问题，相信你可以跳出细节之外，站在项目的整体去思考，去软件工程的知识中寻找答案，一点一滴的积累，这颗种子不久后就会变成参天大树。

专栏从上线至今，已经 4 个月有余，如果你能坚持学习并真的学以致用、学有所有，这将是最美好的事。虽然现在专栏更新结束，但我们还可以通过留言区再见！谢谢你一路的支持，祝工作顺利！