Go 1.27へのアップグレード: 壊れるもの、そしてまず採用すべきもの
内部では json/v2、標準ライブラリの uuid、および GODEBUG の罠を使用しています。
Go 1.27は2026年8月にリリースされました。ほとんどのモジュールでは、アップグレードは go.mod の1行の更新と go fix の実行で完了しますが、静かに動作に影響が出る変更もいくつかあります。ここでは、実際に壊れる箇所と、最初に導入すべき内容をまとめます。
主要な変更点は構造的なものです。encoding/json は内部実装として新しい json/v2 バックエンドになり、uuid パッケージが標準ライブラリに追加され、メソッドが独自の型パラメータを宣言できるようになりました。これらによって全面書き換えを強制されることはありません。Goチームは通常どおり互換性約束を維持しており、ほぼすべてが変更なしでコンパイル・実行できます。

ただし、静かな変更ばかりではありません。複数の GODEBUG 設定が従来の値でビルドに失敗するようになり、compress/flate が異なるバイト列を出力し、go test が新しい stdversion チェックを強制実行するようになりました。また、コンパイラが生成するシンボル名も変更されています。変更のなかった部分の構文リフレッシュが必要な場合、Go チートシート は引き続き有効です。本ガイドは残りの変更点について、各項目のコマンドと修正方法を解説します。
1枚の図で見るアップグレードパス
Goチームは、「まずビルドし、次に修正する」順序を推奨しています。go fix はコードを新しいAPIや慣用句に書き換えますが、ランタイムレベルの変更が自分たちに影響しているかどうかを判断できるのは、クリーンなビルドが成功した場合だけです。
まずツールチェーン自体から開始します。go コマンドでツールチェーンを管理している場合、go.mod でバージョンを固定するか、明示的にインストールします:
go install golang.org/dl/go1.27@latest
go1.27 download
go1.27 version
または、環境変数に GOTOOLCHAIN=go1.27.0 を設定するか、go.mod に toolchain go1.27.0 を追加して go コマンドに取得させます。モジュール中心の関連情報として、Go ワークスペース構造 は、go と toolchain ディレクティブが複数モジュール間でどのように相互作用するかを説明しています。単一モジュールの場合の簡略版は下記のとおりです。
ステップ1: go ディレクティブの引き上げと削除された GODEBUG 設定のクリーンアップ
go mod edit -go=1.27
go mod tidy
go build ./...
go ディレクティブを引き上げることで、新しい言語機能(ジェネリックメソッド、構造体リテラルにおけるプロモートされたフィールドキー、より広い関数型の推論)が有効になり、サポートが削除された GODEBUG 設定に対して go コマンドが厳格になります。Go 1.27では、以下が恒久的に削除されました:
asynctimerchan— タイマーとティックのチャネルは常にバッファなしになりますgotypesaliastlsunsafeekm,tlsrsakex,tls3des,tls10serverx509keypairleaf
新しいルールとして、削除される前の最終デフォルト値に設定されている場合、go コマンドは go.mod(godebug エントリ)やソースコード(//go:debug コメント)内のこれらの設定を受け入れます。行動変化を無効化するための古い値を設定すると、ビルドが失敗します:
//go:debug asynctimerchan=0 // ビルドエラー: 削除された設定の古い値
//go:debug asynctimerchan=1 // 受け入れられますが、代わりに该行を削除すべきです
ここでビルドが失敗する場合、コードはGo 1.23以前のバッファ付きタイマーチャネルに依存していることになります。修正は設定ではなくコード内にあります:同期チャネルに依存する context、キャンセル、およびタイムアウトパターン に照らし合わせて、Timer.Stop と Timer.Reset の使用箇所を確認してください。TLS関連の設定にも同様の検索が適用されます。tls3des=1 や tls10server=1 を固定しているものは古い暗号スイートを生存させており、Go 1.27ではコンパイルを拒否します。
ステップ2: go test の実行 — stdversion の罠
go build と go test の結果が一致しなくなりました。Go 1.27の go test はデフォルトで stdversion の vet チェックを実行します。これにより、参照しているファイルで有効な go バージョンに対して新しすぎる標準ライブラリのシンボルが報告されます。
// go.mod はまだ "go 1.26" と記述
func Base(p string) string {
_, file, ok := strings.CutLast(p, "/") // Go 1.27 で追加
if !ok {
return p
}
return file
}
1.27のツールチェーンの場合、go build は成功します(コンパイラには strings.CutLast があるため)。しかし、go.mod がまだ 1.26 を宣言しているため、go test は vet ステップで失敗します。実際の 1.26 ツールチェーンでは、このファイルはビルド自体に失敗します。教訓は手順論的なものです:アップグレードを検証するために go build だけを絶対に使わず、完全なテストスイートを実行してから go ディレクティブを引き上げることです。go test の vet スイートは、新しい printf の検出項目も拾い上げます:p が *E で E が error を実装している場合の fmt.Errorf("...: %w", p) は警告されます。ポインターをラップすると、型アサーションや errors.Is チェックが失敗するためです。この失敗モードは、Go エラー処理アーキテクチャ で説明されているエラー処理コードのまさにそのパターンで現れます。操作子の型をチェックしてください。フォーマット指定子(verb)だけを見るのは不十分です。
encoding/json: 下の層にある v2 エンジン
最も大きな内部変更:encoding/json/v2(Go 1.25 から GOEXPERIMENT=jsonv2 として実験的提供)が正式化し、従来の encoding/json パッケージは現在、v2実装でバックエンドされている状態です。v1の呼び出し元に対する動作は保存されています — nilスライスは依然として null にエンコードされ、マップキーは依然としてソートされます — が、1つの目に見える違いがあります:一部のエラーメッセージの正確なテキストが変更されたことです。エラー文字列に対してテストや監視を設定している場合、差分を予想してください。
3つの可能性がある問題点:
- エラーテキスト。 従来のv1の表現にキーされた
strings.Contains(err.Error(), ...)のアサーションは更新が必要です。 - パフォーマンス敏感パスが変化する場合がある。 アンマーシャリングは高速化されましたが、書き換えによりアロケーションが発生する操作が変わります。CIでフラグが立てられたベンチマークはそれを示します。
- 互換性エスケープハッチ。
GOEXPERIMENT=nojsonv2は、ビルド時に元のv1実装を復元します。これは一時的なものです — 削除する計画を立て、同梱してリリースする計画を立てないでください。
ゴールデンテストへの二次的影響:compress/flate のエンコーダも同じリリースで変更されたため、archive/zip、compress/gzip、compress/zlib、および image/png の出力は、デコード結果は同一であっても、Go 1.26とバイト単位で異なる可能性があります。古いバイト列を固定するのではなく、圧縮されたフィクスチャを再生成してください。
v2 API への意図的なマイグレーション
v2 APIは別のインポート(encoding/json/v2)であり、Marshal、Unmarshal、およびオプションシステムを持っています。サポートされているマイグレーションパスは、まずv2呼び出しをv1のように動作させ、その後選択的に厳格化することです:
import (
"encoding/json/v2"
"encoding/json/jsontext"
)
// ステップ 1: encoding/json と同一の動作 — 安全で機械的な変更
out, err := jsonv2.Marshal(v, jsonv2.DefaultOptionsV1())
// ステップ 2: v2デフォルトを1つずつ採用
out, err := jsonv2.Marshal(v,
jsonv2.DefaultOptionsV1(),
jsonv2.FormatNilSliceAsNull(false)) // nilスライスは[]としてエンコードされる
DefaultOptionsV1 を削除すると、v2デフォルトが適用され、それはv1より厳格で相互運用性が高いものです:
| 動作 | v1 (encoding/json) |
v2 デフォルト |
|---|---|---|
| 文字列内の無効な UTF-8 | U+FFFD に置き換え | エラー |
| オブジェクト内の重複名 | 許可 | エラー |
| nil スライス / nil マップ | null |
[] / {} |
| 構造体フィールドのマッチング | キャスインセンシティブなフォールバック | キャスセンシティブ |
| 未知のオブジェクトメンバー | 無視 | 無視(RejectUnknownMembers でエラーに設定可能) |
| マップキーの順序 | 常にソート | デフォルトでは未ソート |
time.Duration |
ビルトインの文字列エンコーディング | デフォルトのエンコーディングなし |
未ソートのマップキーがゴールデンファイルテストを驚かせる唯一の要因です:v2は、ソートに時間がかかるためソートをスキップします。安定した出力が必要な場合(フィクスチャ、スナップショットテスト、ハッシュされた表現)は、マップと構造体のソート済み出力を復元する json.Deterministic(true) を渡してください。ORM比較 記事のモデルはすべて json タグを持っています。これらのタグはv2でも引き続き機能しますが、実験中に format タグオプションは削除され、inline は embed に名前が変わったため、マイグレーション前にモデル定義を grep してください。ローレベルのニーズ(ストリーミング、トークンごとの処理、カスタム構文処理)には、コンパニオンの encoding/json/jsontext パッケージが、JSONテキスト上での Encoder/Decoder ステートマシンを公開しています。
stdlib uuid: github.com/google/uuid から離脱
Go 1.27は標準ライブラリに uuid を追加し、RFC 9562を実装します。APIサーフェスは一般的なケースをカバーします:
id := uuid.New() // 現在 NewV4 と同等
v7 := uuid.NewV7() // タイムソート可能、48ビットのタイムスタンプ + ランダム
u, err := uuid.Parse("550e8400-e29b-41d4-a716-446655440000")
github.com/google/uuid は2番目に多く依存されているGoモジュールであるため、このマイグレーションは規模が大きい場合に重要になります。2つのAPIは近く、型は == で比較可能でマップキーとして使用できる [16]byte の値であり、String()、MarshalText/UnmarshalText はラウンドトリップします。また、アップストリームのプロジェクトは go:fix inline シムを準備しており、go fix が uuid.NewV4() 呼び出しを標準パッケージに書き換え、依存関係を削除できるようになります。
興味深い採用判断は NewV7 です。バージョン7のUUIDは最も上位ビットにミリ秒単位のタイムスタンプを埋め込むため、値は作成順にソートされます(システム時計が後退する場合を除く)。データベースの主鍵にとって、これはランダムなUUIDv4よりも良いB-treeの局所性を意味し、識別子自体に created_at のような順序付けが無料で得られます — これはマルチテナントデータモデルにおけるタイムソート可能キーの議論を駆動する同じ性質です。
言語変更: ジェネリックメソッドとその仲間
ジェネリックメソッドは、このリリースで最も要求の多かった言語機能です:メソッドの宣言が独自の型パラメータを宣言できるようになりました。
// Go 1.27: math/rand/v2 は関数とともにジェネリックメソッドを取得
func (r *Rand) N[Int intType](n Int) Int
型システムを決定可能にするための2つの制限があります:インターフェースメソッドは型パラメータを宣言できず、インターフェースメソッドはジェネリックメソッドによって実装できません。それらとともに、構造体リテラルのキーは、トップレベルのフィールド名だけでなく、任意の有効なフィールドセレクタにできるようになりました。また、関数型の推論は、ジェネリック関数が対応する関数型変数に代入されるすべてのコンテキストに一般化されました。これらは既存のコードを壊すことはありません。ワークアラウンド(パッケージレベルのジェネリックヘルパー関数と明示的なインスタンス化)を削除します。
go fix は、隣接するクリーンアップのためのモダナイザーを拡大しました:atomictypes、embedlit、slicesbackward、および unsafefuncs。ハンズオンアップグレードラボからの注意点として、他のパッケージが参照するパッケージレベルの変数の型を変更する場合があるため、go fix -fix=atomictypes は一括処理ではなく意図的に実行してください。waitgroup モダナイザーは waitgroupgo に名前が変わりました。sync.WaitGroup.Go(1.25から)がすでに検出する大部分をカバーしていることに注意してください。ワーカープールのシャットダウンにどのように統合されるかは、有界並行性パターン を参照してください。
静かに壊れるもの
これらは、問題なくコンパイルされますが、リリース日を消費してしまう変更点です:
- 関数リテラルのシンボル名。 コンパイラはクロージャのために、よりシンプルでインライン展開に依存しない名前を生成し、同一の関数リテラルをマージする場合があります。トレースバックやプロファイル内のシンボル名に対するアサーションを持つテストは失敗し、関数のコードポインターの等価性を比較するコードは、より多くのケースで露骨になります。
gofmtの整列。 列の整列ロジックの修正(以前はCPUアーキテクチャによって異なっていた)により、すでにフォーマットされている少数のファイルが再フローされます。アップグレード後のgofmt ./...コミットで、空白のみ変更された差分を予想してください。go mod tidyがgo.modを書き換える。go 1.27のモジュールに対して、tidyは重複したrequireブロックを最大2つ(直接、間接)にマージし、コメントを保存します。リリースコミット後ではなく、前にgo.modの差分を確認し、マージコンフリクトの残骸を解決してください。- トレースバックのラベル。
go 1.27のモジュールのトレースバックでは、ヘッダー行にruntime/pprofのゴルーチンラベルが含まれます。ラベルに機密データが含まれる場合は、GODEBUG=tracebacklabels=0で無効にしてください。 go tool traceが localhost にバインド。-http=:6060は現在 localhost のみにリスニングします。リモートアクセスが必要な場合は、明示的に-http=0.0.0.0:6060を渡してください(共有ホストでのプロファイリングポートはリスクであることを自覚してください — 強化されたセットアップについてはプロダクションプロファイリングワークフローを参照)。
注目すべき実験
2つの実験パッケージが 1.27 にリリースされ、両方ともビルド時に GOEXPERIMENT=simd で有効化されます:
simd— 利用可能な場合にハードウェア命令にマッピングされる、可移植でベクトルサイズに依存しないAPI(Int8s、Float32s、…)。amd64での AVX/AVX2/AVX-512、arm64での NEON、および WebAssembly SIMD をサポートします。標準ライブラリはすでにこのメカニズムを内部で使用しています:Goの Swiss Table マップ実装がそれから恩恵を受けます。simd/archsimd— ベクトル幅を知る必要があるコードのためのアーキテクチャ固有レイヤー。
暗号化面では、crypto/mldsa が FIPS 204 の後量子 ML-DSA 署名方式を3つのパラメータセット(MLDSA44、MLDSA65、MLDSA87)で実装し、crypto/x509 に統合され、TLS 1.3 の署名方式として提供されます。ハイブリッド後量子鍵交換は、tlsmlkem/tlssecpmlkem の GODEBUG フォールバックが設定されている場合でも、Config.CurvePreferences を通じて強制できるようになりました。どちらも現在のところマイグレーション項目ではありませんが、両方とも、サービスが成長していく中で形作られるTLS設定のサーフェスを形作ります。
すぐに採用すべき新しい API
いくつかの追加は日常的な摩擦を解決し、採用コストはかかりません:
// strings.CutLast / bytes.CutLast — LastIndex のダンスの代替
before, after, found := strings.CutLast("a/b/c.tar.gz", ".")
// httptest.NewTestServer — メモリ内フェイクネットワーク、自動クリーンアップ
srv := httptest.NewTestServer(t, mux) // 実際のポートなし、defer srv.Close() 不要
httptest.NewTestServer は、TCPリスナーではなくメモリ内ネットワークでバックエンドされる httptest.Server を作成し、独自の t.Cleanup を登録します。その真の利点は、testing/synctest との組み合わせにあります:HTTPのラウンドトリップは合成時間で実行され、タイムアウトとリトライのテストが高速かつ決定論的になります — この組み合わせは、synctest 並行テスト 記事でデモされています。
アップグレードチェックリスト
順序が項目より重要です。各ステップは、前のステップがグリーンであることを前提としています。
| ステップ | 小型モジュール | サービス / モノリトレポ |
|---|---|---|
| 1. ツールチェーン | go install golang.org/dl/go1.27@latest |
toolchain go1.27.0 を固定;CIイメージを揃える |
2. go.mod |
go mod edit -go=1.27 && go mod tidy |
同上、および require ブロックのマージ差分を解決 |
| 3. 削除された GODEBUG | //go:debug と godebug: を grep |
デプロイされた設定とフラグも監査 |
4. go build ./... |
コンパイルエラーを修正 | モジュールごとに修正;失敗をバイセクション |
5. go test ./... |
stdversion/printf 検出を修正 |
シンボルのドリフトをキャッチするために go バンプ前に実行 |
6. go fix ./... |
差分をレビュー | モダナイザーを1つずつ実行 |
| 7. JSON/ゴールデンテスト | フィクスチャを再生成 | flate ベースのフィクスチャ(zip/gzip/png)も確認 |
8. GOEXPERIMENT=nojsonv2 |
スキップ | 一時的なもののみ;強制された原因についてイシューを起票 |
結び
Go 1.27 の破壊的変更は、ほとんどのプログラムが決して触れない端の部分(削除された GODEBUG のオプトアウト、シンボル名のアサーション、圧縮バイトのフィクスチャ)に集約され、その利点はすべてのプログラムが存在する場所に届きます:高速なJSON、標準UUID、ジェネリックメソッド。クリーンなアップグレードは主に規律です:go build、次に go test、そして go fix、その順序で、go ディレクティブはテスト実行の後ではなく前に引き上げます。nojsonv2 のエスケープハッチと asynctimerchan の最終デフォルトでの受理ルールは時間を買ってくれますが、どちらも消滅予定ですので、このアップグレードは、1.23–1.26 から継承される保留中の修正の締め切りとして扱いましょう。