<?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: Damola Adegbite</title>
    <description>The latest articles on DEV Community by Damola Adegbite (@dax-side).</description>
    <link>https://dev.to/dax-side</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%2F2850111%2F47d0ff13-ee69-464f-a0aa-084f0ffb5ef0.png</url>
      <title>DEV Community: Damola Adegbite</title>
      <link>https://dev.to/dax-side</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dax-side"/>
    <language>en</language>
    <item>
      <title>How Would I Build For Right Now</title>
      <dc:creator>Damola Adegbite</dc:creator>
      <pubDate>Wed, 27 May 2026 17:49:57 +0000</pubDate>
      <link>https://dev.to/dax-side/how-would-i-build-for-right-now-2fmm</link>
      <guid>https://dev.to/dax-side/how-would-i-build-for-right-now-2fmm</guid>
      <description>

&lt;p&gt;&lt;strong&gt;How Would I Build For Right Now&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's a woman selling food on the street. Two years in. She restocks, pays her supplier, feeds her kids, shows up tomorrow. Not rich, not struggling. Moving.&lt;/p&gt;

&lt;p&gt;Someone tells her she needs a proper system. Inventory tracking. Demand forecasting. A loyalty programme. She looks around and the people doing well all have these things. The businesses she admires run on proper systems. So she sets it up. Spends time and money getting it right.&lt;/p&gt;

&lt;p&gt;Six months later she's still on the street. The system is on her phone, untouched. Her margins are thinner from the setup cost. The customers she was supposed to retain with the loyalty programme don't own smartphones.&lt;/p&gt;

&lt;p&gt;She didn't fail because she was careless. She looked at people succeeding and copied what they were doing. Those businesses had enough money to be wrong. They could try a loyalty programme, watch it fail, absorb the cost, and move on. She couldn't. Same pattern, completely different consequence. Nobody told her that part.&lt;/p&gt;




&lt;p&gt;She also built for a version of her business she had no evidence for.&lt;/p&gt;

&lt;p&gt;She knew what sold each day. She knew when she ran out. She knew what she made each week. That was her data. That was her use case. Everything else the forecasting, the loyalty programme, the full system was a guess she dressed up as a plan.&lt;/p&gt;

&lt;p&gt;Your use case is defined by evidence, not aspiration. What you have data for is the only thing you are qualified to build for right now. Everything else is a guess wearing the clothes of planning.&lt;/p&gt;

&lt;p&gt;The data is what makes the problem real. No data, no problem, no justification to build for it.&lt;/p&gt;




&lt;p&gt;There is a version of her business where inventory tracking makes complete sense. Twenty vendors, three locations, a proper supply chain. The businesses she was copying were probably at that version or past it.&lt;/p&gt;

&lt;p&gt;She had no data saying her business was heading there. No evidence the loyalty programme would land. No proof the forecasting would matter. Just the image of people further along than her, doing things that worked for where they were, not where she was.&lt;/p&gt;

&lt;p&gt;The present needed to know what sold today, what to restock tomorrow, how much she made this week. A notebook solves that. She did not need a system. She needed to survive long enough to need one.&lt;/p&gt;




&lt;p&gt;Software is the same problem.&lt;/p&gt;

&lt;p&gt;You get a project, a deadline, a client. Before you write a line you are already making decisions about architecture, standards, what professional looks like. Most of those decisions happen on autopilot, based on what you were taught, what your peers are doing, what the successful companies are running on.&lt;/p&gt;

&lt;p&gt;Everyone knows Netflix uses microservices. Everyone knows the big fintech companies have event-driven architectures. So a new startup gets pitched the same thing on day one, by someone who means well, pointing at companies with thousands of engineers and years of data justifying every layer of that complexity.&lt;/p&gt;

&lt;p&gt;You have a 30-day deadline, one developer, three hundred users. Half of them are your own team testing the thing.&lt;/p&gt;

&lt;p&gt;Those companies had enough engineering capacity to absorb getting it wrong. You do not. Same pattern, completely different consequence. And you have no data for that complexity.&lt;/p&gt;




&lt;p&gt;This is not an argument against microservices or any pattern.&lt;/p&gt;

