https://lambdaisland.com/blog/2022-02-17-the-fg-command
有一个名言:编程是思考,而不是打字。在做过足够长的编程工作后,我有时觉得自己更多情况下是在打字。不论人们在思考还是打字,他们使用自己的大脑,因此我相信单词“typing”是一个隐喻:打字指我们进行的无意识活动,通过肌肉记忆进行,不需要我们刻意关注。像我们这样经验丰富的人,使用了相当多的肌肉记忆。那么问题来了,我们是否也进行了同等程度的思考训练呢?
我们每个人,在日常生活中做出很多决定:有些决定是像买汽车这样的事情,有些决定是像给一个函数命名。进化让我们把某些决定交给潜意识进行,可以称之为大脑的背景过程。因此,我们可以专注于更重要的事情上。问题是:如果潜意识并没有很好地完成这部分决定,怎么办?大多数情况下,这不是严重的问题,把这个问题放到面前直接解决就可以。/例如,当你按下按键,来评价表单,但却是另一个表单。你察觉到了,然后你使用自己的前瞻性思维(foreground thinking)决定下一步应该怎么做。然而,有些时刻,在你开始这项工作以前,你就已经知道潜意识并不能很好地完成交付的工作,你能做什么来阻止潜意识占据控制地位呢?我们是否也有类似于 Linux 命令 fg 这样的功能,能够将后台进程带到前台呢?/
坦白这件事是很难为情的,虽然我已经从事编程长达十年,有些时候,当我遇到一个 bug,我大脑的第一反应是进入恐惧模式长达数小时,然后才回到分析模式。为什么?我的理解是:当处于作为初级开发者、发展技能的那个阶段,我在恐惧模式中解决问题:进行疯狂的 Gogole 搜索、试错和其他解决办法。年复一年的经历让我习惯于在恐惧模式中调试程序。然而, 现在我想改变这一点。有两种方式能够帮助我控制自己的潜意识:
- 将代码的执行过程解释给别人。(Rubber Duck Method)
- 询问自己事先准备好的问题。(Drucker Method)
远程工作教会我橡皮鸭调试法
当和 Gaiwan 团队工作时,我们完全是远程工作。有时,当我想和某人讨论时,我需要等待。有好几次,当我遇到一个 bug,我想寻求帮助,我写下来关于代码的全部细节,运行环境,以及我是如何尝试解决 bug 的。在我把 bug 报告发到 Gaiwan 的交流区后,过了 15 分钟,神奇的事情出现了:我突然有了解决问题的灵感。我快速地解决问题,并修改我已经发送的消息。橡皮鸭法真的起作用了!
实际上,我认为这种通过解释思考的方式有不同的名字:橡皮鸭(Rubber Duck)、文学编程(Literate programming)、费曼学习法(Feynman)等等。它们全都是类似的事情。
正确的问题就像是在你大脑的 fg 命令
现在,每当我遇到一个 bug,我问自己 3 个问题:
- 我是否使用了科学的方法追踪这个 bug?
- 我是否又正确的系统视角来确定问题的范围?
- 我是否拥有必要的测试(telemetry)工具?
当写一个函数、模块或者准备部署时,我存在一些类似的问题:打字太多,思考太少。
这里是一些问题,我做给自己使用的,依然处于 alpha 版本:
关于写函数的问题
- 我应该让命令和查询分离吗?
- 我是否为错误写出有条理的设计,像 try/catch 和日志?
- 我是否为错误写出预防性设计,像 pre 条件或 asset?
- 函数名应该表达目的吗?
- 我在函数旁添加了一些合适的文档字符串了吗?
关于写模块的问题
- 我是否应该明确地指定模块的 API,使得 API 明显不同于内部函数?
- 所有不必要的死代码是否都已经被移除?
- 我是否设计或使用合适的 Clojure records 来为域问题的一些不可变概念建模?
关于集成和部署的问题
- 我是否为域函数添加合适的测试?
- 我是否在安装脚本上设计了合适的反馈信息?
- 一些手动安装步骤能否被自动脚本替代?
「使用 Clojure records」那个问题需要更多解释:对于一些不可变的概念,比如 uri、date、connection,通过设计良好的 records 表示它们能够让实现细节隐藏在合适的一层。这些不可变的概念倾向于拥有相关操作的固定集合,这能够通过 Protocol
建模。
我发现解释或者提出问题能够让我在编程方面更有效率。它们帮助我训练我的潜意识思维,我使用的分析性思维越多,我得到的发散性思维就越好。