<?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: scielon</title>
    <description>The latest articles on DEV Community by scielon (@scielon).</description>
    <link>https://dev.to/scielon</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%2F4147732%2F60516752-ce30-41d8-9d5c-9ded39877555.png</url>
      <title>DEV Community: scielon</title>
      <link>https://dev.to/scielon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scielon"/>
    <language>en</language>
    <item>
      <title>Stop Retrying API Requests Blindly: How Idempotency Prevents Duplicate Operations</title>
      <dc:creator>scielon</dc:creator>
      <pubDate>Sat, 14 Nov 2026 16:30:38 +0000</pubDate>
      <link>https://dev.to/scielon/stop-retrying-api-requests-blindly-how-idempotency-prevents-duplicate-operations-28d0</link>
      <guid>https://dev.to/scielon/stop-retrying-api-requests-blindly-how-idempotency-prevents-duplicate-operations-28d0</guid>
      <description>&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%2Fps4pgox2qw3dknt2aw47.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%2Fps4pgox2qw3dknt2aw47.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Stop Retrying API Requests Blindly: How Idempotency Prevents Duplicate Operations&lt;/p&gt;

&lt;p&gt;Imagine a customer clicks the &lt;strong&gt;Pay Now&lt;/strong&gt; button on your website.&lt;/p&gt;

&lt;p&gt;Your application sends a payment request to the server. The payment provider processes it successfully, but the network connection times out before your application receives the response.&lt;/p&gt;

&lt;p&gt;What happens next?&lt;/p&gt;

&lt;p&gt;Your application does not know whether the payment succeeded. If it simply sends the same request again, the customer might be charged twice.&lt;/p&gt;

&lt;p&gt;This is not just a payment problem. The same situation can occur when creating user accounts, submitting orders, sending emails, registering subscriptions, or processing background jobs.&lt;/p&gt;

&lt;p&gt;Network failures are unavoidable. Duplicate operations, however, are often preventable.&lt;/p&gt;

&lt;p&gt;Two important techniques help solve this problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Idempotency keys.&lt;/li&gt;
&lt;li&gt;Controlled retries with exponential backoff and jitter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these techniques is essential when building reliable APIs and distributed applications.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Why API Requests Sometimes Produce Duplicate Operations
&lt;/h2&gt;

&lt;p&gt;A common mistake in application development is assuming that a failed HTTP request means the server did not process it.&lt;/p&gt;

&lt;p&gt;That assumption is incorrect.&lt;/p&gt;

&lt;p&gt;Consider this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your application sends a POST request.&lt;/li&gt;
&lt;li&gt;The server receives and processes it.&lt;/li&gt;
&lt;li&gt;The database records the transaction.&lt;/li&gt;
&lt;li&gt;The server sends a successful response.&lt;/li&gt;
&lt;li&gt;The network connection fails before the client receives that response.&lt;/li&gt;
&lt;li&gt;The client assumes the operation failed and sends another request.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The server may process the second request as a completely new operation.&lt;/p&gt;

&lt;p&gt;The result could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two payment transactions.&lt;/li&gt;
&lt;li&gt;Two identical orders.&lt;/li&gt;
&lt;li&gt;Duplicate database records.&lt;/li&gt;
&lt;li&gt;Multiple email notifications.&lt;/li&gt;
&lt;li&gt;Repeated subscription creation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important distinction is between a failed request and an unknown request outcome.&lt;/p&gt;

&lt;p&gt;A timeout tells you that the client did not receive a response within the expected time. It does not necessarily tell you what happened on the server.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Understand Idempotency Before Implementing Retries
&lt;/h2&gt;

&lt;p&gt;In HTTP, idempotency describes an operation whose intended effect remains the same when the same request is performed multiple times.&lt;/p&gt;

&lt;p&gt;For example, setting a user's email address to the same value repeatedly should produce the same final state.&lt;/p&gt;

&lt;p&gt;HTTP defines PUT and DELETE as idempotent methods, along with safe methods such as GET.&lt;/p&gt;

&lt;p&gt;POST is not inherently idempotent.&lt;/p&gt;

&lt;p&gt;This distinction matters because HTTP clients should not automatically retry non-idempotent requests unless they have additional guarantees that make the operation safe.&lt;/p&gt;