&lt;p&gt;Nothing is premature if you have evidence for it. The moment you have real traffic, real bottlenecks, a team size that makes a monolith genuinely painful, the architecture that fits that data is not premature. It is the answer the evidence is pointing to.&lt;/p&gt;

&lt;p&gt;The problem is never the pattern. It is the absence of data that justifies it.&lt;/p&gt;

&lt;p&gt;Some things are load-bearing from day one regardless of scale. Building anything with compliance requirements, anything where a wrong number means someone's rent does not go through tests, auditing, proper error handling are not optional there. You follow those standards because the data about what failure costs is already clear. A wrong transaction is not a bug you patch quietly. It is a disaster with a paper trail.&lt;/p&gt;

&lt;p&gt;That is the distinction. Some standards are justified by the data in front of you today. Others are borrowed from a version of the system you have no evidence for yet. The first category does not move. The second goes on the backlog.&lt;/p&gt;




&lt;p&gt;There is an order of operations that matters.&lt;/p&gt;

&lt;p&gt;Can this thing function today? Can it make money, deliver value, hold up under the weight it carries right now? Do you have data that tells you what today looks like?&lt;/p&gt;

&lt;p&gt;After the present is stable, what does the evidence say about where this is going?&lt;/p&gt;

&lt;p&gt;Most people reverse this. They architect for version three before version one has a single real user. The vendor bought a system for a business she had not built yet. The developer ships microservices to three hundred users because a company with three million users made it look like the right move.&lt;/p&gt;

&lt;p&gt;The product dies somewhere in the gap between the version they built for and the version that showed up.&lt;/p&gt;




&lt;p&gt;Before I start anything now, two questions.&lt;/p&gt;

&lt;p&gt;What does the data say this needs to work today? Not at scale. Not in six months. Today, with the users that exist, the deadline that is real, the team that is here.&lt;/p&gt;

&lt;p&gt;What does failure cost in this specific system? Is this the kind of system where a wrong number destroys someone's finances and their trust? Or is this one where a bug gets fixed in the next push and nobody lost sleep?&lt;/p&gt;

&lt;p&gt;Those answers tell me which standards are non-negotiable today and which ones get deferred. Deferred, not ignored. Ignoring means you forgot. Deferring means you made a deliberate call with full awareness of what it will cost later.&lt;/p&gt;

&lt;p&gt;The code is just the answers made real.&lt;/p&gt;




&lt;p&gt;The woman is still on the street. The system is still on her phone. The people she copied are still running their bigger businesses, probably unaware she was ever watching.&lt;/p&gt;

&lt;p&gt;Somewhere a developer is six hours into a distributed architecture for a product with three hundred users, building for a version of the problem they have no data for, wondering why the deadline feels impossible.&lt;/p&gt;

&lt;p&gt;Same pattern. Borrowed from people who could afford to be wrong with it. Applied somewhere it was never meant to land.&lt;/p&gt;




&lt;p&gt;I'm Damola, a backend engineer. Find the rest of this series on &lt;a href="https://github.com/dax-side/how-would-i-build" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Follow me on &lt;a href="https://dev.to/dax-side"&gt;Dev.to&lt;/a&gt; for the next one.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>career</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How Would I Build a Payment System That Doesn't Lose Money</title>
      <dc:creator>Damola Adegbite</dc:creator>
      <pubDate>Wed, 27 May 2026 17:46:53 +0000</pubDate>
      <link>https://dev.to/dax-side/how-would-i-build-a-payment-system-that-doesnt-lose-money-16ap</link>
      <guid>https://dev.to/dax-side/how-would-i-build-a-payment-system-that-doesnt-lose-money-16ap</guid>
      <description>&lt;p&gt;This is the first post in a series I'm calling How Would I Build. I take a real engineering problem, reason through it in plain language first, then attach the actual name for that reasoning at the end. Jargon comes last, not first.&lt;/p&gt;

&lt;p&gt;I'll be upfront, I didn't sit down and immediately know all the answers here. A lot of this came from asking "okay but why would that break" until something clicked. That's the energy I'm going for.&lt;/p&gt;

&lt;p&gt;Starting with this: how would you build a payment system that handles 10,000 transactions per second without losing a single one?&lt;/p&gt;




&lt;h2&gt;
  
  
  Two customers, one suya guy
&lt;/h2&gt;

