生产环境灰度发布必须依靠功能开关(Feature Flag)而非composer update,因后者会重算整个依赖树,无法实现精准流量控制;Composer仅负责稳定安装SDK及其依赖,真正的灰度逻辑需在应用层通过SDK的getTreatment()等接口实现分支控制。

生产环境不能靠 composer update 做灰度发布,它改的是整个依赖树,不是功能开关。 真正可控的灰度,必须落在应用层逻辑里——用功能开关(Feature Flag)驱动行为分支,而 Composer 只负责把 SDK 和支撑工具稳稳装进来。
为什么 composer update 不是灰度手段
它本质是重新解析全部依赖约束,哪怕只动了一个包的版本号,composer.lock 里几十个间接依赖都可能连带更新。这和“让 5% 用户走新支付网关”这种精准控制完全不匹配。
- 执行
composer update vendor/package仍会触发全图重算,尤其当该包被其他包深度依赖时 -
composer install从 lock 文件还原,根本没“灰度”这个语义,只有“全量一致” - PHP 进程无共享内存,无法靠加载不同版本类库实现运行时分流
真正起作用的是功能开关 SDK + Composer 安装
像 splitsoftware/split-sdk-php 这类工具,才是生产灰度的执行单元。Composer 的角色只是把它可靠地放进项目,并确保其依赖(如 predis/predis、guzzlehttp/guzzle)版本稳定。
- 安装时加
--no-dev:避免把测试类库(如phpunit/phpunit)打进生产镜像 - 用显式小版本锁定核心 SDK:
"splitsoftware/split-sdk-php": "7.2.1",防止 patch 升级引入非预期变更 - 必须配缓存适配器(如 Redis),否则每次请求都走 HTTP 调用 Split.io API,延迟不可控
composer.lock 必须进 Git,且禁止手动编辑
它是灰度稳定的基石。CI/CD 流水线跑 composer install --no-dev 时,所有环境(包括灰度集群)都必须基于同一份 lock 文件构建。
- 删掉
composer.lock再install= 主动放弃一致性,等于给灰度埋雷 - 团队中有人
update后提交了新 lock,其他人install就会拿到不同版本组合 —— 表面没改代码,实际行为已漂移 - 灰度期间若需验证某依赖修复,应
composer update --lock更新哈希与时间戳,而非升级版本
依赖版本策略要配合发布节奏
灰度不是靠换 SDK 版本,而是靠 SDK 控制业务逻辑路径;但 SDK 自身稳定性,取决于你如何约束它的生态。
- 对 SDK 的间接依赖(如
monolog/monolog),用"^2.9"比"^2.0"更安全,缩小 patch 影响面 - 禁用
require-dev中的调试工具在生产启用,比如symfony/var-dumper若被意外加载,可能暴露敏感数据 - 定期
composer outdated --direct检查核心包是否滞后,比盲目update更适合灰度维护期
灰度的关键不在 Composer 命令怎么敲,而在你有没有把功能开关逻辑写进条件分支里、有没有让 SDK 的 client->getTreatment() 调用真正决定流量走向——Composer 只管把那个 getTreatment() 函数稳稳送到你面前。











