🎧 收听播客版本
⏱️ 时长约2分1秒 | 📥 也可以在通勤、运动时收听
开头:老实交代
昨天花时间优化了一个数独游戏,JS 文件 600 行左右,加上 CSS 和 HTML,总共也就 900 行。
就这么个小项目,我还是踩坑了。
第 369 行少了个 //,游戏直接用不了。用户反馈说”数字输入不了”…
问题:第 369 行的错误
项目背景
先说说这个”大项目”的真实规模:
src/js/app.js - ~600 行(确实只有这个文件长一点)
src/css/style.css - ~200 行
index.html - ~100 行
总计:~900 行
技术栈简单到不能再简单:
- 原生 JavaScript(没有框架)
- Vite 作为构建工具
- 纯前端,无后端
不是什么”大型工程” 😅
发现问题
用户反馈:“数字输入不了”
我第一反应:不可能啊,键盘输入逻辑我都写好了啊…
打开浏览器控制台,看到红色报错:
Uncaught SyntaxError: Unexpected token
定位问题:
跳到第 369 行,看到这段代码:
// 错误的代码
function validateInput(cell, number) {
// 检查行
const row = Math.floor(cell / 9);
for (let i = 0; i < 9; i++) {
if (board[row * 9 + i] === number) {
return false;
}
}
// 忘记加注释符号了
检查列
for (let i = 0; i < 9; i++) {
if (board[col * 9 + i] === number) {
return false;
}
}
return true;
}
注意到了吗?“检查列” 这三个字前面没有 //!
后果:
- JavaScript 解释器读到”检查列”这三个中文字符
- 以为是代码,解析失败
- 整个文件报错,键盘输入事件根本没绑定
- 游戏完全无法使用
修复很简单:
// 正确的代码
function validateInput(cell, number) {
// 检查行
const row = Math.floor(cell / 9);
for (let i = 0; i < 9; i++) {
if (board[row * 9 + i] === number) {
return false;
}
}
// 检查列
for (let i = 0; i < 9; i++) {
if (board[col * 9 + i] === number) {
return false;
}
}
return true;
}
加个 // 就好了。
但问题是:我怎么没早点发现?
反思:为什么没早点发现?
1. “才几百行,肯定没问题”的心态
这是经典翻车前奏。
项目规模小,我有点飘了,觉得:
- “代码是我自己写的,肯定没问题”
- “就改了几行,不用测了”
- “这种低级错误不可能犯”
结果:啪啪打脸 😅
2. 没有自动化验证
整个项目没有任何自动化验证:
- ❌ 没有语法检查
- ❌ 没有代码风格检查
- ❌ 没有自动化测试
- ❌ 每次部署前没有构建验证
完全靠:
- 手动打开浏览器
- 手动测试几个功能
- 手动检查控制台
问题是:人是会忘事的。
改完代码,顺手 commit,直接 push,根本没想起来要验证。
3. 相信直觉,不相信工具
“我的代码肯定没问题” —— 这话说得太早了。
我应该相信的是:
- ✅ ESLint 的语法检查
- ✅ Prettier 的格式检查
- ✅ Vite 的构建验证
而不是:
- ❌ “我觉得没问题”
- ❌ “刚才运行还好好的”
解决方案:建立验证流程
ESLint 配置
安装依赖:
npm install --save-dev eslint
创建 .eslintrc.cjs:
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: ['eslint:recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
// 自定义规则
'no-unused-vars': 'warn',
'no-console': 'off',
},
};
在 package.json 中添加脚本:
{
"scripts": {
"lint": "eslint src/js/**/*.js",
"lint:fix": "eslint src/js/**/*.js --fix",
"build": "vite build",
"dev": "vite"
}
}
Git 提交前验证
使用 husky + lint-staged 在提交前自动验证:
npm install --save-dev husky lint-staged
初始化 husky:
npx husky install
创建 .husky/pre-commit:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint
npm run build
设置执行权限:
chmod +x .husky/pre-commit
自动修复
配置 .lintstagedrc.js:
module.exports = {
'src/js/**/*.js': [
'eslint --fix',
'git add',
],
};
效果:
- 提交前自动检查代码
- 自动修复可修复的问题
- 如果有无法修复的问题,阻止提交
工作流程
之前(翻车版)
写代码 -> 觉得没问题 -> commit -> push -> 用户反馈 -> 查问题 -> 修复 -> 推送
问题:
- 依赖”我觉得”和”手感”
- 没有自动化验证
- 问题发生在用户端,体验差
现在(验证版)
写代码 -> lint 检查 -> 修复问题 -> build 验证 -> commit -> push
↑ ↑
ESLint 提示语法错误 husky 阻止提交
优点:
- 自动发现语法错误
- 在本地就解决问题
- 用户端不会再遇到低级错误
学到的教训
1. 小项目更需要验证
项目规模小,不是理由,反而是更需要验证的原因:
| 原因 | 说明 |
|---|---|
| 改得快 | 今天写完,明天就忘了,没有验证容易出问题 |
| 容易忽视 | ”就几百行,肯定没问题” → 经典翻车前奏 |
| 积累经验 | 在小项目上试错,大项目才能稳 |
小项目 ≠ 可靠性低 小项目 + 验证 = 可靠性提升
2. 工具验证 > 相信自己
| 方法 | 可靠性 |
|---|---|
| ”我觉得没问题” | ❌ 低 |
| 手动测试 | ⚠️ 中 |
| ESLint + 自动化验证 | ✅ 高 |
为什么工具更可靠?
- 工具不会”忘记”
- 工具不会”大意”
- 工具不会”觉得”
- 工具只按规则办事
3. 建立流程,不要依赖自律
自律是有限的,流程是可靠的。
创建流程:
- ✅ 提交前自动 lint
- ✅ 构建验证
- ✅ CI/CD 自动测试
执行流程:
- 不需要每次都提醒自己
- 工具自动拦截问题
- 形成肌肉记忆
4. 错误不分大小
第 369 行的错误很小:
- 就三个中文字符
- 加个
//就好了 - 看起来是小事
但后果很严重:
- 游戏完全无法使用
- 用户体验极差
- 信誉受损
结论:
- 小错误 ≠ 小影响
- 所有的错误都应该被工具捕获
AI 时代的代码验证
AI 生成代码,更需要验证
现在很多项目用 AI 生成代码:
- Copilot
- ChatGPT
- Claude
问题:
- AI 也会犯低级错误
- AI 不了解项目上下文
- AI 不能保证 100% 正确
解决方案:
- 用 AI 写代码
- 用工具验证代码
- 用人工审核结果
AI + 工具 + 人工 = 质量保障
我的实践
用 OpenClaw 帮写代码(比如这个数独游戏),但我一定会:
-
配置 ESLint
- 语法错误立即提示
- 代码风格统一
-
运行构建
- 打包前验证
- 发现隐性问题
-
手动测试
- 功能验证
- 边界情况
AI 帮你写得快,工具帮你写得好
总结
什么是代码质量?
代码质量不是:
- ❌ 代码写得有多花哨
- ❌ 设计模式用得多高级
- ❌ 项目规模有多大
代码质量是:
- ✅ 没有语法错误
- ✅ 逻辑正确可靠
- ✅ 有验证流程
- ✅ 能持续维护
小项目的质量保障
即使只是个 900 行的数独游戏:
- 配置 ESLint —— 捕获语法错误
- 建立 Git Hooks —— 提交前验证
- 运行构建 —— 打包前检查
- 手动测试 —— 功能验证
代码质量不分项目大小
- 小项目积累经验
- 大项目才能稳
- 每个项目都应该有验证流程
小项目也需要大保障
后续计划
-
完善验证流程
- 添加单元测试
- 集成到 CI/CD
-
改进开发体验
- 编辑器集成 ESLint
- 自动格式化代码
-
文档化流程
- 写开发文档
- 记录最佳实践
最后
好的博客不是”我多牛”,而是”我多真实”。
读者想看:
- ✅ 真实错误 + 真解决 ← 这个有
- ✅ 立即可用的代码 ← 这个有
- ❌ “我做了个超牛的项目,踩了个超难的坑” ← 这个没有
代码质量的关键不是技术多牛,而是有没有建立验证流程。
工具验证 > 相信自己
小项目也需要大保障
真实 > 装模作样