<?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>Forem: Yusuf Adeniyi</title>
    <description>The latest articles on Forem by Yusuf Adeniyi (@easyclick).</description>
    <link>https://forem.com/easyclick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3298136%2F2d2cb557-7e73-444a-b219-4a401ce83f9d.png</url>
      <title>Forem: Yusuf Adeniyi</title>
      <link>https://forem.com/easyclick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/easyclick"/>
    <language>en</language>
    <item>
      <title>Designing for Failure: Building Reliable Crypto-to-Utility Payments with Provider Failover</title>
      <dc:creator>Yusuf Adeniyi</dc:creator>
      <pubDate>Thu, 08 Jan 2026 12:02:49 +0000</pubDate>
      <link>https://forem.com/easyclick/designing-for-failure-building-reliable-crypto-to-utility-payments-with-provider-failover-3jck</link>
      <guid>https://forem.com/easyclick/designing-for-failure-building-reliable-crypto-to-utility-payments-with-provider-failover-3jck</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;On paper, a crypto-to-utility pipeline is a simple sequence: verify the on-chain transaction, trigger a biller's API, and return a success state.&lt;/p&gt;

&lt;p&gt;In production, "simple" integrations are where the most expensive failures happen. When handling user funds, reliability isn't a feature; it’s the foundation. As the lead engineer, I built &lt;a href="https://elites.africa" rel="noopener noreferrer"&gt;Elites Africa&lt;/a&gt; with a Multi-Provider Failover strategy from day one. To balance high availability with latency constraints, I architected the system around a primary provider with a ‘hot’ secondary backup. This ensures we can recover from a provider failure in real-time without exceeding the timeout thresholds that degrade the user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: Designing for Failure
&lt;/h2&gt;

&lt;p&gt;I built the system to handle everything from airtime and data to electricity and Chowdeck (food delivery wallet) funding. Given the nature of crypto payments, the user expectation for "instant value" is high.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Lifecycle
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Selection &amp;amp; Intent:&lt;/strong&gt; The user picks a service (e.g., MTN Data). Instead of a live fetch, we serve this from our local cache to keep the UI snappy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Crypto Leg:&lt;/strong&gt; We leverage the &lt;strong&gt;Coinley SDK&lt;/strong&gt; for the wallet handshake. Once the success callback triggers, our backend takes over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous Execution:&lt;/strong&gt; Instead of relying on asynchronous webhooks that delay user certainty, we process bill payments synchronously. When a provider fails, we detect it immediately and can retry or refund without leaving users waiting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging &amp;amp; Attempts:&lt;/strong&gt; Every request is wrapped in a &lt;code&gt;PaymentAttempt&lt;/code&gt; entity. This gives us a granular audit trail of exactly which provider failed and why.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Problem: Data Fragmentation
&lt;/h2&gt;

&lt;p&gt;When we integrated our second provider, we hit a wall. Provider A might represent an MTN 1GB plan as &lt;code&gt;mtn-data-1gb&lt;/code&gt;, while Provider B uses an integer ID like &lt;code&gt;444402&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the primary provider fails, you can't simply "retry" with the second provider using the first one's metadata. The systems are fundamentally incompatible in their naming conventions and response structures. Without a translation layer, failover logic is dead on arrival.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Internal Code Mapping
&lt;/h2&gt;

&lt;p&gt;To solve this, I implemented an &lt;strong&gt;Internal Abstraction Layer&lt;/strong&gt;. We stopped treating provider codes as the source of truth. Instead, we introduced a standardized internal schema that represents the &lt;em&gt;logical service&lt;/em&gt;. At runtime, the system resolves &lt;code&gt;internalCode → provider-specific metadata&lt;/code&gt; based on provider availability&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Mapping Strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Internal Code Pattern&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Airtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{network}-airtime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mtn-airtime&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{network}-data-{value}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;glo-data-1000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Electricity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{disco}-electricity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ikeja-electricity&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We run a weekly synchronization script that pulls fresh metadata from all providers and maps them to these internal codes. This allows the backend to be provider-agnostic. When a payment fails on Provider A, the system looks up the same &lt;code&gt;internalCode&lt;/code&gt; for Provider B and executes the retry instantly.&lt;/p&gt;

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

