最邻近的这一整年, AI辅助编程的工具好似雨水浇后的竹笋一下子纷纷往上冒那般出现。先是仅有着能够补全单独一条行里面内容的, 而后发展到现今动不动就能够生成一整个文件的, 我们好像正站在一个时期的关键转变节点线上。
可是身为一线开发者, 我一直存有一个疑问, 那些工具在撰写Demo之际顺滑得像丝一样, 然而在实际真切的, 充斥着业务泥沼以及历史包袱的项目里面, 究竟可不可以经得住考验呢?
确定要去验证这个, 我做了决定, 在一个全是从零开始启动的独立全栈项目当中, 进行全面深度的接入。经历三周高强度开发后, 那个项目成功上线了。就在今天, 我打算抛开那些特别酷炫的营销话术, 以客观、细致且毫无保留的态度去复盘这段真实存在的人机协作经历。
项目背景:什么是 ?
乃是一个针对于小型敏捷团队的任务协作, 以及看板系统之所向之处, 你能够将其理解成轻量版本的 Jira 或者 , 其核心功能涵盖着:
多工作区跟项目管理: 团队能够去创建不一样的工作区, 每一个工作区的下面涵盖着多个项目。动态看板: 有着支持拖拽排序功能的任务看板, 其列(状态)是能够进行自定义的。实时协同:在团队成员对任务状态作出修改或者添加评论之际, 其他成员能够实时看到更新情况, 并不需要进行刷新。细粒度权限控制(RBAC): 管理员、编辑者、只读成员具备不同的操作权限。
技术栈选型:
身为一名全栈"独自作业者", 这套技术栈虽说具备现代特性, 然而其所关联的基础建设、配置以及前端与后端的联合调试, 工作量达到了令人震惊的程度。也刚好是引入 的最适宜试验区域。
阶段一:基建狂魔,体验"推背感"与规范的力量
项目才刚刚开启, 那光屏上满眼的空白文档, 着实是最能叫人感到绝望的。而配置数据库连接, 去撰写Model, 搭建Next.js的路由架构, 进行配置等等这些活儿, 其技术含量并不高, 然而却极其耗费时间。
- 秒出的数据模型与脚手架
操作是这样的: 我处于特定的对话窗口之中, 运用自然语言, 详细地去描述我的数据库设计思路, 有五个核心模型, 分别是User, 还有另外三个未明确写出的模型, 以及Task, 针对这些模型, 阐述它们之间存在的复杂关系, 有一对多关系, 有多对多关系, 举例来说, User和特定未明确表述的模型之间, 是借助特定未明确写出的表来进行多对多关联的。
呈现出的表现是: 令我感到极度舒适的情况在于, 它不但瞬间给出了没有错误的, 而且还顺便做了两件事情。
时间戳字段被自动加上了, 是那种和的时间戳字段。隐式中间表的配置被为多对多关系自动生成了。
紧接着, 将基于这些Model生成Next.js的API路由的任务交给它, 它依照App 的规范, 在目录app/api/ 下, 针对每个资源生成了完整的CRUD接口, 并且甚至贴心地加上了基于Zod的请求体校验代码,这是它做的。
公允来讲, 这一部分的体验简直无可挑剔。以往我得耗费大把时间去做那种复制粘贴然后修改的繁杂之事, 现在居然不到半小时就能完成。对于类似嵌套where这样复杂无比的连表查询语法, 它写得比我熟练太多。依靠它, 我能够快速略过最为乏味枯燥的基建阶段, 直接投身到业务开发当中。
- 规范的"传染性"
️ 阶段二:核心业务,人机协作的极限拉扯
基础设施建设完成以后, 迎来了其核心功能, 也就是拖拽看板, 这还是我头一回和它之间出现严重的"分歧"。
- 拖拽看板:被"完美UI"掩盖的灾难逻辑
我进行了这样的操作,我尝试着偷懒, 在某个地方试着输入, 生成一个看板页面, 此页面里面包含着多个呈现不同状态的列, 并且各项任务能够在这些列之间进行拖拽操作, 还要使用dnd - kit。
有这样的表现: 它给出了一个漂亮至极的组件代码, UI结构清晰, 并且还自带了具有平滑效果的CSS过渡动画。
踏上陷阱啦: 代码运行起来之后, 一旦将任务拖拉到另外一列, 整个页面的任务次序就全然紊乱了。认真查看代码, 它犯下了两个典型的人工智能逻辑方面的错误:
它假定: 看板的列是静态的(比如Todo、In、Done), 然而, 我的设计却是: 列由数据库以动态方式驱动, 并且, 用户能够按照自身需求去自定义添加列, 这一点被忽略了业务边界。当任务从A列被拖至B列时, 它仅仅是简单地更改了任务的字段, 可是, 排序权重字段的重新计算它却没有进行处理, 排序算法是缺失的, 如此一来, B列原本的任务顺序就被破坏了。
我的解决: 我被迫放弃"一键生成"的幻想,开始拆解任务。
进行客观的评价, 在面对复杂交互以及深度业务逻辑的状况之下, 其"幻觉"是极为严重的, 其所擅长的是拼凑构建起看似正确的代码骨架, 不过常常在数据流形成闭环之时存在着致命不足, 在这样的时刻, 绝不能心生偷懒之念, 你务必担当架构师的角色, 而它仅仅能够充当包工头, 将大目标分离拆解成为它能力所能涉及范围之内的小任务, 这才是正确之解决途径。
- 实时通信:上下文丢失的危机
为达让那个看板能够持有支持多人协同之效, 我引进了.io, 我促使帮我撰写出一个特制Hook, 专门用来监听服务器推送过来的任务变更情况, 进而去更新的全局状态。
初次进行生成操作时, 它给出了一个看上去仿若十分完美的 Hook, 此 Hook 之中含有 ,, on('') 等一系列逻辑。然而在一经运行过后, 却发觉存在着极其严重的内存泄漏现象, 页面亦因此而出现明显卡顿的状况。
究其实质, 是因为在组件卸载之际, 它没能妥当且准确无误地将事件监听予以取消, 并且也对重复连接这一状况未作处置。更为棘手且恶劣的是, 当我着手对这个 Hook 进行修正时, 鉴于文件代码已然超出了 300 行,它好像已然"忘却"了前述的代码架构, 在修改之时居然把原本应有的 set 逻辑写成了直接对 state 进行修改的违规行径。
应对的办法是, 我必须得把逻辑整个都拆开, 把连接管理、事件监听以及状态更新分成三个各自独立的文件, 并且严格限定每个文件的行数。当文件长度变短、上下文变得明晰之后, 最终才不再胡乱修改代码了。
阶段三:修罗场,权限控制与代码腐化
项目推进到了第二个星期, 碰到了最为令人作呕的状况: 基于角色的动态权限控制那种玩意儿(RBAC)。一般组员仅仅能够进行查看与发表评论, 而管理员则有权限去进行增加、删除以及修改的操作, 并且不同的工作区域在权限方面是相互隔离的。
- AI 生成的代码,正在悄悄腐化你的项目
最开始我的做法是, 于每一个API Route当中, 去使生成鉴权逻辑这件事得以实现。
scss
typescript复制代码
// 它在每一个 route.ts 里都生成了类似的一大段代码
const session = await getServerSession(authOptions);
if (!session) return unauthorized();
const membership = await prisma.membership.findUnique({...});
if (membership.role !== 'ADMIN') return forbidden();
我的 API 文件因这迅速膨胀, 重复的鉴权代码随处可见且到处都是。并且, 当我打算修改鉴权逻辑(像是去增加一个 '' 角色时候), 有一项工作是要到几十个文件里逐个去修改。它遗漏了三个文件未修改, 致使线上出现了严重的越权漏洞。
深切地进行反思: 这乃是运用AI编程工具时极易被忽视的陷阱, 那便是, 若AI生成的代码不加以约束, 它将会以极快的速率侵蚀您的项目结构。这是由于, 对于AI而言, 在当前文件里硬性编码一段逻辑乃是最为直接的解决办法, 它并不具备全局架构的视野, 并且也不会主动去思索复用性。
- 痛定思痛:用架构思维反向约束 AI
为了拯救那渐渐失去控制的代码库, 我不得已停下正在开展的业务开发, 耗费了一整日的时间去进行重构。
核心的高阶函数以及权限策略矩阵是我亲手书写的, 鉴权逻辑和业务逻辑借此被彻底拆解开来, 彼此毫无关联。我对 API Route 的结构进行了强制性的规范, 规范后的结构是什么样的呢, 规范后的结构为:
dart
typescript复制代码
// 我手写的架构骨架
export const POST = withAuth({ requiredRole: 'ADMIN' }, async (req, ctx) => {
// 这里面只写纯业务逻辑
// CodeFlicker 只能在这个闭包内生成代码
});
奇迹效果展现出来: 在我把这个架构约束给予之后, 它的表现产生了质的变化。这是由于它不用再去关注"怎样进行鉴权"这种繁杂的上下文, 它的注意力全部聚焦在了业务逻辑上面。接下来我让它生成"创建项目"、"删除评论"等十几个接口的时候, 它生成的代码极为干净清爽, 再也没有出现过越权漏洞。
客观来讲应该讲清楚一点, 你得比在不采用AI那个时候, 更加着重瞧代码的模块化以及解耦这方面。你所设定的架构边界越是清晰明了, AI所生成的代码质量相应地就会越高。千万别想着依靠AI来替你做架构, 它仅仅会顺着已有的趋势, 把最为简单直接的逻辑给堆积在距离它最近的那个地方。
意外之喜:那些它表现得像神一样的瞬间
存在这样的情况, 尽管踩了好些坑, 然而不得不表明一件事, 在一些特定的场景当中, 呈现出了那种远远超越了人类效率的"神性"。
- 复杂类型体操: 的最佳搭档
当进行重构看板视图这个操作时, 我所要做的, 是将返回的那种平铺的Task数组, 转变成为以某个特定内容为Key的对象字典, 目的是为了实现渲染, 而这一过程涉及到复杂的类型定义。
我朝着其表述了需求, 它不但在刹那间撰写出了能进行转换的函数, 并且还给出了一个令我不禁拍案称快、赞叹不已地类型定义:
arduino
typescript复制代码
// 它自己推导出了根据 Task 的 status 字段进行分组的类型
type TasksByStatus = Record<Task['status'], Task[]>;
如果是自己手动去写, 这样子一种既契合业务逻辑, 同时又极为优雅的TS类型推导, 就算是查询几遍Types文档, 并且反复调试编译报错, 至少也得需要不少时间。然而它却在一秒钟就给出了堪称完美的答案, 类型方面零报错。
- 正则表达式与解析器:降维打击
得要给予支持, 在评论里头 @ 某一个人, 并且将用户 ID 提取出来, 发送通知。评论的文本格式有可能是 @user : 请尽快去处理。
我扔给它好几行示例数据, 它在短短几秒钟之内就给出这么一个完美的正则: /@user:(+)\b/g, 而且还附带了相关解释, 甚至还考虑到了UUID有可能涵盖的十六进制字符边界方面的问题, 这就节省了我去翻阅正则文档以及在线调试所花费的半小时时间。
- 单元测试:从抗拒到享受
说实话, 身为独立开发者, 往昔我极为厌恶撰写单测, 觉着投入与产出的比例太过低了。但是自从有了某些因素 , 状况就发生变化了。
首先, 我完成了一个日期处理工具函数, 它很复杂, 职责是计算任务逾期天数, 并排除周末与节假日, 之后, 我直接促使其生成 Jest 测试用例。这些测试用例, 不但覆盖了正常的日期差值情况, 还自动生成了涉及跨年、闰年以及边界值的测试, 像截止日期为当天 23:59:59 的那种。结果, 测试覆盖率直接达到满值, 并且还揪出了我手写代码里存在的一个 Off-by-one 错误。
把枯燥的测试工作交给 AI,是性价比最高的投资。
认知蜕变:AI 时代的开发者自我修养
三周结束, 成功交房。公平来讲, 帮我把开发效率提高了大约35% - 40% , 主要节省了找文档、做代码范例、撰单位测试和处理字词错误的时间。然而比效率提高更关键的, 是我对人工智能编程工具认识的改变。
- 警惕"黑盒开发",你必须比代码更懂业务
在出现那种, 能够一次性生成 200 行代码, 并且这些代码还能够成功运行状况的时候, 你就会由此产生一种, 觉得自己什么都全会了的错觉。然而, 一旦在线上出现报错情况, 面对这 200 行并非是由你自己逐行敲写出来的代码, 你就会陷入到极度的恐慌之中。
要是不能够看懂, 那么宁愿不去使用。我如今形成了一种习惯, 它所生成的代码, 我会逐行查看。要是碰到它运用了我并不熟悉的应用程序编程接口或者库, 我会强制自己去查阅文档进行理解, 而并非沾沾自喜地认为"能够运行就可以了"。不然的话, 你仅仅是在给自己埋下定时炸弹。
- 从"打字员"进化为"审稿人"
以往写代码时, 有百分之五十的时间是用于敲键盘, 另外百分之五十的时间是用于思考。如今情况反过来了, 仅有百分之五的时间是在敲写提示词, 剩余百分之九十五的时间则是在阅读代码、核查代码以及对代码进行重构。
去阅读他人(就算是 AI)所编写的代码, 相较于自己动手写代码, 会更大量地消耗脑力。你得极为敏锐地去捕捉那潜藏着的逻辑方面的漏洞、性能上的瓶颈以及安全层面的隐患。你的角色正处于从代码的生产者, 转变为代码的架构者以及审核者的过程之中。
- 提示词工程的核心是"逻辑拆解"
不少人觉着AI不好使, 缘由在于提示词写得太过含混, 像"帮我写个电商系统"这般, AI只会给你一堆没法运行的混乱代码, 就像一座屎山一样。
真实的提示词工程, 并非是绚丽的辞藻, 而是严谨的逻辑剖析能力。你得将一个庞大的需求, 剖析成机器能够领会的不存在歧义的小任务实例。例如:
你大脑里的逻辑越清晰,AI 输出的代码越精准。
终局总结
三周的 开发,像是一场人机协作的极限压力测试。
我不会毫无理性地去吹捧, 它在复杂业务形成的完整流程、长时间跨度的上下文能够记住不忘、代码结构体系的规划方面, 显著地存在不足之处, 常常需要我去处理后续遗留的麻烦问题;然而我更不会去否定它所具备的价值, 它于示例代码生成、数据类型的推导、单元测试的编写以及正则表达式的解析这些方面, 呈现出了无法被其他替代的高效优势。
最终,工具决定了你的下限,而架构思维决定了你的上限。
不是那个能让你处于躺着状态交付项目的"全自动程序员", 它更像一台有着强劲马力然而却没有方向盘的发动机, 要是你自己不把控方向盘, 也就是架构设计、业务拆解、代码审查这些方面, 它只会带着你的项目以更快速度朝着悬崖冲去, 可要是你是一名优秀驾驶员, 它肯定能让你在开发的高速公路上感受风驰电掣。
交给它枯燥的, 留给自己创造性的, 这, 才是当下AI编程工具最正确的打开方式。