你写的代码里,有多少是根本不需要的?
我 review 过一份 PR:300 行新代码 + 2 个新依赖,最终功能只需要改 3 行。这不是个例------前端可能是过度工程最严重的领域。一个弹窗用 Modal,一个列表上 Redux,一个动画装 GSAP,一个工具函数装 lodash------我们好像养成了条件反射:遇到问题,第一反应是"装什么"而不是"写不写"。
这不是能力问题,是习惯问题。我们太习惯"写代码"了,以至于忘了先问一句:这东西真的需要写吗?
每行代码都是负债。它需要被理解、被维护、被测试,还可能引入 bug。但没人教我们"什么时候不写代码"。
Ponytail 就是来补这一课的------一套帮你决定"什么时候不写代码"的决策框架。
二、Ponytail 是什么?
2.1 起源
Ponytail 是一个 GitHub 上的开源项目(DietrichGebert/ponytail),作者是 DietrichGebert。它不是一个 npm 包,不是一个 VS Code 插件,也不是一个框架------它是一套开发心智模型,一份关于"什么时候不写代码"的决策指南。
项目的副标题是:"Lazy senior dev mode"(懒散资深开发者模式)。
2.2 "懒散"不是贬义
这里的"懒"需要重新定义:
Lazy means efficient, not careless.
翻译过来就是:懒 = 高效,不是粗心。
资深开发者和新手的区别之一,就是资深开发者知道什么时候不 写代码。新手倾向于用代码量证明自己"在干活",而资深开发者用解决问题来衡量产出------代码只是手段,不是目的。
2.3 核心思想:代码是负债,不是资产
每写一行代码,你就背负了一行债务:
- 这行代码需要被理解
- 需要被维护
- 需要被测试
- 可能会引入 bug
- 增加了打包体积(前端尤其敏感)
所以 Ponytail 的核心主张是:在写任何代码之前,先问自己 7 个问题,停在第一个能回答"是"的台阶上。
2.4 核心隐喻:七层决策阶梯
第 1 层 🟢 这东西真的需要做吗? ← 停在这里最好
第 2 层 🔵 代码库里已有现成的吗?
第 3 层 🟡 标准库能搞定吗?
第 4 层 🟠 平台原生支持吗?
第 5 层 🔴 已安装的依赖能解决吗?
第 6 层 ⚪ 能一行搞定吗?
第 7 层 ⚫ 写最小可行代码 ← 最后的选择
越往上越"省力",越往下代价越大。目标不是"写更少的代码",而是"在尽可能高的台阶上停下来"。
2.5 适用场景
Ponytail 特别适合:
- AI 辅助编程:给 AI 助手装上"刹车",让它少写废话代码
- 日常业务开发:CRUD 页面、表单、列表------80% 的前端工作
- Code Review:审视别人(和自己)的 PR,有没有过度工程
- 技术选型:要不要引入新依赖?要不要封装组件?
- 重构:哪些代码可以删而不是改?
不适合的场景(Ponytail 明确说了不能懒的地方):
- 安全边界(输入验证、XSS 防护)
- 错误处理(防止数据丢失)
- 可访问性
- 真实硬件校准
三、为什么 AI 编程让 Ponytail 更重要?
以上的问题,在 AI 编程时代被放大了十倍。
以前写代码慢,一个人一天写 200 行,过度工程的破坏力是有限的。现在有了 AI,一分钟生成 200 行------你的坏习惯乘以了 60 倍。
AI 天生爱"过度工程"
这不是 AI 的错,是它的训练数据决定的。AI 模型从大量开源代码中学习,而这些代码里充斥着"最佳实践""企业级方案""通用封装"。你跟 AI 说"加一个弹窗",它给你一套完整的 Modal 组件 + 状态管理 + 动画方案。
AI 的默认行为:
- 装库而不是用原生(
<input type="date">→ flatpickr 封装) - 建抽象而不是直接写(通用表格组件而不是一个简单的
map) - 写样板而不是解决问题(action / reducer / selector 三件套)
一个真实的 AI 过度工程案例
需求:在页面上加一个日期选择器。
没有 Ponytail 的 AI:
jsx
// AI 生成了 404 行
// 安装了 flatpickr 依赖
// 写了一个封装组件 + 样式文件 + 类型定义
// 还开始讨论时区问题
有 Ponytail 的 AI:
html
<!-- ponytail: 浏览器自带了 -->
<input type="date">
这是 Ponytail 作者在 README 中展示的真实结果------404 行 → 23 行。
数据说话
Ponytail 作者在真实的 FastAPI + React 项目上做过基准测试,同一个 AI 代理、同一个任务,有和没有 Ponytail 的对比:
| 指标 | 变化 |
|---|---|
| 生成的代码量 | 减少 54%(最高可达 94%) |
| Token 消耗 | 减少 22% |
| 成本 | 降低 20% |
| 速度 | 提升 27% |
| 安全性 | 100% 保持 |
注意最后一行------Ponytail 不是"少写代码就完事",它是在保持安全的前提下减少代码。 相比之下,单纯用"YAGNI + 写一行"的 prompt 虽然也减少了 33% 的代码,但安全性降到了 95%。
Ponytail 是 AI 的刹车,不是油门
在 AI 编程时代,我们需要的不是"写得更快"的能力,而是"少写"的纪律。Ponytail 就是那个刹车。
从这一节开始,我们进入七层阶梯。你会发现它既是给自己看的,也是给 AI 助手看的------把 Ponytail 装进你的编辑器,AI 就会自动遵循这套哲学。
四、七层决策阶梯(逐层详解)
第 1 层 🟢 这东西真的需要做吗?
YAGNI --- You Ain't Gonna Need It
需求是展示一个"当前在线人数"的徽标。PM 说"未来可能要做实时推送",于是你引入了 WebSocket、写了重连逻辑、配了心跳检测:
jsx
class OnlineCountSocket {
constructor() {
this.ws = new WebSocket('wss://api.example.com/ws')
this.reconnectTimes = 0
this.heartbeatTimer = null
// ... 40 行配置
}
}
先问一句:"这个在线人数多久刷新一次?" PM 说:"其实 5 分钟刷一次就够了,不需要实时。"
jsx
const [count, setCount] = useState(0)
useEffect(() => { fetch('/api/online-count').then(setCount) }, [])
核心问题: 你真的理解需求了吗?还是下意识就开始写代码了?
第 2 层 🔵 代码库里已经有现成的吗?
新来一个页面需要发请求,你习惯性写了一个 request 封装:
js
const myRequest = async (url) => {
const res = await fetch(url)
if (!res.ok) throw new Error('Request failed')
return res.json()
}
项目 src/api/ 下已经有现成的 apiClient,封装了 token 注入、错误处理、超时配置:
js
import { apiClient } from '@/api' // 直接复用
写代码前先 grep 一下,是 Ponytail 的基本修养。 前端项目迭代快,新人多,每个人进来都习惯"自己写一套",最后 utils/ 里可能有 3 个 formatDate 函数。
第 3 层 🟡 标准库能搞定吗?
很多前端开发者不知道 ES 标准已经进化到什么程度了。遇到问题第一反应是装 lodash:
js
import { isEmpty, get, debounce } from 'lodash'
if (isEmpty(obj)) { ... }
const name = get(user, 'profile.name', '未知')
其实原生已经够用了:
js
if (Object.keys(obj).length === 0) { ... }
const name = user?.profile?.name ?? '未知' // ES2020
const unique = [...new Set(arr)] // 数组去重一行
const params = new URLSearchParams({ q: search, page }) // URL 参数
原则: 写代码之前,先想想 ECMAScript 202X 是不是已经支持了。
第 4 层 🟠 平台原生支持吗?
浏览器和 CSS 已经进化了很多,很多以前需要 JS 做的事情,现在原生就能搞定。
平滑滚动:
js
// ❌ 自己算滚动位置
const scrollToTop = () => {
const step = () => { /* 20 行 JS */ }
requestAnimationFrame(step)
}
css
/* ✅ 一行 CSS */
html { scroll-behavior: smooth; }
原生 Dialog:
jsx
// ❌ state 管理弹窗,20 行
const [open, setOpen] = useState(false)
{open && <div className="modal-overlay" onClick={() => setOpen(false)}>...</div>}
// ✅ <dialog> 元素,原生支持
<dialog ref={dialogRef}>
<button onClick={() => dialogRef.current.close()}>关闭</button>
</dialog>
原则: 遇到交互需求,先想"CSS 能不能搞定?浏览器有没有原生 API?"
第 5 层 🔴 已安装的依赖能解决吗?
项目已经有 dayjs(2KB),你又装了 moment(300KB)因为"习惯了":
js
import moment from 'moment' // 新装 300KB
js
import dayjs from 'dayjs' // 项目已有,直接用
每加一个依赖,node_modules 就膨胀一次,package.json 就多一个需要维护的条目。 加依赖之前,先问:项目里已有的能不能解决?
第 6 层 ⚪ 能一行搞定吗?
到了这一层,说明前面的 5 层都走不通了,你确实需要写代码。但再问自己:能不能用更少的行数?
js
// ❌
const visibleNames = []
for (let i = 0; i < items.length; i++) {
if (items[i].visible) visibleNames.push(items[i].name)
}
// ✅
const visibleNames = items.filter(i => i.visible).map(i => i.name)
jsx
// ❌ 嵌套 if
function Greeting({ user }) {
if (user) {
if (user.name) return <h1>你好,{user.name}</h1>
else return <h1>你好,用户</h1>
}
return null
}
// ✅ 一行
function Greeting({ user }) { return <h1>你好,{user?.name ?? '用户'}</h1> }
原则: 能不写循环就不写循环,能不写 if 就不写 if。用 filter、map、??、?.、展开运算符把代码压缩到"一眼就能看懂"的程度。
第 7 层 ⚫ 写最小可行代码
前面 6 层都走不通,现在你终于可以写代码了。但请记住:写最少的、能跑通的代码。
jsx
// ❌ 50 行:Storybook + 单元测试 + 类型体操 + 主题系统
interface TagProps { children: ReactNode; color?: 'blue' | 'red' | 'green'; size?: 'sm' | 'md' | 'lg' }
const colorMap = { blue: '#1890ff', red: '#f5222d', green: '#52c41a' }
// ... 30 行实现
jsx
// ✅ 5 行,够用就行
function Tag({ children, color = '#1890ff' }) {
return <span style={{ background: color, color: '#fff', borderRadius: 4, padding: '2px 8px', fontSize: 12 }}>
{children}
</span>
}
// ponytail: 内联样式,等主题统一时再提取 token
注意最后一行注释------这就是 Ponytail 的"诚实标记":你明确知道这是一个简化,并且写下了什么时候需要升级。
五、理解先于行动
Ponytail 有一个反直觉点:"懒"的前提是彻底理解问题。 七层阶梯的隐含前提是:你读完了任务,追踪了真实流程,理解了问题上下文。否则就不是懒,是粗心。
需求:"加一个按钮,点击导出表格数据。"
js
// ❌ 假懒:装 xlsx 库,写 80 行
import * as XLSX from 'xlsx'
const handleExport = () => {
const ws = XLSX.utils.json_to_sheet(data)
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')
XLSX.writeFile(wb, 'export.xlsx')
}
先问清楚:导出什么格式?多少数据?后端能不能直接生成?------问了一下,后端已经有导出接口:
js
// ✅ 真懒:2 行
const handleExport = () => { window.open('/api/export/table-data') }
80 行 → 2 行,不是靠"少写代码"做到的,是靠"先搞清楚问题"做到的。
六、四条核心规则
规则 1:不建没要求的抽象
不要提前为"未来可能的变化"做设计。需求只是两个按钮,别写抽象工厂模式。
jsx
// 直接写,等有 5 个变体再抽象
<button className="btn-primary">提交</button>
<button className="btn-secondary">取消</button>
规则 2:不引入新依赖
每个依赖都是长期维护成本。为一个工具函数装一个库,不如一行搞定:
js
'hello'.replace(/^./, c => c.toUpperCase()) // 不需要 lodash.capitalize
规则 3:删除优于新增
删代码比加代码更高级。发现 utils 里有个 formatDate 函数,但整个项目只有一个地方用了它,而且那个页面已经废弃了------删掉它。
规则 4:平庸优于巧妙
可读性 > 炫技。写出人人都能理解的代码,比写出"聪明"的代码更有价值。
js
const colorMap = { success: '#52c41a', error: '#f5222d', warning: '#faad14' }
const color = colorMap[status] // 一目了然,胜过一行三重三元
七、Ponytail 的边界:什么时候不能懒?
Ponytail 最诚实的地方在于:它明确告诉你哪些地方不能懒。这些地方没有"少写代码"的捷径:
- 输入验证:用户输入不可信,XSS 防护不能省
- 错误处理:API 请求必须 try/catch,展示友好错误
- 安全性 :敏感信息不放 localStorage,不用 eval,外部链接加
rel="noopener noreferrer" - 可访问性 :按钮加
aria-label,确保键盘可导航
记住:Ponytail 懒的是代码量,不是代码质量。
八、ponytail: 注释------把"偷懒"变成工程纪律
有时候确实需要走捷径,但怕后来人骂你怎么办?Ponytail 提供了一个解决方案:ponytail: 注释。
格式:// ponytail: <原因说明> + // 上限:<已知上限> + // 升级路径:<怎么做>
jsx
// ponytail: 直接全量渲染列表,数据量 < 500 条时性能足够
// 上限:数据量 > 500 条时可能出现卡顿
// 升级路径:替换为 react-window 虚拟滚动
function List({ items }) {
return items.map(item => <ListItem key={item.id} item={item} />)
}
js
// ponytail: 使用全局变量代替状态管理,避免引入 Redux
// 上限:跨组件共享状态 < 3 个时可用
// 升级路径:状态增多时改用 zustand
let currentUser = null
这样做的好处:你诚实地标记了"走了捷径",后来者知道为什么这么做,而且有明确的升级路径,不会变成技术债黑洞。
九、前端团队如何实践
- Code Review 清单:PR 审查时多问 "这行代码真的需要吗?浏览器原生能不能搞定?"
- 依赖否决:加一个依赖之前,需要至少一个人说"不"才能通过------有效阻止"随手装包"。
- 新人引导:新人入职先读项目现有工具函数和组件。"先 grep,再写代码"应该成为 onboarding 第一课。
十、结语
前端生态是技术圈最卷的领域之一。但真正的前端功力,不是写得多,而是删得多。
Ponytail 教我们的,不是"不想写代码",而是**"想清楚再写代码"**。
下次写代码之前,走一遍这七层阶梯:
- 🟢 这东西真的需要做吗?
- 🔵 代码库里已经有现成的吗?
- 🟡 标准库能搞定吗?
- 🟠 平台原生支持吗?
- 🔴 已安装的依赖能解决吗?
- ⚪ 能一行搞定吗?
- ⚫ 写最小可行代码
最好的代码,是你没写的那行。
附:如何安装 Ponytail?
Ponytail 已支持多个 AI 编程工具,挑一个你用的:
Claude Code
bash
/plugin marketplace add DietrichGebert/ponytail
/plugin install ponytail@ponytail
(两条命令分两次发送)
Cursor / Windsurf / Cline 从 GitHub 仓库的 .cursor/rules/ 或 .windsurf/rules/ 或 .clinerules/ 复制规则文件到你的项目对应目录。
GitHub Copilot CLI
bash
copilot plugin marketplace add DietrichGebert/ponytail
copilot plugin install ponytail@ponytail
VS Code(Codex 扩展) 把 AGENTS.md 复制到 ~/.codex/AGENTS.md 即可全局生效。
安装后,/ponytail 命令可切换强度等级(lite / full / ultra / off),/ponytail-review 可审查当前代码是否过度工程。
互动话题: 你在项目中见过最过度的前端工程化是什么?欢迎在评论区聊聊。