<?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: OdaloV</title>
    <description>The latest articles on DEV Community by OdaloV (@odalov).</description>
    <link>https://dev.to/odalov</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%2F3720244%2Fdbb171f4-1cc5-4572-b672-6e7e7da7cd84.png</url>
      <title>DEV Community: OdaloV</title>
      <link>https://dev.to/odalov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/odalov"/>
    <language>en</language>
    <item>
      <title>A Go Dev's First Month with JavaScript</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Fri, 24 Jul 2026 14:18:15 +0000</pubDate>
      <link>https://dev.to/odalov/a-go-devs-first-month-with-javascript-p31</link>
      <guid>https://dev.to/odalov/a-go-devs-first-month-with-javascript-p31</guid>
      <description>&lt;p&gt;I learned Go first. When I started picking up JavaScript, I assumed it would just be "different words for the same ideas" I already knew. A week in, I realized it's not just different words, it's a different way of thinking about code entirely. Here's what stood out most in that first week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declaring variables: &lt;code&gt;:=&lt;/code&gt; vs &lt;code&gt;let&lt;/code&gt;/&lt;code&gt;const&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In Go, this is second nature by the time you've written a few programs:&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;count&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"Amina"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript has its own version, but it looks nothing like it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Amina&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My fingers kept typing &lt;code&gt;:=&lt;/code&gt; out of habit, which JavaScript just doesn't understand at all. And there's no single equivalent, JS gives you a choice between &lt;code&gt;let&lt;/code&gt; (reassignable) and &lt;code&gt;const&lt;/code&gt; (not reassignable), where Go's &lt;code&gt;:=&lt;/code&gt; doesn't make that distinction for you upfront.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types are more of a suggestion than a rule
&lt;/h2&gt;

&lt;p&gt;Go locks in a variable's type the moment you declare 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="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;42&lt;/span&gt; &lt;span class="c"&gt;// compile error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript doesn't care:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// totally fine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was the biggest adjustment for me, not because it's hard, but because nothing stops you. In Go, the compiler catches a mistake like this before the program even runs. In JavaScript, the same mistake might run just fine and only cause problems three functions later, somewhere completely unrelated to where the actual issue started.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;undefined&lt;/code&gt; where Go would give you a zero value
&lt;/h2&gt;

&lt;p&gt;Go never leaves a variable truly empty, every type has a default:&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;var&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;  &lt;span class="c"&gt;// ""&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;    &lt;span class="c"&gt;// 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript has an actual concept of "nothing was assigned":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first this felt like less to worry about, no need to think about what a "default" looks like. Then I ran into &lt;code&gt;null&lt;/code&gt; as well, which is different from &lt;code&gt;undefined&lt;/code&gt; but similarly means "empty," and I realized JavaScript actually gives you two ways to represent nothing instead of Go's one predictable zero value. Keeping track of which parts of my code might produce &lt;code&gt;undefined&lt;/code&gt; versus &lt;code&gt;null&lt;/code&gt; versus an actual value took more mental effort than Go's zero values ever did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Curly braces, but looser about it
&lt;/h2&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;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&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;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"positive"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;positive&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript wants parentheses around the condition, Go doesn't. Small thing, but my hands kept leaving them out during that first week.&lt;/p&gt;

&lt;p&gt;The bigger surprise: JavaScript will run code with unused variables, unused function parameters, and even unreachable code without complaint. Go refuses to compile if you've declared something you never use. I didn't realize how much I'd come to rely on that safety net until it wasn't there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functions: fewer rules, but also fewer guarantees
&lt;/h2&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;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&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;No types to declare on the parameters, which felt freeing at first. It stopped feeling that way the first time I called &lt;code&gt;add("2", 3)&lt;/code&gt; and got &lt;code&gt;"23"&lt;/code&gt; back instead of an error. JavaScript will coerce values instead of stopping you, where Go would've refused to compile the mismatch entirely.&lt;/p&gt;

&lt;p&gt;Go's multiple return values also don't carry over. In Go, error handling is built into how a function returns:&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;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&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="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cannot divide by zero"&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="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript reaches for &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; instead, or a rejected Promise for anything asynchronous:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cannot divide by zero&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;try&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;Neither approach is wrong, but switching from checking &lt;code&gt;err != nil&lt;/code&gt; after every call to wrapping things in &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; blocks was a genuine shift in how I thought about where failures could happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semicolons, now optional-ish
&lt;/h2&gt;

&lt;p&gt;Go doesn't want you to type semicolons at all, the compiler inserts them for you. JavaScript technically has automatic semicolon insertion too, but most style guides still expect you to type them yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Amina&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leaving them off doesn't break anything most of the time, but it's inconsistent enough that I just kept typing them to be safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm taking into week two
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The freedom of not declaring types is genuinely convenient for quick scripts, and genuinely risky for anything larger, since nothing catches a type mistake until it actually runs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;undefined&lt;/code&gt; and &lt;code&gt;null&lt;/code&gt; both existing means I have to think about which one my code might actually produce, something Go's single zero-value concept never required.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; is a different mental model from Go's explicit error returns, not just a different keyword for the same idea.&lt;/li&gt;
&lt;li&gt;Async in JavaScript, &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; and Promises, doesn't map onto goroutines and channels at all. That one's going to take longer than a week to feel natural.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Coming from Go, JavaScript's flexibility felt like a relief at first and a little unsettling once I saw how much it lets slide. The syntax differences were the easy part to notice. The harder part was unlearning the assumption that the language would stop me before I made a mistake.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>go</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Challenges of working on two languages at the same time,ft Go and JS</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Wed, 22 Jul 2026 01:07:13 +0000</pubDate>
      <link>https://dev.to/odalov/challenges-of-working-on-two-languages-at-the-same-timeft-go-and-js-1fhn</link>
      <guid>https://dev.to/odalov/challenges-of-working-on-two-languages-at-the-same-timeft-go-and-js-1fhn</guid>
      <description>&lt;p&gt;Nobody tells you this part: when you're learning two languages at once, the hardest bugs aren't logic bugs. They're the moments where your fingers type the wrong language's syntax faster than your brain can stop them.&lt;/p&gt;

&lt;p&gt;If you're picking up Go while still writing JavaScript regularly, maybe a frontend project in one tab and a backend service in another, here's the whiplash you're probably already feeling, and what actually helps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The variable declaration trap
&lt;/h2&gt;

&lt;p&gt;JavaScript gives you &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;const&lt;/code&gt;, and &lt;code&gt;var&lt;/code&gt;. Go gives you &lt;code&gt;var&lt;/code&gt; too, but also &lt;code&gt;:=&lt;/code&gt; for short variable declarations inside functions. It's an easy swap to fumble:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// JavaScript&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Amina&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Go&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"Amina"&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The muscle memory clash goes both ways, writing &lt;code&gt;const&lt;/code&gt; in a &lt;code&gt;.go&lt;/code&gt; file , or writing &lt;code&gt;:=&lt;/code&gt; in JavaScript out of habit. Go's &lt;code&gt;:=&lt;/code&gt; only works inside function bodies too, which adds another layer: package-level variables need the full &lt;code&gt;var&lt;/code&gt; form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static typing
&lt;/h2&gt;

&lt;p&gt;This is the bigger mental gear-shift, and it's not really about syntax, it's about what the language expects from you.&lt;/p&gt;

