我用 Claude Code 的 Superpowers 技能链写了个服务,部署前差点把服务器搞炸

这不是一篇 AI 吹水文。我会把翻车的地方、设计文档容易制造的错觉、以及我这次踩到的安全盲区都摊开讲。

先说背景。上周我用 Claude Code 的 Superpowers 插件体系,从零撸了一个图片占位服务(类似 placehold.co),整个流程走下来------头脑风暴、设计文档、实现计划、子代理逐任务执行------花了几个小时,最终 50 个测试用例全绿,6 种图片格式全部支持。

听起来很完美对吧?

但当我真正把它跑起来,准备部署的时候,发现了一个让我后背发凉的问题: 设计文档里写的并发限制是 10,但它没告诉你 10 个 4000×4000 的 AVIF 请求同时打过来,内存峰值可能接近 1GB。 而我的 Dockerfile 里赫然写着 --max-old-space-size=512。这不是严格意义上的容器内存上限,它限制的是 V8 old space,但对一个大量使用 Sharp/libvips 原生内存的服务来说,已经足够提醒我:这里需要压测,而不是凭感觉上线。

这就是 AI 驱动开发最容易被忽视的问题: 它会在设计文档里把安全风险和资源限制列得明明白白,给你一种"都考虑到了"的安全感。但列出来和解决了,中间隔着一条鸿沟。


Superpowers 是什么

先交代一下背景。Claude Code 的 Superpowers 是一套插件化的开发工作流,核心流程大致是四步:

头脑风暴 → 写设计文档 → 写实现计划 → 子代理逐任务执行

每一步都有对应的 skill。brainstorming 负责梳理需求,writing-plans 把设计文档拆成带完整代码的任务列表,subagent-driven-development 给每个任务派一个独立子代理去实现,完成后自动做两轮 review。

听起来像流水线,用起来也确实像。但真正有意思的是每个环节暴露出来的问题。


头脑风暴:AI 问问题的深度取决于你问问题的深度

brainstorming skill 有个规则:一次只问一个问题。一开始我觉得这节奏太慢了,但走完才发现,这种"逼迫式"的节奏让你没法跳过模糊地带。

第一个反转:技术选型

我最开始说用 Canvas 绘制,AI 推荐了 @napi-rs/canvas。看起来没什么问题,Canvas 绑 Skia,主流方案。

但后来聊到格式支持的时候,我问了一句:"AVIF 支持吗?"AI 才说 @napi-rs/canvas 底层是 Skia,Skia 的 AVIF 编码支持不完整。而我要求同时支持 AVIF 和 GIF 动画,方案才转向 Sharp 这条路:静态格式用 Sharp,GIF 再配合专门的编码器。

如果我当时没追问格式兼容性,AI 不会主动告诉我它的推荐有盲区。 它会很自信地给你一个方案,直到你问到它兜不住的地方。

最终方案改成了 Sharp + SVG 混合架构:所有图片先用字符串拼出 SVG(零依赖),PNG/JPEG/WebP/AVIF 由 Sharp 转换;GIF 则先用 Sharp 渲染出原始帧,再交给 gif-encoder-2 编码。SVG 格式直接返回字符串,不走 Sharp。

这个架构本身很漂亮------SVG 是唯一数据源,Sharp 负责渲染和格式转换,GIF 编码器只处理动画输出。但漂亮归漂亮,后面的内存问题就埋在这里。

第二个反转:竞品调研

我问"尺寸放路径还是放 query 参数"的时候,AI 搜索了所有竞品:placehold.co、dummyimage.com、placeholders.dev------全部用路径放尺寸,没有例外。而且 dummyimage.com 的文档还特意提到了 Content-Length 响应头对旧版客户端的重要性。

这种调研如果让我自己做,大概率就是拍脑袋决定。AI 在这件事上的价值不是"帮你做决定",而是"告诉你整个行业怎么做的,你不用猜"。

第三个反转:安全分析主动提了,但力度不够

