编写可维护的代码 -- 面向AI编程
Oct 16 2025这是最近在小组内做的一次技术分享的文字稿,总体来说我觉得没有表达出特别硬核的内容,我本身也希望说这次分享不用讲的太硬核,在分享的开头还设计了互动环节,分享的过程中也尝试加入一些问答来互动,奈何现公司的技术氛围确实比较沉闷,似乎同事都比较I,好在最后的问答环节有几个比较好的问题,总体来说还算是一次成功的分享吧,希望自己能输出更多的好内容。
P1: 我们写的代码是写给谁看的?
- (🎤 互动环节): “大家有没有过接手一个项目时,看到的第一感觉是:我该从哪下手?结果花了三天才搞明白一个功能的逻辑。有过类似经历的兄弟举个手我看看?”
- 小说家写小说给读者,建筑师盖房子给业主… 那我们程序员写代码,最终是写给未来的自己,和接手我们代码的同事看的。
- 代码的可维护性,决定了我们是在“创造价值”还是在“创造负债”。
P2: 举几个代码坏味道的例子?
- (🎤 互动环节): “看过《重构》的同学应该都知道代码坏味道。来,大家一起吐槽一下,你在项目里见过最痛苦的代码坏味道是什么?” (引导大家说出几个)
- 其实按照我朴素的理解,能让我快速理清逻辑的就是好代码,理解起来很费劲的,那肯定就充满坏味道。
- 正如我在入职后接手的
kitam
项目,它就充满了这些味道:- 代码逻辑不内聚:功能实现像天女散花。
- 分层过多且混乱:一个请求要穿越重重关卡。
- 外部依赖离散:数据库、缓存的调用没有统一规范。
P3: 今天分享的主题
- 今天分享的主题是 编写可维护的代码,以及我们可探讨下面向AI编程。
- 面对代码的坏味道和混乱,我们有什么良药?—— 答案是:一个好的架构。今天我将分享我们
kfinops
项目中使用的架构:整洁架构 (Clean Architecture)。
P4: 我们的敌人是谁?——是“耦合”这头怪兽!
- (🖼️ 视觉元素占位符: 此处放置一张生动的图片,比如一个身上贴满“耦合”标签的小怪兽 🐲)
- 在深入架构之前,我们先要认清我们真正的敌人:耦合 (Coupling)。
- 什么是耦合?当你修改代码的一个地方,却意外地导致另一个看似无关的地方也必须修改,甚至直接崩溃时,你就遇到了耦合。
- 整洁架构的核心目标只有一个:斩断不必要的耦合,为我们的代码建立“防火墙”,控制变更的“爆炸半径”。
- 接下来,我会通过4个我们都经历过的惨痛问题,来展示整洁架构是如何一步步帮我们驯服这头怪兽的。
- (🖼️ 视觉元素占位符: 此处展示完整的整洁架构“洋葱图”,并快速解释四个环:Entities -> Use Cases -> Adapters -> Frameworks)
P5: 问题一:“我只想改个业务规则,为什么还要动数据库和API的代码?”
- (🖼️ 视觉元素占位符: 洋葱图高亮最内层的 “Entities” 环)
痛点场景: 你想修改一个实体(比如
User
)的业务规则。但在很多项目中,这个实体被定义成了一个“万能Struct”:// user.go // 它既是业务实体,又是GORM模型,还是HTTP响应体 type User struct { ID uint `json:"id" gorm:"primaryKey"` // <-- 高亮此行 Name string `json:"name" gorm:"size:255"` Points int `json:"points"` // ... }
- 现在,产品说:为了兼容新App,
ID
字段对外要改成user_id
。 - 你只能修改:
ID uint \
json:“user_id” gorm:“primaryKey”``。 - 问题来了: 你只是在修改一个前端展示字段的名称,却迫使你修改了一个核心业务实体(User)的文件。这就是业务逻辑和展示细节的耦合。
- 现在,产品说:为了兼容新App,
解药:一个纯净的【领域层 Domain Layer】
- 它的唯一职责:建立一个“无菌室”,只存放最核心、最纯粹的业务实体和业务规则。
kfinops
的实践:// internal/domain/subdomain.go // 只有业务属性,没有json,gorm等任何技术标签 type SubDomain struct { ID string // 纯粹的业务ID FullDomain string Status SubDomainStatus }
效果: 业务核心完全稳定。前端/数据库的任何变化,都不会触碰到这个文件。
P6: 问题二:“‘创建子域名’这个功能,代码到底在哪?”
- (🖼️ 视觉元素占位符: 洋葱图高亮第二层的 “Use Cases” 环)
- (🎤 互动环节): “举个手,谁遇到过想看懂一个功能,结果在IDE里跳了十几个文件的情况?”
- 痛点场景: 业务流程像天女散花,心智负担极大,你根本找不到一个地方能看清这个功能的“剧本”。这就是业务流程的逻辑离散。
解药:一个清晰的【用例层 Use Case Layer】
- 它的唯一职责:像一个“导演”,负责编排一个完整的业务故事。一个Use Case就代表系统的一个能力。
kfinops
的实践:// in internal/usecase/subdomain/service.go type SubDomainUseCase struct { repo repository.SubDomainRepository // 依赖接口 } func (uc *SubDomainUseCase) CreateSubDomain(...) error { // 1. 验证输入 (DTO) <-- 高亮 // 2. 创建 Domain 实体 <-- 高亮 // 3. 调用 Domain 实体业务方法 <-- 高亮 // 4. 通过【接口】进行持久化 <-- 高亮 return uc.repo.Save(ctx, domainEntity) }
效果: 想理解一个功能,只需要看这一个文件。它清晰地讲述了“做什么(What)”,而不关心“怎么做(How)”。
P7: 问题三:“数据库要从MySQL换成MongoDB,完了,项目要重写了!”
- (🖼️ 视觉元素占位符: 洋葱图高亮第三层的 “Adapters” 环,并画一个箭头表示依赖反转)
- 痛点场景: 你的代码里到处都是
gorm.DB.Where(...).Find(...)
。你的业务逻辑“知道”你正在使用GORM和MySQL。这就是业务逻辑和数据存储技术的强耦合。 - 解药:【适配器层 Adapter】 + 【依赖倒置原则】
- 它的唯一职责:将技术细节“适配”成业务层能理解的“插件”。
- 核心魔法——依赖倒置:
- Use Case(使用者) 在自己包里定义接口。
- Adapter(实现者) 去实现这个接口。
- 效果: 技术实现变成了可插拔的“零件”。更换数据库?只需要写一个新的Repository实现,然后在DI配置里换掉即可,Use Case层的代码一行都不用改!
P8: 问题四:“这代码根本没法写单元测试!”
- (🖼️ 视觉元素占位符: 一张图片,左边是混乱的毛线球代表紧耦合,右边是整齐的乐高积木代表可测试)
- (🎤 互动环节): “诚实地举手,谁因为代码太难测试而放弃写单元测试的?”
- 痛点场景: 业务逻辑里
new
了一个数据库连接,导致单元测试必须依赖真实环境。这是【可测试性灾难】。 解药:【接口】 + 【依赖注入 Dependency Injection】
- 它的唯一职责:将组件之间的依赖关系从“硬编码”变为“外部配置”。
效果:在单元测试中,我们可以轻松地“骗”过 Use Case:
// 我们可以轻易地用一个 "假的" mockRepo 替换掉 "真的" GormRepo mockRepo := new(MockSubDomainRepository) // 告诉 mockRepo: "当有人调用Save方法时,假装成功" mockRepo.On("Save", mock.Anything, mock.Anything).Return(nil) // <-- 高亮 useCase := NewSubDomainUseCase(mockRepo) // 依赖被注入了! <-- 高亮 err := useCase.CreateSubDomain(...) assert.NoError(t, err) // 测试通过!全程没有碰过数据库!
这让编写快速、可靠的单元测试成为可能,极大地保证了代码质量。
P9: 可维护性的延伸:构建强大的可观测性
- 作为SRE团队,我们不仅要写出可维护的代码,更要确保系统在生产环境中是‘可理解’、‘可诊断’的。
- 可观测性三大支柱:
- 🪵 日志 (Logging) - 定位“哪里”出错了: 接入KAE,结构化日志,包含
request_id
。 - 📊 指标 (Metrics) - 了解“怎么样”了: Prometheus + Grafana Dashboard。
- 🔗 追踪 (Tracing) - 分析“为什么”慢了: OpenTelemetry 分布式追踪。
- 🪵 日志 (Logging) - 定位“哪里”出错了: 接入KAE,结构化日志,包含
- 小结:整洁的架构让代码在“静态时”易于理解;完善的可观测性体系则让系统在“运行时”易于诊断。
P10: 面向AI编程:架构即契约,约束即指导
- (🖼️ 视觉元素占位符: 一个对比图)
- 左边 (无架构): 人类 🤯 → prompt → AI 🤖 → 一堆散乱代码 💩
- 右边 (有架构): 人类 😎 → 短prompt → AI 🤖 → 精准、分层的代码 ✅
- 一个好的架构,就是整个项目的“代码契约 (Code Contract)”,它为AI提供了一个清晰、明确的上下文环境。
- 有了这份“契约”,约束就变成了对AI最有效的指导:
- 架构定义了“剧本 (Playbook)”: AI会像一个熟悉项目的老手一样,在正确的地方写代码。
- 约束提供了“护栏 (Guardrails)”: AI不会在usecase层直接写SQL。
- “契约”简化了我们的“指令 (Prompt)”: 从“一步步告诉AI怎么做”,变成了“为
CreateUser
用例实现API”这样简单的指令。
- 结论:架构即提示 (Architecture as the Prompt)。
P11: 总结与行动指南
- 编写可维护代码的核心是“降低理解成本”和“控制变更影响”。
- 整洁架构是一份强大的“代码契约”,通过强制规则保证了项目的一致性。
- (🚀 行动号召):
- 团队可以马上尝试的实践: 在下一个新功能里,先和同事花10分钟讨论一下它的 Use Case 是什么。
- 推荐大家试用: 克隆我们的
kfinops
项目或go-clean-arch
模板,亲手运行一下,感受分层的清晰。
- 面向AI编程不是一句口号,而是一种新的工作范式。 你的架构越清晰、约束越明确,AI就越能成为你的得力助手。
P12: Q&A
- 谢谢大家!
- 项目模板参考: https://github.com/zhu327/go-clean-arch
- 大家有什么问题吗?或者对我们团队未来的代码规范有什么建议?