<?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: Oge Obubu</title>
    <description>The latest articles on DEV Community by Oge Obubu (@ogeobubu).</description>
    <link>https://dev.to/ogeobubu</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%2F491136%2F9b950d44-c000-47ee-bc0b-d6987f6bada3.jpg</url>
      <title>DEV Community: Oge Obubu</title>
      <link>https://dev.to/ogeobubu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ogeobubu"/>
    <language>en</language>
    <item>
      <title>Stop Using `useEffect` for Data Fetching—Please, I Beg You</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:55:42 +0000</pubDate>
      <link>https://dev.to/ogeobubu/stop-using-useeffect-for-data-fetching-please-i-beg-you-1ni</link>
      <guid>https://dev.to/ogeobubu/stop-using-useeffect-for-data-fetching-please-i-beg-you-1ni</guid>
      <description>&lt;h2&gt;
  
  
  The Scene
&lt;/h2&gt;

&lt;p&gt;It's 2 AM. You're staring at your screen, debugging why your dashboard keeps showing yesterday's data even after you've changed the filter. Your &lt;code&gt;useEffect&lt;/code&gt; dependency array looks like a crime scene. You've got three &lt;code&gt;useState&lt;/code&gt; hooks just to manage loading, error, and data. You added a cleanup function, but somehow the component still throws that dreaded warning: &lt;em&gt;"Can't perform a React state update on an unmounted component."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You take a sip of cold coffee. You wonder where it all went wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with &lt;code&gt;useEffect&lt;/code&gt; for Data Fetching
&lt;/h2&gt;

&lt;p&gt;Let's be honest with ourselves. &lt;code&gt;useEffect&lt;/code&gt; was never designed for data fetching. The React team gave us this hook to synchronize with external systems, DOM events, subscriptions, and timers. But somewhere along the line, we collectively decided to use it as our go-to tool for API calls.&lt;/p&gt;

&lt;p&gt;And look, I get it. When you're learning React, the pattern is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works. Until it doesn't.&lt;/p&gt;

&lt;p&gt;Here's what happens when your application grows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Race Conditions&lt;/strong&gt; — When your user clicks filters too quickly, old requests return after newer ones and override your state. The UI shows mismatched data, and you waste hours adding request cancellation logic that nobody on your team fully understands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unnecessary Re-renders&lt;/strong&gt; — Every state update triggers a re-render. With &lt;code&gt;useEffect&lt;/code&gt;, you're juggling at least three states: &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;loading&lt;/code&gt;, and &lt;code&gt;error&lt;/code&gt;. Three states, three renders, even before React mounts your actual content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Poor Caching&lt;/strong&gt; — If a user visits a page, leaves, and comes back, your &lt;code&gt;useEffect&lt;/code&gt; fires again. Same data, same API call, same network cost. Multiply this by a thousand users, and you're burning your backend for no good reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual Cleanup Headaches&lt;/strong&gt; — Need to cancel pending requests? Need to prevent state updates after unmount? You'll write boilerplate that distracts from your actual business logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter TanStack Query (Formerly React Query)
&lt;/h2&gt;

&lt;p&gt;Now, imagine a different approach. Instead of telling React &lt;em&gt;how&lt;/em&gt; to fetch data, you simply tell it &lt;em&gt;what&lt;/em&gt; data you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No &lt;code&gt;useState&lt;/code&gt;. No &lt;code&gt;useEffect&lt;/code&gt;. No cleanup. Just a declarative statement: &lt;em&gt;"Give me users based on this filter, and handle all the complexity for me."&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What TanStack Query Gives You That &lt;code&gt;useEffect&lt;/code&gt; Never Will
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Automatic Caching&lt;/strong&gt; — When a user navigates away and returns, the data is already there. No additional network requests. Your application feels fast because it &lt;em&gt;is&lt;/em&gt; fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deduplication&lt;/strong&gt; — If two components request the same data simultaneously, TanStack Query makes a single request and shares the response. Your backend thanks you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Background Refetching&lt;/strong&gt; — Stale data automatically updates in the background. Your users always see fresh information without spinners interrupting their flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retry Logic&lt;/strong&gt; — Network failure? Server error? TanStack Query retries intelligently with exponential backoff. You don't have to write that logic yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimistic Updates&lt;/strong&gt; — When a user submits a form, you can update the UI immediately and sync with the server in the background. The experience feels instant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Devtools&lt;/strong&gt; — A Chrome extension that shows you exactly what's cached, what's fetching, and what's stale. No more guessing why data isn't showing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Difference
&lt;/h2&gt;

&lt;p&gt;Here's the thing: when you use &lt;code&gt;useEffect&lt;/code&gt;, you're managing &lt;em&gt;imperative&lt;/em&gt; side effects. You're telling React: &lt;em&gt;"When this component renders, do this thing, then clean up when it unmounts."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you use TanStack Query, you're managing &lt;em&gt;declarative&lt;/em&gt; state. You're telling React: &lt;em&gt;"I need this data. Handle the rest."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One approach makes you a plumber, constantly fixing leaks. The other makes you an architect, designing systems that work.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical Example
&lt;/h2&gt;