&lt;p&gt;In JavaScript, you can do this without thinking twice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// 5&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&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;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// "23"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Go, the compiler stops you before you even run anything:&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;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try passing a string where an &lt;code&gt;int&lt;/code&gt; is expected and Go just won't compile. Coming from JS, where type coercion quietly does something for you , Go's refusal to guess feels strict at first. After a while, it stops feeling strict and starts feeling like the compiler is doing your job for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero values vs undefined/null
&lt;/h2&gt;

&lt;p&gt;This one causes real confusion, not just typo-level mistakes. In JavaScript, an unassigned variable is &lt;code&gt;undefined&lt;/code&gt;, and you can explicitly set something to &lt;code&gt;null&lt;/code&gt;. In Go, every type has a &lt;strong&gt;zero value&lt;/strong&gt;, variables are never "empty" the way JS variables can be:&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;var&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;       &lt;span class="c"&gt;// 0, not undefined&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;      &lt;span class="c"&gt;// "", not undefined&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;       &lt;span class="c"&gt;// false&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;        &lt;span class="c"&gt;// nil (this one's actually similar to JS's null)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gotcha: if you're used to checking &lt;code&gt;if (value)&lt;/code&gt; in JS to catch "nothing was set," that instinct doesn't transfer cleanly. A Go &lt;code&gt;int&lt;/code&gt; that's &lt;code&gt;0&lt;/code&gt; might mean "not set" or it might genuinely mean zero, the language won't tell you which, so you have to design for it .&lt;/p&gt;

&lt;h2&gt;
  
  
  Functions look similar until they don't
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&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;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;double&lt;/span&gt; &lt;span class="o"&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;x&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&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;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&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;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go doesn't have arrow function shorthand, and every parameter needs an explicit type. Multiple return values are where it really diverges, this is idiomatic Go and has no clean JS equivalent:&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;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&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="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"division by zero"&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="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&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;result&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;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&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="c"&gt;// handle it&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JS developers reach for &lt;code&gt;try/catch&lt;/code&gt; or reject a Promise. Go developers check &lt;code&gt;err != nil&lt;/code&gt; after basically every function call that can fail. It feels verbose at first; it becomes automatic fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async is a completely different animal, not just different syntax
&lt;/h2&gt;

&lt;p&gt;This is the one that trips people up longest, because it's not a vocabulary problem, it's a different model entirely.&lt;/p&gt;

