Slide 1

Slide 1 text

Go Modulesの仕組み ~Bundler(Ruby)との⽐較を添えて~ 1 どすこい Fukuoka.go#21

Slide 2

Slide 2 text

2 GMOペパボ Webエンジニア どすこい Daisuke Takeda k1low⽒の弟⼦に習って(=孫弟⼦)、 Goが好きになっちゃったエンジニア 配属先はRails/PHPですが、 そこでもGo⾰命を起こします!! Goコミュニティに参加してくぞ〜! X: @doskoi64

Slide 3

Slide 3 text

PythonやRubyで、 考えなしにパッケージを⼊れると 依存関係解消が失敗することがある... マイナーバージョンアップでも 壊れることある.... ある⽇ イントロダクション 3

Slide 4

Slide 4 text

Railsの場合 イントロダクション 4 Railsガイド https://railsguides.jp/maintenance_policy.html 破壊的変更するんだ...?🤔

Slide 5

Slide 5 text

⼀⽅で Goではあんまり気にしたことないなぁ なんで気にしなくていいんだろ...? ある⽇ イントロダクション 5

Slide 6

Slide 6 text

Go 1.11より イントロダクション 6 https://github.com/golang/go/issues/24301

Slide 7

Slide 7 text

Go 1.11より イントロダクション 7

Slide 8

Slide 8 text

Go 1.11より イントロダクション 8 (前略)、Bundler/Cargo/Depのアプローチの詳 細と、それをGoに組み込むことの意味をより深 く理解し、(中略)、いくつかの細部がGoには適 していないと次第に感じるようになりました。 最初は同じ感じだったんだ? 何が違うんだ?

Slide 9

Slide 9 text

9 今⽇話すこと ● Go Modulesの依存解決の仕組み ● Bundler(Ruby)との⽐較 今⽇話さないこと ● Go Modulesでどこがどう動いてるのか ● Bundlerがどこでどう動いてるのか ● これらの歴史的な経緯

Slide 10

Slide 10 text

Go Modulesの仕組み Go Modulesとは ● Go 1.11から導⼊ ● モジュール:複数のパッケージをまとめてリリース/バージョン管理/配布するための 単位。GitHubやモジュールプロキシサーバからダウンロードが可能 ● モジュールパスによって⼀意に識別してgo.modで宣⾔ ● go.sumにモジュールのハッシュが保持され、ダウンロードしたモジュールの内容が 相違ないかチェック 10 https://go.googlesource.com/proposal/+/master/ design/24301-versioned-go.md ref: Go Modules Reference (https://go.dev/ref/mod)

Slide 11

Slide 11 text

11 Go Modulesの仕組み https://github.com/golang/go/issues/24301 昔はdep, glideなど外部 ツールを⽤いていた。 標準化しようぜ!

Slide 12

Slide 12 text

Go Modulesの仕組み ● go.modに列挙された直接依存から、それぞれが要求す る他の依存(間接依存)を再帰的に集めてビルドリストを 作成 ● 各モジュールでは必要最低限のバージョンを宣⾔ ● これを⽤いてMinimal Version Selection (MVS)で最終 的な依存バージョンを決定 依存解決 12

Slide 13

Slide 13 text

Go Modulesの仕組み ● go.modに記載された直接依存が必要とする依存(間接依 存)を再帰的に収集 ○ 各モジュールは必要最低限なバージョンを指定するた め、すべての依存先から集まった最⼩の要件の中で、 最⼤値となるバージョンが選ばれる ○ module Aではhoge v1.2.3が、module Bでは hoge v1.5.0が要求される時はv1.5.0を採⽤ Minimal Version Selection (MVS) 13

Slide 14

Slide 14 text

Go Modulesの仕組み module Aでhoge v1.2.3が、 module Bでhoge v1.5.0が使われてる→v1.5.0を採⽤ そぼぎ(素朴な疑問) 14 けど、なんでmodule Aでは v1.5.0で動く前提なの?

