β

敏捷的反馈原则与非功能需求

Juven Xu 231 阅读

在敏捷软件开发理念中,我体会最深的一点是反馈。 结对编程的过程中,我敲下的每个字符都有可能得到同伴的反馈; 用测试驱动开发的方式写代码的时候,代码会反馈我说设计有问题或者实现错误; 当我的代码不小心破坏了同伴的测试用例,持续集成也会在数分钟内给我反馈; 当系统间的集成有问题的时候,自动化部署加上自动化验收测试也能在几十分钟内把问题反馈给我。 只要实践了上述方法,短则数秒钟、多则数十分钟,我都能得到真实地软件状态的反馈,从而我能够及时修复错误, 使得错误引入后所造成的损失能被压得非常之低。

上述讲的都是技术手段,非技术手段的反馈作用也是类似的。 例如每日的站立会议能帮助团队成本之间及时发现潜在的问题: ”什么?你打算修改线程池配置?你得当心,我之前测试过……“; 迭代的计划会议和回顾会议能让团队不要浪费时间在不必要的事情上,即消除浪费。 站立会议的反馈周期是天,迭代的反馈周期通常是一周或者两周。

反馈是如此重要,当反馈来得太晚,或者说在损失已经很严重才得到反馈的时候, 我会想这个时候敏捷能帮我做什么,或者说这个时候是不是敏捷方法已经无能为力了?

在一些访问量比较大的互联网应用场景中,对于很多非功能需求是比较难得到全面快速的反馈的。 例如用户登陆,对于这个功能本身,使用单元测试/持续集成/集成测试就能给我们即快又全的反馈。 然而,实际环境中用户数量可能从预期的100万变成实际的200万、用户的网络环境千差万别、 还有很多搜索引擎爬虫、可能还有恶意的攻击、服务器可能挂掉一台…… 诸如此类的因素会导致线下测试完全OK的服务到了线上可能会不稳定甚至挂掉。 我们称这一类需求为非功能需求,或者说是ility需求(security, scalability, reliability…), 虽然名为非功能需求,但是这类需求的重要性一点都不低,想像一下如果你登陆一个网站要花3分钟,你还会用它么? 然而不幸的是,要快速获取非功能需求的反馈,传统的敏捷方法似乎略显苍白无力。

“ility”测试

敏捷测试会强调所谓的“ility”测试,即专门测试非功能需求,模拟海量用户、模拟攻击,以尽早暴露非功能需求的问题。 这是一个很好的方法,切实实践益处良多。(想像一下,应用上线前一天你测试发现当同时在线用户数到1万的时候,网站会无法响应) 然而“ility”测试和难做到完美:

  1. 要模拟真实访问量,往往需要和实际生产环境一致的机器,这可能是几十台虚拟机,也可能是多个物理机集群, 实际工作中,由于成本考虑,实际工作中往往难以做到,因此造成测试的真实性大打折扣。
  2. “ility”测试的实施成本往往比一般的功能测试高很多,具体场景也更难预测,因此一般大家只会做主要场景的测试, 但是实际线上一些非典型性场景也会导致严重问题。
  3. 如果“ility”测试的实施时间比较晚,那么往往会发现,网站的性能问题是由架构决定的,而这个时候修改架构的成本已经很高, 这个调整不仅仅是代码结构层面、还包括运维层面的调整。

”ility“测试能提供很多反馈,然后很显然,完全靠它是不行的。那么实际中大家是如何保证网站平稳运行的呢?

经验与计算机理论

在实际工作中,我遇到有一些人,他们并不关心、也不了解敏捷开发方法,他们也不遵循一套严格的流程, 但是,他们能够很准确的判断系统哪里可能会遇到性能问题,并且能在架构初期就考虑到这些。 我们常常说,这是架构能力,那么架构能力是怎么来的?其实答案无外乎两点,一是经验、二是计算机理论。 他们大多都经历过很多类似的场景,因此即使说不出一二三所以然,感觉也能告诉他们风险可能在哪里, 当然仅仅经验还是不够的,伴随着经验的积累还需要计算机理论的补充和学习,包括计算机结构、操作系统、网络、JVM等等。

如果你的团队有这样的人在,那么恭喜你,你能在最早就避免掉很多非功能需求风险。

如果像我这样缺乏架构能力怎么办?答案很明显,抓住机会参与到具体工作场景中去,去理解整个系统是怎么运转的, 伴随地再学习相关的计算机理论,同时多阅读相关资料,理解业界大家遇到什么场景,具体怎么解决。

我们还能依靠什么?

你做了很完备的测试,经验和理论也告诉你一大堆坑已经避开了,但同时经验也告诉你,应用上线后总有无法预知的问题可能发生,怎么办?

我们不妨向运维去取取经。

作为开发,我曾经修改了应用之后和运维沟通希望他们做运维调整能让我的变更尽快上线,我们有一段对话是这样的:

开发:我的代码做了一些修改,我希望你能帮我调整集群A…再调整集群B…
运维:这么做太危险了,我觉得方案应该是 A‘… B’…. C‘… D’….
开发:这么做太复杂了,直接 A… 再 B… 简单得多
运维:你怎么保证上线后不出问题?
开发:我经过了完备的测试,理论验证,可以保证99%不出问题。
运维:换句话说,没人能保证上线后100%不出问题,对吧?
开发:这…
运维:如果万一上线真出问题了,怎么办?我是说万一。
开发:厄————
运维:我的方案虽然实施起来更复杂,但是能保证如果上线出了问题,系统能快速回滚到之前状态。

对于运维来说,他不知道应用程序内部是怎么运转的,他必须随时预期应用程序会出严重问题, 这时候,回滚通常是最快速,最有效的恢复服务的手段。

另外一种有效避免风险的方法是Staging环境测试,Staging环境是一个与生产环境一致的环境, 唯一的区别是没有实际用户访问,把代码发布到这个环境之后,可以自己模拟用户访问,从而尽早发现由环境引入的问题。 由于Staging环境和生产环境一致,必然会有更多的控制及相关流程,导致基于此环境的测试成本比较高, 因此不宜在这个环境上运行过多测试,而应该以测试环境为重点。

不论测试多完备、流程多完善,实在是有那么些问题是我们预想不到的,这个时候就需要上让实际的用户去用从而发现问题。 金丝雀部署正是这样一种做法, 具体做法是在生产环境中部署两个版本的代码,通过负载均衡让小部分用户先访问较新版本的代码,其余大部分用户访问较稳定的老版本代码。 这样即使新版本代码出了问题,也能让保持影响面在比较小的范围。有了一定比例的真实用户访问,非功能性需求的反馈就会非常全面, 而且把用户切回旧版本的成本也非常的低。

小结

敏捷软件开发理念中的反馈原则真是再怎么强调也不为过,然而正如你看到的,面对很多软件的非功能需求,传统的敏捷实践方法能量有限, 这个时候我们就需要在现有方法的基础上去思考和总结新的方法。业界在DevOps这方面积累了不少经验,值得我们关注和学习。

作者:Juven Xu
Maven, 开源, Java, 敏捷
原文地址:敏捷的反馈原则与非功能需求, 感谢原作者分享。