加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

知识共享者维客:听维客之父细述Wiki前世今生(三)

(2004-02-12 18:21:35)

第II部分            代码和文本集体所有权

集体的代码和文本

Bill Venners:极限编程(XP)的集体代码所有权特点让我想到了wiki,在wiki中,每个人对所有一切负责。

Ward Cunningham:这样做完全是有意的。在设计wiki前的几个月中,我们有过一次争论。我认为Kent Beck和我站在一边。坚信主流软件工程教条的那些人站在另一边。我们说“集体代码所有权很好。”他们则说“太荒谬了。没有职责划分,而没有责任就决不会有质量。让他们避免制造缺陷,就必须把缺陷和某个人挂钩。”我说,“完全不对。”

我设计wiki的决定,很大程度上受到建立一种协同过程模型的渴望所启发,我认为在大型代码库中应该有这种协作。我希望wiki能够模拟这种情况。比方说在一堆代码中有一个问题。您知道怎么解决问题,但是解决需要涉及到大量模块。重构需要大量艰苦的工作,如果要同每个最初的作者协商就更加困难。你只是希望改正问题。

困难在于代码可能是按层次结构组织的,但是解决方案可以从多方面来考虑,而不止是某种层次结构。因此当您在某一方面发现一种贯穿整个层次结构的解决办法时,您只能随着解决方案的要求,在适当的地方加入解决方案。我们经常发现自己处于这样一种境地,人们了解这个程序,但是他们不能将这些知识应用于程序。为什么?因为知识的发展和在拥有这些知识之前做出的某些组织决策相冲突。换句话说,程序抗拒知识的积累。

Bill Venners:抗拒?

Ward Cunningham:程序对某种类型的知识有抵抗力——没有预计到的知识——因为很难在一开始就设立的结构内表达这些知识。很难把不符合这种结构的任何东西加进去。

Wiki中也有一点对预料之外思想的抗拒,但这种抗拒主要在人们的实践中。Wiki中写进去的东西越多,对自身权利的要求越严格,但是如果有人要修改而且到第25页去修改,他们就可以去25页。

比如,在wiki中,发生的某个过程是页面从讨论发展成短文。许多人愿意阅读讨论。那些每天访问wiki的人希望看看昨天又说了什么,因此需要按时间组织的页面。但是对学习而言,投稿先后顺序并不是一种很好的组织原则。因此页面总是保持某种讨论之中的感觉,直到这个讨论结束。后来,有人又回来阅读了这些讨论,把您可能称之为线性模式的页面重新组织成文档模式的页面。

如果在注解之间转来转去,而且有许多类似的彼此相邻的注解,您经常会发现可以去掉一大半篇幅。因为只要位置适当,一句话可能就说明白了。在Ward的wiki上,这个过程被称为“重构(refactoring)”,就像我们在软件中那样称呼这个过程。Ward的wiki是关于软件的,其中有许多从事软件的人,因此称为重构。在其他地方可能就会称为“编辑”了。在Ward的wiki上,重构是一个持续的过程。设想如果某些地方被证明不很合适,就要再次进行重构。一切都是重构的目标。这也是我们愿意在软件中所看到的。

软件的优势是它有明确的解释。因为软件是为机器编写的,我们可以依靠精确的解释编写测试。在重构程序时,我们可以测试没有破坏或者丢失的任何东西。但wiki是为人类编写的,没有精确的解释。我可以说,“哎呀,我可以把这个句子放在这儿,并砍掉一半,因为在这个上下文中很容易理解。”但是我可能错了。对于我容易理解,但可能对其他人很难,我也没法做测试。因此在重构过程中,我们可能会丢失wiki上的某些信息。Wiki像一个信息漏桶。它每天都在丢失信息。但是有更多的信息加进来,因此净结果是正的。即使丢失了某些东西,wiki总是比昨天有更多的内容。

高质量代码的集体激励

Bill Venners:从您的描述中我了解了集体代码所有权的好处。但代价是什么?集体代码所有权的缺陷是什么?

Ward Cunningham:我相信有一些缺陷,但是现在还没有想到。

Bill Venners:所有权的自豪感又如何?人类适应集体自豪感吗?在您自己创建什么时,总是希望把它做好。

