Cyclic Package Dependencies
ID |
java.cyclic_package_dependencies |
Severity |
high |
Remediation Complexity |
hard |
Remediation Risk |
medium |
Remediation Effort |
high |
Resource |
Reliability |
Language |
Java |
Tags |
code-style, reliability |
Description
Reports cycles in the directed graph of in-project package dependencies. A dependency edge pkgA → pkgB is established when a compilation unit in pkgA imports a type from pkgB. When two or more packages mutually depend on each other (directly or transitively), they form a cycle that undermines modularity.
Rationale
Cyclic package dependencies make it impossible to use, test, or deploy one package without dragging in the other packages in the cycle. They increase compilation times, complicate refactoring, and indicate a tangled design:
// com.example.alpha
import com.example.beta.BetaService; // alpha -> beta
// com.example.beta
import com.example.alpha.AlphaService; // beta -> alpha (cycle!)
Cycles often signal that two packages should be merged or that a shared abstraction should be extracted into a third package.
Remediation
Break the cycle by:
-
Introducing an interface package — move shared interfaces to a separate package that both sides depend on.
-
Dependency inversion — have the lower-level package define an interface that the higher-level package implements.
-
Merging packages — if two packages are always used together, merge them.
// com.example.api (new shared interface package)
public interface Service { ... }
// com.example.alpha -> com.example.api (no cycle)
// com.example.beta -> com.example.api
References
-
Robert C. Martin, Clean Architecture, Chapter 14: Component Coupling
-
SEI CERT DCL60-J: Avoid cyclic dependencies between packages