&lt;p&gt;The official HTTP specification explains this behavior in RFC 9110:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.2" rel="noopener noreferrer"&gt;https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, developers can implement idempotency for POST operations using a unique identifier.&lt;/p&gt;

&lt;p&gt;This identifier is commonly called an &lt;strong&gt;Idempotency Key&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. How Idempotency Keys Work
&lt;/h2&gt;

&lt;p&gt;An idempotency key identifies one logical operation.&lt;/p&gt;

&lt;p&gt;Instead of treating every incoming request as a completely new operation, the server checks whether it has already processed the supplied key.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="no"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;payments&lt;/span&gt;
&lt;span class="nc"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="nc"&gt;Idempotency&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8472&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server receives the request and checks whether the key already exists.&lt;/p&gt;

&lt;p&gt;There are two possible outcomes.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;The key does not exist.&lt;/li&gt;
&lt;li&gt;The server processes the payment.&lt;/li&gt;
&lt;li&gt;The result is stored alongside the key.&lt;/li&gt;
&lt;li&gt;The server returns the response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repeated request:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same key is received again.&lt;/li&gt;
&lt;li&gt;The server recognizes the previous operation.&lt;/li&gt;
&lt;li&gt;It returns the previously stored result instead of creating another payment.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
  |
  | POST + Idempotency Key
  v
API Server
  |
  v
Check Idempotency Key
  |
  +---- Key exists ----&amp;gt; Return stored result
  |
  +---- Key is new ----&amp;gt; Process operation
                              |
                              v
                         Save result
                              |
                              v
                         Return response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key is not simply a random request identifier. It must remain the same across retries of the same logical operation.&lt;/p&gt;

&lt;p&gt;A new payment or order should receive a new key.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Implementing an Idempotency Key in Python
&lt;/h2&gt;

&lt;p&gt;Here is a simplified example using Python and the Requests library.&lt;/p&gt;

&lt;p&gt;First, install the dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="no"&gt;API_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://api.example.com/payments"&lt;/span&gt;

&lt;span class="c1"&gt;# Generate one key for this logical operation.&lt;/span&gt;
&lt;span class="n"&gt;idempotency_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;payment_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"8472"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;49.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"USD"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"Idempotency-Key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;idempotency_key&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="no"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payment_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example demonstrates how a client can send an idempotency key.&lt;/p&gt;

&lt;p&gt;However, generating a key on the client is only half the solution.&lt;/p&gt;

&lt;p&gt;The server must actually support and enforce idempotency.&lt;/p&gt;

&lt;p&gt;Also, the example assumes that the API documents support for the &lt;code&gt;Idempotency-Key&lt;/code&gt; header. Not every API supports it.&lt;/p&gt;

&lt;h3&gt;
  
  
  An important implementation detail
&lt;/h3&gt;

&lt;p&gt;If the request times out and you decide to retry it, reuse the original key.&lt;/p&gt;

&lt;p&gt;Do not generate a new UUID for the retry.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Retry the same logical operation.&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="no"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payment_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original &lt;code&gt;headers&lt;/code&gt; dictionary contains the same key.&lt;/p&gt;

&lt;p&gt;In production, persist the key and the operation state so that a process restart does not accidentally create a new key for an operation whose outcome is still unknown.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What Should the Server Store?
&lt;/h2&gt;

&lt;p&gt;A reliable idempotency implementation needs more than a table containing unique keys.&lt;/p&gt;

&lt;p&gt;A typical database record might contain:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;idempotency_key&lt;/td&gt;
&lt;td&gt;Identifies the logical operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;user_id&lt;/td&gt;
&lt;td&gt;Identifies the requesting user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;request_hash&lt;/td&gt;
&lt;td&gt;Detects different payloads using the same key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;status&lt;/td&gt;
&lt;td&gt;Tracks processing state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;response_code&lt;/td&gt;
&lt;td&gt;Stores the original HTTP status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;response_body&lt;/td&gt;
&lt;td&gt;Stores the original result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;created_at&lt;/td&gt;
&lt;td&gt;Records when the operation started&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A simplified database structure could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;idempotency_records&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;BIGSERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="kt"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;idempotency_key&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;request_hash&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="kt"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_code&lt;/span&gt; &lt;span class="kt"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_body&lt;/span&gt; &lt;span class="kt"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="kt"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="kc"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idempotency_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unique constraint is important because two identical requests could arrive simultaneously.&lt;/p&gt;

&lt;p&gt;Without database-level protection, both requests might check for the key before either one has saved it.&lt;/p&gt;

&lt;p&gt;This creates a race condition.&lt;/p&gt;

&lt;p&gt;A production implementation should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scope keys appropriately, such as by account and operation.&lt;/li&gt;
&lt;li&gt;Atomically claim or create the key.&lt;/li&gt;
&lt;li&gt;Compare the incoming request with the original request.&lt;/li&gt;
&lt;li&gt;Prevent concurrent duplicate processing.&lt;/li&gt;
&lt;li&gt;Store the operation result reliably.&lt;/li&gt;
&lt;li&gt;Define what happens when processing is interrupted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the same key arrives with a different payload, the server should reject it rather than silently treating it as the original operation.&lt;/p&gt;

&lt;p&gt;There is another important detail: database transactions alone cannot make an external payment provider and your local database participate in one atomic transaction.&lt;/p&gt;

&lt;p&gt;For external side effects, use the provider's own idempotency support where available, and consider durable job processing or an outbox pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Why Exponential Backoff Matters
&lt;/h2&gt;

&lt;p&gt;Idempotency protects against duplicate operations, but it does not solve every network problem.&lt;/p&gt;

&lt;p&gt;Sometimes a server is temporarily unavailable or overloaded.&lt;/p&gt;

&lt;p&gt;Repeatedly sending requests without waiting can make the problem worse.&lt;/p&gt;

&lt;p&gt;Imagine a server experiencing high traffic. Thousands of clients receive errors and immediately retry their requests.&lt;/p&gt;

&lt;p&gt;The additional traffic can increase server load and make recovery more difficult.&lt;/p&gt;

&lt;p&gt;This is where exponential backoff becomes useful.&lt;/p&gt;

&lt;p&gt;Instead of retrying at fixed short intervals, the waiting time increases after each failed attempt.&lt;/p&gt;

&lt;p&gt;A simple formula is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delay = min(max_delay, base_delay * 2^attempt)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attempt&lt;/th&gt;
&lt;th&gt;Approximate delay&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;8 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;16 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are illustrative values, not universal production settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add jitter to prevent synchronized retries
&lt;/h3&gt;

&lt;p&gt;Exponential backoff alone can still cause problems.&lt;/p&gt;

&lt;p&gt;If thousands of clients fail at the same time, they may all retry at nearly identical intervals.&lt;/p&gt;

&lt;p&gt;Jitter adds randomness to the delay.&lt;/p&gt;

&lt;p&gt;Here is a simple Python implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_retry_delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;exponential_delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;max_delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;base_delay&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;jitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;exponential_delay&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;exponential_delay&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;jitter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_retry_delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Waiting {delay:.2f} seconds"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code demonstrates delay calculation only. It does not implement HTTP retries or determine whether a request is safe to repeat.&lt;/p&gt;

&lt;p&gt;For production systems, use a retry policy that also considers HTTP status codes, request semantics, total deadlines, and server-provided retry instructions such as &lt;code&gt;Retry-After&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;AWS provides a useful explanation of timeouts, retries, exponential backoff, and jitter:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/" rel="noopener noreferrer"&gt;https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Which Requests Should You Retry?
&lt;/h2&gt;

&lt;p&gt;Not every failure should trigger another request.&lt;/p&gt;

&lt;p&gt;A sensible retry strategy distinguishes temporary failures from permanent ones.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Typical action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Connection timeout&lt;/td&gt;
&lt;td&gt;Retry cautiously if operation is safe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 429&lt;/td&gt;
&lt;td&gt;Respect Retry-After and rate limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 500&lt;/td&gt;
&lt;td&gt;Consider a limited retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 502 or 503&lt;/td&gt;
&lt;td&gt;Consider a limited retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 400&lt;/td&gt;
&lt;td&gt;Usually do not retry unchanged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 401&lt;/td&gt;
&lt;td&gt;Correct authentication first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 403&lt;/td&gt;
&lt;td&gt;Investigate permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation error&lt;/td&gt;
&lt;td&gt;Correct the request&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are general guidelines. Actual behavior depends on the API's documentation and the specific error response.&lt;/p&gt;

&lt;p&gt;For example, an HTTP 500 response does not automatically prove that a state-changing operation was not completed.&lt;/p&gt;

