Reorganize messy code into well-structured packages through systematic analysis.
Core: Package design is not a matter of "where to place source code" but a design problem of "where to stop the waves of change" and "how to control the direction of dependencies".
Core Workflow
Phase 1: Analyzing Reasons for Change - Finding the Starting Point for Splitting
The starting point for splitting is not "procedural steps" but "design decisions likely to change".
Based on Parnas' information hiding, first ask the following:
- "What changes?"(Enumerate reasons for change and sources of change)
- "When it changes, how far may it involve other things?"(Release unit and responsibility boundaries)
- Analyze the change history (git log) of the target code and identify groups of files that are changed together
- Organize the scope of impact for each external factor (UI changes, DB changes, API changes, business rule changes)
- For each reason for change, tentatively place boundary candidates such that "this change should be closed here"
Output: A correspondence table of reasons for change and scope of impact
Phase 2: Chunk Down (Decomposition) - Identifying Responsibilities
Extract atomic responsibilities from the target code:
- List all public types, functions, and traits
- Write a "one-sentence responsibility description" for each element
- Identify implicit responsibilities (error handling, logging, configuration, etc.)
- Flag elements with multiple responsibilities (SRP violations)
Output: A list of 10 to 50 atomic responsibilities
Phase 3: Grouping - Creating Candidate Packages
Select axes for splitting responsibilities and group them.
Selecting Split Axes
| Split Axis | Nature of Cohesion | Suitable Cases | Risk |
|---|---|---|---|
| Feature (feature/vertical) | Changes close vertically | Team independence, microservice candidates | Commonization hell |
| Domain (business concept) | Informational cohesion | Emphasis on consistency of the domain model | Translation cost between contexts |
| Layer (technical layer) | Separation of technical responsibilities | Small scale, early adoption | One change scatters across all layers |
| Responsibility (reason for change) | CCP compliant | Change frequency is clear | Initial analysis cost |
| API boundary (public interface) | Minimize surface area | Library design | Balance with internal flexibility |
Grouping Heuristics
- Elements that change together → same package (CCP)
- Elements reused together → same package (CRP)
- Boundaries of domain concepts → natural package boundaries
Output: 3–7 candidate packages (7±2 is a guideline from the perspective of cognitive load)
Validation by MECE (as a checking perspective, not a design goal)
Use MECE not as a "design purpose" but as an "aid for completeness checking". If you stick too strictly to MECE, there is a risk that boundaries become blurred in handling cross-cutting concerns (logging, authorization, transactions, etc.). 80–90% coverage is sufficient.
- Is each responsibility assigned to exactly one package (no duplication)?
- Are there no unassigned responsibilities left (no omissions)?
- Is there exactly one answer to "Where should X go?"
For details, see references/principles.md#mece-分割 .
Phase 4: Chunk Up - Abstraction and Naming
Abstract each group by one level and name it:
- Find the concept that runs through each group
- Name by domain concept, not by technical role
- Confirm that you can state the purpose of that package in one sentence
- If naming is difficult, the grouping is likely wrong → return to Phase 3
Good example: authentication, billing, inventory
Examples to avoid: utils, helpers, common, misc
Phase 5: Dependency Design and Principle Validation
Design the direction of dependencies
Direct dependencies toward the "more stable, more abstract" side.
- Draw the dependency graph between packages
- Check for circular dependencies (ADP)
- Check whether dependencies are reversed from the stable side (depended on by many) to the unstable side (depends on many) (SDP)
- Check whether stable packages are sufficiently abstract (SAP)
Procedure for resolving circular dependencies
If a cycle is found:
- Dependency inversion: Move the abstraction (Interface/Trait) of the depended-on side to the depending side, and inject the implementation in the reverse direction
- Shared extraction: Move mutually referenced common types/logic to a third package and cut the cycle edge
- Integration: If they truly are the same responsibility, integrate the packages
Principle check
| Principle | Check perspective |
|---|---|
| High cohesion | Do the elements within the package converge on a single purpose? |
| Low coupling | Are dependencies between packages minimized? |
| ADP | Are there no circular dependencies? |
| SDP | Are dependencies directed toward the stable side? |
| SAP | Are stable packages sufficiently abstract? |
| CCP | Whether changes with the same reason for change are confined to the same package |
For details, see references/principles.md .
Phase 6: Defining Public Interfaces and Test Boundaries
Public Interfaces
For each package:
pubIdentify the elements that should be (external contracts)- Everything else should be
pub(crate)make it private - If the interface is complex, provide facade types/functions
- Explain the contract in module documentation
Mapping Test Boundaries
Map tests to "boundaries":
| Test Type | Target | Purpose |
|---|---|---|
| Unit Test | Package internals | Preserve internal cohesion |
| Integration tests | Between packages | Whether cross-boundary dependencies are as designed |
| Contract tests | Public API boundaries | Preserve compatibility (integration points) |
Evaluation Checklist
Check whether the proposed structure satisfies the following (the more Yes answers, the higher the cohesion and lower the coupling):
- The purpose of each package can be stated in one sentence (what it provides and what it hides)
- Dependencies that cross boundaries go through public APIs (facades/ports)
- The package dependency graph is acyclic
- Highly stable areas are sufficiently abstracted
- Reasons for change stop at package boundaries
- Boundary tests match the scope of the public API
Quantitative Evaluation Using Metrics
| Metrics | Target | Meaning |
|---|---|---|
| Ca (afferent coupling) | Package | Number of external dependents |
| Ce (efferent coupling) | Package | Number of external dependencies |
| I = Ce/(Ca+Ce) | Package | Instability (0 = most stable, 1 = most unstable) |
| D (distance from the Main Sequence) | Package | Deviation from A + I = 1 |
For details, see references/principles.md#パッケージメトリクス .
Refactoring Triggers
If the following are observed, consider reviewing the split/boundaries:
- Changes are scattered across multiple packages (a sign of CCP violation)
- Ce increases (external dependencies keep growing)
- Package cycles (circular dependencies) occur
- Complexity within a package keeps increasing
Rust-Specific Patterns
references/rust-patterns.md See:
modHierarchical design- Choosing between a workspace and a single crate
- Feature flag strategy
- Guidelines for re-exports
※ In this project Do not use mod.rs. With the 2018 module system, package_name.rs と package_name/ Configure with files under
Comparison of Representative Patterns
| Pattern | Cohesion axis | Advantages | Risk |
|---|---|---|---|
| Layered | Technical responsibility | Easy to introduce | 1 change is scattered across all layers |
| By feature (vertical slicing) | Use Case | Changes are easy to close | Commonalization hell |
| By component | Feature set | Clear boundaries | Public API design cost |
| Bounded Context | Domain boundaries | Model consistency | Inter-context translation cost |
| Ports & Adapters / Clean | Dependency direction | Testability | Structural formalism |
Anti-pattern
- God module: Responsibilities exceeding 500 lines are concentrated in 1 file
- Circular dependency: A → B → C → A (a signal that boundaries have effectively collapsed)
- Unstable dependency: Core modules depend on modules that change frequently (SDP violation)
- Leaky abstraction: Internal types leak into the public API
- Miscellaneous util/common: Loosely related elements are aggregated under "other" (a breeding ground for common coupling)
- Layers only, no boundaries: Even if it looks layered, cross-references and internal references are left unchecked
- cargo cult packaging: Patterns are applied without understanding the reasons, and although the structure looks organized, the intent is lost
Output format
Restructuring proposals are presented in the following format:
## 提案パッケージ構成
package_name.rs (目的: 1文で説明)
package_name/
├── submodule_a.rs
└── submodule_b.rs
### 依存関係
package_a → package_b (reason)
### メトリクス概算
package_a: Ca=3, Ce=1, I=0.25 (安定)
package_b: Ca=1, Ce=2, I=0.67 (不安定)
### 移行手順
1. 新しいモジュール構造を作成する
2. 型や関数を最小変更で移動する
3. import を更新する
4. テストが通ることを確認する
5. 循環依存がないことを静的解析で確認する
Related skills (recommended for co-reading)
When using this skill, also refer to the following skills:
ddd-module-pattern: Domain vocabulary-based module design in a DDD contextrefactoring-packages: Executing refactoring of existing package structuresclean-architecture: The 4-layer architecture that forms the foundation of package design