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

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

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

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

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

 

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

 

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

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

我在理财投顾业务做了很多年,看过不少从互联网其他赛道转过来的工程师,他们最大的不适应不是技术难度,而是每行代码背后那套看不见的约束系统。技术方案评审的时候,"这样实现更优雅"在金融语境里几乎不是有效的论据。有效的论据是:这样实现更容易审计、更容易回溯、出问题更容易定位。

这不是官僚,这是处理别人钱的行业必须付出的代价。

 

审计思维如何重塑每一个技术决策

 

很多人觉得合规和审计是上线前的最后一道关卡,填几张表、照几张截图就过去了。实际上合规要求从需求阶段就开始重塑技术方案,只是这种重塑往往是无声的。

 

数据:你以为是CRUD,其实是账本

 

普通业务的数据操作,增删改查,天经地义。用户改个昵称,UPDATE一下,完事。

金融业务不行。每一笔操作都是一个事件,不能改,只能追。你看到的是一个用户修改了风险评级,审计看到的是一条事件流:原评级A2、时间戳、操作来源、变更原因、新评级A3、审批人。哪怕这是一个自动触发的变更,也要把触发规则编码进记录。

这直接影响你的存储设计和接口设计。普通业务的接口可能是 `PUT /user/risk-level`,请求体里一个字段搞定。金融业务的接口背后是一整套事件记录——谁触发的、为什么触发、前置条件是什么、变更后的完整快照是什么。你的数据库表不是简单的实体表,更接近一个只追加的事件日志加上若干物化视图。

前端也不能独善其身。智能投顾的用户画像每次更新,前端的展示都要能对应到后端具体哪一条规则触发了变更。不是你前端算的,是后端告诉你的,你只是渲染。这个"只是渲染"听起来简单,但它意味着前端状态管理的源头必须是可追溯的——你不能在前端做任何影响金融决策推算的本地计算。

 

错误:你以为是容错,其实是留痕

 

普通业务的错误处理哲学是"优雅降级":出错了别崩,给用户一个友好的提示,后台打个日志,完事。

金融业务的错误处理哲学是"完整留痕":出错了别崩,给用户一个明确的提示(不能模糊,模糊算误导),后台打结构化日志(不是普通log,是审计级别的操作记录),同时记下当前用户看到的完整状态快照,然后确保用户可以在这个状态下做出明确的下一步选择(而不是被悄悄引导到另一个路径)。

举个例子。用户在申购一只理财产品的时候,如果后端返回了一个网络超时,普通业务可能是"抱歉,操作失败,请重试"。金融业务不行,因为这个超时可能发生在后端已经成功但响应没回来的情况。所以你的错误处理要区分:这是可重试的失败,还是状态未知的超时。如果是后者,你要做的不是让用户重试,而是引导用户先查询交易状态。

这个区分不是技术实现的难度,而是你在写代码的时候脑子里有没有这根弦。大多数从非金融行业转来的工程师,第一反应都是"重试"。在金融场景里,盲目重试可能导致用户重复申购——同样是0.01%的概率,乘以日活量级就是必然事件。

 

发布:你以为是效率,其实是风控

 

互联网公司推崇快速迭代、小步快跑。金融业务也说要敏捷,但敏捷的节奏完全不同。

产品日活几十万的时候,一次灰度发布覆盖10%用户,出问题影响几万人,回滚就是了。但理财业务持仓页出一次问题,可能影响的是几百万人的资产展示准确性。展示错误在普通业务里是体验问题,在金融业务里是合规问题。

所以金融业务的灰度发布不是简单的百分比放量。它要考虑:灰度用户是否均匀分布在不同产品线上?灰度期间如果涉及金额计算逻辑变更,是否有对账机制同时生效?回滚方案是否覆盖了灰度期间已经产生的新格式数据?

我见过最极端的场景:一次前端持仓页的收益率展示逻辑调整,灰度了两天,在一个小比例用户群中发现展示数据与后端结算数据存在偶发的精度偏差——不是bug,是浮点数精度在特定数值下四舍五入方向不一致导致的。在普通业务里这就是个小修,但在金融业务里,它触发了完整的问题排查流程:影响范围评估、客户告知策略、数据修正方案。整个灰度持续了将近两周才完成全量。

这不是低效,这是代价。你在别人信任你管钱的前提下做业务,这个信任的折损是不可逆的。

 

合规需求的技术化困境

 

理想情况下,合规需求应该在技术方案设计阶段就被完整地纳入。现实是,合规需求和工程实现之间横亘着一道语义鸿沟。

合规说的是"确保用户在购买前充分了解风险"。工程师听到的是"加一个风险提示弹窗"。但合规要的不是弹窗,是"可证实的充分告知"——也就是说,你在审计的时候要能证明:这个用户在购买前确实看到了风险提示,确实花了一定时间阅读(不是0.1秒就关掉了),确实是本人操作的。

