Estava modularizando um projeto pequeno em go e me deparei pela primeira vez com uma mensagem "import cycle not allowed". Passei um bom tempo procurando soluções para esse problema e quebrando a cabeça com os vários níveis de complexidade que esse problema pode ter. Então hoje vou demonstrar as soluções que encontrei, minha opinião sobre cada uma e porque o go está na verdade nos dando uma lição.
O Problema
O erro de "import cycle not allowed" ocorre quando dois ou mais pacotes importam módulos entre si, levando a um ciclo infinito e circular de importações. O compilador go nativamente bloqueia esse comportamento porque com import cycle ele não saberia qual arquivo deve ser renderizado primeiro, uma vez que a ordem de inicialização dos pacotes é determinada pelo grafo de dependências, mas se um depende do outro, por onde começar?
Go nos dando uma lição
O compilador go não bloqueia import cycle apenas porque ele não saberia como lidar (Bom... Talvez esse seja o principal motivo). Mas também porque a linguagem go preza pela simplicidade, clareza, desempenho de compilação e construção de sistemas modulares robustos. Em outras linguagens (como Java ou Python), ciclos de importação são permitidos ou tratados de forma mais permissiva, o que muitas vezes leva ao chamado "Big Ball of Mud" (Grande Bola de Lama). Ao proibir que você compile código com import cycle ele está empurrando pra dentro do seu código o princípio do Directed acyclic graph, ou DAG, que nesse contexto, determina que em uma árvore de arquivos as importações só podem ocorrer de um arquivo para o outro, nunca nos dois sentidos, a fim de garantir que um ciclo não ocorra.
O conceito de DAG é usado em outros âmbitos sociais como da matemática, biologia (evolução, Árvores Genealógicas , epidemiologia) e em ciência da informação. Para a biologia, toda árvore genealógica é uma representação do conceito de DAG, e a consanguinidade é comprovadamente um risco real para más formações nos seres humanos que viessem a seguir (Assim como nosso código se go deixasse compilar!). Poderia citar mais exemplos da implementação de DAG, mas isso se estenderia demais. O fato é que Go não apenas está nos dando uma limitação técnica, está nos ensinando uma lei fundamental para a vida.
Soluções
Tendo em vista o problema e suas causas, vamos falar sobre as soluções para garantir a continuidade do código de acordo com os princípios do Go, e em ordem decrescente de complexidade.
Inversão de Dependências.
Para este caminho, a intenção é "enganar" um dos módulos a fim de substituir o que deveria ser importado. Esse método é um dos mais complexos de se entender inicialmente, mas depois de pensar um bocado se torna fácil de compreender.
Nesse exemplo, package 1 necessita de uma função do package 2, que depende de uma struct do pacote 1 para fazer a tipagem da mesma, resultando em um import cycle. Para resolver, criamos um segundo arquivo do package 2 (ou pode ser no mesmo arquivo), e introduzimos uma interface que define o comportamento esperado da struct do package 1.
Também criamos um Map, do tipo que recebe como chave uma string e como valor uma função, a mesma que o package 1 irá usar. Para que o package 1 use a função do package 2, basta apenas que ele importe o map do package 2 e acesse a função, passando como parâmetro a struct com os métodos corretos. Assim package 2 não depende mais do package 1 para tipagem, apenas de si mesmo, resultando em uma inversão de dependências.
Essa solução só é possível porque em go uma interface pode ser satisfeita por qualquer tipo concreto (struct, int, string, etc..) que implemente todos os métodos definidos nessa interface, sem a necessidade de uma declaração explícita. Isso significa que você não precisa dizer "esta struct x implementa a interface y". Se x tiver os mesmos métodos de y, ela já é um y.
Desvantagens
Implementar em uma interface os mesmos métodos de outra struct pode ser complicado uma vez que esses métodos, sejam complexos demais para ser implementados, ou não compensem o esforço. Nesse caso, outras soluções podem ser mais convenientes para resolver o problema.
Injeção de Dependência
Nesse modo, a ideia é usar outro caminho além da importação, os argumentos de funções.
Neste exemplo, a função do package 2 não é mais importada para ser usada no package 1, o que causaria um import cycle, mas é o package 2 que importa o método que iria usar sua função do package 1. Depois de fazer isso, o package 2 injeta sua função como parâmetro do método, e a distribui para o resto do código, ocasionando assim, uma injeção de dependência. Esse fluxo pode ocorrer de outras formas e direções dentro do código, mas a ideia de injeção de dependência permanece a mesma.
Desvantagem da injeção de Dependência
Essa solução na maioria das vezes pode parecer confusa, bagunçada e até um pouco de over engineering, mas ainda é uma das soluções para resolver o problema de import cycle.
Fusão
Esse método é o mais simples e rápido de todos, sendo a solução que encontrei e julguei melhor para o caso de import cycle que estava enfrentando.
Entenda essa solução como se o Go estivesse te dizendo o seguinte: "Ei, você não acha que está exagerando na modularização? Que tal juntar esses pacotes?". Esse caminho costuma ser o melhor quando o aviso de import cycle ocorre em projetos extremamente pequenos (Como é o caso do meu), e você pretende manter a integridade do código. Aqui basta juntar os dois pacotes conflitantes, e talvez refatorar alguns nomes para manter a legibilidade e manutenção do código.
Desvantagens da fusão
Se feito nos módulos errados, pode gerar acoplamento no código e acabar se tornando um problema para o seu eu futuro! Tome cuidado com esse método.
Conclusão
O erro de import cycle not allowed não deve ser visto como um obstáculo, mas como um mecanismo de feedback do compilador sobre a saúde do seu design de software. Quando Go nos impede de seguir com um ciclo, ele nos força a parar e perguntar: "Essas duas coisas são realmente distintas ou deveriam ser uma só?" ou "Quem realmente deve mandar em quem aqui?".
Resolver um ciclo de importação quase sempre resulta em um código mais testável, desacoplado e fácil de entender. Seja através da inversão de dependências, da injeção ou da simples fusão de pacotes, o objetivo final é o mesmo: manter o fluxo de dependências claro e unidirecional. Da próxima vez que o compilador reclamar, agradeça a ele; ele está impedindo que você crie um "nó" que seria muito mais difícil de desatar no futuro.
Essas são apenas algumas das soluções que existem para esse problema, espero que tenham gostado das que mostrei aqui, obrigado!
Referências
Fixing import cycle in Go. Pergunta. Stack Overflow, 20 jul. 2018. Disponível em: https://stackoverflow.com/questions/50986022/fixing-import-cycle-in-go. Acesso em: 20 jan. 2026.
DIRECTED acyclic graph. In: WIKIPEDIA, the free encyclopedia. [S. l.]: Wikimedia Foundation, 2023. Disponível em: https://en.wikipedia.org/wiki/Directed_acyclic_graph. Acesso em: 20 jan. 2026.
ATWOOD, Jeff. The Big Ball of Mud and Other Architectural Disasters. Coding Horror, 26 out. 2007. Disponível em: https://blog.codinghorror.com/the-big-ball-of-mud-and-other-architectural-disasters/. Acesso em: 20 jan. 2026.




Top comments (0)