Skip to content

Instantly share code, notes, and snippets.

@podhmo
Created July 20, 2026 20:32
Show Gist options
  • Select an option

  • Save podhmo/444ea66ce9f37a4f20ea82680ef99836 to your computer and use it in GitHub Desktop.

Select an option

Save podhmo/444ea66ce9f37a4f20ea82680ef99836 to your computer and use it in GitHub Desktop.
Uploaded via Gist Uploader - 2026-07-20T20:32:12.403Z

importalias 機能仕様書 (Functional Specification)

  • バージョン: v1.0 Draft
  • 日付: 2026-07-21
  • 対象言語/環境: Go (Golang)
  • 本書の位置づけ: 「何を提供するか(What)」を定義するものであり、「どう実装するか(How)」は意図的に範囲外とする。実装着手時のフィーチャーチェックリストとしての利用を想定する。

0. 背景・課題

  • Goのimport節はファイル単位で記述されるが、パッケージはディレクトリ単位の概念であり、両者の間に構造的なギャップがある。
  • gofmtsed のような機械的な一括書き換えでは、同一パッケージ内でのimport pathおよびqualified import時のalias名の一貫性は保証されない。
  • LLMにコード生成・修正を任せた場合、変数名との衝突を避けるための場当たり的な対応として、特定ファイルだけ異なるalias名を用いてしまう問題が発生する。
  • この「パッケージ内でのimport path / alias不一致」を機械的に検出し、可能な範囲で自動修正できる仕組みが必要である。

1. ゴール

  • G1: 同一パッケージ内で、同一import pathに対して複数の異なるalias名が使われている状態を検出できる。
  • G2: 同一パッケージ内で、同一alias名が複数の異なるimport pathに対して使われている状態を検出できる。
  • G3: 検出結果を元に「どちらが正か」を機械的に決定できる(多数決、または設定ファイルによる明示指定)。
  • G4: 自明なケース(書き換えても意味=セマンティクスが変わらないもの)に限り、自動修正を提供する。
  • G5: CIへの導入が容易であること(go vet 標準インターフェース準拠)。
  • G6: 既存のGoツールチェーン・エコシステム(golangci-lint等)との連携が自明に可能であること。

2. 非ゴール

  • NG1: alias名そのものの命名規則の強制(例:import pathのセグメントに基づく命名規則の是非)は対象外。これは既存ツール Glyphack/go-import-alias の役割であり、本ツールは「パッケージ内一貫性」のみを扱う。
  • NG2: 常に人間が全てのalias対応表を明示的に用意する運用(julz/importas 的な使い方)は必須要件としない。設定ファイルはあくまで補助であり、無指定でも多数決により自律的に動作することを基本とする。
  • NG3: コメント・docstring内の文字列の書き換え。
  • NG4: go vet analyzer自体によるファイルの自動書き換え(ファイルシステムへの副作用)。

3. 提供物の全体像

本プロジェクトは 2つの独立した成果物 として提供される。両者は「4節: 決定ロジック」「5節: 設定ファイル仕様」を共通の基盤として共有するが、責務は明確に分離する。

D1: go vet Analyzer D2: スタンドアロンCLI
主目的 CIでの検出専用 ローカル/補助CIでのスキャン+設定生成+auto-fix
ファイルシステムへの書き込み 一切なし(read-only) あり(設定ファイル・ソースコード)
実行経路 go vet -vettool=... ./... 独立バイナリとして直接実行
インターフェース analysis.Analyzer 準拠 goimportalias(7.7節参照)

4. 共通仕様: 決定ロジック(正となるalias/pathの決定方法)

D1・D2共通で使用される、不整合が見つかった際に「どちらを正とするか」を決めるロジック。

4.1 優先順位

