可维护go代码需从第一天起放弃“能跑就行”思维:包名禁用util/common,按业务边界命名(如email、payment);结构体字段避免类型冗余(name而非username);错误处理尽早return而非嵌套;接口由使用者定义且尽量小(如仅save())。

go 编译快、语法少,容易让人误以为“随便写也能凑合用”。但真实项目里,三个月后你再看自己写的 main.go,如果满屏是嵌套的 if err != nil、包名全是 util 和 common、结构体字段带类型名(比如 UserName string),那维护成本已经悄悄翻倍了。
包命名别碰 util、base、helper
这些名字看似省事,实际等于没命名。团队里没人知道 util 包里到底该放什么——是 HTTP 工具?还是字符串处理?还是数据库连接封装?结果就是所有人都往里塞,最后变成“所有代码的垃圾桶”。
- 按职责边界命名,比如
email、payment、auth,哪怕初期只有两三个函数 - 一个包只做一件事,
payment不该包含用户校验逻辑 - 避免跨包循环依赖:A 包 import B,B 又 import A ——
go build会直接报错import cycle not allowed
结构体字段别用类型名当变量名
type User struct { UserName string; UserAge int } 看似清晰,实则冗余。Go 的零值语义和 IDE 支持足够强,Name 和 Age 在上下文中已足够明确。
- 字段名应表达业务含义,不是类型说明
- 重复类型信息会让结构体变胖,也干扰
json序列化时的字段映射(比如json:"user_name"就多了一层转换) - 统一风格比“看起来像 Java”更重要:Go 标准库里全是
http.Client、os.File,没人写httpClient
错误处理别堆 if err != nil 嵌套
深度嵌套的错误检查会让主逻辑被挤到右边,读起来像解谜。这不是 Go 强制的写法,而是没设计好控制流的结果。
- 尽早
return,而不是用else包裹正常路径 - 把校验提前:参数检查、配置加载失败、依赖初始化失败,都该在函数开头处理
- 不要在中间层“吞掉”错误:调用
db.QueryRow()后只打印日志却不返回,下游永远不知道查询失败了
接口定义要小,且由实现方决定
Go 的接口是隐式实现的,但很多人一上来就定义 type Service interface { Do() error; Undo() error; Retry() error }。问题在于:这个接口是给谁用的?调用方真的需要所有方法吗?
- 接口应该由使用者定义,比如 handler 只需要
Save(),那就只声明它需要的 - 小接口更容易 mock 和测试,也更稳定:加个新方法不会让所有实现都编译不过
- 标准库里最典型的例子是
io.Reader和io.Writer—— 各自只有一个方法,却组合出整个 I/O 生态
go mod init 的那一刻就开始积累。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











