<?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: Kfir</title>
    <description>The latest articles on DEV Community by Kfir (@kfir-g).</description>
    <link>https://dev.to/kfir-g</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%2F1438318%2Fde562ec4-bbad-4f30-bbbd-b37a0a31a7f1.jpg</url>
      <title>DEV Community: Kfir</title>
      <link>https://dev.to/kfir-g</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kfir-g"/>
    <language>en</language>
    <item>
      <title>From Passive FastAPI Developer to Real FastAPI Engineer- Part 2</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Mon, 08 Dec 2025 03:28:33 +0000</pubDate>
      <link>https://dev.to/kfir-g/from-passive-fastapi-developer-to-real-fastapi-engineer-part-2-1510</link>
      <guid>https://dev.to/kfir-g/from-passive-fastapi-developer-to-real-fastapi-engineer-part-2-1510</guid>
      <description>&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%2F66lcvjbc630fjvznb9v3.jpeg" 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%2F66lcvjbc630fjvznb9v3.jpeg" alt="img" width="800" height="532"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Kaue Barbier: &lt;a href="https://www.pexels.com/photo/28182837/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/28182837/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;Your First Raw ASGI App (No Frameworks Allowed).&lt;/p&gt;

&lt;p&gt;Before FastAPI.&lt;br&gt;&lt;br&gt;
Before Starlette.&lt;br&gt;&lt;br&gt;
Before Uvicorn.&lt;/p&gt;

&lt;p&gt;There is &lt;strong&gt;ASGI-&lt;/strong&gt; the low-level async contract that makes modern Python web apps possible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/gitconnected/from-passive-fastapi-developer-to-real-fastapi-engineer-part-1-asgi-059a588a54ae" rel="noopener noreferrer"&gt;In Part 1&lt;/a&gt;, we learned how HTTP works at the byte level. Now in Part 2, you’re going to &lt;strong&gt;build an ASGI app from scratch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No router.&lt;br&gt;&lt;br&gt;
No response class.&lt;br&gt;&lt;br&gt;
No request.query_params.&lt;br&gt;&lt;br&gt;
Just you and the ASGI spec.&lt;/p&gt;

&lt;p&gt;Let’s turn the black box into a glass box.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Why Build a Raw ASGI App?
&lt;/h3&gt;

&lt;p&gt;Most FastAPI developers never touch ASGI.&lt;br&gt;&lt;br&gt;
Most don’t know what’s inside a request.&lt;br&gt;&lt;br&gt;
Most don’t know what Uvicorn gives them.&lt;br&gt;&lt;br&gt;
Most don’t know what Starlette abstracts.&lt;/p&gt;

&lt;p&gt;This chapter fixes that.&lt;/p&gt;

&lt;p&gt;By the end, you will understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How a web server calls your ASGI application&lt;/li&gt;
&lt;li&gt;What a &lt;strong&gt;scope&lt;/strong&gt;  is.&lt;/li&gt;
&lt;li&gt;How receive() and send() work.&lt;/li&gt;
&lt;li&gt;How a single HTTP request becomes  &lt;strong&gt;events.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The difference between WSGI (sync) and ASGI (async).&lt;/li&gt;
&lt;li&gt;What FastAPI really sits on top of.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you know this layer, FastAPI becomes predictable, not magical.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. ASGI in 60 Seconds
&lt;/h3&gt;

&lt;p&gt;ASGI = &lt;strong&gt;Asynchronous Server Gateway Interface&lt;/strong&gt; The modern replacement for WSGI.&lt;/p&gt;

&lt;p&gt;WSGI ASGI Sync Async One call, one response Event-driven HTTP only HTTP + WebSocket + lifespan Blocking Non-blocking.&lt;/p&gt;
&lt;h3&gt;
  
  
  The ASGI callable signature
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async def app(scope, receive, send):
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Where:&lt;/p&gt;

&lt;p&gt;Parameter Meaning scope Immutable connection info (method, headers, path, etc.) receive() Async function: read events from server (request body, disconnect) send() Async function: send events to server (response start/body)&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Step-by-Step: Build a Raw ASGI App
&lt;/h3&gt;

&lt;p&gt;Create a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;raw_asgi_app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# raw_asgi_app.py

