Exploring Circular Dependencies: When Are They Acceptable and How to Handle Them?

This message was imported from the Ruby/Rails Modularity Slack server. Find more info in the import thread.

Message originally sent by slack user U70J5IZ271D

Hi all :wave: I have a question that I keep coming back to: ¿What is your stance around circular dependencies? Are there valid scenarios where a circular dependency is acceptable? Or is it always a pending thing to fix you just haven’t had the time to?
If you think there are acceptable scenarios, could you describe one?

Message originally sent by slack user U70JKF7I8J5

In general, I’d try very hard to avoid them. If A depends on B and B depends on A, then you can’t change either one without both being affected.

In that scenario, A and B may as well be combined since they’re not independent, however, one acceptable scenario is keeping them separated for conceptual organization, even though functionally, they’re coupled together.

Also, sometimes avoiding a circular dependency requires extracting smaller modules or making other substantial changes to the code. Having a larger number of smaller modules is also a tradeoff that’s worth considering. It’s another scenario where you may prefer the larger and/or circular dependencies.

Message originally sent by slack user U70JKF7I8J5

On a previous project, we defined “interfaces” for our modules and kept the implementation separate. This allowed us to somewhat avoid the circular dependency my having the following modules:

A-implementation (depends on A-interface and B-interface)
A-interface
B-implementation (depends on A-interface and B-interface)
B-interface
A-B-integration-tests (depends on A-implementation and B-implementation)

The main consequence of this approach is that we can’t test A-implementation against B-implementation without putting those tests up into a higher-level module, A-B-integration-tests. However, we could test A-implementation against B-interface.

Message originally sent by slack user U70J5IZ271D

“Having a larger number of smaller modules is also a tradeoff that’s worth considering” Oh god have I seen that haha.

Thank you Dan! That’s a great answer :raised_hands:

While implementing packwerk and defining boundaries we have struggled a lot with what you mention here: “one acceptable scenario is keeping them separated for conceptual organization, even though functionally, they’re coupled together”.

Reading your reply shows me this is a common issue that occasionally is just better to consider it acceptable, though it’s a clear warning that there might be an unnecesary coupling or the boundaries might not be the right ones yet.