ブログに戻る

Go のプロジェクト構成(2026 年版):Go らしいディレクトリ構成ガイド

初心者の多くが、スター数 5 万超のリポジトリからフォルダ構成を真似します。しかしそれは Go の公式標準ではなく、Go チーム自身もそう明言しています。Go プロジェクトを構成する本当の公式アプローチを紹介します。思っているよりずっとシンプルです。

Go のプロジェクト構成(2026 年版):Go らしいディレクトリ構成ガイド

「Go プロジェクト構成」で検索すると、2 クリックもしないうちに golang-standards/project-layout にたどり着きます。5 万以上のスターを集めた GitHub リポジトリで、ディレクトリツリーには cmd/、pkg/、internal/、api/、build/ がずらりと並んでいます。いかにも権威がありそうに見えるため、多くの初心者は、ファイルが 5 つしかないプログラムにこの構成を丸ごと取り入れてしまいます。

しかし、これは公式の標準ではなく、Go チームも公の場でそう述べています。公式のガイダンスはもっとシンプルです。最初はフラットに始め、コードが必要としたときだけフォルダを追加します。以下では、天気 CLI と書店 API を例に、そのガイダンスをフォルダ 1 つずつ見ていきます。

Go の標準的なプロジェクト構成とは?

必ず従うべき唯一のディレクトリツリーはありません。Go は src/ も cmd/ も必須にしておらず、モジュールのルートにある go.mod ファイル以外には、どんなフォルダも必要ありません。公式ガイド「Organizing a Go module」は、構成をプロジェクトの規模と結びつけています。そこでは 4 つの構成が、前のものから 1 段階ずつ大きくなる順に示されています。基本的なパッケージ、基本的なコマンド、補助的なパッケージを抱えるまで成長したパッケージ、そして複数のバイナリを持つ「サーバー」プロジェクトです(出典)。

つまり役に立つ問いは、自分のプロジェクトに今どれだけの構成が必要か、であり、ほとんどのプログラムではその答えは「ごくわずか」です。この記事の残りでは、4 つの構成を最もシンプルなものから順に見ていきます。自分のプロジェクトに合う構成を見つけたら、そこで止めてください。

フラットに始める:パッケージ 1 つが Go らしい構成

公式ドキュメントの最初の 2 つの構成は、単一のパッケージと単一のコマンドで、どちらもサブディレクトリを持たずにモジュールのルートに直接置かれます。都市名を読み取って天気を表示する小さなコマンドラインツールなら、プロジェクトはこれですべてです。

example.texttext
weather/
├── go.mod
├── main.go
├── weather.go
└── weather_test.go

go.mod はモジュールパス(たとえば module github.com/you/weather)を宣言します。main.go には package main とエントリーポイントを置きます。weather.go にはロジックを、同じパッケージ内に置きます。weather_test.go には、対象のコードのすぐ隣に置いたテストを書きます。そこが、Go がテストの置き場所として想定している位置です。このままの形でリリースできます。公式ドキュメントはこれを「基本的なコマンド」と呼んでおり、最初の 1 年に書くほとんどのツールにとって適切な形です。

この後に出てくる構成上の判断のほとんどは、1 つのルールで説明できます。ディレクトリはパッケージである、というルールです。ディレクトリ内のすべてのファイルは同じパッケージを宣言し、パッケージ名はインポートパスの最後の要素になります。weather/store/ ディレクトリを追加すると、その中のファイルはすべて package store と宣言します。インポートするときは github.com/you/weather/store と書き、呼び出すときは store.Get と書きます。

まだフォルダは追加しないでください。関数が 1 つだけの utils/ ディレクトリや、struct が 1 つだけの models/ ディレクトリは、インポートパスとパッケージ境界を増やすだけで、見返りは何もありません。フラットであることが実際に問題を起こし始めるまでは、フラットなままにしておきましょう。

internal/ ディレクトリはいつ追加すべきか?

構成を追加する最初の正当な理由は、コードを非公開にすることです。Go の識別子の可視性は 2 段階です。大文字で始まる名前はエクスポートされ、小文字で始まる名前はそのパッケージ内でしか使えません。1 つのパッケージの中ならこれで十分です。しかし、モジュールが複数のパッケージを持つようになると、モジュールの内側のパッケージならどこからでも使えて、外側からはインポートできないコードが欲しくなることがよくあります。それを実現するのが internal/ で、コンパイラがこの制限を強制します。

これは命名上の慣習ではなく、言語の一部です。internal/ という名前のディレクトリ配下にあるパッケージは、その internal/ ディレクトリの親をルートとするコードからしかインポートできず、別のモジュールからインポートするとビルドが失敗します。公式ドキュメントは、パッケージを「できる限り」internal に置くことを推奨しています。外部のコードはそれらをインポートできないので、それらに依存するようになることもありません。公開するつもりのなかった API をサポートし続ける羽目になることもありません(出典)。

