refactor-module について
refactor-moduleは、広範な .plain モジュールを requires チェーンに再編し、各仕様の動作と順序を保つスキルです。分割計画、承認、移行、検証を案内します。
主な機能
- 01仕様と依存関係の分析
- 02意味に沿ったモジュール分割
- 03時系列順序の維持
- 04概念の配置とエクスポート
- 05テスト移行と検証
活用シーン
- 01無関係な機能を含むモジュールの分割
- 02安全なモジュール依存チェーンの計画
- 03動作を保った仕様の移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ plainlang/ plain-forge --skill refactor-moduleインストール後、Claude Code で/refactor-moduleを入力して読み込まれたか確認します。
詳細
モジュールのリファクタリング
まずルールを読み込んでください。 .plain ファイルの読み取り、編集、書き込みを行う前に、load-plain-reference スキルを呼び出し、案内されたルールファイルを読んでください。この会話ですでに load-plain-reference が実行されている場合に限り、これを省略できます。
使用するタイミング
- モジュールに、複数の異なるドメインや機能領域にまたがる機能仕様が多数ある。
- 関連のない関心事が混在しているため、モジュールの把握が難しい。
- モジュールの範囲が広すぎて、新機能を追加しにくい。
- ユーザーが明示的にモジュールの分割や再編成を求めている。
基本原則
リファクタリングは構造の変更のみです。リファクタリング後のモジュールの結合された動作は、元のモジュールと同一でなければなりません。機能仕様を追加、削除、または弱めてはいけません。分割は、関連する仕様をまとめる意味的なものであり、動作を変えるものではありません。
入力
リファクタリング対象の .plain ファイルと、希望するグループ分けについてのユーザーの指示(または、こちらが提案したグループ分けをユーザーに確認します)。
フェーズ 1 — 現在のモジュールを分析する
.plainファイル全体を読む — フロントマター、定義、実装要件、テスト要件、すべての機能仕様を読みます。また、コンテキスト全体を把握するために、importとrequiresの依存関係もすべて読みます。- 機能仕様を一覧にする。 各機能の
func-idを列挙し、参照している:Concepts:を記録します。依存関係マップを作成し、どの仕様が先行する仕様(そこで導入された動作や状態)に依存しているかを確認します。 - 定義を一覧にする。 各コンセプトについて、どの機能仕様が参照しているかを記録します。複数の論理グループで使われるコンセプトと、特定のグループ内でのみ使われるコンセプトを特定します。
- 論理グループを特定する。 機能仕様のまとまり、共有するコンセプト、一貫したドメイン、または単一の機能領域を説明する仕様群など、自然な分割点を探します。一般的なグループ分けの例:
- データモデルのセットアップと CRUD 操作
- ビジネスロジックと処理ルール
- UI 画面とナビゲーション
- 外部サービスとの連携とインターフェース
- 統計、レポート、分析
- グループの独立性を検証する。 各グループの仕様は、時系列順で連続した、またはほぼ連続したブロックを形成する必要があります。あるグループの仕様が散在し、他のグループの仕様と入り交じっている場合は、分割位置の調整が必要なことがあります。
フェーズ 2 — 分割を計画する
変更を行う前に、分割案をユーザーに提示し、明示的な確認を得てください。
2a. モジュールチェーンを定義する
モジュールは、機能仕様の元の時系列順を維持する requires チェーンを形成します。チェーンの最初のモジュールには最も早い仕様を含め、後続の各モジュールは直前のモジュールを requires し、次の論理グループを追加します。
original-module
↓ splits into
module-group-a (no requires — this is the chain root)
module-group-b (requires: module-group-a)
module-group-c (requires: module-group-b)
元のモジュールにすでに requires または import の依存関係がある場合は、最初の新しいモジュールがそれを引き継ぎます。
2b. 機能仕様を各モジュールに割り当てる
各機能仕様は、必ずいずれか 1 つのモジュールに配置してください。配置は時系列順を守る必要があります。
- 各モジュール内では、仕様の相対的な順序を元のモジュールと同じにします。
- チェーン全体でも順序を維持します。モジュール A のすべての仕様は、モジュール B のすべての仕様より前に置き、モジュール B のすべての仕様は、モジュール C のすべての仕様より前に置きます。
- ある仕様が依存する仕様より前のモジュールに移動することはできません。
2c. 定義を各モジュールに割り当てる
各コンセプトについて、配置先を決定します。
| コンセプトの使用範囲 | 配置先 |
|---|---|
| 1 つのモジュールの仕様内でのみ使用 | そのモジュールで定義する |
| チェーン内の複数のモジュールで使用 | 最初に使用するモジュールで定義し、そのモジュールの exported_concepts に追加する |
| すでに import テンプレートで定義済み | import に残す — 変更不要 |
多くのコンセプトをすべてのモジュールで共有する場合は、それらを格納する新しい import モジュール(テンプレート)の作成を検討してください。これにより、exported_concepts の一覧が長くなるのを避け、定義を一元管理できます。その場合は create-import-module スキルを使用してください。
2d. 実装要件とテスト要件を割り当てる
- すべてのモジュールに適用される要件は、共有インポートテンプレートに配置します。すでに存在する場合は、そのまま残します。
- 単一モジュールのドメインに固有の要件は、そのモジュールに移動します。
- 判断に迷う場合は、要件を共有テンプレートに残します。モジュール間で重複させるより、テンプレートが少し広範になるほうが望ましいです。
2e. 計画を提示する
ユーザーに以下を要約します。
- 分割後のモジュール数とその名前
- 各モジュールに含める機能仕様(func-id を列挙)
requiresチェーンの順序- 新たに作成するインポートモジュール
- 各モジュールからエクスポートされる概念
- 元の仕様がすべて反映されており、抜けがないことの確認
作業を進める前に、ユーザーから明示的な確認を得ます。
フェーズ 3 — 分割を実行する
チェーンのルートから順に、新しいモジュールを作成します。
3a. インポートテンプレートを作成または更新する(必要な場合)
共有の定義または要件を新しいインポートテンプレートに移動する場合は、create-import-module スキルを使って最初に作成します。
3b. チェーンのルートを作成する
チェーンの最初のモジュールを作成します。
- YAML フロントマターを設定します — 元のモジュールの
import参照を引き継ぎます。下流のモジュールで必要となる概念があれば、exported_conceptsを追加します。 - このモジュールの仕様で使用する概念と、エクスポートが必要な概念を含む
***definitions***セクションを追加します。 - このモジュール固有の
***implementation reqs***と***test reqs***を追加します(インポートテンプレートで完全にカバーされていない場合)。 - このモジュールに割り当てられた
***functional specs***を、元の順序と正確な文言を保ったまま追加します。 - 移動した機能のテスト用プレーンファイルの
module:キーを新しいモジュール名に設定し、ファイルをそのモジュールの標準的な場所に移動します。func-id とシナリオのテキストは変更しません。
3c. 後続のモジュールを作成する
チェーン内の後続モジュールごとに、以下を行います。
requiresをチェーン内の直前のモジュールに設定します。共有テンプレートを使用する場合はimportも設定します。- チェーン内の後続モジュールで必要となる概念があれば、
exported_conceptsを追加します。 - このモジュール内で定義する概念の
***definitions***を追加します。requires(エクスポート済み)またはimportによってすでに利用可能な概念を再定義しないでください。 - このモジュール固有の
***implementation reqs***と***test reqs***を追加します。 - このモジュールに割り当てられた
***functional specs***を、元の順序と正確な文言を保ったまま追加します。 - 移動した機能のテスト用プレーンファイルを、同じ方法で移動します。
3d. 元のモジュールファイルを処理する
新しいモジュールをすべて作成した後、以下を行います。
- 元のモジュールが完全に置き換えられる場合(すべての仕様が移動した場合)、混乱を避けるために削除するか、名前を変更します。
- 元のモジュールが他のモジュールから必要とされていた場合は、それらのモジュールを更新し、新しいチェーンの最後のモジュールを
requireするようにします。そこに蓄積されたコードと以前の機能仕様が、チェーン全体をカバーします。ただし、エクスポートされた概念は推移的ではありません。下流のモジュールが新しいチェーン内の前のモジュールからエクスポートされた概念を参照する場合は、その前のモジュールもrequiresに列挙する必要があります。
フェーズ 4 — 検証する
4a. 完全性チェック
これは最も重要な検証です。元のモジュールの機能仕様をすべて確認し、それぞれが新しいモジュールのちょうど1つに含まれていることを確かめます。何も失われたり、弱められたり、変更されたり、暗黙のまま残されたりしてはなりません。
チェックリスト:
- 新しいすべてのモジュールに含まれる仕様の合計数が、元の仕様数と一致する
- 各仕様のテキストが元の内容と完全に一致し(言い換えなし)、func-id も保持されている
- 各機能のテスト用プレーンファイルが機能とともに移動され、
module:が更新され、シナリオもそのまま保たれている
4b. 時系列順序のチェック
requires チェーンをルートから末端までたどってすべての機能仕様を読んだとき、時系列順序が元の順序と一致することを確認します。ある仕様が、それに依存する仕様より先に現れてはなりません。
4c. 概念の利用可否チェック
各モジュールについて、次の項目を確認します。
- 仕様で参照されているすべての
:Concept:が、ローカルで定義されているか、import経由で利用可能か、またはrequiresに記載されたモジュールのexported_concepts経由で利用可能である -
exported_conceptsに、下流モジュールが必要とするすべての概念が含まれている - モジュール間で概念名が重複していない
4d. モジュールごとの構造検証
新しい各モジュールについて、次の項目を確認します。
- 機能仕様が少なくとも1つ、実装要件が少なくとも1つある
- YAML フロントマターが正しく記述されている
-
requiresとimportのパスが正しい - モジュールファイルがリポジトリのルートにある(
template/内ではない) - 仕様が言語に依存しない
- すべての外部インターフェースが明示されている
4e. 新しいモジュールをすべて読む
新しい各 .plain ファイルを最後まで読み、最終的な構造をユーザーに提示して承認を得ます。ユーザーから変更の依頼があった場合は、変更を適用して再検証します。
よくある落とし穴
細分化しすぎる
仕様が1~2個だけの小さなモジュールを多数作成すると、明確さが増すことなくオーバーヘッドが増えます。テーマに一貫性を持たせてグループ化し、モジュールごとに機能仕様が3~10個になるようにします。
時系列順序を崩す
エントリーポイント、データベースの初期化、コア CRUD など、基盤となる動作を設定する仕様は、複数のドメインの概念に関係していても、チェーンのルートに維持する必要があります。仕様を明確に分離できる場合にのみ分割します。
exported_concepts を忘れる
モジュール A で定義された概念が、モジュール A を requires するモジュール B の仕様で参照される場合、その概念を A の exported_concepts に含める必要があります。エクスポートが不足すると、レンダリングに失敗します。
分割時に仕様テキストを変更する
これはリファクタリングであり、書き直しではありません。仕様テキストはそのままコピーする必要があります。仕様を新しいモジュールのコンテキストに合わせて言い換える必要がある場合は、分割境界が適切でないことを示しています。その場合はグループ分けを調整します。
テスト用 plain ファイルを失う
テスト用 plain ファイルは、module: と func_id: によって機能に紐付けられます。機能を新しいモジュールに移動すると、その module: キーが変わり、ファイルも新しいモジュールの慣例に沿った場所へ移動します。そうしないと、レンダラーはどの機能にも一致しないペアとして報告します。
検証チェックリスト
- 実行前にユーザーが分割計画を確認した
- 元の機能仕様がすべて、いずれか1つの新しいモジュールに含まれている(失われたものがない)
- 仕様テキストが元のものと同一である(言い換えがない)
- テスト用 plain ファイルが機能とともに移動され、新しいモジュールに再紐付けされている
-
requiresチェーン全体で時系列順序が保たれている - 参照されるすべての
:Concept:が各モジュールで利用可能である(ローカル、インポート、またはエクスポート経由) - 各モジュールの
exported_conceptsが正しく設定されている - 概念名が重複していない
- 各モジュールに機能仕様が少なくとも1つ、実装要件が少なくとも1つある
-
requiresチェーンが正しい順序になっている(ルート → リーフ) - 元のモジュールが削除または更新されている
- 下流モジュール(存在する場合)が、チェーンの最終モジュールに加え、参照するエクスポート済み概念があるチェーン上の先行モジュールも
requiresするよう更新されている - ユーザーが最終結果を承認した