开发者如何穿越AI迷雾
软件开发领域的格局一直处于不断变化之中,最近几个月,最大的浪潮来自人工智能开发。我们在这两个阵营中都有非常强烈的观点,从那些声称“你不再需要学习如何编码”的人到那些预测人工智能及其当前热潮即将消亡的人。
简短版本很简单:好消息是软件开发人员仍然需要,并且在未来可预见的时期内将继续需要。坏消息是所需的技能组合和我们热爱的工艺方法发生了重大变化,不是所有人都会喜欢这一点。
在本文中,我将详细探讨上述陈述,并分享我对你应如何应对这一非常嘈杂的现实的战略建议。
1、再也不需要开发者了
自从ChatGPT发布以来,新闻中一直充斥着关于未来所有代码都将由AI编写的消息,或者公司X因为这种转变而停止招聘的报道,甚至有一些令人震惊的标题,比如“25%的编程职位已经消失”。
图表显示了开放职位数量的大幅减少,并暗示AI是导致这一现象的原因。这强化了“你不需要再学习如何编码”的想法。
现实比这更复杂,重要的是要以一种有远见的方式来看待它,以避免被炒作所迷惑。
实际上,这25%的下降可以归因于许多因素,很难将它们分开来判断哪个因素贡献最大。请看下面的图片:
你可以从中吸取几个教训。首先,虽然两条曲线在2025年都出现了下降,但其中一部分可以归因于对“计算机程序员与软件开发人员”定义的变化,数字在这两者之间转移。
此外,别忘了疫情期间曾有一段疯狂招聘期,许多经验尚浅的新开发者被加入进来。一旦事情恢复正常,公司重新评估需求并开始调整人数。
最后,有人认为这张图表只反映了美国的数据,而疫情期间加速的远程工作趋势并未消失,这意味着即使在美国,公司也会继续外包一些需求给其他市场的开发者。
然而,这里有一个亮点。尽管我提出的这些论点可能表明AI没有影响,但我相信它确实有影响,并且将继续如此,但主要集中在特定类型的开发者身上。
2、你会陷入危险吗?
绝对可能!
如前所述,我认为如今我们看到的很多负面报道主要是由于大软件公司正在经历的政策修正和调整过程。
虽然这对很多人产生了影响,但一旦新的基准确立,我们可以开始挑战并理解AI的真实角色。
目前,我认为那些仅仅因为“有保障的高薪工作”而进入这个行业的人将继续离开这个行业。毕竟这是一个知识型行业,它要求一定的持续改进和适应能力,而这些能力由于威胁工作的相同进步(即生成式AI)变得更容易实现。
现在让我们谈谈“氛围编码”,你是否应该这样做?
3、氛围编码是未来的正确方向吗?
这个术语是由Andrej Karpathy在早期2025年2月提出的,在短短时间内获得了极大的流行度,以至于似乎每个人都在做这件事,所有的工具也都围绕这个展开。
我们听到故事说有人在完全不知道自己在做什么的情况下创建并发布了应用程序,这进一步助长了这种风气。
在我表达我的看法之前,重要的是要确保我们不要掉入语义扩散陷阱。
“语义扩散是指一个词由一个人或一群人创造,并且有一个很好的定义,但在更广泛的社区中传播时,这个词的定义逐渐弱化。这种弱化可能会完全失去定义——以及随之而来的任何有用性。”——Martin Fowler
那么什么是氛围编码?根据他最初的推文,我能想到的最简短解释是当你完全依赖AI的输出,盲目接受更改并通过提示修复错误而不是理解底层代码时。仅此而已。
如果你仍在深入研究实际代码并进行调整,那你正在做别的事情,所以别假装很酷了 :)
问题是,你应该什么时候这样做,如果有的话?Andrej本人分享了一个好的用例是用于一次性项目。对我来说,除此之外,一个好的用例是在你不太关心诸如可维护性、安全性等因素,而更关心完成这部分工作以实现其他目标时。
至少到目前为止,任何其他用途都感觉像是不负责任或无知——意味着仅仅是缺乏必要的考虑意识,而不是个人的失败。
4、我该如何应对这一切
令人惊讶的是,仍有大量开发者和公司尚未找到利用生成式AI作为日常工作流程的一部分的方法。部分原因在于过去几年持续的炒作。
我每天在编码时都会使用生成式AI。它完美吗?不,远远不够。它能帮助我并且让我更高效吗?绝对是。
这一节是为那些仍然抗拒的人准备的,可能是由于过去其他技术/工具被吹嘘为解决所有问题的灵丹妙药时受到过伤害。
- 选择一款集成生成式AI的工具(任何工具)
Windsurf、Cursor、JetBrains、Visual Studio Code、NeoVim等常用工具都已集成了LLMs以帮助代码生成。
哪个是最好的?我不会开始讨论这个问题,尤其是因为我们正处于如此剧烈的变化状态,以至于它们每个月都在互相超越。
重要的是使用它们!学习如何利用它们提供的功能并理解其局限性。
- 提问/聊天并学习
如果你还在离开IDE去询问如何用库Y做X,那就停下来!
大多数工具都支持直接从IDE内开始对话。美在于有些工具会利用你当前的上下文——选中的代码、打开的文件——来定制响应。
或者
- 代码补全
也称为tab/tab/tab,这是利用生成式AI的最简单方式之一。无论是通过提供注释还是简单地编辑代码,LLM都会建议一些可能符合你下一步意图的代码。
在这个例子中,它使用了仓库接口来了解方法,并通过查看预约定义来确定要使用的字段。完美吗?不,但离你可能做的东西不远了。
不幸的是,有时建议的内容偏离太远,你会感到沮丧,因为它展示的东西你需要取消。这真的因情况而异,并且肯定取决于你使用的模型。
总的来说,这是我处理现有代码并在单个文件中进行非常局部的更改时最常用的方式。这是我的...最简单的方法是使用并习惯它。
- 多文件编辑
前面那个案例的演变是在你想要实现的目标需要创建或更改多个文件时。
我在提出新的用例或功能时会用到它,这可能会涉及多个地方。例如,你需要更改验证逻辑及其对应的测试,你可以一次性告诉它进行这些更改,而不是逐个文件地编辑。
虽然你仍然在控制之中,但更改会发生并呈现给你以供确认或迭代。你可以在接受之前通过说明你想做哪些更改来调整它们。
你的体验可能会有所不同,但在某些情况下,你必须在上下文中指定你想要触及的文件。
- 代理
到目前为止最强大的选项。在这种情况下,你可能正在创建/更新功能,同时要求代理代表你执行命令。
这可能包括在你的机器上(虚拟或非虚拟)运行应用程序以及访问网络。例如,我想在我的用例中引入命令处理器。我手动创建了一个,并要求模型更新所有端点并遵循。
然后继续迭代直到完成。
当我想要启动我的应用程序时,我喜欢使用这个选项,生成所有的文件夹和辅助文件,以便我可以继续开发。
它通常是所有选项中最慢的,但它往往很全面,扫描工作区中的现有文件并尝试推断出它将需要触碰的所有文件、在哪里创建它们等。
将其推向极端可能是要求代理与代码交互直到所有测试通过,这会导致可能或可能不会收敛的变化循环。这是一个提醒:要保持控制!知道何时停止循环并改变策略。
5、充分利用人工智能
这无疑是一个移动的目标,每周我们都会有所变化,无论是新模型及其吹嘘的惊人能力和更大的上下文,还是工具和生态系统方面的进展,例如引入MCP来标准化模型向世界的输出通信。
目前,以下提示帮助我理解和充分利用GenAI。
1 — 上下文为王
在GenAI能够未经同意读取我们的思维(猜测我们想实现什么)之前,它依赖于在提示语和/或工作区中提供足够的上下文来指导其决策。
这意味着在要求它做事时尽可能清楚。例如,由于模型的知识训练落后于现实几个月,因此在提示语中提供具体的版本信息。一些工具允许你指定URL以访问并传递给模型作为请求的一部分。
在这个例子中,我告诉它使用特定版本的文档,否则模型不知道。
这样做可以减少产生幻觉或接收错误信息的可能性。
2 — 定义标准
一些工具称之为规则,另一些则称为附加指令,但大多数IDE都允许你定义包含自动传递给模型的信息的外部文件,而无需你明确指定。这会让你的工作更加轻松——减少输入。
例如,我创建了包含我的代码标准、项目结构偏好以及甚至如何使用鉴别联合和Zod进行验证的指令文件。
该指令文件的一个片段可能是
# Test Structure
All tests should be located under /tests and use the namespace.
Example, if you have a class User and we want to generate unit and integration tests, we would have them as
/tests/unit/User.spec.ts
and
/tests/integration/User.spec.ts
## Tooling
Only use Jest for typescript based projects
Use `describe` to group the tests according to their function
Use `it('should ...')` as part of the individual tests
Try to keep a single assertion/expect per `it` test.
## Mocking
Use ts-jest-mocker for mocking.
```typescript
import { mock, Mock } from 'ts-jest-mocker';
```
To mock a class, use the following syntax:
```typescript
import { mock, Mock } from 'ts-jest-mocker';
import { MyClass } from './MyClass';
describe('MyClass', () => {
let myClassMock: Mock<MyClass>;
beforeEach(() => {
myClassMock = mock(MyClass);
});
it('should call the method', () => {
myClassMock.method();
expect(myClassMock.method).toHaveBeenCalled();
});
});
```
To mock an interface, use the following syntax:
```typescript
import { mock, Mock } from 'ts-jest-mocker';
import { MyInterface } from './MyInterface';
import { MyClass } from './MyClass';
describe('MyClass', () => {
let myInterfaceMock: Mock<MyInterface>;
beforeEach(() => {
myInterfaceMock = mock<MyInterface>();
});
it('should call the method', () => {
myInterfaceMock.method();
expect(myInterfaceMock.method).toHaveBeenCalled();
});
});
```
它可以指定使用哪些库、遵循的安全实践,甚至如何与公司可能拥有的私有库交互。
没有定义这样的标准?没关系,如果你有一些现有的代码,你可以要求它分析并记录下它发现的内容。逆向工程大获全胜!
3 — 找到平衡
在演讲中,Michael Feathers提到当前的模型倾向于像我们一样行为,记住对话的开头和结尾比中间部分更多。
当我回顾聊天记录时,看到你最初和最后提出的问题得到了尊重,但其他问题被遗忘时,我就看到了这一点。
将这一点应用到我们与这些模型的互动中,意味着即使你可能有一个完整的系统/功能规格说明,根据规模和细节的数量,可能最好将其分解成更小的部分,也许类似于你应该遵循的迭代过程,如果你是“手动”开发的话。
我喜欢先进行一次迭代,只启动应用程序,创建文件夹、管道定义、IaC等。然后真的取决于手头的项目,但采用按功能的方法,这使我能够随着定义——如通用语言、实体和值对象——变得更加清晰而跟随并完善我的方法。
最后是重构以获得额外的清晰度和性能考虑。
6、未来是……黯淡 | 伟大 | 无论你创造什么
我希望这篇文章为你提供了关于你可以并且应该在工具箱中融入GenAI的多种方式的指导。随着生态系统的发展和演变,开发工艺也会随之发展。
虽然它可能在你特定的上下文/语言/产品中不是那么有帮助,但它肯定会变得更好,否认其潜在价值而不定期重新审视可能是与盲目信任它的一切输出同样错误的做法。
一如既往,关于这个话题还有更多可以讨论的,接下来的文章中我想扩展这里介绍的一些主题并添加更多内容,比如使用PRD(产品需求文档)为你的互动提供更多结构,MCP的角色,以及代理!
但我很好奇你是如何使用GenAI进行开发的,以及你的看法是否发生了变化。
原文链接:Navigating the Current AI Landscape as a Developer — Coding is Dead! Long Live Coding!
汇智网翻译整理,转载请标明出处