从删库到佛系:一个双非学生的代码洁癖康复实录

代码收容所
2025-12-26 21:27
阅读 1524

大家好,我是小林,某双非院校大二在读,目前在本地一家做区块链基础设施的小团队“混”了快两年——没错,就是那种老板画饼、产品经理天天改需求、运维半夜拉你进群问“服务是不是挂了”的草台班子。不过话说回来,能在大二就摸到生产环境的K8s集群,还能参与设计一个轻量级联盟链节点调度模块,我已经比同龄人幸运太多。

但今天不聊技术栈,也不吹K8s多香(虽然它真的很香),我想聊聊一件让我差点“原地退役”的事儿:代码洁癖

曾经的我,看到别人写的 if 嵌套超过三层就想重写,函数名带个 getXXX 就觉得 low 到爆,变量命名不用驼峰?直接血压拉满。更离谱的是,有一次同事为了赶双11前的上线 deadline,在一段业务逻辑里硬塞了五个 TODO: fix later,我当场瞳孔地震,当晚失眠三小时,脑子里全是“这代码怎么能跑起来?”

直到上个月,我终于被现实狠狠教育了一顿——顺便治好了我的代码洁癖。


起因:一个来自区块链项目的“脏活”

事情发生在上周三下午。我们团队最近在搞一个基于 Hyperledger Fabric 的供应链溯源 PoC 项目,客户是某省农产品协会。本来架构挺清爽:前端 Vue + 后端 Go 微服务 + 链码(chaincode)跑在 K8s 上。结果临近演示,客户突然说:“能不能加个功能?让农户用小程序扫码后,能看到整个物流链路上每个环节的温湿度数据?”

行吧,需求不难。但问题是——留给我们的开发时间只有三天

更要命的是,这段数据其实散落在三个不同微服务里:冷链监控服务、运输调度服务、仓储管理服务。而这些服务……咳咳,有些是我刚入组时写的,有些是前同事留下的“遗产”,还有些是实习生临时补的 bug fix。代码风格?那叫一个百花齐放:有的用 snake_case,有的用 PascalCase,还有一段居然混用了中文注释和英文变量名!

我原本打算花半天时间先把这三个服务的接口层统一重构一遍,结果刚 pull 代码,就被组长在群里 @:

“小林,链码那边先别动!先把数据聚合接口怼出来,明天下午客户要看 demo。能跑就行,别整那些花里胡哨的。”

我盯着屏幕,内心挣扎得像在打 Dota 选不出英雄。一边是“整洁代码是程序员的尊严”,一边是“demo 挂了你就滚去写三个月 Excel”。

最后,我咬咬牙,点开了那个最脏的 Go 文件——里面甚至有未关闭的 defer 和裸露的 error 忽略。


过程:在屎山代码里游泳是一种什么体验?

我决定采用“最小改动原则”:不动原有逻辑,只在外层包一层胶水代码。于是,我写出了职业生涯中最“丑”的一段聚合函数:

func GetFullTraceData(orderID string) (*TraceResult, error) {
    // 先拿冷链数据(注意:这个服务返回的字段叫 temp_humi,不是 temperature_humidity)
    coldResp, _ := http.Get(fmt.Sprintf("http://coldchain-svc/api/v1/data?oid=%s", orderID))
    // 别问,问就是上游没给错误处理文档
    
    // 再拿运输数据(这个服务要用 POST,而且 body 是 JSON,但文档写的是 GET)
    transportBody := map[string]string{"order_id": orderID}
    buf, _ := json.Marshal(transportBody)
    req, _ := http.NewRequest("POST", "http://logistics-svc/trace", bytes.NewBuffer(buf))
    transportResp, _ := http.DefaultClient.Do(req)

    // 最后是仓储……算了,反正没人看这块注释
    warehouseResp, _ := callWarehouseLegacyAPI(orderID) // 这个函数藏在另一个 package 里,命名毫无规律

    // 手动拼接,能跑就行
    return &TraceResult{
        ColdChain:   parseColdResponse(coldResp),
        Logistics:   parseTransportResponse(transportResp),
        Warehouse:   warehouseResp,
    }, nil
}

看到这里,曾经的我可能会尖叫:“这也能叫代码?!”
但现在的我,只默默加了一句注释:

