作者简介:叶顺平,现在是国内某个著名的创业公司的技术总监,毕业于北京大学。他是待字闺中特邀作者。我们也希望有更多的作者投稿。
与Google相似,我所在的创业公司也是信仰“工程师文化”的团队,研发团队由Google前科学家、人工智能专家领衔,团队成员来自哈佛、MIT、斯坦福、剑桥、清华北大等名校名企。因为我们公司有谷歌的投资背景,和谷歌也有很多合作,加之主要创始人的谷歌背景,使得我们的工程文化与谷歌有相似之处。在此,我想谈谈我们公司的工程文化,希望能起到管窥谷歌工程文化的作用。
基础代码库
我们公司的很多基础代码,是从很多谷歌著名的开源项目的基础代码发展而来,包括chromium, re2, grpc等。强大简单的基础代码,既保证了工程质量和开发效率,又保证了我们使用和整合谷歌代码的高便捷性和一致性。
我们的基础代码经常更新,以保证各个组所需要的相似需求,能够被高效实现和被良好地测试。我们在平时代码审查的时候,如果发现某个代码实现的不错,并且这个代码(函数,类,或者文件级别的代码)可能组内的其他同学,或者是其他组的同学可能会使用到,那么reviewer就会建议开发者将这段代码放置在基础代码目录下,并进行适当的重构和范化,提高测试的覆盖率。如果需要的话,也考虑在codelab中添加一些example代码演示如何使用。
我们的基础代码库,会保持与时俱进。比如,gcc,g++等编译器会定时升级,以跟进语言的最新发展,同时也享受新编译器带来的性能改进收益。同时升级了新版本的编译器,我们会花时间解决在高版本编译器编译时带来的新的编译警告,保持基础代码的零编译报警(没错,在编译警告级别最高的情况下)。
与时俱进的地方还在于,如果语言有新发展,我们会跟进,并及时重构基础代码。比如我们基础代码里,长期存在Thread, Mutex, MutexLock之类的代码,C++11推出后,这些都有了标准的实现,我们也及时重构了我们的这些代码,该重构的重构,该删除的删除,该添加 deprecated 编译警告的就添加上。相关的代码,也及时跟进。
与时俱进的地方还在于,如果我们发现基础代码库的里函数,有了更高效的实现,我们就会重构相关代码,提高效率。比如有一些开源项目,比如stringencoders就对很多高效的C字符串操作的实现,假如我们基础库本来存在StringToLowerASCII的实现,但是实现方法很普通,那么我们就会考虑移植stringencoders,然后使用其内部实现重构我们的StringToLowerASCII函数,保持接口不变,但是性能却得到了很大的提升。
第三方谷歌项目
我们内部使用了大量的第三方库,充分拥抱开源社区。其中第三方库里,谷歌开源出来的项目占比最多,在内部使用也最为频繁。以下简单列举一些谷歌的开源项目。
RPC通信使用grpc
对象序列化和反序列化使用protobuf
压缩解压使用snappy
简单的对象存储使用或扩展leveldb
正则表达式使用re2
日志使用glog
命令行解析使用gflags
单元测试使用gtest & gmock
Cpu profile & Heap profile使用gperftools
有限状态转换机使用openfst
机器学习平台使用tensorflow
总之,谷歌有什么好用的最新开源项目,我们就会跟进使用。除了谷歌开源项目的优秀品质外,和公司内部统一的代码风格,也使得移植进来的成本比较低。
另外,这些开源项目如果有一些新的版本出来,我们也会及时跟进。比如protobuf之前我们一直使用的是proto2 , 后来proto3出来了,我们也迅速跟进,老代码能迁移的就迁移,不能迁移的暂时使用proto语法,但是新代码强制要求使用新语法。像语音识别领域使用非常广泛的openfst, 最新两年也推出了不少新版本,每个版本也会有一定程度的改动,甚至是接口不兼容。但是我们内部的态度一直是,如果是新版本,尤其是稳定版本,我们会尽可能跟进同步,哪怕需要解决一些编译失败问题。因为我们相信,花费的这些升级版本的时间,相比带来的好处,显而易见是利大于弊。
我们早期使用百度的pbrpc作为protobuf rpc框架,但是百度只开源了C++的实现,并没有其它语言的bindings, 因此虽然pbrpc的代码我们虽然稳定运行了两三多,我们也在Grpc开源后,第一时间跟进迁移,因为grpc有几乎所有主流语言的bindings, 方便我们跨语言交互,比如Java 和C++,比如C++实现server, python实现debug client tool。
上面介绍的一些开源项目,后面如果有时间的话,笔者也希望有机会能做更进一步的介绍和分享。
编译工具
我们最开始使用的是国内的一个开源编译工具blade,后来2015年的时候,Google bazel开源,我们第一时间将内部几乎所有C++代码都迁移到bazel进行编译。包括语音识别,搜索,语音合成,计算机视觉等所有AI相关项目。
此外,我们的Java代码和python等语言的代码,也在慢慢迁移到Bazel。 我们相信,统一的、简洁的编译工具,可以帮助工程师专注在功能实现上,而不需要纠结在编译过程中。
统一各个语言的编译工具,也会促成前后端的协作,促成Java和C++的跨语言交互。最终的目标,当然是打造全栈工程师的环境,不管你写C++还是Java,还是python,你都可以使用统一的编译方式,部署上线也几乎毫无差别。我们公司里,一个写既写网页前端代码,也写Java和C++的后端代码,写安卓或者iOS代码的工程师不在少数,我想这也和公司追求的简单编译的开发环境和编译环境有关。
我们内部实现了很多不同的bazel扩展,以更好地服务不同组的编译需求。比如语音组对GPU比较有需求,我们就研究怎么更好地在bazel中支持gpu,将GPU支持相关的编译都隐藏到编译工具内部,普通开发者基本不需要关注编译命令和编译参数的不同。
总之,我们相信“工欲善其事,必先利其器”。听说谷歌内部bazel的名字叫blaze,其翻译过来就是火焰的意思,相信火焰之锋芒,可以为开发者节省不少时间。
代码风格
在我们公司,我们和谷歌一样,主要使用C++和Java语言,脚本语言使用比较频繁的是python,这三大语言,我们都是follow谷歌的coding style。一致的代码风格,既能让我们提高工作效率,也能更方便地和谷歌进行合作。
另外,我们也在推行工具文化,代码风格的检查,尽可能工具化,保证代码风格的推行得到切实的落实。
另外,我们会重视基础工具的开发和完善,我们觉得投入精力在这上面,是一本万利的事情,是磨刀不误砍材工。我们会及时更新语言风格的变化,及时同时自动化风格检测的脚本,适当的时候也会开发一些自己的脚本,以进行一些检查条例的扩展,或者是其它语言的自动化检查支持。
Code review文化
我们公司的技术Lead,很大一部分精力花在了代码审核上。我们相信,code review既能够保证代码质量的提高,也是技术传承的绝佳手段,可以很快的帮助新同学提高代码水平。Show me the code, 我们的工程师需要show code给相关同事,相关同事会事无巨细地做review,大家都喜欢吹毛求疵,追求谷歌级的代码质量,并引以为荣。
相比其它公司的技术负责人花大部分时间在开会,我们公司的技术Lead,花30%+的时间在做代码review,我们的CTO,也几乎每天都在做代码review 。笔者自己每天除了编写代码外,也花了相当大的精力在代码审查上。有句话叫教学相长,更何况被你review的人本就是同事,可能能力比你还强,作为reviewer,在审查别人代码的同事,自然也能学到很多东西。
对工程性能的追求
我们对性能有着近乎变态的追求,每个模块上线前都需要做详尽的性能测试。我们在嵌入式设备上的离线语音识别,离线语音合成系统,会做各方面的优化,包括速度、内存占用等。在云端跑的各个服务,也会尽可能改进性能,对延时的优化到毫秒级。这不仅是为了节省机器,更是为了锻炼工程师,将对性能的追求,和对代码简洁之美的追求一样,化入骨髓。
性能优化的时候,我们不仅追求算法的高效,编程语言的高效,必要的时候也会跟进不同的平台进行不同的优化,充分利用各个平台的硬件性能。我们追求让程序跑得快的快感。
上面零零散散介绍了我们的的工程实践,也算是工程师文化的几个切面,不成系统。希望对读者朋友有一些帮助。大家在工程师文化方面,有什么好的实践经验,在编程过程中,有什么好的心得,也欢迎留言给我,大家一起讨论总结,让编程变得更舒服,更快乐。
大家可能还感兴趣:
谢谢。