&lt;p&gt;There's a suya spot near you. One guy grilling. Two customers walk up at the same time and both want the last stick.&lt;/p&gt;

&lt;p&gt;The suya guy has two hands. He can't serve both customers simultaneously. One of them has to wait.&lt;/p&gt;

&lt;p&gt;Now imagine he ignores that and just gives both customers the same stick. That's your payment bug.&lt;/p&gt;

&lt;p&gt;Two people hit withdraw on the same wallet at the exact same time. Both requests check the balance. Both see enough money. Both go through. You've just paid out twice what was in the account.&lt;/p&gt;

&lt;p&gt;My first instinct was just — lock it. But I had to figure out what "lock it" actually meant before it made sense.&lt;/p&gt;

&lt;p&gt;The answer is: whoever gets there first locks the wallet in the database before the second request can even read the balance. The second request waits. When the first one finishes, the second one checks the balance again and finds there's nothing left.&lt;/p&gt;

&lt;p&gt;In Postgres that's &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt;. First transaction locks the row, second waits outside. No double spend.&lt;/p&gt;

&lt;p&gt;That whole situation — multiple operations running at the same time and stepping on each other — is a concurrency problem. The specific bug where both read stale data before either writes is a race condition.&lt;/p&gt;




&lt;h2&gt;
  
  
  The suya guy gets popular. He buys a bigger grill.
&lt;/h2&gt;

&lt;p&gt;Business is good now. The queue is long. The suya guy thinks: I need a bigger grill. More charcoal. Better equipment. He upgrades everything.&lt;/p&gt;

&lt;p&gt;It helps. Orders come out faster. But the queue is still there. Because no matter how big the grill is, there's still one person running it. Customers still wait behind each other. And there's a limit to how big a grill can get anyway.&lt;/p&gt;

&lt;p&gt;That's vertical scaling. You give the same server more power — more CPU, more RAM, faster storage. It buys you time but it doesn't solve the problem. The ceiling just moves higher.&lt;/p&gt;

&lt;p&gt;At some point the suya guy realizes the grill isn't the problem. One location is the problem.&lt;/p&gt;

&lt;p&gt;So he opens a second spot. Then a third. Now three customers are being served at the same time across three locations. The individual grills are smaller but the total output is way higher.&lt;/p&gt;

&lt;p&gt;That's horizontal scaling. More machines, not a bigger machine. The limit is now cost, not hardware.&lt;/p&gt;




&lt;h2&gt;
  
  
  But not everything needs the main guy
&lt;/h2&gt;

&lt;p&gt;The suya guy notices something. Most people walking up aren't placing new orders. They're just checking if their order is ready. Those people are slowing down the main grill.&lt;/p&gt;

&lt;p&gt;So he puts a separate person to handle "is my order ready" questions. That person has a copy of the order list and handles all status checks. The main guy now only deals with new orders.&lt;/p&gt;

&lt;p&gt;Read traffic stops choking write performance.&lt;/p&gt;

&lt;p&gt;That's read replicas. The main database handles writes. Copies of it handle reads.&lt;/p&gt;

&lt;p&gt;But here's where I got confused for a bit. Replicas don't fix everything. If 10,000 new orders are coming in per second, the main grill still has a ceiling on how many it can process. Adding more status-check people doesn't change that.&lt;/p&gt;

&lt;p&gt;To scale new orders, you need more main grills. Not copies — actual separate locations taking orders. Customers 1 to 2,000 go to location one. 2,000 to 4,000 go to location two. New orders are now spread across multiple spots.&lt;/p&gt;

&lt;p&gt;That's sharding.&lt;/p&gt;




&lt;h2&gt;
  
  
  What if someone at spot one wants to pay for a friend at spot three
&lt;/h2&gt;

&lt;p&gt;Someone at spot one wants to cover their friend's order at spot three. The suya guy at spot one collects the money. Before he can radio spot three to release the order, his phone dies. Spot three never gets the message. The money is gone and the friend got no suya.&lt;/p&gt;

&lt;p&gt;My instinct was to get both spots to agree before anything moves. Spot one holds the money, spot three holds the order, and only when both confirm does anything proceed. That works but it's slow. And if the person coordinating between them disappears mid-process, both spots are frozen waiting for a signal that never comes.&lt;/p&gt;