設定ファイルによる明示指定は、常に4.2節の多数決より優先される(確定事項。かつて「推定」としていたが、本節で確定した)。以下はその内部での優先順位である。

  • FR-4.1: 設定ファイルにおける明示的なパッケージ名指定を最優先とする。
  • FR-4.2: 次に、設定ファイルにおける foo/... 形式のスコープ指定を評価する。
  • FR-4.3: 次に、設定ファイルにおける "*"(グローバル)指定を評価する。
  • FR-4.4: 上記いずれの設定にも該当しない場合、パッケージ内での使用頻度による多数決で決定する。
  • FR-4.11: 設定ファイルにおいて、あるimport pathに対する値がalias文字列(空文字列以外)の場合、コード上で無aliasであっても、設定のaliasを正として扱う。すなわち、無aliasのimportは「aliasを付与すべきなのに付いていない」不整合として、D1の検出対象・D2のauto-fix対象となる。
  • FR-4.12: 設定ファイルにおいて、あるimport pathに対する値が空文字列("")の場合、「aliasなし(無aliasのimport)」が正であることを意味する。コード上でそのpathにaliasが付与されている場合は不整合として検出し、aliasを取り除く方向がauto-fixの対象となる(詳細は7.3節FR-7.21)。

4.2 多数決ルール

  • FR-4.5: 同一パッケージ内での出現回数が最多となるalias/pathの組を「正」とする。
  • FR-4.6: 最多の組が複数存在する(同数タイ)場合は自動確定しない。
  • FR-4.7: strict オプションが有効な場合、当該パッケージ内に複数の異なるalias/pathが存在する時点で、出現回数に関わらず常に同数タイとして扱う(=多数決による自動決定を行わないモード)。

4.3 タイ発生時の挙動

  • FR-4.8: タイが発生した対象については、自動修正を行わない。
  • FR-4.9: タイが発生した対象については、設定ファイルに候補となる2つのalias/pathの組を書き込む。
  • FR-4.10: 設定ファイルに2つの候補が並記された状態にある間は、当該対象への自動修正を行わない(人間が1つに絞るまで保留)。

5. 共通仕様: 設定ファイル

5.1 配置・形式

  • FR-5.1: 設定ファイルはモジュールルート(go.mod と同じディレクトリ)に配置する。
  • FR-5.2: フォーマットはJSON。
  • FR-5.8: デフォルトのファイル名は importalias.json とする。

設定ファイル全体のトップレベル構造(packages / ignore の2キーでのラップ)は5.6節を参照。以下5.2〜5.4節で説明する優先順位・スキーマは、packages キー配下の内容を指す。

5.2 スコープ記法と優先順位

  • FR-5.3: "*" キーによりグローバルなデフォルトルールを定義できる。
  • FR-5.4: "foo/..." 形式のキーにより、指定パッケージ配下(下位パッケージ含む)へのルールを定義できる。
  • FR-5.5: 明示的なパッケージパス(三点ドットなし)によりルールを定義できる。
  • FR-5.6: 優先順位は「明示的パッケージ名 > foo/..."*"」の順(4.1節と同一)。

5.3 設定ファイルの例(イメージ)

以下はスキーマの一例であり、キー・値の正確な形は実装時に確定してよい。packages キー配下に配置される点に注意(5.6節参照)。

{
  "packages": {
    "*": {
      "github.com/pkg/errors": "errors"
    },
    "foo/...": {
      "github.com/aws/aws-sdk-go/aws": "awssdk"
    },
    "foo/bar/baz": {
      "github.com/aws/aws-sdk-go/aws": "aws"
    }
  }
}

上記の場合、foo/bar/baz パッケージでは awsfoo 配下のその他パッケージでは awssdk、それ以外のパッケージでは errorsgithub.com/pkg/errors に対して)が「正」として扱われる。

5.4 タイ発生時の候補表現(イメージ)

{
  "packages": {
    "foo/bar": {
      "example.com/util": ["util", "util2"]
    }
  }
}

配列で2候補が並記されている間は「未解消」を意味し、人間が1つの文字列に絞ることで解消される(4.3節)。

