I've worked with very high level engineers at Google that thought cyclic dependencies are fine. Sometimes they are just the simplest solution and trying to design abstractions to avoid it just creates a huge mess.
If you've working with Java at Google (Guice) makes you learn to hate hiding circular dependencies behind dependency injection, since your binary gets injected by some huge tree of thousands of dependencies all that can create errors or interfere with each other. And without static checking trying to reason and fix those issues becomes really hard.
I strongly prefer using the languages actual type system to create circular dependencies that you can inspect using well known tools over that any day, better have a problem you can see than hide the same problem to satisfy some tools requirement.
I have to say that in my whole career of doing Java / Clojure, Google’s SDK are one of the worst in terms of dependency hell. It’s incredibly unpleasant to work with, lots of breaking incompatibilities with libraries, etcetera.
I may sound very snarky, but it really feels like an over engineered mess and I suspect the reason they need all these dependency tricks is the fact that it’s over engineered, not necessarily that the problem they’re solving is so unique that they need circular dependencies.
You never truly need circular dependencies, however many times letting functions at the same abstraction level call each other greatly simplifies code.
I think something has been lost along the way with injection and IoC that makes using a fully functional programming language for object wiring a compelling alternative for most engineers.
If you've working with Java at Google (Guice) makes you learn to hate hiding circular dependencies behind dependency injection, since your binary gets injected by some huge tree of thousands of dependencies all that can create errors or interfere with each other. And without static checking trying to reason and fix those issues becomes really hard.
I strongly prefer using the languages actual type system to create circular dependencies that you can inspect using well known tools over that any day, better have a problem you can see than hide the same problem to satisfy some tools requirement.