Slide 15

Slide 15 text

Go Modulesの仕組み module Aでhoge v1.2.3が、 module Bでhoge v1.5.0が使われてる→v1.5.0を採⽤ そぼぎ(素朴な疑問) 15 けど、なんでmodule Aでは v1.5.0で動く前提なの? Semantic Versioningのルールより モジュールパスが同じなら後⽅互換がある! (逆に⾔えばこれが前提のシステム)

Slide 16

Slide 16 text

Go Modulesの仕組み そぼぎ(素朴な疑問) 16 じゃあmodule Aではv1.2.3が、module Bではv2.1.0が 使われてる時、v2.1.0を採⽤するの?(A壊れない?)

Slide 17

Slide 17 text

Go Modulesの仕組み そぼぎ(素朴な疑問) 17 じゃあmodule Aではv1.2.3が、module Bではv2.1.0が 使われてる時、v2.1.0を採⽤するの?(A壊れない?) モジュールパスが異なるv1, v2両⽅採⽤する! ex) gihub.com/oklog/ulidとgithub.com/oklog/ulid/v2 別モジュールとして扱われるので、 v1.2.3とv2.1.0の両⽅をimportすることで 対処できる

Slide 18

Slide 18 text

Go Modulesの仕組み 詳しくはGO Conference2024ののびしーさんの発表があります! 18 https://gocon.jp/2024/sessions/13/

Slide 19

Slide 19 text

Rubyとの⽐較 ● gem の依存関係とバージョンを管理するための ツール ● Gemfileで任意にgemのバージョン を指定できる ○ = のほかに、<=, >などで⼤⼩関係で指定できる Bundler 19

Slide 20

Slide 20 text

Rubyとの⽐較 Gemfileに指定された制約を解析し、 全ての制約を満たすバージョンの  組み合わせを探す ⼤変らしい(NP完全) https://arxiv.org/pdf/2011.07851 (⾼速な解法がないとも⾔えるかも) Bundlerの依存解決アルゴリズム 20

Slide 21

Slide 21 text

Rubyとの⽐較 ● Aでは ○ Cについて”>= 1.0, < 3.0” ● Bでは ○ Cについて”>= 3.0, < 3.5” Bundlerが困る時: 解がないとき 21 最適なバージョンがない場合が発⽣しうる ※ (補⾜) 別gemとして別バージョンを利⽤する⽅法はあるらしい けどGo Modulesはシンプルで⼀意に決まります → 解なし...

Slide 22

Slide 22 text

⽭盾の例 ● Aでは ○ Cについて”>= 1.0, < 3.0” ● Bでは ○ Cについて”>= 3.0, < 3.5” Rubyとの⽐較 なんでGoだとこうならないの? 22 Goでは、C v1.0とC v3.0を 別モジュールとしてimportできる

Slide 23

Slide 23 text

⽭盾の例2 ● Aでは ○ Cについて”>= 1.0, < 1.5” ● Bでは ○ Cについて”>= 1.6, < 2.0” Rubyとの⽐較 なんでGoだとこうならないの? 23

Slide 24

Slide 24 text

⽭盾の例2 ● Aでは ○ Cについて”>= 1.0, < 1.5” ● Bでは ○ Cについて”>= 1.6, < 2.0” Rubyとの⽐較 なんでGoだとこうならないの? 24 Semantic Versioningの後⽅互換の前提があるので マイナーバージョンの上限を指定しないので起こらない → v1.6になる

Slide 25

Slide 25 text

まとめ Go ModulesはSemantiv Versioning を前提に依存関係解消をしている 任意にバージョンを指定できる⾔語の 場合、最適なバージョンをソルバーが 解く必要がある Go Modulesはルールによってシンプル さを獲得した具体例かもしれない 25 今⽇話したこと

Slide 26

Slide 26 text

26 Let’s Go