先ほどの天気 CLI が、書店向けの小さな HTTP API になったとしましょう。リクエストの認証とデータアクセス層が加わりましたが、どちらも公開 API に含めるべきものではありません。

example.texttext
bookstore/
├── go.mod
├── main.go
└── internal/
    ├── auth/
    │   ├── auth.go
    │   └── auth_test.go
    └── store/
        ├── store.go
        └── store_test.go

main.go は bookstore モジュールの中にあるので、github.com/you/bookstore/internal/auth と github.com/you/bookstore/internal/store を自由にインポートできます。他のモジュールのコードはインポートできません。誰かがあなたのモジュールを go get して、これらのパッケージをインポートしようとしても、コンパイラがそのインポートを拒否します。モジュールの外にいる誰も auth や store に依存できないので、関数名の変更、シグネチャの変更、構成の組み直しをいつでも好きなときに行えます。それで他の誰かのコードが壊れることはありません。

cmd/ ディレクトリはいつ追加すべきか?

cmd/ は、複数のバイナリをビルドするリポジトリや、バイナリとインポート可能なライブラリの両方を提供するリポジトリ向けです。バイナリが 1 つだけなら cmd/ は必要なく、ルートに main.go を置くほうがすっきりします。

cmd/ の下では、バイナリごとに専用のサブディレクトリを用意し、そのディレクトリ名がプログラムの名前になります。公式の「サーバープロジェクト」構成もこの形です。各コマンドは cmd/<name>/ に置かれ、internal/ のパッケージを組み合わせる小さな main.go を持ちます(出典)。書店アプリが、API サーバーとデータベースのマイグレーションツールの両方を提供するようになったとしましょう。

example.texttext
bookstore/
├── go.mod
├── cmd/
│   ├── api/
│   │   └── main.go
│   └── migrate/
│       └── main.go
└── internal/
    ├── auth/
    └── store/

go build ./cmd/api を実行すると api という名前のバイナリが、go build ./cmd/migrate を実行すると migrate が生成されます。どちらの main.go も薄いままで、実際の処理は internal/ からインポートします。

この段階的な流れ全体を 1 つの表にまとめました。将来こうなるだろうと思い描いているプロジェクトではなく、今のプロジェクトに合う行を選んでください。

プロジェクトの形推奨される構成理由
小さなツールやスクリプトフラット:ルートに main.go とロジックのファイルを置くパッケージ 1 つで Go らしく、完結している
再利用する内部コードを持つバイナリ 1 つ非公開パッケージ用に internal/ を追加コンパイラが保証する非公開性、時期尚早な API を作らない
2 つ以上のバイナリバイナリごとに cmd/<name>/ を追加各 main が専用のディレクトリを持つ
複数サービスのリポジトリ(バイナリと共有コード)cmd/ と internal/、ドメインごとに分割公式の「サーバープロジェクト」構成

golang-standards/project-layout にまつわる誤解

golang-standards/project-layout リポジトリは、GitHub の Organization 名に「standards」を含み、5 万以上のスターを集め、api/、build/、configs/、deployments/、pkg/、test/ からなる大きなツリーを持っています。Go チームのお墨付きがあると思い込みやすいのですが、実際にはありません。現在はリポジトリの README に、公式の標準ではないという注意書きが載っています。そして、この構成に異を唱えたのは Go 自身の指導者たちでした。

issue #117 で、Go のテックリードである Russ Cox 氏は、これは「標準的な Go プロジェクト構成ではない」と書きました。さらに、ほとんどの Go リポジトリは「はるかにシンプル」で、pkg/ ディレクトリをまったく使っていないとも述べています。このスレッドは Hacker News のトップページに載り、経験豊富な Go 開発者の多くが Cox 氏に同意しました。この構成は、複数のチームで共有する大規模なモノレポなら理にかなう場合もありますが、普通のプロジェクトを始めるときのテンプレートとしては害になります。小さなプログラムにこれを持ち込むと、5 つのファイルが、ほぼ空の 8 つのフォルダの下に埋もれてしまいます。

ここからいくつかのルールが導かれます。これらは、Eli Bendersky 氏や Alex Edwards 氏といった Go の実践者がよく勧めていることとも一致します。

  • src/ ディレクトリは使わないでください。これは Java や古い Node プロジェクトから来た習慣です。Go のコードはモジュールのルートに置き、公式の構成のどれにも src/ はありません。
  • 中に入れるコードがないうちからフォルダを入れ子にしないでください。空の api/、build/、deployments/ フォルダは何の役にも立ちません。フォルダは、中に入れるものができた日に作りましょう。
  • パッケージには役割ではなく、提供するものに合わせて名前を付けてください。utils、common、helpers といった名前のパッケージは、読み手に何も伝えず、何でも放り込むガラクタ置き場になります。store、auth、weather、retry、slugify のように名付けましょう。パッケージ名についての Go の公式のアドバイスは、短く明確にし、呼び出し側で読みやすくすることです。そのため utils.GetFromStore より store.Get のほうが優れています(出典)。

