<?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>DEV Community: Ryan Kikayi</title>
    <description>The latest articles on DEV Community by Ryan Kikayi (@its_ryann).</description>
    <link>https://dev.to/its_ryann</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3723370%2F7e32cf48-68b7-4078-a5eb-92e467cf1890.png</url>
      <title>DEV Community: Ryan Kikayi</title>
      <link>https://dev.to/its_ryann</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/its_ryann"/>
    <language>en</language>
    <item>
      <title>RAG Explained Simply: How to Teach AI About Your Private Data</title>
      <dc:creator>Ryan Kikayi</dc:creator>
      <pubDate>Mon, 31 Aug 2026 00:18:55 +0000</pubDate>
      <link>https://dev.to/its_ryann/rag-explained-simply-how-to-teach-ai-about-your-private-data-5c6f</link>
      <guid>https://dev.to/its_ryann/rag-explained-simply-how-to-teach-ai-about-your-private-data-5c6f</guid>
      <description>&lt;p&gt;You've probably seen the term &lt;strong&gt;RAG&lt;/strong&gt; everywhere lately — "RAG pipeline," "RAG chatbot," "build your own RAG app." It sounds complicated, but the idea behind it is actually pretty simple.&lt;/p&gt;

&lt;p&gt;In this article, I'll explain RAG in plain language, then walk through how it works using a real project I built: &lt;strong&gt;Guidely&lt;/strong&gt;, an internal knowledge assistant that answers questions using a company's own documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem RAG Solves
&lt;/h2&gt;

&lt;p&gt;Large language models (like GPT or Claude) are trained on a huge amount of general knowledge, but they don't know about &lt;em&gt;your&lt;/em&gt; specific data — your company's internal docs, your product manuals, your onboarding guides. They also can't be retrained every time a document changes; that's slow and expensive.&lt;/p&gt;

&lt;p&gt;RAG solves this without retraining the model at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basically:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RAG means: before answering a question, first go find the relevant pieces of your own documents, and hand those to the AI along with the question.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's it. "Retrieval" (finding the right information) + "Augmented Generation" (the AI answers using that information). Instead of the AI answering from memory alone, it answers using facts you hand it in the moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Core Pieces
&lt;/h2&gt;

&lt;p&gt;Let's break down the three things you need to make this work: &lt;strong&gt;chunking&lt;/strong&gt;, &lt;strong&gt;embeddings&lt;/strong&gt;, and &lt;strong&gt;vector search&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Chunking — Breaking Documents Into Pieces
&lt;/h3&gt;

&lt;p&gt;You can't hand an AI model an entire 200-page document and ask it to search through it efficiently. So the first step is splitting documents into smaller, manageable pieces called &lt;strong&gt;chunks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Guidely, I used a &lt;strong&gt;token-window chunker&lt;/strong&gt; — it splits text based on a fixed number of tokens (roughly, pieces of words) per chunk, rather than just splitting by paragraph or sentence. This matters because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chunks that are too big waste space and slow things down.&lt;/li&gt;
&lt;li&gt;Chunks that are too small lose context and produce confusing answers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A token-window approach gives you consistent, predictable chunk sizes, which makes the next steps more reliable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Embeddings — Turning Text Into Numbers
&lt;/h3&gt;

&lt;p&gt;Once you have chunks, you need a way to compare "how similar" two pieces of text are — for example, how similar is a chunk to the user's question?&lt;/p&gt;

&lt;p&gt;Computers can't compare meaning directly, so each chunk gets converted into a list of numbers called an &lt;strong&gt;embedding&lt;/strong&gt; (a vector). Text with similar meaning ends up with numbers that are mathematically close to each other, even if the wording is completely different.&lt;/p&gt;

&lt;p&gt;For example, "How do I reset my password?" and "Steps to change your login credentials" would produce embeddings that are close together, because they mean roughly the same thing — even though the words barely overlap.&lt;/p&gt;

