自动驾驶软件工程设计要点
软件工程是业务系统的基础,基础好不好,直接决定了整个项目的成本和效率。磨刀不误砍柴工,打磨好基础才能无往不利,快速迭代。具体来说需要包含这几点:
- 概念模型要准确且清晰;
把一个业务抽象成怎样的模型,用怎样的机制让系统运作起来,这是设计之初首先要思考的问题。抽象的层面越高,扩展性越好,但是也不要抽象得太过导致过于晦涩,基本上能够cover业务的所有场景就可以。然后把其中可以统一的概念和操作抽象出来,并且考虑在未来的一段时间内哪些因素是可能变化的,把可能变化的地方也抽象出来。这样基本上整个业务的抽象就足够了,如果后期碰到没有预料到的改动,再进行概念模型的进一步抽象。完成了抽象之后,要把机制设计好,业务的运行逻辑如何映射到软件中来,需要把其中的通用机制抽取出来,让策略和机制是分开的,既方便业务的开发和扩展,也方便软件工程的维护升级。抽象出来的概念和机制要和领域专家进行深度的探讨,真正满足业务的需求,并且将这些概念和机制同步给他们,彼此间建立统一的语言和认知,对于今后的开发和迭代都是非常有意义的。
- 编码就是写作,要严谨、优美、清晰;
编码不是一个人的狂欢,是整个公司生产力的一部分。代码的价值不仅体现在功能实现上,还体现在其维护性上。糟糕的代码尽管短期内能用,但是难以理解,充满了混沌,容易存在很多潜在的bug。也没法在团队中进行有效的交接,价值就大打折扣,基本上注定是要重新写的。好的编码和写作是一样的,得有缜密的思路、优美的语言和清晰的结构。首先构思是经得起推敲的,能够给别人讲明白,也能经受别人的challenge,所以编码之前,把思路写出来并且讲出来是十分有必要的,形式可以不限。其次每个变量的命名都是要斟酌的,要清晰表达其意义,用词要准确,不能和别的东西混杂,尤其是在相关性比较强的代码段之间不同类型的变量就应该有完全不一样的关键词缀。最后结构要划分好,函数和模块要划分清楚,不要整得像一坨屎,也不要拆得七零八落。
- 任何一个设计实现都必须是可验证和检测的;
这是经验较少的程序员最容易忽视的问题,一般只考虑怎么实现功能,却没有思考怎么验证。无法验证的代码是没有意义的,上线了只能带来无尽的调试麻烦,出现问题时势必会怀疑这儿怀疑那儿,浪费时间。所以每个函数,每行代码都是要可测的,并且要设计充分的测试用例,证明其准确性和正确性。上线之后也要考虑如何验证关键点的正确性,就像造了一栋大楼会保留很多的检修点一样,软件上线后没法检验出问题就抓瞎,只能再次复现和跟进,最终甚至可能需要返工。所以log一定是最开始要做好的组件,打印log的地方都要思考是否充分且必要,log的内容是否足够定位这个地方可能出现的问题,如果有错误,一定要清晰的打印出来,说明错误的原因。和其它模块的接口部分要有清晰记录,避免后期分锅带来不必要的麻烦,提高研发效率。
- 设计之初就要想好性能指标,并且建设配套的指标体系;
大的软件工程的建设非一日之功,需要长期维护和迭代的。从管理和技术的角度都需要能够看到技术迭代的路径和成效。一个软件实现了整体的功能,其实只实现了20%的目标。商业化产品是需要不断优化来降低成本,实现商业最大化的。硬件能不能换更便宜的,带宽能不能用更小的,研发时间能不能再缩短,这些问题才是那80%的工作。不要忽略软件的每一个环节的性能和效率,从编译、开发、调试、集成和运行时,每个环节的耗时、CPU占用、内存占用、IO、网络带宽都是很重要的指标。就拿编译来说,每次编译时间节省一分钟,整个部门能节省出很多的人力,让算法同学的时间消耗在编译等事情上更是对生产力的严重损耗。运行时的指标就更重要了,关键指标要定义清楚,和第3点一样,要让整个系统是可验证的,能够清楚的知道热点在哪里,这样才可以有的放矢,高效地提升系统性能。
总体来说,软件工程之于现代工业就像土木工程之于现代建筑,只知道搬砖是不够的,这些点是我现在深有体会的点,也希望能够在今后的实践中不断完善我的工程思想,打造我的工程风格。