&lt;p&gt;Likewise, a timeout does not prove that the server failed.&lt;/p&gt;

&lt;p&gt;For non-idempotent operations, you need a reliable way to determine the original outcome or a server-supported idempotency mechanism before retrying.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Common Idempotency Mistakes
&lt;/h2&gt;

&lt;p&gt;Even when developers understand the concept, implementation mistakes can undermine the entire design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating a new key for every retry
&lt;/h3&gt;

&lt;p&gt;This defeats the purpose of idempotency.&lt;/p&gt;

&lt;p&gt;A retry must reuse the original key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the same key for different operations
&lt;/h3&gt;

&lt;p&gt;A key should identify one logical operation, not an entire user session.&lt;/p&gt;

&lt;p&gt;Two different orders should have different keys.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring concurrent requests
&lt;/h3&gt;

&lt;p&gt;A simple check-then-insert implementation is not sufficient.&lt;/p&gt;

&lt;p&gt;Use database constraints and atomic processing to prevent simultaneous requests from performing the same operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing the key without storing the result
&lt;/h3&gt;

&lt;p&gt;The server needs a reliable way to return the original operation outcome.&lt;/p&gt;

&lt;p&gt;A key without a corresponding result or recoverable processing state is incomplete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrying forever
&lt;/h3&gt;

&lt;p&gt;Unlimited retries can consume resources, amplify failures, and create unexpected costs.&lt;/p&gt;

&lt;p&gt;Use bounded retries, timeouts, and an overall execution deadline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forgetting key expiration
&lt;/h3&gt;

&lt;p&gt;Idempotency records need a retention policy.&lt;/p&gt;

&lt;p&gt;However, expiration should be longer than the realistic retry window for the operation. Otherwise, a delayed retry could be processed as a new operation.&lt;/p&gt;

&lt;p&gt;The correct retention period depends on the application and API provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. A Practical Checklist for Production APIs
&lt;/h2&gt;

&lt;p&gt;Before deploying an API that performs important state-changing operations, review the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Generate a unique key for every logical operation.&lt;/li&gt;
&lt;li&gt;[ ] Reuse the same key when retrying that operation.&lt;/li&gt;
&lt;li&gt;[ ] Enforce uniqueness at the database level.&lt;/li&gt;
&lt;li&gt;[ ] Validate that repeated keys use the same request payload.&lt;/li&gt;
&lt;li&gt;[ ] Handle concurrent requests safely.&lt;/li&gt;
&lt;li&gt;[ ] Persist operation states and results.&lt;/li&gt;
&lt;li&gt;[ ] Set connection and response timeouts.&lt;/li&gt;
&lt;li&gt;[ ] Use bounded exponential backoff with jitter.&lt;/li&gt;
&lt;li&gt;[ ] Respect rate limits and Retry-After headers.&lt;/li&gt;
&lt;li&gt;[ ] Avoid retrying permanent validation and authentication errors.&lt;/li&gt;
&lt;li&gt;[ ] Test what happens when the server succeeds but the client loses the response.&lt;/li&gt;
&lt;li&gt;[ ] Test concurrent duplicate requests and process restarts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These checks are particularly important for payments, orders, subscriptions, and other operations where duplicate execution has real consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Reliable APIs are not built by assuming that network requests will always succeed.&lt;/p&gt;

&lt;p&gt;They are built by preparing for uncertainty.&lt;/p&gt;

&lt;p&gt;Idempotency keys help ensure that repeating a logical operation does not create unintended duplicate effects. Exponential backoff and jitter help clients recover from temporary failures without overwhelming struggling services.&lt;/p&gt;

&lt;p&gt;Neither technique replaces the other.&lt;/p&gt;

&lt;p&gt;Idempotency protects the operation. Retry policies protect the communication process.&lt;/p&gt;

&lt;p&gt;If you are building a payment system, an order-processing service, or a background job pipeline, implementing both correctly can make the difference between a fragile integration and a reliable production system.&lt;/p&gt;

&lt;p&gt;The goal is not to eliminate network failures. It is to make sure those failures do not turn into expensive application errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Have you ever experienced duplicate database records, payments, or orders because of an API timeout?&lt;/p&gt;

&lt;p&gt;How did you solve the problem?&lt;/p&gt;