Ward Cunningham:我认为集体所有权实际上更好一些。是的,我对自己所做的事情感到自豪。对于我而言,自豪感是一种激励。通过集体所有权,我们基本上建立了一种小型的社区,训练他们自我肯定所做的工作。但如果归我所有,人们就会说,“那是Ward的模块,我不想看Ward的模块。”如果必须调用Ward的模块,他们可能会喜欢该模块的API。我的模块缺陷率很低也许会给他们留下印象。不过他们可能会说,“他的模块很简单。他有一个容易编写的模块,这就是为何他的缺陷率这么低。”他们不会知道真正的原因。

当人们使用我提供的材料时,他们会感觉到是否容易使用。所谓使用材料,我是指拿来代码调整,以便做更多一点工作或者稍微改变其功能——这些代码应该做的任何事情。当人们使用代码时,他们会看到我努力完成的一些事情,否则的话他们永远也不会注意到。不需要迫使他们说“Ward你很了不起”,但有时候他们会说,“Ward你很了不起。”这就满足了我的自负感。所有权的自豪感?的确如此。

现在,不需要在每一行上写上我的名字。事实上,这证明如果它确实很好,可能是因为他们做得好,而不是我。我可能只是做了一个计划,然后他们完成了而且完成得很好。我可能会受到不应得到的荣誉,不过他们也可能把赞誉送给别人。一个想法来自谁,荣誉应归谁,这种观点是弹性的。但我认为人们在知道谁参与其中的时候,可以很好地解决这种弹性,承认每个人的贡献。通过集体所有权,我们建立了一种社会环境,通过源代码语句中迸发的智慧可以了解一个人。

Bill Venners:集体所有代码的这种特点为何不能出现在wiki页面上,wiki页面上的内容有时候显得有点乱?

Ward Cunningham:我肯定也会这样。

Bill Venners:您刚才谈到某一行代码是谁编写的并不总是很明显。对于wiki页面也是如此。谁写了某一行文本也并不总是很明显的。有时候一个人在wiki页面上写了一些东西,“我有这样的经验”,而作为读者我并不知道这个“我”到底是谁。集体代码所有权和集体文本所有权相比有什么不同,您刚才只谈到了代码而没有涉及文本?

Ward Cunningham:我认为不可预料的代码是很常见的。代码可能能够工作,但如何工作本质上是秘密的,不可能阅读。事实上,这也可能是一种常见情况。因此混乱可能不够确切,应该是不可读。我有与他人长期结对编程的经验,在读到他们的代码时,甚至认为是自己的代码。但在wiki上还没有出现这种情况,我读到某人的文字而认为是自己写的。这可能是因为代码具有更高的组织性,但我认为更可能是因为代码交流的范围更狭窄。代码交流的范围仅限于某种过程的计算机化,而谈话的最佳方式是相当广泛的。这样就会导致和英语相比,代码会更快地具有稳定的组织性。

解决纷争

Bill Venners:在“Extreme Programming Explained”一书中, Kent Beck写道,“集体所有权增强了在项目中对个人能力的认知。您不会永远钉住别人的蠢事不放。您在途中发现了某些东西,然后把它排除掉。”对于什么是蠢事如果不同人的看法互相矛盾时该怎么办?所谓蠢事不是一种主观的评价吗?

Ward Cunningham:啊,我决不会这样说。当我认识到不需要赢得每一次争论的时候,这是我编程生涯中的一个转折点。我正在和别人讨论代码,我说“我认为最好的做法是A”,他们则说“我认为最好用B”。我说“不,应该是A”,他们则坚持说“我们想用B”。当我能够这样回答的时候,对我而言这是一个转折点:“好吧,用B办法。如果我错了这样就不会损害我们。如果我对了,而您用B办法也不会造成多少损害,因为我们可以改正错误。让我们看看这样做是否错了。”

我们不要把自己看成非常幸运的预言者,要求自己预测未来。最好是建立一种环境,这样您就能够试一试B并看看发生什么。现在证明争吵通常是无益的。无论谁编程,都可以自由选择编程的方式,这样就足够了。当然有时候也可能会证明争论是有用的。我们正在做别的事情,看了看说,“您知道,用在那里并不合适,因为B确实不符合。”而这个问题可能是我在宣传A方法时正好考虑到的,也可能不是。它可能是开发人员在方法B的上下文中硬塞进去的。但有时候您了解这些缺陷或者难以改进的地方。于是开发人员说,“你知道,这些代码令我感觉不舒服。”我说“好吧,我可以解决这个问题”。他们问道“您行吗?”我说,“当然,我可以解决这个问题。您做了B,我就使用B。”然后我就可以开始处理它,只要可能就尽量使用B。但是因为承担了职责,我就有机会使它实现需要的功能。

