这门课从 Module 1 开始,每个单元的动手练习其实都在练同一件事:用 AI 生成代码,自己审查、抓错。这个模块把这套一路在练的东西,系统化成一套完整方法论。

Since Module 1, every hands-on exercise in this course has been practicing the same thing: generate code with AI, then review it and catch the errors yourself. This module turns what you have been practicing all along into one complete methodology.

5.1 什么是 Vibe Coding5.1 What Vibe Coding is

Vibe coding 这个词,是 Andrej Karpathy 在 2025 年 2 月提出的。他原本的说法更极端:完全不检查 AI 生成的每一处改动,接受 AI 的一切改动,专注在"感觉对不对",甚至可以不用管代码本身长什么样。他自己也提到,这种做法比较适合心血来潮的周末小项目,不是给正式上线、给真实使用者用的产品用的。

这个词后来被广泛使用,意思也渐渐跟原本不太一样了。现在大部分人说的 vibe coding,泛指"用自然语言描述需求、让 AI 生成代码"这整套工作方式,不一定包含"完全不审查"这层意思。

这门课对 vibe coding 的立场很明确:借助 AI 大幅提升产出速度,但审查这一关不能省。周末的玩具项目,忘记代码存在或许无所谓;东西一旦要给真实使用者用,看得懂、审得出问题,就是整个工作流程里最重要的一环。这也是这门课从头到尾一直在练的东西。

The term vibe coding was coined by Andrej Karpathy in February 2025. His original take was more extreme: do not check every change AI makes at all, accept everything it does, focus on whether it "feels right", and you can even ignore what the code itself looks like. He also noted that this suits a spur-of-the-moment weekend project, not a product that goes live for real users.

The term later spread widely and its meaning drifted from the original. When most people say vibe coding now, they mean the whole way of working where you describe a requirement in plain language and let AI generate the code, without necessarily meaning "no review at all".

This course takes a clear stance on vibe coding: use AI to greatly speed up output, but do not skip the review. For a weekend toy project, forgetting the code exists may not matter; the moment something goes to real users, being able to read it and catch its problems is the most important part of the whole workflow. That is what this course has been practicing from start to finish.

5.2 完整的工作流程:五个步骤5.2 The full workflow: five steps

1. 拆解需求(Module 1.3 提过)

把模糊的想法拆成具体、AI 能执行的小步骤。步骤拆得越细,AI 出错的空间越小,出问题时你也越容易定位是哪一步出错。

2. 下清楚的指令

好的指令要包含:具体要做什么、输入输出长什么样、边界情况要怎么处理。5.3 会具体拆一次。

3. 审查产出(贯穿整门课的动手练习)

结构对不对、逻辑对不对、资安有没有漏洞。5.4 会把整门课学过的检查点汇总成一份清单。

4. 测试与验证

光用眼睛看代码不够,AI 生成的代码看起来合理,不代表跑起来是对的。找几个具体案例,包含正常情况和边界情况(比如清单是空的、数字是负的),实际跑一次。

5. 除错与迭代

发现问题时,不要把整段代码重新丢给 AI 说"这个不能用",而是具体描述"哪里不对、预期应该是什么"。范围缩得越小,AI 越容易一次修对,你也越容易确认它真的修对了。

Vibe Coding 完整工作流程
Vibe Coding 完整工作流程

修完一轮,回到步骤 1,处理下一个功能。上面那张图画的就是这个循环。

1. Break down the requirement (mentioned in Module 1.3)

Break a fuzzy idea into concrete small steps AI can carry out. The finer the steps, the less room AI has to go wrong, and the easier it is to pinpoint which step failed.

2. Give clear instructions

A good instruction includes: what exactly to do, what the input and output look like, and how to handle edge cases. 5.3 unpacks one.

3. Review the output (the hands-on exercises throughout this course)

Is the structure right, is the logic right, are there security holes? 5.4 gathers the checkpoints from across the course into one list.

4. Test and verify

