阅读更多

7顶
0踩

研发管理

转载新闻 代码不是文学作品

2014-04-18 11:42 by 副主编 WnouM 评论(4) 有7149人浏览
我在最近两个工作过的公司(Etsy和Twitter)成立过代码阅读小组,一些人向我征询关于阅读代码以及运作代码阅读小组的建议。想说的太多,一言以蔽之:不要成立代码阅读小组。你应该去成立小组但不是我稍后提到的那样,而在此之前,我要解释一下我是如何得出目前观点的。

作为一位曾读英语专业的业余作者,我以往总是被这样的想法所吸引:代码就像文学作品,我们应该像通过阅读优秀范文学写英文那样来学写代码。当然,持这种观点的不止我一个人。Donald Knuth(著名计算机科学家,译者注),除了致力开发TeX软件和编著《计算机程序设计艺术》,一直以来是被他称作“文学编程”的提倡者,并已经把他的几个大型程序出版成书。

另一方面,早在我加入Etsy并成立第一个代码阅读小组之前,手头上就已经有一些迹象本就表明:这是看待代码的一个错误方式。

首先,当我在编写关于与程序员访谈的《编程人生》(书中记录与15位世界级编程大师的对话,译者注)一书时,我几乎问了每个人关于阅读代码的问题。大部分人说阅读代码是重要的且程序员应该多读,然而当我问到他们最近读了哪些代码时,很少有人有好的答案。在他们还是年轻黑客时,其中有些人认真地读过一些代码,然而几乎没人保有阅读代码的习惯。Knuth作为伟大的计算机科学集大成者,阅读了大量的代码;Brad Fitzpatrick谈到出于恶作剧而阅读过一些开源代码片段。但他们俩是例外。

如果这还不够,那么在完成《编程人生》之后,我获得一次采访Hal Abelson的机会。Hal Abelson是麻省理工大学的著名教授,《计算机程序的构造和解释》的合著者。当我首次跟他谈论,问及之前那样的阅读代码问题时,他给出了普遍的答案:阅读代码是重要的且程序员应该多读。但他也没有说出任何最近读过的代码,除了不得不阅读的代码之外,包括在谷歌休公假时审查同事的代码以及给麻省理工学生评分时的代码。后来我问他关于这种矛盾:

引用
Seibel: 我对人们所说的和实际所做的之间的这种分裂很好奇。每个人说“程序员应该阅读代码”,但似乎少数人真正去做了。我会感到惊讶:如果我采访小说家们并问他们最近读了哪本小说,他们回答说“哦,自从读研究生后,我就没真正地去读一本小说了”。作家们实际上会读其他作者的书,但程序员不会真正得去读其他人的代码,尽管他们说应该去读。

Abelson: 是的。你是对的。但记住,很多时候,你增删查改一个程序最终使之能够运行并完成你需要它做的所有事情,所以就有很多与核心思想无关紧要的东西。

Seibel: 所以基本上你最终是说,大多数代码不值得一读?

Abelson: 或者说程序从一个初始计划或某种伪代码构建起来。书中的许多代码,是一些净化过的版本,不具备使程序运行起来的所有东西。

Seibel: 我想起了《计算机程序的构造和解释》的前言,里面写道“程序应该是为了供人读才写的,然后顺便让机器执行了一下。”然而正好你所描述的事实在实际中却是,大多数程序都是让机器来执行才写的,如果有的话,只顺便供人阅读。

Abelson: 嗯,我认为他们一开始是为了供人阅读的,因为里面包含着一些想法。你解释的东西,仅有一小部分存在我们的书中。书中有一些相当重要的程序。而且部分原因是我们认为解释它做什么的最简单方式就是表达在代码中。


然而即使明显知道大部分真正的代码实际上不是采用人容易读懂的方式去编写的,但这不足以让我在Etsy成立小组时放弃这种文学研讨班模式。由于大多数Etsy开发者熟悉Javascript以及我知道Jeremy Ashkenas对可读性代码的编写有浓厚兴趣,所以在第一次代码阅读小组会时,我选取了Jeremy的backbone.js。我仍计划了一些像文学研讨会一样的流程,但我发现许多人不会预习代码(嗯,这点跟文学研讨班倒是一样)。所以我决定在小组讨论环节之前演示要讨论的代码。

