<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Gabriel Santos</title>
    <description>The latest articles on Forem by Gabriel Santos (@gabriel_santos_fc57509a3c).</description>
    <link>https://forem.com/gabriel_santos_fc57509a3c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2895762%2Fc0b5adb7-4cd6-44e4-8693-70c6a7b4a58f.jpg</url>
      <title>Forem: Gabriel Santos</title>
      <link>https://forem.com/gabriel_santos_fc57509a3c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gabriel_santos_fc57509a3c"/>
    <language>en</language>
    <item>
      <title>Como resolver o ciclo de importação em Go</title>
      <dc:creator>Gabriel Santos</dc:creator>
      <pubDate>Tue, 20 Jan 2026 18:34:32 +0000</pubDate>
      <link>https://forem.com/gabriel_santos_fc57509a3c/como-resolver-o-ciclo-de-importacao-em-go-h9i</link>
      <guid>https://forem.com/gabriel_santos_fc57509a3c/como-resolver-o-ciclo-de-importacao-em-go-h9i</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Problema
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fno38iwtjumsi2ulce8gv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fno38iwtjumsi2ulce8gv.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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?&lt;/p&gt;

&lt;h2&gt;
  
  
  Go nos dando uma lição
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Soluções
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inversão de Dependências.
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqy9w3iltit8f37ykocv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqy9w3iltit8f37ykocv.png" alt=" " width="800" height="1187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Desvantagens
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Injeção de Dependência
&lt;/h2&gt;

&lt;p&gt;Nesse modo, a ideia é usar outro caminho além da importação, os argumentos de funções.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkld72l1kgoykisghq36x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkld72l1kgoykisghq36x.png" alt=" " width="800" height="861"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Desvantagem da injeção de Dependência
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fusão
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkumcrvk3cipxkm7xehfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkumcrvk3cipxkm7xehfb.png" alt=" " width="800" height="1116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Desvantagens da fusão
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;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?".&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Essas são apenas algumas das soluções que existem para esse problema, espero que tenham gostado das que mostrei aqui, obrigado!&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;p&gt;Fixing import cycle in Go. Pergunta. Stack Overflow, 20 jul. 2018. Disponível em: &lt;a href="https://stackoverflow.com/questions/50986022/fixing-import-cycle-in-go" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/50986022/fixing-import-cycle-in-go&lt;/a&gt;. Acesso em: 20 jan. 2026.&lt;/p&gt;

&lt;p&gt;DIRECTED acyclic graph. In: WIKIPEDIA, the free encyclopedia. [S. l.]: Wikimedia Foundation, 2023. Disponível em: &lt;a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Directed_acyclic_graph&lt;/a&gt;. Acesso em: 20 jan. 2026.&lt;/p&gt;

&lt;p&gt;ATWOOD, Jeff. The Big Ball of Mud and Other Architectural Disasters. Coding Horror, 26 out. 2007. Disponível em: &lt;a href="https://blog.codinghorror.com/the-big-ball-of-mud-and-other-architectural-disasters/" rel="noopener noreferrer"&gt;https://blog.codinghorror.com/the-big-ball-of-mud-and-other-architectural-disasters/&lt;/a&gt;. Acesso em: 20 jan. 2026.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>beginners</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How I Reduced Friction in My Studies Using AI, Rust, and Obsidian.</title>
      <dc:creator>Gabriel Santos</dc:creator>
      <pubDate>Fri, 09 Jan 2026 22:15:44 +0000</pubDate>
      <link>https://forem.com/gabriel_santos_fc57509a3c/how-i-reduced-friction-in-my-studies-using-ai-rust-and-obsidian-444d</link>
      <guid>https://forem.com/gabriel_santos_fc57509a3c/how-i-reduced-friction-in-my-studies-using-ai-rust-and-obsidian-444d</guid>
      <description>&lt;p&gt;Every student knows: note-taking is fundamental for retention, but the act of formatting notes often consumes more energy than the learning itself. I found myself stuck in a cycle where research flowed smoothly, but documentation in Obsidian was a manual bottleneck.&lt;/p&gt;

&lt;p&gt;Initially, I researched, practiced, and took notes manually. I tried using AI to generate summaries, but the "Copy from Chat -&amp;gt; Open Obsidian -&amp;gt; Create File -&amp;gt; Paste" process was still too slow. When I had 10 topics to study, I ended up giving up on the notes halfway through.&lt;/p&gt;

&lt;p&gt;With that, I had the idea to further automate the note-taking process. I decided to create an application that does this automatically, and all I need to do for it to work is give it the name of the subject I want to summarize. Thus, noteap was born.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: The Noteap Ecosystem
&lt;/h2&gt;

&lt;p&gt;To automate this, I divided the problem into three layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Interface (React Native): A simple app where I just type the topic (e.g., "Rust Ownership"). No distractions.&lt;/li&gt;
&lt;li&gt;The Brain (Node.js + Gemini AI): A server that receives the topic, uses prompt engineering to generate a structured Markdown with code examples, and saves everything in a MongoDB database.&lt;/li&gt;
&lt;li&gt;The Synchronizer (Rust): Here's the key difference. I developed a Rust client that runs on my desktop. It retrieves pending notes from the server, converts them into .md files, and injects them directly into my "New Notes" folder in Obsidian.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Rust in the Client?
&lt;/h2&gt;

&lt;p&gt;I chose Rust for its performance and the ease of generating a single binary. It starts with the system, consumes irrelevant resources, and ensures that when I open my computer to review, my notes from the previous session are already there, waiting for me. Why not choose C, C++, or Go? Both generate a single binary and are good languages, and the answer lies in the cost-complexity-performance ratio. Of these languages, I believe Rust is the one that maintains the most user-friendly syntax despite being low-level, and with good performance, which is why it was chosen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;To keep the server always online, I put it running inside a Docker container on my AWS EC2 server, so I just need to grab the URL to use it with the other applications in the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Cognitive Load
&lt;/h2&gt;

&lt;p&gt;According to Annie Piolat, in her study "Cognitive effort during note taking," note-taking is one of the most demanding mental tasks, consuming immense cognitive effort. Often, the act of formatting notes and organizing files consumes more energy than the learning itself, creating what I call "documentation friction."&lt;/p&gt;

&lt;p&gt;By automating this workflow, I'm not just "saving time," I'm preserving my mental energy.&lt;/p&gt;

&lt;p&gt;Automation has given me back the time I used to spend on formatting, allowing me to apply the effort that Piolat describes to active review and practical application, and not to the bureaucracy of paper (or the .md file).&lt;/p&gt;

&lt;p&gt;And you? How do you manage your time and your notes? Have you ever tried to automate this process?&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PIOLAT, A.; OLIVE, T.; KELLOGG, R. T&lt;/strong&gt;. Cognitive effort during note taking. Applied Cognitive Psychology, 2005. (Base article on mental effort in note taking).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rust</category>
      <category>obsidian</category>
      <category>notes</category>
    </item>
  </channel>
</rss>
