go包名必须小写单数名词,如package user;避免循环导入需用接口解耦;导出标识符仅限必要项;main包仅含main函数且不可被导入;internal目录保障内部实现安全。

包名必须小写且用单数名词
Go 不允许包名含大写字母或下划线,否则 go build 会直接报错:invalid package name。更隐蔽的坑是:包名该用 user 还是 users?官方规范和标准库(如 net/http、os/exec)全用单数,因为包是命名空间,不是资源集合。
- ✅ 正确:
package user(对应目录user/,导出类型User) - ❌ 错误:
package users(易让人误以为包内全是 slice 或批量操作) - ⚠️ 注意:
import "myproj/user"中路径名可与包名不同,但包声明行必须是package user,不能靠路径“补救”
避免循环导入:用接口解耦依赖方向
当 a.go 导入 pkg/b,而 b.go 又需要调用 a.DoSomething(),就容易写出循环导入错误:import cycle not allowed。硬拆成三个包太重,实际中优先用接口把依赖“翻转”过来。
- 在
pkg/a定义type Processor interface { Process() } - 让
pkg/b接收该接口作为参数,不 importpkg/a -
pkg/main或上层协调者负责实例化并传入,控制流清晰,测试也方便 mock - ⚠️ 坑点:别在接口里塞太多方法——它本质是调用方契约,不是实现方清单
导出标识符首字母大写,但别为了导出而导出
Go 的可见性规则简单粗暴:首字母大写 = 导出 = 外部可访问。但很多人一上来就把所有 struct 字段、helper 函数全大写,结果导致 API 面积膨胀、内部逻辑暴露、后续无法修改。
- 只导出真正需要被其他包使用的类型、函数、常量,比如
User结构体要导出,但userCache全局变量绝不能导出 - 字段是否导出看使用场景:
Name(外部需读写)导出,createdAt(仅内部审计用)保持小写 - 测试时发现某个内部函数被频繁 import,说明职责没理清——考虑把它移到调用方包,或封装进导出类型的某个方法里
main 包只能有 main 函数,且不能被其他包 import
Go 规定 main 包是程序入口,编译成可执行文件。但新手常把业务逻辑全堆进 main.go,甚至试图在别的包里 import "./cmd/myapp" ——这会导致 cannot import "myapp/cmd/myapp" because it imports "main"。
-
main.go应极薄:解析 flag、初始化配置、调用核心 service 层,然后os.Exit() - 所有业务逻辑、数据结构、HTTP handler 必须放在非
main包里,比如pkg/service、internal/handler -
internal/目录是安全区:放在里面的包连同级其他模块都不可被外部 import,适合放纯内部实现
包设计最麻烦的从来不是语法,而是决定“谁该知道什么”。边界划歪了,后面加 interface、改 import、修 test 都得跟着动。多花五分钟想清楚依赖方向,比写十行代码还重要。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