5.5 generated file 判定

  • FR-5.7: ファイル先頭の generated file マーカー(// Code generated ... DO NOT EDIT. 等)を検出した場合に、当該ファイルを解析・修正の対象外とするかどうかを切り替えられること。デフォルトはスキップ(対象外)とする。

5.6 設定ファイル全体の構造(packages / ignore ラップ)

  • FR-5.9: 設定ファイルのトップレベルは packagesignore の2キーでラップする。
    • packages: パッケージスコープ毎のalias/pathルール。5.2〜5.4節の内容はこのキー配下に配置される。
    • ignore: 明示的に解析・修正の対象外とするパッケージのパターンのリスト(5.7節)。

全体像の例:

{
  "packages": {
    "*": {
      "github.com/pkg/errors": "errors"
    },
    "foo/...": {
      "github.com/aws/aws-sdk-go/aws": "awssdk"
    }
  },
  "ignore": [
    "foo/legacy",
    "foo/generated/..."
  ]
}

5.7 ignoreキーの記法

  • FR-5.10: ignore は文字列の配列とする。各要素には、明示的なパッケージパス、または foo/... 形式のパターン(下位パッケージ含む)を指定できる。
  • FR-5.11: ignore に該当するパッケージは、D1の検出対象・D2のスキャン/auto-fix対象の両方から除外される。vendor除外(6.3節/7.5節、標準挙動由来)とは独立した、ユーザーが明示的に指定できる追加の除外リストである。
  • FR-5.12: ignore の判定に優先順位は不要(packages のスコープ優先順位とは異なり、いずれかのパターンにマッチすれば単純に除外される)。

6. D1: go vet Analyzer 要件

6.1 目的

CIパイプラインへの組み込みを主目的とした、検出専用(read-only) のanalyzer。

6.2 インターフェース

  • FR-6.1: golang.org/x/tools/go/analysisAnalyzer 型に準拠する。
  • FR-6.2: go vet -vettool=$(which importalias) ./... 形式での実行に対応する。
  • FR-6.3: golangci-lintのカスタムlinterとしての組み込みも可能であること(インターフェース準拠により自明に満たされる)。ただし主要な利用経路は go vet 本体とし、golangci-lint対応は副次的位置づけとする。

6.3 対象範囲

  • FR-6.4: 単一パッケージ、または foo/... 形式での複数パッケージ(下位ディレクトリ含む)指定に対応する。
  • FR-6.5: vendorディレクトリは対象外とする(go vet 標準の挙動に準拠。go.mod由来の別モジュールと同様の扱い)。
  • FR-6.6: 明示的な「自モジュールのみ」オプションは提供しない(./... のパターン展開が自モジュール内パッケージに限定されるというGoツールチェーン標準の挙動に依存する。詳細は9節ADR-3)。

6.4 対象ファイル

  • FR-6.7: 通常の .go ソースファイルを解析対象とする。
  • FR-6.8: _test.go(内部・外部テストパッケージ双方)も解析対象に含める。
  • FR-6.9: 5.5節の generated file 判定に従い、該当ファイルをスキップできる。
  • FR-6.17: 多数決(4.2節)の集計において、テストパッケージ(_test.go、内部・外部いずれも)と非テストパッケージの出現回数は、分けて集計しても、合算して集計してもよい。実装上、合算する方が容易であればそちらを採用してよい(要件としてはどちらでも良い、という柔軟な仕様とする)。

6.5 検出ロジック

  • FR-6.10: 同一パッケージ内で、同一import pathに複数の異なるalias名が対応している場合、不整合として検出する(ファイルをまたいだ不整合)。
  • FR-6.11: 同一パッケージ内で、同一alias名に複数の異なるimport pathが対応している場合、不整合として検出する(ファイルをまたいだ不整合)。
  • FR-6.16: 単一ファイル内で、同一import pathが複数回、異なるalias名でimportされている場合も検出する(1ファイル内で完結する重複importのケース)。この観点は既存の一般的なlinter(例: staticcheckのST1019相当)でも部分的に検出されるが、alias不整合という同じ問題領域に属するため、本ツールの検出範囲にも明示的に含める。
  • FR-6.12: 「正」の決定は4節のロジックに従う。analyzerは決定結果を元に診断を出すのみで、ファイルへの書き込みは一切行わない。

6.6 出力

  • FR-6.13: 診断は go vet 標準フォーマット(file:line:col: message)に準拠する。
  • FR-6.14: 全ての診断は同一のseverityとして扱う(warning/error等の区別を設けない)。

6.7 副作用に関する制約

  • FR-6.15: 本analyzerはファイルシステムへの書き込みを一切行わない(設定ファイルの生成・更新を含む)。設定ファイルの生成・更新はD2の責務とする。

7. D2: スタンドアロンCLI要件

7.1 目的

D1と同じ検出ロジックを用いつつ、実際のファイル書き換えと設定ファイルの生成・更新を担う独立ツール。CIでの検出用途ではなく、開発者がローカルで(あるいは自動PR作成のような補助的なワークフローで)明示的に実行する用途を想定する。

7.2 スキャン機能

  • FR-7.1: D1と同じ検出ロジック(6.5節)により、対象範囲内の不整合を洗い出す。
  • FR-7.2: スキャン結果を元に、4節の決定ロジックに従い「正」となるalias/pathを算出する。
  • FR-7.3: スキャン完了後、副産物として設定ファイルを生成・更新する(存在しない場合は新規作成、存在する場合は追記・更新)。
  • FR-7.4: タイが発生した箇所については、5.4節に従い2候補を設定ファイルに書き込む。

7.3 auto-fix機能

  • FR-7.5: 決定ロジックにより一意に「正」が定まった不整合について、「自明な変換」に該当する場合に限り自動修正を適用する。
    • 「自明な変換」の定義:書き換えによって変数名との衝突が生じず、コードの意味(セマンティクス)が変化しないケース。
  • FR-7.6: 設定ファイルに2候補が並記されている(タイが未解消の)対象は、auto-fixの対象外とする。
  • FR-7.7: auto-fixはimport節の書き換えに加え、同一ファイル内の該当qualified identifier(pkg.Symbol 形式の参照)も整合するように書き換える。
  • FR-7.8: 「同一alias名に複数の異なるimport pathが対応」(6.5節 FR-6.11)のケースにおける自動修正の具体的な挙動(衝突した側のaliasを何に書き換えるか)は、本書時点では確定しない(10節・11節参照)。少なくとも検出は必須、auto-fixは保留してよい。
  • FR-7.16: 単一ファイル内での重複import(6.5節 FR-6.16)が検出された場合、決定ロジック(4節)で定まったcanonicalなalias/pathに統合し、重複したimport文を削除した上で、当該ファイル内のqualified identifier参照をcanonical側に書き換える。これも「自明な変換」に該当する場合に限り自動修正の対象とする(FR-7.5の定義に従う)。
  • FR-7.21: alias付きimportから無aliasのimportへの書き換え(FR-4.12、空文字列設定によるalias除去)を行う場合、以下を満たすことを確認した上で自動修正を適用する。
    • 対象パッケージの実際のデフォルト識別子名(package 節で宣言されている名前。import pathの末尾セグメントと一致するとは限らない)を正しく取得すること。
    • 書き換え後の識別子名が、当該ファイル内の既存の変数名・他のimportのデフォルト識別子・その他のスコープ内識別子と衝突しないことを確認すること。
    • 衝突する場合は「自明な変換」(FR-7.5)に該当しないと判断し、当該ファイルについてはauto-fixの対象外とする(診断としては検出を維持する)。

7.4 対象ファイル・対象外

  • FR-7.9: _test.go を含む通常のGoソースファイルを対象とする。
  • FR-7.10: vendorディレクトリは対象外とする。
  • FR-7.11: 5.5節の generated file 判定に従い、該当ファイルをスキップできる。
  • FR-7.12: コメント・docstring内の記述は書き換え対象外とする。

7.5 対象範囲

  • FR-7.13: 単一パッケージ、または foo/... 形式での指定に対応する。
  • FR-7.14: 明示的な「自モジュールのみ」オプションは提供しない(D1と同様の理由。9節ADR-3)。

7.6 出力・ログ

  • FR-7.15: スキャン結果・修正内容を人間が確認できる形で出力する。少なくとも「何を修正したか」「どこでタイが発生し保留されたか」が分かる情報を含むこと。

7.7 コマンド仕様

  • FR-7.17: CLIバイナリ名は goimportalias とし、cmd/goimportalias に配置する(プロジェクト全体の名称 importalias とは別に、実行コマンド名として goimportalias を用いる)。
  • FR-7.18: -fix フラグを指定した場合、auto-fix(7.3節)を適用する。
  • FR-7.19: -config フラグで設定ファイルのパスを明示的に指定できる。省略時はモジュールルート直下の importalias.json(FR-5.8)を使用する。
  • FR-7.20: -fix を指定しない場合のデフォルト動作は、スキャンと設定ファイルの生成・更新(7.2節)のみとし、ソースコードの書き換えは行わない。この既定動作の解釈は会話上で明示確認されていない推定であり、10節に記載する。

8. 用語集

用語 説明
多数決 (Majority Vote) 同一パッケージ内でのalias/path出現回数を比較し、最頻出のものを正とするルール
タイ (Tie) 多数決において最多出現回数が複数の候補で並ぶ状態
strictオプション 複数の異なるalias/pathが存在する時点で常にタイ扱いとする厳格モード
自明な変換 変数名衝突が生じずセマンティクスが変化しない書き換え
D1 / D2 本書内での成果物の呼称(3節参照)。正式名称ではない

9. ADR(Architecture Decision Records)— 検討され却下・変更された提案

ADR-1: 既存ツールの採用可否

  • ステータス: 却下(新規開発を採用)
  • 検討した代替案:
    • julz/importas — go/analysisベース、./... 対応、正規表現によるpath→alias変換に対応。golangci-lintにも統合済み。
    • Glyphack/go-import-alias — alias名がpath由来の命名規則に従うかを検証するツール。
  • 却下理由:
    • julz/importas は常に人間が明示的にalias対応表を用意する前提であり、パッケージ内多数決による自律的な不整合検出・自動決定の機能を持たない。auto-fixも存在しない。
    • Glyphack/go-import-alias は目的が異なり(命名規則検証)、パッケージ内一貫性チェックという中核要件を満たさない。
  • 決定: 新規ツール importalias として開発する。julz/importas の実装コードは参考にせず、独自実装とする(OQ-5として残っていた論点はこれにより解消)。

ADR-2: auto-fixをgo vet analyzer自体に統合するか

  • ステータス: 却下(D1/D2への分離を採用)
  • 検討した代替案: Diagnostic.SuggestedFixes を用いて go vet 実行の延長でauto-fixまで完結させる。
  • 却下理由:
    • go vet 本体はSuggestedFixesを適用しない(適用するのは singlechecker -fix 等の別のドライバ)。
    • CI用途では検出のみで十分であり、auto-fixは不要。
    • go vet はunitcheckerプロトコル経由でパッケージ毎に別プロセス実行されるため、共有設定ファイルへの書き込みに競合リスクが生じる。read-onlyであるべきanalyzerの慣習にも反する。
  • 決定: 検出専用のgo vet analyzer(D1)と、auto-fix機能を持つ独立CLI(D2)に役割を分離する。

ADR-3: 「自モジュールのみ」オプションの要否

  • ステータス: 変更(当初要件から削除)
  • 背景: 当初「高速化のために自身のモジュールだけを対象にできると良い」という要件があった。
  • 却下理由: ./... によるパターン展開は標準のGoツールチェーンにおいて既に自モジュール内パッケージに限定される。vendor除外も同様に標準挙動でカバーされる。よって独自オプションとして持つ意義が薄いと判断した。
  • 決定: 明示的な「自モジュールのみ」フラグは提供しない(D1・D2共通)。

ADR-4: コメント・docstringのauto-fix対象化

  • ステータス: 却下
  • 検討内容: コメントやdocstring中の記述もauto-fixの対象に含めるかを検討した。
  • 却下理由: コメント中に載るのは基本的にimport aliasではなくpath名であることが多く、alias名が偶然一致してマッチするケースは考えにくいため、対応する実益が薄いと判断した。
  • 決定: コメント・docstringはauto-fix対象外とする。

ADR-5: severity(warning/error)の区別

  • ステータス: 却下
  • 検討内容: 診断結果にwarning/errorの重要度区分を設けるかを検討した。
  • 却下理由: go vet 自体にはそのような区別の概念が存在しない(検出があれば非ゼロ終了コードになるのみ)。severityの概念を持つのは golangci-lint のような上位ツールであり、本プロジェクトが優先するのは go vet 本体である。
  • 決定: 全ての診断を区別なく同列(warning相当)に扱う。

ADR-6: golangci-lint対応を主軸にするか

  • ステータス: 却下(go vet 本体を優先)
  • 検討内容: golangci-lintのカスタムlinterとしての利用を主要な想定利用経路にするかを検討した。
  • 決定: analysis.Analyzer インターフェースに準拠することでgolangci-lintとの連携自体は自明に可能とするが、CLIとしての主要な実行経路・ドキュメント上のフォーカスは go vet 本体(go vet -vettool=...)とする。

10. 本書作成にあたっての推定・仮定

会話上で明示されなかったが、仕様として「機能する」ために本書作成時に補った推定事項。実装前に要確認。

  • 設定ファイルのJSON具体的スキーマ(5.3節・5.4節の例): 構造のイメージを示すための一例であり、キー名・配列/オブジェクトの選択などの正確な形は未確定。
  • -fix を指定しない場合のデフォルト動作(FR-7.20): 「スキャン+設定ファイル生成のみ、ソースコード書き換えなし」という前提で記述した。会話内で直接は確認されていない推定。

11. 未確定事項(Open Questions)

  • OQ-1: D1・D2間でのコアロジック(検出・多数決計算等)のコード共有方法は未定(本書は「何を提供するか」を扱うため意図的に範囲外)。
  • OQ-2: リポジトリの公開先(個人GitHub想定か等)は未定。
  • OQ-3: 実装着手順(analyzer本体からか、config loaderからか)は未定。
  • OQ-4: go.workによる複数モジュール環境下での「自モジュール」判定の挙動は未検討。
  • OQ-5: 「同一alias名に複数の異なるimport pathが対応」ケース(FR-6.11)における auto-fix の具体的な書き換え先ルールは未定(FR-7.8参照)。
  • OQ-6: go.workのようなワークスペースで複数モジュールを横断する共有設定ファイル(モジュールルート単位ではなくワークスペース全体に効く設定)を別途持つかどうかは未検討。現状は「設定ファイルはモジュールルート単位」(FR-5.1)を前提としており、"*" はそのモジュール自身のimport path配下(実質的に自モジュール全体への .../... 相当)を指すショートハンドと位置づけている。

12. 検証方針(テスト戦略)

  • FR-12.1: 想定されるユースケース(検出パターン: FR-6.10 / FR-6.11 / FR-6.16、決定ロジックの各分岐: 4.1節・4.2節・4.3節、alias除去方向のfixと衝突確認: FR-7.21、auto-fixの各パターン等)ごとに、対応するテストフィクスチャを testdata ディレクトリ配下に用意する。
  • FR-12.2: 上記の testdata ベースのテスト方式を確立し、それを用いて仕様の充足を判断できる状態にすることを、実装の最初のステップとする。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment