Unix·Talk是今年开始实行的木犀团队系列技术分享,面向的是团队内所有的技术同学,尤其是新人。在这个系列分享里,我们会聊聊计算机硬件,工程化实践,互联网文化,当然也会聊聊和Unix操作系统有关的一切。

工程是什么

首先请大家观察这两张图:

vs

如果说左边的小草房是课程作业的话,那右边的高楼大厦,就是我们所说的工程。

大家可以思考一下,建造两者的过程有何不同。建左边的小房子也许不需要太多的材料,也不需要对材料进行深度的加工。在设计上,只要有基本的构想即可,不需要精准的蓝图。在施工上也相对随性一些。

而右边的高楼大厦,需要钢筋混凝土这种深度加工的材料去构建,需要严密的蓝图和施工周期。同时也需要数百倍于左图的人力去建造。

从产出的规模和流程的复杂度来说,软件工程师和建筑工程师或者电气工程师一样,都在一套成熟的流程和体系下,打造大规模的产品。

所以,在互联网行业,工程就是一套完整的产品研发方法论。同时也包括项目开发周期中的项目管理,前期估算,后期测试等流程。

那为什么我们需要这套方法论呢?很简单。如果没有这套方法论指导,我们就无法顺利的开发出大型的软件,或者,我们的开发效率会很低。我们需要用工具来加速我们的开发,自动化一些无聊的工作。我们需要用设计模式来解耦我们的代码,让代码的可维护性更高。我们也需要项目管理,来让不同分工的工程师协同工作,顺利完工。

而这套方法论,学校里是很少有讲到的。这也是因为,工程相关的方法论需要工程师在项目开发的过程中不断的实践总结,毕竟纸上得来终觉浅,而工程这种和实际问题特别相关的方向,则更需要在实际的项目才能有更深入的理解。

工程的几个关键词

工具

俗话说,没有金刚钻,就不揽瓷器活。无论是客户端还是服务端,大家都有专属的神兵利器。

其中大家使用频率最高的便是编辑器或者IDE。前端有Sublime,安卓有Android Studio,iOS有Xcode,后端有Vim。强大的IDE往往 包含了从代码提示到编译打包的一条路服务。而Sublime这样的轻量级编辑器,需要我们进行各种DIY,打造成自己想要的样子。好用的编辑器往往会提高开发的效率,带来生产力的提高。

在客户端开发中,打包构建工具也是一个重要的类别。好用的打包工具不仅仅可以降低打包的时间,在前端等领域,好用的打包工具甚至代表了一种工具生态,串联起了一个完整的工具链。

开发中用到的工具还有很多。很多小工具的诞生,往往是为了解决某一个具体问题的。程序员往往希望一切机械的事务都可以自动化。并且也喜欢将繁杂的数据可视化。这两个动机驱动了很多小工具的诞生。

趁手的工具代表了先进的生产力。这也是构建大型工程时必须借助的垫脚石。工程化程度高的技术团队,往往都有着自己的先进工具。

编码规范

编码规范包括代码风格,目录结构等等。这些编码规范的存在,一方面是约束团队成员的代码质量,使得一些低级错误和语言中的bad practice得到提醒和控制。一方面,代码规范中的一些代码风格相关的部分,是为了让团队成员的代码风格统一,这样有利于后期代码库的维护和迭代。

我们可以使用一些Lint工具和持续集成等技术来将编码规范落地。一个团队的编码规范应该被写在一个单独的仓库或者文档里,以供团队成员参考。

设计模式

设计模式有很多,从常见的工厂模式,单例模式,观察者模式,装饰器模式等到应用架构层面的MVC,MVVM等。

设计模式主要帮我们将一个大型的系统解耦成不同的层次。比如将数据层和视图层分离,并用一个中间层将二者联系起来。

因为大型的系统的模块数量众多,不同模块之间关系复杂。我们要用设计模式来将不同的模块划入不同的层次。

而观察者模式这样的设计模式主要是为了使模块之间可以实现松耦合下的通信。虽然模块之间有着各种的依赖和调用关系,但我们并不打算让不同模块直接相互调用,而是实现一套接口,并借助中间人通信。这样如果某个模块被替换,只要新的模块实现了相同的接口,整个系统依然可以正常运行。

在开发软件时,我们一般都遵循一套成熟的模式,在此基础上,完成业务代码的编写。设计模式也是大型软件的基石。

对于设计模式,关键点往往不是你能说出几个设计模式,而是你懂得在什么情况下使用合适的设计模式。

版本控制

现在主流的版本控制工具是Git。Git主要的作用是版本控制,同时也帮助我们进行多人的协作。

没有Git的话,如果我们需要保留多个不同版本的代码,就需要手动保存。在多人协作时,没有Git也是无法想象的。比如用FTP进行协作,每次都覆盖式的保存,这样的的代码管理无疑是非常原始的。

事实上Git的Branch以及Github的pull request等等机制造就了一套独特的Git工作流。这套工作流无疑是团队协作的最佳选择之一。

抽象

其实编程的精华便在于对过程(precedure)以及数据的抽象。这是一种编程的内功。通过对过程的抽象,我们可以将程序中可复用的模块提取成单独的函数,然后进行组合,这样可以大大增强程序的表现力。通过对数据的抽象,我们可以封装数据的细节,从一个更通用的维度来对数据进行操作。

对于抽象,推荐大家看《计算机程序的构造与解释》这本书。当然抽象要合理才行,过度的抽象也会使代码的易读性受损。

测试

测试主要是为了保证软件的质量。在软件被开发完成之后,会进行一系列的测试,以保证软件的高可用性。一般团队会有专门的测试工程师。负责测试用例的编写。

对于后端这样主要负责数据处理的逻辑,我们可以编写自动化的单元测试,并且进行持续集成。单元测试以一个模块为单元进行测试,这样我们在错误发生时,可以准确的定位到具体的模块。单元测试也被用在测试驱动的开发中,测试驱动的开发就是先写测试用例,然后编写可以通过这个测试用例的代码。

对于客户端代码来说,因为UI复杂多变。所以自动化的单元测试只有在对框架/库的代码进行测试时比较实际,在业务逻辑层面,比如视图层,我们一般采用人工测试。当然现在也有自动化的UI测试方案。

项目管理

项目管理,是学校软件工程课程中主要涉及的。这个主要涉及传统软件的开发流程。从需求分析,成本估算,开发流程到最后的测试。

互联网的开发模型上主要还是比较敏捷的迭代。通常以一个月甚至几周的周期进行迭代。其中Web应用的特点便是可以随时发布更新,所以在这方面显得更突出一些。

关于项目管理或者软件工程,大家可以去看《人月神话》,《构建之法》等书。虽然这些书主要针对的是传统软件工程,但我们在互联网产品的开发中还是可以从中汲取一些经验的。

适合我们团队的一套工程化流程

  • 技术选型-选用最合适的技术栈,当然为了把玩新技术而选用比较新的技术栈也是可以的。
  • 合适的工具-鼓励大家,没有在开源社区找到符合自己要求的工具时,打造自己专属的工具。
  • Git协作-Git工作流
  • 代码规范
  • 项目架构-合适的抽象,不仅仅是MVC等架构,也包括其他可以复用的组件和Utility模块。
  • 代码审查
  • 单元测试(后端)
  • 人肉或自动测试(客户端)