  Dependency cycles pose a significant challenge to software quality and
maintainability. However, there is limited understanding of how practitioners
resolve dependency cycles in real-world scenarios. This paper presents an
empirical study investigating the recurring patterns employed by software
developers to resolve dependency cycles between two classes in practice. We
analyzed the data from 38 open-source projects across different domains and
manually inspected hundreds of cycle untangling cases. Our findings reveal that
developers tend to employ five recurring patterns to address dependency cycles.
The chosen patterns are not only determined by dependency relations between
cyclic classes, but also highly related to their design context, i.e., how
cyclic classes depend on or are depended by their neighbor classes. Through
this empirical study, we also discovered three common counterintuitive
solutions developers usually adopted during cycles' handling. These recurring
patterns and common counterintuitive solutions observed in dependency cycles'
practice can serve as a taxonomy to improve developers' awareness and also be
used as learning materials for students in software engineering and
inexperienced developers. Our results also suggest that, in addition to
considering the internal structure of dependency cycles, automatic tools need
to consider the design context of cycles to provide better support for
refactoring dependency cycles.
