<?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: Michiharu Ono</title>
    <description>The latest articles on DEV Community by Michiharu Ono (@michymono77).</description>
    <link>https://dev.to/michymono77</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1206234%2F5c7b2e6b-ab6b-443a-bf62-d2eef7fe67b3.jpg</url>
      <title>DEV Community: Michiharu Ono</title>
      <link>https://dev.to/michymono77</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michymono77"/>
    <language>en</language>
    <item>
      <title>The Case for Leaky Locks: Redis TTL as Failure Cooldown for Expensive AI Jobs</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Fri, 13 Mar 2026 16:25:39 +0000</pubDate>
      <link>https://dev.to/michymono77/the-case-for-leaky-locks-redis-ttl-as-failure-cooldown-for-expensive-ai-jobs-14el</link>
      <guid>https://dev.to/michymono77/the-case-for-leaky-locks-redis-ttl-as-failure-cooldown-for-expensive-ai-jobs-14el</guid>
      <description>&lt;p&gt;I'm probably not the only one who's been told "always release your locks in a finally block." It's one of those conventions we follow without much thought, and for most situations it's completely right. But I recently ran into a case where doing the opposite was actually the better call.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem I Didn't See Coming: How Releasing Locks Cost Me Money
&lt;/h2&gt;

&lt;p&gt;My job queue was simple: user submits a document → AI evaluates it → result gets stored.&lt;/p&gt;

&lt;p&gt;The issue was that AI calls can fail. Rarely, but they do. Out of nowhere, the model might ignore the expected output format or a rate limit kicks in. So I'd catch the exception, log it, mark the job as failed, and very responsibly release the lock in the finally block.&lt;/p&gt;

&lt;p&gt;Then the user would hit retry.&lt;/p&gt;

&lt;p&gt;And the AI would fail again.&lt;/p&gt;

&lt;p&gt;And they'd hit retry again.&lt;/p&gt;

&lt;p&gt;Each retry triggered another LLM call, and each one cost real money.&lt;/p&gt;

&lt;p&gt;What I had was essentially a retry storm hitting my API at exactly the moment my system was already struggling.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using Lock Expiration as a Cooldown Mechanism
&lt;/h1&gt;

&lt;p&gt;Here's what I ended up doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;lock_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ai_job_lock:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Try to acquire lock with TTL
&lt;/span&gt;&lt;span class="n"&gt;acquired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# If the lock exists, the job is either running or cooling down
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;acquired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;

&lt;span class="k"&gt;try&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_llm_api&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="nf"&gt;save_result&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="c1"&gt;# Release lock only on success
&lt;/span&gt;    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Do NOT release the lock
&lt;/span&gt;    &lt;span class="c1"&gt;# The TTL becomes the cooldown window
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lock just stays there. For five minutes. Then Redis evicts it automatically.&lt;/p&gt;

&lt;p&gt;I know what you're thinking: "That's a memory leak!" When you see a lock that doesn't get released, the mental model most developers reach for is "something that accumulates indefinitely." &lt;/p&gt;

&lt;p&gt;But here, it's a TTL with a 5-minute expiration. So, the worst case in this scenario is one orphaned key per job, and it self-destructs in 5 minutes. Not a leak.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Without TTL:  fail → retry → retry → retry → 20 calls in 60s
With TTL:     fail → blocked → blocked → retry at t=5min
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;p&gt;Let us think about the common reasons AI calls fail. Rate limiting means you'll fail again immediately if you retry. Network issues are often temporary but not instant to resolve. Prompt problems won't fix themselves no matter how many times you retry.&lt;/p&gt;

&lt;p&gt;When your system is already struggling, piling on more load is the last thing you want, especially when each LLM call is very expensive. The TTL acts as a forced cooldown. Five minutes sounds like a lot, but in practice it can be right for some use cases: long enough to recover from rate limits, short enough that for most async workflows users barely notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part I Actually Like
&lt;/h2&gt;

&lt;p&gt;It felt wrong at first but there's no retry logic. No exponential backoff. No complex state machine. Just time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This is literally all the code
&lt;/span&gt;&lt;span class="n"&gt;acquired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# On failure, just let it ride
# Lock expires naturally
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the system still recovers cleanly. If a worker crashes mid-job, the lock expires and the recovery service picks it up. The TTL handles both failure cooldown and crash recovery with zero extra code. (When a retry request arrives during the cooldown window, the worker simply fails to acquire the lock and exits early without calling the LLM.)&lt;/p&gt;

&lt;h2&gt;
  
  
  When This Doesn't Apply
&lt;/h2&gt;

&lt;p&gt;Of course, this pattern isn't universal. &lt;/p&gt;

&lt;p&gt;It's a bad fit for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cheap operations where retries are basically free. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jobs that legitimately need immediate retry. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Situations where users expect a synchronous, instant response.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for expensive and slow AI calls where failure usually means "try again later, not right now", this approach can quietly save you from expensive retry storms you didn't know you were building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Risks worth mentioning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lock duration mismatch&lt;/strong&gt; &lt;br&gt;
If a job runs longer than the TTL, the lock expires while the job is still running and another worker picks it up. Make sure TTL &amp;gt; worst-case runtime, or implement a heartbeat that refreshes the lock while the job is active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deterministic failures&lt;/strong&gt; &lt;br&gt;
The cooldown works well for transient failures like rate limits or network hiccups. It doesn't really help when the failure is caused by bad input or a broken prompt because they will just fail every 5 minutes until someone notices. I believe that it is worth classifying your failures and marking deterministic ones as permanently failed rather than letting them loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User-facing feedback&lt;/strong&gt; &lt;br&gt;
If a user hits retry and nothing happens, it feels broken. Surface the state somewhere: a "retry available in X minutes" message, a job status indicator, anything that tells them the system is aware and waiting rather than silently stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;We spend a lot of time building complex retry mechanisms, circuit breakers, and fallback systems. Sometimes the right answer is just: let it fail, wait a bit, try again later.&lt;/p&gt;

&lt;p&gt;The Redis TTL is the "wait a bit" part, except it's automatic, requires no extra code, and can't be accidentally bypassed.&lt;/p&gt;

&lt;p&gt;Curious if anyone else has hit this or found a better way to handle it.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>python</category>
      <category>fastapi</category>
      <category>redis</category>
    </item>
    <item>
      <title>How (and How NOT) to Name Rails Models Beyond the Obvious</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Sun, 09 Feb 2025 23:20:15 +0000</pubDate>
      <link>https://dev.to/michymono77/how-and-how-not-to-name-rails-models-beyond-the-obvious-2jbo</link>
      <guid>https://dev.to/michymono77/how-and-how-not-to-name-rails-models-beyond-the-obvious-2jbo</guid>
      <description>&lt;p&gt;Have you ever encountered a model in a Rails app that initially seemed to have an understandable name, but as you became more familiar with the code, you realized the name led you to believe one thing when it actually meant something entirely different? 🙋‍♂️&lt;/p&gt;

&lt;p&gt;This kind of naming can introduce unnecessary complexity, forcing you to "decipher" what the code actually does.&lt;/p&gt;

&lt;p&gt;And that's never fun ☕️&lt;/p&gt;

&lt;p&gt;Naming things in Rails is key to keeping your code easy to understand and maintain in the long run. While conventions like using snake_case for variables and methods is a no-brainer, I believe that picking the right names for models and methods requires a bit more thought.&lt;/p&gt;

&lt;p&gt;In this blog, I’ll attempt to unpack meaningful names for your Rails models and methods, helping you avoid confusion and keeping things clear.&lt;/p&gt;

&lt;p&gt;(&lt;strong&gt;Please understand that this blog isn’t a set of hard-and-fast rules&lt;/strong&gt; 🙆‍♂️ It’s based on what I’ve found works well for me and my team in the past, especially when developers come and go and need to collaborate with multiple people. The goal is to make things clearer for everyone in the long run.)&lt;/p&gt;




&lt;h2&gt;
  
  
  How To Name Model
&lt;/h2&gt;

&lt;p&gt;When naming Rails models, it’s crucial that the name communicates the model’s role, responsibility, and the real-world entity it represents. &lt;/p&gt;

&lt;p&gt;In this section, I’ll cover key considerations to help ensure your model names are clear and avoid common pitfalls like vague or action-oriented names.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Does the Model Capture the Entity?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A well-named model should represent a real-world entity that the application works with. Typically, the name is a &lt;strong&gt;noun&lt;/strong&gt; that reflects the object or concept it encapsulates, rather than an &lt;strong&gt;action&lt;/strong&gt; or &lt;strong&gt;process&lt;/strong&gt;. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User&lt;/strong&gt; represents a user entity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product&lt;/strong&gt; represents a product entity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order&lt;/strong&gt; represents an order entity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, naming a model after a process or a task (e.g., &lt;code&gt;ProductProcessor&lt;/code&gt;) can mislead developers into thinking the model performs an action, rather than serving as a core data entity. Such models are often better suited to be &lt;strong&gt;service classes&lt;/strong&gt; or similar, but that’s a topic I’ll cover in a future post on service classes 😌&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Does It Capture the Responsibility?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In addition to representing an entity, a model's name should also reflect its &lt;strong&gt;core responsibility&lt;/strong&gt; within the application. It’s not just about holding data but about &lt;strong&gt;encapsulating the relevant business logic&lt;/strong&gt; for that data.&lt;/p&gt;

&lt;p&gt;The model's name should align with its &lt;strong&gt;primary responsibility&lt;/strong&gt; in order to avoid becoming a dumping ground for unrelated logic. (This happens more often than not! )&lt;/p&gt;

&lt;p&gt;For example, a &lt;code&gt;User&lt;/code&gt; model should primarily represent the &lt;strong&gt;user entity&lt;/strong&gt; and manage core attributes like &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;date_of_birth&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If any additional responsibilities are needed, it is often better to delegate them to &lt;strong&gt;service classes&lt;/strong&gt; (e.g., &lt;code&gt;AuthenticationService&lt;/code&gt;) or similar. This allows the &lt;code&gt;User&lt;/code&gt; model to remain focused on storing and managing user data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bad Model Example:
&lt;/h3&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;UserAuthenticator&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;class&lt;/span&gt; &lt;span class="nc"&gt;UserLogin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Are They Undesirable? 🤔
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;UserAuthenticator&lt;/code&gt;&lt;/strong&gt;: &lt;br&gt;
The name suggests it handles authentication logic rather than representing a data entity. This blurs the line between business logic and data representation, making the model harder to understand and maintain. Before you know it, you’re thinking, “So, this model seems to handle authentication logic... but also stores this data... and now it's doing even more business logic too 🤯” Instead, it’s better to use a dedicated service class or similar, keeping the User related model focused on managing user related data while making authentication logic easier to maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;UserLogin&lt;/code&gt;&lt;/strong&gt;: &lt;/p&gt;

&lt;p&gt;While the name may seem intuitive at first, it can invite responsibilities that are outside the model's intended scope. The name suggests a process (logging in), not a data entity, and this can lead to confusion. Models should represent &lt;strong&gt;data entities&lt;/strong&gt;, not actions or processes. As a result, this name may cause developers to think &lt;code&gt;UserLogin&lt;/code&gt; is a service object or something responsible for the login process, when it's actually a model meant to represent user data.&lt;/p&gt;

&lt;p&gt;If you start adding behaviors like session management &lt;strong&gt;because they seem to align with the name&lt;/strong&gt; ⚠️, you risk turning the model into a "god object" that tries to handle too many responsibilities. As the model takes on both data and behavior, it becomes harder to maintain and extend. Over time, this can lead to a tangled codebase where the responsibilities of the model are unclear, making it difficult to manage and debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Avoid Misleading/Imprecise Names&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Choosing precise names for models is critical to maintaining clarity in your codebase. &lt;/p&gt;

&lt;p&gt;Misleading or imprecise names can lead to confusion and introduce subtle bugs, as they fail to communicate the model’s true role or behavior. Clear naming helps developers quickly understand the purpose of a model, what data it holds, and how it interacts with other parts of the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example 1: Dialog vs Modal&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Consider &lt;strong&gt;UI components&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dialog&lt;/strong&gt;: A non-blocking window that allows users to interact with other elements in the app while it's open. Common use cases include information displays, prompts, or optional actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modal&lt;/strong&gt;: A blocking window that prevents the user from interacting with the rest of the application until the action within the modal is addressed. It's commonly used for critical tasks like confirming a deletion or completing an essential form.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though they are similar, the distinction between these two components can be important. Mislabeling a &lt;strong&gt;Modal&lt;/strong&gt; as a &lt;strong&gt;Dialog&lt;/strong&gt; can confuse developers and lead to incorrect assumptions about its behavior, affecting both the code and user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example 2: Consent vs Agreement&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In legal contexts, &lt;strong&gt;Consent&lt;/strong&gt; and &lt;strong&gt;Agreement&lt;/strong&gt; have distinct meanings, but they’re often used interchangeably, which can lead to ambiguity when naming models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consent&lt;/strong&gt;: The &lt;strong&gt;act of granting permission&lt;/strong&gt; or approval for something to happen. In legal contexts, it’s typically one-sided and can be revoked at any time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agreement&lt;/strong&gt;: A &lt;strong&gt;mutual understanding or contract&lt;/strong&gt; between two or more parties, usually involving commitments or promises, and often legally binding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When naming models related to permissions or legal terms, it’s crucial to use these terms correctly. Confusing &lt;strong&gt;Consent&lt;/strong&gt; with &lt;strong&gt;Agreement&lt;/strong&gt; could mislead developers and result in improper handling of user data or permissions, which might have legal consequences. Proper naming ensures that models represent the right concepts and helps maintain clarity in both the code and the business logic.&lt;/p&gt;

