Go 语言命名与目录结构规范全解

文件 / 目录 / 包 / 变量 / 常量 / 函数 / struct / 接口 / 方法接收者 —— 把《Effective Go》《Go Code Review Comments》的约定一次讲清,配对照表与图。

文件 目录/包 项目结构 导出红线 变量 常量 函数 struct 接口 接收者 错误 注释 杂项 速查表

Go 的「规范」有三大权威来源,按优先级:

① gofmt(机器强制)
缩进用 tab、括号位置、import 排序都由 gofmt 自动统一,人和编辑器都不吵架。语法层面的规范没有商量余地。
② Effective Go(官方约定)
go.dev/doc/effective_go 给出命名、包、接收者、getter 等约定,是「社区默认答案」。
③ Go Code Review Comments
github.com/golang/go/wiki/CodeReviewComments,补充缩写词、receiver 名、包名等细节,是 PR review 的标尺。
④ Standard Go Project Layout
golang-standards/project-layout,虽非官方,却是社区事实标准(cmd / internal / pkg 等)。
一句话总纲:Go 故意不用 snake_case 也不用 PascalCase 匈牙利前缀,统一用「大小写驼峰(MixedCaps)」靠首字母大小写表达「是否导出」。所有命名规范都围绕这一条展开。

① 文件(.go)命名规范

小写 + 下划线分隔,绝不用驼峰、绝不大写开头。

✅ 推荐
user_service.go
http_handler.go
order_repository.go
❌ 禁止
userService.go(驼峰)
UserService.go(大写开头)
user-service.go(连字符,import 路径才允许)

特殊后缀(Go 工具链识别)

后缀含义说明
_test.go测试文件go test 编译;包名可同包或 包名_test(黑盒外部测试)
_<GOOS>.go平台专属socket_linux.goread_windows.go(Go 1.17+ 自动按 GOOS 选编)
_<GOARCH>.go架构专属crc32_amd64.goasm_arm64.s
_test 包名外部测试package user_test 只能访问导出符号,用来测「公开 API」而非内部
注意:_ 在文件名里只用于「下划线分隔多词」和「构建约束后缀」两种场景。普通业务文件名一律小写加下划线即可,别画蛇添足。

② 目录与包(package)命名规范

目录名小写单词、包名与目录同名且小写无下划线、避免 util/common 等泛词。

目录 user/ 小写,无下划线无驼峰 package user 与目录同名·小写·无下划线 import 路径 github.com/x/user 调用处: user.GetByID(123) ← 包名就是前缀,所以字段/方法别再带包名

包名三大铁律

规则正确错误原因
小写、单词、无下划线/驼峰http, strconv, userurlUtils, user_svc包名是调用前缀,越短越好读
与目录同名dir user/package userdir user/package usersvc降低心智负担,工具约定
避免泛词具体语义名util, common, base, misc, tool泛词等于没命名,应就近拆包
不加类型后缀user(含 User 类型)usermodel, userpkg包已表达容器,类型再表达语义
import 路径里可以出现连字符(如 github.com/foo/bar-user),但路径里的连字符不会进入包名——包名仍是单驼峰小写。为省心,目录也尽量不用连字符。

③ 项目目录结构规范(Standard Go Project Layout)

官方不强制布局,但社区事实标准长这样。小型库可只有 go.mod + 包目录。

myproject/ ├── cmd/ # 各可执行程序的 main 包(一个子目录一个二进制) │ └── server/ │ └── main.go # package main,只做组装与启动 ├── internal/ # 私有代码:外部模块 import 不到(Go 关键字 internal) │ ├── service/ │ └── repo/ ├── pkg/ # 公开库:可被外部安全 import(谨慎放,放出去就兼容承诺) ├── api/ # API 定义:.proto / openapi / .api(go-zero 风格) ├── configs/ # 配置文件样例(yaml/toml) ├── docs/ # 文档 ├── test/ # 额外测试数据/集成测试 ├── scripts/ # 构建/部署脚本 ├── build/ # 打包产物、Dockerfile ├── go.mod # 模块根(必须) ├── go.sum ├── LICENSE └── README.md
internal/ 是语言特性
Go 规定:internal/ 下的包只能被以 internal 父目录为根的子树 import,是「私有」的硬保障,不是约定。
cmd/ 每个二进制一个目录
避免多个 main 包挤在一起。main 包应极薄:建 svc、注册路由、Start,业务塞进 internal。
pkg/ 要克制
放出去的 API 要长期兼容。不确定是否公开时,先放 internal,用熟了再提升。
go-zero 的变体
go-zero 用 api/(.api 文件)、rpc/etc/(yaml 配置)、internal/(handler/logic/svc),本质仍是这套思路。

④ 导出 vs 不导出:首字母大小写

这是 Go 命名里最重要的一条——首字母大写 = 跨包可见,小写 = 仅包内可见。没有 public/private 关键字。

