1背景最近参与了一个大团队的项目实现。项目金额数千万,人数近百人。但项目实施后暴露出以下问题:(1)质量较差,团队成员水平参差不齐,软件内外质量一致性差;(2)需求不确定,工期很紧,代码改动频繁,越来越丑,效率越来越低。为确保项目按时、高质量交付,质量提升刻不容缓。因此,在立项之初,做了以下三件事:(1)制定统一的接口规范,制定统一的参考范例,定期对全体成员进行接口规范培训和复习;(2)制定统一的代码规范,制定《评审文化构建》、?PPT,培养团队的质量文化和评审文化,并落实评审;(3)引入管理工具ReviewBoard。项目统一了标准和规范的建设,有效保证了接口的一致性,但代码质量的提升并不是那么简单。在这里,我就不分享项目的情况了。以后我会用更详细的文章来介绍规范化、规范化的软件开发方法对项目的重要性。2ReviewBoard简介这里有一个优秀的工具,ReviewBoard。该工具易于使用且功能强大。通过它来实现codereview是非常有效的。代码审查分为提交前审查(Pre-Review)和提交后审查(Post-Review)两种方式。Reviewbeforesubmission,即开发者代码变更后,需要提交给ReviewBoard。审核通过后,即可提交到SVN源码服务器。人员先提交代码,再提交修改给ReviewBoard。如果审核不通过,则修改代码并更新审核,直至通过。这两种方法各有优缺点。我们采用后一种方式,不阻止开发者提交代码,不能100%控制所有质量,但可以达到80%以上。ReviewBoard设计之初,更多的考虑是支持Pre-Review的方式。因此,存在以下问题:(1)提交通过后,Review不能自动将状态变为closed,会和所有通过审核的ReviewRequests混在一起;(2)不能看到没有通过审核的请求再次更新,因为如果再次更新,就意味着需要进行二次审核。在管理过程中,会查询哪些请求未通过审核,并定期向开发者发送通知。因此,我决定自定义ReviewBoard来支持以上功能。而ReviewBoard是基于Python语言和Django框架开发的。没学过Python,更没学过Django,怎么修改呢?所以,我开始研究ReviewBoard。ReviewBoard的***版本只能在非Windows上运行。我在Ubuntu14.04上安装部署,同时使用MySQL数据库。于是,我开始尝试做出改变。3尝试修改ReviewBoard3.1探索第一步:数据库首先研究数据库,发现数据库设计的非常工整。您无需查看任何文档即可了解数据库中表和字段的含义。找到几个关键的表,就是reviews_reviewrequest,看了一下它的字段,和界面显示的差不多,一下子就明白是什么意思了。根据字段的含义,我找到了另外两个关键表diffviewer_diffsethistory和diffviewer_diffset。通过查询这些表的数据,我就可以轻松解决第一个问题。我可以通过一条SQL语句关闭审核通过的ReviewRequest,这样我就不需要查看这些审核请求了。3.2学习Python并修改代码接下来的问题是需要修改代码。我花了3个小时学习Python。通过在线英语帮助,直接在控制台上做实验学习了基本的语法结构,学习了类和对象编程,学习了Python模块和编译,熟悉了Python之后,我决定开始修改代码。于是,我首先通过类似的“Submitter”栏查询了所有的文件,通过它很快找到了columns.py和grids.py这两个文件。看完代码发现ReviewBoard的审核请求是通过这两个文件来的。意识到,如果你再次查询名称,将找不到关联的文件。通过这两个文件可以发现columns.py用来定义所有要显示的列,grids.py用来组合显示列。然后你可以简单地模仿并添加一列。该列定义为差异。1234567891011121314151617181920212223242526272829classDiffSetCountColumn(Column):"""显示评论请求的已发布评论的差异集计数."""def__init__(self,*args,**kwargs):super(DiffSetCountColumn,self).__init__(label=_('Diffs'),detailed_label=_('NumberofDiffs'),shrink=True,sortable=True,link=False,*kwargs,**kwargs)defrender_data(self,state,review_re任务):如果review_request.mydiffsetcount>1:返回('''!%s'''%review_request.mydiffsetcount)else:return''defaugment_queryset(self,state,queryset):returnqueryset.extra(select={'mydiffsetcount':"""从diffviewer_diffset中选择计数(*)WHEREdiffviewer_diffset.history_id=reviews_idiffset_history.diff""}) 简单说明一下,我定义了DiffSetCountColumn类,它继承了Column类,类的初始化方法是__init__,相当于构造函数,这里的self就是之前经常用到的this。二定义了一个方法,一个是augment_queryset,用来显示当前列的数据,定义名称为mydiffsetcount,另外定义了一个方法render_data,用于显示表中的数据,其他列定义参考这里。接下来在grids.py中声明这个列的使用,非常简单。在第25行添加:DiffSetCountColumn,在第99行添加:diffset_count=DiffSetCountColumn()然后重启Apache服务器,简单测试一下,惊奇的发现可以了。3.3进一步添加功能有了这个尝试,接下来想进一步自定义。目前每个群提交到不同的SVN地址,通过SVN可以区分每个群的提交状态。但是ReviewBoard不会过滤SVN地址。首先修改columns.py,修改Repository列。123456789101112131415161718192021222324classRepositoryColumn(Column):"""显示审核请求更改所在的存储库的名称。"""def__init__(self,*args,**kwargs):super(RepositoryColumn,self).__init__(label=_('Repository'),db_field='repository__name',shrink=True,sortable=True,link=True,link_func=复制代码self.link_to_object,css_class='repository-column',*args,**kwargs)defaugment_queryset(self,state,queryset):returnqueryset.select_related('repository')defrender_data(self,state,obj):returnsuper(RepositoryColumn,self).render_data(state,obj)或''deflink_to_object(self,state,obj,value):importurllibreturnlocal_site_reverse('all-review-requests',request=state.datagrid.request)+'?'+urllib.urlencode({'repository':obj.repository_id})把这里的列改成支持Link,然后定义link_to_object函数。在这里搜索了一会,发现可以使用local_site_reverse获取需要的url,然后与url结合。一开始没有用urlencode,一直无法正常渲染。因为在ASP.NET中使用了urlencode,知道这里面有坑,于是上网查了一下Python的urlencode,然后解决了,发现可以。然而,接下来的问题来了,那就是链接后如何过滤?在这里我找到了它的“showclosed”过滤功能。通过代码查询,知道grids.py是怎么工作的,所以加过滤也不难。如下。grids.py第116行self.repository_query=''grids.py第133行self.repository_query=self.request.GET.get('repository','')如果len(self.repository_query)>0:self.queryset=self...为什么我可以从Python学习1天修改一个完全黑盒的软件?答案是ReviewBoard有非常干净的代码。换句话说,ReviewBoard具有非常好的内部和外部质量。数据库设计简洁明了,结构非常清晰,可读性很强,完全不需要数据库文档。文档是多余的!!!代码风格非常一致。我们发现ReviewBoard的评论很少。Bob大叔的《代码整 洁之道》强调,注释是对代码有意表现失败的补充。最好的代码不需要一行注释。ReviewBoard的命名从前到后都非常一致。大小写、下划线、类名、方法名规则统一,命名准确。没有不专业的命名或不专业的缩写。ReviewBoard的类非常简单。在columns.py文件中,可以发现每个Column基本都在50行以内,每个方法也很简单。ReviewBoard完美的实现了单一职责,明确的告诉我可以用哪些类,方法,代码来完成什么任务。ReviewBoard的代码风格很好,没有难看的收缩,没有难看的拥挤,该空格的时候空格,该空行的时候空行,该紧缩的时候缩。在这里,我没有看到任何代码损坏的味道。有了这种优秀的软件“混”,我很难写出糟糕的代码。在写代码这件小事上,很多人可能只看到架构、框架之类的东西,但实际上很多工作多年的程序员,代码还是一坨屎,没有规范的风格和良好的编码习惯。很多人都能完成一份工作,但并不漂亮。不知不觉中,随着时间的推移,你只会做更多丑陋的事情。从我的编码经验,我可以总结一个程序员的现状。混沌状态1到3年的开发者,能完成功能也不错;一是去找黑客,抽象的代码只有自己和上帝才能看懂,一是变得专业,倾向于写一些团队其他成员能看懂的代码,二是维持现状;5年后,开发人员又是三种,一种是去管理,也是一个不错的选择,一种是去架构师或者专家等更高的技术路线,还有一种就是成为废物.随着时间的流逝,它们的空间越来越小,越来越不值钱。一般来说,通过训练,越是年轻的人成长得越快,越有出息。要想提高自己的水平,codereview是最有效的方法。通过复习和培训,让他们知道什么是美的,什么是丑的。什么是好什么是坏。5Tribute再次向ReviewBoard致敬!ReviewBoard不仅是提升我们综合实力的有效工具,更是一款值得学习的软件,感谢ReviewBoard!(激动捐给它的时候找不到按钮~~)另外,我们使用了TaoReviewBoard、FindBugs、JSHint等工具,谢谢~~。在codereview中引用了很多Bob大叔的两本名著《代码整洁之道》、《程序员的职业素养》,非常好的书,感谢Bob大叔!