关于Code Review, 从coolshell的一篇文章所想到的.

酷壳( coolshell.cn )是我个人非常喜欢的一个技术博客,创办者陈皓( @左耳朵耗子)在这个博客里面提供了很多非常有营养的文章,没看过的程序员朋友一定要看一看.

这篇博文源自于这篇文章:

从Code Review 谈如何做技术

看到作者这种大牛在阿里这种以工程师文化闻名的公司推行code review的时候遇到的种种阻力,想想自己从抵触到接受,到现在认为review完的代码才能commit,整个过程说出来我觉得多少还是有些意思的.

在工作的前5年里(shame),我基本上没有做过任何形式的code review,无论是让别人review我的代码,还是review别人的代码.原因太多了,公司没有强制规定,小组也没有形成氛围,在这种情况下,贸然请别人花时间review自己的代码,别人是不一定乐意的,自己自告奋勇review同事代码更是一种类似挑衅的行为.当然,如果有一段代码的处理逻辑非常复杂,我没有信心能够独自一人很顺利搞定的时候,我会向经理提出申请,邀请一个同事来和我一起写,对,就是类似敏捷提倡的那种”结对编程”的模式,只是多数时间是我在写,他则在旁边跟我聊聊天,在我脑子发热的时候一语中的的指出我的list中包含的到底是什么样的数据结构.也就是所谓的”当局者迷,旁观者清.”.一来这种非常复杂的开发工作不会天天有,而且开发时间不会特别长(一般不超过半天),二来坐在旁边看别人写代码其实是一个稍微轻松的事情,因此这种举动并没有引来经理和同事的反感和抵触.

首次接触”疑似”review是在前一家公司,我们开发的整套环境都是一个老家伙带着团队搭的(天知道他们积累了多久才搞了这么多玩意),从vim的配置,php开发框架,一直到上线分发的脚本都有,其中上线前最重要的部分就是将最新版本和线上版本的代码依次做vimdiff,只有都确认ok的时候才会上线.我所在的team只有我和另外一个老家伙具有上线权限.因此每次上线前,一个一个文件的查看diff,确认新的修改ok就成了必然的工作.当然,最开始我对此感到的是好奇和新鲜(从eclipse插件开发到php网站开发,一个月时间,如此跨度,不新鲜才怪),后来在发现了几个明显的拼写错误和几个隐蔽的逻辑bug之后,我意识到这么做原来真的可以发现这么多问题,就对这件事情逐渐重视起来.当然,甚是土鳖的我,当时必然是不知道review board这种高级玩意的存在的.

从前公司离职加入现在公司之后,开发语言从php又换回了老本行-java.我作为一个新人,首先肯定还是适应新的团队和环境,因此review这种事情就作为内心中一个隐隐的向往,就给放下了(其实作为程序员,我心中已经有很多类似的隐隐向往了,比如”我一定要重构XX模块,我一定要开发一个XX工具,我一定要努力把数学水平提高点….”,多一个不多).后来公司来了一个CTO,当然,还来了很多高大上公司的天才,如果没记错的话,Code Review是作为新技术管理团队的三把火工程提出来的.当然,经理层中有没有人为了这个事情把CTO微博拉黑我是不知道得:-),但是下面的一线程序员议论的还是有一些,讨论的主要内容和上面陈皓文章中写的差不多,人手不够工期紧blabla,但是讨论归讨论,毕竟因为这个事情跟经理闹翻的事情是不会有人做的.我在配置了reviewboard之后,立刻爱上了这个玩意.我擦原来还有这么高大上的东西.当然,作为三把火工程的配套工程,新团队还给了一些suggestion,比如关于代码规范性方面的建议,比如提review应该小步快跑,比如最好同时提给2个以上的人看等等.然后就是近似强制使用到现在.

个人总结一下code review带来的好处:

