写给编程小白看的 Vibe Debugging 注意事项(2)

前情提要,在本系列的第一篇里,我着重提到「组件化思维」以及软件工程的一些基本概念,对于正在尝试 Vibe Coding 的编程小白来说,是必须掌握的思考方式与技能。

这种思维方式与技能,在掌握了之后,当程序运行时遇到 bug,就不会直接把报错复制粘贴过去,而是可以迅速猜到问题产生的地方,从而提高与大模型的沟通效率,也能够节约 token。

良好的结构,是软件开发中立于不败之地的重要前提。

那么问题来了,什么是良好的软件结构?良好的软件结构有什么特点?如何了解自己手上开发的软件是否具备良好的结构?如何学习,让自己具备这种良好软件结构的嗅觉?

数据流清晰,符合单一原则

程序它有两个口,一个是入口,一个是出口。入口输入的是数据,出口输出的也是数据。程序的目的,就是为了处理数据,不管是处理 1+1 = 2,还是把字体改为加粗,都是对输入的数据进行处理,然后再输出。

所以,良好的软件结构,在处理数据的时候,会非常清晰,这中间数据从组件 A 到组件 B,再到组件 C,这是一条清晰的路线,不会中间打岔。

每个组件的职责也很清晰,从哪里获得数据,如何处理,输出什么结果——不过一个优秀的组件通常职责是单一的,它只解决一种,甚至是一个问题,绝不大包大揽。

当然对于编程小白而言,直接观察数据的流动是有点难的,因为极大的可能,是连数据的类型都不懂。

我一开始也这样,好像「鬼打墙」一样,同一个 bug 反复出现。当时我完全不懂数据类型,但是我用另外一个方法,让我掌握了数据的流动,那就是观察函数之间的相互调用。

调用这个词看上去很专业,其实理解并不难,就是你复制了一个函数名,然后,在 VSCode 里面全局搜索一遍,看看这个函数名是否会出现其它的函数里。如果出现了,那么证明了这个函数被其它函数调用。

这是一个最简单的方法,让你在完全不具备编程概念,去了解程序里数据流动的一个方法。另外就是,揣摩函数名的意思,LLM 给函数命名一般都很直白,尽管有时候显得啰嗦。

总之,不管是不是编程小白,都不可避免要阅读代码。如果想提高自己的问题解决能力,那就养成习惯,不要一出现问题就马上问 LLM,而是应该先阅读问题产生的代码,看看问题发生的地方。

以终为始的思考方式

我不知道你身边是否有那种朋友,他们思维逻辑很清晰,而且总能快人一步发现问题,解决问题的能力也非常强。我身边挺多这种朋友的,经过观察,他们通常都喜欢用「以终为始」的方式去思考问题。

「以终为始」就是,以最终目标出发点,一步步倒推,一直倒推到开始的第一步为止。这种思考通常比较消耗精力,但是只要习惯了,这种思考上的消耗就没什么大不了的。

通常,在开发一个软件之前,我们在一步步倒推的时候,就能够摸出一个清晰的数据路线。我的习惯是,一边倒推一边用执笔写写画画,帮助自己思考。这时候,我会在草稿纸上列出这一次开发想达到的目标,然后我会往下列一个个要达到这个步骤。

实际上,这些步骤,就是我在第一篇里提到的「组件」。第一个步骤要完成什么,然后才能让第二个步骤处理什么,以此类推,很快你得到了一张路线图,也同时得到了一个软件结构图。

而在这个过程中, 如果你发现哪里步骤不清晰,哪里步骤有问题,那都是不好的结构,只要你逐步修正,一个良好的结构就会自然浮出水面。

从任务执行的角度来看待程序,它就是一系列经过设计的,达到目的之前需要完成的步骤。

多参考优秀项目

这里涉及一个人的「品位」。大家也知道,「品味」是一种微妙的感觉,你说不出是为什么,但就是「真 TMD 的好!」

如果想让自己在 Vibe Coding 的时候,培养对软件工程的品味,不必每一次都和神出鬼没的 bug 玩捉迷藏,我们可以直接参考优秀的项目,看看它们是怎么做的。

我说的参考,不是打开一个 Github 仓库,然后看看里面的分别有什么文件就算完事了,而是要深入到代码层。就好像我刚才说的, 直接全局检索函数名,然后看看它都出现在哪里。

而且现在有 LLM 以及 DeepWiki 还有 zread 等工具,都可以直接解读一个 Github 仓库里的代码之间的关系和结构。

不过我还推荐我的做法,直接将一个仓库 Clone 到硬盘,然后启动 Gemini CLI,输入一句:这个项目是干什么的?它的结构是什么?

然后,你就等着这位赛博菩萨吭哧吭哧地帮你整理出一个优秀的文档来,这个项目是干嘛的, 有什么组件,每个组件是干嘛的,罗列得很清楚。然后按照这份地图,打开对应的文件,参观里面的代码,看看别人是怎么写的。

多读代码,多读别人的,多读自己的,然后对比一下。书读多了,其义自现。