被代码重构淘汰:一个Rust重写引发的团队崩解与警示

作者:微信公众号:【架构师老卢】
7-21 8:36
17

重写之前,我们濒临崩溃 我们是一个六人团队。作为后端工程师,我们疲于奔命地应付着微服务、流水线、运维补丁以及读起来像心理治疗笔记的事故报告。

我们的技术栈对于一个快速发展的初创公司来说很典型:

  • Node.js 微服务
  • Redis 队列
  • AWS Lambdas
  • 几乎一切都用 MongoDB

我们并非能力不足,只是不够快。服务功能是有的,但很脆弱。

有些早晨,我们会一起盯着 Datadog 仪表盘,看着队列积压的消息数超过一万条,等待垃圾回收赶上进度。

我们每天都在“救火”。

然后,我们中最沉默寡言的 Kabir 说:

“我觉得我可以用 Rust 重写这个图像处理流水线。”

接下来发生的事情本应显而易见 我们都耸耸肩。重构项目从来都活不下来。

但 Kabir 没有寻求帮助。他没有预订设计会议,也没有要求估算时间。他只是默默地开始了。

在我们给 API 的分页功能打补丁时,他正在用 image-rs 库对比测试我们的 Node 脚本。

我们甚至没有注意到——直到他做了演示。

让我们过时的数据 Kabir 在下一次迭代评审会上演示了新的 Rust 服务。我们笑了。我们鼓掌了。

我们当时没有意识到,我们是在为自己被取代而鼓掌。

他展示的数据如下:

| 指标 | Node.js (重构前) | Rust (重构后) | | :------------------ | :--------------- | :------------ | | P95 延迟 | 243ms | 39ms | | Lambda 冷启动时间 | 1.8s | 240ms | | 内存使用量 | 300MB | 32MB | | 每日错误数 | ~500 | <10 | | 基础设施月成本 | ~$1200 | $110 |

Kabir 不仅仅是提升了性能。 他降低了 AWS 账单。 他消除了那些不稳定的依赖项。 他让值班轮换变得几乎无聊。

我们当时没说,但我们都感觉到了: 这不仅是一个更好的服务,更体现了一个更优秀的工程师。

我们其他人迅速落后了 Kabir 成了负责性能的人。

当我们还在修复损坏的 Mongoose 模式、争论 GraphQL 和 REST 时,他已经在撰写关于零成本抽象(zero-cost abstractions)以及 epoll 与 kqueue 的 RFC(征求意见稿)了。

他并不傲慢。但他也没有等我们。

我们开始问这样的问题:

  • “为什么这个处理程序在空负载时会崩溃(panic)?”
  • Pin<Box<T>> 到底是什么来着?”
  • “我需要安装 nightly 版本才能运行这个吗?”

差距迅速扩大。

前一周,他还在用 Axum 框架构建我们的健康检查。 下一周,他已经用原子计数器(atomic counters)和 parking_lot 库做出了一个生产就绪的速率限制器。

我们停止评审他的 PR(Pull Request)了。我们跟不上了。

组织悄然转变,然后剧烈变动 Kabir 不仅仅是在构建更快的服务。他正在改变所有权的归属。

产品经理(PM)开始把功能请求直接分配给他。 站点可靠性工程师(SRE)请他协助修改 Terraform 配置。 领导层开始在全员会议(all-hands)上展示他的仪表盘。 他成了“那个后端专家”——即使我们还有五个人在岗。

那天我们并没有失业。

但我们不再是那个团队了。

然后裁员来了 他们没有称之为裁员。他们从来不会这么说。

他们说公司要“重新聚焦”。说是在“优化交付层”。

我们一个接一个地收到了来自人力资源部(HR)的日历邀请。

没有绩效改进计划(PIP)。没有警告。只有“感谢您这段时间的付出”。

他们留下了 Kabir。

他们当然会留下他。

他现在负责了一半的基础设施。而且做得比我们整个团队过去做的还要好。

重构并非邪恶——它是合乎逻辑的 需要澄清的是:Kabir 并没有陷害我们。他没有游说反对任何人。他也没有要求组织缩减规模。

他只是让自己变得不可或缺,无法被裁掉。

在一家衡量每次部署投资回报率(ROI)的初创公司里,你不会裁掉那个能以 10 倍速度交付、成本却只有 1/5 的人。

我们被解雇不是因为我们差劲。 我们被解雇是因为他让我们看起来可有可无。

以下是一段取代了我们的代码示例

use axum::{Router, routing::get, Json};
use serde::Serialize;
use std::{sync::Arc, time::SystemTime};

#[derive(Serialize)]
struct Health {
    status: &'static str,
    uptime_seconds: u64,
}

async fn health_check(start_time: Arc<SystemTime>) -> Json<Health> {
    let uptime = SystemTime::now()
        .duration_since(*start_time)
        .unwrap_or_default()
        .as_secs();

    Json(Health {
        status: "ok",
        uptime_seconds: uptime,
    })
}

#[tokio::main]
async fn main() {
    let start_time = Arc::new(SystemTime::now());

    let app = Router::new().route(
        "/health",
        get({
            let start_time = start_time.clone();
            move || health_check(start_time.clone())
        }),
    );

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

是的,它很简洁。是的,它很快。

但这不是一个 Node.js 工程师可以轻松上手的。

学习曲线是陡峭的(vertical),而公司里没有其他人爬了上去。

我们的反思报告 回顾过去,问题出在这里——而且没有一条是关于 Rust 本身的:

  1. 我们忽视了这次重构: 我们把 Kabir 的重构当作一个个人项目。我们没有和他结对编程。我们没有阅读早期的提交记录。等我们意识到它已成为核心基础设施时,它已经上线运行了。
  2. 我们假设团队 > 人才: 我们以为文化、协作和流程最重要。但当预算吃紧时,公司不会问谁人好相处。他们问的是谁能毫无阻碍地交付成果。
  3. 我们没有学习新工具: 我们本有机会学习 Rust —— 或者至少足够理解它以便提供帮助。但我们留在了舒适区。这付出的代价比我们想象的要大得多。

最后一点思考 解雇我们的不是 Rust。

但一场没有团队共识的 Rust 重构,却可以改变团队本身的构成。

如果一个人在重构一切,而其他人还在写 Jira 工单,那么他们不仅仅是在提升吞吐量——他们是在重构组织结构图。

如果你正目睹这一切发生?

不要只是旁观。

去学习。去结对。去贡献。否则,你就有可能成为历史。


相关留言评论
昵称:
邮箱:
阅读排行