&lt;p&gt;Consider a dashboard with filters, pagination, and auto-refresh. With &lt;code&gt;useEffect&lt;/code&gt;, your code grows into a tangled mess of dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isMounted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`/api/sales?page=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;filter=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;setSales&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMounted&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AbortError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;isMounted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at that. Twenty lines of boilerplate just to fetch data with cleanup, cancellation, and polling.&lt;/p&gt;

&lt;p&gt;Now the TanStack Query version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sales&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchSales&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;refetchInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&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;Eight lines. Clean. Readable. Maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  But Wait—Is &lt;code&gt;useEffect&lt;/code&gt; Completely Obsolete?
&lt;/h2&gt;

&lt;p&gt;No. Let's not throw the baby out with the bathwater.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useEffect&lt;/code&gt; still has valid use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronising with non-React systems&lt;/strong&gt; — Integrating with third-party libraries like Google Maps, Chart.js, or Stripe elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adding event listeners&lt;/strong&gt; — Window resize, scroll position, keyboard shortcuts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managing animations&lt;/strong&gt; — Triggering transitions when props change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging analytics&lt;/strong&gt; — Tracking page views and user interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key distinction is this: &lt;code&gt;useEffect&lt;/code&gt; is for &lt;em&gt;synchronisation&lt;/em&gt;, not &lt;em&gt;data fetching&lt;/em&gt;. If your effect reaches out to a server, you're probably using the wrong tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Gain by Making the Switch
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Faster Development&lt;/strong&gt; — Less boilerplate means more time building features that matter to your users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fewer Bugs&lt;/strong&gt; — Race conditions, memory leaks, and stale data become problems of the past.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better User Experience&lt;/strong&gt; — Instant navigation, background updates, and optimistic mutations make your app feel native.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reduced Server Load&lt;/strong&gt; — Intelligent caching cuts your API calls by 60–80%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Easier Onboarding&lt;/strong&gt; — New developers grasp the data flow in minutes rather than days.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Word for the Sceptics
&lt;/h2&gt;

&lt;p&gt;I know what some of you are thinking: &lt;em&gt;"Another library? I'm tired of learning new things."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But here's the truth—this isn't just a library. It's a paradigm shift. It's acknowledging that we've been doing data fetching the hard way for too long.&lt;/p&gt;

&lt;p&gt;TanStack Query has been battle-tested in production for years. It powers applications at companies you know and respect. It's not going anywhere.&lt;/p&gt;

&lt;p&gt;And once you learn it, you'll wonder how you ever lived without it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Your &lt;code&gt;useEffect&lt;/code&gt; code isn't working. It's working &lt;em&gt;enough&lt;/em&gt;, which is different.&lt;/p&gt;

&lt;p&gt;You've been surviving, not thriving.&lt;/p&gt;

&lt;p&gt;You've been fixing bugs that shouldn't exist.&lt;/p&gt;

&lt;p&gt;You've been writing code that future you will curse.&lt;/p&gt;

&lt;p&gt;Stop making your life difficult. Stop making your users suffer. Stop making your backend engineers cry.&lt;/p&gt;

&lt;p&gt;The tools exist. The knowledge is available. The only thing standing in your way is inertia.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Next Step
&lt;/h2&gt;

&lt;p&gt;Tomorrow, open your codebase. Find a single &lt;code&gt;useEffect&lt;/code&gt; that fetches data. Replace it with &lt;code&gt;useQuery&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Time it. See how long it takes.&lt;/p&gt;

&lt;p&gt;Then ask yourself: why didn't I do this sooner?&lt;/p&gt;

&lt;p&gt;Because once you taste the difference, you'll never go back.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Your API is tired. Your users are waiting. Your team deserves better.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make the switch today.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>We Built a Super-App in 30 Days. Here's the Chaos Nobody Saw.</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Wed, 15 Jul 2026 19:49:02 +0000</pubDate>
      <link>https://dev.to/ogeobubu/we-built-a-super-app-in-30-days-heres-the-chaos-nobody-saw-5fd6</link>
      <guid>https://dev.to/ogeobubu/we-built-a-super-app-in-30-days-heres-the-chaos-nobody-saw-5fd6</guid>
      <description>&lt;p&gt;&lt;strong&gt;One team. Thirty days. Nine service verticals. Fifty-plus screens. And one question that kept us up at 5 AM: "Can this actually work?"&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Day 1: "It's Just a Food App… Right?"
&lt;/h2&gt;

&lt;p&gt;Picture this.&lt;/p&gt;

&lt;p&gt;It's June 1st. We're staring at a food delivery app. It works. Customers can order jollof rice from restaurants nearby. Life is good.&lt;/p&gt;

&lt;p&gt;Then someone says the words that changed everything:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What if users could also order from pharmacies? Supermarkets? Local markets? What if they could send parcels? Do laundry? Plan events for 500 guests?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We laughed.&lt;/p&gt;

&lt;p&gt;Then we opened the codebase.&lt;/p&gt;

&lt;p&gt;And for the next 30 days, we didn't sleep.&lt;/p&gt;




&lt;h2&gt;
  
  
  Week 1: The Multi-Service Monster Awakens
&lt;/h2&gt;

&lt;p&gt;Here's what nobody tells you about building a super-app: it's not one app. It's nine apps wearing the same jacket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monday&lt;/strong&gt; — We started with pharmacy. Simple, right? Browse pharmacies. Search medications. Add to cart. Checkout.&lt;/p&gt;

&lt;p&gt;Except "simple" turned into service-specific checkout flows, quantity-based ordering, and a whole new category discovery system pulled live from vendor inventory.&lt;/p&gt;

&lt;p&gt;**Tuesday—Supermarket hit the codebase. Grocery products. Category tabs. Search that actually returns what you're looking for. Another service. Another checkout flow. Another 2,000 lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wednesday&lt;/strong&gt; — Local Market. Think open-air Nigerian markets — but on your phone. Vendors. Items. Real-time availability. The same pattern, but different enough to need its own identity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thursday&lt;/strong&gt; — Laundry. Pickup addresses. Express service options. Insurance support. Because what's a super app without someone washing your clothes?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Friday&lt;/strong&gt; — We stepped back and looked at the home screen. It had become a cluttered mess of cards and banners. So we rebuilt it. A clean 3-column grid. Service-specific accent colors—teal for pharmacy, blue for laundry, green for local market, and yellow for supermarket. Each section pulling its own featured vendors from the backend.&lt;/p&gt;

&lt;p&gt;By Friday night, the app had gone from one service to five. The home screen looked like it belonged in a design portfolio, not a sprint review.&lt;/p&gt;

&lt;p&gt;The codebase had grown by &lt;strong&gt;8,020 insertions across 40 files&lt;/strong&gt; in the first week alone.&lt;/p&gt;

&lt;p&gt;But we weren't done. Not even close.&lt;/p&gt;




&lt;h2&gt;
  
  
  Week 2: The Features That Made People Say "Wait, This App Does THAT?"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Meal Subscription Engine
&lt;/h3&gt;

&lt;p&gt;Picture a busy professional in Ile-Ife. Every morning, they stare at their phones, wondering, &lt;em&gt;"What am I going to eat today?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now imagine they could subscribe to a meal plan—breakfast, lunch, and dinner—for an entire month. Pick their vendor. Set their delivery times. Let the app handle the rest.&lt;/p&gt;

&lt;p&gt;We built a calendar-based subscription system with a scrollable view, in-place editing of delivery times and addresses, pause and cancel functionality, auto-renewal toggles, and a real-time cost preview that updates as you configure your plan.&lt;/p&gt;

&lt;p&gt;The calendar alone took three days. The backend integration took another two. The UX debates? Those are still ongoing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Corporate Meal Plans (Staff Lunch)
&lt;/h3&gt;

&lt;p&gt;Then the sales team dropped a bomb:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What if companies could manage staff lunches through the app?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Staff count. Meal budget per person. Delivery schedule across workdays. Delivery time windows.&lt;/p&gt;

&lt;p&gt;We built a full corporate meal program—because apparently, the app that started with jollof rice is now running companies' lunch breaks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Search That Actually Finds Things
&lt;/h3&gt;

&lt;p&gt;You know those search bars that show you the same three results no matter what you type? Yeah, we didn't want that.&lt;/p&gt;

&lt;p&gt;We built a unified global search with a scoring algorithm. Type "paracetamol," and it finds the pharmacy vendor, the specific medication, AND deep-links you to the right screen. Type "pizza" and you get restaurants, specific menu items, and even your past orders.&lt;/p&gt;

&lt;p&gt;Fifty-plus screens. All searchable. All from one floating button in the tab bar.&lt;/p&gt;




&lt;h2&gt;
  
  
  Week 3: Gamification, Predictions, and the Spin Wheel That Broke Our Brains
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Spin Wheel
&lt;/h3&gt;

&lt;p&gt;Everyone loves a good spin wheel. Ours has animated graphics with multi-rotation physics, eligibility checks, daily limits, live expiry countdowns on rewards, and a dedicated inventory screen where users track their claimed, pending, and expired rewards.&lt;/p&gt;

&lt;p&gt;The animation alone required custom interpolation math. But when that wheel spins and the user wins? Worth every broken keyboard shortcut.&lt;/p&gt;

&lt;h3&gt;
  
  
  FIFA 2026 World Cup Predictions
&lt;/h3&gt;

&lt;p&gt;This one was pure chaos — the good kind.&lt;/p&gt;

&lt;p&gt;We built an in-app prediction system where customers pick match winners for the 2026 World Cup. View fixtures. Submit predictions. Track your prediction history. Climb the leaderboard.&lt;/p&gt;

&lt;p&gt;There's a leaderboard that filters by week and month. Trophy icons. Gold badges. The works.&lt;/p&gt;

&lt;p&gt;It turned a delivery app into a social experience. People aren't just ordering food anymore. They're arguing about whether Brazil beats Germany.&lt;/p&gt;




&lt;h2&gt;
  
  
  Week 4: Payments, Parcels, and the Stuff That Actually Matters
&lt;/h2&gt;

&lt;p&gt;Here's the unglamorous truth: the flashiest features mean nothing if payments fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seamless In-App Payments
&lt;/h3&gt;

&lt;p&gt;Before this month, payments opened in an external browser. Users would get redirected, lose context, and sometimes never come back.&lt;/p&gt;

&lt;p&gt;We moved payments into an in-app experience. Seamless. In-context. No browser switches.&lt;/p&gt;

&lt;p&gt;But that was just the beginning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Payment retry for failed orders&lt;/strong&gt; — because networks are unreliable and users shouldn't have to start over&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate submission protection&lt;/strong&gt;—no more double-charging because someone tapped "Pay" twice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background transaction verification&lt;/strong&gt; — the app checks payment status even when the user isn't looking&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Parcel Delivery, Done Right
&lt;/h3&gt;

&lt;p&gt;Send a parcel from Ile-Ife to Abuja. Enter sender and recipient details. Pick a pickup address. Choose a drop-off. Schedule the delivery. Get a delivery code. Track the parcel in real time.&lt;/p&gt;

&lt;p&gt;We built autocomplete for addresses using Google Places. Time pickers for scheduling. Reverse geocoding for manual address entries. A timeline tracker that shows every step from pickup to delivery.&lt;/p&gt;

&lt;p&gt;It's not just sending a package. It's watching it travel across the country.&lt;/p&gt;

&lt;h3&gt;
  
  
  Live Delivery Tracking
&lt;/h3&gt;

&lt;p&gt;Speaking of maps—we went deep on Google Maps integration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time rider markers moving across the screen&lt;/li&gt;
&lt;li&gt;Route directions with distance and ETA&lt;/li&gt;
&lt;li&gt;Automatic map framing&lt;/li&gt;
&lt;li&gt;Rider profile display with a call button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The delivery map is the screen users stare at the most. We made sure it's beautiful, reliable, and informative.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Invisible Architecture Nobody Talks About
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Guest-to-Authenticated Cart Merging
&lt;/h3&gt;

&lt;p&gt;A user opens the app without logging in. Browses pharmacies. Adds paracetamol to their cart. Then signs in.&lt;/p&gt;

&lt;p&gt;The paracetamol is still there. Seamlessly merged. No data loss. No duplicate cart entries.&lt;/p&gt;

&lt;p&gt;Behind the scenes, a guest token system ensures every item makes the transition from anonymous browsing to a full account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network Resilience
&lt;/h3&gt;

&lt;p&gt;Every API call has automatic retries with exponential backoff. The app doesn't crash when the network flickers. It waits, retries, and recovers.&lt;/p&gt;

&lt;p&gt;Because in Nigeria, a network isn't a guarantee. We built for that reality.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;Let's talk scale:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Service Verticals&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screens&lt;/td&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lines Inserted (This Month)&lt;/td&gt;
&lt;td&gt;8,020+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Files Modified&lt;/td&gt;
&lt;td&gt;40+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gamification Features&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;We're not slowing down.&lt;/p&gt;

&lt;p&gt;The "Coming Soon" slide in the onboarding screen isn't decoration—it's a promise. More services. More features. More reasons to open the app instead of downloading another one.&lt;/p&gt;

&lt;p&gt;Because the goal was never to build a food delivery app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal was to build the last app you'll ever need for daily services.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And in 30 days, we got very, very close.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Foodmartex is available on Android. iOS is coming soon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built with React Native, Redux Toolkit, and an unreasonable amount of determination.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built a Super-App Backend in 2 Weeks. Here's What Nobody Tells You About the Chaos.</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Sat, 11 Jul 2026 04:42:15 +0000</pubDate>
      <link>https://dev.to/ogeobubu/i-built-a-super-app-backend-in-2-weeks-heres-what-nobody-tells-you-about-the-chaos-43ip</link>
      <guid>https://dev.to/ogeobubu/i-built-a-super-app-backend-in-2-weeks-heres-what-nobody-tells-you-about-the-chaos-43ip</guid>
      <description>&lt;h2&gt;
  
  
  Day 1: "How hard can it be?"
&lt;/h2&gt;

&lt;p&gt;I had 14 days. One Laravel backend. Five verticals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Food delivery&lt;/li&gt;
&lt;li&gt;Marketplace&lt;/li&gt;
&lt;li&gt;Pharmacy&lt;/li&gt;
&lt;li&gt;Laundry&lt;/li&gt;
&lt;li&gt;Parcel delivery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;"Just connect everything and ship,"&lt;/em&gt; I told myself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Narrator: It was not that simple.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Number That Keeps Me Up at Night
&lt;/h2&gt;

&lt;p&gt;By Day 14, I was staring at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1,028&lt;/code&gt; PHP files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;236&lt;/code&gt; database migrations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;65&lt;/code&gt; commits in the Git history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sixty-five commits in fourteen days.&lt;/p&gt;

&lt;p&gt;That's not a project. That's a hostage situation with &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Commission Problem Nobody Warned You About
&lt;/h2&gt;

&lt;p&gt;Here's the thing about building a multi-vendor platform: &lt;strong&gt;everybody wants their cut.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The vendor wants theirs. The rider wants theirs. The platform wants its cut. And somehow, all of this needs to calculate correctly, down to the kobo, across every single order type.&lt;/p&gt;

&lt;p&gt;I spent 3 days building a commission engine that handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vendor commission percentages&lt;/li&gt;
&lt;li&gt;Platform commission splits&lt;/li&gt;
&lt;li&gt;Rider delivery fees&lt;/li&gt;
&lt;li&gt;Markup calculations (global AND vendor-specific)&lt;/li&gt;
&lt;li&gt;Null handling for every edge case that breaks at 2 AM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;"What happens when both markup types are null?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Nobody tells you about this in tutorials. You find out at 2 AM when your test user places an order, and the whole system divides by zero.&lt;/p&gt;




&lt;h2&gt;
  
  
  Notifications: The Invisible Monster
&lt;/h2&gt;

&lt;p&gt;You know what's harder than building an order system?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Telling people about it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I built a notification system that sends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Push notifications with images (because text-only is 2019)&lt;/li&gt;
&lt;li&gt;Real-time status updates for every order state&lt;/li&gt;
&lt;li&gt;Market order events: &lt;code&gt;accepted&lt;/code&gt;, &lt;code&gt;en route&lt;/code&gt;, &lt;code&gt;delivered&lt;/code&gt;, &lt;code&gt;cancelled&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pharmacy order matching alerts&lt;/li&gt;
&lt;li&gt;Laundry dispute handling flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each notification has to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instant&lt;/li&gt;
&lt;li&gt;Contextual&lt;/li&gt;
&lt;li&gt;Delivered across multiple channels&lt;/li&gt;
&lt;li&gt;Actually useful (not spam)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wrote &lt;strong&gt;428 lines of notification logic&lt;/strong&gt; in one week alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Payment Resurrection
&lt;/h2&gt;

&lt;p&gt;Users abandon carts. Payment gateways time out. Networks flicker.&lt;/p&gt;

&lt;p&gt;So I built a &lt;strong&gt;payment resurrection system&lt;/strong&gt;: when a payment fails, or times out, customers can reinitialise and try again without losing their order state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Market orders — recoverable&lt;/span&gt;
&lt;span class="c1"&gt;// Pharmacy orders — resumable&lt;/span&gt;
&lt;span class="c1"&gt;// Laundry orders — retryable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All recoverable. All resumable. All tested.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Because nobody's perfect. But your payment system should be.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-Time Order Tracking
&lt;/h2&gt;

&lt;p&gt;Every order has a lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Placed → Accepted → En Route → Delivered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what happens when things go wrong?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer disputes a laundry item&lt;/li&gt;
&lt;li&gt;Rider can't find the address&lt;/li&gt;
&lt;li&gt;Vendor runs out of stock after confirming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built status logs for every single state change. Every transition. Every timestamp. Every reason.&lt;/p&gt;

&lt;p&gt;Not because the client asked for it.&lt;/p&gt;

&lt;p&gt;Because &lt;strong&gt;debugging without logs is just guessing with confidence.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Feature Toggle That Saved the Day
&lt;/h2&gt;

&lt;p&gt;Not everything should be live on Day 1.&lt;/p&gt;

&lt;p&gt;So I built an &lt;code&gt;ensureFeatureAvailable&lt;/code&gt; method that gracefully handles feature unavailability. If a feature isn't ready, users get a clean response instead of a &lt;code&gt;500 Internal Server Error&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;featureEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$feature&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'This feature is coming soon.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;403&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;Small thing. Huge impact.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned in 14 Days
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Migrations are forever.&lt;/strong&gt; Make them idempotent or make them twice. I chose twice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Edge cases aren't edge cases.&lt;/strong&gt; They're just cases you haven't hit yet. I handled null values in markup calculations on Day 12. Should've been Day 1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Notifications are a product.&lt;/strong&gt; Not an afterthought. The way you notify users IS the user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commission math is a philosophical problem.&lt;/strong&gt; It's not about the numbers. It's about who gets paid, when, and how you prove it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;1,028&lt;/code&gt; files is not a flex. It's a cry for help.&lt;/strong&gt; But the system works. Every route. Every controller. Every relationship. It all connects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logs are not optional.&lt;/strong&gt; If you can't trace an order from placement to delivery, you're not building a system. You're building a house of cards.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;&lt;code&gt;14&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git commits&lt;/td&gt;
&lt;td&gt;&lt;code&gt;65&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1,028&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database migrations&lt;/td&gt;
&lt;td&gt;&lt;code&gt;236&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verticals shipped&lt;/td&gt;
&lt;td&gt;&lt;code&gt;5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edge cases fixed at 2 AM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;7&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cups of coffee&lt;/td&gt;
&lt;td&gt;&lt;em&gt;[redacted]&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── Market Orders
├── Pharmacy Orders
├── Laundry Orders
├── Restaurant Orders
├── Parcel Delivery
├── Payment Recovery
├── Admin Dashboard
├── Spin Campaigns
├── Promo Engine
└── Notification System
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One backend. Five verticals. One codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The backend is live. The API is stable. The foundation is solid.&lt;/p&gt;

&lt;p&gt;But this is just the beginning.&lt;/p&gt;

&lt;p&gt;Building the infrastructure was the hard part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making it scale?&lt;/strong&gt; That's the interesting part.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you're building something similar and want to talk shop, or if you just want to feel better about your own deadline, hit me up.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; — If anyone asks, yes, I did rewrite the same migration three times. No, I will not elaborate.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The 4 PM Rush: A Day Inside a Growing Food Tech Platform</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Sun, 28 Jun 2026 21:13:58 +0000</pubDate>
      <link>https://dev.to/ogeobubu/the-4-pm-rush-a-day-inside-a-growing-food-tech-platform-141n</link>
      <guid>https://dev.to/ogeobubu/the-4-pm-rush-a-day-inside-a-growing-food-tech-platform-141n</guid>
      <description>&lt;p&gt;&lt;em&gt;What happens when thousands of people decide they're hungry at the exact same time?&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Quiet Before the Storm
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;10:00 PM.&lt;/strong&gt; The numbers are gentle tonight. One hundred eighty-nine requests trickle in. Someone in Lagos is ordering late-night suya. A rider in Ibadan is wrapping up his last delivery. In Bangladesh, someone is just discovering us for the first time.&lt;/p&gt;

&lt;p&gt;By &lt;strong&gt;11:00 PM&lt;/strong&gt;, things get quiet. Just 8 requests. The platform takes a breath.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2:00 AM.&lt;/strong&gt; A mystery. &lt;strong&gt;151 requests&lt;/strong&gt; spike out of nowhere. We check the logs. Nothing unusual. Just a group of night owls ordering food, maybe shift workers, maybe students pulling an all-nighter. The beauty of a platform is we're always on, always ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7:00 AM.&lt;/strong&gt; Good morning, Nigeria. Fifty-five requests. People waking up, checking their wallets, planning their day. The coffee hasn't even brewed yet, but the platform is already humming.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Morning Rush
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;9:00 AM.&lt;/strong&gt; &lt;strong&gt;315 requests.&lt;/strong&gt; The workday begins. Offices buzz with conversations about lunch plans. If someone searches "foodmat site" for the third time this week, they're getting closer to finding us. A corporate client logs in to set up their employee meal program for the first time.&lt;/p&gt;

&lt;p&gt;By &lt;strong&gt;10:00 AM&lt;/strong&gt;, the traffic settles to 50 requests. A calm before the real storm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11:00 AM.&lt;/strong&gt; &lt;strong&gt;173 requests.&lt;/strong&gt; The hunger is building. People are making decisions about what to eat, where to order, and which vendor to choose. Our World Cup campaign notifications ping. Someone shares their referral code. The viral loop begins.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Lunch Explosion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;12:00 PM.&lt;/strong&gt; &lt;strong&gt;321 requests.&lt;/strong&gt; It's happening. The platform comes alive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1:00 PM.&lt;/strong&gt; &lt;strong&gt;339 requests.&lt;/strong&gt; The peak is building. Our servers are handling it smoothly. This is where the magic happens when thousands of people decide they're hungry at the exact same time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2:00 PM.&lt;/strong&gt; &lt;strong&gt;289 requests.&lt;/strong&gt; Still going strong. Vendor dashboards refresh. Riders accept orders. Laundry bookings come in alongside food deliveries. If someone cancels an order with a reason, we take note. Every interaction teaches us something.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3:00 PM.&lt;/strong&gt; &lt;strong&gt;528 requests.&lt;/strong&gt; The highest peak of the day. It's the post-lunch rush, the afternoon cravings. People are ordering snacks and planning their evening meals. Our cache hits 53%, serving data from memory instead of the origin. The platform is fast, responsive, and alive.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Evening Surge
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;4:00 PM.&lt;/strong&gt; &lt;strong&gt;214 requests.&lt;/strong&gt; A slight dip, but don't be fooled. Dinner planning begins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5:00 PM.&lt;/strong&gt; &lt;strong&gt;254 requests.&lt;/strong&gt; The commute starts. People are ordering before they leave work, so dinner arrives when they get home. A parent orders for the family. A young professional uses their corporate meal benefit for the first time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6:00 PM.&lt;/strong&gt; &lt;strong&gt;392 requests.&lt;/strong&gt; This is the golden hour. The dinner rush. Our cache hit rate jumps to 77%. The platform is serving data fast, efficiently, and reliably. Over 11 MB of data flows through menu images, order histories, and rider tracking updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7:00 PM.&lt;/strong&gt; &lt;strong&gt;458 requests.&lt;/strong&gt; The dinner peak. This is when the platform truly shines. &lt;strong&gt;32 unique visitors&lt;/strong&gt; are active at this exact moment. They're not just browsing; they're ordering, tracking, and interacting. Our World Cup predictions are rolling in. Referral rewards are being claimed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8:00 PM.&lt;/strong&gt; &lt;strong&gt;213 requests.&lt;/strong&gt; The rush settles. Full bellies. Happy customers. &lt;strong&gt;10 unique visitors&lt;/strong&gt; are still active, maybe checking their order history, maybe planning tomorrow's lunch.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Day in Numbers
&lt;/h2&gt;

&lt;p&gt;Let's zoom out.&lt;/p&gt;

&lt;p&gt;In just 24 hours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Over 4,100 requests&lt;/strong&gt; served&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~350 unique visitors&lt;/strong&gt; found us&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;90 MB of data&lt;/strong&gt; delivered to hungry customers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Peak usage at 3 PM&lt;/strong&gt; with 528 requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong cache performance&lt;/strong&gt; at 90% during peak times&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the numbers only tell half the story.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes This Possible
&lt;/h2&gt;

&lt;p&gt;Behind every request is a story.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A student in Lagos ordering their first laundry pickup&lt;/li&gt;
&lt;li&gt;A vendor in Abuja managing their first corporate meal program&lt;/li&gt;
&lt;li&gt;A rider in Port Harcourt checking their commission earnings&lt;/li&gt;
&lt;li&gt;A user in Bangladesh searching for "foodmat app download" for the first time&lt;/li&gt;
&lt;li&gt;A customer in Australia discovering food delivery from the other side of the world&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We see the queries people search:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"foodmartex"&lt;/strong&gt; — 60% click-through rate. They found us.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"foodmat site"&lt;/strong&gt;—159 impressions. They're looking for us. We're getting there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"iya ruka ile ife"&lt;/strong&gt; — 2 impressions. Someone, somewhere, searching for something meaningful in their native tongue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every search, every click, every order—it's a signal. A sign that we're building something people need.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens Next
&lt;/h2&gt;

&lt;p&gt;The platform grows. The numbers go up. But the goal isn't just bigger numbers.&lt;/p&gt;

&lt;p&gt;It's the &lt;strong&gt;3 PM rush&lt;/strong&gt; becoming &lt;strong&gt;600 requests&lt;/strong&gt;. It's the &lt;strong&gt;cache hit rate&lt;/strong&gt; hitting 95%. It's the &lt;strong&gt;rating&lt;/strong&gt; climbing from 3.67 to 4.5. It's the &lt;strong&gt;monthly active users&lt;/strong&gt; jumping from 180 to 500.&lt;/p&gt;

&lt;p&gt;It's making food delivery faster. Laundry simpler. Make corporate meals more accessible. Make referral rewards more exciting. World Cup predictions more fun.&lt;/p&gt;

&lt;p&gt;It's watching a platform become a community.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Invitation
&lt;/h2&gt;

&lt;p&gt;We don't build this alone. Every user, every vendor, every rider—they're the heartbeat of the platform.&lt;/p&gt;

&lt;p&gt;If you're reading this and you've ever used FoodMartex:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You were part of that &lt;strong&gt;528-request peak at 3 PM&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;3.67 average rating&lt;/strong&gt; helps us get better&lt;/li&gt;
&lt;li&gt;Your referral keeps the platform growing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if you haven't tried us yet?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We're ready. The servers are waiting. The food is hot. The laundry is fresh. The World Cup predictions are open.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Come find us. Search "foodmartex." Be part of the story.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Final Number
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;7:00 PM.&lt;/strong&gt; The day's final peak: &lt;strong&gt;458 requests.&lt;/strong&gt; &lt;strong&gt;32 unique visitors. 85% cache hit rate.&lt;/strong&gt; &lt;strong&gt;10.8 MB of data served.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And somewhere in Nigeria, someone just placed their first order.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The platform lives. The platform grows. The platform serves.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And tomorrow, it starts all over again.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>We Built 6 Major Features in 30 Days (And Only Cried Twice)</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Fri, 26 Jun 2026 16:12:11 +0000</pubDate>
      <link>https://dev.to/ogeobubu/we-built-6-major-features-in-30-days-and-only-cried-twice-184c</link>
      <guid>https://dev.to/ogeobubu/we-built-6-major-features-in-30-days-and-only-cried-twice-184c</guid>
      <description>&lt;p&gt;The rule of thumb in software engineering is simple: &lt;em&gt;under-promise and over-deliver&lt;/em&gt;. This June, we completely ignored that rule.&lt;/p&gt;

&lt;p&gt;Our team looked at the calendar, looked at our product roadmap, and collectively decided that sleep was optional. Over the last 30 days, we didn't just ship updates; we practically rebuilt the marketplace engine while the plane was mid-flight.&lt;/p&gt;

&lt;p&gt;If you've ever wondered what it looks like when a product team operates on pure adrenaline, espresso, and a shared dream, here is a peek behind the curtain of our craziest month yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛑 The Chaos: What We Upended
&lt;/h2&gt;

&lt;p&gt;We didn't just tweak the UI. We introduced entirely new business models into the ecosystem simultaneously.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Pivot to Clean Clothes: We built an entire logistics lifecycle for laundry ordering from scratch. Express options, insurance, custom pickup/delivery windows, and real-time vendor tracking. We went from "food delivery" to "handling your delicate fabrics" in a matter of weeks.&lt;/li&gt;
&lt;li&gt;Chasing the World Cup Hype: Capitalizing on cultural moments is hard. Capitalizing on them with software is harder. We launched a fully interactive World Cup prediction engine with live leaderboards and referral loops to capture the tournament energy.&lt;/li&gt;
&lt;li&gt;The B2B Leap: We quietly opened a massive new revenue stream by launching our Corporate Meal Program, allowing companies to manage employee meal benefits directly through us.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oh, and because our engineers apparently don't like weekends, we also revamped bulk ordering, overhauled our SMS campaign architecture, and added pending payment triggers.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ The Hardest Problems We Had to Solve
&lt;/h2&gt;

&lt;p&gt;The "Happy Path" in software is a myth. Here are the realities we ran into that didn't make it into the official corporate slides:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The State Machine Nightmare&lt;/strong&gt;&lt;br&gt;
When you introduce laundry and bulk ordering, your standard "order lifecycle" breaks. A pizza goes from oven to rider to door. A laundry bag goes from customer to rider to vendor, sits for 48 hours, undergoes a status change (washing/drying), goes back to a different rider, and returns home. Designing a state machine that handles this without swallowing orders was an absolute brain-melter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Scaling Dilemma&lt;/strong&gt;&lt;br&gt;
Launching a viral prediction campaign means sudden, unpredictable spikes in database reads and writes every time a whistle blows. We spent a lot of time optimizing leaderboards and caching queries so an influx of football fans wouldn't accidentally bring down our checkout funnel.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏆 The Big Wins (and Small Joys)
&lt;/h2&gt;

&lt;p&gt;Despite the chaos, the ship didn't just float; it soared.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6 major features were&lt;/strong&gt; pushed to production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10+ Core UX Enhancements&lt;/strong&gt; (including some highly satisfying balloon animations on the dashboard, because software should be fun).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero catastrophic downtime. What this actually means:&lt;/strong&gt; Our customers have three times as many ways to use the platform, our corporate partners have an entirely new perk for their teams, and our vendors are unlocking revenue streams they didn't have on June 1st.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 What’s Next (From the Safety of Staging)
&lt;/h2&gt;

&lt;p&gt;We aren't done. Right now, our staging branch is heavy with code that's currently undergoing final stress testing. Automated rider matching for those complex laundry routes is around the corner, alongside advanced subscription order management.&lt;/p&gt;

&lt;p&gt;To the engineering and product teams who lived on GitHub and Slack this month: thank you.&lt;/p&gt;

&lt;p&gt;Now, if you'll excuse me, I'm going to go close 47 open browser tabs and sleep for a week. Or at least until Monday. 🚀&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>SMS in Nigeria Broke at 2 AM - So We Built a Multi-Channel Messaging Architecture That Routes Around Failure</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Thu, 25 Jun 2026 21:29:17 +0000</pubDate>
      <link>https://dev.to/ogeobubu/sms-in-nigeria-broke-at-2-am-so-we-built-a-multi-channel-messaging-architecture-that-routes-4enj</link>
      <guid>https://dev.to/ogeobubu/sms-in-nigeria-broke-at-2-am-so-we-built-a-multi-channel-messaging-architecture-that-routes-4enj</guid>
      <description>&lt;p&gt;The Problem&lt;br&gt;
In Nigeria, SMS delivery is not guaranteed. Carriers drop messages, gateways go down, and your carefully crafted transactional alert silently vanishes into the void.&lt;/p&gt;

&lt;p&gt;When you run a food delivery marketplace where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A vendor needs to know about a new order within seconds&lt;/li&gt;
&lt;li&gt;A rider needs pickup instructions to arrive on time&lt;/li&gt;
&lt;li&gt;A customer's OTP must reach them before they abandon signup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…SMS reliability isn't nice-to-have. It's table stakes.&lt;/p&gt;

&lt;p&gt;We started with one provider. We learned the hard way.&lt;/p&gt;

&lt;p&gt;The Architecture&lt;br&gt;
We now run two independent messaging services, each with different strengths:&lt;br&gt;
Transactional SMS (primary)&lt;br&gt;
&amp;nbsp;→ KudiSMS service with error classification&lt;br&gt;
&amp;nbsp;→ Critical failures → developer alerts&lt;br&gt;
&amp;nbsp;→ Structured validation + phone number parsing&lt;br&gt;
Escalation &amp;amp; multi-channel (secondary)&lt;br&gt;
&amp;nbsp;→ Termii service (SMS + WhatsApp + Voice)&lt;br&gt;
&amp;nbsp;→ Configurable per-channel toggles&lt;br&gt;
&amp;nbsp;→ Used for time-sensitive vendor/admin alerts&lt;/p&gt;

&lt;p&gt;How they work together&lt;br&gt;
KudiSMS handles the bulk of transactional traffic. Every order confirmation, OTP, and status update flows through a dedicated notification channel backed by KudiSMS. The service has explicit error classification, retryable failures vs critical failures, and logs them differently. Critical failures trigger alerts so someone knows immediately.&lt;/p&gt;

&lt;p&gt;Termii covers the gaps. It supports SMS, WhatsApp, and Voice calls through a single API. For order escalation, a job dispatches alerts through multiple channels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SMS to the vendor's phone (first attempt)&lt;/li&gt;
&lt;li&gt;WhatsApp if the vendor's preferred channel is configured&lt;/li&gt;
&lt;li&gt;Voice call as a last resort for urgent orders&lt;/li&gt;
&lt;li&gt;Admin escalation - if the vendor hasn't responded after N minutes, admins get WhatsApp + Voice alerts too&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each channel can be toggled independently via configuration. This means we can disable SMS for maintenance without affecting WhatsApp or Voice delivery.&lt;/p&gt;

&lt;p&gt;The subtle design decisions that matter&lt;br&gt;
Phone number normalisation is separate per service. Nigerian numbers come in different formats (080…, +23480…, 23480…). Each service normalises independently; if one has a bug, the other still works.&lt;br&gt;
Validation fails fast, not silently. KudiSMS validates configuration on every request and throws immediately if credentials are missing. A misconfigured service fails loudly rather than quietly dropping messages.&lt;br&gt;
Errors are classified by severity. We distinguish between retryable failures (network blips) and critical failures (auth issues, invalid responses). Only critical errors wake someone up. This prevents alert fatigue from transient network hiccups.&lt;/p&gt;

&lt;p&gt;Escalation has state awareness. The order escalation job checks whether the order has already been accepted before sending an alert. It won't spam a vendor who already picked up the phone and accepted the order.&lt;br&gt;
What this looks like in production&lt;/p&gt;

&lt;p&gt;A vendor gets a new order alert:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;KudiSMS sends the transactional SMS → delivered, great&lt;/li&gt;
&lt;li&gt;If KudiSMS fails → the error is classified and logged, and a developer gets an alert&lt;/li&gt;
&lt;li&gt;Meanwhile, the escalation timer starts ticking&lt;/li&gt;
&lt;li&gt;After N minutes without acceptance → Termii sends a WhatsApp message&lt;/li&gt;
&lt;li&gt;After N more minutes → Termii makes a voice call&lt;/li&gt;
&lt;li&gt;After N more minutes → admins get notified via WhatsApp and voice
The system doesn't just fail over. It escalates through increasingly intrusive channels.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lessons Learned&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redundancy means independent failure modes. Two SMS providers using the same carrier or the same API pattern will fail together. Our providers have different infrastructure.&lt;/li&gt;
&lt;li&gt;Multi-channel is better than multi-provider. WhatsApp and Voice don't compete with SMS carriers. Adding channels gives you genuinely orthogonal delivery paths.&lt;/li&gt;
&lt;li&gt;Alert fatigue kills incident response. We spent as much time designing when not to alert as we did building the alerting itself. Classify errors by severity and keep the noisy ones quiet.&lt;/li&gt;
&lt;li&gt;Test the fallback. If you've never seen your secondary provider actually handle traffic, you don't have a fallback. We periodically route test traffic through Termii to validate the path.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Frontend Engineer Will Not Be Replaced by AI</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Tue, 23 Jun 2026 18:53:03 +0000</pubDate>
      <link>https://dev.to/ogeobubu/the-frontend-engineer-will-not-be-replaced-by-ai-3k2l</link>
      <guid>https://dev.to/ogeobubu/the-frontend-engineer-will-not-be-replaced-by-ai-3k2l</guid>
      <description>&lt;h2&gt;
  
  
  Prologue: The 2 AM Deployment
&lt;/h2&gt;

&lt;p&gt;It was 2:17 AM. I was staring at a terminal, sweat beading on my forehead, waiting for a deployment to finish. The feature? A simple discount coupon flow. Three forms, a validation step, and a confirmation screen. Nothing special by any standard.&lt;/p&gt;

&lt;p&gt;But this was a grocery platform. And in the morning, thousands of people would wake up, open this app, and try to feed their families. If the coupon didn't apply correctly, someone would overpay. Someone would be frustrated. Someone might not be able to afford what they needed that week.&lt;/p&gt;

&lt;p&gt;The deployment succeeded. I sat back, exhaled, and realized something I've never forgotten: &lt;strong&gt;the code I write has weight.&lt;/strong&gt; It's not abstract. It's not just syntax. It's the difference between someone eating dinner and someone going hungry. Between a business staying open and closing. Between a delivery arriving on time or a child missing their school lunch.&lt;/p&gt;

&lt;p&gt;Five years later, I've shipped hundreds of features. Some small ones. Some that moved millions in revenue. But that 2 AM deployment, that moment of realizing my code mattered, is the fire that's kept me going.&lt;/p&gt;

&lt;p&gt;And now I'm watching the news tell an entire generation of aspiring engineers, "AI is coming for your job. Don't bother learning to code. It's over."*&lt;/p&gt;

&lt;p&gt;They're wrong. Profoundly, dangerously, &lt;strong&gt;completely&lt;/strong&gt; wrong.&lt;/p&gt;

&lt;p&gt;Let me tell you why.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part I: The Fear, The Headlines, and The Misunderstanding
&lt;/h2&gt;

&lt;p&gt;Walk into any tech X (formerly Twitter) space, open any LinkedIn feed, scroll through any YouTube comment section, and you'll find the same narrative:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Cursor wrote this entire app."&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"V0 generates production-ready UI in seconds."&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Why hire a frontend engineer when Claude can build your entire landing page?"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Frontend is dead."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I get it. It's scary. When I first saw an AI generate a functional React component from a sentence, my stomach dropped. I'd spent years learning to write that exact thing. Was it all for nothing?&lt;/p&gt;

&lt;p&gt;Here's what I learned: &lt;strong&gt;AI generates output. It does not generate ownership.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Anyone can prompt. Almost no one can &lt;strong&gt;ship.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A generated component is just a sketch. It has no context. It doesn't know why the button is orange instead of blue. It doesn't know that the loading state needs to appear for exactly 300 milliseconds, not 200, not 400, because user testing showed that 300ms is the sweet spot between perceived speed and perceived effort.&lt;/p&gt;

&lt;p&gt;It doesn't know that the disabled submit button should show a tooltip explaining &lt;em&gt;why&lt;/em&gt; it's disabled. It doesn't know that the error message should say, "This email is already registered. Did you forget your password? Let us help you reset it" instead of "Error: duplicate entry."&lt;/p&gt;

&lt;p&gt;AI doesn't know these things because &lt;strong&gt;these things are not in the training data.&lt;/strong&gt; They're in the trenches. They're in the user testing sessions. They're in the 2 AM realizations. They're in the conversations with product managers and customer support agents and actual human beings who use what you build.&lt;/p&gt;

&lt;p&gt;The fear is real. The headline is wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part II: What Five Years of Shipping Actually Taught Me
&lt;/h2&gt;

&lt;p&gt;I've spent over five years building full-stack applications. I've built e-commerce platforms that connect thousands of vendors with hundreds of thousands of customers. I've built real-time logistics systems that track deliveries down to the exact GPS coordinate. I've built promotional campaign engines that reward customers for predictions and referrals. I've built admin dashboards that give businesses superpowers, the ability to see, understand, and act on their operations in ways they never could before.&lt;/p&gt;

&lt;p&gt;And here's what I've learned about what matters:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Syntax is the cheapest thing you own
&lt;/h3&gt;

&lt;p&gt;When I started, I thought knowing JavaScript deeply was the secret sauce. Then I thought React. Then TypeScript. Then the latest state management library. Then the hottest CSS framework.&lt;/p&gt;

&lt;p&gt;Every six months, the "essential" tool changed.&lt;/p&gt;

&lt;p&gt;If I'd tied my identity to syntax, I'd have been replaced ten times over. Instead, I tied my identity to something AI can't touch: &lt;strong&gt;the ability to translate human needs into technical solutions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's what I mean: a customer doesn't care if you used Redux or Zustand or signals or context. They care that when they tap "Checkout," their order goes through. They care that when they're confused, the interface guides them. They care that when something breaks, the system doesn't punish them.&lt;/p&gt;

&lt;p&gt;A business owner doesn't care if your admin panel is built with React or Vue or Svelte or vanilla HTML. They care that they can find the data they need in three clicks instead of thirty. They care that their team can onboard in an hour instead of a week.&lt;/p&gt;

&lt;p&gt;Your framework is temporary. Your ability to understand what humans need? That's permanent.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The hard problems are not technical
&lt;/h3&gt;

&lt;p&gt;Five years ago, I thought the hardest problems were technical. Architecture. Performance. Edge cases.&lt;/p&gt;

&lt;p&gt;Today, I know the hardest problems are human:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do you tell a user their order failed without making them panic?&lt;/li&gt;
&lt;li&gt;How do you build a dashboard that a non-technical admin can use confidently?&lt;/li&gt;
&lt;li&gt;How do you design a referral flow that feels like a gift, not a transaction?&lt;/li&gt;
&lt;li&gt;How do you handle a vendor who accidentally set their inventory to zero and needs to fix it fast?&lt;/li&gt;
&lt;li&gt;How do you show a customer their delivery is delayed without making them angry at the rider?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every single one of these is a frontend problem. Every single one requires empathy. Every single one requires understanding context, emotion, and real-world consequences.&lt;/p&gt;

&lt;p&gt;AI can generate the form. It cannot generate trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The best code is code you don't write
&lt;/h3&gt;

&lt;p&gt;This is the paradox of experience: the more you know, the less you build.&lt;/p&gt;

&lt;p&gt;Junior me would write 500 lines to solve a problem. Today, I write 50. Not because I'm lazier, but because I've learned that every line of code is a liability. Every component is something that needs to be maintained, tested, and understood by the next person.&lt;/p&gt;

&lt;p&gt;The most valuable skill I've developed isn't writing code faster. It's knowing &lt;strong&gt;what not to build.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do we need a custom date picker, or should we use the library we already have?&lt;/li&gt;
&lt;li&gt;Does this need to be a new page, or can it be a modal?&lt;/li&gt;
&lt;li&gt;Do we need real-time updates, or is polling every 30 seconds good enough?&lt;/li&gt;
&lt;li&gt;Does this feature need to exist at all, or are we solving a problem nobody has?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI will never ask those questions. It will generate whatever you prompt. It has no judgment. It has no experience of maintaining a codebase for three years and knowing exactly which dependencies have caused problems and which patterns lead to bugs.&lt;/p&gt;

&lt;p&gt;That judgment, the hard-earned wisdom of having shipped, failed, fixed, and shipped again, is irreplaceable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part III: A Tour Through Real Features I've Built
&lt;/h2&gt;

&lt;p&gt;Let me walk you through a few real features I've built. Not to impress you, but to show you the difference between what AI generates and what an engineer &lt;em&gt;actually&lt;/em&gt; does.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Promotional Campaign Engine
&lt;/h3&gt;

&lt;p&gt;A client wanted a promotional campaign: customers predict match scores and refer friends. Correct predictions and successful referrals earn rewards.&lt;/p&gt;

&lt;p&gt;An AI could generate the CRUD. The forms. The tables.&lt;/p&gt;

&lt;p&gt;But here's what the AI wouldn't know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The admin needs six different tabs&lt;/strong&gt; because they check different data at different frequencies. Settings once. Matches daily. Winners hourly. Putting them all on one page would be chaos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The prediction flow needs to show teams by name&lt;/strong&gt; — but the database stores them inconsistently. Sometimes &lt;code&gt;home_team&lt;/code&gt;. Sometimes &lt;code&gt;home_team_name&lt;/code&gt;. Sometimes &lt;code&gt;home&lt;/code&gt;. The AI would pick one. I had to write code that handles all three, because the backend team was still migrating. That's not syntax. That's context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The referral progress bar needs to handle multiple data sources.&lt;/strong&gt; Progress could come from a dedicated endpoint, or it could be calculated from the entries list, or it could come from a cached value. The AI would assume one shape. Real APIs change. I had to write code that gracefully degrades through every possible source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The status badge needs to be green for "correct" but also for "qualified" and "rewarded" and "won."&lt;/strong&gt; And orange for "pending" but not for "incorrect." And red for "incorrect" but also for "lost" and "expired." These aren't technical decisions. They're &lt;em&gt;human&lt;/em&gt; decisions, what does each status &lt;em&gt;mean&lt;/em&gt; to the person reading it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI generates output. Engineers generate meaning.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Real-Time Logistics Dashboard
&lt;/h3&gt;

&lt;p&gt;Trucks move across a city. Customers refresh their browsers, waiting for their groceries. The status needs to update without a page reload. The driver's location needs to show on a map. The estimated arrival needs to be accurate.&lt;/p&gt;

&lt;p&gt;An AI could wire up WebSockets and drop a marker on a map.&lt;/p&gt;

&lt;p&gt;But here's what the AI wouldn't know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The customer is anxious.&lt;/strong&gt; Every second the spinner shows, their stress increases. So the UI needs to show &lt;em&gt;something&lt;/em&gt;, not just a loading state, but a meaningful status: "Your rider is at the store" → "Your rider is packing your order" → "Your rider is on the way" → "Your rider is 3 minutes away." Each step is a small reassurance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rider's GPS updates every 5 seconds, but we should only re-render the map every 15 seconds.&lt;/strong&gt; Because re-rendering every 5 seconds drains battery, creates visual noise, and makes the UI feel jittery. The AI doesn't know about battery. It doesn't know about visual comfort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When the rider is late, the customer's frustration needs to be directed somewhere productive.&lt;/strong&gt; So we show "Your rider is delayed by traffic," not "ETA updated." We frame the delay as external, not as a system failure. This is UX psychology, not code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The admin needs to see all active deliveries on one screen&lt;/strong&gt; with color-coded status—green for on-time, yellow for borderline, and red for late. A simple dashboard that takes 10 minutes to build but saves hours of phone calls from anxious customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is in any training data. It's all learned from watching real users struggle with real interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Vendor Onboarding Flow
&lt;/h3&gt;

&lt;p&gt;A marketplace lives and dies by its supply side. If vendors can't onboard quickly, the catalog stays empty. If the catalog is empty, customers don't come.&lt;/p&gt;

&lt;p&gt;The onboarding form needed to collect business details, bank information, food safety certification, delivery zones, opening hours, and menu items. That's about 50 fields spread across 6 steps.&lt;/p&gt;

&lt;p&gt;An AI could generate a multi-step form.&lt;/p&gt;

&lt;p&gt;But here's what the AI wouldn't know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vendors are busy.&lt;/strong&gt; They're running a business. The form needs to save progress at every step so they can come back later. The AI wouldn't think about partial saves because it's never been interrupted by a customer walking into a store while filling out a form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bank details step is stressful.&lt;/strong&gt; People get nervous entering financial information. The UI needs to look secure, padlock icons, reassuring copy, and clear explanations of why each field is needed. The AI wouldn't know that the visual design of a form field can change someone's emotional state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The certification upload step has a specific failure mode:&lt;/strong&gt; vendors take photos of documents on their phones, and those photos are often sideways, blurry, or too large. The frontend needs to handle orientation correction, compression, and clear error messages about quality requirements. The AI wouldn't know that 30% of users would upload a sideways image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When a vendor gets stuck, they don't email support.&lt;/strong&gt; They just leave. So every step needs inline validation that explains &lt;em&gt;what's wrong and how to fix it,&lt;/em&gt; not just "invalid input." The AI would generate generic error messages. Real vendors need specific guidance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Admin Dashboard
&lt;/h3&gt;

&lt;p&gt;The most complex thing I've ever built isn't a customer-facing feature. It's the tool that runs behind the scenes.&lt;/p&gt;

&lt;p&gt;Admin dashboards are where business operations happen. They're where customer support agents resolve issues. Where finance teams track payments. Where operations managers monitor deliveries. Where marketing teams launch campaigns.&lt;/p&gt;

&lt;p&gt;An AI could generate a table with filters.&lt;/p&gt;

&lt;p&gt;But here's what the AI wouldn't know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every admin user has a different workflow.&lt;/strong&gt; The support agent needs to search by customer name. The finance person needs to filter by date range and payment status. The operations manager needs to see everything sorted by urgency. One dashboard needs to serve multiple mental models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admins are under pressure.&lt;/strong&gt; They're handling customer complaints, vendor disputes, and delivery delays. The UI needs to be fast, not just technically fast, but cognitively fast. Every extra click, every confusing label, every slow filter adds to their stress. The AI wouldn't know that an extra 2 seconds of load time during peak hours means 50 fewer tickets resolved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The most important data needs to be most visible.&lt;/strong&gt; Not everything in the dashboard is equally important. The orders that are 2 hours overdue matter more than the ones that were just placed. The vendors who haven't been paid in 3 weeks matter more than the ones who were paid yesterday. The AI would treat all data equally. Real dashboards need to tell a story about what needs attention right now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admins make mistakes.&lt;/strong&gt; They accidentally delete records, change values they shouldn't, and take actions they can't undo. Every destructive action needs confirmation, undo capability, or, at minimum, a clear audit trail. The AI wouldn't think about undo because it's never accidentally deleted a production record at 6 PM on a Friday.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part IV: The Skills That Matter Now (And Will Matter More)
&lt;/h2&gt;

&lt;p&gt;If you're an aspiring frontend engineer reading this, you're probably wondering, "What should I actually learn?"*&lt;/p&gt;

&lt;p&gt;Let me be specific.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Skills AI Cannot Amplify (That You Must Own)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Problem decomposition
&lt;/h4&gt;

&lt;p&gt;Before you write a single line of code, you need to understand the problem. Not the technical problem, the human problem.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"The checkout page is too slow"&lt;/em&gt; is not a problem statement. It's a symptom.&lt;/p&gt;

&lt;p&gt;The real problem might be "Users abandon the checkout when they see unexpected shipping costs."*&lt;/p&gt;

&lt;p&gt;The real solution might not be a faster checkout. It might be showing shipping costs earlier. It might be free shipping over a certain amount. It might be a progress bar that shows "add ₦1,000 more for free delivery."&lt;/p&gt;

&lt;p&gt;AI can optimize the checkout. It cannot diagnose the real problem.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Communication across disciplines
&lt;/h4&gt;

&lt;p&gt;Every feature you build requires translating between worlds. The business team speaks in revenue and conversion. The design team speaks in flows and pixels. The backend team speaks in APIs and data models. The customer support team speaks in tickets and pain points.&lt;/p&gt;

&lt;p&gt;Your job as a frontend engineer is to &lt;strong&gt;speak all these languages&lt;/strong&gt; and translate between them. You're the bridge. Without you, each team operates in isolation. With you, the entire company moves in one direction.&lt;/p&gt;

&lt;p&gt;AI cannot attend a meeting, understand what a product manager is really asking for, and translate that into technical requirements. It can't read between the lines of "can we make this pop?" and understand that the PM means "the conversion rate on this button is too low, and we need to make it more prominent."&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Judgment about tradeoffs
&lt;/h4&gt;

&lt;p&gt;Every engineering decision is a tradeoff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Speed vs. quality&lt;/li&gt;
&lt;li&gt;Custom vs. off-the-shelf&lt;/li&gt;
&lt;li&gt;Build vs. buy&lt;/li&gt;
&lt;li&gt;Ship now vs. ship right&lt;/li&gt;
&lt;li&gt;Optimize for the 90% case or handle the 10% edge case&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no objectively correct answer. Every decision depends on context: the team size, the business stage, the user base, the technical debt already accumulated, the timeline, and the budget.&lt;/p&gt;

&lt;p&gt;AI cannot judge tradeoffs. It can only optimize for what you tell it to optimize for. And most of the time, you don't know what to optimize for until you've made the wrong choice a few times and learned from it.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Ownership and accountability
&lt;/h4&gt;

&lt;p&gt;This is the biggest one.&lt;/p&gt;

&lt;p&gt;When AI generates code and it breaks, who fixes it? When a bug slips into production at 11 PM on a Friday, who's debugging? When a customer's order fails because of a frontend validation edge case, who's explaining to the support team what happened?&lt;/p&gt;

&lt;p&gt;You. Every time.&lt;/p&gt;

&lt;p&gt;Ownership means this is my code, my users, and my responsibility. It means staying up late to fix the bug. It means apologizing when you break something. It means learning from every failure and making sure it doesn't happen again.&lt;/p&gt;

&lt;p&gt;AI has no skin in the game. It doesn't get paged at 3 AM. It doesn't have to explain to a CEO why revenue dropped. It doesn't care.&lt;/p&gt;

&lt;p&gt;You do. And that caring, that deep, exhausting, beautiful commitment to your users, is the most valuable thing you bring to any team.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Skills AI Can Amplify (That You Should Still Master)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Speed of iteration
&lt;/h4&gt;

&lt;p&gt;AI can generate a prototype in seconds. Use this. Generate the first draft, then apply your judgment to refine it. You get to skip the boring scaffolding and focus on what matters: the details, the edge cases, the polish.&lt;/p&gt;

&lt;p&gt;But here's the trap: if you can't judge whether the AI output is good, you're stuck. You need to know enough to be a critic, not just a prompter.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Code generation for known patterns
&lt;/h4&gt;

&lt;p&gt;Any pattern you've implemented ten times, forms, tables, CRUD endpoints, or auth flows, AI can generate competently. Use it. Free up your brain for the novel problems.&lt;/p&gt;

&lt;p&gt;But if you've never implemented these patterns yourself, you won't know when the AI generates something subtly wrong. The worst-case scenario isn't obvious bugs. It's &lt;em&gt;subtle&lt;/em&gt; bugs that look correct but fail under specific conditions.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Exploration and learning
&lt;/h4&gt;

&lt;p&gt;AI is phenomenal for answering "how do I do X in Y framework?" It compresses hours of documentation scrolling into seconds. Use it to learn faster.&lt;/p&gt;

&lt;p&gt;But don't confuse &lt;em&gt;knowing how to prompt for an answer&lt;/em&gt; with &lt;em&gt;understanding the answer.&lt;/em&gt; Real understanding comes from implementing, debugging, and shipping.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part V: A Letter to the Aspiring Engineer
&lt;/h2&gt;

&lt;p&gt;Dear future engineer,&lt;/p&gt;

&lt;p&gt;I know it's scary out there. I know the headlines are loud. I know every day brings a new announcement about AI replacing some part of our craft.&lt;/p&gt;

&lt;p&gt;But here's the truth that nobody's saying loudly enough:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The demand for people who can ship real products, understand real users, and solve real problems is higher than it has ever been.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, the entry barrier has shifted. The junior roles that were about "write this component" are disappearing. But the roles that require judgment, empathy, and ownership are growing. They pay more. They matter more. And they're harder to fill.&lt;/p&gt;

&lt;p&gt;Here's my advice:&lt;/p&gt;

&lt;h3&gt;
  
  
  Build things that matter.
&lt;/h3&gt;

&lt;p&gt;Not another todo app. Not another weather widget. Build something that a real human being would use. A tool for a local business. A helper for a family member. A dashboard for a friend's startup.&lt;/p&gt;

&lt;p&gt;When you build for real users, you learn things no tutorial can teach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You learn that your beautiful architecture doesn't matter if the page takes 3 seconds to load on a phone&lt;/li&gt;
&lt;li&gt;You learn that your clever CSS animation makes some users nauseous&lt;/li&gt;
&lt;li&gt;You learn that what you thought was clear is confusing to everyone else&lt;/li&gt;
&lt;li&gt;You learn that shipping imperfect code today is better than shipping perfect code next month&lt;/li&gt;
&lt;li&gt;You learn that the joy of building isn't in the technology, but in watching someone use what you made and seeing their face light up&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Read code. Read a lot of it.
&lt;/h3&gt;

&lt;p&gt;Read the code of open-source projects you admire. Read your dependencies. Read the pull requests of senior engineers. The more code you read, the more patterns you absorb. And the more patterns you absorb, the better your judgment becomes.&lt;/p&gt;

&lt;p&gt;AI reads all the code. But it doesn't &lt;em&gt;understand&lt;/em&gt; any of it. It doesn't know which patterns lead to maintainable codebases and which lead to spaghetti. It doesn't know which abstractions are worth the complexity and which aren't.&lt;/p&gt;

&lt;p&gt;That understanding only comes from reading, writing, and shipping code over years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find a mentor (or be one).
&lt;/h3&gt;

&lt;p&gt;The best engineers I know all have one thing in common: they learned from someone more experienced. Not from a bootcamp. Not from a course. From someone who sat next to them, looked at their code, and said, "Here's a better way."&lt;/p&gt;

&lt;p&gt;If you're early in your career, find someone who's been shipping for a decade and ask them questions. Most senior engineers love mentoring. We remember what it was like to feel lost, and we want to help.&lt;/p&gt;

&lt;p&gt;If you're experienced, mentor someone. Teaching forces you to articulate what you know, which deepens your own understanding. And watching someone grow is one of the most satisfying experiences in this field.&lt;/p&gt;

&lt;h3&gt;
  
  
  Embrace the boring.
&lt;/h3&gt;

&lt;p&gt;The hottest framework, the newest pattern, the most elegant architecture, none of it matters if the product doesn't work.&lt;/p&gt;

&lt;p&gt;The best code I've written is boring code. Simple components. Clear data flows. Predictable patterns. Nothing clever. Nothing impressive. Just reliable, maintainable, understandable code that does what it's supposed to do.&lt;/p&gt;

&lt;p&gt;AI loves clever code. AI generates interesting solutions. But interesting is not what users need. They need reliability. They need boring. They need code that works at 2 AM when their order depends on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't compete with AI. Use it.
&lt;/h3&gt;

&lt;p&gt;The worst strategy is to pretend AI doesn't exist. The second-worst strategy is to let AI do all your thinking.&lt;/p&gt;

&lt;p&gt;The best strategy is AI handling the boilerplate. You handle the meaning.&lt;/p&gt;

&lt;p&gt;Use AI to generate your tests. Use it to catch your edge cases. Use it to explore alternative approaches. But never let it make the final judgment call. That's yours. That's always been yours.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part VI: Why Frontend Matters More Than Ever
&lt;/h2&gt;

&lt;p&gt;Here's something nobody talks about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The frontend is where value is delivered.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The backend is where data lives. The database is where information is stored. The APIs are where logic executes. But none of that matters if a human being can't access it.&lt;/p&gt;

&lt;p&gt;Every dollar your company makes passes through the frontend. Every customer interaction happens on the frontend. Every time someone chooses your product over a competitor's, they're choosing your frontend experience.&lt;/p&gt;

&lt;p&gt;The frontend is not "just UI." It's the &lt;strong&gt;moment of truth&lt;/strong&gt;, the place where all the backend complexity, all the business logic, all the data processing, and all the engineering effort become real for a human being.&lt;/p&gt;

&lt;p&gt;If the frontend is confusing, the product is confusing. If the frontend is slow, the product is slow. If the frontend is untrustworthy, the product is untrustworthy.&lt;/p&gt;

&lt;p&gt;The frontend engineer is not a "UI developer." You are the &lt;strong&gt;steward of the user's trust.&lt;/strong&gt; Every pixel, every animation, every micro-interaction, every loading state, and every error message, they all either build trust or erode it.&lt;/p&gt;

&lt;p&gt;AI can generate pixels. It cannot generate trust.&lt;/p&gt;

&lt;p&gt;Trust is earned through consistency. Through reliability. Through understanding what your users fear and addressing it before they have to ask. Through shipping features that make your users' lives measurably better.&lt;/p&gt;

&lt;p&gt;That's not a technical challenge.&lt;/p&gt;

&lt;p&gt;That's a human one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part VII: The Architecture of Empathy
&lt;/h2&gt;

&lt;p&gt;Let me share something I've never written down before.&lt;/p&gt;

&lt;p&gt;There's a moment in every project where the code becomes invisible. Where you stop thinking about React components and API calls and start thinking about the human being on the other end of the connection.&lt;/p&gt;

&lt;p&gt;It usually happens around 3 AM, after you've fixed the same bug three times.&lt;/p&gt;

&lt;p&gt;You realize that the bug wasn't technical. It was emotional.&lt;/p&gt;

&lt;p&gt;The user wasn't confused because the UI was unclear. They were confused because they were stressed, distracted, and in a hurry, and the interface demanded more attention than they had to give.&lt;/p&gt;

&lt;p&gt;The solution wasn't a better error message. It was removing the need for the error message entirely.&lt;/p&gt;

&lt;p&gt;This is what I call &lt;strong&gt;the architecture of empathy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's the realization that every interface is a conversation. Every button is a question. Every confirmation dialog is a reassurance. Every loading state is a promise.&lt;/p&gt;

&lt;p&gt;And like any good conversation, the best interfaces are the ones where the other person feels understood.&lt;/p&gt;

&lt;p&gt;Here's how you build with empathy:&lt;/p&gt;

&lt;h3&gt;
  
  
  Know who you're building for
&lt;/h3&gt;

&lt;p&gt;Not "users." Not "customers." Specific people with specific contexts.&lt;/p&gt;

&lt;p&gt;The cashier is processing orders at 7 PM after a 10-hour shift. The single parent ordering groceries while holding a toddler. The admin who's been doing data entry for 4 hours and desperately needs this one filter to work.&lt;/p&gt;

&lt;p&gt;Every time you make a UX decision, ask: &lt;em&gt;Would this help the person who's tired, stressed, distracted, and just wants to get this done?&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Know what they're afraid of
&lt;/h3&gt;

&lt;p&gt;Users come to every interface with fear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Will I lose my data?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Will I be charged twice?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Will I make a mistake I can't undo?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Will this take too long?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Will I look stupid?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your job is to address every fear before they have to articulate it. Show them their data is saved. Show them the transaction was successful. Let them undo mistakes. Make the fast parts visible. Design error states that don't blame the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Know what they're trying to do
&lt;/h3&gt;

&lt;p&gt;Nobody wakes up wanting to use your app. They wake up wanting to feed their family, run their business, or make it through their shift.&lt;/p&gt;

&lt;p&gt;Your app is a means to an end.&lt;/p&gt;

&lt;p&gt;Every feature should either get them closer to their goal faster or reduce the friction of getting there. If it doesn't do either, it doesn't belong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part VIII: The Real Future of Frontend
&lt;/h2&gt;

&lt;p&gt;I've been doing this for over five years. I've built things I'm proud of and things I've learned from. I've stayed up late, shipped under pressure, debugged impossible bugs, and felt the incomparable joy of watching someone use something I built and seeing it make their day better.&lt;/p&gt;

&lt;p&gt;And I can tell you with absolute certainty: &lt;strong&gt;the future of frontend engineering is not in writing more code. It's in understanding people better.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The AI revolution is not the end of our craft. It's the liberation of our craft.&lt;/p&gt;

&lt;p&gt;We get to stop writing scaffolding and start designing experiences. We get to stop debugging CSS and start debugging user journeys. We get to stop optimizing render cycles and start optimizing for human delight.&lt;/p&gt;

&lt;p&gt;The tools will change. They always do. The frameworks will evolve. They always will. AI will get smarter. It's already smart, and it's only going to get smarter.&lt;/p&gt;

&lt;p&gt;But the fundamental equation hasn't changed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technology + Empathy = Value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI brings the technology.&lt;/p&gt;

&lt;p&gt;Only you bring empathy.&lt;/p&gt;

&lt;p&gt;Only you can sit with a user, watch them struggle, and understand what they need. Only you can stare at a screen at 2 AM and realize that a single pixel adjustment will save thousands of people a moment of confusion. Only you can look at a business requirement and translate it into an interface that feels natural, intuitive, even joyful.&lt;/p&gt;

&lt;p&gt;That's not going away. It's becoming more valuable every day.&lt;/p&gt;




&lt;h2&gt;
  
  
  Epilogue: The 2 AM Deployment, Revisited
&lt;/h2&gt;

&lt;p&gt;It's 2 AM again, years later.&lt;/p&gt;

&lt;p&gt;I'm watching a deployment pipeline run. This time it's a major feature, a campaign engine that will run for weeks, involving thousands of customers predicting scores, referring friends, and earning rewards.&lt;/p&gt;

&lt;p&gt;The stakes are higher. The system is more complex. The audience is bigger.&lt;/p&gt;

&lt;p&gt;And yet, the feeling is the same: &lt;em&gt;this matters.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Somewhere out there, a customer will wake up, open this app, and see their prediction was correct. They'll smile. They'll tell a friend. They'll feel good about using this product.&lt;/p&gt;

&lt;p&gt;Somewhere out there, an admin will log into the dashboard, see the referral leaderboard, and know exactly who to reward. They'll feel in control. They'll trust the system.&lt;/p&gt;

&lt;p&gt;Somewhere out there, a business owner will look at the campaign metrics and see that revenue went up because more customers engaged, because the experience felt good, because someone, an engineer, cared about the details.&lt;/p&gt;

&lt;p&gt;That engineer was me.&lt;/p&gt;

&lt;p&gt;And for the next generation reading this: &lt;strong&gt;that engineer will be you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not AI. You.&lt;/p&gt;

&lt;p&gt;The AI will write the components. The AI will handle the boilerplate. The AI will catch the edge cases.&lt;/p&gt;

&lt;p&gt;But only you can write the future.&lt;/p&gt;

&lt;p&gt;Only you can care enough to make it matter.&lt;/p&gt;

&lt;p&gt;Only you can look at a blank screen and imagine something that doesn't exist yet, something that will make someone's life measurably better, and then build it, ship it, and watch it change the world, one interface at a time.&lt;/p&gt;

&lt;p&gt;That's not a skill AI can learn.&lt;/p&gt;

&lt;p&gt;That's a choice only a human can make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So make it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Build something that matters. Ship it imperfectly. Learn from the failures. Celebrate the wins. And never, ever let anyone tell you that what you do is "just frontend."&lt;/p&gt;

&lt;p&gt;What you do is &lt;strong&gt;where value becomes real.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What you do is &lt;strong&gt;where technology meets humanity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What you do is magic. It always has been. And AI? It's just another tool in your magic kit.&lt;/p&gt;

&lt;p&gt;The real magician?&lt;/p&gt;

&lt;p&gt;Still you.&lt;/p&gt;

&lt;p&gt;Always you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I've been building full-stack applications for over five years, e-commerce platforms, logistics systems, promotional engines, and admin dashboards. I've shipped code that moved millions and code that mattered to exactly one person. If you're early in your journey and wondering whether this path is worth it, it is. If you're experienced and wondering whether your skills still matter, they do. More than ever.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What are you building? I'd love to see it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Follow Me&lt;br&gt;
X: &lt;a href="https://x.com/ogeobubu" rel="noopener noreferrer"&gt;@ogeobubu&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/ogeobubu" rel="noopener noreferrer"&gt;@ogeobubu&lt;/a&gt;&lt;br&gt;
Instagram: &lt;a href="https://instagram.com/ogeobubu" rel="noopener noreferrer"&gt;@ogeobubu&lt;/a&gt;&lt;br&gt;
Threads: &lt;a href="https://threads.com/ogeobubu" rel="noopener noreferrer"&gt;@ogeobubu&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How We Scaled from 0 to 164 Monthly Active Users in 28 Days (And What We Learned)</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Sun, 21 Jun 2026 20:35:55 +0000</pubDate>
      <link>https://dev.to/ogeobubu/how-we-scaled-from-0-to-164-monthly-active-users-in-28-days-and-what-we-learned-2ccc</link>
      <guid>https://dev.to/ogeobubu/how-we-scaled-from-0-to-164-monthly-active-users-in-28-days-and-what-we-learned-2ccc</guid>
      <description>&lt;p&gt;&lt;strong&gt;The story of Foodmartex's biggest week yet and the surprising lessons that came with it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Night Everything Changed
&lt;/h2&gt;

&lt;p&gt;It was 2:37 AM on a Wednesday when I refreshed our analytics dashboard for the hundredth time that week. My eyes were burning, but the numbers told a story I couldn't look away from. Something was happening.&lt;/p&gt;

&lt;p&gt;Our app had just hit &lt;strong&gt;164 monthly active users&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For a platform that was practically invisible just months ago, this was the moment we'd been working toward. But here's the thing about growth: it never comes from where you expect it. And the week of June 15–21, 2026, taught me lessons I won't forget.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Traffic That Didn't Make Sense
&lt;/h2&gt;

&lt;p&gt;Let me start with the weirdest part of our week.&lt;/p&gt;

&lt;p&gt;According to our analytics, we should have been celebrating a massive spike in Nigerian traffic. After all, Nigeria is our home market. It's where our earliest users came from, where our brand recognition is strongest, and where we've invested the most energy.&lt;/p&gt;

&lt;p&gt;And sure enough, Nigeria showed up. &lt;strong&gt;3,183 requests in just 24 hours.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But here's where it gets interesting.&lt;/p&gt;

&lt;p&gt;The Netherlands, a country we've barely marketed to, was right on Nigeria's heels with &lt;strong&gt;1,953 requests&lt;/strong&gt;. That's not a typo. A country thousands of miles away, where we have zero brand presence, was sending almost as much traffic as our core market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The United States came in third with 466 requests&lt;/strong&gt;, followed by the UK at 416 and Germany at 107.&lt;/p&gt;

&lt;p&gt;I sat there staring at this data, trying to make sense of it. Was it bots? Scrapers? Some viral post we didn't know about? Or was it something else entirely, the early signs of unexpected market interest?&lt;/p&gt;

&lt;p&gt;We're still investigating. But here's what I've learned: &lt;strong&gt;growth doesn't always knock on the door you expect it to. Sometimes it kicks down a window in Amsterdam.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Misspelling That Changed Everything
&lt;/h2&gt;

&lt;p&gt;If you've ever launched a product, you know the agony of watching people misspell your name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"foodmat.site"&lt;/strong&gt;, not our domain, but close enough to hurt.&lt;/p&gt;

&lt;p&gt;In the past week alone, this misspelling and its variations generated over &lt;strong&gt;1,300 impressions&lt;/strong&gt; on Google. That's 1,300 people searching for something that sounds like us, looks like us, but isn't quite us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the painful part:&lt;/strong&gt; our click-through rate on these terms was abysmal. People were searching, seeing results, and moving on. They wanted Foodmartex. They typed something slightly wrong. And we were nowhere to be found.&lt;/p&gt;

&lt;p&gt;But the moment they typed &lt;strong&gt;"foodmartex"&lt;/strong&gt; correctly? We ranked &lt;strong&gt;number one&lt;/strong&gt; with a click-through rate of &lt;strong&gt;67.74%&lt;/strong&gt;. One out of every three people searching for our exact name clicked through.&lt;/p&gt;

&lt;p&gt;That's the power of branding. But it's also the danger of assumptions. We assumed people would find us. We assumed they'd type correctly. &lt;strong&gt;We assumed wrong.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The lesson here is simple: your customers are typing your name wrong. Are you capturing those searches? Or are you letting them slip through your fingers?&lt;/p&gt;




&lt;h2&gt;
  
  
  The App That Refused to Crash
&lt;/h2&gt;

&lt;p&gt;Now let's talk about something I'm genuinely proud of.&lt;/p&gt;

&lt;p&gt;Our mobile app has a &lt;strong&gt;perfect 5.00-star rating&lt;/strong&gt; on Google Play. I know, I know; early days, small sample size. But here's what that rating actually represents:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero crashes. Zero ANR (Application Not Responding) errors. Zero stability issues.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a world where users will abandon an app after just one bad experience, we've built something that just works. And over the last 28 days, we've seen that stability translate into real growth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;75 new device acquisitions&lt;/strong&gt; (up 19%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;52 first opens&lt;/strong&gt; (up 11%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;164 monthly active users&lt;/strong&gt; (up 32%)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;312 total installs&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those numbers might not sound like much to a giant tech company. But to us? They represent 312 people who trusted us enough to install our app. 164 people who came back. 52 people who opened it for the very first time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every one of those people made a choice. And we're determined to make sure it is the right one.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Week That Felt Like a Year
&lt;/h2&gt;

&lt;p&gt;Behind these numbers, our engineering team was doing something remarkable.&lt;/p&gt;

&lt;p&gt;In just seven days, we built and launched features that would normally take a month:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We made laundry ordering production-ready.&lt;/strong&gt; Not just "it works," but full lifecycle support. Customers can order, track, and cancel. Vendors can confirm and manage. Riders get matched automatically and settled fairly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We built an admin SMS system from scratch.&lt;/strong&gt; Thousands of messages queued, delivered, and tracked. With a dashboard to see exactly what's working and what isn't. (And yes, we immediately rotated our API tokens when we realized they were visible. Lesson learned.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We made our payment math flawless.&lt;/strong&gt; Rewards expire automatically. Referrals credit correctly. Riders get exactly what they're owed, and the company keeps exactly what it's owed. No rounding errors. No edge cases. No surprises.&lt;/p&gt;

&lt;p&gt;And we did all of this while maintaining &lt;strong&gt;zero downtime&lt;/strong&gt; and &lt;strong&gt;zero user-facing bugs&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 946 People Who Matter Most
&lt;/h2&gt;

&lt;p&gt;Here's a number I keep coming back to: &lt;strong&gt;946 unique visitors&lt;/strong&gt; to our website in the last 7 days.&lt;/p&gt;

&lt;p&gt;Not all of them became users. Not all of them converted. But they all came. They all looked. They all gave us a chance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;361 of them came on our best day.&lt;/strong&gt; 85 is our slowest.&lt;/p&gt;

&lt;p&gt;That spread tells me something important: our traffic isn't consistent yet. We're not a steady stream. We're waves crashing against the shore. Some days, the tide is high. Other days, it's a trickle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But here's the thing about waves: they keep coming.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And as long as people keep searching, keep typing, and keep trying to find us, we'll keep building. We'll fix the misspellings. We'll capture the stray traffic. We'll make our platform so good that no one ever has to search for a competitor again.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We Learned (So You Don't Have To)
&lt;/h2&gt;

&lt;p&gt;If you're building something, an app, a platform, or a dream, here's what this week taught me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Your customers are searching for you in the wrong way.&lt;/strong&gt; &lt;br&gt;
Find those misspellings. Capture them. Don't let a typo stand between someone and your product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Growth doesn't come from where you expect it.&lt;/strong&gt;&lt;br&gt;
Nigeria is our core. But the Netherlands showed up uninvited. Don't ignore the unexpected. It might be your next market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stability is your most powerful marketing tool.&lt;/strong&gt;&lt;br&gt;
A 5-star rating is worth more than any ad campaign. Build something that doesn't break. People will notice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Your team is capable of incredible things in a single week.&lt;/strong&gt;&lt;br&gt;
19 commits. 7 days. 3 major features. Don't underestimate what focused people can accomplish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Growth is a numbers game, but it's also a people game.&lt;/strong&gt;&lt;br&gt;
946 visitors. 312 installs. 164 active users. Behind every number is someone who took a chance on us. We don't forget that.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Road Ahead
&lt;/h2&gt;

&lt;p&gt;We're not a giant company yet. We're not even close to where we want to be. But we're growing. We're learning. We're building.&lt;/p&gt;

&lt;p&gt;And that's the point, isn't it?&lt;/p&gt;

&lt;p&gt;The week of June 15–21, 2026, was our best week yet. But it's not going to be our best week forever. Because next week, we'll be better. And the week after that, better still.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We're Foodmartex. We're building the future of everyday convenience. And we're just getting started.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Stay hungry. Stay curious. And for the love of everything, please type our name correctly.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; — If you're one of the 312 people who installed our app this month, thank you. You made this week unforgettable. If you're not, what are you waiting for? &lt;a href="https://play.google.com/store/apps/details?id=com.foodmartex.customer&amp;amp;hl=en" rel="noopener noreferrer"&gt;Foodmartex App&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S.&lt;/strong&gt; — To the person in the Netherlands who's been refreshing our site all week: we see you. We appreciate you. We'd love to know what brought you here. Drop us a comment below.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What Being a Hands-On CTO Is Teaching Me About Leadership</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Fri, 19 Jun 2026 10:43:55 +0000</pubDate>
      <link>https://dev.to/ogeobubu/what-being-a-hands-on-cto-is-teaching-me-about-leadership-1la3</link>
      <guid>https://dev.to/ogeobubu/what-being-a-hands-on-cto-is-teaching-me-about-leadership-1la3</guid>
      <description>&lt;p&gt;When I became a CTO, I knew the role would involve making technical decisions, shaping product direction, and thinking about scalability.&lt;/p&gt;

&lt;p&gt;What I did not fully appreciate was how often leadership would require me to move between the big picture and the smallest implementation details.&lt;/p&gt;

&lt;p&gt;One moment, I am thinking about architecture, business priorities, and the future of Foodmartex. Next, I am sitting with our frontend developer, tracing an API response, investigating a broken user flow, or figuring out why the interface is not behaving as expected.&lt;/p&gt;

&lt;p&gt;That contrast has become one of the most rewarding parts of the role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leadership Is More Than Giving Instructions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is easy to imagine technical leadership as having all the answers and telling people what to do.&lt;/p&gt;

&lt;p&gt;My experience has been different.&lt;/p&gt;

&lt;p&gt;Sometimes leadership means providing direction. Sometimes it means asking the right questions. Other times, it means getting close to the problem and working through it with the person responsible for solving it.&lt;/p&gt;

&lt;p&gt;Helping our frontend developer has taught me that my job is not to take over whenever something becomes difficult. My responsibility is to provide enough clarity and support for the developer to move forward confidently.&lt;/p&gt;

&lt;p&gt;The goal is not dependence. The goal is growth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Frontend Reveals the Truth&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A backend feature can look complete until someone tries to use it through the interface.&lt;/p&gt;

&lt;p&gt;That is where hidden problems begin to appear: inconsistent API responses, unclear status transitions, missing validation, confusing user journeys, and assumptions that seemed reasonable during development but do not work in practice.&lt;/p&gt;

&lt;p&gt;Working closely with the frontend has helped me see our product through the eyes of the people who will eventually use it.&lt;/p&gt;

&lt;p&gt;It has also reminded me that the frontend and backend are not separate products. They are two parts of the same experience. If they do not communicate properly, the user does not care which side caused the problem. They only know that the product is not working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowing When to Step Back&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Being hands-on comes with its own challenge.&lt;/p&gt;

&lt;p&gt;When you have the experience to solve a problem quickly, it can be tempting to jump in and do everything yourself. But constantly taking over may fix today’s issue while preventing someone else from developing the confidence to solve tomorrow’s.&lt;/p&gt;

&lt;p&gt;I am learning to pause before stepping in.&lt;/p&gt;

&lt;p&gt;Does the developer need an answer, more context, or simply the space to investigate?&lt;/p&gt;

&lt;p&gt;Good technical leadership requires knowing when to lead from the front, when to work beside your team, and when to get out of their way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I Am Growing Too&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The work we are doing on Foodmartex is still a journey. We are building, testing, correcting assumptions, and strengthening the foundation behind the product.&lt;/p&gt;

&lt;p&gt;But the product is not the only thing being developed.&lt;/p&gt;

&lt;p&gt;This experience is also shaping me into a better leader: one who listens more carefully, communicates more clearly, and understands that leadership is not about appearing distant from the work.&lt;/p&gt;

&lt;p&gt;It is about being close enough to understand the challenges, experienced enough to provide direction, and disciplined enough to let other people grow.&lt;/p&gt;

&lt;p&gt;I am still learning.&lt;/p&gt;

&lt;p&gt;I am still building.&lt;/p&gt;

&lt;p&gt;And that may be the most honest description of leadership I can give.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building Backend Systems That Hold Up Beyond the Happy Path</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Fri, 19 Jun 2026 05:55:27 +0000</pubDate>
      <link>https://dev.to/ogeobubu/building-backend-systems-that-hold-up-beyond-the-happy-path-1bp7</link>
      <guid>https://dev.to/ogeobubu/building-backend-systems-that-hold-up-beyond-the-happy-path-1bp7</guid>
      <description>&lt;p&gt;This week, my work as a backend engineer was focused on one thing: making real business flows behave like real business operations.&lt;/p&gt;

&lt;p&gt;A lot of backend work is invisible when it is done well. Users do not see the state transitions, the rider matching rules, the settlement timing, the validation layers, or the historical checks behind a clean API response. But those details are what make a platform reliable.&lt;/p&gt;

&lt;p&gt;At the start of the week, I worked through customer-to-rider order flow tracing, mapping how requests move across customer, vendor, rider, and admin surfaces. That kind of work matters because it prevents teams from building on assumptions. Before changing a flow, you need to know who owns each action, which endpoint is canonical, and where the handoff really happens.&lt;/p&gt;

&lt;p&gt;A major part of my week went into strengthening the laundry service flow. Laundry is not a simple one-leg delivery like food. It has pickup, vendor receipt, processing, readiness, return dispatch, delivery, and settlement. I worked on making that lifecycle more professional by improving rider matching, pickup and return handling, status transitions, delivery fee/service charge calculations, cancellation reasons, and rider settlement fields.&lt;/p&gt;

&lt;p&gt;I also worked on reward and onboarding logic, especially around welcome rewards and customer state. One important backend lesson from this: current profile data does not always tell the full historical story. A customer may have completed onboarding, received a reward, and later changed or removed address data. Good backend logic has to separate live relationship state from historical eligibility state.&lt;/p&gt;

&lt;p&gt;Some highlights from this week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improved laundry rider matching and two-leg order handling&lt;/li&gt;
&lt;li&gt;Added settlement support for laundry pickup and return flows&lt;/li&gt;
&lt;li&gt;Enhanced laundry cart summaries with service charges and total calculations&lt;/li&gt;
&lt;li&gt;Added express and insurance options for laundry services&lt;/li&gt;
&lt;li&gt;Improved cancellation handling across laundry, market, and parcel orders&lt;/li&gt;
&lt;li&gt;Enhanced rider parcel delivery filtering and history queries&lt;/li&gt;
&lt;li&gt;Improved rider ongoing order details with current rider information&lt;/li&gt;
&lt;li&gt;Traced customer-to-rider route surfaces to clarify real API contracts&lt;/li&gt;
&lt;li&gt;Debugged onboarding and welcome reward behavior using historical state, not just current payload fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Backend engineering is not just writing endpoints. It is designing trust into the system.&lt;/p&gt;

&lt;p&gt;The best backend work answers questions before they become production issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can this flow recover from partial progress?&lt;/li&gt;
&lt;li&gt;Does the next actor know what to do?&lt;/li&gt;
&lt;li&gt;Can finance settle correctly?&lt;/li&gt;
&lt;li&gt;Can support understand what happened?&lt;/li&gt;
&lt;li&gt;Can the frontend trust the response?&lt;/li&gt;
&lt;li&gt;Can the business scale this without manual explanation?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was the theme of my week: turning complex operational workflows into backend systems that are clearer, safer, and more reliable.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Connect with me:&lt;/em&gt;&lt;br&gt;
GitHub-&amp;gt; &lt;a href="https://github.com/ogeobubu" rel="noopener noreferrer"&gt;@ogeobubu&lt;/a&gt;&lt;br&gt;
X -&amp;gt; &lt;a href="https://x.com/ogeobubu" rel="noopener noreferrer"&gt;@ogeobubu&lt;/a&gt;&lt;br&gt;
Instagram -&amp;gt; &lt;a href="https://instagram.com/ogeobubu" rel="noopener noreferrer"&gt;@ogeobubu&lt;/a&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>productivity</category>
      <category>programming</category>
      <category>career</category>
    </item>
    <item>
      <title>Building Better Product Experiences Through Quiet Improvements</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Wed, 17 Jun 2026 05:22:46 +0000</pubDate>
      <link>https://dev.to/ogeobubu/building-better-product-experiences-through-quiet-improvements-1dii</link>
      <guid>https://dev.to/ogeobubu/building-better-product-experiences-through-quiet-improvements-1dii</guid>
      <description>&lt;p&gt;Yesterday was focused on the kind of product work that often happens behind the scenes but directly affects the quality of the user experience.&lt;/p&gt;

&lt;p&gt;The team worked on improving access to promotional experiences within the platform, making it easier for users to discover offers and value-driven moments. Work like this may seem small, but navigation and discoverability are important parts of how people experience a product.&lt;/p&gt;

&lt;p&gt;There was also cleanup done within the frontend codebase. Removing unnecessary complexity helps keep the platform leaner and easier to maintain. It also gives the team more room to move faster when building future improvements.&lt;/p&gt;

&lt;p&gt;Alongside that, a stability issue in the app setup was corrected. These kinds of fixes are important because they protect the user experience from avoidable friction.&lt;/p&gt;

&lt;p&gt;Not every product update needs to be loud. Some of the most important progress happens in the details: cleaner structure, better access, fewer rough edges, and a stronger foundation for what comes next.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Day We Fixed Our Signup Pipeline</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:23:48 +0000</pubDate>
      <link>https://dev.to/ogeobubu/the-day-we-fixed-our-signup-pipeline-3664</link>
      <guid>https://dev.to/ogeobubu/the-day-we-fixed-our-signup-pipeline-3664</guid>
      <description>&lt;p&gt;It started with a graph.&lt;/p&gt;

&lt;p&gt;Our signup numbers were climbing every week. The team was excited—growth was happening. But something felt off. A lot of those "users" never came back. Their email addresses looked strange. And our activation rate was quietly dropping.&lt;/p&gt;

&lt;p&gt;One Friday afternoon, I decided to dig into the data.&lt;/p&gt;

&lt;p&gt;What I found was not growth. It was noise.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Diagnosis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I ran a simple query: group signups by IP address, count them, and sort descending.&lt;/p&gt;

&lt;p&gt;The top result: one IP address had registered hundreds of accounts in the past 24 hours.&lt;/p&gt;

&lt;p&gt;Same browser fingerprint. Same pattern. A script, probably hitting our register endpoint, generating accounts with throwaway email domains, and moving on.&lt;/p&gt;

&lt;p&gt;I checked the user agents. Tools, not people.&lt;/p&gt;

&lt;p&gt;I checked the email domains. Addresses that expire before the welcome email is even sent.&lt;/p&gt;

&lt;p&gt;Our signup pipeline was wide open. Anyone, or anything, could walk in.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We needed protection in layers. Not a single gate, but a series of filters. Each one catches something the others might miss.&lt;/p&gt;

&lt;p&gt;We decided on three layers. And we built them all in a single sprint.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Layer 1: Throttling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most obvious fix: rate limiting. But not just one limit, two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-IP throttling:&lt;/strong&gt; A small number of signup attempts per IP address within a short window. If you hit the limit, you get a clear response telling you when to retry.&lt;/p&gt;

&lt;p&gt;No ambiguity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-domain throttling:&lt;/strong&gt; An even smaller tolerance for signups from the same email domain within a longer window. This catches distributed attacks, a bot using different IPs but always the same throwaway domain.&lt;/p&gt;

&lt;p&gt;Two counters. A bot that shares either an IP or a domain gets stopped.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Layer 2: Blocklists&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rate limiting slows things down, but it doesn't stop determined attackers. They'll wait and come back. We needed permanent blocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocked email domains:&lt;/strong&gt; We maintain a list of disposable email domains. Any registration attempt using one gets rejected before the data touches the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocked user agents:&lt;/strong&gt; If the request comes from a non-browser tool, it's rejected with a simple error. No explanation. No details. Attackers don't need to know why they were blocked.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Layer 3: IP Blocklist&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some IPs are persistent. They've been flagged before. They've abused other endpoints, not just signup. Rate limiting won't stop them. Blocking their user agent won't stop them if they switch.&lt;/p&gt;

&lt;p&gt;We needed a hard blocklist, IPs that are rejected for every request, not just registration.&lt;/p&gt;

&lt;p&gt;The message is deliberately terse. No explanation. No recourse. The middleware runs on every route, every method, every request. If your IP is on the list, you don't get through.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Before and After&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Hundreds of accounts from a single IP in 24 hours&lt;/li&gt;
&lt;li&gt;Throwaway email domains made up a significant percentage of new signups&lt;/li&gt;
&lt;li&gt;Activation rate was diluted by fake accounts&lt;/li&gt;
&lt;li&gt;Every data-driven decision was slightly wrong because the denominator was inflated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same-IP registration farming dropped to zero&lt;/li&gt;
&lt;li&gt;Disposable domain signups disappeared&lt;/li&gt;
&lt;li&gt;Our signup numbers reflected actual human intent&lt;/li&gt;
&lt;li&gt;Activation rate recovered, because the denominator now represented real users&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What We Learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Signal matters more than volume.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When signups are inflated by bots, every metric that depends on user count becomes unreliable. Activation rate. Retention. Revenue per user. Cleaning the pipeline made every downstream number trustworthy again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A small amount of code can fix a big problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The solution wasn't complex. It was layered. Three simple mechanisms, each handling what the others miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Layers matter.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A single rate limit would have slowed the bots but not stopped them. A single blocklist would have caught the obvious ones but missed the persistent ones. The combination covers more surface area than any one approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Not all rejections are equal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We chose our responses carefully. Legitimate users get useful feedback. Bad actors get silence. The message you send reveals how much information you want the requester to have.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Takeaway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A healthy signup pipeline doesn't make the news. Nobody sends a tweet saying "I tried to sign up and couldn't because the IP throttling worked correctly."&lt;/p&gt;

&lt;p&gt;But the absence of this work would eventually make headlines, just not the kind you want.&lt;/p&gt;

&lt;p&gt;Growth is not just about getting users in the door. It's about making sure the users who come through are real. Every decision you make, product direction, marketing spend, feature priorities, depends on the quality of your data. And your data quality starts at the first line of defense: the registration endpoint.&lt;/p&gt;

&lt;p&gt;The bots stopped. The data cleaned up. And our growth metrics finally told the truth.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>devops</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