&lt;p&gt;JavaScript is single-threaded with an event loop. &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; and Promises manage &lt;em&gt;when&lt;/em&gt; code runs, not &lt;em&gt;where&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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;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;`/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&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="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;Go has actual concurrent execution via goroutines, and channels for communication between them:&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;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultChan&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;someBlockingCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resultChan&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no direct JS analog to a goroutine, it's not "async but faster," it's genuinely a different concurrency model (real parallelism across OS threads, managed by Go's scheduler, vs. JS's cooperative single-threaded event loop). Trying to map one onto the other conceptually causes more confusion than just accepting they're different tools for different problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Braces, semicolons, and the small stuff
&lt;/h2&gt;

&lt;p&gt;Minor, but it adds up over a day of context-switching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go doesn't want semicolons at the end of statements (the compiler inserts them automatically); JS wants them, sort of, depending on who you ask about ASI.&lt;/li&gt;
&lt;li&gt;Go is strict about unused variables and unused imports, code that would just sit there unused in JS won't even compile in Go.&lt;/li&gt;
&lt;li&gt;Go's &lt;code&gt;if&lt;/code&gt; statements don't use parentheses around the condition; JS requires them.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Go&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// JavaScript&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of these are hard individually. They're just the kind of thing that quietly wrecks your flow when you're bouncing between a frontend tab and a backend tab in the same afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually helps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separate your terminals/windows by language&lt;/strong&gt;, not just by project, reduces the "which file am I even in" moment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lean into the differences instead of looking for symmetry.&lt;/strong&gt; Go and JS solve similar problems in genuinely different ways (typing, concurrency, error handling). Trying to make one feel like the other causes more confusion than accepting they're different tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let the compiler be your friend in Go.&lt;/strong&gt; Every "wait, why won't this compile" moment is usually catching something JS would've let through silently and let you debug at runtime instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write small, throwaway snippets when switching contexts&lt;/strong&gt;, a 5-line Go file just to remind your hands what &lt;code&gt;:=&lt;/code&gt; feels like before diving into a bigger task.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The syntax whiplash is real, and it doesn't fully go away, it just gets faster to recover from. The confusion isn't a sign you're doing something wrong; it's just what it feels like to hold two different mental models at once. Give it time, and your fingers eventually learn which language they're in before your brain has to think about it.&lt;/p&gt;

</description>
      <category>go</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Running PostgreSQL with Docker</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Wed, 22 Jul 2026 00:54:32 +0000</pubDate>
      <link>https://dev.to/odalov/running-postgresql-with-docker-14m2</link>
      <guid>https://dev.to/odalov/running-postgresql-with-docker-14m2</guid>
      <description>&lt;p&gt;Installing Postgres directly on your machine works, but it gets messy fast once you're juggling multiple projects that each want different versions, extensions, or seed data. Docker sidesteps all of that you get a clean, disposable Postgres instance per project, and your host machine stays untouched.&lt;/p&gt;

&lt;p&gt;This guide covers running Postgres in Docker for local development: quick one-off containers, &lt;code&gt;docker-compose&lt;/code&gt; for anything you'll come back to, persistent data, and a few things that trip people up.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The quickest way to get a Postgres instance running
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; my-postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;devuser &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; postgres:16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking that down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--name my-postgres&lt;/code&gt; — a friendly name so you can reference the container later instead of a random hash&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; — required; the container won't start without it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSTGRES_USER&lt;/code&gt; / &lt;code&gt;POSTGRES_DB&lt;/code&gt; — optional; default to &lt;code&gt;postgres&lt;/code&gt; if omitted&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p 5432:5432&lt;/code&gt; — maps container port 5432 to host port 5432&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt; — detached, runs in the background&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;postgres:16&lt;/code&gt; — pin a version; avoid &lt;code&gt;latest&lt;/code&gt; since it can silently jump major versions later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check it's running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect with &lt;code&gt;psql&lt;/code&gt; (if installed locally) or from inside the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; my-postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; devuser &lt;span class="nt"&gt;-d&lt;/span&gt; myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Using docker-compose for anything persistent
&lt;/h2&gt;

&lt;p&gt;For a real project, &lt;code&gt;docker-compose.yml&lt;/code&gt; is the better default , it's version-controlled, reproducible, and easy to extend with more services later (Redis, pgAdmin, your app itself).&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&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;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp-postgres&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&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_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devuser&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;secret&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&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;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pgdata:/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pgdata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop it (keeps data):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop and wipe data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Why the volume matters
&lt;/h2&gt;

&lt;p&gt;Without a named volume, all data lives inside the container's writable layer , delete the container, lose the database. The &lt;code&gt;pgdata:/var/lib/postgresql/data&lt;/code&gt; mapping above stores Postgres's actual data files in a Docker-managed volume that survives container restarts and even &lt;code&gt;docker compose down&lt;/code&gt; (without &lt;code&gt;-v&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To see where Docker keeps it on disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume inspect myapp_pgdata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather control the exact host path :&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;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./pgdata:/var/lib/postgresql/data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Seeding initial data
&lt;/h2&gt;

&lt;p&gt;Postgres's official image runs any &lt;code&gt;.sql&lt;/code&gt; or &lt;code&gt;.sh&lt;/code&gt; files placed in &lt;code&gt;/docker-entrypoint-initdb.d/&lt;/code&gt; , but only on first startup, when the data directory is empty.&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&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_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devuser&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;secret&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pgdata:/var/lib/postgresql/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./init:/docker-entrypoint-initdb.d&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pgdata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop a file at &lt;code&gt;./init/001-schema.sql&lt;/code&gt; with your &lt;code&gt;CREATE TABLE&lt;/code&gt; statements, and it runs automatically the first time the container spins up with a fresh volume. This is a solid pattern for local dev seed data, though it won't run again once the volume already has data , if you need to reseed, &lt;code&gt;docker compose down -v&lt;/code&gt; first.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Health checks
&lt;/h2&gt;

&lt;p&gt;If you're running your Go/Node/whatever app as another service in the same compose file, a health check stops it from starting before Postgres is actually ready to accept connections:&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&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_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devuser&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;secret&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&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;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pgdata:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&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;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;devuser&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-d&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;myapp"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

  &lt;span class="na"&gt;app&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;.&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&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;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&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;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://devuser:secret@db:5432/myapp&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pgdata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the connection string from &lt;code&gt;app&lt;/code&gt; uses &lt;code&gt;db&lt;/code&gt; as the host, not &lt;code&gt;localhost&lt;/code&gt; , inside Docker's network, services reach each other by container/service name.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Common gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"password authentication failed" after changing env vars.&lt;/strong&gt; Postgres only applies &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt;/&lt;code&gt;POSTGRES_USER&lt;/code&gt; on first initialization of an empty data directory. If you change them later, the existing volume still has the old credentials. You'll need to either update the password inside Postgres directly (&lt;code&gt;ALTER USER devuser WITH PASSWORD 'newpass';&lt;/code&gt;) or wipe the volume and start fresh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port already in use.&lt;/strong&gt; If Postgres is also installed locally, port 5432 will conflict. Either stop the local service or map to a different host port: &lt;code&gt;-p 5433:5432&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data "disappearing" between runs.&lt;/strong&gt; Almost always means no volume was mounted, or a different volume name was used across runs. Double-check &lt;code&gt;docker volume ls&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connecting from your host app vs. a containerized app.&lt;/strong&gt; From your host machine , use &lt;code&gt;localhost:5432&lt;/code&gt;. From another container in the same compose file, use the service name (&lt;code&gt;db:5432&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Quick reference
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# View logs&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; db

&lt;span class="c"&gt;# Open a psql shell&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp-postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; devuser &lt;span class="nt"&gt;-d&lt;/span&gt; myapp

&lt;span class="c"&gt;# Stop (keep data)&lt;/span&gt;
docker compose down

&lt;span class="c"&gt;# Stop and delete data&lt;/span&gt;
docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;span class="c"&gt;# Back up a database&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp-postgres pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; devuser myapp &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; backup.sql

&lt;span class="c"&gt;# Restore&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;backup.sql | docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; myapp-postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; devuser &lt;span class="nt"&gt;-d&lt;/span&gt; myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For local development, &lt;code&gt;docker-compose&lt;/code&gt; with a named volume covers almost everything you need: a clean, disposable Postgres instance, seed data on first run, and health checks so dependent services don't race ahead of the database. It's a small setup cost that saves you from "works on my machine" version mismatches down the line.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>docker</category>
    </item>
    <item>
      <title>GORM: Dev's Guide to Go's Most Popular ORM</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Wed, 22 Jul 2026 00:38:39 +0000</pubDate>
      <link>https://dev.to/odalov/gorm-devs-guide-to-gos-most-popular-orm-3263</link>
      <guid>https://dev.to/odalov/gorm-devs-guide-to-gos-most-popular-orm-3263</guid>
      <description>&lt;p&gt;If you're working with Go services that talk to a relational database, chances are you've bumped into &lt;strong&gt;GORM&lt;/strong&gt;. It's the most widely used ORM in the Go ecosystem, and for good reason. It wraps a lot of the tedium of &lt;code&gt;database/sql&lt;/code&gt; ,manual scanning, hand-written migrations, string-built queries in a much friendlier API.&lt;/p&gt;

&lt;p&gt;This article walks through GORM from setup to the patterns you'll actually use day to day: models, migrations, CRUD, associations, transactions, and a few gotchas that trip people up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why reach for an ORM in Go ?
&lt;/h2&gt;

&lt;p&gt;Go's standard &lt;code&gt;database/sql&lt;/code&gt; package is deliberately low-level. You write SQL strings, manually scan rows into structs, and manage connections yourself. That's fine for small projects, but it gets repetitive fast once you have a dozen tables and endpoints that all need similar create/read/update/delete logic.&lt;/p&gt;

&lt;p&gt;GORM sits on top of &lt;code&gt;database/sql&lt;/code&gt; and gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Struct-based models mapped to tables&lt;/li&gt;
&lt;li&gt;Auto migrations&lt;/li&gt;
&lt;li&gt;A chainable query builder&lt;/li&gt;
&lt;li&gt;Associations (has-one, has-many, many-to-many, belongs-to)&lt;/li&gt;
&lt;li&gt;Hooks (before/after create, update, delete)&lt;/li&gt;
&lt;li&gt;Built-in support for transactions, connection pooling, and prepared statements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It supports PostgreSQL, MySQL, SQLite, SQL Server, and more, through swappable drivers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get &lt;span class="nt"&gt;-u&lt;/span&gt; gorm.io/gorm
go get &lt;span class="nt"&gt;-u&lt;/span&gt; gorm.io/driver/postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap &lt;code&gt;postgres&lt;/code&gt; for &lt;code&gt;mysql&lt;/code&gt;, &lt;code&gt;sqlite&lt;/code&gt;, or &lt;code&gt;sqlserver&lt;/code&gt; depending on your database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to a database
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"gorm.io/driver/postgres"&lt;/span&gt;
    &lt;span class="s"&gt;"gorm.io/gorm"&lt;/span&gt;
    &lt;span class="s"&gt;"gorm.io/gorm/logger"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"host=localhost user=postgres password=secret dbname=myapp port=5432 sslmode=disable"&lt;/span&gt;

    &lt;span class="n"&gt;db&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;gorm&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="n"&gt;postgres&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="n"&gt;dsn&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;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&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="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;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to connect to database: %v"&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;sqlDB&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;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&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;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to get generic db object: %v"&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;sqlDB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetMaxOpenConns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sqlDB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetMaxIdleConns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&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;That &lt;code&gt;db.DB()&lt;/code&gt; call gives you the underlying &lt;code&gt;*sql.DB&lt;/code&gt;, which is where connection pool settings live. It's easy to forget this step and end up with an ORM that opens far more connections than your database can handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining models
&lt;/h2&gt;

&lt;p&gt;GORM models are plain structs with tags:&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;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;        &lt;span class="kt"&gt;uint&lt;/span&gt;   &lt;span class="s"&gt;`gorm:"primaryKey"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`gorm:"size:100;not null"`&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`gorm:"uniqueIndex;not null"`&lt;/span&gt;
    &lt;span class="n"&gt;Posts&lt;/span&gt;     &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;
    &lt;span class="n"&gt;CreatedAt&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;
    &lt;span class="n"&gt;UpdatedAt&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;
    &lt;span class="n"&gt;DeletedAt&lt;/span&gt; &lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeletedAt&lt;/span&gt; &lt;span class="s"&gt;`gorm:"index"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;       &lt;span class="kt"&gt;uint&lt;/span&gt;   &lt;span class="s"&gt;`gorm:"primaryKey"`&lt;/span&gt;
    &lt;span class="n"&gt;Title&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`gorm:"size:255;not null"`&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;UserID&lt;/span&gt;   &lt;span class="kt"&gt;uint&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CreatedAt&lt;/code&gt; / &lt;code&gt;UpdatedAt&lt;/code&gt; are populated automatically by GORM , no extra code needed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DeletedAt gorm.DeletedAt&lt;/code&gt; enables &lt;strong&gt;soft deletes&lt;/strong&gt;. Calling &lt;code&gt;db.Delete(&amp;amp;user)&lt;/code&gt; won't actually remove the row; it sets &lt;code&gt;deleted_at&lt;/code&gt; and every future query filters those rows out automatically.&lt;/li&gt;
&lt;li&gt;Field-level tags (&lt;code&gt;size&lt;/code&gt;, &lt;code&gt;not null&lt;/code&gt;, &lt;code&gt;uniqueIndex&lt;/code&gt;) get translated into actual SQL constraints during migration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Auto migrations
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoMigrate&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;User&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;Post&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates tables if they don't exist and adds missing columns/indexes. It won't drop columns or change types that could cause data loss , which is a deliberate safety choice, but it also means &lt;code&gt;AutoMigrate&lt;/code&gt; isn't a full substitute for a proper migration tool once you're in production. Many teams use it for local dev and rely on something like &lt;code&gt;golang-migrate&lt;/code&gt; or &lt;code&gt;atlas&lt;/code&gt; for production schema changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic CRUD
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Create:&lt;/strong&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;user&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Amina Otieno"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"amina@example.com"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&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;user&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;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&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;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&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;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New user ID:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Read:&lt;/strong&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="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;First&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                   
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;First&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"email = ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"amina@example.com"&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;users&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name LIKE ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%Otieno%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&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;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&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;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&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;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Amina O."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Update multiple fields&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&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;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Updates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Amina O."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"new@example.com"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;Updates&lt;/code&gt; with a struct only updates non-zero fields. If you need to set a field to its zero value (empty string, 0, false), use a &lt;code&gt;map[string]interface{}&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete:&lt;/strong&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;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Delete&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;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Associations and preloading
&lt;/h2&gt;

&lt;p&gt;Given the &lt;code&gt;User&lt;/code&gt;/&lt;code&gt;Post&lt;/code&gt; relationship above, GORM can eager-load associations to avoid N+1 query problems:&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;var&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Posts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&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;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs two queries total (one for users, one for all related posts), rather than one query per user. If you only need a subset of associated records, &lt;code&gt;Preload&lt;/code&gt; accepts conditions too:&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;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Posts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"created_at &amp;gt; ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;someDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&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;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For many-to-many relationships, GORM manages the join table for you:&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;type&lt;/span&gt; &lt;span class="n"&gt;Tag&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;    &lt;span class="kt"&gt;uint&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Posts&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt; &lt;span class="s"&gt;`gorm:"many2many:post_tags;"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Transactions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transaction&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;tx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&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="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&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;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&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="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="c"&gt;// rolls back&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="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&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;Post&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"First post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&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="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;span class="k"&gt;return&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;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;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"transaction failed:"&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;This is the pattern you want anywhere multiple writes need to succeed or fail together — say, deducting a balance and recording a ledger entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks
&lt;/h2&gt;

