<?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: Drishti Tripathi</title>
    <description>The latest articles on DEV Community by Drishti Tripathi (@drishtitripathi2230).</description>
    <link>https://dev.to/drishtitripathi2230</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3951803%2F1e91efb4-4791-40fd-9259-9624c9b3e684.jpeg</url>
      <title>DEV Community: Drishti Tripathi</title>
      <link>https://dev.to/drishtitripathi2230</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/drishtitripathi2230"/>
    <language>en</language>
    <item>
      <title>What I Learned Building a Redis Clone in C++</title>
      <dc:creator>Drishti Tripathi</dc:creator>
      <pubDate>Sun, 31 May 2026 11:05:41 +0000</pubDate>
      <link>https://dev.to/drishtitripathi2230/what-i-learned-building-a-redis-clone-in-c-450m</link>
      <guid>https://dev.to/drishtitripathi2230/what-i-learned-building-a-redis-clone-in-c-450m</guid>
      <description>&lt;h1&gt;
  
  
  What I Learned Building a Redis Clone in C++
&lt;/h1&gt;

&lt;p&gt;A few weeks ago I was studying backend development from first principles. Not frameworks, not tutorials that skip the hard parts — actual fundamentals. How does data get stored? How does a server talk to a client? What even is a database at its core?&lt;/p&gt;

&lt;p&gt;That's when I came across Redis for the first time.&lt;/p&gt;

&lt;p&gt;I was reading about caching and kept seeing Redis mentioned everywhere. I looked it up and found out it's just a key-value store — you give it a key, it gives you back a value. Fast. Simple. Used by Twitter, GitHub, Snapchat.&lt;/p&gt;

&lt;p&gt;And I thought: &lt;em&gt;how hard can it be to build something like that?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Spoiler: harder than I expected. But I learned more from this one project than from weeks of reading.&lt;/p&gt;

&lt;p&gt;Here's everything I built, everything I got wrong, and everything I learned.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Key-Value Store?
&lt;/h2&gt;

&lt;p&gt;Before I get into the code, let me explain what I was even building.&lt;/p&gt;

&lt;p&gt;A key-value store is the simplest possible database. Instead of tables and rows like SQL, it just stores pairs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"Drishti"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"20"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"city"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"Delhi"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You store something with a key. You retrieve it with the same key. That's it. Redis is the most popular key-value store in the world and it's used everywhere — caching, session storage, rate limiting, pub/sub messaging.&lt;/p&gt;

&lt;p&gt;My goal was to build a simplified version from scratch in C++ and compare it to the real thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Plan
&lt;/h2&gt;

&lt;p&gt;I gave myself a clear roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TCP server on port 6380&lt;/li&gt;
&lt;li&gt;GET / SET / DELETE commands&lt;/li&gt;
&lt;li&gt;LRU eviction (automatically remove old keys when store is full)&lt;/li&gt;
&lt;li&gt;Disk persistence (data survives server restarts)&lt;/li&gt;
&lt;li&gt;Benchmark against real Redis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me walk through each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: The TCP Server
&lt;/h2&gt;

&lt;p&gt;The first thing I had to learn was how servers actually work at the socket level. I'd used HTTP APIs before but never thought about what happens underneath.&lt;/p&gt;

&lt;p&gt;A socket is just an endpoint for communication. When you connect to a server, both sides get a socket and they use it to send and receive data over TCP.&lt;/p&gt;

&lt;p&gt;Here's the core of my server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;SOCKET&lt;/span&gt; &lt;span class="n"&gt;server_fd&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="n"&gt;AF_INET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SOCK_STREAM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;sockaddr_in&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin_family&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AF_INET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin_addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;INADDR_ANY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;htons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6380&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sockaddr&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&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;client_fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// handle client...&lt;/span&gt;
    &lt;span class="n"&gt;closesocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_fd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In plain English:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a socket&lt;/li&gt;
&lt;li&gt;Bind it to port 6380&lt;/li&gt;
&lt;li&gt;Listen for connections&lt;/li&gt;
&lt;li&gt;In a loop — accept a client, handle them, close the connection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why port 6380? Redis uses 6379. I wanted to run both simultaneously for benchmarking, so I picked the next port.&lt;/p&gt;

&lt;p&gt;One thing that caught me off guard: on Windows you have to explicitly initialize the socket library with &lt;code&gt;WSAStartup&lt;/code&gt; before using any socket functions. On Linux you don't need this. Small difference, but it tripped me up at first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Parsing Commands
&lt;/h2&gt;

&lt;p&gt;Once the server could accept connections, I needed it to actually understand commands.&lt;/p&gt;

&lt;p&gt;The client sends a raw string like &lt;code&gt;"set name Drishti"&lt;/code&gt;. My server receives it as a buffer of bytes. I needed to split it into tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"set name Drishti"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Drishti"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wrote a parser that splits the string by spaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;parse_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;npos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then handle each command based on &lt;code&gt;tokens[0]&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokens&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokens&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokens&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;erase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One bug I hit early: telnet sends &lt;code&gt;\r\n&lt;/code&gt; at the end of each command. So &lt;code&gt;tokens[2]&lt;/code&gt; would be &lt;code&gt;"Drishti\r\n"&lt;/code&gt; instead of &lt;code&gt;"Drishti"&lt;/code&gt;. The fix was trimming trailing whitespace and newlines before parsing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: LRU Eviction
&lt;/h2&gt;

&lt;p&gt;This was the most interesting part to implement.&lt;/p&gt;

&lt;p&gt;The problem: memory is finite. If clients keep adding keys forever, the store will eventually run out of memory. I needed a way to automatically remove old keys when the store gets full.&lt;/p&gt;

&lt;p&gt;LRU — Least Recently Used — removes the key that hasn't been accessed in the longest time. The logic is simple: if you haven't used something recently, you probably don't need it.&lt;/p&gt;

&lt;p&gt;Example with capacity 5:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set a 1   →  [a]
set b 2   →  [a, b]
set c 3   →  [a, b, c]
set d 4   →  [a, b, c, d]
set e 5   →  [a, b, c, d, e]  ← full!
get a     →  [b, c, d, e, a]  ← 'a' just used, moves to front
set f 6   →  evict 'b' (least recently used)
             [c, d, e, a, f]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation uses two data structures together:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A doubly linked list&lt;/strong&gt; tracks order of use. Most recently used is at the front, least recently used is at the back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A hash map&lt;/strong&gt; maps each key directly to its position in the list for O(1) lookup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;List: [f] &amp;lt;-&amp;gt; [a] &amp;lt;-&amp;gt; [e] &amp;lt;-&amp;gt; [d] &amp;lt;-&amp;gt; [c]
Map:  "a" → node, "f" → node, ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither structure alone is enough. The list gives you order but lookup is O(n). The map gives you fast lookup but has no concept of order. Together they give you both in O(1).&lt;/p&gt;

&lt;p&gt;Every GET or SET moves that key to the front of the list. When you need to evict, just remove from the back. That's it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Disk Persistence
&lt;/h2&gt;

&lt;p&gt;Without persistence, all data disappears when the server restarts. Not great.&lt;/p&gt;

&lt;p&gt;I implemented an Append-Only File (AOF) — the same strategy Redis uses. Every SET and DELETE gets logged to 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;set a 1
set b 2
set c 3
delete b
set d 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file only ever gets appended to — never edited or rewritten. On startup, the server reads the file top to bottom and replays every command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replay: set a 1    →  {a=1}
replay: set b 2    →  {a=1, b=2}
replay: set c 3    →  {a=1, b=2, c=3}
replay: delete b   →  {a=1, c=3}
replay: set d 4    →  {a=1, c=3, d=4}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing this felt satisfying. Set some keys, kill the server, restart it, get the same keys back. It works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Benchmarks vs Redis
&lt;/h2&gt;

