加入收藏 | 设为首页 | 会员中心 | 我要投稿 站长网 (https://www.1461.cn/)- 应用程序、AI行业应用、CDN、低代码、区块链!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

逻辑先行:90%程序员忽略的三大编码铁律

发布时间:2026-11-09 08:04:11 所属栏目:语言 来源:DaWei
导读:  跟产品吵完那会儿我手还在抖,不是气的——是刚拔掉耳机线,左耳塞卡在耳道里没抠出来,按了三次侧边键才唤出 Siri。PM 那句“你先把接口文档写出来我们再对齐”飘在空气里还没散,我盯着屏幕上那个空荡荡的 Swagger 模

  跟产品吵完那会儿我手还在抖,不是气的——是刚拔掉耳机线,左耳塞卡在耳道里没抠出来,按了三次侧边键才唤出 Siri。PM 那句“你先把接口文档写出来我们再对齐”飘在空气里还没散,我盯着屏幕上那个空荡荡的 Swagger 模板,突然就笑了一声。不是冷笑,就是生理性的、短促的、带点鼻音的“呵”。前天前端发来个合并请求,标题写着“临时兜底逻辑”,点开一看,三层嵌套 promise.then 里塞了个 try/catch,catch 里又 setTimeout(0) 重试——重试的不是 API,是本地 localStorage 读取失败。这他妈哪来的逻辑?这叫逻辑性缺失的具象化。


  逻辑先行,不是说“先想清楚再写”,是说代码结构必须能被静态分析推导出行为边界——连边界都推不出,你还部署什么?去年七月我亲手把一个风控规则引擎的主干逻辑从 JS 搬到 WASM,不是为了性能,是发现某次上线后,六个后端工程师对着同一份 JSON 规则配置,三人认为“should_block: true”仅触发拦截,两人认为它还会写审计日志,一人坚称它会触发下游 webhook。没人错,全是对的。因为那条规则压根没定义副作用契约。最后我把所有规则编译成 WASM 的时候,故意加了两个 trap:一个在无返回值函数末尾,一个在未声明的 global 访问前。跑通那一刻,我原以为会松口气,结果盯着那行 wasm-validate: ok 的终端输出看了十八秒,手指悬在键盘上没动——因为我知道,下一个坑不在编译期,在运行时与策略团队对齐的第十三轮会议里。


  第一铁律:任何函数不能同时承担判断和动作。你见过哪个函数既 return true 又发 MQ 又改 DB 又记埋点?见过。我上周三在 PR 里拦下三个。其中一个作者是后端,名字带“涛”,他写了七百行 Java,中间有四百三十行在处理“如果当前用户是 VIP 且距上次领券超七十二小时且当日未领过且库存大于零且营销活动状态为生效中”,然后用同一个 if 块塞进 update_coupon_status 和 send_kafka_event。我 comment 里只打了一句话:“这个 if 的布尔值,能被单元测试 assert 吗?”他回得快:“能啊,我测了。”——我点开他的 test 文件,发现 assert 语句后面紧跟着 mock.verify(),但 verify 调的是另一个类里的方法。他根本没测那个 if 本身。那个 if 还活着,像一块焊死在主板上的锡渣。


  Lighthouse 跑分截图发到群里没人回,下午三点,窗外云层压得很低,我顺手点了杯冰美式,糖包撕开一半掉在键盘右下角,没捡。桌面那个没关的录屏软件一直闪着红点,录的不是代码,是上周五晨会的共享屏幕——当时我在讲 Figma 插件里状态机的 transition table 怎么映射到 Redux reducer,讲到一半,PM 打断说“能不能先支持深色模式”。我停了两秒,说:“可以,但需要定义‘深色模式’是否影响 toast 的 zIndex,否则 toast 会盖住主题切换按钮。”没人接。后来前端自己硬设了个 z-50,结果在某个安卓 WebView 里,z-index 被 reset 成 auto,toast 飞到了 DOM 根节点底下。这事过去八天了,issue 状态还是 open,assignee 是空的。


  第二铁律:变量命名必须包含可观测性承诺。不是“userStatus”或“status”,是 “userLoginStatusAfterTwoFactorCheck”;不是“result”,是 “apiFetchResultForUserProfileWithoutCache”。别跟我扯可读性——你写的代码从来不是给人看的,是给下次 grep 用的,是给半年后新人查线上 error log 时用正则去 match 的。我敢打包票:去年所有紧急回滚里,67% 的 root cause 是某个叫 data 的变量在第 112 行被赋值为对象,第 208 行变成数组,第 399 行又变成 null——而类型注解只写了 any。那个“any”,是人类对不确定性签署的投降书。对面工程师换了一根充电线,白的,Type-C,弯头那种,插上去手机显示“正在快充”,但电脑上显示“接入未知设备”。我就坐在那儿看他试了四次 USB-A 口,最后他骂了一句,把线拔了扔进抽屉。我没说话。有些东西不兼容,硬接只会烧保险丝。


  第三铁律:分支必须可证伪。if/else 必须能在单元测试里覆盖每条路径;switch 必须穷举已知枚举;try/catch 的 catch 块必须能被抛出的错误精确命中——不是捕获 Error,是捕获 InvalidTokenError 或 NetworkTimeoutError。我原以为现在大家至少知道用 enum 做 status,结果上周 Code Review 看到一段 TS:status: string & { __brand: 'PaymentStatus' },后面跟着 switch(status)。我点了进去,发现 type PaymentStatus = 'pending' | 'success' | 'failed' | 'refunded' | 'processing';但他只写了 case 'pending': case 'success': case 'failed':,漏了另外两个。我 comment 里写了“缺 default 且少两个 case”,他回:“refunded 和 processing 目前没业务走。”我说:“那你删掉那俩 enum 成员,或者补上 case throw new UnreachableCaseError(status)。”他没回。到现在 PR 悬着。这不算严重?等明年财务那边真开始走退款流程时,整个支付页会静默 fallback 到 pending 状态——而前端不会报错,日志里也不会留痕,因为 catch 捕的是任何 Error,但那段代码根本没抛错。它只是……走丢了。


  中午那会儿我去倒水,看见隔壁组在测 CDN 缓存穿透,他们搞了个新策略:响应头里加 X-CDN-Hit,值为 HIT/MISS/PASS,但忘了在 PASS 场景下设置 max-age=0——结果一堆 404 页面被缓存了十七分钟。这事我本来不想提,但刚才突然想起 KPI 折算表里有一项叫“缓存有效性率”,上季度我们组是 92.7%,他们组是 89.3%。我不知道这两个数字怎么算出来的,也不知道要不要填进月报,反正 HR 系统下周二自动关窗口。


文章配图,仅供参考

  优胜劣汰?不是指人淘汰,是指逻辑淘汰——烂逻辑活不过三次迭代,糊弄过的函数扛不住两个产品经理的需求变更,靠注释解释行为的模块会在第一次跨端复用时崩溃。我不是反对快速交付,我是反对用“逻辑不可验证”换时间。你可以今天 merge,但请在 commit message 里写清楚:“此处逻辑依赖 PM 下周三邮件确认的最终字段名,如未确认,请勿发布”。不然等到线上出现「用户余额显示负一百八十元」的时候,别翻 Git blame,翻你自己的 README.md ——如果那玩意儿还开着。


  我现在正把那套 WASM 规则引擎的 schema 验证层拆出来,单独跑在 CI 里,要求每个 PR 提交前必须通过 schema validate ——不是 JSON Schema,是我们自己用 TypeScript interface 写的 runtime validator。刚配好,还没合进主干。等我把它推上去之后,我会把那个没关的录屏关掉。至于咖啡凉了没?没尝。键盘上那半包糖,我打算让它待到明天早上。

(编辑:站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!