&lt;p&gt;GORM calls certain methods automatically if your model defines them:&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;BeforeCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Available hooks include &lt;code&gt;BeforeCreate&lt;/code&gt;, &lt;code&gt;AfterCreate&lt;/code&gt;, &lt;code&gt;BeforeUpdate&lt;/code&gt;, &lt;code&gt;AfterUpdate&lt;/code&gt;, &lt;code&gt;BeforeDelete&lt;/code&gt;, &lt;code&gt;AfterDelete&lt;/code&gt;, and their &lt;code&gt;*Save&lt;/code&gt; equivalents. Useful for normalization, validation, or audit logging without cluttering your handler code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things worth knowing
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero values in updates.&lt;/strong&gt; As mentioned above, &lt;code&gt;Updates()&lt;/code&gt; with a struct silently skips zero-valued fields. This bites people when they try to clear a field to &lt;code&gt;""&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soft delete surprises.&lt;/strong&gt; If &lt;code&gt;DeletedAt&lt;/code&gt; is present on a model, every &lt;code&gt;Find&lt;/code&gt;/&lt;code&gt;First&lt;/code&gt;/&lt;code&gt;Where&lt;/code&gt; call filters out soft-deleted rows by default. To include them, use &lt;code&gt;.Unscoped()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;N+1 queries.&lt;/strong&gt; Forgetting &lt;code&gt;Preload&lt;/code&gt; on associations is the most common performance issue in GORM codebases. Turn on &lt;code&gt;logger.Info&lt;/code&gt; mode in development so you can actually see the queries GORM is generating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context propagation.&lt;/strong&gt; Use &lt;code&gt;db.WithContext(ctx)&lt;/code&gt; in request-scoped code so query cancellation and timeouts actually work with your HTTP handler's context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection pool tuning.&lt;/strong&gt; Don't skip &lt;code&gt;SetMaxOpenConns&lt;/code&gt;/&lt;code&gt;SetMaxIdleConns&lt;/code&gt; , the defaults aren't tuned for production load.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;GORM won't eliminate the need to understand SQL , and you shouldn't want it to , but it removes a lot of boilerplate around scanning rows, building migrations, and wiring up associations. For most Go backend services, especially ones backed by PostgreSQL, it hits a solid balance between productivity and control.&lt;/p&gt;

</description>
      <category>go</category>
      <category>postgres</category>
      <category>gorm</category>
    </item>
    <item>
      <title>Postgres vs SQLite</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Thu, 16 Jul 2026 14:17:08 +0000</pubDate>
      <link>https://dev.to/odalov/postgres-vs-sqlite-4j25</link>
      <guid>https://dev.to/odalov/postgres-vs-sqlite-4j25</guid>
      <description>&lt;p&gt;SQLite is one of the most deployed pieces of software on the planet ,it's everywhere. It's also a genuinely great database for a huge range of apps. So why would anyone reach for Postgres instead?&lt;/p&gt;

&lt;p&gt;The honest answer: because SQLite and Postgres aren't really competing for the same job. One is an embedded file format with a SQL interface. The other is a client-server database designed to be shared. Once you understand that distinction, it's obvious when each one wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency
&lt;/h2&gt;

&lt;p&gt;SQLite locks the entire database file when a write happens. Older versions blocked all other writers (and in some modes, readers) until that write finished. Newer WAL mode improves concurrent reads during writes, but SQLite still fundamentally serializes writes  there's no getting around that it's one writer at a time.&lt;/p&gt;

&lt;p&gt;Postgres uses MVCC (multi-version concurrency control), so readers never block writers and writers don't block readers. Multiple clients can write to different rows simultaneously without stepping on each other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Postgres: two clients can safely update different rows of the same table&lt;/span&gt;
&lt;span class="c1"&gt;-- at the same time, with no manual locking required&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your app has more than one process or more than a handful of concurrent users writing data, this alone usually settles the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's a server, not a file
&lt;/h2&gt;

&lt;p&gt;SQLite reads and writes a single file on disk from inside your application process. That's its superpower for embedded use — and its ceiling for anything else. There's no network protocol, no remote access, no separate process to manage.&lt;/p&gt;