Bill Venners:您是说再回到A。

Ward Cunningham:如果需要的话。

Bill Venners:也可能是到C。

Ward Cunningham:通常会变成C。对于我们双方这都是一种学习的经历。如果没有这种经历,我们就都没有学到什么。Ward赢了,其他人输了。或者相反。这和一场战争差不多。为什么不说,“好吧,让我们编码看看怎么样。如果不行的话我们再改变。”

我无法告诉你我花费了多少时间担心无关紧要的决策。如果能够做一项决策,然后看看结果如何,当然会大大减少这种担忧,但是这意味着您必须建立一种环境,当确实出问题时可以修正。如果某些事情确实出了问题,不会浪费您和您的客户过多的成本。这不是一种可笑的花费。如果您处于不能承受错误的情况下,就很难做正确的事情。因此如果尝试做正确的事情,正确的事情可以抵消犯错误所造成的代价,要远比猜测什么是正确的好。

比如,在一个项目中我们通过经常升级抵消了错误成本。我们是通过建立一个相当精细的数据库模式演化机制实现的。。我们曾经每周发布一次,每周都修改模式。我们可以为不同的客户根据不同的要求对模式作不同的修改,把这一切结合起来最终得到正确的结果。因为我们每周都在做,大约六周或八周以后,我们就确信我们可以完成它了。我们从没有担心过。多数人都说,“无论做什么,千万不要做模式演化除非你已经做了。”但在项目结束的时候,他们说,“天哪,我们真的做到了。”因此在从来没有做过之前,他们说,“只要我们去做,就让我们做完它吧。”他们做了一个巨型项目,以前从未有这样的经验。你猜怎么样?他们错了。相反,我们每周都在做。每周做一点。我们确实从中受益了。我们永远不会害怕它。它也从来没有出现问题。

因此我们不是通过说“我们要做的工作性质就是这样”,从而通过抹去问题来解决问题。要是这么简单的话才是真的奇怪了。实际上更多的是提问的方法,“您希望擅长什么?”,如果您希望擅长它,就找出一种方法来每天应用它。如果每天都要应用,那么就只能熟练掌握了。因此,选择您希望擅长什么。您应该擅长您所害怕的东西,这样您就不会再害怕它了。

第III部分      塑造程序

变更的成本

Bill Venners:在“Extreme Programming Explained”一书中,Kent Beck写道,“软件工程中一个普遍的假设是程序修改的成本随着时间呈指数级增长。”并建议说,“通过技术和编程实践的结合,有可能得到一条方向相反的曲线。”怎么能够实现变更成本曲线的扁平化呢?

Ward Cunningham:传统上来说,变更成本曲线告诉我们,早发现变更的需要与晚发现这种需要相比,进行变更所花费的代价越小。我批判了这种曲线,因为根据这种理论,我们差不多可以故意犯错,然后在实践中改正错误,这样有助于减少以后变更的成本。

我们认为,任何变更的决定因素不是何时进行变更,而是需要做多少思考。如果我们每周做一次变更,而理解我们的真正需要花费了两天,那么做这种变更就需要两天。如果我们每21周变更一次,理解我们的真正需要也花费两天时间,那么这个变更也用了两天。

在每周一次的变更中,我们可能要写20条语句。在21周变更一次时,我们可能需要写20条语句并修改4条语句。但是如果您习惯于变更,修改4条语句也花不了多少时间。只需要找到那些语句并修改它,可能只需要1分钟。

因此理解变更的需要是决定性因素。编程实现变更并不重要。只要我们理解了变更,我们就可以编程实现,或早或迟。修改代码的实际成本并不在编程中占有主导地位。主要的成本是理解所花费的时间,这就给出了一条趋向平缓的变更成本曲线。

许多人害怕变更,是因为尽管在编写的时候还理解代码,但这种理解很快就消失了。他们对你说,“我们为这些语句费尽了心血,无论如何不要改变这些语句!”他们并不想回到原来的代码,因为重新理解太费劲了。因此使变更成本曲线扁平的另一种方法,即以后变更的成本不会比现在更大,就是确定人们必须能够看到他们将要改变什么并理解它。因此,当你在编写代码时,你就会更多地为将要阅读代码的人编写,而不是为运行它的机器编写。