大写开头 → 导出 🌐 type User struct{} GetUser() 函数 Name 字段 / ErrNotFound 跨包可访问,需写文档注释 小写开头 → 不导出 🔒 type user struct{} getUser() 函数 name 字段 / errNotFound 仅本包内可用,实现细节 可见性由首字母决定
任何「想给别人用」的标识符(类型、函数、方法、字段、变量、常量、错误)首字母必须大写。反之,只要是内部实现细节,一律小写——这是 Go 封装的唯一手段。

⑤ 变量命名规范

驼峰(MixedCaps)、缩写词全大写、短作用域用短名、不在名字里带类型。

核心规则

规则正确错误
驼峰而非下划线userCount, requestIDuser_count, request_id
缩写词全大写一致userID, httpServer, parseURLuserId, HTTPServer, parseUrl
不携带类型(反匈牙利)users []UseruserSlice, userList []User
布尔用谓词/前缀isValid, hasPerm, canEditvalid, permission, flag
短作用域用短名i, err, ok, n, b, w, rindex, errorResult, isValidFlag
常用缩写词必须全大写:IDURLHTTPJSONXMLSQLAPIHTMLRPCTCPUTF8ASCII。来源:Go Code Review Comments 明确点名 ID/URL。

声明风格

// 函数内优先用短声明 :=
func handler(w http.ResponseWriter, r *http.Request) {
    userID := r.FormValue("id")   // 局部短名
    users, err := repo.List(r.Context())
    if err != nil { /* err 是约定名 */ }

    var total int            // 包级/零值/需显式类型时用 var
    const timeout = 30 * time.Second
}

⑥ 常量命名规范

驼峰导出大写;枚举用 iota,首值显式给类型,后续继承。

// 普通常量:驼峰,导出大写
const (
    MaxRetries = 3
    DefaultTimeout = 30 * time.Second
)

// 枚举:iota 自增,首值带类型
type OrderStatus int
const (
    StatusPending OrderStatus = iota   // 0
    StatusPaid                               // 1,继承 OrderStatus 类型
    StatusShipped                            // 2
    StatusDone                               // 3
)

// 需要「跳过值/位标志」时也用 iota
const (
    Read  Perm = 1 << iota   // 1
    Write                          // 2
    Exec                           // 4
)
枚举的「字符串化」一般另配 func (s OrderStatus) String() string,而不是把常量直接写成字符串(避免魔法字符串满天飞)。

⑦ 函数命名规范

驼峰、导出大写;构造函数 NewXxx;getter 不加 Get 前缀;error 永远放最后。

场景约定示例
普通函数驼峰,导出大写GetUser, SaveOrder, ParseConfig
构造函数NewXxx 返回指针(或值)NewClient, NewServer
Getter不加 Get 前缀u.Name()u.GetName()
Setter可加 Set 前缀u.SetName(n)
返回 errorerror 作为最后一个返回值(res Result, err error)
返回副本 vs 修改语义清晰,避免歧义WithXxx() 返回新值常见
// 构造 + getter/setter(Go 风格,无 Get 前缀)
type User struct { name string }

func NewUser(name string) *User {   // 构造函数
    return &User{name: name}
}
func (u *User) Name() string { return u.name }      // getter:无 Get
func (u *User) SetName(n string) { u.name = n }  // setter

// error 永远最后
func GetUser(ctx context.Context, id int64) (*User, error) {
    if id <= 0 {
        return nil, fmt.Errorf("invalid id %d", id)
    }
    // ...
}
「getter 不加 Get」是 Effective Go 的建议;但若方法真的会「去远程拉取/读库」,用 GetXxx 反而更诚实。判断标准:是否只是读字段。

⑧ struct 命名规范

类型名 UpperCamel 不带 Struct 后缀;字段驼峰、导出大写;用 tag 标注序列化;构造函数 NewXxx。

// ✅ 类型名即语义,不叫 UserStruct / UserModel
type User struct {
    ID        int64  `json:"id" db:"id"`        // 导出字段大写
    Name      string `json:"name"`
    email     string                                // 不导出:仅包内
    CreatedAt time.Time `json:"created_at"`
}

// 构造函数返回指针(结构体较大或需共享状态时)
func NewUser(name string) *User {
    return &User{Name: name, CreatedAt: time.Now()}
}
规则正确错误
类型名不带冗余后缀User, Order, ConfigUserStruct, UserModel, UserObj
字段驼峰createdAt, userIDcreated_at, UserID_field
序列化 tag 用反引号json:"created_at"双引号/漏 tag
零值可用则提供零值构造直接 User{}强制必须 NewUser
Go 没有构造函数的语法糖,NewXxx 只是社区约定。若该类型零值即可用(如 bytes.Buffer),就别强行要求 New,让用户直接 var b bytes.Buffer

⑨ 接口(interface)命名规范

单方法接口以 -er 结尾;小接口组合大接口;不写 I 前缀、不写 Impl 后缀。

// 单方法接口 → -er 后缀(来自方法名)
type Reader interface { Read(p []byte) (int, error) }
type Writer interface { Write(p []byte) (int, error) }
type Stringer interface { String() string }
type Closer interface { Close() error }

