维护一个企业系统 12 年:我学到的三件事
发布于 2026年9月27日
我为美国一家社会服务机构的案件管理平台工作了 12 年——登记无家可归者、协调他们获得的帮助,每个个案工作者的日常都经过它。在 20 人的团队里,我负责地基:网关、验证、公共库、统一数据操作引擎,以及 Account、Chat、Library 几个服务和它们的多年升级。地基这种东西有一种特殊的信任结构:你的模块挂了,所有人的都挂。
12 年足够把很多"最佳实践"验证成"真的吗"。以下三件事是我最确定的。
一、演进不能缺席,但可以按自己的节奏
系统从 2007 年的 VB.NET + WinForms 起步,跨了六代:Silverlight、ASP.NET MVC、AngularJS SPA、Angular + .NET Core,直到今天的 .NET 8 微服务。每一次都是在线迁移,不是推倒重来——业务连续性压倒一切,系统不能停,所以"先停机再升级"从来不是选项。
我见过太多系统死法,最常见的不是"大升级升挂了",而是一直没升级。技术债不是线性累积的,是过了某个点突然计息:老框架停止安全更新、招不到会用的人、新依赖装不上——每一项单独看都"还能忍",合起来就是系统死亡。反过来,认真规划的大升级几乎都能安全落地:我们把六代迁移的每一代都活着扛过来了,靠的不是胆子大,是把"演进"当成和写代码同等级的工程任务:先迁边缘、再迁核心、新旧并存到最后一刻。
长期系统死于被跳过的升级,远多于死于被认真做完的大升级。
二、守住数据层
我做的一个关键决策是把数据访问收敛成一个统一引擎:读取、刷新、更新数据源,全部走一个接口。业务团队因此从不手写裸数据访问——不是靠 code review 拦,是构造上就没有第二条路。12 年里前端换了五张脸,数据引擎纹丝不动。
这背后是一个分层原则:变化频繁的层和变化缓慢的层要分开锁住。UI 五年一换代,数据模型十五年不动——让它们共享命运,就是让最稳定的东西陪最快的东西一起折腾。数据层按负载分别用 EF 和 Dapper,是这个原则的微观应用:写路径要开发效率,热路径要裸 SQL 的控制力,但两边的调用者看到的仍然是同一个引擎接口。
最艰苦的战役是 SQL Server → Oracle 的复制管线:千万行级、Change Tracking 跟踪、对端 Windows Service 消费。早期版本又慢又丢数据,最终架构让丢数据在构造上不可能——三道闸,每道都独立成立:
SQL Server 端 Oracle 端
├─ 前置 CT 健康检查 ├─ Windows Service
├─ 变更状态查询 API ├─ 事务化批量插入
└─ 批量读取 ├─ 结构化错误跟踪(定位到批次)
└─ 漂移即邮件报警
"让丢数据在构造上不可能"和"测试覆盖丢数据场景"是两个重量级的承诺。前者是架构承诺:不是每次都对,而是没有出错的路径。
三、让系统自己会喊疼
同步管线稳定运行的最后一块拼板不是算法,是可观测性:结构化错误跟踪定位到批次、任何漂移立即邮件报警、一条状态查询 API 随时回答"同步健康吗"。
这三样东西上线前后,团队的生活是两个物种。之前,"同步出问题了吗"是一次排查:登服务器、翻日志、对行数;之后,它是一条查询,而且大多数时候是报警先找到你。"同步健康吗?"从一次排查变成一条查询——这不是锦上添花,这是值班的人能不能睡觉的区别。
12 年维护教会我的元教训:运维的本质不是不出问题,是问题出现时你已经知道。
这 12 年对客户意味着什么
如果你在评估要不要把系统交给一个人长期维护,看的不是他会不会写代码,而是:他守不守地基、跟不跟演进、建没建可观测性。会写代码的人很多,愿意在场 12 年的人很少。
最后补一段后记:这三条教训现在是我 AI 工作流的地基。"分层锁住"变成了接缝设计——全站只有一个测试接缝;"构造上不可能"变成了断言从真实数据源动态推导——规则不能靠记住,要靠构造;"系统自己会喊疼"变成了 183 项跑在 CI 里的断言——问题出现时,构建已经红了。方法论会换代,教训是复利的。