同样,你也不愿意编写大量的注释,告诉别人如何进行他们所需要的修改,因为您并不知道他们要进行何种修改。最好抱有这样一种观点,您不能帮助将来的程序员进行修改。您所能做到的就是使他们容易理解您努力去做的事。如果您非常小心,避免做太多的事情,这样最有助于他们理解您的努力。您试图完成的功能越多,将来的程序员要理解您的代码就越困难。

比方说,如果您明显地忽略了一种情形,以后的程序员需要解决它,他们打开代码发现您显然是忽略了这种情形。这意味着他们可以自由实现需要的任何功能。但是如果您试图应付这种情形,他们来了首先要确定哪里不工作。他们将看到您试图解决这种情况,因此他们首先要尝试理解您在做什么。一旦理解了您要做什么,他们就可以指出如何修改以实现需要的功能。他们更愿意接手的时候发现您甚至没有考虑到这一点。也许您想到了,但完全没有对此编程。

对未来的预测

Bill Venners:每个人都同意预测未来是很困难的,但预测总是这么糟吗?

Ward Cunningham:在科学中预言未来很简单。科学建立在对物理系统行为研究的基础上,被证明具有惊人的可预言性——可能天气除外。我们已经能够向太空发射火箭并使它沿轨道运行,这是预测的一个范例。但是当开始谈及对未来的期望时,我们可能有某些直觉,这些直觉也许是对的,但不会总是对的。我们必须考虑到不正确的情况。

当一个新的需求出现时,我们看了看说,“好的,这不难。这个程序就是为它而作的。”我们在程序中加入一些代码,然后就成了——我喜欢这样。我讨厌这种情况,新需求的出现不能很好地满足,仿佛程序的设计就是为了和需求作对。这种情况下,我们有大量的工作要做。但是这项工作的性质要求首先修改程序使它更容易适应新的需求,然后把新的需求包含进来就很容易了。换句话说,不是为新的需求在并不适合这种需求的结构上打补丁,而是全力以赴做艰难的任务修改结构,使它能够很容易实现这种需求。打补丁的办法意味着,后来者不但要理解并非为这种需求设计的系统,还要理解试图弥补但不改变系统的那些补丁。最好是修改系统以便很容易适应新的特性。

有人也许会说,“为什么不向前看一看,了解我们必须做到的所有工作呢?为什么不从一开始就把系统设计成使所有工作更方便呢?”如果您能够实现这样一个系统,那真是太好了。正是这样,人们一次又一次地试图设计系统使明天的工作更容易。但是当明天到来时,却发现他们并没有很好地理解明天的工作,实际上他们使明天的工作更难了。

意外的体系结构

Bill Venners:为了批驳变更成本曲线,您发现了一种方法可以在项目的整个生命期中进行变更。这就使得对将来的计划不那么重要了,因为可以在以后真正需要展开的时候进行变更。整个体系结构仅仅是在每次只关注一小步的过程中逐渐浮现出来的吗?

Ward Cunningham:我喜欢塑造程序这种说法,就象艺术家塑造一团泥巴一样。艺术家想做一个雕塑,但是在开始雕塑之前,她只是把泥巴揉来揉去。她开始逐渐塑造成形,并看到泥巴要成为什么样子。揉捏得越多,泥巴就越像她希望的样子,最终变得完全符合她的想法。

一个开发小组用了数月编写一段代码。最初,他们做了一段代码,有点僵硬。代码很短,但仍然有点僵硬。他们搅动这些代码,代码稍微变软了点。在上面提到的一个项目[第II部分]中,我们向数据库增加了模式演化功能。它软化了程序,变更容易多了。每次变更模式时,我们都作一点改进。程序员和代码——作为一个整体——都软化了。我们塑造程序并保持它的柔软性。

在项目结束时您已经完成了需要做的所有事情——有人为之付钱的所有功能——您看了看代码问道,“这一堆东西中的核心是什么呢?这是怎么完成的?我们日复一日地编写程序,它是怎么结束的呢?”通常程序的结束都是令人惊诧的。您会说,“这是一种优美的结构。”那么体系结构又从何而来呢?

在这里,体系结构意味着我们处理不同需求的系统化方式。当我们根据需要塑造程序时,体系结构使我们能够发现进展到哪里了。融入程序的是一个系统,包括我们所做的每一个小决策——正确的小决策,错误但改正了的小决策。从某种意义上讲我们得到的这个体系结构并没有经过尝试。在其他决策上下文中的所有决策凝结成了一种体系结构。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有