コンテンツにスキップ

AI Session Notes - 2026-03-29

Go のエラー処理パターン

学んだこと

  • Go には例外(try/catch)がなく、エラーを戻り値として返すスタイル
  • 関数は (結果, error) の複数戻り値を返し、呼び出し側で if err != nil をチェックする
  • 成功時は error に nil を返し、失敗時はエラー情報を返す
  • if err != nil の繰り返しが冗長だという批判もあり、Go 2 での改善が長年議論されている
// 関数は (結果, error) の2つを返す
func readFile(path string) (string, error) {
    file, err := os.Open(path)
    if err != nil {
        return "", err  // 失敗を返す
    }
    defer file.Close()

    data, err := io.ReadAll(file)
    if err != nil {
        return "", err
    }

    return string(data), nil  // 成功時は nil
}

// 呼び出し側
content, err := readFile("/path/to/file.txt")
if err != nil {
    return err
}

Go の基本構文

:=(短縮変数宣言)

  • := は変数の宣言と代入を同時に行う演算子。型推論により型を書かなくてよい
  • = は既存の変数への代入のみ。:= は新しい変数を作るときだけ使える
  • Go コミュニティでは := を積極的に使うのがイディオム。var x int = 42 は冗長とみなされる
  • var を明示的に書くのはゼロ値で初期化したいとき(var count int)など
x := 42           // 型推論で int(推奨)
var x int = 42    // 冗長(非推奨)
var count int     // ゼロ値初期化(var を使うべき場面)

複数戻り値

  • Go の関数は複数の値を返せる。これがエラー処理パターンの基盤になっている
  • func 関数名(引数名 型) (戻り値型1, 戻り値型2) の構文

関数は第一級オブジェクト

  • 関数を変数に入れたり、引数として渡すことができる
  • HTTP ハンドラの登録(http.HandleFunc("/path", handler))はコールバックパターン

Go のパッケージシステム

学んだこと

  • すべてのファイルは package 宣言が必須
  • package main + func main() がプログラムの起動地点。特別扱いされ、import されることがない
  • それ以外のパッケージはディレクトリ名と一致させるのが慣例
  • import パスは「モジュール名 + ディレクトリパス」のフルパスで指定する。相対パス("./fizzbuzz")は使えない
  • モジュール名は go.mod で定義する
myapp/
├── go.mod          ← module myapp
├── main.go         ← package main
└── fizzbuzz/
    └── fizzbuzz.go ← package fizzbuzz
import "myapp/fizzbuzz"

fizzbuzz.Run(100)  // パッケージ名.関数名 で呼ぶ

public / private は命名規則で決まる

  • 大文字始まり → public(他パッケージから呼べる)
  • 小文字始まり → private(パッケージ内のみ)
  • 関数だけでなく、変数・構造体にも同じルールが適用される

Go のリントツール・フォーマッター

学んだこと

  • go vet: Go 標準搭載の静的解析ツール。バグになりそうなコードを検出
  • gofmt: Go 標準のフォーマッター。コードスタイルを強制的に統一
  • golangci-lint: コミュニティのデファクトスタンダード。go vet を含む数十種類のリンターをまとめて実行できるメタリンター(別途インストールが必要)
  • かつて公式チーム製の golint があったが 2021 年に非推奨。後継的な立ち位置は golangci-lint に含まれる revive
go vet ./...           # 標準(インストール不要)
gofmt -d .             # 標準(インストール不要)
golangci-lint run      # 別途インストールが必要

Go のバージョン管理

学んだこと

  • Ruby の rbenv に相当するツールとして goenvasdf がある
  • ただし Go は後方互換性が非常に強く、バージョン間で壊れることが少ないため、バージョン管理ツールなしで運用している人も多い
  • go.modgo 1.22 はランタイムのバージョンを切り替える仕組みではなく、互換性の範囲を宣言するもの。Go 1.25 がインストールされていても、1.22 以降に追加された言語機能を使うとエラーにしてくれる
  • 公式サイトのインストーラで入れた場合は /usr/local/go/ に配置される。Homebrew で入れた場合とは管理方法が異なる

Go と Docker

学んだこと

  • Go はマルチステージビルドでバイナリだけの軽量イメージを作れる
  • ステージ 1: Go 入りのイメージでビルド
  • ステージ 2: scratch(空のイメージ)にバイナリだけコピー
  • 最終イメージは 10〜20MB 程度(Node.js 等は数百 MB〜1GB)
FROM golang:1.25 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

FROM scratch
COPY --from=builder /app/myapp /myapp
CMD ["/myapp"]
  • バイナリ 1 つで動くなら Docker の恩恵は薄い。Docker を使う理由は Go 自体の都合ではなく、周辺インフラ・運用の都合(docker compose での複数サービス管理、Kubernetes 前提の運用、CI/CD パイプラインの統一フロー等)
  • CLI ツールやシンプルな API サーバーならバイナリを直接置いて動かすのもあり

WebSocket チャットの実装アーキテクチャ(Go)

学んだこと

  • Go の HTTP サーバーは http.ListenAndServe が内部で無限ループし、接続を待ち続ける。main 関数の最終行に置くのが一般的
  • ルーティングはコールバックパターン。http.HandleFunc("/ws", handleWebSocket) で「このパスに来たらこの関数を呼んでね」と登録するだけで、実際の呼び出しはフレームワーク側が行う
  • WebSocket 接続ごとに go readMessages(conn) / go writeMessages(conn) で goroutine を起動し、読み書きを並列で動かす
  • go キーワードをつけないと無限ループでブロックし、次の行に進めない
func main() {
    http.HandleFunc("/ws", handleWebSocket)
    http.ListenAndServe(":8080", nil)  // ここでブロック(接続待ち)
}

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
    conn := // WebSocket 接続を確立
    go readMessages(conn)   // goroutine で読み込み
    go writeMessages(conn)  // goroutine で書き込み
}

接続が増えたときの挙動

  • ユーザーが接続するたびに handleWebSocket が呼ばれ、goroutine が 2 個ずつ増える
  • 3 人接続すれば goroutine 6 個が並列で動く
  • 3/28 で学んだ通り、I/O 待ちの goroutine は M から外れて RAM 上で寝ているだけなので、大量接続でもメモリ消費が少ない

チャット実装に必要な追加知識

  • HTTP サーバー(net/http
  • goroutine / channel の実際の書き方
  • struct(構造体)— ユーザーやメッセージの定義
  • WebSocket ライブラリ(gorilla/websocket 等)
  • フロント側は HTML + JavaScript の WebSocket API で最低限動く

メタ情報

  • ツール: Claude Code
  • 関連技術: Go, エラーハンドリング, パッケージシステム, 型推論, golangci-lint, go vet, Docker, マルチステージビルド, WebSocket, net/http, goroutine