&lt;p&gt;Postgres runs as its own server that any number of applications, services, or machines can connect to over the network:&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;# Any service on your network can connect to the same database&lt;/span&gt;
psql &lt;span class="s2"&gt;"postgresql://app_user:password@db.internal:5432/production"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the difference between "one app owns this data" and "this data is a shared resource for your whole system" , which matters the moment you have more than one service (an API, a background worker, an analytics job) that all need the same data.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real type system
&lt;/h2&gt;

&lt;p&gt;SQLite is dynamically typed by default ,a column declared &lt;code&gt;INTEGER&lt;/code&gt; will still accept text unless you explicitly opt into strict typing (added in SQLite 3.37+). Postgres enforces types strictly by default, and gives you far more of them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Postgres: rich, enforced types out of the box&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;roles&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&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;Arrays, JSONB, UUIDs, ranges, network address types, enums. These aren't bolted on, they're core to how Postgres models data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying power
&lt;/h2&gt;

&lt;p&gt;SQLite's SQL support is deliberately minimal, which keeps it small and fast to embed. Postgres supports the full range of modern SQL plus extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Window functions: rank each order within a customer's history&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;spend_rank&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Full-text search built into the engine, no extra service required&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt; &lt;span class="n"&gt;to_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'database &amp;amp; performance'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things like materialized views, recursive CTEs at scale, PostGIS for geospatial data, and pgvector for embeddings simply aren't in SQLite's scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access control
&lt;/h2&gt;

&lt;p&gt;SQLite has no concept of users or permissions, if a process can read the file, it can read (and write) everything in it. Access control is whatever your filesystem gives you.&lt;/p&gt;

&lt;p&gt;Postgres has real roles, grants, and even row level security:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Restrict a role to only see rows belonging to their own tenant&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;tenant_isolation&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.tenant_id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If different users or services need different levels of access to the same data, Postgres can enforce that at the database layer instead of trusting every client to behave.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where SQLite still wins
&lt;/h2&gt;

&lt;p&gt;None of this makes SQLite a worse database — it makes it a different one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero ops.&lt;/strong&gt; No server to install, patch, or monitor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portability.&lt;/strong&gt; A whole database is one file you can copy, email, or check into version control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed for local, single-writer workloads.&lt;/strong&gt; Mobile apps, desktop apps, browser storage, embedded devices, CLI tools, and tests are often faster and simpler with SQLite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity.&lt;/strong&gt; For a prototype or a small app that will only ever run as one process, standing up Postgres is pure overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The actual decision
&lt;/h2&gt;

&lt;p&gt;Ask one question: &lt;strong&gt;does more than one process need to write to this data?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If no — one app, one process, local file — SQLite is usually the better call.&lt;/li&gt;
&lt;li&gt;If yes — multiple services, multiple users, anything accessed over a network . Postgres is built for exactly that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plenty of successful products start on SQLite and migrate to Postgres later, and that's a fine path. The mistake is picking SQLite for a multi-writer, networked system because it seemed simpler at the start, and picking Postgres for a single-user local app because it seemed more "production-grade." Match the tool to the shape of the problem, not its reputation.&lt;/p&gt;




</description>
      <category>architecture</category>
      <category>database</category>
      <category>postgres</category>
      <category>sql</category>
    </item>
    <item>
      <title>Building on the Blockchain: A Developer's Guide to Solidity &amp; Smart Contracts</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Mon, 01 Jun 2026 17:48:28 +0000</pubDate>
      <link>https://dev.to/odalov/building-on-the-blockchain-a-developers-guide-to-solidity-smart-contracts-414c</link>
      <guid>https://dev.to/odalov/building-on-the-blockchain-a-developers-guide-to-solidity-smart-contracts-414c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Code is law — and on Ethereum, it runs forever."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Is a Smart Contract?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;smart contract&lt;/strong&gt; is a self-executing program stored on a blockchain. It runs automatically when predefined conditions are met. No middlemen, no downtime, no censorship.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Immutable (once deployed, the logic doesn't change)&lt;/li&gt;
&lt;li&gt;Transparent (anyone can read the code)&lt;/li&gt;
&lt;li&gt;Trustless (no central authority needed)&lt;/li&gt;
&lt;li&gt;Global (accessible from anywhere on Earth)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Solidity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Solidity&lt;/strong&gt; is the primary language for writing smart contracts on Ethereum and EVM-compatible chains like Polygon, BNB Chain, Avalanche, Base and others.&lt;/p&gt;

