2025年 Packwerkの現在と弊社の状況
はじめに
こんにちは。Linc'wellでバックエンド基盤チームを担当している山本です。このブログはLinc’well Advent Calendar 2025の18日目として公開しています。
弊社ではPackwerkを導入して、約2年が経過しました。Packwerk自体は2020年にShopifyによりリリースされ、Shopifyを始め多くの企業で利用されている背景があります。
この記事では、今一度Packwerkが世の中でどのように利用されているか、他の企業と比較して我々の方向性はどうなっているのかを振り返り、今後のモジュラーモノリスの方針を考えてみたいと思います。
用語の定義
弊社では誤解を生まないよう「パッケージ化」と「モジュラーモノリス化」の用語を分けて使っています。
- パッケージ化
チームが管理する機能をPackwerkが管理する特定のディレクトリに入れる行為を指す。 - モジュラーモノリス化
Packwerkを使って他パッケージとのインターフェースが整理されており、独立したデータストレージを保有し、他パッケージとの依存関係が明確になっている状態を指す。
Shopifyの振り返り
2024年2月に公開されているShopifyのPackwerkについてのレトロスペクティブ(振り返り)の記事です。
この記事からはPackwerkの歴史を紐解きながら、Shopifyで発生した様々な問題について読み解くことができます。
今回はこの振り返りの内容を確認しつつ、弊社でのPackwerk運用に当てはめて考察してみたいと思います。
そもそもPackwerkは「依存関係管理ツール」である
Packwerkを開始するには以下の2点を準備する必要があると書かれています。
- Define a set of packages, captured in (possibly nested) file directories.
(ネストされている可能性のある) ファイル ディレクトリにキャプチャされたパッケージのセットを定義します。- Define a non-circular set of dependency relationships between these packages.
これらのパッケージ間の非循環的な依存関係のセットを定義します。
ここに書かれている「非循環的な依存関係」をセットすることは非常に重要です。Packwerkとは無関係に、Railsプロジェクト内に無秩序に作成されたクラスが相互に参照していると複雑性を増し、コードの理解やリファクタリングの大きな妨げとなります。
Packwerkを導入している企業の多くはGitHub ActionsやCircleCIのフローで packwerk check を実行しているかと思いますが、このコマンドでは循環参照の検出は行われません。Packwerkで最も重要なのは各モジュール間の循環参照を検出することだと私も考えています。循環参照の検出は packwerk validate の実行が必要です。このコマンドはpackage.ymlに記載されているdependenciesに基づき、循環参照を検出します。弊社ではこの2つのコマンドをCircleCIで実行し循環参照を未然に防止しています。
# CircleCIのconfig.ymlの一部
packwerk:
<<: *defaults
docker:
- <<: *web_image
steps:
- checkout
- ruby/install-deps
- run:
name: install graphviz
command: sudo apt-get update && sudo apt-get install -y graphviz
- run:
name: generate packwerk.png
command: bin/rake graphwerk:generate
- store_artifacts:
path: packwerk.png
- restore_cache:
keys:
- packwerk-cache-{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ .Branch }}-{{ .Revision }}
- packwerk-cache-{{ .Environment.CIRCLE_CACHE_VERSION }}-
- run:
name: packwerk validate & check
command: |
echo "Executing bin/packwerk validate"
bin/packwerk validate
echo "Executing bin/packwerk check"
PARALLEL_PROCESSOR_COUNT=2 bin/packwerk check
- save_cache:
key: packwerk-cache-{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ .Branch }}-{{ .Revision }}
paths:
- tmp/cache/packwerk
Packwerkの本来の目的はAPI設計ツールではない
Packwerk acquired an entirely different usage in its early stages, in the form of “privacy checks” which could be enabled on the same set of packages above to statically declare public APIs.
Packwerkは初期段階で全く異なる用途を獲得しました。それは「プライバシーチェック」という形で、上記のパッケージ群で有効化することで、パブリックAPIを静的に宣言できるものでした。
Packwerkの初期の仕様ではプライバシーチェッカーの機能が内包されていました。これにより、パッケージ内のクラスをなるべく隠蔽し、外部に公開するAPIを設計するツールの代用とされてしまった経緯があります。
この方針自体は悪いものではありませんが、私は以下の問題が発生すると考えています。
- Railsと相性が悪い
Ruby on Railsの良いところはMVC(Model, View, Controller)のそれぞれ役割が異なるコードの格納場所があらかじめ決められている点だと考えています。Packwerkが提供するpublicディレクトリ以下はMVC関係なくフラットにファイルを置く必要があるため、Railsと相性が悪いです。 - 改修コストがかかりすぎる
単純にパッケージ内のライブラリを参照するだけでよいはずなのに、プライバシーチェッカーを回避するためだけに、本来不要なAPI用クラスを作成した事はないでしょうか?これでは無駄にコード量が増し、かえって可読性が低下してしまいます。
現在プライバシーチェッカーはPackwerk本体からは切り離されpackwerk-extensionsに切り出されています。
現在、弊社ではプライバシーチェッカーは有効になっているものの、あまり厳密に運用されていない状況です。実際にマイクロサービス化を見据えてパッケージを作成するには、単純にファイルを移動するだけでは完結せず、API設計のやり直しが発生するかと思います。一旦大雑把にパッケージ化を行い、package_todo.ymlで外部パッケージからの参照の可視化が行われている状態でも十分にPackwerkの恩恵は受けることができます。
依存性を解決するガイダンスは存在しない
Packwerk provides no guidance here, and is happy with any choice you make.
Packwerk はこの点に関してガイダンスを提供せず、ユーザーがどのような選択をしても受け入れます。
上記はShopifyの振り返りの中で記載されている内容です。
Packwerkは静的解析により、コード間の依存性を解析しtodoファイルを作成します。しかし、todoファイルからどのように依存性を解決していくかのガイダンスは提供してくれません。したがってこの部分は人間が考えていく必要があります。
ガイダンス機能が無い中我々はどのようにpackage_todo.ymlと向き合えばよいでしょうか?私は以下の2パターンを常に考えています。
- 依存の警告は放置してpackage_todo.ymlの情報を活かす
「todo」という名前からいずれ解決しなければならない問題と取られがちですが、依存関係の可視化がなされている重要な情報と捉えると、高い改修コストを払ってまで無理やり解決する必要はないと考えています。むしろtodoファイルが存在しなければパッケージに格納されているクラスがどこから参照されているか知ることはできません。この情報はパッケージの改修やリファクタリングなどに活かされています。 - チーム組成を意識したモジュラーモノリスを設計・開発する
将来的に専門のチームを組成し、いずれはマイクロサービスに切り出すことも念頭に入れている場合は、専用に設計を行いモジュラーモノリス化に取り組みます。この取り組みにはイベントストーミングを用いて、ビジネスプロセスやシステムの挙動を視覚化して大規模なリアーキテクチャを実施する予定です。現在モジュラーモノリスと言えるパッケージは1つのみですが、2026年は少なくとももう一つのモジュラーモノリスパッケージの作成を計画しています。
弊社のパッケージの状況
2025年12月現在、119個のパッケージが存在します。これは他社の事例と比較しても、かなり多い数だと認識しています。それぞれのパッケージはチーム名のprefixが付いたパッケージ名で機能別に管理され、それぞれGitHubの機能を使ってCODEOWNERが設定されています。
外部APIが整理され、ルートパッケージ及び他のパッケージとほぼ完全に分離したモジュラーモノリスはそのうち1つだけです。
チームのメンバーが増え、チーム間のコミュニケーションコストを考えると、モジュラーモノリスもしくはマイクロサービスに切り出すことが重要です。
代替手段の検討
2025年現在、Packwerk自体を置き換えるほどのモジュラーモノリスのソリューションは存在しないようです。Rails内に小さなRailsプロジェクトを作成するRails Enginesは依然として代替手段として考えられますが、Packwerkの大きな使命である「依存性の検出」という面に関しては該当機能は存在しないようです。
その他はpacksというPackwerkをRustで実装し、高速に動作するようにしたものが存在するようです。今のところPackwerkのキャッシュがうまく機能しており速度面での問題は発生していませんが、速度に不満があるようであれば試す価値はあると思います。
まとめ
主にShopifyのレトロスペクティブを通して弊社のパッケージ化、モジュラーモノリス化の状況を振り返ってみました。
まとめると以下のような状況です。
- パッケージ化は進んでいるが、モジュラーモノリス化は進んでいない。
- パッケージ化によってパッケージのオーナーが明確になりコミュニケーションが取りやすくなった。
- マイクロサービスを見据えたモジュラーモノリス化が進んでいない。
こうして考えてみるとPackwerk=完全に独立したパッケージを設計開発するためのツールではなく、あくまでモジュラーモノリス化を助けるためのツールであることが再確認できます。そして、モジュラーモノリスを作り上げるためにはPackwerkを使うだけではなく既存の業務を分析し、新たにパッケージ設計を行うことが重要となります。
今後、弊社では2026年に向けて新たなモジュラーモノリスパッケージの作成を進めつつ、イベントストーミングを活用した本格的なアーキテクチャ改善に取り組んでいきます。
今回の調査と振り返りがPackwerkの導入を検討している・もしくは導入しているが今後の進め方に迷っている皆様の役に立てば幸いです。
弊社のPackwerkに関係する過去の記事
Discussion