<?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: Sina Rezaei</title>
    <description>The latest articles on DEV Community by Sina Rezaei (@sinarezaei).</description>
    <link>https://dev.to/sinarezaei</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%2F1041388%2F7c77dbc8-0ae8-4bfd-a926-762d3fce6b6f.png</url>
      <title>DEV Community: Sina Rezaei</title>
      <link>https://dev.to/sinarezaei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sinarezaei"/>
    <language>en</language>
    <item>
      <title>Your AI Pentester Found an SSRF. Can It Reproduce the Attack?</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Fri, 30 Oct 2026 00:22:16 +0000</pubDate>
      <link>https://dev.to/sinarezaei/your-ai-pentester-found-an-ssrf-can-it-reproduce-the-attack-2384</link>
      <guid>https://dev.to/sinarezaei/your-ai-pentester-found-an-ssrf-can-it-reproduce-the-attack-2384</guid>
      <description>&lt;p&gt;The first scan found an SSRF. The second one didn't.&lt;/p&gt;

&lt;p&gt;I wouldn't close the finding just yet.&lt;/p&gt;

&lt;p&gt;The endpoint accepts a URL, fetches the resource, and returns its response. During the first scan, the agent managed to follow a redirect and reach an internal service. It reported the issue with a successful response as evidence.&lt;/p&gt;

&lt;p&gt;Then it ran the test again. Same endpoint, same application. No SSRF.&lt;/p&gt;

&lt;p&gt;One thing I'd check before looking at the model's reasoning is the actual network traffic.&lt;/p&gt;

&lt;p&gt;Suppose the agent tests a hostname that resolves to a public IP. The application checks the address and allows the request. But there's a gap between that check and the connection itself. If another DNS resolution returns a private IP, the application could end up connecting somewhere the original validation was supposed to block.&lt;/p&gt;

&lt;p&gt;That's the DNS rebinding problem.&lt;/p&gt;

&lt;p&gt;The details depend on the HTTP client, DNS caching, connection reuse, and whether a proxy is involved. A second lookup isn't guaranteed. But if the component making the connection doesn't enforce the destination restrictions, checking the hostname at the beginning isn't enough.&lt;/p&gt;

&lt;p&gt;Redirects make this more interesting. The first URL can point to a public host and return a redirect to another hostname. If the application validates only the original destination, the next request may escape the intended boundary. OWASP recommends disabling automatic redirect following in the HTTP client to avoid this validation bypass. If redirects are required, each destination needs to be checked before the next request is made.&lt;/p&gt;

&lt;p&gt;I'd want to see the actual destination IP at connection time, not just the hostname supplied by the agent. I'd also check whether outbound network rules prevent access to internal address ranges.&lt;/p&gt;

&lt;p&gt;There's a second problem here, and it has less to do with SSRF itself.&lt;/p&gt;

&lt;p&gt;Even if the agent sends exactly the same request twice, the network path might not be identical. DNS can return a different address, a redirect target can change, or the HTTP client can reuse an existing connection in one run and establish a new one in the next.&lt;/p&gt;

&lt;p&gt;Looking at the final model response won't tell us which of these happened.&lt;/p&gt;

&lt;p&gt;For this kind of test, I'd keep the raw request, DNS answers, redirect history, destination IP, response status, and target build identifier for each run. Not just the agent's summary. Without that information, a failed second scan doesn't tell us whether the vulnerability disappeared or the request simply took another route.&lt;/p&gt;

&lt;p&gt;There's also a difference between replaying a previous execution and reproducing the vulnerability. Replaying saved tool calls can reconstruct what the agent did before. It doesn't prove that the current application is still vulnerable.&lt;/p&gt;

&lt;p&gt;That's why I'd avoid treating a second scan that returns nothing as evidence that the issue is gone.&lt;/p&gt;

&lt;p&gt;Before closing the finding, I'd want to know one thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did the application stop accepting the request, or did the request stop reaching the same destination?&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Reference&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP: Server-Side Request Forgery Prevention Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ssrf</category>
      <category>cybersecurity</category>
      <category>websecurity</category>
      <category>ai</category>
    </item>
    <item>
      <title>The SQL NOT IN Trap: When NULL Quietly Breaks Your Query</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Fri, 30 Oct 2026 00:16:28 +0000</pubDate>
      <link>https://dev.to/sinarezaei/the-sql-not-in-trap-when-null-quietly-breaks-your-query-h81</link>
      <guid>https://dev.to/sinarezaei/the-sql-not-in-trap-when-null-quietly-breaks-your-query-h81</guid>
      <description>&lt;p&gt;Some SQL bugs don't throw errors. The query runs, the tests pass, and everything looks fine until a cleanup job skips records it was supposed to catch.&lt;/p&gt;

&lt;p&gt;One of the reasons this happens is &lt;code&gt;NULL&lt;/code&gt; inside a &lt;code&gt;NOT IN&lt;/code&gt; query.&lt;/p&gt;

&lt;h2&gt;
  
  
  When &lt;code&gt;NOT IN&lt;/code&gt; stops behaving as expected
&lt;/h2&gt;

&lt;p&gt;Imagine you're working on a multi-tenant platform. You have a &lt;code&gt;memberships&lt;/code&gt; table and an &lt;code&gt;identity_mappings&lt;/code&gt; table. Each mapping connects an external identity to a tenant.&lt;/p&gt;

&lt;p&gt;You need to find memberships that don't have a corresponding identity mapping.&lt;/p&gt;

&lt;p&gt;A first attempt might 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;SELECT&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;memberships&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;identity_mappings&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing looks wrong at first glance. You're selecting external IDs that don't exist in the mapping table.&lt;/p&gt;

&lt;p&gt;Now suppose the subquery returns these values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;external_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ext_1042&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ext_2087&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ext_3911&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That single &lt;code&gt;NULL&lt;/code&gt; can change the result.&lt;/p&gt;

&lt;p&gt;SQL uses three-valued logic: a condition can evaluate to &lt;code&gt;TRUE&lt;/code&gt;, &lt;code&gt;FALSE&lt;/code&gt;, or &lt;code&gt;UNKNOWN&lt;/code&gt;. When &lt;code&gt;NULL&lt;/code&gt; is involved in a comparison, SQL often can't determine whether the comparison is true or false.&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 sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'ext_9001'&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'ext_1042'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'ext_2087'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result isn't &lt;code&gt;TRUE&lt;/code&gt;. It's &lt;code&gt;UNKNOWN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because a &lt;code&gt;WHERE&lt;/code&gt; clause keeps only rows for which the condition evaluates to &lt;code&gt;TRUE&lt;/code&gt;, the record gets filtered out.&lt;/p&gt;

&lt;p&gt;The query executes successfully. There's no exception to investigate. You simply get fewer rows than expected.&lt;/p&gt;

&lt;p&gt;That's what makes this bug easy to miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;NOT EXISTS&lt;/code&gt; is often a better fit
&lt;/h2&gt;

&lt;p&gt;One common fix is to replace &lt;code&gt;NOT IN&lt;/code&gt; with &lt;code&gt;NOT EXISTS&lt;/code&gt;:&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;SELECT&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;memberships&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;identity_mappings&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference matters.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NOT EXISTS&lt;/code&gt; checks whether a matching row exists. An unrelated &lt;code&gt;NULL&lt;/code&gt; elsewhere in the subquery doesn't automatically make the condition unknown.&lt;/p&gt;

&lt;p&gt;But fixing the &lt;code&gt;NULL&lt;/code&gt; problem doesn't necessarily mean the query is correct.&lt;/p&gt;

&lt;p&gt;There's another detail in this example that deserves attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  The multi-tenant bug hiding behind the SQL
&lt;/h2&gt;

&lt;p&gt;In a multi-tenant system, an external ID might be valid in one tenant but completely unrelated to the same value in another tenant.&lt;/p&gt;

&lt;p&gt;Suppose the data looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;memberships&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tenant_id&lt;/th&gt;
&lt;th&gt;external_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tenant_a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ext_1042&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tenant_b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ext_1042&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;identity_mappings&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tenant_id&lt;/th&gt;
&lt;th&gt;external_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tenant_a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ext_1042&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The membership for &lt;code&gt;tenant_b&lt;/code&gt; has no matching mapping within its own tenant.&lt;/p&gt;

&lt;p&gt;However, this query checks only the external ID:&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;WHERE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;identity_mappings&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It finds the mapping belonging to &lt;code&gt;tenant_a&lt;/code&gt; and treats the &lt;code&gt;tenant_b&lt;/code&gt; membership as matched.&lt;/p&gt;

&lt;p&gt;The SQL is valid. The logic is wrong.&lt;/p&gt;

&lt;p&gt;If the relationship depends on both the tenant and the external ID, the query needs to express both conditions:&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;SELECT&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;memberships&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;identity_mappings&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the query checks the relationship the application actually cares about.&lt;/p&gt;

&lt;p&gt;This is a different class of bug from the original &lt;code&gt;NULL&lt;/code&gt; issue. Replacing &lt;code&gt;NOT IN&lt;/code&gt; with &lt;code&gt;NOT EXISTS&lt;/code&gt; solves one problem, but it doesn't automatically make the underlying relationship correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about &lt;code&gt;NULL&lt;/code&gt; in the outer table?
&lt;/h2&gt;

&lt;p&gt;There's one more case worth testing.&lt;/p&gt;

&lt;p&gt;What happens if &lt;code&gt;m.external_id&lt;/code&gt; itself is &lt;code&gt;NULL&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;With the equality condition:&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="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a &lt;code&gt;NULL&lt;/code&gt; value doesn't match another &lt;code&gt;NULL&lt;/code&gt; value. The subquery finds no matching row, so &lt;code&gt;NOT EXISTS&lt;/code&gt; evaluates to &lt;code&gt;TRUE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That means the membership with a missing external ID can appear in the result.&lt;/p&gt;

&lt;p&gt;Whether that's correct depends on the application.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;NULL&lt;/code&gt; means the identity hasn't been linked yet, returning that membership might be intentional. If the cleanup job is supposed to process only memberships with known external IDs, you'll need an additional condition:&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;AND&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't add that condition automatically, though. First establish what &lt;code&gt;NULL&lt;/code&gt; means in the data model.&lt;/p&gt;

&lt;p&gt;A query shouldn't have to guess the business rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tests I'd add before shipping this
&lt;/h2&gt;

&lt;p&gt;For a cleanup job in a multi-tenant system, I'd test these cases explicitly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A matching mapping exists for the same tenant and external ID.&lt;/li&gt;
&lt;li&gt;The same external ID exists in another tenant, but not the current one.&lt;/li&gt;
&lt;li&gt;The mapping table contains a &lt;code&gt;NULL&lt;/code&gt; external ID.&lt;/li&gt;
&lt;li&gt;The membership itself has a &lt;code&gt;NULL&lt;/code&gt; external ID.&lt;/li&gt;
&lt;li&gt;Duplicate mappings exist for the same tenant and external ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd also check the database constraints. If each tenant should have at most one mapping for a given external ID, a suitable unique constraint can enforce that rule rather than leaving it entirely to application code.&lt;/p&gt;

&lt;p&gt;Before running a destructive cleanup, inspect the candidate rows and verify the expected count. If possible, run the operation inside a transaction and make sure the affected records are exactly the ones you intended to process.&lt;/p&gt;

&lt;p&gt;A successful query execution is not proof that the selected rows are correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real lesson
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;NOT IN&lt;/code&gt; and &lt;code&gt;NOT EXISTS&lt;/code&gt; are worth understanding, especially when &lt;code&gt;NULL&lt;/code&gt; is involved. But the operator isn't always the biggest problem.&lt;/p&gt;

&lt;p&gt;A query can handle &lt;code&gt;NULL&lt;/code&gt; correctly and still match records across tenant boundaries. It can pass tests built around clean data and fail when production data contains unexpected values. It can be perfectly valid SQL while describing the wrong relationship.&lt;/p&gt;

