<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>我的编程之路</title>
		<link>http://localhost:1313/</link>
		<description>Recent content on 我的编程之路</description>
		<generator>Hugo</generator>
		<language>zh-CN</language>
		
		
		
		
			<lastBuildDate>Sat, 10 Oct 2026 00:00:00 +0800</lastBuildDate>
		
			<atom:link href="http://localhost:1313/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>博客上线「图书馆」模块：在线读 EPUB / PDF &#43; 下载</title>
				<link>http://localhost:1313/posts/2026-10-09-library-launch/</link>
				<pubDate>Fri, 09 Oct 2026 12:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-10-09-library-launch/</guid>
				<description>&lt;p&gt;博客新增 &lt;strong&gt;图书馆&lt;/strong&gt; 模块，可以：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在线阅读 &lt;strong&gt;EPUB&lt;/strong&gt;（基于 Epub.js，支持翻页 / 目录 / 进度记忆）&lt;/li&gt;&#xA;&lt;li&gt;在线阅读并一键下载 &lt;strong&gt;PDF&lt;/strong&gt;（浏览器原生查看器，零依赖）&lt;/li&gt;&#xA;&lt;li&gt;每本书同时提供 EPUB / PDF 两种格式的下载&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;前往 &lt;a href=&#34;http://localhost:1313/library/&#34;&gt;图书馆&lt;/a&gt; 看看示例书，或直接阅读 &lt;a href=&#34;http://localhost:1313/library/sample-book/&#34;&gt;Sample Technical Book&lt;/a&gt;。&lt;/p&gt;</description>
			</item>
			<item>
				<title>部署器开源记 · 第 1 篇 · 发版那五步，我为什么没做成命令行</title>
				<link>http://localhost:1313/posts/2026-10-01-deployer-os01-five-steps/</link>
				<pubDate>Thu, 01 Oct 2026 14:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-10-01-deployer-os01-five-steps/</guid>
				<description>三条约束怎么推出一条固定执行链</description>
			</item>
			<item>
				<title>开发随笔 · 第 1 篇 · 说一个「等多久才算久」的事</title>
				<link>http://localhost:1313/posts/2026-10-01-deployer-es04-how-long-is-long/</link>
				<pubDate>Thu, 01 Oct 2026 00:25:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-10-01-deployer-es04-how-long-is-long/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 1 / 3 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/deployer-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-10-01-deployer-es05-backed-up-but-cannot-go-back/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;这套程序里有几个动作是发出去之后要等的。让一个网站停下来，等它真的停了才能换文件；让一批文件复制完，等它们都落地了才能继续。&lt;/p&gt;&#xA;&lt;p&gt;这类「等」的地方一共有 8 处，我全部用了同一套办法，每隔一小会儿去看一眼状态，成了就往下走，一直没成就等到某个时间上限，然后放弃。&lt;/p&gt;&#xA;&lt;h2 id=&#34;两个数字&#34;&gt;两个数字&lt;/h2&gt;&#xA;&lt;p&gt;8 处用的时间上限只有两个。&lt;/p&gt;&#xA;&lt;p&gt;网站和应用相关的那 4 处，我给的是 30 秒。文件相关的那 4 处，我给的是 30 分钟。&lt;/p&gt;&#xA;&lt;p&gt;两个数字差了 60 倍。&lt;/p&gt;&#xA;&lt;p&gt;这不是随手填的。网站启停正常情况下一两秒就好，30 秒是留给异常情况的余量，超过 30 秒基本可以断定是卡住了，再等下去没意义。文件不一样，文件多的时候复制真的要跑很久，半小时都不算离谱。&lt;/p&gt;&#xA;&lt;p&gt;所以这两档我心里是有数的。我不满意的是另外两件事。&lt;/p&gt;&#xA;&lt;p&gt;这两个数字还有个问题，它们躺在代码里。8 处里，4 处直接写着「30000」，4 处直接写着「1800000」。没有常量，没有说明。&lt;/p&gt;&#xA;&lt;p&gt;我当初要是想改其中一档，得记得有 4 个地方要一起改。这种事最容易漏，漏一处之后，同一个动作在不同地方的上限就不一样了，而这种不一致很难查。&lt;/p&gt;&#xA;&lt;p&gt;这个毛病不严重，但它属于「我明知故犯」的那类。写的时候就知道该提成常量，觉得就 4 个地方，先这样。&lt;/p&gt;&#xA;&lt;h2 id=&#34;等完了我没看结果&#34;&gt;等完了，我没看结果&lt;/h2&gt;&#xA;&lt;p&gt;还有一处是真的问题。&lt;/p&gt;&#xA;&lt;p&gt;等待那套办法里有个关键判断，到底是「在规定时间内等到了」还是「干等到上限也没等到」。程序里明明能拿到这个结果，我把它存在一个变量里，然后&lt;strong&gt;从头到尾没用过&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;真正决定成败的是另一句，等结束之后，不管是怎么结束的，再去看一眼状态。成了就算成功，没成就算超时。&lt;/p&gt;&#xA;&lt;p&gt;大多数时候这两句的结果一样，所以四年没出事。但它们的含义不一样。前者是「在时间之内完成了」，后者是「我看的最后一眼它是完成的」。有一种情况会分叉，确实等到了上限，而在超时的那一瞬间状态刚好变成完成，这时候按第二句会报成功。&lt;/p&gt;&#xA;&lt;p&gt;对网站启停来说，报成功其实无害，它确实起来了。但这件事的后果是，&lt;strong&gt;「超时」这个结果不能当作「事情没做成」来用。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;我当时大概是先写了前一句，后来改成后一句的方案，忘了把前面那个变量删掉。留着它，看起来像我在用它，其实没有。&lt;/p&gt;&#xA;&lt;h2 id=&#34;我现在的看法&#34;&gt;我现在的看法&lt;/h2&gt;&#xA;&lt;p&gt;等待的时间上限是个承诺。你写 30 秒，就是在说「我承诺 30 秒内这件事会有结果，没有就是出问题了」。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 1 篇 · 说一个代码里混进的不该有的词</title>
				<link>http://localhost:1313/posts/2026-09-30-wml-es02-word-that-shouldnt-be-there/</link>
				<pubDate>Wed, 30 Sep 2026 17:30:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-wml-es02-word-that-shouldnt-be-there/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 1 / 5 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/witsml-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-wml-es03-switch-that-rules-everyone/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;有回我打开一个算法相关的文件夹，里面整整齐齐排着几个策略文件，名字都规规矩矩。唯独其中一个，名字里夹了一句不该说的话，不是技术词，是那种平时不会当着同事面说出口的词，被原样写进了文件名，还提交进了仓库。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Strategy/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├─ AbstractAlgorithmStrategy.cs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├─ AlgorithmMode.cs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├─ InterpolateAlgorithmStrategy.cs&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─ ▓▓▓▓▓AlgorithmStrategy.cs        ← 这一行的名字，我没法原样敲进文章&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我盯着那个文件名愣了几秒。它就这样挂在目录里，谁能看见谁尴尬，但没人撕掉它。&lt;/p&gt;&#xA;&lt;p&gt;我没法确定当时是谁、因为什么写下的。比较可能的情况是，他写代码写到烦躁，顺手吐了个槽，存成了文件名，当时觉得好玩，或者单纯没多想。类似的随手我在别处也见过，注释里骂一句、变量名叫 &lt;code&gt;temp2&lt;/code&gt;、提交说明写「又是这破玩意儿」。&lt;/p&gt;&#xA;&lt;p&gt;那时候我对这类事的态度是，只要不影响运行，就别上纲上线。代码能跑，名字难听点算什么。现在回想，这个态度本身就挺危险。&lt;/p&gt;&#xA;&lt;p&gt;真正让我改观的是把这个项目交给别人接手那次。新人 clone 下来，打开目录，第一眼就看见那个词。他没说什么，但我看得出来，那一瞬间他对这个团队的专业度打了个折扣。&lt;/p&gt;&#xA;&lt;p&gt;而且这种东西特别难收。它进了文件名，就被引用、被搜索、被记进历史。你想悄悄删，得先确认没有别处依赖它，还要改提交记录，比当初随手写它麻烦一百倍。我顺着引用去找了一下，它不只躺在文件名里，调度的地方也写着它（等价改写，名字用掩码代替）：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mode&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AlgorithmMode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Interpolate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;strategies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;InterpolateAlgorithmStrategy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;strategies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;▓▓▓▓▓&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AlgorithmStrategy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 名字再难看，也得照着写&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说，任何一个读这段代码的人，都会在那个 else 分支上正面撞见它一次。文件名可以装作没看见，调用点是躲不开的。&lt;/p&gt;&#xA;&lt;p&gt;代码是会说话的。它不只跟机器说怎么运行，也跟每一个后来者说写它的人是什么样。一个不雅的名字传出去的不是幽默，是不专业，和一种「这里没人认真看」的信号。&lt;/p&gt;&#xA;&lt;p&gt;还有一点我后来越想越在意。那个词一定是某次评审里大家都没吱声、放过去的，如果当时有人说了句「这名字咱换个吧」，它就不会挂到现在。命名是协作的门面，不是私人的发泄口。别把随意当个性，真正专业的随意，是连随意都收拾得干干净净。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 1 篇 · 说一个把名字写错的事</title>
				<link>http://localhost:1313/posts/2026-09-30-es01-spelling/</link>
				<pubDate>Wed, 30 Sep 2026 13:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es01-spelling/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 1 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es03-previous/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻一套工业数据 WebApi 的源码时，我先被一个拼写卡住了。&lt;/p&gt;&#xA;&lt;p&gt;项目里有个业务层目录，叫 &lt;code&gt;Bussiness&lt;/code&gt;。多了一个 s。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）它长这样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;namespace&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Contoso.WebApi.Bussiness&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// 多了一个 s&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;BussinessController&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这一个错，不是只在目录名里。全仓搜一下，这个拼错的 &lt;code&gt;Bussiness&lt;/code&gt; 命中 &lt;strong&gt;3985&lt;/strong&gt; 处。一个手滑的单词，被复制了近四千次。&lt;/p&gt;&#xA;&lt;h2 id=&#34;它是怎么蔓延的&#34;&gt;它是怎么蔓延的&lt;/h2&gt;&#xA;&lt;p&gt;命名这东西，第一次写错是偶然，后面全是被&amp;quot;照着抄&amp;quot;。新模块要引用业务层，IDE 自动补全给的就是那个错的名字，复制粘贴的人根本不会多看一眼。错的命名一旦进了公共接口，就成了契约——下游所有调用方都得跟着拼错，改一个就编译不过一片。&lt;/p&gt;&#xA;&lt;p&gt;我没法确认是谁第一次多打了那个 s，也没法确认当时为什么没拦住。但更可能的情况是：早期没有命名规范检查，第一个目录名定下来，后面就都顺着它走了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;代价是具体的&#34;&gt;代价是具体的&lt;/h2&gt;&#xA;&lt;p&gt;近四千处，意味着这个错已经不是&amp;quot;改一个文件&amp;quot;能解决的。它是地基里的错字，上面盖了多少层，就牵连多少层。&lt;/p&gt;&#xA;&lt;p&gt;更隐蔽的是信任。新人看到 &lt;code&gt;Bussiness&lt;/code&gt;，第一反应是&amp;quot;原来你们就这么叫&amp;quot;，于是他也这么写、这么读。错的命名会被当成正确的术语传播，等有人想纠正，阻力已经不是技术，是&amp;quot;大家都这么用&amp;quot;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;改起来比想象中费劲&#34;&gt;改起来比想象中费劲&lt;/h2&gt;&#xA;&lt;p&gt;我后来给这套代码配了命名分析器，把 &lt;code&gt;Bussiness&lt;/code&gt; 这类指纹列进禁止列表，新代码一写就标红。存量那近四千处，靠一次性的重命名加编译验证慢慢收。最关键的一步其实不是改，是让这个错不再被复制。&lt;/p&gt;&#xA;&lt;p&gt;一个单词的拼写，决定不了程序跑不跑得起来。但它会决定，后来读这段代码的人，是更容易相信这里，还是先疑神疑鬼。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 1 篇 · 读一段没写 Sleep 的采集线程</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es01-busy-wait/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es01-busy-wait/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 1 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es02-noise-is-picked-not-smeared/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻一台常年开在工业现场的测控设备配套程序时，我先是被一段循环卡住了。&lt;/p&gt;&#xA;&lt;p&gt;那段采集线程的主循环长这样（等价改写，名字换掉了）：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_stopRequested&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_acquiring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;samples&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_device&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ReadAllValues&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;_buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;samples&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 没有 else，也没有 Sleep&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;条件不满足的时候，它在做一件事：以能达到的最快速度，反复问要不要采。没活的时候也不歇。而且它的优先级是 &lt;code&gt;Highest&lt;/code&gt;，UI 线程想刷个界面都得排在它后面。&lt;/p&gt;&#xA;&lt;p&gt;我顺手跑了三条命令确认范围：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grep -rn &lt;span class=&#34;s2&#34;&gt;&amp;#34;Priority = ThreadPriority&amp;#34;&lt;/span&gt; --include&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;*.cs .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grep -rn &lt;span class=&#34;s2&#34;&gt;&amp;#34;\.Suspend()&amp;#34;&lt;/span&gt; --include&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;*.cs .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grep -rn &lt;span class=&#34;s2&#34;&gt;&amp;#34;Thread.Abort&amp;#34;&lt;/span&gt; --include&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;*.cs .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;命中结果是优先级拉满 5 处，&lt;code&gt;Suspend&lt;/code&gt; 4 处，&lt;code&gt;Abort&lt;/code&gt; 4 处。C# / WPF、net48，全仓 306 个文件、3.2 万行。&lt;/p&gt;&#xA;&lt;p&gt;我没法确认作者当时怎么想，但更可能的原因是这个：&lt;code&gt;Sleep(1)&lt;/code&gt; 在 Windows 上可能实际睡到 10 多毫秒，他担心采集延迟，于是干脆不睡。这个推演在&amp;quot;数据一直来&amp;quot;的时候成立，可设备大部分时间并不在采集，上料、下料、等待都占时间。一个为数据洪峰设计的循环，被放在九成时间没数据的场景里裸奔。&lt;/p&gt;</description>
			</item>
			<item>
				<title>部署器开源记 · 第 2 篇 · 十个工程，依赖只有一个方向：这份代码从哪读起</title>
				<link>http://localhost:1313/posts/2026-10-01-deployer-os02-project-layout/</link>
				<pubDate>Thu, 01 Oct 2026 09:20:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-10-01-deployer-os02-project-layout/</guid>
				<description>十个工程分五层，依赖只朝一个方向</description>
			</item>
			<item>
				<title>开发随笔 · 第 2 篇 · 说一个「备份做了却退不回去」的事</title>
				<link>http://localhost:1313/posts/2026-10-01-deployer-es05-backed-up-but-cannot-go-back/</link>
				<pubDate>Thu, 01 Oct 2026 00:30:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-10-01-deployer-es05-backed-up-but-cannot-go-back/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 2 / 3 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/deployer-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-10-01-deployer-es04-how-long-is-long/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-10-01-deployer-es06-who-pays-the-cost/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;这套程序升级之前会先备份。把要被替换的文件复制一份到备份目录，然后再动手。&lt;/p&gt;&#xA;&lt;p&gt;我一直觉得这一步挺稳妥的。直到我把它的三处细节连起来看了一遍。&lt;/p&gt;&#xA;&lt;h2 id=&#34;备份目录里只有最近一次&#34;&gt;备份目录里只有最近一次&lt;/h2&gt;&#xA;&lt;p&gt;备份的文件直接放进备份目录，不加任何区分。而下一次升级开始的时候，同样的文件会再复制一遍，名字一样，就把上一份盖掉了。&lt;/p&gt;&#xA;&lt;p&gt;结果是备份目录里永远只有「最近一次升级之前」的状态。上一次的、上上次的，全没了。&lt;/p&gt;&#xA;&lt;p&gt;有意思的是，我做归档的时候想到了这一层。脚本执行完要归档，归档目录我是用当天日期拼出来的，不同日子各存一份。备份这一步的写法跟它几乎一样，就差那一行日期。&lt;/p&gt;&#xA;&lt;p&gt;我漏掉了那一行。&lt;/p&gt;&#xA;&lt;p&gt;第二条是，失败了也算成功。复制一个文件，成功没成功我连看都没看；失败了抛异常，我只记一行，然后继续复制下一个，最后这一步照样算成功。&lt;/p&gt;&#xA;&lt;p&gt;所以备份一个文件都没拷成的时候，界面上是绿的。&lt;/p&gt;&#xA;&lt;p&gt;这一条我在另一篇里说过。放在这里说的意思不一样，它意味着&lt;strong&gt;你没法确认备份到底有没有做&lt;/strong&gt;。界面不会告诉你备份是空的，你要想知道，得自己打开备份目录去看。&lt;/p&gt;&#xA;&lt;h2 id=&#34;最根本的一条&#34;&gt;最根本的一条&lt;/h2&gt;&#xA;&lt;p&gt;前两条都能改，改起来都是几行的事。真正让我没法退回去的是第三条。&lt;/p&gt;&#xA;&lt;p&gt;备份的是文件。升级的时候改的不只是文件，还有数据库里的内容，那些脚本会往库里加表、加字段、插数据。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;数据库不在备份范围里。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;所以升级失败之后，就算文件全部退回了原样，库里已经跑掉的那部分改动还在，退不回去。而那些改动跟退回来的旧文件对不上，系统处在一个两边都不匹配的状态。&lt;/p&gt;&#xA;&lt;p&gt;更要命的是，脚本执行完要归档，归档没做的话脚本还留在待执行的地方。下次再升级，它们会被再执行一遍。加一列的脚本跑第二遍会报错，插一行数据的脚本跑第二遍会插出两行。&lt;/p&gt;&#xA;&lt;h2 id=&#34;我现在的看法&#34;&gt;我现在的看法&lt;/h2&gt;&#xA;&lt;p&gt;备份不是目的，能退回去才是。&lt;/p&gt;&#xA;&lt;p&gt;我当初做备份的时候，想的是「万一要还原，有东西可以拿」，这个想法本身没错。但我没有往下推一步，&lt;strong&gt;真出事的那天，我要怎么退？&lt;/strong&gt; 退哪些？退到哪一次？库怎么办？&lt;/p&gt;&#xA;&lt;p&gt;推一步就能发现，文件能退、库退不回，这个备份只能算做了一半。&lt;/p&gt;&#xA;&lt;p&gt;四年没出事，是因为升级失败的次数本来就少，少到没撞上这个缺口。这不是设计得好，是运气。&lt;/p&gt;&#xA;&lt;p&gt;现在要补的话，顺序我排好了，先让备份失败真的算失败，再给备份目录加日期，最后才考虑数据库那半边。前两条几行就能改，第三条要重做整个流程，得单独安排。&lt;/p&gt;&#xA;&lt;p&gt;这个工具本身后来开源出来写成了《部署器开源记 · 第 1 篇 · 发版那五步，我为什么没做成命令行》：&lt;a href=&#34;http://localhost:1313/posts/2026-10-01-deployer-os01-five-steps/&#34;&gt;发版那五步，我为什么没做成命令行&lt;/a&gt;。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 2 篇 · 说一个我图省事、结果管住所有人的开关</title>
				<link>http://localhost:1313/posts/2026-09-30-wml-es03-switch-that-rules-everyone/</link>
				<pubDate>Wed, 30 Sep 2026 17:30:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-wml-es03-switch-that-rules-everyone/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 2 / 5 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/witsml-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-wml-es02-word-that-shouldnt-be-there/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-wml-es04-machine-copied-code/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;早些年我负责一块挺重复的功能，好几种差不多的东西，都要走同一套「收请求、取数据、转格式、回结果」的流程。我嫌麻烦，不想给每种东西都重写一遍，就琢磨了个偷懒的法子，做一个通用的骨架，把流程里不变的那截固定住，把每种东西不一样的地方用一个开关推开。&lt;/p&gt;&#xA;&lt;p&gt;骨架长这样（下面是等价改写，名字都换过了）：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 骨架：写一遍，几种对象共用&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;internal&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ProviderTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TModel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TCriteria&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;OperationResult&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Retrieve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IProviderContext&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 变体：一种对象一个类，开关值写在类头上&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;[Handles(StoreObjectType.Log)]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;internal&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;sealed&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;LogProvider&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ProviderTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;...&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;新来一种东西，只要把开关拨到对应位置，骨架就自动知道该走哪条路。那时候我对「省事」的理解很朴素，能不写第二遍的绝不写第二遍，当时心里还挺得意。&lt;/p&gt;&#xA;&lt;p&gt;我没意识到的是，这个开关一旦立起来，就不再只为我服务了。后面的人接手新功能，第一件事就是去拨这个开关、按它规定的姿势写。我图的那点省事，变成了所有人必须遵守的写法。&lt;/p&gt;&#xA;&lt;p&gt;转机出现在一次排错。有个功能跑得不对，新人来问我，我顺着那个开关一路查，才发现它牵连的东西比我想的多得多。它串起了好几个层级，每一层都指望着开关给出正确的信号，开关一拨错，好几层一起歪。&lt;/p&gt;&#xA;&lt;p&gt;具体到代码，那个拨动只发生在一处，但它管着后面所有的事：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RetrieveResponse&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DispatchRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RetrieveRequest&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objectType&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 就这一查表，决定后面每一层走哪条路&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ProviderRegistry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Factory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TryGet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;objectType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;provider&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FailureResponse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Codes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;NotSupported&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;query not supported.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;provider&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Retrieve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SenderFor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- 开关分叉图 --&gt;&#xA;&lt;svg viewBox=&#34;0 0 680 300&#34; style=&#34;width:100%;height:auto;display:block;margin:1.6em 0&#34; role=&#34;img&#34; aria-label=&#34;一处查表把请求分到四个 Provider，开关值长在类头上&#34;&gt;&#xA;  &lt;rect x=&#34;0.5&#34; y=&#34;0.5&#34; width=&#34;679&#34; height=&#34;299&#34; rx=&#34;12&#34; style=&#34;fill:var(--bg-elevated);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;20&#34; y=&#34;26&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:11px&#34;&gt;一个开关值，决定后面每一层走哪条路&lt;/text&gt;&#xA;  &lt;rect x=&#34;20&#34; y=&#34;121&#34; width=&#34;120&#34; height=&#34;48&#34; rx=&#34;10&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--border-strong)&#34;/&gt;&#xA;  &lt;text x=&#34;80&#34; y=&#34;150&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:12px&#34;&gt;取数请求&lt;/text&gt;&#xA;  &lt;line x1=&#34;140&#34; y1=&#34;145&#34; x2=&#34;178&#34; y2=&#34;145&#34; style=&#34;stroke:var(--border-strong)&#34; stroke-width=&#34;1.2&#34; marker-end=&#34;url(#ar1)&#34;/&gt;&#xA;  &lt;defs&gt;&#xA;    &lt;marker id=&#34;ar1&#34; viewBox=&#34;0 0 10 10&#34; refX=&#34;9&#34; refY=&#34;5&#34; markerWidth=&#34;6&#34; markerHeight=&#34;6&#34; orient=&#34;auto&#34;&gt;&#xA;      &lt;path d=&#34;M0,0 L10,5 L0,10 z&#34; style=&#34;fill:var(--border-strong)&#34;/&gt;&#xA;    &lt;/marker&gt;&#xA;  &lt;/defs&gt;&#xA;  &lt;rect x=&#34;180&#34; y=&#34;107&#34; width=&#34;180&#34; height=&#34;76&#34; rx=&#34;10&#34; style=&#34;fill:none;stroke:var(--neon)&#34; stroke-width=&#34;1.4&#34;/&gt;&#xA;  &lt;text x=&#34;270&#34; y=&#34;136&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--neon);font-family:var(--mono);font-size:12px&#34;&gt;ProviderRegistry&lt;/text&gt;&#xA;  &lt;text x=&#34;270&#34; y=&#34;155&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11px&#34;&gt;按对象类型查一次&lt;/text&gt;&#xA;  &lt;text x=&#34;270&#34; y=&#34;172&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10.5px&#34;&gt;查不到就整体失败&lt;/text&gt;&#xA;  &lt;path d=&#34;M360,145 C395,145 395,61 428,61&#34; style=&#34;fill:none;stroke:var(--border-subtle)&#34; stroke-width=&#34;1.1&#34;/&gt;&#xA;  &lt;path d=&#34;M360,145 C395,145 395,117 428,117&#34; style=&#34;fill:none;stroke:var(--border-subtle)&#34; stroke-width=&#34;1.1&#34;/&gt;&#xA;  &lt;path d=&#34;M360,145 C395,145 395,173 428,173&#34; style=&#34;fill:none;stroke:var(--border-subtle)&#34; stroke-width=&#34;1.1&#34;/&gt;&#xA;  &lt;path d=&#34;M360,145 C395,145 395,229 428,229&#34; style=&#34;fill:none;stroke:var(--border-subtle)&#34; stroke-width=&#34;1.1&#34;/&gt;&#xA;  &lt;rect x=&#34;430&#34; y=&#34;40&#34; width=&#34;230&#34; height=&#34;42&#34; rx=&#34;9&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;444&#34; y=&#34;58&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11.5px&#34;&gt;WellProvider&lt;/text&gt;&#xA;  &lt;text x=&#34;444&#34; y=&#34;74&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10px&#34;&gt;[Handles(StoreObjectType.Well)]&lt;/text&gt;&#xA;  &lt;rect x=&#34;430&#34; y=&#34;96&#34; width=&#34;230&#34; height=&#34;42&#34; rx=&#34;9&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;444&#34; y=&#34;114&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11.5px&#34;&gt;WellboreProvider&lt;/text&gt;&#xA;  &lt;text x=&#34;444&#34; y=&#34;130&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10px&#34;&gt;[Handles(StoreObjectType.Wellbore)]&lt;/text&gt;&#xA;  &lt;rect x=&#34;430&#34; y=&#34;152&#34; width=&#34;230&#34; height=&#34;42&#34; rx=&#34;9&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;444&#34; y=&#34;170&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11.5px&#34;&gt;LogProvider&lt;/text&gt;&#xA;  &lt;text x=&#34;444&#34; y=&#34;186&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10px&#34;&gt;[Handles(StoreObjectType.Log)]&lt;/text&gt;&#xA;  &lt;rect x=&#34;430&#34; y=&#34;208&#34; width=&#34;230&#34; height=&#34;42&#34; rx=&#34;9&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;444&#34; y=&#34;226&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11.5px&#34;&gt;ChangeLogProvider&lt;/text&gt;&#xA;  &lt;text x=&#34;444&#34; y=&#34;242&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10px&#34;&gt;[Handles(StoreObjectType.ChangeLog)]&lt;/text&gt;&#xA;&lt;p&gt;&lt;text x=&#34;20&#34; y=&#34;286&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11px&#34;&gt;加一种对象，就是往右边多挂一个框；左边那一处一个字都不用改。&lt;/text&gt;&#xA;&lt;/svg&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 2 篇 · 噪声不是抹平的，是挑出来的</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es02-noise-is-picked-not-smeared/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es02-noise-is-picked-not-smeared/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 2 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es01-busy-wait/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es03-baseline-separation/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;读一段老代码时，我注意到它处理旋转信号的方式和我习惯的完全相反。&lt;/p&gt;&#xA;&lt;p&gt;它要测的是回转件一圈的径向变化，回答两个问题：偏差最大是多少，出现在圆周哪个位置。信号毛得很，传感器漂、装夹偏、表面有划痕、车间在震，全叠在上面。&lt;/p&gt;&#xA;&lt;p&gt;我自己的第一反应一直是加滤波，把曲线抹平。但这套代码里我翻遍了也没找到任何滤波器。它干的事很奇怪：先把整条曲线做一次频谱分解，然后只取其中一个分量，剩下的全不要。&lt;/p&gt;&#xA;&lt;p&gt;我看到那行代码时愣了一下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;amplitude&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Sqrt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;re&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;re&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;im&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;im&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那个 &lt;code&gt;* 4 / nCount&lt;/code&gt; 我看不懂，后来自己推了一遍才明白：对一串正弦做频谱分解，它在自己那个频点上的能量是幅值乘以点数的一半，反过来一解，再换算成峰峰值，正好是这个系数。一步就把幅值算出来了，连找最大值最小值都省了。&lt;/p&gt;&#xA;&lt;p&gt;但真正让我改观的不是这个系数，是它背后的思路。它压根没打算把噪声抹掉。&lt;/p&gt;&#xA;&lt;p&gt;我把频谱图摊开看，各个成分是分开待着的：零位漂移在最左边一格，我要的信号在转频那一格，划痕和键槽在更右边，随机噪声铺满整条。它们根本不住在一起。那我为什么要用一把刷子把整张图刷一遍，直接用梳子把那一格梳出来不就行了。&lt;/p&gt;&#xA;&lt;p&gt;这件事后来改变了我看很多脏数据的习惯。以前拿到一条脏信号，第一反应是把它弄干净；现在我会先问一句，我要的和不要的，是不是在同一个格子里。在，那确实只能硬扛；不在，大多数情况都是这样，那抹平这个动作本身就找错了方向。&lt;/p&gt;&#xA;&lt;p&gt;顺带提一个细节。那段代码里界面上是有&amp;quot;滤波点数&amp;quot;这个参数的，用户能设，配置也能存，可我 grep 了一圈，算法里从头到尾没人读过它。一个没人读的参数，安静地待在那里，像是在说这条路我们没走。&lt;/p&gt;&#xA;&lt;p&gt;这种问法也不只用在信号上。一堆报错日志刷过来，我以前的做法是先按时间聚合、降采样，把量压下去再看，可真正有用的那几条往往就淹没在这个压下去的动作里。后来我改成先想清楚要找的那类错误长什么样，再按这个样子去筛，剩下的不看。动作还是那个动作，方向反过来了，一个是把多的变少，一个是把要的挑出来。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 2 篇 · 说一个不敢删的文件夹</title>
				<link>http://localhost:1313/posts/2026-09-30-es03-previous/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es03-previous/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 2 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es01-spelling/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es04-multi-db/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻这套工业数据 WebApi 的目录时，我盯着一个叫 &lt;code&gt;.Previous&lt;/code&gt; 的文件夹看了很久。&lt;/p&gt;&#xA;&lt;p&gt;它不是一两个，是成片地留着。全仓算下来，整插件的旧版本目录有 &lt;strong&gt;6&lt;/strong&gt; 个，插件内部还藏着 &lt;strong&gt;23&lt;/strong&gt; 个旧版子目录，加在一起 &lt;strong&gt;29&lt;/strong&gt; 处旧代码还躺在构建里。ts01 那篇写过&amp;quot;为什么删不掉&amp;quot;，我这篇想说另一面：为什么&amp;quot;不敢删&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）其中一个整插件旧版，大致是这样被新加载器指着：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 新框架仍然加载 Contoso.Business.Previous 里的旧实现&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;_context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LoadFromAssemblyPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Combine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pluginDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Contoso.Business.Previous.dll&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;引用链没断&#34;&gt;引用链没断&lt;/h2&gt;&#xA;&lt;p&gt;我试着顺着其中一个 &lt;code&gt;.Previous&lt;/code&gt; 往上找，发现新代码里还有调用方直接指着它的入口类。删掉这个目录，那一方立刻编译不过。换句话说，旧版不是孤立的尸体，它还在被活着的代码牵着。&lt;/p&gt;&#xA;&lt;p&gt;更麻烦的是这些引用藏在插件边界后面。一个插件引用另一个插件的旧实现，依赖关系是隐形的，光看目录根本看不出谁在用它。我没法确认当年每一处保留的动机，但更可能的情况是：某次迁移只迁了一半，旧版先留着顶着，等哪天有空再断链——然后就没有哪天了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;代价是沉默的&#34;&gt;代价是沉默的&lt;/h2&gt;&#xA;&lt;p&gt;这 29 处旧代码不报错。它们只是让每一个新人进来的第一件事变成：先搞清&amp;quot;现在到底用哪个版本&amp;quot;。这个判断没有任何文档兜底，只能靠问老人，问着问着老人也忘了。&lt;/p&gt;&#xA;&lt;p&gt;它还会把迁移钉死。旧版依赖的某些接口在新框架里已经不推荐，只要引用链没断，整体就往前走不动。6 个整插件旧版，等于 6 个被按下暂停的迁移任务，23 个内部子目录是更小的同样故事。&lt;/p&gt;&#xA;&lt;h2 id=&#34;断链比删除难&#34;&gt;断链比删除难&lt;/h2&gt;&#xA;&lt;p&gt;我后来没敢一把全删，而是给每个 &lt;code&gt;.Previous&lt;/code&gt; 做了一张&amp;quot;谁还在指它&amp;quot;的引用表，按引用数从少到多逐个断。断一个、编译一遍、跑一下插件加载测试，确认没方报错，再删目录。内部那 23 个子目录同理，先确认新路径已经覆盖，才动手清。&lt;/p&gt;&#xA;&lt;p&gt;清理之后，主方案里不再有带 &lt;code&gt;.Previous&lt;/code&gt; 的工程，新加载器只认当前版本。但真正难的不是删，是承认：这些文件夹从来不是&amp;quot;忘了删&amp;quot;，是&amp;quot;暂时不动&amp;quot;的决定堆成的。技术债不一定会报错，它只是让下一步永远差一点点。&lt;/p&gt;</description>
			</item>
			<item>
				<title>部署器开源记 · 第 3 篇 · 参数是怎么填进配置文件的，以及填错会停在哪</title>
				<link>http://localhost:1313/posts/2026-10-01-deployer-os03-parameter-injection/</link>
				<pubDate>Thu, 01 Oct 2026 09:40:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-10-01-deployer-os03-parameter-injection/</guid>
				<description>模板占位符替换与匹配数校验</description>
			</item>
			<item>
				<title>开发随笔 · 第 3 篇 · 说一个把代价留给谁的事</title>
				<link>http://localhost:1313/posts/2026-10-01-deployer-es06-who-pays-the-cost/</link>
				<pubDate>Thu, 01 Oct 2026 00:35:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-10-01-deployer-es06-who-pays-the-cost/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 3 / 3 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/deployer-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-10-01-deployer-es05-backed-up-but-cannot-go-back/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;把这套程序从头翻了一遍之后，我得出一个当时完全没有意识到的结论。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;我写的每一行都在把代价分配给某个人，而我分配的时候并不知道自己在分配。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;几个具体的例子&#34;&gt;几个具体的例子&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;为了让第一次访问不慢，我把代价给了机器。&lt;/strong&gt;&#xA;程序建的网站，我设成一直开着、从不休息、也不因为没人访问就歇下来。这样早上第一个人来点，页面立刻就出来。代价是内存一直占着，应用本身的毛病也不再被定期清理掉，会一直累积。&lt;/p&gt;&#xA;&lt;p&gt;我当时只算了「快」这一头。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;为了让程序不崩，我把代价给了现场的人。&lt;/strong&gt;&#xA;出错的地方我大多包起来，出错了记一行然后继续。程序不会崩，进度条会走完。代价是出问题时人看到的可能不是真正的原因，得从很远的地方往回猜。&lt;/p&gt;&#xA;&lt;p&gt;我当时只算了「不崩」这一头。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;为了少装一个依赖包，我把代价给了后来接手的人。&lt;/strong&gt;&#xA;装配零件这件事我是手写的，每个零件自己保管一份自己，一共三十几处。好处是客户机器上不用多装东西。代价是谁想知道「这个东西从哪来」，得翻进方法体里去看，签名上看不出来。&lt;/p&gt;&#xA;&lt;p&gt;我当时只算了「少一个包」这一头。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;为了让人不会操作错，我把代价给了想改设置的人。&lt;/strong&gt;&#xA;有些地方我做成只能看不能改，改了要点保存才生效。好处是乱点的概率小了。代价是真想调整的人没有一个顺畅的入口。&lt;/p&gt;&#xA;&lt;h2 id=&#34;这些选择有没有错&#34;&gt;这些选择有没有错&lt;/h2&gt;&#xA;&lt;p&gt;我不打算把它们判成错的。&lt;/p&gt;&#xA;&lt;p&gt;这四个选择都是在当时的约束下做的，而且四年运行下来，没有哪一个造成过事故。机器内存够用，现场的人没被卡住太久，依赖包确实一个都没多装，误操作也确实少。&lt;/p&gt;&#xA;&lt;p&gt;让我现在重新想一遍的不是「选得对不对」，是&lt;strong&gt;我当时不知道自己在选&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;我当时的判断标准很单一，这一步能不能跑通、会不会出错、客户会不会点不动。这三问都是短期的，问的是「今天下午能不能部署完」。它们没有一个问到「半年后谁为这个决定多花时间」。&lt;/p&gt;&#xA;&lt;h2 id=&#34;现在我看这类事的方式&#34;&gt;现在我看这类事的方式&lt;/h2&gt;&#xA;&lt;p&gt;我现在遇到一个设计选择，会多问一句，&lt;strong&gt;如果它出问题，是谁在什么时候发现？&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;这一问很便宜，但能把很多选择照出另一面。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;关掉定期回收 → 出问题是内存慢慢吃满，发现的人是一年后加内存的那个人&lt;/li&gt;&#xA;&lt;li&gt;出错不声张 → 发现的人是出故障那天在现场排查的人&lt;/li&gt;&#xA;&lt;li&gt;依赖手写 → 发现的人是下一个读这段代码的人&lt;/li&gt;&#xA;&lt;li&gt;改不动的拼写 → 发现的人是每一个第一次看到它、停下来想「这是不是笔误」的人&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;第四类最轻微，但它有个特点，&lt;strong&gt;每来一个人，代价就付一次。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;这套程序现在还在用，我改它的原则是能小改就不重写，改完客户那边感觉不到变化。&lt;/p&gt;&#xA;&lt;p&gt;这个原则本身也是一次代价分配，我把「代码不够干净」的代价留给了自己，把「不用重新学一遍」的好处给了用的人。&lt;/p&gt;&#xA;&lt;p&gt;这一次我是知道的。&lt;/p&gt;&#xA;&lt;p&gt;这个工具本身后来开源出来写成了《部署器开源记 · 第 1 篇 · 发版那五步，我为什么没做成命令行》：&lt;a href=&#34;http://localhost:1313/posts/2026-10-01-deployer-os01-five-steps/&#34;&gt;发版那五步，我为什么没做成命令行&lt;/a&gt;。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 3 篇 · 说一堆让机器替我抄的重复代码</title>
				<link>http://localhost:1313/posts/2026-09-30-wml-es04-machine-copied-code/</link>
				<pubDate>Wed, 30 Sep 2026 17:30:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-wml-es04-machine-copied-code/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 3 / 5 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/witsml-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-wml-es03-switch-that-rules-everyone/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-wml-es05-cant-touch-it/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;有回我打开一个老工程的目录，看见几个体积不太对劲的文件。其中一个 4 MB 出头，点开一看，十三万行，全是同一种形状的类，字段、属性、序列化标记一个接一个，注释也是一个模子印出来的。&lt;/p&gt;&#xA;&lt;p&gt;当时我给自己定的规矩很简单，生成的东西不用读。它不是我写的，也不是同事写的，是外部那套标准模板照着吐出来的，既然不归我写，就不归我读。&lt;/p&gt;&#xA;&lt;p&gt;后来我数了一次，才发现「稳赚」站不住。&lt;/p&gt;&#xA;&lt;p&gt;先看那个 4 MB 的文件头上压着的那句话，原文是英文：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//This code was generated using the Energistics Generator tool.  Direct changes to this code will be lost&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//during regeneration.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;翻译过来就是，这份代码是生成器吐的，你直接改它，下次重新生成全丢。这些行在体制上是只读的，长得跟旁边的代码一样，编译也一起过，可你碰它的那一刻就等于没改。&lt;/p&gt;&#xA;&lt;p&gt;然后我把行数加起来，这一加让我沉默。&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;行数&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;整个仓库的 &lt;code&gt;.cs&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;197,802&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;挂着那句只读声明的那个文件&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;135,258&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;同目录里另一个机器吐的枚举文件&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;20,787&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;剩下人手写的&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;41,757&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;光挂着声明的那一个，就占了仓库的六成八。加上同目录那个机器吐的枚举文件，两个合计 156,045 行，接近八成。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 3 篇 · 想看清一样东西，先量两样不相干的</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es03-baseline-separation/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es03-baseline-separation/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 3 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es02-noise-is-picked-not-smeared/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es04-press-amount-with-memory/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;看一套测量方案时，有个地方我一开始没想通：明明要测的是中间那个探头下面的零件，旁边还装了两个探头，它们压根不碰零件，一个贴左边工装，一个贴右边。&lt;/p&gt;&#xA;&lt;p&gt;后来才明白那两个探头是给中间那个当尺子用的。一圈采样里混着四种东西：零件真弯曲的正弦、主轴顶尖本身的偏心跳动、工件没夹正的倾斜、还有采集噪声。后面三种，和弯了长得一模一样。更坑的是，偏心和倾斜不是随机噪声，它们是低频的、空间相关的，所有探头同一时刻读到的偏移高度一致。你取平均，只会把信号按个比例缩小，那把弯的尺子还是弯的。&lt;/p&gt;&#xA;&lt;p&gt;那两个不碰零件的探头，量的是工装和顶尖自己的状态。用它们连成一条基线，中间探头该读到多少直的值就出来了。实测减去这条基线，剩下的才是零件真弯了多少。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sRight&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;baseline&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;refLeft&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;refRight&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;refLeft&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;trueValue&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;measured&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;baseline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;k 是按物理坐标算的，不是随手写的 0.5。还要防除零，如果左右参考探头重合，分母为零，代码直接给一个哨兵值 &lt;code&gt;-10&lt;/code&gt;，让上层知道这次基准不可信，而不是算出一个 NaN 或无穷污染后续。&lt;/p&gt;&#xA;&lt;p&gt;这件事改了我一个习惯。想看清楚一个对象，先别急着多测它，先想清楚它周围的基准是什么，有没有被它带着跑。基准和对象混在一起的时候，多测几遍取平均没用，平均假设误差是随机的、会互相抵消，可系统误差偏不随机。得把基准单独量出来，再扣掉。&lt;/p&gt;&#xA;&lt;p&gt;我后来把这套用到别的地方。读一份报表，先看清它的口径和基准是什么，而不是急着看数字本身；接手一个老模块，先找它的参考探头，那些定义清楚、不被业务带着跑的契约，而不是一头扎进业务逻辑。基准清不清，往往决定了你能不能看清对象。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 3 篇 · 说一套代码伺候三种库房的事</title>
				<link>http://localhost:1313/posts/2026-09-30-es04-multi-db/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es04-multi-db/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 3 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es03-previous/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es05-singleton/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻这套工业数据 WebApi 的数据层时，我发现它在同时伺候三座库房：SQL Server、PostgreSQL，还有一座信创场景下的某国产数据库。&lt;/p&gt;&#xA;&lt;p&gt;一套工业数据采集中枢，要落地的数据既要在老系统里读，也要在新平台里写，还得满足信创替换的要求。于是三种方言被塞进了同一个代码库。ts03 那篇写过它怎么搭起来的，我这篇想说代价。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）为了盖住三种方言，仓库侧有一个统一的提供入口：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 不同库房走各自的实现，但对外只露一个口&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;IRepositoryProvider&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// SQL Server / PostgreSQL / 某国产库 各有一份实现&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;方言是最先绊脚的&#34;&gt;方言是最先绊脚的&lt;/h2&gt;&#xA;&lt;p&gt;三种库不是&amp;quot;换个连接串就行&amp;quot;。分页写法不一样，某国产库对大小写和关键字的容忍度跟另两家不同，自增主键的生成方式也各搞一套。同一句查询，落到不同库房要翻译成不同样子。我没法确认每一处适配是哪次需求逼出来的，但更可能的情况是：先接一家，后来要兼容下一家，于是补丁越叠越厚。&lt;/p&gt;&#xA;&lt;h2 id=&#34;事务的语义悄悄分了家&#34;&gt;事务的语义悄悄分了家&lt;/h2&gt;&#xA;&lt;p&gt;最隐蔽的是事务。三家对隔离级别、对&amp;quot;写一半回滚&amp;quot;的默认行为并不完全一致。同样一段落库逻辑，在一家跑得好好的，换一家可能偶发锁等待或写入顺序错位。这种问题不会在编译期冒头，只在生产环境的某个时刻冷不丁出现。&lt;/p&gt;&#xA;&lt;h2 id=&#34;心智负担是长期的&#34;&gt;心智负担是长期的&lt;/h2&gt;&#xA;&lt;p&gt;为了统一，代码里有一个仓库提供入口，全仓有 &lt;strong&gt;94&lt;/strong&gt; 处 &lt;code&gt;RepositoryProvider&lt;/code&gt; 相关的引用。这个数字本身说明：抽象是有的，但抽象之下的三套语义，每个改动的工程师都得在脑子里同时装下。新人接手第一关，往往不是业务逻辑，而是&amp;quot;这一段到底落在哪座库房、用哪套方言&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;一套代码伺候三种库房，换来的是迁移时的弹性；代价是每个人都要替三座库房记笔记。值不值，得看那座信创库房是不是非换不可——但对已经背着它跑的系统来说，先把三套语义的差异记清楚，比急着统一更重要。&lt;/p&gt;</description>
			</item>
			<item>
				<title>部署器开源记 · 第 4 篇 · 升级包解开以后，走的是同一条五步链</title>
				<link>http://localhost:1313/posts/2026-10-01-deployer-os04-upgrade-reuses-chain/</link>
				<pubDate>Thu, 01 Oct 2026 10:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-10-01-deployer-os04-upgrade-reuses-chain/</guid>
				<description>升级复用同一条执行链</description>
			</item>
			<item>
				<title>开发随笔 · 第 4 篇 · 说一个明明能改、却没人敢动的东西</title>
				<link>http://localhost:1313/posts/2026-09-30-wml-es05-cant-touch-it/</link>
				<pubDate>Wed, 30 Sep 2026 17:30:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-wml-es05-cant-touch-it/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 4 / 5 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/witsml-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-wml-es04-machine-copied-code/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-wml-es06-result-never-used/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;回头看自己经手过的那些决定，当初都是为了某个具体的省事或对齐做的，可等这摊东西长大，它们一个个都变成了改不动的东西。&lt;/p&gt;&#xA;&lt;p&gt;不是说技术上改不了，是改的代价太大、牵连太广，或者外面还有人指着它活着，于是没人敢动。明明手能伸过去，就是缩了回来。&lt;/p&gt;&#xA;&lt;p&gt;早年我写代码，默认自己拥有全部主动权。一个名字起错了，改；一个结构不顺，重排；一个写法别扭，推翻重来。那时候项目小、用人少，改起来零成本，我就养成了「不对就改」的习惯。我没想过，这个习惯是建立在「没人依赖你」的前提上的。一旦你的代码被别人接住、被外面引用、被历史记住，主动权就不再完全是你自己的了。&lt;/p&gt;&#xA;&lt;p&gt;最能说明这件事的，我觉得是几个最不起眼的字符串常量。这个服务对外要报一份能力说明，里面有几项版本号，它们是这么写的（等价改写）：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ServiceConsts&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ServiceVersion&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;1.4.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ApiVersion&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;1.4.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CapabilityVersion&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;1.0.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SchemaRevision&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;1.4.1.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PayloadEncoding&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;gzip&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我数了一下，就这么七行、五个字符串。全仓引用它们的地方一共六处，五处在拼装那份对外的能力说明，一处在版本应答里。没有一处是给自己人看的，它们只往外走。&lt;/p&gt;&#xA;&lt;p&gt;改其中一个字符，从编译到运行不会有一个测试失败。但只要对面有客户端在读这个版本号，它当场就要重新判断一次你是谁、还能不能跟你说话。&lt;/p&gt;&#xA;&lt;!-- 私事与协议的分界 --&gt;&#xA;&lt;svg viewBox=&#34;0 0 680 250&#34; style=&#34;width:100%;height:auto;display:block;margin:1.6em 0&#34; role=&#34;img&#34; aria-label=&#34;分界不在代码里，在谁依赖它&#34;&gt;&#xA;  &lt;rect x=&#34;0.5&#34; y=&#34;0.5&#34; width=&#34;679&#34; height=&#34;249&#34; rx=&#34;12&#34; style=&#34;fill:var(--bg-elevated);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;20&#34; y=&#34;26&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:11px&#34;&gt;分界不在代码里，在谁依赖它&lt;/text&gt;&#xA;  &lt;rect x=&#34;20&#34; y=&#34;44&#34; width=&#34;300&#34; height=&#34;164&#34; rx=&#34;10&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;36&#34; y=&#34;70&#34; style=&#34;fill:var(--success);font-family:var(--mono);font-size:12px&#34;&gt;只是我自己的事&lt;/text&gt;&#xA;  &lt;text x=&#34;36&#34; y=&#34;88&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10.5px&#34;&gt;改完零个人受影响&lt;/text&gt;&#xA;  &lt;rect x=&#34;36&#34; y=&#34;102&#34; width=&#34;128&#34; height=&#34;24&#34; rx=&#34;12&#34; style=&#34;fill:none;stroke:var(--border-strong)&#34;/&gt;&#xA;  &lt;text x=&#34;100&#34; y=&#34;118&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11px&#34;&gt;私有方法名&lt;/text&gt;&#xA;  &lt;rect x=&#34;176&#34; y=&#34;102&#34; width=&#34;128&#34; height=&#34;24&#34; rx=&#34;12&#34; style=&#34;fill:none;stroke:var(--border-strong)&#34;/&gt;&#xA;  &lt;text x=&#34;240&#34; y=&#34;118&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11px&#34;&gt;局部变量&lt;/text&gt;&#xA;  &lt;rect x=&#34;36&#34; y=&#34;134&#34; width=&#34;128&#34; height=&#34;24&#34; rx=&#34;12&#34; style=&#34;fill:none;stroke:var(--border-strong)&#34;/&gt;&#xA;  &lt;text x=&#34;100&#34; y=&#34;150&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11px&#34;&gt;类内注释&lt;/text&gt;&#xA;  &lt;rect x=&#34;176&#34; y=&#34;134&#34; width=&#34;128&#34; height=&#34;24&#34; rx=&#34;12&#34; style=&#34;fill:none;stroke:var(--border-strong)&#34;/&gt;&#xA;  &lt;text x=&#34;240&#34; y=&#34;150&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11px&#34;&gt;日志文案&lt;/text&gt;&#xA;&lt;p&gt;&lt;text x=&#34;36&#34; y=&#34;186&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:11px&#34;&gt;顺手改，不用想&lt;/text&gt;&lt;/p&gt;&#xA;  &lt;rect x=&#34;360&#34; y=&#34;44&#34; width=&#34;300&#34; height=&#34;164&#34; rx=&#34;10&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;376&#34; y=&#34;70&#34; style=&#34;fill:var(--danger);font-family:var(--mono);font-size:12px&#34;&gt;已经成了别人的协议&lt;/text&gt;&#xA;  &lt;text x=&#34;376&#34; y=&#34;88&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10.5px&#34;&gt;同一处改动，先要通知对方&lt;/text&gt;&#xA;  &lt;rect x=&#34;376&#34; y=&#34;102&#34; width=&#34;128&#34; height=&#34;24&#34; rx=&#34;12&#34; style=&#34;fill:none;stroke:var(--danger)&#34;/&gt;&#xA;  &lt;text x=&#34;440&#34; y=&#34;118&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11px&#34;&gt;版本字符串&lt;/text&gt;&#xA;  &lt;rect x=&#34;516&#34; y=&#34;102&#34; width=&#34;128&#34; height=&#34;24&#34; rx=&#34;12&#34; style=&#34;fill:none;stroke:var(--danger)&#34;/&gt;&#xA;  &lt;text x=&#34;580&#34; y=&#34;118&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11px&#34;&gt;对象类型名&lt;/text&gt;&#xA;  &lt;rect x=&#34;376&#34; y=&#34;134&#34; width=&#34;128&#34; height=&#34;24&#34; rx=&#34;12&#34; style=&#34;fill:none;stroke:var(--danger)&#34;/&gt;&#xA;  &lt;text x=&#34;440&#34; y=&#34;150&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11px&#34;&gt;命名空间&lt;/text&gt;&#xA;  &lt;rect x=&#34;516&#34; y=&#34;134&#34; width=&#34;128&#34; height=&#34;24&#34; rx=&#34;12&#34; style=&#34;fill:none;stroke:var(--danger)&#34;/&gt;&#xA;  &lt;text x=&#34;580&#34; y=&#34;150&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11px&#34;&gt;生成物&lt;/text&gt;&#xA;&lt;p&gt;&lt;text x=&#34;376&#34; y=&#34;186&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:11px&#34;&gt;也是改几下，但得先打招呼&lt;/text&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;text x=&#34;340&#34; y=&#34;232&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11px&#34;&gt;左右两边改起来的工夫差不多，差的是改完要跟几个人解释。&lt;/text&gt;&#xA;&lt;/svg&gt;&lt;/p&gt;&#xA;&lt;p&gt;同一批「改起来两分钟」的东西还有好几个，那 4 MB 的生成文件、那个拼错的命名空间、那个对象类型的枚举名，难度都是两分钟，代价都是「得跟外面打个招呼」。&lt;/p&gt;&#xA;&lt;p&gt;所以我动手之前会先问自己一句，这是我一个人的私事，还是已经变成了别人的协议。是私事，怎么顺手怎么来；是协议，就得像对待对外承诺那样谨慎，命名当 API，结构当约定，版本当承诺。&lt;/p&gt;&#xA;&lt;p&gt;那五个版本号，我当初写下它们的时候，没人会当这是承诺。它就是一个字符串，跟旁边的日志文案长得一样。是外面有人开始读它，才把它变成了承诺。承诺不是在写代码那天订立的，是在别人依赖你的那天订立的，而且没人会来通知你。&lt;/p&gt;&#xA;&lt;p&gt;能改不代表该动，我看久了才明白，把约束守住也是一种设计。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 4 篇 · 同样的动作，第二次不能照搬力道</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es04-press-amount-with-memory/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es04-press-amount-with-memory/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 4 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es03-baseline-separation/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es05-dead-config-knobs/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;读一段控制算法时，它压多少取决于三样东西：当前弯了多少、有没有压过屈服点、以及之前同一方向已经压过几次。&lt;/p&gt;&#xA;&lt;p&gt;最后一项最让我停了一下。同一种弯，第一次压没压动，第二次它会多压一点，因为它记得上次没压动，料到这次也会回弹。这就像拧一个有点滑丝的螺丝，第一下没拧紧，第二下你会下意识多使点劲。&lt;/p&gt;&#xA;&lt;p&gt;模型写出来是：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;press&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bend&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tolerance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;B&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;C&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;repeatCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A 管弯多少压多少，B 管必须压过屈服点，C 管压不动就加量。这个设计本身我挑不出毛病。&lt;/p&gt;&#xA;&lt;p&gt;但它有一段翻车的地方。处理压反了的分支，本意是&amp;quot;压反了就加力&amp;quot;，写成了循环里拿次数自己乘自己：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;repeatCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;++)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;baseB&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;baseB&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;repeatCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 循环里乘的是次数，不是系数&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果是 &lt;code&gt;baseB = B × repeatCount^repeatCount&lt;/code&gt;。翻面一次还是 B，两次变成 4B，三次直接 27B，底量随翻面次数指数爆炸。而旁边明明有个专门给压反时 B 值系数准备的字段，注释都写好了，从头到尾没参与过计算。正确的应该是 &lt;code&gt;baseB *= bFactor&lt;/code&gt;，乘那个系数，而不是拿循环变量自己累乘。&lt;/p&gt;&#xA;&lt;p&gt;这个 bug 藏了很久才被发现，不是因为隐蔽，是因为它不报错。baseB 算飞了，程序一路照跑，没有异常，没有崩溃。现场表现就是偶尔翻面的工件压过头。这种偶发、偏工艺的现象，第一反应往往是设备或材料问题，没人会去怀疑一个累乘变量。&lt;/p&gt;&#xA;&lt;p&gt;最让我在意的不是 bug 本身，是那个定义了却没接线的系数。它的注释写得很清楚，压反时 B 值系数，作者显然意识到了需要它，但那一行赋值之后再没被读过。一个本该是安全闸的字段，安静地躺在代码里，让所有人以为有保护，其实没有。&lt;/p&gt;&#xA;&lt;p&gt;状态变量一旦参与迭代，每写一行都得想清楚它下一轮会变成什么。一个定义了却没被用的字段，比没有这个字段更危险，它让人以为有保护，其实没有。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 4 篇 · 说一台咖啡机的事</title>
				<link>http://localhost:1313/posts/2026-09-30-es05-singleton/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es05-singleton/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 4 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es04-multi-db/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es06-attribute-driven/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻这套工业数据 WebApi 时，我注意到一个反复出现的小东西：一个叫 &lt;code&gt;Holder&lt;/code&gt; 的私有静态内部类。它干的事，像全楼共用一台咖啡机。&lt;/p&gt;&#xA;&lt;p&gt;楼里每台工位不需要各自买一台咖啡机，大家共用一台，谁想喝谁来接。代码里也是——某些全局只该有一份的东西（比如一个配置读取器、一个缓存容器），不用每人 new 一个，大家都从一个口拿同一份。README 曾给过这套单例 Holder 一个具体引用数，我顺便数了数，实测对不上；这个数我就不引了，只说&amp;quot;若干处&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）它长这样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;BizCache&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BizCache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Holder&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BizCache&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instance&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BizCache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BizCache&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Holder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 懒加载、线程安全&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;它为什么线程安全&#34;&gt;它为什么线程安全&lt;/h2&gt;&#xA;&lt;p&gt;要点在那层嵌套静态类 &lt;code&gt;Holder&lt;/code&gt;。它的静态字段 &lt;code&gt;Instance&lt;/code&gt; 在类第一次被访问时才初始化，而 .NET 保证静态初始化的线程安全——不管多少个线程同时第一次来取，这个实例都只构造一次。外层的 &lt;code&gt;BizCache&lt;/code&gt; 构造函数是私有的，谁也 new 不出来第二份。&lt;/p&gt;&#xA;&lt;p&gt;这正是&amp;quot;全楼一台咖啡机&amp;quot;的模型：机器只有一台（私有构造 + 单实例），但谁第一个想喝，它才真正开机（懒加载），而且开机这件事不会被两个人同时触发两次。&lt;/p&gt;&#xA;&lt;h2 id=&#34;代价也是共用的&#34;&gt;代价也是共用的&lt;/h2&gt;&#xA;&lt;p&gt;共用意味着共用状态。这台&amp;quot;咖啡机&amp;quot;里的东西是全局共享的，一个地方写错了，全楼都尝到怪味。多线程下更要小心：它解决了&amp;quot;创建&amp;quot;的线程安全，却没解决&amp;quot;使用&amp;quot;的线程安全。如果那一份实例内部有可变状态，读写不加锁，照样会乱。&lt;/p&gt;&#xA;&lt;p&gt;我没法确认当年每一处 Holder 是不是都该是单例，但更可能的情况是：大家都图省事，把&amp;quot;全局只要一份&amp;quot;的东西随手塞进同一个套路。套路本身没错，错在没人回头数清楚，到底有几台机器、哪台还在用。&lt;/p&gt;&#xA;&lt;p&gt;一台咖啡机省了买机器的钱，也把&amp;quot;谁弄脏了机器&amp;quot;变成了全员的问题。单例好看在省，难看在它把责任也摊成了共用。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 5 篇 · 说一个算好了、最后没用上的东西</title>
				<link>http://localhost:1313/posts/2026-09-30-wml-es06-result-never-used/</link>
				<pubDate>Wed, 30 Sep 2026 17:30:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-wml-es06-result-never-used/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 5 / 5 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/witsml-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-wml-es05-cant-touch-it/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻一个老工程的时候，我撞见一组看着特别正常的代码。一个方法收下一个选项，选项一共两个，写在一张单子上，名字也不含糊。方法里按选中的值分成两条路，各造一个对象去干同一件事。整段读下来挑不出毛病，注释写着，缩进规矩，命名比周围都整齐。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SamplingMode&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Interpolate&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// 线性插值&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Nearest&lt;/span&gt;     &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// 就近取值&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;单看这一段，这是个好习惯。留两条路很常见，写的时候你不知道将来用哪一条，或者明知现在只用得上一条，还是先把另一条写出来，省得以后换的时候回头改。留一条路空着，和留一条路能用但一直在睡觉，是两件不同的事。&lt;/p&gt;&#xA;&lt;p&gt;我想看看平时大家传的是哪个值，就去搜这个方法的调用点。搜完整棵树，一共两个地方在调用它，两处传的都是同一个值。另一个值，从这段代码存在的那天起就没被人传过。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 全仓只有这两处调用，两处都传 Nearest&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CurveSampler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Calculate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SamplingMode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Nearest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CurveSampler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Calculate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SamplingMode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Nearest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// SamplingMode.Interpolate：调用点 0 处&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;到这里还算正常。我差点就往下翻了，顺手多看了那个没人走的分支一眼。&lt;/p&gt;&#xA;&lt;!-- 两条路 --&gt;&#xA;&lt;svg viewBox=&#34;0 0 680 250&#34; style=&#34;width:100%;height:auto;display:block;margin:1.6em 0&#34; role=&#34;img&#34; aria-label=&#34;两条路里只有一条在跑，而没在跑的那条自己也没写完&#34;&gt;&#xA;  &lt;rect x=&#34;0.5&#34; y=&#34;0.5&#34; width=&#34;679&#34; height=&#34;249&#34; rx=&#34;12&#34; style=&#34;fill:var(--bg-elevated);stroke:var(--border-subtle)&#34;/&gt;&#xA;  &lt;text x=&#34;20&#34; y=&#34;26&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:11px&#34;&gt;留着两条路，但只有一条能走&lt;/text&gt;&#xA;  &lt;rect x=&#34;20&#34; y=&#34;102&#34; width=&#34;130&#34; height=&#34;48&#34; rx=&#34;10&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--border-strong)&#34;/&gt;&#xA;  &lt;text x=&#34;85&#34; y=&#34;123&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11.5px&#34;&gt;一个选项&lt;/text&gt;&#xA;  &lt;text x=&#34;85&#34; y=&#34;140&#34; text-anchor=&#34;middle&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10px&#34;&gt;SamplingMode&lt;/text&gt;&#xA;  &lt;path d=&#34;M150,126 C195,126 195,76 240,76&#34; style=&#34;fill:none;stroke:var(--border-subtle)&#34; stroke-width=&#34;1.1&#34;/&gt;&#xA;  &lt;path d=&#34;M150,126 C195,126 195,178 240,178&#34; style=&#34;fill:none;stroke:var(--border-subtle)&#34; stroke-width=&#34;1.1&#34;/&gt;&#xA;  &lt;rect x=&#34;240&#34; y=&#34;50&#34; width=&#34;220&#34; height=&#34;52&#34; rx=&#34;10&#34; style=&#34;fill:none;stroke:var(--danger)&#34; stroke-dasharray=&#34;5 4&#34;/&gt;&#xA;  &lt;text x=&#34;256&#34; y=&#34;72&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11.5px&#34;&gt;InterpolateStrategy&lt;/text&gt;&#xA;  &lt;text x=&#34;256&#34; y=&#34;90&#34; style=&#34;fill:var(--danger);font-family:var(--mono);font-size:10.5px&#34;&gt;算好了，最后没有返回它&lt;/text&gt;&#xA;  &lt;rect x=&#34;240&#34; y=&#34;152&#34; width=&#34;220&#34; height=&#34;52&#34; rx=&#34;10&#34; style=&#34;fill:var(--bg-subtle);stroke:var(--success)&#34;/&gt;&#xA;  &lt;text x=&#34;256&#34; y=&#34;174&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11.5px&#34;&gt;NearestStrategy&lt;/text&gt;&#xA;  &lt;text x=&#34;256&#34; y=&#34;192&#34; style=&#34;fill:var(--success);font-family:var(--mono);font-size:10.5px&#34;&gt;生产上跑的就是这条&lt;/text&gt;&#xA;&lt;p&gt;&lt;text x=&#34;476&#34; y=&#34;72&#34; style=&#34;fill:var(--danger);font-family:var(--mono);font-size:11px&#34;&gt;调用点 0 处&lt;/text&gt;&#xA;&lt;text x=&#34;476&#34; y=&#34;90&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10.5px&#34;&gt;从存在的第一天起&lt;/text&gt;&#xA;&lt;text x=&#34;476&#34; y=&#34;174&#34; style=&#34;fill:var(--text-primary);font-family:var(--mono);font-size:11px&#34;&gt;调用点 2 处&lt;/text&gt;&#xA;&lt;text x=&#34;476&#34; y=&#34;192&#34; style=&#34;fill:var(--text-muted);font-family:var(--mono);font-size:10.5px&#34;&gt;两处传的都是它&lt;/text&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;text x=&#34;20&#34; y=&#34;234&#34; style=&#34;fill:var(--text-secondary);font-family:var(--mono);font-size:11px&#34;&gt;虚线那条更麻烦：就算哪天有人把选项拨过去，它也交不出活。&lt;/text&gt;&#xA;&lt;/svg&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 5 篇 · 说贴标签的事</title>
				<link>http://localhost:1313/posts/2026-09-30-es06-attribute-driven/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es06-attribute-driven/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 5 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es05-singleton/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es07-multi-db-pluggable/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻这套工业数据 WebApi 时，我注意到一种很轻的&amp;quot;上岗&amp;quot;方式：贴个标签，方法就进了调度。&lt;/p&gt;&#xA;&lt;p&gt;代码里有个 &lt;code&gt;[StrategyMethod]&lt;/code&gt; 特性，全仓命中 &lt;strong&gt;61&lt;/strong&gt; 处。意思是：你不用去某张表、某个工厂里登记&amp;quot;我是策略&amp;quot;，只要在方法上贴这个特性，框架扫一遍就把它收编。贴标签就上岗，比写一堆注册代码省事得多。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）它大致是这样用的：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;[StrategyMethod(&amp;#34;curve.decode&amp;#34;)]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DecodeCurve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* 业务处理 */&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 框架扫描所有 [StrategyMethod]，按名字把方法挂进调度表&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;约定优于配置&#34;&gt;约定优于配置&lt;/h2&gt;&#xA;&lt;p&gt;这套思路叫&amp;quot;约定优于配置&amp;quot;。你遵守&amp;quot;贴标签&amp;quot;这个约定，框架就替你办完接入的杂活。好处是明显的：新增一个策略，不用动注册中心，不用改工厂的 switch，一个特性搞定。工业数据采集中，要处理的业务实体、曲线、记录文件类型很多，每加一种就贴一个，扩展面很平。&lt;/p&gt;&#xA;&lt;p&gt;我没法确认这个特性是哪次需求催出来的，但更可能的情况是：早期策略还少，手写注册还能忍，等类型一多，就换成&amp;quot;贴标签&amp;quot;来摊平成本。&lt;/p&gt;&#xA;&lt;h2 id=&#34;入口也藏进了标签&#34;&gt;入口也藏进了标签&lt;/h2&gt;&#xA;&lt;p&gt;代价是入口变隐形了。一个方法到底被谁调用、什么时候生效，不再写在某处集中的代码里，而是分散在 61 个贴了标签的方法上。新人想找&amp;quot;某类数据是谁处理的&amp;quot;，得先知道有这个扫描机制，再去全局搜特性名，而不是直接顺着一条调用链摸过去。&lt;/p&gt;&#xA;&lt;p&gt;更微妙的是优先级与覆盖。当两处贴了同名标签，谁生效？这种规则往往不在方法旁边，而在扫描器的某段逻辑里。标签越多，这套&amp;quot;看不见的调度表&amp;quot;越难一眼看清。还有一个爱犯的毛病：标签上的代号拼错一个字母，启动时不报错，等到按代号翻花名册才告诉你找不到——编译期查不出，得跑起来才露馅。&lt;/p&gt;&#xA;&lt;p&gt;贴标签省了登记的力气，代价是把&amp;quot;谁在上岗&amp;quot;这个答案，从一处明账变成了 61 处分散的小纸条。约定优于配置不是免费午餐，它只是把配置的成本，挪到了&amp;quot;你得记住有这个约定&amp;quot;的脑子里。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 5 篇 · 那些转了没反应的旋钮</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es05-dead-config-knobs/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es05-dead-config-knobs/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 5 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es04-press-amount-with-memory/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es06-swallowed-exceptions/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;清一个点位系统的配置项时，我顺着某个参数名去代码里搜，发现它只在界面和配置文件里出现，真正做计算的那段代码，从头到尾没读过它。&lt;/p&gt;&#xA;&lt;p&gt;界面上能编辑、能保存的参数，和代码里会生效的参数，中间隔着一段经常断掉的线。设置页给人的天然错觉是，你都能改它、存它了，它凭什么不干活。&lt;/p&gt;&#xA;&lt;p&gt;这类断裂不是孤例。一个滤波点数之类的信号处理参数，界面和配置都有，算法层零读取；一个平均次数参数，同样只存在于配置；一个模拟运行开关，在监控循环里被注释掉了，等于永远走真实路径；一个优先模式的选择，只有界面和配置，算法分支没接。它们共同的特征是，配置项先被界面和产品定义出来，算法要么后写没赶上，要么那条功能规划了没实现，参数就留着占位。&lt;/p&gt;&#xA;&lt;p&gt;第一种危害是现场调试的人。他调了半天没效果，会怀疑是自己不会调、是设备不行，而不是这个旋钮压根没接线。信任错了输入，排错方向就反了。&lt;/p&gt;&#xA;&lt;p&gt;更麻烦的是还有一类薛定谔的生效：参数在某条少走的路径上被读了，大部分路径不读。结果同一个设置，有时候灵有时候不灵，比彻底不灵还难查。你复现时它生效，客户复现时它不生效。&lt;/p&gt;&#xA;&lt;p&gt;现在我加任何配置项，都会顺手验证一遍，设了它，行为到底变没变。也学会了反向看那些看起来调了没用的旋钮，不一定是设备坏了，很可能只是没人接这根线。先去代码里搜一下，比换硬件便宜得多。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 6 篇 · 把报错的声音关掉，问题没消失</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es06-swallowed-exceptions/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es06-swallowed-exceptions/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 6 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es05-dead-config-knobs/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es07-naming-fossils/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;读一套 7×24 运行的工控软件时，一个数字一直让我不舒服：34 处空 catch。就是 &lt;code&gt;catch {}&lt;/code&gt; 里面什么都不写的那种。&lt;/p&gt;&#xA;&lt;p&gt;我第一次数出来的时候，以为是我 grep 姿势不对，又数了一遍，还是 34。分布在通信层、数据库层、算法层、UI 层。&lt;/p&gt;&#xA;&lt;p&gt;一个异常被吞掉的时候，问题没有消失，它只是不再出声。程序带着这个隐患继续跑，结果要么更错，要么在更远的地方以更奇怪的样子炸出来。&lt;/p&gt;&#xA;&lt;p&gt;最要命的是现场的人只看到结果不对，日志里却什么都没有。你连错在哪一行都不知道，怎么复现。那些复现不了的玄学故障，很多就是被空 catch 把证据埋了。&lt;/p&gt;&#xA;&lt;p&gt;少数情况可以静默。幂等的清理动作，比如重复 Dispose、重复关闭连接，抛了也无害；明确预期会失败、失败也无所谓的探测，比如探测某个可选设备是否存在。但即使这些情况，也建议至少留一行级别低的日志，因为无害是当下的判断，设备换了它可能就有害了。&lt;/p&gt;&#xA;&lt;p&gt;我现在写 catch，要么真的记一笔日志，哪怕级别低，要么用 &lt;code&gt;throw;&lt;/code&gt; 把问题交上去，绝不让它悄无声息地死在我这一层。那种包住就安全的错觉，是用一次次复现不了的现场故障换来的。&lt;/p&gt;&#xA;&lt;p&gt;接手任何老系统，我第一件事不是看它能跑，而是数它的空 catch。空 catch 多的系统，就像一栋墙上全糊着胶布的房子，你不知道底下补的是裂缝还是窟窿，但你知道，这房子主人选的是别让我看见，而不是修好它。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 6 篇 · 说一套代码伺候三种库房的好办法</title>
				<link>http://localhost:1313/posts/2026-09-30-es07-multi-db-pluggable/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es07-multi-db-pluggable/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 6 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es06-attribute-driven/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es09-filter-chain/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻这套工业数据 WebApi 的源码时，最让我佩服的一处，是它真的做到了&amp;quot;一套业务代码，伺候三种库房&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;客户用的数据库不一样：有的是 SQL Server，有的是 PostgreSQL，还有一批在换自主可控的某国产数据库。三种库的方言、分页、导出写法都不通。早年间这种差异会一路漏到业务层，加一家库就改一片代码。但我翻到数据访问那一层，发现他们把差异关进了一个可替换的 provider 笼子里。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 图纸：业务只说&amp;#34;存/取&amp;#34;，方言一字不写&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;IRepository&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Row&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Row&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 三库各自照图施工，方言消化在自己那间&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SqlServerRepo&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;IRepository&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;PostgresRepo&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;IRepository&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DomesticRepo&lt;/span&gt;  &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;IRepository&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* 某国产库的写法 */&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;业务层只认这张图纸，不认具体哪家库。要加一种新库房，就新建一个实现、挂上登记，业务一行不动。我在代码里数到 RepositoryProvider 这一层被引用了 &lt;strong&gt;94&lt;/strong&gt; 处——全仓这么多数据访问点，全被这个抽象稳稳接住，没有一处把方言漏上来。&lt;/p&gt;&#xA;&lt;p&gt;更让我意外的是，三种库不是谁取代谁，而是长期并存：老客户还在 SQL Server 上，新项目往 PostgreSQL 迁，信创要求的那批落在国产库。一个抽象同时兜住三代库房，业务代码一行都不用感知底下换没换。&lt;/p&gt;&#xA;&lt;p&gt;我后来才看清，这笼子就是把依赖倒置落了地。es04 那篇讲过&amp;quot;适配三种库&amp;quot;的代价，这篇要补的半句是正面的：代价管不管得住，就看笼子关得死不死。关死了，加一家库就像给商场加个摊位；关不死，差异满地跑，加一家就得改全网。&lt;/p&gt;&#xA;&lt;p&gt;现在回头看，把差异关进底层一个可替换的笼子，是这个平台能长期长胖、又不被方言拖垮的根本原因之一。我后来带新人看这层，第一句总是：别去记哪家库长什么样，记住这张图纸就行——一个抽象把&amp;quot;数据库的差异&amp;quot;和&amp;quot;业务的脑力&amp;quot;隔开了，这才是它最值钱的地方。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 7 篇 · 你以为大家都懂的缩写</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es07-naming-fossils/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es07-naming-fossils/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 7 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es06-swallowed-exceptions/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es08-plugin-architecture/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;读一套多人经手、常年维护的工控软件，最让我头大的不是逻辑难，是名字看不懂。有一批字段名是拼音首字母缩写，混在英文命名里。把一个量的中心缩成两个字母，长度缩成另外两个，我盯着屏幕看了半天，每个字母都认识，连起来不知道是什么。&lt;/p&gt;&#xA;&lt;p&gt;问原作者，他答得飞快，这个就是中心的缩写嘛。那一刻我意识到，这种名字只在一个人脑子里是清楚的。&lt;/p&gt;&#xA;&lt;p&gt;更隐蔽的是拼写错误。有人把某个常见单词少拼了一个字母，后面的人复制粘贴，错拼跟着扩散。到最后，你拼对了反而编译不过，错拼成了正确，正确反而成了错误。一个错别字一旦进入代码，传播路径很固定：手滑拼错，复制粘贴带走，编辑器自动补全记住错拼，一旦成了公开接口，改名要动所有引用，成本越来越高，于是将错就错。&lt;/p&gt;&#xA;&lt;p&gt;化石的定义是一个生物死后身体被矿化、固化，不可逆地留在地层里，记录着当时的环境。代码里的错别字和拼音缩写也一样，它们记录了这摊代码是怎么长出来的，谁写的、用什么输入法、当时赶不赶、有没有 Code Review。它们是团队沟通方式的无意沉积，而且一旦固化，几乎不可逆。你删不掉，因为改一个公开名字要动一片引用；你假装看不见，它们就一直在那里把新人的阅读速度拖慢。&lt;/p&gt;&#xA;&lt;p&gt;命名是写给未来的自己看的，不是写给人当下的键盘看的。一个只有你懂的缩写，是把未来的接班人挡在门外的锁。化石一旦形成，清理它比写它贵十倍，因为你要说服的不是编译器，是一直这么叫也没出事的惯性。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 7 篇 · 说每个请求都要过的几道关</title>
				<link>http://localhost:1313/posts/2026-09-30-es09-filter-chain/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es09-filter-chain/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 7 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es07-multi-db-pluggable/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es10-scheduler/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻这套工业数据 WebApi 时，我发现每个请求进来都要过几道&amp;quot;关&amp;quot;。验身份、管加解密、记耗时——这些事跟具体业务没关系，但每个接口都得做。这类跟业务无关、却每个接口都要做的事，最怕各写各的：口径不一，出了事谁都说不清。&lt;/p&gt;&#xA;&lt;p&gt;我早年写接口，把这些逻辑直接塞进每个 Controller，复制粘贴得到处都是，改一处得改十处。有回要加一道新安检，漏了该加的接口，出了岔子才回头补。这套平台把这类逻辑收成了一条通道。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 每道安检从同一套模板长出来，长相一致好替换&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;CheckFilter&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;IActionFilter&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;OnExecuting&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Ctx&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Has&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SkipAttribute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;())&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 贴&amp;#34;免检&amp;#34;就跳过&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;DoCheck&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;                          &lt;span class=&#34;c1&#34;&gt;// 子类写具体安检&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DoCheck&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Ctx&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每道安检是个可插拔的过滤器，统一从一套模板长出来。我在代码里数到 Filter 相关的接口有 &lt;strong&gt;28&lt;/strong&gt; 处，全仓的安检逻辑都挂在这条通道上，没有一处散回业务层。我年轻时以为复制粘贴最省事，后来才知道，重复才是最大的坑——改一处要找十处，漏一处就出事。通道把&amp;quot;每个请求都该做的事&amp;quot;收成一条，谁过哪道关一目了然。&lt;/p&gt;&#xA;&lt;p&gt;更妙的是它能&amp;quot;按门决定开不开&amp;quot;：给某个接口贴个免检标签，这道关就跳过它；或者要求&amp;quot;必须带了某标签才放行&amp;quot;。敏感逻辑（比如加密、数据安全）则只放在通道层统一做，业务代码根本不碰——规则写在走廊上，房子里的人不用操心。&lt;/p&gt;&#xA;&lt;p&gt;我后来规定，这类横切的事一律不准进业务层。默认每个请求都过安检，特例才贴免检——比写进每个房间干净一百倍。我踩过漏加安检的坑，所以现在格外较真这道关：默认都过、特例才免，比&amp;quot;要不要验&amp;quot;默认关更省心。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 8 篇 · 留插槽，别焊死主梁</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es08-plugin-architecture/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es08-plugin-architecture/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 8 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es07-naming-fossils/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es09-responsibility-boundary/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;读一套长期维护的工控软件架构时，有一点是它让我印象最深的：加一个功能页面，居然不用改主程序。&lt;/p&gt;&#xA;&lt;p&gt;它的做法是，每个功能是一个独立的插件包，自己带一张清单，写明我要挂一个页面、我要提供一套参数设置。框架启动时挨个读清单，把插件挂到该挂的地方。主程序本身几乎是个空壳，只负责读清单、搭舞台。&lt;/p&gt;&#xA;&lt;p&gt;我顺着这个思路往下看，发现模块之间不直接 new 依赖，而是向一个服务定位器要。要什么能力，问有没有实现某某接口的服务，拿到就用。接口是契约，实现可替换，只要新模块实现了同一个接口、注册进框架，就能替换掉老模块，调用方一行不用改。&lt;/p&gt;&#xA;&lt;p&gt;最让我服气的是一次真实需求：要支持一种新品牌的下位机。换作以前，我得在主程序里改通信层的分支、改设备选择逻辑、改一堆配置。但在那套系统里，下位机通信是一个可替换的接口，只新建了一个通信插件，把协议写进去，框架按配置挑它，主程序一行没动。&lt;/p&gt;&#xA;&lt;p&gt;可插拔的价值，不在酷，在边界清晰。当一个功能被关在自己的插件包里，它就不能随便去碰别的模块的内部；当它要被替换，你删掉这个包就行，不会在主程序里留下一地断线。当然插件化也不是白给的，间接层多了，追一个问题要绕几道弯，新人得先学这套扩展点的词汇，小项目上这么搞是给自己找麻烦。但那种要养很多年、不断加东西的系统，留插槽比焊死聪明太多。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 8 篇 · 说半夜自动干活的事</title>
				<link>http://localhost:1313/posts/2026-09-30-es10-scheduler/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es10-scheduler/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 8 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es09-filter-chain/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es11-define-runtime-engine/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻这套工业数据 WebApi 时，有几件事是半夜自己干的：每天同步一次数据、每小时跑个统计、每周清一次临时文件。我早年用系统自带的计时器，散在代码各处，有回排查&amp;quot;为什么半夜有笔操作&amp;quot;，翻了半天才找到一个藏在某类里的定时器。&lt;/p&gt;&#xA;&lt;p&gt;定时任务最怕&amp;quot;藏起来&amp;quot;。这套平台把它做成贴标签就上岗。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 给方法贴标签，就说清&amp;#34;隔多久跑、几点段跑、优先级&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;[Scheduled(Interval = 30, AllowedHour = &amp;#34;0-8&amp;#34;, Priority = Normal)]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;每日统计&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* 业务只管写&amp;#34;干什么&amp;#34; */&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我在代码里数到，这套调度在 Quartz 之上封装了 &lt;strong&gt;124&lt;/strong&gt; 处——全仓的定时任务都走这层薄薄的封装，业务方只面对&amp;quot;贴标签&amp;quot;这件事。标签上写清：隔几秒跑一次、哪天跑、一天里哪个时段允许跑、优先级多高。引擎读标签，自动排进自己的时间表。改这段代码的人，一眼看见&amp;quot;这个方法每天半夜跑&amp;quot;，自然就会多想一步：它跑的时候依赖的数据在不在、它失败了谁知道。意图写在代码身上，比写在文档或脑子里牢靠。&lt;/p&gt;&#xA;&lt;p&gt;我接手这段代码时，看方法头上的标签就知道它的脾气：每 30 秒一次、只在凌晨 0 到 8 点跑、优先级普通。不用去翻调度配置。我早年那套散装的计时器，恰恰相反——任务藏在类深处，半年后连我自己都忘了它还在跑。声明式的好处，是先让&amp;quot;什么时候跑&amp;quot;自己喊出来。&lt;/p&gt;&#xA;&lt;p&gt;我后来才看清，这跟 es06 那套&amp;quot;贴标签就上岗&amp;quot;是一脉相承的：把意图写在代码自己身上。调度引擎很复杂，但用一层封装把它藏起来，维护的人少掉很多头发。我现在的习惯就是，评审调度任务先看标签在不在方法上。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 9 篇 · 说图纸和施工分开的事</title>
				<link>http://localhost:1313/posts/2026-09-30-es11-define-runtime-engine/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es11-define-runtime-engine/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 9 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es10-scheduler/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es13-infra-layering/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;翻这套工业数据 WebApi 的源码，最让我舒服的一处设计，是它把&amp;quot;图纸&amp;quot;和&amp;quot;施工&amp;quot;分得很干净。&lt;/p&gt;&#xA;&lt;p&gt;每个能力在框架里都拆成两份：一份 &lt;code&gt;Define&lt;/code&gt;，只放接口，规定&amp;quot;能干什么&amp;quot;；一份 &lt;code&gt;Runtime&lt;/code&gt; 或 &lt;code&gt;Engine&lt;/code&gt;，放实现，决定&amp;quot;怎么干&amp;quot;。业务代码从头到尾只认 &lt;code&gt;Define&lt;/code&gt; 那一层的接口，从不去碰具体实现里用的是哪套 ORM、哪个协议、哪类数据库。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）典型的契约长这样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 图纸：只定义能力，不绑定任何框架&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;IDataPointProvider&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IReadOnlyList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DataPoint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ReadAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 施工：实现可以换数据库、换协议，业务看不见&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;KdbRepository&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;IDataPointProvider&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* 某国产数据库 */&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;它怎么被用上的&#34;&gt;它怎么被用上的&lt;/h2&gt;&#xA;&lt;p&gt;这种分离在平台里不是摆设。像 &lt;code&gt;RepositoryProvider&lt;/code&gt; 这个数据契约，被全仓 &lt;strong&gt;94&lt;/strong&gt; 处引用，但引用方全都指着接口，没有一处直接 &lt;code&gt;new&lt;/code&gt; 了具体的库实现。换句话说，业务描述的是&amp;quot;我要读一个点&amp;quot;，而不是&amp;quot;我要用某国产数据库读这个点&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;这正是端口-适配器思路的落点：业务站在端口这头，只对着契约说话；适配器（具体实现）在另一头，想怎么换都行。框架升级、库换了版本，只要适配器还认那张图纸，业务这头连通知都不用收到。&lt;/p&gt;&#xA;&lt;h2 id=&#34;好处是具体的&#34;&gt;好处是具体的&lt;/h2&gt;&#xA;&lt;p&gt;业务不依赖框架，换来的是换技术时不用动业务。某天要把底层存储从一种库迁到另一种，只要新实现还满足那张图纸，上层一行都不用改。契约成了稳定的边界，框架选型、版本升级都被挡在这条线之外。&lt;/p&gt;&#xA;&lt;p&gt;我翻代码时特意追了一条链路：一个采集任务从调度进来，到真正落库，中途过了好几道读写，但没有一处直接依赖具体数据库类型。它只认 &lt;code&gt;IRepositoryProvider&lt;/code&gt; 这类图纸。这套系统把&amp;quot;用什么&amp;quot;和&amp;quot;做什么&amp;quot;彻底错开了。&lt;/p&gt;&#xA;&lt;p&gt;我没法确认当年是不是一开始就想清楚了这一层，但站在这套两百多个工程、约 59 万行 C# 的体量上看，把&amp;quot;图纸&amp;quot;留稳、把&amp;quot;施工&amp;quot;放开，是最值的一笔账。后来我在评审别人设计时，也先看图纸和施工有没有混在一间房里——混了的，早晚出事。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 9 篇 · 谁该干什么，划清楚比聪明重要</title>
				<link>http://localhost:1313/posts/2026-09-30-sm-es09-responsibility-boundary/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-sm-es09-responsibility-boundary/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 9 / 9 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/straightening-machine-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-sm-es08-plugin-architecture/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;读一套测控系统的分工时，最让我安心的一个设计是它把算和动分得很开。上位机在工控机上算，测一圈信号、判断零件弯没弯、下一刀压多少；下位机在设备边动，伺服转、压头下、支撑托。&lt;/p&gt;&#xA;&lt;p&gt;我以前写这类东西，容易一股脑全塞进上位机。反正都能调，干脆上位机把动作时序也一起发了，下位机当个传声筒。看起来效率高，少一层通信，少一套协议，上位机点一下设备就动，简单粗暴，也好调试，至少在我自己的开发机上。&lt;/p&gt;&#xA;&lt;p&gt;但我忽略了一件事：上位机跑的是 Windows，有线程调度、有 GC、有界面卡顿，这些不确定性对算无所谓，对动要命。&lt;/p&gt;&#xA;&lt;p&gt;真正让我改观的是安全那一侧。那套系统的安全联锁，限位、急停、互锁，全在下位机本地自成回路。也就是说，哪怕上位机整个崩了、通信线断了，设备也不会因为没收到上位机的停而继续危险动作。&lt;/p&gt;&#xA;&lt;p&gt;还有一个细节：零件合格还是不合格，是上位机算出来的，但把合格的放这边、不合格的放那边这个分拣动作，是下位机自己执行的。上位机只把合格不合格这个结果递过去，具体怎么动下位机自己编排。上位机是法官，下位机是法警，法官只下判决，不亲自上手。&lt;/p&gt;&#xA;&lt;p&gt;边界划清楚，比谁更聪明更重要。把判断留在灵活但不确定的那一层，方便迭代算法；把动作放在确定且保命的那一层，实时、安全；两边各司其职，谁崩溃都不连累另一边的核心职责。反过来，如果你把动作时序也交给上位机，等于把设备安不安全绑在了 Windows 今天卡不卡上，那不是聪明，是赌。&lt;/p&gt;&#xA;&lt;p&gt;后来我不管写什么系统，都先画一条线：哪边不确定，哪边要保命。判断留在灵活那层，动作交给确定那层，这条线画对了系统就稳，画反了你天天在给不确定性擦屁股。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 10 篇 · 说水电煤气分开走的事</title>
				<link>http://localhost:1313/posts/2026-09-30-es13-infra-layering/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es13-infra-layering/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 10 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es11-define-runtime-engine/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es14-topology/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;下一篇 ›&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;工业数据 WebApi 这套平台里，最让我放心的是它的&amp;quot;水电煤气&amp;quot;是分开走的。&lt;/p&gt;&#xA;&lt;p&gt;所谓水电煤气，是指 ORM、缓存、加密、配置、日志这几类基础设施。早年这些能力常散在业务里，换一个缓存要改十几个模块，漏一处就出诡异的 bug。我早年就为这种散落吃过苦头，改到手软还漏了三处。在 &lt;code&gt;Frames&lt;/code&gt; 目录里，它们现在各自是一个独立的 frame：ORM 是 &lt;code&gt;NetORMCore.SqlSugar&lt;/code&gt;，缓存是 &lt;code&gt;CacheManagerCore&lt;/code&gt;，加密是 &lt;code&gt;Encrypt&lt;/code&gt; 那一支，配置是 &lt;code&gt;Configure&lt;/code&gt;，日志是 &lt;code&gt;LoggingCore&lt;/code&gt;。每一类只管自己的事，对外只露一个干净的入口。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）业务里调用它们的样子是各取所需、互不串门：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Configure&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Core&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;repo:primary&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// 配置&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CacheManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Core&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetOrAdd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 缓存&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Logging&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Core&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;provider loaded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;                 &lt;span class=&#34;c1&#34;&gt;// 日志&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;它为什么重要&#34;&gt;它为什么重要&lt;/h2&gt;&#xA;&lt;p&gt;分开走，意味着一种基础设施的写法变动，不会渗到另一种里面。加缓存不用动日志，换加密算法不用碰配置。每一根管都只接自己的表，谁也不往谁的管线里灌脏数据或隐式依赖。比如日志，业务只调 &lt;code&gt;Logging.Core.Info&lt;/code&gt;，至于它落文件还是落远程，业务不知道；哪天换日志后端，只动 &lt;code&gt;LoggingCore&lt;/code&gt; 一间。业务方完全无感，这才是分层该有的样子。&lt;/p&gt;&#xA;&lt;h2 id=&#34;代价换来的是清爽&#34;&gt;代价换来的是清爽&lt;/h2&gt;&#xA;&lt;p&gt;代价是&amp;quot;管&amp;quot;多了，接入时要清楚自己在用哪一根。但换来的是排查问题时能顺着一根管追到底，不会因为日志里混了缓存逻辑而迷路。我见过把缓存写进业务、把加解密揉进数据访问的项目，那种代码一升级就浑身疼。这里每根管独立，单测也能各自打桩。哪天要换掉某一个基础设施，影响的面被这根管子限定死了，不怕牵一发动全身。&lt;/p&gt;&#xA;&lt;p&gt;分层不是为了好看。它让&amp;quot;基础设施&amp;quot;真正成为可以整体替换的零件，而不是焊死在业务里的习惯。站在这套约 59 万行 C# 的体量上看，把水电煤气分清楚，是它还能稳步长大的底气之一。这种清爽，是大规模协作最稀缺的东西。&lt;/p&gt;</description>
			</item>
			<item>
				<title>开发随笔 · 第 11 篇 · 说一个能装两百多个摊位的商场</title>
				<link>http://localhost:1313/posts/2026-09-30-es14-topology/</link>
				<pubDate>Wed, 30 Sep 2026 08:00:00 +0800</pubDate>
				<guid>http://localhost:1313/posts/2026-09-30-es14-topology/</guid>
				<description>&lt;!-- series-nav --&gt;&#xA;&lt;div style=&#34;margin:1.4em 0;padding:.9em 1.1em;border:1px solid var(--neon,#009e8f);border-radius:10px;background:color-mix(in srgb,var(--neon,#009e8f) 8%,transparent);font-size:.9em;display:flex;gap:10px;flex-wrap:wrap;align-items:center&#34;&gt;&#xA;  &lt;span style=&#34;padding:.25em .7em;border-radius:999px;background:var(--neon,#009e8f);color:#fff;font-weight:600&#34;&gt;开发手记 · 第 11 / 11 篇&lt;/span&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/series/webapi-dev-notes/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;合集目录&lt;/a&gt;&#xA;  &lt;a href=&#34;http://localhost:1313/posts/2026-09-30-es13-infra-layering/&#34; style=&#34;padding:.25em .7em;border-radius:999px;border:1px solid var(--neon,#009e8f);color:var(--neon,#009e8f);text-decoration:none&#34;&gt;‹ 上一篇&lt;/a&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;写到这一篇，我想给整套工业数据 WebApi 平台画个总图。它给我的感觉，是一座能装很多摊位的商场。&lt;/p&gt;&#xA;&lt;p&gt;它从几个人、几个工程，慢慢长成现在这样。能长而不乱，靠的就是这四层把&amp;quot;谁该住哪&amp;quot;说清楚了。我维护过那种全塞一个工程的项目，加个人都怕动；这次从一开始就在想怎么长大之后还清楚。全仓一共 &lt;strong&gt;243&lt;/strong&gt; 个 &lt;code&gt;.csproj&lt;/code&gt;，约 &lt;strong&gt;59&lt;/strong&gt; 万行 C#。这个体量，靠乱堆是撑不住的。它们不是摊在一起，而是分成四层：最底下是宿主，也就是真正起服务的 &lt;code&gt;WebServerAPICore&lt;/code&gt;；往上是框架层 &lt;code&gt;Frames&lt;/code&gt;，装着前面说的加载器、契约与基础设施；再往上是两类摊位——业务插件（比如各类数据提供方、数据服务）和基础插件（ORM、缓存、加密、配置、日志）。&lt;/p&gt;&#xA;&lt;p&gt;（等价改写，名字换掉了）四层大致长这样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;宿主   WebServerAPICore&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  └─ 框架   Frames/*（加载器·契约·基础设施）&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ├─ 业务插件   RepositoryProvider/*、DataService…&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       └─ 基础插件   ORM·缓存·加密·配置·日志&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;商场是怎么运转的&#34;&gt;商场是怎么运转的&lt;/h2&gt;&#xA;&lt;p&gt;宿主只认框架，框架把摊位按约定接进来。业务插件和基础插件都&amp;quot;租用&amp;quot;框架提供的同一套规则，彼此不直连。宿主薄、框架厚、业务插件化，是这个商场的黄金比例：宿主只开门迎客，不干业务；框架层沉淀通用能力；新业务就是新摊位，拎包入驻，不用动商场本身。要扩张就加摊位，结构和物业标准都不用改，所以两百多个工程还能各归其位。&lt;/p&gt;&#xA;&lt;h2 id=&#34;收个尾&#34;&gt;收个尾&lt;/h2&gt;&#xA;&lt;p&gt;这一篇是系列的最后一篇。回看前几篇：热加载靠隔离、可插拔靠图纸分离、自我生长靠零件柜、横切能力靠分层——它们能站稳，根都扎在这个四层拓扑里。从第一个拼错的单词，到能装两百多个摊位的商场，我看到的不是某一项惊世骇俗的设计，而是一连串&amp;quot;让边界待在边界里&amp;quot;的耐心决定。正是这些朴素的选择，撑起了这套平台的样子。回头看，好的架构不是不长大，是长大了还清楚谁在哪。&lt;/p&gt;</description>
			</item>
			<item>
				<title>中华经典藏书（升级版）上（32册）</title>
				<link>http://localhost:1313/library/%E4%B8%AD%E5%8D%8E%E7%BB%8F%E5%85%B8%E8%97%8F%E4%B9%A6-%E5%8D%87%E7%BA%A7%E7%89%88-%E4%B8%8A-32%E5%86%8C/</link>
				<pubDate>Sat, 10 Oct 2026 00:00:00 +0800</pubDate>
				<guid>http://localhost:1313/library/%E4%B8%AD%E5%8D%8E%E7%BB%8F%E5%85%B8%E8%97%8F%E4%B9%A6-%E5%8D%87%E7%BA%A7%E7%89%88-%E4%B8%8A-32%E5%86%8C/</guid>
				<description>&lt;p&gt;《中华经典藏书（升级版）上（32册）》已加入图书馆，可在线阅读或下载。&lt;/p&gt;</description>
			</item>
			<item>
				<title>Sample Technical Book</title>
				<link>http://localhost:1313/library/sample-book/</link>
				<pubDate>Fri, 09 Oct 2026 00:00:00 +0000</pubDate>
				<guid>http://localhost:1313/library/sample-book/</guid>
				<description>&lt;p&gt;这是示例书的正文摘要区。点击「阅读」可进入在线阅读页，或直接从下方 / 列表页下载 EPUB / PDF。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;说明：本文件是随需求文档一并生成的演示书目，证明图书馆模块可正常工作；正式使用时替换为你的真实书目即可。&lt;/p&gt;&#xA;&lt;/blockquote&gt;</description>
			</item>
			<item>
				<title>关于</title>
				<link>http://localhost:1313/about/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>http://localhost:1313/about/</guid>
				<description>&lt;h1 id=&#34;关于我的编程之路&#34;&gt;关于「我的编程之路」&lt;/h1&gt;&#xA;&lt;p&gt;这里记录嵌入式、前端、Linux、Python 等领域的学习笔记与实战复盘。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;技术栈：C / Go / Python / Node.js&lt;/li&gt;&#xA;&lt;li&gt;兴趣：单片机、智能家居、自动化运维&lt;/li&gt;&#xA;&lt;li&gt;联系方式：见页脚社交图标&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;本站由 Hugo + PaperMod 构建，评论与阅读量由 Waline 提供。&lt;/p&gt;&#xA;&lt;/blockquote&gt;</description>
			</item>
			<item>
				<title>友链</title>
				<link>http://localhost:1313/friends/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>http://localhost:1313/friends/</guid>
				<description>&lt;h1 id=&#34;友情链接&#34;&gt;友情链接&lt;/h1&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;站点&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;简介&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;a href=&#34;https://example.com&#34;&gt;示例博客 A&lt;/a&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;嵌入式与物联网&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;a href=&#34;https://example.com&#34;&gt;示例博客 B&lt;/a&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;前端工程化&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;申请友链请通过页脚邮箱联系。&lt;/p&gt;&#xA;&lt;/blockquote&gt;</description>
			</item>
			<item>
				<title>归档</title>
				<link>http://localhost:1313/archives/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>http://localhost:1313/archives/</guid>
				<description></description>
			</item>
			<item>
				<title>搜索</title>
				<link>http://localhost:1313/search/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>http://localhost:1313/search/</guid>
				<description>&lt;h1 id=&#34;站内搜索&#34;&gt;站内搜索&lt;/h1&gt;&#xA;&lt;p&gt;本站全文搜索由前端索引方案提供（推荐 &lt;a href=&#34;https://pagefind.app&#34;&gt;Pagefind&lt;/a&gt;）。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;部署说明&lt;/strong&gt;：&lt;code&gt;hugo&lt;/code&gt; 构建后执行 &lt;code&gt;npx pagefind --site public&lt;/code&gt; 生成索引，&#xA;再在本页接入 &lt;code&gt;pagefind.search()&lt;/code&gt; 即可启用。本地预览阶段此处为占位，&#xA;导航结构已与参考站 1:1 对应。&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
