<?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>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>