当准备我的演示文档时,我发现自己掉进了惯常模式:每当试图真正理解或心领神会一段代码时,我不得不从根本上重写它。为了更好理解,我将开始重命名一些名称,接着顺着我组织代码思路去移动一些语句。很快,我就已经开始深入代码抽象化(或具体化),并开始对更大程度地重组代码结构。一旦完成代码重写,我通常已很好地领悟了,甚至可以溯源并理解初始版本。我经常觉得这种阅读代码的方式不好,但它是迄今为止我理解代码的唯一方式。

在代码阅读小组演示时,我以初始的backbone.js作为开始,然后在自己想法的指引下,一步一步演变使之更易懂。当我问大家是否应该转向小组讨论环节时,但似乎没有人很感兴趣。好在看到我的重构让组员对原始代码的底层结构有了与之前我通过重构获得的相同见解。

Etsy的第二次代码阅读小组会由Avi Bryant主持,展示了如何利用SmallTalk的代码浏览功能来查看一些代码。因为在Etsy少数工程师跟Smalltalk打过交道,所以我们对组员会预习代码这件事不抱任何希望。但这次演示,对组员来说是一次领略SmallTalk魅力的绝佳机会,也让有我有机会发难Avi关于Smalltalk和Lisp的区别。

当我来到Twitter,文学研讨会模式仍莫名其妙地盘旋在脑海中,尽管在Etsy的两次看起来备受青睐的小组会几乎没有遵循这种模式。当我发邮件邀请twitter的工程师参加代码阅读小组时,回应相当热情。再次,第一次小组会由Marius Eriksen演示了一段代码。在这个示例中,展示了Scala语言实现的Future包的内部构件。这个构件被用于Twitter的很多服务,其大部分由Eriksen本人编写。

在演示了一段时间后,我终于明白一个浅显的道理:代码不是文学作品。我们不去阅读代码,而是解译、审查它。一段代码不是文学片段,而是样本。当我问Knuth关于他自己阅读代码时,他回答的其实早就给我指明了这个方向:

引用
Knuth: 但建立在大脑的东西是真正有价值的。那么我怎么利用它的呢?曾经有台型号为Bunker Ramo 300的机器,有人告诉我这台机器的Fortran编译器真的是不可思议的快,但没有人知道它是怎么做到的。我得到了编译器源代码的副本。因为我没有机器的说明书,所以甚至连机器语言是什么都不知道。

但是我把它当成是一项有趣的挑战。我能搞清楚BEGIN,然后我就开始解码。这些指令码对应着双字指令助记符,所以我开始合计着“这个可能是装载指令,而这个可能是分支”。接着我知道它是Fortran编译器,所以到一定程度后,看着卡片的第七列就能分辨是不是注释。

三个小时后,我已经弄懂机器的一小部分。接着我碰到了重大问题——跳转表。因此这是一个难题,之后我坚持画了些图表,就像我在某安全局试图解译一个密码一样。但我知道它是个运行很快的Fortran编译器,在某种意义来说这不是加密而是有意地掩盖;因为弄不到机器的说明书,所以答案就藏在代码中。

最终我弄明白了为什么这个编译器如此快。不幸的是,它并不是因为使用了卓越的算法,仅仅是因为他们采用非结构化编程并且最大限度地优化了代码。

画图表、获取更多一点信息并作假设仅是你基本上解决某种难题的方法。通常,当我阅读技术文件时,是同样的挑战。我试图理解作者的思想,尝试搞懂这个概念是什么。我认为,你试着去读别人的东西越多,未来你更能创造属于自己的东西。


他没有在描述文学作品的阅读,而在描述一个科学性调查。所以现在我对人们应该如何一起从代码中获得深刻见解这个问题有了新答案,正如我向Twitter代码阅读小组解释的一样:

引用
在准备与一起编程的女儿们即将进行的对话时,我开始思考告诉她们一些关于代码阅读以及应该读什么代码的事情。我再次突然想到了那些所有口惠而实不至的代码阅读想法,绝大部分程序员不会真正地阅读大量代码,至少不是纯粹为了阅读代码而读。这有一个简单检验方法:说出一段你读过的代码并且肯定大部分优秀程序员会去读或者至少听说过。不多,对不对?可能一行都没有。

但我想到代码不是文学作品,我们不是读者。更确切地说,有趣的代码片段是样本,我们是博物学家。所以我认为更好的方式是:我们其中的一位扮演着一个刚从异国回来的19世纪博物学家在国内科学界引发关于他们所发现的一只新型昆虫的议论:“看这怪物的触须!这些触须看起来非常笨拙,然而该物种的雄性可利用它们杀死小青蛙,然后雌性将卵产在其尸体中”,而不是像一群学比较文学的研究生那样挑出一段代码并去阅读、讨论它。