&lt;p&gt;The simpler approach is to not wait for both at the same time. Spot one takes the money and writes down "I took this, spot three owes a release." If spot three never confirms, spot one automatically reverses the charge and refunds the customer. No coordination needed upfront. Each spot handles its own piece and knows exactly what to undo if something breaks.&lt;/p&gt;

&lt;p&gt;That pattern is called a SAGA. Failure recovery for distributed operations. Each step has a compensating action ready before anything runs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multiple spots, but who tells customers where to go
&lt;/h2&gt;

&lt;p&gt;You've opened five suya spots. A new customer walks up. They see five spots and have no idea which one has the shortest queue. They just stand there confused.&lt;/p&gt;

&lt;p&gt;So you put someone at the entrance whose only job is to look at all five spots and point each new customer to the next free one. The customer doesn't think about it. They just get directed.&lt;/p&gt;

&lt;p&gt;That's a load balancer. It sits in front of your servers and distributes incoming traffic so no single server carries everything. Nginx handles this well.&lt;/p&gt;

&lt;p&gt;But now the person at the entrance is a single point of failure. They go down, everyone is lost.&lt;/p&gt;

&lt;p&gt;So you have a backup person standing right next to them. If the first one goes down, the backup steps in automatically. That switch is called failover. Running both is a high availability setup.&lt;/p&gt;

&lt;p&gt;Rate limiting sits on top of this. You cap how many orders one person can place per minute. It won't solve legitimate volume but it stops bad actors from making the problem worse.&lt;/p&gt;




&lt;h2&gt;
  
  
  Something goes wrong and you have no idea which spot caused it
&lt;/h2&gt;

&lt;p&gt;At 10,000 transactions per second, a failed transaction could have broken at the entrance, the order taker, the payment handler, the records person, or the notification sender. Logs scattered across twenty servers.&lt;/p&gt;

&lt;p&gt;I genuinely didn't think about this until I asked myself — if something breaks at this scale, where do I even start looking?&lt;/p&gt;

&lt;p&gt;The fix is to give every order a unique ticket number the moment it enters the system, then write that ticket number into every record that order touches across every spot.&lt;/p&gt;

&lt;p&gt;When something breaks, you search for that ticket number and see the full journey of that order in one place. Every step, every spot, in order.&lt;/p&gt;

&lt;p&gt;That ticket number is called a correlation ID. Following a request across services using it is called distributed tracing.&lt;/p&gt;

&lt;p&gt;Tools like Datadog or the ELK stack pull logs from all your servers into one searchable place. Elasticsearch stores the logs. Kibana is the dashboard you use to search them.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the full picture looks like
&lt;/h2&gt;

&lt;p&gt;A request comes in. The load balancer directs it to one of several servers, with a standby ready if the active one dies. Rate limiting filters abuse. The server processes the transaction with pessimistic locking to prevent race conditions. Reads hit replicas, writes hit the primary. Sharding distributes write load across multiple database nodes. Cross-spot transactions use SAGA so failures don't leave money in limbo. Every step logs with a correlation ID that flows into a centralized system where any transaction can be traced end to end in seconds.&lt;/p&gt;

&lt;p&gt;That's a payment system that doesn't lose money.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Damola, a backend engineer. Find the rest of this series on GitHub. Follow me on &lt;a href="https://dev.to/dax-side"&gt;Dev.to&lt;/a&gt; for the next one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>backend</category>
      <category>programming</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>How Would I Build a Payment System That Doesn't Lose Money</title>
      <dc:creator>Damola Adegbite</dc:creator>
      <pubDate>Thu, 21 May 2026 23:05:11 +0000</pubDate>
      <link>https://dev.to/dax-side/how-would-i-build-a-payment-system-that-doesnt-lose-money-2pho</link>
      <guid>https://dev.to/dax-side/how-would-i-build-a-payment-system-that-doesnt-lose-money-2pho</guid>
      <description>&lt;h2&gt;
  
  
  How Would I Build a Payment System That Doesn't Lose Money
&lt;/h2&gt;

&lt;p&gt;This is the first post in a series called &lt;em&gt;How Would I Build&lt;/em&gt;. I take a real engineering problem, reason through it in plain language, then name the concept at the end. Jargon comes last.&lt;/p&gt;

