Fact-checked by Grok 2 weeks ago

Circular dependency

A circular dependency in refers to a between two or more modules, classes, or program units that depend on each other either directly or indirectly, forming a in the . This arises when, for instance, module A requires definitions or functionality from module B, while module B simultaneously requires those from module A, preventing independent compilation or execution. Such dependencies are common in object-oriented designs and , where they manifest as bad smells that degrade and reusability. Circular dependencies pose significant challenges during and maintenance, as they can trigger errors in build tools like , complicate testing by requiring combined module evaluation, and increase that hinders . In languages such as C++, they often lead to compilation failures due to unresolved references, whereas may tolerate them at runtime. Beyond core programming, circular dependencies appear in related areas like goal-oriented requirements modeling, where mutually dependent goals create cycles that undermine model quality and validation. To mitigate circular dependencies, developers employ strategies such as introducing interfaces or abstract classes to break cycles, refactoring operations to a single owning module, or restructuring components into uni-directional relationships. Detection tools, including search-based algorithms like , help identify these cycles in large codebases or models, enabling proactive resolution. In aspect-oriented contexts, treating cyclically dependent aspects as a unified group or transforming them into orthogonal units preserves functionality while reducing tight . Overall, avoiding or eliminating circular dependencies promotes cleaner, more maintainable architectures in complex software systems.

Fundamentals

Definition

A circular dependency, also referred to as a cyclic dependency, occurs when two or more entities—such as modules, components, or processes—in a depend on each other, either directly or indirectly, forming a cycle in the that hinders independent resolution or initialization. This cycle creates tight coupling between the entities, violating principles like the Acyclic Dependencies Principle, which advocates for dependency structures that allow for modular design and easier maintenance. Key characteristics of circular dependencies include their manifestation as cycles in directed graphs: direct cycles involve mutual dependencies between two entities (e.g., entity A depends on B, and B on A), while indirect cycles arise through a chain of dependencies (e.g., A depends on B, B on C, and C on A). In contrast, acyclic dependencies form a (DAG), where no such cycles exist, enabling straightforward ordering and processing of entities. Understanding circular dependencies requires familiarity with prerequisite concepts in applied to . A is a where nodes represent system entities and edges denote directional dependencies from one entity to another it relies upon. is an that linearizes the nodes of a DAG such that for every directed edge from node U to V, U precedes V in the ordering, facilitating tasks like build order resolution or task scheduling; however, the presence of cycles renders topological sorting impossible, as no valid linear order exists.

Basic Examples

A classic everyday analogy for a circular dependency is the "chicken-or-egg" , where the must exist to lay the , but the must exist to hatch the , creating an interdependent with no clear starting point. This illustrates how circular dependencies can stall progress by requiring mutual prerequisites that cannot be resolved sequentially. In a simple technical context, consider two software or packages where A requires functionality from B to operate, while B similarly requires A; for example, package com.example.a imports com.example.b, and com.example.b imports com.example.a. This mutual reliance forms a during or loading, preventing the system from initializing either first, as each awaits the other's completion. Such relationships are often represented in a , where nodes denote components (e.g., modules A and B) and directed edges show dependencies (e.g., A → B means A depends on B). A non-circular, linear dependency might appear as:
A → B
This allows for resolution, starting with B then A. In contrast, a circular dependency forms a :
A → B
↑   ↓
B ← A
This , akin to the formal definition of a dependency containing a , defies linear processing and requires intervention to break.

Applications in

In Programming and Modules

