Text

developer.51cto.com

项目经理被问到最多的问题就是,“这个项目什么时候才能完成?”

被问的时候,可能项目才定下来,仅仅知道大概的功能模块,非功能性需求还模糊不清,甚至团队成员都没到位。但是上级、销售、客户急切地要知道,这个项目什么时候才能完成?

被问的时候,也可能项目已临近结束,或者说临近当初计划的交付日期。然而待完成的功能还有一堆,测试出来的bug有一大堆,客户又提出了新的需求,团队正有人要离职 …。但是上级、销售、客户非常急切地要知道,这个项目到底什么时候才能完成?

这还不算糟糕。更头疼的问题是:“再有三周,项目应该完成了吧?”

因为后者根本不是问题,而是命令。项目经理必须要能够合理解释为什么三周不能够完成项目;或者说明在三周内,能够完成什么。

我们都用过MS Project, 但是那上面的漂亮表格对这样的困境毫无帮助。相反,正是Project 中的甘特图和日程表,埋下了陷阱。因为,在Project 中无法预估需要多少工作日才能完成模糊不清的需求,也无法体现实际情况发生变化后对进度的影响。

当我们讨论进度的时候,其实包含了两个未知的变量。第一是完成需求所要的工作量,包括需求定义、开发内容边界;第二是团队的工作能力,包括成员的行业知识专业技能,成员之间、成员和外部的沟通能力,等等。

关键就在于,这两项都是变量。如果任务是搬一千块砖头,每分钟每人能搬10块,那么结果是显而易见的。

在敏捷开发中,采用相对估算和迭代求精的方法来处理项目进度的问题。

首先是工作量。用估算代码行数或者界面元素的方式,就像论斤卖书一样,只适用于粗制滥造的软件生产过程。用户需要的并不是代码或者按钮,而是可靠易用的功能。

在敏捷开发方式中,先由用户和设计人员粗略估计各个功能模块的相对规模和难度,给出一定的分值。分值不代表具体人月,起相对比较的作用。例如有查询、显示、修改三个模块,如果实现显示模块的工作量是10分,那么查询模块可能是15分,而修改为20分。

下一步,选择一个工作量估分最低的模块,例如这里是显示模块,然后进一步考量其工作量。例如要准备数据库、设计界面、执行查询,显示内容等等。假设这轮估算得出此模块需要10人天,从而得出单位分值对应的人天为1;那么,整个项目就需要45人天。

这个估算建立在对项目的初步了解上,主要依赖项目经理的经验。有偏差?没关系。接下来通过迭代来求精。先来实现显示模块,如果事实上花费了12人天,那么根据比例关系,剩余内容的估算大约就是42人天。

当然,比例关系也不是一成不变的。随着模块的逐个完成,项目经理对项目的认识也在加深,他可以再调整剩余模块的相对分值。

在实际操作中,项目经理首先按照优先级排列功能模块。然后把高优先级的模块尽可能地细分,再选择分值最小的模块开始开发。统计总工作量时,按比例累加其他模块的工作量,并加一定的调整系数,因为模块的复杂度不是线性增长的。每次迭代开发完成后,逐步降低调整系数。通常4~5次迭代后,可以将调整系数归零。

在上面的例子中,第一次估算的初步结果是45人天,因为完全是凭经验,因此要给较大的调整系数,比如说0.4,因此给出的估算工作量区间为[45*0.6,45*1.4],即27到63人天之间。为保险起见,项目经理上报的工作量为70人天。

第二次估算,剩余内容的初步估算为42,调整系数下降为0.3,因此给出估算区间为30到50人天之间。依此类推,通过不断迭代,对剩余工作量的估算将越来越精确。

这样估算的好处在哪里?

首先,工作量变量的很大一部分因素,存在于非功能需求,例如界面的美观程度。而同一项目的不同模块之间,非功能需求往往是一致的,相对估算法过滤了这一层复杂度。团队能力这一变量因素也是如此。当然,随着项目的进展,成员的开发能力应该有一定的上升,但随着加班出差等因素,投入程度也可能下降,因而会相互抵消。总之在周期6个月以内的项目中,很少出现团队工作能力戏剧性变化的情形。因此相对估算也过滤了这个复杂度。