&lt;p&gt;I didn't sit down knowing all of this. Most of it came from asking "okay but why would that break" until something clicked.&lt;/p&gt;

&lt;p&gt;Starting here: how do you build a payment system that handles 10,000 transactions per second without losing a single one?&lt;/p&gt;




&lt;h2&gt;
  
  
  Two customers, one suya guy
&lt;/h2&gt;

&lt;p&gt;There's a suya spot near you. One guy grilling. Two customers walk up at the same time and both want the last stick.&lt;/p&gt;

&lt;p&gt;He can't serve both at once. One of them has to wait.&lt;/p&gt;

&lt;p&gt;Now imagine he ignores that and gives both customers the same stick. That's your payment bug.&lt;/p&gt;

&lt;p&gt;Two people hit withdraw on the same wallet at the exact same time. Both requests check the balance. Both see enough money. Both go through. You've paid out twice what was in the account.&lt;/p&gt;

&lt;p&gt;My first instinct was: lock it. But I had to understand what "lock it" meant before it made any sense.&lt;/p&gt;

&lt;p&gt;Whoever gets there first locks the wallet in the database before the second request can even read the balance. The second request waits. When the first finishes, the second checks again and finds nothing left.&lt;/p&gt;

&lt;p&gt;In Postgres that's &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt;. First transaction locks the row, second waits outside. No double spend.&lt;/p&gt;

&lt;p&gt;Multiple operations running at the same time and stepping on each other is a &lt;strong&gt;concurrency problem&lt;/strong&gt;. The specific bug where both reads happen before either write is a &lt;strong&gt;race condition&lt;/strong&gt;. The lock pattern is &lt;strong&gt;pessimistic locking&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The suya guy gets popular. He buys a bigger grill.
&lt;/h2&gt;

&lt;p&gt;The queue is long now. So he upgrades everything. Better grill, more charcoal, faster setup.&lt;/p&gt;

&lt;p&gt;It helps. But the queue comes back. Because no matter how big the grill is, one person is still running it. There's a ceiling.&lt;/p&gt;

&lt;p&gt;That's &lt;strong&gt;vertical scaling&lt;/strong&gt;. More power to the same machine. It buys time, nothing more.&lt;/p&gt;

&lt;p&gt;At some point the grill isn't the problem. One location is.&lt;/p&gt;

&lt;p&gt;So he opens a second spot. Then a third. Three customers served simultaneously across three locations. Smaller grills, higher total output.&lt;/p&gt;

&lt;p&gt;That's &lt;strong&gt;horizontal scaling&lt;/strong&gt;. More machines, not a bigger one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Not everything needs the main guy
&lt;/h2&gt;

&lt;p&gt;Most people walking up aren't placing new orders. They're checking if their order is ready. Those people are slowing down the grill.&lt;/p&gt;

&lt;p&gt;So he puts a separate person on "is my order ready" questions. That person has a copy of the order list and handles all status checks. The main guy handles new orders only.&lt;/p&gt;

&lt;p&gt;Read traffic stops choking write performance.&lt;/p&gt;

&lt;p&gt;That's &lt;strong&gt;read replicas&lt;/strong&gt;. The main database handles writes. Copies handle reads.&lt;/p&gt;

&lt;p&gt;But replicas don't fix everything. If 10,000 new orders are coming in per second, the main grill still has a ceiling. To scale writes, you need more main grills — actual separate locations taking orders, not copies. Customers 1 to 2,000 go to location one. 2,000 to 4,000 go to location two.&lt;/p&gt;

&lt;p&gt;That's &lt;strong&gt;sharding&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What if someone at spot one wants to pay for a friend at spot three
&lt;/h2&gt;

&lt;p&gt;Spot one collects the money. Before the suya guy can radio spot three to release the order, his phone dies. Spot three never gets the message. The money is gone and the friend got no suya.&lt;/p&gt;

&lt;p&gt;Getting both spots to agree before anything moves works, but it's slow. And if the person coordinating disappears mid-process, both spots freeze waiting for a signal that never comes.&lt;/p&gt;

