人工智能

写给2025年的我:在保持真我与适应社会之间找到平衡点

在这个复杂多变的世界里,每个人都以自己独特的方式存在着,而我,则被周围人贴上了“憨”、“固执”这样的标签。面对这些评价时,我的内心经历了从最初的困惑不解到后来逐渐接受的过程。起初,我试图去改变自己,让自己看起来更加圆滑、世故,但很快我发现,这样的尝试不仅让我感到疲惫不堪,更重要的是,它违背了我的本心——那个渴望真实表达自我的灵魂。

Vibe Coding在生产环境活不过三天

"Vibe Coding"这个词火了有一阵子了。Andrej Karpathy说他写代码时更像是给AI指个方向,然后让AI去实现,自己更像是在"vibing"——感受代码的走向,而不是一行行敲出来。这说法一出,无数开发者深有共鸣。毕竟谁不想当那个只管提需求、不用写实现的人呢?

这体验确实爽。你告诉AI"帮我写一个用户认证模块",唰唰唰生成了一个看起来像模像样的实现。你甚至不用读完每一行代码,直接点运行,测试通过,感觉自己效率起飞。

但我观察到一个现象:Vibe Coding在Demo里无所不能,在生产环境里处处掣肘。那些靠AI快速拼出来的代码,一旦进入有历史包袱的真实业务系统,就开始不断露馅。而且问题往往不是代码本身有Bug,是这些代码跟现有系统格格不入。

这不是AI不够聪明。这是Vibe Coding这个工作方式本身的前提就不成立。

 

你以为在写代码,其实在丢上下文

 

Vibe Coding的核心体验是什么?你只需要表达意图,代码自动出来。这个体验让人上瘾,因为它跳过了编程中最痛苦的部分——把模糊的想法翻译成精确的逻辑。

但有一个被忽略的前提:你跳过的那些部分,恰恰是你建立代码上下文的过程。

错误处理的谎言:你以为在兜底,其实在埋雷

打开任何一个前端项目的代码,搜索 `catch`,你大概率会看到这样的画面:

```javascript
try {
await fetchData();
} catch (e) {
console.error(e);
message.error('操作失败,请稍后重试');
}
```

后端也好不到哪去:

```javascript
try {
await processOrder(orderId);
} catch (err) {
logger.error('订单处理失败', { orderId, error: err.message });
throw new BusinessException('系统异常,请重试');
}
```

这两段代码有一个共同的特质——它们让你觉得错误被"处理"了。日志打了,提示也弹了,异常也抛了,一切看起来都很体面。

但你仔细想想:用户看到"操作失败,请稍后重试"之后能做什么?重试?然后呢——再失败一次?运维看到一条"订单处理失败"的日志,能定位到什么?是库存扣减超时,还是支付回调丢失,还是风控拦截?

前端状态管理的终极错觉:我们都在重新发明数据库

前端的现状有点荒诞:我们嘴上说自己在写 UI,实际上大部分时间在跟数据较劲。Redux、Zustand、React Query、SWR、Dva、Pinia——每出一个新方案,大家就觉得"这次终于对了"。但如果你把所有这些方案放在一起,去掉它们的外壳看本质,会发现一个尴尬的事实:它们解决的问题,数据库几十年前就解决了,而且解决得更好。

我们不是在做状态管理,我们是在重新发明数据库。只是发明得很差。

 

一个 Redux Store 就是一个简化版数据库

 

Redux 可能是最明显的例子。你写一个 Redux store 的时候,本质上在做什么?

定义一个全局的数据结构——这叫 Schema。写 reducer 处理 action——这叫事务(Transaction),action 本身就是 WAL 日志。写 selector 查询数据——这叫查询引擎。写 middleware 拦截 action——这叫触发器(Trigger)。用 normalize 归一化数据——这不就是数据库范式吗?

接口契约的幻象:为什么类型对齐救不了前后端协作

前后端分离之后,我们发明了一个安慰自己的概念——"接口契约"。Swagger 文档一摆,TypeScript 类型一生成,团队觉得天上地下都對齐了:字段名对齐了,类型对齐了,必填选填对齐了。然后上线第一天就出了 bug——status 为 3 的情况前端压根没处理,amount 的单位后端返回的是分前端当成了元,createTime 在后端是 UTC 前端按本地时间解析了。

没错,类型是对上了。但类型对上和意思对上之间,隔的不是一行代码,是一个认知深渊。

 

接口文档给了我们一种虚假的安全感

 

我做过很多次前后端协作的接口评审,也主持过不少次。每次的流程都差不多:后端同事打开 Swagger 或 YApi,一个字段一个字段地过,前端同事点头说"收到"。大家都很认真,评审记录也写了,看起来协作很顺畅。

但评审过程有个微妙的问题——我们在确认的是"形状",不是"语义"。

什么叫形状?`{ orderId: string, status: number, amount: number }` 这就是形状。字段名有了,类型有了,看起来很完整。但这句话里藏了多少没说清楚的东西?

Node.js的运行时幻觉:为什么你的服务能跑全靠运气

很多从前端转 Node.js 的开发者,对这门运行时的理解停留在"写 JavaScript,能跑服务"这个层面。开发环境跑得好好的,上了生产环境,问题一个接一个冒出来,而且每一个都反直觉。

这不是水平问题,是 Node.js 的运行时设计给了你一种安全感——一种在开发环境完全验证不了、只有在流量压上来的那一刻才会破碎的安全感。

我自己在做 BFF 层的这些年,踩过太多这种坑。最深的感触是:Node.js 不会替你兜底,它只是让你以为它会。

 