1.能缓解技术团队管理上的单点问题.共同维护一个大系统的程序员团队很容易成为某人负责某个模块的形式.不可否认这么分配效率是最高的,但是风险也很大,原因很简单,Single Point.纵然管理上一直在提敏捷,说所有人对所有代码负责,但是最终的结果基本上就是没人对别人的代码负责.但是如果强制的去要求团队成员轮流换模块开发,那么整个团队的开发效率又会有问题(尤其是项目规模稍大,且业务逻辑非常复杂的情况下,要每个人都非常清楚所有模块,确实不太可能).那么采用codeReview的形式,将2~3人分组,一个主力开发,另外的人负责review,那么至少会有人对代码熟悉.单点情况能缓解很多.

2.对于个人来说,当我知道我的代码必然会被别的同事审阅的时候,那么在无形中我就会更认真的对待.比如在变量命名上更仔细,或者在代码结构和运行效率上考虑的更多.那么当我review别人的代码的时候我也会更加注意同事的代码有没有疏忽和遗漏.当然,在经历了前几次扭捏之后,现在大家在review的时候提问题已经脸不变色心不跳了,毕竟都是就事论事,也算是加深了同事之间的相互信任吧.

3.有助于培养团队整体的代码风格.每个人都有自己的代码风格,那么某种自己熟悉的写法如果受到团队其他成员的一致差评(破窗户被第一次发现),那么这个问题下一次一定会被纠正过来的(立刻被修补).反之,阅读别人代码时,我也会有惊喜:原来还有这么牛叉的api和用法, 下次我也用!

 

对于还没有接触过code review的同学,我再给出一点小建议:

1.无论团队有没有code review的习惯,自己在commit之前,重新diff一遍,认真的读一遍自己的代码是绝对有好处的.

2.在团队中开发中,自己可以偷偷的check下来同事的两个相邻的版本,然后看看diff,一来可以了解一下业务,二来培养一下自己阅读代码的水平,毕竟读开源代码所花费的精力远超过阅读自己天天接触的系统的代码.

操作系统 (一) 计算机系统概述

操作系统的书拿到公司已经很久了,但是由于工作繁忙一直没有拿出来认真读起,马上就要过年了,工作节奏终于放缓,于是又重新拿起,争取趁着春节期间看完.这次我决定读完每章的2天以后再着手写读书笔记,一来可以先对之前的内容做个回顾和复习,二来每次读完书后往往都会比较晚,此时写读书笔记时间上也会有问题.

第一章 计算机系统概论

本章主要是复习了计算机组成原理中的部分知识:

寄存器,IO,寻址,中断,缓存,LRU等内容.

其中关于缓存部分的附录是我比较喜欢的部分.

这里讲到了局部性原理,包括空间局部性和时间局部性.

空间局部性(Spacial Locality)指执行涉及很多簇聚的存储器单元的趋势,这反映了处理器顺序访问指令的倾向,同时也反映了程序顺序访问数据单元的倾向.

时间局部性(Temporal Locality)指处理器访问最近使用过的存储器单元的趋势,例如,当执行一个循环时,处理器重复执行相同的指令集合.

局部性原理无论是从语言或者研究层面都得到了验证,这也是缓存器设计的基本依据.

<淘宝技术这十年>读书笔记–好架构和好团队是随着好公司一起成长的.

粗略的翻完了子柳的<淘宝技术这十年>,非常赞同”好架构不是设计出来的”.

1.淘宝最初的lamp代码是花钱买的,此时投入产出比非常重要.

2.最初应对数量增长的思路是加机器(可以看出从webserver到DB读写分离,其实思路差不多).在前期这其实是最为划算和最简单的.

3.当加机器无法满足压力时,放弃了开源产品而转投商业产品.比如购买IBM小机,外聘Sun公司来开发支付宝,但是由于外来团队的一些理念,包括商业利益在作祟,最终还是走了一些弯路,比如EJB的使用.

4.当商业产品也无法满足时(或许还有成本考虑),果断的去IOE.这个过程也是最为关键的思路转变,因为对技术团队的要求非常高,而且意味着技术团队本身要开始摸着石头过河,面对很多商业产品也未曾涉及的领域进行研发.虽然这个转变并不容易,至少历经了三年以上的时间,但是也是奠定淘宝技术大佬地位的关键.