&lt;p&gt;Every architecture has its "debt." Here is how we weighed our decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous Processing vs. Event-Driven:&lt;/strong&gt; By choosing synchronous calls, we prioritize user certainty over server throughput. The trade-off is increased latency during provider timeouts, but in fintech, a slow success is better than a fast "maybe."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database vs. Live Fetch:&lt;/strong&gt; Storing plans locally allows for provider switching without redeploying code. The downside is the "stale data" risk, which we mitigate with our weekly cron syncs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial Catalog Failover (80/20 Rule):&lt;/strong&gt; Not all plans exist on all providers (e.g., certain niche data bundles). Our fallback logic currently covers about 80% of our catalog. For the remaining 20%, we’ve accepted that a failure on the primary provider will result in a refund or manual intervention; a conscious choice to avoid over-engineering for edge cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;After 7 years in engineering, you realize that the "happy path" is easy. The real work is in the edge cases. At Elites Africa, we didn't just build a bill payment platform; we built a system that assumes providers will fail and has a plan for when they do.&lt;/p&gt;

&lt;p&gt;True architectural maturity isn't about the newest tech stack; it's about how gracefully your system fails.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>fintech</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>My Git Workflow and Essential Commands</title>
      <dc:creator>Yusuf Adeniyi</dc:creator>
      <pubDate>Fri, 01 Aug 2025 09:00:39 +0000</pubDate>
      <link>https://forem.com/easyclick/my-git-workflow-and-essential-commands-2plj</link>
      <guid>https://forem.com/easyclick/my-git-workflow-and-essential-commands-2plj</guid>
      <description>&lt;p&gt;Learning to use Git effectively isn’t always straightforward—especially for many of us developers. Git has a vast set of commands, and honestly, no one knows them all. However, some essential commands like &lt;code&gt;git pull&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt; are used regularly.&lt;/p&gt;

&lt;p&gt;While Git proficiency comes with experience, learning key commands and best practices early can significantly improve your workflow and make you a better developer. In this guide, I’ll share some Git commands I use regularly, starting with the basics and moving to more advanced ones. I hope it helps anyone reading this.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧱 Basic Git Commands
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git init&lt;/code&gt; &lt;br&gt;
Initialize a new Git repository in your current directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git status&lt;/code&gt;&lt;br&gt;
Shows the current state of your working directory and staging area.&lt;br&gt;
&lt;em&gt;Tip:&lt;/em&gt; Run this often to check what’s staged, modified, or untracked.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git clone &amp;lt;repository&amp;gt;&lt;/code&gt;&lt;br&gt;
Downloads a remote repository to your local machine.&lt;br&gt;
&lt;em&gt;Example:&lt;/em&gt; &lt;code&gt;git clone https://github.com/user/repo.git&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git add .&lt;/code&gt;&lt;br&gt;
Stage all changes in your working directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git add &amp;lt;filepath&amp;gt;&lt;/code&gt;&lt;br&gt;
Stage a specific file for commit.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git commit -m "message"&lt;/code&gt;&lt;br&gt;
Commit staged changes with a descriptive message.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git log&lt;/code&gt;&lt;br&gt;
View the commit history.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git checkout &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;br&gt;
Switch to an existing branch.&lt;br&gt;
&lt;em&gt;Tip:&lt;/em&gt; Consider using &lt;code&gt;git switch &amp;lt;branch&amp;gt;&lt;/code&gt; for a more intuitive experience.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git checkout -b &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;br&gt;
Create and switch to a new branch.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git branch&lt;/code&gt;&lt;br&gt;
List all branches.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git branch -D &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;br&gt;
Delete a branch.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git branch -M &amp;lt;old&amp;gt; &amp;lt;new&amp;gt;&lt;/code&gt;&lt;br&gt;
Rename a branch from &lt;code&gt;&amp;lt;old&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;new&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git pull&lt;/code&gt;&lt;br&gt;
Fetch and merge changes from the remote branch.&lt;br&gt;
&lt;em&gt;Tip:&lt;/em&gt; Use &lt;code&gt;git pull --rebase&lt;/code&gt; to avoid unnecessary merge commits.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git push&lt;/code&gt;&lt;br&gt;
Push your local commits to the remote repository.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git fetch&lt;/code&gt;&lt;br&gt;
Retrieve changes from the remote without merging.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git merge &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;br&gt;
Merge another branch into your current branch.&lt;/p&gt;


&lt;h2&gt;
  
  
  🚀 Advanced Git Commands
&lt;/h2&gt;