In , a circular dependency arises when two or more classes mutually depend on each other, such as class A requiring an instance of class B in its constructor and class B requiring an instance of class A, which can result in infinite recursion during object instantiation or at runtime. Empirical studies of applications indicate that such cycles are prevalent, with 45% of sufficiently large programs containing cycles involving at least 100 classes, often signaling design issues rather than inherent necessities. These cycles increase tight , complicating maintenance and testing by propagating changes across dependent classes. Specific behaviors vary by programming language. In Java, circular dependencies at the class level are tolerated during class loading due to lazy initialization, but package-level cycles—where packages mutually import classes from one another—are strongly discouraged, as they form non-directed acyclic graphs, leading to fragile builds, reduced reusability, and potential runtime errors like ClassNotFoundError. For instance, if package account imports from package user and vice versa, refactoring one package may require simultaneous changes to the other, hindering independent development. In Python, mutual import statements between modules can trigger an ImportError if one module attempts to access attributes from the other before the importing module is fully executed, as the import system adds partially initialized modules to sys.modules to avoid unbounded recursion but raises exceptions for undefined attributes. An example is two modules foo.py and bar.py where foo imports bar and immediately uses a bar attribute, while bar imports foo; this fails with ImportError: cannot import name 'attribute' from partially initialized module 'foo'. In C++, circular inclusions between header files cause compilation failures due to infinite expansion loops, unless mitigated by forward declarations for pointers or references, which allow incomplete type usage without full definitions. For example, if A.h includes B.h and B.h includes A.h, the compiler reports errors like "invalid use of incomplete type"; resolving this involves declaring class B; in A.h and using B* instead of full objects. Module systems in package managers also address circular dependencies through detection and resolution mechanisms. In with , the module system permits cyclic dependencies by returning a partially constructed exports object during require() calls, preventing infinite loops while allowing modules to complete loading sequentially. For instance, if module A requires B and B requires A mid-execution, B receives A's unfinished exports, but once B finishes, A continues, ensuring both are usable afterward—though this requires careful design to avoid accessing uninitialized properties. , for projects, detects cyclic dependencies during transitive resolution and reports them as build errors, enforcing acyclic graphs to maintain ; the Maven Enforcer Plugin's banCircularDependencies rule explicitly scans direct and transitive dependencies across specified scopes, failing the build if cycles are found. This configuration might specify scopes like compile and test to ignore optional dependencies while flagging cycles in core artifacts.

In System Design and Architecture

In architectures, circular dependencies arise when one service relies on another for functionality, and the second service reciprocally depends on the first, often manifesting as synchronous calls that form a , such as Service A invoking Service B, which then calls back to Service A. This can lead to deployment or deadlocks, where independent of services becomes impossible due to the intertwined nature of their operations, complicating fault and overall . To mitigate such issues, architects often refactor by introducing asynchronous messaging or shared data layers to break the without merging services. Circular dependencies also violate core principles in established , particularly in layered architectures where the dependency rule mandates that outer layers (e.g., ) depend only on inner layers (e.g., and data access), preventing loops such as the data layer indirectly depending back on the presentation layer through shared interfaces. In dependency injection frameworks like , these cycles occur when beans form mutual references—Bean A injected into Bean B, and vice versa—potentially halting application startup if resolved via constructor injection; addresses this by favoring setter injection or to allow partial bean creation before full wiring. Such violations undermine modularity, as they blur boundaries intended by patterns like MVC or Clean Architecture. In environments, circular references frequently appear in database schemas through s that create cycles, for instance, A referencing B via a while B references A, which enforces but complicates data insertion and deletion by requiring deferred constraint checks until transaction commit. Similarly, in cloud architectures, dependencies can form cycles when services in platforms like AWS expose endpoints that mutually invoke each other, or when infrastructure-as-code tools like CloudFormation define resources with reciprocal references (e.g., an EC2 instance depending on a security group that depends back on the instance), stalling deployments until the cycle is broken via explicit ordering or resource refactoring. These patterns extend module-level cycles observed in programming but amplify risks at scale due to distributed and failure propagation.

Consequences

Technical Challenges

Circular dependencies often manifest as build-time issues, where the compilation or linking process fails due to unresolved references among interdependent modules. In C++, for instance, circular dependencies between static libraries can lead to linker errors, as the linker requires a specific order to resolve symbols but cannot determine it when libraries depend on each other mutually, resulting in undefined reference complaints. Similarly, in projects using bundlers like , circular imports can trigger errors or warnings during the bundling phase, where modules are partially resolved, leading to failures in generating complete bundles or runtime-undefined variables if not detected. At runtime, circular dependencies can precipitate severe problems such as infinite loops during object initialization or stack overflows from recursive invocations. In dependency injection frameworks like , constructor-based circular dependencies cause the container to enter an endless creation cycle—attempting to instantiate Bean A requires Bean B, which in turn requires Bean A—resulting in a BeanCurrentlyInCreationException and preventing application startup. This recursion can escalate to stack overflows in languages like .NET, where the service provider recursively resolves services in a loop during startup, exhausting the call stack without explicit cycle detection. Testing circular dependencies introduces significant complications, as isolating components for unit tests becomes infeasible without resolving the cycle, often forcing reliance on integration tests or brittle mocks that replicate the entire . In Spring applications, for example, unit tests fail to load the application context due to the same BeanCurrentlyInCreationException encountered at , requiring additional annotations like @Lazy or redesigns to enable test execution, which undermines test isolation and reliability. This makes mocking individual dependencies challenging, as the cycle prevents independent instantiation, leading to fragile tests that mirror production behavior rather than verifying isolated logic.