&lt;p&gt;Do you implement idempotency keys in your applications, or do you rely on other techniques?&lt;/p&gt;

&lt;p&gt;Share your experience in the comments.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;AI assistance disclosure:&lt;/strong&gt; This article was prepared with AI assistance. Technical explanations and code examples should be reviewed and tested in the context of the target application before production use.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;More Technology Insights&lt;/strong&gt;&lt;br&gt;
For more articles covering artificial intelligence, emerging technologies, software, and practical technology guides, visit &lt;a href="https://scielon.com/" rel="noopener noreferrer"&gt;Scielon&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Stop Building AI Agents for Everything: Why Simple Automation Often Wins</title>
      <dc:creator>scielon</dc:creator>
      <pubDate>Fri, 13 Nov 2026 17:56:10 +0000</pubDate>
      <link>https://dev.to/scielon/stop-building-ai-agents-for-everything-why-simple-automation-often-wins-2alo</link>
      <guid>https://dev.to/scielon/stop-building-ai-agents-for-everything-why-simple-automation-often-wins-2alo</guid>
      <description>&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%2Fsbl1zj1029inuzpq56ay.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%2Fsbl1zj1029inuzpq56ay.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Stop Building AI Agents for Everything: Why Simple Automation Often Wins
&lt;/h1&gt;

&lt;p&gt;Artificial intelligence has changed the way developers think about automation. Tasks that once required complex software, multiple integrations, and hours of manual work can now be handled with AI-powered tools.&lt;/p&gt;

&lt;p&gt;But there is a growing problem: developers are starting to use AI agents for tasks that do not actually need them.&lt;/p&gt;

&lt;p&gt;Not every workflow needs an autonomous agent. Sometimes, a simple script, a well-designed API integration, or a traditional automation pipeline is faster, cheaper, and more reliable.&lt;/p&gt;

&lt;p&gt;The real challenge is not building the most sophisticated AI system. It is choosing the right level of complexity for the problem.&lt;/p&gt;

&lt;p&gt;In this article, we will explore why simpler automation can outperform complex AI agents, how to decide when an agent is necessary, and how to design workflows that are easier to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Problem With Overengineering AI Automation
&lt;/h2&gt;

&lt;p&gt;AI agents are impressive because they can interpret instructions, make decisions, use tools, and perform multi-step tasks.&lt;/p&gt;

&lt;p&gt;However, these capabilities introduce additional complexity.&lt;/p&gt;

&lt;p&gt;Consider a simple workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receive a new content idea.&lt;/li&gt;
&lt;li&gt;Collect relevant information.&lt;/li&gt;
&lt;li&gt;Generate a draft.&lt;/li&gt;
&lt;li&gt;Check the output.&lt;/li&gt;
&lt;li&gt;Send it for human review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You could build an autonomous AI agent to handle the entire process.&lt;/p&gt;

&lt;p&gt;But should you?&lt;/p&gt;

&lt;p&gt;If the steps are predictable, a traditional workflow with clearly defined stages may be a better solution.&lt;/p&gt;

&lt;p&gt;An autonomous agent introduces additional questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens if it chooses the wrong tool?&lt;/li&gt;
&lt;li&gt;How do you prevent repeated actions?&lt;/li&gt;
&lt;li&gt;What happens when an API request fails?&lt;/li&gt;
&lt;li&gt;How do you verify the information it produces?&lt;/li&gt;
&lt;li&gt;How do you control unexpected costs?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More autonomy does not automatically mean better results.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Understand the Difference Between Automation and AI Agents
&lt;/h2&gt;

&lt;p&gt;Before choosing an architecture, understand what each approach does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traditional automation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Traditional automation follows predefined instructions.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive an input.&lt;/li&gt;
&lt;li&gt;Validate the input.&lt;/li&gt;
&lt;li&gt;Call an API.&lt;/li&gt;
&lt;li&gt;Save the result.&lt;/li&gt;
&lt;li&gt;Notify the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The system follows the same predictable sequence every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-powered automation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI-powered automation adds intelligence to specific steps.&lt;/p&gt;

&lt;p&gt;For example, an AI model might classify incoming messages, summarize documents, or extract important information.&lt;/p&gt;

&lt;p&gt;The overall workflow can still remain structured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agents&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An AI agent can make decisions about what to do next, select tools, and execute multiple steps based on its current context.&lt;/p&gt;

