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

我第一次真正感受到“效率”这个词在软件工程中的分量,是在两年前接手的一个老项目。那是一个内部使用的数据分析平台,代码结构杂乱,依赖项多得令人窒息,每次本地构建都需要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 |

这些数字背后带来的变化不只是快了几分钟,更重要的是:
- 提高了开发者信心:大家不再害怕改动代码,敢于提交 Pull Request。
- 提升了交付频率:我们能更快响应业务需求,不再是“每次上线都像渡劫”。
- 减少了人为错误:自动化程度高了,人工干预少了,出错率自然下降。
我的经验建议:效率提升的本质是什么?
这几年干下来,我对“效率提升”这件事的理解越来越深刻。它并不是简单地用个更快的构建工具、或者写个更好的 CI 脚本,而是一种系统性的工程思维。
✅ 系统性 > 工具本身
你可以用最快的机器、最先进的工具,但如果流程设计不合理,依然无法高效。真正的效率来自对整个开发链路的理解和优化。
✅ 沟通成本 > 技术选型之争
在技术选型过程中,我们曾为是否采用 Lerna 还是 Nx、Poetry 还是 Pipenv 争执不下。后来发现,真正重要的是团队能否达成共识、文档是否清晰、流程是否易于维护。
✅ 可观测性是长期保障
我们后来在项目中加了一个简单的 dashboard,用于显示每次构建的耗时分布、失败率等指标。虽然只是一个静态页面,但它帮助我们及时发现了几个隐藏的性能瓶颈。
总结:效率不是目的,而是手段
回顾这段经历,我更加坚信一个道理:
优秀的工程师不会一味追求更快的技术,而是懂得用系统化的思维去解决问题。
每一个构建慢、部署卡、调试难的问题背后,其实都是一个个设计上的疏忽或技术债的堆积。而真正的“效率提升”从来不是一个命令就能解决的事,而是需要从流程、工具、协作、文化等多个维度去综合考量。
希望我的经历能给你带来一点启发。如果你也正在被“构建慢”、“部署烦”折磨,不妨静下心来,看看你所在的项目有没有类似的影子。
也许你只需要一个小小的重构、一个合理的缓存配置,或是干脆换个思路来看待“自动化”这件事。
别急,先把“效率”这两个字放回到你的日常思考中去。
如你想进一步了解某些工具的具体配置方式,欢迎留言交流。我也整理了一些常用的 CI 模板和 Dockerfile 实践指南,有需要的话我可以分享出来。

评论 0