从单体 CLI 到模块化插件:应用绞杀树模式

发布: (2025年12月4日 GMT+8 11:41)
6 min read
原文: Dev.to

I’m happy to help translate the article, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you’ve already provided) here? Once I have it, I’ll translate it into Simplified Chinese while preserving the formatting, markdown, and technical terms as requested.

TL;DR – 安全迁移策略

  • 将命令提取到 外部插件(以 beta 形式发布)
  • 在核心中将插件添加为 依赖(v2.x – 零破坏性更改)
  • 收集反馈,迭代,稳定
  • 移除依赖 在 v3.0.0(破坏性更改,但已做好准备)
  • 用户仅安装 他们需要的部分

结果:安全迁移 + 独立插件演进

问题:CLI 如何变得难以维护

Your CLI probably started like this:

cli/
├── commands/
│   ├── deploy.js
│   └── status.js
├── utils/
│   └── helpers.js
└── index.js

干净。简洁。一切井然有序。

Six months later:

cli/
├── commands/      (37 files)
├── utils/         (23 files)
├── services/      (15 files)
├── integrations/  (12 files)
└── shared/        (who even knows?)

Now you’re dealing with:

  • 紧耦合 – 每个命令都会导入 10+ 个工具文件
  • 共享状态噩梦 – 全局配置被所有东西触及
  • 风险发布 – 一处改动需要测试整个 CLI
  • 开发缓慢 – 添加功能需要数周的协调
  • 破坏性变更地狱 – 无法在不破坏其他部分的情况下演进某一部分

Sound familiar? 你已经构建了一个单体系统。

后端课程:从单体到模块化

旧方式(单体 API)

Everything in one codebase

Tightly coupled domains

Coordinated releases

High‑risk deployments

Slow feature velocity

现代方式(微服务 / 模块化)

Thin API Gateway

Isolated domain services

Independent deployments

Versioned contracts

Fast, safe iterations

关键洞见: 隔离领域,建立清晰边界,支持独立演进。这同样适用于 CLI。

解决方案:核心 + 插件 架构

轻量核心

Your core should be boring and stable:

core/
├── auth/           // Auth logic
├── config/         // Config management
├── dispatcher/     // Command routing
├── utils/          // Cross‑cutting concerns
└── plugin-loader/  // Plugin discovery

核心提供基础设施,并且不干扰业务逻辑。

插件

Each plugin is a self‑contained domain:

plugins/
├── deploy-plugin/
│   ├── commands/
│   ├── tests/
│   ├── README.md
│   └── package.json   // Independent versioning!
├── logs-plugin/
└── backup-plugin/

每个插件:

  • 只承担唯一职责
  • 拥有自己的语义化版本号
  • 可独立安装(npm i @cli/deploy-plugin
  • 变更时不影响其他插件
  • 在完全隔离的环境中进行测试

Source:

迁移策略:绞杀树模式

绞杀树模式(Martin Fowler)让你能够逐步迁移,而无需冒险进行一次性的大改写。

步骤 1 – 以 Beta 版发布外部插件

# Release your first plugin in beta
npm publish @my-cli/deploy-plugin@1.0.0-beta.1
npm publish @my-cli/logs-plugin@1.0.0-beta.1

为什么是 beta? 用户可以自行选择加入测试,你可以快速迭代,且破坏性变更是可以预期的。

// deploy-plugin/package.json
{
  "name": "@my-cli/deploy-plugin",
  "version": "1.0.0-beta.1",
  "main": "dist/index.js",
  "peerDependencies": {
    "@my-cli/core": "^2.0.0"
  }
}

步骤 2 – 在 Core 中临时将插件作为依赖添加

// core/package.json
{
  "name": "@my-cli/core",
  "version": "2.5.0",
  "dependencies": {
    "@my-cli/deploy-plugin": "^1.0.0-beta.1",
    "@my-cli/logs-plugin": "^1.0.0-beta.1"
  }
}
  • 用户安装 core → 插件会自动随之安装
  • 对已有用户没有破坏性变更
  • 插件可以独立使用,方便早期采用者

步骤 3 – 逐步迁移命令

v2.5.0(初始 Beta)

deploy → Plugin (bundled as dependency)
logs   → Legacy in core
backup → Legacy in core

v2.8.0(Beta 2)

deploy → Plugin (bundled as dependency)
logs   → Plugin (bundled as dependency)
backup → Legacy in core

每一步都安全发布,用户感受不到差异,插件在真实环境中逐渐成熟。

步骤 4 – 大切换:移除依赖(v3.0.0)

// core/package.json (v3.0.0)
{
  "name": "@my-cli/core",
  "version": "3.0.0",
  "dependencies": {
    // No more plugin dependencies!
  },
  "peerDependencies": {
    // Plugins are now optional
  }
}

用户迁移指南

# Old way (v2.x) – everything bundled
npm install -g @my-cli/core

# New way (v3.x) – install what you need
npm install -g @my-cli/core
npm install -g @my-cli/deploy-plugin   # 只在需要 deploy 时安装
npm install -g @my-cli/logs-plugin     # 只在需要 logs 时安装

为什么是大版本号: 破坏性变更——插件不再自动安装,用户拥有更多控制权,安装体积更小,安装速度更快。

步骤 5 – 用户按需安装

# Minimal installation (just core utilities)
npm install -g @my-cli/core

# À la carte (only what I use)
npm install -g @my-cli/core @my-cli/deploy-plugin

# Everything (opt‑in to all plugins)
npm install -g @my-cli/all   # metapackage

收益

  • 安装体积更小(例如 15 MB 对比 250 MB)
  • 启动速度更快
  • 每个插件可以独立演进
  • 发布更安全、更可控

通过把你的 CLI 当作微服务生态系统来对待,并运用绞杀树模式,你可以在最小风险和最大灵活性的前提下,从单体代码库平滑过渡到模块化、插件驱动的架构。

Back to Blog

相关文章

阅读更多 »

让终端逐像素变得更美

2025年11月13日 我们很高兴宣布对 Gemini CLI 用户体验进行重大升级,使您的终端交互更加健壮、直观,并且…