显示标签为“软件工程”的博文。显示所有博文
显示标签为“软件工程”的博文。显示所有博文

2007-07-16

Joel论管理

李广和程不识二者的比较对我们有何借鉴呢?对软件工程管理(领导)有何借鉴呢?

在大洋彼岸的Joel估计是没有机会来从《资治通鉴》吸取营养了,不懂汉语可不能怨别人,但却不影响其对此问题的思考和得出的结论。在其Three Management Methods系列中,对三种类型的管理的见解很有见地,值得看过以后回味发散一番:

The Command and Control Management Method:此方法即来自军队的管理方法,其基本思想就是人们做你告诉他们做的事,如果他们不听,就向他们吼叫,如果还不听就惩罚,直到去做为止。Joel认为此方法对高科技团队无效:

  1. 人们不喜欢被命令。尤其是自认比其他任何人都聪明的程序员们。
  2. 管理者没有精力对每个人实行微观管理,因为每个人做的事情都不同,不像在军队,一个命令对大家都管用。因为没有精力对每个人工作有那么详细的了解,不可能对每个人下正确的命令,只有执行者自己掌握的信息才是最准确的。
  3. 公司不同于军队的地方是职员在感觉不爽的时候可以辞职,有选择的自由。越是优秀的职员可选择的机会越多。

军队用这种方法也不是因为士兵喜欢这样,而是只有这种方法可以让人迎着枪林弹雨冲锋陷阵。士兵逃跑有军事法庭处理,而员工辞职受法律保护。

The Econ 101 Management Method:Econ 101是多数美国大学里的一门面向非经济学专业学生的基本经济学课程。放在这里当然是讽刺那些对经济学一知半解,但却热衷在管理中使用经济激励的管理者。这些管理者认为所有人都被钱所驱动着,因此让人做事的最有效方法就是物质奖励和物质惩罚。同样,Joel认为不可行:

  1. 用外在激励代替了内在激励。本来人们有做好事情的内在自然欲望,当管理者无视这些内在驱动力,而用外在的物质奖励驱动时,人们会受Overjustification Effect影响,美好的自我实现欲望被引导为物质追求,内在驱动力被埋没。而外在激励在强度上要远弱于内在激励,因此,奖励反而起到相反的作用。(这也就是所谓的《奖励的惩罚》)
  2. 这种方法会刺激人们寻找最大化收益的方法。奖励总会有个标准,比如多少行代码多少钱,如此,大家都会努力多写代码,而不是努力开发高质量的软件,更不是努力开发能赢利更多的软件。(道高一尺,魔高一丈)
  3. 这种管理方法的最大问题是它更本就不是管理,管理者拒绝思考和培训员工如何让事情做得更好,只是用金钱引诱大家做事,用各自的方法去做。

管理者很重要的职责是创造一个系统,包括各种工作流程等,在其中大家能把事情做好。在系统中,大家可以在基本原则指导下充分发挥主动性完成工作。

The Identity Management Method:努力使团队向一个方向努力,这个方向指向管理者想达到的目标。和Econ 101方法相反,Identity方法激发团队成员的内在驱动力,让所有人和组织保持一致和认同。

  1. 整个团队有共同的愿景,是一个整体,像一个联系紧密的家庭。大家在一起相互信任和忠诚。
  2. 为了保持一个方向,要和成员共享必要的信息,让大家有足够的信息做决定,而非命令。

Joel当然是支持Identity方法的,因为这就是他管理自己公司的方法。

从Joel对上述三种管理方法的分析来看,虽然没有读过李广和程不识的故事,却好像专门总结了二者的治军方法。The Command and Control Management Method就是程不识的治军方法,而The Identity Management Method对应李广的治军方法,所谓:

得赏赐辄分其麾下,饮食与士共之。

广之将兵,乏绝之处,见水,士卒不尽饮,广不近水,士卒不尽食,广不尝食。宽缓不苛,士以此爱乐为用。

2007-07-15

激励

李广和程不识在管理上,一宽一严,都成功了。士兵爱戴李广,愿意随之出生入死,而司马光却告诫后人不用随便学李广,除了说李广是学不来的,后继者无法继续外,还说:

