大晚上的有个朋友也没睡,在Gtalk上找我聊敏捷,一聊就聊了一个多小时,谁叫我是话唠呢?我对敏捷的了解并不深,也许说的有很多不对的,不过下面这些东西都是我的一些思考,所以,贴出来就是露怯,我也不刻意藏拙了。
11:42 PM 朋友: 老兄,怎么看到现在,说的很热闹的敏捷开发方法
像ThoughtWorks这种公司
像ThoughtWorks这种公司
11:43 PM Tinyfool: 我接触的不多,方法确实很好
11:44 PM 朋友: 所谓的敏捷开发,主要是为了解决软件开发哪些问题?
Tinyfool: 其实还是软件工程问题
就是几个点
多人怎么协作
程序的质量怎么保证
11:45 PM 软件项目怎么组织,怎么计划
应该就是这些老问题
11:47 PM 朋友: 恩,这些老问题,其实不只是软件开发,好像只要是多人协作的工作,都会遇到
敏捷方法,能解决哇
还是,在探索过程中
Tinyfool: 大多数问题都还是可以解决的
11:48 PM 朋友: 就是说,已经有了一些好的经验和模型?
Tinyfool: 只不过有的时候在于你实施的怎么样
或者说,很多中软件工程的方法不是解决不了问题,而是实施不下去
11:49 PM 你用C写程序,写错了,编译器就不给你编译
但是软件工程是个管理问题,人是不确定的动物
我对敏捷的认识大概是这样的
11:50 PM 朋友: 很有道理
Tinyfool: 传统的软件工程方法不是不好
但是往往很笨重
或者说问题的前提很笨重
比如,人月神话,里面的前提,是历史上最大的一个项目之一
11:51 PM system 360
但是现在的开发团队,往往都没有那么大
或者说,我觉得应该不要太大
敏捷可能说,方法轻量级一些
所以更加的灵活一些
11:53 PM 朋友: 学习:)
Tinyfool: 我对敏捷了解的也不多,叶公好龙一下而已
11:54 PM 某些方法我们在用
或者想用
更多只是了解了一下而已
朋友: 哦
从不用敏捷方法,到用,中间会遇到什么需要改变的吗?
或者比较难过渡的地方吗
11:55 PM Tinyfool: 相对来说敏捷应该算好实施的吧
11:56 PM 朋友: 这敏捷倒是个美妙的概念
Tinyfool: 恩,不过也有些问题
就是很多概念看着很简单
朋友: 实施了敏捷,但是还出了问题,也可以把责任推到人身上
哈哈
Tinyfool: 但是真正要做好,其实也不是那么简单的
呵呵
你这么说也可以
我给你举个最简单的例子
11:57 PM 敏捷有一个方法叫做结对编程
简单说起来,这个应该是最好实施的
朋友: 恩,倒是听说,我一直觉得这个东西很诡异
一个哥们在旁边看着,干啥啊
Tinyfool: 就是说两个人写一个程序,不是说以前那种你写一段,我写一段,各自坐在座位上面写
对,一个写一个看,就叫结对
11:58 PM 简单说起来,你会觉得这个太好实施了
但是其实不然
这个东西的好处其实很多
我先给你说说好处吧
1、可以增进沟通
11:59 PM 在公司里面,号称是做一个工作,大家都写一个程序,或者是都做一个网站
但是也经常完全没有沟通
相互之间,可能会做出来重复的工作,或者是做出相反的工作来
12:00 AM 结对呢,代码是一起写的,两个人思路可以比较紧密的联系起来
2、避免错误
12:01 AM 人的思维,容易出错,但是自己很难发现。比如,很多人说他把一个问题想明白了,你让他描述一遍,他立刻就会发现他的逻辑漏洞百出
但是他自己想的时候,就会觉得全都是想得通的。
12:02 AM 结对,就给了你一个机会,每一步思路写成代码,旁边有另外一个人用他的脑子去理解你的代码。就像是有人听你讲一遍你的逻辑一样。
这样,你也很容易发现你的逻辑漏洞
如果你发现不了,他可能会发现,因为两个人很难同时犯一样的错误,几率太小。
12:03 AM 3、可以带新人
等等
但是实施起来,却没有那么简单
比如,两个人怎么做,是不是双屏,有时候都是实施成功的关键
坐
12:04 AM 如果旁观者,没有办法快速清楚的看到,写代码的人的代码,那也没啥用处
朋友: 恩,我觉得还有意见不一致,到底听谁的啊
12:05 AM Tinyfool: 或者,就算客观条件,你都具备了。可以项目经理总是会觉得两个人浪费在一个代码上,是一种巨大的浪费。
虽然他知道,如果真的两人结对的话,往往可以会效率更高。但是他往往不敢放心的去这么做。
朋友: 恩,我也觉得,成本太高,经济危机下,肯定会咔嚓掉坐在边上的一个
Tinyfool: “恩,我觉得还有意见不一致,到底听谁的啊”这个好办,如果出现了不一致的意见反而是好处,呵呵
12:06 AM 这说明找到问题了
其实成本是低的
但是,从人的直觉上觉得成本是高的。
12:07 AM 你现在貌似多余的放了一个人在那里,但是这能大大减少低级错误,和逻辑错误,维护成本会低多了。
但是人们往往会根据直觉行事。
朋友: 这么说对
Tinyfool: 这也是另外一个敏捷方法
单元测试,很多团队没有办法实施的原因
单元测试,也有点反直觉
12:08 AM 传统的测试,是程序员写好程序以后,提交给质量控制的小组,测试人员写测试代码,或者人工测试
这个很符合直觉
但是其实弊端很多
测试人员是黑盒测试
12:09 AM 不知道实现方式,所以有很多典型的问题,从代码一望而知的问题
他们如果没有经验就完全猜不到
所以就是撞大运
这也是为什么有编程经验的人做测试,往往水平会比较高的原因
他知道程序员常见的一切错误
12:10 AM 比如边界问题,代码在最大最小值容易出错,输入0容易出错等等
还有管理方面的问题
我以前在金远见的时候,同宿舍的人是搞测试的
12:11 AM 他就告诉我,产品有一测二测三测,终测
测试组的老油条,经常在一测二测三测发现了小问题迅速报告
12:12 AM 发现了大问题就不说
朋友: 啊
Tinyfool: 等到终测的时候,再报告
因为,如果一测二测三测都没问题,测试组奖金就少
但是一测二测三测出现bug的奖金没有终测的高
12:13 AM 所以他们会留到终测,这样可以拿到超额的奖金
这对开发组来说就很被动
朋友: 。。。。。。。。。。。
Tinyfool: 最后终测有些大bug根本没时间好好改了
12:14 AM 只能延期,或者是简单的绕过去,看看能不能骗过测试组
朋友: 呵呵
Tinyfool: 单元测试代替不了传统测试
但是他是程序员做的
而且按照标准的流程,也就是测试驱动的原则
12:15 AM 你要先写单元测试
然后写程序
比如,你要写一个算法,实现100以内的数字×2这个功能
12:16 AM 你要先写一个测试样例,for循环1到100,然后×2,看看等不等于他们的2倍。
写完了这个测试样例,你才能去写实现。
写实现的时候,你要先写一个错误的实现
比如,不管输入什么,都返回0,或者-1
这样执行一下测试样例
得到失败的结果
12:17 AM 这才完成了第一部
然后,你才应该去写正确的实现
朋友: 恩,这个太繁琐了
Tinyfool: 写好了,执行一下测试样例,对了,你的代码才算写完了
对,确实太繁琐了
所以,以至于我是很认同这个原则的
但是我很少这么写
12:18 AM 但是这个东西非常有用
对保证质量作用极大
程序员写代码经常会写出来那种
你随便用两个数字测试一下,他是对的
12:19 AM 但是用另外几个数字测试就会错的代码
这种就很难自查出来
单元测试就是要杜绝这种问题
这是潜层次的
还有更深层次的问题
程序大了以后的问题
12:20 AM 这就涉及到了敏捷的思想的综合使用了
写程序之所以需要软件工程,其实差不多就是三件事情
我前面说了
协作怎么协作
质量怎么保证
怎么维护
维护是最大的问题
12:21 AM 经常是
维护,也不是像一般人想的,只有软件卖出去了才有维护的问题
很多软件要写几个月到几年
而你昨天写的代码,你今天就可能全忘光了
12:22 AM 所以,往往程序还没写完,维护问题就出现了
举个最简单的维护的例子
比如,你有一个算法是算工资的
朋友: 恩
Tinyfool: 很简单,就是 日平均工资×21
12:23 AM 你就写 s = c * 21
这个多简单
但是你这个系统很复杂
到处都需要算工资
你就把这句话到处粘来粘去
代码中有几百个这句话
12:24 AM 有时候,你需要算工资减去保险
那么就有了 s1 = c*21 - w
好了
你觉得没什么大问题吧
现在出问题了
老板说了,我们以后按照22天算工作日
12:25 AM 我这里还是简化了问题,有公司按照每月实际工作日算工资的
你就需要在代码中,把所有的这种21都变成22
朋友: 恩,有道理
Tinyfool: 你不能用查找替换
因为别的算法里面也有21这个数字
12:26 AM 你替换了别的算法就错了
这个s = c * 21直接替换s = c * 22,也不行
因为你有s1 = c*21 - w形式的
还有空格多了少了
就没办法了
这就是我们程序员说的代码有坏味道
12:27 AM 想不坏很容易
你把算工资写成一个函数
function s(c){ return c*21;}
老板说了,21改成22
你不用查找替换,也不用改100处
你该函数里面就可以了
对吧
12:28 AM 朋友: 恩,是的
Tinyfool: 这其实就是最简单的抽象
我的原则是,代码重复超过两遍
就要函数化
因为它就有了重复100遍的趋势
12:29 AM 随着你的工程的变大,很可能就会100遍
就像人类说名字,第一遍是全名
第二遍再提这个人,就用代词了
这就是抽象了一下
12:30 AM 朋友: 这个比喻好啊
Tinyfool: 软件是活的
你一开始写s=c*21;没有坏味道
因为他只有一遍
到处都用味道立刻就坏了
所以,你要随着项目的增长不停地去改善你的代码
12:31 AM 这其实叫重构,也是敏捷方法之一
重构是很多人心向往之,但是不敢为之的事情
一方面是很多的项目经理,觉得你在浪费时间
因为抽构完的代码味道虽然好,但是功能没有任何的改变
12:32 AM 另外一个方面,是怕你把程序改错了
第二个方面最重要
因为按照先贤的统计
你修正一个bug,就会引入4个bug
也就是说改代码总是危险的
尤其是,代码有可能不是你写的
12:33 AM 或者说,虽然是你写的
但是你已经不是你了
你已经是几十天以后的你了
你都忘了,当时这个函数是做啥的
注意事项是什么
12:34 AM 还有,本来一个程序已经对了,经过了测试组的测试
然后你什么新功能都没出来,就去再测试一次
老板和项目经理都不会喜欢你的
这时候
12:35 AM 单元测试的好处来了
单元测试是重构可以进行的保障
真正的重构有两种保障
第一,是IDE和其他工具的机制保障
这个说起来有点深了
最早重构这本书的意义就在与此
12:36 AM 人人都有自发的重构的思想
那么干嘛要这么书呢?
这本书,把重构方法论化,提出了重构的一些具体方法
这些方法都是非常具有操作性的
这就提升到了理论的高度
12:37 AM 这样,很多现代IDE就集成了这些重构方法的功能
这样,你就不会手工去重构
朋友: 我得好好读读你说的东西,对明儿的访谈特有帮助,
Tinyfool: 举个最简单的例子,你有个函数名字起的不好
12:38 AM 你要改掉,最简单的就是查找替换
但是这里有个问题
如果字符串里面提到了函数的名字怎么办?
只是凑巧一致而已
这就错误的替换了
12:39 AM 而IDE的重构功能里面的函数改名,就可以保证只替换函数声明实现,以及函数被调用的地方
不会改掉凑巧一样的字符串或者是其他的东西
这是重构正确的第一个保障
第二个就是单元测试
因为有了单元测试
所以,你可以放心大胆的修改函数的实现细节
12:40 AM 只要你修改后的函数还可以通过单元测试就可以了
像现在著名的一些开源软件
常常有数万个单元测试
12:41 AM 可以保障,不管你修改代码中的任意部分,都可以确保,你修改过的代码和以前的代码功能一致
而对于这种大型软件
修改以后,人工去核对功能是否一直,简直就是不可能完成的任务
前些日子,我帮人做一个词典软件
有个小bug
查某些词的结果不对
12:42 AM 这就很难办了
不是全不对,某些词又对
所以,最后我的做法,就是把词表倒出来做了一个5万个词的单元测试
每个词都查一边比较和预期的结果到底对不对
12:43 AM 这样找出了100个不对的
这100个不对的,其实对应了5种不同的情况
当然我事先不知道
我就先看第一个不对的
跟踪程序
找到原因
修改,然后再跑单元测试
12:44 AM 这下只有60个不对的了
就这么我其实就找到了5种情况,就保证了程序的百分百正确
如果我不做一个单元测试出来
我很难找全这5种情况
12:45 AM 我修改了一个bug以后,也不知道是不是解决了所有的问题
你理解这个例子吧?
朋友: 恩,明白了
Tinyfool: 在单元测试思想出来之前,你遇到这种
问题
是很难彻底解决的
12:46 AM 单元测试可以帮助我们开发出来几乎可以说完全没有错误的程序
朋友: 哎,通过你这些例子,俺对敏捷是长进了一大块
Tinyfool: 只靠黑盒能找到我刚才那个错误中的两三个情况就不错了
12:47 AM 我的了解还是很肤浅的
Tinyfool: 哈哈,那倒是可以去看看
12:54 AM 朋友: 打扰你这么长时间,真不好意思啊
12:55 AM Tinyfool: 呵呵
别客气
我把你的名字隐去,然后发blog你不介意吧
呵呵
12:56 AM 朋友: 哈哈,这怎么会介意
我刚想说,你刚才的内容,真可以写一篇文章了
Tinyfool: 呵呵,我自己写就懒的写,跟人聊心态轻松点
朋友: 算是对敏捷的一个梳理
12:57 AM Tinyfool: 我的了解还是不够深入
朋友: 哈哈哈,那,以后,想写博客前记得找我
Tinyfool: 好像到现在为止还没看过一本全面的敏捷的书呢,呵呵
:)
后:
聊天是我最没有压力的写东西的方式,前些日子的TL聚会有人反应我一个人说了3/4的话,就是因为如此。如果是聊天,我就可以没有压力的海阔天空的说,一旦正正经经的写个文章,我就会花大量的时间去考据,查资料,以免露怯,所以我最近一两年写东西越来越少。结对编程是我最心向往之的敏捷方法,可惜一直没有好好的实践过,这反而我和霍炬倒是经常一起结对设计,呵呵。8月23日,即本周日,Beta沙龙的话题是我的合伙人霍炬来讲我们如何在应用中使用Snmp协议来进行服务监控的,欢迎大家参加。


请不要吝惜您的评论,每一条评论,都是我在漫漫长夜前行的力量
4 条评论:
你的朋友是绝佳的“倾听”者。
11:43 上午
你这不就是说我话唠么,呵呵
11:55 上午
的确是很好地一个访谈,说得很到位。应该整理成一个文章到哪里发表一下,要不可惜了。
从我实践的角度来说,我的组,在双人编程、重构,执行效果非常好,但是在单元测试方面,坚持了一段时间,就逐渐蜕化了。
软件工程,其实就是一个一个的实践。从基础性的,例如版本SVN管理、Bug管理等等,到高级一点的双人编程、重构、单元测试、Code Review……一点点形成习惯。
关键还是人,敏捷编程,好像是“轻”了,其实一个代价是,对人的要求高了,人必须主动参与,主动去实践。而不是被动的被管理。
有个规律是,越是老手密集,或有热情的人密集的团队,敏捷实施的成功可能性就高一些。如果都是不太积极的成员,或者有内部矛盾,就比较麻烦。
6:22 下午
呵呵,这样放在这里就好了,我懒得再整理一下
8:12 下午
发表评论
<< 主页