17 January 2016

虽然我的技术方向偏机器学习、数据挖掘,或者说算法策略,但我一直以为合格的算法工程师要有算法策略、工程开发和产品思维这三方面的综合能力,并突出算法策略这块的能力。这篇文章就着重说说我对工程开发,也就是软件开发的一些体会。

不少算法工程师认为玩模型弄最优化算法最重要,写代码这块可以比较随意,但其实这观点是不合适的。固然模型和算法最重要,但是要把模型算法实现出来,全得靠代码。如果代码上的一些细节没有做到位,极有可能造成写出的代码功能与真实需要的代码功能有偏差,并会影响最终的模型算法效果。

很多人都有接手其他人写的模块的经历。大家一般都会评价对方写的代码质量如何,有些是写得好容易懂,加需求也容易实现;有些是写得一般,加一些小需求还凑活,加复杂需求就有点吃力;还有些写得不好,代码很难读懂,需要做细致梳理,维护起来特别费劲,有种“这代码怎么能写成这样,真想重写一把”的感觉。既然我们在接手别人的代码时会去评价别人的代码,那么别人在接手我们的代码时自然也会评价我们的代码。虽然评价甚至是批评总是存在的,但是我们大家都得先做好自己。开发是一种团队活动,需要团队协作,让自己爽,也要让队友爽。这个道理可以引申到团队管理、项目管理,道理都是相通的,就是尽可能让大家都舒服。

软件是一种产品,它包括代码、配置文件、数据文件和文档等几个方面。工厂生产的产品在出厂前会有质量评估,软件也类似,在上线前有质量评估。在很多谈软件工程的书中都会提到软件质量因素。软件质量因素有很多方面,这里就提一些常挂在工程师嘴边的内容:

  • 正确性
  • 性能
  • 健壮性
  • 可扩展性
  • 可靠性
  • 易用性
  • 安全性
  • 兼容性
  • 可移植性

对上面的这些方面做些解释:

  • 正确性一般是指对正常的输入进行计算得到正确的结果,而健壮性则侧重强调对异常输入数据,这个系统仍能保持稳定运行并返回恰当的结果,而不是会造成系统崩溃。
  • 可扩展性是指当新需求来了,是否容易实现且给整个系统框架带来的影响较小。新需求其实是一种补丁,一般来说,打补丁打多了或多或少的会影响系统框架的稳定性。
  • 可靠性一般是指长时间运行程序时是否能一直保持正常状态,比如没有内存泄露问题。
  • 第6项到第9项,做客户端开发的或者做前端页面开发的工程师会关注的多些,算法工程师可以不用那么重视。

为了提高软件质量,执行良好的开发规范是很有必要的。不少工程师以为开发规范只是提些代码格式、命名格式之类的,其实不然。代码规范 > 代码格式 + 命名规定。以 Google开发规范 为例,代码格式和命令格式只是其中的第6点和第8点(如图1所示),还有很多其他方面的内容,它们与健壮性、可扩展性和可靠性非常相关。


图1 Google开发规范的目录截图

这里举几个 Google C++开发规范 中提到的点作为例子:

  • 不要在构造函数、析构函数中做复杂对象的初始化、销毁,而把复杂对象的初始化、销毁分别交给Init()、Destroy()接口来做。这个能提高代码的健壮性。
  • 不要在构造函数中调用虚函数。这个能提高代码的健壮性。
  • 构造函数只有1个参数时,需要加explicit关键字进行约束。这个能提高代码的健壮性。
  • 能加const约束的地方一定加const约束。这个能提高代码的健壮性。
  • 不要使用重载,而试试在函数名中加入参数类型的信息。这个能提高代码的可扩展性和健壮性。
  • 不要使用异常。这个能让代码易读,提高代码的可扩展性。
  • 不要写代码过长的函数。这个能让代码易读,提高代码的可扩展性。

开发规范能有效提高系统的健壮性、可扩展性等等,值得认真读读。可是关于开发规范,有些工程师会有下面2个误区:

  • 完成功能完成KPI就好,没必要照着开发规范来
  • 照着规范开发费时,先完成KPI,以后再做规范化

对第1个误区“完成功能完成KPI就好,没必要照着开发规范来”,这个观点是不可取的。如果是写一次性的代码,比如代码就一两百行,也不会被其他模块所调用,只是为了快速看结果,可以适当的不那么规范。但很多情况下开发规范是十分重要的,比如下面这些场景:

  • 维护项目:多数项目都会有维护,对于大项目而言,维护时间有时会达到整个软件生命周期的一半以上。
  • 开发大项目:在大项目中需要同事之间的协同开发。整个项目遵照同一规范,会有效提高代码质量。
  • 提供基础接口
  • Peer review

对第2个误区“照着规范开发费时,先完成KPI,以后再做规范化”,这个观点也是不可取的。有下面这些原因:

  • 当有这个想法时,一般以后也就不容易启动规范化。主要原因在于,站在公司的角度,重构系统这事很多时候不会带来收益,但这事又需要花不少时间。所以一般不那么容易启动重构系统这个工作,除非这个系统到了“不重构的话,加功能要花费很多时间,否则容易引入很多错误或者bug”的地步。
  • 养成规范开发的习惯后,开发时间其实差不多,我的感觉是至多多20%的耗时,多出来的这部分耗时主要用在模块抽象设计这块。
  • 重要的是,规范开发能有效减少系统的维护时间。

上面提到开发规范的必要性,其实关于规范的这些内容反映在诸多软件开发的理念中。站在团队协作这一层面,个人认为有2个理念是时刻要注意时刻要执行的:

  • 代码不是给计算机看的,而是给人看的
    • 这句话中的“人”包括:3个月后的自己,review代码的同事,接手模块的同事等
  • KISS (Keep It Simple, Stupid)
    • 给定同样的需求,不同人写出的代码的可读性不同,健壮程度不同。其实simple和stupid就是指让写的代码浅显易懂。不要去挑战读代码的人的智商,要让人很快了解你的模块在干啥是最重要的。

除了上面提到的内容外,还有必要养成一些好的开发习惯:

  • 代码风格保持统一
  • 开发的同时就写好单测
  • 模块代码写好后,要进行代码走读。
    • 因为编译器只会检查语法问题,不会检查逻辑问题。逻辑问题还是需要人来解决。代码走读能有效减少后面的bug修复、错误修复的时间。
  • 培养工具化、模块复用的意识

写代码和开车有点像,都有好习惯和坏习惯的说法。只要肯用心,自然就会养成好习惯。规范开发就是一种好习惯,它体现的是严谨,减掉的是技术债,值得每一位工程师做到。


原创文章,转载请注明:转载自vividfree的博客

本文链接地址:对软件开发的一点思考




blog comments powered by Disqus