8.2 KiB
Go コーディング規約
1. 一般的なガイドライン
- コードフォーマットは 必ず
gofmt(またはgoimports)に従う。手で整形しない。 - インデントは タブ(
\t)を使用する(gofmtが自動で設定する)。 - 1行の長さは厳密な制限はないが、できれば 100 文字前後を目安にする。
- 1ファイルは 300〜400 行以内を目安とし、長くなりすぎる場合はファイル分割を検討する。
- パッケージ単位で責務を分割し、小さく・単機能な関数を心がける。
- エクスポート(大文字始まり)するものは最小限にし、原則として パッケージ内で完結する API を設計する。
2. コメント/ドキュメント
Go では godoc 形式のコメントが重要です。
-
パッケージコメント:
package宣言の直前に、そのパッケージの説明を書く。// Package user provides user management functionalities such as // registration, authentication, and profile updates. package user -
エクスポートされた関数/メソッドには、シグネチャ名から始まるコメントを書く。
// CreateUser creates a new user and stores it into the repository. func CreateUser(name string, age int) (*User, error) { // 実装 } -
エクスポートされた型やフィールドにもコメントを書く。
// User represents a user entity in the system. type User struct { // ID is a unique identifier of the user. ID int64 // Name is the display name of the user. Name string } -
非エクスポート(小文字始まり)のものでも、複雑な処理や分岐には行コメントで意図や前提条件を書いておく。
// cache にヒットした場合は DB に問い合わせない if v, ok := cache[id]; ok { return v, nil }
3. 命名規則
Go は CamelCase / mixedCaps を使います(snake_case をあまり使わない)。
-
パッケージ名:
- すべて小文字、
_や数字を極力避ける。 - 短く意味のある名前にする(
usersよりuserのように単数形が好まれがち)。
package user // 良い例 - すべて小文字、
-
変数・関数名(非エクスポート): 先頭小文字の
mixedCaps。func loadUser() (*User, error) { userCount := 0 _ = userCount // ... return nil, nil } -
エクスポート関数・構造体: 先頭大文字の
MixedCaps。type UserService struct { repo Repository } func NewUserService(repo Repository) *UserService { return &UserService{repo: repo} } -
定数:
- エクスポートする定数は
CamelCase。 - パッケージ内専用であれば先頭小文字。
const DefaultTimeout = 5 * time.Second const maxRetryCount = 3 - エクスポートする定数は
-
レシーバ名:
- 型名の1〜2文字の略を使う。
UserService→us、Server→sなど。
func (s *Server) Start() error { // ... return nil }
4. インポート/パッケージ構成
-
インポートは 自動整形ツール(goimports)にまかせる。
-
グループ順は以下が一般的:
- 標準ライブラリ
- サードパーティ
- 自プロジェクト内パッケージ
グループ間は空行で区切る。
import ( "context" "fmt" "net/http" "github.com/julienschmidt/httprouter" "example.com/project/internal/user" "example.com/project/pkg/logger" ) -
循環参照が起きないようにパッケージを分割する。
cmd/… エントリポイント(mainパッケージ)internal/… アプリ内部でのみ使うパッケージpkg/… 外部にも公開可能なパッケージ
5. エラーハンドリング
-
Go のエラーは 戻り値の
errorで扱う。user, err := repo.FindByID(ctx, id) if err != nil { return nil, fmt.Errorf("failed to find user: %w", err) } -
エラーはできるだけ 早めに return し、ネストを浅く保つ。
if err != nil { return nil, err } // ここから成功時の処理 -
panicは プログラミングミスなど「復旧不能」なケースのみで使用し、通常のエラー処理には使わない。 -
errors.Is/errors.Asを使ってエラー種別を判定する。if errors.Is(err, sql.ErrNoRows) { // not found の扱い }
6. 複数ファイル間の分割時の書き方
6.1 同じパッケージ内での分割
Go では 同じディレクトリ配下の .go ファイルは同じ package 名であれば、1つのパッケージとして扱われます。
user/
user.go
service.go
repository.go
すべてのファイルで package user と書きます。
// user/user.go
package user
type User struct {
ID int64
Name string
}
// user/service.go
package user
// Service handles user-related usecases.
type Service struct {
repo Repository
}
func NewService(repo Repository) *Service {
return &Service{repo: repo}
}
// user/repository.go
package user
type Repository interface {
FindByID(id int64) (*User, error)
Save(user *User) error
}
同一パッケージ内であれば、インポートなしですべてのシンボルを参照できます。
6.2 main パッケージと内部パッケージの分割
cmd/app/main.go
internal/user/service.go
internal/user/repository.go
// cmd/app/main.go
package main
import (
"log"
"example.com/project/internal/user"
)
func main() {
repo := user.NewInMemoryRepository()
svc := user.NewService(repo)
if err := svc.Run(); err != nil {
log.Fatal(err)
}
}
// internal/user/service.go
package user
type Service struct {
repo Repository
}
func NewService(repo Repository) *Service {
return &Service{repo: repo}
}
func (s *Service) Run() error {
// 実装
return nil
}
// internal/user/repository.go
package user
type Repository interface {
// ...
}
type inMemoryRepository struct {
// ...
}
func NewInMemoryRepository() Repository {
return &inMemoryRepository{}
}
ポイント:
- ディレクトリとパッケージ名を一致させると分かりやすい。
cmd/app/のmain.goはできるだけ薄くし、構造体やビジネスロジックはinternal/やpkg/に置く。
6.3 テストファイルの分割
テストコードは基本的に同じパッケージか package xxx_test で書きます。
// user/service_test.go
package user_test
import (
"testing"
"example.com/project/internal/user"
)
func TestService_Run(t *testing.T) {
repo := user.NewInMemoryRepository()
svc := user.NewService(repo)
if err := svc.Run(); err != nil {
t.Fatalf("Run() error = %v", err)
}
}
7. gofmt / Lint / VSCode の設定
7.1 フォーマット・リンター
-
必須ツール
gofmt(標準)goimports(インポートも自動整形)
-
推奨リンター
golangci-lint(多数のリンターを統合したツール)
プロジェクトルートに .golangci.yml を置く例:
run:
timeout: 3m
tests: true
linters:
enable:
- govet
- gofmt
- gosimple
- staticcheck
- unused
- errcheck
issues:
exclude-use-default: false
7.2 VSCode の設定例 (.vscode/settings.json)
{
"go.useLanguageServer": true,
"go.formatTool": "goimports",
"gopls": {
"staticcheck": true
},
"editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
"go.lintTool": "golangci-lint",
"go.lintOnSave": "file",
"go.lintFlags": [
"run",
"--fast"
],
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true
}