Performance and Maintainability Issues

Circular dependencies introduce tight among components, leading to degradation primarily through increased resource demands and inefficient processing. In large-scale software projects, such as those developed in C++, cyclic physical dependencies result in excessive compile-time and link-time overhead, as modifications to one component trigger unnecessary recompilations across the entire cycle, significantly prolonging build times. For instance, in empirical studies of systems like and TeaStore, refactoring cyclic dependencies reduced method-level execution time by up to 47% and service-level memory consumption by up to 20%, with statistically significant improvements observed in 50-80% of methods via large effect sizes in Mann-Whitney U-tests. These inefficiencies stem from the heightened interdependencies that amplify computational costs during both build and phases. Maintainability suffers due to the unpredictable propagation of changes within cycles, rendering refactoring efforts risky and error-prone. Modifications in one module can cascade through the cycle, causing severe and unforeseen effects on dependent components, which complicates understanding the system's and modularization. Empirical evidence from open-source systems like shows that while classes directly in cycles are not inherently more change-prone, those adjacent to cycles exhibit a 76.3% involvement in 94% of overall changes, elevating costs by increasing the probability of . This tight also impairs and reusability, as components cannot be isolated or independently evolved without affecting the cycle, a pattern observed across multiple quality sub-characteristics in architectural analyses. In evolving systems, circular dependencies impose scalability limits by obstructing parallel and team collaboration, as intertwined components deter independent workstreams and foster accumulation. As software grows, these cycles exacerbate coordination challenges, with industrial case studies indicating that unresolved architectural smells like cyclic dependencies hinder long-term evolvability and increase the effort required for system expansion. This buildup of debt manifests in reduced , where teams face heightened risks during and deployment in distributed environments.

Detection and Mitigation

Identification Techniques

Static analysis techniques parse or to construct dependency graphs and identify cycles without executing the program. These methods are particularly useful in for early detection during build or linting processes. Tools such as JDepend for analyze package dependencies by traversing databases, reporting cyclic dependencies as a key design quality metric. Similarly, Madge for generates visual graphs of module dependencies and explicitly flags circular ones using command-line or programmatic interfaces. In , detects cyclic imports between modules during static code analysis, issuing refactor warnings for top-level import cycles. Graph-based algorithms provide a foundational approach to in directed graphs, where s represent components and s denote dependencies. (DFS) traverses the graph, marking s as visited; a back to a in the current indicates a . Alternatively, attempting a topological sort, such as Kahn's algorithm, processes s with zero in-degree iteratively; failure to order all s reveals a due to unresolved dependencies. Dynamic detection occurs at by tracing execution paths in live systems, especially distributed ones, to uncover circular calls that may not manifest statically. In architectures, tools like Jaeger and Zipkin collect and visualize distributed traces, allowing identification of call loops through inspection of span dependencies and latency patterns. This approach complements static methods by revealing context-specific cycles, such as those induced by conditional logic or external integrations.

Resolution Approaches

One effective refactoring technique for breaking circular dependencies involves introducing interfaces or abstract layers to decouple interdependent modules. By defining stable abstractions that both high-level policies and low-level details depend on, direct concrete dependencies are eliminated, allowing independent development and testing of components. For instance, in systems with mutual class references, extracting shared functionality into a third abstract entity or using method extensions can resolve cycles while preserving behavior, as demonstrated in case studies on frameworks like Seaside where 55% of proposed refactorings were adopted. Event-driven patterns, such as publish-subscribe, further support by replacing synchronous direct calls with asynchronous notifications through a , preventing tight in distributed or modular systems. In languages supporting incomplete types, like C++, forward declarations enable the use of pointers or references to classes without including their full definitions in headers, thus avoiding compilation-time circular includes. defers instantiation of dependent objects until needed, reducing runtime coupling in environments like or .NET where eager initialization might trigger cycles. Applying design principles like the principles, particularly dependency inversion, promotes acyclic architectures by ensuring abstractions govern dependencies rather than concretions. Tools for automatic untangling, such as those reorganizing components based on architectural metrics, can systematically eliminate cycles of any size with minimal structural changes, as validated in open-source and industrial applications. To prevent circular dependencies, best practices include enforcing acyclic structures through rigorous code reviews that scrutinize inter-module references and automated (CI) checks using architecture enforcement libraries. Designing modular monoliths with explicit boundaries—such as layered packages where dependencies flow unidirectionally—further mitigates risks by isolating concerns and facilitating early detection during builds.

