pyproject.toml 示例

长安码客
2025-06-27 23:34
阅读 208

从一个构建慢到崩溃的项目说起:效率提升的一些思考

从一个构建慢到崩溃的项目说起:效率提升的一些思考

我第一次真正感受到“效率”这个词在软件工程中的分量,是在两年前接手的一个老项目。那是一个内部使用的数据分析平台,代码结构杂乱,依赖项多得令人窒息,每次本地构建都需要10分钟以上。而部署流程更是复杂,CI/CD 的脚本像是上古遗留物一样,稍有风吹草动就报错不断。

那时候我们团队还在用传统的 Jenkins Pipeline,但因为项目的模块拆分不清晰、版本控制混乱,CI 每次跑测试都要拉取整个仓库、安装所有依赖,最终导致一次完整的流水线执行竟然要花费将近半小时。更头疼的是,经常在构建中途卡住或者失败,还得手动登录服务器去排查日志,一天下来光是等构建结果的时间就够让人抓狂的。

我当时就在想,如果连最基础的开发效率都无法保障,谈何敏捷开发?谈何快速迭代?

这篇文章就是从那次经历出发,讲一讲我在过去几年中关于“效率提升”的一些真实思考与实践——不是纸上谈兵,而是从踩过的坑、走过的弯路、以及真正起效的方案中总结出来的经验。


背景:那个让人崩溃的项目

这个项目的前身是一个简单的 Flask 后端服务,后来随着功能扩展,慢慢演变成了一个多模块架构。前端用了 React + Webpack,后端则是 Python + Flask + 多个微服务,中间还夹杂着几个 Go 编写的离线任务处理器和 ETL 工具。

刚开始我只是负责其中一个子系统的优化工作,但很快我就意识到问题远比想象中复杂:开发者的等待时间太长了

  • 本地启动:5分钟起步(npm install + pip install)
  • 代码改动热重载延迟:有时候要等好几秒
  • CI 构建耗时:Jenkins 跑一次完整 pipeline 超过30分钟
  • 部署不稳定:环境差异大,经常出现“在我机器上能跑”的问题

这些问题严重影响了我们的交付节奏,也导致了团队士气下滑。我们每周只能做两三次合并提交,怕一旦出问题就要花半天来调试,而且线上故障频发。

这让我下定决心要做点什么。


痛点分析:效率到底卡在哪?

我花了两周时间仔细梳理整个项目的开发流程,找出影响效率的几个关键环节:

1. 依赖管理混乱

前后端都存在大量冗余依赖,npm 依赖包没有按需加载,Python 的 requirements.txt 几乎包含了整个宇宙。甚至有些废弃的功能还在引用旧库,没人敢删。

2. 构建流程低效

Webpack 构建配置臃肿,很多 loader 都可以精简,而 Python 项目的打包工具还在使用 setup.py 和手动管理的 requirements 文件,导致每次 build 都需要全量安装。

3. CI/CD 流程冗长

Jenkins 流水线脚本几乎没人维护,每个 stage 都是从头开始拉代码、装依赖、跑测试,完全缺乏缓存机制,也没有并行能力。

4. 环境配置繁琐

开发、测试、生产环境之间的配置差异大,部署时常因路径或权限问题报错。Docker 使用不当,镜像臃肿且构建慢。

5. 缺乏监控与反馈机制

出现问题后很难快速定位。构建失败的日志散落在多个地方,没有人系统性地归档或分析。


解决方案:从重构构建到引入新工具链

针对上述问题,我和团队一起制定了一个为期两个月的“效率攻坚计划”,目标是将整体开发周期压缩50%以上。

1. 模块化重构 + 依赖清理(前端篇)

React 前端部分我们做了两件事:

  • 使用 lerna 进行 monorepo 管理,把不同功能模块拆分成 packages,明确边界。
  • 利用 depcheck 分析无用依赖,删除了将近30%的 npm 包。

同时优化了 Webpack 构建配置,增加了 cache-loader、HappyPack 来提高编译速度,并启用了 production mode 的 tree-shaking:

// webpack.config.js
module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true,
    minimize: true,
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: {
          loader: 'cache-loader', // 提升构建速度
        },
        include: [path.resolve(__dirname, 'src')],
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      }
    ]
  }
}

重构后,前端本地启动时间从原来的 5 分钟缩短到了 1 分钟左右,热更新也变得几乎瞬间完成。

2. Python 项目模块解耦 + PyPI 私服搭建

Python 部分我们做了以下改造:

  • 将核心模块发布为私有 PyPI 包,其他服务通过 pip 安装方式引用,避免重复打包。
  • 替换了 setup.py 方式,统一使用 pyproject.toml + Poetry 进行依赖管理。
  • 使用 pip-tools 锁定依赖版本,减少冲突可能。
[tool.poetry]
name = "core-service"
version = "0.1.0"
description = ""

[tool.poetry.dependencies]
python = "^3.9"
fastapi = "^0.68.0"
uvicorn = "^0.15.0"

[tool.poetry.dev-dependencies]
pytest = "^6.2"

这一改动使得每个服务的构建过程只关注自己的依赖,不再需要拉取整个项目的 package,CI 上的构建时间明显缩短。

3. CI 流水线升级:GitLab CI + 缓存 + 并行构建