AI 在头脑风暴阶段就主动做了威胁分析:XSS 注入、资源耗尽、参数欺骗、路径遍历,列了一个矩阵。每一步都有对应的缓解措施。当时我觉得:嗯,安全这块考虑得挺全。

事后证明,这个"挺全"的感觉是最大的陷阱。 具体后面说。


设计文档:写得漂亮和跑得稳是两码事

头脑风暴结束后,AI 把所有讨论整理成了一份设计文档。涵盖 API 设计、项目架构、安全设计、内存安全、环境变量------每个章节看起来都很专业。

但有几个细节,代码跑起来之后才暴露:

1. 字体策略:文档和实现完全是两个东西

设计文档里关于字体是这么写的:M+ Fonts 嵌入 SVG 的 base64 @font-face。理由也充分------开源字体,支持拉丁 + CJK,base64 嵌入保证跨环境一致性。

但实际实现中,这一步完全没做。最终用的是 font-family="sans-serif",依赖系统字体。

为什么?一个支持 CJK 的 M+ Fonts 字体文件 2MB,base64 编码后膨胀到 2.7MB。如果每次请求都把 2.7MB 的字体嵌进 SVG 再喂给 Sharp,那响应体积和内存开销完全是另一个量级。设计文档没有评估这个成本,只是在理想条件下给了一个方案。

这不是 AI 的错------是没有人追问"这个方案的实际代价是什么"。

2. 并发限制:文档说信号量,代码是全局计数器

内存安全章节写得很详细:像素上限 16MP、文本硬限制 200 字符、并发限制、请求超时、Node.js 的 --max-old-space-size、PM2/K8s 基于内存的重启策略。看起来面面俱到。

但并发限制的实际实现是这样的:

ts 复制代码
let activeCount = 0;

export function concurrencyLimiter() {

return async (c: Context, next: Next) => {

if (activeCount >= config.maxConcurrency) {

// 返回 503 错误图片

}

activeCount++;

try { await next(); } finally { activeCount--; }

};

}

一个模块级变量 activeCount。它能做单进程内的粗粒度并发拒绝,但没有队列,不会按请求成本加权,进程重启就归零,集群部署下每个进程独立计数------离真正的全局限流还差很远。

设计文档写了"信号量",代码实现是计数器。这两个东西在并发控制语义上不是一回事。

3. GIF 动画:设计文档没评估极端尺寸

GIF 的方案是生成两帧(正常帧 + 微亮覆盖层),制造闪烁效果。技术上可行,但仔细算一下内存:

  • 4000×4000 的 RGBA 原始数据一帧是 64MB

  • 两帧是 128MB

  • gif-encoder-2 的 neuquant 颜色量化还要额外分配内存

  • 如果并发 10 个 GIF 请求,光原始帧数据就超过 1.28GB

再加上 Sharp 的解码缓冲区、AVIF 编码的峰值内存,如果把服务放进内存较紧的容器里,OOM 风险非常高。但设计文档里的并发限制给了一个固定的 10,没有根据图片尺寸做动态调整。

这说明 AI 做的安全分析是"静态"的------它知道应该限制并发,但没有把并发阈值和请求成本关联起来。10 个 100×100 的 PNG 和 10 个 4000×4000 的 GIF,对内存的压力完全不是一个数量级。


实现计划:细致到让人窒息,但也带来了新问题

writing-plans skill 生成的计划有 15 个任务,每个任务包含完整的实现代码------不是伪代码,是复制粘贴就能跑的代码。计划遵循 TDD:先写测试确认失败,再写实现确认通过,最后 git commit。

任务粒度极细。举个例子,Task 4(参数校验器)的步骤:

  1. 写 18 个测试用例

  2. 跑测试,确认全部失败

  3. 写实现代码

  4. 跑测试,确认全部通过

  5. git commit

50 个测试用例覆盖了参数校验、SVG 生成、图片转换、缓存头、全链路集成。如果纯手写,我大概率会偷懒跳过一半。