&lt;p&gt;One thing I added in Guidely: a &lt;strong&gt;SHA-256 hash-based embedding cache&lt;/strong&gt;. Generating embeddings costs time and money, so before creating a new embedding, I hash the chunk's content and check if it's already been embedded before. If it has, I reuse the cached version instead of generating it again. This alone cut down repeated work significantly, especially when documents get updated but most of the content stays the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Vector Search — Finding the Right Chunks
&lt;/h3&gt;

&lt;p&gt;Now that every chunk has a numeric embedding, you need a fast way to search through thousands (or millions) of them to find the ones closest to the user's question.&lt;/p&gt;

&lt;p&gt;This is where a &lt;strong&gt;vector database&lt;/strong&gt; comes in. In Guidely, I used &lt;strong&gt;FAISS&lt;/strong&gt; (Facebook AI Similarity Search) — a library built specifically for searching through large sets of embeddings quickly.&lt;/p&gt;

&lt;p&gt;The flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User asks a question.&lt;/li&gt;
&lt;li&gt;The question gets converted into an embedding, using the same method as the chunks.&lt;/li&gt;
&lt;li&gt;FAISS compares that embedding against all the stored chunk embeddings and returns the closest matches.&lt;/li&gt;
&lt;li&gt;Those matching chunks are the ones most likely to contain the answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Here's the full RAG flow, using Guidely as the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Documents come in (company docs, guides, wikis)
        ↓
2. Chunker splits them into token-window chunks
        ↓
3. Each chunk is embedded (with caching to skip repeat work)
        ↓
4. Embeddings are stored in a FAISS index
        ↓
5. User asks a question
        ↓
6. Question is embedded and compared against the FAISS index
        ↓
7. Top matching chunks are retrieved
        ↓
8. Chunks + question are sent to the AI model
        ↓
9. AI generates an answer grounded in the actual documents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend for this in Guidely runs on &lt;strong&gt;FastAPI&lt;/strong&gt;, with &lt;strong&gt;FAISS&lt;/strong&gt; handling the vector search, and a &lt;strong&gt;React/Vite&lt;/strong&gt; frontend for the chat interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Approach Works Well
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No retraining needed.&lt;/strong&gt; Update a document, re-chunk and re-embed it, and the assistant instantly "knows" the new information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answers are grounded in real sources.&lt;/strong&gt; Instead of the AI guessing from general training data, it answers based on your actual documents — which also means you can show users exactly where an answer came from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It scales.&lt;/strong&gt; FAISS is built to handle large volumes of embeddings efficiently, so this approach holds up as your document set grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Few Things I Learned Building This:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chunk size matters more than you'd expect. Too large, and irrelevant details creep into the AI's context. Too small, and you lose the surrounding meaning needed to answer well.&lt;/li&gt;
&lt;li&gt;Caching embeddings isn't just a nice-to-have — it's a real cost and speed saver once you're re-processing documents regularly.&lt;/li&gt;
&lt;li&gt;Good retrieval matters more than a fancier AI model. If the wrong chunks get retrieved, even the best model will give a wrong or vague answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;RAG isn't magic — it's really just: break documents into chunks, turn them into searchable numbers, find the closest matches to a question, and let the AI answer using those matches. Once you see it broken down like this, it becomes a lot less intimidating to build yourself.&lt;/p&gt;

&lt;p&gt;If you're building something similar or have questions about any part of the pipeline, drop a comment below.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>beginners</category>
      <category>learning</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Structure a Project(Next.js + Go)</title>
      <dc:creator>Ryan Kikayi</dc:creator>
      <pubDate>Sun, 30 Aug 2026 23:52:54 +0000</pubDate>
      <link>https://dev.to/its_ryann/how-to-structure-a-projectnextjs-go-30l0</link>
      <guid>https://dev.to/its_ryann/how-to-structure-a-projectnextjs-go-30l0</guid>
      <description>&lt;h2&gt;
  
  
  How I Structure a Next.js + Go Project (Frontend, Backend, and Deployment)
&lt;/h2&gt;

