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

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

防资损这道题,测试答不了

金融系统出bug和出资损,是两个完全不同的量级。功能写错了,改过来就是;页面丑了,迭代一轮就行;但钱算错了,那是客诉、是赔付、是监管约谈,严重的时候直接停业整改。

我观察到一个很普遍的反应模式:出了资损,先加测试用例;又出一次,加人肉对账;再出一次,加发版审批。每一步都在做加法,每一步都离根本原因更远。因为大部分人把资损当成了一种"严重的bug",用防bug的思路来防资损。这个认知偏差才是资损反复出现的根源。

 

资损不是bug的严重版

 

普通bug和资损的核心区别在于:bug是你功能做错了,资损是你的功能没做错,但钱错了。

一个理财产品页面,把年化收益率3.5%展示成3.8%,这是bug。但用户基于3.8%做了购买决策,实际到账按3.5%结算,用户体验上的落差就是资损——即使从功能角度看,展示模块和计算模块各自都"按逻辑运行"。

更典型的场景:用户申购一笔理财,网络超时,前端重试了一次,后端处理了两笔。这两笔处理的每一笔单独看都没"错"——接口接收请求、参数校验通过、数据库写入成功。但因为缺少幂等约束,结果就是资损。

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

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

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

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

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

 

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

 

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

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

开会成瘾的技术团队,病根不在日历

上周清理日历的时候顺手做了个统计——我所在的技术团队,10个人,一周62个会议。人均每周6.2个,平均每天1.2个。如果按每个会议40分钟算,将近四小时。还没算上会前准备和会后消化,还没算上那些"5分钟碰一下"的临时通话。

这不是个别现象。我跟同行聊过,金融科技领域尤其严重——合规对齐要开会,风险评审要开会,跨团队排期要开会。一个需求的开发周期里,真正写代码的时间可能还不到一半。

很多团队的第一反应是"减会"——砍掉每日站会,缩短周会,设立无会议日。然后呢?信息断了,对齐塌了,出问题的概率反而上升。砍完的会议像野草一样换个形式长回来。

我后来想明白了这件事:会议不是问题,会议是症状。就像发烧不是病,是身体在对抗感染。你把体温压下去,不等于治好了。

 

每个砍不掉的会,都在替系统还债

 

技术团队里的会议,大致可以分几类。每一类背后都藏着不同的问题。你不可能一刀切地减掉它们,因为砍掉会议后露出来的那个洞,可能比会议本身更危险。

同步会:你的模块边界画错了

两个子系统需要"定期同步",这是最常见的一种会。前端和后端每周对齐,A团队和B团队双周同步。听起来很合理,但仔细想想——如果两个模块之间需要定期人工同步,说明什么?

代码的第一读者不是机器,是审计

上周组里一个产品上线前的合规评审会上,合规专员盯着一段前端代码问了一个问题:"这个收益率计算逻辑,用户在购买前看到的结果和购买后持仓页显示的结果,走的是不是同一条计算链路?"

在场的两个前端工程师面面相觑。答案是"不是"。购买前用的是推荐接口的预计算数据,持仓页用的是资产接口的实时结算数据。两条链路,两套精度处理,两组四舍五入规则。在普通业务里这根本不算事——数据来源不同,展示逻辑不同,非常自然。但在金融业务里,这意味着用户可能看到购买前年化3.52%,购买后变成3.51%。

0.01%的偏差,万亿规模下就是上亿的资金差异预期。合规专员当场拍了桌子,上线延期两周。

这个场景在互联网公司几乎不会发生。但在金融科技团队,它是日常。

 

金融业务的代码有两套读者

 

大部分工程师写代码时心里只有一个读者:运行时环境。代码能不能跑,跑得快不快,内存占不占得住——所有的优化方向都指向机器。

金融业务的代码不一样。它有两套读者:机器和审计。而且讽刺的是,审计这个读者往往比机器更难伺候。机器只要逻辑正确就行,审计要的是"逻辑正确且可追溯且可解释且可复现"。多出来的这三个"且",才是金融科技工程真正的成本。

DRY的幻觉:你消灭的重复,正以耦合的形式回来找你

DRY——Don't Repeat Yourself。这四个字母可能是软件工程里被引用最多、被误解最深的原则。

每个程序员入行第一天就被告知:重复代码是邪恶的,看到重复就要提取,看到提取就要抽象,看到抽象就要泛化。于是我们疯狂地消灭重复——公共函数、工具类、基础库、共享模块......代码库越来越"干净",重复率越来越低,CI里没有任何duplicate code的警告。

然后某一天,一个业务需求来了——需要改动某个"公共"逻辑。你打开那个被47个地方引用的utils函数,改了一行,跑了一下测试——绿了。上线,结果三个业务线同时报警。你突然发现,那三个业务线依赖的是同一个函数的三种不同行为,被那个"公共"函数强制统一了。

你消灭了重复代码,但你制造了耦合。而耦合,是比重复贵十倍的技术债。

 

重复代码不是问题,问题是不知道为什么重复

 

所有关于DRY的讨论,都默认了一个前提:重复等于坏。但这个前提本身就是幻觉。

重复代码至少有三种完全不同的面目。

第一种是意外重复——两个开发者不知道对方写了同样的功能,造了两个轮子。这种重复才是真正需要消灭的,因为它意味着信息浪费和潜在的不一致。

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

打开任何一个前端项目的代码,搜索 `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 }` 这就是形状。字段名有了,类型有了,看起来很完整。但这句话里藏了多少没说清楚的东西?

页面