<audio id="audio" title="04丨主观能动性：为什么程序员，需要发挥主观能动性？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/66/36/6637a3d8edbe9bc85ea8473f8903bb36.mp3"></audio>

其实关于这个主题，我也仔细想过，是放在生存发展篇合适，还是放在职业素养篇合适。最终还是觉得，作为程序员，发挥主观能动性应该算是一个基本的职业素养。

很多时候，只要我们勤勤恳恳，认真负责地做好老大交代的任务，就算是一个合格甚至优秀的员工了。但是程序员这份工作，如果只是做到这一点，最多算是合格。由于软件开发的特殊性，一个任务完成的界限是非常模糊的，而且会根据具体的情况而变化。那么这时候，就要求程序员发挥自己的主观能动性，才能把事情做成，能够交付，而不仅仅字面意义上的“完成工作”。

你可能会想，说得这么玄乎，是真的么？我们来看两个例子。

## 数据清理和数据标准化背后的要求

在一个数据处理和分析的项目中，有两个功能是对订单数据进行数据清理（data cleaning）和数据标准化（data normalization）。

我首先简单介绍一下数据清理和数据标准化。我们要知道，来自不同的订单数据源的数据质量是不一样的。有的会缺失重要的信息，比如说购买者的ID、商品详情、订单时间等等。过滤掉这种缺失信息的不合格的数据，这就是数据清理。

数据标准化则是对数据中的数据格式进行标准化转换。比如有的时间用毫秒表示，有的用秒数表示，有的用不同的格式“2020年5月15日 15点30分15秒”“2020-3-15 15:34:45”，有的甚至用不同时区的时间。再比如对于苹果这个品牌，有的数据用“Apple”表示，有的用“苹果”表示，有的用“苹果（Apple）”表示等等。那么数据标准化的任务就是要将这些数据转换成统一的格式。

### 怎样才叫“任务完成了”？

如果按照需求文档里的描述，我们完成了数据清理和数据标准化处理，这样是不是就叫完成了呢？如果你是负责开发的程序员，你还会做些什么呢？

你可能会想到单元测试，代码覆盖率等。不错，这说明你已经是一个“摸着良心”干活的程序员了。那么除此之外呢？还有什么可以做的呢？如果就这么交付出去可以吗？

一个有经验的程序员会想到，这种功能可能会用到不同的计算框架上，比如 Spark、Flink 甚至 是Hive。而之前蹚过的坑会告诉他，不同的计算框架内置的Jar 包的版本都是不一样的，自己的程序要能够尽可能少用兼容性差的 Jar 包。那么也许他就会在开发期间，跑去问相关的用户，这个功能可能会跑在什么框架的什么版本上，然后按照计算框架的版本，确定自己使用的 Jar 包的版本。当然，系统架构设计等也是一个需要用心的地方，我们在后面再细聊。在这里先不涉及。

做了这一步，用户集成的过程就会顺畅很多，虽然你自己确实付出了一些额外的时间，但可以帮助整个项目的进度不被 Jar 包兼容性的问题所阻塞。

当然，从责任上来说，功能是否要适配到不同的计算框架，应该是在需求上写清楚的。但是道理归道理，实际归实际。没有人能把所有的细枝末节都考虑全面，互联网时代，软件开发和迭代速度并没有给我们这么宽裕的时间。

如果因为各种需求没有说清楚，导致最终做出来的功能无法使用，我们程序员虽然可以把锅甩出去，但程序员作为一线工作人员，很多细节可能只有走到那一步的时候，才能想得全面。如果一个程序员做事情永远只知道按照需求中写的做，不多考虑一分，实际上就是自己的失职。从结果上看，就是程序员没能交付自己的工作。长期如此，是很难成长为一名合格的、让人觉得可靠的程序员的。

站在用户的角度试想一下，如果用户的这个项目要在 Spark 上用到两个功能。一个功能出现了各种 Jar 包版本兼容性问题，各种跑不起来，各种修改 Jar 包版本，甚至还需要修改代码，整个集成过程从原计划的三天拖延到了三周。另一个功能一下就用上了，一点毛病没有，原计划三天的集成时间，一天就搞定了。你会给这两个功能打多少分呢？又会倾向和谁合作呢？

当然，这里的例子其实是一个比较明显的例子，确实应该在需求中写清楚平台和版本。但是在程序员的工作中，确实有很多我们需要考虑的细节，有很多考验我们“良心”的地方。发挥自己的主观能动性，多为用户考虑一点，是评判一个程序员是否合格的重要标准。

### 一个合格的Dashboard是怎样的？

聊完了数据清洗和数据标准化，我们接着聊下一个需求。这个需求是设计一个Dashboard，把每天的销量和销售额用一个Dashboard展示出来。

