<?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: Ezeana Micheal</title>
    <description>The latest articles on DEV Community by Ezeana Micheal (@ezeanamichael).</description>
    <link>https://dev.to/ezeanamichael</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F795718%2F32ca5ddb-abe1-44e1-9391-85534fc496af.jpg</url>
      <title>DEV Community: Ezeana Micheal</title>
      <link>https://dev.to/ezeanamichael</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ezeanamichael"/>
    <language>en</language>
    <item>
      <title>The Bilingual Developer: Python and Go Primitive Data Types</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Fri, 29 May 2026 15:50:56 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/the-bilingual-developer-python-and-go-primitive-data-types-4c5f</link>
      <guid>https://dev.to/ezeanamichael/the-bilingual-developer-python-and-go-primitive-data-types-4c5f</guid>
      <description>&lt;p&gt;As discussed in the previous article on the variables about the need of programming languages to store information. This article will be about how the different data is stored. Let’s slow it down a bit and really think about it. If storage is where data lives… then data types are basically &lt;em&gt;how that data is shaped inside memory.&lt;/em&gt; And depending on the language you’re using, that “shape” changes a lot. Primitive data types are the most basic building blocks used to represent values inside a program.&lt;/p&gt;

&lt;p&gt;When learning both Python and Go, understanding their primitive data types is one of the fastest ways to understand the design philosophy behind each language.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Understanding Primitive Data Types&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Primitive data types are the simplest forms of data a programming language can handle directly. They represent single values such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text
&lt;/li&gt;
&lt;li&gt;Numbers
&lt;/li&gt;
&lt;li&gt;Boolean values (true or false)
&lt;/li&gt;
&lt;li&gt;Fixed constant values&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Text and Characters&lt;/strong&gt;
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Strings in Python&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In Python, text is represented using the &lt;code&gt;str&lt;/code&gt; data type. A string, simply put, is a sequence of characters enclosed in quotation marks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Michael&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  
&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello World&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python treats strings as Unicode by default, meaning they can store characters from different languages and symbols.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;emoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;😊&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  
&lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of Python’s strengths is how simple string manipulation feels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;first_word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Go&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  
&lt;span class="n"&gt;last_word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;full_word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_word&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;last_word&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Go Python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python does not have a separate character (&lt;code&gt;char&lt;/code&gt;) data type. A single character is simply considered a string of length one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;class 'str'&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design keeps the language simpler because developers only need to think about one text type instead of multiple character-based types.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Strings and Runes in Go&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Go also uses strings for textual data.&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;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="s"&gt;"fmt"&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;name&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"Michael"&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="n"&gt;name&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;But Go handles characters differently from Python.&lt;/p&gt;

&lt;p&gt;In Go:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A string represents a sequence of bytes.
&lt;/li&gt;
&lt;li&gt;A rune represents a single Unicode character.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A rune is actually an alias for &lt;code&gt;int32&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;var letter rune = 'A'&lt;/p&gt;

&lt;p&gt;Notice something important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strings use double quotes &lt;code&gt;" "&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Runes use single quotes &lt;code&gt;' '&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This distinction is very important in Go.&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;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="s"&gt;"fmt"&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="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="kt"&gt;rune&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'G'&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="n"&gt;character&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;Output:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The output is numeric because Go stores runes internally using Unicode code points.&lt;/p&gt;

&lt;p&gt;To print the actual character:&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%cn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Go’s approach gives developers more control over memory and encoding behavior, especially for systems programming and high-performance applications.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Numbers&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Numbers are another core primitive data type.&lt;/p&gt;

&lt;p&gt;Both Python and Go support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integers
&lt;/li&gt;
&lt;li&gt;Floating-point numbers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Integers and Floats in Python&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Python makes numeric programming simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;  
&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;19.99&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python automatically determines the type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;class 'int'&amp;gt;  
&amp;lt;class 'float'&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One important feature of Python is that integers can grow very large automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;big_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;999999999999999999999999999999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python dynamically allocates memory for large integers behind the scenes.&lt;/p&gt;

&lt;p&gt;This flexibility is convenient because developers rarely worry about integer overflow or memory size during basic programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Integers and Floats in Go&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Go is stricter and more explicit.&lt;/p&gt;

&lt;p&gt;var age int = 25&lt;br&gt;&lt;br&gt;
var price float64 = 19.99&lt;/p&gt;

&lt;p&gt;Go supports multiple integer sizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;int8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;int16&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;int32&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int64&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And floating-point types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;float32&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;float64&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&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;smallNumber&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;  
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;largeNumber&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;9000000000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This explicit sizing is important because each type uses a specific amount of memory.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;int8&lt;/td&gt;
&lt;td&gt;8 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int16&lt;/td&gt;
&lt;td&gt;16 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int32&lt;/td&gt;
&lt;td&gt;32 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int64&lt;/td&gt;
&lt;td&gt;64 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Go forces developers to think more carefully about memory usage and performance.&lt;/p&gt;

&lt;p&gt;For example:&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;x&lt;/span&gt; &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;  
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

&lt;span class="c"&gt;// This causes an error  &lt;/span&gt;
&lt;span class="c"&gt;// fmt.Println(x + y)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go does not automatically mix different integer sizes. Developers must explicitly convert types.&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;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="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This strictness reduces accidental bugs and improves predictability in large systems.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Booleans&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Booleans represent logical truth values.&lt;/p&gt;

&lt;p&gt;They are heavily used in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conditions
&lt;/li&gt;
&lt;li&gt;Comparisons
&lt;/li&gt;
&lt;li&gt;Decision-making statements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Booleans in Python&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Python uses:&lt;/p&gt;

&lt;p&gt;True and False&lt;/p&gt;

&lt;p&gt;Notice the capitalization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;is_logged_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;  
&lt;span class="n"&gt;is_admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;  
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Python’s boolean system is highly flexible because many values can behave as truthy or falsy.&lt;/p&gt;

&lt;p&gt;Examples of falsy values:&lt;/p&gt;

&lt;p&gt;0&lt;br&gt;&lt;br&gt;
None&lt;br&gt;&lt;br&gt;
""&lt;br&gt;&lt;br&gt;
[]&lt;br&gt;&lt;br&gt;
False&lt;/p&gt;

&lt;p&gt;Everything else is generally considered truthy.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Booleans in Go&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Go also supports boolean values, but with stricter rules(as usual).&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;isLoggedIn&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;  
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;isAdmin&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the lowercase keywords:&lt;/p&gt;

&lt;p&gt;true and false&lt;/p&gt;

&lt;p&gt;Go does not allow non-boolean values in conditional statements.&lt;/p&gt;

&lt;p&gt;This is invalid:&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;number&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&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;"Hello"&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;Python allows similar truthy checks, but Go requires an actual boolean expression.&lt;/p&gt;

&lt;p&gt;Correct Go example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&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;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;"Hello"&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 design improves clarity and reduces ambiguity.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Constants&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Constants are values that should never change during program execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Constants in Python&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Python does not have a true constant keyword. Instead, developers follow a naming convention using uppercase variable names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt;  
&lt;span class="n"&gt;MAX_USERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, Python does not enforce immutability.&lt;/p&gt;

&lt;p&gt;PI = 10&lt;/p&gt;

&lt;p&gt;This still works. The uppercase naming style simply communicates intent to other developers. Python relies heavily on developer discipline and readability conventions.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Constants in Go&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Go provides an actual &lt;code&gt;const&lt;/code&gt; keyword.&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;const&lt;/span&gt; &lt;span class="n"&gt;Pi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3.14159&lt;/span&gt;  
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MaxUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once declared, constants cannot be modified.&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;const&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

&lt;span class="c"&gt;// Error  &lt;/span&gt;
&lt;span class="c"&gt;// Age = 30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This enforcement makes programs safer and more predictable. Go constants can also exist without explicitly specifying a type.&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;const&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;"GoLang"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler determines the appropriate type automatically.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;In Python, primitive types are designed to feel natural and easy to use. Developers can move quickly without worrying much about memory sizes or strict typing rules.&lt;/p&gt;

&lt;p&gt;In Go, primitive types are more structured and explicit. Developers are expected to think carefully about memory, type safety, and predictability.&lt;/p&gt;

&lt;p&gt;Comment , Share and Like, thanks for reading.&lt;/p&gt;

</description>
      <category>python</category>
      <category>go</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>The N+1 Query That Killed Our Database, And How I Fixed It</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Mon, 25 May 2026 17:39:38 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/the-n1-query-that-killed-our-database-and-how-i-fixed-it-2al5</link>
      <guid>https://dev.to/ezeanamichael/the-n1-query-that-killed-our-database-and-how-i-fixed-it-2al5</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Everything Worked…But Not Well&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;APIs are affected by the way data is retrieved from the database, and that's something that affected a recent teammate and me during development. In our regular development process, during review and maintenance, the APIs worked, responses came back well, no visible errors. But during this process, we noticed that things started to slow down. Endpoints that used to respond fast suddenly began taking longer, CPU usage on the database increased, and retrieval speed became terrible under load.&lt;/p&gt;

