程序员和工程师有什么不一样?
我刚参加工作时,面试官曾对我说:好好干两年,能迅速从程序员成长为工程师。当时我十分诧异,从众多招聘启事来看,“程序员”和“工程师”似乎并无区别,我觉得“工程师”只是个更体面的称呼。工作多年后,我才明白“程序员”和“工程师”确实不同——程序员仅负责编写程序,而工程师编写的程序要能在现实世界中创造价值。
遗憾的是,许多软件开发人员未必清楚两者的差异,有些人即便工作多年,严格来讲也只能算程序员,而非工程师。接下来,我将结合自身观察与经验,谈谈程序员和工程师的区别。
一、工程师不写黑箱程序
“程序 = 数据结构 + 算法”,这一著名公式广为人知。然而,它并不适用于描述工程领域或现实世界中的程序。有不少程序,数据结构和算法设计精妙,功能强大,系统复杂,但调试困难,一旦运行就难以停止,且无人知晓程序当前的运行状态以及内部发生的情况。
别觉得这很荒谬,我见过不少有三到六年工作经验的开发人员,仍在不断产出黑箱程序。他们遇到问题时,第一反应是直接杀掉进程重启(难道程序不能安全关闭吗)。更有甚者,直接用开发机连接生产数据库(在防火墙上开个洞)进行调试。
你能说他们技术不佳吗?他们的确能解决各种技术问题。你能说他们缺乏系统意识吗?他们做过的程序也不简单。但他们所做的充其量只能称作“程序”,而非工程上成熟的“系统”。
什么样的程序不是黑箱呢?需要考虑程序的层次划分,明确哪些(功能之外的)运行信息必须暴露和记录,以及采用何种方式进行暴露和记录,还要考虑这些操作对性能的影响,以及程序需要对外提供哪些操纵接口。当你将这些因素都考虑周全,编写出能让运行细节“尽在掌握”的程序时,就意味着你已踏入“工程”的门槛。
在这方面,互联网和软件开发的大厂更为关注,但这并不意味着个人就没有提升空间。网络爬虫是大家都会编写的程序,也都清楚要想精准抓取数据,调试过程十分繁琐。我有个朋友在某大厂编写了一套“可视化”爬虫,可通过逐步操纵语句执行,快速定位问题。这种水平的工程师可遇不可求,大家提及此事时仍会赞叹不已。
二、工程师注意实现和接口分离
Java面试中有一道经典问题:请描述抽象类和接口的区别。通常,大家都知道要将“接口”和“实现”分离。但遗憾的是,很多人所理解的“接口”,仅仅是特定语言提供的狭义的 Interface,并未考虑“接口”的真正含义。
接口的真正含义是什么呢?计算机擅长处理信息,能让信息摆脱现实障碍,实现高速流动。如果说“实现”是从事具体的繁琐工作,那么“接口”就是发出工作指令的窗口。具体工作只需完成一次,但发出指令的窗口可以有很多。
更确切地说,完成功能的是程序员,而完成功能并能设想其使用场景、让使用更便捷的是工程师。我见过不少这样的程序:登录会话最初存放在本地内存中并无问题,但当需要迁移到数据库以方便会话转移时,就需要进行大规模修改,尽管本质上只是存取操作;程序自动加载的数据出现问题时,就无法手动加载;之前手动加载的数据,改成自动加载时就得重新编写。
是否具备接口意识,能否真正区分接口和实现,是区分程序员和工程师的重要标志。
三、工程师注重功能的逻辑联系
许多系统都处于不断变化和改进的过程中。程序员关注的是功能点,而工程师关注的是功能点之上的逻辑。
任何系统都是由若干功能组成的。但在功能点之上,需要构建一张具有逻辑意义的大网,将功能点组合起来,降低系统复杂度,使其易于理解。以最简单的“登录”功能为例,它包含数据输入、数据验证、登录信息记录等功能,“登录”是这些功能的逻辑集合,也是理解这些功能的基础。
随着时间推移和业务增长,新功能会不断增加,如用户数据加载、好友通知、广告推送准备等。这些功能实现起来并不困难(因为具体明确),但功能堆积会导致复杂度急剧上升,因为功能之间的逻辑联系被切断了。因此,工程师必须思考如何组合这些功能,将它们纳入具有逻辑意义的操作中,例如“登录”。通过持续的思考,系统的复杂度才能保持在较低水平,便于大家理解。
这个例子看似简单,但实际操作并不容易。我有时看到复杂的系统操作手册,简直哭笑不得:1) 点击这里;2) 点击那里;3) 输入这个……这些操作的逻辑意义十分明确,本应一次性自动完成,将它们割裂开来会大幅提高系统复杂度,既不利于维护,也不便操作。最终收拾烂摊子的,还是开发人员自己。
我常反思自己所接受的教育,在学校编写程序和工作后编写程序,既有相似之处,又似乎截然不同。其中的差异,只有亲身经历和思考才能体会。所以,我想把自己的所见所感分享出来。悟性好且有机会接受良好训练的同学,或许无需了解这些。但对于条件有限的同学,希望我的这些分享能对你们有所帮助。