<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Emmanuel Yeboah — Writing</title>
    <link>https://noelzappy.dev/writing</link>
    <atom:link href="https://noelzappy.dev/rss.xml" rel="self" type="application/rss+xml" />
    <description>Notes on backend engineering, distributed systems, and migrations.</description>
    <language>en</language>
    <item>
      <title>Are we now just bystanders while AI takes over?</title>
      <link>https://noelzappy.dev/writing/are-we-now-just-bystanders-while-ai-takes-over</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/are-we-now-just-bystanders-while-ai-takes-over</guid>
      <pubDate>Mon, 20 Apr 2026 00:00:00 GMT</pubDate>
      <description>AI is turning the internet into a loop of machines talking to machines, writing emails, reviewing code, and deploying systems with little human input. Our value is no longer in typing faster than the bots, but in judgment: deciding what should be built, why it matters, and where it should go.</description>
      <category>ai</category><category>opinion</category>
      <content:encoded><![CDATA[<!--[--><p>Have you ever noticed how the internet is just becoming AI talking to AI?<br/> You get an email that was definitely written by ChatGPT. You use Gemini to summarize it and spit out a polite reply, and you hit send without even reading it. Then you jump on LinkedIn, see a totally AI-generated post, screenshot it, ask Claude for a spicy reply, and post that. Even with jobs, you use AI to write your resume, and the recruiter uses an AI tool to reject it.<br/> It’s wild. The internet is turning into this weird loop of bots just feeding off each other.<br/> But honestly, the social media and email stuff is just noise. The craziest part is what’s happening right now in our codebases.<br/> We’re literally at the point of vibe coding. An AI agent picks up a ticket, writes the logic, and opens a PR. And instead of a senior dev pulling the branch to check it, another AI agent reviews it, approves it, and merges it straight to main.<br/> The actual code running things is being written, reviewed, and deployed without a human ever looking at it. The machine is literally building the machine.<br/> As someone who spends a lot of time writing “beautiful” code, this feels like a massive shift. Code used to be how we talked to the computer. Now, the bots are doing the talking.<br/> It pushes us into a weird spot. We aren’t really coders in the traditional sense anymore; we’re more like system wardens. We don’t have to stress over a blank screen or basic syntax, but now we have the massive headache of babysitting a synthetic system to make sure some AI doesn’t hallucinate a bug that brings the whole architecture down. We traded the typing for the stress of constant oversight.<br/> So, does this mean we’re just spectators now?<br/> I think we are, but only if we try to compete at the boilerplate level.<br/> An AI agent can merge a PR in seconds, but it has no idea if the infrastructure it just built actually solves a problem for real people in the local market. It can write a clean function, but it can’t tell a genuine story or figure out what a business actually needs to survive.<br/> The internet is definitely running on autopilot, but deciding where to steer it? That’s still on us. We just need to stop stressing over the individual bricks and focus on what we’re actually trying to build.</p><!--]-->]]></content:encoded>
    </item>
    <item>
      <title>Software Engineering is Just Advanced Damage Control.</title>
      <link>https://noelzappy.dev/writing/software-engineering-is-just-advanced-damage-control</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/software-engineering-is-just-advanced-damage-control</guid>
      <pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate>
      <description>What actually separates a senior engineer from the pack? It’s not just writing features; it’s error handling. It’s optimizing a system to fail gracefully. Think about the lifecycle of a single request. When it depends on an external network call, do you just try it once, throw an error, and hope the</description>
      <category>engineering</category><category>opinion</category>
      <content:encoded><![CDATA[<!--[--><p>What actually separates a senior engineer from the pack? It’s not just writing features; it’s error handling. It’s optimizing a system to fail gracefully.<br/> Think about the lifecycle of a single request. When it depends on an external network call, do you just try it once, throw an error, and hope the user hits refresh? Or do you build in idempotent retry mechanisms and handle downstream server failures?<br/> What happens if a database connection drops mid-query? What’s the fallback when the DB is entirely unavailable? From an operating system process malfunctioning to complete memory or disk exhaustion, computers are inherently prone to failure.<br/> The defining trait of a mature engineer is the assumption that things will go wrong. You have to design systems to be fault-tolerant, ensuring they can recover when the inevitable happens.<br/> At the end of the day, software engineering might just be the art of preventing computers from failing.</p><!--]-->]]></content:encoded>
    </item>
    <item>
      <title>The Shipping Anxiety of AI-Generated Code</title>
      <link>https://noelzappy.dev/writing/the-shipping-anxiety-of-ai-generated-code</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/the-shipping-anxiety-of-ai-generated-code</guid>
      <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
      <description>I’ve never quite bought into the hype of the &quot;zero-oversight&quot; AI developer. If you spend five minutes on tech social media, you’ll see developers bragging about giving Claude or Gemini full access to their codebase. They talk about letting the AI write entire features, merging the pull requests, and</description>
      <category>ai</category><category>engineering</category>
      <content:encoded><![CDATA[<!--[--><p>I’ve never quite bought into the hype of the “zero-oversight” AI developer.<br/> If you spend five minutes on tech social media, you’ll see developers bragging about giving Claude or Gemini full access to their codebase. They talk about letting the AI write entire features, merging the pull requests, and moving on without a second thought.<br/> But honestly? I still haven’t gotten over the feeling that the AI is eventually going to mess me up.<br/> I tried it once, letting AI completely handle a non-critical feature for an internal tool without my usual deep, manual review. I didn’t feel the rush of 10x productivity. Instead, I felt a sudden drop in confidence. Even though it was low-stakes, there was this lingering, mild anxiety when it went live. <em>Did I catch every edge case? Does it actually do what I think it does?</em> It highlighted the psychological trade-off of the modern stack: you might save a few hours of typing, but you buy yourself days of low-grade panic waiting for a bug report.</p> <p>This isn’t just about developer pride; it’s about strategic risk. AI has produced some incredible software, and I’m more than happy to let it do whatever it pleases with the frontend. If a UI component breaks or a layout shifts, it’s a visual annoyance that can be quickly patched.<br/> But the backend is a completely different story.<br/> When you are building financial systems, when your platform is actively processing real money on a daily basis, the stakes change completely. I honestly still struggle with the concept of letting an LLM write critical logic for the core engine of the financial systems I work on, including <a href="https://susupaa.com/?ref=ghost.noelzappy.dev" rel="nofollow">SusuPaa.</a> In fintech, a backend hallucination isn’t just a broken div; it’s a catastrophic breach of trust with the users relying on you to keep their funds safe.</p> <p>Ultimately, confidence in shipping comes from comprehension. If you don’t fully understand the code, you don’t really own the feature. Responsible innovation isn’t about rejecting AI; it’s knowing when to use it as an accelerator, and when you need to keep your own hands firmly on the wheel.</p><!--]-->]]></content:encoded>
    </item>
    <item>
      <title>How I Migrated 170k Users from PHP to Node.js Without Stopping the World</title>
      <link>https://noelzappy.dev/writing/how-i-migrated-170k-users-from-php-to-node-js-without-stopping-the-world</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/how-i-migrated-170k-users-from-php-to-node-js-without-stopping-the-world</guid>
      <pubDate>Wed, 07 Jan 2026 00:00:00 GMT</pubDate>
      <description>Migrating a system with 170,000 users isn’t about clever rewrites; it’s about not breaking trust. This post covers how I moved a legacy PHP/MySQL platform to Node.js and PostgreSQL, kept both systems in sync, and scaled performance without downtime.</description>
      <category>systems</category><category>migrations</category>
      <content:encoded><![CDATA[<!--[--><p>In 2025, I joined the RBL team, a dating platform that had been running since 2015. The original goal was simple: improve filtering, fix location-based matching, and assess whether the system could keep scaling.</p> <p>At the time, the platform had over <strong>170,000 users</strong> running on a legacy <strong>PHP + MySQL</strong> stack. I was hired to add features, but once I dug into the codebase, it became clear the system had hit a hard ceiling.</p> <h4>The “WTF” Moments</h4> <p>We all know that feeling when you open a legacy codebase and realize standard feature requests are going to be nightmares. Two things stood out immediately:</p> <p><strong>1. Location matching wasn’t real geo-spatial matching</strong><br/> There was no concept of coordinates or radius-based search. Location was inferred by extracting city names from IP addresses using a paid third-party service, then doing string comparisons. If User A was in “Accra” and User B was in “Accra”, they matched. Anything like “people within 50km” was impossible.</p> <p><strong>2. Extreme over-normalization</strong><br/> The database schema was rigid to the point of hurting read performance. Simple attributes like height weren’t stored as numbers. There was a separate <code>heights</code> table (<code>1ft</code>, <code>1.2ft</code>, … <code>8ft</code>) and user profiles referenced it via foreign keys. Fetching a single profile required joins across tables for <code>height</code>, <code>hobbies</code>, <code>education</code>, and more. Reads were slow, and the schema made iteration painful.</p> <p>At that point, patching felt irresponsible.</p> <h4>The Rewrite</h4> <p>I made the case to stakeholders that this wasn’t a refactor problem. It was a rewrite problem. The new direction:</p> <ul><li><strong>Node.js (Express)</strong> for better concurrency</li> <li><strong>PostgreSQL</strong> with <strong>PostGIS</strong> for proper geospatial queries</li> <li>A flatter schema optimized for reads</li> <li>Refactored the mobile client to be modern <strong>React Native</strong> compatible.</li></ul> <p>I spent about <strong>three months</strong> rebuilding the system end-to-end.</p> <ul><li><strong>Backend:</strong> Moved from PHP to Node.js (Express).</li> <li><strong>Data:</strong> Moved from MySQL to PostgreSQL (flattened schema, enums instead of join tables, implemented proper geospatial indexing with PostGIS).</li> <li><strong>Mobile:</strong> Moved client from Legacy React Native to modern React Native.</li></ul> <h4>The Migration (The Scary Part)</h4> <p>The platform had <strong>170k active users</strong>. I couldn’t flip a switch, shut down PHP, and assume everyone would update their app overnight. So I didn’t.</p> <p>Instead, I built a <strong>dual-write proxy</strong>.</p> <p>This middleware sat in front of both the old and new backends. When a user on the legacy app updated their profile (for example, changing their bio), the proxy would:</p> <ol><li>Validate the request</li> <li>Transform the payload to match the new PostgreSQL schema</li> <li>Send it to the new Node.js backend</li> <li>Transform it again and forward it to the old PHP backend</li></ol> <p>Both systems stayed in sync, while a separate job backfilled historical data.</p> <p>A user on the old app could chat with a user on the new React Native app without realizing they were on completely different backends.</p> <p>Once adoption crossed a safe threshold, I finally shut down the PHP server.</p> <p><img src="/writing/how-i-migrated-170k-users-from-php-to-node-js-without-stopping-the-world/architecture-diagram.webp"/></p> <h4>Post-Launch &amp; The 6-Second API Call</h4> <p>After launch, everything looked good… until traffic ramped up.</p> <p>The “swipe” endpoint started timing out. Some requests took <strong>up to 6 seconds</strong>.</p> <p>The cause was straightforward and painful:<br/> I was running the full matching algorithm — geo filtering, compatibility scoring — <strong>on-demand</strong> every time a user requested a card.</p> <p>At this scale, that doesn’t fly.</p> <h3>The Fix: Redis + Prefetching</h3> <p>I introduced <strong>Redis</strong> and changed the model entirely.</p> <p>Instead of computing matches in real time:</p> <ul><li>When a user opens the app, a background job precomputes their next batch of matches</li> <li>The results are cached in Redis</li> <li>The API simply reads from cache</li></ul> <p>Response times dropped from seconds to <strong>milliseconds</strong>.</p> <h3></h3> <h4>The Result</h4> <p>Today, the platform:</p> <ul><li>Supports <strong>200,000+ users</strong></li> <li>Has real radius-based geo filtering</li> <li>Uses a schema that doesn’t require 10 joins to read a profile</li> <li>Runs on a stack that can actually scale without fear</li></ul> <p>More importantly, the migration happened without downtime and without breaking the experience for existing users.</p><!--]-->]]></content:encoded>
    </item>
    <item>
      <title>Why I built Voltax: Unifying the African Payment Stack</title>
      <link>https://noelzappy.dev/writing/why-i-built-voltax-unifying-the-african-payment-stack</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/why-i-built-voltax-unifying-the-african-payment-stack</guid>
      <pubDate>Mon, 05 Jan 2026 00:00:00 GMT</pubDate>
      <description>I built Voltax to be the &quot;universal payment adapter&quot; I always wished I had. It’s an open-source, strictly typed SDK that abstracts the chaos of African payment gateways behind a single, unified interface.</description>
      <category>open-source</category><category>api-design</category>
      <content:encoded><![CDATA[<!--[--><p>If you’ve ever built a checkout flow in Ghana or Nigeria, you know the drill. You integrate Paystack, and it works great. Then a client asks for a specific payment flow that only Flutterwave supports, or you need to expand to East Africa, or you simply want to switch to a new provider that offers better fees.</p> <p>Suddenly, your clean codebase is littered with <code>if (provider === 'flutterwave')</code> checks. You’re dealing with different response shapes, inconsistent error codes, and three different ways to say “transaction successful.”</p> <p>I got tired of solving this problem from scratch for every project, so I built <a href="https://voltax.noelzappy.dev/?ref=ghost.noelzappy.dev" rel="nofollow"><strong>Voltax</strong></a>.</p> <p>I built Voltax to be the “universal adapter” I always wished I had. It’s an open-source, strictly typed SDK that abstracts the chaos of African payment gateways behind a single, unified interface.</p> <p>Instead of wrestling with documentation for three different providers, you just initialize <code>Voltax</code>, pass your config, and the SDK handles the normalization.</p> <p>This initial version is built using <strong>TypeScript</strong> for NodeJS and is live on NPM. I, however, have plans to release versions in <code>Go</code> and <code>Python</code>, etc. You can also submit a pull-request for whatever language or payment gateway you have in mind.</p> <p>This is my attempt to build the infrastructure I want to see in our ecosystem, and hopefully you can contribute too, and let’s make it work.</p> <p>Check it out:</p> <ul><li><a href="https://www.npmjs.com/package/@noelzappy/voltax?ref=ghost.noelzappy.dev" rel="nofollow">NPM Package</a></li> <li><a href="https://voltax.noelzappy.dev/?ref=ghost.noelzappy.dev" rel="nofollow">Documentation</a></li> <li><a href="https://github.com/noelzappy/voltax?ref=ghost.noelzappy.dev" rel="nofollow">Github</a></li></ul><!--]-->]]></content:encoded>
    </item>
    <item>
      <title>I'm leaving React for Svelte</title>
      <link>https://noelzappy.dev/writing/im-leaving-react-for-svelte</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/im-leaving-react-for-svelte</guid>
      <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
      <description>After writing React since 2019, I learned Svelte in an hour, and it completely redefined my workflow. Here’s why I’m migrating my projects, including this portfolio, and why I might never go back.</description>
      <category>frontend</category><category>opinion</category>
      <content:encoded><![CDATA[<!--[--><p>I wrote my first complete Svelte app last week and have basically been on autopilot since. It’s interesting how a single experiment can redefine your entire development perspective.</p> <p>My journey with React started back in 2019, and while I’ve built a career on it, moving to Svelte has been surprisingly swift. I was able to grasp the fundamentals and build my first app in less than an hour, a testament to documentation that is truly best-in-class. It smoothed out the learning curve I spent months battling with React.</p> <p>The developer experience just feels… lighter. Navigating routes doesn’t feel like a heavy chore anymore; the app just snaps into place. Svelte’s syntax feels like native HTML and JavaScript. I’m not inventing new patterns or fighting with <code>className</code> versus <code>class</code> I’m just writing web code. Even state management, which usually requires a complex mental model in React, is handled by Svelte “runes” in a way that just makes sense.</p> <p>This experience means one thing right now: I’m not just planning future projects in Svelte, I’ve already begun migrating my existing ones, including this portfolio. It’s clear there’s no going back.</p> <p>Good luck my fellow React devs. Hopefully, you’ll come to see the light.</p><!--]-->]]></content:encoded>
    </item>
    <item>
      <title>Why Developer Experience Is Now a Product Priority</title>
      <link>https://noelzappy.dev/writing/why-developer-experience-is-now-a-product-priority</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/why-developer-experience-is-now-a-product-priority</guid>
      <pubDate>Mon, 11 Aug 2025 00:00:00 GMT</pubDate>
      <description>A few years ago, nobody pitched “Developer Experience” as a core value proposition outside of the dev tools and infra world. That’s changed.</description>
      <category>developer-experience</category>
      <content:encoded><![CDATA[<!--[--><p>I’ve been thinking a lot about why some tools just <em>click</em>, while others make me want to shut my laptop and walk away.</p> <p>You know the feeling. You’re trying to integrate an API, the docs are vague, the error messages are useless, and you’re deep into Stack Overflow just to make a simple request work. That experience sticks with you, and not in a good way.</p> <p>On the flip side, tools like Stripe, Supabase, and Vercel feel almost… relaxing to use. Things work. The examples actually run. You copy, paste, and suddenly you’re productive. That feeling matters way more than we used to admit.</p> <p>DevX used to be something teams promised to “improve later.” Now it feels like the product. With APIs everywhere and AI writing more of our code, bad developer experience stands out instantly. If something is painful to use, it’s easier than ever to just switch.</p> <p>For me, good DevX is pretty simple:<br/> How fast can I get something working?<br/> Do the docs help me when I’m stuck?<br/> Do the errors tell me what I did wrong instead of blaming me?</p> <p>More and more, the tools that win aren’t the most powerful ones. They’re the ones that make you feel capable while using them.</p> <p>And honestly, as someone who’s integrated a lot of APIs and fought through a lot of bad docs, I’m really glad this is finally getting the attention it deserves.</p><!--]-->]]></content:encoded>
    </item>
    <item>
      <title>Why Every Ghanaian Tech Professional Should Take the 2025 Ghana Tech Ecosystem Survey</title>
      <link>https://noelzappy.dev/writing/why-every-ghanaian-tech-professional-should-take-the-2025-ghana-tech-ecosystem-survey</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/why-every-ghanaian-tech-professional-should-take-the-2025-ghana-tech-ecosystem-survey</guid>
      <pubDate>Sun, 03 Aug 2025 00:00:00 GMT</pubDate>
      <description>Ghana's tech sector has experienced remarkable growth over the past decade. However, to sustain this momentum and unlock our full potential, we need comprehensive data about where we stand as an ecosystem.</description>
      <category>community</category>
      <content:encoded><![CDATA[<!--[--><p>I’ve been working in Ghana’s tech scene long enough to see how much has changed and how much we still kind of guess our way through.</p> <p>We talk a lot about “growth,” fintech, startups, and opportunity, but when it comes down to real numbers, salaries, roles, challenges, who’s building what, we mostly don’t have solid data to point to.</p> <p>That’s why I’m taking the <a href="https://bit.ly/25TechSurvey?ref=ghost.noelzappy.dev" rel="nofollow">2025 Ghana Tech Ecosystem Survey by Code &amp; Cocktails</a>, and why I think it’s worth a few minutes of your time too.</p> <p>Last year’s survey actually produced useful insights. Not vibes. Real data that helped people understand where the ecosystem stands and sparked better conversations around policy, careers, and investment.</p> <p>This year only works if enough of us show up. Developers, founders, designers, operators, everyone counts. The survey is anonymous, takes about 10 minutes, and your answers become part of a bigger picture instead of just another Twitter thread.</p> <p>If you care about the future of tech in Ghana, or you’re simply working in it, this is one small way to contribute.</p> <p><strong>Ready to make your voice count?</strong> Take the survey now at:</p> <p><a href="https://www.codeandcocktails.live/ecosystem-survey?ref=ghost.noelzappy.dev" rel="nofollow">https://bit.ly/25TechSurvey</a></p><!--]-->]]></content:encoded>
    </item>
    <item>
      <title>Estimating Project Timelines as a Freelancer: My Personal Guide</title>
      <link>https://noelzappy.dev/writing/estimating-project-timelines-as-a-freelancer-a-developers-guide</link>
      <guid isPermaLink="true">https://noelzappy.dev/writing/estimating-project-timelines-as-a-freelancer-a-developers-guide</guid>
      <pubDate>Tue, 08 Apr 2025 00:00:00 GMT</pubDate>
      <description>Learn how I estimate timelines for freelance dev projects after building over 20 apps. From scoping to scheduling, here’s my practical approach to avoid underestimating and overpromising.</description>
      <category>process</category>
      <content:encoded><![CDATA[<!--[--><p>One of the hardest things I had to learn as a freelance developer wasn’t a framework or a language, it was time estimation.</p> <p>You can be really good at writing code, but if you constantly underestimate how long things take, it catches up with you fast. Missed deadlines, stressed nights, and awkward client conversations… I’ve been there.</p> <p>After about six years and a couple dozen projects, I’ve learned that timelines almost never slip because of the “hard” coding. They slip because of unclear scope, waiting on feedback, edge cases, and life happening in between.</p> <p>Now my approach is simple:<br/> I break work into small pieces.<br/> I estimate in hours, not vibes.<br/> I add a buffer, even when I feel confident.<br/> And I’m honest about how much time I <em>actually</em> have.</p> <p>Clients don’t need perfect estimates. They need realistic ones. I’ve found that communicating progress in milestones builds way more trust than just promising a delivery date and hoping everything goes right.</p> <p>These days, I’d rather sound a bit conservative upfront and deliver early than be optimistic and explain delays later.</p> <p>Accuracy beats optimism, every time.</p><!--]-->]]></content:encoded>
    </item>
  </channel>
</rss>