&lt;p&gt;When you're building a full-stack app with &lt;strong&gt;Next.js&lt;/strong&gt; on the frontend and &lt;strong&gt;Go&lt;/strong&gt; on the backend, one of the first questions you'll face is: how do I organize this so it doesn't turn into a mess later?&lt;/p&gt;

&lt;p&gt;I ran into this while building a school management system with Go, Next.js, and PostgreSQL — a project with several modules (students, finance, timetable, staff, and more). In this article, I'll walk through how I split the frontend and backend, how they talk to each other, and how I deploy both together.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. One Repo or Two?
&lt;/h2&gt;

&lt;p&gt;You have two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monorepo&lt;/strong&gt; — frontend and backend live in the same repository, in separate folders (e.g. &lt;code&gt;/frontend&lt;/code&gt; and &lt;code&gt;/backend&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate repos&lt;/strong&gt; — each app has its own repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a solo project or a small team, I prefer a &lt;strong&gt;monorepo&lt;/strong&gt;. It's easier to keep frontend and backend changes in sync, and you only need to clone one project to get started. Separate repos make more sense once you have separate teams working independently, or when the frontend and backend have very different release schedules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project/
├── backend/     (Go)
├── frontend/    (Next.js)
└── docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Structuring the Backend (Go)
&lt;/h2&gt;

&lt;p&gt;Instead of organizing your Go code by technical layer only (all handlers in one folder, all models in another), I organize it &lt;strong&gt;by feature/module first&lt;/strong&gt;, then by layer inside each module. This matters a lot once your app has several modules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── student/
│   │   ├── handler.go
│   │   ├── service.go
│   │   └── model.go
│   ├── finance/
│   │   ├── handler.go
│   │   ├── service.go
│   │   └── model.go
│   └── ...
├── pkg/
│   └── (shared utilities, e.g. auth, db, validation)
└── go.mod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why this works well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each module is self-contained. If you need to change how "Finance" works, you know exactly where to look.&lt;/li&gt;
&lt;li&gt;It's easier to onboard someone new — they can understand one module without reading the whole codebase.&lt;/li&gt;
&lt;li&gt;If a module ever needs to become its own service later, it's already mostly separated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Structuring the Frontend (Next.js)
&lt;/h2&gt;

&lt;p&gt;On the frontend, I follow a similar idea — group by feature, and keep a clear separation between UI, data-fetching, and shared code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frontend/
├── app/
│   ├── students/
│   ├── finance/
│   └── ...
├── lib/
│   ├── api/
│   │   ├── client.ts
│   │   ├── students.ts
│   │   └── finance.ts
│   └── types/
│       └── (shared TypeScript types)
├── components/
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;lib/api/client.ts&lt;/code&gt; file is important — this is where all requests to the backend go through one place, instead of writing raw &lt;code&gt;fetch&lt;/code&gt; calls all over the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. How the Frontend and Backend Talk to Each Other
&lt;/h2&gt;

&lt;p&gt;This is where a lot of things can go wrong if you're not careful, so here's what worked for me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep a single API client.&lt;/strong&gt; Instead of calling &lt;code&gt;fetch()&lt;/code&gt; everywhere, wrap it in one function that adds the base URL, headers, and auth token automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/api/client.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_API_URL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;apiFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// however you store it&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Request failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every module then builds on top of this — e.g. &lt;code&gt;lib/api/students.ts&lt;/code&gt; calls &lt;code&gt;apiFetch("/students")&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep your types close to your API shape.&lt;/strong&gt; In Go, define clear structs for your request/response bodies. On the frontend, write matching TypeScript types. It's manual work, but it saves you from a lot of confusing bugs where the frontend expects a field that the backend doesn't send.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handle loading and error states the same way everywhere.&lt;/strong&gt; With several modules, it's easy to end up with five different ways of showing "loading..." or an error message. Pick one pattern (a simple hook, or a wrapper component) and reuse it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Local Development Setup
&lt;/h2&gt;

&lt;p&gt;Running two apps at once can be annoying if it's not automated. I use &lt;code&gt;docker-compose&lt;/code&gt; to start everything with one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend/.env&lt;/span&gt;

  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend/.env&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;

  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432:5432"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For local dev, you also need &lt;strong&gt;CORS&lt;/strong&gt; enabled on the Go backend so the frontend (running on a different port) can call it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;corsMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Origin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:3000"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Methods"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"GET, POST, PUT, DELETE, OPTIONS"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Headers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Content-Type, Authorization"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, you'll usually avoid this problem entirely by putting both apps behind the same domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Deployment
&lt;/h2&gt;

&lt;p&gt;There are two common ways to deploy a Next.js + Go project:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A: Same server, reverse proxy in front.&lt;/strong&gt;&lt;br&gt;
Both apps run on the same machine. A reverse proxy like &lt;strong&gt;Nginx&lt;/strong&gt; or &lt;strong&gt;Caddy&lt;/strong&gt; sits in front and routes traffic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/api/*&lt;/code&gt; - Go backend&lt;/li&gt;
&lt;li&gt;everything else - Next.js frontend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This avoids CORS issues completely and keeps things simple for small to mid-sized projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B: Separate hosting.&lt;/strong&gt;&lt;br&gt;
Frontend goes to a platform like Vercel, backend goes to a VM, container platform, or somewhere like Render/Fly.io. This gives you more flexibility to scale each part independently, but you'll need to handle CORS and manage two deployment pipelines instead of one.&lt;/p&gt;

&lt;p&gt;For a project like the school management system, I went with &lt;strong&gt;Option A&lt;/strong&gt; — one server, Go binary running behind Caddy, Next.js served alongside it. It kept the deployment simple, and simplicity mattered more than independent scaling at that stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I started this project again, I'd generate the TypeScript types directly from the Go structs instead of writing them by hand on both sides. Keeping them in sync manually works, but it's an easy place to introduce bugs as the project grows. Tools like &lt;code&gt;swaggo&lt;/code&gt; (for generating OpenAPI docs from Go) paired with an OpenAPI-to-TypeScript generator would save time and reduce mismatches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Structuring a Next.js + Go project comes down to a few simple decisions: organize by feature on both sides, centralize how the frontend talks to the backend, and pick a deployment setup that matches the size of your project — not the size you imagine it might become.&lt;/p&gt;

&lt;p&gt;If you're working on something similar, I'd love to hear how you've structured yours. Drop a comment below.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>howto</category>
      <category>go</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Image vs. Container: The Ultimate Guide to Stop Confusing the Two</title>
      <dc:creator>Ryan Kikayi</dc:creator>
      <pubDate>Mon, 01 Jun 2026 15:52:00 +0000</pubDate>
      <link>https://dev.to/its_ryann/image-vs-container-the-ultimate-guide-to-stop-confusing-the-two-123j</link>
      <guid>https://dev.to/its_ryann/image-vs-container-the-ultimate-guide-to-stop-confusing-the-two-123j</guid>
      <description>&lt;p&gt;We've all been there. You're 45 minutes into a Docker tutorial, feeling great about yourself, and then someone casually drops: "Just pull the image and spin up a container."&lt;br&gt;
And you think: "...wait, aren't those the same thing?"&lt;br&gt;
First - this has happened to a good number of us if we are to be honest. Even almost every single DevOps engineer, cloud architect, and platform wizard you admire has typed the wrong term in a sentence at least once in their career. It's practically a rite of initiation. There should be a badge for it if you ask me.&lt;/p&gt;
&lt;h1&gt;
  
  
  Why Does This Trip Everyone Up?
&lt;/h1&gt;

&lt;p&gt;Here's the sneaky truth: Docker commands blur the line constantly. You type &lt;code&gt;docker run nginx&lt;/code&gt; and something called a "container" starts — but wait, didn't you just use an "image" called &lt;code&gt;nginx&lt;/code&gt;? Where did one end and the other begin?&lt;br&gt;
The confusion lives in the fact that they are deeply related — one literally gives birth to the other. But they are fundamentally, completely different things. Getting this distinction straight is your official rite of passage into DevOps. Once it clicks, the rest of Docker feels like cheating.&lt;br&gt;
Basically, &lt;strong&gt;A Docker Image&lt;/strong&gt; is the &lt;strong&gt;blueprint&lt;/strong&gt;: a frozen, static snapshot of everything your app needs - the OS layer, the dependencies, the config files, your actual code. It just sits there on disk, completely inert. You can't &lt;code&gt;run&lt;/code&gt; a blueprint.&lt;br&gt;
A &lt;strong&gt;Docker Container&lt;/strong&gt; is the &lt;strong&gt;house&lt;/strong&gt;: the live, running instance that was built from that blueprint. It has processes running, files potentially being written, network ports being listened on. It's alive.&lt;br&gt;
And now, just like one blueprint can produce 10 identical houses on different streets - one &lt;strong&gt;Image&lt;/strong&gt; can launch 10 identical &lt;strong&gt;Containers&lt;/strong&gt; simultaneously; and that's where Docker's scaling magic comes from.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="c"&gt;# The image just sits here, unchanging&lt;/span&gt;
docker pull nginx

&lt;span class="c"&gt;# Now we BUILD a house (container) from the blueprint&lt;/span&gt;
docker run nginx

&lt;span class="c"&gt;# Build THREE houses from the same single blueprint&lt;/span&gt;
docker run nginx
docker run nginx
docker run nginx 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example using a Class and an Object to help you understand; and it's very simple actually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;class Dog &lt;span class="o"&gt;=&lt;/span&gt; Image
&lt;span class="c"&gt;#The definition. Describes what a Dog has (name, breed) and can do (bark).&lt;/span&gt;
&lt;span class="c"&gt;#Defined once. Never changes.&lt;/span&gt;

new Dog&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; Container
&lt;span class="c"&gt;#A real, live dog. Created from the class.&lt;/span&gt;
&lt;span class="c"&gt;#Barking, eating, chewing on things it shouldn't - Even **"running"**😉&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Dog&lt;/code&gt; class is your Image - written once, stored, never actually "lives". Every time you call &lt;code&gt;new Dog("Rex")&lt;/code&gt;, you're spinning up a Container. And by doing this you can instantiate as many as you like, and they all share the same starting definition while doing their own independent thing at runtime.&lt;/p&gt;

&lt;h1&gt;
  
  
  Quick Reference: The Cheat Sheet
&lt;/h1&gt;

&lt;p&gt;Stick this onto your wall, tattoo it on your forearms, set it as your desktop backgrounds. Whatever works for you guys:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Docker Image&lt;/th&gt;
&lt;th&gt;Docker Container&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;static&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Dynamic/ Running&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stored where?&lt;/td&gt;
&lt;td&gt;On disk (registry or local)&lt;/td&gt;
&lt;td&gt;In memory + writable layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mutable?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Read-only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Read + Write&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can you run it?&lt;/td&gt;
&lt;td&gt;No - you run from it&lt;/td&gt;
&lt;td&gt;Yes - it &lt;em&gt;is&lt;/em&gt; running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analogy&lt;/td&gt;
&lt;td&gt;Blueprint/ Class&lt;/td&gt;
&lt;td&gt;House/ Object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Created with&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker build&lt;/code&gt; or &lt;code&gt;docker pull&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker run&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How many from one?&lt;/td&gt;
&lt;td&gt;Infinite containers&lt;/td&gt;
&lt;td&gt;One instance per &lt;code&gt;run&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🎓 You're Now Officially Initiated
&lt;/h3&gt;

&lt;p&gt;Here's the TL;DR, burn it into your brain forever:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image&lt;/strong&gt; = the frozen, static blueprint. Read-only. Lives on disk. Does nothing by itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container&lt;/strong&gt; = the live, running instance built from an image. Has state. Things are happening.&lt;/li&gt;
&lt;li&gt;One image can produce many containers. Containers are born from images and then go live their own lives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📊Next Level&lt;/p&gt;

&lt;p&gt;Now that this is locked in, go explore &lt;code&gt;docker ps&lt;/code&gt; (lists running containers), &lt;code&gt;docker images&lt;/code&gt; (lists images), and &lt;code&gt;docker stop&lt;/code&gt; (stops a container without touching the image).&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Go Error Handling: Annoying or Awesome?</title>
      <dc:creator>Ryan Kikayi</dc:creator>
      <pubDate>Mon, 25 May 2026 07:40:27 +0000</pubDate>
      <link>https://dev.to/its_ryann/go-error-handling-annoying-or-awesome-4dja</link>
      <guid>https://dev.to/its_ryann/go-error-handling-annoying-or-awesome-4dja</guid>
      <description>&lt;p&gt;For weeks when I was completely new to coding,I would always keep wondering; "What did I get myself into?", barely understanding what I'm looking at whenever I come across code that looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="n"&gt;MyStruct&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for some reason it just did not make any sense to me at first; what do you mean we're using 9 lines of code just for error checking - has to be a joke?!&lt;br&gt;
Over at YouTube, no tutorial was relatable. I mean there were tutorials on error checking for python, and javascript, but the concept wasn't just sticking.&lt;br&gt;
Eventually, I ended up just copying chunks of code that I barely understood and pasted them into mine. Function returns two things, assign them to a variable and &lt;code&gt;err&lt;/code&gt;. Basically pasting the &lt;code&gt;if err != nil&lt;/code&gt; block was what I knew I was doing, the reason why? Not so much.&lt;br&gt;
I wasn't reading the error half of the time, I was just calling &lt;code&gt;log.Fatal(err)&lt;/code&gt; hoping it would never have to run.&lt;/p&gt;

&lt;p&gt;Later on after getting comfortable with the language, I decided to work on a personal project - a CLI tool that reads config files, makes API calls, and writes its output to disk. Unfortunately, it broke in production, the problem;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had used a blank identifier &lt;code&gt;_&lt;/code&gt; to ignore the error completely - a habit I discovered to avoid having to write the check. This caused an API call to fail silently because the marshal was sending an empty body, and the API was rejecting it. And there I was, trying to debug something a single error message would have told me immediately.&lt;br&gt;
I realized, in Go errors are not exceptions, they're values - like regular data, a functions gives back to you. When you call a function that can fail, it returns two things; the result, and an error. Go hands you the error and trusts you to deal with it. So basically, writing &lt;code&gt;if err != nil&lt;/code&gt; is like saying "Something could go wrong here. What do you want to do about it?" in Go. Once  I finally understood that I started seeing them as decision points.&lt;/p&gt;

&lt;p&gt;So how do you actually make peace with it?&lt;/p&gt;

&lt;p&gt;Early on, I used to write my errors the same: &lt;code&gt;log.Fatal(err)&lt;/code&gt;. When something broke, I'd get a vague message like unexpected end of JSON input with zero context about where in my program it came from.&lt;br&gt;
The fix was wrapping errors with context using &lt;code&gt;fmt.Errorf&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to read config file: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The %w keeps the original error intact so you can still inspect it, but now every error message tells you what your program was trying to do when it failed. That one change made debugging significantly less painful.&lt;br&gt;
The second thing that helped was setting up a snippet in my editor. In VS Code, typing &lt;code&gt;ife&lt;/code&gt; and hitting Tab expands to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The official Go extension already includes this — check your snippet settings if you haven't already. It sounds like a small thing, but when you're writing the pattern twenty times a day, not having to type it from scratch every time genuinely reduces the friction.&lt;/p&gt;

&lt;p&gt;So — annoying or awesome?&lt;/p&gt;

&lt;p&gt;Honestly? Both. Annoying at first, awesome once you understand why it exists.&lt;br&gt;
Go made a deliberate choice to make error handling visible and local, rather than something that silently bubbles up through your stack and crashes things in ways you didn't expect. Every if err != nil is a decision point baked directly into your code. You always know where an error came from. You always have to decide what to do with it.&lt;br&gt;
For a first language, that turned out to be a surprisingly good environment to learn in. I couldn't ignore problems — Go wouldn't let me. And when things went wrong, the error was almost always exactly where Go said it was.&lt;br&gt;
Would I have picked a different first language if someone had warned me about this pattern? Maybe. Am I glad I stuck with Go? Absolutely.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're learning Go right now, I'm curious — what part of the language caught you off guard the most? Drop it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>go</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Vibe Coding is a Last Resort Mistake!!</title>
      <dc:creator>Ryan Kikayi</dc:creator>
      <pubDate>Thu, 12 Feb 2026 09:31:38 +0000</pubDate>
      <link>https://dev.to/its_ryann/vibe-coding-is-a-last-resort-mistake-4ndp</link>
      <guid>https://dev.to/its_ryann/vibe-coding-is-a-last-resort-mistake-4ndp</guid>
      <description>&lt;p&gt;We've all been there. You're deep into a project, making great progress, and then you hit a wall. Maybe the app is having trouble reading data from the API, or your code for handling mistakes feels messy and confusing.&lt;br&gt;
Instead of reaching for the official docs, you reach for the AI—your "coding genius."&lt;br&gt;
That was my first big mistake.&lt;br&gt;
Ten minutes later, all my clean, manual progress was gone. The AI had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restructured my structs without asking.&lt;/li&gt;
&lt;li&gt;    Injected unnecessary pointers that made no sense.&lt;/li&gt;
&lt;li&gt;    Turned my logic into a "hallucinated" masterpiece that wouldn't even run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn’t realize it then, but I had fallen into the trap. I barely understood what was happening in my own file. It became a frantic back-and-forth conversation with ChatGPT where all I was doing was prompting, copying error messages, and pasting code. We all know how the copy-paste game ends.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I Saved the Project:
&lt;/h3&gt;

&lt;p&gt;To save my work, I had to stop the cycle. I was the victim, and my project was the casualty. To get back on track from that AI-generated mess, I had to take these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Hard Reset
I had to go back to my last working version. If you aren't using Git yet, let this be your wake-up call. Being able to "undo" a hallucination is the ultimate safety net. I opened my terminal and ran:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash
# This command resets your file to the last saved version
git checkout -- weather_app.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like that, the AI "slop" was gone, and my original logic was back.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Surgical Prompting
I stopped giving the AI my entire file. I isolated one specific logic block and asked: "Why is this specific line throwing an error?"&lt;/li&gt;
&lt;li&gt;Verification
Once the AI suggested a solution, I verified it using outside resources like StackOverflow and the Official Go Docs. This helped me actually learn the why behind the fix.&lt;/li&gt;
&lt;li&gt;The "No-Paste" Rule
Instead of pasting the code directly, I forced myself to manually type the solution back into my editor. This rebuilt the mental connection between my brain and the screen.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  The "S.P.E.C." Framework:
&lt;/h4&gt;

&lt;p&gt;From this encounter, I’ve learned that AI should be your last resort, not your first move when you hit a dead end. To keep your code from turning into "Slop," follow this framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S - Scope: Give the AI only the snippet it needs. "Only edit the FetchWeather function."&lt;/li&gt;
&lt;li&gt;    P - Parameters: Explicitly tell it: "Use only standard libraries."&lt;/li&gt;
&lt;li&gt;    E - Edge Cases: Ask: "How should this handle a 404 or a timeout?"&lt;/li&gt;
&lt;li&gt;    C - Constraints: Be firm: "Do not change my existing struct definitions."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final Thoughts:
&lt;/h3&gt;

&lt;p&gt;In 2026, anyone can prompt, but not everyone can engineer.&lt;br&gt;
Don't let AI be the lead architect of your learning journey. Use it as a pair programmer that requires strict instructions. The moment you stop understanding what you're doing or what is on your screen—stop.&lt;br&gt;
Go back to your original code, follow the framework, and stay in control of your logic.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