Reading the code with your eyes is not enough. AI-generated code looking reasonable does not mean it runs correctly. Take a few concrete cases, including normal ones and edge cases (like an empty list or a negative number), and actually run it.

5. Debug and iterate

When you find a problem, do not throw the whole block back at AI saying "this does not work". Describe specifically what is wrong and what you expected. The smaller the range, the more likely AI fixes it in one go, and the easier it is for you to confirm it really did.

The full Vibe Coding workflow
The full Vibe Coding workflow

After one round of fixes, return to step 1 for the next feature. The diagram above shows this loop.

5.3 怎么写出 AI 能确实执行的需求5.3 How to write a requirement AI can actually carry out

拿 3.E 的建立订单功能对比一次:

不好的需求:"帮我做一个建立订单的功能"

好的需求:"做一个建立订单的 API,POST /api/orders,需要登入才能用。接收前端送来的商品清单,后端自己重新计算总价,不要相信前端算好的数字。写入数据库时用参数化查询。成功回传 200 和订单资讯,商品清单是空的话回 400。"

差别在于:好的需求指定了路径和方法(对应 3.B)、指定了资安要求(对应 3.A、3.C)、指定了权限要求(对应 3.D)、也指定了边界情况怎么处理。AI 拿到这样的需求,出错的空间小很多,你事后审查也有明确的标准可以对照。

Compare using the create-order feature from 3.E:

A weak requirement: "make me a create-order feature".

A good requirement: "Build a create-order API, POST /api/orders, that requires login. Take the item list from the frontend, recalculate the total on the backend, and do not trust the frontend's number. Use a parameterized query for the database write. Return 200 with the order info on success, and 400 if the item list is empty."

The difference: the good requirement specifies the path and method (3.B), the security requirements (3.A, 3.C), the permission requirement (3.D), and how to handle edge cases. With a requirement like this, AI has far less room to go wrong, and you have a clear standard to check the result against.

5.4 系统化审查清单5.4 A systematic review checklist

把这门课到目前为止学过的检查点,汇总成一份完整清单:

前端

  • 标签有没有语义化,还是整篇都是 div(2.A)
  • 有没有漏 box-sizing: border-box,选择器优先级有没有搞错(2.B)
  • 变量型态对不对,字串和数字有没有搞混(2.C)
  • class、id、data 属性名字有没有跨档案对齐(2.D)

后端

  • HTTP 方法和状态码用得对不对,网址符不符合 REST 风格(3.A、3.B)
  • 有没有用 GET 执行会改变资料的动作(3.B)
  • 数据库查询有没有用参数化,而不是字串拼接(3.C)
  • 密码有没有哈希过再存,需要登入的路由有没有挂认证中间件(3.D、3.E)
  • 后端有没有自己重新计算关键数字、重新验证关键权限,而不是照单全收前端送来的(3.A、3.E)

工具与设定

  • 密钥有没有写死在代码里,还是放进环境变量(1.5、4.3)
  • .env 有没有被排除在 .gitignore 之外(4.3)

这份清单不用每次都从头到尾照着念一遍,但审查任何一段 AI 生成的代码时,至少先问自己:这段属于清单里的哪几类,对应的坑有没有踩到。

Gathering the checkpoints from across the course into one complete list:

Frontend

  • Are tags semantic, or is it all divs (2.A)?
  • Is box-sizing: border-box missing, is selector priority mixed up (2.B)?
  • Are variable types right, are strings and numbers confused (2.C)?
  • Do class, id and data attribute names line up across files (2.D)?

Backend

  • Are HTTP methods and status codes right, does the URL follow REST style (3.A, 3.B)?
  • Is GET ever used for an action that changes data (3.B)?
  • Do database queries use parameters instead of string joining (3.C)?
  • Are passwords hashed before storing, do login-only routes have auth middleware (3.D, 3.E)?
  • Does the backend recalculate the key numbers and re-verify the key permissions itself, instead of taking the frontend's at face value (3.A, 3.E)?