References

  1. [1]
    [PDF] The Twenty-Five Most Common Mistakes with Real-Time Software ...
    Each circular dependency (a cycle in the graph) reduces the ability to reuse the software module. Testing can only occur for the combined set of dependent ...
  2. [2]
    Object-oriented analysis
    Circular dependency occurs when an operation depends on more than one object in the primary visibility matrix. Circular dependency is a problem because program ...
  3. [3]
    [PDF] On Composition and Reuse of Aspects
    Circular dependency is the strongest form of depen- dency. It occurs when several aspects are mutually depen- dent. The simplest form is encountered when two ...
  4. [4]
    26. Eliminating Circular Dependency - University of Iowa
    A strategy for Eliminating Circular Dependencies. Consider the following skeleton describing one circular dependency in our road-network simulator: class Road { ...Missing: engineering | Show results with:engineering
  5. [5]
  6. [6]
    Cyclic Dependency - an overview | ScienceDirect Topics
    Cyclic dependencies refer to a situation in software systems where two or more abstractions refer to each other, creating a circular relationship.
  7. [7]
  8. [8]
    [PDF] Engineering Software Development in Java - University of Maryland
    Definition of Graph Dependency. A dependency graph is a directed graph representing dependencies among objects. E. F. C. B. A.
  9. [9]
    Topological Sort of Directed Acyclic Graph | Baeldung on Computer ...
    Mar 18, 2024 · 2. Topological Sort. In many applications, we use directed acyclic graphs to indicate precedences among events. For example, in a scheduling ...
  10. [10]
    [PDF] Constructive Formal Control Synthesis through Abstraction and ...
    Aug 16, 2019 · Thus we run into a circular dependency resembling the problem of “the chicken or the egg”. To prevent controllers from inadvertently causing ...
  11. [11]
    Cyclic Dependency Removal
    A cyclic dependency in such a graph is a cycle, and these prevent topological sorting. An example of cyclic dependency: Chicken depends on Egg. Egg depends ...Missing: circular | Show results with:circular
  12. [12]
    An empirical study of cycles among classes in Java
    In this paper we present the first significant empirical study of cycles among the classes of 78 open- and closed-source Java applications. We find that, of the ...
  13. [13]
  14. [14]
    5. The import system — Python 3.14.0 documentation
    The import statement combines two operations; it searches for the named module, then it binds the results of that search to a name in the local scope.
  15. [15]
    Headers and Includes: Why and How - C++ Forum
    May 2, 2009 · A circular dependency is when two (or more) classes depend on each other. For example, class A depends on class B, and class B depends on class ...
  16. [16]
    CommonJS modules | Node.js v25.1.0 Documentation
    The bar package may itself have dependencies, and in some cases, these may even collide or form cyclic dependencies. Because Node.js looks up the realpath ...ECMAScript modules · OS · Node:module API
  17. [17]
    Introduction to the Dependency Mechanism - Apache Maven
    Maven's dependency mechanism manages dependencies, including transitive dependencies, and helps define, create, and maintain reproducible builds.
  18. [18]
    Ban Circular Dependencies – Extra Enforcer Rules - MojoHaus
    This rule checks the dependencies and fails if the groupId:artifactId combination exists in the list of direct or transitive dependencies.<|control11|><|separator|>
  19. [19]
    The vicious cycle of circular dependencies in microservices
    Nov 17, 2020 · What are circular dependencies? A circular dependency is defined as a relationship between two or more application modules that are codependent.
  20. [20]
    Decomposition in microservice architectures - Thom's Blog
    Mar 12, 2022 · If you have a circular dependency between two services, consider whether their domains are tightly coupled enough that they should be ...
  21. [21]
    The Dependency Rule | Khalil Stemmler
    Mar 28, 2020 · A software architecture rule that specifies the relationship between layers, namely that an inner layer should never rely on anything from an outer layer.<|separator|>
  22. [22]
    Dependency Injection :: Spring Framework
    Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other ...<|separator|>
  23. [23]
    Circular Dependencies in Spring | Baeldung
    May 11, 2024 · A circular dependency occurs when a bean A depends on another bean B, and the bean B depends on bean A as well: Bean A → Bean B → Bean A.
  24. [24]
    Foreign keys in PostgreSQL: Circular dependencies
    Circular dependencies occur when tables depend on each other, preventing inserts. The solution is to use 'INITIALLY DEFERRED' to delay constraint checks until ...
  25. [25]
    Handling circular dependency errors in AWS CloudFormation
    Apr 16, 2019 · A circular dependency, as the name implies, means that two resources are dependent on each other or that a resource is dependent on itself.
  26. [26]
    Library order in static linking - Eli Bendersky's website
    Jul 9, 2013 · Presumably, linkers could just re-scan the whole library list until no new symbols got resolved. This would eliminate most circular-dependency ...The Linking Process · Simple Examples · Circular Dependency<|control11|><|separator|>
  27. [27]
    Runtime error in case of circular dependency between ES Modules ...
    May 11, 2020 · In certain edge cases the way webpack generates code doesn't allow hoisting function correctly in circular dependencies.
  28. [28]
    Circular DI causes endless loop instead of exception · Issue #105900
    Aug 2, 2024 · Some cases of circular dependencies trigger a clear and helpful exception. The case below causes an endless loop.Missing: constructors | Show results with:constructors
  29. [29]
    Large-Scale C++ Volume I: Process and Architecture - O'Reilly
    Drawing on over 30 years of hands-on experience building massive, mission-critical enterprise systems, John Lakos shows how to create and grow Software Capital.
  30. [30]
    [PDF] Impact of Architectural Smells on Software Performance - CS@GSSI
    ABSTRACT. Architectural smells have been studied in the literature looking at several aspects, such as their impact on maintainability as a source.
  31. [31]
    [PDF] Studying the Relationship between Architectural Smells and ...
    Lippert & Roock explain that cyclic dependencies can affect maintainability negatively since they can have “severe and unpredictable consequences”—modifications ...
  32. [32]
    [PDF] Circular dependencies and change-proneness: An empirical study
    Mar 12, 2020 · In this paper, we present a study that investigates this issue for Java programs. We use the qualitas corpus [40] data set in our study.
  33. [33]
    An empirical investigation of the impact of architectural smells on ...
    For instance, the Cyclic Dependency architectural smell is claimed to impair all sub-characteristics of maintainability and is therefore thought to be one of ...
  34. [34]
    Identifying Design Problems that Impact Software Failure
    Apr 19, 2021 · Clique—Circular dependency is a well-known design problem that can cause unwanted effects in software programs. Clique refers to a group of ...
  35. [35]
    clarkware/jdepend: A Java package dependency analyzer ... - GitHub
    JDepend allows you to automatically measure the quality of a design in terms of its extensibility, reusability, and maintainability to effectively manage and ...
  36. [36]
    pahen/madge: Create graphs from your CommonJS, AMD or ES6 ...
    Madge is a developer tool for generating a visual graph of your module dependencies, finding circular dependencies, and giving you other useful info.Issues 113 · Actions · Pull requests 6 · Activity
  37. [37]
    cyclic-import / R0401 - Pylint 4.1.0-dev0 documentation
    Description: Used when a cyclic import between two or more modules is detected. Problematic code: __init__.py :.
  38. [38]
    [PDF] Directed Graphs - cs.Princeton
    DFS enables direct solution of simple digraph problems. □. Reachability. □. Cycle detection. □.<|control11|><|separator|>
  39. [39]
    Features | Jaeger
    Jaeger is used for monitoring and troubleshooting microservices-based distributed systems, including: High Scalability, Jaeger backend is designed to have no ...
  40. [40]
  41. [41]
    [PDF] The Dependency Inversion Principle - Object Mentor
    This article is an extremely condensed version of a chapter from my new book: Pat- terns and Advanced Principles of OOD, to be published soon by Prentice Hall.Missing: original | Show results with:original
  42. [42]
    [PDF] Resolving cyclic dependencies between packages ... - RMOD Files
    Nov 20, 2012 · SUMMARY. Dependency structural matrix (DSM) is an approach originally developed for process optimization. It has.
  43. [43]
    The pros and cons of the Pub-Sub architecture pattern - Red Hat
    May 6, 2021 · The Pub-Sub pattern is one in which a process sends a message into a message broker. Then, the message is forwarded to one or more parties listening for ...
  44. [44]
    Circular Dependencies in Spring - GeeksforGeeks
    Apr 28, 2025 · A circular dependency is a relation between two or more modules that either directly or indirectly depend on each other to function properly.
  45. [45]
  46. [46]
    Unit test your Java architecture
    ### Summary: Does ArchUnit Help Detect or Prevent Circular Dependencies?