5.TSF,TDDL等一系列产品的出炉都是在对自身业务需求清晰的梳理后,运用强大研发团队自己开发的.可以看出,最开始出现的都是一些相对偏重应用层面,研发难度稍小的中间件和功能模块,后来逐渐深入到了存储,网络分发等更加偏底层,且研发难度更高的基础架构优化.这种高压力倒逼技术升级的场景,使得技术人员越发优秀,研发团队的自信心逐渐增强.

6.淘宝从开源到商业再到自主,是一条常规的技术升级之路.只是淘宝规模的迅速扩大,使得这种技术的升级迭代更快.可以看出,几乎淘宝前5年时间就走完了从lamp到真正的分布式计算之路,高速发展依赖于人才,我觉得这里面透露出淘宝在人才储备和培养方面工作也很出色.

重读基础—计算机硬件及组成原理学习笔记(十,十一)

读完了第十,十一章,由于阅读本书的目的是为了了解CPU基本体系结构,而非为了编写汇编语言,所以对各平台的指令部分只是略读。重点关注的是寄存器,寻址方式,状态寄存器,指令类型及执行过程中的异同。

现总结如下:

名称 68k 8086/X86 ARM
指令类型 CISC CISC(Pentium后采用CISC指令+译码为RISC执行) RISC
寄存器 地址寄存器+数据寄存器+状态寄存器 地址寄存器+数据寄存器+状态寄存器 通用寄存器+状态寄存器X2
寻址方式 寄存器寻址+存储器寻址 寄存器寻址+存储器寻址(分段寻址),80386后由于地址线增加,故不需再使用段寻址。 寄存器寻址(使用Load和Store与存储器进行数据交换),由于指令定长,所以立即数只能通过4偏移X2+8位进行计算,这样做的弊端在于不能表示所有的32位立即数,当立即数并非在此区间时,需要多条指令来计算此立即数。
指令长度 不定长,执行需要多个时钟周期 不定长,执行需要多个时钟周期 定长(32bit),多数情况下单时钟周期即可执行完毕,只有少数指令需要多个时钟周期,支持较为复杂的汇编指令,如MOVES
其他 除了地址寄存器A7和A7‘,其他数据寄存器之间和地址寄存器之间是通用的。 某些指令必须指定固定的寄存器,比如乘法必须使用AX,且结果放入AX中。 乘法累加器(用于计算积分),桶形移位器(MOVE+位移,由于是异步的,所以单时钟周期即可完成)

重读基础—计算机硬件及组成原理学习笔记(八,九)

这两张讲的内容比较相近,所以放在一起写.
主要内容有两点:
68k的主要寻址模式
68k的指令集相关

这两点知识引申出了许多的内容
1.CPU存储部分主要包含 数据寄存器,地址寄存器,状态寄存器,指令地址寄存器,管理栈指针(A7和A7`,这里牵涉到用户态和管态,详细内容参考操作系统)

当使用计算等操作时,如果产生了溢出,除零,结果为0等状态,其状态寄存器的相应位置都会被置有效,而后方便CMP(比较)或者BEQ(条件转移)等指令进行判断和处理.

2.栈指针地址寄存器是由高到低变化的,方便实现LIFO.

当使用跳转指令进入子程序时,一般的处理方法是子程序内负责保存当前各寄存器的数值到栈中,在结束运行时,再进行恢复,而后才会跳转回上级.—–了解了这条知识以后,很自然的就联想到了尾递归的实现,结果经过查找,发现C语言对于尾递归果然是需要编译器加优化参数才可以实现的 🙂  参考材料点击 这里

3.假如要做可控范围内的一个循环,那么简单的将指令堆积,运算速度最快.这种不需要进行转移或者循环的内联代码,计算机处理效率最高,原因以后在学习到流水线的时候会讲到.

4.C语言到汇编的过程中,由于编译程序会考虑所有的情况,所以编译后的代码中会有很多无用的代码,这也是为什么手写汇编会比C语言快一些.

5.简单的阐述了一下由机器码反汇编的知识.主要是汇编到机器码的一些映射规则,这些由于比较细节,所以只是简单了解了一下.