この記事ではUIデザインに特化して解説します。デザインシステム・UI生成全般は デザインシステムとは?仕組み・構成要素・有名事例をエンジニア向けに完全解説 をご覧ください。
Go命名規則ガイドとは
Alex Edwards氏が運営するブログ alexedwards.net に、Go言語の命名規則を体系的に解説した実践ガイド「Go Naming Conventions: A Practical Guide」が公開された。Hacker Newsで多数のコメントを集め、Goコミュニティで広く参照されている。
このガイドが注目される理由は、公式ドキュメントが「こうすべき」を列挙するのに対し、なぜその命名になるのか という設計意図まで解説している点にある。Python/Java/Rustから移行した開発者がつまずきやすいポイントを的確に押さえている。
パッケージ名 / 変数・定数 / 関数・メソッド / 構造体フィールド / インターフェース / レシーバ / 頭字語・略語
Goの命名の根本原則:可視性制御との一体化
他の言語と最も異なる点を先に理解しておく必要がある。Goでは 識別子の先頭文字の大文字/小文字がそのままパッケージ外への公開・非公開を決定する。これはコンパイラが強制するルールであり、違反するとビルドが通らない。
package mypackage
// Exported(公開): パッケージ外からアクセス可能
type User struct {
Name string // 公開フィールド
email string // 非公開フィールド
}
// Exported function
func NewUser(name, email string) *User {
return &User{Name: name, email: email}
}
// unexported function(パッケージ内からのみ)
func validateEmail(email string) bool {
return len(email) > 0
}
この設計により、命名規則とアクセス制御が一体化している。public / private キーワードが不要で、コードが簡潔になる反面、命名の意識が直接APIデザインに影響する。
パッケージ名:小文字のみ、連結する
Goのパッケージ名は 小文字のみ で、複数単語をつなぐ場合は単純に連結する。アンダースコアもキャメルケースも使わない。
// 良い例
package httputil
package stringconv
package nethttp
// 悪い例
package http_util // アンダースコアはNG
package httpUtil // キャメルケースはNG
package HttpUtil // 大文字始まりはNG
パッケージ名はインポート側で使われるため、短く明確であることが重要だ。標準ライブラリが fmt、io、net/http のように短い名前を使っているのはこの設計思想による。
// 使う側から見たときのわかりやすさを重視
import (
"net/http" // http.Get()、http.Post()と書ける
"encoding/json" // json.Marshal()と書ける
)
変数・関数・型:PascalCaseとcamelCase
公開要素は PascalCase、非公開要素は camelCase を使う。これはコンパイラルールの直接の帰結だ。
// 公開型と非公開型
type DatabaseConfig struct { ... } // 公開
type internalState struct { ... } // 非公開
// 公開関数と非公開関数
func ProcessRequest(r *http.Request) {} // 公開
func parseHeaders(r *http.Request) {} // 非公開
// 変数
var MaxRetries = 3 // 公開変数
var defaultTimeout = 30 // 非公開変数
変数名は スコープが広いほど長く、狭いほど短く という慣習がある。
// ループカウンタ: i, j, k で十分
for i := 0; i < 10; i++ { ... }
// 関数スコープ: 意味が伝わる短い名前
func process(r *http.Request) {
b, err := io.ReadAll(r.Body)
...
}
// パッケージスコープ: より説明的に
var requestTimeout = 30 * time.Second
頭字語・略語:全文字を大文字または小文字に統一
頭字語(HTTP、XML、URL、IDなど)はすべての文字を大文字か小文字にそろえる。混在表記(HttpClient、XmlParser)は禁止だ。
// 正しい例
type HTTPClient struct { ... } // 公開: HTTP全大文字
type XMLParser struct { ... } // 公開: XML全大文字
type urlValidator struct { ... } // 非公開: url全小文字
var userID int // 非公開: ID全大文字NG → id全小文字
var UserID int // 公開: UserIDが正しい(IDを全大文字に)
// 間違った例
type HttpClient struct { ... } // NG: 混在表記
type XmlParser struct { ... } // NG: 混在表記
var userId int // NG: 大文字D、小文字i の混在
これは Effective Go から引き継がれたルールで、読者が略語境界を瞬時に認識できる ようにするための約束事だ。
// 複合的な例
func ServeHTTP(w http.ResponseWriter, r *http.Request) {}
// HTTP: 全大文字(Goの標準インターフェース名)
type APIResponse struct {
StatusCode int
RequestID string // ID は全大文字
}
定数:ALL_CAPSは使わない
C言語やPython由来の ALL_CAPS 定数表記は Go では使わない。通常の識別子ルール(公開は大文字始まり、非公開は小文字始まり)に従う。
// Goの正しい定数定義
const (
MaxConnections = 100 // 公開定数: PascalCase
defaultTimeout = 30 // 非公開定数: camelCase
StatusOK = 200 // 公開定数
)
// 間違った書き方(他言語の慣習をそのまま持ち込んだ例)
const (
MAX_CONNECTIONS = 100 // NG: Goでは使わない
DEFAULT_TIMEOUT = 30 // NG
)
理由はシンプルだ。GoではALL_CAPSにしても可視性は変わらない。大文字で始まれば公開、小文字で始まれば非公開、それだけだ。
レシーバ名:1〜2文字の略語、selfは禁止
メソッドのレシーバ名は 型名の1〜2文字の略語 を使う。self や this は Goのコミュニティで明確に禁止されている。
type Server struct {
addr string
port int
}
// 正しい: 型名の略語(Server → s)
func (s *Server) Start() error {
return listenOn(s.addr, s.port)
}
func (s *Server) Stop() {
// ...
}
// 間違い: self や this は使わない
func (self *Server) Start() error { ... } // NG
func (this *Server) Start() error { ... } // NG
同じ型のメソッドでは 一貫したレシーバ名を使う こと。同じ型に s と srv が混在するのはNG。
type DatabaseClient struct { ... }
// 良い例: 全メソッドで db を統一
func (db *DatabaseClient) Query(sql string) {}
func (db *DatabaseClient) Close() error { return nil }
func (db *DatabaseClient) Ping() bool { return true }
命名の意思決定フロー
使う?"} B -->|"Yes"| C["大文字始まり
(PascalCase)"] B -->|"No"| D["小文字始まり
(camelCase)"] C --> E{"頭字語を
含む?"} D --> E E -->|"Yes"| F["頭字語は
全大文字 or 全小文字に統一
例: HTTP, url"] E -->|"No"| G{"パッケージ名?"} F --> H["完成"] G -->|"Yes"| I["小文字のみ
単語を連結
例: httputil"] G -->|"No"| J{"レシーバ?"} I --> H J -->|"Yes"| K["型名の1〜2文字略語
self/this は使わない"] J -->|"No"| L{"定数?"} K --> H L -->|"Yes"| M["通常の識別子ルール
ALL_CAPS は使わない"] L -->|"No"| H M --> H
他言語との比較:なぜGoの命名は独自なのか
Python/JavaからGoに移行した開発者がつまずく主なポイントを比較する。
| 概念 | Python | Java | Rust | Go |
|---|---|---|---|---|
| 公開・非公開の区別 | _prefix 慣習 |
public/private キーワード |
pub キーワード |
先頭文字の大文字/小文字 |
| 定数スタイル | ALL_CAPS |
ALL_CAPS |
ALL_CAPS |
通常の識別子ルール |
| パッケージ名 | snake_case |
lowercase |
snake_case |
小文字のみ連結 |
| クラス/型 | PascalCase |
PascalCase |
PascalCase |
PascalCase |
| レシーバ(self/this) | self(慣習) |
this(慣習) |
self(キーワード) |
型略語(self禁止) |
| 頭字語 | MyHttp |
MyHttp |
MyHttp |
MyHTTP(全大文字統一) |
特にPythonからの移行者が混乱するのは定数のALL_CAPS禁止と、Goのレシーバが self ではなく型略語であることだ。Javaからの移行者は HttpClient を HTTPClient に変えることを忘れがちだ。
golangci-lintによる命名規則の自動チェック
チーム開発では命名規則を手動でレビューするのは非効率だ。golangci-lint を使えば多くのルールを自動検出できる。
# .golangci.yml
linters:
enable:
- revive # 一般的なGoコードスタイル
- stylecheck # st1003: 頭字語の大文字統一チェック
- govet # 公式の静的解析
linters-settings:
revive:
rules:
- name: var-naming # 変数命名チェック
- name: exported # エクスポート識別子のコメントチェック
- name: receiver-naming # レシーバ名チェック(self/thisを検出)
- name: package-comments # パッケージコメントチェック
stylecheck:
checks:
- "ST1003" # 頭字語: HTTPClient等のチェック
- "ST1020" # エクスポートされた型のコメント
# ローカルで実行
golangci-lint run ./...
# CIへの組み込み(GitHub Actions)
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
□ パッケージ名: 小文字のみ、アンダースコアなし
□ 公開/非公開: 大文字/小文字始まりで判断、public/private キーワードはない
□ 頭字語: HTTP、XML、ID などは全大文字(公開)or全小文字(非公開)
□ 定数: ALL_CAPS は使わない
□ レシーバ: 型名の1〜2文字略語(self/this 禁止)
□ インターフェース: 1メソッドなら -er サフィックス(Reader, Writer)
□ golangci-lint の設定ファイルを共有リポジトリに含める
エラー変数の命名:err は特別扱い
Goのイディオムとして、エラー変数には特有の命名パターンがある。
// 関数スコープのエラーは単純に err
result, err := doSomething()
if err != nil {
return err
}
// エラー型の変数(パッケージレベル)は Err プレフィックス
var ErrNotFound = errors.New("not found")
var ErrTimeout = errors.New("timeout")
// カスタムエラー型は Error サフィックス
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
OpenHandsのようなAIコーディングエージェントがGoコードを生成する際も、このエラー命名パターンに従うことでコードレビューの摩擦が減る。
インターフェース命名:-er パターン
1つのメソッドを持つインターフェースには -er サフィックスをつける慣習がある。これはGoの標準ライブラリ全体で採用されているパターンだ。
// 標準ライブラリの例
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Stringer interface {
String() string
}
// 自分で定義する場合も同様
type Fetcher interface {
Fetch(url string) ([]byte, error)
}
type Validator interface {
Validate() error
}
複数メソッドを持つインターフェースは役割を表す名前にする(Storage、Cache、Repository など)。