夫小人之情,乐于安肆而昧于近祸,彼既以程不识为烦扰而乐于从广,且将仇其上而不服

至此,司马光这个老封建的反动本性显露出来了,诬蔑我们“人民子弟兵”好逸恶劳,还试图扼杀李广式的爱兵如子的将军,以免大家有逃离大棒,转投胡萝卜的想法。这事放到现在判一个反人道主义罪或反人类罪应该没啥问题!

当然,对于一个千年以前的人愤怒是毫无意义的,心平气和地分析才是更重要的。

李广真的不能学吗?其实不是不能学,而是一般人学不会。李广“行无部伍、行陈,就善水草舍止,人人自便,不击刁斗以自卫,莫府省约文书”,以至“咸乐为之死”,这对一般人来说太高深了,对士兵实行的是内在激励方式,使士兵自己主动卖命,而不是需要后面跟着一个端着刺刀逼迫才前进的。这当然不是例行公事的领导所能做到的,因此才说后继的很可能就做不到。

程不识呢?和李广相反,“正部曲、行、伍、营陈,击刁斗,士吏治军簿至明,军不得休息”,靠的是严酷和纪律,但“士卒亦多乐从李广而苦程不识”,典型的外激励方式。这种方式需要做到严格执行,例行公事即可。

这两种方法的境界有高低,但效果很难说哪个更好,得看环境。抛去阶级感情来看,对于军队来说,司马光分析的还是很有道理的。军队里工作方式是要真正拼命的,要使内在驱动力达到能使人拼命的程度,激励的成本是很高的,相较之下,由国家机器在背后支持下的大棒式外在激励却成本低廉,对于全国大规模推广具有可行性。

2007-07-02

Cunningham's CRC for OOD training

做开发的恐怕没有人不知道“面向对象”这个词,虽然对其不感冒的人也是有的,但毕竟是极少数,大部分程序员还是极力拥抱“面向对象”的。您要是指责某人不懂“面向对象”,非跟你急不可。

对于“面向对象”的特性和特征,大部分人应该还是说的上来的,但要说动手练练,那又是另外一回事了,“挂羊头卖狗肉”者大有人在,即用“面向对象”语言写着“过程式”程序,还一口咬定“我这就是货真价实的‘面向对象’,假一赔十!……”

其实,这也不能怨这些人,老师就这么教的,即使老师没有这么教,也没有说不能这样做,国内教科书也一样,简单堆砌了一些概念就以为“面向对象”结束了,结果的确学会了“类”、“成员变量”、“方法”……并且也能和以前用“过程”语言(如C语言)一样写出程序来,一样能跑通,于是简历上就加了一项“精通面向对象程序开发……”

曾经也经历过这个阶段,还用这种方式写出不少代码,这些代码还进入了正式系统 -_-!

最终的迷途知返是因为看了一些国外牛人的书,再加上之前摸爬滚打过程中的体验,才在“原来如此!”的惊叹中明白自己原来根本没有懂过“面向对象”,继而庆幸这些没有白读。

如下是一篇OOD文章中的片段,其中有一部分介绍了如下的CRC面向对象设计教学法,眼睛为之一亮:“今儿这文章没白读”

In 1989, Kent Beck and Ward Cunningham taught classes on OO design, and they had problems getting people to abandon the get/set mentality. They characterized the problem as follows:

The most difficult problem in teaching object-oriented programming is getting the learner to give up the global knowledge of control that is possible with procedural programs, and rely on the local knowledge of objects to accomplish their tasks. Novice designs are littered with regressions to global thinking: gratuitous global variables, unnecessary pointers, and inappropriate reliance on the implementation of other objects.

Cunningham developed a teaching methodology that nicely demonstrates the design process: the CRC (classes, responsibilities, collaboration) card. The basic idea is to make a set of 4x6 index cards, laid out in three sections:

  • Class: The name of a class of objects.
  • Responsibilities: What those objects can do. These responsibilities should focus on a single area of expertise.
  • Collaborators: Other classes of objects that can talk to the current class of objects. This set should be as small as possible.

The initial pass at the CRC card is just guesswork—things will change.

Beck and Cunningham then picked a use case and made a best guess at determining which objects would be required to act out the use case. They typically started with two objects and added others as the scenario played out. They selected people from the class to represent those objects and handed them a copy of the associated CRC card. If they needed several objects of a given class, then several people represented those objects.

The class then literally acted out the use case following these rules:

  • Perform the activities that comprise the use case by talking to one another.
  • You can only talk to your collaborators. If you must talk to someone else, you should talk to a collaborator who can talk to the other person. If that isn't possible, add a collaborator to your CRC card.
  • You may not ask for the information you need to do something. Rather, you must ask the collaborator who has the information to do the work. It's okay to pass to that collaborator information he needs to do the work, but keep this interaction to a minimum.
  • If something needs to be done and nobody can do it, create a new class (and CRC card) or add a responsibility to an existing class (and CRC card).
  • If a CRC card gets too full, you must create another class (CRC card) to handle some of the responsibilities. Complexity is limited by what you can fit on a 4x6 index card.

A recording made of the entire conversation is the program's dynamic model. The finished set of CRC cards is the program's static model. With many fits and starts, you can solve just about any problem this way.

2007-06-30

Java/OOP Code Inspection Checklist

Code Inspection的checklist可以分两方面内容:基本编程原则检查和语言相关检查。

基本编程原则

基本编程原则对大多数语言都适用,尤其是面向对象语言。

  • 格式
    • 由于有IDE(Eclipse, Netbeans……)的支持,代码格式可以很容易地自动编排,统一大家的代码格式很方便。
    • 乱糟糟的代码看起来很晕,没法让大家集中精力检查。
  • 命名
    • “编程语言”也是“语言”,需要传递信息。编程语言的规则能保证计算机能理解,但不能保证人能很好地理解。为了让人能理解,必须在变量、方法、类等命名方面下功夫,让人见名知意。
    • 有一些基本的规则,如:类用名词、方法用动词、变量用名词等,可以参考各语言约定俗成的编程规范。
    • 好的命名可以极大增加程序可读性。文档、注释也是为了理解程序。相较而言,程序同时还是最终真正“产品”,因此,应当让程序自身可读性加强,减少对文档和注释量的需求。这样在维护代码时会减少对文档和注释的维护量。
  • 消灭魔数
    • 所谓“魔数”指的是程序逻辑中包含的数字,这些数字可能在多处出现,或可能随时调整,如:
      • if(user.type==1) ......
    • 1代表嘛意思?得写个注释。如果定义成常量,有个名字,这地方就好理解多了。同时修改常量值比在代码中到处替换容易多了。
  • 注释
    • 意义就不用说了,老生常谈了。
    • 虽然老生常谈,但往往做成了两个极端:太少,太多。
      • 太少不奇怪,毕竟没有注释编译时也不会报错,人本性总有惰性。
      • 太多怎么可能呢,多了怕嘛?!的确有太多的情况出现,恨不得每一句话都写一个注释,解释这句话的作用,或者在每个方法、程序块前详细描述其中的操作过程,代码就是注释的映射。这就太过了:首先,打开程序看的应该都是程序员,看合格的代码应该不会比看大段的注释难,注释白费了。其次,稍微修改程序就得同步修改注释,否则以后以谁为准?麻烦得很!
    • 注释解释这段代码解决什么问题;代码本身接受如何解决。
  • 解决两地分居
    • 变量声明和使用相隔太远,或超过了必要的生命范围。
    • 在某些语言中,这是语言自身的限制,只得将就了。但对于变量声明位置灵活的语言,再这样做就不厚道了。
    • 变量就近声明;最小化生命范围。
  • 重复代码
    • 计算机上copy & paste太方便了,以至于大段的代码在程序中不断copy & paste。
    • 由于copy & paste的太快,往往该改的地方没有改过来,产生bug。
    • 修改代码的时候,由于被paste到太多地方,往往修改不完全,产生bug。
    • 应该训练一种意识,写代码时只要按ctrl+C的时候就好好想想能否把copy的东西抽出来做成共用方法,避免重复代码。
    • 即使没有copy & paste,多次手写重复代码也应有以上意识。
  • 长方法
    • 长方法难于阅读,受限于人脑的“缓存”,往往读了后面忘了前面,尤其在上下翻屏的情况下。
    • 抽出多个方法。主逻辑中通过方法名就可以知道逻辑过程,岂不美哉?!
    • 抽出的方法别其他地方调用的可能性大大增加,减少重复代码。
  • 巨类
    • 无所不包的类。由于包含了太多的功能,没有一个明确的现实对应,“四不象”。命名困难就是一个信号。
    • 一个类只应该有一个方面功能,不要多管闲事。
  • 耦合度
    • 类/模块之间的耦合度应尽量低。来回频繁反复调用应该避免。
    • 往往由于类/模块之间功能划分不合理造成。把方法放到合适的类/模块中去。
    • 调用者在一个逻辑中多次调用被调用者的方法。可以认为是“微观管理”。
      • Don't ask for the information you need to do the work; ask the object that has the information to do the work for you.
  • 滥用全局/成员变量
    • 全局变量是否是必须的?
    • 成员变量是否应该是类的一个属性?
  • ……

语言相关检查

各语言有自己的pitfalls需要检查。例如Java中:

  • null
    • 是否在使用对象变量前判断不等于null?
  • equals
    • 对象的比较应该用equals。除非比较两个对象是否为“同一”对象,否则不能用==比较。
    • 自定义类,如果需要用equals比较,是否重写了equals方法?
  • 资源释放
    • 如IO操作使用的file、socket等资源,需要在使用后主动释放。
    • 为了保证资源的释放,需要在finally中主动释放。
  • ……

2007-06-28

Effective review

如何在软件项目中进行effective review?这是个问题。

在时间紧张的时候,大家过于关注自己的部分,往往对其他人的工作没有足够的熟悉程度,这时候进行review往往得不到好的效果,尤其是在不提前进行准备的情况下。

一直以来,认为大家先在底下独自review,然后再聚到一起把review结果汇报一下的做法不经济,所以选择让大家会上当场review,随着owner的引导,大家一路review过去。但一直有一个问题:很多人跟不上队伍,或者干脆不打算跟上,这样的review会留下很多隐患,以至于有些功能遗漏居然没有发现。其实大家独自review也会有这个问题,只是时间宽松一点,可以做的细致一点而已,但同样没有效率。

review最根本的功能有两个方面:例行检查与功能检查。例行检查目标是找出不符合普遍规范的地方,例如文档不完整,code形式不符合规范等。功能检查的目标是找出错误理解需求或遗漏需求的地方,也包括算法或架构性能、安全等方面的缺陷。

在采用现场review的时候,由于节奏很快,把这两方面的检查一起进行,可能就会导致很多人“身在曹营心在汉”的状况,review草草收场。

怎么办?分开呗,进行至少两遍review,第一遍例行检查,第二遍功能检查。如果有意见,也可以颠倒顺序。例行检查根据checklist进行,功能检查根据上一环节的input,如需求文档,设计文档等。

Document Review

首先owner展示文档,reviewer检查各部分是否齐全,是否遵守约定的模板。第二遍功能检查视各文档目的而定,如设计文档要看能否覆盖需求,如果不能,是否已在文档中指出。

Code Inspection

1.功能检查

按scenario进行,owner需要在会前准备scenario,并且标注每个scenario对应的需求点。

Inspection的第一步是检查scenario是否符合需求,覆盖所有需求点。

接下来对着每个scenario进行功能实现检查。

2.例行检查

例行检查的checklist需要在项目启动的时候定义好,或在编码前定义好,如果有人不熟悉,需要进行培训或沟通。

这个checklist需要经验的积累或借鉴别人经验的结晶,根据编程语言有所不同,例如C++语言需要检查指针操作、内存的分配/释放等,而Java项目就无须检查这些。但基本编程原则的check是一致的,例如变量命名等。