How to control the metacognition process of programming?

https://lambdaisland.com/blog/2022-02-17-the-fg-command

有一个名言:编程是思考,而不是打字。在做过足够长的编程工作后,我有时觉得自己更多情况下是在打字。不论人们在思考还是打字,他们使用自己的大脑,因此我相信单词“typing”是一个隐喻:打字指我们进行的无意识活动,通过肌肉记忆进行,不需要我们刻意关注。像我们这样经验丰富的人,使用了相当多的肌肉记忆。那么问题来了,我们是否也进行了同等程度的思考训练呢?

我们每个人,在日常生活中做出很多决定:有些决定是像买汽车这样的事情,有些决定是像给一个函数命名。进化让我们把某些决定交给潜意识进行,可以称之为大脑的背景过程。因此,我们可以专注于更重要的事情上。问题是:如果潜意识并没有很好地完成这部分决定,怎么办?大多数情况下,这不是严重的问题,把这个问题放到面前直接解决就可以。/例如,当你按下按键,来评价表单,但却是另一个表单。你察觉到了,然后你使用自己的前瞻性思维(foreground thinking)决定下一步应该怎么做。然而,有些时刻,在你开始这项工作以前,你就已经知道潜意识并不能很好地完成交付的工作,你能做什么来阻止潜意识占据控制地位呢?我们是否也有类似于 Linux 命令 fg 这样的功能,能够将后台进程带到前台呢?/

坦白这件事是很难为情的,虽然我已经从事编程长达十年,有些时候,当我遇到一个 bug,我大脑的第一反应是进入恐惧模式长达数小时,然后才回到分析模式。为什么?我的理解是:当处于作为初级开发者、发展技能的那个阶段,我在恐惧模式中解决问题:进行疯狂的 Gogole 搜索、试错和其他解决办法。年复一年的经历让我习惯于在恐惧模式中调试程序。然而, 现在我想改变这一点。有两种方式能够帮助我控制自己的潜意识:

  1. 将代码的执行过程解释给别人。(Rubber Duck Method)
  2. 询问自己事先准备好的问题。(Drucker Method)

远程工作教会我橡皮鸭调试法

当和 Gaiwan 团队工作时,我们完全是远程工作。有时,当我想和某人讨论时,我需要等待。有好几次,当我遇到一个 bug,我想寻求帮助,我写下来关于代码的全部细节,运行环境,以及我是如何尝试解决 bug 的。在我把 bug 报告发到 Gaiwan 的交流区后,过了 15 分钟,神奇的事情出现了:我突然有了解决问题的灵感。我快速地解决问题,并修改我已经发送的消息。橡皮鸭法真的起作用了!

实际上,我认为这种通过解释思考的方式有不同的名字:橡皮鸭(Rubber Duck)、文学编程(Literate programming)、费曼学习法(Feynman)等等。它们全都是类似的事情。

正确的问题就像是在你大脑的 fg 命令

现在,每当我遇到一个 bug,我问自己 3 个问题:

  1. 我是否使用了科学的方法追踪这个 bug?
  2. 我是否又正确的系统视角来确定问题的范围?
  3. 我是否拥有必要的测试(telemetry)工具?

当写一个函数、模块或者准备部署时,我存在一些类似的问题:打字太多,思考太少。

这里是一些问题,我做给自己使用的,依然处于 alpha 版本:

关于写函数的问题

  1. 我应该让命令和查询分离吗?
  2. 我是否为错误写出有条理的设计,像 try/catch 和日志?
  3. 我是否为错误写出预防性设计,像 pre 条件或 asset?
  4. 函数名应该表达目的吗?
  5. 我在函数旁添加了一些合适的文档字符串了吗?

关于写模块的问题

  1. 我是否应该明确地指定模块的 API,使得 API 明显不同于内部函数?
  2. 所有不必要的死代码是否都已经被移除?
  3. 我是否设计或使用合适的 Clojure records 来为域问题的一些不可变概念建模?

关于集成和部署的问题

  1. 我是否为域函数添加合适的测试?
  2. 我是否在安装脚本上设计了合适的反馈信息?
  3. 一些手动安装步骤能否被自动脚本替代?

「使用 Clojure records」那个问题需要更多解释:对于一些不可变的概念,比如 uri、date、connection,通过设计良好的 records 表示它们能够让实现细节隐藏在合适的一层。这些不可变的概念倾向于拥有相关操作的固定集合,这能够通过 Protocol 建模。

我发现解释或者提出问题能够让我在编程方面更有效率。它们帮助我训练我的潜意识思维,我使用的分析性思维越多,我得到的发散性思维就越好。

Layout of comment panels