这种演示的关键在于介绍者要选一段自己深刻理解的代码,并通过从进化碎屑层(亦为临时补救方案–指快速有效但丑陋的方案,译者注)中指出核心思想,有助于听众理解。一种合理的方式应该是展示真正的源码并剥离、重写关键部分,就像一个生物学家去染色样本,使不同特征更容易辨别一样。

典型的演示应面向所有程序员,或男或女,或聪明或一般,但必须对与代码出处有关的任何特定知识不作要求。为了让组员理解代码,演示文档应该提供足够的上下文,解释对一般水平程序员而言比较晦涩的实现语言的任何细节。


由于我的顿悟,我们采用新模式,已成功举行了几次代码阅读小组会议,现在被称为Twitter的旨在提高编码知识的英国皇家学会。我们仍在摸索讲演代码的最佳方法,尽管目前方法感觉很不错。另外,我不再觉得我这种解剖式阅读代码的方式是糟糕的。

至今最大的教训是,代码是非常稠密的。半小时的报告只够用来呈现大约十二行耐人寻味的代码和一个主要思想。几乎可以肯定的是:报告人必须真正深入某段代码,要比任何人有更深的理解。除此之外,一个优秀的演示至少可让组员接触到的核心思想,对于决定自己去阅读代码的人们来说,可能是一个好的开端。

原文链接: Peter Seibel   翻译: 伯乐在线 - heloowird
译文链接: http://blog.jobbole.com/64548/
来自: 伯乐在线
7
0
评论 共 4 条 请登录后发表评论
4 楼 JamesQian 2014-04-22 14:01
程序应该是为了供人读才写的,然后顺便让机器执行了一下。”然而正好你所描述的事实在实际中却是,大多数程序都是让机器来执行才写的,如果有的话,只顺便供人阅读.
3 楼 tmartin 2014-04-18 22:22
dohkoos 写道
因为很多大师认为阅读代码很重要,但是他们没时间去读或不想去读,所以就在书里写上这么一笔。说阅读代码很重要,程序员应该去读。这就是“强调的就是缺失的”体现。