&lt;p&gt;This flexibility is useful when the task requires reasoning and the next action cannot always be determined in advance.&lt;/p&gt;

&lt;p&gt;The important distinction is that an AI agent is an architectural choice, not a requirement for every AI-powered application.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A Practical Example: Building a Content Automation Workflow
&lt;/h2&gt;

&lt;p&gt;Imagine you want to automate part of a technology blog's content production process.&lt;/p&gt;

&lt;p&gt;Instead of asking an AI agent to manage everything independently, divide the process into smaller stages.&lt;/p&gt;

&lt;p&gt;A practical architecture might look 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;Content Idea
     |
     v
Input Validation
     |
     v
Research Collection
     |
     v
AI Content Generation
     |
     v
Quality Validation
     |
     v
Human Review
     |
     v
Manual Publication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each stage has a specific responsibility.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input validation ensures the topic is clear.&lt;/li&gt;
&lt;li&gt;Research collection gathers supporting information.&lt;/li&gt;
&lt;li&gt;AI generation creates the initial draft.&lt;/li&gt;
&lt;li&gt;Quality validation checks structure and completeness.&lt;/li&gt;
&lt;li&gt;Human review verifies accuracy and originality.&lt;/li&gt;
&lt;li&gt;Publication remains under human control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach makes problems easier to identify and fix.&lt;/p&gt;

&lt;p&gt;If research collection fails, you do not need to rebuild the entire system.&lt;/p&gt;

&lt;p&gt;You simply retry that stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Add Basic Validation Before Trusting AI Output
&lt;/h2&gt;

&lt;p&gt;One of the biggest mistakes in AI automation is assuming that a successful API response means a successful result.&lt;/p&gt;

&lt;p&gt;It does not.&lt;/p&gt;

&lt;p&gt;An AI model can return a technically valid response containing incomplete, irrelevant, or inaccurate information.&lt;/p&gt;

&lt;p&gt;For example, consider a simple Python validation function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;required_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"introduction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"conclusion"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;required_fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Missing field: {field}"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Article body is too short"&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Article passed basic validation"&lt;/span&gt;


&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Practical AI Automation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"introduction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"An introduction to AI workflows."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"A detailed article body goes here..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"conclusion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Choose the right architecture."&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a basic example, not a complete quality-control system.&lt;/p&gt;

&lt;p&gt;It checks whether essential fields exist and whether the article meets a minimum length.&lt;/p&gt;

&lt;p&gt;More advanced validation could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checking whether source URLs are accessible.&lt;/li&gt;
&lt;li&gt;Detecting repeated sections.&lt;/li&gt;
&lt;li&gt;Verifying required headings.&lt;/li&gt;
&lt;li&gt;Checking whether claims have supporting sources.&lt;/li&gt;
&lt;li&gt;Flagging suspicious or unsupported information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember: structural validation cannot prove factual accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Design for Failure, Not Just Success
&lt;/h2&gt;

&lt;p&gt;Reliable automation is not about assuming everything will work.&lt;/p&gt;

&lt;p&gt;APIs can become unavailable. Network requests can time out. Rate limits can interrupt workflows. External services can change their behavior.&lt;/p&gt;

&lt;p&gt;A robust workflow should account for these possibilities.&lt;/p&gt;

&lt;p&gt;Consider these practices:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use retries carefully.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Temporary failures can often be resolved by retrying a request.&lt;/p&gt;

&lt;p&gt;However, repeated retries should have limits. Use exponential backoff rather than sending requests continuously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep logs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Record important events, errors, and execution times. Logs make troubleshooting significantly easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid duplicate actions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a workflow is interrupted and restarted, it should not accidentally create duplicate records or repeat irreversible operations.&lt;/p&gt;

&lt;p&gt;Use unique identifiers or idempotency mechanisms where appropriate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separate workflow stages.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keep research, generation, validation, and delivery independent where possible.&lt;/p&gt;

&lt;p&gt;This makes maintenance easier and limits the impact of individual failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. When Should You Actually Use an AI Agent?
&lt;/h2&gt;

&lt;p&gt;There are situations where an agent is genuinely useful.&lt;/p&gt;

