Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go のフラットな パッケージに 「ファイル単位の private」を 〜Linter “de...

Avatar for mpyw mpyw
September 17, 2026

Go のフラットな パッケージに 「ファイル単位の private」を 〜Linter “declscope” を作った話〜

https://zenn.dev/yumemi_inc/articles/go-declscope-file-scoped-private のスライド版です。
AI に書かせました。

Repository: https://github.com/mpyw/declscope

Avatar for mpyw

mpyw

September 17, 2026

More Decks by mpyw

Other Decks in Programming

Transcript

  1. Go のフラットな パッケージに 「ファイル単位の private」を GO × STATIC ANALYSIS ×

    AI Linter “declscope” を作った話 🔒 mpyw / Accenture Japan Ltd 2026 01 / 14
  2. THE GAP Go の可視性は、ほぼ 2 段階 Exported import した全 package

    unexported 同じ package の全ファイル ファイル単位 この段階は 存在しない package database package database package api order_repository.go order_repository.go db.ScanUser() scanOrder scanOrder 素通り 届く ✕ 止めたい package database user_repository.go user_repository.go ScanUser scanUser scanUser normalizeEmail normalizeEmail ファイルの壁は越え放題。 コンパイラは何も言わない。 ファイル単位の private は、 Go には無い。 package の壁を越える。 大文字始まりで書くだけ。 unexported ≠ このファイルだけ 02 / 14
  3. INVISIBLE OWNERSHIP 暗黙の所有権は、コードから見えない package database user_repository.go order_repository.go func scanUser(...) {

    ... } func scanOrder(...) { func normalizeEmail(...) { ... } コンパイルは通る email = normalizeEmail(email) } 「この helper は、このファイルのもの」—— コンパイラは知らない 03 / 14
  4. AGENTIC CODING AI は規約を破るというより、 見える API を使う 規約を書く CLAUDE.md コメント

    レビュー文化 AI が見るもの → 見える宣言 コメントの規約 一定確率で読む → 負債も高速化 生成が速いぶん、 曖昧な境界も速く崩れる 必ず見える 自然言語の規約は効く。でも、守られるかどうかは確率的 04 / 14
  5. TRADE-OFF だからといって、package を割れば 終わりでもない 単一 フラットに保つ 依存は素直。ただし package 内の全宣言が互いに見え る。

    複数 細かく分ける 循環参照 の階層 リファクタリングで 境界を変更するコスト internal/ 欲しいのは package 分割ではなく、その内側にある軽い境界 05 / 14
  6. WHAT DECLSCOPE GUARDS declscope が守るのは、2 種類の境界 boundary qualify この namespace

    から あの namespace のものに 触ってよいか? シンボルに namespace を含めて 識別性を確保しているか? orderRepository → normalizeEmail in userRepository 既定で ON boundary は利用関係、qualify は名前と置き場所を検査する。 B scan → 誰の? scanUser → user のもの 既定は OFF・ON 推奨 Q 06 / 14
  7. ONLY TWO CONCEPTS 覚えることは 2 つだけ 01 ファイル名が namespace user_repository.go

    ↓ userRepository 02 scope は 2 種類 private package 自分の namespace package 内のどこでも public は Go の Exported がすでに担当。 07 / 14
  8. BOUNDARY RULE boundary 違反への答えは、必ず 2 つ A B 境界を守る 使用箇所や宣言を同一

    namespace に移 す。 move usage move declaration email.go OR 共有を宣言する //declscope:package func normalizeEmail(s string) string に移し、共有物だという判断をソースへ残す 08 / 14
  9. SHARED NAMESPACE 複数ファイルで 1 つの namespace に まとめても OK namespace:

    statement //declscope:namespace statement statement.go query.go type Statement func (*Statement) wheres []string Where(...) package database func (s *Statement) Where(...) { ... } args []any 物理ファイルを出発点にしつつ、実際の関心範囲へ合わせられる。 09 / 14
  10. QUALIFY RULE は、シンボル名のどこかに namespace が含まれることを要求する デフォルトではプレフィクスを提案するが、そのまま従う必要はない。直す場所は 2 つある。 qualify A.

    名前のほうを直す B. namespace のほうを直す scanUser //declscope:namespace user ↓ namespace をプレフィクスに ↓ 置き場所のほうを宣言 userRepositoryScanUser scanUser ✓ これが自動修正の提案。でも、名前が不自然。 すでに user を含んでいる。名前は変えなくてよい。 問われているのは、その名前と置き場所が噛み合っているか 10 / 14
  11. CASE STUDY: SPF13/COBRA 最も古いファイルだけ、リネームを忘れていた ほかの completion generators genZshComp genFishComp genPowerShellComp

    genBashComp // V2 どれも、自分の shell 名を名乗っている。 古い bash_completions.go gen writeFlag writeCommands → → → genBashCommands writeBashFlag writeBashCommands Bash の名前がなかった。リネームで namespace を明示。 実在 OSS でも、フラットパッケージの地雷を検出できた spf13/cobra: 14 ファイル / 6,138 行、開始時 boundary 35 / qualify 119 → 最終 0 / 0 11 / 14
  12. DURABLE INTENT AI 時代に必要なのは、判断を記録すること 👤 人間 診断 何の境界を越えたか → source

    code //declscope:package //declscope:namespace user 診断・ディレクティブ・導入 skill directive 何を共有してよいか → 🤖 次の agent skill 安全な導入順序 12 / 14
  13. TAKEAWAYS フラットさを保ったまま、 内側の境界を検査する ✓ Go にはファイル単位の private がない ✓ 暗黙の所有は、人にも

    AI にも見えにくい ✓ declscope は越境を決定論的に検査する ✓ 答えは「守る」か「共有を宣言する」 ✓ directive が設計判断の引き継ぎになる github.com/mpyw/declscope 使ってみた感想・Issue、お待ちしています。 13 / 14