我们决定将 Jenkins 替换为 GitLab CI,原因有几个:

  • Jenkins 维护成本高,脚本风格老旧
  • GitLab CI 对于我们已有的 GitLab 托管仓库支持更好
  • 支持更好的缓存策略和并行任务配置

比如下面这个 .gitlab-ci.yml 片段展示了如何利用缓存加速安装阶段:

image: python:3.9-slim

stages:
  - build
  - test
  - deploy

build_frontend:
  script:
    - npm ci
    - npm run build
  cache:
    key: frontend-deps
    paths:
      - node_modules/

build_backend:
  script:
    - pip install poetry
    - poetry config virtualenvs.create false
    - poetry install --no-dev
  cache:
    key: backend-deps
    paths:
      - .venv/

test_all:
  parallel:
    matrix:
      - TEST_SUITE: ["unit", "integration"]
  script:
    - pytest tests/${TEST_SUITE}

有了缓存机制之后,后续构建不需要每次都下载依赖,极大加快了流程。再加上并行运行单元测试和集成测试,整个流程从原来的 30+ 分钟缩减到了 7~8 分钟。

4. Docker 镜像瘦身 + BuildKit 加速

早期我们的 Dockerfile 写得很原始:

FROM python:3.9
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]

这种方式最大的问题是每一步 RUN 都会产生一层镜像,且无法复用已有层,导致构建特别慢。

于是我们改成了多阶段构建的方式:

# 构建阶段
FROM python:3.9-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt

# 最终镜像
FROM python:3.9-alpine
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]

配合 BuildKit 开启并发构建:

export DOCKER_BUILDKIT=1
docker build --target builder ...

镜像体积从原来的 800MB 减到了不到 100MB,构建时间也缩短了至少一半。


踩坑经验分享:那些你以为没问题但其实会拖累效率的事

❌ 一开始没考虑缓存策略,走了回头路

我们在最初尝试 GitLab CI 的时候,没注意缓存配置。结果发现每次还是要重新安装依赖,浪费时间不说,网络不稳定时还会失败。后来才明白缓存是加速的关键。

⚠️ 不要盲目追求“全栈单体”,模块化才是长期主义

曾经为了“节省构建次数”,我们一度把多个服务打包在一起构建,结果后期修改某个小功能都必须触发全部流程。后来切回模块化设计,才体会到“按需构建”的好处。

🧩 技术债不是不能留,但要留下可追踪的记录

我们有个遗留的 Shell 脚本用来做数据初始化,它藏在一个不起眼的目录里。某天有人不小心把它删掉了也没人发现,直到上线时才发现数据库字段不对。后来我们强制要求所有构建相关脚本都纳入版本管理,并加入 CI 流程中自动检查。


效果与收益:效率真的能被量化

经过两个月的努力,我们最终取得的效果如下:

指标 改造前 改造后
本地启动时间 ~5min ~1min
单次 CI 构建时间 >30min ~8min
线上部署频率 每周 1~2 次 每天 1~2 次
团队平均每天有效开发时间 4h 6.5h

版本控制工具使用-1

这些数字背后带来的变化不只是快了几分钟,更重要的是:

  • 提高了开发者信心:大家不再害怕改动代码,敢于提交 Pull Request。
  • 提升了交付频率:我们能更快响应业务需求,不再是“每次上线都像渡劫”。
  • 减少了人为错误:自动化程度高了,人工干预少了,出错率自然下降。

我的经验建议:效率提升的本质是什么?

这几年干下来,我对“效率提升”这件事的理解越来越深刻。它并不是简单地用个更快的构建工具、或者写个更好的 CI 脚本,而是一种系统性的工程思维。

✅ 系统性 > 工具本身

你可以用最快的机器、最先进的工具,但如果流程设计不合理,依然无法高效。真正的效率来自对整个开发链路的理解和优化。

✅ 沟通成本 > 技术选型之争

在技术选型过程中,我们曾为是否采用 Lerna 还是 Nx、Poetry 还是 Pipenv 争执不下。后来发现,真正重要的是团队能否达成共识、文档是否清晰、流程是否易于维护。

✅ 可观测性是长期保障

我们后来在项目中加了一个简单的 dashboard,用于显示每次构建的耗时分布、失败率等指标。虽然只是一个静态页面,但它帮助我们及时发现了几个隐藏的性能瓶颈。


总结:效率不是目的,而是手段

回顾这段经历,我更加坚信一个道理:

优秀的工程师不会一味追求更快的技术,而是懂得用系统化的思维去解决问题。

每一个构建慢、部署卡、调试难的问题背后,其实都是一个个设计上的疏忽或技术债的堆积。而真正的“效率提升”从来不是一个命令就能解决的事,而是需要从流程、工具、协作、文化等多个维度去综合考量。

希望我的经历能给你带来一点启发。如果你也正在被“构建慢”、“部署烦”折磨,不妨静下心来,看看你所在的项目有没有类似的影子。

也许你只需要一个小小的重构、一个合理的缓存配置,或是干脆换个思路来看待“自动化”这件事。

别急,先把“效率”这两个字放回到你的日常思考中去。


如你想进一步了解某些工具的具体配置方式,欢迎留言交流。我也整理了一些常用的 CI 模板和 Dockerfile 实践指南,有需要的话我可以分享出来。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