&lt;p&gt;Consider using one when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The task requires multiple decisions based on changing information.&lt;/li&gt;
&lt;li&gt;The next action depends on previous results.&lt;/li&gt;
&lt;li&gt;The system needs to select between different tools dynamically.&lt;/li&gt;
&lt;li&gt;The workflow contains unexpected scenarios that cannot easily be represented with fixed rules.&lt;/li&gt;
&lt;li&gt;The value of autonomous reasoning justifies the additional complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a research assistant that searches several sources, compares findings, identifies missing information, and decides what to investigate next may benefit from an agent-based architecture.&lt;/p&gt;

&lt;p&gt;On the other hand, automatically resizing images, sending notifications, or moving structured data between applications usually does not require an autonomous agent.&lt;/p&gt;

&lt;p&gt;A useful rule is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use deterministic automation for predictable tasks, AI models for tasks requiring interpretation, and agents for tasks requiring dynamic decision-making.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. A Simple Decision Framework
&lt;/h2&gt;

&lt;p&gt;Before building an AI automation system, ask yourself these questions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Recommended approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Are the steps always predictable?&lt;/td&gt;
&lt;td&gt;Traditional automation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does one step require language understanding?&lt;/td&gt;
&lt;td&gt;AI-powered automation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does the system need to decide what to do next?&lt;/td&gt;
&lt;td&gt;Consider an AI agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Are errors expensive or difficult to reverse?&lt;/td&gt;
&lt;td&gt;Add human approval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can the task be completed with a simple API call?&lt;/td&gt;
&lt;td&gt;Avoid unnecessary agents&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This framework helps prevent unnecessary engineering.&lt;/p&gt;

&lt;p&gt;The objective is not to use as much AI as possible. It is to solve the problem with the smallest reliable system.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. The Future of AI Automation Is Not Necessarily More Autonomy
&lt;/h2&gt;

&lt;p&gt;As AI models become more capable, it is tempting to assume that every application should become fully autonomous.&lt;/p&gt;

&lt;p&gt;But production systems have different requirements from experimental demonstrations.&lt;/p&gt;

&lt;p&gt;A successful application needs predictable behavior, manageable costs, clear error handling, security controls, and measurable results.&lt;/p&gt;

&lt;p&gt;In many cases, the strongest architecture will combine traditional software engineering with AI capabilities in carefully selected places.&lt;/p&gt;

&lt;p&gt;Developers who understand when not to use AI may build better systems than those who try to introduce an agent into every workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI agents are powerful, but power without a clear purpose creates unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Start with the problem, map the workflow, identify where intelligence is genuinely needed, and choose the simplest architecture that meets your requirements.&lt;/p&gt;

&lt;p&gt;You can always introduce more autonomy later when the workflow demonstrates a real need for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build systems that are reliable first and intelligent where necessary.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;What is your approach to AI automation?&lt;/p&gt;

&lt;p&gt;Do you prefer structured workflows with AI-powered steps, or do you believe autonomous agents should handle most tasks?&lt;/p&gt;

&lt;p&gt;Share your experience in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AI assistance disclosure: This article was prepared with AI assistance and reviewed for clarity and technical consistency.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>architecture</category>
      <category>automation</category>
    </item>
    <item>
      <title>Windows 11 Backup and Recovery: OneDrive vs File History vs Recovery Tools</title>
      <dc:creator>scielon</dc:creator>
      <pubDate>Fri, 13 Nov 2026 17:37:48 +0000</pubDate>
      <link>https://dev.to/scielon/windows-11-backup-and-recovery-onedrive-vs-file-history-vs-recovery-tools-g9f</link>
      <guid>https://dev.to/scielon/windows-11-backup-and-recovery-onedrive-vs-file-history-vs-recovery-tools-g9f</guid>
      <description>&lt;p&gt;One important Windows backup detail: a Recovery Drive does not contain your personal files. It can help reinstall or recover Windows, but your documents and photos need a separate backup. File History can save versions of personal files to an external or network drive, while OneDrive can synchronize selected folders and settings. These tools are useful, but they are not interchangeable. What do you currently rely on: cloud storage, an external drive, or both? Follow each community's rules and add useful context rather than posting an unexplained link.&lt;/p&gt;

&lt;p&gt;CTA: The complete guide compares the built-in Windows options.&lt;/p&gt;

&lt;p&gt;Article link:&lt;a href=""&gt; https://sch2030.blogspot.com/2026/11/windows-11-backup-and-recovery-onedrive.html&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
