# 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` 建模。

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