<?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: Obinna Victor</title>
    <description>The latest articles on DEV Community by Obinna Victor (@sopuruchii).</description>
    <link>https://dev.to/sopuruchii</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%2F3958363%2F3ad1f999-7860-4e9a-861a-3c6ca445179c.jpg</url>
      <title>DEV Community: Obinna Victor</title>
      <link>https://dev.to/sopuruchii</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sopuruchii"/>
    <language>en</language>
    <item>
      <title>production-grade CLI that automates GitHub PR management and changelog generation.</title>
      <dc:creator>Obinna Victor</dc:creator>
      <pubDate>Thu, 06 Aug 2026 12:11:34 +0000</pubDate>
      <link>https://dev.to/sopuruchii/production-grade-cli-that-automates-github-pr-management-and-changelog-generation-cc7</link>
      <guid>https://dev.to/sopuruchii/production-grade-cli-that-automates-github-pr-management-and-changelog-generation-cc7</guid>
      <description>&lt;p&gt;In plain English, gitdevflow helps developers save time and reduce repetitive work when managing projects on GitHub. It automates two main tasks:&lt;/p&gt;

&lt;p&gt;Managing Pull Requests (PRs) : It can list, check, label, and even create PRs automatically. For example, instead of manually adding labels like “bug” or “feature” to every PR, gitdevflow does it for you based on rules you set. It also checks if PR titles follow a consistent format, so your project stays organized without manual policing.&lt;/p&gt;

&lt;p&gt;Generating Release Notes (Changelogs): When you release a new version of your software, you usually need to compile a list of what changed. gitdevflow scans all the merged pull requests between two versions, groups them by category (like “New Features”, “Bug Fixes”), and writes a clean, readable changelog ready to share with your users.&lt;/p&gt;

&lt;p&gt;So, instead of spending hours on busywork like labeling PRs or writing release notes by hand, a developer can run a single command and let gitdevflow handle it in seconds. It’s like having a personal assistant for your GitHub repository’s housekeeping.&lt;/p&gt;

&lt;p&gt;📦 pip install gitdevflow&lt;br&gt;
🔗 pypi.org/project/gitdevflow&lt;/p&gt;

&lt;p&gt;📖 Docs: blockforge-dev.github.io/gitdevflow&lt;br&gt;&lt;br&gt;
⭐ GitHub: github.com/BlockForge-Dev/gitdevflow&lt;/p&gt;

&lt;h1&gt;
  
  
  Python #OpenSource #CLI #DevTools #GitHub
&lt;/h1&gt;

</description>
      <category>devtools</category>
      <category>cli</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>Idempotency Is Not Just UUIDs: Handling Ambiguous Payment Outcomes</title>
      <dc:creator>Obinna Victor</dc:creator>
      <pubDate>Thu, 02 Jul 2026 10:35:12 +0000</pubDate>
      <link>https://dev.to/sopuruchii/idempotency-is-not-just-uuids-handling-ambiguous-payment-outcomes-2dg3</link>
      <guid>https://dev.to/sopuruchii/idempotency-is-not-just-uuids-handling-ambiguous-payment-outcomes-2dg3</guid>
      <description>&lt;p&gt;Most developers learn idempotency as:&lt;/p&gt;

&lt;p&gt;“Send an idempotency key so the same request does not happen twice.”&lt;/p&gt;

&lt;p&gt;That is true.&lt;/p&gt;

&lt;p&gt;But in financial systems, it is not enough.&lt;/p&gt;

&lt;p&gt;Idempotency is not just about generating a UUID.&lt;/p&gt;

&lt;p&gt;It is about making sure the same financial action cannot be executed twice, even when workers crash, providers timeout, webhooks arrive late, and clients retry.&lt;/p&gt;

&lt;p&gt;This becomes even more important when the action touches money:&lt;/p&gt;

&lt;p&gt;payment&lt;br&gt;
transfer&lt;br&gt;
refund&lt;br&gt;
stablecoin transfer&lt;br&gt;
blockchain transaction submission&lt;br&gt;
AI-agent requested financial action&lt;/p&gt;

&lt;p&gt;This is one of the core problems I’m thinking about while building Azums, a non-custodial durable financial execution layer for APIs and AI agents.&lt;/p&gt;

&lt;p&gt;Azums launches with Paystack payments, Paystack transfers, Paystack refunds, Solana transactions, and Solana USDT transfers.&lt;/p&gt;

&lt;p&gt;But before talking about Azums, let’s talk about the real problem.&lt;/p&gt;

&lt;p&gt;The dangerous payment flow&lt;/p&gt;

&lt;p&gt;Imagine this flow:&lt;/p&gt;

&lt;p&gt;Client&lt;br&gt;
  → Your API&lt;br&gt;
    → Payment Provider&lt;br&gt;
      → Provider processes request&lt;/p&gt;

&lt;p&gt;Your system calls the provider.&lt;/p&gt;

&lt;p&gt;Then the request times out.&lt;/p&gt;

&lt;p&gt;What do you do?&lt;/p&gt;

&lt;p&gt;Many systems do this:&lt;/p&gt;

&lt;p&gt;timeout = failed&lt;br&gt;
retry = try again&lt;/p&gt;

&lt;p&gt;That is dangerous.&lt;/p&gt;

