<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>工程哲学 on FXIO 技术博客</title><link>https://fxio.site/tags/%E5%B7%A5%E7%A8%8B%E5%93%B2%E5%AD%A6/</link><description>Recent content in 工程哲学 on FXIO 技术博客</description><generator>Hugo -- 0.157.0</generator><language>zh-cn</language><lastBuildDate>Mon, 17 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://fxio.site/tags/%E5%B7%A5%E7%A8%8B%E5%93%B2%E5%AD%A6/index.xml" rel="self" type="application/rss+xml"/><item><title>架构师的权衡艺术：从 Maven、pnpm 到 Go MVS，看三大生态的版本管理哲学</title><link>https://fxio.site/posts/note/maven-pnpm-go-version-philosophy/</link><pubDate>Mon, 17 Aug 2026 00:00:00 +0000</pubDate><guid>https://fxio.site/posts/note/maven-pnpm-go-version-philosophy/</guid><description>&lt;blockquote&gt;
&lt;p&gt;为什么三个生态面对同一个&amp;quot;版本冲突&amp;quot;问题，给出了截然相反的答案？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- more --&gt;
&lt;h2 id="那个让我加班到凌晨三点的依赖冲突"&gt;那个让我加班到凌晨三点的依赖冲突&lt;/h2&gt;
&lt;p&gt;去年一个周五晚上，生产环境突然爆出 &lt;code&gt;NoSuchMethodError&lt;/code&gt;。排查了两个小时，最终发现是一个同事在公共模块里升级了 Guava，而另一个模块还在用旧版 API。Maven 的&amp;quot;就近原则&amp;quot;在本地构建时选了新版，到了 fat jar 打包时又选了旧版——同一个项目，两种行为，取决于你从哪个子模块触发构建。&lt;/p&gt;
&lt;p&gt;那天晚上我一边修 bug 一边想：为什么 Go 的项目从来没遇到过这种事？为什么前端项目几百个 &lt;code&gt;node_modules&lt;/code&gt; 嵌套也没炸？&lt;/p&gt;
&lt;p&gt;答案藏在三个生态对同一个问题的不同哲学选择里。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="三种哲学强约束隔离共存与极简主义"&gt;三种哲学：强约束、隔离共存与极简主义&lt;/h2&gt;
&lt;h3 id="maven-的大一统宁可痛苦不要歧义"&gt;Maven 的&amp;quot;大一统&amp;quot;：宁可痛苦，不要歧义&lt;/h3&gt;
&lt;p&gt;Maven 的核心信条是&lt;strong&gt;一个项目里，同一个库只能有一个版本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这听起来很武断，但背后有深刻的理由。Java 运行在 JVM 上，类加载器的行为决定了：如果同一个类（全限定名相同）被两个不同版本的 jar 同时加载，轻则方法签名不匹配抛 &lt;code&gt;NoSuchMethodError&lt;/code&gt;，重则序列化反序列化直接炸掉。这不是&amp;quot;可能出问题&amp;quot;，而是&amp;quot;一定会出问题&amp;quot;。&lt;/p&gt;
&lt;p&gt;所以 Maven 选择了最暴力的策略——Dependency Mediation（依赖裁决）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- Maven 的冲突裁决逻辑（简化版） --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- 1. 路径最短优先 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- 2. 路径相同时，pom.xml 中先声明的赢 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- 3. 你可以在 dependencyManagement 里强行指定 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.guava&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;guava&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;33.0.0-jre&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- 我说用哪个就用哪个 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种策略的代价是&lt;strong&gt;升级是一场战争&lt;/strong&gt;。你不能只升级直接依赖，还必须处理所有传递依赖的兼容性。大厂的 Java 项目每年花大量时间在&amp;quot;依赖版本升级周&amp;quot;上，这不是笑话，是真实的企业级成本。&lt;/p&gt;</description></item></channel></rss>