&lt;p&gt;It's a statically-typed, contract-oriented language with syntax that feels familiar if you've used JavaScript, C++, or Java.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract HelloBlockchain {
    string public message = "gm, world";

    function setMessage(string calldata _msg) external {
        message = _msg;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Core Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. State Variables &amp;amp; Storage
&lt;/h3&gt;

&lt;p&gt;Everything stored on-chain costs &lt;strong&gt;gas&lt;/strong&gt;. Be deliberate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract Bank {
    mapping(address =&amp;gt; uint256) public balances; // stored on-chain
    uint256 public totalDeposits;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Functions &amp;amp; Visibility
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modifier&lt;/th&gt;
&lt;th&gt;Who Can Call&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;public&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anyone (internal + external)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;external&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only from outside the contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;internal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only this contract + children&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;private&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only this contract&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Payable Functions — Receiving ETH
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function deposit() external payable {
    balances[msg.sender] += msg.value;
    totalDeposits += msg.value;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;msg.sender&lt;/code&gt; = the caller's address&lt;br&gt;&lt;br&gt;
&lt;code&gt;msg.value&lt;/code&gt; = ETH sent with the call (in wei)&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Events — Your On-Chain Logs
&lt;/h3&gt;

&lt;p&gt;Events are cheap to emit and essential for off-chain apps to track activity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event Deposited(address indexed user, uint256 amount);

function deposit() external payable {
    balances[msg.sender] += msg.value;
    emit Deposited(msg.sender, msg.value);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Modifiers — Reusable Guards
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modifier onlyOwner() {
    require(msg.sender == owner, "Not the owner");
    _;
}

function withdraw(uint256 amount) external onlyOwner {
    payable(owner).transfer(amount);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  A Real Example: Simple Token
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract SimpleToken {
    string public name = "DevToken";
    string public symbol = "DEV";
    uint8 public decimals = 18;
    uint256 public totalSupply;

    mapping(address =&amp;gt; uint256) public balanceOf;

    event Transfer(address indexed from, address indexed to, uint256 value);

    constructor(uint256 _initialSupply) {
        totalSupply = _initialSupply * 10 ** decimals;
        balanceOf[msg.sender] = totalSupply;
    }

    function transfer(address to, uint256 amount) external returns (bool) {
        require(balanceOf[msg.sender] &amp;gt;= amount, "Insufficient balance");
        balanceOf[msg.sender] -= amount;
        balanceOf[to] += amount;
        emit Transfer(msg.sender, to, amount);
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the essence of an &lt;strong&gt;ERC-20&lt;/strong&gt; token. The real standard adds &lt;code&gt;approve&lt;/code&gt;, &lt;code&gt;transferFrom&lt;/code&gt;, and &lt;code&gt;allowance&lt;/code&gt;  but this gives you the foundation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security: The Non-Negotiables
&lt;/h2&gt;

&lt;p&gt;Smart contract bugs are &lt;strong&gt;permanent and public&lt;/strong&gt;. The stakes are high.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reentrancy Attack
&lt;/h3&gt;

&lt;p&gt;The infamous bug behind the $60M DAO hack (2016).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// VULNERABLE 
function withdraw() external {
    uint256 amount = balances[msg.sender];
    (bool success,) = msg.sender.call{value: amount}(""); // external call BEFORE state update
    balances[msg.sender] = 0; // too late
}

// SAFE 
function withdraw() external {
    uint256 amount = balances[msg.sender];
    balances[msg.sender] = 0;           // 1. Update state
    (bool success,) = msg.sender.call{value: amount}(""); // 2. Then interact
    require(success, "Transfer failed");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; Always update state &lt;em&gt;before&lt;/em&gt; making external calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Common Pitfalls
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Integer overflow/underflow&lt;/strong&gt; — Solidity 0.8+ handles this automatically with built-in checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tx.origin vs msg.sender&lt;/strong&gt; — Never use &lt;code&gt;tx.origin&lt;/code&gt; for authorization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unchecked return values&lt;/strong&gt; — Always check the return value of &lt;code&gt;call()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp dependence&lt;/strong&gt; — &lt;code&gt;block.timestamp&lt;/code&gt; can be manipulated slightly by miners&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Developer Toolchain
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hardhat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local dev environment, testing, deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Foundry&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Blazing-fast testing in Solidity itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Remix IDE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser-based IDE, great for prototyping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenZeppelin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Battle-tested contract libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ethers.js / Viem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JavaScript libraries to interact with contracts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alchemy / Infura&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node providers for mainnet/testnet access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Quick Start with Hardhat
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-contract &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my-contract
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; hardhat
npx hardhat init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy a Contract
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// scripts/deploy.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hardhat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&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;main&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SimpleToken&lt;/span&gt;&lt;span class="dl"&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForDeployment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deployed to:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAddress&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat run scripts/deploy.js &lt;span class="nt"&gt;--network&lt;/span&gt; sepolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Gas Optimization Tips
&lt;/h2&gt;

&lt;p&gt;Gas efficiency = lower costs for your users = competitive advantage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//  Expensive: reading from storage in a loop
for (uint i = 0; i &amp;lt; users.length; i++) {
    total += balances[users[i]];
}

//  Cheaper: cache storage reads in memory
uint256 len = users.length;
for (uint i = 0; i &amp;lt; len; i++) {
    total += balances[users[i]];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other wins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;uint256&lt;/code&gt; over smaller types ,the EVM works in 32-byte slots.&lt;/li&gt;
&lt;li&gt;Mark functions &lt;code&gt;view&lt;/code&gt; or &lt;code&gt;pure&lt;/code&gt; when they don't modify state&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;calldata&lt;/code&gt; instead of &lt;code&gt;memory&lt;/code&gt; for external function parameters&lt;/li&gt;
&lt;li&gt;Pack struct variables to fit in fewer storage slots&lt;/li&gt;
&lt;li&gt;Emit events instead of storing data you only need off-chain&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Contract Standards Worth Knowing
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Standard&lt;/th&gt;
&lt;th&gt;What It Is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ERC-20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fungible tokens (USDC, DAI, UNI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ERC-721&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Non-fungible tokens / NFTs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ERC-1155&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-token standard (games, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ERC-4626&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tokenized vaults (DeFi yield)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EIP-2612&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gasless approvals via signatures&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;OpenZeppelin has production-ready implementations of all of these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor() ERC20("MyToken", "MTK") {
        _mint(msg.sender, 1_000_000 * 10 ** decimals());
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/Token.test.js (Hardhat + Ethers)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hardhat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SimpleToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should assign total supply to deployer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigners&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SimpleToken&lt;/span&gt;&lt;span class="dl"&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&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;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;totalSupply&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should transfer tokens correctly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigners&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SimpleToken&lt;/span&gt;&lt;span class="dl"&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseUnits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&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;recipientBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipientBalance&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseUnits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&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;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Smart contract development is one of the most &lt;strong&gt;high-stakes&lt;/strong&gt; disciplines in software. A bug that slips through in a web app might annoy users. A bug in a smart contract holding $10M can be catastrophic and irreversible.&lt;/p&gt;

&lt;p&gt;The upside? You're building &lt;strong&gt;programmable money&lt;/strong&gt;, &lt;strong&gt;unstoppable applications&lt;/strong&gt;, and &lt;strong&gt;trustless infrastructure&lt;/strong&gt; that can outlive any company or server.&lt;/p&gt;




</description>
      <category>blockchain</category>
      <category>ethereum</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Middleware in Go</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Thu, 14 May 2026 16:26:13 +0000</pubDate>
      <link>https://dev.to/odalov/middleware-in-go-2b9n</link>
      <guid>https://dev.to/odalov/middleware-in-go-2b9n</guid>
      <description>&lt;p&gt;If you've built any web application in Go, you've probably heard about middlewares. But what exactly are they?&lt;/p&gt;

&lt;p&gt;A middleware is just a function that sits between your HTTP server and your route handlers. It can inspect, modify, or even stop a request before it reaches your main application logic.&lt;/p&gt;

&lt;p&gt;Think of it as a pipeline.Every request passes through each middleware in order before hitting your handler, and the response travels back through them on the way out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;A middleware accepts a handler and returns a new one:&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;MyMiddleware&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;HandlerFunc&lt;/span&gt;&lt;span class="p"&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;return&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;c&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// runs before your handler&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;// runs after your handler&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  c.Next()
&lt;/h2&gt;

&lt;p&gt;When you register multiple middlewares, your framework doesn't run them independently. It builds a chain. Each middleware wraps the next one, like nested functions. &lt;code&gt;c.Next()&lt;/code&gt; is the call that says ,pass control to whatever comes next in that chain.&lt;/p&gt;

&lt;p&gt;Without it, the chain stops dead. Your route handler never runs, and the client gets no response.&lt;/p&gt;

&lt;p&gt;This also means you can intentionally skip it to reject a request early:&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;RequireAuth&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;HandlerFunc&lt;/span&gt;&lt;span class="p"&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;return&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;c&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&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;c&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&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;StatusUnauthorized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"unauthorized"&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="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// only reach here if auth passes&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;h2&gt;
  
  
  Registering middleware
&lt;/h2&gt;

&lt;p&gt;Most frameworks let you attach middleware at three levels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Global&lt;/strong&gt; middleware runs on every single request your app receives ,logger and panic recovery belong here:&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;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Recover&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Group&lt;/strong&gt; middleware applies only to a subset of routes. This is the right place for auth ,you probably don't want to guard your &lt;code&gt;/health&lt;/code&gt; endpoint the same way you guard &lt;code&gt;/api/users&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;api&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequireAuth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Route-level&lt;/strong&gt; middleware is for one-off logic that doesn't belong anywhere else:&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;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;adminHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RequireAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a middleware needs to run everywhere, make it global. If it only makes sense for a set of routes, scope it to a group. Route-level is a last resort.&lt;/p&gt;

</description>
      <category>go</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Kenya's proposed AI Bill 2026</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Mon, 11 May 2026 08:30:49 +0000</pubDate>
      <link>https://dev.to/odalov/kenyas-proposed-ai-bill-2026-2n4h</link>
      <guid>https://dev.to/odalov/kenyas-proposed-ai-bill-2026-2n4h</guid>
      <description>&lt;p&gt;Kenya has the &lt;strong&gt;highest AI adoption rate globally&lt;/strong&gt; – 97.5% of internet users engage with AI monthly (Digital 2026). 68% of Kenyan firms aim for full AI adoption by end of 2026 (KPMG).&lt;/p&gt;

&lt;p&gt;But the proposed &lt;strong&gt;AI Bill, 2026&lt;/strong&gt; has issues:&lt;br&gt;
&lt;strong&gt;1. Prison time for locals.&lt;/strong&gt; Violations carry fines up to $38,000 and &lt;strong&gt;three years in jail&lt;/strong&gt;. Directors must prove their innocence, reversing Kenya's constitutional burden of proof.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A loophole for Big Tech.&lt;/strong&gt; OpenAI's clinical AI runs in 16 Nairobi clinics. A Dutch TB screener processes thousands of X-rays. Google is testing maternal ultrasound. &lt;strong&gt;None are explicitly covered.&lt;/strong&gt; A local dev faces prison. OpenAI faces nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. No evidence of harm.&lt;/strong&gt; The Bill was drafted without consulting health, agriculture, or finance sectors about what AI harms they actually experience. It defaults to maximum deterrence (prison) by reflex, not data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Remove criminal penalties, close the foreign loophole, and follow the EU model of administrative fines.&lt;/p&gt;

&lt;p&gt;Kenya's developers deserve precision.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>kenya</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Mon, 11 May 2026 08:23:33 +0000</pubDate>
      <link>https://dev.to/odalov/-4aoa</link>
      <guid>https://dev.to/odalov/-4aoa</guid>
      <description></description>
    </item>
    <item>
      <title>Digital Hoarding</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Sat, 11 Apr 2026 15:52:47 +0000</pubDate>
      <link>https://dev.to/odalov/digital-hoarding-3p5h</link>
      <guid>https://dev.to/odalov/digital-hoarding-3p5h</guid>
      <description>&lt;p&gt;&lt;em&gt;A personal journey through 4GB of laptop clutter and over 800 phone screenshots I'll never look at again&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The discovery
&lt;/h2&gt;

&lt;p&gt;During some late-night reading, I stumbled across a 2025 paper by Liu &amp;amp; Liu in &lt;em&gt;Frontiers in Psychology&lt;/em&gt; on something called &lt;strong&gt;digital hoarding&lt;/strong&gt;.&lt;br&gt;
I'd never heard the term before.&lt;br&gt;
But as I kept reading, I felt Called out.&lt;/p&gt;

&lt;p&gt;They defined it as: &lt;em&gt;"The compulsive accumulation of digital files to the point of distress and disorganization."&lt;/em&gt;&lt;br&gt;
And I thought to myself ,that's not only a research subject. that's my laptop,and probably my phone too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On my laptop:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/Downloads&lt;/code&gt; – 47 PDFs, 12 driver installers, 3 tutorials I never finished. Oldest: 2019&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/Desktop&lt;/code&gt; – Screenshots of error messages I Googled and fixed. Oldest: 2021&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/code/experiments&lt;/code&gt; – 23 half-built projects, 18 with broken dependencies. Oldest: 2020&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/Documents/old-work&lt;/code&gt; – Repos from two jobs ago. Two jobs ago! Oldest: 2018&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;On my phone:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Screenshots folder – 1,112 images. Error messages, tweets, "read later" articles, memes.&lt;/li&gt;
&lt;li&gt;Photos (duplicates) – The same picture saved 3-4 times.&lt;/li&gt;
&lt;li&gt;Old app caches – 2.1 GB from apps I haven't opened in months.&lt;/li&gt;
&lt;li&gt;Voice notes – 47 recordings. "Reminder to self" from 2022 to 2026.&lt;/li&gt;
&lt;li&gt;WhatsApp images – From groups I left in 2023.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was a digital hoarder with a terminal and a smartphone addiction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Liu &amp;amp; Liu taught me
&lt;/h2&gt;

&lt;p&gt;The paper described digital hoarding as a &lt;strong&gt;"double-edged sword."&lt;/strong&gt;&lt;br&gt;
On one edge: It feels responsible. "I might need this someday." "Better to save it than lose it."&lt;br&gt;
On the other edge: It's actively hurting me — on every device I own.&lt;br&gt;
They listed consequences that hit too close to home:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cognitive load&lt;/strong&gt; – Every extra file is a tiny decision I don't make&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decision fatigue&lt;/strong&gt; – Which of these 14 project folders is the real one?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anxiety&lt;/strong&gt; – "What if I delete something important?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Battery drain&lt;/strong&gt; – Your phone constantly indexing thousands of unused files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the part that really got me: &lt;em&gt;"A protective behavior that eventually becomes a burden."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The hidden cost never measured
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before cleanup:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage used: 10 GB&lt;/li&gt;
&lt;li&gt;Boot time: 48 seconds&lt;/li&gt;
&lt;li&gt;Search time: 6-8 seconds&lt;/li&gt;
&lt;li&gt;Battery life: ~6 hours&lt;/li&gt;
&lt;li&gt;Backup time: 45 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After deleting 500+ old files and 2,000+ screenshots:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage used: 8 GB (down 2 GB)&lt;/li&gt;
&lt;li&gt;Boot time: 31 seconds (down 35%)&lt;/li&gt;
&lt;li&gt;Search time: 1-2 seconds (down 75%)&lt;/li&gt;
&lt;li&gt;Battery life: ~8 hours (up 33%)&lt;/li&gt;
&lt;li&gt;Backup time: 12 minutes (down 73%)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No hardware upgrade. No factory reset. Just… deleting things.&lt;/p&gt;




&lt;h2&gt;
  
  
  The developer-specific hoarding patterns
&lt;/h2&gt;

&lt;p&gt;Liu &amp;amp; Liu didn't study developers specifically. But I noticed patterns unique to our craft — and how they spill onto our phones.&lt;/p&gt;

&lt;h3&gt;
  
  
  1: The tutorial graveyard
&lt;/h3&gt;

&lt;p&gt;We start a tutorial. Clone the repo. Follow along for 30 minutes. Get distracted. Never finish.&lt;br&gt;
But we keep the folder. &lt;em&gt;Just in case.&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;My count:&lt;/strong&gt; 23 unfinished tutorial projects. 18 using packages that are now deprecated.&lt;/p&gt;

&lt;h3&gt;
  
  
  2: The error screenshot museum
&lt;/h3&gt;

&lt;p&gt;Something breaks. We screenshot the error on our laptop.  Then we Google it. Fix it. Feel proud.&lt;br&gt;
But we never delete some of the screenshots.&lt;br&gt;
&lt;strong&gt;My count:&lt;/strong&gt; 110 laptop screenshots + 1,110 phone screenshots. I will never look at 99% of them again.&lt;/p&gt;

&lt;h3&gt;
  
  
  3: I'll refactor this someday
&lt;/h3&gt;

&lt;p&gt;Old projects. Old code. Old mistakes. We keep them like trophies.&lt;br&gt;
&lt;strong&gt;My count:&lt;/strong&gt; 4GB of code I haven't touched in over two years.&lt;/p&gt;

&lt;h3&gt;
  
  
  4: The screenshot spiral
&lt;/h3&gt;

&lt;p&gt;See something interesting? Screenshot. Want to remember a tweet? Screenshot. Need to save a receipt? Screenshot.&lt;br&gt;
Then never organize them. Never look at them. Never delete them.&lt;br&gt;
&lt;strong&gt;My count:&lt;/strong&gt; 1,112 screenshots. I remember taking maybe 200 of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  5: The "I'll clean this later" cache (phone)
&lt;/h3&gt;

&lt;p&gt;Apps cache images, videos, and files. We ignore it. It grows. We run out of storage. &lt;br&gt;
&lt;strong&gt;My count:&lt;/strong&gt; 2 GB of "System Data" — a black hole of forgotten files.&lt;/p&gt;




&lt;h4&gt;
  
  
  Liu &amp;amp; Liu ended their paper with a question:
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;"How do we design systems that support selective retention rather than indiscriminate accumulation?"&lt;/em&gt;&lt;br&gt;
I don't have an answer. But for me,I'll start small. One old file. One screenshot. One deleted folder at a time.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Progressive Web Apps (PWAs): The Best of Both Worlds</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Sun, 01 Mar 2026 12:38:02 +0000</pubDate>
      <link>https://dev.to/odalov/progressive-web-apps-pwas-the-best-of-both-worlds-49gl</link>
      <guid>https://dev.to/odalov/progressive-web-apps-pwas-the-best-of-both-worlds-49gl</guid>
      <description>&lt;h2&gt;
  
  
  What is PWA?
&lt;/h2&gt;

&lt;p&gt;Progressive Web Apps are regular websites built with standard web technologies such as HTML, CSS and JavaScript, that progressively enhance to deliver an app-like experience. They work in any browser but can be installed on your device like a native app. Think of them as websites that put on an app costume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Technologies
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;HTTPS - Secure connection required for all modern web capabilities.&lt;/li&gt;
&lt;li&gt;Web App Manifest - A JSON file defining your app's name, icons, colors, and launch behavior.&lt;/li&gt;
&lt;li&gt;Service Workers - Background scripts that enable offline functionality, caching, and push notifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why PWAs Matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  For Users
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;    Install with one click - No app stores, no approvals, no fees.&lt;/li&gt;
&lt;li&gt;    Works offline - Access content even without internet.&lt;/li&gt;
&lt;li&gt;    Lightweight &lt;/li&gt;
&lt;li&gt;    Cross-platform - Same app works on any device with a browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  For Developers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; Single codebase - One app for all platforms instead of separate iOS/Android builds.&lt;/li&gt;
&lt;li&gt;    Lower costs - PWA development ranges from $15,000-$150,000 versus $50,000+ per native platform.&lt;/li&gt;
&lt;li&gt;    No app store gatekeepers - Deploy updates instantly without waiting for approval.&lt;/li&gt;
&lt;li&gt;    SEO-friendly - PWAs are indexable by search engines unlike native apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Microsoft:
&lt;/h3&gt;

&lt;p&gt;Microsoft took a data-driven approach to PWAs, automatically identifying 1.5 million progressive web apps for inclusion in the Windows Store. This discovery opened up a massive ecosystem of applications that could run natively on Windows without any additional development work.&lt;/p&gt;

&lt;p&gt;The impact was twofold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;  Users gained access to millions of apps without waiting for developers to build Windows-specific versions&lt;/li&gt;
&lt;li&gt;    Developers saw their apps appear in the Windows Store automatically—no extra effort required&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Microsoft's embrace of PWAs validated that the technology wasn't just for small projects—it was a legitimate distribution channel for the world's largest operating system.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Choose PWA
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PWAs are ideal for :
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E-commerce - Higher conversions, better mobile experience.

Content-driven apps - News, blogs, media.

Businesses with limited budget - One app for all platforms.

Emerging markets - Works on slow connections.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The web is evolving, and PWAs are leading the charge toward a future where the line between websites and apps disappears entirely.&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Agentic-AI :Simple tools to autonomous partners</title>
      <dc:creator>OdaloV</dc:creator>
      <pubDate>Mon, 02 Feb 2026 09:26:32 +0000</pubDate>
      <link>https://dev.to/odalov/agentic-ai-simple-tools-to-autonomous-partners-f99</link>
      <guid>https://dev.to/odalov/agentic-ai-simple-tools-to-autonomous-partners-f99</guid>
      <description>&lt;p&gt;If you have been following AI development,then  you've seen the shift, from ChatGPT answering questions to Devin writing entire codebases. From simple chatbots to systems that plan, execute, and adapt.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes an AI Agentic?
&lt;/h2&gt;

&lt;p&gt;Agentic AI is about ,agency,the capacity to act independently toward goals. While a traditional AI model generates text based on patterns, an AI agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Sets and pursues goals&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Breaks problems into steps&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Uses tools to interact with the world&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learns from feedback&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adapts its approach when stuck&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's like moving from a &lt;strong&gt;calculator&lt;/strong&gt;;executes commands, to a &lt;strong&gt;mathematician&lt;/strong&gt;;solves problems using tools and reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fundamental Loop: Perception → Reasoning → Action
&lt;/h2&gt;

&lt;p&gt;The simplest theoretical model of an AI agent is the &lt;strong&gt;Perception-Reasoning-Action cycle&lt;/strong&gt;:&lt;br&gt;
&lt;strong&gt;In practice with LLMs, this becomes the ReAct pattern&lt;/strong&gt; (Reason + Act):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    python

    # How an AI agent thinks and acts
    def agent_loop(objective, tools, memory):
    while not objective.achieved():
    # 1. PERCEPTION: Look around
    observation = perceive_environment()

    # 2. REASONING: Think about next step
    thought = llm_reason(objective, observation, memory)

    # 3. ACTION: Do something with tools
    action = choose_action(thought, tools)
    result = execute_action(action)

    # 4. UPDATE: Learn and continue
    memory.update(thought, action, result)

return "Task completed"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Breaking It Down with Fast Food &lt;/p&gt;

&lt;p&gt;Let's say you're craving KFC and tell an AI agent: "Get me KFC for dinner"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Old-School AI (Traditional Chatbot)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Would give you instructions:&lt;/p&gt;

&lt;p&gt;Go to the KFC website or use Glovo&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentic AI (Autonomous Assistant)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Actually does it for you. Here's how it thinks:&lt;/p&gt;

&lt;p&gt;Agent's Thought Process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;PERCEIVES:&lt;/p&gt;

&lt;p&gt;Checks your current location&lt;/p&gt;

&lt;p&gt;Remembers you ordered KFC last Tuesday&lt;/p&gt;

&lt;p&gt;Notes you usually ask for extra ketchup&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;REASONS :&lt;/p&gt;

&lt;p&gt;They want KFC. Let me check where they are&lt;/p&gt;

&lt;p&gt;Found 3 KFCs nearby. Which one has the shortest wait time?&lt;/p&gt;

&lt;p&gt;They like the Zinger Burger based on last order.&lt;/p&gt;

&lt;p&gt;Should check for any discounts or offers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ACTS :&lt;/p&gt;

&lt;p&gt;Searches for nearby KFC locations&lt;/p&gt;

&lt;p&gt;Checks opening hours and current wait times&lt;/p&gt;

&lt;p&gt;Compares prices and specials&lt;/p&gt;

&lt;p&gt;Places the order via API&lt;/p&gt;

&lt;p&gt;Tracks the delivery in real-time&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UPDATES :&lt;/p&gt;

&lt;p&gt;Noted: they want extra ketchup. Remember for next time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The bottom line: The agent doesn't just talk about KFC ,it gets you the actual chicken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Agentic AI represents a fundamental shift:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You give goals, not step-by-step instructions&lt;/p&gt;

&lt;p&gt;AI handles complex multi-step processes&lt;/p&gt;

&lt;p&gt;Systems improve through experience&lt;/p&gt;

&lt;p&gt;One agent replaces what used to take multiple tools&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>agents</category>
      <category>python</category>
    </item>
  </channel>
</rss>