这个需求看似很简单，就是把数据按天展示出来嘛。按照需求，我们先原封不动地作出一张如下图所示的Dashboard。

<img src="https://static001.geekbang.org/resource/image/e7/61/e768fcec1e1f9bfa5e0cb5f6c0c25361.png" alt="">

我相信，大部分用户看到这个图，第一个问题都会是：怎么就一根线？你可能会说，我确实把销量和销售额都展现在图上了啊，按照需求做的没毛病啊。用户有问题那是他们需求有问题，用户只看到一条线那是用户不会看。

这种看似忠于需求的工作态度，其实并不能真正地让用户对工作成果感到满意。当然，程序员将需求做出来了，确实只是如此，没有人能挑出毛病，但也不会有人喜欢跟这种程序员合作。

为什么呢？正如上面的例子所说，在互联网快速迭代的今天，需求可能不会那么细致。我们依然要站在用户的角度看问题。

那么这张图到底有什么毛病？

在上图中，因为销售额比销量大很多，销量被销售额的数字“压”得几乎成了一条底部的直线。那么这样一来，有一个很明显的事实就是，销量这根线，已经不能传递任何信息了。除非用户是列文·虎克，有耐心还喜欢拿着显微镜看报表。

所以用上面两个例子我想说明什么呢？

完成明面上的用户需求，仅仅只是我们工作的合格线而已。一个程序员应该基于需求，把自己的触角延伸到需求之外，交付用户真正想要的东西。比如给报表增加按照对数设置Y轴坐标的功能，让数据相差特别大的两根线，也能在同一个Dashboard里展现自己的“曲线”，如下图所示：

<img src="https://static001.geekbang.org/resource/image/e5/5d/e5b6b055996aa9032ac1633cf65a775d.png" alt="">

我相信，任何一个用户都会更喜欢第二种方案。虽然需求没有明确说要支持这种功能。但是从交付的角度，第二种方案才能对用户产生实际的价值。

那么前面通过两个例子，我描述了一下程序员这个工种对发挥主观能动性的要求。下面我们来谈谈如何发挥主观能动性。

## 如何发挥主观能动性

其实发挥主观能动性的方式会随着程序员具体工作内容的变化而变化，比如说前端工程师、后端软件开发师、架构师等等。但总有一些东西是共性的，那么在这里，我就说说我的几点建议以及需要注意的东西。

我在上面反复强调交付思维，所以我觉得这一条应该列在第一位。

### 交付思维

发挥主观能动性，究其核心，我觉得就是一点：站在用户的角度，交付用户想要的东西。也就是说，不能止步于用户的需求。程序员作为冲在第一线的人，对细节的掌握是最多的。我们需要依靠这些细节，结合用户的需求，理解用户需求背后真正想要的东西，然后努力向这个目标发展。

正如前面的两个例子，其实做得好的标准，就是理解用户没说出来的需求，能够为用户着想，交付用户想要的东西。

### 注意时间

发挥主观能动性的一个代价，就是会用掉更多的时间。这方面一定要注意。比起功能的完美，在规定的时间内实现基本功能，才是优先级更高的事情。

假如你突然对一件事情有了想法，但是时间来不及，或者不确定是不是对用户有价值，那么可以及时和用户交流。如果用户觉得这个细节确实很重要，即使延期也值得做，那么大家可以商量新的时间线。如果用户觉得可有可无，或者可以放在后续迭代来做，那么就专心做好需求里描述好的功能。

程序员在发挥主观能动性的时候，也难免会“夹带私货”。比如说，自己想用个什么新技术，试试不同的做法。这时候也要注意时间。用户可能一时无法理解新东西给自己带来的好处，但是用户肯定知道项目无法按时完工的坏处。所以在“夹带私货”的时候，一定要保证自己对项目的进度有所把控，不要因为自己的私欲让整个项目无法完成。

## 总结

程序员这个职业，已经远远延伸到了写代码之外。对内我们要DevOps，对外我们要交付对用户有价值的东西。而发挥主观能动性，就是帮助我们做对用户有价值的事情。程序员接到需求之后，要进一步理解需求背后的用户意图，理解用户的问题。

正所谓，将在外，军令有所不受。又有言：让听得见炮声的人决策。程序员就是那个拿着作战目标，冲在一线，能够听得到炮声的人。面对系统实现时各种复杂的情况，我们有责任，也有义务发挥自己的主观能动性，达成最终的作战目标。

<img src="https://static001.geekbang.org/resource/image/5b/f6/5b6aaf94e387a5f4767519d6df7c60f6.jpg" alt="">

## 思考题

你在工作中，有发挥自己主观能动性的习惯吗？有发挥自己主观能动性的场景吗？

欢迎你在评论区和我分享你的留言，也欢迎你把这篇文章分享给你的朋友或者同事，一起交流进步一下。
