Skip to content
tezvyn:

Why does Go forbid circular dependencies, and how do you resolve them?

Source: appliedgo.netMediumHow cards are made

Why does Go forbid circular dependencies, and how do you resolve them?
Summary

Knowledge of Go's DAG package model.

Key points

Cycles break incremental compilation; resolve by moving logic down, merging coupled packages, or using dependency injection.

Watch out for

Suggesting compiler workarounds over fixing design.

What's really being asked

This question probes whether you understand the architectural rationale behind Go's package system, not just the mechanical fact that import cycles fail. The interviewer wants to see that you value directed acyclic graphs for build performance and separation of concerns, and that you can refactor code to restore a clean dependency structure.

The full answer

First, explain why the compiler forbids cycles. Go requires package dependencies to form a DAG because that enables fast incremental compilation; the compiler can skip unmodified packages and walk the graph in a single pass. It also enforces cleaner architecture by making coupling explicit. Second, describe at least two refactoring patterns. The first pattern is relocating logic to the lower-level package. If package A and B import each other, ask which concern is more fundamental and move the dependent code there. The second pattern is merging packages. When two packages are so tightly coupled that separating them creates constant back-and-forth, combining them into one package removes the cycle outright. The third pattern, dependency injection via interfaces, is especially useful for layer violations. If an abstract domain package depends on a concrete storage package, define a storage interface in the domain layer and inject the concrete implementation at startup. This inverts the dependency so the concrete layer depends on the abstract interface.

The mistakes people make

A major red flag is suggesting compiler flags, build tags, or single-package workarounds to trick the toolchain. Another weak response is claiming cycles are forbidden only to make the compiler easier to write; the correct answer ties the restriction to incremental builds and architectural clarity. Candidates who can only describe one fix, such as merging everything into one package, signal shallow experience with larger codebases.

What usually comes next

The interviewer may ask how you would detect a cycle in a large repository, how dependency injection affects testability, or whether Go modules changed anything about cycle detection. They might also ask you to sketch a specific refactor for a hypothetical service with domain, storage, and API layers.

A concrete example

Imagine an e-commerce app where a product package imports inventory to check stock, inventory imports order to subtract pending orders, and order imports product to sum prices. To break the cycle, move the InStock method from product into the inventory package, since stock status is inventory's concern, not the product's. If inventory and product remain too entangled, merge them into a single goods package. Finally, if inventory directly calls a concrete inventory_storage package, introduce a storage interface inside inventory and inject the storage implementation during initialization so the domain layer no longer imports the concrete layer.

Interview question

An abstract domain package and a concrete storage package import each other. Which refactor best restores clean architecture?

  • a.Move all domain logic into the storage package to eliminate the upward import
  • b.Merge both packages into a single package since they are already tightly coupled
  • c.Use build tags to hide the cyclic imports from the compiler
  • d.Define a storage interface in the domain layer and inject the concrete implementation at startupCorrect
Why?

Defining the interface in the domain layer and injecting the concrete implementation at startup inverts the dependency, which is the recommended pattern for layer violations. Merging is better reserved for packages with no clear hierarchy, and build tags are a red-flag workaround that avoids fixing the design.

Just read this? Test yourself on what you have been reading.

Read the original → appliedgo.net

You just looked this up. Could you explain it out loud?

That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on go — each one lists the topics its interview covers.

See open roles