フォルダではなくコードに現れる間違いについては、Go でよくある間違いと回避方法のガイドをご覧ください。

初心者のための Go プロジェクト構成の経験則

ここまでの流れは、プロジェクトについてのいくつかの質問にまとめられます。上から順に答えていき、答えが求めるときだけディレクトリを追加してください。

flowchart TD
    A["New Go project"] --> B["Is it one package<br/>of related code?"]
    B -->|Yes| C["Keep it flat:<br/>main.go + logic in the root"]
    B -->|"Grew past that"| D["Need to hide code<br/>from outside importers?"]
    D -->|Yes| E["Add internal/<br/>put private packages there"]
    E --> F["More than one<br/>binary to build?"]
    F -->|No| G["Done. main.go in root,<br/>logic in internal/"]
    F -->|Yes| H["Add cmd/name/<br/>one dir per binary"]
    H --> I["Many services sharing code?"]
    I -->|Yes| J["Server layout:<br/>cmd/ + internal/ by domain"]

どの段階も、具体的な必要に応えるものです。パッケージ境界、2 つ目のバイナリ、非公開にしておきたいコードのいずれかです。そのどれも挙げられないなら、おそらくそのフォルダは必要ありません。フラットに始め、コンパイラに非公開性を強制させたくなったら internal/ を追加し、2 つ目のバイナリができたら cmd/ を追加します。本格的なサーバー構成に移るのは、リポジトリが実際に複数のサービスを抱えるようになったときだけです。

よくある質問

Go に公式のプロジェクト構成はありますか?

いいえ。必須とされる唯一のフォルダ構成はありません。公式ドキュメント「Organizing a Go module」は、1 つの固定された構成ではなく、プロジェクトの規模に合わせて成長する構成を説明しています。フラットなパッケージから internal/、次に cmd/、そして本格的なサーバー構成へと進みます。自分のプロジェクトに合う、最も小さな構成を使ってください(出典)。

pkg/ ディレクトリは使うべきですか?

通常は使いません。Go のテックリードである Russ Cox 氏は、ほとんどの Go リポジトリは pkg/ ディレクトリを使っておらず、「はるかにシンプルな傾向がある」と指摘しています。非公開のコードは、コンパイラが非公開性を強制してくれる internal/ に置きましょう。モジュールのルートでパッケージを公開するのは、公開 API としてサポートする予定があるときだけにします(出典)。

internal/ と cmd/ にはそれぞれ何を置きますか?

internal/ には、自分のモジュールだけが使えるインポート可能なライブラリコードを置き、その制限はコンパイラが強制します。cmd/<name>/ には、ビルドするバイナリごとに薄い main パッケージを置き、そこで internal/ のコードを組み合わせます。ロジックは internal/ に、エントリーポイントは cmd/ に置きます(出典)。

実際の Go プロジェクトを 1 つのファイルやパッケージだけで構成してもよいですか?

はい。公式の構成ガイドの最初の例は、モジュールのルートに置いた単一のパッケージです。本番環境で使われているツールにも、その形でリリースされているものはたくさんあります。go.mod、main.go、そしてテストを備えた 1 つのパッケージは、Go らしく完結した構成です。コードがその構成に収まらなくなったら、構成を追加してください(出典)。

Go のプロジェクトは Java や Node のプロジェクトとどう違いますか?

Go には src/ ディレクトリがありません。コードはモジュールのルートに置き、ディレクトリ 1 つが 1 つのパッケージになり、パッケージ名はパスの最後の要素になります。Maven やバンドラーのように、ビルドツールがツリーを押し付けることもありません。そのため Go の構成はフラットに保たれ、必要になったときだけ大きくなります(出典)。

次のステップ

プロジェクト構成は、Go の中では比較的やさしい部分です。プログラムはフラットなままリリースし、internal/ や cmd/ はコードが必要としたときに追加します。なぜそのフォルダがあるのか説明できないなら、おそらく削除してかまいません。

感覚をつかむ一番の方法は、CLI と HTTP サービスのような実際のプログラムをいくつか作り、進めながら構成を育てていくことです。LevelUpGo の Clean Go Code トラックでは、ブラウザ上で自分で書いて実行する演習を通して、Go らしいパッケージ設計とプロジェクト構成を学べます。Go Fundamentals では、最初の go.mod とツールチェーンから始められます。インタラクティブなロードマップの全体もご覧いただけます。

シニアエンジニアのように Go を書く

ブラウザで学べるインタラクティブなレッスン。最初のレッスンは無料です。

無料レッスンを試すまたは無料アカウントを作成