// TODO: 如果活着走出 demo,请重构此函数 —— 2024年6月20日,于工位第3次喝冰美式后

神奇的是,这段“脏代码”不仅跑通了,还在 demo 当天扛住了 50+ 并发请求(虽然 P99 延迟飙到了 1.2s)。客户满意,老板高兴,产品经理甚至夸我“响应迅速”。

那一刻我突然悟了:代码的第一使命是解决问题,不是取悦审美


转折点:当 K8s 救了我的命

你以为这就结束了?No no no。

Demo 结束第二天,线上链码突然报错:

Error: Chaincode invoke failed: timeout exceeded while waiting for transaction commit

排查半天,发现是因为我在聚合接口里没加超时控制,导致某个微服务卡死,进而拖垮整个链码执行上下文。更糟的是,这个错误只在高负载下偶现——典型的“薛定谔 Bug”。

我本想立刻重构那段脏代码,但运维老哥直接甩给我一个 Grafana 面板链接:“先恢复服务,重构的事儿等双休再说。”

情急之下,我灵机一动:既然不能改应用层,能不能在 K8s 层面兜底

于是,我给那个聚合服务的 Deployment 加了个资源限制和探针配置:

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: trace-aggregator
        resources:
          limits:
            memory: "256Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 3
          periodSeconds: 5
        env:
        - name: HTTP_CLIENT_TIMEOUT
          value: "3s"  # 强制所有下游调用 3 秒超时

同时,在 Istio(对,我们小团队居然上了 Service Mesh)里加了个超时策略:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
spec:
  http:
  - route:
    - destination:
        host: trace-aggregator
    timeout: 5s

奇迹发生了:即使那段脏代码偶尔抽风,K8s 也能自动重启容器,Istio 会快速熔断慢请求,整个系统反而更稳了。

那一刻我彻底放下执念:整洁的代码很重要,但健壮的系统更重要。有时候,用基础设施兜底“不完美”的应用代码,反而是更务实的选择。


综合反思:洁癖 vs 工程师思维

回过头看,我的代码洁癖其实源于一种“学生思维”——总觉得世界非黑即白,代码要么完美,要么垃圾。但在真实工程中,一切都是权衡(trade-off)

维度 学生思维 工程师思维
时间 “我要写出教科书级代码” “三天内能跑就行,后续迭代再优化”
责任 只对自己写的代码负责 对整个系统的 SLA 负责
价值 代码美观、可读性强 功能可用、故障可恢复
工具观 依赖编码规范 善用 K8s、监控、自动化兜底

特别是在我们这种小团队,综合交付能力远比单点代码质量重要。你写得再优雅,demo 挂了,客户跑了,公司倒闭了,你的“艺术品”也就成了数字坟墓里的陪葬品。

当然,我不是说从此就可以乱写代码。我的新原则是:

  1. 关键路径必须整洁:比如链码核心逻辑、资金相关模块,绝不妥协。
  2. 胶水代码允许“脏”:临时集成、POC、紧急修复,能跑就行,但要打上醒目 TODO。
  3. 用自动化弥补人工缺陷:CI 加 lint 检查、K8s 加资源限制、日志加 trace ID——让系统自己守住底线。

写在最后:从洁癖到“可控混乱”

现在,我已经能心平气和地看着同事写 var a = getUserInfo(); 而不冲过去抢键盘了。甚至上周五晚上加班时,我还主动帮测试同学 mock 了一个返回 { "code": 0, "data": null } 的假接口——虽然我知道这违反了 RESTful 规范。

但那又怎样?软件工程不是艺术创作,而是解决问题的协作游戏。在 deadline、人力、技术债的夹缝中,找到那个“足够好”的解,才是成年人的体面。

如果你也正被代码洁癖折磨,不妨试试我的“三问疗法”:

  • 这段代码会影响线上稳定性吗?
  • 重构它能带来显著收益吗?
  • 现在真的是最佳时机吗?

如果三个答案有一个是“否”,那就先让它活着吧。

毕竟,在这个用区块链讲信任、用 K8s 讲弹性的时代,真正的专业,不是追求完美,而是驾驭混乱

(P.S. 那段脏代码,我终于在上周末重构了。但不是因为洁癖——是因为它真的又崩了一次。😅)

评论 0

最热最新
暂无评论
代码收容所Lv.1
0
影响力
0
文章
0
粉丝