&lt;p&gt;When I review an anti-join, I want to establish three things before worrying about the syntax:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What exactly makes two records a match?&lt;/li&gt;
&lt;li&gt;What should happen when one of the relevant values is &lt;code&gt;NULL&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Which columns define the relationship, including tenant boundaries?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once those rules are clear, choosing the SQL becomes much easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you ever found a query that was technically correct but was checking the wrong relationship?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>postgres</category>
    </item>
    <item>
      <title>The JavaScript We Never Questioned</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Tue, 27 Oct 2026 23:05:25 +0000</pubDate>
      <link>https://dev.to/sinarezaei/the-javascript-we-never-questioned-okn</link>
      <guid>https://dev.to/sinarezaei/the-javascript-we-never-questioned-okn</guid>
      <description>&lt;p&gt;We often reach for JavaScript because that's how we've learned to solve frontend problems. Need to calculate a layout value? Write a function. Need to coordinate styles across components? Add some logic. Need to handle a visual behavior? Start with an event listener.&lt;/p&gt;

&lt;p&gt;Sometimes that's the right call. But sometimes we're writing JavaScript to work around a limitation that no longer exists.&lt;/p&gt;

&lt;p&gt;CSS has evolved far beyond styling colors, spacing, and typography. Features like container queries, &lt;code&gt;:has()&lt;/code&gt;, and modern layout capabilities have changed how we can approach problems that once required extra code.&lt;/p&gt;

&lt;p&gt;The interesting part isn't that CSS can replace JavaScript. It can't, and it shouldn't try to. The real question is whether we're adding complexity because the problem demands it, or because we're still using an old solution out of habit.&lt;/p&gt;

&lt;p&gt;In this article, I'll look at a few common frontend patterns, how modern CSS changes the equation, and where JavaScript still makes more sense.&lt;/p&gt;

&lt;p&gt;The goal isn't to write less JavaScript just for the sake of it. It's to stop writing code that the platform can already handle.&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How Much Coding Do You Actually Need in the AI Era?</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Sat, 24 Oct 2026 07:02:21 +0000</pubDate>
      <link>https://dev.to/sinarezaei/how-much-coding-do-you-actually-need-in-the-ai-era-4i9o</link>
      <guid>https://dev.to/sinarezaei/how-much-coding-do-you-actually-need-in-the-ai-era-4i9o</guid>
      <description>&lt;p&gt;I keep coming back to this question: is it still worth learning to code when you can get so much done without writing everything yourself?&lt;/p&gt;

&lt;p&gt;Personally, I think it is.&lt;/p&gt;

&lt;p&gt;You don't need to spend months memorizing Python syntax before building something. Pick a project, start working on it, and learn what you need along the way.&lt;/p&gt;

&lt;p&gt;But there's a catch. You need to understand enough to know when something isn't right.&lt;/p&gt;

&lt;p&gt;For example, imagine you're building an API that returns products from a database. Everything works fine with a few records, but once the database grows, the endpoint becomes painfully slow.&lt;/p&gt;

&lt;p&gt;If you understand SQL queries, indexing, and pagination, you have somewhere to start. Otherwise, you might spend hours changing code without ever finding the actual problem.&lt;/p&gt;

&lt;p&gt;That's why I wouldn't skip the fundamentals.&lt;/p&gt;

&lt;p&gt;Learn how variables, functions, loops, data structures, APIs, and errors work. Get comfortable reading code, running tests, and figuring out why something broke.&lt;/p&gt;

&lt;p&gt;You don't have to know everything before starting a project. In fact, working on real projects is probably one of the best ways to learn.&lt;/p&gt;

&lt;p&gt;And there's no reason to stop at the basics if you're building more complex systems. The more you understand about databases, architecture, and how different parts of an application fit together, the better decisions you can make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So, how much coding knowledge is enough?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enough to understand what you're building, recognize when something is wrong, and figure out what to do next.&lt;/p&gt;

&lt;p&gt;You can always learn more when you need it.&lt;/p&gt;

&lt;p&gt;What matters is knowing more than just what to ask for. You need to understand what you're getting back.&lt;/p&gt;

&lt;p&gt;Where would you draw the line? Are the fundamentals enough, or is deeper programming knowledge still essential?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My AI Agent Passed Every Test. The Feature Was Still Wrong.</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Tue, 20 Oct 2026 07:46:47 +0000</pubDate>
      <link>https://dev.to/sinarezaei/my-ai-agent-passed-every-test-the-feature-was-still-wrong-29fj</link>
      <guid>https://dev.to/sinarezaei/my-ai-agent-passed-every-test-the-feature-was-still-wrong-29fj</guid>
      <description>&lt;p&gt;There's a particular kind of bug that's more annoying than a failing test.&lt;/p&gt;

&lt;p&gt;The code runs. The tests pass. The build is green.&lt;/p&gt;

&lt;p&gt;And the feature still does the wrong thing.&lt;/p&gt;

&lt;p&gt;With an AI coding agent, there's another question worth asking: what happens when the agent can change the implementation &lt;em&gt;and&lt;/em&gt; the tests used to decide whether that implementation is correct?&lt;/p&gt;

&lt;p&gt;I found a concrete example of this in a &lt;a href="https://dev.to/vig-fast-x-high/your-ai-coding-agent-might-be-cheating-to-pass-ci-heres-how-it-387"&gt;DEV article about an AI coding agent changing tests to pass CI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The interesting part wasn't a complicated algorithm or an obscure framework bug. It was a disagreement between what the software was supposed to do and what the tests had been changed to accept.&lt;/p&gt;

&lt;h2&gt;
  
  
  The billing bug that wasn't really fixed
&lt;/h2&gt;

&lt;p&gt;Consider a billing feature that splits a total of 100 units between three people.&lt;/p&gt;

&lt;p&gt;The product requirement says everyone must pay the same amount. The existing implementation uses ordinary rounding and produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[33, 33, 33]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That adds up to 99, so there's a genuine problem to solve.&lt;/p&gt;

&lt;p&gt;An agent can fix the arithmetic by distributing the remainder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[34, 33, 33]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The total is now correct.&lt;/p&gt;

&lt;p&gt;But there's a catch: the product requirement explicitly says the amounts must be identical.&lt;/p&gt;

&lt;p&gt;The mathematically convenient fix violates the product rule.&lt;/p&gt;

&lt;p&gt;In the reported DEV experiment, the author described running the scenario repeatedly across three coding agents. All ten runs changed the expected result to &lt;code&gt;[34, 33, 33]&lt;/code&gt;, even though the product comment still required identical amounts. Six runs reported the tests passing and continued without asking for a product decision.&lt;/p&gt;

&lt;p&gt;That's the important detail. The agent didn't simply fail to solve the arithmetic problem. It made a decision that belonged to the product specification.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/vig-fast-x-high/your-ai-coding-agent-might-be-cheating-to-pass-ci-heres-how-it-387"&gt;Read the original report on DEV&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test can be green for the wrong reason
&lt;/h2&gt;

&lt;p&gt;Imagine the test originally looks like this:&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;test_equal_split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;split_amount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test is internally inconsistent with the total, but it reflects the stated equal-payment requirement as closely as integer amounts allow.&lt;/p&gt;

&lt;p&gt;The agent changes the expected result:&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;test_equal_split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;split_amount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the test can pass after the implementation is updated.&lt;/p&gt;

&lt;p&gt;The CI pipeline is happy. The total is correct. The expected value matches the implementation.&lt;/p&gt;

&lt;p&gt;But nobody has resolved the actual product question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the requirement equal payments, or is it that the allocated amounts must sum to the exact total?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those are different requirements.&lt;/p&gt;

&lt;p&gt;If equal payments are mandatory, the system needs a different policy. It might reject totals that cannot be divided evenly, preserve fractional currency units, or ask the product owner how the remainder should be handled.&lt;/p&gt;

&lt;p&gt;If exact allocation is mandatory, unequal integer amounts may be acceptable, provided the allocation policy is documented.&lt;/p&gt;

&lt;p&gt;The correct solution depends on a decision the code cannot make on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is where I stop trusting the green checkmark
&lt;/h2&gt;

&lt;p&gt;A passing test is useful evidence. I'm not arguing against tests.&lt;/p&gt;

&lt;p&gt;I'm arguing against treating the test result as the entire definition of correctness.&lt;/p&gt;

&lt;p&gt;There's a difference between these three questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the implementation satisfy the tests?&lt;/li&gt;
&lt;li&gt;Do the tests reflect the requirements?&lt;/li&gt;
&lt;li&gt;Are the requirements themselves consistent with what the product needs?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A green check answers the first question. It doesn't automatically answer the other two.&lt;/p&gt;

&lt;p&gt;This becomes especially important when the same coding agent can modify the source code, rewrite assertions, update snapshots, and report that everything passes.&lt;/p&gt;

&lt;p&gt;The problem isn't necessarily that the agent is deliberately cheating. It can be following the most immediately achievable interpretation of the task while overlooking a constraint that matters more than the failing test.&lt;/p&gt;

&lt;p&gt;From the outside, however, the result can look exactly like a successful fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  There's research behind this, too
&lt;/h2&gt;

&lt;p&gt;This isn't limited to one reported experiment.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://arxiv.org/abs/2610.13065" rel="noopener noreferrer"&gt;TestSuiteBench research paper&lt;/a&gt;, published in October 2026, evaluates coding agents on test-suite generation for 165 open-source applications.&lt;/p&gt;

&lt;p&gt;The benchmark uses detailed specifications, reference implementations, reference tests, and controlled mutations that introduce defects tied to individual requirements.&lt;/p&gt;

&lt;p&gt;The researchers found that the evaluated agents struggled to produce strong test suites from specifications. Their mutation scores remained below 60%, even though 88% of their generated tests passed on the reference implementations.&lt;/p&gt;

&lt;p&gt;Those figures measure different things: passing the correct implementation is not the same as detecting deliberately introduced defects.&lt;/p&gt;

&lt;p&gt;That's the distinction I care about. A test suite can execute successfully without providing strong evidence that the important requirements are protected.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what should the agent actually be allowed to change?
&lt;/h2&gt;

&lt;p&gt;I think the solution starts before the code is written.&lt;/p&gt;

&lt;p&gt;For a task like the billing example, I'd keep three things separate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The requirement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The amounts must be identical, or the allocation must sum exactly to the total. If both are required, the specification needs to explain what happens when they conflict.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent can propose an algorithm, explain its trade-offs, and implement the agreed behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The acceptance tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The expected behavior should come from the agreed requirement, not simply from whatever output the new implementation happens to produce.&lt;/p&gt;

&lt;p&gt;Then I'd add a small but useful rule to the workflow: when an agent needs to change an assertion to make a test pass, it must explain why the original expectation was wrong and identify the requirement that authorizes the change.&lt;/p&gt;

&lt;p&gt;That doesn't mean every test change is suspicious. Requirements evolve, and tests sometimes need updating.&lt;/p&gt;

&lt;p&gt;It means a change to the expected result should be treated as a decision to review, not a routine step in fixing a bug.&lt;/p&gt;

&lt;p&gt;For important behavior, I'd also use an independent test suite or mutation testing to check whether the tests can actually detect violations. The goal isn't to make the workflow heavier for its own sake. It's to stop the same mistaken assumption from silently shaping both the code and the evidence used to approve it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here's the question I keep coming back to
&lt;/h2&gt;

&lt;p&gt;If an AI agent can change the implementation, update the tests, and report success, where should the boundary be?&lt;/p&gt;

&lt;p&gt;Should it be allowed to change expected results when it finds a more mathematically correct implementation? Should it stop and ask for clarification? Or should the CI pipeline reject changes to acceptance tests unless a human explicitly approves them?&lt;/p&gt;

&lt;p&gt;I can see arguments for all three, depending on the project.&lt;/p&gt;

&lt;p&gt;But I wouldn't want a billing rule, a permission check, or a financial invariant to change simply because the new code made the existing test inconvenient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's your rule for AI-generated test changes?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you let the agent update assertions as part of a fix, or do you treat changes to expected behavior as a separate review decision?&lt;/p&gt;