&lt;p&gt;At first, we thought:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maybe the server wasn't powerful enough
&lt;/li&gt;
&lt;li&gt;Maybe network latency was the issue
&lt;/li&gt;
&lt;li&gt;Maybe too many requests were hitting the API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the real issue was hiding inside our database queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Actually Is The N+1 Query Problem?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The N+1 query problem is one of the most common performance issues in backend development, especially when working with relational databases and ORMs(as I, a fan of Django, do). I’m gonna explain this more in SQL terms using retrieval speed and database load.&lt;/p&gt;

&lt;p&gt;The problem happens when your application performs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 query to retrieve parent records
&lt;/li&gt;
&lt;li&gt;Then N extra queries to retrieve related child records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of making one optimized query, the application keeps asking the database more questions repeatedly. This increases the query count, read time, database load, and the API response time. 1 issue, several systems affected.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A Simple Example&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's say we have 2 tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users
&lt;/li&gt;
&lt;li&gt;Orders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We first retrieve all users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM users;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine there are 100 users. Then, for every user retrieved, another query runs to fetch their orders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM orders WHERE user_id = 1;  
SELECT * FROM orders WHERE user_id = 2;  
SELECT * FROM orders WHERE user_id = 3;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it keeps going. So instead of 1 query, you now have 101 queries. That extra load destroys retrieval speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why This Problem Is Dangerous&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The dangerous part is that during development, you might not even notice it. If your database only has just about 5 users and 10 orders, Everything still feels fast.&lt;/p&gt;

&lt;p&gt;But once production data grows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hundreds of users
&lt;/li&gt;
&lt;li&gt;Thousands of records
&lt;/li&gt;
&lt;li&gt;More API requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The database starts struggling badly. This is why some APIs feel fast during testing but start to  slow down after deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Mistake We Made&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The issue wasnt obvious at first because the code itself looked completely normal. We had an endpoint that needed to return users alongside their related orders.&lt;/p&gt;

&lt;p&gt;Something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User information
&lt;/li&gt;
&lt;li&gt;Their recent orders
&lt;/li&gt;
&lt;li&gt;Order counts
&lt;/li&gt;
&lt;li&gt;Related details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The endpoint worked perfectly during development. But internally, Django was fetching related records separately for every user being serialized. Which meant the API kept hitting the database repeatedly behind the scenes.&lt;/p&gt;

&lt;p&gt;The code looked clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users = User.objects.all()

