AI 吐代码,工程师吐槽(逐字稿)
对观众的说明
因为本篇演讲第二个案例而拖延的重感冒病情发展出了类似 COVID-19 的刀片嗓,无法亲口和大家面对面交流,只能让 AI 在后台朗读本逐字稿。
本篇内容全是基于事实的个人评论和观点,与主办方无关,谁觉得难受就全网公开骂我。
今天致敬我老乡唐香玉 —— 这篇是纯骂人,毫无意义!
AI 拉屎山,工程师通马桶
AI 吐千行单位的 .vue,Vue 官方开发扩展把 VS Code 搞崩了……
这是一个甲方用不止一个 AI 代码编辑器堆砌后的二次开发项目,截图是没有甲方商业机密的纯 CSS 样式代码,是一个 .vue 格式的页面文件,总长 3419 行……
图中代码全白了不只是因为 Vue 官方开发扩展崩溃了,是它把所有其它扩展一波带走,是整个 VS Code 扩展宿主机进程崩溃了…… 而且这种崩溃是自动重启后每 5 分钟左右复发一次,每次重启需要恢复所有被杀死的 VS Code 扩展,整个过程也得半分钟或更久…… 这还是在 GitHub codespaces 这种线上专业开发主机上。
当我把代码拉到本地 Windows 11 最新版后,VS Code 倒是不崩溃了,但 Vue 官方开发扩展非常卡,每输入一个单词要等半分钟左右才提示匹配了什么变量名、函数名、组件名、属性名…… 也等于不可用。
甲方之前要求“用你的经验优化、重构一下 AI 写的代码”,我完全低估了 AI 生成代码的屎山体量和天坑程度,项目也在被甲方完全无视的十一黄金周假期中延期了。但不经常亲自打开每个文件敲代码的甲方自然会质疑我遇到的大 bug,所有上线延期的黑锅自然也要我背……
去他爹的 Vue 单文件组件,TSX 语法糖干翻你!
我在 Vue 2 时代就觉得单文件组件语法和开发扩展很难用,每次都是遇到 Vue 项目临时装上,开发完就卸载。即使是 Vue 3 大力改进 TypeScript 支持,每次官方扩展发新版都说性能如何如何提升,但每次装上还是很慢…… 那不如彻底抛弃 Vue 官方扩展、回归 TypeScript 官方语言服务器,TSX 语法糖就是 Vue 模板语法的最佳代替~
于是我开源了一个 mobx-vue-helper 工具库,对齐 mobx-react 的 observer 装饰器函数接口,再配合一个中国码农开源的 Vue 3 类组件装饰器库,熟悉的函数组件、类组件统统回归!
import { Vue, Component, toNative } from 'vue-facing-decorator';
import { observer } from 'mobx-vue-helper';
import counterStore from './models/Counter';
@Component
@observer
class MyMobX extends Vue {
render() {
return (
<button onClick={() => counterStore.increment()}>
Count: {counterStore.count}
</button>
);
}
}
export default toNative(MyMobX);import { observer } from 'mobx-vue-helper';
import counterStore from './models/Counter';
export const MyMobX = observer(() => (
<button onClick={() => counterStore.increment()}>
Count: {counterStore.count}
</button>
));AI 打死写不出 Nuxt.js 服务端渲染自定义 Markdown
由于甲方希望在服务端渲染阶段对 Markdown 某些元素加以扩展,但我不熟悉 Vue、Nuxt.js 更底层的自定义渲染 API,我便让 AI 帮我先写一版,我边干边学嘛。结果,不但 AI 反复写出来的都是客户端渲染代码,以下官方文档中的方法在 .vue 文件中不是报错就是静默失败……
去他爹的 Nuxt.js 官方文档、issue,类组件上大分~
既然 Vue 官方和 AI 都靠不住,那还是拿出我写 Web components 框架全家桶的看家本领吧 —— 用 MDX 官方编译器配合 JSX runtime 手搓自定义渲染函数。结果,我用类组件的 @Setup() 装饰器回调函数做注入,很快就成功了!
import { Suspense } from 'vue';
import * as runtime from 'vue/jsx-runtime';
import { Component, Setup, toNative, Vue } from 'vue-facing-decorator';
import { evaluate } from '@mdx-js/mdx';
import { Link } from '../components/Link';
const evaluateMDX = (link: string) =>
defineAsyncComponent(async () => {
const { data, error } = await useFetch<string>(link);
if (error.value) throw new URIError(error.value?.message);
const { default: MDXContent } = await evaluate(data.value!, runtime);
// @ts-expect-error Upstream Type compatibility issue
return () => <MDXContent components={{ a: Link }} />;
});
@Component
class MdxDemoPage extends Vue {
@Setup(() => evaluateMDX('https://cdn.jsdelivr.net/gh/idea2app/Nuxt-MobX-Shadcn-ts/README.md'))
MDXContent?: ReturnType<typeof evaluateMDX>;
render() {
const { MDXContent } = this;
return (
<div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
<Suspense>{MDXContent ? <MDXContent /> : <p>Loading MDX content...</p>}</Suspense>
</div>
);
}
}
export default toNative(MdxDemoPage);Vite 不支持 ES 装饰器 stage-3 候选发布标准?Parcel 来救场~
这些年,每次想在 Vue 项目中用装饰器语法都极为痛苦 —— 无论是基于 webpack 的 Vue CLI,还是如今的 Vite 1234567,配置都极其复杂。而我最爱的 Parcel 则以一贯的“零配置”、“标准第一公民”的设计轻松支持各种 ECMAScript 新语法提案,更甭说装饰器这种用了很多年的候选发布提案。
于是我就在知乎认真回答了一个与 Vite 火爆程度对比的问题,客观总结了二者在用户增长相关因素上的优劣。没想到这个回答和点赞数很少的帖子竟然引来了 Vue 用户强词夺理的批判,Vue 创始人竟然也下场来堵我的嘴……
AI 能 solo?工程师擦屁股
甲方:现在 AI 代码编辑器都能 solo 了,你们有十倍效率提升了吧……
半年前的一个敏感的夜晚,本篇第二个案例的甲方突然找我聊天说:现在用 Cursor、Trae 做软件开发效率提升 10 倍,你一个人能干 10 个人的活了吧?
我回复:根据各种水平码农的项目实测,以前写不好代码的人现在也不知道 AI 写对了没,以前写得好代码的人也只是降低了他们做牛马的工作强度,10 倍提升是绝没有的……
敏捷笑话永不过时:老板认为一个女人十月怀胎,用了敏捷,十个女人一月怀胎就生了!
甲方:3 天 solo 出所有前端页面,我要给客户演示……
(合同中的开发)计划赶不上(甲方的)变化……
萧山鸡排哥:做完你的做你的,做完你的做你的……
台北王世坚:本来应该从从容容、游刃有余~
台北王世坚:到现在是匆匆忙忙、连滚带爬!
AI:你一次性让我写一套页面赶工期,我就开始编……
前端页面的数量和细节复杂性让 AI 的识图遵从性很差
页面布局、文案内容等各方面都是 AI 按照这个业务的大概主题发挥想象来写代码的,从结果来看,截图它就没看两眼,基本上每套功能页面我要手改半数代码……
类人的疲态表现说明:这不是技术问题,是阶级问题!
谁当牛马谁发疯!碳基、硅基概莫能外!
后端识图抽字段的遵从性反而很高
每套增删查改的代码分支我只需少许修正即可合并、部署,毫不耽误前端开发。
当前 AI 的能与不能
- 天马行空做个新项目 —— 表现惊艳
- 既定框架下赶工期 —— 表现拉垮
- 知无不言、言无不尽的提示词 —— 尚能思考
但我既然都能写出那么详细的提示词了,那不就已经是“伪代码”了吗?大学上过 C 语言的,老师应该都教过一个学习方法 —— 先用自然语言写伪代码,逻辑通了再翻译成 C 语言,这样更容易思考。所以此时职业工程师干嘛不直接靠 IDE 提示把真代码写了,还和 AI 较劲干啥啊…… 没有“AI 的氛围”我就不会“编程”了?
立党哥是我嘴替!
- 中国古人云:将在外,君命有所不受!
- 美国西点军校名言:让听到炮声的官兵做决定,而不是五角大楼!
AI 时代更需要甲方做“禁欲系”男友
克制一下拍脑袋既要又要还要也要都要全要的欲望……
我:真正容易让 solo 的不是“主流库”,而是让人和 AI 都能低代码
甲方觉得“主流”的 Shadcn UI 只会助长 AI 在我的 Git 历史记录里堆屎山
因为绝大多数人只追求“主流”的安稳,让 UI 看起来像那个时代的“正常”,给不起钱的外包项目没有多少对现成组件库的定制,“灵活地改代码”对他们其实是伪需求。最早他们追 Bootstrap,后来又追 Material Design,再后来中国有了自己的 Ant Design、Element UI,现在 AI 时代又流行 Shadcn UI 了,子子孙孙无穷匮也……
但我可不想让人读 pull request、AI 读源代码时被几千行和业务逻辑毫无关系的上下文卡住,就开源了一个 Git ignore 所有 Shadcn UI 官方源码的命令行工具,支持 React、Vue、Svelte 三大框架和 Next.js、Nuxt.js 的 page/app router 模式。
ThoughtWorks 中国 CTO:复杂需求必需图灵完备,而掌握它的只有程序员
水歌推论:好工具是服务高频用户的。
基于 MVC 和装饰器语法的数据表定义
基于上述理念,我首先依靠 TypeScript class 既是类型实体也是 JavaScript 运行时实体的二相性,配合装饰器语法定义数据库表结构。
@Entity()
export class User extends Base {
@IsString()
@Column()
name: string;
@IsEnum(Gender)
@IsOptional()
@Column({ type: 'simple-enum', enum: Gender, nullable: true })
gender?: Gender;
@IsUrl()
@IsOptional()
@Column({ nullable: true })
avatar?: string;
@IsEmail()
@IsOptional()
@Column({ nullable: true })
email?: string;
@IsMobilePhone()
@IsOptional()
@Column({ nullable: true })
mobilePhone?: string;
@IsStrongPassword()
@IsOptional()
@Column({ nullable: true, select: false })
password?: string;
@IsEnum(Role, { each: true })
@IsOptional()
@Column('simple-json')
roles: Role[];
@IsJWT()
@IsOptional()
token?: string;
}当 TypeScript 编译时,类型定义文件也可同时输出,放在独立目录就可发布为一个 GitHub NPM 注册表私有包,任一支持 TypeScript 的各种端侧开发框架都可引用,包括但不限于 Web、NativeScript、React Native、WebF(编译为 Flutter)、Taro(编译为国内小程序、鸿蒙应用)。
基于 service 封装增删查改
接着再用一个独立的 service 类封装对一张表的通用增删查改,除了对数据库的基本存取,还有一些附加的关系、日志、权限等处理,负责 API 路由定义的 controller 侧就极为简单,30 行代码就新增一套 RESTful API。
不知道最近谁又在喊“MVC 已死”,肯定是哪个不认真写代码的老营销号!
@JsonController('/template')
export class TemplateController {
service = new UserServiceWithLog<Template>(Template, ['name', 'description']);
@Post()
@Authorized()
@HttpCode(201)
@ResponseSchema(Template)
createOne(@Body() data: Template, @CurrentUser() createdBy: User) {
return this.service.createOne(data, createdBy);
}
@Get('/:id')
@OnNull(404)
@ResponseSchema(Template)
getOne(@Param('id') id: number) {
return this.service.getOne(id);
}
@Put('/:id')
@Authorized()
@ResponseSchema(Template)
updateOne(@Param('id') id: number, @Body() data: Template, @CurrentUser() updatedBy: User) {
return this.service.editOne(id, data, updatedBy);
}
@Get()
@Authorized()
@ResponseSchema(TemplateListChunk)
getList(@QueryParams() filter: TemplateFilter) {
return this.service.getList(filter);
}
}基于 MVVM 和 RESTful API 封装接口调用
我认为在前端,MVVM 思想也同样不过时 —— 正确地将 RESTful API 的资源描述为 Model,将资源方法及其状态描述为 View Model,那 View 层的书写将极为简洁。
下面的代码则是以 GitHub 组织代码库 API 为例的封装类,RESTful API 规范除了分页无法统一规定,其它操作都能由标准的 HTTP 协议语义实现,因此最规范的 API 只需定义查列表某一页的方法,其它全部复用基类实现。而具体某一套 API 往往有统一的分页实现,所以也可以将它进一步抽成业务基类,让每个 View Model 文件都不超过 10 行。
import { buildURLData } from 'web-utility';
import { Filter, ListModel } from 'mobx-restful';
import { components } from '@octokit/openapi-types';
import { client } from './client';
export type Organization = components['schemas']['organization-full'];
export type Repository = components['schemas']['minimal-repository'];
export class RepositoryModel<
D extends Repository = Repository,
F extends Filter<D> = Filter<D>
> extends ListModel<D, F> {
client = client;
baseURI = 'orgs/idea2app/repos';
async loadPage(page: number, per_page: number) {
const { body } = await this.client.get<D[]>(
`${this.baseURI}?${buildURLData({ page, per_page })}`
);
const [_, organization] = this.baseURI.split('/');
const {
body: { public_repos }
} = await this.client.get<Organization>(`orgs/${organization}`);
return { pageData: body, totalCount: public_repos };
}
}
export default new RepositoryModel();然后我们就可以基于前面的数据表类型、RESTful View Model 来大一统一套业务的增删查改组件,实现类似 Ant Design Pro table 的效果,但无需写一大堆异步函数、分页参数、功能开关。
import { text2color } from 'idea-react';
import { computed } from 'mobx';
import { GitRepository } from 'mobx-github';
import { observer } from 'mobx-react';
import { Column, RestTable } from 'mobx-restful-table';
import { Component } from 'react';
import { Badge, Container } from 'react-bootstrap';
import { repositoryStore } from '../model/service';
import { i18n, t } from '../model/Translation';
@observer
export class PaginationPage extends Component {
@computed
get columns(): Column<GitRepository>[] {
return [
{
key: 'full_name',
renderHead: t('repository_name'),
renderBody: ({ html_url, full_name }) => (
<a target="_blank" href={html_url} rel="noreferrer">
{full_name}
</a>
)
},
{ key: 'homepage', type: 'url', renderHead: t('home_page') },
{ key: 'language', renderHead: t('programming_language') },
{
key: 'topics',
renderHead: t('topic'),
renderBody: ({ topics }) => (
<>
{topics?.map(topic => (
<Badge
key={topic}
as="a"
className="me-2 text-decoration-none"
bg={text2color(topic, ['light'])}
target="_blank"
href={`https://github.com/topics/${topic}`}
>
{topic}
</Badge>
))}
</>
)
},
{
key: 'stargazers_count',
type: 'number',
renderHead: t('star_count')
}
];
}
render() {
return (
<Container style={{ height: '91vh' }}>
<RestTable
className="h-100 text-center"
columns={this.columns}
store={repositoryStore}
translator={i18n}
striped
hover
editable
deletable
onCheck={console.log}
/>
</Container>
);
}
}最终 3 天变态排期没赶上,但 5 天完成了前后端所有主体代码,相当于缩短了一半工期,也实证了我的“开源封装 + 敏捷 AI 迭代”思路比“纯 AI solo”更快、更稳!
AI 新时代?工程师操碎心
以下是 2023 年初 GPT 3 发布不久后,我与某 AI 大厂中国区高管的一次聊天
AI 时代警言第一弹《雪国列车》
剧情部分进行时:欧美严重的左右撕裂、族群撕裂、LGBT 权益滥用,中国互联网上严重的性别对立、代际对立、牛马与老板的对立,还有各国严重的贫富差距等等。
AI 时代警言第二弹《机械公敌》
剧情部分进行时:中国有一大批具身智能创业公司已经在卷了,如剧中呆滞、统一外观的人形家用机器人离量产也不是很远了,如无尽早的安全规范,发生大规模安全事故乃至灾难也是有生之年的事,在座的各位都有望见证历史……
AI 时代警言第三弹《西部世界》
具身智能发展到以假乱真的地步,剧中预言会控制乃至取代很多真人。
AI 时代警言第四弹《终结者》
碳基人类与硅基 AI 找不到共处之道,在各个时空全面开战。
AI 时代警言第五弹《黑客帝国》
碳基进化终究不敌硅基进化,人类沦为 AI 的生物电池,活在算力渲染的元宇宙中。(元宇宙行业的人果然会超前投资)
高管重复:这是“第四次工业革命”云云……
像西部世界里未觉醒的机器人,对现实世界的照片总机械地重复:我没看出有什么问题……
在我反复追问下,她才应付了一句:那也是没办法的事,他们就是要被时代淘汰的人……
我震惊的不是这个时代发展的客观事实,我一个程序员比一个高管更清楚 IT 行业又能颠覆哪个传统行业,而是面对很快将深刻改变人类前途命运的 AI 爆发,一位母亲竟然对很可能碾碎千万家庭的动荡,都不做一点哪怕茶余饭后闲聊式的反思,还重复着职场上“AI 未来十年发展”的光明前景……
潜台词就是 —— 我管他们死活,我未来十年能靠 AI 挣钱就完了!
人工智能的反面 —— 能治工人
我回味过来前面说的十倍效率 —— 甲方的潜台词其实是说:我可要十倍地砍工期和预算啦!
我:我月初的重感冒,在没休息好的情况下,又加重到“类新冠”症状了……
甲方 2:你每天用 20% 的精力写 AI 提示词也能赶上演示节点!
我:我就是让 AI 反反复复改联调细节代码写不好,最后还得我来擦屁股!
多少倍的效率提升我是没见着,但前些天几档的高烧我是上去了…… 下面我们先来做个小学算术题:
- 重感冒期间日均有记录的静息心率 100 ~ 120 次每分钟(实际感受上不封顶)
- 健康时静息心率平均 75 次每分钟
- 按近日很常出现的 120 次为例,心动过速 45 次每分钟
- 按医学统计,人静息心率每升高 10 ~ 15 次,体温升高 1℃
- 按 15 次 1 度保守估算,45 ÷ 15 = 3℃
- 以新冠疫情期间频繁测体温的均值 36.5℃ 为正常体温,则 36.5 + 3 = 39.5℃
- 若以 10 为单位,高烧已到 41℃(我小时候被同一体温烧昏厥过,抢救及时才没烧成脑残)
甲方的意思就是 —— 你只要还能坐在电脑前按得动键盘,你就能写提示词指挥 AI 干活!
我写提示词不思考怎么写 AI 遵从度高吗?AI 写完了我不看它哪里写得有问题?我不得思考怎么改提示词让 AI 接着改吗?这些不需要给大脑额外供血吗?静息心率 120 再加 20?爆的不是你的心脏,猝死的不是你哦!
所以深切地感受到,相比 20 年前有免费网页模板拿来改一改、10 年前有开源代码拿来改一改,当今 AI agent solo 的时代,甲方员工、乙方公司这些具体执行者比之前更不被当人看!
应开发提问智能 —— 能治问题
AI: step by step, please! No Mission Impossible!
我:不要解决不了问题,就解决提出问题的人!
关于前面那位女高管:
- 小八卦:我在和她同在的一个行业协会同为理事,今年我被当成提出问题的人解决了……
- 好消息:她今年被 AI 大厂裁了~
- 坏消息:她加入了一家具身智能创业公司……
时代发展、技术无罪,但要警惕 AI 幕后人的伦理
谢谢大家,我是 idea2app 的水歌!
左边是我的个人飞书,右边是我们开源社区的飞书群,欢迎扫码交流~