// 小接口组合出大能力(io.ReadWriter)
type ReadWriter interface {
    Reader
    Writer
}
✅ 推荐
Reader, Formatter, Notifier
小接口(1–3 方法),「使用时再定义」
❌ 禁止
IReader(别加 I 前缀)
ReaderImpl(别加 Impl 后缀)
巨型接口(几十方法,难 mock)
Go 的接口是隐式实现:不需要 implements 声明。所以「消费方定义小接口」是最佳实践——在调它的包里写 type Storage interface{ Save(...) error },而不是在实现方塞一个大接口。

⑩ 方法接收者命名与值/指针规范

接收者名用类型缩写(1–2 字母);不用 this/self;同类型所有方法接收者名保持一致;值/指针选择要一致。

type Client struct { timeout time.Duration }

// 接收者名 = 类型缩写 c,不是 this/self
func (c *Client) Do(req Request) (Response, error) {
    // 用 c.timeout ...
}
func (c *Client) Close() error { // 同一类型统一叫 c
    return nil
}
选择用指针接收者 *T用值接收者 T
判断依据方法需修改接收者 / 结构体较大 / 含 sync.Mutex 等不可拷贝字段小型不可变值(如 type Point struct{X,Y float64}
关键同一类型的所有方法必须统一用指针或统一用值,不要混用
sync.Mutex 等字段的结构体绝不能用值接收者——值接收会拷贝锁,直接出错。含锁或需修改状态时一律指针接收者。

⑪ 错误(error)命名规范

哨兵错误用 ErrXxx 导出;变量名用 err;用 %w 包装保留链。

// 哨兵错误:导出用 Err 前缀,首字母大写
var ErrNotFound = errors.New("not found")
var ErrClosed = errors.New("already closed")

// 包装错误用 %w 保留链路(Go 1.13+)
if err != nil {
    return fmt.Errorf("query user %d: %w", id, err)
}
// 判断用 errors.Is / errors.As
if errors.Is(err, ErrNotFound) { // 处理未找到 }
规则正确错误
哨兵错误名ErrNotFound, ErrTimeoutNotFoundErr, notFound
局部错误变量erre, error, err1
错误包装%w(可 errors.Is)%v(断链)

⑫ 注释与文档注释规范

导出标识符必须有文档注释,且以标识符名开头;godoc 据此生成文档。

// Package user 提供了用户领域模型的存储与查询能力。
package user

// User 表示一个已注册的用户。
type User struct { /* ... */ }

// GetUser 按 ID 查询用户;不存在时返回 ErrNotFound。
func GetUser(ctx context.Context, id int64) (*User, error) {
    // ...
}
规则说明
导出符号必写注释类型/函数/方法/包,注释以名字开头(// User 表示...
完整句子首字母大写、结尾句号,方便 godoc 拼接
包注释位置放 doc.go 或含 package xxx 的文件顶部,// Package xxx ...
别解释「显然」代码注释讲 why,不讲 what;好的命名可自解释

⑬ 其他风格规范(gofmt 强制 + 社区约定)

缩进用 tab
gofmt 默认;别用空格。编辑器设「tab 宽度 4」即可,存盘自动格式化。
import 分组
标准库 / 第三方 / 本项目,三组间空一行:std / external / internal
不用分号
Go 自动插分号。除非一行多语句,否则别手写 ;
行长度
gofmt 不强制,社区约 80–100 列;过长就拆行。
避免 init()
包级 func init() 难以控制顺序与测试,能免则免;必要时集中在一处。
空白导入用 _
import _ "net/http/pprof" 仅为副作用;一般放 main 包。
落地建议:把 gofmt(或 goimports)+ go vet + golangci-lint 接进 Git pre-commit / CI,命名与格式问题在提交前自动修掉,评审只聊设计。

⑭ 命名规范速查表(背这张就够了)

对象风格✅ 正确❌ 错误
文件名小写_下划线user_service.gouserService.go
目录名小写单词user/userSvc/、User/
包名小写·无下划线·与目录同名package userpackage usersvc
导出判定首字母大写User, GetUseruser, getUser
变量驼峰userID, orderCountuser_id, UserID
缩写词全大写userID, httpURLuserId, httpUrl
常量驼峰/iotaMaxRetries, StatusDoneMAX_RETRIES
函数驼峰NewUser, Savenew_user, Create_User
getter无 Getu.Name()u.GetName()
structUpperCamel 无后缀User, OrderUserStruct
struct 字段驼峰createdAtcreated_at
接口(单方法)-er 结尾Reader, CloserIReader, Readable
接收者名类型缩写(c *Client)(this *Client)
哨兵错误ErrXxxErrNotFoundNotFoundErr
布尔变量谓词isValid, hasKeyvalid, flag

延伸阅读(本仓库)

· go-zero 基础 —— 框架整体认知
· go-zero 项目规约 —— 本仓库的目录/命名落地约定
· go-zero 类型 —— struct/tag 与代码生成
· go-zero 服务启动 —— main 到端口监听