&lt;p&gt;Because the provider may have already received and processed the request.&lt;/p&gt;

&lt;p&gt;So your system may be in this state:&lt;/p&gt;

&lt;p&gt;Your API thinks: failed&lt;br&gt;
Provider state: maybe succeeded&lt;br&gt;
Customer state: unknown&lt;/p&gt;

&lt;p&gt;That is not failure.&lt;/p&gt;

&lt;p&gt;That is an ambiguous outcome.&lt;/p&gt;

&lt;p&gt;And ambiguous outcomes require a different design.&lt;/p&gt;

&lt;p&gt;The wrong way to use idempotency&lt;/p&gt;

&lt;p&gt;A common mistake is generating a new UUID for every retry.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;const idempotencyKey = crypto.randomUUID();&lt;/p&gt;

&lt;p&gt;await provider.transfer({&lt;br&gt;
  amount,&lt;br&gt;
  recipient,&lt;br&gt;
  idempotencyKey,&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;This looks fine until retry happens.&lt;/p&gt;

&lt;p&gt;If the client or server creates a new key on retry, the provider may treat it as a new financial action.&lt;/p&gt;

&lt;p&gt;That defeats the point of idempotency.&lt;/p&gt;

&lt;p&gt;The key idea is:&lt;/p&gt;

&lt;p&gt;Same financial action must map to the same idempotency identity.&lt;/p&gt;

&lt;p&gt;For many systems, a better key is derived from stable business identity:&lt;/p&gt;

&lt;p&gt;workspace_id + action_type + external_reference&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;workspace_123:paystack_transfer:order_789&lt;/p&gt;

&lt;p&gt;or:&lt;/p&gt;

&lt;p&gt;workspace_123:refund:payment_ref_456:refund_001&lt;/p&gt;

&lt;p&gt;The key should represent the action, not the attempt.&lt;/p&gt;

&lt;p&gt;Action vs attempt&lt;/p&gt;

&lt;p&gt;This distinction matters.&lt;/p&gt;

&lt;p&gt;A financial action is the thing the user or system wants:&lt;/p&gt;

&lt;p&gt;Refund ₦10,000 for payment X&lt;br&gt;
Transfer ₦50,000 to recipient Y&lt;br&gt;
Submit signed Solana USDT transfer Z&lt;/p&gt;

&lt;p&gt;An attempt is one try to execute that action.&lt;/p&gt;

&lt;p&gt;One action may have multiple attempts.&lt;/p&gt;

&lt;p&gt;But it should have only one final financial execution.&lt;/p&gt;

&lt;p&gt;A simplified model:&lt;/p&gt;

&lt;p&gt;financial_actions (&lt;br&gt;
  id,&lt;br&gt;
  workspace_id,&lt;br&gt;
  action_type,&lt;br&gt;
  idempotency_key,&lt;br&gt;
  payload_hash,&lt;br&gt;
  status,&lt;br&gt;
  provider_reference,&lt;br&gt;
  created_at,&lt;br&gt;
  updated_at&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;execution_attempts (&lt;br&gt;
  id,&lt;br&gt;
  financial_action_id,&lt;br&gt;
  attempt_number,&lt;br&gt;
  status,&lt;br&gt;
  started_at,&lt;br&gt;
  finished_at,&lt;br&gt;
  error_class,&lt;br&gt;
  provider_reference&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;The action is the root object.&lt;/p&gt;

&lt;p&gt;Attempts are evidence.&lt;/p&gt;

&lt;p&gt;Do not bill, receipt, or finalize based only on attempts.&lt;/p&gt;

&lt;p&gt;Finalize based on the root financial action and external truth.&lt;/p&gt;

&lt;p&gt;Payload fingerprinting&lt;/p&gt;

&lt;p&gt;The same idempotency key should not allow different payloads.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;First request:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "amount": 10000,&lt;br&gt;
  "recipient": "recipient_A"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Retry with same key but changed payload:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "amount": 50000,&lt;br&gt;
  "recipient": "recipient_B"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This should not execute.&lt;/p&gt;

&lt;p&gt;The system should return conflict.&lt;/p&gt;

&lt;p&gt;A common pattern:&lt;/p&gt;

&lt;p&gt;idempotency_key + payload_hash&lt;/p&gt;

&lt;p&gt;If the key exists and the payload hash matches, return the existing transaction.&lt;/p&gt;

&lt;p&gt;If the key exists and the payload hash differs, reject with conflict.&lt;/p&gt;

&lt;p&gt;Example behavior:&lt;/p&gt;

&lt;p&gt;same key + same payload = return existing transaction&lt;br&gt;
same key + different payload = 409 conflict&lt;br&gt;
new key + valid payload = create new transaction&lt;/p&gt;

&lt;p&gt;This prevents accidental or malicious mutation of a financial action.&lt;/p&gt;

&lt;p&gt;Retries need execution boundaries&lt;/p&gt;

&lt;p&gt;Retries are not always safe.&lt;/p&gt;

&lt;p&gt;A worker can fail at different points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;before provider call&lt;/li&gt;
&lt;li&gt;during provider call&lt;/li&gt;
&lt;li&gt;after provider accepted request&lt;/li&gt;
&lt;li&gt;after provider completed request&lt;/li&gt;
&lt;li&gt;before local database update&lt;/li&gt;
&lt;li&gt;before receipt creation&lt;/li&gt;
&lt;li&gt;before callback delivery&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each point requires different handling.&lt;/p&gt;

&lt;p&gt;If failure happens before provider submission, retry may be safe.&lt;/p&gt;

&lt;p&gt;If failure happens after provider submission, retry may duplicate money movement unless the system first checks provider truth.&lt;/p&gt;

&lt;p&gt;So the system needs an execution boundary:&lt;/p&gt;

&lt;p&gt;Before provider submission:&lt;br&gt;
  safe to retry&lt;/p&gt;

&lt;p&gt;After provider submission:&lt;br&gt;
  do not blindly retry&lt;br&gt;
  verify provider state first&lt;/p&gt;

&lt;p&gt;This is where many systems break.&lt;/p&gt;

&lt;p&gt;Webhooks are not enough&lt;/p&gt;

&lt;p&gt;Webhooks are useful, but they are not enough.&lt;/p&gt;

&lt;p&gt;A webhook can be:&lt;/p&gt;

&lt;p&gt;delayed&lt;br&gt;
duplicated&lt;br&gt;
missed&lt;br&gt;
delivered out of order&lt;br&gt;
received before internal state is ready&lt;/p&gt;

&lt;p&gt;A strong system treats webhook events as signals, not final truth.&lt;/p&gt;

&lt;p&gt;The safer model:&lt;/p&gt;

&lt;p&gt;Webhook = fast signal&lt;br&gt;
Provider verification = direct check&lt;br&gt;
Reconciliation = external truth comparison&lt;/p&gt;

&lt;p&gt;For Paystack, that means verifying payment, transfer, or refund status against Paystack.&lt;/p&gt;

&lt;p&gt;For Solana, that means checking signature, confirmation, finality, token movement, amount, recipient, and network.&lt;/p&gt;

&lt;p&gt;For Solana USDT, that also means verifying the correct token mint and token account movement.&lt;/p&gt;

&lt;p&gt;Receipt creation should happen after truth&lt;/p&gt;

&lt;p&gt;A receipt should not just mean:&lt;/p&gt;

&lt;p&gt;“Our worker finished.”&lt;/p&gt;

&lt;p&gt;A receipt should mean:&lt;/p&gt;

&lt;p&gt;“We have enough evidence to state the final outcome of this financial action.”&lt;/p&gt;

&lt;p&gt;That receipt should include:&lt;/p&gt;

&lt;p&gt;transaction id&lt;br&gt;
action type&lt;br&gt;
amount or asset&lt;br&gt;
rail&lt;br&gt;
provider or chain reference&lt;br&gt;
final outcome&lt;br&gt;
policy decision&lt;br&gt;
approval reference if needed&lt;br&gt;
reconciliation status&lt;br&gt;
receipt hash&lt;br&gt;
created timestamp&lt;/p&gt;

&lt;p&gt;This is important because customers do not only need logs.&lt;/p&gt;

&lt;p&gt;They need proof.&lt;/p&gt;

&lt;p&gt;What about AI agents?&lt;/p&gt;

&lt;p&gt;AI agents make this problem more urgent.&lt;/p&gt;

&lt;p&gt;If an AI agent can request a refund or transfer, the system must answer:&lt;/p&gt;

&lt;p&gt;Who is the agent?&lt;br&gt;
What can it request?&lt;br&gt;
What amount is allowed?&lt;br&gt;
Does this require approval?&lt;br&gt;
Was a one-use permission created?&lt;br&gt;
Was the permission consumed?&lt;br&gt;
Was execution submitted once?&lt;br&gt;
Was the outcome verified?&lt;br&gt;
Was a receipt created?&lt;/p&gt;

&lt;p&gt;An AI agent should not directly call the payment provider.&lt;/p&gt;

&lt;p&gt;It should request a financial action through a control layer.&lt;/p&gt;

&lt;p&gt;That control layer should enforce policy, approval, idempotency, receipts, reconciliation, and exceptions.&lt;/p&gt;

&lt;p&gt;This is one of the reasons I’m building Azums.&lt;/p&gt;

&lt;p&gt;A better lifecycle&lt;/p&gt;

&lt;p&gt;A safer financial execution lifecycle looks like this:&lt;/p&gt;

&lt;p&gt;Request received&lt;br&gt;
→ identity verified&lt;br&gt;
→ idempotency checked&lt;br&gt;
→ payload fingerprinted&lt;br&gt;
→ policy evaluated&lt;br&gt;
→ approval required if needed&lt;br&gt;
→ one-use permission created if needed&lt;br&gt;
→ execution queued&lt;br&gt;
→ provider or chain execution submitted once&lt;br&gt;
→ outcome tracked&lt;br&gt;
→ receipt created&lt;br&gt;
→ callback delivered&lt;br&gt;
→ reconciliation checked&lt;br&gt;
→ exception opened if reality disagrees&lt;/p&gt;

&lt;p&gt;This is more work than simply calling a provider API.&lt;/p&gt;

&lt;p&gt;But this is the work that makes financial execution trustworthy.&lt;/p&gt;

&lt;p&gt;How Azums approaches this&lt;/p&gt;

&lt;p&gt;Azums is being built as a durable financial execution layer for APIs and AI agents.&lt;/p&gt;

&lt;p&gt;The first launch scope is narrow:&lt;/p&gt;

&lt;p&gt;Paystack payments&lt;br&gt;
Paystack transfers&lt;br&gt;
Paystack refunds&lt;br&gt;
Solana transactions&lt;br&gt;
Solana USDT transfers&lt;/p&gt;

&lt;p&gt;Azums is non-custodial.&lt;/p&gt;

&lt;p&gt;It does not hold customer funds or private keys.&lt;/p&gt;

&lt;p&gt;The goal is to control execution around financial actions:&lt;/p&gt;

&lt;p&gt;identity&lt;br&gt;
idempotency&lt;br&gt;
policy&lt;br&gt;
approval&lt;br&gt;
one-use permissions&lt;br&gt;
provider or chain execution&lt;br&gt;
receipts&lt;br&gt;
callbacks&lt;br&gt;
reconciliation&lt;br&gt;
exceptions&lt;br&gt;
audit proof&lt;/p&gt;

&lt;p&gt;The key belief is:&lt;/p&gt;

&lt;p&gt;Financial systems should never silently fail, duplicate money movement, or pretend uncertainty is success.&lt;/p&gt;

&lt;p&gt;Final thought&lt;/p&gt;

&lt;p&gt;Idempotency is not just a UUID.&lt;/p&gt;

&lt;p&gt;It is a correctness contract.&lt;/p&gt;

&lt;p&gt;It says:&lt;/p&gt;

&lt;p&gt;This financial action has one identity, one payload, one lifecycle, and one final truth.&lt;/p&gt;

&lt;p&gt;When you combine idempotency with durable execution, receipts, reconciliation, and exceptions, you get a system that can survive the messy reality of financial infrastructure.&lt;/p&gt;

&lt;p&gt;That is the kind of system I believe more teams will need as APIs and AI agents start touching real money.&lt;/p&gt;

&lt;p&gt;I’m building Azums to explore that future.&lt;/p&gt;

&lt;p&gt;Follow the build at &lt;a href="https://www.getazums.com" rel="noopener noreferrer"&gt;getazums.com&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1szn9zqd5nhxhs4p0x1r.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1szn9zqd5nhxhs4p0x1r.png" alt=" " width="799" height="368"&gt;&lt;/a&gt;&lt;br&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flk6zqjjn9eh3whr5u52t.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flk6zqjjn9eh3whr5u52t.png" alt=" " width="799" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>fintech</category>
      <category>systemdesign</category>
      <category>webdev</category>
    </item>
    <item>
      <title>One RPC Provider Is Not Blockchain Reliability</title>
      <dc:creator>Obinna Victor</dc:creator>
      <pubDate>Sun, 28 Jun 2026 19:30:33 +0000</pubDate>
      <link>https://dev.to/sopuruchii/one-rpc-provider-is-not-blockchain-reliability-2i60</link>
      <guid>https://dev.to/sopuruchii/one-rpc-provider-is-not-blockchain-reliability-2i60</guid>
      <description>&lt;p&gt;-One RPC Provider Is Not Blockchain Reliability&lt;/p&gt;

&lt;p&gt;A lot of blockchain applications start with a very simple backend setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BACKEND_RPC_URL=https://some-rpc-provider.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then everything goes through that one provider.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;balance checks&lt;/li&gt;
&lt;li&gt;account reads&lt;/li&gt;
&lt;li&gt;transaction lookups&lt;/li&gt;
&lt;li&gt;latest block/slot checks&lt;/li&gt;
&lt;li&gt;transaction simulation&lt;/li&gt;
&lt;li&gt;transaction submission&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first, this feels fine.&lt;/p&gt;

&lt;p&gt;The app works.&lt;br&gt;
The backend can read chain state.&lt;br&gt;
The frontend can show balances.&lt;br&gt;
Transactions can be submitted.&lt;/p&gt;

&lt;p&gt;But in production, one RPC URL can quietly become a hidden source of fragility.&lt;/p&gt;

&lt;p&gt;Because one RPC provider is not the blockchain itself.&lt;/p&gt;

&lt;p&gt;It is only one gateway into the blockchain.&lt;/p&gt;

&lt;p&gt;If your entire backend depends on that one gateway, then your app is not only trusting the blockchain.&lt;/p&gt;

&lt;p&gt;It is trusting one provider’s availability, freshness, latency, rate limits, supported methods, and view of the chain.&lt;/p&gt;

&lt;p&gt;That is not reliability.&lt;/p&gt;

&lt;p&gt;That is a single point of failure.&lt;/p&gt;

&lt;p&gt;What RPC means in blockchain&lt;/p&gt;

&lt;p&gt;RPC means Remote Procedure Call.&lt;/p&gt;

&lt;p&gt;In general backend terms, RPC is a way for one program to ask another program or server to execute an operation and return a result.&lt;/p&gt;

&lt;p&gt;In blockchain systems, an RPC endpoint lets your application talk to a blockchain node.&lt;/p&gt;

&lt;p&gt;For example, your app may call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;getBalance
getAccountInfo
getTransaction
getLatestBlockhash
sendTransaction
simulateTransaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app asks the RPC node a question or submits a transaction.&lt;/p&gt;

&lt;p&gt;The RPC node responds or broadcasts the transaction to the network.&lt;/p&gt;

&lt;p&gt;So a typical design looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend / Backend
        ↓
RPC Provider
        ↓
Blockchain Network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is normal.&lt;/p&gt;

&lt;p&gt;The problem starts when this becomes your entire reliability model.&lt;/p&gt;

&lt;p&gt;The naive architecture&lt;/p&gt;

&lt;p&gt;A lot of apps are designed like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  ↓
Frontend
  ↓
Backend
  ↓
One RPC URL
  ↓
Blockchain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend trusts whatever the provider says.&lt;/p&gt;

&lt;p&gt;If the provider responds, the backend assumes the response is enough.&lt;/p&gt;

&lt;p&gt;If the provider times out, the backend treats it like failure.&lt;/p&gt;

&lt;p&gt;If the provider cannot find a transaction, the backend assumes the transaction is missing.&lt;/p&gt;

&lt;p&gt;If the provider is rate-limited, the app becomes degraded.&lt;/p&gt;

&lt;p&gt;This works on the happy path.&lt;/p&gt;

&lt;p&gt;It does not handle production reality well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure mode 1: the RPC provider is slow
&lt;/h2&gt;

&lt;p&gt;Imagine the backend needs a response within two seconds.&lt;/p&gt;

&lt;p&gt;The provider responds after ten seconds.&lt;/p&gt;

&lt;p&gt;Your backend times out.&lt;/p&gt;

&lt;p&gt;A beginner system may treat that timeout as failure.&lt;/p&gt;

&lt;p&gt;But timeout does not mean the blockchain failed.&lt;/p&gt;

&lt;p&gt;It means:&lt;/p&gt;

&lt;p&gt;My system did not receive an answer in time.&lt;/p&gt;

&lt;p&gt;That is not the same thing as:&lt;/p&gt;

&lt;p&gt;The operation failed on-chain.&lt;/p&gt;

&lt;p&gt;This distinction matters a lot.&lt;/p&gt;

&lt;p&gt;Especially when the request is related to transaction status or submission.&lt;/p&gt;

&lt;p&gt;Failure mode 2: the provider is rate-limited&lt;/p&gt;

&lt;p&gt;Now imagine your product starts getting more usage.&lt;/p&gt;

&lt;p&gt;Many users are checking balances.&lt;br&gt;
Workers are checking transaction status.&lt;br&gt;
Background jobs are polling chain state.&lt;br&gt;
The dashboard is refreshing operational data.&lt;/p&gt;

&lt;p&gt;Then the RPC provider starts returning rate-limit errors.&lt;/p&gt;

&lt;p&gt;If your backend only has one provider, your whole system becomes dependent on that provider’s quota.&lt;/p&gt;

&lt;p&gt;A better system should be able to fail over, shed load, cache safe reads, or route intelligently.&lt;br&gt;
 Failure mode 3: the provider is stale&lt;/p&gt;

&lt;p&gt;This is one of the most dangerous cases.&lt;/p&gt;

&lt;p&gt;Suppose your backend checks whether a transaction landed.&lt;/p&gt;

&lt;p&gt;Provider A says:&lt;/p&gt;

&lt;p&gt;Transaction not found&lt;/p&gt;

&lt;p&gt;But Provider B can already see it.&lt;/p&gt;

&lt;p&gt;If your backend only trusts Provider A, it may mark the transaction as failed or unknown too early.&lt;/p&gt;

&lt;p&gt;In blockchain systems, stale reads can create bad product behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wrong user balances&lt;/li&gt;
&lt;li&gt;wrong transaction status&lt;/li&gt;
&lt;li&gt;incorrect failure messages&lt;/li&gt;
&lt;li&gt;unnecessary retries&lt;/li&gt;
&lt;li&gt;confused operators&lt;/li&gt;
&lt;li&gt;broken reconciliation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One provider’s view is not always enough.&lt;/p&gt;

&lt;p&gt;Failure mode 4: provider-specific errors&lt;/p&gt;

&lt;p&gt;Sometimes one provider returns an error while another provider would have succeeded.&lt;/p&gt;

&lt;p&gt;This can happen because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provider outage&lt;/li&gt;
&lt;li&gt;regional latency&lt;/li&gt;
&lt;li&gt;method support differences&lt;/li&gt;
&lt;li&gt;rate limits&lt;/li&gt;
&lt;li&gt;stale indexers&lt;/li&gt;
&lt;li&gt;degraded nodes&lt;/li&gt;
&lt;li&gt;provider-specific bugs&lt;/li&gt;
&lt;li&gt;chain lag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the problem is not simply:&lt;/p&gt;

&lt;p&gt;Did the blockchain work?&lt;/p&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;p&gt;Is this provider giving my backend a reliable view of the blockchain?&lt;/p&gt;

&lt;p&gt;The dangerous part: knowing what actually happened&lt;/p&gt;

&lt;p&gt;In blockchain systems, the dangerous part is not only sending a transaction.&lt;/p&gt;

&lt;p&gt;The dangerous part is knowing what actually happened after the transaction was sent.&lt;/p&gt;

&lt;p&gt;Questions your backend should be able to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the transaction land?&lt;/li&gt;
&lt;li&gt;Did it fail?&lt;/li&gt;
&lt;li&gt;Is it still pending?&lt;/li&gt;
&lt;li&gt;Did the provider timeout before returning the signature?&lt;/li&gt;
&lt;li&gt;Did the provider return stale data?&lt;/li&gt;
&lt;li&gt;Did another provider see the transaction?&lt;/li&gt;
&lt;li&gt;Was the backend trusting incomplete information?&lt;/li&gt;
&lt;li&gt;Is it safe to retry?&lt;/li&gt;
&lt;li&gt;What evidence do we have?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your system cannot answer those questions, your operators are blind.&lt;/p&gt;

&lt;p&gt;And if operators are blind, users eventually feel it.&lt;/p&gt;

&lt;p&gt;A better architecture: RPC reliability layer&lt;/p&gt;

&lt;p&gt;A better architecture introduces an RPC reliability layer.&lt;/p&gt;

&lt;p&gt;User&lt;br&gt;
  ↓&lt;br&gt;
Frontend&lt;br&gt;
  ↓&lt;br&gt;
Backend&lt;br&gt;
  ↓&lt;br&gt;
RPC Gateway / Reliability Layer&lt;br&gt;
  ↓&lt;br&gt;
Provider A&lt;br&gt;
Provider B&lt;br&gt;
Provider C&lt;br&gt;
  ↓&lt;br&gt;
Blockchain Network&lt;/p&gt;

&lt;p&gt;The backend no longer directly trusts one provider.&lt;/p&gt;

&lt;p&gt;Instead, it routes through infrastructure that understands provider failure.&lt;/p&gt;

&lt;p&gt;This RPC layer can handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provider health checks&lt;/li&gt;
&lt;li&gt;request timeouts&lt;/li&gt;
&lt;li&gt;failover&lt;/li&gt;
&lt;li&gt;provider scoring&lt;/li&gt;
&lt;li&gt;method-aware routing&lt;/li&gt;
&lt;li&gt;safe caching&lt;/li&gt;
&lt;li&gt;request coalescing&lt;/li&gt;
&lt;li&gt;cross-provider validation&lt;/li&gt;
&lt;li&gt;operational response headers&lt;/li&gt;
&lt;li&gt;status visibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is mindset.&lt;/p&gt;

&lt;p&gt;The naive system says:&lt;/p&gt;

&lt;p&gt;I trust this one RPC URL.&lt;/p&gt;

&lt;p&gt;The better system says:&lt;/p&gt;

&lt;p&gt;I route through infrastructure that understands RPC failure.&lt;/p&gt;

&lt;p&gt;Provider failover&lt;/p&gt;

&lt;p&gt;Provider failover means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If Provider A fails, try Provider B.
If Provider B fails, try Provider C.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple idea, big impact.&lt;/p&gt;

&lt;p&gt;One provider being down should not mean your whole blockchain app is down.&lt;/p&gt;

&lt;p&gt;The system should track things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which provider is healthy&lt;/li&gt;
&lt;li&gt;which provider is slow&lt;/li&gt;
&lt;li&gt;which provider recently failed&lt;/li&gt;
&lt;li&gt;which provider is behind&lt;/li&gt;
&lt;li&gt;which provider is rate-limited&lt;/li&gt;
&lt;li&gt;which provider has better success rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it should route intelligently.&lt;/p&gt;

&lt;p&gt;Timeout handling&lt;/p&gt;

&lt;p&gt;Timeouts should be treated carefully.&lt;/p&gt;

&lt;p&gt;A timeout is not proof of failure.&lt;/p&gt;

&lt;p&gt;It is proof that the backend did not receive an answer in time.&lt;/p&gt;

&lt;p&gt;For read requests, retries are usually safer.&lt;/p&gt;

&lt;p&gt;For write requests like &lt;code&gt;sendTransaction&lt;/code&gt;, retry behavior needs more care.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because transaction submission and transaction status are not the same as reading account data.&lt;/p&gt;

&lt;p&gt;Your system needs method awareness.&lt;/p&gt;

&lt;p&gt;Method policy&lt;/p&gt;

&lt;p&gt;Not all RPC methods should be treated the same.&lt;/p&gt;

&lt;p&gt;Some methods are read-only.&lt;/p&gt;

&lt;p&gt;Some submit transactions.&lt;/p&gt;

&lt;p&gt;Some can be cached.&lt;/p&gt;

&lt;p&gt;Some should never be cached.&lt;/p&gt;

&lt;p&gt;Some are more important for correctness.&lt;/p&gt;

&lt;p&gt;Examples:&lt;br&gt;
getBalance              -&amp;gt; read&lt;br&gt;
getAccountInfo          -&amp;gt; read&lt;br&gt;
getTransaction          -&amp;gt; status/evidence read&lt;br&gt;
getLatestBlockhash      -&amp;gt; time-sensitive read&lt;br&gt;
simulateTransaction     -&amp;gt; simulation&lt;br&gt;
sendTransaction         -&amp;gt; write/broadcast&lt;/p&gt;

&lt;p&gt;A serious RPC gateway should have method policy.&lt;/p&gt;

&lt;p&gt;That policy can answer:&lt;/p&gt;

&lt;p&gt;Can this method be cached?&lt;br&gt;
Should this method be validated?&lt;br&gt;
Is this method consensus-critical?&lt;br&gt;
Is retry safe?&lt;br&gt;
Should this method use multiple providers?&lt;/p&gt;

&lt;p&gt;This is how you move from random RPC calls to infrastructure design.&lt;/p&gt;

&lt;p&gt;Request coalescing&lt;/p&gt;

&lt;p&gt;Request coalescing is another useful pattern.&lt;/p&gt;

&lt;p&gt;Imagine 100 requests ask for the same data at the same time.&lt;/p&gt;

&lt;p&gt;The naive system sends 100 identical upstream calls to the RPC provider.&lt;/p&gt;

&lt;p&gt;That increases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;load&lt;/li&gt;
&lt;li&gt;cost&lt;/li&gt;
&lt;li&gt;rate-limit risk&lt;/li&gt;
&lt;li&gt;latency pressure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A better gateway can notice that the requests are identical.&lt;/p&gt;

&lt;p&gt;It sends one upstream request and shares the result with the waiting callers.&lt;/p&gt;

&lt;p&gt;100 identical local requests&lt;br&gt;
          ↓&lt;br&gt;
1 upstream RPC call&lt;br&gt;
          ↓&lt;br&gt;
shared result&lt;/p&gt;

&lt;p&gt;That is request coalescing.&lt;/p&gt;

&lt;p&gt;It helps the backend stay stable under traffic.&lt;/p&gt;

&lt;p&gt;Caching, but carefully&lt;/p&gt;

&lt;p&gt;Caching can help, but it must be method-aware.&lt;/p&gt;

&lt;p&gt;The mistake is caching everything blindly.&lt;/p&gt;

&lt;p&gt;If you cache the wrong data, your app may show stale state.&lt;/p&gt;

&lt;p&gt;If you cache nothing, your app may hit rate limits faster and waste money.&lt;/p&gt;

&lt;p&gt;So caching should depend on the method.&lt;/p&gt;

&lt;p&gt;Questions to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this method safe to cache?&lt;/li&gt;
&lt;li&gt;How long should it be cached?&lt;/li&gt;
&lt;li&gt;Is this data user-facing?&lt;/li&gt;
&lt;li&gt;Is this data used for a financial or execution decision?&lt;/li&gt;
&lt;li&gt;Is stale data dangerous here?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caching is not just a performance feature.&lt;/p&gt;

&lt;p&gt;In blockchain infrastructure, it is also a correctness decision.&lt;/p&gt;

&lt;p&gt;Cross-provider validation&lt;/p&gt;

&lt;p&gt;For some important reads, one provider may not be enough.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Provider A: transaction not found
Provider B: transaction found
Provider C: transaction found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What should the backend believe?&lt;/p&gt;

&lt;p&gt;For every request, cross-provider validation may be too expensive.&lt;/p&gt;

&lt;p&gt;But for important reads around execution state, settlement, wallet safety, or transaction status, it can protect your system from trusting one stale provider.&lt;/p&gt;

&lt;p&gt;The point is not to call every provider all the time.&lt;/p&gt;

&lt;p&gt;The point is to know when one signal is too weak.&lt;/p&gt;

&lt;p&gt;Operator visibility&lt;/p&gt;

&lt;p&gt;A good RPC reliability layer should not hide what happened.&lt;/p&gt;

&lt;p&gt;It should expose operational truth.&lt;/p&gt;

&lt;p&gt;When something goes wrong, operators should be able to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which provider did we use?&lt;/li&gt;
&lt;li&gt;Did the provider timeout?&lt;/li&gt;
&lt;li&gt;Did we retry another provider?&lt;/li&gt;
&lt;li&gt;Was the response from cache?&lt;/li&gt;
&lt;li&gt;Was the request coalesced?&lt;/li&gt;
&lt;li&gt;Did we validate across providers?&lt;/li&gt;
&lt;li&gt;Did providers disagree?&lt;/li&gt;
&lt;li&gt;Was this method considered critical?&lt;/li&gt;
&lt;li&gt;Did the backend make a decision from enough evidence?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why response metadata matters.&lt;/p&gt;

&lt;p&gt;Infrastructure should not only return data.&lt;/p&gt;

&lt;p&gt;It should explain how the data was obtained.&lt;/p&gt;

&lt;p&gt;My RPC Gateway project&lt;/p&gt;

&lt;p&gt;I explored this idea in my RPC Gateway project.&lt;/p&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/BlockForge-Dev/RPC-Gateway" rel="noopener noreferrer"&gt;https://github.com/BlockForge-Dev/RPC-Gateway&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal of the project is not just to forward JSON-RPC requests.&lt;/p&gt;

&lt;p&gt;The goal is to make provider failure, latency variance, stale reads, and read disagreement visible.&lt;/p&gt;

&lt;p&gt;The project explores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multi-provider failover&lt;/li&gt;
&lt;li&gt;adaptive hedging&lt;/li&gt;
&lt;li&gt;predictive provider scoring&lt;/li&gt;
&lt;li&gt;Solana method policy&lt;/li&gt;
&lt;li&gt;request coalescing&lt;/li&gt;
&lt;li&gt;method-aware caching&lt;/li&gt;
&lt;li&gt;consensus validation for important reads&lt;/li&gt;
&lt;li&gt;provider health tracking&lt;/li&gt;
&lt;li&gt;response headers that explain selected provider, attempts, cache behavior, hedging, and validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also recorded a video explaining the idea here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/Mv7R9ISm0rA" rel="noopener noreferrer"&gt;https://youtu.be/Mv7R9ISm0rA&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bigger lesson
&lt;/h2&gt;

&lt;p&gt;Blockchain apps are not reliable just because they use a blockchain.&lt;/p&gt;

&lt;p&gt;Your backend can still be fragile.&lt;/p&gt;

&lt;p&gt;Your RPC provider can fail.&lt;br&gt;
Your worker can crash.&lt;br&gt;
Your webhook can arrive late.&lt;br&gt;
Your provider can be stale.&lt;br&gt;
Your transaction can be ambiguous.&lt;br&gt;
Your UI can show the wrong state.&lt;/p&gt;

&lt;p&gt;So serious blockchain infrastructure needs to design for failure from the beginning.&lt;/p&gt;

&lt;p&gt;Not after users complain.&lt;/p&gt;

&lt;p&gt;Not after funds are stuck.&lt;/p&gt;

&lt;p&gt;Not after operators are confused.&lt;/p&gt;

&lt;p&gt;From the beginning.&lt;/p&gt;

&lt;p&gt;Final thought&lt;/p&gt;

&lt;p&gt;One RPC provider is not blockchain reliability.&lt;/p&gt;

&lt;p&gt;One RPC URL is just one view into the chain.&lt;/p&gt;

&lt;p&gt;If that view is slow, stale, rate-limited, or down, your backend needs a better plan.&lt;/p&gt;

&lt;p&gt;That better plan includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple providers&lt;/li&gt;
&lt;li&gt;health checks&lt;/li&gt;
&lt;li&gt;timeouts&lt;/li&gt;
&lt;li&gt;failover&lt;/li&gt;
&lt;li&gt;method policy&lt;/li&gt;
&lt;li&gt;safe retries&lt;/li&gt;
&lt;li&gt;request coalescing&lt;/li&gt;
&lt;li&gt;caching with care&lt;/li&gt;
&lt;li&gt;cross-provider validation for important reads&lt;/li&gt;
&lt;li&gt;status visibility&lt;/li&gt;
&lt;li&gt;receipts&lt;/li&gt;
&lt;li&gt;reconciliation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the kind of backend and blockchain infrastructure I build.&lt;/p&gt;

&lt;p&gt;I build systems around reliable transaction execution, RPC reliability, operator truth, receipts, and reconciliation.&lt;/p&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/BlockForge-Dev" rel="noopener noreferrer"&gt;https://github.com/BlockForge-Dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>backend</category>
      <category>rust</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Why I treat API timeouts as "unknown", not failures</title>
      <dc:creator>Obinna Victor</dc:creator>
      <pubDate>Fri, 29 May 2026 12:23:31 +0000</pubDate>
      <link>https://dev.to/sopuruchii/why-i-treat-api-timeouts-as-unknown-not-failures-509a</link>
      <guid>https://dev.to/sopuruchii/why-i-treat-api-timeouts-as-unknown-not-failures-509a</guid>
      <description>&lt;p&gt;Every payment gateway I've ever worked on had the same hidden bug.&lt;/p&gt;

&lt;p&gt;A provider API times out. The code says "failure". So you retry. But the original request actually succeeded – the provider just took too long to respond. Now you've double‑charged the customer.&lt;/p&gt;

&lt;p&gt;I built &lt;code&gt;Azums&lt;/code&gt;, an open‑source payment gateway in Rust, specifically to stop this pattern.&lt;/p&gt;

&lt;p&gt;_&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;_The fix: make ambiguity explicit.&lt;/p&gt;

&lt;p&gt;Instead of &lt;code&gt;pending → success/fail&lt;/code&gt;, I designed a state machine with five states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pending&lt;/code&gt; (request sent, waiting)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;succeeded&lt;/code&gt; (confirmed success)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;failed&lt;/code&gt; (confirmed failure)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;retryable&lt;/code&gt; (temporary error, safe to retry)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;unknown&lt;/code&gt;&lt;/strong&gt; (timeout or ambiguous response – needs investigation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a timeout happens, the system doesn't guess. It marks the transaction as &lt;code&gt;unknown&lt;/code&gt; and stops. No blind retries. No double charges.&lt;/p&gt;

&lt;p&gt;Why this matters beyond payments&lt;/p&gt;

&lt;p&gt;This same pattern applies anywhere you talk to unreliable external systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blockchain RPCs that timeout after the transaction was submitted&lt;/li&gt;
&lt;li&gt;AI agent API calls that hang but may have executed&lt;/li&gt;
&lt;li&gt;Messaging queues that lose acknowledgements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treating ambiguity as a real state is the difference between a system that guesses and a system that you can trust.&lt;/p&gt;

&lt;p&gt;My full implementation is on GitHub: &lt;a href="https://github.com/BlockForge-Dev/Azums" rel="noopener noreferrer"&gt;BlockForge-Dev/Azums&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's your worst "timeout caused a disaster" story? Let me know in the comments.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>distributedsystems</category>
      <category>sre</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