支持
2 楼 麦田的设计者 2014-04-18 18:34
Teacher always encourage us to read or copy the codes which have been defined right
1 楼 dohkoos 2014-04-18 15:59
因为很多大师认为阅读代码很重要,但是他们没时间去读或不想去读,所以就在书里写上这么一笔。说阅读代码很重要,程序员应该去读。这就是“强调的就是缺失的”体现。

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 用户管理系统

    用EasyUi做的一个Web项目 后台代码采用java SpringMVC框架 前端采用了EasyUi框架 注解简单,通俗易懂

  • 面向对象的通讯录系统C++

    今天复习c++,想起来自己努力写了那么久的通讯录系统居然没发过博客,虽然写的不好,但还是想发上来留个纪念。 //温馨的通讯录系统 #include<iostream> #include<fstream> #include<cstdio> #include<iomanip> #include<cmath> #include<cstring> #include<string> #include<algorithm>

  • 用面向对象做一个简单的注册登入管理系统实现用户增删改查

    用面向对象实现增删改查

  • 面向对象课程设计——类与对象——人员信息管理系统

    人员信息管理系统: 以一个小型公司的人员管理为例,说明类及成员函数的设计。提示: 类的设计: 某小型公司,需要存储雇员的编号、级别、月薪,并显示全部信息。根据这些需求,设计一个类employee,在该类中,包括的数据成员有: 编号(int individualEmpNo) 级别(int grade) 月薪(float accumPay) 成员函数有: 设置编号(void setEmpNo(int...

  • 面向接口编程和面向实现编程

    面向接口表示子类是实现接口或者抽象类,面向实现表示继承普通类。 明显前者耦合比较低,因修改代码比较低,因为接口和抽象类本身有的方法不干事情,而是子类去去实现。 设计原则: 找出应用对象中可能需要变化的部分,把它们独立出来,不要跟不变化的放在一起。 面向实现方式用例: //面向实现编程 /** * 普通类animal */ class animal { public $na

  • 面向对象设计

    面向对象设计的任务是对面向对象分析的结果作进一步的规范化整理,以便能够被面向对象编程直接接受。     概念面向对象设计是一种软件设计方法,是一种工程化规范。这是毫无疑问的。面向对象设计的主要工作包括:— 确定需要的类;— 给每个类提供一组完整的操作;— 明确地使用继承来表现共同点。概括地说,面向对象设计就是“根据需求决定所需的类、类的操作,以及类之间关联的过程”。从面

  • 高复用的面向接口编程接口设计(以用户开发过程为例)

    在用户模块开发过程中经常要想前台传递数据信息,假设这个json格式是这样的 {        state:0,        msg:"success",        data:{一堆不同类型的数据} } 每次弄不同的东西的话,很烦。为了实现这个功能就设计了一个高复用的接口类ServiceResponse,并将其json序列化: package com.mmal.common;

  • 面向对象分析和设计(OOA/D)

    UML不是OOA/D,也不是方法,它仅仅是一种图形表示法(表示的是OOA/D的想法),我们将在OOA/D中应用UML;分析,就是理解客户脑子中的概念,跟客户来沟通,分析出专业术语;设计,对分析出来的专业术语进行归纳; OOA/D的过程:定义用例、定义领域模型、定义交互图、字义设计类图 示例:软件模拟游戏者投掷两个色子,如果总点数是7则赢得游戏,否则为输。 第一步:定义用例:用例是需求分析的一...

  • 面向对象分析与设计:顺序图或通信图

    实验四:顺序图或通信图1. 链接:实验一:https://blog.csdn.net/miny_Chen/article/details/80084189实验二:https://blog.csdn.net/miny_Chen/article/details/80287214 2. 类图:3. 内容说明:1) 登陆Ø 类整体说明:注册登陆,学生、教师或教务员登陆的信息(账号和密码)。Ø 属性说明:账...

  • 实战面向对象_工资管理系统

    /* Employee.h文件 */ # include using namespace std; class Employee { protected: string m_name; int m_time; public: Employee(string name, int time) //构造函数 : m_name(name), m_time(time) { } st

  • 好友信息管理系统

    设计要点:设计并编程实现一个好友信息管理的小系统。系统应包含每位好友的全部信息,包括姓名、性别、生日、爱好、联系方式、性格特点等。系统实现对好友信息进行增、删、改、查、生日提醒、统计计算等功能。 题目要求: 需求分析:     本阶段是整个软件开发过程中最重要的环节。通过了解实际运行的系统或与用户交谈,明确系统要完成的任务是什么。  &nbs...

  • 【c++面向对象编程】工资管理系统

    1、名词实际上就是对象(员工派生出老员工、新员工) 2、利用虚函数实现多态 3、对象指针数组 头文件 #include #include using namespace std; class Emplyee{ protected: string Name; int Years; public: virtual int getSalary() =0; Emplyee(string

  • SMBMS

    SMBMS mysql -hlocalhost -uroot -p set global time_zone = ‘+8:00’; 10、MVC三层架构 什么是MVC:Module View Controller 模型,视图,控制器 10.1、早些年 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecklOf6u-1651655697760)(${images}/image-20210504092643736.png)] 用户直接访问控制层,控制层就可以直接操作数据

  • Python学习笔记(二十三)面向对象版学员管理系统

    Python学习笔记(二十三)面向对象版学员管理系统 一、系统需求 使用面向对象编程思想完成学员管理系统的开发,具体如下: 系统要求:学员数据存储在文件中 系统功能:添加学员、删除学员、修改学员信息、查询学员信息、显示所有学员信息、保存学员信息以及退出系统等功能。 二、准备程序文件 2.1 分析 角色分析 学员 管理系统 工作中注意事项: 为了方便维护代码,一般一个角色一个程序文...

  • 学生信息管理系统Python面向对象版

    """ 程序名称:学生信息管理系统 版本信息:0.1 开发者:飞宇 开始时间:2018.3.23 19:45 版本更新时间:2018.4.2 23:08 格式:IPO OOP面向对象 """ # 学生类 class Student: def __init__(self, stuId, name, age, sex, dept, na...

  • 面向对象分析与设计: 设计类图

    实验七: 设计类图一、实验链接:实验一:https://blog.csdn.net/miny_chen/article/details/80084189实验二:https://blog.csdn.net/miny_chen/article/details/80287214实验三:https://blog.csdn.net/miny_Chen/article/details/80408875试验四:...

  • 学生管理系统(一)

    需求:实现学生的增删改查 操作: 后台 npm start 网页访问http://localhost:3000/

Global site tag (gtag.js) - Google Analytics