&lt;p&gt;Using misleading or imprecise names for models can cause confusion, making it difficult for developers to understand their role and function. Clear, accurate names are essential for maintaining the integrity of your codebase and ensuring proper functionality. Precise naming ensures that the right concepts are represented and makes it easier to manage the application as it grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Avoid Using Verbs in Model Names&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When naming models, you should often avoid using &lt;strong&gt;verbs&lt;/strong&gt;(or names that ends with -ing or -er) that imply actions. &lt;/p&gt;

&lt;p&gt;Using verbs in model names can create unnecessary confusion, as it might suggest that the model is responsible for performing an action rather than simply representing data.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Avoid:&lt;/strong&gt;
&lt;/h3&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;FeeAdder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FeeAdder&lt;/code&gt; implies that the model is responsible for performing an action—specifically, adding a fee. However, models should represent &lt;strong&gt;data entities&lt;/strong&gt;, not actions. The model name doesn’t clearly convey what the class represents, but rather what it is supposed to do. This could confuse developers about the class's purpose and make it unclear where the actual logic for adding a fee should reside.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Better:&lt;/strong&gt;
&lt;/h3&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;Fee&lt;/span&gt;
  &lt;span class="c1"&gt;# Represents a fee entity, focusing on attributes like amount, type, and associated details,&lt;/span&gt;
  &lt;span class="c1"&gt;# without being tied to the action of adding or creating the fee.&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 model now represents the entity itself, which could include attributes such as amount, fee type, payment schedule, or any other characteristics relevant to the fee. The action of adding or creating a fee is better handled by a separate component, such as a service object or controller, rather than in the model itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. Avoid Using Vague or Overly General Names&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Names like &lt;code&gt;Info&lt;/code&gt;, &lt;code&gt;Data&lt;/code&gt;, or &lt;code&gt;Details&lt;/code&gt; might seem fine at first glance, but they are too vague to effectively describe the specific role of the model. These names don’t provide enough context or clarity, making it difficult for developers to understand what the model actually represents or how it fits into the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Avoid:&lt;/strong&gt;
&lt;/h3&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;FeeInfo&lt;/span&gt;
  &lt;span class="c1"&gt;# "Info" is too vague and doesn’t specify what kind of information this model holds.&lt;/span&gt;
  &lt;span class="c1"&gt;# It could refer to any kind of fee-related data, such as amounts, types, discounts, etc.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using generic terms like "Info" can make things unclear and lead to confusion. For example, a name like &lt;code&gt;FeeInfo&lt;/code&gt; could mean a lot of things—whether it's about a fee's description, its discount structure, payment terms, or just a list of amounts. This vagueness makes it harder for developers to quickly understand what the model is really for, and they might make incorrect assumptions about its purpose. &lt;/p&gt;

&lt;p&gt;This confusion can also cause headaches when it's time to extend or maintain the model. For example, if you're adding logic for fee discounts based on customer types, it's not clear if &lt;code&gt;FeeInfo&lt;/code&gt; is the right model for that. This uncertainty can slow down both adding new features and debugging existing ones. 🛠️&lt;/p&gt;

&lt;p&gt;Plus, names like "Info" end up making the model names longer without really helping anyone understand what it does. This bloats the codebase and makes it harder to navigate. If you have models named &lt;code&gt;UserInfo&lt;/code&gt;, &lt;code&gt;OrderInfo&lt;/code&gt;, and &lt;code&gt;ProductInfo&lt;/code&gt;, for example, developers might waste time figuring out what each one actually does.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Better:&lt;/strong&gt;
&lt;/h3&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;FeeStructure&lt;/span&gt;
  &lt;span class="c1"&gt;# Indicates that the model represents the fee structure,&lt;/span&gt;
  &lt;span class="c1"&gt;# including different fee types, rules, and pricing logic.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FeeStructure&lt;/code&gt; explicitly describes the fee-related logic, avoiding confusion. This is much more specific and directly convey the purpose of the model.  &lt;/p&gt;




&lt;h2&gt;
  
  
  How to Name Model Methods
&lt;/h2&gt;

&lt;p&gt;When naming model methods, it’s essential to choose names that clearly communicate the &lt;strong&gt;behavior&lt;/strong&gt;, &lt;strong&gt;purpose&lt;/strong&gt;, and &lt;strong&gt;intent&lt;/strong&gt; of the method. A well-named method provides instant clarity on &lt;strong&gt;what it does&lt;/strong&gt;, &lt;strong&gt;why it exists&lt;/strong&gt;, and &lt;strong&gt;how it should be used&lt;/strong&gt;, without requiring anyone to read through its implementation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Behavior&lt;/strong&gt;: Focuses on &lt;strong&gt;what the method does&lt;/strong&gt; in terms of business logic. Does it calculate something? Retrieve data? Or modify state?

&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;order.total_price&lt;/code&gt; → Calculates the total price, accounting for discounts and taxes.&lt;/li&gt;
&lt;li&gt;❌ &lt;code&gt;order.price&lt;/code&gt; → Unclear whether this is the base price, discounted price, or something else.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Purpose&lt;/strong&gt;: Explains &lt;strong&gt;why the method exists&lt;/strong&gt;—what problem it solves or what role it plays in the system.

&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;subscription.active_users&lt;/code&gt; → Returns only the users with an active subscription.&lt;/li&gt;
&lt;li&gt;❌ &lt;code&gt;subscription.users&lt;/code&gt; → Too vague—does it return all users, just active ones, or even canceled ones?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Intent&lt;/strong&gt;: Reflects &lt;strong&gt;how the method should be used&lt;/strong&gt;—whether it's a query (retrieving data) or a command (modifying state).

&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;user.deactivate!&lt;/code&gt; → Clearly modifies the user’s state, indicating a destructive action.&lt;/li&gt;
&lt;li&gt;❌ &lt;code&gt;user.set_inactive&lt;/code&gt; → The intent is unclear—does it save the change or just set an instance variable?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In my experience, if you struggle to name a method, it’s often a sign that the model name itself might be unclear.&lt;/p&gt;

&lt;p&gt;A well-named method should feel &lt;strong&gt;natural and intuitive&lt;/strong&gt; when read aloud in the context of the model. If you find yourself hesitating while naming a method, it could mean that the model’s name doesn’t accurately reflect the entity it represents.&lt;/p&gt;

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

&lt;p&gt;✅ &lt;code&gt;User.admin?&lt;/code&gt; → &lt;strong&gt;Makes sense&lt;/strong&gt; because "User" is a well-defined entity, and asking if a user is an admin is a natural question to ask.&lt;/p&gt;

&lt;p&gt;❌ &lt;code&gt;UserAdding.admin?&lt;/code&gt; → &lt;strong&gt;Feels unnatural&lt;/strong&gt; because &lt;code&gt;UserAdding&lt;/code&gt; suggests an action, not an entity. What does it mean for an "adding process" to be an admin? This awkwardness signals that the model itself might need renaming.&lt;/p&gt;

&lt;p&gt;I tend to find that this issue often appears when models are named after &lt;strong&gt;processes&lt;/strong&gt; or actions (&lt;code&gt;UserCreation&lt;/code&gt;, &lt;code&gt;FeeProcessing&lt;/code&gt;, &lt;code&gt;SubscriptionHandling&lt;/code&gt;) instead of the actual domain entity (&lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Fee&lt;/code&gt;, &lt;code&gt;Subscription&lt;/code&gt;). When the model is not well-named, every method added to it will feel slightly off, leading to unclear and awkward method names.&lt;/p&gt;

&lt;p&gt;A good rule of thumb: &lt;strong&gt;If you struggle to name a method concisely, reconsider the model’s name first.&lt;/strong&gt; A well-named model leads to method names that feel obvious and easy to understand.&lt;/p&gt;

&lt;p&gt;With this in mind, let’s dive deeper into specific strategies for naming model methods clearly and concisely. The following tables will walk you through different types of methods and provide examples of good and bad naming practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Naming Methods for Getting Data&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Methods that retrieve data should clearly express &lt;strong&gt;what&lt;/strong&gt; they return. They should be &lt;strong&gt;descriptive, but not redundant&lt;/strong&gt;, avoiding unnecessary prefixes like &lt;code&gt;get_&lt;/code&gt;. The method name should make it clear whether it returns a &lt;strong&gt;single item or a collection&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Good Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Bad Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;order.total_price&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;order.price&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"price"&lt;/code&gt; is too vague—does it mean the &lt;strong&gt;base price, discounted price, or final total&lt;/strong&gt;? &lt;code&gt;"total_price"&lt;/code&gt; is explicit.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;company.employees&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;company.get_employees&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"get_"&lt;/code&gt; is unnecessary—method calls should be &lt;strong&gt;declarative, not imperative&lt;/strong&gt;. &lt;code&gt;"employees"&lt;/code&gt; is enough.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;2. Naming Methods for Updating Data&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Methods that &lt;strong&gt;modify data&lt;/strong&gt; should describe &lt;strong&gt;what they change&lt;/strong&gt;, not how they do it. Avoid vague or generic names like &lt;code&gt;modify&lt;/code&gt;, which &lt;strong&gt;don’t specify what is being changed&lt;/strong&gt;. If a method modifies data, consider adding &lt;code&gt;!&lt;/code&gt; to signal a &lt;strong&gt;state change&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Good Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Bad Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user.update_email_address!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user.modify_email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"modify"&lt;/code&gt; is ambiguous—it doesn’t specify whether it &lt;strong&gt;updates, deletes, or formats&lt;/strong&gt; the email.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;subscription.cancel!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;subscription.change_status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"change_status"&lt;/code&gt; is vague—it could mean activating, pausing, or canceling. &lt;code&gt;"cancel!"&lt;/code&gt; is explicit.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;3. Naming Methods That Return Boolean Values&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Boolean methods should &lt;strong&gt;ask a clear question&lt;/strong&gt; and return &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. Use &lt;code&gt;?&lt;/code&gt; at the end of the method to indicate a &lt;strong&gt;predicate&lt;/strong&gt;. Avoid method names that suggest they return a &lt;strong&gt;status, object, or string&lt;/strong&gt; rather than a boolean value.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Good Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Bad Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user.admin?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user.is_admin?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"is_"&lt;/code&gt; is redundant—&lt;strong&gt;Ruby convention prefers &lt;code&gt;?&lt;/code&gt; methods for predicates&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;order.paid?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;order.payment_status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"payment_status"&lt;/code&gt; sounds like it returns a &lt;strong&gt;string or object&lt;/strong&gt;, not just &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;product.in_stock?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;product.has_stock?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"has_stock?"&lt;/code&gt; is unnecessary—&lt;strong&gt;"in_stock?" is more natural&lt;/strong&gt; and aligns with human language.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;4. Avoiding Implementation Details in Method Names&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Method names should focus on &lt;strong&gt;what they represent&lt;/strong&gt;, not &lt;strong&gt;how they work&lt;/strong&gt;. Avoid tying methods to specific implementations, like database queries or API calls, since these details &lt;strong&gt;can change over time&lt;/strong&gt;. Instead, name methods based on their &lt;strong&gt;domain meaning&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Good Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Bad Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fetch_user_from_db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"fetch_user_from_db"&lt;/code&gt; ties the method to &lt;strong&gt;one specific implementation&lt;/strong&gt;. What if users are cached?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;notifications&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;retrieve_notifications_from_redis&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"retrieve_notifications_from_redis"&lt;/code&gt; locks the method to &lt;strong&gt;a specific storage&lt;/strong&gt;. &lt;code&gt;"notifications"&lt;/code&gt; allows flexibility.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;5. Clearly Differentiating Between Queries and Commands&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Queries return data and should never modify state, while commands modify state and should include "!" if they have potentially dangerous side effects. Mixing these two can cause unexpected behavior—or worse, accidentally change data when you only meant to retrieve it.&lt;/p&gt;

&lt;p&gt;Imagine asking a waiter:&lt;br&gt;
🧑‍💼 "Do you have a blueberry muffin?"&lt;br&gt;
👨‍🍳 "Yes, and I just placed an order for you! You will be charged $12!"&lt;/p&gt;

&lt;p&gt;Wait, you just wanted to check, right? Not commit to an order! This is what happens when a method that looks like a query (order.cancel) actually modifies state—it surprises developers and leads to unintended changes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Good Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Bad Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;order.cancel!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;order.cancel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Commands that modify state should end in &lt;code&gt;!&lt;/code&gt;&lt;/strong&gt; to signal they change data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;subscription.renew!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;subscription.renew_subscription&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"renew_subscription"&lt;/code&gt; is unnecessarily long—&lt;strong&gt;"renew!" is already clear within the context of a subscription&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user.active?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user.is_active&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Queries should &lt;strong&gt;end with &lt;code&gt;?&lt;/code&gt;&lt;/strong&gt; to indicate they return a boolean.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cart.total_price&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cart.calculate_total_price&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"calculate_total_price"&lt;/code&gt; suggests an &lt;strong&gt;action&lt;/strong&gt; rather than a simple lookup—&lt;strong&gt;queries should describe what they return, not how&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;product.discontinued?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;product.check_if_discontinued&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"check_if_discontinued"&lt;/code&gt; sounds like an action—&lt;strong&gt;a simple &lt;code&gt;?&lt;/code&gt; method is clearer&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;6. Handling Plurality Correctly&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Use &lt;strong&gt;plural names for collections&lt;/strong&gt; and &lt;strong&gt;singular names for single objects&lt;/strong&gt;. Avoid redundant terms like &lt;code&gt;_list&lt;/code&gt;, &lt;code&gt;_records&lt;/code&gt;, or &lt;code&gt;_collection&lt;/code&gt;, as it’s already understood that a method returning multiple objects is an array or collection.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Good Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Bad Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;company.employees&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;company.employee_list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"employee_list"&lt;/code&gt; adds unnecessary verbosity—&lt;strong&gt;"employees" is expected to be a collection&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user.friends&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user.friend_records&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"friend_records"&lt;/code&gt; makes it sound like a &lt;strong&gt;raw database query instead of a meaningful method&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Wrapping Up:
&lt;/h3&gt;

&lt;p&gt;Naming is a small but powerful aspect of software development in rails that greatly impacts code clarity and maintainability. The process of naming might take time to get used to, but it’s a goodd investment in cleaner, more maintainable code 😎. With thoughtful naming practices, your Rails applications will not only function well but also be easier to scale and collaborate on 😌&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>beginners</category>
      <category>oop</category>
    </item>
    <item>
      <title>When Controllers Take on Too Much Responsibility</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Sun, 12 Jan 2025 05:01:30 +0000</pubDate>
      <link>https://dev.to/michymono77/when-controllers-take-on-too-much-responsibility-1mfi</link>
      <guid>https://dev.to/michymono77/when-controllers-take-on-too-much-responsibility-1mfi</guid>
      <description>&lt;p&gt;In software development, following object-oriented programming (OOP) principles is key to building systems that are easy to maintain(If you were interested, I've covered this topic in this &lt;a href="https://dev.to/michymono77/getting-started-with-object-oriented-design-part-1-shifting-from-process-oriented-to-jd3"&gt;post&lt;/a&gt; so please have a look 👀)&lt;/p&gt;

&lt;p&gt;But let’s face it—while we all recognize the importance of OOP, actually implementing these principles can be tricky, especially early in our careers or when we’re racing to meet deadlines.&lt;/p&gt;

&lt;p&gt;One common pitfall in backend development is when &lt;strong&gt;controllers end up taking on too much responsibility&lt;/strong&gt;. I believe this happens a lot especially when backend design is heavily shaped by frontend requirements, leading to bloated controllers that mix concerns and violate OOP principles.&lt;/p&gt;

&lt;p&gt;In this post, I’ll attempt to unpack why this tends to happen, explore the risks of overburdened controllers, and share how to design a more maintainable solution that keeps your codebase scalable. Let’s get started! 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Do Controllers Take on Too Much Responsibility?
&lt;/h2&gt;

&lt;p&gt;Before diving into examples, let’s take a moment to reflect on &lt;em&gt;why&lt;/em&gt; controllers often end up doing more than they should 🤔 In my observation, there are a couple of reasons why developers sometimes let controllers carry too much weight:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Pressure to Ship Quickly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deadlines can be relentless, and under that pressure, quick fixes sometimes win over thoughtful design. It’s “easy” to write everything directly into the controller—fetching data, applying business logic, and formatting JSON—because it feels like the fastest way to meet frontend requirements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Temporary Features That Overstay Their Welcome&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, developers ship features intended to be temporary—a quick fix for an event, promotion, or beta test. Because these features are labeled as "short-term," taking the time to structure the code properly often feels unnecessary. After all, why bother refactoring or adding extra layers for something that’s supposed to disappear soon, right?&lt;/p&gt;

&lt;p&gt;But here’s the catch ⚠️: those temporary features have a way of sticking around much longer than expected. Deadlines slip, priorities shift, or stakeholders decide to make the feature permanent. Suddenly, what started as a quick-and-dirty addition becomes a lasting part of the application, leaving behind tightly coupled controller code that’s difficult to maintain, extend, or debug.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;“It works, so what’s the problem?😗”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you haven’t yet experienced the downsides of overburdened controllers—like wrestling with a tangled codebase, overhauling the frontend, chasing down elusive bugs, or enhancing existing features—it’s easy to overlook the importance of separation of concerns. I’ll admit, I’ve been guilty of this myself 🙋‍♂️.&lt;/p&gt;

&lt;p&gt;It’s a bit like boxing: you don’t fully appreciate the value of keeping your guard up until you take a punch to the face.&lt;/p&gt;

&lt;p&gt;Without the perspective of long-term projects, it can be tempting to focus on code that “just works” in the moment, rather than considering how all the pieces will fit together down the line.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What Happens When Controllers Take on Too Much?
&lt;/h2&gt;

&lt;p&gt;When controllers take on too many responsibilities—fetching data, applying business logic, and formatting responses—it can often lead to two major issues. These problems not only make the code harder to work with but can also create a ripple effect of complications down the line:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Tight Coupling Between Backend and Frontend Code&lt;/p&gt;

&lt;p&gt;When controllers handle both backend logic and frontend-specific requirements, it creates tight coupling between the two. For example, if the frontend expects a specific JSON format to display a product’s sale status, any change to that format might require updates to both the backend logic and the frontend code.&lt;/p&gt;

&lt;p&gt;This direct connection means that updates in one area can unexpectedly break or require adjustments in the other, making maintenance more complex. A more flexible approach is to decouple the backend and frontend, allowing each to evolve independently without creating unnecessary dependencies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Shifting from Object Interaction to Step-by-Step Procedural Flow&lt;/p&gt;

&lt;p&gt;In OOP, objects should manage their own behavior. For example, a &lt;code&gt;Product&lt;/code&gt; object would know if it's on sale (&lt;code&gt;on_sale?&lt;/code&gt;). However, when controllers become overly focused on frontend needs, they start manually assembling data in a step-by-step fashion. This results in procedural flow, where the controller handles all the logic instead of letting the objects themselves manage it.&lt;/p&gt;

&lt;p&gt;By shifting away from object interaction, we lose the benefits of encapsulation, reusability, and maintainability.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example: Product Listing
&lt;/h2&gt;

&lt;p&gt;Imagine you’re building a product listing page for your web app. The frontend needs details like product names, prices, and whether each product is on sale. A quick implementation might look like this:&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;ProductController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&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;product&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;is_on_sale: &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;products: &lt;/span&gt;&lt;span class="n"&gt;products&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 implementation works—but it might come with several challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced Reusability&lt;/strong&gt;: &lt;/p&gt;

&lt;p&gt;The sale determination logic is now tied to the context of this specific controller method. If another part of the application (e.g., a report generator or an API endpoint) needs to determine if a product is on sale, developers might copy the logic instead of reusing it, leading to more duplication.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tightly Coupled JSON Formatting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the controller directly defines how JSON is structured to meet frontend requirements, it mixes presentation logic with business logic. The controller’s primary responsibility should be handling the request/response cycle, not deciding how the data should be presented. This makes the controller more complex and tightly coupled to the frontend, so any changes to the frontend’s data format will require updates in the controller, leading to unnecessary dependencies and making the system harder to maintain.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Testing Challenges&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you write tests in controller level? In rails, most of the time, it is better to write business logic else where and controller should just focus on fetching objects. If you code like above, testing the controller now requires ensuring that the sale logic is correct in addition to verifying the controller's primary responsibility (e.g., routing and rendering). This increases the complexity of the test suite and makes the tests more fragile, as they are tied to both business logic and controller logic.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  More "Maintainable" Solution
&lt;/h2&gt;

&lt;p&gt;Instead of cramming everything into the controller, let’s spread the responsibilities across models and presenters. This approach keeps each piece of code focused on its specific role.&lt;/p&gt;

&lt;p&gt;Here’s a refactored version:&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;Product&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_sale?&lt;/span&gt;
    &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;class&lt;/span&gt; &lt;span class="nc"&gt;ProductPresenter&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;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;product&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;as_json&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;price: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;is_on_sale: &lt;/span&gt;&lt;span class="vi"&gt;@product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_sale?&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;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;ProductPresenter&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="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;as_json&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;products: &lt;/span&gt;&lt;span class="n"&gt;products&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;h3&gt;
  
  
  &lt;strong&gt;💡Why This Works&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;By distributing responsibilities across models, presenters, and controllers, we achieve a cleaner and more maintainable solution. Each layer now focuses on its core responsibility, making the codebase more flexible, reusable, and testable. Here's why this approach is more maintainable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Encapsulation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Product&lt;/code&gt; model encapsulates the logic for determining if it’s on sale. Any changes to this logic only need to be made in one place.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Abstraction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ProductPresenter&lt;/code&gt; handles JSON formatting, separating it from both the controller and the model.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reusability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you need to format products differently in another context (e.g., an admin dashboard), you can create a new presenter without touching the core logic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Testability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each layer can be tested independently 😎: models for business rules, presenters for formatting, and controllers for request handling. It is easy to write tests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




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

&lt;p&gt;When controllers take on too much responsibility, they violate key principles of OOP and separation of concerns. This leads to tightly coupled, procedural-style code that’s harder to maintain and extend.&lt;/p&gt;

&lt;p&gt;By focusing on designing robust objects and delegating responsibilities appropriately, you can keep your codebase clean and adaptable to changing requirements. Remember: controllers aren’t meant to carry all the weight of your backend—they’re just one part of a well-designed system 😌 Keep them lean and focused, and your future self and teammates will thank you!&lt;/p&gt;

&lt;p&gt;(BTW, I’m not necessarily against so-called 'dirty coding.' Feel free to check out this &lt;a href="https://dev.to/michymono77/how-focusing-on-business-impact-improved-my-technical-debt-conversations-21pe"&gt;post&lt;/a&gt; I wrote previously on the topic.)&lt;/p&gt;

</description>
      <category>rails</category>
      <category>beginners</category>
      <category>oop</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Big Picture: Practical Steps for Upgrading the Ruby on Rails Version in a Small to Medium-Sized App 🚀</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Sun, 08 Dec 2024 03:27:19 +0000</pubDate>
      <link>https://dev.to/michymono77/big-picture-practical-steps-for-upgrading-the-ruby-on-rails-version-in-a-small-to-medium-sized-app-292d</link>
      <guid>https://dev.to/michymono77/big-picture-practical-steps-for-upgrading-the-ruby-on-rails-version-in-a-small-to-medium-sized-app-292d</guid>
      <description>&lt;p&gt;Upgrading a Rails app to the latest version can feel overwhelming. &lt;/p&gt;

&lt;p&gt;I had no idea where to start when I tackled it for the first time. &lt;/p&gt;

&lt;p&gt;Deprecation warnings, gem conflicts, and the infamous “it works locally but not on production 🫥” surprises can make the process seem like a maze of headaches.&lt;/p&gt;

&lt;p&gt;But don’t worry. With a clear plan and a step-by-step approach, it doesn’t have to be daunting. After upgrading my app from Rails 5 to Rails 7 and learning through mistakes, I would like to share the steps I’ve found most helpful 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 0: Have a Degradation Test Ready
&lt;/h2&gt;

&lt;p&gt;Before upgrading rails, it’s crucial to have a &lt;strong&gt;degradation test&lt;/strong&gt; in place. A degradation test is a combination of &lt;strong&gt;automated tests&lt;/strong&gt; (like unit, integration, or system tests) and &lt;strong&gt;manual tests&lt;/strong&gt; that ensure the critical functionality of your application remains intact after changes. Essentially, it’s your safety net to catch breaking issues early.&lt;/p&gt;

&lt;p&gt;If you already have a robust set of tests for major upgrades, great!  If you don’t, it’s probably not realistic to start creating a comprehensive automated test suite just for the rails upgrade—it would be a monumental task. Instead, focus on creating &lt;strong&gt;comprehensive manual tests&lt;/strong&gt; for your app’s core functionality 👀 Identify key features that must work without fail and document step-by-step processes to verify them.&lt;/p&gt;

&lt;p&gt;With these tests ready, you'll feel more assured about handling the upgrade without worrying about breaking key features. While creating them might seem tedious now, they’ll save you time and headaches during this and future upgrades.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Read the Official Migration Guide&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is one of the most important steps 🔥 in upgrading your Rails version—it sets the foundation for a smooth and successful migration.&lt;/p&gt;

&lt;p&gt;Before diving in, bookmark the &lt;a href="https://guides.rubyonrails.org/upgrading_ruby_on_rails.html" rel="noopener noreferrer"&gt;official Rails upgrade guide&lt;/a&gt;. This is your go-to resource for version-specific advice, highlighting key changes that could impact your app.&lt;/p&gt;

&lt;p&gt;Take a moment to grab a coffee ☕️ and read through the documentation. As you go, create a checklist of the changes you’ll need to tackle, so you can work through them step by step. (Personally, I also create a list of what I don’t understand so that I can systematically research later.)&lt;/p&gt;

&lt;p&gt;At first, it might feel overwhelming, especially if it’s your first time upgrading Rails—there’s a lot of information, and some parts may be unfamiliar. But that’s actually a good thing! 💡 Rails goes the extra mile to provide comprehensive guides to help you, something you don’t always get with other libraries. 😅&lt;/p&gt;

&lt;p&gt;You may need to do some additional research along the way, but this effort pays off 👍 By understanding the changes and their impact on your codebase, you’ll be better equipped to troubleshoot issues, update your tests, and confidently make the necessary updates later.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Fix Deprecation Warnings&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When upgrading rails, one of the first things I always deal with is &lt;code&gt;DEPRECATION WARNING&lt;/code&gt;. These warnings are rails' way of notifying you that certain features or methods you're using are outdated and will be removed in future versions. &lt;/p&gt;

&lt;p&gt;While these warnings don’t necessarily break your app right away, ignoring them could cause issues when you eventually upgrade to the next rails version. &lt;/p&gt;

&lt;p&gt;I’ve written an extensive post on the subject that walks you through the process in detail. &lt;/p&gt;

&lt;p&gt;Check it out here: &lt;a href="https://dev.to/michymono77/rails-and-ruby-deprecation-warnings-stop-ignoring-start-fixing-1kca"&gt;Rails and Ruby Deprecation Warnings: Stop Ignoring, Start Fixing&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Update Gems&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Upgrading rails almost always requires updating your gems to resolve dependency conflicts. While starting with &lt;code&gt;bundle outdated&lt;/code&gt; to identify outdated gems might seem like a logical first step, it can be unrealistic for older apps with numerous gems or complex configurations.&lt;/p&gt;

&lt;p&gt;A practical approach I often follow is to create a separate test branch for the upgrade. First, I update the Rails version in the Gemfile to the desired version, then run &lt;code&gt;bundle update rails&lt;/code&gt; to begin the upgrade process. At this point, &lt;strong&gt;you'll likely encounter dependency conflicts, but that's expected&lt;/strong&gt; 😌&lt;/p&gt;

&lt;p&gt;The point here is to identify gems that have dependency conflicts so that you can systematically address these conflicts by updating gems one at a time in a different brach, ensuring each update works seamlessly with your current app’s setup. I believe this method keeps the process manageable while maintaining stability throughout the upgrade.&lt;/p&gt;

&lt;p&gt;Here’s a step-by-step approach to manage gem updates effectively:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a Test Branch&lt;/strong&gt;&lt;br&gt;
Start by creating a new branch for the gem updates to keep your main branch stable. This allows you to experiment without affecting the production codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run &lt;code&gt;bundle update&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Execute &lt;code&gt;bundle update rails&lt;/code&gt; . This step will likely trigger dependency conflicts. This is part of the process so it is okay 👍&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Make a List of Conflicting Gems&lt;/strong&gt;&lt;br&gt;
Review the errors carefully and compile a list of conflicting gems. This will give you a clear roadmap for addressing each issue systematically and at the right time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Update Gems Incrementally in Separate Branches&lt;/strong&gt;&lt;br&gt;
Update each gem from the list in a separate branch. In some cases, &lt;strong&gt;certain gems can only be updated after upgrading your rails version&lt;/strong&gt;. Make a note of these gems  📝, and remember to revisit later.&lt;/p&gt;

&lt;p&gt;For a more detailed guide on updating gems effectively, check out my post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/michymono77/how-to-keep-your-ruby-gems-up-to-date-without-the-stress-2ikf"&gt;How to Keep Your Ruby Gems Up-to-Date Without the Stress&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This systematic approach ensures a smoother process, keeping your app stable while aligning with Rails’ latest requirements.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4 : Update gem rails version&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once you’ve updated all the necessary gems and resolved dependency issues, it’s time to update your rails version. Run &lt;code&gt;bundle update rails&lt;/code&gt; to ensure your application is using the latest rails version specified in your &lt;code&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5 : Run &lt;code&gt;rails app:update&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once you’ve successfully updated your rails version, the next step is to run the &lt;code&gt;rails app:update&lt;/code&gt; command. This command is a useful tool that helps create new files and update existing ones to align with the latest rails version. However, you’ll also need to adjust the code to fit your specific configuration and needs.&lt;/p&gt;

&lt;p&gt;Before running this command, I strongly recommend visiting &lt;a href="https://railsdiff.org/" rel="noopener noreferrer"&gt;RailsDiff&lt;/a&gt; to review the changes between your current Rails version and the one you’re upgrading to. This step helps you prepare for the updates and better understand the adjustments being made.&lt;/p&gt;

&lt;p&gt;One important thing to note is that running this command might update the &lt;code&gt;load_defaults&lt;/code&gt; version in your app in config/applicatin.rb. ⚠️You DO NOT have to update the version of load_defaults when upgrading rails version 🙆‍♂️ To better understand how to manage and update &lt;code&gt;load_defaults&lt;/code&gt;, refer to my blog:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/michymono77/demystifying-rails-loaddefaults-mastering-configuration-updates-7fm"&gt;Demystifying Rails load_defaults: Mastering Configuration Updates&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6 : Update Existing Code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After upgrading rails, it's possible that your application contains code that relies on now-deprecated behaviors. (Carefully review the deprecation warnings you encountered earlier, as they often pinpoint specific areas of your code that need attention.) Use the checklist you created in Step 1 and incrementally make change ✅&lt;/p&gt;

&lt;p&gt;In addition, some gem upgrades may only be possible after upgrading rails itself. For these gems, you might find that their behaviors have changed, requiring you to update how your application interacts with them. Use the list from Step 3 and ensure their corresponding code adjustments are thoroughly tested after the rails upgrade is complete.&lt;/p&gt;

&lt;p&gt;To streamline this process, I recommend you to use your test suite to catch regressions early. Running your tests after each change can help identify areas where functionality breaks, allowing you to address issues incrementally. Don't forget to also manually test complex workflows or areas that aren’t well-covered by automated tests.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Step 7: Test, Test, Test 😎&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Testing is the keystone of any successful rails upgrade—your last line of defense against unexpected issues and your best assurance that your app is production-ready.&lt;/p&gt;

&lt;p&gt;By this point, you’ve already laid a solid foundation: you’ve followed the upgrade guide, fixed deprecation warnings, and updated your gems. Now, it’s time to validate everything. This isn’t just about running tests—it’s about systematically uncovering and addressing any lingering issues. Let’s break it down. 🚦&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Run Your Automated Test Suite&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Automated tests can quickly identify broken functionality caused by the upgrade. Again, fix issues incrementally and rerun tests after each fix to confirm that the problem has been resolved.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Conduct Manual Testing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Automated tests alone may not be sufficient, especially for complex workflows, user interactions, or edge cases. This is where your manual degradation tests from Step 0 come into play.  Focus on critical user-facing features like payment, form submissions, and reports—anything that directly impacts core functionalities.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Test in a Staging Environment&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before merging into production, deploy your upgraded Rails app to a staging environment that mirrors production. This step helps catch environment-specific issues that might not appear locally.  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Plan for Post-Deployment Testing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Even with the most thorough testing, surprises can happen in production. Be prepared to monitor logs, errors, and user reports closely after deployment. &lt;strong&gt;Having a rollback plan ready can be a lifesaver in case something goes wrong.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Testing is the final hurdle in your Rails upgrade journey, and while it might feel tedious, it’s worth the effort. Think of it as your safety net that ensures your users experience a smooth, reliable application.&lt;/p&gt;

&lt;p&gt;With automated tests passing, manual workflows verified, and staging tests completed, you’ll be ready to confidently deploy your upgraded Rails app. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Wrapping up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Upgrading a Rails app may seem overwhelming at first, but with a clear plan, it becomes a rewarding process. By preparing degradation tests, addressing deprecation warnings, and updating gems incrementally, you can minimize risks and maintain stability. Thorough testing—both automated and manual—ensures a smooth transition to the new version. Each upgrade strengthens your app’s foundation and boosts your confidence as a developer. Good luck 🚀&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Demystifying Rails load_defaults: Mastering Configuration Updates</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Thu, 05 Dec 2024 21:45:15 +0000</pubDate>
      <link>https://dev.to/michymono77/demystifying-rails-loaddefaults-mastering-configuration-updates-7fm</link>
      <guid>https://dev.to/michymono77/demystifying-rails-loaddefaults-mastering-configuration-updates-7fm</guid>
      <description>&lt;p&gt;Have you seen this line in your &lt;code&gt;config/application.rb&lt;/code&gt; file?&lt;code&gt;config.load_defaults 7.2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or maybe you've stumbled across an enigmatic-looking initializer like this?👇&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config/initializers/new_framework_defaults_7_0.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you weren’t specifically looking for these, you might not even notice they exist. &lt;/p&gt;

&lt;p&gt;In this post, I’ll explain what these settings are and guide you through the steps to manage them effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is config.load_defaults?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;config.load_defaults&lt;/code&gt; is a configuration setting in rails that helps manage the default configuration values for a specific rails version. &lt;/p&gt;

&lt;p&gt;It ensures that your application uses the configuration settings that are appropriate for the version of rails you're running, while &lt;strong&gt;maintaining compatibility with previous versions&lt;/strong&gt; during an upgrade.&lt;/p&gt;

&lt;p&gt;When you specify a version with &lt;code&gt;config.load_defaults&lt;/code&gt;, rails will load the default settings for that version into your application. This includes changes to things like middleware, default behaviors, and other framework settings that may have evolved over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  But, why does config.load_defaults exit?
&lt;/h2&gt;

&lt;p&gt;Upgrading a rails application often means dealing with changes in configuration settings, some of which might involve breaking changes. These changes can impact significant parts of your codebase, and updating everything at once can be risky and time-consuming. &lt;/p&gt;

&lt;p&gt;This is where config.load_defaults becomes a lifesaver. When you upgrade Rails, you can use config.load_defaults to explicitly specify the version of Rails that determines your application's default configuration settings.&lt;/p&gt;

&lt;p&gt;For example, if you're using Rails 7.1, it’s possible that your app's configurations are still using defaults from 7.0 or below. This happens because rails gives you the flexibility to adopt the new defaults gradually. By doing so, your application continues running without any surprising behavior changes, while you methodically transition to newer settings.&lt;/p&gt;

&lt;p&gt;This incremental approach allows you to align your app with the latest version’s best practices and performance improvements at your own pace. It’s a great way to minimize disruptions during an upgrade, giving you time to test and address any issues that might arise 😄&lt;/p&gt;

&lt;h2&gt;
  
  
  What is new_framework_defaults_x_y.rb?
&lt;/h2&gt;

&lt;p&gt;During the version upgrade process, rails creates a special file in your &lt;code&gt;config/initializers&lt;/code&gt; directory called something like &lt;code&gt;new_framework_defaults_X_Y.rb&lt;/code&gt; (where X and Y matches your target Rails version) when you run &lt;code&gt;rails app:update&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Think of this file as a cheat sheet 📝 for all the new configuration defaults introduced in that rails version. The settings are conveniently listed as commented-out lines, waiting for you to decide when (and if) you want to enable them.&lt;/p&gt;

&lt;p&gt;Why is this file helpful? &lt;/p&gt;

&lt;p&gt;Adopting new defaults can sometimes lead to changes in behavior that might affect your app. By uncommenting the settings &lt;strong&gt;one at a time&lt;/strong&gt;, you can test each change individually, ensuring your application behaves as expected. This step-by-step approach minimizes risks and gives you the chance to handle compatibility issues without overwhelming your team—or your app 😌&lt;/p&gt;

&lt;p&gt;Once you've uncommented all the lines and are confident your app is running smoothly with the new defaults, you can delete this initializer file. At this point, you should also update your &lt;code&gt;config.load_defaults&lt;/code&gt; version to reflect the new Rails version.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if You Can’t Update to a New Setting?
&lt;/h2&gt;

&lt;p&gt;Sometimes, you might find yourself in a situation where updating to a new configuration setting isn't feasible due to specific constraints. &lt;/p&gt;

&lt;p&gt;Don’t worry—you can still update your &lt;code&gt;load_defaults&lt;/code&gt; version and work around the issue. 👍&lt;/p&gt;

&lt;p&gt;Let’s say you’re working with &lt;code&gt;new_framework_defaults_5_1.rb&lt;/code&gt; (meaning your Rails version is 5.1, but your &lt;code&gt;load_defaults&lt;/code&gt; is still set to 5.0). If you can’t update the following setting because of a restriction:&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;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unknown_asset_fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can explicitly override this setting by adding it to your &lt;code&gt;config/application.rb&lt;/code&gt; file or one of your initializer files, like 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="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unknown_asset_fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've added this override, you can safely bump the &lt;code&gt;load_defaults&lt;/code&gt; version to 5.1. This ensures your app benefits from other new defaults while maintaining compatibility for the settings you couldn’t update.&lt;/p&gt;

&lt;p&gt;⚠️ However, keep in mind that by taking this approach, you’re deviating from the framework’s default behavior. Make sure you document this exception clearly and regularly revisit it to ensure it remains necessary as your app evolves. Strive to align with the defaults whenever possible to keep your application consistent with rails’ best practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Keeping your app’s configuration aligned with Rails’ defaults is a critical step in maintaining stability and taking advantage of new features. The combination of &lt;code&gt;config.load_defaults&lt;/code&gt; and &lt;code&gt;new_framework_defaults_x_y.rb&lt;/code&gt; gives you the flexibility to upgrade safely, one step at a time.&lt;/p&gt;

&lt;p&gt;By gradually adopting new settings, you reduce the risk of compatibility issues and ensure your app remains modern and efficient. Document any exceptions, revisit them often, and strive to align with Rails best practices. With these tools and strategies, you can confidently keep your app up-to-date and ready for the future.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Rails and Ruby Deprecation Warnings: Why You Should Care and How to Fix Them</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Mon, 02 Dec 2024 21:54:39 +0000</pubDate>
      <link>https://dev.to/michymono77/rails-and-ruby-deprecation-warnings-stop-ignoring-start-fixing-1kca</link>
      <guid>https://dev.to/michymono77/rails-and-ruby-deprecation-warnings-stop-ignoring-start-fixing-1kca</guid>
      <description>&lt;p&gt;If you’ve ever fired up your rails server (&lt;code&gt;rails s&lt;/code&gt;) or console (&lt;code&gt;rails c&lt;/code&gt;) and been greeted with a loud &lt;strong&gt;"DEPRECATION WARNING&lt;/strong&gt;,” you’re definitely not alone. &lt;/p&gt;

&lt;p&gt;These warnings pop up because rails, in its ongoing evolution , regularly retires older features—features that might still work today but could spell trouble down the road.&lt;/p&gt;

&lt;p&gt;In this post, I want to go through what deprecation warnings mean and how to handle them effectively in order to avoid common pitfalls.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;TL;DR&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deprecation warnings are rails’(and ruby's) way of giving you a heads-up about features that will soon be removed or changed.&lt;/li&gt;
&lt;li&gt;Ignoring them can lead to unexpected issues during upgrades.&lt;/li&gt;
&lt;li&gt;Use configurations like &lt;code&gt;config.active_support.deprecation&lt;/code&gt; to handle these warnings in development or test environments.&lt;/li&gt;
&lt;li&gt;You should at least pay attention to deprecation warnings from &lt;strong&gt;both rails and ruby.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Why Should You Care About Deprecation Warnings?
&lt;/h3&gt;

&lt;p&gt;Deprecation warnings are essentially rails’ polite heads-up that a feature or method you’re using is on its way out. &lt;/p&gt;

&lt;p&gt;While technically it is just a “warning” that won’t break your app today, ignoring it could set you up for headaches down the road. After all, if it’s called a “warning,” you should probably pay attention to it, right? 😌 When the deprecated feature is eventually removed in a future rails version, your app could break unexpectedly.&lt;/p&gt;




&lt;h3&gt;
  
  
  When Deprecation Warnings Actually Matter?
&lt;/h3&gt;

&lt;p&gt;From my experience, deprecation warnings matters most when you’re updating your app—especially during rails version upgrades.&lt;/p&gt;

&lt;p&gt;Imagine this: you’re in charge of upgrading rails, juggling multiple breaking changes, and managing dependencies across a bunch of gems. The last thing you want is to be blindsided by deprecated features that have been quietly lurking in your app, only to find out they’re no longer supported in the new version. Not fun.&lt;/p&gt;

&lt;p&gt;The earlier you tackle these warnings, the smoother your upgrade process will be, and the fewer bugs you’ll run into later. (I get it—keeping an eye on warnings and addressing them right away isn’t always realistic. But trust me, a little proactive effort here can save you down the road.)&lt;/p&gt;




&lt;h3&gt;
  
  
  Where Do We Find Deprecation Warnings?
&lt;/h3&gt;

&lt;p&gt;In rails, you might see deprecation warnings in various places depending on your environment setup and the context of the issue. Here's where you commonly encounter them:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Development Environment Console&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Deprecation warnings often appear in the rails server console (&lt;code&gt;rails s&lt;/code&gt;) when you run your application in the development environment. These warnings are printed in real-time as you use the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Test Suite Output&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When running your test suite, deprecation warnings might appear in the terminal output if your tests trigger deprecated methods or features. The configuration file for the test environment (&lt;code&gt;test.rb&lt;/code&gt;) usually has a different value set for &lt;code&gt;config.active_support.deprecation&lt;/code&gt; than &lt;code&gt;development.rb&lt;/code&gt;, which is why you may only see the warning when you run your tests. We’ll talk about this in more detail in a later section.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Rails Logs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Deprecation warnings are logged in your application's log files (e.g., &lt;code&gt;log/development.log&lt;/code&gt;, &lt;code&gt;log/test.log&lt;/code&gt;, or &lt;code&gt;log/production.log&lt;/code&gt; depending on the environment). These logs can be reviewed to identify and address issues. (This is also based on your value of &lt;code&gt;config.active_support.deprecation&lt;/code&gt; )&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Rails Console&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you're experimenting or debugging in the rails console (&lt;code&gt;rails c&lt;/code&gt;), deprecation warnings can appear when you use deprecated methods. &lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;CI/CD Pipelines&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you have continuous integration or deployment pipelines, deprecation warnings might appear in the build logs when running tests or tasks.&lt;/p&gt;




&lt;h3&gt;
  
  
  How do we mange  deprecation warning?
&lt;/h3&gt;

&lt;p&gt;There are two primary types of deprecation warnings you should definitely watch for to ensure a smoother upgrade experience in the future:&lt;/p&gt;

&lt;p&gt;・&lt;strong&gt;Rails-specific warnings&lt;/strong&gt;&lt;br&gt;
・&lt;strong&gt;Ruby-specific warnings&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Deprecation Warnings in Rails
&lt;/h3&gt;

&lt;p&gt;Rails provides built-in tools to help you manage deprecation warnings effectively. You can configure how your application responds when a deprecated feature is used by adjusting the &lt;code&gt;config.active_support.deprecation&lt;/code&gt; setting in your configuration files.&lt;/p&gt;

&lt;p&gt;Rails offers several actions you can take to control how deprecation warnings are handled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;raise&lt;/strong&gt;: Immediately raises an error, halting execution. This is useful for catching deprecated features early in development or testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;stderr&lt;/strong&gt;: Prints the warning to the standard error output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;log&lt;/strong&gt;: Sends the warning to your application's log file. Ideal for apps that have been around for a while and need time to address warnings gradually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;notify&lt;/strong&gt;: Triggers a notification, like a Slack message or email, whenever a deprecation occurs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;silence&lt;/strong&gt;: Suppresses the warning entirely. This should be used sparingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are creating a new app, I would probably configure this setting to &lt;code&gt;:raise&lt;/code&gt; in development and test environments. This means that when a deprecated feature is encountered, rails will raise an error immediately. By catching deprecation warnings early in the development cycle, you’ll be able to address them before they become bigger issues.&lt;/p&gt;

&lt;p&gt;You can add like this to your &lt;code&gt;config/environments/development.rb&lt;/code&gt; or &lt;code&gt;config/environments/test.rb&lt;/code&gt;:&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;active_support&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deprecation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HOWEVER, if the app has been around for a while, it might have a lot of warnings so &lt;code&gt;:raise&lt;/code&gt; is not an ideal option. You could send the warning to logs and later review it or notify to slack to regularly review. &lt;/p&gt;

&lt;p&gt;Furthermore, if you’re not comfortable with globally raising errors for all deprecation warnings, rails allows more granular control. The &lt;code&gt;config.active_support.disallowed_deprecation&lt;/code&gt; setting lets you specify individual deprecations that should be handled differently.&lt;/p&gt;

&lt;p&gt;This is useful if you've already addressed certain deprecations but want to be alerted if they reappear. You can configure specific deprecations to raise an error, which helps you identify and fix regressions quickly.&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;active_support&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disallowed_deprecation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:raise&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;active_support&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disallowed_deprecation_warnings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="sr"&gt;/will be removed in Rails \d+\.\d+/&lt;/span&gt; 
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, you'll be notified if certain deprecated features are reintroduced into your app.&lt;/p&gt;




&lt;h3&gt;
  
  
  Managing Deprecation Warnings in Ruby
&lt;/h3&gt;

&lt;p&gt;Dealing with deprecations in rails is only part of the story. You also need to pay attention to Ruby deprecations, as these can have a significant impact on your app's future compatibility as well.&lt;/p&gt;

&lt;p&gt;A strategy I recommend is to apply the same approach to Ruby deprecation warnings as you do for rails. By handling both sets of warnings in a similar manner, you ensure a more consistent and proactive approach to keeping your app up-to-date.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://blog.arkency.com/authors/piotr-jurewicz/" rel="noopener noreferrer"&gt;Piotr Jurewicz&lt;/a&gt;, who clearly explains how to print Ruby deprecation warnings (e.g., “DEPRECATION WARNING: [RUBY]”), the process is straightforward.&lt;/p&gt;

&lt;p&gt;In essence, the &lt;code&gt;Warning&lt;/code&gt; module in Ruby includes the &lt;code&gt;warn&lt;/code&gt; method, which issues all warnings, typically printed to &lt;code&gt;$stderr&lt;/code&gt;. You can customize the behavior of &lt;code&gt;Warning.warn&lt;/code&gt; by extending the module with your own method, allowing you to filter warnings or redirect their output. To maintain the default behavior, simply call &lt;code&gt;super&lt;/code&gt; within your custom method. However, it's advised not to redefine the instance method &lt;code&gt;Warning#warn&lt;/code&gt;, as doing so may interfere with the default warning functionality.&lt;/p&gt;

&lt;p&gt;(Read more about the &lt;code&gt;Warning&lt;/code&gt; module here: &lt;a href="https://ruby-doc.org/core-3.1.0/Warning.html" rel="noopener noreferrer"&gt;Ruby Documentation&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;For example, here’s how you can capture Ruby 2.7 deprecation warnings:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You could also enhance this logic to send notifications to Slack or any other service when a deprecation warning occurs.&lt;/em&gt;&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;module&lt;/span&gt; &lt;span class="nn"&gt;MyWarningFilter&lt;/span&gt;
  &lt;span class="no"&gt;RUBY_DEPRECATIONS_2_7_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"Using the last argument as keyword parameters is deprecated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# Add other deprecations as needed&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;RUBY_DEPRECATIONS_2_7_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;super&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;span class="no"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyWarningFilter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I highly recommend reading Piotr’s blog post, as it provides detailed guidance on creating initializer files like the one above for &lt;strong&gt;various Ruby and Rails combinations&lt;/strong&gt;. You’ll gain deeper insights into how to handle deprecation warnings effectively:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.arkency.com/do-you-tune-out-ruby-deprecation-warnings/" rel="noopener noreferrer"&gt;Do You Tune Out Ruby Deprecation Warnings?&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;When you see deprecation warnings, it’s better not to ignore them—taking responsibility and action now will save you time and frustration later. &lt;/p&gt;

&lt;p&gt;Proactively managing these warnings ensures your app stays compatible with future versions of rails and Ruby. Addressing them early helps prevent issues during upgrades and maintains the health of your application. By utilizing rails’ built-in tools and customizing Ruby’s &lt;code&gt;Warning&lt;/code&gt; module, you can stay ahead of potential problems. Taking action now ensures a smoother, more stable development process in the long run.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to use gem omniauth and omniauth-oauth2 to implement SSO for multiple customers</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Sun, 01 Dec 2024 03:01:44 +0000</pubDate>
      <link>https://dev.to/michymono77/how-to-use-gem-omniauth-and-omniauth-oauth2-to-implement-sso-for-multiple-customers-45e7</link>
      <guid>https://dev.to/michymono77/how-to-use-gem-omniauth-and-omniauth-oauth2-to-implement-sso-for-multiple-customers-45e7</guid>
      <description>&lt;p&gt;I recently had the opportunity to set up SSO in a Ruby on Rails app for a very unique situation, so I decided to write about my experience. &lt;/p&gt;

&lt;p&gt;This guide might be helpful to you if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You plan to use OpenID Connect as the authentication protocol.&lt;/li&gt;
&lt;li&gt;You need to implement SSO for multiple customers, and at least two of them use the same identity provider (IdP), such as Okta.&lt;/li&gt;
&lt;li&gt;Your Rails setup does not support dynamically configuring client information as described in other &lt;a href="https://github.com/omniauth/omniauth/wiki/Setup-Phase" rel="noopener noreferrer"&gt;solutions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Due to customer-side limitations, their IdP CANNOT send custom parameters back to your Rails application. (*Most of the time, they should be able to do so. )&lt;/li&gt;
&lt;li&gt;You cannot use third-party tools like Keycloak because of resource constraints or compliance requirements prohibiting external IAM solutions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose you have to implement SSO for two customers that use &lt;code&gt;Okta&lt;/code&gt; for identity management, given the limitation above, there is a walk around😃&lt;/p&gt;

&lt;p&gt;Let's dive into the actionable steps to implement SSO in your Ruby on Rails app while addressing the limitations described earlier!&lt;/p&gt;

&lt;p&gt;Step 1: Add the required gems and run &lt;code&gt;bundle install&lt;/code&gt;&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="c1"&gt;# Gemfile&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'devise'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'omniauth'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'omniauth-rails_csrf_protection'&lt;/span&gt; &lt;span class="c1"&gt;# NOTE: Required as a countermeasure for CVE-2015-9284 in the omniauth gem.&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'omniauth-oauth2'&lt;/span&gt; &lt;span class="c1"&gt;# NOTE: Used to create Strategies classes for omniauth.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Define the endpoints.&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="c1"&gt;# config/routes.rb &lt;/span&gt;
&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'auth/customer_a/callback'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'omniauth/sessions#create'&lt;/span&gt;
&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'auth/customer_b/callback'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'omniauth/sessions#create'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Add a migration file and run &lt;code&gt;rails db:migrate&lt;/code&gt;.&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="c1"&gt;# db/migrate/xxxx.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddOmniauthColumnsToUsers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;7.2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&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;Step 4: Add the &lt;code&gt;self.from_omniauth(auth)&lt;/code&gt; method as a class method in the &lt;code&gt;User&lt;/code&gt; model to create or find a user based on authentication data from an external service.  (&lt;strong&gt;Ensure to include error handling tailored to your app's requirements.&lt;/strong&gt;)&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="c1"&gt;# app/models/user.rb&lt;/span&gt;
 &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&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;devise&lt;/span&gt; &lt;span class="ss"&gt;:database_authenticatable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:registerable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:recoverable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rememberable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:trackable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:encryptable&lt;/span&gt;
    &lt;span class="o"&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;from_omniauth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;find_or_create_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'info'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'email'&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;user&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'provider'&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="nf"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'uid'&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="nf"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'info'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'email'&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="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'info'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'name'&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 5: Add a controller (&lt;strong&gt;Ensure to include error handling tailored to your app's requirements.  Your customer could make their mistakes in configuration setup as well.&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;*Note: You need the id_token to end an Okta session, so it is recommended to store it in the session. Use the rail’s &lt;code&gt;reset_session&lt;/code&gt; helper to remove this when a user wants to log out.&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;Omniauth::SessionsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&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;omniauth_auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'omniauth.auth'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;id_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;omniauth_auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'extra'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'id_token'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_omniauth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;omniauth_auth&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;user&lt;/span&gt;
      &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id_token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id_token&lt;/span&gt;
      &lt;span class="n"&gt;sign_in&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="n"&gt;user&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;root_path&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;redirect_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;alert: &lt;/span&gt;&lt;span class="s1"&gt;'Add a custom alert statement here'&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;Step 6: Create a method to define an OmniAuth OAuth2 strategy for each customer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Most of the code is from the following gem: &lt;a href="https://github.com/omniauth/omniauth-okta" rel="noopener noreferrer"&gt;omniauth-okta&lt;/a&gt;&lt;/em&gt;&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="c1"&gt;# config/initializers/omniauth_okta.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'omniauth-oauth2'&lt;/span&gt;

&lt;span class="no"&gt;OIDC_DEFAULT_SCOPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%{openid profile email}&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_omniauth_strategy&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="no"&gt;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="no"&gt;OmniAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Strategies&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;OAuth2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="ss"&gt;:skip_jwt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="ss"&gt;:jwt_leeway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;

    &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="ss"&gt;:client_options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;site:                 &lt;/span&gt;&lt;span class="s1"&gt;'https://your-org.okta.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;authorize_url:        &lt;/span&gt;&lt;span class="s1"&gt;'https://your-org.okta.com/oauth2/default/v1/authorize'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;token_url:            &lt;/span&gt;&lt;span class="s1"&gt;'https://your-org.okta.com/oauth2/default/v1/token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;user_info_url:        &lt;/span&gt;&lt;span class="s1"&gt;'https://your-org.okta.com/oauth2/default/v1/userinfo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;response_type:        &lt;/span&gt;&lt;span class="s1"&gt;'id_token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;authorization_server: &lt;/span&gt;&lt;span class="s1"&gt;'default'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;audience:             &lt;/span&gt;&lt;span class="s1"&gt;'api://default'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="ss"&gt;:scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OIDC_DEFAULT_SCOPE&lt;/span&gt;

    &lt;span class="n"&gt;uid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;raw_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'sub'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;name:       &lt;/span&gt;&lt;span class="n"&gt;raw_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&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="n"&gt;raw_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="n"&gt;raw_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'given_name'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;last_name:  &lt;/span&gt;&lt;span class="n"&gt;raw_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'family_name'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;image:      &lt;/span&gt;&lt;span class="n"&gt;raw_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'picture'&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;extra&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{}.&lt;/span&gt;&lt;span class="nf"&gt;tap&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;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:raw_info&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw_info&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;skip_info?&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;
          &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id_token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id_token&lt;/span&gt;

          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:skip_jwt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;id_token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
            &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id_info&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validated_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id_token&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;span class="k"&gt;end&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;client_options&lt;/span&gt;
      &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:client_options&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;raw_info&lt;/span&gt;
      &lt;span class="vi"&gt;@_raw_info&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user_info_url&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Errno&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ETIMEDOUT&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Timeout&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&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;callback_url&lt;/span&gt;
      &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:redirect_uri&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_host&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;callback_path&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;id_token&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;

      &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id_token'&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;authorization_server_path&lt;/span&gt;
      &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:site&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;authorization_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:authorization_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'default'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/oauth2/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;authorization_server&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;def&lt;/span&gt; &lt;span class="nf"&gt;authorization_server_audience&lt;/span&gt;
      &lt;span class="n"&gt;client_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:audience&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'default'&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;validated_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;verify_iss:        &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;verify_aud:        &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;iss:               &lt;/span&gt;&lt;span class="n"&gt;authorization_server_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;aud:               &lt;/span&gt;&lt;span class="n"&gt;authorization_server_audience&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;verify_sub:        &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;verify_expiration: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;verify_not_before: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;verify_iat:        &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;verify_jti:        &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;leeway:            &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:jwt_leeway&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&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;span class="n"&gt;okta_sso_customers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'customer_a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'customer_b'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;okta_sso_customers&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="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;strategy_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_omniauth_strategy&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="no"&gt;OmniAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Strategies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;const_set&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="nf"&gt;camelize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strategy_class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 7: Add &lt;code&gt;omniauth.rb&lt;/code&gt;. Use &lt;code&gt;ENV&lt;/code&gt; variables and avoid hardcoding secrets in configuration files.&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="c1"&gt;# config/initializers/omniauth.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="no"&gt;OmniAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="ss"&gt;:customer_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_CLIENT_ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_CLIENT_SECRET'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;client_options: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;site: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_ISSUER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;authorization_server: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_SERVER_NAME'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# e.g., 'default'&lt;/span&gt;
      &lt;span class="ss"&gt;authorize_url: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_ISSUER'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/oauth2/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_SERVER_NAME'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/authorize"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;token_url: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_ISSUER'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/oauth2/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_SERVER_NAME'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;user_info_url: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_ISSUER'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/oauth2/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_SERVER_NAME'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/userinfo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;audience: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_AUDIENCE'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# e.g., 'api://default'&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;redirect_uri: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'OKTA_REDIRECT_URI'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

   &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="ss"&gt;:customer_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Implementing SSO with the outlined steps makes it easier to set up seamless authentication for multiple customers using OpenID Connect—even in tricky situations like this one! 😊 &lt;/p&gt;

&lt;p&gt;I know this is not really the most straightforward way of using &lt;code&gt;gem omniauth&lt;/code&gt; but if you find yourself in a similar boat, think of this guide as your starting template 🛠️, ready to be customized for your specific needs. By using OmniAuth and creating tailored strategies for each identity provider, you’re building a scalable and secure authentication setup that works for everyone involved. 🚀&lt;/p&gt;

&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://medium.com/@petro.yakubiv/authentication-with-okta-and-devise-in-ruby-on-rails-app-2e81a8c6b198" rel="noopener noreferrer"&gt;https://medium.com/@petro.yakubiv/authentication-with-okta-and-devise-in-ruby-on-rails-app-2e81a8c6b198&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Practical Steps: How to Strategically Update Your Ruby Gems</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Fri, 29 Nov 2024 00:29:07 +0000</pubDate>
      <link>https://dev.to/michymono77/how-to-keep-your-ruby-gems-up-to-date-without-the-stress-2ikf</link>
      <guid>https://dev.to/michymono77/how-to-keep-your-ruby-gems-up-to-date-without-the-stress-2ikf</guid>
      <description>&lt;p&gt;Updating Ruby gems seem like a simple task, but it could be a bit more complicated especially when you deal with apps that have been around for a while. &lt;/p&gt;

&lt;p&gt;Keeping your Ruby gems up to date is essential for maintaining the security, performance, and compatibility of your application. Regular updates ensure you’re not only benefiting from the latest features but also protecting your app from vulnerabilities. &lt;/p&gt;

&lt;p&gt;However, as with most things in development, we all know that it’s rarely that simple 🤷‍♂️ You’ll likely encounter breaking changes, dependency conflicts, and other challenges that require a more thoughtful approach. &lt;/p&gt;

&lt;p&gt;In this post, I’ll walk you through a step-by-step guide to updating Ruby gems, including how to handle conflicts and assess your options when things go wrong. By understanding the overall process, I hope that you'll feel more confident and equipped to tackle these challenges when they arise 😊&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps to update gems
&lt;/h3&gt;

&lt;p&gt;Updating a gem isn’t just about bumping its version number—it’s a process that requires careful consideration to avoid introducing issues into your app. I tend to follow  the structured approach below to make it manageable and easier to communicate changes to the team:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step1: Read the Changelog:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first step is to thoroughly read the changelog or release notes. These typically outline new features, improvements, bug fixes, and, most importantly, any breaking changes that could affect your application. (If the repository is well-maintained, it usually includes changelogs or at least release notes.) A few key things to look for in the changelog:&lt;/p&gt;

&lt;p&gt;👀 &lt;u&gt;&lt;em&gt;Breaking Changes&lt;/em&gt;&lt;/u&gt;: These are the changes that can cause your code to break, so you definitely want to pay attention to them. They might involve things like renamed methods, altered method signatures, or the removal of features. If there are significant changes, you’ll probably need to adjust some parts of your code to make everything work with the new version.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;u&gt;&lt;em&gt;Deprecations&lt;/em&gt;&lt;/u&gt;: Sometimes gems mark certain features or methods as "deprecated." This means they’ll still work for now but will be removed in future updates. It might be tempting to ignore them since they aren't technically breaking your app yet, but it’s a good idea to tackle these early on. Fixing deprecations as they pop up saves you from headaches later and makes future updates smoother.&lt;/p&gt;

&lt;p&gt;🚀 &lt;u&gt;&lt;em&gt;New Features and Enhancements&lt;/em&gt;&lt;/u&gt;: Updates often bring new features or performance improvements, and these can be real game-changers. These new additions are designed to make your life easier, so it’s definitely worth taking a moment to explore them.&lt;/p&gt;

&lt;p&gt;(By the way, when upgrading to major versions, like updating Rails,  you can also look for the “migration guides”. These guides are created specifically to help you navigate the big transition smoothly! )&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step2: Review Dependencies:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When updating a gem, it’s essential to take a look at its dependencies—other gems or libraries it relies on to function properly. Many gems have specific version requirements for their dependencies, and those dependencies might also need to be updated to ensure compatibility.&lt;/p&gt;

&lt;p&gt;If the gem you're updating depends on an older version of another gem, you might run into conflicts, especially if that dependency also needs to be updated for your app to function correctly. For example, if you’re updating a gem that depends on an older version of Rails, you may need to update Rails as well or adjust some of your app’s code to match the new dependency requirements. We will talk a bit more about this later. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step3: Change your codebase if necessary:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve reviewed the changelog and dependencies, you can begin making any necessary changes to your codebase. (In some cases, refactoring the existing codebase beforehand can make the gem update process smoother.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step4: Test Thoroughly:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run your test suite before and after the gem update. This ensures the update doesn’t introduce regressions or break functionality(Also, do manual testing when necessary.). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step5: (optional) Use Dependabot or Similar Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tools like Dependabot automate dependency updates and alert you to conflicts or vulnerabilities. &lt;/p&gt;




&lt;h2&gt;
  
  
  What to Do When You Encounter a Dependency Conflict
&lt;/h2&gt;

&lt;p&gt;Updating gems doesn’t always go smoothly. Dependency conflicts can often arise, especially in apps with complex or older codebases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand the cause
&lt;/h3&gt;

&lt;p&gt;Start by reading the error messages or Bundler output carefully. Determine which gem or dependency is causing the issue and why. For example, a conflict could arise because one gem depends on an older version of a library, while another gem requires the latest version. &lt;/p&gt;

&lt;p&gt;Knowing the exact cause helps you decide whether you need to update the conflicting gem or whether you need to address other issues in your app’s dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  So what action should I take?
&lt;/h3&gt;

&lt;p&gt;When you exactly know why you have the issue, you can now evaluate your options😃 I listed six options below (but the list is not exhaustive): &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;==Option 1: Update the Conflicting Dependency==&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the conflict stems from an outdated dependency, the first and most straightforward step is to update the conflicting gem or its dependency to a version that resolves the issue.&lt;/p&gt;

&lt;p&gt;This is your go-to option when the conflicting dependency is outdated and you can safely update it without causing issues for other dependencies. (but ensure you understand the changes of conflicting gem and run tests accordingly)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;==Option 2: Downgrade the Gem==&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If updating the conflicting dependency isn’t feasible (perhaps due to a major release with breaking changes), you may need to downgrade to a version of the gem that’s compatible with the rest of your dependencies.&lt;/p&gt;

&lt;p&gt;This option is useful when an update introduces breaking changes that are difficult to address immediately or when the update causes issues that are not easily resolved. If the gem remains functional at a lower version, it can serve as a temporary solution.&lt;/p&gt;

&lt;p&gt;However, downgrading a gem is not ideal, as it may result in losing important features, bug fixes, or security patches. Additionally, if the gem is no longer actively maintained, this could leave you vulnerable in the long term. If you choose this approach, it’s essential to have a plan to address the issue in the future. 🙂&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;==Option 3: Fork the Gem==&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;When the gem you rely on doesn’t offer a solution or has an issue that the maintainers haven’t addressed, you can fork the gem and apply a patch or modify it to fix the issue. Forking is a good option when the gem is particularly critical to your app and there are no alternatives. &lt;/p&gt;

&lt;p&gt;However, it should only be used as a temporary fix, and you should aim to contribute your changes back to the original repository if possible. I personally found that maintaining a fork of a gem can be time-consuming, especially if the gem is updated frequently. &lt;/p&gt;

&lt;p&gt;You’ll need to stay on top of changes to the upstream repository to ensure your fork doesn’t fall behind. Additionally, applying your own fixes could introduce unforeseen bugs. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;==Option 4: Use Patch Gems or Monkey Patching==&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the conflict is isolated to a specific part of a gem, you can apply a patch to address the issue, or use monkey patching to modify certain methods or behaviors in the gem.&lt;/p&gt;

&lt;p&gt;This approach is useful when you need a quick fix for a minor issue in the gem but should be used sparingly. Monkey patching can make your codebase a little harder to maintain and debug, especially if the gem undergoes updates that conflict with your patches. It can also lead to subtle bugs that are hard to trace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;==Option 5: Delay the Update==&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the gem update is not immediately critical, consider postponing it until a more stable version is released or a resolution to the conflict becomes available.&lt;/p&gt;

&lt;p&gt;This approach is reasonable if the update is not urgent and resolving the conflict would take significant time or effort. However, delaying an update may not be ideal if the new version includes critical security patches. Assess the risks of delaying carefully, especially if the update addresses vulnerabilities or is part of a broader release that enhances your application’s stability or functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;==Option 6: Replace the Gem Entirely==&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a gem consistently causes issues or no longer meets your requirements, it is a time to consider replacing it with an alternative that offers similar functionality without the conflicts.This approach is ideal when persistent conflicts cannot be resolved through updates, downgrades, or patches, and when better-maintained or more compatible alternatives are available.&lt;/p&gt;

&lt;p&gt;Keep in mind that replacing a gem can be time-intensive, particularly if it is deeply integrated into your codebase. It may require substantial code changes and adaptation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Updating Ruby gems is crucial for keeping your app secure and efficient, but it can sometimes be tricky. By following the steps in this guide, you'll be prepared to tackle dependency conflicts and breaking changes with confidence. No matter what option you choose—whether it’s updating, downgrading, or replacing a gem—just remember to test thoroughly and stay patient. You’ve got this! 💪&lt;/p&gt;

</description>
      <category>rails</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Getting Started with Object-Oriented Design (Part 2): Design Principles and Design Patterns</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Wed, 27 Nov 2024 00:52:58 +0000</pubDate>
      <link>https://dev.to/michymono77/getting-started-with-object-oriented-design-part-2-design-principles-and-design-patterns-1e7c</link>
      <guid>https://dev.to/michymono77/getting-started-with-object-oriented-design-part-2-design-principles-and-design-patterns-1e7c</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/michymono77/getting-started-with-object-oriented-design-part-1-shifting-from-process-oriented-to-jd3"&gt;previous article&lt;/a&gt;, we have discussed the fundamental goal of Object-Oriented Design (OOD), which is to manage the relationships between objects, allowing the system to adapt efficiently to future changes.&lt;/p&gt;

&lt;p&gt;This naturally leads us to the next question: &lt;strong&gt;How do we effectively manage the relationships between objects? 🤔&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The truth is, there’s no one-size-fits-all approach to this. Every system and context is different, and there are many ways to go about it.&lt;/p&gt;

&lt;p&gt;Thankfully, there are some incredibly useful tools to simplify this process 😌  —tools crafted by industry pioneers like the Gang of Four. We owe these visionaries a great deal for sharing their wisdom, which has laid the groundwork for many of the techniques we rely on today.&lt;/p&gt;

&lt;p&gt;With that in mind, I'd like to briefly introduce two practical concepts that can help you manage object relationships more effectively: &lt;strong&gt;Design Principles&lt;/strong&gt; and &lt;strong&gt;Design Patterns&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;They will help set you on the path toward building scalable systems or at least nudge you in the right direction. By applying these concepts, you'll not only enhance the quality of your code but also make your development process smoother and more predictable.&lt;/p&gt;




&lt;h3&gt;
  
  
  Understanding “Design Principles”
&lt;/h3&gt;

&lt;p&gt;Design principles are general guidelines or best practices that help you make better decisions when writing code. These principles aren’t really specific solutions to problems, but rather broad rules that you choose to follow when applies. &lt;/p&gt;

&lt;p&gt;Some popular design principles include:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Single Responsibility Principle (SRP)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This principle is about keeping each class focused on a single responsibility(*some might argue but it does not necessarily mean a single class must have only a single public method). This makes the code easier to understand and modify. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Open-Closed Principle (OCP)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This principle ensures your code can accommodate new functionality without altering existing code. This reduces the risk of introducing bugs when extending the app. For instance, by using inheritance or composition, you can add features while maintaining the integrity of the original class. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Liskov Substitution Principle (LSP)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You should be able to use a subclass anywhere you'd use its parent class without breaking anything. In other words, a subclass should honor the expectations set by its parent class, ensuring it behaves consistently and predictably without introducing errors or unexpected results.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Interface Segregation Principle (ISP)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This principle ensures that classes aren’t forced to implement methods they don’t need by promoting the use of smaller, more focused interfaces. In essence, it advocates for splitting large, "fat" interfaces into smaller, specific ones to reduce unnecessary dependencies and improve modularity. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. Dependency Inversion Principle (DIP)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This states that high-level modules should not depend on low-level modules; instead, both should depend on abstractions. Abstractions should not depend on details, but details should depend on abstractions.&lt;/p&gt;

&lt;p&gt;This concept might be self-explanatory at first, but don’t worry—let’s break it down with an example.&lt;/p&gt;

&lt;p&gt;For instance, in a coffee maker system, the high-level module is the coffee maker, and the low-level module is the brewing method (e.g., drip, espresso).&lt;/p&gt;

&lt;p&gt;If you apply the Dependency Inversion Principle (DIP), the coffee maker depends on an abstraction of the brewing method (an interface or abstract class that &lt;strong&gt;defines the brewing process&lt;/strong&gt;), not on a specific implementation (such as a &lt;code&gt;DripBrew&lt;/code&gt; or &lt;code&gt;EspressoBrew&lt;/code&gt; class). This allows you to swap out different brewing methods without altering the core coffee maker logic. The coffee maker just needs to know how to interact with the abstraction, making the system more flexible and maintainable.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;6. DRY (Don’t Repeat Yourself)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Duplicated code can lead to inconsistencies and make maintenance more difficult. Abstract common functionality into reusable components or methods. This reduces redundancy and ensures that changes only need to be made in one place.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;7. Law of Demeter&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The Law of Demeter is often summarized as "Only talk to your immediate friends." This principle emphasizes minimizing an object's knowledge of other objects' internal structures or properties. Instead of relying on deep method chaining (obj.getSomething().doAnotherThing()), delegate responsibilities to direct collaborators.&lt;/p&gt;

&lt;p&gt;It is important to note that there are MANY MORE of these principles! You don’t really need to perfectly memorize them all but it is nice to know to stop your hands and think about it once in a while. (By the way, if you have every heard of SOLID, it is about the principles from 1 to 5.)&lt;/p&gt;

&lt;p&gt;Also, make sure not to overemphasize one principle over another. ⚠️ For example, if you care too much about DRY principle for example, you might compromise other areas that may affect managing the relationships between objects.&lt;/p&gt;




&lt;h3&gt;
  
  
  Understanding “Design Patterns”
&lt;/h3&gt;

&lt;p&gt;When it comes to object-oriented software design, principles like Single Responsibility and Open-Closed lay the groundwork for creating maintainable systems. &lt;/p&gt;

&lt;p&gt;But what about recurring challenges that developers face across projects? &lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;design patterns&lt;/strong&gt; come into play!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What Are Design Patterns?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Design patterns are proven, reusable solutions to common problems encountered in software design, providing  you with practical templates for solving particular challenges. &lt;/p&gt;

&lt;p&gt;Think of design patterns as ready-to-use blueprints that you can implement when you face a certain type of problem. These patterns help you avoid reinventing the wheel 🔄  and offer solutions that have already been tested and refined over time.&lt;/p&gt;

&lt;p&gt;While design principles provide guidance on how to design, design patterns give you specific techniques or approaches to address particular issues. &lt;/p&gt;

&lt;p&gt;Here are five of the commonly used design patterns in object-oriented programming. (There are many more!)&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Singleton Pattern&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Ensures that a class has only one instance and provides a global point of access to that instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Managing shared resources like configuration settings where only one instance should exist across the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Observer Pattern&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Allows one object (the "subject") to notify other objects (the "observers") about state changes, creating a one-to-many relationship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Used for situations where multiple parts of the system need to respond to changes in another part, such as updating UI elements when a model changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Factory Method Pattern&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Defines an interface for creating objects, allowing subclasses to alter the type of objects that will be created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Creating instances of different types of objects dynamically, for example, when deciding which type of user (admin, guest, etc.) to create based on conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Decorator Pattern&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Adds new functionality to an object dynamically without altering its structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Extending the functionality of existing objects, like adding additional presentation logic to a model object, often used for enhancing UI components.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Service Object Pattern&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Encapsulates business logic into separate classes, improving maintainability and keeping controllers thin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Use Case:&lt;/strong&gt; Handling complex logic or operations that do not belong in controllers or models, such as processing payments.&lt;/p&gt;

&lt;p&gt;Design patterns are like trusty blueprints for tackling common challenges in software design, but they’re not always the right tool for the job. It’s easy to get excited and want to use a design pattern everywhere—I’ve definitely been guilty of this myself 🙋‍♂️&lt;/p&gt;

&lt;p&gt;However, overusing them can add unnecessary complexity, so it’s important to step back and consider whether a pattern truly fits your specific problem. When applied thoughtfully, design patterns can be game-changers, helping you build code that’s maintainable, scalable, and clear.&lt;/p&gt;




&lt;h3&gt;
  
  
  Wapping up
&lt;/h3&gt;

&lt;p&gt;Design principles and patterns are essential tools in a developer's toolkit 🛠️, offering both guidance and practical solutions for creating maintainable, adaptable systems. They’re not always applicable, but when used strategically and thoughtfully, they can simplify complexity and enhance your code's structure. &lt;/p&gt;

&lt;p&gt;Always remember to assess your context 🔍 before applying any principle or pattern—what works for one problem might not suit another. With a balanced approach, you can leverage these concepts to build systems that stand the test of time, making both your life as a developer and your codebase far more enjoyable to work with! 😊&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>webdev</category>
      <category>oop</category>
    </item>
    <item>
      <title>Getting Started with Object-Oriented Design (Part 1): Shifting from Process-Oriented to Object-Oriented Thinking</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Sun, 24 Nov 2024 02:59:18 +0000</pubDate>
      <link>https://dev.to/michymono77/getting-started-with-object-oriented-design-part-1-shifting-from-process-oriented-to-jd3</link>
      <guid>https://dev.to/michymono77/getting-started-with-object-oriented-design-part-1-shifting-from-process-oriented-to-jd3</guid>
      <description>&lt;p&gt;“Hmm… Have you ever heard of OOD?”&lt;/p&gt;

&lt;p&gt;That’s what one of the senior developers at my company once asked me, just 10 seconds after glancing at my code.&lt;/p&gt;

&lt;p&gt;Without thinking, I quickly replied, “Yes.” &lt;/p&gt;

&lt;p&gt;It was an instinctive answer, but the truth was, I didn’t fully understand what Object-Oriented Design (OOD) really meant at the time—or why it even mattered 🤷‍♂️&lt;/p&gt;

&lt;p&gt;When we’re just starting out as web developers, it’s easy to focus on learning new languages, frameworks, and building projects as quickly as possible. The goal is often just to get something working, and concepts like OOP or OOD can feel abstract, like something you’ll figure out “later.”&lt;/p&gt;

&lt;p&gt;But here’s the thing: &lt;em&gt;later&lt;/em&gt; can come much sooner than you expect, especially when your code starts growing in complexity. (At least, it did for me 😅)&lt;/p&gt;

&lt;p&gt;At its core, &lt;strong&gt;object-oriented design is about &lt;em&gt;managing the relationships between objects, enabling the system to adapt efficiently to future changes&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But why do we need this? And what does managing the relationships between objects mean exactly?&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Ways to Think About Systems: Process vs. Object-Oriented Perspectives
&lt;/h2&gt;

&lt;p&gt;Before we dive into answering that question, let’s first explore the object-oriented view of the world. Without adopting this mindset, the principles and patterns of OOP can feel abstract and difficult to grasp. 😌&lt;/p&gt;

&lt;p&gt;To build a solid foundation first, I want to start by comparing two key ways of thinking about systems: the process-oriented approach and the object-oriented approach. These two paradigms offer different perspectives on how we break down and model problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. The Process-Oriented View&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In general, the &lt;strong&gt;process-oriented&lt;/strong&gt; perspective is about focusing on the &lt;strong&gt;sequence of actions&lt;/strong&gt; or &lt;strong&gt;steps&lt;/strong&gt; that need to be performed in a set order to achieve a result. &lt;/p&gt;

&lt;p&gt;Don't think too hard on this 🙆‍♂️&lt;/p&gt;

&lt;p&gt;It’s similar to following a recipe, where each action follows a specific order, and one step leads to the next. The key here is &lt;strong&gt;flow&lt;/strong&gt;—moving from one state to another by completing predefined actions.&lt;/p&gt;

&lt;p&gt;Let’s take a look at the example of making tea from a process-oriented perspective, where the focus is on the &lt;strong&gt;sequence&lt;/strong&gt; of steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Boil Water&lt;/strong&gt;: The first step is to boil water. The key here is that the water needs to be hot enough for the tea, regardless of how long it takes to boil.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Tea Leaves&lt;/strong&gt;: Once the water has boiled, the next step is to add the tea leaves. This step can only happen after the previous one is completed, as it depends on the water being ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let it Steep&lt;/strong&gt;: After the tea leaves are added, the water must steep for a specific amount of time to extract the flavor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pour Tea into a Cup&lt;/strong&gt;: Finally, after the steeping process is finished, the tea is poured into a cup.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this approach, the focus is on performing actions in a specific order. If any action is skipped or carried out out of sequence, the result will not turn out as intended. &lt;/p&gt;

&lt;p&gt;The process-oriented view sees the world as a series of actions, each building on the one before it, where every step must follow a predetermined flow to achieve the desired outcome.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. The Object-Oriented View&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In contrast, in object-oriented view, the focus is on "objects." These objects are like little workers that have two things: information (called attributes) and actions they can do (called methods).&lt;/p&gt;

&lt;p&gt;Instead of viewing tasks as a strict sequence of &lt;strong&gt;actions&lt;/strong&gt;, here we emphasize the &lt;strong&gt;interaction&lt;/strong&gt; between objects, where each object performs actions based on the requests (or "messages") it receives from other objects. In other words, each object can "ask" other objects to do things, and they act based on those requests.&lt;/p&gt;

&lt;p&gt;Let’s revisit the tea-making example from an object-oriented perspective.&lt;/p&gt;

&lt;p&gt;==&lt;strong&gt;The Objects in Tea-Making&lt;/strong&gt;== &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kettle&lt;/strong&gt;: The Kettle has information like the current temperature of the water and whether it's boiling. It also has an action it can perform, like boiling the water.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TeaBag&lt;/strong&gt;: The TeaBag has information about the type of tea and whether it’s steeped or not. It can also steep the tea in hot water.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cup&lt;/strong&gt;: The Cup holds the tea once it’s made. It has information about what’s inside (like tea or water) and whether it’s full.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person&lt;/strong&gt;: The Person is the one who makes everything happen. The Person asks the Kettle to boil water, tells the TeaBag when to steep, and pours the tea into the Cup. The Person coordinates the entire process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can already see how we capture the world is a little bit different from the setup. Now, let’s see some examples of how these objects might work together to make tea:&lt;/p&gt;

&lt;p&gt;==&lt;strong&gt;How the Objects May Work Together&lt;/strong&gt;==&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Person tells the Kettle to start boiling the water. The Kettle listens and heats the water without needing further help from the Person.&lt;/li&gt;
&lt;li&gt;After the water is ready, the Person checks the TeaBag, maybe looking at how strong the tea should be or how long to steep it. The Person then tells the TeaBag to start steeping the tea (the Person might put the TeaBag in the water and monitor how long it steeps).&lt;/li&gt;
&lt;li&gt;Person pours the Tea into the Cup once the tea has steeped enough, the Person checks the tea. If it’s ready, they tell the Kettle to pour the tea into the Cup. The Cup now holds the tea, ready for the Person to drink.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is describing the same tea-making process, but do you now see the difference from the process oriented view? 🙂&lt;/p&gt;

&lt;p&gt;In the object-oriented view, the focus is on how objects interact with each other by sending and receiving messages, rather than following a linear sequence of actions. It is just a different point of view💡 Each object has its own responsibilities and responds to messages (or method calls) from other objects based on its current state. This allows for &lt;strong&gt;flexibility&lt;/strong&gt;—objects don’t rely on a predefined sequence of events but instead communicate as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 So, why do we need object oriented “DESIGN”?
&lt;/h2&gt;

&lt;p&gt;In object-oriented applications, components (or objects) interact with each other, forming complex relationships and dependencies.&lt;/p&gt;

&lt;p&gt;If these relationships aren’t properly managed, what would happen? A small change in one part of the code can cause problems in other areas, leading to instability and making the application harder to maintain.&lt;/p&gt;

&lt;p&gt;Let's take the same tea-making example again, but this time imagine that the process isn’t organized well at all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Kettle and TeaBag are Too Dependent on Each Other:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Imagine that the &lt;strong&gt;Kettle&lt;/strong&gt; object not only boils the water but also decides how long the &lt;strong&gt;TeaBag&lt;/strong&gt; should steep for some reason. In this setup, if you want to increase the steeping time, it might also affect how long the kettle boils the water, creating unnecessary links between unrelated tasks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;The TeaBag Controls the Boiling Process:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;If the &lt;strong&gt;TeaBag&lt;/strong&gt; itself is somehow responsible for managing the boiling of the water, then it's now mixing two responsibilities: the boiling process and the steeping process. This would mean that any change to how the tea leaves steep (like changing tea types) could unintentionally interfere with how the kettle boils the water. This makes each object too tightly coupled and hard to modify without breaking other parts of the process.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;The Cup Gets Involved in the Steeping:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Now, let’s say the &lt;strong&gt;Cup&lt;/strong&gt; is responsible for checking if the tea is steeped properly before it’s even poured. It may decide how long the tea should steep based on what is poured into it, creating confusion and redundant responsibilities. This prevents each object from focusing on just one task.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In this disorganized setup, making a change in one object (like adjusting how long the water boils) could unintentionally affect other objects, like the steeping time, and cause undesirable consequences, such as over-steeping or under-boiling the water. This results in a lack of flexibility and control, making it harder to manage and maintain.&lt;/p&gt;

&lt;p&gt;Now, compare that to the organized object-oriented approach, where we treat each task as an independent object with its own responsibilities. If you want to change how long the tea steeps, you just adjust the &lt;strong&gt;TeaBag&lt;/strong&gt; object without worrying about how the &lt;strong&gt;Kettle&lt;/strong&gt; or &lt;strong&gt;Cup&lt;/strong&gt; will be affected. This way, each part of the process is flexible, independent, and easier to manage. You can make changes to one object without breaking the entire system.&lt;/p&gt;

&lt;p&gt;This is why we need OOD. By keeping these tasks separated, OOD lets you make adjustments and scale your system without chaos. This is why OOD is so powerful—it lets your code grow and adapt in a structured, maintainable way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;OOD offers a way to manage the relationships between objects. While it might feel a little tricky at first, learning OOD ensures that your code isn’t just working for today but is also flexible and ready for future changes. By planning ahead, OOD helps you build code that’s not only sustainable but also scalable, making it easier to adapt as your project evolves.&lt;/p&gt;

&lt;p&gt;In the next post, I will explain how to approach object-oriented design and introduce you to some tools.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>learning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How 'Conventional Commits' Improved My Development Workflow</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Sat, 23 Nov 2024 06:43:29 +0000</pubDate>
      <link>https://dev.to/michymono77/how-conventional-commits-improved-my-development-workflow-1c8a</link>
      <guid>https://dev.to/michymono77/how-conventional-commits-improved-my-development-workflow-1c8a</guid>
      <description>&lt;p&gt;When you code, do you already know what goes into your commit messages when creating pull requests? 🤔&lt;/p&gt;

&lt;p&gt;It’s not a topic we often discuss, but I’ve found that being intentional with commit messages naturally shapes how I organize my pull requests.&lt;/p&gt;

&lt;p&gt;In fact, thinking about how you’ll structure your commits before you even begin coding can significantly benefit your development process in two major ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It naturally helps you structure your work&lt;/strong&gt;: Thinking about the commit message beforehand naturally informs what goes into each commit. For example, you’ll start to distinguish between commits that add new files and those that improve existing features. This makes your commits more logical and easier to track.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It improves code clarity for others and yourself&lt;/strong&gt;: A clear commit message helps the reviewer understand the purpose of each change. It also aids you in the future when revisiting your code—whether for bug fixes or adding new features—since you'll be able to easily identify what was done in each commit.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Practical Benefits of “Conventional Commits”
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zi33ca3871aqesc0ne0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zi33ca3871aqesc0ne0.png" alt=" " width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To keep things consistent, I’ve been using the &lt;strong&gt;“Conventional Commits”&lt;/strong&gt; standard.&lt;/p&gt;

&lt;p&gt;If you’re unfamiliar, &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/" rel="noopener noreferrer"&gt;Conventional Commits&lt;/a&gt; is a standardized way to write commit messages using the format:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;type(scope): description&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This format isn’t just about aesthetics—it makes commits more human-readable and machine-processable, which opens the door to automation. For example, tools can use these messages to generate changelogs, handle versioning, or even enforce commit rules in CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;By using prefixes like &lt;code&gt;feat&lt;/code&gt;, &lt;code&gt;fix&lt;/code&gt;, or &lt;code&gt;docs&lt;/code&gt;, you quickly convey the intent of each change, improving both collaboration and traceability.&lt;/p&gt;

&lt;p&gt;Here’s a quick example of a “Conventional Commit”:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;feat: implement filters for product search by price and category&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can tell it’s simple, clear, and instantly communicates the purpose of the change.&lt;/p&gt;




&lt;h3&gt;
  
  
  Make It Easier with VS Code
&lt;/h3&gt;

&lt;p&gt;One great thing about “Conventional Commits” is that you don’t have to memorize the format. If you use &lt;strong&gt;VS Code&lt;/strong&gt;, there’s an extension to simplify the process:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits" rel="noopener noreferrer"&gt;Install the Conventional Commits Extension&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This extension guides you through creating standardized commit messages right from your editor, making it easier to adopt this practice. It's so easy.&lt;/p&gt;




&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Being intentional about commit messages might seem like extra work at first, but it’s an investment that pays off VERY QUICKLY. With “Conventional Commits,” you’ll find your workflow becoming more structured, your pull requests clearer, and your development experience smoother overall.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>github</category>
    </item>
    <item>
      <title>How Focusing on Business Impact Improved My “Technical Debt” Conversations</title>
      <dc:creator>Michiharu Ono</dc:creator>
      <pubDate>Thu, 14 Nov 2024 23:33:06 +0000</pubDate>
      <link>https://dev.to/michymono77/how-focusing-on-business-impact-improved-my-technical-debt-conversations-21pe</link>
      <guid>https://dev.to/michymono77/how-focusing-on-business-impact-improved-my-technical-debt-conversations-21pe</guid>
      <description>&lt;p&gt;“&lt;em&gt;The fact that you have technical debt is a good thing.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;I know this sounds counterintuitive. &lt;/p&gt;

&lt;p&gt;A senior developer with over 20 years of experience once told me this, and it completely changed the way I thought about technical debt—especially in the fast-paced environment of small startups. &lt;/p&gt;

&lt;p&gt;In case if you’ve never heard of the term, &lt;em&gt;technical debt&lt;/em&gt; refers to the hidden costs and risks that accumulate in a codebase when shortcuts or compromises are made to achieve short-term goals—such as delivering new features quickly. For example, delaying updates to core dependencies or skipping key testing steps can accelerate development in the moment. But over time, these decisions add up, resulting in a backlog of issues that eventually hinder the team. This debt can lead to a range of future problems, including compatibility issues, maintenance challenges, performance limitations, and even security vulnerabilities. As technical debt grows, each new feature becomes harder to implement, testing becomes more cumbersome, and the system itself becomes more fragile. &lt;/p&gt;

&lt;p&gt;When I joined the company as a junior developer, the company was already dealing with significant technical debt, and I struggled with it for over two years. For example, in 2023, while Rails 7.1 was available, we were still using version 5.2. This version lag limited our ability to use newer features and frameworks and left us with outdated dependencies. Furthermore, many libraries had critical updates available, but they hadn’t been applied (fortunately, we hadn’t experienced any security breaches). Additionally, there were several design flaws and coding practices that caused confusion, even among the developers. The lack of proper testing also made it difficult to ensure stability. However, when I observed the tasks assigned to developers, the focus was always on releasing new features by a set deadline. Nobody seemed to address the growing technical debt, and the codebase kept becoming more complex with each new feature built on top of it.&lt;/p&gt;

&lt;p&gt;As I grew more anxious about the code’s state, especially as security patches continued to be ignored, I decided to bring up my concerns with a technical advisor at the company. I expected he might encourage me to push harder for code quality or maintenance efforts. Instead, his response completely reframed my perspective.&lt;/p&gt;

&lt;p&gt;He explained that, having worked with many startups, he’d seen that most of them failed before they ever accumulated significant technical debt. “&lt;em&gt;The fact that we’re facing these problems&lt;/em&gt;,” he said, “&lt;em&gt;is a sign that the business has survived long enough to reach this stage.&lt;/em&gt;” This was actually a positive indication: it meant the company was moving forward, gaining users, and facing the same challenges that most successful companies face over time. Without this technical debt, we might not even have a product to work on. He also stressed the importance of prioritizing business success. “&lt;em&gt;In order to for you to keep a job as a developer, the business needs to succeed first and foremost&lt;/em&gt;,” he said. “&lt;em&gt;Everything we do should be driven by how it impacts the business, not just by technical concerns.&lt;/em&gt;” While code quality and system stability are important, he reminded me that our job as developers is to support the business—sometimes, that means balancing technical debt with the urgency to grow.&lt;/p&gt;

&lt;p&gt;That said, he emphasized the importance of always being aware of technical debt and continuously improving the codebase. For developers who don’t control project priorities or tasks, it’s crucial to have good communication skills when discussing the state of the code. For example, if you come out and say, “&lt;em&gt;There are technical debt and security issues, and it’s a problem that needs fixing&lt;/em&gt;,” non-technical stakeholders might just see a working product and not understand the urgency. However, if you frame it like this: “&lt;em&gt;Currently, there are unpatched security vulnerabilities in key libraries that could be exploited by hackers. If left unaddressed, this puts the company at risk for data breaches, potential financial losses, and serious reputational harm, as seen in cases like Company XX.&lt;/em&gt;” Or, you could add, “&lt;em&gt;Upgrading these libraries will not only mitigate these risks but also reduce ongoing costs, enhance the user experience, and streamline the development process, helping our team to work more efficiently.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;After the talk with him, I believe the way I communicate—not only within the dev team but across the entire company—has improved. By shifting the focus of the conversation to “How can I contribute to the success of the business?” rather than simply saying “Some problems need fixing,” decision-makers are more likely to pay attention and engage in the conversation. Framing the issue in terms of business impact makes it easier to align technical challenges with company goals. As a result, it becomes clearer why certain improvements are important, and it promotes a more collaborative approach to solving problems. Plus, it helps keep everyone on the same page—developers, business stakeholders, and the entire team working toward the same goals.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