async def app(scope, receive, send):
    print("=== SCOPE RECEIVED ===")
    print(scope)
    # Only handle HTTP requests
    if scope["type"] != "http":
        return
    # Wait for request event (headers/body)
    event = await receive()
    print("=== REQUEST EVENT ===")
    print(event)
    body = b"Hello from raw ASGI!"
    # Send HTTP start
    await send({
        "type": "http.response.start",
        "status": 200,
        "headers": [(b"content-type", b"text/plain")]
    })
    # Send response body
    await send({
        "type": "http.response.body",
        "body": body,
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Run the App With Uvicorn
&lt;/h3&gt;

&lt;p&gt;Even though we didn’t build routing or server logic, Uvicorn &lt;strong&gt;can execute any ASGI app&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;uvicorn raw_asgi_app:app --host 127.0.0.1 --port 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl "http://127.0.0.1:8000/hello?name=kfir"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch your terminal print:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;scope&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;request event&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The ASGI lifecycle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the moment where ASGI becomes real.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Understanding the ASGI Scope
&lt;/h3&gt;

&lt;p&gt;Example (shortened):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "type": "http",
    "method": "GET",
    "path": "/hello",
    "query_string": b"name=kfir",
    "headers": [
        (b"host", b"127.0.0.1:8000"),
        (b"user-agent", b"curl/8.0"),
        (b"accept", b"*/*")
    ],
    "client": ("127.0.0.1", 53921),
    "server": ("127.0.0.1", 8000)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is exactly the same information Starlette uses to build a Request object.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Understanding receive() Events
&lt;/h3&gt;

&lt;p&gt;For HTTP requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "type": "http.request",
    "body": b"",
    "more_body": False
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For clients disconnecting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"type": "http.disconnect"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Understanding send() Events
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Start the HTTP response:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "type": "http.response.start",
    "status": 200,
    "headers": [...],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Send body:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "type": "http.response.body",
    "body": b"...",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If streaming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"body": chunk, "more_body": True}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Diagram — The ASGI Lifecycle
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client (curl)
   │
   ▼
Raw HTTP bytes
   │
   ▼
Uvicorn (parsing, connection, events)
   │
   ▼
ASGI App (your app)
   │ ▲
 send() │ receive()
   ▼ │
Response events
   │
   ▼
Uvicorn → TCP → Client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9. Compare This With WSGI
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def app(environ, start_response):
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async def app(scope, receive, send):
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WSGI gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sync&lt;/li&gt;
&lt;li&gt;blocking&lt;/li&gt;
&lt;li&gt;no streaming&lt;/li&gt;
&lt;li&gt;no WebSockets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ASGI gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;async&lt;/li&gt;
&lt;li&gt;streaming&lt;/li&gt;
&lt;li&gt;background tasks&lt;/li&gt;
&lt;li&gt;WebSockets&lt;/li&gt;
&lt;li&gt;HTTP/2 support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  10. Quick Exercises
&lt;/h3&gt;

&lt;h3&gt;
  
  
  1. Return JSON manually
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body = b'{"msg":"hello"}'
headers = [(b"content-type", b"application/json")]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Parse query params manually
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qs = scope["query_string"].decode()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Print content length
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for name, value in scope["headers"]:
    if name == b"content-length":
        print("Content-Length:", value.decode())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Respond in two chunks (streaming)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await send({"type": "http.response.body", "body": b"Hello ", "more_body": True})
await send({"type": "http.response.body", "body": b"Kfir!"})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Wrapping Up: Why You Just Built ASGI by Hand
&lt;/h4&gt;

&lt;p&gt;By writing an ASGI app without FastAPI, Starlette, or any framework, you’ve crossed an important threshold: you now understand what &lt;strong&gt;actually happens before any modern Python web framework can do its job&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You saw how:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A TCP connection becomes &lt;strong&gt;raw HTTP bytes.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Those bytes become an &lt;strong&gt;ASGI scope + events.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your Python code responds using &lt;strong&gt;send()/receive() .&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The entire request/response lifecycle is just &lt;strong&gt;structured events&lt;/strong&gt; , not magic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This understanding is what separates a &lt;strong&gt;FastAPI user&lt;/strong&gt; from a &lt;strong&gt;FastAPI engineer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every routing decision, middleware execution, background task, and WebSocket message ultimately reduces to the exact pattern you implemented manually.&lt;/p&gt;

&lt;p&gt;If you can build this tiny ASGI app from scratch, you can understand, debug, and optimize any modern Python web stack- from Uvicorn workers to Starlette internals all the way up to FastAPI dependencies.&lt;/p&gt;

&lt;p&gt;Congratulations. You’re officially “under the hood.”&lt;/p&gt;

&lt;h3&gt;
  
  
  Official References (Highly Recommended)
&lt;/h3&gt;

&lt;p&gt;These are the &lt;em&gt;authoritative sources&lt;/em&gt; behind everything in this blog:&lt;/p&gt;

&lt;h3&gt;
  
  
  ASGI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Official ASGI Spec
&lt;a href="https://asgi.readthedocs.io/en/latest/specs/main.html" rel="noopener noreferrer"&gt;https://asgi.readthedocs.io/en/latest/specs/main.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  HTTP / Request Lifecycle
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;HTTP Semantics — RFC 9110
&lt;a href="https://httpwg.org/specs/rfc9110.html" rel="noopener noreferrer"&gt;https://httpwg.org/specs/rfc9110.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;HTTP/1.1 Message Syntax — RFC 9112
&lt;a href="https://httpwg.org/specs/rfc9112.html" rel="noopener noreferrer"&gt;https://httpwg.org/specs/rfc9112.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Uvicorn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Official Uvicorn Docs
&lt;a href="https://www.uvicorn.org" rel="noopener noreferrer"&gt;https://www.uvicorn.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Starlette (ASGI Toolkit)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Official Starlette Docs
&lt;a href="https://www.starlette.io" rel="noopener noreferrer"&gt;https://www.starlette.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FastAPI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Official FastAPI Docs
&lt;a href="https://fastapi.tiangolo.com" rel="noopener noreferrer"&gt;https://fastapi.tiangolo.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WSGI / CGI History
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;WSGI PEP 333
&lt;a href="https://peps.python.org/pep-0333" rel="noopener noreferrer"&gt;https://peps.python.org/pep-0333&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WSGI PEP 3333
&lt;a href="https://peps.python.org/pep-3333" rel="noopener noreferrer"&gt;https://peps.python.org/pep-3333&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Common Gateway Interface
&lt;a href="https://www.rfc-editor.org/rfc/rfc3875" rel="noopener noreferrer"&gt;https://www.rfc-editor.org/rfc/rfc3875&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>starlette</category>
      <category>fastapi</category>
      <category>python</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>From Passive FastAPI Developer to Real FastAPI Engineer- Part 1: ASGI</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Fri, 21 Nov 2025 15:42:25 +0000</pubDate>
      <link>https://dev.to/kfir-g/from-passive-fastapi-developer-to-real-fastapi-engineer-part-1-asgi-5d5j</link>
      <guid>https://dev.to/kfir-g/from-passive-fastapi-developer-to-real-fastapi-engineer-part-1-asgi-5d5j</guid>
      <description>&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%2Fyytieky1r061vwop96dt.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%2Fyytieky1r061vwop96dt.png" alt="Kaue-Barbier" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by Kaue Barbier: &lt;a href="https://www.pexels.com/photo/28182842/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/28182842/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding ASGI by Breaking Down an HTTP Request
&lt;/h3&gt;

&lt;p&gt;Modern Python web frameworks feel effortless- you write a function, return some JSON, and boom: a web app. But underneath that smooth layer lies a ton of complexity: raw bytes over TCP, state machines, protocols, concurrency, and error-prone details you never want to touch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ASGI exists to shield you from that pain.&lt;/strong&gt; Let’s break an HTTP request down and see how ASGI fits into the picture.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is ASGI?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ASGI (Asynchronous Server Gateway Interface)&lt;/strong&gt; is a &lt;strong&gt;Python specification&lt;/strong&gt; that defines how web servers communicate with asynchronous Python applications.&lt;/p&gt;

&lt;p&gt;Official spec: &lt;a href="https://asgi.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;https://asgi.readthedocs.io/en/latest/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can think of it as the evolution of WSGI, designed for modern async apps, HTTP/2, and WebSockets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Do We Need ASGI?
&lt;/h3&gt;

&lt;p&gt;Inside every web server, there are three conceptual layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transport -&amp;gt; Protocol -&amp;gt; Application Logic
TCP/SSL HTTP/WS Your FastAPI code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem:&lt;br&gt;&lt;br&gt;
If every framework had to re-implement TCP handling, HTTP parsing, connection lifecycle, etc., we’d live in a world of endless duplicated code and subtle bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ASGI proposes a clean split into two Python components:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Protocol Server&lt;/strong&gt; (e.g., Uvicorn, Hypercorn)&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Handles raw network I/O&lt;/li&gt;
&lt;li&gt;Transforms bytes into structured ASGI events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Application&lt;/strong&gt; (e.g., Starlette, FastAPI, custom ASGI apps)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receives events like http.request&lt;/li&gt;
&lt;li&gt;Returns events like http.response.start and http.response.body&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This “bridge” is ASGI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Protocol Server &amp;lt;---- ASGI ----&amp;gt; Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Quick Timeline of Request Interfaces
&lt;/h3&gt;

&lt;p&gt;Year Spec Notes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1993&lt;/strong&gt; CGI Process-per-request. Ancient, slow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2003&lt;/strong&gt; WSGI (PEP 333) Standardized sync Python apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2010&lt;/strong&gt; WSGI 1.0.1 (PEP 3333) Updated for Python 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2016&lt;/strong&gt; ASGI Async, event-based design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2019&lt;/strong&gt; ASGI 3.0 Modern form used today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why WSGI Wasn’t Enough
&lt;/h3&gt;

&lt;p&gt;WSGI apps are &lt;strong&gt;synchronous&lt;/strong&gt; and take exactly two parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def app(environ, start_response):
    start_response("200 OK", [("Content-Type", "text/plain")])
    return [b"hello"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for HTTP/1.1 but fails for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concurrency&lt;/li&gt;
&lt;li&gt;streaming&lt;/li&gt;
&lt;li&gt;long-lived connections&lt;/li&gt;
&lt;li&gt;WebSockets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WSGI can’t represent async behavior or event-driven protocols.&lt;/p&gt;

&lt;h3&gt;
  
  
  ASGI’s Core Idea
&lt;/h3&gt;

&lt;p&gt;An ASGI application is &lt;strong&gt;an async callable&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;async def app(scope, receive, send):
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;scope&lt;/strong&gt; → Metadata about the connection (type, path, headers, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;receive()&lt;/strong&gt; → Wait for events from server (e.g., request body chunks)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;send()&lt;/strong&gt; → Emit events back (response start, response body, WebSocket messages)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This event system is the real power. It makes communication flexible and supports full-duplex protocols.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Minimal ASGI App (Raw)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async def app(scope, receive, send):
    assert scope["type"] == "http"

await send({
        "type": "http.response.start",
        "status": 200,
        "headers": [(b"content-type", b"text/plain")],
    })
    await send({
        "type": "http.response.body",
        "body": b"Hello from raw ASGI!",
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with Uvicorn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uvicorn example:app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HTTP Request Flow in WSGI vs ASGI
&lt;/h3&gt;

&lt;h3&gt;
  
  
  WSGI Flow (Sync)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
  |
HTTP Request
  |
WSGI Server parses bytes
  |
(environ, start_response)
  |
WSGI App
  |
Iterables of bytes
  |
WSGI Server → HTTP Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No events. No async. No real-time messaging.&lt;/p&gt;

&lt;h3&gt;
  
  
  ASGI Flow (Event-Based, Async)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
  |
Raw Bytes
  |
Protocol Server (Uvicorn / Daphne)
  |
ASGI Event: "http.request", "http.disconnect"
  |
async app(scope, receive, send)
  |
Your app emits events:
  - "http.response.start"
  - "http.response.body"
  - "websocket.accept"
  - "websocket.send"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This model is more general, flexible, and future-proof.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why ASGI Matters
&lt;/h3&gt;

&lt;h3&gt;
  
  
  1. Concurrency
&lt;/h3&gt;

&lt;p&gt;Async I/O = thousands of simultaneous connections.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. WebSockets Support
&lt;/h3&gt;

&lt;p&gt;WSGI can’t do WebSockets.&lt;br&gt;&lt;br&gt;
ASGI makes WebSockets just another protocol supported by events.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Full-Duplex Communication
&lt;/h3&gt;

&lt;p&gt;Send and receive independently- essential for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSockets&lt;/li&gt;
&lt;li&gt;HTTP streaming&lt;/li&gt;
&lt;li&gt;Server-Sent Events&lt;/li&gt;
&lt;li&gt;Background events&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Protocol Independence
&lt;/h3&gt;

&lt;p&gt;ASGI officially supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;http&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;websocket&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;lifespan&lt;/strong&gt; (startup/shutdown events)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More protocols can be added without rewriting frameworks.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Tiny Diagram: ASGI in FastAPI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      ┌──────────────────┐
      │ Your FastAPI │
      │ App │
      └────────┬─────────┘
               │ ASGI callable
               ▼
        ASGI Interface
               ▲
               │ events
      ┌────────┴─────────┐
      │ Uvicorn Server │
      │ (Protocol Layer) │
      └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You “inject” your application into the protocol server.&lt;br&gt;&lt;br&gt;
 The server handles all low-level details- your app just reacts to events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Summary
&lt;/h3&gt;

&lt;p&gt;ASGI gives us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;async concurrency&lt;/li&gt;
&lt;li&gt;flexible event-driven communication&lt;/li&gt;
&lt;li&gt;WebSocket support&lt;/li&gt;
&lt;li&gt;protocol independence&lt;/li&gt;
&lt;li&gt;clean separation of logic vs transport&lt;/li&gt;
&lt;li&gt;a standard interface for modern Python web servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s the foundation that makes &lt;strong&gt;Starlette&lt;/strong&gt; and &lt;strong&gt;FastAPI&lt;/strong&gt; so fast and the reason you never have to manipulate raw HTTP bytes yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  References:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;ASGI official specification&lt;/strong&gt; at &lt;a href="https://asgi.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;https://asgi.readthedocs.io/en/latest/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;WSGI standard&lt;/strong&gt; via &lt;strong&gt;PEP 333&lt;/strong&gt; at &lt;a href="https://peps.python.org/pep-0333/" rel="noopener noreferrer"&gt;https://peps.python.org/pep-0333/&lt;/a&gt; and &lt;strong&gt;PEP 3333&lt;/strong&gt; at &lt;a href="https://peps.python.org/pep-3333/" rel="noopener noreferrer"&gt;https://peps.python.org/pep-3333/&lt;/a&gt;, the historical &lt;strong&gt;CGI/1.1 RFC&lt;/strong&gt; at &lt;a href="https://www.rfc-editor.org/rfc/rfc3875" rel="noopener noreferrer"&gt;https://www.rfc-editor.org/rfc/rfc3875&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Uvicorn ASGI server&lt;/strong&gt; documentation at &lt;a href="https://www.uvicorn.org/" rel="noopener noreferrer"&gt;https://www.uvicorn.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Starlette framework&lt;/strong&gt; documentation at &lt;a href="https://www.starlette.io/" rel="noopener noreferrer"&gt;https://www.starlette.io/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tmhe &lt;strong&gt;HTTP/1.1 protocol&lt;/strong&gt; definition at &lt;a href="https://www.rfc-editor.org/rfc/rfc7230" rel="noopener noreferrer"&gt;https://www.rfc-editor.org/rfc/rfc7230&lt;/a&gt;, and the &lt;strong&gt;WebSocket protocol spec&lt;/strong&gt; at &lt;a href="https://www.rfc-editor.org/rfc/rfc6455" rel="noopener noreferrer"&gt;https://www.rfc-editor.org/rfc/rfc6455&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>python</category>
      <category>softwareengineering</category>
      <category>fastapi</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Why I Use curl Instead of Postman: Learning HTTP by Feel</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Mon, 10 Nov 2025 03:01:51 +0000</pubDate>
      <link>https://dev.to/kfir-g/why-i-use-curl-instead-of-postman-learning-http-by-feel-eeo</link>
      <guid>https://dev.to/kfir-g/why-i-use-curl-instead-of-postman-learning-http-by-feel-eeo</guid>
      <description>&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%2Fxpab4jh809hpk10o1z1s.jpeg" 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%2Fxpab4jh809hpk10o1z1s.jpeg" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Kelly : &lt;a href="https://www.pexels.com/photo/motor-bike-running-close-up-photography-2519374/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/motor-bike-running-close-up-photography-2519374/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;Sometimes you don’t need another GUI. You need to touch the protocol. While everyone opens Postman tabs, I open my terminal and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST http://127.0.0.1:8000/api/shorten \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it- raw, direct, and honest. No buttons, no noise. Just HTTP, right under your fingertips.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning Through the Fingertips
&lt;/h3&gt;

&lt;p&gt;Typing the request yourself makes every part of HTTP visible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;-X POST chooses the verb with intent&lt;/li&gt;
&lt;li&gt;-H sets the headers precisely&lt;/li&gt;
&lt;li&gt;-d sends the data explicitly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And when you hit enter, you see exactly what the server gives back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"short_url": "http://localhost:8000/000001"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every curl command teaches you the rhythm of request and response- the web’s real heartbeat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seeing the Protocol, Not the UI
&lt;/h3&gt;

&lt;p&gt;When you use curl -v, the terminal becomes your microscope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -v http://127.0.0.1:8000/api/000001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see every move:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; GET /api/000001 HTTP/1.1
&amp;gt; Host: 127.0.0.1:8000
&amp;gt; User-Agent: curl/8.7.1
&amp;gt; Accept: */*
&amp;lt; HTTP/1.1 307 Temporary Redirect
&amp;lt; location: https://example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The protocol becomes transparent. You understand what’s happening, not just that something happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discovering More
&lt;/h3&gt;

&lt;p&gt;The beauty of curl is in its depth.&lt;br&gt;&lt;br&gt;
 You don’t have to memorize- you explore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --help
curl --help all | less
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each flag opens a new door like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;-I fetch headers only&lt;/li&gt;
&lt;li&gt;-L follow redirects automatically&lt;/li&gt;
&lt;li&gt;-o save output to a file&lt;/li&gt;
&lt;li&gt;-w "%{http_code}" show only the response code&lt;/li&gt;
&lt;li&gt;-s silent mode (no progress bar)&lt;/li&gt;
&lt;li&gt;-S show errors even when silent&lt;/li&gt;
&lt;li&gt;-k ignore SSL certificate checks&lt;/li&gt;
&lt;li&gt;-x &lt;a href="http://proxy:8080" rel="noopener noreferrer"&gt;http://proxy:8080&lt;/a&gt; use a proxy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The official docs are an endless rabbit hole of discovery: &lt;a href="https://curl.se/docs/manpage.html" rel="noopener noreferrer"&gt;https://curl.se/docs/manpage.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don’t want to leave the terminal, you can explore everything from the CLI itself. Here are a few ways to learn curl directly through your shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# See all common options with short descriptions
curl --help

# See every possible flag and advanced usage
curl --help all | less

# Get a manual-style explanation (same as the website docs)
man curl

# Search inside the manual for a specific flag
man curl | grep timeout

# Quick reminder for a specific option
curl --help | grep -i header
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the beauty of curl: the docs live right where you work. You don’t have to Google anything- the knowledge is built into the tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  How curl Fits in Everyday Dev Life
&lt;/h3&gt;

&lt;p&gt;curl is not just for testing APIs. It’s a daily development companion.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Health checks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -fsSL http://localhost:8000/health || echo "Service down"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Quick response code check
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Debugging with headers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -v -X GET http://localhost:8000/api/items
curl -i http://localhost:8000/api/status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Testing authentication
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -u admin:password http://localhost:8000/api/users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Sending JSON data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST http://localhost:8000/api/shorten \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Simulating different HTTP verbs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X PUT http://localhost:8000/api/items/1 -d '{"name": "updated"}'
curl -X DELETE http://localhost:8000/api/items/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Working inside Docker containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec -it tinyurl-app-fastapi-1 curl http://redis:6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Testing redirects and caching
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -L http://localhost:8000/short/123
curl -I http://localhost:8000/static/style.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9. Monitoring endpoints continuously
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;watch -n 2 'curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/health'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  10. Downloading and uploading files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -O https://example.com/file.zip
curl -T myfile.txt ftp://ftp.example.com/ --user user:password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Terminal Teaches Clarity
&lt;/h3&gt;

&lt;p&gt;When you write requests in curl, you’re not hiding behind tools. You’re speaking to your server directly.&lt;/p&gt;

&lt;p&gt;Each request is a sentence. Each flag is a verb. Each header is a whisper between client and server.&lt;/p&gt;

&lt;p&gt;curl slows you down just enough to see what’s really happening. And once you see it, you never unsee it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feel the Protocol
&lt;/h3&gt;

&lt;p&gt;curl is how you think in HTTP. It’s how you debug, learn, and build with precision. It’s that small command you reach for when nothing else feels right.&lt;/p&gt;

&lt;p&gt;So the next time you open your terminal, type curl and listen. The protocol has been trying to talk to you all along.&lt;/p&gt;




</description>
      <category>softwaredevelopment</category>
      <category>networking</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Shipping FastAPI Like a Pro: My CI/CD Pipeline for TinyURL</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Sun, 02 Nov 2025 05:06:03 +0000</pubDate>
      <link>https://dev.to/kfir-g/shipping-fastapi-like-a-pro-my-cicd-pipeline-for-tinyurl-5b7c</link>
      <guid>https://dev.to/kfir-g/shipping-fastapi-like-a-pro-my-cicd-pipeline-for-tinyurl-5b7c</guid>
      <description>&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%2Fchayfjmkvcdkfaxfyxnu.jpeg" 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%2Fchayfjmkvcdkfaxfyxnu.jpeg" width="800" height="532"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Jan Zakelj: &lt;a href="https://www.pexels.com/photo/a-person-holding-yellow-pipes-9389356/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/a-person-holding-yellow-pipes-9389356/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I first deployed &lt;strong&gt;TinyURL-&lt;/strong&gt; my FastAPI + Postgres + Nginx URL shortener-I wanted a workflow that felt real, not just docker compose up on my laptop. So I built a simple but solid &lt;strong&gt;CI/CD pipeline&lt;/strong&gt; using GitHub Actions that runs tests, builds containers, and deploys automatically.&lt;/p&gt;

&lt;p&gt;Here’s what I learned and how it all fits together.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. CI/CD isn’t about tools- it’s about trust
&lt;/h3&gt;

&lt;p&gt;The main goal wasn’t automation for its own sake. It was to reach a point where every push to main could safely go live- no surprises, no “works on my machine.”&lt;/p&gt;

&lt;p&gt;That mindset helped me design a pipeline that checks, builds, and ships with zero manual steps.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. The build pipeline: test early, build once
&lt;/h3&gt;

&lt;p&gt;Here’s a simplified view of my GitHub Actions workflow (.github/workflows/deploy.yml):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Deploy TinyURL

on:
  push:
    branches:
      - main
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: 3.11
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run tests
        run: pytest -q
      - name: Build Docker images
        run: docker compose -f docker-compose.prod.yml build
      - name: Deploy via SSH
        env:
          HOST: ${{ secrets.EC2_HOST }}
          KEY: ${{ secrets.EC2_SSH_KEY }}
        run: |
          echo "$KEY" &amp;gt; key.pem
          chmod 600 key.pem
          scp -i key.pem docker-compose.prod.yml ubuntu@$HOST:/home/ubuntu/tinyurl/
          ssh -i key.pem ubuntu@$HOST "cd /home/ubuntu/tinyurl &amp;amp;&amp;amp; docker compose -f docker-compose.prod.yml up -d --build"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the heartbeat of my project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every push triggers the pipeline.&lt;/li&gt;
&lt;li&gt;Tests must pass before anything deploys.&lt;/li&gt;
&lt;li&gt;The build happens &lt;strong&gt;once&lt;/strong&gt; , and that same build runs in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Containerization made CI/CD predictable
&lt;/h3&gt;

&lt;p&gt;The biggest win came from using &lt;strong&gt;Docker Compose&lt;/strong&gt; for both local and production setups.&lt;/p&gt;

&lt;p&gt;My dev environment runs three containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;db (Postgres)&lt;/li&gt;
&lt;li&gt;fastapi (backend)&lt;/li&gt;
&lt;li&gt;nginx (reverse proxy)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And my production setup? Exactly the same- except the compose file uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different environment variables&lt;/li&gt;
&lt;li&gt;Mounted SSL certs from Let’s Encrypt&lt;/li&gt;
&lt;li&gt;No auto-reload or debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because both stacks share the same Docker config, the CI/CD pipeline doesn’t need special cases or hacks. If it runs locally, it’ll run in CI.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Deployment: zero downtime, one command
&lt;/h3&gt;

&lt;p&gt;Once the new image is built and pushed, the EC2 host just pulls and restarts containers with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose -f docker-compose.prod.yml up -d --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nginx handles live traffic, so backend containers can restart without downtime. Since FastAPI and Nginx talk over Docker’s internal network, there’s no need for manual reconfiguration.&lt;/p&gt;

&lt;p&gt;That “click → push → deploy” feeling is addictive.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Secrets are sacred
&lt;/h3&gt;

&lt;p&gt;The pipeline uses &lt;strong&gt;GitHub Secrets&lt;/strong&gt; to store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2_HOST (server IP or domain)&lt;/li&gt;
&lt;li&gt;EC2_SSH_KEY (private key)&lt;/li&gt;
&lt;li&gt;POSTGRES_PASSWORD&lt;/li&gt;
&lt;li&gt;FASTAPI_SECRET_KEY&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No plaintext credentials in the repo, no .env files in commits. That alone made me sleep better at night.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. What I learned the hard way
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don’t skip tests-&lt;/strong&gt; failing fast is cheaper than debugging in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don’t rebuild images on the server-&lt;/strong&gt; build once, deploy the same artifact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep Docker tags consistent-&lt;/strong&gt; otherwise latest will surprise you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate HTTPS renewal&lt;/strong&gt; s- my Let’s Encrypt cron job now runs monthly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These small habits make the pipeline boring- and boring pipelines are the best kind.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. The result
&lt;/h3&gt;

&lt;p&gt;Now every push to main runs tests, builds containers, and ships a fully reproducible environment to production. No manual steps, no “did I rebuild?” anxiety. Just continuous confidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tech Stack Recap
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI-&lt;/strong&gt; backend API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL-&lt;/strong&gt; database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamlit-&lt;/strong&gt; frontend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx-&lt;/strong&gt; reverse proxy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions-&lt;/strong&gt; CI/CD&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Compose-&lt;/strong&gt; orchestration&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“CI/CD isn’t about speed- it’s about peace of mind.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;This post is part of my TinyURL series- real-world lessons from turning a small FastAPI project into a production-grade service.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cicdpipeline</category>
      <category>githubactions</category>
      <category>fastapi</category>
      <category>docker</category>
    </item>
    <item>
      <title>How the Browser Works</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Mon, 20 Oct 2025 03:34:34 +0000</pubDate>
      <link>https://dev.to/kfir-g/how-the-browser-works-3369</link>
      <guid>https://dev.to/kfir-g/how-the-browser-works-3369</guid>
      <description>&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%2Ftjtc3e5j4zjgg7bnhnqo.jpeg" 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%2Ftjtc3e5j4zjgg7bnhnqo.jpeg" width="800" height="1333"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Jonny Caspari on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most of us use browsers every day, but few understand how they actually work under the hood.&lt;br&gt;&lt;br&gt;
 Knowing this helps us write more efficient, predictable web applications — and, honestly, it’s just fascinating.&lt;/p&gt;

&lt;p&gt;Let’s take a look at what happens when you open a webpage, step by step.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Browser Is Almost an Operating System
&lt;/h3&gt;

&lt;p&gt;A modern browser isn’t just a window to the web- it’s a small operating system.&lt;br&gt;&lt;br&gt;
It manages memory, processes, file storage, networking, rendering, JavaScript execution, and a user interface layer.&lt;/p&gt;
&lt;h3&gt;
  
  
  Browser Capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Networking:&lt;/strong&gt; Fetch resources over HTTP(S).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data storage:&lt;/strong&gt; Cookies, localStorage, IndexedDB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution:&lt;/strong&gt; Run JavaScript securely in a sandboxed environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering:&lt;/strong&gt; Parse and paint HTML and CSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI:&lt;/strong&gt; Manage tabs, buttons, and the visual interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these parts works together seamlessly- and it all starts with fetching and parsing data.&lt;/p&gt;
&lt;h3&gt;
  
  
  Behind the Scenes: Browser Architecture
&lt;/h3&gt;

&lt;p&gt;At a high level, a browser consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI Layer:&lt;/strong&gt; What you interact with- address bar, tabs, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Storage Layer:&lt;/strong&gt; Where cookies, cache, and site data live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser Engine:&lt;/strong&gt; The core that coordinates everything. It’s divided into two main parts:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering Engine:&lt;/strong&gt; Parses HTML and CSS, creates the visual output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript Engine:&lt;/strong&gt; Parses, compiles, and executes JS code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blink&lt;/strong&gt; (used by Chrome, Edge, Opera)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebKit&lt;/strong&gt; (used by Safari)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gecko&lt;/strong&gt; (used by Firefox)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The HTML Journey
&lt;/h3&gt;

&lt;p&gt;When you load a webpage, here’s what happens to the &lt;strong&gt;HTML&lt;/strong&gt;  file:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Get raw bytes&lt;/strong&gt; from the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convert bytes → characters&lt;/strong&gt; using the correct encoding (e.g., UTF-8).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokenize&lt;/strong&gt; the characters into meaningful pieces — e.g.,
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;, &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;, &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build objects&lt;/strong&gt; for each element (with parent/child/sibling relationships).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Construct the DOM (Document Object Model)&lt;/strong&gt; — a live tree structure representing the page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The DOM isn’t just a static representation- it’s &lt;em&gt;interactive&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
 JavaScript can modify it, and the browser will update the display accordingly.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://html.spec.whatwg.org/multipage/" rel="noopener noreferrer"&gt;HTML Living Standard&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  CSS and the Render Tree
&lt;/h3&gt;

&lt;p&gt;CSS follows a similar process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parse raw bytes → characters → tokens → nodes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Build the &lt;strong&gt;CSSOM (CSS Object Model)-&lt;/strong&gt; representing all style rules.&lt;/li&gt;
&lt;li&gt;Combine the &lt;strong&gt;DOM&lt;/strong&gt; + &lt;strong&gt;CSSOM&lt;/strong&gt; → &lt;strong&gt;Render Tree&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The render tree goes through:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Layout:&lt;/strong&gt; Calculating sizes and positions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Painting:&lt;/strong&gt; Filling pixels on the screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;rendering engine&lt;/strong&gt; handles this- running complex optimizations to repaint only what’s needed.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://drafts.csswg.org/cssom/" rel="noopener noreferrer"&gt;CSSOM Specification&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  JavaScript: The DOM Manipulator
&lt;/h3&gt;

&lt;p&gt;When the browser encounters a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag, it &lt;strong&gt;pauses&lt;/strong&gt; HTML and CSS parsing to execute JavaScript.&lt;/p&gt;

&lt;p&gt;That’s because JavaScript can modify the DOM or request additional resources, so the browser needs to know the final structure before continuing.&lt;/p&gt;

&lt;p&gt;However, this can block rendering if the &lt;strong&gt;CSSOM&lt;/strong&gt; isn’t ready yet- since the script might depend on styles.&lt;/p&gt;

&lt;p&gt;To avoid blocking, we can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"app.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;defer&lt;/strong&gt; tells the browser to continue parsing HTML and execute JS &lt;em&gt;after&lt;/em&gt; the DOM is ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;async&lt;/strong&gt; executes JS as soon as it’s downloaded, independently of DOM parsing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reference: &lt;a href="https://html.spec.whatwg.org/multipage/scripting.html#the-script-element" rel="noopener noreferrer"&gt;HTML Spec- Script Element&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Here’s a simplified flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTML → DOM
CSS → CSSOM
DOM + CSSOM → Render Tree → Layout → Paint
JS → Can manipulate DOM/CSSOM → Re-render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every click, scroll, or animation triggers a careful dance between these systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Understanding how browsers work helps you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write non-blocking, performant code.&lt;/li&gt;
&lt;li&gt;Optimize paint and layout cycles.&lt;/li&gt;
&lt;li&gt;Debug complex rendering or loading issues.&lt;/li&gt;
&lt;li&gt;Appreciate just how much happens before your app even loads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Browsers are one of the most sophisticated pieces of software on your machine- and now you know why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work" rel="noopener noreferrer"&gt;MDN: How browsers work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>css</category>
      <category>browsers</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Demystifying Nginx: From Reverse Proxy to Secure Gateway</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Tue, 14 Oct 2025 02:20:43 +0000</pubDate>
      <link>https://dev.to/kfir-g/demystifying-nginx-from-reverse-proxy-to-secure-gateway-2di9</link>
      <guid>https://dev.to/kfir-g/demystifying-nginx-from-reverse-proxy-to-secure-gateway-2di9</guid>
      <description>&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%2Fre6jjkbt8z0b3opper31.jpeg" 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%2Fre6jjkbt8z0b3opper31.jpeg" width="800" height="532"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by RealToughCandy.com: &lt;a href="https://www.pexels.com/photo/person-holding-a-sticker-with-green-letters-11035538/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/person-holding-a-sticker-with-green-letters-11035538/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;When I started building &lt;strong&gt;TinyURL&lt;/strong&gt; , a simple FastAPI + PostgreSQL service for shortening URLs, I thought Nginx would just “serve traffic.” Turns out, it became the most critical piece of my deployment — the gatekeeper that made everything behind it work smoothly and securely.&lt;/p&gt;

&lt;p&gt;In this post, I’ll share what I actually learned while wiring Nginx into my FastAPI backend and Streamlit frontend, both running in Docker.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Nginx is more than a web server
&lt;/h3&gt;

&lt;p&gt;Before this project, I mostly thought of Nginx as something you use to “host static files.” In reality, it’s a powerful &lt;strong&gt;reverse proxy&lt;/strong&gt;  — it sits in front of your app, receives every incoming request, and decides where it should go.&lt;/p&gt;

&lt;p&gt;In my stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → Nginx → FastAPI (backend) → Postgres
                     ↳ Streamlit (UI)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nginx doesn’t just forward traffic- it also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles HTTPS termination&lt;/li&gt;
&lt;li&gt;Adds security headers&lt;/li&gt;
&lt;li&gt;Manages WebSocket upgrades for Streamlit&lt;/li&gt;
&lt;li&gt;Hides internal services (FastAPI and Postgres) from the public internet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I saw that flow working inside Docker Compose, it finally clicked how production apps are structured.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Reverse proxying is all about headers and trust
&lt;/h3&gt;

&lt;p&gt;Here’s a simplified version of my production config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;location /api/ {
    proxy_pass http://fastapi:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These headers are crucial. Without them, FastAPI would have no idea what the original client’s IP or scheme was.&lt;br&gt;&lt;br&gt;
 X-Forwarded-Proto in particular tells the backend if the original request came over HTTPS- that’s how FastAPI can generate correct redirect URLs or absolute links.&lt;/p&gt;

&lt;p&gt;This part made me appreciate how Nginx “translates” external requests for internal services while preserving client context.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. HTTPS is not optional
&lt;/h3&gt;

&lt;p&gt;The first time I tried running my app on an EC2 instance, Chrome immediately complained: &lt;em&gt;“Not secure.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s when I integrated &lt;strong&gt;Let’s Encrypt&lt;/strong&gt; with Nginx. The setup was surprisingly simple once I understood what was happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The /.well-known/acme-challenge/ path lets Certbot verify domain ownership.&lt;/li&gt;
&lt;li&gt;Once validated, Let’s Encrypt issues free SSL certificates.&lt;/li&gt;
&lt;li&gt;Nginx then terminates HTTPS using those certs:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssl_certificate /etc/letsencrypt/live/kg-tiny-url.xyz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/kg-tiny-url.xyz/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I also learned to add a separate HTTP server block just to redirect everything to HTTPS. Small detail, big difference.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Security headers are free wins
&lt;/h3&gt;

&lt;p&gt;I wanted to understand what real production configs look like, so I dug into HTTP security headers. Adding these took one line each in Nginx but instantly hardened the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self'" always;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don’t notice them in daily use, but tools like Mozilla Observatory or Lighthouse will thank you.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Docker networking just works- if you name things right
&lt;/h3&gt;

&lt;p&gt;Inside Docker Compose, every container gets a DNS name. My proxy_pass &lt;a href="http://fastapi:8000" rel="noopener noreferrer"&gt;http://fastapi:8000&lt;/a&gt; line works because the FastAPI container is literally named &lt;strong&gt;fastapi&lt;/strong&gt; in the YAML file.&lt;/p&gt;

&lt;p&gt;That’s how Nginx can find it without any IP addresses.&lt;br&gt;&lt;br&gt;
 Once I realized that Docker’s internal DNS makes service discovery effortless, networking suddenly felt simple instead of scary.&lt;/p&gt;
&lt;h3&gt;
  
  
  6. The moment it all worked
&lt;/h3&gt;

&lt;p&gt;After wiring it all up, I opened my browser to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://kg-tiny-url.xyz/ui/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Streamlit UI loaded. When I shortened a link, it called the FastAPI backend through /api/.&lt;br&gt;&lt;br&gt;
 Everything was encrypted, routed, and logged properly- all through Nginx.&lt;/p&gt;

&lt;p&gt;That was the moment I understood why every real-world deployment uses a reverse proxy.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Final takeaway
&lt;/h3&gt;

&lt;p&gt;Nginx isn’t just a traffic router- it’s the &lt;strong&gt;security layer, performance booster, and network translator&lt;/strong&gt; that makes containerized apps production-ready.&lt;/p&gt;

&lt;p&gt;By the end of this project, I didn’t just “set up Nginx.” I learned how the internet actually talks to my code.&lt;/p&gt;




&lt;p&gt;If you’re new to Nginx or just want to deepen your understanding, I highly recommend pairing this write-up with &lt;strong&gt;this free YouTube tutorial&lt;/strong&gt; (&lt;a href="https://www.youtube.com/watch?v=7VAI73roXaY" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=7VAI73roXaY&lt;/a&gt;) for a hands-on introduction, and &lt;strong&gt;the Udemy “NGINX Crash Course”&lt;/strong&gt; (&lt;a href="https://www.udemy.com/course/nginx-crash-course/?kw=ngi&amp;amp;src=sac" rel="noopener noreferrer"&gt;https://www.udemy.com/course/nginx-crash-course/?kw=ngi&amp;amp;src=sac&lt;/a&gt;) for a more structured, in-depth path. These two resources helped me grasp concepts like reverse proxying, SSL termination, WebSocket upgrades, and scaling more confidently — and I think they’ll help you too.&lt;/p&gt;




</description>
      <category>reverseproxy</category>
      <category>nginx</category>
      <category>softwareengineering</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>Understanding Isolation in PostgreSQL: A Deep Dive into the “I” in ACID</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Mon, 07 Jul 2025 18:28:31 +0000</pubDate>
      <link>https://dev.to/kfir-g/understanding-isolation-in-postgresql-a-deep-dive-into-the-i-in-acid-3650</link>
      <guid>https://dev.to/kfir-g/understanding-isolation-in-postgresql-a-deep-dive-into-the-i-in-acid-3650</guid>
      <description>&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%2Fmnxnwk1c0q4b3owlb5rm.jpeg" 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%2Fmnxnwk1c0q4b3owlb5rm.jpeg" width="800" height="1067"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Matheus Viana: &lt;a href="https://www.pexels.com/photo/open-locker-in-wooden-locker-room-31220117/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/open-locker-in-wooden-locker-room-31220117/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;When we talk about PostgreSQL being &lt;em&gt;ACID-compliant&lt;/em&gt;, Isolation is the “I” that quietly does a lot of heavy lifting. In my previous articles, we explored Atomicity and Consistency -how PostgreSQL makes sure that transactions either happen fully or not at all, and that the database remains valid. Now it’s time to look at how Isolation keeps transactions from interfering with each other when multiple operations run at the same time.&lt;/p&gt;

&lt;p&gt;Isolation sounds simple: each transaction should run as if it’s the only one. But the details can get tricky, especially when different isolation levels come into play. Let’s see what this means, how it works in PostgreSQL, and what to watch out for.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Isolation Means
&lt;/h3&gt;

&lt;p&gt;In short, Isolation makes sure that the intermediate state of a transaction is invisible to other transactions. If one transaction is in the middle of changing a row, another transaction shouldn’t see a half-finished version of that row.&lt;/p&gt;

&lt;p&gt;Without Isolation, you can run into problems like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dirty reads&lt;/strong&gt; : reading uncommitted changes that might get rolled back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-repeatable reads&lt;/strong&gt; : getting different results when you read the same row twice in a transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phantom reads&lt;/strong&gt; : rows appear or disappear when you run the same query again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PostgreSQL handles this with different isolation levels, each balancing correctness and performance.&lt;/p&gt;
&lt;h3&gt;
  
  
  PostgreSQL’s Isolation Levels
&lt;/h3&gt;

&lt;p&gt;PostgreSQL supports the standard SQL isolation levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read Uncommitted&lt;/strong&gt;
PostgreSQL actually treats this the same as Read Committed, so dirty reads are never allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read Committed&lt;/strong&gt; (default)
Each query sees a snapshot of the database taken at the start of that query. Changes committed by other transactions become visible to the next query in the same transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeatable Read&lt;/strong&gt;
All queries in the transaction see the same snapshot taken when the transaction starts. You avoid non-repeatable reads, but phantom reads can still happen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serializable&lt;/strong&gt;
The strictest level. Transactions run as if they were executed one after another. PostgreSQL uses Serializable Snapshot Isolation (SSI) to make this work and may abort transactions that can’t be serialized.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Let’s See Isolation in Action
&lt;/h3&gt;

&lt;p&gt;Here’s a simple table to experiment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE inventory (
  id SERIAL PRIMARY KEY,
  stock INT
);

INSERT INTO inventory (stock) VALUES (100);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open two psql sessions: Session A and Session B.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read Committed: Non-Repeatable Reads
&lt;/h3&gt;

&lt;p&gt;In Session A:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

SELECT stock FROM inventory WHERE id = 1;
-- stock = 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Session B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE inventory SET stock = 50 WHERE id = 1;
COMMIT;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back in Session A:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT stock FROM inventory WHERE id = 1;
-- stock = 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same row gave a different value within the same transaction. This is a non-repeatable read, and it’s allowed in Read Committed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repeatable Read: Same Snapshot
&lt;/h3&gt;

&lt;p&gt;Now try Repeatable Read.&lt;/p&gt;

&lt;p&gt;Session A:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

SELECT stock FROM inventory WHERE id = 1;
-- stock = 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Session B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE inventory SET stock = 30 WHERE id = 1;
COMMIT;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back in Session A:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT stock FROM inventory WHERE id = 1;
-- stock = 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, the value stays the same because Repeatable Read ensures you always see the same snapshot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serializable: Strictest Isolation
&lt;/h3&gt;

&lt;p&gt;With Serializable, PostgreSQL checks whether your transactions could produce different results than if they ran one after the other.&lt;/p&gt;

&lt;p&gt;Session A:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

SELECT stock FROM inventory WHERE id = 1;
-- 100

UPDATE inventory SET stock = stock - 10 WHERE id = 1;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Session B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

SELECT stock FROM inventory WHERE id = 1;
-- 100

UPDATE inventory SET stock = stock - 20 WHERE id = 1;
COMMIT;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back in Session A:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COMMIT;
-- ERROR: could not serialize access due to concurrent update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL detects that running these transactions together could lead to inconsistencies, so it rolls one back. Better safe than sorry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing the Right Level
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read Committed&lt;/strong&gt; works well for most use cases where short transactions don’t need repeatable reads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeatable Read&lt;/strong&gt; is useful for consistent reads in longer transactions or reports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serializable&lt;/strong&gt; is the safest but can lead to transaction rollbacks, so you need to handle retries.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Isolation is about making sure your transactions don’t see each other’s work half-finished. It’s not something you think about every day- until things go wrong because of subtle race conditions.&lt;/p&gt;

&lt;p&gt;Getting Isolation right is key to safe and predictable data in multi-user systems. Now you know what each level does and how PostgreSQL keeps your data consistent even when things get busy.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/transaction-iso.html" rel="noopener noreferrer"&gt;PostgreSQL: Isolation Levels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/transaction-iso.html#XACT-SERIALIZABLE" rel="noopener noreferrer"&gt;PostgreSQL: Serializable Isolation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>databaseengineering</category>
      <category>acid</category>
      <category>isolation</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Understanding Durability in PostgreSQL -A Deep Dive into the “D” in ACID</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Mon, 07 Jul 2025 18:27:07 +0000</pubDate>
      <link>https://dev.to/kfir-g/understanding-durability-in-postgresql-a-deep-dive-into-the-d-in-acid-4p29</link>
      <guid>https://dev.to/kfir-g/understanding-durability-in-postgresql-a-deep-dive-into-the-d-in-acid-4p29</guid>
      <description>&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%2Fxv7gh4lhnpo2ge1ag0st.jpeg" 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%2Fxv7gh4lhnpo2ge1ag0st.jpeg" width="800" height="1000"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Matheus Viana: &lt;a href="https://www.pexels.com/photo/person-on-bike-2372972/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/person-on-bike-2372972/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;So far in this series, we’ve looked at how PostgreSQL handles Atomicity, Consistency, and Isolation. But what happens &lt;em&gt;after&lt;/em&gt; you commit a transaction? What if the power goes out, the server crashes, or someone trips over the plug?&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Durability&lt;/strong&gt; , the final letter in ACID, comes in. Durability means that once a transaction is committed, it will &lt;em&gt;not&lt;/em&gt; be lost- no matter what.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Does Durability Really Mean?
&lt;/h3&gt;

&lt;p&gt;In practical terms, Durability means that when your application gets a COMMIT confirmation, the data is safely stored and recoverable even if the database crashes immediately afterward.&lt;/p&gt;

&lt;p&gt;PostgreSQL achieves this with its &lt;strong&gt;Write-Ahead Logging (WAL)&lt;/strong&gt; mechanism. Instead of immediately rewriting table files on disk, PostgreSQL writes changes to a log file first. If the system crashes, PostgreSQL can replay this log to get back to a consistent state.&lt;/p&gt;
&lt;h3&gt;
  
  
  How Does WAL Work?
&lt;/h3&gt;

&lt;p&gt;Here’s the basic flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You run a transaction that changes some data.&lt;/li&gt;
&lt;li&gt;PostgreSQL writes the changes to the WAL file on disk.&lt;/li&gt;
&lt;li&gt;Only &lt;em&gt;after&lt;/em&gt; the WAL record is safely on disk does PostgreSQL acknowledge the COMMIT.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the WAL acts like a black box flight recorder for your database.&lt;/p&gt;
&lt;h3&gt;
  
  
  Let’s See Durability in Action
&lt;/h3&gt;

&lt;p&gt;You can see WAL files in action in your data directory. Here’s a simple demo.&lt;/p&gt;

&lt;p&gt;First, check your WAL settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SHOW wal_level;
SHOW synchronous_commit;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, synchronous_commit is on. This means PostgreSQL waits until WAL changes are flushed to disk before confirming the commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test: Crash Recovery (Safe Experiment)
&lt;/h3&gt;

&lt;p&gt;Let’s simulate how WAL protects your data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a test table:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE durability_test (id SERIAL PRIMARY KEY, data TEXT);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Insert some rows and commit:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO durability_test (data) VALUES ('Important Data 1'), ('Important Data 2'); COMMIT;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Now, force PostgreSQL to checkpoint to flush data to disk:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If the server crashes &lt;em&gt;after&lt;/em&gt; the WAL is written but &lt;em&gt;before&lt;/em&gt; data files are updated, PostgreSQL will replay the WAL on startup to make sure your rows are still there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Of course, don’t yank the power cord — but you can trust that WAL would restore the committed rows.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Durability Can Be Tuned
&lt;/h3&gt;

&lt;p&gt;Sometimes, applications may want to trade off strict Durability for speed. For example, you can turn off synchronous_commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SET synchronous_commit = off;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now PostgreSQL can acknowledge a commit &lt;em&gt;before&lt;/em&gt; the WAL is flushed to disk. If the server crashes immediately, you could lose the last few transactions.&lt;/p&gt;

&lt;p&gt;This is faster but obviously less durable — so use it only when you’re okay with that risk (like bulk loads or cache tables).&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Takeaway
&lt;/h3&gt;

&lt;p&gt;Durability is what lets you sleep at night knowing your committed data won’t vanish. PostgreSQL’s WAL and crash recovery have been battle-tested for decades. As long as you keep your WAL files safe (and your disks healthy), your data stays safe too.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/wal-intro.html" rel="noopener noreferrer"&gt;PostgreSQL: Write-Ahead Logging (WAL)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/runtime-config-wal.html#GUC-SYNCHRONOUS-COMMIT" rel="noopener noreferrer"&gt;PostgreSQL: Synchronous Commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/wal-reliability.html" rel="noopener noreferrer"&gt;PostgreSQL: Crash Recovery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>postgres</category>
      <category>database</category>
      <category>durability</category>
      <category>acid</category>
    </item>
    <item>
      <title>Understanding Python Memory and Garbage Collection Through Hands-On Experiments</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Sun, 06 Jul 2025 07:57:08 +0000</pubDate>
      <link>https://dev.to/kfir-g/understanding-python-memory-and-garbage-collection-through-hands-on-experiments-4g0p</link>
      <guid>https://dev.to/kfir-g/understanding-python-memory-and-garbage-collection-through-hands-on-experiments-4g0p</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://medium.com/gitconnected/understanding-python-memory-and-garbage-collection-through-hands-on-experiments-86f25aa6b5d5" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; under the &lt;a href="https://levelup.gitconnected.com/" rel="noopener noreferrer"&gt;Level Up coding&lt;/a&gt; publication.&lt;/p&gt;




&lt;p&gt;Python is a high‑level language that takes care of much of the memory management for you. However, understanding how it works under the hood can give you better insights into performance optimization and how to manage your resources efficiently. In this blog post, we’ll explore Python’s memory management and garbage collection (GC) with hands‑on examples. We will focus on three core concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memory Allocation and Reference Counting&lt;/li&gt;
&lt;li&gt;Cyclic References&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;gc&lt;/code&gt; to Manage Garbage Collection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s dive in with an experiment‑driven approach using a small Python script that demonstrates these concepts in action.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Memory Allocation and Reference Counting
&lt;/h2&gt;

&lt;p&gt;Memory management in Python is primarily handled through reference counting. Every object in Python has a reference count that tracks how many references point to it. When the reference count drops to zero, the object is automatically deleted, and its memory is freed. &lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Memory Allocation of Simple Objects
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="c1"&gt;# Create two variables pointing to the same integer object
&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="c1"&gt;# Both x and y point to the same memory location
&lt;/span&gt;
&lt;span class="c1"&gt;# Memory address and reference count
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Memory address of x (ID): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;id&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="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Memory address of y (ID): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reference count for x: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getrefcount&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="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; point to the same integer object (&lt;code&gt;10&lt;/code&gt;), as integers are cached in Python.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;id()&lt;/code&gt; function shows that both variables share the same memory address.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sys.getrefcount()&lt;/code&gt; reveals that the reference count is greater than 1 because both &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; reference the same object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After deleting one of the references (&lt;code&gt;del x&lt;/code&gt;), the object is still alive because &lt;code&gt;y&lt;/code&gt; still holds a reference to the object:&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="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reference count after deleting x: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getrefcount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&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;Memory address of x (ID): 140705611380560
Memory address of y (ID): 140705611380560
Reference count for x: 2
Reference count after deleting x: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown, the reference count of the object decreases after &lt;code&gt;x&lt;/code&gt; is deleted, but the object is still not destroyed because &lt;code&gt;y&lt;/code&gt; still holds a reference to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Cyclic References and Garbage Collection
&lt;/h2&gt;

&lt;p&gt;Sometimes, objects reference each other in a cycle (e.g., &lt;code&gt;A → B → A&lt;/code&gt;), making it impossible for the reference count to reach zero. Python’s garbage collector handles these cases by detecting and cleaning up cyclic references.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Creating a Cycle
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="c1"&gt;# Create a cycle
&lt;/span&gt;&lt;span class="n"&gt;node1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;node2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;node1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node2&lt;/span&gt;
&lt;span class="n"&gt;node2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node1&lt;/span&gt;  &lt;span class="c1"&gt;# This creates a cyclic reference
&lt;/span&gt;
&lt;span class="c1"&gt;# Even after deleting both references, the objects are not collected because they still reference each other.
&lt;/span&gt;&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;node1&lt;/span&gt;
&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;node2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Triggering Garbage Collection
&lt;/h3&gt;

&lt;p&gt;To break the cycle and reclaim memory, Python’s garbage collector (&lt;code&gt;gc&lt;/code&gt;) can be manually triggered using &lt;code&gt;gc.collect()&lt;/code&gt;:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;gc&lt;/span&gt;

&lt;span class="c1"&gt;# Manually trigger garbage collection to clean up the cycle
&lt;/span&gt;&lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Point:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python’s GC is able to detect and clean up cyclic references, ensuring that memory is freed even when reference counting can’t handle it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Using &lt;code&gt;gc&lt;/code&gt; for Custom Garbage Collection
&lt;/h2&gt;

&lt;p&gt;Python provides the &lt;code&gt;gc&lt;/code&gt; module to interact with the garbage collector and control the cleanup process. We can check if garbage collection is enabled and even force a collection to clean up unreachable objects. ([medium.com][1])&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;gc&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Is Garbage Collection enabled? &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isenabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Force garbage collection
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Garbage after collect: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;garbage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gc.isenabled()&lt;/code&gt; checks if garbage collection is enabled in the current Python session.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gc.collect()&lt;/code&gt; forces the garbage collection process, which can help clean up cyclic references or unreachable objects.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Using &lt;code&gt;__del__&lt;/code&gt; for Custom Cleanup
&lt;/h2&gt;

&lt;p&gt;Python allows you to define custom cleanup code using the &lt;code&gt;__del__&lt;/code&gt; method. This method is called when an object is about to be destroyed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Using &lt;code&gt;__del__&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="n"&gt;self&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="n"&gt;name&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyObject &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; is created!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__del__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyObject &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; is being destroyed!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;obj1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Object 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;obj2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj1&lt;/span&gt;  &lt;span class="c1"&gt;# obj2 is another reference to the same object
&lt;/span&gt;&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;obj1&lt;/span&gt;     &lt;span class="c1"&gt;# This will not delete the object, as obj2 still holds a reference
&lt;/span&gt;&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;obj2&lt;/span&gt;     &lt;span class="c1"&gt;# The object is now destroyed because both references are gone
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When &lt;code&gt;obj1&lt;/code&gt; is deleted, the object is not destroyed because &lt;code&gt;obj2&lt;/code&gt; still holds a reference to it.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;obj2&lt;/code&gt; is also deleted, the object’s &lt;code&gt;__del__&lt;/code&gt; method is called, and the object is destroyed. ([medium.com][1])&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Python’s memory management and garbage collection mechanisms are designed to handle most tasks automatically. By understanding reference counting and cyclic references, as well as how to manually trigger the garbage collector, you can ensure that your programs are more efficient and resource‑friendly.&lt;/p&gt;

&lt;p&gt;By experimenting with these examples, you gain hands‑on experience in how Python manages memory, how cyclic references are handled, and how to clean up resources using the &lt;code&gt;gc&lt;/code&gt; module.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧠 Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reference Counting:&lt;/strong&gt; Tracks object references and deletes objects when they are no longer referenced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Garbage Collection:&lt;/strong&gt; Handles cyclic references that reference counting can’t clean up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;__del__&lt;/code&gt; Method:&lt;/strong&gt; Enables custom cleanup when objects are destroyed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This knowledge helps you write more efficient and optimized Python code, particularly for resource‑intensive applications.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/c-api/memory.html" rel="noopener noreferrer"&gt;Memory Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/memory-management-in-python/" rel="noopener noreferrer"&gt;Memory Management in Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/gc.html" rel="noopener noreferrer"&gt;&lt;code&gt;gc&lt;/code&gt; — Garbage Collector interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/garbage-collection-python/" rel="noopener noreferrer"&gt;Garbage Collection in Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/python/cpython/blob/main/InternalDocs/garbage_collector.md" rel="noopener noreferrer"&gt;garbage collector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://www.pexels.com/photo/creative-iphone-new-york-sony-9136482/" rel="noopener noreferrer"&gt;Photo&lt;/a&gt; by badr mourafiq&lt;/p&gt;

</description>
      <category>python</category>
      <category>memory</category>
      <category>gc</category>
    </item>
    <item>
      <title>Understanding Consistency in PostgreSQL: A Deep Dive into the “C” in ACID</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Sun, 01 Jun 2025 22:36:31 +0000</pubDate>
      <link>https://dev.to/kfir-g/understanding-consistency-in-postgresql-a-deep-dive-into-the-c-in-acid-1723</link>
      <guid>https://dev.to/kfir-g/understanding-consistency-in-postgresql-a-deep-dive-into-the-c-in-acid-1723</guid>
      <description>&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%2Ft91rf7cilingq0mpw46u.jpeg" 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%2Ft91rf7cilingq0mpw46u.jpeg" width="800" height="1066"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Matheus Viana: &lt;a href="https://www.pexels.com/photo/orange-wall-and-sitting-boy-10216566/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/orange-wall-and-sitting-boy-10216566/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;In systems where data correctness is non-negotiable- whether you’re handling user authentication, e-commerce inventory, or hospital patient records- &lt;strong&gt;Consistency&lt;/strong&gt; is the invisible guardrail that ensures the rules of your domain are never broken.&lt;/p&gt;

&lt;p&gt;This article explores &lt;strong&gt;Consistency&lt;/strong&gt; , the second principle of ACID. We’ll walk through how PostgreSQL enforces it via constraints, how violations are handled, and what developers need to be mindful of to avoid state corruption in real-world systems.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Consistency Really Means
&lt;/h3&gt;

&lt;p&gt;Consistency ensures that a transaction brings the database from one valid state to another. That means all defined &lt;strong&gt;constraints, rules, and invariants&lt;/strong&gt; must be satisfied before and after every transaction.&lt;/p&gt;

&lt;p&gt;If any part of a transaction would leave the data in an invalid state, PostgreSQL &lt;strong&gt;rejects the entire transaction-&lt;/strong&gt; not just the failing statement-preserving the integrity of your system.&lt;/p&gt;
&lt;h3&gt;
  
  
  A Schema With Invariants
&lt;/h3&gt;

&lt;p&gt;Let’s return to our simple banking system and define some rules we want PostgreSQL to enforce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE accounts (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    balance INTEGER NOT NULL CHECK (balance &amp;gt;= 0),
    UNIQUE(name)
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’ve defined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every account must have a non-null name.&lt;/li&gt;
&lt;li&gt;Balances must be zero or greater.&lt;/li&gt;
&lt;li&gt;Names must be unique.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are &lt;strong&gt;declarative constraints&lt;/strong&gt;. PostgreSQL uses them to guarantee that no transaction can violate your domain rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Violating Consistency with a Bad Transaction
&lt;/h3&gt;

&lt;p&gt;Say Alice tries to transfer more than she has:&lt;br&gt;
&lt;/p&gt;

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

UPDATE accounts SET balance = balance - 1200 WHERE name = 'Alice';
UPDATE accounts SET balance = balance + 1200 WHERE name = 'Bob';

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

&lt;/div&gt;



&lt;p&gt;If Alice only has 1000, PostgreSQL will raise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: new row for relation "accounts" violates check constraint "accounts_balance_check"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the transaction is marked as failed and must be rolled back:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt; : No changes are applied. This is Consistency at work- the integrity rule (balance &amp;gt;= 0) was violated, so PostgreSQL refused to apply any part of the transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enforcing Business Logic with Triggers
&lt;/h3&gt;

&lt;p&gt;Not all rules are easily encoded as column constraints. For more complex logic- like age-based restrictions or cross-table validations- we can use &lt;strong&gt;triggers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example: prevent creating users under age 18.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION check_age()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.age &amp;lt; 18 THEN
        RAISE EXCEPTION 'Age must be at least 18';
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER check_age_trigger
BEFORE INSERT OR UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION check_age();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, any INSERT or UPDATE that violates this rule will automatically fail, and the transaction will be rolled back- keeping the system in a consistent state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Referential Integrity via Foreign Keys
&lt;/h3&gt;

&lt;p&gt;Let’s introduce a transactions table that logs money transfers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE transactions (
    id SERIAL PRIMARY KEY,
    from_account INTEGER REFERENCES accounts(id),
    to_account INTEGER REFERENCES accounts(id),
    amount INTEGER NOT NULL CHECK (amount &amp;gt; 0)
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL ensures that both from_account and to_account actually exist. Attempting to insert a transaction for a non-existent account will fail immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO transactions (from_account, to_account, amount)
VALUES (999, 2, 100);

ERROR: insert or update on table "transactions" violates foreign key constraint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s PostgreSQL maintaining consistency via &lt;strong&gt;referential integrity&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistency in Application Code (Python + psycopg3)
&lt;/h3&gt;

&lt;p&gt;Here’s how consistency violations behave in real-world application code:&lt;br&gt;
&lt;/p&gt;

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

conn = psycopg.connect("postgresql://user:pass@localhost/db")

try:
    with conn.transaction():
        with conn.cursor() as cur:
            cur.execute("UPDATE accounts SET balance = balance - 1200 WHERE name = 'Alice'")
            cur.execute("UPDATE accounts SET balance = balance + 1200 WHERE name = 'Bob'")

except Exception as e:
    print(f"Transaction failed: {e}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the balance - 1200 fails the CHECK constraint, the entire block is rolled back. You’ll never end up in a partial state where Alice lost money but Bob gained nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary of Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Constraint violations&lt;/strong&gt; : Cause immediate rollback of the entire transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triggers&lt;/strong&gt; : Allow custom rules that extend beyond column-level checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Foreign key violations&lt;/strong&gt; : Prevent inconsistent references between tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application-level transactions&lt;/strong&gt; : psycopg3 ensures that rule violations bubble up cleanly, triggering a rollback.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Consistency is the safety net that protects your data from logical corruption. PostgreSQL’s constraint system- combined with triggers and foreign keys- ensures that your application’s data model is always honored.&lt;/p&gt;

&lt;p&gt;If you’re building systems where correctness matters, you must treat consistency as a first-class design concern. Don’t rely on application code alone to enforce business rules- declarative constraints and transactional integrity are your best allies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/ddl-constraints.html" rel="noopener noreferrer"&gt;PostgreSQL Constraints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-FK" rel="noopener noreferrer"&gt;PostgreSQL Foreign Keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/plpgsql-trigger.html" rel="noopener noreferrer"&gt;Triggers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.psycopg.org/psycopg3/docs/basic/transactions.html" rel="noopener noreferrer"&gt;psycopg3 Transactions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>dataengineering</category>
      <category>postgres</category>
      <category>consistency</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>What Really Happens When You Type `google.com` in Your Browser?</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Sun, 18 May 2025 14:15:44 +0000</pubDate>
      <link>https://dev.to/kfir-g/what-really-happens-when-you-type-googlecom-in-your-browser-49fk</link>
      <guid>https://dev.to/kfir-g/what-really-happens-when-you-type-googlecom-in-your-browser-49fk</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://medium.com/gitconnected/what-really-happens-when-you-type-google-com-in-your-browser-c33e8c2e6eed" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; under the &lt;a href="https://levelup.gitconnected.com/" rel="noopener noreferrer"&gt;Level Up coding&lt;/a&gt; publication.&lt;/p&gt;




&lt;p&gt;Every time you type &lt;code&gt;google.com&lt;/code&gt; into your browser’s address bar and press Enter, a complex sequence of events unfolds behind the scenes. This post will break down each step in detail with Python examples and references to official documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. &lt;strong&gt;Domain Name Resolution (DNS Lookup)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before your browser can connect to Google’s servers, it needs the IP address corresponding to &lt;code&gt;google.com&lt;/code&gt;. This process is called &lt;strong&gt;DNS resolution&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it Works:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The browser first checks its &lt;strong&gt;DNS cache&lt;/strong&gt; to see if the domain has been resolved recently.&lt;/li&gt;
&lt;li&gt;If not found, it queries the OS’s DNS resolver.&lt;/li&gt;
&lt;li&gt;If still unresolved, the OS sends a request to a &lt;strong&gt;recursive DNS server&lt;/strong&gt; (provided by your ISP or a third-party like Google’s &lt;code&gt;8.8.8.8&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The recursive DNS server queries &lt;strong&gt;root name servers&lt;/strong&gt; → &lt;strong&gt;TLD name servers&lt;/strong&gt; (&lt;code&gt;.com&lt;/code&gt;) → &lt;strong&gt;Authoritative name servers&lt;/strong&gt; for &lt;code&gt;google.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The authoritative name server provides the IP address (e.g., &lt;code&gt;142.250.184.14&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The response is cached for future queries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🔗 &lt;strong&gt;Official Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc1034" rel="noopener noreferrer"&gt;IETF RFC 1034 - DNS Concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/speed/public-dns" rel="noopener noreferrer"&gt;Google Public DNS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Python Code for DNS Resolution:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;

&lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gethostbyname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resolved &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. &lt;strong&gt;Establishing a TCP Connection&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once the IP is known, the browser establishes a &lt;strong&gt;TCP connection&lt;/strong&gt; using the &lt;strong&gt;three-way handshake&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SYN&lt;/strong&gt;: The client sends a TCP packet with the SYN flag set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SYN-ACK&lt;/strong&gt;: The server responds with a packet containing SYN and ACK flags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACK&lt;/strong&gt;: The client acknowledges and the connection is established.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🔗 &lt;strong&gt;Official Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc793" rel="noopener noreferrer"&gt;IETF RFC 793 - TCP Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tldp.org/LDP/nag2/index.html" rel="noopener noreferrer"&gt;Linux TCP/IP Networking Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Python Code for TCP Connection:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;

&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_connection&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# HTTPS uses port 443
&lt;/span&gt;&lt;span class="n"&gt;secure_sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;domain&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TCP connection established with Google&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. &lt;strong&gt;TLS Handshake &amp;amp; Secure Connection&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Since Google uses HTTPS, an additional &lt;strong&gt;TLS handshake&lt;/strong&gt; takes place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client Hello&lt;/strong&gt;: The browser sends supported encryption protocols.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Hello&lt;/strong&gt;: The server responds with a chosen encryption algorithm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certificate Exchange&lt;/strong&gt;: The server provides an SSL certificate for verification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Exchange&lt;/strong&gt;: Both parties establish an encrypted communication channel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🔗 &lt;strong&gt;Official Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc8446" rel="noopener noreferrer"&gt;IETF RFC 8446 - TLS 1.3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Transport_Layer_Security" rel="noopener noreferrer"&gt;Mozilla TLS Overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Python Code for TLS Handshake:
&lt;/h3&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="n"&gt;secure_sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;version&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c1"&gt;# Print the TLS version used
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. &lt;strong&gt;Sending an HTTP Request&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once connected, the browser sends an HTTP request. A standard request for Google’s homepage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET / HTTP/1.1
Host: google.com
Connection: close
User-Agent: Mozilla/5.0 (compatible; Chrome)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔗 &lt;strong&gt;Official Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc7230" rel="noopener noreferrer"&gt;IETF RFC 7230 - HTTP/1.1 Message Syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages" rel="noopener noreferrer"&gt;Mozilla HTTP Request Structure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Python Code for Sending an HTTP Request:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET / HTTP/1.1&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Host: google.com&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;secure_sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. &lt;strong&gt;Receiving and Processing the Response&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Google’s server responds with an HTTP response, typically a &lt;strong&gt;301 Redirect&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;HTTP/1.1 301 Moved Permanently
Location: https://www.google.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instructs the browser to retry the request at &lt;code&gt;https://www.google.com/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Official Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc9110" rel="noopener noreferrer"&gt;IETF RFC 9110 - HTTP Semantics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/fundamentals/performance/http2#http-status-codes" rel="noopener noreferrer"&gt;Google HTTP Status Codes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Python Code for Receiving Response:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secure_sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ignore&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. &lt;strong&gt;Rendering the Webpage&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once the browser receives the response, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parses the HTML.&lt;/li&gt;
&lt;li&gt;Fetches additional resources (CSS, JavaScript, images).&lt;/li&gt;
&lt;li&gt;Executes scripts.&lt;/li&gt;
&lt;li&gt;Renders the page on the screen.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🔗 &lt;strong&gt;Official Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work" rel="noopener noreferrer"&gt;MDN Browser Rendering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/blog/inside-browser-part1/" rel="noopener noreferrer"&gt;Google Chrome Rendering Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. &lt;strong&gt;Handling Subsequent Requests (Cookies, Caching, CDN)&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cookies:&lt;/strong&gt; The server might set authentication cookies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching:&lt;/strong&gt; The browser stores assets for faster loading in future requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN Requests:&lt;/strong&gt; Additional requests might be sent to Google’s CDN for assets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔗 &lt;strong&gt;Official Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc6265" rel="noopener noreferrer"&gt;RFC 6265 - HTTP Cookies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/speed/public-dns/docs/caching" rel="noopener noreferrer"&gt;Google CDN Optimization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;When you type &lt;code&gt;google.com&lt;/code&gt; and press Enter, a series of sophisticated network processes occur, including DNS resolution, TCP/TLS handshakes, HTTP requests, and webpage rendering. Each step plays a critical role in delivering web pages securely and efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Want to Try It Yourself?
&lt;/h3&gt;

&lt;p&gt;Run the Python snippets above to see the process in action! 🚀&lt;/p&gt;

</description>
      <category>dns</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Understanding Atomicity in PostgreSQL: A Deep Dive into the “A” in ACID</title>
      <dc:creator>Kfir</dc:creator>
      <pubDate>Mon, 12 May 2025 14:25:59 +0000</pubDate>
      <link>https://dev.to/kfir-g/understanding-atomicity-in-postgresql-a-deep-dive-into-the-a-in-acid-209a</link>
      <guid>https://dev.to/kfir-g/understanding-atomicity-in-postgresql-a-deep-dive-into-the-a-in-acid-209a</guid>
      <description>&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%2F61swv36z1vp20c51ocj2.jpeg" 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%2F61swv36z1vp20c51ocj2.jpeg" width="800" height="1067"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Thiago Matos : &lt;a href="https://www.pexels.com/photo/orange-led-cfl-bulb-2338672/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/orange-led-cfl-bulb-2338672/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;In production systems that manage critical data- whether financial transactions, user state, or infrastructure configuration- the integrity of state transitions is non-negotiable. That’s where the ACID properties of relational databases come into play.&lt;/p&gt;

&lt;p&gt;This article takes a focused look at &lt;strong&gt;Atomicity&lt;/strong&gt; , the first principle of ACID. We’ll analyze how PostgreSQL enforces it, how failures are handled, and what engineers must understand to avoid subtle consistency bugs in real-world systems.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Atomicity Really Means
&lt;/h3&gt;

&lt;p&gt;Atomicity guarantees that a &lt;strong&gt;transaction is all-or-nothing&lt;/strong&gt; : every operation in the transaction either completes successfully, or none of them do. There is no concept of “partial success.”&lt;/p&gt;

&lt;p&gt;Atomicity is enforced at the transaction level, not the statement level. PostgreSQL ensures that even if a failure occurs midway through a transaction, all previous operations in that transaction are automatically rolled back.&lt;/p&gt;

&lt;p&gt;For reference, see the official &lt;a href="https://www.postgresql.org/docs/current/tutorial-transactions.html" rel="noopener noreferrer"&gt;PostgreSQL Transaction Tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  A Simple Transfer Operation
&lt;/h3&gt;

&lt;p&gt;Consider a simplified banking example. We’ll create an accounts table and simulate transferring funds between two accounts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE accounts (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    balance INTEGER NOT NULL CHECK (balance &amp;gt;= 0)
);

INSERT INTO accounts (name, balance)
VALUES ('Alice', 1000), ('Bob', 1000);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we implement a basic transfer operation using an explicit transaction:&lt;br&gt;
&lt;/p&gt;

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

UPDATE accounts SET balance = balance - 200 WHERE name = 'Alice';
UPDATE accounts SET balance = balance + 200 WHERE name = 'Bob';

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

&lt;/div&gt;



&lt;p&gt;If both statements succeed, the transaction is committed and both updates become permanent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simulating a Failure
&lt;/h3&gt;

&lt;p&gt;Now let’s simulate an error that occurs in the second statement:&lt;br&gt;
&lt;/p&gt;

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

UPDATE accounts SET balance = balance - 200 WHERE name = 'Alice';
-- Intentional typo: column name is misspelled
UPDATE accounts SET balnce = balance + 200 WHERE name = 'Bob';

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

&lt;/div&gt;



&lt;p&gt;PostgreSQL will raise an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: column "balnce" does not exist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the transaction is marked as failed. No further operations are allowed until a rollback is issued:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Although the first UPDATE ran without error, its effect is discarded because the transaction was never successfully committed. This is atomicity in practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  What If You Don’t Use Transactions?
&lt;/h3&gt;

&lt;p&gt;When operations are issued individually without an explicit BEGIN block, PostgreSQL executes each as its own atomic unit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE accounts SET balance = balance - 200 WHERE name = 'Alice';
-- This succeeds

UPDATE accounts SET balnce = balance + 200 WHERE name = 'Bob';
-- This fails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, Alice’s balance is reduced, but Bob never receives the funds. This violates our consistency requirements and results in data loss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; without explicit transactions, the database cannot enforce atomicity across multiple operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Statement-Level Atomicity
&lt;/h3&gt;

&lt;p&gt;PostgreSQL does provide implicit transaction handling for single statements. If a single INSERT, UPDATE, or DELETE statement fails, it is discarded entirely and does not affect the database. However, for multiple operations that must succeed together, atomicity is not guaranteed unless you explicitly wrap them in a transaction.&lt;/p&gt;

&lt;p&gt;Relevant documentation: &lt;a href="https://www.postgresql.org/docs/current/sql-begin.html" rel="noopener noreferrer"&gt;PostgreSQL BEGIN&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Constraint Failures and Runtime Errors
&lt;/h3&gt;

&lt;p&gt;PostgreSQL also enforces atomicity in the presence of constraint violations or other runtime errors. For example, suppose we enforce that balances cannot be negative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALTER TABLE accounts ADD CONSTRAINT non_negative_balance CHECK (balance &amp;gt;= 0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then attempt:&lt;br&gt;
&lt;/p&gt;

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

UPDATE accounts SET balance = balance - 1100 WHERE name = 'Alice';
UPDATE accounts SET balance = balance + 1100 WHERE name = 'Bob';

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

&lt;/div&gt;



&lt;p&gt;Alice only has 1000, so her balance would go negative. The first statement violates the constraint, and the entire transaction is aborted. No changes are applied.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Transactions in Application Code
&lt;/h3&gt;

&lt;p&gt;Here’s how atomicity works in a real-world PostgreSQL client using Python with &lt;a href="https://www.psycopg.org/psycopg3/docs/basic/transactions.html" rel="noopener noreferrer"&gt;psycopg3&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

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

conn = psycopg.connect("postgresql://user:pass@localhost/db")

try:
    with conn.transaction():
        with conn.cursor() as cur:
            cur.execute("UPDATE accounts SET balance = balance - 200 WHERE name = 'Alice'")
            cur.execute("UPDATE accounts SET balnce = balance + 200 WHERE name = 'Bob'") # Bug

except Exception as e:
    print(f"Transaction failed: {e}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, conn.transaction() ensures that the entire block is treated as atomic. When the second query fails, psycopg3 automatically rolls back the transaction. No data is persisted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary of Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single&lt;/strong&gt;  &lt;strong&gt;UPDATE&lt;/strong&gt; : Executed without an explicit transaction block, PostgreSQL treats it as an implicit transaction. It either succeeds or fails as a single atomic operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple&lt;/strong&gt;  &lt;strong&gt;UPDATEs without&lt;/strong&gt;  &lt;strong&gt;BEGIN&lt;/strong&gt; : When multiple operations are issued individually without wrapping them in a transaction, atomicity is not guaranteed. If one statement succeeds and another fails, partial state changes can persist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BEGIN /&lt;/strong&gt;  &lt;strong&gt;COMMIT with syntax or runtime error&lt;/strong&gt; : When using an explicit transaction, any error- be it a syntax issue or constraint violation- causes the entire transaction to be rolled back. This ensures atomicity across all enclosed operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using psycopg3 with&lt;/strong&gt; &lt;strong&gt;with conn.transaction()&lt;/strong&gt;: In application code, using this context manager ensures atomicity. If any exception is raised during the block, psycopg3 automatically rolls back the transaction.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;PostgreSQL provides strong atomicity guarantees, but they’re only effective when transactions are used intentionally and correctly. Multi-statement operations should &lt;strong&gt;always&lt;/strong&gt; be wrapped in an explicit transaction, and developers must anticipate rollback paths as part of the application’s normal flow.&lt;/p&gt;

&lt;p&gt;Failing to do so can result in partial writes, corrupted state, and long-tail data integrity bugs that are difficult to trace.&lt;/p&gt;

&lt;p&gt;For more details, refer to the official documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/tutorial-transactions.html" rel="noopener noreferrer"&gt;PostgreSQL Transactions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/sql-begin.html" rel="noopener noreferrer"&gt;BEGIN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/sql-commit.html" rel="noopener noreferrer"&gt;COMMIT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/sql-rollback.html" rel="noopener noreferrer"&gt;ROLLBACK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.psycopg.org/psycopg3/docs/basic/transactions.html" rel="noopener noreferrer"&gt;psycopg3 Transactions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>transactions</category>
      <category>database</category>
      <category>postgres</category>
      <category>atomicity</category>
    </item>
  </channel>
</rss>