但代价是: 计划文档 1500 行。 信息密度很低------每个任务里大段的代码占满了屏幕。适合机器执行,不适合人阅读。如果你想在动手前通读一遍计划,做好心理准备。


子代理执行:两个翻车现场

subagent-driven-development 的工作方式是每个任务派一个独立子代理实现,完成后自动做两轮 review(先查规格合规,再查代码质量)。

先说亮点。

子代理在实现 validators 的时候发现了一个测试顺序 bug:validateSize(4000, 4001) 这个用例里,height=4001 同时触发了高度上限和像素上限两个错误条件。如果先检查高度范围,返回的错误码和预期的像素超限错误不一致。子代理自己调换了检查顺序------把像素上限检查提前到 height 范围检查之前------测试全绿。

这种边界条件我自己写也未必会注意到。子代理因为严格按测试用例驱动,反而能抓到。

再说翻车。

第一个翻车:路由匹配。实现计划里用的是 Hono 的正则路由:

ts 复制代码
app.get('/:size{[0-9]+x[0-9]+\\.[a-zA-Z]+}', handleImageRequest);

单元测试全部通过------因为 vitest 用的是 app.request() 内部方法,不经过真实的 HTTP 服务器栈。

但启动真实服务器用 curl 测试, 所有带正则的路由返回 404。

我没有继续深挖到底是路由 pattern 写法、版本差异,还是 @hono/node-server 适配层的问题;但现象很明确:正则 pattern 在 app.request() 里能匹配,换到真实 HTTP 请求就不行。

最终改成了 catch-all 路由 + 手动解析:

ts 复制代码
app.get('/*', (c, next) => {

if (c.req.path === '/') return next();

return handleImageRequest(c);

});

单元测试全绿 ≠ 生产可用。 尤其是路由、中间件、序列化/反序列化这些和运行时强相关的代码,AI 生成的方案在测试环境里可能完全正常,换到真实环境就暴露差异。

第二个翻车:入口文件的副作用。index.ts 在底部直接调了 serve(),但集成测试文件 import { app } from '../src/index.js' 触发了模块加载,导致每次跑测试都启动一个真实的 HTTP 服务器,端口冲突报 EADDRINUSE。

修复方案是加条件判断:

ts 复制代码
if (process.argv[1]?.includes('index.ts') || process.argv[1]?.includes('index.js')) {

serve({ fetch: app.fetch, port: config.port, hostname: config.host });

}

这种"副作用在 import 时触发"的问题是 Node.js 的经典坑。AI 生成的代码默认是为"直接运行"写的,不考虑被 import 的场景。如果你用 monorepo 或者测试框架 import 了入口文件,必踩。


安全与资源:设计文档不会替你兜底的部分

这部分是整篇文章最想讲的。因为 AI 在设计阶段的"安全感"太有欺骗性了。

设计阶段讨论了但没解决透的问题

1. 内存占用的动态特性

前面算过了,10 个 4000×4000 的 AVIF 并发请求,内存峰值可能接近 1GB。正确的做法不是只给一个固定并发数,而是:根据请求的图片尺寸和格式动态计算预估内存,动态调整并发上限。小图可以放更多并发,大图严格限流。

AI 知道要做并发限制,但没有继续把并发限制的阈值和请求参数关联起来。

2. GIF 的内存放大效应

gif-encoder-2 的 neuquant 算法需要构建颜色调色板,内存开销会随着帧数、尺寸和颜色复杂度上升。设计文档只写了"2 帧微闪",没有评估编码器在极端尺寸下的资源消耗。如果服务上线后有人用脚本批量请求 4000×4000 的 GIF,不需要传统意义上的 DDoS,也可能把服务打到 OOM。

3. SVG XSS 的防护深度不够

当前实现是 HTML 实体编码:

ts 复制代码
function escapeHtml(text: string): string {

return text

.replace(/&/g, '&')

.replace(/</g, '&lt;')

.replace(/>/g, '&gt;')

.replace(/"/g, '&quot;');

}

这能防当前 ?text= 文本节点里的 <script> 注入。当前实现里,用户主要能控制文本和颜色;颜色参数走的是正则白名单,所以 <foreignObject>、onload、xlink:href="javascript:..." 这些向量暂时没有直接注入入口。

真正的问题在于扩展性:SVG 的攻击面不止文本节点。如果后续加了更多用户可控的 SVG 属性(比如自定义字体、自定义尺寸标注、图片 URL),每个新参数都是一个新的注入面。

安全防护最容易出错的地方不是"没做",而是"做了一半,给了自己一种做完了的错觉"。

4. 速率限制只存在设计文档里

威胁矩阵里写了速率限制,优先级标的是"平台级(Serverless)/ 可选中间件(自托管)"。实际上最终代码里没有速率限制的实现。对于自托管部署来说,这意味着任何人都可以无限制请求生成图片。虽然 Sharp 的处理速度本身就是一种自然限速,但这不构成安全策略。

Express 生态有 express-rate-limit,Hono 虽然可以接第三方 middleware 或者交给平台层限流,但这里没有把它落到代码里。这个遗漏不是语法问题,而是部署策略问题:公共服务不能只靠"图片生成比较慢"来当限流。

设计阶段没落地或没讨论的问题

1. 请求超时只存在配置里

设计文档里写了 request timeout,配置文件里也有 REQUEST_TIMEOUT=5000,但最终路由和图片生成代码没有真正使用这个配置。也就是说,一个慢请求并不会因为超过 5 秒被主动中断。对于 Sharp/AVIF/GIF 这种可能吃 CPU 和内存的路径来说,这比"没写配置"更危险------因为你会以为它已经生效了。

2. Content-Length 响应头缺失

生成的图片响应没有 Content-Length 头。虽然 HTTP/1.1 可以用 Transfer-Encoding: chunked,但某些旧版 HTTP 客户端和 CDN 回源时需要 Content-Length 来判断响应完整性。dummyimage.com 的文档专门强调了这一点------AI 做了竞品调研,但调研结果里的这个细节没有体现在实现中。

3. 没有健康检查端点

设计文档把健康检查放在了 Phase 2(延期功能)。但实际部署到 K8s 或 Docker Compose 时,没有 /health 端点意味着编排系统无法做存活探测。对于一个要上线的服务,健康检查不属于"锦上添花",属于"基本生存条件"。

4. Dockerfile 拷贝了 public 但没拷贝 fonts

Dockerfile 里有 COPY public ./public,但 fonts/ 目录没有被拷贝。虽然当前实现用的是系统字体,但如果后续有人想把 M+ Fonts 的字体嵌入加回来,Dockerfile 不会报错------字体文件不存在,运行时会在某个请求上静默失败,而不是启动时报错。


社区的现状和我的看法

我最近看到的很多关于"AI 写代码"的讨论,常常会滑向两个极端:一派说 AI 替代初级工程师只是时间问题,另一派说 AI 生成的代码都是屎山不能用。

两种说法都不对。

AI 最大的价值不是"替代人写代码",而是"强制走完流程"。 没有 AI 的情况下,多少人会为一个图片占位服务写设计文档?会写 50 个测试用例?会做竞品调研?会在实现前把威胁矩阵列出来?

大部分人不会。不是能力问题,是时间问题。DDL 压在头上的时候,设计文档和测试永远是第一个被牺牲的。

AI 驱动开发的核心优势不是快,是 不让你跳过该做的事。

但同时,AI 在这类工作流里有一个很容易被低估的问题:它做的安全分析和性能评估经常是 静态的、孤立的 。它知道"应该限制并发",但未必会继续追问"不同尺寸的图片并发时内存压力差几个数量级"。它知道"应该防 XSS",但未必会把 SVG 后续扩展时的攻击面讲透。它能把设计文档写成一篇满分作文,但不会自动告诉你哪些段落是"愿望",哪些段落已经落地了。

这也是为什么"AI 替代初级工程师"这个说法至少没那么简单。 初级工程师的价值不只在于写代码,更在于他们会在生产环境出问题之后长记性,会把团队里的事故经验变成下一次设计时的直觉。AI 如果没有被明确喂进这些上下文,不会自动继承上一轮的翻车教训。

Superpowers 这套工作流的核心洞见其实是: 把 AI 定位为"流程执行者"而不是"决策者"。 它不去替你判断方案好坏,而是逼你把判断过程走完。brainstorming 的"一次一个问题"就是最典型的设计------节奏是人在控制,AI 只负责在每个节点把信息铺开。

但反过来,这套流程对简单项目来说太重了。我这个图片占位服务的核心逻辑不到 300 行,但走完 brainstorming → 设计文档 → 实现计划 → 子代理执行,流程本身花的时间不比写代码少。 如果你做的是一周内能手工写完的项目,用全流程可能得不偿失。 头脑风暴那一步值得保留,后面的计划和执行可以按需裁减。


最终成果

功能上没什么问题,6 种格式全绿:

arduino 复制代码
PNG → 200 image/png

JPEG → 200 image/jpeg

WebP → 200 image/webp

AVIF → 200 image/avif

GIF → 200 image/gif (2 帧闪烁动画)

SVG → 200 image/svg+xml

错误参数 → 400 image/png(返回错误图片,不破坏 <img> 标签)

文档首页 → 200 text/html

架构简洁,代码量不大,测试覆盖完整。作为一个 evening project,质量远超预期。

但如果要上线,至少还要补:速率限制、基于尺寸的动态并发控制、真正生效的请求超时、健康检查端点、Content-Length 头,以及一轮真实流量下的内存压力测试。


几点实在的建议

如果你也想试试这种工作流,说几条掏心窝子的:

1. 安全和资源问题不要只看设计文档。 AI 会在文档里把该列的列出来,让你觉得"都考虑到了"。但列出来和解决了之间,需要你自己去验证------尤其是并发场景下的内存、极端参数组合下的性能、以及第三方库在边界条件下的行为。

2. 路由、中间件、配置这些和运行时强相关的东西,必须在真实环境跑一遍冒烟测试。 app.request() 通过不代表 curl 能通。AI 生成的代码默认是给"理想运行时"写的,真实运行环境的兼容性问题可能覆盖不到。

3. 设计文档里的 Phase 2 和配置项要认真对待。 被推迟的功能往往是安全性和可运维性的底线------健康检查、速率限制、请求超时、字体策略。如果你打算上线,这些不是"以后再说"的事。

4. 头脑风暴是整个流程里 ROI 最高的环节。 不是为了听 AI 的建议,而是利用它做竞品调研和方案对比的能力。多问"竞品怎么做的"和"这个方案在什么情况下会出问题"。

5. 项目越小,流程越要裁剪。 一个 weekend project 不需要 1500 行的实现计划。brainstorming 做足,后面的按自己节奏来就行。


项目地址是: place-image ,感兴趣可以star或者fork搭一个玩玩。遇到内存问题别慌,大家都一样。

相关推荐
陈随易1 小时前
Postman 被曝自动往 Claude Code 和 Codex 装插件
前端·后端·程序员
云-缩影3 小时前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
Dream-Y.ocean3 小时前
鸿蒙PC平台宅匠 3D 室内设计适配实战:基于 Electron 壳方案的 Three.js 双渲染引擎与参数化户型编辑器开发
javascript·3d·harmonyos
考虑考虑4 小时前
Posrgresql恢复.backup文件
数据库·后端·postgresql
Csvn4 小时前
性能监控
前端
Highcharts.js4 小时前
Highcharts Gantt使用符号的自定义数据标签开发甘特图示例
javascript·甘特图·highcharts·图表开发·实战代码·响应式图表
无奈何杨4 小时前
风控决策系统决策流,策略模式,权重、顺序、投票、最坏
后端
Csvn4 小时前
构建工具
前端
FRing035 小时前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Patrick_Wilson5 小时前
Go 为什么需要 defer?从函数怎么收尾说起
后端·go·编程语言