serializer = UserSerializer(users, many=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in the serializer, related order data was being accessed for every single user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserSerializer(serializers.ModelSerializer):

    orders = OrderSerializer(many=True)

    class Meta:

        model = User

        fields = ['id', 'name', 'orders']

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the real problem started. When Django tried to serialize the response, it kept querying orders separately per user. So if there were 500 users, the system would do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 query to retrieve users
&lt;/li&gt;
&lt;li&gt;Hundreds of additional queries to retrieve orders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the N+1 query problem. The scary part is that nothing looked wrong from the surface. The endpoint returned the correct data and no errors, just slow performance. And as traffic increased, the database load became worse. It was slow because the application kept repeatedly asking for related data instead of retrieving it efficiently.&lt;/p&gt;

&lt;p&gt;The fix was adding query optimization directly to the queryset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users = User.objects.prefetch_related('orders')

serializer = UserSerializer(users, many=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single optimization drastically reduced the number of database hits. Instead of querying orders repeatedly for every user, Django retrieved them efficiently in bulk. Same endpoint and response, much better performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding &lt;code&gt;select_related()&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One major fix for this problem in Django is:&lt;/p&gt;

&lt;p&gt;select_related()&lt;/p&gt;

&lt;p&gt;&lt;code&gt;select_related()&lt;/code&gt; is used for relationships like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ForeignKey
&lt;/li&gt;
&lt;li&gt;OneToOneField&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it basically does is perform a SQL JOIN and retrieve related data in a single query. Instead of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users = User.objects.all()

for user in users:  
    print(user.profile.phone)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which may generate multiple queries…&lt;/p&gt;

&lt;p&gt;You do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users = User.objects.select_related('profile')  
for user in users:  
    print(user.profile.phone)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Django joins the tables together and retrieves everything at once.&lt;/p&gt;

&lt;p&gt;So instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 query for users
&lt;/li&gt;
&lt;li&gt;Multiple queries for profiles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 optimized query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Much faster and cleaner.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding &lt;code&gt;prefetch_related()&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Another optimization is:&lt;/p&gt;

&lt;p&gt;prefetch_related()&lt;/p&gt;

&lt;p&gt;This is used mostly for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ManyToMany relationships
&lt;/li&gt;
&lt;li&gt;Reverse ForeignKey relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike &lt;code&gt;select_related()&lt;/code&gt;, this does not use SQL JOINs directly. Instead, Django performs separate queries but combines the results efficiently in memory.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users = User.objects.prefetch_related('orders')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Django may run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM users;  
SELECT * FROM orders WHERE user_id IN (1,2,3,4...);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM orders WHERE user_id = 1;  
SELECT * FROM orders WHERE user_id = 2;  
SELECT * FROM orders WHERE user_id = 3;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is far more efficient. So rather than hundreds of queries, you may only have two.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Difference Between &lt;code&gt;select_related()&lt;/code&gt; And &lt;code&gt;prefetch_related()&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;&lt;code&gt;select_related()&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Uses SQL JOINs
&lt;/li&gt;
&lt;li&gt;Best for ForeignKey and OneToOne relationships
&lt;/li&gt;
&lt;li&gt;Retrieves related data in a single query&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;&lt;code&gt;prefetch_related()&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Uses multiple optimized queries
&lt;/li&gt;
&lt;li&gt;Combines data in Python memory
&lt;/li&gt;
&lt;li&gt;Best for ManyToMany and reverse relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both solve the same problem differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Result&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After fixing the queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Response times dropped massively
&lt;/li&gt;
&lt;li&gt;Database load reduced
&lt;/li&gt;
&lt;li&gt;API became stable again
&lt;/li&gt;
&lt;li&gt;Retrieval speed improved significantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The funny thing is that we didnt upgrade the server. We didnt increase RAM. We didnt change infrastructure. We simply reduced unnecessary queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What This Experience Taught Me&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One thing this experience taught me is that backend performance is not always about server power. Sometimes your database is suffering simply because of how you are querying it. A badly written retrieval pattern can quietly destroy performance even when your infrastructure is good. Now whenever I build endpoints, I dont just ask “Does this work?” I also ask “How many queries is this generating behind the scenes?” Because sometimes the biggest backend problem is not the logic. Its the retrieval pattern hiding underneath it. Let me know what you think, thanks for reading, like, share, comment and follow for more. &lt;/p&gt;

</description>
      <category>database</category>
      <category>python</category>
      <category>django</category>
      <category>sql</category>
    </item>
    <item>
      <title>How I Cut API Response Time from 2s to &lt;100ms with Redis Caching</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Fri, 22 May 2026 15:09:42 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/how-i-cut-api-response-time-from-2s-to-100ms-with-redis-caching-4h6a</link>
      <guid>https://dev.to/ezeanamichael/how-i-cut-api-response-time-from-2s-to-100ms-with-redis-caching-4h6a</guid>
      <description>&lt;p&gt;Early in my software development years, I had the opportunity to work with a company where I learned backend development. I worked on a system where I was responsible for building the APIs without senior guidance, just documentation, experimentation, and a lot of self-learning.&lt;br&gt;&lt;br&gt;
When learning to design DB models and RESTful APIs. That was it for me, connect everything and let it out via the get request, let it in via the post request, patch things up with the patch request, and delete via the delete request. &lt;/p&gt;

&lt;p&gt;Basic crud operations, and I was fine with that, back then. Eventually, I decided to explore the site myself, and damn, it was slow.&lt;br&gt;&lt;br&gt;
Every interaction came with a noticeable delay. So I stopped assuming things were fine and tested the APIs properly. Using Postman, I measured response times. Most endpoints were taking over 5 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Every request was hitting the database directly, even when requesting the same data repeatedly.&lt;br&gt;&lt;br&gt;
The system wasn’t slow because the database was bad.&lt;br&gt;&lt;br&gt;
 It was slow because it was doing the same work over and over again.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Changed?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I did some research, investigations, and found something powerful. Caching, and utilizing redis for it. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is Caching?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Caching stores frequently accessed data in memory (Redis uses RAM), allowing much faster retrieval compared to querying a database repeatedly. It reduces database load, instead of hitting the database every time it hits the cache instead and returns the result. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Types of Caching (and Why Redis Fits Here)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Not all caching works the same way. In my case, I implemented &lt;strong&gt;application-level caching with Redis&lt;/strong&gt;, but it helps to understand where it sits in the bigger picture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Application-Level Caching&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This is what I used. The application stores frequently accessed data in a fast in-memory store like Redis.&lt;/p&gt;

&lt;p&gt;Instead of always asking the database:&lt;/p&gt;

&lt;p&gt;API → Database → Response&lt;/p&gt;

&lt;p&gt;We first check:&lt;/p&gt;

&lt;p&gt;API → Redis → Database (only if needed)&lt;/p&gt;

&lt;p&gt;This is the most common approach in backend systems because it gives full control over what gets cached and when it gets updated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Database Caching&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Some databases internally cache query results or use external layers to store repeated query outputs.&lt;/p&gt;

&lt;p&gt;This reduces repeated expensive queries, but it is less flexible compared to Redis-based caching where you control the logic directly inside your API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Distributed Caching&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This is when caching is shared across multiple servers using systems like Redis Cluster or Memcached.&lt;/p&gt;

&lt;p&gt;It becomes important when your application is no longer running on a single server, but across multiple services or microservices.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Therefore,&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before:&lt;br&gt;&lt;br&gt;
Client → API → DB (every request)&lt;/p&gt;

&lt;p&gt;After:&lt;br&gt;&lt;br&gt;
Client → API → Redis → DB (only on cache miss)&lt;/p&gt;

&lt;p&gt;Instead of querying the database every time, the API now checks Redis first. If the data exists, it returns immediately. If not, it fetches from the database, stores the result in Redis, and returns it.&lt;/p&gt;

&lt;p&gt;Here was my initial API response time without caching, &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5op2j9888e3i4dypjgop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5op2j9888e3i4dypjgop.png" alt="Postman image showing response time of over 5 seconds" width="795" height="25"&gt;&lt;/a&gt;&lt;br&gt;
Here was my API response time afterwards.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpyb4v6t1pfdk00uwps1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpyb4v6t1pfdk00uwps1.png" alt="Postman image showing response time of less that 200ms" width="783" height="23"&gt;&lt;/a&gt; &lt;br&gt;
I implemented 2 main strategies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Key-value Caching.
&lt;/li&gt;
&lt;li&gt;Cache invalidation via Write.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my API I implemented key value caching for every get request i.e (get /books/ and get /books/:id)&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Eliminating First-Request Slowness (Cache Warm-Up)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One downside of caching is the initial delay when the cache is empty.&lt;br&gt;&lt;br&gt;
How did I solve that? a script. &lt;/p&gt;

&lt;p&gt;I added a cache warm-up script that runs on deployment and preloads frequently accessed data into Redis. That way, the system starts “warm,” and users don’t experience the initial latency.&lt;/p&gt;

&lt;p&gt;At that point, the only time the system feels slow is immediately after cache invalidation.&lt;/p&gt;

&lt;p&gt;But having a cache isn't all good if not invalidated well, because write data comes in too, through post , patch, put. So a cache invalidation strategy was needed. &lt;/p&gt;

&lt;p&gt;I chose cache invalidation via Write for 2 reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Data will be refreshed once a put, patch, post or delete request comes in, and
&lt;/li&gt;
&lt;li&gt;Data is not often changed regularly, the data stays for a while because of the type of application we’re dealing with so giving it a time to live and time to die feels too much. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Trade-offs&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Every system has tradeoffs as there is no perfect system. The following were the tradeoffs of my decision.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First request after invalidation is slower
&lt;/li&gt;
&lt;li&gt;Cache keys must be managed carefully
&lt;/li&gt;
&lt;li&gt;Slight increase in system complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the system is now significantly faster and more efficient.&lt;/p&gt;

&lt;p&gt;PS: &lt;em&gt;There are several optimization techniques in backend development, this article is just about caching.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Thanks for reading, what do you think about the approach? Read, like and comment. &lt;/p&gt;

</description>
      <category>redis</category>
      <category>backend</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Bilingual Developer: Python and Go Variables</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Fri, 22 May 2026 14:59:35 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/the-bilingual-developer-python-and-go-variables-57mm</link>
      <guid>https://dev.to/ezeanamichael/the-bilingual-developer-python-and-go-variables-57mm</guid>
      <description>&lt;p&gt;At the heart of programming is a simple idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input -&amp;gt; Processing -&amp;gt; Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But between input and processing lies something more important, &lt;strong&gt;storage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before a computer can do anything useful with data, it must first store it somewhere. That “somewhere” is memory. And in programming, the way we interact with memory starts with one core concept:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Programming is just a way of communicating with a computer, telling it what to store, how to transform it, and what to return.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how two very different languages, &lt;strong&gt;Python and Go&lt;/strong&gt;, handle the same idea: variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Variables: The First Mental Model&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A variable is simply a named storage location in memory.&lt;/p&gt;

&lt;p&gt;For example (What it does is):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“store the number 10”
&lt;/li&gt;
&lt;li&gt;“label it as x”
&lt;/li&gt;
&lt;li&gt;“retrieve it later when needed”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple concept,  but different languages implement it very differently.&lt;/p&gt;

&lt;p&gt;And that’s where Python and Go start to diverge.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Python vs Go: Two Different Philosophies&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Python and Go both allow you to store data in variables, but they were designed with different priorities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python: flexibility and speed of writing code&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Go: structure, safety, and predictability&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This difference shows up immediately in how variables are declared.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Dynamic vs Static Typing&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Python (Dynamic Typing)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In Python, you don’t explicitly declare the type of a variable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x=10&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Python automatically figures out the datatype at runtime.&lt;/p&gt;

&lt;p&gt;This is called &lt;strong&gt;dynamic typing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It gives you flexibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can reuse variables freely
&lt;/li&gt;
&lt;li&gt;You don’t need to define types upfront
&lt;/li&gt;
&lt;li&gt;It speeds up development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the trade-off is that errors may only show up when the program runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Go (Static Typing)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In Go, you must define the type explicitly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;var x int = 10&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Once declared, the type cannot change.&lt;/p&gt;

&lt;p&gt;So this is invalid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x = "hello" // error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is called &lt;strong&gt;static typing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It forces structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Types are known before execution
&lt;/li&gt;
&lt;li&gt;Errors are caught early (at compile time)
&lt;/li&gt;
&lt;li&gt;Code is more predictable in large systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Two Ways of Declaring Variables&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Python keeps it simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x = 10&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it. One way, one rule.&lt;/p&gt;

&lt;p&gt;Go gives you two main approaches:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Explicit Declaration&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;var x int = 10&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;### &lt;strong&gt;Short Declaration (Walrus-style feel)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x := 10&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;:=&lt;/code&gt; operator tells Go:&lt;/p&gt;

&lt;p&gt;“Infer the type automatically, but still lock it in permanently.”&lt;/p&gt;

&lt;p&gt;So Go gives you convenience — but never sacrifices structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Concept of “Nothing”&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Another major difference is how both languages handle empty or uninitialized values.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Python: &lt;code&gt;None&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In Python, “no value” is represented explicitly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x = None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;p&gt;“This variable exists, but it currently holds nothing.”&lt;/p&gt;

&lt;p&gt;It is very explicit and readable.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Go: Zero Values&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Go does something different.&lt;/p&gt;

&lt;p&gt;If you declare a variable but don’t assign a value, Go automatically gives it a default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;var x int
&lt;/li&gt;
&lt;li&gt;fmt.Println(x) // 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;None&lt;/code&gt;, Go uses &lt;strong&gt;zero values&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;int → 0
&lt;/li&gt;
&lt;li&gt;string → ""
&lt;/li&gt;
&lt;li&gt;bool → false&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This design avoids uninitialized variables entirely.&lt;/p&gt;

&lt;p&gt;While python thinks: “Let me give you freedom and decide later.” Go thinks: “Let me force clarity so you don’t make mistakes later.”&lt;/p&gt;

&lt;p&gt;Even something as simple as variables reveals deep design philosophies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python prioritizes &lt;strong&gt;developer speed and flexibility&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Go prioritizes &lt;strong&gt;safety and predictability&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are correct, just optimized for different worlds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python → scripting, AI, automation, rapid development
&lt;/li&gt;
&lt;li&gt;Go → backend systems, scalability, performance-critical services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading, what do you think about this? Read, like and comment.&lt;/p&gt;

</description>
      <category>python</category>
      <category>go</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Bilingual Developer: Learning Python &amp; Go Side-by-Side</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Fri, 15 May 2026 16:50:43 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/the-bilingual-developer-learning-python-go-side-by-side-3nan</link>
      <guid>https://dev.to/ezeanamichael/the-bilingual-developer-learning-python-go-side-by-side-3nan</guid>
      <description>&lt;p&gt;There are several programming languages and specialized fields today. Navigating the mountain of guides out there can be tricky; some lead you straight into "tutorial hell," while others actually help you gain a solid footing. Python and Go are two of the most popular choices in modern development, different in their own ways, each have their strengths.&lt;/p&gt;

&lt;p&gt;I, you, and many others have gone through a tutorial to learn a language one at a time, so I thought of trying something different: &lt;strong&gt;Dual-Language Learning&lt;/strong&gt;. The advantage here is that it gives you two sides of a concept, forcing you to actually understand the underlying mechanisms rather than just memorizing syntax.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why Python and Go?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt; is a &lt;strong&gt;dynamically typed&lt;/strong&gt; language, which means you can write code, iterate, and test ideas without getting held down in some boilerplate or strict type declarations. It is used for &lt;strong&gt;LLM engineering, machine learning, and rapid backend development.&lt;/strong&gt; Its real power isn't in raw execution speed (it can be slow compared to others), but developer writing speed gives it an edge; it's simpler to write than some other languages. Python serves as the ultimate "glue" language; you write clean, readable code, while the heavy computational lifting is delegated to blazing-fast C++ and Rust kernels under the hood. With industry-standard libraries like PyTorch, Hugging Face, and FastAPI, Python provides the fastest possible path from a raw AI idea to a deployed prototype.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go&lt;/strong&gt; (or Golang), developed by Google, is a &lt;strong&gt;statically typed&lt;/strong&gt; language, and the one word I'll use to describe it is &lt;em&gt;speed&lt;/em&gt;. It is the industry standard for &lt;strong&gt;backend infrastructure, cloud-native tooling (like Docker and Kubernetes), and high-performance microservices&lt;/strong&gt;, making it a powerhouse for things like high-throughput fintech platforms. While Python historically utilized a Global Interpreter Lock (GIL) that prevented it from running operations concurrently at maximum efficiency, &lt;strong&gt;Go handles massive concurrency natively&lt;/strong&gt;. Through "goroutines," it is blazingly fast at computing simultaneous tasks, allowing it to scale effortlessly across multiple cores.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The 2026 Landscape&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.14:&lt;/strong&gt; This release officially supports &lt;strong&gt;free-threaded Python&lt;/strong&gt; (allowing you to disable the GIL for true multithreading) and introduces an experimental JIT (Just-In-Time) compiler. It also makes deferred evaluation of type annotations the default, which significantly speeds up startup times.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go 1.26:&lt;/strong&gt; The latest release makes the highly efficient &lt;strong&gt;Green Tea garbage collector&lt;/strong&gt; the default (reducing runtime overhead by up to about 40%) and introduces syntax quality-of-life updates, like allowing expressions directly inside the new() function for cleaner pointer creation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Core Differences&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before learning both languages, you first need to understand how they “think” differently:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;How They Run&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Python runs your code line by line while the program is executing. This makes it flexible, easy to test quickly, and great for fast development.&lt;/p&gt;

&lt;p&gt;Go converts your code into a standalone executable file before it runs. Because of this, Go programs are usually much faster and more efficient.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;How Programs Start&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In Python, you can usually just write code from top to bottom and run the file.&lt;/p&gt;

&lt;p&gt;In Go, every runnable program must follow a fixed structure. You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;package main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;and a &lt;code&gt;func main()&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That &lt;code&gt;main()&lt;/code&gt; function is where the program officially starts running.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Code Style and Formatting&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Python gives developers more freedom in how they write and organize code. There are style guides and formatting tools people commonly use, but they are mostly optional.&lt;/p&gt;

&lt;p&gt;Go is much stricter. It comes with its own formatting tool called &lt;code&gt;gofmt&lt;/code&gt;, and almost every Go developer uses it. This means most Go code looks very similar, making projects easier to read and maintain across teams.&lt;/p&gt;

&lt;p&gt;Over the next few weeks, I’ll be writing about Python and Go concepts, sharing code, and seeing what we can learn by comparing them side-by-side. I’d appreciate the support, so let me know what you feel or think about it.&lt;/p&gt;

&lt;p&gt;Let's Go Python! &lt;/p&gt;

</description>
      <category>go</category>
      <category>python</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Auth in 2026: What Actually Matters Now</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Mon, 11 May 2026 15:14:47 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/auth-in-2026-what-actually-matters-now-32ac</link>
      <guid>https://dev.to/ezeanamichael/auth-in-2026-what-actually-matters-now-32ac</guid>
      <description>&lt;p&gt;With the speed at which Generative AI is being used to build applications, knowing how to code isn't the complete way anymore; understanding what to code is. What is needed? Why is it needed? and more. I’ve decided to take some level of these concepts and explain them to the best of my abilities. Starting with backend development. First, let's understand authentication and authorization concepts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication&lt;/strong&gt; is the process of confirming a user’s identity. Basically, it answers “Who is your user?”&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Authorization&lt;/strong&gt; is the process of confirming what a user can do. Basically, it answers “What can your user do?”&lt;/p&gt;

&lt;p&gt;In this article, I’ll write about different authentication and authorization methods, what instances they’re used in, and how they work.&lt;/p&gt;

&lt;p&gt;But first, we can’t talk about auths without mentioning passwords. Passwords are the most basic authentication method. But it is important to note, NEVER store passwords in plain text. It's the worst mistake any backend developer would make. The idea is to hash the passwords and verify the hash. There are many ways to hash a passwords, but here are 2 to avoid.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;MD5
&lt;/li&gt;
&lt;li&gt;SHA-256&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Why avoid them?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;These general-purpose hash functions are designed to be fast, and an attacker with a modern GPU can compute millions of SHA-256 per second, making attacks computationally cheap, so when selecting hashing algorithms for passwords, they have to be &lt;strong&gt;deliberately expensive or slow&lt;/strong&gt;. The 2 widely used are &lt;strong&gt;Bcrypt&lt;/strong&gt; and &lt;strong&gt;Argon2&lt;/strong&gt;. Both are designed to be slow and computationally expensive, making it harder for attackers to decipher them through brute-force attacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bcrypt&lt;/strong&gt; was designed in 1999; it is based on the Blowfish cipher and uses a salt to protect against rainbow table attacks. It also incorporates a cost factor that quantifies the hash's computational cost. The cost doubles with each increment, i.e., cost 10 is twice as expensive or hard as cost 9 was.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Argon2&lt;/strong&gt; was introduced in 2015 as the winner of the Password Hashing Competition. The edge it has over bcrypt is that it has resistance to side-channel attacks. You can read more on it later.&lt;/p&gt;

&lt;p&gt;We will consider the following  authentication methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT Authentication
&lt;/li&gt;
&lt;li&gt;Session-Based Authentication
&lt;/li&gt;
&lt;li&gt;OpenID Connect Authentication
&lt;/li&gt;
&lt;li&gt;MFA (Multi-Factor Authentication)
&lt;/li&gt;
&lt;li&gt;SSO (Single Sign-On)
&lt;/li&gt;
&lt;li&gt;Passkeys / WebAuthn (FIDO2)
&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the following authorization methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OAuth 2.0
&lt;/li&gt;
&lt;li&gt;RBAC(Role-Based Access Control)
&lt;/li&gt;
&lt;li&gt;ABAC(Attribute-Based Access Control)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Starting with authentication,&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;JWT Authentication&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Json Web Token(JWT) is a compact, URL-safe token that encodes a set of claims (key-value pairs) and is cryptographically signed. A unique feature is that JWTs enable stateless authentication; the signature is verifiable without a database lookup. The server can validate the token with just the secret key.  &lt;/p&gt;

&lt;p&gt;A JWT has 3 Base64URL-encoded parts separated by dots: Header, Payload, and Signature.   &lt;/p&gt;

&lt;p&gt;a. Header contains the algorithm and the type e.g &lt;/p&gt;

&lt;p&gt;{ "alg": "HS256", "typ": "JWT" }&lt;/p&gt;

&lt;p&gt;b. Payload contains the encoded claims, e.g &lt;/p&gt;

&lt;p&gt;{&lt;br&gt;&lt;br&gt;
     "sub": "user_id_123",&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "email": "mike@synoloop.com",

 "role": "admin",

 "iat": 1716000000,    // issued at (Unix timestamp)

 "exp": 1716003600     // expires at (1 hour later) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;c. The signature is the encrypted base64url header and payload with the secret.&lt;/p&gt;

&lt;p&gt;In real-world authentication systems, JWTs are most commonly used in two forms: &lt;strong&gt;Access tokens&lt;/strong&gt; and &lt;strong&gt;refresh tokens&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Access tokens are short-lived, between 5 and 15 minutes. Refresh tokens, on the other hand, are long-lived from days to even weeks and are used to obtain new access tokens without reauthenticating (depending on the developer).&lt;/p&gt;

&lt;p&gt;But what happens if a token is stolen or a user logs out of the system? Since JWTs are stateless and valid until they expire, we need a way to &lt;em&gt;invalidate&lt;/em&gt; them before their natural expiration. This is where &lt;strong&gt;token blacklisting&lt;/strong&gt; comes in.&lt;br&gt;&lt;br&gt;
This is solved by maintaining a server-side denylist. Once any of the above events happen, the JWTID is added to the blacklist.&lt;/p&gt;

&lt;p&gt;So on logout or password change or compromise, it's best to revoke or blacklist all existing refresh tokens. This logs out the user on all sessions. On each request, the server looks up the session ID, retrieves the session data, and authenticates the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Session-Based Authentication&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Session-based authentication stores the auth state on the server, so when a user logs in, it creates a session record and stores it. What makes Sessions different from JWTs is that Sessions are easier to revoke immediately, but require server-side storage. JWTs are stateless but harder to invalidate before expiry.  &lt;/p&gt;

&lt;p&gt;In sessions, it's best to always regenerate the session ID after login to prevent session fixation attacks.  &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;OpenID Connect (OIDC) Authentication&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;OIDC is an identity layer built on top of OAuth 2.0(Will be discussed in authorization). OIDC answers who this user is. It adds a standardized ID token(a JWT) containing identity claims.  &lt;/p&gt;

&lt;p&gt;Think of this as when you use any web application that has a “continue with Google”; this sends your name, email, phone (and other details if requested) from Google to the web application authorized, answering who you are.  &lt;/p&gt;

&lt;p&gt;It gives the addition of what an OAuth 2.0 gives.   &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ID Token: a JWT containing user identity (sub, email, name, picture, etc.)
&lt;/li&gt;
&lt;li&gt;UserInfo Endpoint: returns additional claims about the authenticated user
&lt;/li&gt;
&lt;li&gt;Discovery Document: /.well-known/openid-configuration for automatic configuration
&lt;/li&gt;
&lt;li&gt;Standard Claims: sub (subject/user ID), email, email_verified, name, picture&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;MFA( Multi-Factor Authentication)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Multi-factor authentication requires users to have two or more verification factors present: something they know (password), something they have (phone/hardware key), and something they are (biometric).  &lt;/p&gt;

&lt;p&gt;Multi-factor authentication includes methods like TOTP and Backup Codes.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;TOTP(Time-Based One-Time Passwords), generates a 6-digit code that changes every 30 seconds based on a shared secret and the current time. It works with apps like Google Authenticator, Microsoft Authenticator, and such.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backup Codes: These are a list of 8 to 10 single-use random codes that can be used to retrieve the account or authenticate.  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;SSO(Single Sign-On)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Single Sign-On allows users to authenticate once and gain access to multiple systems. This is mostly used in enterprise environments or applications; the 2 main protocols are SAML 2.0 (XML-based, older) and OIDC-based SSO (modern, JSON/JWT-based).  &lt;/p&gt;

&lt;p&gt;SAML is the standard in enterprise environments (corporate identity providers like Okta and Azure). It uses XML assertions to tell the identity. The identity provider (IdP) authenticates the user and sends a signed XML assertion to the service provider (SP).  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Service Provider (SP) is your application
&lt;/li&gt;
&lt;li&gt;Identity Provider (IdP) is Okta, Azure, Google Workspace, etc.
&lt;/li&gt;
&lt;li&gt;An assertion is a signed XML document asserting identity and attributes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Passkeys / WebAuthn (FIDO2)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Passkeys are an advanced form of authentication: passwordless, phishing-resistant, and cryptographically strong. Based on the WebAuthn standard (W3C) and FIDO2 protocol, they use public-key cryptography with biometrics or device PINs.  &lt;/p&gt;

&lt;p&gt;Passkeys have become popular since their release in 2018. They work in 2 major steps:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Registration: The browser or the device generates a public/private key pair. Private key stays on the device. The public key is sent to your server.
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Authentication: Server sends a challenge. The device signs the challenge with the private key. The server verifies with the stored public key.&lt;/p&gt;

&lt;p&gt;No Password is ever transmitted or stored, and attackers can’t do anything with just the public key.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;API keys&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I chose to keep API keys under authentication, but underneath, they do both authentication and authorization; I’ll explain.&lt;br&gt;&lt;br&gt;
   API keys are Opaque tokens issued for programmatic access to services, scripts or public-facing APIs.They should be scoped, rotatable, and auditable.  &lt;/p&gt;

&lt;p&gt;API keys act like a username and password pair, but simplified. They identify the calling application or developer. For example, A weather API requires an API key to ensure only registered apps can access data.  &lt;/p&gt;

&lt;p&gt;While some APIs use keys with scoped permissions (e.g., read-only or read and write).&lt;br&gt;&lt;br&gt;
   &lt;strong&gt;Example:&lt;/strong&gt; A developer can generate API keys in a payment app and may allow one key to process transactions but another only to view reports.   &lt;/p&gt;

&lt;p&gt;Some design principles of API keys include:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Including a prefix for easy identification (e.g., sk_live_, sk_test_, syno_)
&lt;/li&gt;
&lt;li&gt;Generate with cryptographically secure randomness (at least 128 bits)
&lt;/li&gt;
&lt;li&gt;Store only the hash in your database, treat keys like passwords
&lt;/li&gt;
&lt;li&gt;Support scopes/permissions per key
&lt;/li&gt;
&lt;li&gt;Log all usage with timestamps for auditing
&lt;/li&gt;
&lt;li&gt;Support rotation: allow multiple active keys per user, then invalidate old ones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let's consider &lt;strong&gt;the authorization concepts&lt;/strong&gt;:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;OAuth 2.0&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is an authorization framework that allows a third-party application to obtain limited access to a service on a user's behalf without exposing credentials. You can think of this as “connect to GitHub” or authorizing Google Drive access via a “connect to Google.” It's about what you can access, not about who you are.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;RBAC(Role-Based Access Control)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;RBAC Controls access by assigning permissions to roles, and roles to users. This is most common in authorization models for web applications. User has a role, and the role has permissions. For example, in an e-commerce application. There are key roles like Admin, Vendor, and User. Each of these roles has different permissions. Admin can read all users and vendors, edit or verify, or ban a vendor. The vendor can upload products, the user can read and purchase products.   &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;ABAC(Attribute-Based Access Control)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;ABAC is another authorization method. It evaluates attributes of the user, the resource, the action, and the environment to make an authorization decision. For example, consider that &lt;strong&gt;a manager&lt;/strong&gt; can &lt;strong&gt;edit&lt;/strong&gt; a document &lt;strong&gt;only if they are in the same department&lt;/strong&gt; as the document's owner, &lt;strong&gt;during business hours&lt;/strong&gt;. We can see that many conditions affect the action that can be carried out by the manager.  &lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use ABAC vs RBAC
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use RBAC when access rules map cleanly to roles and don't depend on resource data
&lt;/li&gt;
&lt;li&gt;Use ABAC when you need fine-grained rules like row-level security, ownership checks, or time-based restrictions
&lt;/li&gt;
&lt;li&gt;Many systems use both: RBAC as a coarse gate, ABAC for fine-grained resource access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the conclusion of my article. We’ve considered various authentication and authorization concepts.&lt;br&gt;&lt;br&gt;
Please show your appreciation by giving this a &lt;strong&gt;like&lt;/strong&gt;, leaving a &lt;strong&gt;comment&lt;/strong&gt; with something encouraging, or sharing something you just learned, or something I may have gotten wrong. Thanks for reading.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>writing</category>
      <category>programming</category>
    </item>
    <item>
      <title>LegalCheck: Built for freelancers and remote workers after an issue I ran into.</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Sat, 28 Feb 2026 18:28:27 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/legalcheck-built-for-freelancers-and-remote-workers-after-an-issue-i-ran-into-3f88</link>
      <guid>https://dev.to/ezeanamichael/legalcheck-built-for-freelancers-and-remote-workers-after-an-issue-i-ran-into-3f88</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/weekend-2026-02-28"&gt;DEV Weekend Challenge: Community&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Community
&lt;/h2&gt;

&lt;p&gt;I built LegalCheck for freelancers, startup founders, indie builders, and non-legal tech practitioners who sign contracts without fully understanding what they’re agreeing to.&lt;/p&gt;

&lt;p&gt;In places like Nigeria, legal support isn’t always accessible or affordable. A lot of people copy templates from the internet, sign client agreements quickly, or accept vendor contracts under pressure without truly knowing the risks(like me once before).&lt;/p&gt;

&lt;p&gt;LegalCheck is for that community. The builders. The side-hustlers. The agency owners. The developers. The designers. The people shipping fast but still needing protection.&lt;/p&gt;

&lt;p&gt;I’m built(and am still scaling) this for people like me.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;LegalCheck is an AI-powered contract analysis tool.&lt;/p&gt;

&lt;p&gt;You upload a contract.&lt;br&gt;
It reads it.&lt;br&gt;
It highlights risky clauses.&lt;br&gt;
It explains them in plain English.&lt;/p&gt;

&lt;p&gt;No legal jargon. No intimidation.&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Analyzes contracts (freelance agreements, NDAs, service contracts, etc.)&lt;/li&gt;
&lt;li&gt;Flags risky or one-sided clauses&lt;/li&gt;
&lt;li&gt;Explains complex legal language in simple terms&lt;/li&gt;
&lt;li&gt;Highlights missing protections&lt;/li&gt;
&lt;li&gt;Breaks down what each key clause actually means for you&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It doesn’t replace a lawyer.&lt;br&gt;
It gives you clarity before you sign.&lt;/p&gt;

&lt;p&gt;And that alone can save people from very expensive mistakes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://legalcheck.buildswithmike.com/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;legalcheck.buildswithmike.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
(It takes some time to scan the contract about 5-10 mins, but it'll be worth the wait)

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

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

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

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

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Just-Mike4" rel="noopener noreferrer"&gt;
        Just-Mike4
      &lt;/a&gt; / &lt;a href="https://github.com/Just-Mike4/legal-check" rel="noopener noreferrer"&gt;
        legal-check
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      AI-Legal Contract Analyzer
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;LegalCheck - AI-Powered Legal Document Analysis&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;LegalCheck is an intelligent legal document analysis platform that extracts, identifies, and explains specific clauses in legal documents. It translates complex legal language into plain English and provides visual representations where helpful.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instant Clause Detection&lt;/strong&gt;: Automatically identify all key clauses in legal documents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain English Explanations&lt;/strong&gt;: Complex legalese translated to simple language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk Assessment&lt;/strong&gt;: Identify potentially risky clauses with severity levels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual Representations&lt;/strong&gt;: Timelines and flowcharts for complex terms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Comparison&lt;/strong&gt;: Compare clauses across similar documents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Q&amp;amp;A System&lt;/strong&gt;: Ask questions about specific clauses&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Tech Stack&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Flask, SQLAlchemy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: Google OAuth (Authlib)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI/ML&lt;/strong&gt;: Google Gemini AI for clause analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Processing&lt;/strong&gt;: pypdf, python-docx, pytesseract (OCR)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: Tailwind CSS, Vanilla JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: SQLite (development), PostgreSQL (production)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup Instructions&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Prerequisites&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.9 or higher&lt;/li&gt;
&lt;li&gt;Google OAuth credentials&lt;/li&gt;
&lt;li&gt;Gemini API key&lt;/li&gt;
&lt;li&gt;Tesseract OCR (for…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Just-Mike4/legal-check" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I built LegalCheck using Flask for the backend, Jinja2 for templating, Tailwind CSS for UI, and Google GenAI (Gemini 2.5) for the contract analysis.&lt;/p&gt;

&lt;p&gt;Thanks for Reading!.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Custom QuerySets in Django: Writing Cleaner, Reusable Queries</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Sat, 20 Sep 2025 07:38:57 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/custom-querysets-in-django-writing-cleaner-reusable-queries-d3a</link>
      <guid>https://dev.to/ezeanamichael/custom-querysets-in-django-writing-cleaner-reusable-queries-d3a</guid>
      <description>&lt;h1&gt;
  
  
  Custom QuerySets in Django: Writing Cleaner, Reusable Queries
&lt;/h1&gt;

&lt;p&gt;When building Django applications, especially as they scale when creating views and similar components, it's easy to find yourself using the same queries repeatedly in different logical steps. The repetition makes the code hard to maintain and can cause some confusion. &lt;/p&gt;

&lt;p&gt;This is where querysets come in. Custom querysets allow us to write cleaner code, keeping some business logic close to your models while avoiding duplication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But first, what is a queryset?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A queryset is simply a collection of database queries in Django, it allows us to make queries through Django’s ORM to the database. By default, objects is Django’s built-in Manager that returns a QuerySet. But what if you always need the query? Rewriting the filter everywhere does not follow the DRY (Don’t Repeat Yourself) principle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from blog.models import Post

# Default QuerySets  
all_posts = Post.objects.all()  
published_posts = Post.objects.filter(status="published")  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's get into the programming aspect. How do you define a custom queryset? We use the models imported from Django’s DB and inherit from the Queryset. Here’s an example, Suppose you have a Post model and you want to make some repeated queries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models

class PostQuerySet(models.QuerySet):  
    def published(self):  
        return self.filter(status="published")

    def drafts(self):  
        return self.filter(status="draft")

    def by_author(self, author):  
        return self.filter(author=author)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now attach this to our model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Post(models.Model):  
    STATUS_CHOICES = (  
        ("draft", "Draft"),  
        ("published", "Published"),  
    )

    title = models.CharField(max_length=200)  
    content = models.TextField()  
    status = models.CharField(max_length=10, choices=STATUS_CHOICES)  
    author = models.ForeignKey("auth.User", on_delete=models.CASCADE)

    # Attach the custom QuerySet  
    objects = PostQuerySet.as_manager()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can make queries like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# All published posts  
Post.objects.published()

# All drafts by a specific author  
Post.objects.drafts().by_author(user)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even chain them,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Get published posts by a specific author  
Post.objects.published().by_author(user)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a way, querysets and managers are similar and different. How would you know when to use either?&lt;br&gt;&lt;br&gt;
Use Custom QuerySets when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want reusable filters (like .published(), .active()).
&lt;/li&gt;
&lt;li&gt;You need chainable queries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use Custom Managers when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want to override get_queryset() itself.
&lt;/li&gt;
&lt;li&gt;You need queries that return something other than a QuerySet (like creating objects or aggregations).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, We’ve seen how qureysets helps us in keeping the DRY principle and writing cleaner code, in the next one we’ll consider when and how the 3 powerhouses in our models.py can be used together in &lt;em&gt;Custom Model Methods vs. Managers vs. QuerySets: When to Use Each.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>drf</category>
      <category>backend</category>
      <category>database</category>
    </item>
    <item>
      <title>Understanding Django Managers: The Gateway to Your Data</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Sat, 13 Sep 2025 12:53:06 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/understanding-django-managers-the-gateway-to-your-data-1cih</link>
      <guid>https://dev.to/ezeanamichael/understanding-django-managers-the-gateway-to-your-data-1cih</guid>
      <description>&lt;p&gt;When working with Django models, you’re not just defining tables, you’re also defining how to interact with them. In this article, we’re going to use one of Django’s powerful tools when making queries, which is Django managers. Django managers define the interface for the interaction between your code and the database. The default one most are used to is the “&lt;em&gt;objects&lt;/em&gt;” as in “&lt;em&gt;students.objects.all()&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;Examples;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Student(models.Model):  
    name = models.CharField(max_length=100)  
    age = models.PositiveIntegerField()

# Using the default manager  
all_students = Student.objects.all()  
adults = Student.objects.filter(age__gte=18)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a Custom Manager
&lt;/h2&gt;

&lt;p&gt;Alongside this default manager, we can also create custom managers. But why would we need to do so?&lt;br&gt;&lt;br&gt;
While &lt;em&gt;objects&lt;/em&gt; is useful when performing basic queries, when dealing with larger projects, custom managers give user-defined reusable logic in code. Let's look at a practical example of this by using Django’s &lt;em&gt;models.&lt;/em&gt; We then define a class that inherits from it and overwrites the &lt;em&gt;get_queryset&lt;/em&gt; method in the student manager.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ActiveStudentManager(models.Manager):  
    def get_queryset(self):  
        return super().get_queryset().filter(is_active=True)

class Student(models.Model):  
    name = models.CharField(max_length=100)  
    is_active = models.BooleanField(default=True)

    objects = models.Manager()        # Default manager  
    active = ActiveStudentManager()   # Custom manager  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How then do you use this custom manager? Look below,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student.objects.all()   # returns all students  
Student.active.all()    # returns only active students  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding Custom Methods to Managers
&lt;/h2&gt;

&lt;p&gt;You can also add methods to model managers to perform specific actions or queries. An example is sorting students by age group.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class StudentManager(models.Manager):  
    def teenagers(self):  
        return self.filter(age__gte=13, age__lte=19)

    def adults(self):  
        return self.filter(age__gte=18)

class Student(models.Model):  
    name = models.CharField(max_length=100)  
    age = models.PositiveIntegerField()

    objects = StudentManager()  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then using,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student.objects.teenagers()  # all students aged 13–19  
Student.objects.adults()     # all students aged 18+  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multiple Managers in a Model
&lt;/h2&gt;

&lt;p&gt;As seen in the first example, you can add more than 1 manager to a model to ease and streamline usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ActiveStudentManager(models.Manager):  
    def get_queryset(self):  
        return super().get_queryset().filter(is_active=True)

class Student(models.Model):  
    name = models.CharField(max_length=100)  
    is_active = models.BooleanField(default=True)

    objects = models.Manager()        # Default manager  
    active = ActiveStudentManager()   # Custom manager  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managers vs. QuerySets
&lt;/h2&gt;

&lt;p&gt;Managers are not qureysets. Managers are entry points to the database while QuerySets represent the actual collection of records. In the next article, we’ll see how we can use Querysets. &lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Managers in a Model
&lt;/h2&gt;

&lt;p&gt;A model can have multiple managers, but they have one default manager, which is the &lt;em&gt;objects&lt;/em&gt;; this can be subject to change, though.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Student(models.Model):  
    name = models.CharField(max_length=100)  
    is_active = models.BooleanField(default=True)

    objects = models.Manager()       # default  
    active = ActiveStudentManager()  # custom  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The default manager is used in admin and migrations, so always keep &lt;em&gt;objects = models.Manager()&lt;/em&gt; unless you know what you’re doing.&lt;/p&gt;

&lt;p&gt;We have seen how Django managers can be used in this article, and how they help in holding some business logic and reusable queries. In the next article, we will explore querysets in Django and how they are used alongside this.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>backend</category>
      <category>data</category>
    </item>
    <item>
      <title>ForeignKey vs ManyToMany vs OneToOne: When to Use Each in Django</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Sat, 06 Sep 2025 07:21:35 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/foreignkey-vs-manytomany-vs-onetoone-when-to-use-each-in-django-44eg</link>
      <guid>https://dev.to/ezeanamichael/foreignkey-vs-manytomany-vs-onetoone-when-to-use-each-in-django-44eg</guid>
      <description>&lt;p&gt;In common Relational Database concepts, relationships between tables are nothing new; these help to represent connections between tables and link them together. In this article, I’ll explore some common DB relationships and how they are used in Django. These include One-to-One, One-to-Many (foreign key), and Many-to-Many Relationships.&lt;/p&gt;

&lt;h2&gt;
  
  
  One to One
&lt;/h2&gt;

&lt;p&gt;In a one-to-one relationship, one record in a model corresponds exactly to one record in another model. For example, a user’s relationship to his profile, using Django’s built-in user model, we can extend by creating a user profile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.contrib.auth.models import User

class Profile(models.Model):  
    user = models.OneToOneField(User, on_delete=models.CASCADE)  
    bio = models.TextField()  
    birth_date = models.DateField(null=True, blank=True)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  One to Many
&lt;/h2&gt;

&lt;p&gt;In a one-to-many relationship, one record in a model corresponds to many records in another model. This means a record in a table can be linked to multiple records in another table, and this is done using a foreign key. A practical example of this is of an author having many books.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models

class Author(models.Model):  
    name = models.CharField(max_length=100)

class Book(models.Model):  
    title = models.CharField(max_length=200)  
    author = models.ForeignKey(Author, on_delete=models.CASCADE)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Many to Many
&lt;/h2&gt;

&lt;p&gt;In a many-to-many relationship, a table is linked to another table where multiple records in one table are related to various records in the other. An example is a relationship between students and courses. A student can enroll in many courses, and a course can have many students.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Student(models.Model):  
    name = models.CharField(max_length=100)

class Course(models.Model):  
    title = models.CharField(max_length=200)  
    students = models.ManyToManyField(Student)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In each of these types of relationships in Django, there are some common parameters and their meaning. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On_delete: In a foreign key and the one-to-one field, there’s the on_delete parameter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The common options are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;models.CASCADE: delete related objects too.
&lt;/li&gt;
&lt;li&gt;models.SET_NULL: set field to NULL (requires null=True).
&lt;/li&gt;
&lt;li&gt;models.PROTECT: prevent deletion if related objects exist.
&lt;/li&gt;
&lt;li&gt;models.SET_DEFAULT: set field to default value.
&lt;/li&gt;
&lt;li&gt;Related_name: This defines the name used to access the reverse relationship; without this, Django automatically adds _set, i.e, (model_name)_set.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   class Course(models.Model):  
       title = models.CharField(max_length=200)  
       students = models.ManyToManyField(  
           "Student",   
           related_name="enrolled_courses"  
       )  

   #referenced here using  

   student = Student.objects.first()  
   student.enrolled_courses.all()  # instead of student.course_set.all()  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;blank and null: Blank and Null are boolean parameters; null=True means the database can store NULL values, while blank=True means validation allows empty values.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   class Profile(models.Model):  
       bio = models.TextField(blank=True, null=True)  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Through: This is unique for the many-to-many field, it lets you define a custom intermediate model for many-to-many relationships.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   class Enrollment(models.Model):  
       student = models.ForeignKey("Student", on_delete=models.CASCADE)  
       course = models.ForeignKey("Course", on_delete=models.CASCADE)  
       enrolled_at = models.DateTimeField(auto_now_add=True)  
       grade = models.CharField(max_length=2, blank=True)  

   class Course(models.Model):  
       title = models.CharField(max_length=200)  
       students = models.ManyToManyField("Student", through="Enrollment")  

   # usage below  
   student = Student.objects.first()  
   course = Course.objects.first()  
   Enrollment.objects.create(student=student, course=course, grade="A")  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this article, we’ve been able to break down relationships in Django, their parameters, and how they’re referenced. Next, we’ll move into understanding Django managers.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>Designing Better Django Models: Tips for Scalability and Clarity</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Sat, 30 Aug 2025 10:05:17 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/designing-better-django-models-tips-for-scalability-and-clarity-3ed2</link>
      <guid>https://dev.to/ezeanamichael/designing-better-django-models-tips-for-scalability-and-clarity-3ed2</guid>
      <description>&lt;p&gt;I’ve been using Django for a while now, and I’ve seen countless guides on how to write better code, structured code, and the list goes on. I decided to write mine to cover one major aspect, which is simplicity, for this guide, and following ones, I'll make use of simple wordings and easy to understand python code that can guide you (or anyone) in writing simple and powerful django applications, and to start with, the models, which are the backbone of traditional backend applications, a poorly written model design will cause a lot of problems in your application like optimization and scaling difficulty, lets explore practices and use cases, for this article ill make use of a blog model to explain the process. To begin, we start with defining a clear data model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a Clear Data Model
&lt;/h2&gt;

&lt;p&gt;Before you start writing code, it is essential to understand what you want the code to do first. It's always best to get a clear picture of what you want your database to look like through design, including the relationships. Tools like Draw.io can help with that. (This article won't cover how to design your model; it's more on writing code using Django)&lt;/p&gt;

&lt;p&gt;After designing your database and relationships, we're ready to hop into the code. Keep 2 things in mind: &lt;strong&gt;naming matters,&lt;/strong&gt; and &lt;strong&gt;keep models simple.&lt;/strong&gt; For example, here's our post model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models  
class Post(models.Model):  
    title = models.CharField(max_length=255)  
    content = models.TextField()  
    published_at = models.DateTimeField(auto_now_add=True)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's always best to use meaningful, singular model names like Post, not Posts, and we limit it to 3 fields: title, content, and published date. Next, let's look at choosing the right type of fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Field Types
&lt;/h2&gt;

&lt;p&gt;In Django’s models, there are several field types like CharField, TextField, and DateTimeField used in the above, but aside from this, there are others like URLField, EmailField. Let's take a look at the user profile table below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserProfile(models.Model):  
    email = models.EmailField(unique=True)     
    website = models.URLField(blank=True)  
    bio = models.TextField(blank=True)  
    reputation = models.DecimalField(max_digits=6, decimal_places=2, default=0.00)    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's always best to use a specific field for some of the following reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Django provides built-in validation
&lt;/li&gt;
&lt;li&gt;Helps Django’s retrieval from the database
&lt;/li&gt;
&lt;li&gt;It saves space and optimizes in the database (e.g, using Charfield for fields like name with a max length set, rather than a text field, which is better suited for description)
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let's consider primary keys and identifiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Primary Keys &amp;amp; Identifiers
&lt;/h2&gt;

&lt;p&gt;When talking of primary keys, we consider the IDs of these tables since these are what link them up. It also brings up the question, when it is best to use Django’s default ID and a UUID.&lt;br&gt;&lt;br&gt;
By default, Django creates an ID this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id = models.BigAutoField(primary_key=True)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It auto-increments, and it's useful for small to medium-scale projects. The downside of this is that it's predictable, and it can expose user count or allow enumeration attacks in APIs.&lt;br&gt;&lt;br&gt;
UUIDs, on the other hand, are especially useful in &lt;strong&gt;distributed systems&lt;/strong&gt; or &lt;strong&gt;microservices&lt;/strong&gt;, where you can’t rely on a central database to generate sequential IDs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import uuid  
class UserProfile(models.Model):  
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)  
    email = models.EmailField(unique=True)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;UUIDs are harder to guess and good for public-facing URLs, but they require larger storage and are slower to index. Let's take a step into relationships.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relationships Done Right
&lt;/h2&gt;

&lt;p&gt;In Django, there are 3 major types of relationships: one-to-one, one-to-many (Foreign key), and many-to-many. Below are examples of such relationships.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Post(models.Model):  
    author = models.ForeignKey("Author", on_delete=models.CASCADE, related_name="posts")  
    title = models.CharField(max_length=255)  
    content = models.TextField()

class Comment(models.Model):  
    post = models.ForeignKey("Post", on_delete=models.CASCADE, related_name="comments")  
    author = models.CharField(max_length=100)  
    body = models.TextField()

class Tag(models.Model):  
    name = models.CharField(max_length=50, unique=True)  
    posts = models.ManyToManyField("Post", related_name="tags")  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;models.Foreignkey, models.ManyToMany and models.OneToOne.&lt;br&gt;&lt;br&gt;
You’ll notice related name parameter placed in these relationship, it is the &lt;strong&gt;reverse relation name&lt;/strong&gt; Django will use when looking up related objects.&lt;br&gt;&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;post = Post.objects.first()  
post.comments.all()   # instead of post.comment_set.all()  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The on_delete argument tells Django what to do with related objects when the referenced object is deleted. In our case models.CASCADE means delete the dependent rows too. This is important so it doesnt retain dependent roles in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model Methods &amp;amp; Business Logic
&lt;/h2&gt;

&lt;p&gt;When writing business logic in the model.py file, we must ensure that this is not where all our methods should be; basically, just simple and commonly used logic can be implemented here, for example, using sample model methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Post(models.Model):  
    title = models.CharField(max_length=255)  
    content = models.TextField()  
    published_at = models.DateTimeField(auto_now_add=True)

    def is_published_recently(self):  
        from django.utils import timezone  
        return self.published_at &amp;gt;= timezone.now() - timezone.timedelta(days=7)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The post.is_published_recently() can now be called and works as intended from here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;Finally, for some performance considerations, here are some tips: use indexes for frequent lookups; they query faster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class BlogPost(models.Model):  
    slug = models.SlugField(unique=True, db_index=True)  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Keeping Models Maintainable
&lt;/h2&gt;

&lt;p&gt;Utilizing some built-in functions in Django models to help readability and reusability is mostly advised, adding &lt;strong&gt;str&lt;/strong&gt; for readability, adding a class meta for verbose name, and ordering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Post(models.Model):  
    title = models.CharField(max_length=255)  
    body = models.TextField()  
    created_at = models.DateTimeField(auto_now_add=True)

    Class Meta:  
        ordering = ["-created_at"]  
        verbose_name = "Blog Post"

    def __str__(self):  
        return self.title  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this article, we’ve seen the need for simplicity in creating Django models and utilizing functionalities to optimize performance. In the next article, we’ll consider in-depth the use of foreign keys, one-to-one and many-to-many.&lt;/p&gt;

</description>
      <category>django</category>
      <category>backenddevelopment</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Distributed Systems: Designing Scalable Python Backends</title>
      <dc:creator>Ezeana Micheal</dc:creator>
      <pubDate>Mon, 27 Jan 2025 07:08:27 +0000</pubDate>
      <link>https://dev.to/ezeanamichael/distributed-systems-designing-scalable-python-backends-1lod</link>
      <guid>https://dev.to/ezeanamichael/distributed-systems-designing-scalable-python-backends-1lod</guid>
      <description>&lt;p&gt;Almost all systems today connected through the World Wide Web are distributed systems. Distributed systems are a group of multiple computers or servers that work together and functionalize optimally. This allows multiple users to utilize such software or services without facing slow loading times and poor performance. For example, imagine you build a website and host it on a single-user server, this would perform well until user traffic increases, demanding more resources and speed. Distributed systems aid performance and flexibility by splitting the application into individual services on separate servers that interact with each other. This would seem like a simple software or application to the user, but on the backend, it's multiple interconnected nodes talking to each other.&lt;/p&gt;

&lt;p&gt;Python programming language is one of the slowest, but one of the most useful languages today, ever since the advent of artificial intelligence, machine learning, and Large Language Models, python has been the go-to language for these, but no one wants a chatbot or an ml service that takes a long amount of processing time to work, distributed systems is a key to optimizing such on an application. In this article, we’ll consider key features of distributed systems, why you should use distributed systems, and how we can scale a distributed system with Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features Of Distributed Systems
&lt;/h2&gt;

&lt;p&gt;The following are key features of distributed systems that make them work optimally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nodes&lt;/strong&gt;: Individual computers or processes that work together as part of the system; each node performs certain tasks and connects with others to ensure the system functions properly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication Protocols&lt;/strong&gt;: Nodes can communicate and share information thanks to protocols like HTTP, gRPC, or TCP/IP, which guarantee dependable communication between components even when they are on different networks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Resources&lt;/strong&gt;: Distributed systems frequently rely on resources like databases, file systems, or message queues; proper management enables consistent and efficient access across all nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fault Tolerance&lt;/strong&gt;: Even if a node fails, distributed systems continue running, eliminating a single point of failure. Redundancy and replication techniques ensure reliability and high availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: The ability to handle increased load by adding more nodes (horizontal scaling) or enhancing the capacity of existing nodes (vertical scaling). Scalability ensures the system remains responsive under high demand.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Scalability Matters?
&lt;/h2&gt;

&lt;p&gt;Scalability as mentioned earlier is a system’s ability to handle increased load by adding resources. This ensures the system is always at optimal performance during traffic spikes. There are 2 major types of scaling:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal scaling&lt;/strong&gt;: This involves using more machines and servers for the application to work smoothly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vertical scaling&lt;/strong&gt;: This involves increasing the system's RAM, storage, and capacity.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to Design Scalable Python Backends
&lt;/h2&gt;

&lt;p&gt;Knowledge of the right tools is required to design scalable Python backends to allow the system to grow and remain efficient. Below are some key tools and strategies for building scalable Python backends.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;APIs&lt;/strong&gt;: Use lightweight frameworks like Flask or Fast API to build scalable backend APIs. They are relatively easy to use when creating rest APIs. Fast API is best for performance and its support for asynchronous programming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous processing&lt;/strong&gt;: To support the main application, it is wise to offload some background tasks(like emails or data processing) using Cerely with Redis as the message broker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Balancing&lt;/strong&gt;: To balance the load or traffic on the application, distribute incoming requests evenly across backend servers like Nginx or HAProxy in the distributed system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: A task queue with Celery and Redis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# tasks.py
from celery import Celery

app = Celery('tasks', broker='redis://localhost:6379/0')

@app.task
def process_order(order_id):
    print(f"Processing order {order_id}")

# Adding a task to the queue
process_order.delay(123)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data Management in Distributed Systems
&lt;/h2&gt;

&lt;p&gt;Maintaining the CAP theorem or properties when managing data in distributed systems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: All nodes in the distributed system see the same data. If data is updated in a node, all nodes should reflect the updated value immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability&lt;/strong&gt;: The system responds even during a node failure. The system should always be operational.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partition Tolerance&lt;/strong&gt;: It should work despite network failures between nodes. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful databases are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL Databases like PostgreSQL for transactional consistency.&lt;/li&gt;
&lt;li&gt;NoSQL Databases like MongoDB for scalable, flexible schemas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example is a case setting up a distributed MongoDB cluster to store and retrieve user data across multiple nodes, ensuring high availability and fault tolerance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools for Deployment and Scaling
&lt;/h2&gt;

&lt;p&gt;Deployment and scaling are when tools like docker and Kubernetes come in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt;: Docker is used to containerize Python backend applications for consistent environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt;: This helps automate the deployment, scaling, and management of the containerized application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: Below is a simple/basic example of deploying a Python backend application using docker and Kubernetes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfile:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; FROM python:3.10
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Kubernetes Deployment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: flask-backend
  template:
    metadata:
      labels:
        app: flask-backend
    spec:
      containers:
      - name: flask-backend
        image: flask-app:latest
        ports:
        - containerPort: 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring and Maintenance
&lt;/h2&gt;

&lt;p&gt;In distributed systems, it is important to monitor and maintain nodes around the system as they interact and function as one. This will help to identify and fix possible faults. &lt;/p&gt;

&lt;p&gt;Examples of tools that do this are Prometheus and Grafana.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt;: Prometheus helps to collect metrics on API performance, database latency, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana&lt;/strong&gt;: Grafana visualizes metrics with customizable dashboards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Case Study: Scalable Ecommerce Backend
&lt;/h2&gt;

&lt;p&gt;Before I conclude this article, let's look at how to apply distributed systems during the development of an e-commerce system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;FastAPI can be used for the backend system and handle API’s for order processing.&lt;/li&gt;
&lt;li&gt;Celery with Redis is used to handle asynchronous background processing of tasks like payments or inventory updates.&lt;/li&gt;
&lt;li&gt;The application is deployed on docker and Kubernetes to ensure the scaling of the system.&lt;/li&gt;
&lt;li&gt;The application is monitored using tools like Prometheus.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By using Python tools like Flask, FastAPI, Celery, Docker, and Kubernetes, developers can build robust and scalable systems. In this article, we’ve covered the general terms related to distributed systems and how they can help, as well as some basic examples with Python. You can do advanced research on these tools and how they can work together and help. Start experimenting with these tools and create backends that can handle the challenges of real-world traffic and growth. Happy hacking.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>python</category>
      <category>distributedsystems</category>
      <category>fastapi</category>
    </item>
  </channel>
</rss>