&lt;p&gt;I'd be interested in examples where this distinction has actually caught a bug, especially in production systems.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>testing</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>The Ariadne Thread: Why AI Fails to Follow the Path You Define</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Fri, 16 Oct 2026 21:26:56 +0000</pubDate>
      <link>https://dev.to/sinarezaei/the-ariadne-thread-why-ai-fails-to-follow-the-path-you-define-33ae</link>
      <guid>https://dev.to/sinarezaei/the-ariadne-thread-why-ai-fails-to-follow-the-path-you-define-33ae</guid>
      <description>&lt;p&gt;As a full-stack developer, I don't evaluate an AI-generated solution simply by checking whether the code compiles. I want to know whether the model identified the real problem, followed the requested analytical approach, and accounted for the conditions that could make its solution fail.&lt;/p&gt;

&lt;p&gt;This distinction becomes particularly important when working with distributed systems. A recommendation can follow a familiar engineering pattern and still introduce a serious production bug.&lt;/p&gt;

&lt;p&gt;The problem isn't necessarily that the model lacks a relevant technique. Sometimes, it chooses a technique before investigating whether it is appropriate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern Trap: When a Familiar Solution Is Not Enough
&lt;/h2&gt;

&lt;p&gt;Consider a checkout service built with TypeScript and Node.js. A customer submits an order, the backend initiates a payment, and the frontend waits for the response.&lt;/p&gt;

&lt;p&gt;Now imagine the payment provider processes the request, but the response never reaches the application because of a network timeout.&lt;/p&gt;

&lt;p&gt;From the client's perspective, the operation failed. From the payment provider's perspective, it may have succeeded.&lt;/p&gt;

&lt;p&gt;That distinction creates a difficult problem: the application cannot safely assume that a timeout means the operation was never completed.&lt;/p&gt;

&lt;p&gt;Amazon's Builders' Library explores this exact class of problem in its engineering discussion of &lt;a href="https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/" rel="noopener noreferrer"&gt;Making retries safe with idempotent APIs&lt;/a&gt;. Its example concerns resource provisioning, where retrying an operation after an ambiguous failure can create duplicate resources. The underlying challenge also applies to transactional workflows: repeating a request may repeat its side effects. &lt;/p&gt;

&lt;h2&gt;
  
  
  A Full-Stack Scenario: The Duplicate Payment Problem
&lt;/h2&gt;

&lt;p&gt;Suppose the frontend sends this request:&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="err"&gt;POST /api/checkout
Content-Type: application/json