其次,迭代求精的方式让项目经理对估算时间更有把握。最初出现偏差是必然的,但只要团队稳定,没有大的需求变动,估算范围将迅速收缩。这比一次性报数更准确。

它的额外好处是,敏捷开发是遵循优先级的,即使对剩余时间(即低优先级模块的开发时间)的估算不十分准确,影响也不是非常大。

对比一下甘特图方式,在开发初期就要把各个模块的开发时间估算出来以统计总量,这就是瀑布开发的模式。

进度问题的另一方面,是项目经理如何了解团队以及每个开发人员的开发速度。当任务分配之后,项目经理如何做到心中有数,估算任务实际完成时间。

敏捷开发过程中,由开发人员自己来估算完成该任务所需要的时间。当然,每个人的能力不同;每个人的心态也不同,有的人保守,有的人乐观。没关系,还是靠迭代来逐步求精。

在每天的例会上,开发人员被要求对当前任务的剩余开发时间做重估。不同于Project 统计每人每天在任务中花费了多少时间,敏捷方式只关心这项任务还需要多少时间去完成,直到归零,然后再来统计实际的工作时间。

为什么?因为统计开发过程中的花费时间是毫无意义的。这和搬砖头不同,也许昨天用了8个小时没有一点进展,今天一旦想通了就事半功倍。我们真正关心的,就是到底还需要多少时间来完成任务,而不是已经花费掉不可恢复的时间成本。

在每天例会中,项目经理需要注意时间曲线保持水平的成员,他是不是遇到瓶颈了,是否需求帮助?也要留意时间曲线下降幅度过大的成员,他发现了什么好的办法,有没有低估需求?这样,项目经理会更面向结果,只要按计划保证质量完成任务就行,成员到底花了多少时间是个人的事。传统做法记录每个人每天的工作内容,第一是因繁琐而失真。其次,一旦上级发现某人工作时间不够(即便他完成了任务),忍不住会派新任务,从而造成越干活越多,反过来打击程序员的积极性。

敏捷估算的关键之处,是把成员能力这个变量的估算,交给最合适的人去做,即程序员本人。然后通过比较历次迭代时的预估和实际时间,给出校正系数,以避免程序员过于保守或过于乐观。这肯定不是绝对准确的,但效果往往比项目经理自己拍脑袋估算,然后强行指定deadline 要好得多。

在敏捷开发中,做计划比计划本身更重要。项目经理需要时刻向前考虑,考虑各种动态因素,而不是死报着计划本身。在进度估算的时候,项目经理应该在不同阶段,根据实际情况,给出合乎情理的回答。

Text

developer.51cto.com

前两天,在一次聊天中,我问了问未来开发组的组长:眼看设计过程就要结束了,项目启动到现在进行了3个月的时间,想一想,前一阶段我们所做的工作中,有百 分之多少是对后面的工作有意义的?开发组长无奈的苦笑了一下。显然大家心里面都有数,这个比例很不理想。尤其是设计文档中的模块设计部分,大量的工作量, 完全是为了设计文档能够评审通过。

对于敏捷软件开发,目前理解其最重要的目的是识别软件过程中没有必要的任务或者是性能低下的任务,然后去除之或者改进之。基于这个出发点回顾一下目前的情况:

需求分析。唯一不变的就是变化。项目的早期集中时间进行需求分析然后确认基线,再等到真正动手开发某一模块的时候可能已经过去了一段时间,并且需求已经发生了变化。这样还需要对新的需求进行再次分析。在这样的情况下,前后的需求分析则存在工作量的浪费。

敏捷过程则是需求分析存在与整个项目的始终。细节的需求分析工作尽在真正开始代码之前才进行。虽然以后仍然有可能发生变化,但起码针对上面的情况做了优化。

软件设计。目前的项目,设计阶段需要的交付物有:架构设计,概要设计,详细设计,物理模型设计,接口设计等等。设计的目的当然是为了能够知道开发。这一部分工作显然是必须的,但是能否优化呢?

敏捷过程典型的对策是

1.仅仅做足够的设计;

2.测试驱动开发。

系统的架构设计是需要的。但是针对某一功能来讲,仅仅在开发之前才做相应的详细设计。设计仅仅做到能够指导开发的程度,对文档的格式要求比较宽松。

传统的UML设计包括类图、顺序图、协作图、活动图、状态图等等。这个敏捷过程一般没有强制要求。XP过程中推荐以TDD和CRC分析来代替。其中CRC分 析基本上是UML协作图的替代物,但是更加强调团队共同完成。而TDD则是在保证了测试自动化的同时还能够对软件设计进行指导,做一件事达到两个效果,体现其高效率。

界面原型。我前面说过,希望能够在需求分析阶段就能够对系统的界面进行确认,还找了一些专业做页面原 型的辅助工具。现在看来,其实也存在浪费。其实更高效的做法是以最快的速度拿出开发出的成品或者半成品让用户进行确认(这样一来,唯一的问题是你能有多 快?;-) )。界面草图有的时候是需要的,但是它变成了非常不正式的中间产物,不必要进行存档。

重构。仔细想想,其实重构的工作对于任何一个项目来讲都是必须的。而现实是,如果没有采取全面的自动化测试的话,传统的软件开发项目没有能力也不敢进行深度的重构。而相反基于TDD的XP团队则更有勇气对软件进行彻底重构。

集成。集成的工作量无法节省。敏捷过程的做法是持续集成――将工作量分散到整个软件生命周期。这样能够及时发现问题,并从感受上减少工作量。

我有一个朋友,看他上网打桥牌的时候,经常发现他的搭档在抱怨他发生了失误。而实际的情况是怎么样的呢?我的朋友是全国顶级桥牌赛事的前四名获得者。那些被抱怨失误的牌都是由于他的搭档的水平不够,对于他的精妙招数无法理解。

在软件开发领域,其实我们也正在处于我的朋友的搭档的水平。经历的项目越多,越会发现业内大师的很多理论的精妙之处。很多情况是:如果大师们的理论你认为有很大问题的话,其实是因为你的层次还不够。

Text

aqee.net

桌子


做一张桌子看起来十分的简单。任何人都可以做,不是吗?找个大块的四方形木板,四个等长的木头柱子,四个钉子和一个锤子。把四个柱子钉到四方板的四个角上,你就做成了一个桌子。当当当!

现在来让一个真正的木匠为你打造一张桌子。首先他要花时间跟你讨论桌子的用途和功能——用在室内还是室外,厨房还是餐厅,主要用来展示还是使用,它需要能够承受多大的负载。然后他需要确定使用什么样的材料——软木还是硬木,用木板还是三合板还是铁轨枕木。然后他会研究桌子的风格——桌边的截面设计,桌腿底座设计,桌腿是按在中央还是四角。这最后,他才去动手做这张桌子,他花大量的时间去斜截,凿眼,契合所有的连接点,安装撑条,使用高质量的粘胶,木钉,螺钉,他要检查表面平整,打磨它,着色,封蜡,抛光——打造出一张能够引以为豪的桌子。看起来是一大堆的事情要做,不是吗?它只是一张桌子,不是吗?

两种做桌子的方法存在着很多不同之处,你看出来了没有?

只用了四个钉子的桌子看起来很毛糙,它不稳,因为桌腿不牢固,桌脚不平,顶多能用一个星期,它的一个腿就会扭断,根本承受不起一个盐罐子的重量。木匠的桌子看起来更好,好用而且美观,它很牢固,不摇晃,桌腿经过矫正,直的,很平,没有棱角,能用很久,你完全可以放心的在它上面摆上一顿丰盛的晚餐跟一群客人享用。你更想要哪个?

当人们谈论起软件,大多数人想像的工作量很像第一张桌子的建造过程。只需要创建一个数据库,填上一些数据,画出一些界面,这就完了,你就得到了一个软件产品。我无论如何都想不出人们这样的一种认识是怎么产生的。也许是人们用惯了Excel——“我可以用Excel做这些,所以编程应该很简单”。什么?

开发软件不容易。需要做很多的工作。非常像那个木匠做桌子的方式。作为设计师和程序员,我们需要理解业务、用户用例,以及产品功能。我们需要讨论和理解谁将要用它,它有什么功能,它如何和其它软件集成,它要用在什么地方,数据量如何,等等,等等。我们然后才去花大量的时间搭建架构,确保它能承受用户和数据负载压力,要能够升级和扩展,要能处理大数据量情况(例如单表负载)。我们然后需要更多的时间写出有质量的、可读的、可维护的代码,测试每个组件,确保它们正确、快捷的运行,改正bug,清除瓶颈(在数据库表模型上使用正确的结构和关联)。我们还要使用更多的时间来把这些组件组合到一起,确保每个接口都正确的调用,确保错误都被捕捉到,确保界面设计可用,简单,美观(桌子的切截,钻孔,契合,打磨,着色,抛光)。最终,我们交付客户一个完整的可信赖的产品,一个符合需求的产品,一个我们和你们都引以为豪的产品。

我相信,木匠很少会遇到像程序员需要面对的那样让人抓狂的事。也许他的客户也会抱怨做这个桌子花了太长的时间,或造价太高。也许他的客户并没有说桌子的用途或应该是个什么样,然后大声的抱怨它跟他从未说出的期望的样子不一致。也许他们的客户会瞪着他说做这样的桌子只需要几块木板和几个小时,木匠做桌子已经有几千年的历史了,这种桌子能有什么不同。也许他们的客户会说这很简单,所以应该很快,很便宜,魔术般的做出来,我猜测他们是这样想的。

我们的客户完全也是这样。而且几乎每次都这样。从没例外过,最初给我们新需求时说“这只需要你花几个小时的时间…”,或“你可以这样做…这样更简单”,或“你只需要简单的把它改成…”。什么?

对软件产品增加新功能可不像多钉一颗钉子或多加一个柱子。我们需要理解新功能的形式和功用,创建它,测试它,写文档,确保新增加的功能不会影响现有的系统。如果我们不这样做,软件就会做出错,到处是bug,不能正确的运行,给用户来带来大量的问题。有很多软件项目都是这样,任何用过这种有问题的软件的人都不会信任它,甚至不愿意使用它。就像那个摇晃的桌子。

如果你需要一个新功能,尽管提,但你要做好准备跟我们的木匠——程序员们认真仔细的讨论它。我们会用正确的方案开发你要的应用,保证让它正确的运行,一直正确的运行。你到我们这里来是想要有质量、可信赖的软件,就像是你要从木匠那得到一个有质量的桌子。

千万不要告诉我们这样多简单,多容易——除非你自己做过。如果你真的认为能既迅速又简单的做出来,你自己试试。干吧,拼装出一个摇晃的桌子。

如果你希望得到一个好的产品,你要理解明白开发需要时间和技能,有很多你根本想象不到的问题在里面,工匠们也是人,是有感情的,一个好的产品是一个永远让人快乐的事,值得我们付出努力和耐心,这样,它将成为我们所有人能引以为豪的东西。

[本文英文原文链接:It Should Only Take You a Few Hours… ]

GTD系列教程3:顶级知识管理工具Evernote的GTD应用详细指南

GTD的核心理念是“迁出大脑内所有杂事,纳入一个可靠的外在体系(行动提醒系统 + 资料归档系统)直接管理”。换言之,就是把所有杂事控制在“手边”,而非“脑海”里。究其实质GTD是一个自激循环的正反馈闭环管理系统。

GTD系列教程1:GTD精义和待办事宜服务Toodledo实践

日程表上排的满满当当,突发事件总是不期而至,接踵而来,害得你手忙脚乱,顾此失彼,忙忙碌碌了一整天,却颓然发现劳而无功一无所成,仿佛陷入深不见底的泥潭,越是挣扎越是无法自拔?

想要寻求超脱困境拔出泥潭的办法么,试试GTD!

拒绝平庸——浅谈WEB登录页面设计

传统行业的商家极为重视门面的装潢,因为一个好的门面可以聚集人气,招揽更多的顾客。

Text

aqee.net

bigmac_newmlogo

编程很像吃饭。

晚上我饿了。我不在家。我不能到厨房里找点东西吃。我四周看了看,看到只有一家街对面的麦当劳。

于是,我就进入了一个任何时候我离开家、离开我那舒适的小窝后遇到的两难境地:是吃那些垃圾食品,先填饱肚子呢?还是先饿着肚子,等回到家后去吃更健康的食物?

当我在思考这种困境时,我不禁或多或少会把这个问题跟代码质量作一定的联系。当然更多的还是先考虑吃的问题。

我的话只是表达我自己的观点,我并不认为麦当劳是一个能够提供我健康食品的餐馆。作为一个追求绿色食物的人,甚至只是作为一个注意饮食的人,麦当劳的食品远在我的健康食品的标准之下。但是,我相信,跟你们很多人一样,我们已经养成了吃麦当劳里的食物时不再思考这些食物的健康价值了。我饿了,麦当劳里有食物。

既然我已经认识到了麦当劳食物对我身体的短期和长期的影响,我就会全力避免去吃它。对所有预处理过的食物都要这样。这对我来说是漫漫长路。决定去改变自己的饮食习惯并不是一蹴而就的事,
健康饮食是一种持续动作

很快你就会发现这一切都跟编程有联系。

健康饮食是一种持续动作?

麦当劳的食品不是一种健康食品。然而吃巨无霸汉堡加水果沙拉会比吃巨无霸汉堡加油炸土豆条要稍微健康一些。吃星巴克的金枪鱼会比吃大汉堡要稍微健康些。在本地咖啡连锁店里喝一碗汤要比吃星巴克的沙丁鱼要稍微健康些。在家里吃新鲜蔬菜做的食物要比其它任何地方的食物都健康。

按这样的方式看食物,我们会得出两点信息:

  1. 所有的事情都是相对的。你永远都可以做到更好,也一直可以做的更差。吃一个巨无霸汉堡并不意味着你会死,而为了能在家里吃饭而忍住一天不进食也未必更健康。
  2. 每一顿饭都是一个新的开始。人很容易落入这样一种陷进:“既然我昨天已经吃了一个大汉堡,不妨今天就再吃一个了”。这种思考方式是毫无逻辑的,也是有害的。每顿饭要吃什么都是一个新的决定。即使你今天早餐吃了一个大汉堡,午餐时吃家做的食物也是你的健康饮食之旅上前进的一步。

把程序代码当作食物

很像食物,代码质量的保证也是个持续的过程。

从简单层面上我们可以认为,一个没有测试和文档就发布的程序包要比有测试和文档的相同的程序包质量要差。

可是,在这些一眼就能看清楚的问题之外,还有很多说不清楚的东西。没有人都对什么是”健康”的代码有自己的标准。有些人非常清楚自己的编程问题,并在不断的改进自己。有些人数年来一直写着相同烂代码却从来没有想过这些代码是否“健康”。

做出健康的选择

像对待食物那样对待程序代码。

就像你需要吃饭一样,你最终需要发布你的程序。那么,如何处理这短期和长期里的影响呢?

如果你按照吃麦当劳食物的态度,发布了烂程序出去,那么,短期内你的需求是满足了。但从长期看,你将会一直受到它的影响。你的代码将会很难维护,需要你投入更多的精力。它对你长期的生活和健康没有什么好处。

如果你遵循吃家做食物态度,短时间里你的需求可能不会得到满足。这短期的影响是你会饿会儿肚子,但这样做的一个好的理由是:它对你长期的影响是正面的。你的程序代码会更容易维护,bug会更容易解决,而且需求变更后修改起来更容易。你的程序代码会有一个更长更健康的生命期。

抉择

写健康的代码跟你保持健康饮食一样,需要:认识和承诺。从吃垃圾食品到健康食品需要你认识到有些事情需要改进。对这些认识采取行动需要你对决定作出承诺。

改进你的编程习惯需要同样的事情:认识到有些地方需要改进,并承诺做出行动。

通常来说,你的抉择需要符合实际。你必须要吃饭,你的软件到一定时候必然要发布。找不到健康的食物,麦当劳必须是你的选择。我不知道你的软件项目会有什么样的限制因素,有时你不得不发布烂程序。但你要记住, 健康的编程是一个持续过程。用沙拉换掉你的巨无霸汉堡边的炸薯条。

世上没有‘最健康’的食品。你可以花上一整天来烹饪你认为的最健康的食品,但如果它没有进入你的胃里,就谈不上有什么用处。 代码质量很重要,但如果你不发布它,一切都没有意义。

最好的获得更健康的代码的途径是持续的改进。让你的程序每天都有改进,而不是期望一朝一夕就丢掉你所有的不好的编程习惯、期望以一个全新的更健康的来代替。

[本文英文原文链接:The Hungry Programmer ]

Unix目录结构的来历

Unix(包含Linux)的初学者,常常会很困惑,不明白目录结构的含义何在。