&lt;p&gt;The better approach: don't wait for both at once. Spot one takes the money and writes down "I took this, spot three owes a release." If spot three never confirms, spot one reverses the charge. Each step knows exactly what to undo if something breaks. No upfront coordination.&lt;/p&gt;

&lt;p&gt;That pattern is a &lt;strong&gt;SAGA&lt;/strong&gt;. Every step has a compensating action ready before anything runs. Stripe uses this for cross-service money movement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multiple spots, but who directs customers
&lt;/h2&gt;

&lt;p&gt;Five suya spots now. A new customer walks up and has no idea which one has the shortest queue.&lt;/p&gt;

&lt;p&gt;So someone stands at the entrance. Their only job is to look at all five spots and point each new customer to the next free one.&lt;/p&gt;

&lt;p&gt;That's a &lt;strong&gt;load balancer&lt;/strong&gt;. It sits in front of your servers and distributes incoming traffic. Nginx handles this well.&lt;/p&gt;

&lt;p&gt;That person at the entrance is now a single point of failure. So a backup stands right next to them. First one goes down, the backup steps in. That switch is &lt;strong&gt;failover&lt;/strong&gt;. Running both is a &lt;strong&gt;high availability&lt;/strong&gt; setup.&lt;/p&gt;

&lt;p&gt;Rate limiting sits on top of this. One customer can only place so many orders per minute. It won't solve legitimate volume, but it stops bad actors from making things worse.&lt;/p&gt;




&lt;h2&gt;
  
  
  Something breaks and you don't know where
&lt;/h2&gt;

&lt;p&gt;At 10,000 transactions per second, a failed transaction could have broken at the entrance, the order taker, the payment handler, the records person, or the notification sender. Logs across twenty servers.&lt;/p&gt;

&lt;p&gt;The fix: stamp every transaction with a unique ID the moment it enters the system, something like &lt;code&gt;txn-8f3a2c&lt;/code&gt;, and write that ID into every log it touches. When something breaks, you search that one ID and see the full journey in one place.&lt;/p&gt;

&lt;p&gt;That ID is a &lt;strong&gt;correlation ID&lt;/strong&gt;. Following a request across services using it is &lt;strong&gt;distributed tracing&lt;/strong&gt;. Tools like Datadog or the ELK stack pull all those logs into one searchable place.&lt;/p&gt;




&lt;h2&gt;
  
  
  The full picture
&lt;/h2&gt;

&lt;p&gt;A request comes in. The load balancer sends it to one of several servers, with a standby ready if the active one dies. Rate limiting filters abuse. The server runs &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt; to lock the row. Reads hit replicas, writes hit the primary. Sharding splits write load across multiple database nodes. Cross-node transactions use SAGAs so failures don't leave money in limbo. Every step logs a correlation ID that flows into a centralised system where any transaction can be traced end to end.&lt;/p&gt;

&lt;p&gt;The suya guy who once stood alone at one grill now runs five locations, a backup at every entrance, a separate person on status checks, and a ledger that knows exactly what to reverse when something goes wrong.&lt;/p&gt;

&lt;p&gt;That's a payment system that doesn't lose money.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Damola, a backend engineer. Find the rest of this series on &lt;a href="https://github.com/dax-side/how-would-i-build" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Follow me on &lt;a href="https://dev.to/dax-side"&gt;Dev.to&lt;/a&gt; for the next one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>backend</category>
      <category>programming</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>I built a tool that shows developers what their backend costs the planet</title>
      <dc:creator>Damola Adegbite</dc:creator>
      <pubDate>Sat, 18 Apr 2026 19:44:55 +0000</pubDate>
      <link>https://dev.to/dax-side/i-built-a-tool-that-shows-developers-what-their-backend-costs-the-planet-jm1</link>
      <guid>https://dev.to/dax-side/i-built-a-tool-that-shows-developers-what-their-backend-costs-the-planet-jm1</guid>
      <description>&lt;p&gt;&lt;strong&gt;I built a tool that shows developers what their backend costs the planet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-04-16"&gt;Weekend Challenge: Earth Day Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




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

&lt;p&gt;carbondebt is a cloud emissions dashboard for backend developers. Put in your cloud provider, region, and monthly compute hours. It calculates your CO2e footprint, breaks it down by service, and uses Google Gemini to return ranked actions with estimated carbon savings per month.&lt;/p&gt;