Tools and config

  • Are keys hard-coded, or put in environment variables (1.5, 4.3)?
  • Is .env excluded by .gitignore (4.3)?

You do not need to read this list top to bottom every time, but when you review any AI-generated code, at least ask first: which categories on the list does this fall into, and has it hit the matching traps.

完整走一次审查流程Walk through a full review

你跟 AI 说:"帮我做一个功能,VIP 会员结帐时自动打 9 折。"

AI 给你:

You tell AI: "Make a feature that automatically applies a 10% discount for VIP members at checkout."

AI gives you:

app.post('/api/checkout', function(req, res) {
  const items = req.body.items;
  const isVip = req.body.isVip;
  let total = req.body.total;
  if (isVip) {
    total = total * 0.9;
  }
  const query = "INSERT INTO orders (total) VALUES (" + total + ")";
  db.execute(query);
  res.json({ message: '结帐成功' });
});

这段代码藏了四个问题,分别对应这门课前面不同单元讲过的内容。给自己 5 分钟,对照 5.4 那份清单,把四个都找出来,再往下看答案。

This code hides four problems, each matching a different earlier unit. Give yourself 5 minutes, and against the checklist in 5.4, find all four, then read the answer.

看答案

答案

  1. const isVip = req.body.isVip:直接信任前端送来的 VIP 身份标记。使用者只要打开开发者工具,把送出去的 isVip 改成 true,不是 VIP 也能拿到 9 折。跟信任前端送来的总价是同一类问题(对应 3.A),只是这次信任的是身份标记,不是数字。真正的 VIP 身份,要由后端自己去数据库查这个使用者的会员等级,不能相信前端说了算。
  2. let total = req.body.total:一样直接信任前端送来的总价,没有自己根据商品清单重新计算(对应 3.A)。
  3. 字串拼接组 SQL"INSERT INTO orders (total) VALUES (" + total + ")",有 SQL 注入风险(对应 3.C)。
  4. 没有 requireAuth:这个路由没有挂认证中间件,理论上结帐这种动作应该要先确认使用者登入了、知道是谁在下单(对应 3.D、3.E)。

四个问题改好之后,这段代码应该长这样:

Answer:

  1. const isVip = req.body.isVip: it trusts the VIP flag the frontend sends. The user only has to open developer tools and change the isVip being sent to true, and a non-VIP gets the 10% off too. This is the same kind of problem as trusting the frontend's total (3.A), except here it is an identity flag, not a number. The real VIP status must be looked up by the backend from the user's membership level in the database, not decided by the frontend.
  2. let total = req.body.total: again it trusts the frontend's total, without recalculating from the item list (3.A).
  3. String-joined SQL: "INSERT INTO orders (total) VALUES (" + total + ")" has a SQL injection risk (3.C).
  4. No requireAuth: this route has no auth middleware, yet a checkout action should first confirm the user is logged in and know who is placing the order (3.D, 3.E).

With all four fixed, the code should look like this:

app.post('/api/checkout', requireAuth, async function(req, res) {
  const items = req.body.items;
  const user = await db.execute("SELECT * FROM users WHERE id = ?", [req.user.id]);
  let total = calculateTotal(items);
  if (user.isVip) {
    total = total * 0.9;
  }
  await db.execute(
    "INSERT INTO orders (user_id, total) VALUES (?, ?)",
    [req.user.id, total]
  );
  res.json({ message: '结帐成功', total: total });
});

如果这四个问题你都在往下看答案之前找出来了,代表这门课从 Module 1 到现在练的东西,已经变成你审查代码时的直觉反应,不用刻意提醒自己去想。

If you found all four before reading the answer, it means what you have practiced from Module 1 until now has become your instinct when reviewing code, without needing to remind yourself.

延伸资源

Module 6 讲产品化:从玩具到上线,讲一个能跑的 demo,跟一个真正能给别人用的产品之间,还差哪几步。

Module 6 covers productionizing: from toy to launch, the steps still between a demo that runs and a product real people can use.