&lt;p&gt;These are often extensions of basic commands with extra arguments or specific use cases.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;git add -p &amp;lt;filepath&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Stage parts of a file instead of the whole file. It opens an interactive mode where you select specific "hunks" (sections of code) to stage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;y&lt;/code&gt;: stage this hunk
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt;: don’t stage this hunk
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;q&lt;/code&gt;: quit (don’t stage this or remaining hunks)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a&lt;/code&gt;: stage this hunk and all later hunks
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;d&lt;/code&gt;: don’t stage this hunk or later ones
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s&lt;/code&gt;: split the hunk into smaller chunks
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e&lt;/code&gt;: manually edit the hunk
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?&lt;/code&gt;: show help&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Mini story:&lt;/strong&gt; When I first started, I pushed large commits with unrelated changes. Using &lt;code&gt;git add -p&lt;/code&gt; helped me group changes logically, making code review easier.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  &lt;code&gt;git commit --amend&lt;/code&gt; or &lt;code&gt;git commit -a --no-edit&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Add new staged changes to the previous commit, especially if related.&lt;br&gt;
&lt;strong&gt;Warning:&lt;/strong&gt; Only use this for commits &lt;strong&gt;not yet pushed&lt;/strong&gt; to remote repositories.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;code&gt;git log branchA..branchB&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Shows commits in &lt;code&gt;branchB&lt;/code&gt; not in &lt;code&gt;branchA&lt;/code&gt;. Combine with &lt;code&gt;--oneline&lt;/code&gt; for readability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; branchA..branchB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;git diff branchA..branchB&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Shows code changes in &lt;code&gt;branchB&lt;/code&gt; that aren’t in &lt;code&gt;branchA&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;git diff --name-only branchA..branchB&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Shows &lt;strong&gt;filenames only&lt;/strong&gt; that differ between two branches.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;git cherry-pick &amp;lt;commit1&amp;gt; &amp;lt;commit2&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Apply specific commits from another branch onto your current branch. Useful when releasing select features or fixes.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;git stash&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Temporarily saves uncommitted changes to work on something else.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash push &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"WIP on feature X"&lt;/span&gt;
git stash pop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;git rebase -i&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Interactive rebase lets you clean up commit history—squash, reorder, or remove commits.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Best Practices and Advice
&lt;/h2&gt;



&lt;h3&gt;
  
  
  1. Commit Often, but Meaningfully
&lt;/h3&gt;

&lt;p&gt;Keep commits small and focused on a single purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Write Clear Commit Messages
&lt;/h3&gt;

&lt;p&gt;Explain the “why,” not just the “what.”&lt;br&gt;&lt;br&gt;
&lt;em&gt;Good:&lt;/em&gt; &lt;code&gt;Fix login error when password is empty&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Bad:&lt;/em&gt; &lt;code&gt;fix bug&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Never Rebase Shared Branches
&lt;/h3&gt;

&lt;p&gt;Rebasing rewrites history; only do it on local branches not pushed to others.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Use Branches for Features and Fixes
&lt;/h3&gt;

&lt;p&gt;Keep your main branch clean. Create new branches for features or bug fixes.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Pull Often, Push When Ready
&lt;/h3&gt;

&lt;p&gt;Regularly run &lt;code&gt;git pull --rebase&lt;/code&gt; to minimize conflicts. Push only when your code is ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Clean Up Before Merging
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;git rebase -i&lt;/code&gt; or squash commits to tidy history before merging.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; GitHub's &lt;strong&gt;Squash and merge&lt;/strong&gt; feature can do this for you automatically during pull request merges.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  7. Learn by Breaking (Locally)
&lt;/h3&gt;

&lt;p&gt;Experiment freely. You can recover lost commits using &lt;code&gt;git reflog&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Use &lt;code&gt;.gitignore&lt;/code&gt; Wisely
&lt;/h3&gt;

&lt;p&gt;Exclude environment files, dependencies, or secrets to keep your repo clean.&lt;/p&gt;




&lt;h2&gt;
  
  
  Useful Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/doc" rel="noopener noreferrer"&gt;Official Git Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://education.github.com/git-cheat-sheet-education.pdf" rel="noopener noreferrer"&gt;GitHub Git Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learngitbranching.js.org/" rel="noopener noreferrer"&gt;Interactive Git Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;💬 Got Questions or Tips?&lt;br&gt;
I wrote this guide to share how I use Git day-to-day and to help others who might be feeling overwhelmed by all the commands. If you have questions, feedback, or your own Git tips, feel free to drop them in the comments. I'd love to hear how you use Git or what helped you learn it!&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This is not an exhaustive list of Git commands, but it includes the ones I use the most. Git has a steep learning curve at first, but understanding just a handful of useful commands and when to use them can greatly improve your workflow and confidence.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
