使用 GenAI 构建和运送 joinstems.com
2024 年 9 月,我们发布了 joinstems.com 测试版,这是一个音乐爱好者和制作人可以访问知名曲目官方主干并分享混音的平台。从概念到发布只用了不到 5 个月的时间,团队只有 1.5 名开发人员(好吧,说实话,更像是 1.2 名),在头几周内就覆盖了 1 万名用户并生成了近 1000 首混音。
这个项目之所以引人入胜,是因为它与 GenAI 开发工具的快速发展同步。我从 5 月份开始使用 GitHub Copilot 进行代码补全,尝试使用 Supermaven,最终在 Claude 和 Cursor 中找到了适合自己的节奏。在整个过程中,我目睹了 GenAI 从一个简单的代码补全工具转变为一个更像是协作开发伙伴的东西。
尽管互联网上充斥着无数的 AI 演示和教程,但我发现使用 GenAI 推出实际产品的实践案例却很少。在推出各种产品十年之后,我想分享建立 joinstems.com 的真正见解 - 包括成功和“好吧,这行不通”的时刻。让我们深入了解哪些方法真正有效,哪些方法无效,以及在此过程中令我感到惊讶的事情。
堆栈简要描述
首先,我们来谈谈堆栈。我们以 T3 为基础 - 这是一个现代 TypeScript 堆栈,我很了解它,并且因为它的类型安全性和出色的 DX 而值得信赖。以下是我们正在运行的内容:
事实证明,这个堆栈是 AI 辅助开发的理想基础,尽管并不总是像我预期的那样。类型安全尤其重要 - 它有助于在偶尔出现的 AI 幻觉成为生产问题之前将其捕获。
但真正的魔力在于这些工具如何补充 GenAI 开发。Cursor 的代码生成与 Prisma 的模式优先方法和 react-admin 的模式相结合,创建了一个令人惊讶的强大工作流程。让我向您展示 GenAI 为我们带来变革的具体和切实的例子。
GenAI 的闪光点
让我们从最直接的胜利开始——粉碎样板代码。
管理面板生成
我们选择了以 ra-data-simple-prisma 为基础的 react-admin,它已经提供了可靠的抽象。将 GenAI 添加到这个堆栈中,几乎可以实现 10 倍的效果。
建立初始架构后,工作流程变得非常简单:
对于管理面板,我使用 react-admin 和 ra-data-simple-prisma
主文件是@AdminApp.tsx,实体包装器示例是@Track.tsx,支持后端路由器@route.ts
为刚刚创建的新通知模型生成必要的管理页面。
输出将包括:
这里值得注意的是一致性和可靠性。在实施 2-3 个模型后,人工智能在维持我们既定模式方面变得非常准确
在应用程序中重用模式
作为上一个示例的扩展,GenAI 最受赞赏的优势之一是它如何加速跨不同特征的模式复制。想象一下,这就像拥有一个开发人员,他不仅记住了您建立的每个模式,还可以立即将其适应新的环境 - 而无需像往常一样问“等等,我们上次是怎么做到的?”。
这个项目中我最喜欢的例子展示了我们如何处理数据加载和更新,结合服务器端 trpc、客户端无限滚动和乐观更新。实现流程如下:
在调整此模式以适应评论时,我没有手动重写所有内容,而是简单地向 AI 展示了我们的混音实现,并给出如下提示:“这是我们处理混音数据加载和更新的方式。你能否调整此模式以适应评论,同时记住它们嵌套在混音下?”
人工智能不仅复制了模式,还能自然地处理嵌套结构——否则这项任务需要仔细的手动调整。
这种模式复制成为 GenAI 最强大的用例之一。最初作为混音解决方案的方案,后来成为我们跨多个功能的标准方法,每次调整所花的时间都比以前少得多。关键是要建立良好的初始模式 - 一旦我们建立好模式,GenAI 就会变得非常擅长在处理特定于功能的要求的同时保持一致性。
响应式设计
根据我在这个项目中的经验,GenAI 在为各种视口采用布局方面表现得特别好,而且通常第一次尝试就能做到。通常只需编写一个桌面版本,然后解释我希望其他视口是什么样子,该模型就会在第一次尝试时提出一个近乎完美的解决方案。
例如,我们在桌面上的混音卡组件突出显示了波形可视化,右侧是音轨详细信息,下面是交互按钮(例如,分享,评论)。当我需要将其调整为移动版本时,我简单地解释了以下内容:
“对于移动视口,我希望:
保持波形为主要焦点,但略微降低其高度
在其下方堆叠曲目标题和艺术家信息
将操作按钮排列成紧凑的一行
为所有交互元素保持方便点击的间距”
第一次尝试就取得了完美的效果 - 保持视觉层次结构,同时确保良好的移动可用性。人工智能既理解设计目标,也理解我们既定的响应模式,节省了通常需要多轮 CSS 调整的工作,现在只需一轮微调即可。
摆脱困境
当我感到懒惰或不知道从哪里开始时,我通常会提示一些事情,而与人工智能进行讨论可以让我开始做事。这就像拥有一个耐心的合作者,即使你还不确定自己在做什么,他也随时准备集思广益。
我不用盯着空白的编辑器不知道从哪里开始,而是可以从一些模糊的提示开始,例如:“我需要建立一个新评论通知系统。我们应该考虑哪些关键组件?”或“看看我们的 remix feed 组件 - 添加排序选项的第一步是什么?”
即使我最终没有使用人工智能的大部分建议,这些对话也帮助我克服了最初的阻力。人工智能的回应经常会引发这样的想法:“好吧,这不是我想要做的,但是......”——突然间,我就开始积极解决问题,而不是拖延。
事实证明,这对于“我稍后再做”的任务(如错误处理或可访问性改进)特别有价值。有了 AI 来交流想法,就可以更轻松地解决这些不太令人兴奋但至关重要的开发方面。
GenAI 的不足之处
尽管 GenAI 在加速我们的开发方面发挥了重要作用,但它仍然存在不足或不一致的地方。以下是我遇到的主要问题以及我如何解决这些问题。
性能优化
除非明确提示性能注意事项,否则当前模型通常会生成次优代码。在我们的案例中,这最明显地体现在过度获取和未优化的 Prisma 查询中。当我们只需要特定字段时,AI 会很乐意生成提取整个记录的代码,或者创建单独的查询,而单个连接会更有效。
在某些情况下,编写单个原始 SQL 查询会是更好的解决方案,但除非明确指示考虑性能影响,否则我还没有看到 LLM 自行提出这种方法。
图书馆层面的挑战
如果您要组合库,那么 GenAI 模型的好坏取决于库本身。当需要在库级别而不是通过配置或使用模式解决问题时,AI 会遇到困难。一个典型的例子是我们与 Wavesurfer.js 的集成以实现音频可视化。我们需要 Wavesurfer.js 波形在卸载时不停止外部媒体元素,这需要我对库本身进行修补。
虽然人工智能可以帮助完成基本设置和常见使用模式,但当涉及到核心功能问题时,它一直建议不同的配置方法,而不是查看库代码本身。即使解决方案需要修改库的源代码,我尝试过的所有 GenAI 模型都没有建议这种方法,而是生成相同无效解决方案的变体。
版本敏感的库
一个特别的陷阱:AI 经常为过时的库版本生成代码。我在使用 react-admin、headless-ui 和 tanstack-query 时就遇到了这个问题,如果没有指定确切的库版本,就会经常出现过时的语法。解决方案是在我们的提示中明确指定版本,但有趣的是,仅引用 package.json 并不总是可靠地工作。
错误一致
我经常遇到一个有趣的怪癖:一旦模型在理解我们的代码库或实现时犯了错误,它往往会在后续的代码生成中不断重复这个错误。这会产生一种“错误级联”,即使在聊天的不同阶段得到纠正,错误模式也会一遍又一遍地重复,或者至少不会得到一致修复。
例如,我经常注意到语法不一致的情况。假设 GenAI 模型使用过时的语法版本生成代码。应用更改后,您作为开发人员会更正语法和/或指示模型使用特定版本。然而,在聊天的某个时候,过时的语法会再次出现。
我尝试了各种提示质量和方法,但始终无法始终解决此问题。我见过的唯一现实解决方案是重新启动聊天,但不幸的是,这通常会丢失有价值的上下文。
最大的个人收获
生成的代码的好坏取决于你的提示。我偶然发现了一句与我的经历产生深刻共鸣的名言:“如果你不能自己写代码,你就不可能很好地提示它。”这句话完美地抓住了使用 GenAI 的本质——至少在技术的现阶段是如此。虽然模型可能会超越这一限制,但目前该工具主要是扩大你现有的知识,而不是取代它。
正如我们维护和版本控制代码库一样,我发现存储和不断迭代提示也至关重要。最有效的提示往往通过多次改进而出现,有趣的是,我开始使用人工智能本身来帮助改进我的提示。这是一个元学习周期:使用人工智能,了解哪些方法有效,改进提示,获得更好的结果。
我相信,人工智能提示本身将成为未来知识产权的重要组成部分。它们不仅囊括了开发的内容,还囊括了开发的方式——模式、偏好和积累的知识,这些使得代码不仅具有功能性,而且制作精良。
意外的惊喜
在此过程中,我发现了一个有趣的现象:直接使用 Claude 与通过 Cursor 进行使用会产生明显不同的结果,这可能是由于 Cursor 为提高成本效率而进行的额外快速优化。虽然两者都提供了宝贵的帮助,但它们的输出往往在风格和方法上有所不同。这不一定是好事或坏事 - 更像是有两个各有长处的合作者。我还建议尝试 Typing Mind 并通过 API 尝试不同的模型,以更好地了解 GenAI 的工作原理。
结论
使用生成式 AI 构建 joinstems.com 是一次令人大开眼界的体验。虽然这项技术大大加快了我们的开发过程,但它也凸显了一个重要的现实:我们目前处于 AI 辅助软件工程阶段,其中 AI 更多地充当放大器而非替代品。它放大了开发人员的优势和劣势,使扎实的工程基础比以往任何时候都更加重要。
尽管在模式复制和响应式设计等领域表现出色,但很明显,GenAI 工具尚未准备好端到端交付哪怕是中等复杂的产品。它们擅长特定任务 - 粉碎样板、调整既定模式、加速初始实施 - 但仍然需要仔细监督和经验丰富的人来指导它们走向可用于生产的解决方案。
然而,我们也清楚地认识到,我们正处于软件开发的新时代。自 ChatGPT 发布以来的短短两年内,开发工作流程已发生了翻天覆地的变化。进步的速度表明,这仅仅是个开始。