&lt;p&gt;No account. No setup. Results in under ten seconds.&lt;/p&gt;

&lt;p&gt;Developers track technical debt across quarters. We argue about it in code review, carry it into sprints. But there is another kind of debt building up in every infrastructure decision, one that never shows up in your AWS bill. Carbon debt works the same way. It compounds quietly and stays invisible until someone surfaces it. This tool surfaces it.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://carbondebt-client.vercel.app" rel="noopener noreferrer"&gt;carbondebt-client.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the app, select AWS, set the region to us-east-1, leave compute hours at 720. Then switch to eu-north-1. Stockholm's grid is 76% cleaner than Virginia's. That difference is a decision most developers have never been asked to make.&lt;/p&gt;

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dax-side" rel="noopener noreferrer"&gt;
        dax-side
      &lt;/a&gt; / &lt;a href="https://github.com/dax-side/carbondebt" rel="noopener noreferrer"&gt;
        carbondebt
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;carbondebt&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;carbondebt is a cloud emissions dashboard for developers
Enter your workload numbers, then get a monthly footprint estimate and a ranked action list.&lt;/p&gt;
&lt;p&gt;Live demo: &lt;a href="https://carbondebt-client.vercel.app" rel="nofollow noopener noreferrer"&gt;carbondebt-client.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What you get&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Total monthly CO2e&lt;/li&gt;
&lt;li&gt;Grid intensity for the selected region&lt;/li&gt;
&lt;li&gt;Emissions split by service (compute, database, storage, network, lambda)&lt;/li&gt;
&lt;li&gt;Best lower-carbon region inside the same cloud provider&lt;/li&gt;
&lt;li&gt;Gemini suggestions sorted by estimated monthly savings&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How carbon is calculated&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The backend uses scope 2 location-based emissions from local region intensity data.
Intensity values come from Ember Global Electricity Review 2025.&lt;/p&gt;
&lt;p&gt;Formula:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;CO2e (kg) = kWh * grid_intensity (gCO2e/kWh) / 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Source file for region values: &lt;code&gt;server/src/data/regions.ts&lt;/code&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stack&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Frontend: React, Vite, TypeScript, Tailwind CSS&lt;/li&gt;
&lt;li&gt;Backend: Node.js, Express, TypeScript&lt;/li&gt;
&lt;li&gt;Validation: Zod&lt;/li&gt;
&lt;li&gt;AI: Google Gemini&lt;/li&gt;
&lt;li&gt;Logging: Morgan + Winston&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;API&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;GET /api/health&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Returns service status.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;POST /api/carbon&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Request body:&lt;/p&gt;
&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;{
  &lt;span class="pl-ent"&gt;"provider"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;aws&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
  &lt;span class="pl-ent"&gt;"region"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;us-east-1&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"computeHours"&lt;/span&gt;: &lt;span class="pl-c1"&gt;720&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dax-side/carbondebt" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The frontend is React with TypeScript and Tailwind. The backend is Node.js and Express. No database. Every request is self-contained.&lt;/p&gt;

&lt;p&gt;Carbon calculations run locally on the server using regional grid intensity data from the Ember Global Electricity Review 2025 dataset. No third-party emissions API. The numbers are transparent and traceable to the source.&lt;/p&gt;

&lt;p&gt;One thing I added that changed how the data reads: the "equivalent to X km driven" metric. Saying a workload produces 125 kg CO2e per month is accurate but abstract. Converting it to kilometres driven makes the number land for people who have never thought about this before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini integration:&lt;/strong&gt; After the carbon report is calculated, it goes to Gemini which returns 3 to 5 ranked infrastructure suggestions specific to the submitted workload. Not generic advice. Things like "switch to Graviton3 instances" or "stop non-production servers outside business hours" with an estimated kg saving attached to each one. The formula gives you the number. Gemini tells you what to do with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Google Gemini&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gemini takes the carbon report and turns it into something a developer can act on today. Each suggestion is ranked by estimated monthly CO2e saving and tied to the actual workload submitted. If Gemini is unavailable, the carbon report still renders. The suggestions are an addition, not a dependency.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>GitNotion — GitHub to Notion sync with AI reports</title>
      <dc:creator>Damola Adegbite</dc:creator>
      <pubDate>Mon, 16 Mar 2026 11:09:57 +0000</pubDate>
      <link>https://dev.to/dax-side/i-built-an-mcp-server-that-syncs-github-into-notion-and-generates-ai-reports-5ao6</link>
      <guid>https://dev.to/dax-side/i-built-an-mcp-server-that-syncs-github-into-notion-and-generates-ai-reports-5ao6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;GitNotion is an MCP server that pulls GitHub activity into Notion and uses Gemini to write reports on top of it. Point it at any repo and it syncs issues, PRs, and commits into structured Notion databases, then generates weekly summaries, release notes, and contributor breakdowns. All free APIs, ships via npx.&lt;/p&gt;

&lt;p&gt;Eight MCP tools, accessible from Claude Desktop, Copilot, or any MCP client:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setup_workspace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates 4 Notion databases under a parent page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sync_issues&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pulls GitHub issues into Notion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sync_pull_requests&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pulls PRs into Notion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sync_commits&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logs recent commits to Notion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;generate_summary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sends last week of activity to Gemini, writes a summary page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;generate_release_notes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generates release notes from merged PRs and commits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_contributor_insights&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-contributor stats with an AI written report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;full_sync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs everything above in sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Re-running any sync tool updates existing entries. No duplicates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq96nob80sygj17atbgm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq96nob80sygj17atbgm2.png" alt="Notion page showing 4 databases created by setup_workspace: Issues, Pull Requests, Commits, and Summaries for vuejs/core"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/-S-R8Pg7BA4"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Show us the code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/dax-side/gitnotion" rel="noopener noreferrer"&gt;https://github.com/dax-side/gitnotion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;npm:&lt;/strong&gt; &lt;code&gt;npx -y gitnotion&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Notion MCP
&lt;/h2&gt;

&lt;p&gt;GitNotion embeds the official &lt;code&gt;@notionhq/notion-mcp-server&lt;/code&gt; as a dependency and uses it as the write layer for all page operations. The architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Agent (Claude, Copilot, etc.)
    ↓ MCP protocol
GitNotion MCP Server  ← custom server
    ├── GitHub API (read repo data)
    ├── Gemini API (generate reports)
    └── Official Notion MCP Server  ← all page/block writes go here
         └── Notion API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a sync or AI tool runs, GitNotion spawns the official Notion MCP server as a subprocess, connects to it as an MCP client, and routes all page create, update, and block append calls through it using &lt;code&gt;API-post-page&lt;/code&gt;, &lt;code&gt;API-patch-page&lt;/code&gt;, and &lt;code&gt;API-patch-block-children&lt;/code&gt;. Database creation goes through the Notion SDK directly since the official server doesn't expose that endpoint.&lt;/p&gt;

&lt;p&gt;Gemini returns markdown. Notion doesn't accept markdown — it wants structured block objects for every heading, list item, table row, and inline annotation. The converter handles all of that, then the blocks get sent to the official MCP server via &lt;code&gt;API-patch-block-children&lt;/code&gt; in chunks of 100. Chunking is needed because passing large content in a single call times out. The pattern is: create the page first with no children, then append in batches.&lt;/p&gt;

&lt;p&gt;To use it, add this to your MCP client config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gitnotion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gitnotion"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GITHUB_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GITHUB_REPO"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"owner/repo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"NOTION_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"NOTION_PARENT_PAGE_ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GEMINI_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four free things needed: GitHub token, Notion integration token (connected to your page via &lt;code&gt;•••&lt;/code&gt; &amp;gt; Connections), Gemini API key, and the Notion parent page ID from the URL. Ask your MCP client to run &lt;code&gt;setup_workspace&lt;/code&gt;, then pass the returned database IDs to &lt;code&gt;full_sync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27zyial36zlzlvca4r4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27zyial36zlzlvca4r4a.png" alt="Weekly summary page in Notion for vuejs/core showing bullet points, bold highlights, and inline code rendered from Gemini output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx2perkdeu1mk902z2ky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx2perkdeu1mk902z2ky.png" alt="Contributor insights page in Notion showing a table with columns for Contributor, Commits, PRs Created, PRs Merged, and Issues"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>notionchallenge</category>
      <category>mcp</category>
      <category>devchallenge</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
