Guided Tutorials
Break a Monolith into Modules
Decompose a large, tangled codebase into focused modules with clear interfaces and dependency boundaries. This tutorial extracts one module at a time so the codebase stays working throughout the process.
What you'll learn
- How to analyze a codebase dependency graph to find natural module boundaries
- How to extract a tightly coupled cluster into a module with a clean public API
- How to update all consumers and enforce the module boundary
- How to iterate the extraction process across the remaining codebase
Before you start
- Autohand Code installed. Run
autohand --versionto confirm. See Your First Autohand Session if you need to install it. - A working test suite. At minimum, integration tests that exercise the main paths through the code. You will run these after every extraction.
- Git with a clean working tree. Each extracted module should be a separate commit so you can roll back individual extractions.
- A codebase you understand at a high level. You should know what the main responsibilities are (auth, billing, notifications, etc.).
Analyze dependencies
Before extracting anything, you need to understand the actual dependency structure. Gut feeling about what belongs together often does not match what the code actually does.
The output will look something like this:
This output tells you which cluster is the safest first extraction. A cluster with high internal cohesion and a small, well-defined set of external dependencies is the right place to start.
Identify module boundaries
Based on the dependency analysis, ask Autohand to propose a clean boundary for the first module. The goal is to find a boundary that minimizes what needs to change in the rest of the codebase.
A good proposed API looks like this:
The AuthDependencies interface is important. It makes the module's external requirements explicit and injectable, rather than having the module reach out and grab them directly.
Extract the first module
With the boundary agreed on, ask Autohand to do the extraction. Be explicit about the target directory and the files to move.
Autohand will:
- Move the files to the new location
- Update all relative imports within those files
- Create the
index.tswith only the public exports - Update all callers in the rest of the codebase to import from
src/modules/authinstead of the old paths
Tip: Do the file move and the API cleanup as two separate steps. Move first, update imports, run tests. Then clean up the public API. This makes each step easy to validate independently.
Define the public interface
After the files are moved, enforce the public API boundary. The module's index file should only export what external code should use.
After this step, a good src/modules/auth/index.ts has no implementation details leaking through:
The internal files like hash.js and the email templates are not exported. Code outside the module cannot import them directly, which enforces the boundary.
Update consumers
The rest of the codebase was importing directly from the old paths. Autohand updated these during the extraction, but verify the update was complete.
If any are found, update them:
The middleware that validates tokens is usually the trickiest consumer because it runs on every request. Pay special attention to it:
Verify everything still works
Run the full test suite after the extraction is complete.
If tests pass, commit the extracted module as a single clean commit:
If tests fail, ask Autohand to help diagnose:
Extract the next module
Once the auth module is stable, repeat the process for the next cluster from your dependency analysis. Each extraction is easier than the last because you now have a working pattern and a cleaner codebase to work with.
After two or three extractions, look at what is left in the old directories. Often a large portion of the monolith has been moved, and what remains is thin glue code or the application entry point itself.
What you learned
- Analyzed a codebase dependency graph to identify tightly coupled clusters
- Extracted the auth cluster into a standalone module with an explicit public API
- Updated all consumers to import from the module and verified tests still pass
- Established a repeatable pattern for extracting additional modules from the monolith