事件循环不是安全网

 

Node.js 的文档里有一句很关键的话:事件循环在单个线程中运行。但很多人对这句话的理解是——"异步代码就不会阻塞"。

这是最大的幻觉。

`async/await` 只是语法糖,它让异步代码看起来像同步代码,但它不会把同步操作变成异步的。当你在一个请求处理函数里做了这些事情,整个事件循环都会停住:

- `JSON.parse` 一个 50MB 的请求体
- 用正则表达式匹配一个精心构造的字符串(回溯攻击)
- 在同步循环里做密集计算,比如加密、排序大数据集
- 调用了某个底层 C++ 扩展的同步接口

你选的 Git 工作流,暴露了你的发布能力

每次团队讨论 Git 工作流,场面都很热烈。有人搬出 Git Flow 的严谨分支模型,有人推荐 Trunk-Based Development 的极简主义,还有人觉得 GitHub Flow 够用就行。辩论往往持续一个下午,最后投票选一个,README 里写上规范,全员执行。

然后三个月后你会发现:分支还是乱成一锅粥,release 分支上堆了二十个 cherry-pick,hotfix 从 master 拉出来合不回去,有人偷偷开了 feature branch 三周没合,还有人在 master 上直接 commit——"就改了一行,没必要开分支"。

工作流选错了?不,工作流没选错。是大部分团队在选工作流的时候,选的是自己向往的工作方式,而不是自己实际具备的发布能力。

 

Git Flow:为发布窗口而生的妥协

 

Vincent Driessen 在 2010 年提出 Git Flow 的时候,他的假设非常明确:你的软件有固定的发布周期。develop 分支积累功能,release 分支冻结测试,master 分支只放生产代码,hotfix 从 master 拉出紧急修复。每一步都有清晰的语义。

AI+机器人创业:软件思维才是最大的坑

过去一年,我接触了不少AI和机器人方向的创业团队。有做具身智能的,有做服务机器人的,有做工业检测的,也有做消费级陪伴机器人的。聊完之后一个很强烈的感受:大量的创业者——尤其是技术背景出身的——正在用软件的思维做硬件的生意,用SaaS的逻辑评估一个物理世界的项目。

这个错位,比大多数人以为的要严重得多。

 

Demo到产品的距离,在硬件世界里是光年

 

软件创业的核心叙事是MVP——最小可行产品。两周上线一个版本,收集用户反馈,快速迭代。这套方法论在过去十年被验证了无数次,几乎成了互联网创业的圣经。

但这个叙事在机器人领域完全不成立。

一个做餐厅配送机器人的团队跟我说,他们的软件系统三个月就搭好了,SLAM算法调了两个月效果不错,避障也过得去。但整机跑起来之后,发现轮子打滑导致里程计漂移,激光雷达在不同光照条件下噪点差异巨大,电池在冬天衰减了30%导致续航不达标,餐厅地面的油渍让轮子寿命从设计的一半都不到。每一个都是"小问题",但要解决任何其中一个,都需要改模具、换供应商、重新做可靠性测试——一个循环下来三个月起步。

软件出bug可以热修复,硬件出问题只能召回。软件改一行代码的成本是几分钟,硬件改一个结构件的成本是几万块起,加上开模周期至少四周。这不是量变,这是质变。

从Redux到Signal,状态管理为什么换了三代还是不满意

前端开发有一个有趣的规律:每隔两三年,社区就会宣布一个新的状态管理"最佳实践"。Redux曾是不可撼动的标准,然后MobX说响应式才是正道,Dva试图让Redux更人性化,Zustand说状态管理不该这么啰嗦,Jotai说应该是原子化的,现在Signal又告诉你要从原语层面重新思考这个问题。每一次更迭都伴随着"旧方案已死"的宣言,和一波迁移重构。

但有一个问题始终没人回答:如果每个新方案都比上一个好,为什么我们还是不满意?

经历过Redux到Dva再到Zustand的完整迁移,我发现每一次换库的时候,团队都觉得这次终于对了。每一次,半年后又会发现新的别扭。这种循环让人不得不怀疑——问题可能根本不在库里。

 

Redux的铁律和代价

 

Redux的核心思想并不复杂:单一数据源,纯函数更新,不可变数据。这三条铁律在2016年前后确实解决了一个真实痛点——复杂应用中状态变化的不可预测性。组件树里的状态到处飞,回调层层传递,谁能改、谁不能改、改了之后谁是新的,没人说得清。Redux用一套严格的规则堵住了这个口子。

向后兼容的谎言:版本号救不了你的接口

每个设计接口的人都对自己说过同样的话——"加个字段就行了,老的调用方不受影响。"

三个月后,你发现老调用方也在读新字段,但逻辑完全不是你预期的。半年后,当初保证"兼容"的那个人转岗了。一年后,没人动得了任何一个字段,接口文档变成了考古现场——一半字段没人用,另一半的含义靠口头传承。你在注释里看到 `// 这个字段别删,XX团队在用`,去找XX团队,他们说早就不用了,但不敢确认,要再查查。

这不是向后兼容,这是技术债在穿正装。

 

兼容性承诺是一种债务

 

接口设计里最危险的一句话不是"我们重构吧",而是"我们保证向后兼容"。前者好歹意味着你在正视问题,后者意味着你在透支未来。

向后兼容不是免费的。每多兼容一个旧版本,你就多了一条隐形的约束链。这条链不会出现在代码评审里,不会出现在架构文档里,但会在每一次你需要修改字段含义、调整返回结构的时候跳出来,像一个极其礼貌的绑匪——"您不能动这个,还有调用方在用。"

页面