于是"加个弹窗"就变成了:弹窗内容实时从合规配置拉取(不能写死,因为合规文案会随监管变化)、弹窗展示时长要有记录(埋点上报)、用户关闭弹窗的动作要有日志(以后追溯用)、弹窗的展示逻辑不能被前端绕过(即使接口返回可以跳过也要兜底校验)。一个"加个弹窗"的需求,变成了一整套合规埋点+日志+兜底校验的工程方案。

这就是金融科技工程的真实成本:每个看起来简单的产品功能,背后都拖着一长串合规实现。而这些合规实现往往不会出现在产品经理的PRD里,也不会出现在设计师的稿子里——它们是完全属于工程师的责任范围。如果你不主动识别和补齐这些需求,上线后合规巡检发现缺失,补丁的成本比一开始就做好高出一个数量级。

更微妙的问题是,合规需求本身也会过期。监管规则在变,去年合规的方案今年可能不合规。这意味着你维护的不只是业务代码,还有一整套随时间演进的合规适配层。我见过一个理财产品详情页,合规相关的条件渲染逻辑比业务逻辑还复杂——不同风险等级的用户看到不同的提示,不同地区的用户适用不同的披露规则,不同时期的用户适用不同的合规版本。

三层条件嵌套,产品标准版、云南地区版、2024年3月后新用户版。叠加起来,一个页面的合规分支有十几种。这比任何框架的响应式系统都棘手,因为它是外部强加的规则,不遵循任何内部逻辑一致性。

 

三条实用经验

 

说了这么多困境,我并不是要劝退。金融科技工程有它独特的满足感——当万亿规模的业务稳定运行,你知道每一行代码都经得起追溯和检验。只是这套打法确实和互联网主流的工程实践差异很大,不主动切换思维就很容易踩坑。

总结几条我实际用下来的经验,不一定普适,但至少在理财业务场景里是有效的。

把审计当作一等公民纳入设计。不是写完代码再补审计日志,是在技术方案设计阶段就把"这条链路怎么追溯"作为和"这条链路怎么实现"同等重要的设计维度。具体来说,每个涉及金额、状态变更、用户决策的技术方案,都应该包含一个"审计路径"部分:从用户操作到后端记录,数据沿着什么路径流转,每一步留下了什么痕迹。如果审计路径里有断裂——某个环节没有记录,或者记录粒度不够——方案就不该通过评审。

前端不做金融计算。这条听起来简单,做起来需要持续的意识对抗。前端工程师天然喜欢在客户端做计算:格式化、排序、筛选、聚合。在普通业务里这些是性能优化手段,在金融业务里这些是风险来源。收益率计算、金额汇总、资产占比——这些在前端只能做展示格式化,运算必须交后端。前端拿到的就是最终结果,连排序都最好用后端返回的顺序。这不是性能问题,是一致性问题。如果你在前端对持仓列表按收益率排序,而用户看到排序结果在某个瞬间和后端不一致——在金融语境里,这可能被解读为"提供误导性信息"。

灰度方案独立于功能方案设计。不要把灰度当成功能发布的一个步骤,而是把它当作一个独立的技术方案来设计。金融业务的灰度不是上线流程,是风控手段。它需要单独考虑:灰度期间的数据对账怎么做、异常检测指标是什么、回滚操作在每种已产生的数据状态下怎么安全执行。把这些想清楚了,比简单配一个百分比灰度要安全得多。

 

不是所有代码都要写成审计级

 

写了这么多,我也得承认一个现实:不是所有金融业务的代码都需要按审计级来写。一个运营活动的弹窗、一个非金融信息的展示页面、一个内部管理后台——这些地方的审计要求远低于核心交易链路。

但问题是,边界在哪?什么算"金融决策相关",什么不算?

我的判断标准很简单:如果这个功能的数据或操作,在争议发生时可能被用户或监管用来作为证据,它就是审计级的。理财产品详情页上展示的收益率文字?审计级。首页上的banner图?非审计级。用户完成风险评估后推荐的 products 列表?审计级。用户修改登录密码的交互流程?偏审计级(身份验证链路)。

这种二分法不完美,但比"全部当审计级来写"要现实得多。全部审计级的代价不仅是开发效率降低,还有代码可维护性的下降——当每一行都有追溯约束的时候,重构就变成了拆炸弹。

 

写给从互联网转金融的工程师

 

如果读者中有正在从互联网转向金融科技的同僚,我的建议是:先忘掉"move fast and break things"。这句话在金融行业不是信条,是事故报告的开头。

金融科技工程的节奏不慢——业务需求一样紧迫,版本迭代一样频繁。但每个迭代的"安全余量"比互联网大得多。这个余量不是浪费,是对"别人把钱交给你"这件事情的基本尊重。

你的代码第一读者确实不是机器,是审计。这听起来枯燥,但当你习惯了这种思维之后,你会发现它反而让你的系统设计更清晰——因为可追溯和可解释的需求,天然排斥过度复杂的设计。审计友好的架构,通常也是维护友好的架构。

只是很多工程师要走过一段弯路,才会意识到这一点。包括我自己。

You voted 3. Total votes: 18

添加新评论