CVE-2026-24002：严重沙箱逃逸漏洞致格里斯电子表格沦为远程代码执行攻击工具
赛拉研究实验室在 Grist-Core 中发现的一处严重漏洞，让看似无害的电子表格公式，成为攻陷整个企业组织的关键。该漏洞编号为CVE-2026-24002，CVSS 评分高达 9.1 分，攻击者可利用其突破该平台的 Pyodide 沙箱防护，实现完全的远程代码执行（RCE）。Grist 作为 Excel 的可编程替代工具，在政府、教育及各行业中应用广泛，其核心优势是支持在单元格中运行 Python 代码以实现工作流自动化。但研究人员发现，这一 “可编程数据层” 的底层架构存在严重脆弱性。与依赖明显代码注入手段的传统攻击不同，该漏洞的利用方式是将合法的数据包武器化，攻击流量会沿着 Grist 解析公式的常规数据处理路径执行，让日常的电子表格更新操作，沦为悄无声息的系统接管行为。研究报告详细阐述了攻击者可突破沙箱边界的三种具体方式： 类层级遍历：利用 Python 的对象模型，访问os.system()等被禁用的内置函数； 直接调用 C 库：借助 ctypes 库，从内存中直接调用系统底层函数； 滥用 Emscripten 运行时：调用emscripten_run_script_string()函数，在宿主运行时环境中执行 JavaScript 代码。 该漏洞造成的后果极为严重：此次沙箱逃逸并非仅能实现本地服务器的远程代码执行，还可攻陷运行租户工作流的 SaaS 控制层，实现该层面的远程代码执行。Grist 安全团队已迅速采取行动修复该漏洞，此次修复涉及底层架构的根本性调整：将 Pyodide 公式的执行环节，默认迁移至 Deno 运行时环境中进行。这一调整的关键意义在于改变了漏洞的触发后果：此前的情况是 “一旦突破 Pyodide 沙箱，就会直接攻陷宿主系统”，而现在宿主运行时的权限完全由 Deno 的权限模型管控，从根源上降低了风险。研究人员敦促管理员立即将 Grist 升级至1.7.9 及更高版本，同时需注意切勿禁用新的防护机制。报告警示，若设置配置项GRIST_PYODIDE_SKIP_DENO=1，将会绕过此次修复，该操作应被视为主动选择使用防护能力更弱的隔离边界。研究人员指出：当一款自动化或数据平台成为受信任的执行环境时，其沙箱逃逸漏洞带来的就不再是单一服务器的故障，而是整个信任边界的崩塌。
从底层引擎优化角度，深入剖析 Hono 路由器的极致性能奥秘引言在众多 JavaScript Web 框架中，Hono 以其极致的性能表现脱颖而出。特别是在 Cloudflare Workers、Deno 等边缘计算环境中，Hono 的路由匹配速度在同类框架中名列前茅。这背后的秘密是什么？答案就藏在它的 RegExpRouter 实现中。⚠️ 重要提示：本文重点讨论 RegExpRouter 的核心原理。在实际应用中，Hono 使用 SmartRouter 自动组合多种路由器（RegExpRouter、TrieRouter、LinearRouter）以达到最佳性能。本文将通过一个精简的实现，深入剖析 Hono 路由器的核心原理，帮助你理解：为什么 RegExp 路由比传统路由快路由是如何预编译的参数提取的巧妙设计📌 关键要点项目说明核心原理将路由预编译为正则表达式，利用引擎底层优化性能优势减少 JS 层开销，一次原生调用完成匹配最佳环境Cloudflare Workers、Deno 等边缘计算平台实际架构SmartRouter + RegExpRouter + TrieRouter 组合权衡考虑Node.js 环境性能打折扣，路由注册较慢传统路由的性能瓶颈在理解 Hono 的优化之前，我们先看看传统路由的处理方式：// 传统路由的典型实现 function matchRoute(path, routes) { for (const route of routes) { // 1. 字符串分割 const pathParts = path.split('/'); const routeParts = route.path.split('/'); // 2. 逐段比较 if (pathParts.length !== routeParts.length) continue; const params = {}; let matched = true; // 3. JS 层面的循环和条件判断 for (let i = 0; i < pathParts.length; i++) { if (routeParts[i].startsWith(':')) { params[routeParts[i].slice(1)] = pathParts[i]; } else if (pathParts[i] !== routeParts[i]) { matched = false; break; } } if (matched) return { route, params }; } return null; }性能问题在哪里？❌ 大量的字符串操作（split、slice）❌ 多层循环和条件判断，全在 JavaScript 层面执行❌ 每个请求都要重复这些操作Hono 的解决方案：下沉到引擎层Hono 的核心思想非常简单却极其巧妙：将路由匹配逻辑从 JavaScript 层下沉到 JavaScript 引擎底层（C++ 实现的正则表达式引擎）1. 预编译阶段（应用启动时）在应用启动时，Hono 会将路由路径转换为正则表达式：// 路由路径：/user/:id/posts/:postId // 转换为正则：/^\/user\/([^/]+)\/posts\/([^/]+)$/ const paramNames: string[] = []; const regexPath = path.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => { paramNames.push(paramName); // 记录参数名 return '([^/]+)'; // 替换为捕获组 }); const pattern = new RegExp(`^${regexPath}$`);关键点：:id → ([^/]+)：匹配除 / 外的任意字符参数名被保存在 paramNames 数组中这个"昂贵"的编译过程只在启动时执行一次2. 匹配阶段（请求到来时）当请求到来时，只需要一行代码：const match = pattern.exec(path);这行代码的魔法：✅ exec 是 JavaScript 引擎的原生方法，由 C++ 实现✅ 正则引擎在底层进行了高度优化（状态机、JIT 编译等）✅ 无需手动分割字符串、无需 JS 层循环✅ 参数提取通过捕获组自动完成3. 参数提取匹配成功后，参数提取也非常简洁：const params: Record<string, string> = {}; route.paramNames.forEach((name, index) => { params[name] = match[index + 1]; // match[0] 是完整匹配 });完整实现解析让我们看完整的简化实现，包含所有必要的类型定义：// ============ 类型定义 ============ /** * 路由处理函数类型 * @param params - 从 URL 中提取的参数对象 */ type Handler = (params: Record<string, string>) => void; /** * 路由信息接口 */ interface Route { method: string; // HTTP 方法（GET, POST, etc.） path: string; // 原始路径模式（如 /user/:id） handler: Handler; // 路由处理函数 paramNames: string[]; // 参数名数组（如 ['id', 'postId']） } /** * 匹配结果接口 */ interface MatchResult { handler: Handler; params: Record<string, string>; } // ============ 核心路由器实现 ============ export class DemoRegExpRouter { // 存储预编译的正则表达式和路由信息 private routes: { pattern: RegExp; route: Route }[] = []; // 注册路由：预编译 add(method: string, path: string, handler: Handler) { const paramNames: string[] = []; // 将 :param 转为正则捕获组 const regexPath = path.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => { paramNames.push(paramName); return '([^/]+)'; }); const pattern = new RegExp(`^${regexPath}$`); this.routes.push({ pattern, route: { method, path, handler, paramNames } }); } // 匹配路由：执行正则 match(method: string, path: string) { for (const { pattern, route } of this.routes) { if (route.method !== method && route.method !== 'ALL') continue; const match = pattern.exec(path); // 核心：原生正则匹配 if (match) { const params: Record<string, string> = {}; route.paramNames.forEach((name, index) => { params[name] = match[index + 1]; }); return { handler: route.handler, params }; } } return null; } }实战示例const router = new DemoRegExpRouter(); // 注册路由 router.add('GET', '/user/:id', (params) => { console.log(`获取用户详情，ID: ${params.id}`); }); router.add('GET', '/user/:id/posts/:postId', (params) => { console.log(`获取用户 ${params.id} 的文章 ${params.postId}`); }); // 匹配请求 const result = router.match('GET', '/user/123/posts/456'); if (result) { result.handler(result.params); // 输出: 获取用户 123 的文章 456 }性能优势分析为什么 RegExpRouter 更快？减少 JS 执行开销：从多层循环降到一次原生调用引擎优化：正则引擎经过数十年优化，包含 JIT、状态机等技术减少内存分配：无需频繁创建数组、对象一次性编译：预编译阶段完成所有准备工作，请求时零额外开销性能对比概览方案实现层次执行效率适用场景传统路由JavaScript 层中等通用场景RegExpRouter引擎底层极快边缘计算、高并发📊 实际 Benchmark：根据 Hono 官方测试，在 Cloudflare Workers 和 Deno 环境中，Hono 是同类框架中最快的。但在 Node.js 环境中，由于适配器开销，性能优势会打折扣。Hono 真实实现的进一步优化本文的简化版每个路由都是独立的正则表达式。而 Hono 的真实实现采用了多路由器协同的策略：1. SmartRouter（智能路由器）Hono 默认使用 SmartRouter，它会：根据路由特征自动选择最快的路由器动态组合 RegExpRouter、TrieRouter、LinearRouter检测路由模式并持续使用最优方案2. 路由器分工路由器适用场景特点RegExpRouter动态参数路由最快，但不支持所有模式TrieRouter复杂路由模式支持所有模式，性能优秀LinearRouter少量路由简单可靠，路由少时够用3. 实际优化策略静态路由优先：/about、/contact 等使用 Map 快速查找路由预分组：按 HTTP 方法分组，减少匹配次数延迟编译：RegExpRouter 编译较慢，适合应用启动时初始化，不适合无服务器环境的冷启动这些优化使得 Hono 在处理数百个路由时仍然保持极高性能。局限性与权衡RegExpRouter 并非银弹，在使用时需要注意以下限制：RegExpRouter 的局限不支持所有路由模式：某些复杂的路由规则无法用简单正则表示注册阶段较慢：路由编译需要时间，不适合每次请求都重新初始化的环境（如某些无服务器冷启动场景）调试难度：编译后的正则表达式可读性较差，出错时难以定位模式约束：如带严格约束的参数 /:id(\\d+) 需要额外处理运行环境差异环境性能表现原因Cloudflare Workers⭐⭐⭐⭐⭐ 极快原生 Web 标准 APIDeno⭐⭐⭐⭐⭐ 极快原生 Web 标准 APIBun⭐⭐⭐⭐ 很快高性能 JS 运行时Node.js⭐⭐⭐ 中等需要适配器转换💡 选型建议：如果你的应用运行在边缘计算环境（Cloudflare Workers、Vercel Edge Functions 等），Hono 是绝佳选择。如果是传统 Node.js 服务器，Fastify 可能是更好的选择。但对于大多数 Web 应用场景，RegExpRouter 的性能与功能平衡已经足够优秀。总结Hono 的 RegExpRouter 通过"预编译 + 引擎下沉"的策略，在特定环境中将路由匹配性能提升到极致：核心优势预编译：启动时一次性将路由转为正则，摊销成本引擎下沉：利用 C++ 实现的正则引擎，避免 JS 层开销参数捕获：巧妙利用正则捕获组，无需手动解析智能组合：SmartRouter 自动选择最优路由器，兼顾性能与功能设计哲学这种设计哲学值得我们思考：性能优化的终极目标，往往是让"热路径"代码尽可能多地运行在更底层的优化环境中。对于 Web 框架来说，路由匹配就是最热的路径之一。Hono 在边缘计算环境中找到了这个问题的最优解，同时通过多路由器架构保证了广泛的适用性。最佳实践✅ 边缘计算/Serverless：Hono 是首选（Cloudflare Workers、Deno Deploy）✅ 高并发场景：充分发挥 RegExpRouter 性能优势⚠️ Node.js 环境：考虑性能权衡，或选择 Fastify 等专门优化的框架⚠️ 复杂路由规则：了解 RegExpRouter 的限制，必要时使用 TrieRouter延伸阅读：Hono 官方文档Hono Routers 详解Hono BenchmarksV8 正则引擎优化技术Web 框架性能对比工具技术资源：Hono GitHub 仓库RegExpRouter 创建者：Taku Amano📝 文章说明本文基于对 Hono 框架的学习和研究编写，代码示例为简化版实现，用于教学目的。实际的 Hono RegExpRouter 实现更加复杂和优化。性能数据和对比来自 Hono 官方文档和社区测试，具体数值会因测试环境、负载类型、运行时版本等因素而异。实际使用时请根据自己的场景进行测试。如果您发现文中有任何不准确之处，欢迎指正。
一个基于 Hono 的全栈 Web 框架，结合了 Islands 架构和边缘计算的强大能力引言在现代 Web 开发中，我们面临着一个永恒的挑战：如何在提供丰富交互体验的同时，保持快速的加载速度和优秀的性能？传统的单页应用（SPA）虽然交互流畅，但首屏加载慢、SEO 困难；而传统的服务端渲染（SSR）虽然首屏快，但缺乏现代前端框架的开发体验。HonoX 的出现，为这个问题提供了一个优雅的解决方案。它是基于超快的 Hono Web 框架构建的全栈框架，采用 Islands 架构，完美平衡了性能和开发体验。什么是 HonoX？HonoX 是一个全栈 Web 框架，它建立在 Hono 之上。Hono 是一个轻量级、超快速的 Web 框架，可以运行在任何 JavaScript 运行时（Cloudflare Workers、Deno、Bun、Node.js 等）。核心特性Islands 架构 - 渐进式水合，只在需要的地方加载 JavaScript文件路由系统 - 基于文件系统的直观路由边缘优先 - 为 Cloudflare Workers 等边缘运行时优化类型安全 - 完整的 TypeScript 支持零配置 - 开箱即用的最佳实践极致性能 - 继承 Hono 的超快性能Islands 架构：重新思考前端水合什么是 Islands 架构？Islands 架构是一种现代前端架构模式，最早由 Etsy 的前端架构师 Katie Sylor-Miller 提出，后来被 Astro、Fresh 等框架采用。想象一个网页是一片海洋，而需要交互的组件是海洋中的"岛屿"：┌─────────────────────────────────┐ │ 静态 HTML（服务端渲染） │ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ Island │ │ Island │ │ │ │ (交互) │ │ (交互) │ │ │ └─────────┘ └─────────┘ │ │ │ │ ┌─────────┐ │ │ │ Island │ │ │ │ (交互) │ │ │ └─────────┘ │ └─────────────────────────────────┘这种架构的优势在于：减少 JavaScript 负载 - 只加载真正需要的 JavaScript提升首屏性能 - 静态内容立即可见渐进式增强 - 交互组件逐步加载和激活更好的 SEO - 完整的服务端渲染内容HonoX 中的 Islands在 HonoX 中使用 Islands 非常简单：// app/islands/Counter.tsx import { useState } from 'hono/jsx' export default function Counter({ initialCount = 0 }) { const [count, setCount] = useState(initialCount) return ( setCount(count - 1)}>- {count} setCount(count + 1)}>+ ) }只需将组件放在 app/islands/ 目录下，HonoX 会自动处理：服务端渲染客户端代码分割按需水合在页面中使用：// app/routes/index.tsx import Counter from '../islands/Counter' export default function Home() { return ( 我的页面 这段文字是纯静态的，不需要 JavaScript <Counter initialCount={0} /> ) }文件路由系统：约定优于配置HonoX 采用基于文件的路由系统，让路由管理变得直观：app/routes/ ├── index.tsx → / ├── about.tsx → /about ├── blog/ │ ├── index.tsx → /blog │ └── [slug].tsx → /blog/:slug └── api/ ├── users.ts → /api/users └── users/ └── [id].ts → /api/users/:id动态路由使用方括号定义动态路由参数：// app/routes/blog/[slug].tsx import { createRoute } from 'honox/factory' export default createRoute((c) => { const { slug } = c.req.param() return c.render( 文章：{slug} ) })API 路由API 路由返回 JSON 数据：// app/routes/api/users/[id].ts import { Hono } from 'hono' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' const app = new Hono() // GET /api/users/:id app.get('/:id', (c) => { const id = c.req.param('id') return c.json({ id, name: 'User ' + id }) }) // POST /api/users/:id const schema = z.object({ name: z.string().min(1), email: z.string().email(), }) app.post('/:id', zValidator('json', schema), (c) => { const data = c.req.valid('json') const id = c.req.param('id') return c.json({ id, ...data, updated: true }) }) export default app中间件系统：强大且灵活HonoX 继承了 Hono 的中间件系统，让你可以轻松处理横切关注点：// app/routes/_middleware.tsx import { createRoute } from 'honox/factory' import { compress } from 'hono/compress' import { logger } from 'hono/logger' import { secureHeaders } from 'hono/secure-headers' export default createRoute((c, next) => { // 日志记录 logger()(c, next) // 安全头 secureHeaders()(c, next) // 响应压缩 compress()(c, next) return next() })自定义中间件创建自定义中间件也很简单：// 性能计时中间件 export const timing = createMiddleware(async (c, next) => { const start = Date.now() await next() const end = Date.now() c.header('Server-Timing', `total;dur=${end - start}`) }) // 认证中间件 export const auth = createMiddleware(async (c, next) => { const token = c.req.header('Authorization') if (!token) { return c.json({ error: 'Unauthorized' }, 401) } // 验证 token... await next() })性能优化：从框架层面开始HonoX 内置了多种性能优化：1. 自动代码分割每个 Island 组件自动分割成独立的 chunk：// 自动生成类似这样的输出 dist/ ├── client/ │ ├── island-Counter.js (3KB) │ ├── island-Search.js (5KB) │ └── island-Modal.js (4KB) └── server/ └── index.js2. 流式 SSR使用 Suspense 实现流式渲染：import { Suspense } from 'hono/jsx' import AsyncData from '../islands/AsyncData' export default function Page() { return ( 立即显示的标题 <Suspense fallback={ 加载中... }> <AsyncData /> </Suspense> ) }页面渲染流程：立即发送 HTML 头部和静态内容异步组件准备好后流式发送最后发送激活脚本3. 智能缓存策略// 静态资源长期缓存 app.get('/static/*', async (c) => { c.header('Cache-Control', 'public, max-age=31536000, immutable') return c.next() }) // API 响应 ETag 缓存 app.get('/api/data', async (c) => { const data = await fetchData() const etag = generateETag(data) if (c.req.header('If-None-Match') === etag) { return c.body(null, 304) } c.header('ETag', etag) return c.json(data) })类型安全：端到端的 TypeScriptHonoX 提供完整的类型安全，从路由到 API：// 定义 API 类型 type User = { id: string name: string email: string } // API 路由自动推断类型 const app = new Hono<{ Variables: { user: User } }>() app.get('/api/user', (c) => { const user = c.get('user') // 类型：User return c.json(user) }) // 在客户端使用类型 const response = await fetch('/api/user') const user: User = await response.json()部署：边缘优先HonoX 针对边缘运行时优化，特别是 Cloudflare Workers：Cloudflare Pages 部署# 构建 npm run build # 部署 npm run deploy优势：全球 CDN - 300+ 个边缘节点零冷启动 - Workers 即时响应自动扩展 - 无需配置低成本 - 免费层每天 100,000 请求其他平台HonoX 也支持部署到：Vercel - 使用 Node.js 适配器Netlify - Edge FunctionsDeno Deploy - 原生支持传统服务器 - Node.js实战案例：构建一个博客让我们用 HonoX 构建一个完整的博客系统：1. 文章列表页// app/routes/blog/index.tsx import { createRoute } from 'honox/factory' import { getPosts } from '../../lib/posts' export default createRoute(async (c) => { const posts = await getPosts() return c.render( 博客文章 {posts.map(post => ( {post.title} {post.date} ))} ) })2. 文章详情页// app/routes/blog/[slug].tsx import { createRoute } from 'honox/factory' import { getPost } from '../../lib/posts' import CommentSection from '../../islands/CommentSection' export default createRoute(async (c) => { const { slug } = c.req.param() const post = await getPost(slug) if (!post) { return c.notFound() } return c.render( {post.title} {post.date} {post.author} {/* 评论区使用 Island 实现交互 */} <CommentSection postId={slug} /> , { title: post.title, description: post.excerpt, } ) })3. 交互式评论组件// app/islands/CommentSection.tsx import { useState } from 'hono/jsx' type Comment = { id: string author: string content: string createdAt: string } export default function CommentSection({ postId }: { postId: string }) { const [comments, setComments] = useState<Comment[]>([]) const [loading, setLoading] = useState(false) const loadComments = async () => { setLoading(true) const res = await fetch(`/api/comments/${postId}`) const data = await res.json() setComments(data.comments) setLoading(false) } const submitComment = async (e: Event) => { e.preventDefault() const form = e.target as HTMLFormElement const formData = new FormData(form) await fetch(`/api/comments/${postId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ author: formData.get('author'), content: formData.get('content'), }), }) form.reset() loadComments() } return ( 评论 {loading ? '加载中...' : '加载评论'} {comments.map(comment => ( {comment.author} {comment.content} {comment.createdAt} ))} 提交评论 ) }4. 评论 API// app/routes/api/comments/[postId].ts import { Hono } from 'hono' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' const app = new Hono() const commentSchema = z.object({ author: z.string().min(1).max(50), content: z.string().min(1).max(1000), }) // 获取评论 app.get('/:postId', async (c) => { const { postId } = c.req.param() // 从数据库获取评论 const comments = await db.comments .where('postId', postId) .orderBy('createdAt', 'desc') .get() return c.json({ comments }) }) // 添加评论 app.post('/:postId', zValidator('json', commentSchema), async (c) => { const { postId } = c.req.param() const data = c.req.valid('json') const comment = await db.comments.create({ postId, ...data, createdAt: new Date().toISOString(), }) return c.json(comment, 201) }) export default app与其他框架对比HonoX vs Next.jsHonoX 的优势：更轻量（核心更小）边缘优先设计更简单的学习曲线更快的冷启动Next.js 的优势：更成熟的生态系统更多的官方集成React Server Components更强大的图像优化HonoX vs Astro相似点：都使用 Islands 架构都注重性能都支持多框架HonoX 的优势：更好的 API 路由原生支持边缘运行时更轻量的运行时Astro 的优势：可以混用多个前端框架更丰富的内容处理功能更好的静态站点生成HonoX vs Fresh相似点：都基于 Islands 架构都使用文件路由都注重性能HonoX 的优势：支持更多运行时更灵活的中间件系统基于 Hono 的强大生态Fresh 的优势：Deno 原生集成Preact 默认支持更简单的配置最佳实践1. 合理使用 Islands✅ 好的做法：// 只将需要交互的部分做成 Island {title} {content} <ShareButtons /> {/* Island */} <CommentSection /> {/* Island */} ❌ 避免：// 不要把整个页面都做成 Island export default function Page() { const [state, setState] = useState() // 整个页面都会在客户端水合 }2. 优化数据获取✅ 好的做法：// 在服务端并行获取数据 export default createRoute(async (c) => { const [user, posts, comments] = await Promise.all([ getUser(), getPosts(), getComments(), ]) return c.render(<Page user={user} posts={posts} comments={comments} />) })❌ 避免：// 避免串行请求 const user = await getUser() const posts = await getPosts() // 等待上一个完成 const comments = await getComments() // 又要等待3. 使用流式渲染// 对于慢速数据使用 Suspense export default function Page() { return ( <> {/* 快速渲染 */} <Suspense fallback={<Skeleton />}> <SlowData /> {/* 异步加载 */} </Suspense> </> ) }4. 实现有效缓存// 分层缓存策略 const app = new Hono() // 1. 边缘缓存 app.use('/api/*', cache({ cacheName: 'api-cache', cacheControl: 'max-age=60', })) // 2. 浏览器缓存 app.use('/static/*', async (c, next) => { await next() c.header('Cache-Control', 'public, max-age=31536000, immutable') }) // 3. 条件请求 app.use('/data/*', etag())未来展望HonoX 还在快速发展中，以下是一些令人期待的方向：更多的运行时支持 - 包括 AWS Lambda、Azure Functions 等增强的开发工具 - 更好的调试体验、性能分析工具更丰富的生态 - 官方插件、第三方集成框架无关的 Islands - 支持 React、Vue、Svelte 等增量静态生成 - 类似 Next.js 的 ISR结论HonoX 代表了现代全栈框架的一个重要方向：性能优先 - Islands 架构和边缘计算开发体验 - 简单直观的 API灵活性 - 支持多种运行时和部署方式类型安全 - 完整的 TypeScript 支持如果你正在寻找一个轻量、快速、现代的全栈框架，特别是需要部署到边缘运行时，HonoX 是一个值得考虑的选择。虽然它还比较年轻，生态系统不如 Next.js 那样成熟，但它的设计理念和技术方向都非常正确。随着 Hono 生态的发展，HonoX 也将变得越来越强大。资源链接HonoX GitHubHono 官方文档Islands 架构介绍Cloudflare Workers 文档本项目示例代码欢迎在评论区分享你对 HonoX 的看法和使用经验！
Node.js 之父宣判“手写代码时代结束”！DHH 明确反对“结束论”：大模型还差口气，手写更有竞争力
Ryan Dahl 在 1 月 20 日给软件工程下了结论：“人类写代码的时代已经结束。”留下的工作里，不包括继续手写语法。 如果这话出自某个科技网红，大概刷过去就算了。但 Ryan Dahl 不一样——他不仅写出了 Node.js，后来还“推倒重来”做了 Deno。你可以把他的意思理解为：写代码这部分会越来越自动化，而人的价值会更多落在判断、取舍和责任上。 而在 Ryan Dahl 这次“宣判”之前，1 月 3 日，Ruby on Rails 作者 DHH 也在 X 上连发多条，语气罕见地偏“乐观派”： “别让那些粗制滥造和尴尬翻车，遮住你对 AI 的惊叹。自从我们把计算机连上互联网以来，这是我们让计算机做到过的最令人兴奋的事。如果你在 2025 年一直对 AI 悲观或怀疑，不如在 2026 年的开端，用一点乐观和好奇再试试看？” 于是，社区里迅速冒出一种更夸张、但传播力极强的解读：“DHH 都松口了。”“连最不买账的人都开始给 AI 站台——你还有什么理由不用？”甚至有人干脆把它说成：“DHH 也扛不住了，最终还是向 AI 屈服低头了。” 但你真去听 DHH 的原话，会发现所谓“DHH 屈服论”，并不是那么回事儿。 在最新一期播客中，他说在 37signals，AI 没有在写真实产品，更谈不上“从零写出什么东西”。 他在用 AI，而且每天都用，但更多是做那种“一发入魂”的小实验；一旦进入真工程：要持续演进、要迭代、要打磨，他就会觉得：“这在浪费我的时间，到这一步我自己写更快。” 所以他们的新产品 Fizzy 里 95% 的代码，还是人类亲手敲出来的。 他还补了一句：我们离那种“AI 让一切始终更好、更快、更省心”的明显拐点，还差一点。 “就现在而言，我仍然在意代码的样子。我在意它的美感。我在意打磨、推敲、润色。” 更关键的是，他不是在怀旧。他明确说：“手写代码依然有竞争力。”“至少在此时此刻，这是一个仍然有竞争力的选择。” 而且他的判断正好和 Ryan Dahl 相反：“我们并没有到 AGI，没有到那种‘人类写代码的时代死了’的程度。” 挺好玩的是，DHH 还说要远离 Anthropic 的 CEO：他一听到那种“再过五分钟就不需要程序员了”的口吻就火大，直接开喷：“你们到底用的啥模型啊？”反正他自己用的是 Opus 4.5（或当下版本），但在他的体验里，这种“程序员马上下岗”的说法完全不符合现实——尤其是那些要长期维护、持续迭代、不断演进的真实工程，离“五分钟结束”差得十万八千里。 以下是 DHH 播客整理全文翻译： “如果浏览 Web 的不再是人类” 主持人：欢迎大家来到《Next Token》。今天这期节目对我来说有点特别，可能要追溯到 25 年前。很高兴请到 DHH——David Heinemeier Hansson。欢迎你。DHH：很高兴来，谢谢邀请。 主持人：我猜你可能是刚从赛车里下来（笑）。DHH：现在是休赛期，正好歇一歇。 主持人（Torsten）：那我就先来点“热血沸腾”的话题。我从 2010 年左右就开始关注你，你可能是对我影响最大的前五位程序员之一。如果没有你，我可能不会走到今天。我职业生涯中有七八年都在写 Rails，看了你所有的书、博客。我们其实从没见过面，但有一次“交集”让我印象极深——我发过一条关于 Cookie Banners 的吐槽推文，那是我人生中传播最广的一条推文。那天中午我被 Cookie Banners 气疯了，随手发了一条，然后彻底炸了。第二天你转推并评论说：“这就是为什么人们不再浏览 Web，而是开始用 ChatGPT。” 所以我想直接问你：欧盟最近说要“取消 Cookie Banners”，你觉得这真的能改善什么吗？还是说——已经太迟了？ DHH：我认为 Cookie Banners 是 Web 体验变得糟糕的一个主要原因。它们几乎比早期那种弹窗广告还要糟糕——你知道的，“打地鼠”“打猴子”那种 2000 年初的弹窗。当年浏览器还能通过技术手段封杀弹窗，但 Cookie Banners 没有一个统一、有效的技术解决方案。我知道有插件能挡，但大多数人不会装。结果就是：Cookie Banners 成了互联网的一场瘟疫。 我是丹麦人，所以我觉得我有资格狠狠吐槽欧盟。Cookie Banners 最初的出发点是“高尚的”——限制数据收集、提高透明度。但这套东西在第一个 Cookie Banners 出现 5 分钟后，就已经被证明是失败的。可欧盟花了整整 15 年，才开始承认这个问题。现在他们说要“移除”Cookie Banners。但“移除”是什么意思？你以为这就能抹掉你对互联网造成的破坏吗？不可能。接下来30 年，仍然会有大量网站继续保留 Cookie Banners——因为删掉它比留着更麻烦，或者网站早就没人维护了。 这是一件非常悲哀的事。当然，我并不是说：如果没有 Cookie Banners，人们就不会去用 ChatGPT。 那不现实。但它确实在可测量的层面上伤害了 Web，让浏览体验变得远比必要的程度更糟。 一旦你已经在用户体验上制造了第一道伤口，后面再多来几刀，心理成本就低多了。Cookie Banners 把“底线”拉得太低了，以至于很多 Web 设计师会觉得：再多放点广告、再恶心一点，好像也没那么糟。 这就像“破窗理论”。 主持人：那在 Cookie Banners 把 Web 搞成这样之后，你觉得互联网浏览的未来会走向哪里？如果未来主要“浏览 Web 的不再是人类”，那这些问题还重要吗？ DHH：这是一个好问题。我觉得现在有很多聪明的人都在试图搞明白这件事，我们也在尝试各种不同的做法。某种意义上，这真的很像上世纪 90 年代中后期——当时我们在摸索互联网的第一个版本：这一切究竟会怎么运作？谁会掌握权力？谁会成为平台？谁又会成为把关者？所有这些问题，如今再次被抛回到空中，悬而未决。 不管我个人怎么看它最终会走向哪里，我都觉得这是一件令人兴奋的事情。互联网和计算技术，已经很久没有像现在这样让人感到兴奋了——上一次有这种感觉，还是在 2007 年。 那是 iPhone 刚刚问世的时候，我们迎来了一个全新的形态。随后经历了很长一段时间：好，一切都转向移动端了。而现在，我们又站在另一次巨大的转折点上——这一次，不只是“移动”不再以同样的方式重要了，它不再是你思考和构建产品时的那个主导视角。 与此同时，还有大量没有答案的问题。如果人类不再亲自阅读互联网内容，因此也不再阅读广告，那究竟是谁在为互联网写作？谁还会去生产那些美好的内容？当我们摆脱了 cookie 弹窗，重新拥有一个“干净体面”的门面，这件事真的还重要吗？ 如果这件事本身已经不再重要，如果人们不再想为互联网写作，那 AI 又将从哪里获取它所需要的信息？我觉得现在有太多悬而未决的问题，以至于没有任何人哪怕稍微知道，最终的解决方案会是什么样子。而这，恰恰是活在这个时代最令人振奋的地方。 我毫不怀疑，将来我们回头看今天这个时刻时，会说：“好吧，这里发生了一次决定性的变化。”而且，这种变化在当下的可感知程度，甚至比前两次都要更明显。 互联网的出现，花了五六年的时间才真正渗透进社会，对整个社会产生巨大影响。后来是手机，速度快了一些，但也没有快到哪里去——iPhone 本身也经历了好几代迭代，我们一开始甚至都没有 App Store，这些东西都是慢慢才出现的。 但 AI 不一样。 AI 的出现，在当下这一刻就已经非常明显。任何一个用过第一版 ChatGPT 的人，都会立刻意识到：哇，这完全是一个全新的东西，它将重写规则。 所以，在这三次巨大的技术变迁中——互联网的诞生、移动时代的到来，以及现在的 AI——这是第一次，我们在实时发生的过程中就清楚地知道：世界一定会变得完全不同，而我们却不知道最终会变成什么样。 因此，我觉得你能做的最好的事情，就是接受三点：第一，我们不知道答案；第二，这真的令人兴奋；第三，赶紧上车，狠狠干脆坐稳了，看看它会把我们带到哪里去。 因为还有另一种冲动，过去在互联网时代出现过，在移动时代也出现过：那就是一部分人会说，“我更喜欢以前的样子。我喜欢变革发生之前的一切。我不喜欢 AI。我不喜欢也许会被整个互联网重新中介化。我不喜欢这些东西。我们能不能把一切都倒回去？” 不，不能。你没有这种权力。你无法把这些东西倒回去。 你当然可以在个人层面选择：我不用生成式 AI，或者我不买任何包含 AI 方案的产品。但这种想法，本质上是一种“阿米什式”的思维方式——而在任何时代，这都只是非常小众的选择。 如果这就是你，如果这就是你想与世界互动的方式，那很好，祝你一切顺利。我们有时候确实需要一些“疯子”来提醒我们：事情也可以用完全不同的方式来做。但这，并不会改变历史前进的轨迹。 “这真的是一个无比令人兴奋的时代” 主持人：你的兴奋更多来自哪里？是因为规则被打乱、棋盘被掀翻？还是因为你真的想用 AI 做事？ DHH：首先也是最重要的一点，我热爱计算机。我喜欢看到计算机做出以前做不了的新事情。说实话，让我觉得非常惊讶的是：有这么多在科技行业工作的人，其实并不怎么喜欢计算机——甚至包括那些每天都要和计算机打交道、让计算机“跳舞”的程序员，也并不是所有人都真的喜欢计算机。 但我不一样。我爱计算机。我真的爱计算机本身，爱的是它作为一台机器的纯粹性。我并不是只把计算机当成一种“工具”，不是只想用它来完成某个目的。确实有一大类人，把计算机仅仅视为通往某个结果的手段。但不是这样，对我来说，这要更深得多——我就是单纯地热爱计算机这个东西本身，也热爱看到它去做全新的事情。 而现在发生的这件事，是计算机在我这一生中做过的最令人兴奋的新事情之一，至少可以和当年“计算机连上网络”这件事相提并论。 那时我们从 Commodore 64、Amiga 时代走过来，突然“砰”地一下就上网了，用小小的调制解调器拨号，连接世界各地的 BBS，听着它唱出那种刺耳却又美妙的声音——那同样是一次巨大的转变，也彻底改变了我和计算机之间的关系。 而现在，很可能是第二次这样规模的变化。 另一件让我感到兴奋的，是棋盘被彻底翻转了。尤其是我们已经形成了一些根深蒂固的格局。比如 Apple，我和那家公司有过不少摩擦。我非常期待看到 Apple 通过 App Store 以及整个移动生态所建立的那种“封闭控制”，被彻底掀翻，因为它也许将不再以同样的方式重要。 当然，我也并不天真到以为：只要棋盘一翻转，接下来就会迎来一个人人和谐共处的“涅槃世界”，一切都会变成开放平台，没有任何人占据主导地位。这显然不可能发生。不管最终的主导者叫 OpenAI、xAI、Google，还是别的什么名字，某种形式的集中和垄断，迟早都会出现。 但至少在现在，我们还处在“尚未整合”的阶段。有这么多公司同时在追逐前沿模型，却没有任何一家明显胜出。 就在五秒钟前，整个科技行业还准备给 Google 判死刑——“他们错过了浪潮”，“早期研究是他们做的，《Attention Is All You Need》那篇论文也是他们团队出的，但后来落后了整整九个月”，当时大家已经在谈论 Google 的衰落了。而现在，他们也许又重新回到了领先位置，至少在某些领域确实如此。 这种不确定性本身就让人兴奋——我们并不知道，最终谁会占据主导地位，甚至都不确定“主导地位”这种东西是否一定会出现。 这件事也很有意思。就在几周前，我还在推特上说，跑本地模型这件事有点“奇怪”。因为我之前试过一些本地模型，说不上什么时候，总之那时体验一般。但就在这周，我又开始重新跑本地模型，然后我心里想：“靠，我之前说的话，保质期也太短了吧。” 现实变化的速度已经快到：三个月前说的任何一句话，现在看起来都可能有点傻。 而且我真的被本地模型现在的水平震惊到了。它们当然还比不上最前沿的模型，但如果再往前看两年呢？有没有一种可能，根本不会出现一个“唯一的赢家”？赢家反而会是开放模型？最终的局面，会不会类似开源软件对后端软件世界造成的影响？ 过去我们是有绝对主导者的。我们有过 Sun，有过 IBM，在某种程度上也有过 Microsoft。但这些都已经不存在了。整个后端世界——从 Linux 到各种数据库，再到 Ruby、Rails，以及所有这些东西——几乎全都是开源的。你再也看不到那种一家独大的绝对统治。 而在另一边，在前端世界，尤其是移动端，我们却看到的是彻底的垄断：只有两个赢家，Google 和 Apple。他们对平台拥有完全的控制权，而且还在不断收紧螺丝。我们唯一的希望，似乎只剩下立法或监管，而说实话，我对这条路也已经越来越悲观了。 所以现在的局面真的很令人兴奋——它可能朝两个完全不同的方向发展。 我们很可能还是会走向某种形式的垄断，因为这是面向用户的界面层。而在历史上，我几乎想不起有哪个时代，这种层面没有被“征服”过。 但也有另一种可能：这些开放模型会好到一个程度，以至于“谁占据商业主导地位”这件事根本不重要，你甚至不需要那种商业上的统治。 这真的是一个无比令人兴奋的时代。 “我们的产品也试过 AI 功能，但最后都没上线” 主持人：这挺有意思的——你正好是在这个变动时期推出新产品。HEY 大概是五年前发布的，然后最近 Fizzy 也上线了。我们特别想知道：37signals 内部现在到底在发生什么？你们到底怎么用 AI？你们做 Fizzy 的时候，用没用 AI？用到什么程度？我很想听点“细节层面的现实”，AI 在 37signals 具体怎么落地、怎么被用起来的。 DHH：哦，用的，当然用。我们每一个开发者都在某种程度上使用 AI。我自己每天也在用 AI。但我也得先加一句前提：我虽然对我们即将进入的新现实非常兴奋，但我每天处理的仍然是“此时此刻真实存在的东西”。你必须学会在“ hype 的列车”和“现实的列车”之间保持平衡。 而在我的“现实列车”里，AI 没有在写 Fizzy（一个 Kanban 工具）。 AI 也没有从零写任何东西。 我确实用过 AI 做过各种“一发入魂”的实验——但它们通常都只停留在“一发入魂”。因为只要我进入真正的细节：要持续演进、要迭代、要打磨，我就会想：“嗯，这就是在浪费我的时间。到这个阶段，我自己写反而更快。” 当然，AI 在另一些方面确实能大幅加速。我们在做这些产品时，也在一定程度上使用 AI。但我们并没有大量用 AI 来写 Ruby 代码。如果用 AI 写 Ruby，通常也只是“机械式翻译”——比如：“这里有个我们知道已经存在的东西，你能把它用 Ruby 版本写出来吗？” 它能给出一个初稿，有时候会稍微帮点忙。 AI 更有价值的地方是在我们的一些 Go 代码上，因为那里面“样板代码”更多，收益更明显。但即便是 Ruby 和 Go 这两块，也谈不上“改变游戏规则”。 真正改变游戏规则的是：你想学习一个新 API你想理解一个新概念模型或者我们做实验，直接用 AI 去尝试构建“能真正带来价值”的 AI 功能在这些方面，收益更大。 但我们离那种——某些 CEO（比如 Anthropic 的 CEO 那种语气）说的——“再过五分钟我们就不需要程序员了”还差得远。我就想问一句：你们到底用的是什么模型？我用的是 Opus 4.5（或者现在的版本），但那种说法完全不符合现实——至少对于“持续演进”这类工作来说，是完全不成立的。 我仍然保持开放心态，我也能看到那种承诺。我记得互联网在 1994、1995 年那会儿是什么状态，我当然能做外推：我们也许真的会走到那一步。也许我们会到一个阶段：人类不再编写大多数代码。 但如果你看 Fizzy：95% 的代码，是人类亲手敲出来的。 主持人：有意思。真的？你们内部也这样认为？ DHH：你回头看 Fizzy 的整个开发历史，会更有意思。我们在 Fizzy 里做过一堆 AI 功能实验：我们试过做一个 AI 驱动的命令行，用来和卡片（cards）交互；我们也试过 AI 摘要，给一些内容自动做总结。但最后这两项我们都没有发布。 Basecamp 也是一样：我们实验过很多不同的 AI 功能，但没有一个能达到“明显更好、用户会一直爱用”的标准，所以都没进最终版本。 我仍然相信未来这会改变。只是我们现在还没到那个时刻。 我也见过其他地方做得更成熟的案例。比如我在 Shopify 董事会，Shopify 做的 Sidekick（他们的 AI agent）——用来帮助商家搭建店铺、优化店铺——真的很不可思议。那里面有一些非常具体、非常可触达的收益，我觉得几乎无可争辩。 我们仍然处在一个阶段：距离“AI 让一切始终更好、更快、更省心”那种明显的拐点，还差一点。 也正因为还没到那个拐点，所以才会出现一些反弹——我认为其中不少反弹甚至是合理的。因为很多人用了所谓“AI 功能”之后会觉得：“这玩意儿太烂了。”“不更好，也不更快，甚至很蠢。” 比如摘要。我们刚刚还提到 Apple。Apple 对新闻、短信之类的摘要，我真不知道有多少人真喜欢开着它。它在很多情况下都离谱地糟糕、离谱地错误。连 Apple 这种体量的公司都做不对，那你基本可以合理推测：很多别的公司也同样做不对。 不过我也想强调：最近我们确实找到了几个非常好的 AI 用例。其中一个是我们的安全漏洞赏金项目（通过 HackerOne 运行）。我们会收到海量的报告——某个研究员声称在我们的应用里发现了漏洞。我们必须处理这些报告，而现实的数学非常残酷。我们大概会收到……可能一个季度 300 份报告之类的数量。但真正“靠谱、有效、值得修”的——大概只有 3 份。 也就是说，真正有价值的比例大概只有1%。而这个 1% 非常重要，因为它们可能真的指出了一个严重问题，我们必须修。但为了抓住这 1%，你必须花巨大精力去验证剩下99%的垃圾——这对团队来说是巨大的麻烦、巨大的时间黑洞、巨大的烦躁来源。 AI 在这件事上简直太厉害了：它能在报告进来时就先处理一遍，给我们一个初步判断——“这到底是扯淡，还是不扯淡？”然后还会帮我们写回复邮件。 而写回复其实才是痛点的一半：当 99% 的提交都是彻头彻尾的狗屎，写这些狗屎的人还常常—— 根本不懂自己在说什么，却又特别理直气壮，还特别不耐烦，甚至还一副“你必须立刻给我 5000 美金赏金”的态度。 这时候让人类程序员保持冷静、不直接对他们开喷，是很难的。真的，你会很想直接骂人。 AI 就完全没这个负担。它特别乐意用一种非常冷静的语气写一大段回复：“为什么你这个东西不成立。”它帮我们省了大量时间。 主持人：有意思。所以 AI 是拿到报告之后，去看你们代码库，然后判断它到底对不对？ DHH：对。没错。就是这样。把这两件事结合起来。 主持人：听起来需要一点技巧：拿到安全报告，很多是垃圾，但到了某个层级，你确实得打开代码去确认“这到底是不是真的”。 DHH：以前要看 100 份报告，现在可能只要看 5 份——这就是真实的生产力提升。就算你最后要看 10 份、20 份，只要你能把原本 100 份的工作压缩到 20 份，这就是 AI 承诺的生产力收益。如果我们能把这种压缩能力用到业务的其他方面——那简直太好了。这也是为什么我们一直在尝试把 AI 用在一些具体环节上。 另一个我们断断续续尝试了好几年的方向是客服支持（support）。但 support 很微妙：如果你只能 90% 正确，那其实很糟糕。因为这意味着你会有 10% 的概率把事情说错——而且是对着客户说错。你如果给客户一个完全错误的答案，让客户体验很差，客户可能就直接流失了。 那这个客户的终生价值是多少？ 你以为 AI 带来的那点“节省成本”，可能瞬间就被一次流失抵消得干干净净。我们上一次认真测试让 AI “做完整客服链路”，大概是 18 个月前左右。效果不太行。但一切都在飞速变化。我知道 Intercom 有一个叫 Finn 的 AI agent，采用得很好，看起来我们也确实该再试一次。 而这又回到我最初的那种兴奋：一切变化太快了。 有些人会觉得这很让人迷失方向，我觉得这也是很多焦虑的来源。但如果你像我一样，只是单纯喜欢看计算机变得更强大——那现在真的就是一场大戏。坐在第一排，实时看它发生。 我们从“那个吃意大利面的人”——看起来像噩梦一样的生成图——走到了今天这种几乎不可区分的输出。接下来，我们很可能会在更多领域看到同样的跃迁。你得保持一种“敬畏感”和“惊奇感”。 如果你此刻身处这个行业，和计算机打交道——你的“惊奇感”就是你的安全绳。它能对冲焦虑，对冲不确定性，让这一切变得可承受。 当然，我们并不能消除不确定性和焦虑。比如：我的工作三个月后还存在吗？这种焦虑非常合理。但你可以用惊奇感来对冲它：“这些硅做的小东西也太聪明了吧。”AI 时代，为什么你发布的产品别人看不见？ 主持人：它们真的很神奇。这就引出了一个更大的问题：软件商业模式的未来到底会怎样？这确实很神奇，但也真的太不一样了。你能不能展开讲讲：创业公司会走向哪里？软件产品会走向哪里？软件工程师会走向哪里？未来到底会怎样？ DHH：有一点我现在非常确定：今天发布一个新产品，从“把它做出来”的角度看，是史上最容易的。AI 让构建更容易；工具史上最好；Ruby 和 Rails 也从未如此成熟。对所有人来说，这都很棒。结果就是：市场被海量新产品发布淹没了——永无止境的“上百万、上亿级别”的新发布。 这就是你现在要面对的现实。门槛被降低了。而我不确定所有人都会在“轮到自己发布时”还为门槛降低而兴奋——因为你一发布，可能就是一片寂静，连个回响都没有。我们刚发布 Fizzy，算是一次不错的发布，但它并没有像我们历史上某些发布那样“声量巨大”。 这当然不只是 AI 的原因，还有社交媒体算法的原因。以前，我在 X（Twitter）上有粉丝，他们就能看到我发的东西。但现在，你会发现：X 上正在发生 Facebook 在 2010 年左右发生过的那一幕——你有粉丝，但你触达不了他们，除非你付钱给平台“买触达”。 但现在甚至都不只是“付钱”这么简单。问题变成：我甚至都看不到我合伙人 Jason 的推文了。除非他发了一条“爆款（banger）”，爆到病毒式传播，否则他的内容就不会出现在我的 For You 页面里。一切被压缩成了“你能不能发出爆款”。 拥有大量粉丝这件事的价值，被严重稀释了。我在 X 上有五十多万粉丝——这在我发一些犀利观点、能引起传播时依然好用。但当我想发“右勾拳”（也就是营销、转化）的时候，它不再提供过去那种收益。当然，这种变化也不全是坏处。现在小账号也可能爆：就算你只有 10 个粉丝，只要你发了一条爆款，算法也可能把你推上去。算法选赢家和输家的方式，反而让那些没有花 20 年积累粉丝的人受益。但这真的好吗？我大概发了 7 万条推文——这真是离谱。但 18 年下来，这些投入几乎没有“可积累的剩余权益”（residual equity）。 我不确定这是不是我们长期想要的生态。但可以确定的是：对我们的营销方式、产品发布方式来说，这已经是一个全新的世界。 我们公司现在的阶段是：我们能承受“靠一靠、观望一下”，说一句“挺有意思”。但如果你还处在“必须打出名气”的阶段，你肯定会更焦虑。因为以前那套打法，已经不像过去那样奏效，你得发明新的东西。 事实上，这种认知直接影响了 Fizzy 的发布策略：我们承认——你不能再用老办法发布产品了。你手里的名单、你已有的受众，不可能再用“传统方式”被激活。你需要持续不断的“滴灌”：一滴、一滴、一滴。 如果我们希望 Fizzy 这个品牌能在用户心里留下印象，以至于当他们遇到我们要解决的问题时，会想起它、会去 fizzy.do，我们就必须设计一种策略，让我们能一直这样做下去。这也部分解释了为什么我们从一开始就把 Fizzy 开源。 把 Fizzy 从发布第一天就开源——对所有想学习“生产级 Ruby/Rails 应用如何构建”的人来说，这是一个巨大的礼物；同时，对我们来说，它也给了我们一个“更频繁谈论 Fizzy 的许可”。 现在社交平台上，纯商业化的转化号召（call-to-action）越来越推不动。以前它传播力也一般，但好歹还能“硬塞”一下——那就是所谓的“右勾拳”。现在右勾拳打不出去，你就得换一种卖法。我目前觉得最管用的策略，是把“给价值”和“求转化”合成一拳：轻击（jab）和右勾拳（right hook）不再分开打，而是同一条内容里同时完成。 比如我会发：“Fizzy 里有个很酷的小功能——可能是我们做的，也可能是社区做的，或者我只是想提醒你注意到它。”这条对开发者有用；与此同时，我也顺势把品牌名反复露出来：Fizzy、Fizzy、Fizzy……品牌就是靠重复进入脑子。 关键是：重复仍然有效，但必须绑着价值一起出现。光当“慷慨的好人”持续免费输出已经不够了——你得把输出和你正在做的产品强绑定。这就是我们现在的打法。当然规则也可能随时被改写，但就此刻来看，这就是现实的游戏规则。 主持人：你说“现在你只要把东西做出来就行”，这句话听起来很有趣，因为我觉得你以前不会这么说。你从一开始就很重视营销——从最早的 Rails demo、到各种“挑衅”、到你如何推销愿景……你一直都在想怎么卖、怎么讲故事。但现在市场被淹没了，好像营销反而变得更重要。 更巧的是，我们内部也在聊类似的事。我们在做 AMP（我们在做一个 coding agent），我们内部一直说：现在外界没有太多“强烈的 OTE”（那种外溢式的注意力/势能）。我们想做的是：用一个故事把人“拉着走”——告诉他们我们在这个动荡的时代学到了什么，让他们产生一种感觉：“如果你跟我们走，门是开着的；如果你跟我们走，我们会分享我们学到的东西。”这不是那种“社交媒体上再来 10 个小贴士”的套路，而更像是：“我们一起干这件事。” 而你刚刚说的，正好对应了很多人最近在讲的： “爆款发布（big launch）这套已经不灵了。”Product Hunt 死了。Hacker News 的 launch 也…… 而且我认识 Fizzy，就是因为 Jason 一直在 X 上做这些小 screencast：“现在进展到哪了”、“这里出了一些 X 问题”、“这里哪里又崩了”。我会偶尔刷到它们，可能是 Grok 或者算法觉得我会喜欢。但我的感觉是：我被“拉着走”了——像在跟着你们一起把产品做出来。所以我后来才注意到：噢，原来它上线了。 DHH：你说得对，这确实是我们这个时代发生的巨大变化之一。我记得我们在 2006 年写《Getting Real》（那本书）时，我们谈过“爆款发布（blockbuster launch）”这套模型：先放 teaser（预告），再放 trailer（预热视频），最后来一个 blockbuster launch（大爆发）。 这套模型已经死了。爆款不再发生。因为我们已经没有共享文化了。没有共享的事件。我们只有每个人各自的个性化信息流——正如你说的，算法之神决定：今天给你投喂哪一小块“刚好合适”的东西。所以，一方面，你必须“灌满渠道”（flood the channel）。 另一方面，也有个有意思的反面：以前我会更克制，比如提醒自己别发太多推。有时候我会突然进入那种“多条意识流同时开喷”的状态，但在过去你会想：“哎，我今天已经发第七条了，会不会太多？” 现在这种限制不存在了。你一天发 100 条都没关系。因为你不会“淹没”任何人的 For You 页面——算法会替你处理。而你发得越多，你就越有机会让一些小种子落地、生长、发芽。你还需要更长的周期。 爆款发布以前的核心逻辑是：“就在这一天，我们发布，然后所有人都在这一天关注。”现在不会了。大家不会在同一天关注同一件事。但随着时间推移，如果你把“发布”理解为：一整个季度、或者一年、甚至某些情况下是一整个十年——你依然可以做“分步骤的搭建”，依然能起作用。因为营销的底层真价值仍然成立：口碑传播、故事激活、好产品、好钩子——这些依然有效。 只是，它变得慢得多。你不会再看到那种巨大峰值，然后被“发布日的高潮”爽到。某种意义上，现在的发布没有那个“超级尖峰”了。当然，很多人本来也从来没有过“超级尖峰”，因为大多数发布都什么也不会发生——失败一直是常态。但我现在更强烈地觉得：你越来越难“工程化制造一个爆款”。 这个夏天我又学到（或者说被提醒）了一点。我在做一个项目叫Omarchy——一个 Linux 发行版。我做得很开心。当我推进它时，我从营销角度体会到：如果你不断分享项目进展、再配合一个疯狂的发布节奏，价值非常大。 我记得第一个月我做了大概 40 次发布？简直离谱。节奏快得惊人，整个过程一直都充满了不确定性，所以特别刺激、特别带劲。这让我可以连续三个月“轰炸”所有人的信息流。更有意思的是：人们明明意识到自己在被轰炸，却仍然无力抵抗。我收到过无数条推文，大意都是：“行行行，我第 17 次听说 Omarchy 了，我服了，我试一下。”“我投降，好吧，我装。”这又回到了营销最本质的东西：重复。 有一个老的经验法则（我也不知道现在是不是过时了）：你需要听到一个品牌七次，它才会在你遇到问题时被激活——你才会想起它能解决什么。所以我当时就是在努力让尽可能多的人“听到七次”。同时我也在做 Jason 说的那个：enthusiasm transfer（热情迁移）——把创作者的兴奋感转移给别人。这一直是营销的一部分，但现在比以前更重要，因为营销越来越“人格化”。 我们还发现：社交平台从来就不怎么喜欢公司账号，但现在它们几乎把公司账号都“幽灵化”了。我们公司账号发什么都没用：从 37signals 发，没人理；从 Basecamp 发，也没人理。一片寂静。然后我看到一些“巨型媒体账号”——几百万粉丝那种——表现也一样惨。这就是算法：它现在真的讨厌品牌账号。除非你是那种“神级品牌账号”——有账号运营团队，能自己成为内容源。 但另一部分也让我们意识到：这游戏即便对我们而言仍然很残酷——而且很耗人。这种耗人让我想起我听一些 YouTuber 讲过的东西：如果你是 influencer（网红）、content creator（内容创作者）——这俩词简直是现代词汇里最让我厌恶的词之一——你就会被迫持续生产内容。 你维持曝光的方式只有一个：不停输出、不停输出、不停输出（chop chop chop）。以前还有一种“喘息”：你做完 teaser、trailer、爆款发布，然后你还能休息五分钟。现在不行了。那种节奏不存在了。所以一切的速度被推到一个夸张的程度。说实话，我很庆幸我现在不需要“去攒人生的第一桶金”了（笑）。 主持人：我们最近也在高频发东西：过去 10 天我们写了 8 篇 release post。这和你做 Omarchy 的方式很像：你需要重复。但那种 5 年前的“空洞重复”已经不行了——比如：“两天前我们大发布，记得吗？”“一周前我们大发布，记得吗？”这种完全没效果。你必须一直有新内容，否则算法不推。节奏太夸张了。 而在我们这个做 AI agents 的领域，你还会被大模型厂商不断“催更”——他们两天发一个新模型，用户两天后就来问：“你怎么还不切？怎么还没上新？”所以现在疯狂的事情特别多。 我的问题是：你写过《It Doesn’t Have to Be Crazy at Work》（工作不必这么疯狂），但现实已经如此——这在实践中到底怎么改变软件开发？你一直是小团队、小公司路线的拥护者。 但现在如果你想让产品成功，你好像必须把一天切成两半：一半写代码，一半发推、做内容、做传播、分享进展。你觉得这会怎么影响未来的软件开发者/软件公司？营销和软件是在融合吗？ DHH：我一直都说：这些东西本来就是一回事。“Marketing is everything（营销就是一切）”——这是《Rework》里的一章。而“everything”真的就是一切：软件、发布、客服、那些乱七八糟的推文、写作、播客……全都是。我们这么干已经 25 年了。但我同意：现在的节奏、算法的胃口，确实到了一个“无底洞”的程度，这种感觉以前没有这么强烈。不过我也觉得：这可能就是竞争加剧的样子。 当年我们做 Basecamp 的时候，行业比现在小太多了。那时做 Web 产品的团队少得可怜，以至于我们能关注到每一次发布。后来进入 Product Hunt 时代，你至少还能“一天看一个新东西”。现在结束了。 甚至 OpenAI 发一个新模型——那可能烧了 4 亿美元——它也只能获得几个小时的峰值关注与兴奋。 所以，它在很多方面变得更难了。可另一方面，基本面依然没变，你得小心别被这些压力带着跑偏。做有趣的东西、做值得讲的东西——这带来的杠杆还在。 你要“脱颖而出”的难度变大了，因为参与者更多了。 但只要你真的突出，注意力仍然在那里。注意力并没有从系统里被抽走。甚至可以说：注意力比以往更多，因为参与系统的人更多了。 这有点像 Spotify。你总听音乐人抱怨 Spotify 付得太少，但你再看数据：音乐产业的规模依然很大，甚至更大，而且在很多情况下，更多收入是直接流向音乐人（因为他们不再必须签那些苛刻的发行合约）。 所以一部分现实就是：我们在抱怨“事情太美好了”，但又没有人真的开心。 有个段子讲得很好：“一切都很棒，但没人开心。”我觉得这确实说中了某种人性。事情确实很棒：越来越多人能更快地做出东西。而这自然会带来更多竞争。资本家最讨厌的一件事是什么？是竞争。这就是那个系统最大的讽刺。我们都在拼命挖“护城河（moat）”。但护城河是用来挡谁的？不是挡“龙”（Not dragons）——是挡竞争对手。 竞争对手，这才是护城河真正要挡的东西。这个隐喻本身也很有趣：你会想，那它把谁“圈”在里面？客户？你在护城河里放鳄鱼，让客户别游出来？这个隐喻挺自利，也挺资本家叙事的。但无论如何，我玩这个游戏，也乐在其中。同时我也很高兴——现在我比过去任何时候都更清楚地知道：我对“什么真正有效、什么无效”的确定性变少了。 一直以来，很多东西本就是谜。比如我们 2004 年发布 Basecamp，它一路成了现象级成功，今天仍然成功。 我经常会想：为什么？为什么偏偏是 Basecamp？在我 25 年的职业生涯里，我做过很多东西，但没有任何一个产品层面的命中，能像 Basecamp 这么“正中靶心”。我至今也不完全明白原因。尤其是现在，Basecamp 所在的领域竞争者多得多。但每周仍然有成千上万的人注册一个新的 Basecamp 账号。每周我都会想：这怎么可能？怎么会每周都有几千几千人来注册？ 这一直是个巨大的谜。 我觉得这种谦逊非常重要——无论你在做产品、还是在做营销，你都要记住：你不可能了解一切。你不可能确切知道什么有效、什么无效。你能做的，是去尝试很多东西，然后得到一些迹象、一些推力、一些暗示：市场想要什么、算法想要什么、客户想要什么。 但你不可能制定一套“主战略”，并指望它具备可重复的复刻性。即便是在一个高度“爆款驱动”的行业——比如我刚刚提到的音乐行业——也没人真正搞明白。的确，有些人比别人更擅长做出爆款，但也没有谁掌握一套公式：“照着这套流程，我们就能稳定生产爆款。”商业也是一样。 只是现在曲调又变了。你可以因此沮丧：“我以前那套把戏不灵了。”也可以因此兴奋：“什么？那我更迫不及待想学习——现在到底什么才有效！”我也接受一个现实：我不可能永远拥有过去拥有的一切。世界不是这样运作的。 “独立开发者”之梦没变：核心还是“一个人也能干” 主持人：我感觉我们好像回到了 2004 年。我记得你发布 Basecamp 的时候，你在 YC 还是哪里做过一个演讲，你当时大意是说：如果你有个想法，然后能找到 1000 个客户，每人每月付你 25 美元，你的人生就彻底不一样了。那次演讲就是我决定辞掉 Web 开发工作、去做 Dropsend 的起点——也开启了我整个职业生涯。 我觉得我们又回到了那种状态：现在你真的可以有一个想法，甚至可能是“一人团队”。所以，我们现在是不是就处在这个阶段？还是说，所有 indie hackers（独立开发者）最终都会被“吃掉”？这难道不是好事吗？ DHH：我也觉得这是好事。而且这里还有个讽刺点：我 20 多年来一直在讲——开发者生产力真的重要。 这就是 Ruby 和 Rails 的核心前提：你不需要一个八人团队，你一个人也能做出来。Rails 从一开始就试图成为“单人开发者的框架”，而且我认为它在这件事上比几乎所有框架都做得更成功。 而我们今天对 AI 兴奋的原因也一样：我们对小团队能获得的杠杆感到兴奋，因为 AI 能做很多事。 有一个根本事实没变：当你降低实验成本、降低构建一个“值得做的东西”的生产力成本时，你就会有更多“射门次数”（shots on goal）。 Ruby + Rails 能做到这一点；AI 也能做到；甚至更好的是：AI + Ruby on Rails 一起做到。但我不确定游戏的本质在这点上发生了根本变化，也许只是变得对更多人可及了。 我觉得这大概率是好事——不，只能说：这就是好事。我们应该从“对人类整体有什么分类级别的好处”来理解：对全人类而言，难道不是更好——我们有更多实验吗？即便最终“命中并变成可持续商业”的人，可能比例更低（我甚至不确定这是否属实，但先这么假设）。 而作为一个文明整体，我们最终仍然会在更多类别、更多细分领域里，更快地获得更好的软件。问题的一部分在于：无论是 Web 开发圈，还是独立开发者（indie hacker）圈，很多讨论都过于短视地集中在那些我们一直反复折腾的“通用大类”上。 比如待办事项应用。好吧，我职业生涯里大概已经做过七个了，而全球可能已经有二十亿个同类产品。最后真正成功的，可能也就那么几个，剩下 99% 都失败了。 但你知道吗？你有没有试过给美发沙龙做软件？他们可没有一万种选择。有时候，他们甚至几乎没有任何选择，除了那些“狗屎一样”的系统。那种三十年前做出来的烂软件，出自一些对“好软件”毫不在意的人之手。所以，如果你愿意跳出这些吸引了绝大多数人的大而泛的领域，其实机会依然多得很。 颇具讽刺意味的是，我自己长期以来恰恰以“不去碰这些方向”为傲——只解决我自己的问题。因为我觉得那样更简单，而且也确实如此：当你解决的是自己的问题时，你立刻就能判断你做出来的软件到底好不好。 这并不意味着它一定会成功，但至少你有了第一道过滤器。如果让我去给美发沙龙做软件，我其实并不知道什么是好、什么是坏，我得不停地去问别人：“你们怎么看？你们给我什么反馈？”老实说，我不确定自己是否适合为了正在构建的软件，去进行这么多和他人的互动。 但我认为，对那些愿意这么做的创业者来说，机会是非常多的，而这其实也是大多数人。只要我们稍微把视野放宽一点，不要总是说：“天啊，现在再做一个新的待办事项应用太难了。”因为这个领域在过去三十年里，已经被来来回回地“薅”了大概五十亿次。 但你往外看——就只要离开它五米远——到处都是一大片未被开发的绿地。真的，到处都是。 DHH 说 95% 代码是手写的，但他又天天用 AI 主持人：David 你说 Fizzy 95% 的代码还是手写的，对吧？你每天都在用 AI。但对我来说，今年正好相反：我现在大概 90% 的代码都是 AI 写的。所以我的疑问是：如果你说你不怎么用 AI 写代码、或者 AI 不替你写代码——那生产力提升到底从哪里来？尤其对一家小公司来说，比如给美发店做软件，它不需要庞大的客服团队，也不需要很多外围部门，核心就是把软件做出来、交付出来。所以你觉得 AI 让软件开发更快的关键在哪里？ DHH：我说说我自己的体验——从这波 AI 开始我就一直在用。 我的生产力提升，主要来自：它让我更强、更聪明、更快——更快上手新 API、新技术更快理解新概念（我会让 AI 解释给我听）更快找到“为什么这个 bug 会这样”的正确线索 比如 Omarchy 这个项目，如果没有 AI，它就不会存在。我不会有耐心去 Linux 论坛里翻半天，去解读那些晦涩的错误信息到底是什么意思。这对我来说不可能。 AI 带来的巨大提升，是给了我一个地方，把错误信息贴进去，然后得到比那种居高临下、还过时三年的 Stack Overflow 回答更好的线索。 收益巨大。真的巨大。 还有我需要读某个东西时、学习某个东西时，它也很有帮助。举个快例子：我们最近把 Rails 的 CSRF 防护机制改了——从以前“把 token 放进 cookie”的方式，改成使用现代浏览器的新特性：通过一个 header 来做。 我可以直接问 AI：“那个 header 是什么？”“什么时候开始支持的？”“具体有哪些细节？”这些答案我当然也能手动查：去 caniuse.com、看历史、查 RFC……全都能做。但 AI 能把这些东西一盘端上来，整合在一起，省事又快。 “AI 只是让我变聪明了” 我能更快学到更多东西。而这正是我真正喜欢的地方：不是让 AI 替我做事，而是用 AI让我更聪明。 当然，这种模式未来未必会成为主流。 就像你说的，你已经让 AI 写很多代码，甚至多数代码。我完全准备好在某个时点，我也会进入那种状态。 但就现在而言，我仍然在意代码的样子。我在意它的美感。我在意打磨、推敲、润色。 这可能是一种“奢侈”，有点像现代的马鞍匠：他会在意字母压得是否刚好、针脚是否完美。你可以说：“但你已经不是交通运输的主力生产体系了。”我会说：那又怎样？只要我还享受，我就会继续做我手写代码的“马鞍”。 而且我也意识到：这种模式目前仍然是有竞争力的。 在 37signals，我们并不觉得自己在产出能力、发布能力、改进能力上落后。因此我对一些说法保持怀疑：“AI 已经强到可以把标准 SaaS 公司的一半程序员裁掉，还能跑得更快。”我没看到。 我当年也用同一套“根本测试”来审视云计算：“我们能不能用更少的人、花更少的钱，做更多的事？”我们几年前退出云，就是因为这个测试没有通过。而且我也不太听说这个测试在别处通过过。云计算并没有让你把运维团队砍半、把基础设施预算砍半。很多时候恰恰相反：上云之后团队规模翻倍，账单翻四倍。 主持人：你们切换之后是不是省了类似每月一百万美元？很夸张的数字？ DHH：我们现在大概是一年省200 万美元。我们云预算峰值大概是 340 万美元，现在的持续成本在 100 多万美元左右。所以在成本上，节省非常巨大。 这和 AI 有一些相似之处——不完全相同，但有相似之处：我觉得现在很多人在用 AI，脑子里觉得自己“好高产”，但他们其实交付更少、做出来的东西更少，甚至理解得更少。 “Vibe Coding”的风险：能力会从指尖流走DHH：AI 还有另一个因素：当我尝试“氛围式写代码”（vibe coding）的时候——尤其在一个我还没完全内化的新领域——我能明显感觉到我的能力在从指尖滴走。 我刚开始做 Omarchy 时，写了很多 bash。我以前从没系统写过大量 bash，最多就是命令行里用用。然后我发现自己一次又一次问 AI：“某个 if 条件到底怎么写？” 这时你就会想：“为什么我没有内化这件事？我没内化，是因为我把它外包给 AI 了。”那这样更好吗？我现在更划算了吗？还是说，我跟当年那些老师一样天真：他们以为有了计算器，学生就不需要背乘法表了？不对。如果你不能迅速在脑子里算出 7×7，你真的会把自己变成傻子。 主持人：那你有没有形成一种直觉：该在哪里划线？你不可能知道一切，对吧？你也会把你不会的事交给信任的同事去做，你不会因为让同事设计某个东西就觉得“能力在流失”。你能接受：“这事我不需要会 / 我不想会”。那在 2025 这样疯狂的一年里，你有没有更清晰的边界：哪些你想自己掌握、哪些你可以忽略？比如 bash。为了推进 Omarchy，你觉得 bash 该学到什么程度？又有哪些可以不学？ DHH：我觉得我得会几乎全部，除了怎么在 bash 里搞数组（笑）。因为 bash 里数组那玩意儿复杂得离谱，简直反人类。但我其实认为：人类大脑是个很惊人的器官，它不会像 LLM 那样“容量到顶就装不下”。我们用得越多，记忆和能力的“配额”会增长。 所以我真正担心的趋势是：随着时间推移，我知道得更少、我变得更不胜任。我需要一条向上增长的移动平均线。 我不需要把所有领域都吞进去——我不需要什么都懂。但一年结束时，我应该在更多领域懂得更多。如果我不在这种上升轨道上，我会无聊。我无聊就会没动力。没动力我就什么也不干。这也是 AI 讨论的一部分：我们得想清楚，我们真正享受这套方程式里的哪一部分。 我个人不享受当项目经理。我会做——而且不止偶尔——因为我想要“组织一群人”能产出的结果。 但当我看 AI 这件事时，我不想当一群 AI agent 的项目经理。那不是我想要的状态。 我喜欢写代码。而至少在此时此刻，这是一个仍然有竞争力的选择。 当然，这可能三个月后就变了；下周就变了；随时都可能变。但 AI 公司那些领袖已经预言“再过五分钟就结束了”预言了很久了——现在也没结束。 你看 AI 公司自己，它们也还在招聘大量程序员。 我们并没有到 AGI，没有到那种“人类写代码的时代死了”的程度。 这并不否认你说的：有些程序员已经觉得自己大多数代码都让 AI 写了。但至少在市场上——按我看到的情况——还没有出现那种“压倒性差距”，就像：一个公司用马车送啤酒，另一个公司用卡车送啤酒。那种经济差距会非常快把前者淘汰。我还没在 AI 身上看到这种情况。也许数据有滞后；也许已经发生了——我仍然怀疑。 即便我在长期上是极度“AI 乐观派”，但就当下，我没看到。 有时神得离谱，有时烂得没法维护 DHH：而且原因之一是：我每天都在“盯”着它。我一直在问 AI：你能给我写这段代码吗？它会写。然后我会想：“不，我不喜欢这个。”“我甚至不想维护它。”“它做得还不如大多数初级程序员会被要求做到的水平。” 但偶尔，它也会给出另一种答案：我问它一个东西，它拼出来的结果让我震惊：“它怎么知道的？它怎么能把这些全部串起来？”那真的很惊人。 所以我感觉它像一个闪烁的灯泡：你在完全黑暗里，它突然一闪——你觉得“我什么都看见了”。两秒后，啪，又全黑。如果你能让这个灯泡稳定下来、一直亮着——那对人类当然是巨大的福音。 顺便说一句，我很喜欢美国的一点就是：美国把这个“闪烁灯泡”当成一种信仰——相信我们能把它变可靠，能到 AGI。现在大家就是一场巨大的押注：押注这一定会发生。即便我这么 AI 乐观，我仍然会对这种规模的“集体确信”感到惊叹：一个经济体一起说： “不管花多少代价，100 万亿、1000 万亿，我不在乎，我们一定能到那里。”我会想：这也许就是为什么它会成为“第一名”。 主持人：确实是个令人兴奋的时代。就像你说的——能活在此时此刻本身就是一种奇迹。我们也差不多到一小时的时间上限了。今天能和你重新连上线真的很开心，感谢你抽时间来。你现在也在忙 Fizzy。要不你简单跟大家说说：Fizzy 是什么？在哪能了解更多？然后我们就收尾。 DHH：当然。Fizzy 在fizzy.do。它是对 Kanban（看板）的一个全新诠释。这里还有个小故事：Jason 特别擅长解释“为什么值得回头重新解决一个问题”。 Kanban 这个概念来自 50 年代，是丰田为了管理生产线提出来的。后来我们把它做成了软件。第一代软件化的版本大概是 2000 年初。再后来 Trello 出现，把这个领域彻底带火、带爆。但我们还是回到这个领域，说：“你知道吗？我觉得我们还能做一个更好、更舒服的版本。” 很多人很难理解软件这件事：明明一个问题领域已经有很多玩家了，为什么你还要进去？原因可能只是：你想做得更好、更有趣、更轻量、更丰富多彩、更令人愉悦、功能更少——这些带着“爱”的细节，我们都烘焙进了 Fizzy。而且我们把它定价得很便宜：1000 张卡片免费，之后是 每月 20 美元。同时我们也把整个代码库开源了：如果你想自托管（self-host），你可以免费用。服务器我们不替你付，你自己折腾就行。你也可以贡献代码，也可以从中学习。 做 Fizzy 是一件很快乐的事，而且它也像一个实验室。我们现在正在做 Basecamp 5。我们在 Fizzy 上尝试了很多新技术——不管是编程层面还是产品层面——我们会把最好的想法带回 Basecamp 5。如果你关心我对这些话题（或任何话题）的观点，你可以去 dhh.dk，我的东西都在那。 主持人：太棒了。很高兴你来做客，也迫不及待想看未来会发生什么。感谢你的时间，我们下期再见。 参考链接：https://www.youtube.com/watch?v=uWqno4HM4xAhttps://www.reddit.com/r/ClaudeCode/comments/1qhiicv/the_creator_of_nodejs_says_the_era_of_writing/
使用 deno 每天定时重部署 Render 项目（CLI Proxy API）
CLI Proxy API 的核动力驴一天 3 个小版本，每天打开电脑都是打开 render 更新部署。于是搓了这样一个用 Deno Deploy 的 cron 触发 Render deploy hook 的小工具每天凌晨 4 点自动更新 render 部署的 cpa（你也可以用来更新其他 render 项目）去这里复制一下 Deploy Hook 就好了，改一下代码里面占位的 RENDER_HOOK_URL非常简单的小工具/** * Project: render-auto-deploy (sanitized, hardcoded URL version) * Purpose: 用 Deno Deploy Cron 定时触发 Render Deploy Hook（自动 redeploy） * * ✅ 你需要改的地方（部署时只改这两处）： * 1) 把 RENDER_HOOK_URL 改成你自己的 Render Deploy Hook URL（包含 service id + key） * 2) 如果想换时间，把 CRON_SCHEDULE 改成你要的 cron（注意：Deno.cron 用 UTC） * * ✅ 验证方法： * - 打开 "/"：看到 running + 当前 UTC 时间 + schedule * - 打开 "/trigger"：立刻触发一次 Render 部署（返回 deploy.id） * - 去 Render Events：会出现 “Triggered via Deploy Hook” * * 🔥 时区提醒： * - Deno.cron 的 cron 表达式按 UTC 解释 * - 例：UTC 20:00 = 北京时间次日 04:00 */ // ======================【部署时改这里 1】====================== // Render Deploy Hook（占位符，换成你自己的） // 形如：https://api.render.com/deploy/<YOUR_SERVICE_ID>?key=<YOUR_DEPLOY_KEY> const RENDER_HOOK_URL = "https://api.render.com/deploy/<YOUR_SERVICE_ID>?key=<YOUR_DEPLOY_KEY>"; // ======================【部署时改这里 2】====================== // Cron 表达式按 UTC 解释： // - "0 20 * * *" => 每天 UTC 20:00（北京时间次日 04:00） // - 测试用： "*/1 * * * *" => 每分钟触发一次（用来验证 cron 是否生效） const CRON_SCHEDULE = "0 20 * * *"; function nowIso() { return new Date().toISOString(); } async function triggerRenderDeploy(source: "cron" | "http") { const runId = crypto.randomUUID(); console.log(`[${nowIso()}] [${source}] runId=${runId} ⏰ trigger start`); // 超时：避免网络卡住导致任务悬挂 const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 30_000); try { const resp = await fetch(RENDER_HOOK_URL, { method: "POST", // Render deploy hook 通常 GET/POST 都可，这里用 POST signal: controller.signal, headers: { "user-agent": "render-auto-deploy/deno" }, }); const text = await resp.text().catch(() => ""); if (!resp.ok) { // 抛错 => 让 cron 的 backoffSchedule 生效，自动重试 throw new Error( `Render hook failed: ${resp.status} ${resp.statusText} body=${text.slice(0, 500)}`, ); } console.log( `[${nowIso()}] [${source}] runId=${runId} ✅ trigger ok body=${text.slice(0, 500)}`, ); return { ok: true, runId, status: resp.status, body: text }; } finally { clearTimeout(timeout); } } // ====== 定时任务（必须在模块顶层定义，Deno Deploy 才会识别为 Cron）====== Deno.cron( "Daily Render Auto Deploy", CRON_SCHEDULE, // 失败重试节奏（毫秒）：1s, 5s, 10s { backoffSchedule: [1000, 5000, 10000] }, async () => { try { await triggerRenderDeploy("cron"); } catch (e) { console.error(`[${nowIso()}] [cron] ❌`, e); throw e; // 继续抛出以触发重试 } }, ); // ====== Web：健康检查 + 手动触发（方便部署后立即验证）====== Deno.serve(async (req) => { const url = new URL(req.url); // 健康检查：确认服务活着 if (url.pathname === "/") { return new Response( `Auto-Deploy is running.\nUTC now: ${nowIso()}\nSchedule(UTC): ${CRON_SCHEDULE}\n`, { headers: { "content-type": "text/plain; charset=utf-8" } }, ); } // 手动触发：GET /trigger // 部署完立刻访问一次，看返回里有没有 deploy.id if (url.pathname === "/trigger") { try { const result = await triggerRenderDeploy("http"); return new Response(JSON.stringify(result, null, 2), { headers: { "content-type": "application/json; charset=utf-8" }, }); } catch (e) { const msg = e instanceof Error ? e.message : String(e); return new Response(JSON.stringify({ ok: false, error: msg }, null, 2), { status: 500, headers: { "content-type": "application/json; charset=utf-8" }, }); } } return new Response("Not found", { status: 404 }); }); 📌 转载信息来源： https://linux.do/t/topic/1488224原作者： sssun转载时间： 2026/1/20 10:04:34
Deno 高危漏洞可导致密钥泄露（CVE-2026-22863）与代码执行（CVE-2026-22864）
以 “默认安全” 架构闻名的现代 JavaScript/TypeScript 运行时 Deno 近期曝出两个重大安全漏洞。这两个漏洞分别影响运行时的加密兼容性层和 Windows 平台的命令执行机制，可能导致敏感服务器密钥泄露，并允许攻击者执行任意代码。 CVE-2026-22863：高危密钥泄露漏洞（CVSS 9.2） 两个漏洞中最严重的是 CVE-2026-22863，CVSS 评分 9.2。该漏洞存在于 Deno 的 node:crypto 兼容层 —— 一个用于让 Deno 运行 Node.js 代码的模块。 根据安全公告，node:crypto 的实现没有正确终结（finalize）加密器（cipher）。在标准的加密流程中，final() 方法应结束加密过程并清理内部状态。然而在受影响的 Deno 版本中，该方法没有真正关闭 cipher，导致加密器处于 “可无限加密” 的异常状态。 这一缺陷的影响极为严重。报告指出，这种状态管理错误 “可能导致攻击者进行暴力破解，或发起更高级的攻击以窃取服务器密钥”。分析中提供的 PoC 显示，调用 cipher.final() 后返回的是一个仍处于激活状态的 Cipheriv 对象，其内部缓冲状态仍然可被访问，而非预期的已关闭 CipherBase。 CVE-2026-22864：Windows 命令执行绕过（RCE） 第二个漏洞 CVE-2026-22864 影响 Deno 在 Windows 上启动子进程的能力。研究人员发现，这是一个 “对已修补漏洞的绕过”，涉及在执行批处理文件时使用 Deno.Command API。 Deno 原本包含防止 shell 注入的安全机制。当用户尝试直接运行 .bat 文件时，Deno 会抛出 PermissionDenied 错误，并提示开发者 “使用 shell 执行 bat 或 cmd 文件”，以确保参数被安全处理。 然而研究人员找到了绕过方法：通过修改文件扩展名的大小写（例如使用 .BAT）或操控传入命令的参数，攻击者可以绕过 Deno 的安全限制。报告中包含的 PoC 截图显示，一个脚本通过在批处理执行上下文里注入参数 ["&calc.exe"]，成功执行了 Windows 计算器（calc.exe），从而在目标机器上实现任意代码执行。 建议与修复 官方强烈建议用户立即升级到 Deno v2.6.0 或更高版本以修复这两个漏洞。
一个 cors 跨域网关代码生成器，支持 cloudflare 和 deno 数据，可以在线调试
主要用来解决纯前端发起 api 请求时的 cors 跨域问题，直接透传给目标 api cors-gw-generator.252035.xyz CORS Gateway Generator 支持在线调试，为了防止大串 base64 干扰，Body 里会显示高亮占位符，真实请求时自动替换为图片 base64📌 转载信息来源： https://linux.do/t/topic/1427560原作者： fish2018转载时间： 2026/1/11 08:37:04
Toolbaz 2api Python 版（仅非流）支持：Gemini 2.5 Pro、GPT-5、Claude Sonnet 4 开发调优，Lv1直接运行就可以对话，注意不支持流式传输！ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Toolbaz.com AI Writer API 对话脚本 支持多轮对话和流式响应 """import json import base64 import time import random import string import reques…在佬的基础上改了个 deno 版的加了自动获取模型和去除末尾模型标记单 ip 每天限制 150 次好像每分钟 5 次📌 转载信息来源： https://linux.do/t/topic/1420271原作者： zzzx9转载时间： 2026/1/8 17:39:20