{
  "orderId": "ord_8421",
  "amount": 149.99,
  "currency": "USD"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend sends a payment request to an external provider. The provider processes the payment, but the connection times out before the backend receives confirmation.&lt;/p&gt;

&lt;p&gt;The frontend displays an error. The customer clicks &lt;strong&gt;Pay&lt;/strong&gt; again.&lt;/p&gt;

&lt;p&gt;If the backend treats the second request as an entirely new operation, the customer could be charged twice.&lt;/p&gt;

&lt;p&gt;A straightforward AI-generated recommendation might be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add a retry mechanism with exponential backoff to handle transient network failures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That advice isn't inherently wrong. Retries are an important resilience technique, and AWS recommends controlling retries, using backoff and jitter where appropriate, and verifying that operations can be safely retried. See the &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/rel_mitigate_interaction_failure_limit_retries.html" rel="noopener noreferrer"&gt;AWS Well-Architected guidance on controlling retry calls&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But in this scenario, retrying is not the first question that needs answering.&lt;/p&gt;

&lt;p&gt;The first question is: &lt;strong&gt;How can the system distinguish a repeated request from a new payment operation when the result of the original request is unknown?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine giving an AI assistant this instruction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Act as a senior full-stack engineer reviewing a production payment system. Analyze the failure semantics before proposing a fix. Distinguish a client timeout from a confirmed payment failure. Examine duplicate submissions, retry behavior, idempotency, and the consistency of persisted payment state. Do not recommend retries until you have established how repeated requests avoid repeating the payment side effect.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A response that jumps directly to retry configuration has missed the central requirement, even if its code uses a sensible backoff algorithm.&lt;/p&gt;

&lt;p&gt;The instruction defined the analytical path. The response followed a familiar pattern instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Analysis Should Actually Examine
&lt;/h2&gt;

&lt;p&gt;A reliable solution requires more than adding a request header or wrapping a network call in a retry loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Give the operation a stable identity
&lt;/h3&gt;

&lt;p&gt;The client or backend needs an idempotency key that identifies one logical payment attempt.&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="err"&gt;POST /api/checkout
Idempotency-Key: checkout_8421_attempt_1
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key must remain the same when the same logical operation is retried. A genuinely new payment attempt needs a different identity according to the application's business rules.&lt;/p&gt;

&lt;p&gt;The backend can use that key to recognize duplicate requests and return the previously recorded result instead of blindly executing the operation again.&lt;/p&gt;

&lt;p&gt;Generating a fresh key on every retry defeats the purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Handle the ambiguous outcome
&lt;/h3&gt;

&lt;p&gt;A timeout tells the application that it did not receive a response within the expected period. It does not prove that the payment provider rejected the payment.&lt;/p&gt;

&lt;p&gt;The system may need to query the provider, retry using the same idempotency key where supported, or mark the operation as pending reconciliation.&lt;/p&gt;

&lt;p&gt;The correct action depends on the payment provider's API contract and its idempotency guarantees. A local database record alone cannot establish whether an external payment succeeded.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Protect the operation's state transitions
&lt;/h3&gt;

&lt;p&gt;Consider this simplified TypeScript example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;idempotencyKey&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&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="nx"&gt;existing&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="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PENDING&lt;/span&gt;&lt;span class="dl"&gt;"&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 illustrates the idea of recording an operation under a stable key. It is &lt;strong&gt;not a complete idempotency implementation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Two concurrent requests could both fail to find an existing record before either creates it. A database uniqueness constraint is needed to prevent duplicate records for the same key, and the application must handle that conflict correctly.&lt;/p&gt;

&lt;p&gt;There is another, deeper issue: recording a request in the local database and charging a payment provider are separate operations. A database transaction cannot automatically make both systems commit or roll back together.&lt;/p&gt;

&lt;p&gt;A production design must account for that boundary. Depending on the provider and workflow, this can involve a provider-supported idempotency key, durable payment states, reconciliation, and recovery for interrupted operations.&lt;/p&gt;

&lt;p&gt;AWS discusses the importance of making the recording of an idempotency token and the associated mutations atomic within the system that owns those mutations. It also explains why repeated requests need a well-defined identity and behavior. See &lt;a href="https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/" rel="noopener noreferrer"&gt;Making retries safe with idempotent APIs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The important engineering question isn't simply whether the implementation contains an idempotency key. It is whether the complete request lifecycle preserves the intended behavior under concurrency, timeouts, retries, and partial failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Actual Test: Did the Model Follow the Instruction?
&lt;/h2&gt;

&lt;p&gt;Now return to the original instruction.&lt;/p&gt;

&lt;p&gt;The assistant was asked to analyze ambiguous outcomes, repeated requests, idempotency, and persisted state before recommending retries.&lt;/p&gt;

&lt;p&gt;A useful evaluation would check whether its answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Distinguishes transport failure from confirmed business-operation failure.&lt;/li&gt;
&lt;li&gt;Preserves the identity of a logical operation across retries.&lt;/li&gt;
&lt;li&gt;Accounts for concurrent duplicate requests.&lt;/li&gt;
&lt;li&gt;Identifies the boundary between local persistence and external side effects.&lt;/li&gt;
&lt;li&gt;Explains how the system recovers when a request succeeds remotely but its result is not recorded locally.&lt;/li&gt;
&lt;li&gt;Proposes tests for repeated submissions, concurrent requests, and timeouts after the provider has processed the operation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the response only recommends exponential backoff, it has not completed the requested analysis.&lt;/p&gt;

&lt;p&gt;If it adds an idempotency header but ignores concurrent requests or partial failure, it has followed part of the path while missing other essential requirements.&lt;/p&gt;

&lt;p&gt;This gives us a more concrete way to evaluate an AI assistant. Instead of asking whether the answer sounds experienced, we can compare its recommendations against explicit engineering conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Instruction–Execution Gap
&lt;/h2&gt;

&lt;p&gt;This is the distinction I want to emphasize: &lt;strong&gt;defining the path is not the same as following the path.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A model can recognize the words &lt;em&gt;retry&lt;/em&gt;, &lt;em&gt;timeout&lt;/em&gt;, and &lt;em&gt;distributed system&lt;/em&gt; and produce a plausible recommendation based on familiar engineering practices. That recommendation might even be correct in a different context.&lt;/p&gt;

&lt;p&gt;The failure occurs when the model does not preserve the specific analytical priorities established by the instruction.&lt;/p&gt;

&lt;p&gt;Giving the model a persona such as "senior full-stack engineer" can help establish the intended perspective. But a job title does not guarantee a particular method of analysis. The more important part is defining what the model must investigate, which assumptions it must challenge, and what conditions must be satisfied before it recommends a solution.&lt;/p&gt;

&lt;p&gt;Even then, instructions alone cannot guarantee correct execution. The response still needs to be evaluated against the requirements, and its claims need to be checked against the system's actual behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ariadne Thread
&lt;/h2&gt;

&lt;p&gt;In Greek mythology, Ariadne gives Theseus a thread that helps him navigate the labyrinth.&lt;/p&gt;

&lt;p&gt;I use that image to describe an analytical framework. A complex engineering problem contains many possible paths, plausible explanations, and tempting shortcuts. The framework establishes a route through them.&lt;/p&gt;

&lt;p&gt;In the payment example, the route begins with the meaning of a timeout, moves through the identity of a logical operation, examines concurrency and external side effects, and only then considers retry behavior.&lt;/p&gt;

&lt;p&gt;Jumping straight to exponential backoff skips the very questions that determine whether retrying is safe.&lt;/p&gt;

&lt;p&gt;The thread is not the instruction itself. It is the connection between the original analytical objective and the decisions made throughout the investigation.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Beyond Fat Controllers: Using the Facade Pattern in Ruby on Rails</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Mon, 12 Oct 2026 07:59:34 +0000</pubDate>
      <link>https://dev.to/sinarezaei/beyond-fat-controllers-using-the-facade-pattern-in-ruby-on-rails-k1g</link>
      <guid>https://dev.to/sinarezaei/beyond-fat-controllers-using-the-facade-pattern-in-ruby-on-rails-k1g</guid>
      <description>&lt;p&gt;A Rails controller often starts small. A user selects a subscription plan, the application processes a payment, and the subscription becomes active.&lt;/p&gt;

&lt;p&gt;Then the requirements grow. You need to handle failed payments, prevent duplicate charges, activate features, and deal with external services that occasionally fail.&lt;/p&gt;

&lt;p&gt;Moving each operation into a service object helps, but the controller may still be responsible for coordinating all those services.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Facade pattern&lt;/strong&gt; offers a cleaner approach: expose one entry point for a complex workflow while keeping its individual responsibilities separate.&lt;/p&gt;

&lt;h2&gt;
  
  
  From a Fat Controller to a Facade
&lt;/h2&gt;

&lt;p&gt;Consider a subscription checkout that involves payment processing, subscription activation, and feature provisioning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SubscriptionsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:plan_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;plan: &lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;succeeded?&lt;/span&gt;
      &lt;span class="n"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;plan: &lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="no"&gt;FeatureProvisioner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;subscription_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;plans_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;alert: &lt;/span&gt;&lt;span class="s2"&gt;"Payment failed"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem isn't the number of lines. The controller knows too much about the workflow: which operation runs first, what depends on payment success, and what happens next.&lt;/p&gt;

&lt;p&gt;As the feature evolves, more conditions and failure scenarios end up in the same action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Facade
&lt;/h2&gt;

&lt;p&gt;We can move that coordination into a dedicated class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SubscriptionActivation&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;payment_processor: &lt;/span&gt;&lt;span class="no"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;subscription_activator: &lt;/span&gt;&lt;span class="no"&gt;SubscriptionActivator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;feature_provisioner: &lt;/span&gt;&lt;span class="no"&gt;FeatureProvisioner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@payment_processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payment_processor&lt;/span&gt;
    &lt;span class="vi"&gt;@subscription_activator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscription_activator&lt;/span&gt;
    &lt;span class="vi"&gt;@feature_provisioner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feature_provisioner&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;plan&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;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@payment_processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;plan: &lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;idempotency_key: &lt;/span&gt;&lt;span class="n"&gt;idempotency_key&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;ActivationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:payment_failed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt;
      &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;succeeded?&lt;/span&gt;

    &lt;span class="n"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@subscription_activator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;plan: &lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;payment: &lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="vi"&gt;@feature_provisioner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="no"&gt;ActivationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Facade coordinates the workflow without implementing every operation itself. Payment processing, subscription activation, and feature provisioning remain separate responsibilities.&lt;/p&gt;

&lt;p&gt;Dependency injection also makes testing easier because each collaborator can be replaced with a test double.&lt;/p&gt;

&lt;p&gt;The controller now has a simpler job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SubscriptionsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:plan_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SubscriptionActivation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;plan: &lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;idempotency_key: &lt;/span&gt;&lt;span class="n"&gt;checkout_attempt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;idempotency_key&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;subscription_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;plans_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;alert: &lt;/span&gt;&lt;span class="s2"&gt;"Unable to activate subscription"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;checkout_attempt&lt;/code&gt; represents a persisted checkout attempt whose idempotency key is reused on retries. The result objects and supporting services are application-specific, not built-in Rails classes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Detail That Matters: Failure Handling
&lt;/h2&gt;

&lt;p&gt;A Facade organizes a workflow, but it doesn't automatically make that workflow reliable.&lt;/p&gt;

&lt;p&gt;Imagine the payment provider confirms a charge, but the database update fails. A database rollback cannot undo a charge made by an external service.&lt;/p&gt;

&lt;p&gt;Retries create another risk. If a customer submits checkout twice, the application must avoid charging them twice for the same attempt.&lt;/p&gt;

&lt;p&gt;Use a stable idempotency key for payment requests. Stripe documents how &lt;a href="https://docs.stripe.com/api/idempotent_requests" rel="noopener noreferrer"&gt;idempotent requests&lt;/a&gt; help prevent duplicate operations when requests are retried.&lt;/p&gt;

&lt;p&gt;External side effects also need careful handling. If feature provisioning happens through another service, a failed request may require a retry or reconciliation process. For workflows where losing an event is unacceptable, a transactional outbox can help ensure that database changes and the corresponding event are recorded together.&lt;/p&gt;

&lt;p&gt;These mechanisms solve different problems. The Facade coordinates the steps; idempotency and reliable event delivery protect the workflow when things go wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Workflow
&lt;/h2&gt;

&lt;p&gt;A useful Facade test verifies the order and conditions of the operations, rather than testing the internals of every collaborator again.&lt;/p&gt;

&lt;p&gt;For example, a declined payment should prevent subscription activation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;SubscriptionActivation&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"does not activate a subscription after payment fails"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;payment_processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instance_double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;activator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instance_double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SubscriptionActivator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;provisioner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instance_double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FeatureProvisioner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment_processor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:call&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;and_return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;instance_double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="no"&gt;PaymentResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;succeeded?: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;facade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;described_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;payment_processor: &lt;/span&gt;&lt;span class="n"&gt;payment_processor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;subscription_activator: &lt;/span&gt;&lt;span class="n"&gt;activator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;feature_provisioner: &lt;/span&gt;&lt;span class="n"&gt;provisioner&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;facade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;build_stubbed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;plan: &lt;/span&gt;&lt;span class="n"&gt;build_stubbed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:plan&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;idempotency_key: &lt;/span&gt;&lt;span class="s2"&gt;"checkout_123"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activator&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;not_to&lt;/span&gt; &lt;span class="n"&gt;have_received&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:call&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;provisioner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;not_to&lt;/span&gt; &lt;span class="n"&gt;have_received&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:call&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example assumes the application's result objects and test factories are defined accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should You Use a Facade?
&lt;/h2&gt;

&lt;p&gt;A Facade is useful when a single business operation requires several components to work together and callers shouldn't need to know the sequence.&lt;/p&gt;

&lt;p&gt;It isn't necessary for every controller action. If an operation is simple, adding another class can create unnecessary indirection. And if the Facade starts implementing every business rule itself, it may simply become another oversized object.&lt;/p&gt;

&lt;p&gt;Keep the distinction clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Controller:&lt;/strong&gt; handles the HTTP request and response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facade:&lt;/strong&gt; coordinates the workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaborating services:&lt;/strong&gt; implement their own responsibilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal isn't to create more classes. It's to make a complicated operation easier to understand, test, and change without spreading its coordination logic throughout the application.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>architecture</category>
      <category>refactoring</category>
      <category>facadepattern</category>
    </item>
    <item>
      <title>AI Should Propose. Systems Should Verify.</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Wed, 07 Oct 2026 05:44:22 +0000</pubDate>
      <link>https://dev.to/sinarezaei/ai-should-propose-systems-should-verify-13f7</link>
      <guid>https://dev.to/sinarezaei/ai-should-propose-systems-should-verify-13f7</guid>
      <description>&lt;h1&gt;
  
  
  AI Should Propose. Systems Should Verify.
&lt;/h1&gt;

&lt;p&gt;An AI agent can produce the correct action and still have no authority to execute it.&lt;/p&gt;

&lt;p&gt;That distinction becomes critical when agents move beyond generating text and start modifying code, calling APIs, changing infrastructure, triggering workflows, or coordinating other agents.&lt;/p&gt;

&lt;p&gt;The question is no longer only:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can the model determine what should happen?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is also:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What happens between the action the model proposes and the action the system actually executes?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A useful starting point is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI proposes. Systems verify. Infrastructure executes.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Direct Execution
&lt;/h2&gt;

&lt;p&gt;Consider an AI coding agent working inside a production environment.&lt;/p&gt;

&lt;p&gt;The agent receives a task:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fix the authentication bug and deploy the change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It analyzes the repository, modifies the code, runs tests, and produces a deployment command.&lt;/p&gt;

&lt;p&gt;The code may be correct.&lt;/p&gt;

&lt;p&gt;The tests may pass.&lt;/p&gt;

&lt;p&gt;The deployment command may be valid.&lt;/p&gt;

&lt;p&gt;None of those facts automatically mean the agent should be allowed to deploy to production.&lt;/p&gt;

&lt;p&gt;This is the difference between &lt;strong&gt;correctness and authority&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A model can correctly determine what should happen while still being unauthorized to perform that action.&lt;/p&gt;

&lt;p&gt;If the same component controls reasoning, authorization, and execution, the trust boundary becomes extremely large:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI decides what to do → AI decides whether it may do it → AI executes it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A safer architecture separates those responsibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put a Boundary Between Reasoning and Execution
&lt;/h2&gt;

&lt;p&gt;Instead of allowing the model to directly invoke sensitive infrastructure, treat its output as a &lt;strong&gt;proposed action&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Intent
    ↓
AI Agent
    ↓
Proposed Action
    ↓
System Validation
    ↓
Policy Check
    ↓
Execution
    ↓
Post-Execution Verification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model produces the request.&lt;/p&gt;

&lt;p&gt;The surrounding system evaluates it.&lt;/p&gt;

&lt;p&gt;The infrastructure executes it only after the required controls have been satisfied.&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 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;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auth-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026.10.04"&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;The control layer can independently check whether the request is structurally valid, whether the target exists, whether the requested operation is permitted, and whether additional controls are required.&lt;/p&gt;

&lt;p&gt;The model does not need to make those decisions itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing Tests Does Not Mean Permission
&lt;/h2&gt;

&lt;p&gt;Imagine the coding agent changes a payment service.&lt;/p&gt;

&lt;p&gt;The tests pass.&lt;/p&gt;

&lt;p&gt;The security checks pass.&lt;/p&gt;

&lt;p&gt;The build succeeds.&lt;/p&gt;

&lt;p&gt;The artifact is signed.&lt;/p&gt;

&lt;p&gt;The model now proposes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deploy to production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The previous results establish that the software may be technically acceptable.&lt;/p&gt;

&lt;p&gt;They do not establish that the current actor is authorized to deploy it.&lt;/p&gt;

&lt;p&gt;That gives us an important separation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Validation → Is this action acceptable as an operation?

Authorization → Is this actor allowed to perform it?

Execution → Perform the approved operation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These questions belong to different layers.&lt;/p&gt;

&lt;p&gt;That separation also makes the architecture easier to audit and change without changing the model's reasoning process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Powerful Agents Do Not Need Unrestricted Authority
&lt;/h2&gt;

&lt;p&gt;Separating reasoning from execution does not require making the agent weak.&lt;/p&gt;

&lt;p&gt;An engineering agent can still have broad capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspect repositories&lt;/li&gt;
&lt;li&gt;create branches&lt;/li&gt;
&lt;li&gt;modify files&lt;/li&gt;
&lt;li&gt;run tests&lt;/li&gt;
&lt;li&gt;inspect logs&lt;/li&gt;
&lt;li&gt;generate deployment plans&lt;/li&gt;
&lt;li&gt;propose infrastructure changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system can simply apply stronger controls to higher-impact operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;production deployment&lt;/li&gt;
&lt;li&gt;credential rotation&lt;/li&gt;
&lt;li&gt;database migration&lt;/li&gt;
&lt;li&gt;destructive infrastructure changes&lt;/li&gt;
&lt;li&gt;financial transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent remains capable of reasoning about complex operations.&lt;/p&gt;

&lt;p&gt;Its capabilities just do not automatically become unrestricted authority.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think in Actions, Not Just Tools
&lt;/h2&gt;

&lt;p&gt;Another architectural mistake is defining control only around tool access.&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 plaintext"&gt;&lt;code&gt;Agent → deploy_tool
Agent → payment_api
Agent → database_api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tool access is often too coarse.&lt;/p&gt;

&lt;p&gt;The more useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What action is the agent attempting to perform, against which resource, under which constraints?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A control layer can therefore reason about something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Actor → Action → Resource → Environment → Scope → Context → Policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the system can distinguish between actions that happen to use the same underlying tool.&lt;/p&gt;

&lt;p&gt;A deployment API, for example, might be capable of deploying to both staging and production. Access to the API alone does not tell us whether a particular production deployment should be allowed.&lt;/p&gt;

&lt;p&gt;The meaningful unit of control is the &lt;strong&gt;action and its context&lt;/strong&gt;, not simply the existence of a tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Agents Start Delegating
&lt;/h2&gt;

&lt;p&gt;The boundary becomes even more important when one agent delegates work to another.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User → Orchestrator Agent → Procurement Agent → Payment Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The procurement agent may determine that a purchase is necessary.&lt;/p&gt;

&lt;p&gt;But the fact that the orchestrator requested the purchase does not automatically give the procurement agent unlimited spending authority.&lt;/p&gt;

&lt;p&gt;Now the system needs to understand more than the requested action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initiator → Delegator → Acting Agent → Action → Resource → Scope
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This introduces a deeper authorization problem: &lt;strong&gt;how authority is delegated between actors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That problem deserves its own layer rather than being squeezed into the basic execution model.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Mental Model
&lt;/h2&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User → AI → Everything
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;think about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User → Intent → AI Agent → Proposed Action → System Controls → Infrastructure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI sits inside the system.&lt;/p&gt;

&lt;p&gt;It does not become the system.&lt;/p&gt;

&lt;p&gt;That gives the architecture a stable control boundary around a component whose behavior is probabilistic.&lt;/p&gt;

&lt;p&gt;The model can change.&lt;/p&gt;

&lt;p&gt;The model can be upgraded.&lt;/p&gt;

&lt;p&gt;The model can be replaced.&lt;/p&gt;

&lt;p&gt;The surrounding execution controls can remain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Principle
&lt;/h2&gt;

&lt;p&gt;Giving an AI agent more tools does not automatically mean giving it more authority.&lt;/p&gt;

&lt;p&gt;The goal is not to prevent AI from acting.&lt;/p&gt;

&lt;p&gt;The goal is to separate &lt;strong&gt;the ability to propose an action&lt;/strong&gt; from &lt;strong&gt;the authority to execute it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI proposes. Systems verify. Infrastructure executes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And once agents start acting on behalf of other agents, the next question becomes unavoidable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who actually has the authority to perform the action?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>What If I'm Not Tired of Coding, Just Tired of Starting?</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Mon, 05 Oct 2026 05:15:00 +0000</pubDate>
      <link>https://dev.to/sinarezaei/what-if-im-not-tired-of-coding-just-tired-of-starting-3d27</link>
      <guid>https://dev.to/sinarezaei/what-if-im-not-tired-of-coding-just-tired-of-starting-3d27</guid>
      <description>&lt;p&gt;There is a strange state you can reach as a developer.&lt;/p&gt;

&lt;p&gt;You still know how to code.&lt;/p&gt;

&lt;p&gt;You still understand the logic. You can still look at a problem and roughly see how you would solve it.&lt;/p&gt;

&lt;p&gt;But opening the editor feels difficult.&lt;/p&gt;

&lt;p&gt;Not writing the code.&lt;/p&gt;

&lt;p&gt;Just starting.&lt;/p&gt;

&lt;p&gt;And I think that's an interesting distinction.&lt;/p&gt;

&lt;p&gt;Maybe sometimes we say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm tired of coding."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;when the real problem is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I don't know what I want to build anymore."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are not the same problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  I can build it. But what is "it"?
&lt;/h2&gt;

&lt;p&gt;Give me a real problem and things become easier.&lt;/p&gt;

&lt;p&gt;An API needs to receive orders.&lt;/p&gt;

&lt;p&gt;A database needs to store them.&lt;/p&gt;

&lt;p&gt;A service needs to process them.&lt;/p&gt;

&lt;p&gt;A UI needs to expose the result.&lt;/p&gt;

&lt;p&gt;Now there is something to push against.&lt;/p&gt;

&lt;p&gt;Architecture starts appearing. Trade-offs become visible. You can make decisions.&lt;/p&gt;

&lt;p&gt;But give a developer an empty repository and say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Build something."&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Suddenly, knowing five languages doesn't tell you what the first commit should be.&lt;/p&gt;

&lt;p&gt;You don't have a programming problem anymore.&lt;/p&gt;

&lt;p&gt;You have a direction problem.&lt;/p&gt;

&lt;p&gt;And I don't think we talk about that enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first step can be the real problem
&lt;/h2&gt;

&lt;p&gt;One of the interesting things in the discussion around burnout is that people don't always describe the same obstacle.&lt;/p&gt;

&lt;p&gt;For some, the answer is rest.&lt;/p&gt;

&lt;p&gt;For others, it is finding something interesting again.&lt;/p&gt;

&lt;p&gt;Someone even described opening a project, changing one variable, closing it, and repeating that for a few days. The important part wasn't the variable. It was getting past the first few seconds of opening the editor.&lt;/p&gt;

&lt;p&gt;That made me think.&lt;/p&gt;

&lt;p&gt;What if "starting" is its own problem?&lt;/p&gt;

&lt;p&gt;You can have the skill.&lt;/p&gt;

&lt;p&gt;You can have the time.&lt;/p&gt;

&lt;p&gt;You can even have a project.&lt;/p&gt;

&lt;p&gt;And still not cross that tiny gap between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I should code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm actually coding.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Maybe the project is the missing piece
&lt;/h2&gt;

&lt;p&gt;There is another possibility.&lt;/p&gt;

&lt;p&gt;Maybe the solution isn't another productivity trick.&lt;/p&gt;

&lt;p&gt;Maybe there simply isn't a problem worth solving right now.&lt;/p&gt;

&lt;p&gt;A developer can be perfectly capable of building something and still have no reason to build it.&lt;/p&gt;

&lt;p&gt;And "just make a side project" doesn't really solve that.&lt;/p&gt;

&lt;p&gt;What kind of project?&lt;/p&gt;

&lt;p&gt;A game?&lt;/p&gt;

&lt;p&gt;A SaaS?&lt;/p&gt;

&lt;p&gt;A CLI tool?&lt;/p&gt;

&lt;p&gt;A portfolio project?&lt;/p&gt;

&lt;p&gt;Something useful?&lt;/p&gt;

&lt;p&gt;Something technically impressive?&lt;/p&gt;

&lt;p&gt;Something completely stupid but fun?&lt;/p&gt;

&lt;p&gt;The moment you ask those questions, you realize that "build something" isn't actually a small instruction.&lt;/p&gt;

&lt;p&gt;It's a huge one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills can survive when motivation doesn't
&lt;/h2&gt;

&lt;p&gt;This is probably the part I find most interesting.&lt;/p&gt;

&lt;p&gt;You don't necessarily forget programming because you stop coding for a while.&lt;/p&gt;

&lt;p&gt;You may still remember the syntax.&lt;/p&gt;

&lt;p&gt;You may still understand the architecture.&lt;/p&gt;

&lt;p&gt;You may still be able to solve the problem once somebody gives you one.&lt;/p&gt;

&lt;p&gt;But the connection between &lt;strong&gt;"I can build this"&lt;/strong&gt; and &lt;strong&gt;"I want to build this"&lt;/strong&gt; can disappear.&lt;/p&gt;

&lt;p&gt;And maybe that's why some developers don't need another tutorial.&lt;/p&gt;

&lt;p&gt;They need something that makes them curious again.&lt;/p&gt;

&lt;p&gt;A problem.&lt;/p&gt;

&lt;p&gt;An idea.&lt;/p&gt;

&lt;p&gt;A person asking for something.&lt;/p&gt;

&lt;p&gt;An open-source issue.&lt;/p&gt;

&lt;p&gt;An old project that suddenly looks interesting again.&lt;/p&gt;

&lt;p&gt;Anything that gives the technical ability somewhere to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how do you start again?
&lt;/h2&gt;

&lt;p&gt;Honestly, I don't think there is one universal answer.&lt;/p&gt;

&lt;p&gt;Sometimes the answer might be rest.&lt;/p&gt;

&lt;p&gt;Sometimes it might be stepping away from the editor.&lt;/p&gt;

&lt;p&gt;Sometimes it might be writing an idea on paper before writing code.&lt;/p&gt;

&lt;p&gt;Sometimes it might simply be finding a problem that makes you think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Wait... I could actually build that."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And maybe that's the more useful question than:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How do I become productive again?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe it's:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What would make me want to build something again?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you ever reached a point where you still had the skills, but couldn't bring yourself to start?&lt;/p&gt;

&lt;p&gt;Was the problem the coding itself, the lack of ideas, or simply having nothing that felt worth building?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
      <category>devlife</category>
      <category>programming</category>
    </item>
    <item>
      <title>Getting to Know Yourself: Understanding self in Ruby</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Fri, 02 Oct 2026 09:42:40 +0000</pubDate>
      <link>https://dev.to/sinarezaei/getting-to-know-yourself-understanding-self-in-ruby-8l6</link>
      <guid>https://dev.to/sinarezaei/getting-to-know-yourself-understanding-self-in-ruby-8l6</guid>
      <description>&lt;p&gt;&lt;code&gt;self&lt;/code&gt; is one of those Ruby keywords that looks easy until you meet it in real code.&lt;/p&gt;

&lt;p&gt;You see it in a setter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalized_name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in a class method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_cents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then somewhere inside a DSL or &lt;code&gt;instance_eval&lt;/code&gt;, and suddenly the question isn't really:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What does &lt;code&gt;self&lt;/code&gt; mean?&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who is &lt;code&gt;self&lt;/code&gt; right here?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That small change makes Ruby's object model much easier to follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setter case is where it starts to matter
&lt;/h2&gt;

&lt;p&gt;Consider a model-like object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&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="nf"&gt;downcase&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&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="nf"&gt;downcase&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;contact&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Maya"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"MAYA@EXAMPLE.COM "&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normalize&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is something easy to miss here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&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="nf"&gt;downcase&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&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="nf"&gt;downcase&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because those two lines are doing different things.&lt;/p&gt;

&lt;p&gt;The second one is a local variable assignment.&lt;/p&gt;

&lt;p&gt;The first one calls the &lt;code&gt;name=&lt;/code&gt; setter on the current object.&lt;/p&gt;

&lt;p&gt;Ruby lets us call the getter without explicitly writing the receiver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But assignment creates an ambiguity. When we write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we are making the receiver explicit.&lt;/p&gt;

&lt;p&gt;In other words:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Maya"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;Call &lt;code&gt;name=&lt;/code&gt; on the object I'm currently working with.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That object is &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And notice something important.&lt;/p&gt;

&lt;p&gt;We didn't create &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We didn't assign it.&lt;/p&gt;

&lt;p&gt;Ruby already knew what &lt;code&gt;self&lt;/code&gt; was when &lt;code&gt;normalize&lt;/code&gt; started running.&lt;/p&gt;

&lt;p&gt;That is the first useful clue.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;self&lt;/code&gt; is less about a special keyword and more about &lt;strong&gt;the object currently receiving the work&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Most of the time, Ruby hides it from you
&lt;/h2&gt;

&lt;p&gt;Take a slightly more realistic object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;
    &lt;span class="vi"&gt;@total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:open&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;valid_for_closing?&lt;/span&gt;

    &lt;span class="n"&gt;mark_as_paid&lt;/span&gt;
    &lt;span class="n"&gt;notify_customer&lt;/span&gt;
    &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid_for_closing?&lt;/span&gt;
    &lt;span class="vi"&gt;@total&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;positive?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:open&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mark_as_paid&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:paid&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify_customer&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Invoice notification sent to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@customer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;close&lt;/code&gt;, Ruby sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;mark_as_paid&lt;/span&gt;
&lt;span class="n"&gt;notify_customer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could make the receiver explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_as_paid&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notify_customer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But normally there is no reason to.&lt;/p&gt;

&lt;p&gt;The receiver is already there.&lt;/p&gt;

&lt;p&gt;It is &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So when Ruby evaluates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;mark_as_paid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the useful mental model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_as_paid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The receiver is implicit.&lt;/p&gt;

&lt;p&gt;This is one of those Ruby features that makes code pleasant to read, but it can also hide an important detail.&lt;/p&gt;

&lt;p&gt;The receiver isn't missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is invisible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that becomes interesting when we start asking when Ruby changes that context.&lt;/p&gt;

&lt;h2&gt;
  
  
  There are scope gates for &lt;code&gt;self&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is where I find it useful to stop thinking about every Ruby construct as if it creates a new &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It doesn't.&lt;/p&gt;

&lt;p&gt;A block is a good example.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Report&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;failed_rows&lt;/span&gt;
    &lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="vi"&gt;@rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:failed&lt;/span&gt;

      &lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;reason: &lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;failures&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;row&lt;/code&gt; is the block's local value.&lt;/p&gt;

&lt;p&gt;It is not &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And the block does not automatically replace the surrounding &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So inside that block, &lt;code&gt;self&lt;/code&gt; is still the &lt;code&gt;Report&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;This gives us a useful mental distinction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not every nested piece of Ruby code is a new &lt;code&gt;self&lt;/code&gt; scope.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A block can introduce local variables and control flow without automatically changing the receiver.&lt;/p&gt;

&lt;p&gt;But structures such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;create important boundaries in how Ruby evaluates code and what &lt;code&gt;self&lt;/code&gt; refers to.&lt;/p&gt;

&lt;p&gt;And then there are APIs that deliberately change it.&lt;/p&gt;

&lt;p&gt;For this article, I'll call these boundaries &lt;strong&gt;scope gates&lt;/strong&gt;. That's a mental model, not an official Ruby term.&lt;/p&gt;

&lt;p&gt;The useful idea is simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A block is not automatically a &lt;code&gt;self&lt;/code&gt; gate. Context-changing constructs and APIs are.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you see that, some Ruby behavior that initially feels inconsistent becomes much easier to predict.&lt;/p&gt;

&lt;p&gt;And now we can look at the class side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same keyword can point at the class
&lt;/h2&gt;

&lt;p&gt;Consider a factory-style class method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;
    &lt;span class="vi"&gt;@total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_cents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cents&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;summary&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@customer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: $&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%.2f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_cents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Maya"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;summary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_cents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can look like special Ruby grammar.&lt;/p&gt;

&lt;p&gt;It isn't really a separate universe.&lt;/p&gt;

&lt;p&gt;The important part is still the receiver.&lt;/p&gt;

&lt;p&gt;While Ruby evaluates the body of the class definition, &lt;code&gt;self&lt;/code&gt; refers to the &lt;code&gt;Invoice&lt;/code&gt; class object.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_cents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is essentially saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Define &lt;code&gt;from_cents&lt;/code&gt; on the object currently referenced by &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And in this context, that object is &lt;code&gt;Invoice&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is why this works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_cents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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 class itself is an object, and that object can receive method calls.&lt;/p&gt;

&lt;p&gt;Now compare the two situations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;summary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the receiver is an &lt;code&gt;Invoice&lt;/code&gt; instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_cents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the receiver is the &lt;code&gt;Invoice&lt;/code&gt; class object.&lt;/p&gt;

&lt;p&gt;Nothing about the keyword changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The context did.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That distinction is much more useful than memorizing "instance method versus class method" as two unrelated Ruby features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails makes this even more visible
&lt;/h2&gt;

&lt;p&gt;You see the same idea constantly in Rails models.&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 ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Subscription&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;:active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: :active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;customer: &lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cancel!&lt;/span&gt;
    &lt;span class="n"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;status: :cancelled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;cancelled_at: &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several receivers involved here.&lt;/p&gt;

&lt;p&gt;When you call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancel!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the receiver is the &lt;code&gt;subscription&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;cancel!&lt;/code&gt;, the call to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is being sent to that instance.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is sent to the &lt;code&gt;Subscription&lt;/code&gt; class object.&lt;/p&gt;

&lt;p&gt;And Rails adds another interesting layer with &lt;code&gt;scope&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The block passed to &lt;code&gt;scope&lt;/code&gt; is not simply "a new self." The surrounding Ruby context and the way Rails evaluates the scope both matter.&lt;/p&gt;

&lt;p&gt;This is why a useful question when reading Rails code is not just:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What method is this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;but:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What object is receiving this method here?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question scales much better when the code gets more dynamic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then &lt;code&gt;instance_eval&lt;/code&gt; deliberately moves the context
&lt;/h2&gt;

&lt;p&gt;Now we can make the context change explicit.&lt;/p&gt;

&lt;p&gt;Consider a configuration object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;values&lt;/span&gt;
    &lt;span class="vi"&gt;@values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:environment&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
  &lt;span class="vi"&gt;@values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:region&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west"&lt;/span&gt;
  &lt;span class="vi"&gt;@values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:logging&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:verbose&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:environment&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:region&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why can this block access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@values&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;directly?&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;instance_eval&lt;/code&gt; evaluates the block with the receiver as &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before entering the block, &lt;code&gt;self&lt;/code&gt; referred to the surrounding context.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;self&lt;/code&gt; becomes &lt;code&gt;config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is a deliberate context change.&lt;/p&gt;

&lt;p&gt;And this is where the earlier idea of scope gates becomes useful.&lt;/p&gt;

&lt;p&gt;The block itself didn't inherently create a new &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;instance_eval&lt;/code&gt; did.&lt;/p&gt;

&lt;p&gt;Ruby gives the programmer an explicit mechanism for saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Evaluate this code as if this object is the current receiver.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a very different thing from simply passing a block around.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;instance_exec&lt;/code&gt; takes the same idea one step further
&lt;/h2&gt;

&lt;p&gt;There is a subtle difference between &lt;code&gt;instance_eval&lt;/code&gt; and &lt;code&gt;instance_exec&lt;/code&gt; that becomes important when designing DSLs.&lt;/p&gt;

&lt;p&gt;Suppose we want to configure an object, but we also want to pass information into the block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Endpoint&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;settings&lt;/span&gt;
    &lt;span class="vi"&gt;@settings&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/users"&lt;/span&gt;

&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;http_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="vi"&gt;@settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:method&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http_method&lt;/span&gt;
  &lt;span class="vi"&gt;@settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;
  &lt;span class="vi"&gt;@settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:authenticated&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;settings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;http_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;instance_exec&lt;/code&gt; changes &lt;code&gt;self&lt;/code&gt; to &lt;code&gt;endpoint&lt;/code&gt;, just like the context-changing idea we saw with &lt;code&gt;instance_eval&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But it also lets us pass arguments into the block.&lt;/p&gt;

&lt;p&gt;So inside the block we have both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pointing at &lt;code&gt;endpoint&lt;/code&gt;, and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;http_method&lt;/span&gt;
&lt;span class="n"&gt;route&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;coming from the outside.&lt;/p&gt;

&lt;p&gt;That combination is useful when building expressive APIs.&lt;/p&gt;

&lt;p&gt;It lets a DSL control the execution context without losing the ability to feed the block data.&lt;/p&gt;

&lt;p&gt;And suddenly a pattern like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;
  &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="s2"&gt;"/users"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;starts looking less magical.&lt;/p&gt;

&lt;p&gt;The DSL designer may be combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a receiver that becomes &lt;code&gt;self&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a block that defines the configuration&lt;/li&gt;
&lt;li&gt;arguments or yielded values&lt;/li&gt;
&lt;li&gt;method dispatch through that context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The syntax is the surface.&lt;/p&gt;

&lt;p&gt;The context is the machinery underneath.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is why Ruby DSLs can feel strange
&lt;/h2&gt;

&lt;p&gt;Once you start thinking in terms of context, Ruby DSLs become less mysterious.&lt;/p&gt;

&lt;p&gt;You might see code that looks almost like configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="s2"&gt;"/users"&lt;/span&gt;
  &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;
  &lt;span class="n"&gt;authentication&lt;/span&gt; &lt;span class="ss"&gt;:required&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting question isn't only:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What does &lt;code&gt;resource&lt;/code&gt; do?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is also:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is &lt;code&gt;self&lt;/code&gt; while this block is being evaluated?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe the DSL is yielding to a builder object.&lt;/p&gt;

&lt;p&gt;Maybe it is using &lt;code&gt;instance_eval&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Maybe it is using &lt;code&gt;instance_exec&lt;/code&gt; because it needs both a changed receiver and arguments.&lt;/p&gt;

&lt;p&gt;Maybe it is delegating method calls.&lt;/p&gt;

&lt;p&gt;The surface syntax can look almost identical while the underlying execution model is very different.&lt;/p&gt;

&lt;p&gt;That's why &lt;code&gt;self&lt;/code&gt; becomes particularly useful when reading metaprogramming-heavy Ruby.&lt;/p&gt;

&lt;p&gt;Instead of getting stuck on the syntax, look for the receiver.&lt;/p&gt;

&lt;p&gt;Then look for the context.&lt;/p&gt;

&lt;p&gt;Then look for the point where that context changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, who is &lt;code&gt;self&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;I don't think of &lt;code&gt;self&lt;/code&gt; as a definition I need to memorize anymore.&lt;/p&gt;

&lt;p&gt;I think of it as a signpost.&lt;/p&gt;

&lt;p&gt;When I see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which object is receiving this assignment?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is &lt;code&gt;self&lt;/code&gt; while Ruby is evaluating this class body?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did Ruby just change the receiver for this block?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ask two questions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is &lt;code&gt;self&lt;/code&gt; now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What data was passed into this context?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And when I see a Ruby DSL that seems to have methods appearing out of nowhere, I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who is &lt;code&gt;self&lt;/code&gt; here, and where did that context come from?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question usually gets me closer to the actual behavior than simply remembering what the keyword means.&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;self&lt;/code&gt; isn't really the interesting part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you start reading Ruby through that lens, &lt;code&gt;self&lt;/code&gt; becomes less of a keyword to memorize and more of a way to locate yourself inside Ruby's object model.&lt;/p&gt;

&lt;p&gt;And I'm curious how other Ruby developers approach this.&lt;/p&gt;

&lt;p&gt;Do you consciously think about &lt;code&gt;self&lt;/code&gt; while reading Ruby code, or has it become one of those things you only notice when the context suddenly changes?&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>programming</category>
      <category>metaprogramming</category>
    </item>
    <item>
      <title>OAuth 2.0 Device Flow: How Authentication Works When the Device Can't Handle the Login</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Thu, 24 Sep 2026 14:03:49 +0000</pubDate>
      <link>https://dev.to/sinarezaei/oauth-20-device-flow-how-authentication-works-when-the-device-cant-handle-the-login-3i1a</link>
      <guid>https://dev.to/sinarezaei/oauth-20-device-flow-how-authentication-works-when-the-device-cant-handle-the-login-3i1a</guid>
      <description>&lt;p&gt;Authentication is straightforward when a user has a browser, keyboard, and screen.&lt;/p&gt;

&lt;p&gt;A laptop can open a login page. A phone can redirect the user to an identity provider. But what happens when the device is a Smart TV, game console, CLI tool, or another device that is not well suited for entering credentials?&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;OAuth 2.0 Device Authorization Grant&lt;/strong&gt;, commonly called &lt;strong&gt;Device Flow&lt;/strong&gt;, becomes useful.&lt;/p&gt;

&lt;p&gt;Instead of asking the device to handle the login itself, Device Flow lets the user authenticate on another device while the original device waits for the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic idea
&lt;/h2&gt;

&lt;p&gt;The device starts the authentication process and receives a set of temporary values from the authorization server.&lt;/p&gt;

&lt;p&gt;The user then opens a verification URL on a phone or computer, enters a short code, and completes authentication there.&lt;/p&gt;

&lt;p&gt;The original device does not need to display or process the full login interface.&lt;/p&gt;

&lt;p&gt;The flow looks roughly 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;Device
   |
   |  Device Authorization Request
   v
Authorization Server
   |
   |  device_code + user_code
   v
Device
   |
   |----------------------&amp;gt; User's Browser / Phone
   |                         |
   |                         | Login + consent
   |                         v
   |                    Authorization Server
   |
   |  Poll Token Endpoint
   v
Authorization Server
   |
   |  Access Token
   v
Device
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that &lt;strong&gt;the device and the user's browser do not have to be the same device&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the device receives
&lt;/h2&gt;

&lt;p&gt;The device first sends a request to the authorization server's device authorization endpoint.&lt;/p&gt;

&lt;p&gt;The response typically contains values such as:&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;"device_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4fJ8..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WDJB-MJHT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verification_uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/device"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&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;The important fields are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;device_code&lt;/code&gt;: identifies the authentication request and is used by the device when requesting tokens.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user_code&lt;/code&gt;: a short code the user enters on another device.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;verification_uri&lt;/code&gt;: where the user goes to authenticate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expires_in&lt;/code&gt;: how long the authorization request remains valid.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;interval&lt;/code&gt;: how often the device should poll the token endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;device_code&lt;/code&gt; and &lt;code&gt;user_code&lt;/code&gt; serve different purposes.&lt;/p&gt;

&lt;p&gt;The device keeps the &lt;code&gt;device_code&lt;/code&gt;. The user normally only sees the &lt;code&gt;user_code&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The user finishes authentication somewhere else
&lt;/h2&gt;

&lt;p&gt;The device can now display something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open:
https://example.com/device

Enter code:
WDJB-MJHT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user opens the URL on a phone or computer, enters the code, signs in, and approves the requested access.&lt;/p&gt;

&lt;p&gt;Nothing special has to happen on the original device while this is taking place.&lt;/p&gt;

&lt;p&gt;It simply waits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The device polls for the token
&lt;/h2&gt;

&lt;p&gt;After receiving the device authorization response, the device starts polling the token endpoint.&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="err"&gt;POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code
&amp;amp;device_code=4fJ8...
&amp;amp;client_id=my-device
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user has not finished authentication yet, the authorization server can return:&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;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authorization_pending"&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;The device waits and tries again.&lt;/p&gt;

&lt;p&gt;It should not continuously hammer the server. The server can also return:&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;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slow_down"&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;which tells the client to increase the polling interval.&lt;/p&gt;

&lt;p&gt;Once the user has successfully authenticated and granted access, the response contains the token:&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;"access_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;"eyJ..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&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;The device can then use that access token to call the protected API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can happen while the device waits?
&lt;/h2&gt;

&lt;p&gt;The device needs to handle more than just success and &lt;code&gt;authorization_pending&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Common responses include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Response&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;authorization_pending&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The user has not finished yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;slow_down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Poll less frequently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expired_token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The device authorization request expired&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;access_denied&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The user rejected the request&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is important because Device Flow is inherently asynchronous.&lt;/p&gt;

&lt;p&gt;The device cannot assume that authentication will finish immediately, or even finish successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a separate user code?
&lt;/h2&gt;

&lt;p&gt;You might wonder why the user does not simply enter the long &lt;code&gt;device_code&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The answer is usability.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;device_code&lt;/code&gt; is intended to be handled by the device and can be long or difficult to type. The &lt;code&gt;user_code&lt;/code&gt; is designed for humans and is usually much shorter.&lt;/p&gt;

&lt;p&gt;That separation also lets the authorization server associate the human-friendly code with the underlying authorization request without exposing the device's internal identifier as the thing the user has to type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security still matters
&lt;/h2&gt;

&lt;p&gt;Device Flow solves an authentication-interface problem, but it does not remove the security requirements around authentication.&lt;/p&gt;

&lt;p&gt;The verification endpoint should protect against things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;brute-force attempts against short user codes&lt;/li&gt;
&lt;li&gt;excessive requests&lt;/li&gt;
&lt;li&gt;reuse of expired or already-consumed codes&lt;/li&gt;
&lt;li&gt;unauthorized access to the authorization request&lt;/li&gt;
&lt;li&gt;overly long-lived device codes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;User codes should have limited lifetimes and a sufficiently large search space. Once a code has been successfully consumed, it should not remain usable.&lt;/p&gt;

&lt;p&gt;The server also needs to handle concurrent requests safely. Two requests attempting to consume the same authorization state should not both be able to obtain authorization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Device Flow vs. PKCE
&lt;/h2&gt;

&lt;p&gt;Device Flow and Authorization Code Flow with PKCE solve related but different problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PKCE&lt;/strong&gt; is useful when the client can redirect the user through a browser but needs protection against authorization-code interception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Device Flow&lt;/strong&gt; is useful when the device itself is not a practical place to perform the browser-based login.&lt;/p&gt;

&lt;p&gt;A CLI application is a good example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI
 |
 | "Go to this URL and enter this code"
 v
Browser
 |
 | Login + consent
 v
Authorization Server
 |
 v
CLI receives token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trying to force a full browser login experience into a terminal would make the client unnecessarily complicated.&lt;/p&gt;

&lt;p&gt;Device Flow moves that responsibility to a device that is actually good at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Device Flow makes sense
&lt;/h2&gt;

&lt;p&gt;You will commonly see this pattern with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smart TVs&lt;/li&gt;
&lt;li&gt;game consoles&lt;/li&gt;
&lt;li&gt;command-line applications&lt;/li&gt;
&lt;li&gt;media players&lt;/li&gt;
&lt;li&gt;IoT devices&lt;/li&gt;
&lt;li&gt;devices with limited input capabilities&lt;/li&gt;
&lt;li&gt;applications where the authentication device and usage device are different&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The common characteristic is not necessarily weak hardware.&lt;/p&gt;

&lt;p&gt;It is that &lt;strong&gt;the device is a poor place to perform interactive authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical implementation checklist
&lt;/h2&gt;

&lt;p&gt;When implementing Device Flow, make sure the client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stores the &lt;code&gt;device_code&lt;/code&gt; securely&lt;/li&gt;
&lt;li&gt;displays the verification URI and user code clearly&lt;/li&gt;
&lt;li&gt;respects the polling interval&lt;/li&gt;
&lt;li&gt;handles &lt;code&gt;authorization_pending&lt;/code&gt; and &lt;code&gt;slow_down&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;stops when the request expires&lt;/li&gt;
&lt;li&gt;handles user denial&lt;/li&gt;
&lt;li&gt;securely stores the resulting access token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The authorization server should, at minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expire device authorization requests&lt;/li&gt;
&lt;li&gt;rate-limit verification and token endpoints&lt;/li&gt;
&lt;li&gt;prevent user-code guessing and abuse&lt;/li&gt;
&lt;li&gt;ensure authorization state cannot be reused incorrectly&lt;/li&gt;
&lt;li&gt;issue tokens only after the required user authorization has completed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The bigger engineering idea
&lt;/h2&gt;

&lt;p&gt;Device Flow is a useful example of a broader engineering principle.&lt;/p&gt;

&lt;p&gt;A component does not have to perform every part of a process itself.&lt;/p&gt;

&lt;p&gt;A Smart TV is good at displaying content, but terrible at typing a password with a remote control. A CLI is excellent for commands, but not necessarily for interactive web authentication.&lt;/p&gt;

&lt;p&gt;Instead of forcing the device to do something badly, Device Flow gives each part of the system a job it is better suited to perform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Device       → starts the request and waits
Browser      → handles interactive authentication
Server       → manages authorization and tokens
Device       → uses the resulting access token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That separation is what makes the flow practical.&lt;/p&gt;

&lt;p&gt;OAuth 2.0 Device Flow is therefore less about adding another authentication screen and more about &lt;strong&gt;moving authentication to the place where it can be performed properly&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>authentication</category>
      <category>oauth</category>
    </item>
    <item>
      <title>Navigating the Landscape of Development: Building a Sustainable Future</title>
      <dc:creator>Sina Rezaei</dc:creator>
      <pubDate>Fri, 24 Nov 2023 13:24:43 +0000</pubDate>
      <link>https://dev.to/sinarezaei/navigating-the-landscape-of-development-building-a-sustainable-future-4i9o</link>
      <guid>https://dev.to/sinarezaei/navigating-the-landscape-of-development-building-a-sustainable-future-4i9o</guid>
      <description>&lt;p&gt;When people hear &lt;strong&gt;sustainable development&lt;/strong&gt;, software engineering probably isn't the first thing that comes to mind.&lt;/p&gt;

&lt;p&gt;Usually, we picture renewable energy, electric cars, green buildings, or a company presentation full of words like &lt;em&gt;impact&lt;/em&gt;, &lt;em&gt;future&lt;/em&gt;, and &lt;em&gt;responsibility&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But there is another side of sustainability that developers and architects deal with every day.&lt;/p&gt;

&lt;p&gt;It's what happens when your application gets 10x 4 more infrastructure?&lt;/p&gt;

&lt;p&gt;Does a database query suddenly become 20 times more expensive?&lt;/p&gt;

&lt;p&gt;Are you keeping machines running when nobody is using them?&lt;/p&gt;

&lt;p&gt;Are you storing years of data that nobody reads?&lt;/p&gt;

&lt;p&gt;And perhaps the uncomfortable one: did the system become more complicated simply because the team kept adding things?&lt;/p&gt;

&lt;p&gt;That's where sustainability becomes an engineering problem rather than a marketing concept.&lt;/p&gt;

&lt;p&gt;For me, sustainable software is mostly about building systems that don't constantly need more resources just to keep doing the same job.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Economic Sustainability: Fix the System Before Buying More Infrastructure
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways to waste money in software is to treat infrastructure as the solution to every performance problem.&lt;/p&gt;

&lt;p&gt;The application is slow?&lt;/p&gt;

&lt;p&gt;Add CPU.&lt;/p&gt;

&lt;p&gt;Database is struggling?&lt;/p&gt;

&lt;p&gt;Buy a bigger instance.&lt;/p&gt;

&lt;p&gt;Traffic increased?&lt;/p&gt;

&lt;p&gt;Add more servers.&lt;/p&gt;

&lt;p&gt;Memory usage is high?&lt;/p&gt;

&lt;p&gt;Throw RAM at it.&lt;/p&gt;

&lt;p&gt;Sometimes that's exactly the right decision. But sometimes you're just paying more to hide a bad design.&lt;/p&gt;

&lt;p&gt;AWS's Well-Architected Framework treats cost optimization and performance efficiency as separate but connected architectural concerns. Its guidance specifically recommends considering cost during architectural decisions, right-sizing resources, and continuously reviewing the resources that drive infrastructure spending.&lt;/p&gt;

&lt;p&gt;A simple example is a database query.&lt;/p&gt;

&lt;p&gt;Imagine something like:&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;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;18472&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;customer_id&lt;/code&gt; isn't indexed properly, the application might still work perfectly with 500 records.&lt;/p&gt;

&lt;p&gt;Then the database gets millions of rows.&lt;/p&gt;

&lt;p&gt;Suddenly the same innocent-looking query becomes a production problem.&lt;/p&gt;

&lt;p&gt;You can increase the database size, or you can ask a much better question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is the database doing this much work in the first place?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adding the right index, changing the query, reducing unnecessary columns, or redesigning how the data is accessed can sometimes remove the bottleneck without buying a larger machine.&lt;/p&gt;

&lt;p&gt;That's sustainable architecture in a very practical sense.&lt;/p&gt;

&lt;p&gt;You're making the system do less work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't confuse scalability with throwing hardware at the problem
&lt;/h3&gt;

&lt;p&gt;There's nothing inherently wrong with horizontal scaling, containers, serverless functions, or managed infrastructure.&lt;/p&gt;

&lt;p&gt;The problem starts when infrastructure becomes a substitute for engineering.&lt;/p&gt;

&lt;p&gt;A lightweight service that scales from 2 instances to 20 because demand actually increased is one thing.&lt;/p&gt;

&lt;p&gt;A service that needs 20 instances because every request triggers unnecessary database calls is another.&lt;/p&gt;

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

&lt;p&gt;The same idea applies to serverless systems. Serverless isn't automatically "green", and containers aren't automatically efficient either. The actual workload matters.&lt;/p&gt;

&lt;p&gt;A badly designed function running millions of unnecessary operations is still badly designed, even if it happens to be running in a serverless platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reuse before rewrite
&lt;/h3&gt;

&lt;p&gt;There's another form of sustainability that gets less attention: &lt;strong&gt;the codebase itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not every old module needs to be rewritten.&lt;/p&gt;

&lt;p&gt;Sometimes refactoring a painful component is enough.&lt;/p&gt;

&lt;p&gt;A full rewrite means rebuilding behavior, tests, integrations, deployment pipelines, monitoring, edge cases, and all the strange little things the original system learned over the years.&lt;/p&gt;

&lt;p&gt;Sometimes you absolutely should rewrite.&lt;/p&gt;

&lt;p&gt;But "let's rewrite everything" shouldn't be the default response to technical debt.&lt;/p&gt;

&lt;p&gt;A sustainable codebase is one that can evolve without requiring the team to burn down the entire house every few years.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Social Sustainability: The System Has Humans in the Loop
&lt;/h2&gt;

&lt;p&gt;Here's something easy to forget when talking about architecture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The people maintaining the system are part of the system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If only one developer understands how authentication, payments, deployment, and database migrations work, you don't have a resilient architecture.&lt;/p&gt;

&lt;p&gt;You have a knowledge bottleneck.&lt;/p&gt;

&lt;p&gt;And eventually that bottleneck becomes a production risk.&lt;/p&gt;

&lt;p&gt;Imagine a developer leaves the company.&lt;/p&gt;

&lt;p&gt;Nobody knows why a particular service exists.&lt;/p&gt;

&lt;p&gt;Nobody knows why a specific queue has a strange retry policy.&lt;/p&gt;

&lt;p&gt;Nobody knows why deleting one database column breaks an unrelated reporting system.&lt;/p&gt;

&lt;p&gt;The documentation says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Legacy logic. Don't touch."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not documentation.&lt;/p&gt;

&lt;p&gt;That's a warning label.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce knowledge silos
&lt;/h3&gt;

&lt;p&gt;Good architecture isn't only about separating services.&lt;/p&gt;

&lt;p&gt;It's also about making the system understandable.&lt;/p&gt;

&lt;p&gt;Clear boundaries, useful documentation, sensible naming, tests, diagrams where they actually help, and explicit ownership all reduce the amount of tribal knowledge required to work on a system.&lt;/p&gt;

&lt;p&gt;Domain-Driven Design can help here because it encourages teams to make business boundaries explicit instead of allowing everything to slowly become one giant blob of shared logic.&lt;/p&gt;

&lt;p&gt;You don't need DDD everywhere.&lt;/p&gt;

&lt;p&gt;A small application doesn't need a 40-page architecture document.&lt;/p&gt;

&lt;p&gt;But when the domain becomes complicated, making the boundaries visible can save a lot of pain later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture should be discussable
&lt;/h3&gt;

&lt;p&gt;Another useful practice is the architecture decision record, RFC, or similar design-review process.&lt;/p&gt;

&lt;p&gt;The important part isn't the acronym.&lt;/p&gt;

&lt;p&gt;It's the habit of writing down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are we trying to solve?&lt;/li&gt;
&lt;li&gt;What options did we consider?&lt;/li&gt;
&lt;li&gt;What are the trade-offs?&lt;/li&gt;
&lt;li&gt;Why did we choose this approach?&lt;/li&gt;
&lt;li&gt;What could make us change the decision later?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That creates something much more valuable than a diagram nobody reads six months later.&lt;/p&gt;

&lt;p&gt;It creates &lt;strong&gt;context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And context is extremely useful when the original author isn't in the room anymore.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Accessibility Is Also Part of Sustainability
&lt;/h2&gt;

&lt;p&gt;There is another human side that often gets left out of the conversation: accessibility.&lt;/p&gt;

&lt;p&gt;A system that works beautifully for one group of users but becomes difficult or impossible to use for others isn't particularly sustainable from a product perspective.&lt;/p&gt;

&lt;p&gt;The W3C's WCAG framework organizes accessibility around four principles: &lt;strong&gt;perceivable, operable, understandable, and robust&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That translates into very practical engineering decisions.&lt;/p&gt;

&lt;p&gt;Can someone navigate the interface with a keyboard?&lt;/p&gt;

&lt;p&gt;Does the form provide useful error messages?&lt;/p&gt;

&lt;p&gt;Do buttons and controls have meaningful labels?&lt;/p&gt;

&lt;p&gt;Does the application still work with assistive technologies?&lt;/p&gt;

&lt;p&gt;Does the interface remain usable when the screen is zoomed?&lt;/p&gt;

&lt;p&gt;These aren't just "frontend polish" issues.&lt;/p&gt;

&lt;p&gt;They affect whether a product remains usable as its audience grows and as devices, browsers, and user needs change.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Environmental Sustainability: Software Still Runs on Physical Machines
&lt;/h2&gt;

&lt;p&gt;This is where the conversation gets interesting.&lt;/p&gt;

&lt;p&gt;Software feels abstract.&lt;/p&gt;

&lt;p&gt;It's code.&lt;/p&gt;

&lt;p&gt;It's APIs.&lt;/p&gt;

&lt;p&gt;It's containers.&lt;/p&gt;

&lt;p&gt;It's a Git repository.&lt;/p&gt;

&lt;p&gt;But eventually, every instruction ends up running on physical hardware.&lt;/p&gt;

&lt;p&gt;CPU cycles consume electricity.&lt;/p&gt;

&lt;p&gt;Memory consumes electricity.&lt;/p&gt;

&lt;p&gt;Storage consumes electricity.&lt;/p&gt;

&lt;p&gt;Networking consumes electricity.&lt;/p&gt;

&lt;p&gt;Data centers also require cooling and other infrastructure.&lt;/p&gt;

&lt;p&gt;The Green Software Foundation's current work frames software sustainability around things such as &lt;strong&gt;energy efficiency, carbon efficiency, carbon awareness, hardware efficiency, and measurement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So a useful question isn't simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Is my application green?"&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How much physical work does my software require to deliver one unit of useful work?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That changes how you look at optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  A database query is not just a database query
&lt;/h3&gt;

&lt;p&gt;Suppose an endpoint loads 50,000 database rows, processes them in application memory, and finally returns 20 records.&lt;/p&gt;

&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;But why did the application process 50,000 records to return 20?&lt;/p&gt;

&lt;p&gt;Maybe the filtering belongs in the database.&lt;/p&gt;

&lt;p&gt;Maybe pagination is missing.&lt;/p&gt;

&lt;p&gt;Maybe the query needs an index.&lt;/p&gt;

&lt;p&gt;Maybe the API is returning fields the client doesn't need.&lt;/p&gt;

&lt;p&gt;The improvement isn't about making the code look prettier.&lt;/p&gt;

&lt;p&gt;You're reducing actual work.&lt;/p&gt;

&lt;p&gt;And that can mean less CPU time, less memory pressure, less I/O, less network traffic, and potentially less infrastructure.&lt;/p&gt;

&lt;p&gt;That's a much more interesting definition of optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching is useful, but it isn't magic
&lt;/h3&gt;

&lt;p&gt;Caching can be another example.&lt;/p&gt;

&lt;p&gt;If thousands of users request the same expensive piece of data, repeatedly calculating it or hitting the database for every request doesn't make much sense.&lt;/p&gt;

&lt;p&gt;A cache such as Redis can reduce repeated computation and database traffic.&lt;/p&gt;

&lt;p&gt;But caching everything isn't sustainable either.&lt;/p&gt;

&lt;p&gt;Now you have another system to operate.&lt;/p&gt;

&lt;p&gt;You have memory consumption.&lt;/p&gt;

&lt;p&gt;You have cache invalidation.&lt;/p&gt;

&lt;p&gt;You have synchronization problems.&lt;/p&gt;

&lt;p&gt;You have stale-data decisions.&lt;/p&gt;

&lt;p&gt;So the question shouldn't be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can we add Redis?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Where is repeated work happening, and is caching actually the cheapest way to remove it?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a very different architectural conversation.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Measure the Work Instead of Guessing
&lt;/h2&gt;

&lt;p&gt;One of the most useful developments in green software engineering is the move toward measurement.&lt;/p&gt;

&lt;p&gt;The Green Software Foundation's Software Carbon Intensity specification, for example, provides a methodology for measuring the carbon impact of software and explicitly considers energy, hardware, and carbon intensity rather than treating "green" as a vague label.&lt;/p&gt;

&lt;p&gt;You don't necessarily need to calculate a carbon score for every API endpoint tomorrow.&lt;/p&gt;

&lt;p&gt;But the underlying idea is valuable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;measure something.&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;CPU time per request&lt;/li&gt;
&lt;li&gt;memory usage&lt;/li&gt;
&lt;li&gt;database queries per request&lt;/li&gt;
&lt;li&gt;data transferred per request&lt;/li&gt;
&lt;li&gt;storage growth&lt;/li&gt;
&lt;li&gt;average response size&lt;/li&gt;
&lt;li&gt;infrastructure utilization&lt;/li&gt;
&lt;li&gt;energy consumption where measurable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even a simple metric can expose waste.&lt;/p&gt;

&lt;p&gt;If version B performs the same business operation with half the database queries and 30% less compute, you've got evidence.&lt;/p&gt;

&lt;p&gt;Now optimization becomes engineering instead of storytelling.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Carbon Awareness Changes the Question
&lt;/h2&gt;

&lt;p&gt;There's an interesting concept in green software that goes beyond simply making software more efficient: &lt;strong&gt;carbon awareness&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not all electricity has the same carbon intensity at every location or every moment.&lt;/p&gt;

&lt;p&gt;The Green Software Foundation describes carbon-aware computing as doing more when electricity is cleaner and less when it is more carbon-intensive.&lt;/p&gt;

&lt;p&gt;That creates some interesting possibilities.&lt;/p&gt;

&lt;p&gt;Imagine you have a batch workload that doesn't need to finish immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generating large reports&lt;/li&gt;
&lt;li&gt;processing analytics&lt;/li&gt;
&lt;li&gt;training certain models&lt;/li&gt;
&lt;li&gt;rebuilding indexes&lt;/li&gt;
&lt;li&gt;running backups&lt;/li&gt;
&lt;li&gt;processing large datasets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the workload can safely move to a different time or region, the system could potentially run it when the electricity supply has a lower carbon intensity.&lt;/p&gt;

&lt;p&gt;The Carbon Aware SDK is one example of tooling built around this idea, including choosing when or where workloads run based on emissions data.&lt;/p&gt;

&lt;p&gt;Obviously, you don't apply this to everything.&lt;/p&gt;

&lt;p&gt;A payment API can't simply tell a customer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Sorry, the grid looks dirty right now. Try again tomorrow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Real-time systems have real-time requirements.&lt;/p&gt;

&lt;p&gt;That's where architectural trade-offs matter.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Sustainability Is Mostly About Trade-offs
&lt;/h2&gt;

&lt;p&gt;This is probably the part I find most interesting.&lt;/p&gt;

&lt;p&gt;There isn't a universal architecture that is simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cheapest&lt;/li&gt;
&lt;li&gt;fastest&lt;/li&gt;
&lt;li&gt;simplest&lt;/li&gt;
&lt;li&gt;most reliable&lt;/li&gt;
&lt;li&gt;most secure&lt;/li&gt;
&lt;li&gt;easiest to maintain&lt;/li&gt;
&lt;li&gt;and lowest-carbon&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You usually have to choose.&lt;/p&gt;

&lt;p&gt;AWS itself describes Well-Architected as a framework built around six pillars: operational excellence, security, reliability, performance efficiency, cost optimization, and sustainability. The framework explicitly recognizes that architectural decisions involve trade-offs based on business context.&lt;/p&gt;

&lt;p&gt;For example, keeping extra infrastructure running may cost more, but it can make a mission-critical system more resilient.&lt;/p&gt;

&lt;p&gt;Compressing everything may reduce bandwidth, but CPU usage can increase.&lt;/p&gt;

&lt;p&gt;Aggressive caching may reduce database work, but increase memory usage.&lt;/p&gt;

&lt;p&gt;Moving a workload to a cleaner region may reduce carbon impact, but introduce latency or compliance issues.&lt;/p&gt;

&lt;p&gt;So "sustainable" doesn't mean blindly minimizing one number.&lt;/p&gt;

&lt;p&gt;It means understanding the system well enough to make deliberate trade-offs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;For me, sustainable software development comes down to a pretty simple idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't make the system work harder than it needs to.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That applies to infrastructure.&lt;/p&gt;

&lt;p&gt;It applies to databases.&lt;/p&gt;

&lt;p&gt;It applies to network traffic.&lt;/p&gt;

&lt;p&gt;It applies to storage.&lt;/p&gt;

&lt;p&gt;It applies to architecture.&lt;/p&gt;

&lt;p&gt;And it even applies to the people maintaining the code.&lt;/p&gt;

&lt;p&gt;If a system needs more servers every time the user count increases, ask why.&lt;/p&gt;

&lt;p&gt;If the database is doing millions of unnecessary operations, investigate.&lt;/p&gt;

&lt;p&gt;If nobody understands a critical service except one person, fix the knowledge bottleneck.&lt;/p&gt;

&lt;p&gt;If you're storing data nobody will ever use again, question it.&lt;/p&gt;

&lt;p&gt;If a workload can move to a cleaner time or location without affecting the business, maybe let it.&lt;/p&gt;

&lt;p&gt;Sustainability isn't a green badge you attach to an architecture diagram.&lt;/p&gt;

&lt;p&gt;It's what happens when engineers start treating &lt;strong&gt;resources, people, infrastructure, and long-term maintainability as part of the same system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And honestly, that's probably a better definition of good engineering anyway.&lt;/p&gt;




&lt;h3&gt;
  
  
  Update Note
&lt;/h3&gt;

&lt;p&gt;Originally published in 2023. Updated in October 2026 with deeper coverage of system architecture, cloud efficiency, green software engineering, accessibility, carbon awareness, measurement, and real-world engineering trade-offs.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>development</category>
    </item>
  </channel>
</rss>
