Most teams modularizing a project eventually create modules named Common, Core, or Utils.
As decisions like “it’s used in several places, so put it here for now” accumulate, this module quietly becomes the project’s largest, most frequently changed, and most depended-on module.
The short version: naming a module Common is effectively admitting that it has no cohesion. “Shared” is not a criterion for grouping code.
This article explains why Common modules undo modularization and summarizes layer structure (vertical separation) and feature structure (horizontal separation) to prevent it.
Why are Common modules dangerous?
The problem develops in three stages.
Stage 1: becoming a dumping ground. Any “code with no obvious home” goes into Common. A network client sits beside a date formatter, next to a custom button. They have nothing in common.
Stage 2: universal dependency. Every feature module imports Common. At this point, Common occupies the graph’s most downstream position—the place that should be the most stable.
Stage 3: rebuilding everything. Because Common is a dumping ground, it changes most often. The stable dependencies principle from the previous article has been completely reversed. Change one button color, and every module rebuilds.
Once the most depended-on module also changes most often, module boundaries become meaningless.
Layer structure: divide vertically
The first preventive axis is vertical separation by responsibility. A typical three- or four-layer structure looks like this.
| Layer | Contents | Examples |
|---|---|---|
| Feature | Screen/feature modules | Home, Search, Orders, Settings |
| Domain | Business rules, models, use cases | Order policies, member models |
| Core | Thin wrappers for specific technologies | Networking, storage, logging |
| Shared | Truly general-purpose utilities | Date formatters, string extensions |
There is one rule: dependencies flow only from top to bottom.
Feature may use Domain and Core, but Core must not know about Feature. Features also avoid direct references to one another; the parent app target assembles them.
The important distinction from Common is this: Core is not one giant module, but several modules split by technology.
- For example, CoreNetwork, CoreStorage, and CoreLogging are independent modules.
- If Search uses only logging, it imports only CoreLogging.
Then fixing networking does not rebuild a module that only uses logging.
Feature structure: divide horizontally
The second axis is horizontal separation by domain. Even within the same layer, Home, Search, and Orders should be separate modules.
The criterion is the question from the first article: “Do these pieces of code change for the same reason?”
- If changing search policy will not require changing order code, they are different modules.
- If the order screen and order use case always change together, splitting them further vertically may be excessive.
Overlay vertical (layer) and horizontal (feature) divisions, and you get a grid. A real module occupies one cell, such as “Order Domain” or “Search Feature.”
What if Common has already grown too large?
You do not need to blow up the existing Common all at once. A field-tested sequence is:
- Stop new additions: first establish a rule that no new code enters Common from today onward.
- Trace usage: starting with the code that changes most often, find where it is actually used.
- Find the right home: move code used by one feature into that feature module, and technology wrappers into a Core module.
- Rename what remains: isolate only truly general-purpose code left at the end under a narrow name such as Shared.
It takes months, but at each stage you can visibly confirm that the rebuild scope is shrinking.
When is this structure necessary, and when is it overkill?
- With 4–5 or more developers and at least 3 feature areas: layer rules are well worth establishing.
- For a 1–2 person project: starting with two layers, Feature/Shared, is enough. Drawing the full grid is overkill.
- At any scale: adopt the rule “never create modules named Common or Utils” from the beginning.
This is how I ask about it in interviews
Q. What problems arise when a shared module (Common) grows, and how should it be designed?
The module everyone depends on becomes the one that changes most often, so even small edits require a full rebuild and broad impact analysis. The stable dependencies principle has been reversed. Split modules finely by responsibility—networking, storage, and logging—and isolate only truly general-purpose code in a minimal Shared module.
Q. Explain the rules for dividing a module structure into layers.
Define responsibility-based layers such as Feature, Domain, and Core, and allow dependencies in only one direction, from top to bottom. Modules in the same layer must not reference one another directly; connect them in the higher-level assembly layer. When this rule holds, the impact of changes is confined to the layers above.
That concludes this three-part introduction to modularization principles. Once you have boundaries, dependency direction, and layer structure, you have the conceptual tools you need.
From the next article, we apply these principles directly to iOS projects, starting with how to split modules using Swift Package.

![Cover image for [Modularization #3] Why Common Modules Become Dumping Grounds](/assets/images/posts/5485c078-a71f-4122-920c-335272b4db96/1.jpg)