&lt;p&gt;The moment of truth. I installed Redis 7.0.15 on WSL and wrote a Python benchmark that sends 1000 SET and 1000 GET commands to both servers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;My KV Store&lt;/th&gt;
&lt;th&gt;Redis 7.0.15&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SET 1000 keys&lt;/td&gt;
&lt;td&gt;3.55s (281 req/s)&lt;/td&gt;
&lt;td&gt;3.19s (313 req/s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET 1000 keys&lt;/td&gt;
&lt;td&gt;3.55s (281 req/s)&lt;/td&gt;
&lt;td&gt;1.12s (892 req/s)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;SET is almost equal&lt;/strong&gt; — only 11% slower. Honestly didn't expect that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GET has a 3x gap&lt;/strong&gt; — Redis is significantly faster here.&lt;/p&gt;

&lt;p&gt;But here's the thing: this comparison isn't entirely fair. And understanding &lt;em&gt;why&lt;/em&gt; it isn't fair taught me more than the numbers themselves.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Numbers Don't Tell You
&lt;/h2&gt;

&lt;p&gt;My benchmark script opens a new TCP connection for every single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connect → send "set a 1" → receive "OK" → disconnect
connect → send "get a"   → receive "1"  → disconnect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every TCP connection requires a 3-way handshake (SYN, SYN-ACK, ACK). That's network overhead on every single request. Real Redis clients use &lt;strong&gt;persistent connections&lt;/strong&gt; — one connection stays open and thousands of commands flow through it.&lt;/p&gt;

&lt;p&gt;My store isn't slow because C++ is slow. It's slow because of connection overhead that has nothing to do with the actual KV logic.&lt;/p&gt;

&lt;p&gt;This was one of the most valuable lessons from the project: &lt;strong&gt;benchmarks are only meaningful in context&lt;/strong&gt;. The numbers matter less than understanding what the numbers are actually measuring.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other Limitations (Being Honest)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single-threaded&lt;/strong&gt;: My server handles one request at a time. Redis uses event-driven I/O (epoll) to handle thousands of concurrent clients on a single thread. Production servers use async I/O or thread pools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AOF grows forever&lt;/strong&gt;: My file never gets compacted. Redis periodically rewrites the AOF to remove redundant entries — if a key was set 1000 times, only the last value matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No RESP protocol&lt;/strong&gt;: Real Redis uses RESP (Redis Serialization Protocol), a structured binary format. My store uses plain text, so it's not compatible with &lt;code&gt;redis-cli&lt;/code&gt; or any Redis SDK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LRU capacity is hardcoded to 5&lt;/strong&gt;: Fine for a demo, not for production.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Actually Learned
&lt;/h2&gt;

&lt;p&gt;Going in, I thought this project would teach me about databases. It did — but it taught me much more than that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TCP sockets from scratch.&lt;/strong&gt; I now understand what actually happens when two programs communicate over a network. Before this, HTTP was magic to me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why data structures matter in practice.&lt;/strong&gt; LRU eviction is a textbook problem, but implementing it made the O(1) requirement real. You can't just use a list. You can't just use a map. You need both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The gap between "working" and "production-grade".&lt;/strong&gt; My store works. Redis works at 1 million req/s under production load. Understanding that gap — connection pooling, async I/O, protocol design, crash recovery — is what separates a learning project from real infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benchmarking is a skill.&lt;/strong&gt; Writing a benchmark that actually measures what you think it's measuring is harder than it sounds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First principles learning works.&lt;/strong&gt; I could have used a Redis client library and moved on. Instead I built the thing. Now when I use Redis in a real project, I'll understand what it's doing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Full source code on GitHub: &lt;a href="https://github.com/DrishtiTripathi2230/kv-store" rel="noopener noreferrer"&gt;https://github.com/DrishtiTripathi2230/kv-store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The README has a detailed breakdown of every component with diagrams and examples — worth a read if you want to understand any part in more depth.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: #cpp #systems #redis #showdev #beginners&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>showdev</category>
      <category>programming</category>
      <category>cpp</category>
    </item>
    <item>
      <title>I Built India's Missing Local Transport App — Then Abandoned It for 5 Months</title>
      <dc:creator>Drishti Tripathi</dc:creator>
      <pubDate>Fri, 29 May 2026 10:01:38 +0000</pubDate>
      <link>https://dev.to/drishtitripathi2230/from-dead-prototype-to-full-stack-app-how-i-finally-shipped-autoconnect-1m4l</link>
      <guid>https://dev.to/drishtitripathi2230/from-dead-prototype-to-full-stack-app-how-i-finally-shipped-autoconnect-1m4l</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;AutoConnect is India's inclusive local transport app — connecting &lt;br&gt;
passengers with auto rickshaws, e-rickshaws, cycle rickshaws, and &lt;br&gt;
single-engine bikes through a single mobile-first platform.&lt;/p&gt;

&lt;p&gt;I originally built AutoConnect in December 2025 as a personal project. &lt;br&gt;
I had a clear vision — a transport app built specifically for India's &lt;br&gt;
local vehicles that bigger apps ignore. I designed the full UI, built &lt;br&gt;
every screen, and made it look like a real app. But it was just screens. &lt;br&gt;
Every button was simulated. The README literally said "No backend &lt;br&gt;
integration." I ran out of time, lost momentum, and abandoned it.&lt;/p&gt;

&lt;p&gt;5 months later, this challenge made me finally finish what I started.&lt;/p&gt;

&lt;p&gt;AutoConnect now has a real Spring Boot backend, a live database, GPS &lt;br&gt;
location detection, real document upload, and a complete ride flow — &lt;br&gt;
passenger requests a ride, driver sees it in real time, accepts it, &lt;br&gt;
and the passenger gets confirmed. All deployed and live.&lt;/p&gt;

&lt;p&gt;This project means something to me personally — India's local transport ecosystem is fragmented and most apps ignore auto rickshaws and e-rickshaws completely. AutoConnect is built specifically for that gap, with an accessibility-first UI designed for users with low digital literacy.&lt;/p&gt;

&lt;p&gt;AutoConnect is built for drivers that no platform currently serves — cycle rickshaw operators and e-rickshaw drivers in tier 3 and tier 4 towns. These drivers find customers by standing at corners or through WhatsApp contacts. No app exists for them. AutoConnect is built specifically for that gap — simple, accessible, and designed for users with low digital literacy on both sides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;strong&gt;Live App:&lt;/strong&gt; &lt;a href="https://auto-connect-mobile-app.vercel.app" rel="noopener noreferrer"&gt;https://auto-connect-mobile-app.vercel.app&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Backend API:&lt;/strong&gt; &lt;a href="https://autoconnect-backend-production.up.railway.app" rel="noopener noreferrer"&gt;https://autoconnect-backend-production.up.railway.app&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Frontend Repo:&lt;/strong&gt; &lt;a href="https://github.com/DrishtiTripathi2230/AutoConnect-Mobile-App" rel="noopener noreferrer"&gt;https://github.com/DrishtiTripathi2230/AutoConnect-Mobile-App&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Backend Repo:&lt;/strong&gt; &lt;a href="https://github.com/DrishtiTripathi2230/AutoConnect-Backend" rel="noopener noreferrer"&gt;https://github.com/DrishtiTripathi2230/AutoConnect-Backend&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to test the full flow:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;As a Passenger:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the live app → Select "I'm a Passenger"&lt;/li&gt;
&lt;li&gt;Sign up with your name and phone number&lt;/li&gt;
&lt;li&gt;Enter pickup location (or use GPS) and destination&lt;/li&gt;
&lt;li&gt;Click "Find Rides" — this creates a real ride in the database&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;As a Driver (open in a different browser):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select "I'm a Driver" → Sign up with vehicle details&lt;/li&gt;
&lt;li&gt;Upload documents → Submit&lt;/li&gt;
&lt;li&gt;See the passenger's ride request appear on your dashboard&lt;/li&gt;
&lt;li&gt;Click Accept — passenger gets confirmed instantly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9o491hy622tnpvupk29n.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%2F9o491hy622tnpvupk29n.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flf4qyc97us0ul9ur2sx9.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%2Flf4qyc97us0ul9ur2sx9.png" alt=" " width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Where it started
&lt;/h3&gt;

&lt;p&gt;In December 2025, I built AutoConnect as a personal project. I had a &lt;br&gt;
clear vision — a transport app built specifically for India's local &lt;br&gt;
vehicles. I spent days designing every screen, every flow, every color. &lt;br&gt;
The UI looked like a real app.&lt;/p&gt;

&lt;p&gt;But it was completely hollow.&lt;/p&gt;

&lt;p&gt;No backend. No database. No API calls. Every button navigated to a &lt;br&gt;
hardcoded screen. The README admitted it openly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Design Focus: UI/UX (No backend integration)"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qcm4uxe9lvgbo4kct3p.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%2F2qcm4uxe9lvgbo4kct3p.png" alt=" " width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This project is a UI/UX prototype intended for design demonstration"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I ran out of momentum and abandoned it for 5 months.&lt;/p&gt;

&lt;p&gt;Here's the proof — every commit is from December 23, 2025:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7ysxvr3d3dvemtuq0s4.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%2Fy7ysxvr3d3dvemtuq0s4.png" alt=" " width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  What I changed
&lt;/h3&gt;

&lt;p&gt;When I saw the GitHub Finish-Up-A-Thon challenge, I knew immediately &lt;br&gt;
which project to revive.&lt;/p&gt;

&lt;p&gt;Here's everything I built in May 2026:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend (built from scratch):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring Boot REST API with Driver and Ride endpoints&lt;/li&gt;
&lt;li&gt;PostgreSQL database with JPA entities&lt;/li&gt;
&lt;li&gt;Driver registration, availability, and ride matching&lt;/li&gt;
&lt;li&gt;Ride lifecycle — REQUESTED → ACCEPTED → COMPLETED&lt;/li&gt;
&lt;li&gt;Deployed on Railway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Frontend integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connected all UI screens to real backend APIs&lt;/li&gt;
&lt;li&gt;Real GPS location detection using browser Geolocation API&lt;/li&gt;
&lt;li&gt;Phone number validation (10 digits, numbers only)&lt;/li&gt;
&lt;li&gt;Name validation (letters only)&lt;/li&gt;
&lt;li&gt;Real file upload for driver documents&lt;/li&gt;
&lt;li&gt;Deployed on Vercel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The moment it became real:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I entered pickup and destination, clicked Find Rides, and saw &lt;br&gt;
"✅ Backend Connected | Ride #3" appear in the corner — that was the &lt;br&gt;
moment 5 months of abandoned work finally felt worth it.&lt;/p&gt;




&lt;h3&gt;
  
  
  Before vs After
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before (Dec 2025)&lt;/th&gt;
&lt;th&gt;After (May 2026)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Spring Boot + Railway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;PostgreSQL with real data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactions&lt;/td&gt;
&lt;td&gt;Simulated&lt;/td&gt;
&lt;td&gt;Real API calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;td&gt;Hardcoded&lt;/td&gt;
&lt;td&gt;Real GPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Phone + name + docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Vercel + Railway&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot was a genuine partner in finishing this project, not &lt;br&gt;
just autocomplete.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Copilot helped
&lt;/h3&gt;

&lt;p&gt;The most impressive moment was when I asked Copilot to add fare &lt;br&gt;
calculation for all vehicle types. Instead of just writing a function, &lt;br&gt;
Copilot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read my entire codebase automatically&lt;/strong&gt; — it scanned 
PassengerHome.tsx and VehicleSelection.tsx before writing a single line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identified a problem I hadn't noticed&lt;/strong&gt; — fare rates were 
duplicated across two components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Created a shared utility file&lt;/strong&gt; — centralized the fare logic so 
both screens use the same calculation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updated 3 files simultaneously&lt;/strong&gt; — +27 lines, clean and consistent&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This wasn't just code generation. Copilot understood the architecture &lt;br&gt;
of my project and made a better decision than I would have made alone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fporoltp75q0gn6q34w64.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%2Fporoltp75q0gn6q34w64.png" alt=" " width="799" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlatw8ja8pezf90fuelt.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%2Fzlatw8ja8pezf90fuelt.png" alt=" " width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I learned
&lt;/h3&gt;

&lt;p&gt;Copilot works best when you give it context. Asking it to "add fare &lt;br&gt;
calculation" while having the relevant files open gave it everything &lt;br&gt;
it needed to make smart decisions about where the code should live.&lt;/p&gt;

&lt;p&gt;For a project like AutoConnect — with multiple interconnected components &lt;br&gt;
— that architectural awareness saved me significant debugging time.&lt;br&gt;
— AutoConnect isn't perfect — but it's real now. And that's the whole point of finishing things.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>webdev</category>
      <category>github</category>
    </item>
  </channel>
</rss>
