<?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: Nemwel Boniface</title>
    <description>The latest articles on DEV Community by Nemwel Boniface (@nemwelboniface).</description>
    <link>https://dev.to/nemwelboniface</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%2F879351%2F80229b4f-1365-4363-a05b-08ca3d379f18.jpeg</url>
      <title>DEV Community: Nemwel Boniface</title>
      <link>https://dev.to/nemwelboniface</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nemwelboniface"/>
    <language>en</language>
    <item>
      <title>Rails Authorization Beyond Models: Securing Dashboards and Service Controllers with CanCanCan</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Fri, 06 Feb 2026 08:44:00 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/rails-authorization-beyond-models-securing-dashboards-and-service-controllers-with-cancancan-4fgj</link>
      <guid>https://dev.to/nemwelboniface/rails-authorization-beyond-models-securing-dashboards-and-service-controllers-with-cancancan-4fgj</guid>
      <description>&lt;p&gt;Hello friends! I’m back with more Ruby on Rails concepts I refreshed this week to stay productive and avoid the social media "doom scroll." 2026 is the year we kill the bad habits hindering our potential, and for me, that means trading the doom-scroll for the docs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fru2iii2laoal0wbz6hn3.gif" 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%2Fru2iii2laoal0wbz6hn3.gif" alt="Welcome to A Guide to Rails Authorization Beyond Models: Securing Dashboards and Service Controllers with CanCanCan" width="498" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction - Authentication and Authorization. What are these?
&lt;/h2&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%2Fvimsh2huuip3nz4g969x.gif" 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%2Fvimsh2huuip3nz4g969x.gif" alt="Authentication and Authorization. What are these?" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Authentication&lt;/em&gt; and &lt;em&gt;authorization&lt;/em&gt; are often confused, and many people think they are the same, but they are not. Similarly, their ease of implementation is often confused. You might think they are easy to integrate. But are they really? Let me explain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cloudflare.com/learning/access-management/what-is-authentication/" rel="noopener noreferrer"&gt;Authentication&lt;/a&gt;, which is verifying who you are, is the easy part. A quick Devise gem install and a migration, and your system knows its users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/intro-to-iam/what-is-authorization" rel="noopener noreferrer"&gt;Authorization&lt;/a&gt;, which decides what they can do, is where the mess begins. Most of us start with &lt;a href="https://github.com/CanCanCommunity/cancancan" rel="noopener noreferrer"&gt;CanCanCan&lt;/a&gt; basics: you add &lt;code&gt;load_and_authorize_resource&lt;/code&gt; to a &lt;code&gt;PostsController&lt;/code&gt;, and magic happens. Admins edit, users read. Life is good.&lt;/p&gt;

&lt;p&gt;But what happens when you have a controller that doesn't map neatly to a database table? Think about a &lt;code&gt;DashboardController&lt;/code&gt; or a &lt;code&gt;SearchController&lt;/code&gt;. There is no &lt;strong&gt;"Dashboard"&lt;/strong&gt; table in your database.&lt;/p&gt;

&lt;p&gt;Suddenly, the magic breaks and reality sinks in. You start getting confusing error messages or, even worse, silent security gaps that only appear once a user accidentally accesses classified data.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how to handle &lt;em&gt;authorization&lt;/em&gt; when you need to step outside the standard model framework, specifically using tools like &lt;code&gt;authorize_resource class: false&lt;/code&gt; to keep your "non-model" controllers secure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2goy52wsc59wxg0q25zu.gif" 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%2F2goy52wsc59wxg0q25zu.gif" alt="Lets begin A Guide to Rails Authorization Beyond Models: Securing Dashboards and Service Controllers with CanCanCan" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Foundation: The Rulebook (Ability.rb)
&lt;/h2&gt;

&lt;p&gt;Before we touch our controllers, we have to understand the Source of Truth. In &lt;em&gt;CanCanCan&lt;/em&gt;, your &lt;code&gt;app/models/ability.rb&lt;/code&gt; file is the rulebook. Everything we do in the rest of the app is just asking this one file a single question: "Is this user allowed to do X to Y?". Let me show you the anatomy of one below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user

    if user.admin?
      can :manage, :all
    else
      # Scenario A: Database Model Permission
      # Users can manage their own posts
      can :manage, Post, user_id: user.id

      # Scenario B: Abstract Permission
      # Users can read the dashboard, even though there is no Dashboard model
      can :read, :dashboard 
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pay close attention to &lt;strong&gt;Scenario B&lt;/strong&gt;. Notice how we defined that permission using a symbol (&lt;code&gt;:dashboard&lt;/code&gt;) instead of a class name like &lt;code&gt;Post&lt;/code&gt;. In Rails, we are used to everything being linked to a database table. But here, we are telling the rulebook that "Dashboard" is just a concept, a place the user can go. Understanding this shift from Classes to Symbols is the key to solving our future &lt;strong&gt;"Model Not Found"&lt;/strong&gt; headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Secure by Default" Mindset
&lt;/h2&gt;

&lt;p&gt;Before we look at specific tools, we need to set a baseline. In a professional application, you shouldn't have to remember to add security; security should be there by default. You should only have to turn it off when you explicitly mean to, in very rare situations. In your ApplicationController, I recommend adding this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ApplicationController &amp;lt; ActionController::Base
  # Throw an error if a controller action runs without checking authorization first.
  check_authorization unless: :devise_controller?
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of this as your &lt;em&gt;Authorization&lt;/em&gt; Alarm System. If you create a new controller and forget to define any permissions, Rails will yell at you with a &lt;code&gt;CanCan::AuthorizationNotPerformed&lt;/code&gt; error, which is much better to have your app crash during development because you forgot a security check than to have it silently expose private data in production. We skip &lt;code&gt;devise_controller?&lt;/code&gt; because Devise handles its own "Who are you?" logic, and we don't want to get locked out of the login page!&lt;/p&gt;

&lt;p&gt;Now that the alarm system is armed, let me show you how we satisfy it in different scenarios below:&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 1: The Standard CRUD Resource (This is the Happy Path)
&lt;/h2&gt;

&lt;p&gt;Imagine you have a &lt;em&gt;Product&lt;/em&gt; model and a standard &lt;code&gt;ProductsController&lt;/code&gt; to handle the basics: looking at a list, viewing a single item, or editing details. The Solution would be to add the &lt;code&gt;load_and_authorize_resource&lt;/code&gt; at the top of the controller. This will then tell &lt;code&gt;CanCanCan: _"Hey, look at the controller name (Products), find the matching model (&lt;/code&gt;Product&lt;code&gt;), load the data into a variable (like&lt;/code&gt;@product`), and check the Rulebook to see if this user is allowed to be here."_&lt;/p&gt;

&lt;p&gt;You are to use this ideally 90% of the time if your controller maps directly to a database table and performs standard RESTful actions. Example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iskt3r6zonuaca2e3bt.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%2F3iskt3r6zonuaca2e3bt.png" alt="The Standard CRUD Resource" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The beauty here is Clarity. By moving the data loading and the security check to a single line at the top, your controller actions stay "pure." They only focus on what happens after the door has been opened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 2: The "Service" Controller (When there is no Database Model)
&lt;/h2&gt;

&lt;p&gt;This is where the confusion usually starts. You have a controller that performs a service-like a &lt;code&gt;DashboardsController&lt;/code&gt;, a &lt;code&gt;SearchController&lt;/code&gt;, or a &lt;code&gt;ReportsController&lt;/code&gt;. If you check your database, you will find that there is no &lt;code&gt;"Dashboards"&lt;/code&gt; table. Dashboard here is just a page that aggregates data from other places.&lt;/p&gt;

&lt;p&gt;If you try to use ur favourite &lt;code&gt;load_and_authorize_resource&lt;/code&gt; here, &lt;em&gt;CanCanCan&lt;/em&gt; will go looking for a &lt;code&gt;Dashboard&lt;/code&gt; model, fail to find it, and crash your app because it does not exist. The solution is using &lt;code&gt;authorize_resource class: false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This tells &lt;em&gt;CanCanCan&lt;/em&gt;: &lt;em&gt;"I need to secure this controller, but stop looking for a matching database class. Just check if the user is allowed to access the concept of this controller."&lt;/em&gt;. When you set &lt;code&gt;class: false&lt;/code&gt;, &lt;em&gt;CanCanCan&lt;/em&gt; converts the controller name into a symbol (e.g., &lt;code&gt;DashboardsController&lt;/code&gt; becomes &lt;code&gt;:dashboard&lt;/code&gt;) and checks your Rulebook for that specific symbol. This is best used whenever your controller represents a concept, a process, or a service rather than a specific row in a database table.&lt;br&gt;
Example (Recall our &lt;code&gt;Ability.rb&lt;/code&gt; had &lt;code&gt;can :read, :dashboard&lt;/code&gt;.):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnlhgn6hpxbqs8blvrwj.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%2Fpnlhgn6hpxbqs8blvrwj.png" alt="The " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a user tries to access this page without the &lt;code&gt;can :read, :dashboard&lt;/code&gt; rule in their &lt;code&gt;ability.rb&lt;/code&gt;, they’ll get an &lt;em&gt;"Access Denied"&lt;/em&gt; error. With this, you get the same level of security as a standard model, but without the database baggage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 3: The Complex or Expensive Action
&lt;/h2&gt;

&lt;p&gt;Sometimes, checking permissions at the very start of a request is too "blunt". Imagine you have an action that kicks off a heavy background job or calls an external API that charges you per request. You ideally don't want to waste any CPU cycles or money until you are 100% sure the user has the right to be there. The solution is the explicit inline &lt;code&gt;authorize!&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the manual approach, and instead of letting the controller handle security automatically at the top, you take the wheel and tell &lt;em&gt;CanCanCan&lt;/em&gt; exactly when and what to check inside the method itself.&lt;/p&gt;

&lt;p&gt;Situations when you are supposed to use this include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When your logic is too dynamic for the standard helpers.&lt;/li&gt;
&lt;li&gt;When you need to do some "cheap" setup (like checking params) before doing an "expensive" authorization check.&lt;/li&gt;
&lt;li&gt;When your action handles multiple different types of objects at once.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxdao3gn3pj70ypvjz6h.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%2Ffxdao3gn3pj70ypvjz6h.png" alt="The Complex or Expensive Action" width="800" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using &lt;code&gt;authorize!&lt;/code&gt; manually, you ensure that your expensive code is protected by a high-security gate. If the user isn't authorized, the method stops right there and raises an exception before the &lt;code&gt;HeavyReportingJob&lt;/code&gt; ever gets queued.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary and a useful Cheat Sheet
&lt;/h3&gt;

&lt;p&gt;Choosing the right tool comes down to two questions: &lt;strong&gt;"Does this match a database table?"&lt;/strong&gt; and &lt;strong&gt;"When do I need the check to happen?"&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;Scenario&lt;/th&gt;
&lt;th&gt;Mapped to DB?&lt;/th&gt;
&lt;th&gt;Tool to Use&lt;/th&gt;
&lt;th&gt;The "Why"&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Standard RESTful&lt;/strong&gt; (Posts, Users)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;load_and_authorize_resource&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The magic bullet. Loads data and checks rules automatically.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Service/Concept&lt;/strong&gt; (Dashboard, Search)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;authorize_resource class: false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authorizes against a &lt;strong&gt;symbol&lt;/strong&gt; so Rails doesn't look for a missing class.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complex/Expensive Actions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Maybe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Explicit &lt;code&gt;authorize!&lt;/code&gt; in the action&lt;/td&gt;
&lt;td&gt;Gives you total control. Best for guarding expensive API calls or background jobs.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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%2Ftgeoxhqj5nw5xz75b1z2.gif" 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%2Ftgeoxhqj5nw5xz75b1z2.gif" alt="Now the different approaches to authorisation start to make sense" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.health.harvard.edu/mind-and-mood/doomscrolling-dangers" rel="noopener noreferrer"&gt;Doom-scrolling&lt;/a&gt; gives you a dopamine hit that lasts five seconds. Solving a tricky authorization bug gives you a secure, professional application that stays protected while you sleep.&lt;/p&gt;

&lt;p&gt;By moving past the basic "magic" and mastering tools like &lt;code&gt;class: false&lt;/code&gt; and explicit &lt;code&gt;authorize!&lt;/code&gt; calls, you stop fighting the framework and start making it work for your specific needs. In 2026, we aren't just building apps that "work"; we are building apps that are secure, intentional, and architected for the long haul.&lt;/p&gt;

&lt;p&gt;Next time Rails "yells" at you about a missing model, don't reach for your phone to scroll. Reach for your &lt;code&gt;ability.rb&lt;/code&gt; and check if you're trying to authorize a class that doesn't exist.&lt;/p&gt;

&lt;p&gt;Do you prefer the "magic" of automated authorization, or are you a "manual control" developer who likes to see every security gate in your code? I’d love to hear your horror stories about AccessDenied errors in the comments!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy806dk56b318m3m1gs2m.gif" 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%2Fy806dk56b318m3m1gs2m.gif" alt="I will be back with more technical articles" width="480" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Quick Side Note:&lt;/strong&gt; While I’m heads-down building this search engine, I’m also looking to pitch in on new projects. If you’re looking for a mid-level Rails engineer with solid JavaScript experience who prioritizes security, authorization logic, and building defensive, scalable systems, I'd love to chat. You can find me on [&lt;a href="https://www.linkedin.com/in/nemwel-nyandoro/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;/&lt;a href="https://x.com/nemwel_bonie" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;] or check out my other work &lt;a href="https://github.com/Nemwel-Boniface" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>What Happens When a User Deletes Their Account? A Guide to Rails ActiveRecord dependent: Strategies</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Tue, 20 Jan 2026 06:25:43 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/what-happens-when-a-user-deletes-their-account-a-guide-to-rails-activerecord-dependent-strategies-1279</link>
      <guid>https://dev.to/nemwelboniface/what-happens-when-a-user-deletes-their-account-a-guide-to-rails-activerecord-dependent-strategies-1279</guid>
      <description>&lt;p&gt;Hello friends! I'd like to show you Ruby on Rails concepts I refreshed this week to Avoid Social Media Doom Scrolling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fru2iii2laoal0wbz6hn3.gif" 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%2Fru2iii2laoal0wbz6hn3.gif" alt="Welcome to A Guide to Rails ActiveRecord dependent: Strategies for Ruby on Rails Developers" width="498" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We’ve all been there. You’re at the end of a high-intensity coding session, or maybe you're in the middle of a &lt;a href="https://blogs.uoc.edu/mel/the-pomodoro-method-maximizing-productivity-with-interval-time/" rel="noopener noreferrer"&gt;Pomodoro&lt;/a&gt; break. Your brain is fried, and the easiest "hit" is a social media feed scroll. Suddenly, you're deep into &lt;a href="https://www.health.harvard.edu/mind-and-mood/doomscrolling-dangers" rel="noopener noreferrer"&gt;doom-scrolling&lt;/a&gt;, watching phenomenal creators like Simon Squibb ask people, "What’s your dream?" (He is honestly my favourite creator right now, and his videos are raw and authentic and a whole vibe! Who’s your favorite creator lately?)&lt;/p&gt;

&lt;p&gt;This week, I decided to fight the scroll. I closed the "Trending" tab and opened the Rails Documentation instead. My goal was to master &lt;a href="https://guides.rubyonrails.org/association_basics.html" rel="noopener noreferrer"&gt;ActiveRecord Association&lt;/a&gt; Dependencies.&lt;/p&gt;

&lt;p&gt;If you're building a user-centric app, like the "Google-style" search engine I’m currently architecting, you quickly realize that choosing how a record "dies" is just as critical as how it lives. In a search engine that tracks saved queries, leads, and API keys, our strategy has to balance two things: User Privacy (deleting personal data) and Business Continuity (preserving lead data for analytics).&lt;/p&gt;

&lt;p&gt;Here is how I structured my models to ensure my database doesn't become a "ghost town" of orphaned records.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Clean Sweep: &lt;code&gt;dependent: :destroy&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In my app, when a user deletes their account, I want their Saved Searches to vanish. These are private and hold zero value once the owner is gone.&lt;/p&gt;

&lt;p&gt;I chose &lt;code&gt;:destroy&lt;/code&gt; because it’s the "polite" way to say goodbye. Unlike a cold database delete, :destroy instantiates each record and triggers its specific callbacks. If you have logic that says "Notify the search engine to de-index this query when deleted," this is the only way to ensure that "cleanup" logic actually executes.&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;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;has_many&lt;/span&gt; &lt;span class="ss"&gt;:saved_searches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. The Bulk Cleanup: &lt;code&gt;dependent: :delete_all&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Sometimes, you don’t need a polite goodbye; you need raw speed. Imagine a user has millions of Audit Logs. Calling &lt;code&gt;:destroy&lt;/code&gt; would try to load every single log into Ruby memory, a surefire way to crash your server or hang your database.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:delete_all&lt;/code&gt; skips the Rails middleware entirely. It sends a single, sharp SQL command directly to the database. This, however, has a Trade-off: If your AuditLog has a before_destroy hook that cleans up a file on Amazon S3, it will not run. Use this only for "passive" data where speed is the priority over side effects.&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;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="c1"&gt;# If we had millions of log entries, we didn't need to process&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:audit_logs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :delete_all&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. The "Legacy" Handover: &lt;code&gt;dependent: :nullify&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This was the trickiest one for me to grasp, but it’s vital for B2B apps. In our search engine scenario, if a User generates a Lead and then deletes their account, the business still needs that Lead record to calculate conversion rates.&lt;/p&gt;

&lt;p&gt;If I delete the lead, I break the business analytics. By using nullify, the user_id on the Lead becomes NULL, but the record itself stays on the "shelf." The user’s privacy is respected (their name is gone), but the business value is preserved.&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;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;has_many&lt;/span&gt; &lt;span class="ss"&gt;:leads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :nullify&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. The Soft Block: &lt;code&gt;dependent: :restrict_with_error&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I want you to think of this as the "Safety Valve." Sometimes, a user cannot be allowed to leave yet. In my app, if a user has Active API Keys, letting them delete their account would leave external integrations in a broken state.&lt;/p&gt;

&lt;p&gt;Instead of a hard crash, &lt;code&gt;:restrict_with_error&lt;/code&gt; adds a message to the errors object. This allows me to tell the user in the UI: &lt;strong&gt;&lt;em&gt;"You can't close your account while you have active API keys. Please disable them first."&lt;/em&gt;&lt;/strong&gt; It’s a UX-friendly way to protect critical system resources.&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;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="c1"&gt;# "You can't leave us while you still have an active API Key!"&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:api_keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :restrict_with_error&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. The Hard Guard: &lt;code&gt;dependent: :restrict_with_exception&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the ultimate safety net for financial data. I use this for Billing Subscriptions. If the system attempts to delete a user who still has an active billing record, Rails will raise a hard &lt;code&gt;ActiveRecord::DeleteRestrictionError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This isn't for a &lt;strong&gt;"nice UI message."&lt;/strong&gt; This is for your &lt;strong&gt;"Security-First"&lt;/strong&gt; logic, a last line of defense to ensure your financial integrity is never compromised by a stray user.destroy call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="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;has_many&lt;/span&gt; &lt;span class="ss"&gt;:billing_subscriptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :restrict_with_exception&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Short Recap
&lt;/h3&gt;

&lt;p&gt;When a user leaves, not all data should be treated the same. Privacy, performance, and business continuity pull in different directions. Pick &lt;code&gt;:destroy&lt;/code&gt; when cleanup must run, &lt;code&gt;:delete_all&lt;/code&gt; for raw speed, &lt;code&gt;:nullify&lt;/code&gt; to keep business artifacts, and &lt;code&gt;:restrict_with_*&lt;/code&gt; to block dangerous deletions.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.health.harvard.edu/mind-and-mood/doomscrolling-dangers" rel="noopener noreferrer"&gt;Doom-scrolling&lt;/a&gt; gives you a dopamine hit that lasts five seconds. Solving a database integrity problem gives you a stable application that lasts years.&lt;/p&gt;

&lt;p&gt;By strategically picking between &lt;code&gt;:destroy&lt;/code&gt;, &lt;code&gt;:nullify&lt;/code&gt; and &lt;code&gt;:restrict&lt;/code&gt;, I’ve ensured that my system handles user churn like a pro. My advice? Next time you’re bored, don't check the "Trending" tab. Check your schema.rb. There is always a relationship that could be handled more gracefully.&lt;/p&gt;

&lt;p&gt;Which dependent strategy do you find yourself reaching for most often? Are you a "Clean Sweep" developer who keeps a pristine database, or do you lean heavily on &lt;code&gt;:nullify&lt;/code&gt; to protect business analytics? I’d love to hear how you handle the "afterlife" of your data in the comments!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy806dk56b318m3m1gs2m.gif" 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%2Fy806dk56b318m3m1gs2m.gif" alt="I will be back with more technical articles" width="480" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Quick Side Note:&lt;/strong&gt; While I’m heads-down building this search engine, I’m also looking to pitch in on new projects. If you’re looking for a mid-level Rails engineer with solid JavaScript experience who cares about building defensive, scalable systems, I’d love to chat. You can find me on [&lt;a href="https://www.linkedin.com/in/nemwel-nyandoro/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;/&lt;a href="https://x.com/nemwel_bonie" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;] or check out my other work &lt;a href="https://github.com/Nemwel-Boniface" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
      <category>database</category>
      <category>rails</category>
      <category>ruby</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Architecture Backwards: Engineering a Self-Defending System Before the UI Arrives</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Fri, 09 Jan 2026 07:28:31 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/architecture-backwards-engineering-a-self-defending-system-before-the-ui-arrives-4ohg</link>
      <guid>https://dev.to/nemwelboniface/architecture-backwards-engineering-a-self-defending-system-before-the-ui-arrives-4ohg</guid>
      <description>&lt;p&gt;Happy New Year!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjov1i4vokd7eje74ls4a.gif" 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%2Fjov1i4vokd7eje74ls4a.gif" alt="Happy new year as we aim to learn how to architect systems starting from the backend to the front end" width="500" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we kick off 2026, many of us are starting new projects or refining old ones. This year, I’ve decided to challenge the "industry standard" of &lt;a href="https://www.atlassian.com/agile/product-management/minimum-viable-product" rel="noopener noreferrer"&gt;MVP&lt;/a&gt; development. Instead of racing to build a login page, I’m spending my time in the trenches of the core engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the rush to launch a Minimum Viable Product (MVP), the standard advice is almost always: Start with the User. Build the login page, the profile, and the "Join Now" button. We often prioritise Authentication and Authorization, then move to the core logic, the actual reason the product exists, nearly as an afterthought.&lt;/p&gt;

&lt;p&gt;This approach works, and it’s the industry standard. But what if I tell you there’s a more resilient way to architect a system? One that isn't as glamorous or visually aesthetic, but is infinitely more stable?&lt;/p&gt;

&lt;p&gt;Lately, I’ve been building a new system where I’ve intentionally flipped the script. I am working backwards, starting with the "brain" of the engine rather than the face of the app. I am two weeks into development, and I haven't even touched a User module. Instead, I’ve spent my time building Self-Defence Mechanisms and &lt;a href="https://www.splunk.com/en_us/blog/learn/what-is-telemetry.html" rel="noopener noreferrer"&gt;Telemetry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2dihjcqvegkfeypu4zz.gif" 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%2Fe2dihjcqvegkfeypu4zz.gif" alt="Urging my readers to stay with me so that I can keep explaining to them why we need to architect systems starting from the backend to the front end" width="600" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hear me out, while authentication and authorisation in your system is vital, we have battle-tested gems like Devise and CanCanCan that can be dropped in later and will solve this for you in minutes. The core health of your system, however, cannot be "dropped in" as an afterthought and therefore needs a complete system architecture shift. Here is why the most scalable systems are built from the inside out using what I call the &lt;strong&gt;&lt;em&gt;Sensor &amp;amp; Suitcase&lt;/em&gt;&lt;/strong&gt; pattern, which I will introduce in the second section below.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The "Horizontal" Nature of Users
&lt;/h2&gt;

&lt;p&gt;When working with a modern Ruby on Rails application, the User resource can be treated as a horizontal layer. I want you to think of the User layer as the front door to your house. It’s how people get in, and it’s what they see first. But before I invite guests over, I want to make sure the foundation is solid and the electricity is wired correctly. Because Rails emphasises &lt;strong&gt;&lt;a href="https://rubyonrails.org/doctrine#convention-over-configuration" rel="noopener noreferrer"&gt;Convention over Configuration&lt;/a&gt;&lt;/strong&gt; architecture, adding a &lt;em&gt;user_id&lt;/em&gt; to a record later is often just a simple migration away. I have the expensive luxury of treating "Identity" as a pluggable module because the framework and the core language, Ruby, are &lt;a href="https://rubyonrails.org/doctrine" rel="noopener noreferrer"&gt;designed for developer happiness&lt;/a&gt;. I am actually happy working with this stack for the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg11n0v8rcu434m4kbcvn.gif" 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%2Fg11n0v8rcu434m4kbcvn.gif" alt="Adding the user resource is an easy plug in and this is why we need to architect systems starting from the backend to the front end" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, when we talk about the core engine, the part that actually does the work, this should be seen as vertical. If that engine is fragile, it won't matter how many users you have or how beautiful your login page is.&lt;/p&gt;

&lt;p&gt;If your core system cannot handle the "real world" problems that come with scale, such as &lt;strong&gt;race conditions, API rate limits, or cache stampedes&lt;/strong&gt;, the system will collapse under the weight of its own success.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv1285p59urr5uye51nq.gif" 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%2Fcv1285p59urr5uye51nq.gif" alt="The reason why I am describing this new way will start to make sense as I go on and it will slowly be clear why we need to architect systems starting from the backend to the front end" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 The Tortoise and the API
&lt;/h3&gt;

&lt;p&gt;This might feel like a re-invention of the story of the &lt;em&gt;Hare&lt;/em&gt; and the &lt;em&gt;Tortoise&lt;/em&gt;. The &lt;em&gt;"Hare"&lt;/em&gt; approach is to race toward a UI, a &lt;em&gt;"Join Now"&lt;/em&gt; button, and a beautifully designed user dashboard. It looks fast, but it’s tripping over every unhandled exception along the way.&lt;/p&gt;

&lt;p&gt;By building &lt;em&gt;"Backwards,"&lt;/em&gt; I am the &lt;em&gt;Tortoise&lt;/em&gt;. I am building a system that knows how to protect itself first. I am ensuring that when the &lt;em&gt;"Hare"&lt;/em&gt; (the User) finally arrives, the ground beneath them is solid, monitored, and reinforced.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Building the "Defensive By Design Perimeter."
&lt;/h2&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%2Fnq2o7qms7a6s0q6w07sj.gif" 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%2Fnq2o7qms7a6s0q6w07sj.gif" alt="Penguin gif showing layering bricks to symbolise the building phase. I hope to make sense as I go on and it will slowly be clear why we need to architect systems starting from the backend to the front end" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before a single user is able trigger any request through my new system, I needed to know that the system will protect its most expensive resource: &lt;strong&gt;External API Credits!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am making use of what is called the &lt;strong&gt;Sensor &amp;amp; Suitcase&lt;/strong&gt; pattern. By implementing the &lt;strong&gt;Sensor &amp;amp; Suitcase&lt;/strong&gt; pattern, we shift the architecture from being &lt;strong&gt;reactive&lt;/strong&gt; (fixing things when users complain) to &lt;strong&gt;proactive&lt;/strong&gt; (handling failures before the user even notices).&lt;/p&gt;

&lt;p&gt;Here is why this "Backwards" approach is a game-changer for your system:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Total Visibility (The Sensors):&lt;/strong&gt; Most developers fly blind until an error log hits their inbox. By using ActiveSupport::Notifications to broadcast every “heartbeat”, you create a "Black Box" recorder for your app. Remember that a heartbeat is any user-defined activity and can be something like a user requesting a resource from my system. This way, you aren't just guessing how the system performs; the system is telling you exactly where it feels strain in real-time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The following snippets of code and all that follow are  
# intentionally simplified to communicate architectural  
# intent rather than production-ready implementations.
# Implementation of the 'Sensor' within the service layer
def fetch_resource(query, location)
  # Wrapping the core logic in a 'Sensor' block
  ActiveSupport::Notifications.instrument('&amp;lt;Module name&amp;gt;::external_call', {
    query: query,
    location: location
  }) do |payload|
    result = &amp;lt;Module name&amp;gt;::Client.execute(query, location)

    # Enrich the sensor data with metadata for real-time telemetry
    payload[:status] = result.status
    payload[:duration_ms] = result.duration

    result
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. De-risking Through Isolation (The Suitcase):&lt;/strong&gt; External APIs are often the "chaos variables" of web development. By packing those risks into SolidQueue background jobs, you decouple your app’s success from an outsider's failure. If a third-party service hangs for 10 seconds, your user doesn't see a spinning loading icon; they see a finished task because your "Suitcase" is handling the heavy lifting in the background.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The 'Suitcase' - Isolating external volatility from the main request thread
class &amp;lt;Module name&amp;gt;::IngestJob &amp;lt; ApplicationJob
  queue_as :external_api

  # Defensive retries with exponential backoff to handle provider downtime
  retry_on &amp;lt;Module name&amp;gt;::ExternalError, wait: :exponentially_longer, attempts: 3

  def perform(signal_id)
    signal = &amp;lt;Module name&amp;gt;::Signal.find(signal_id)

    # Perform the 'heavy lifting' away from the user's eyes
    &amp;lt;Module name&amp;gt;::Ingestor.new(signal).call
  rescue StandardError =&amp;gt; e
    # Log the failure for the 'Black Box' recorder
    &amp;lt;Module name&amp;gt;::Telemetry.record_failure(e)
    raise e
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. The "Snappiness" Factor:&lt;/strong&gt; In a Rails 8 monolith, speed is often about what you don't do during the request-response cycle. This pattern strips the main thread down to its bare essentials, ensuring your UI stays lightning-fast while the complex logic executes safely behind the scenes.&lt;/p&gt;

&lt;p&gt;The Core Benefit here is that you aren't just building a feature; you are building a self-healing perimeter. Your system defends its own performance metrics, regardless of how the outside world (or the user) behaves.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Solving the "Thundering Herd" Problem
&lt;/h2&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%2F1m9s1v81ru26xzahxt6o.gif" 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%2F1m9s1v81ru26xzahxt6o.gif" alt="Gif showing an actual herd running. I hope to make sense as I go on and it will slowly be clear why we need to architect systems starting from the backend to the front end" width="480" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the most satisfying parts of this "Backwards" approach was preparing for a "Thundering Herd" before it ever happened. This refers to the moment when multiple requests hit your system at once for the same resource.&lt;/p&gt;

&lt;p&gt;Usually, this is where most systems start to show off their glamorously covered-up cracks. In a situation where 10 users request the same data at the exact same millisecond, a standard app might fire off 10 expensive, redundant calls to an external API.&lt;/p&gt;

&lt;p&gt;However, by focusing on the "Self-Defence" layer first, I solved this before a single user ever signed up on my system. I used &lt;strong&gt;atomic increments in Redis&lt;/strong&gt; and &lt;strong&gt;PostgreSQL row locking&lt;/strong&gt; to make the system smarter. Now, when those 10 requests hit at the same time, the system identifies the "herd" and performs one expensive API call, while the other nine requests simply wait for a heartbeat to share that single result.&lt;/p&gt;

&lt;p&gt;Instead of 10 separate stresses on the engine, it’s just one. This is the difference between a panicked crowd and an organised queue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Utilizing PostgreSQL Advisory Locks to organize the 'Herd'
def find_or_create_resource(query, location)
  # Create a unique lock key based on the specific search parameters
  lock_key = Digest::SHA256.hexdigest("#{query}:#{location}")

  # Ensure only ONE process handles the expensive external call
  &amp;lt;Module name&amp;gt;::Database.with_advisory_lock(lock_key) do
    # Re-check cache inside the lock; the first worker filled it already
    return cached_result if cached_result.present?

    # This is the single 'Tortoise' worker performing the expensive API trip
    execute_expensive_external_call(query, location)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. The Admin-First Dashboard
&lt;/h2&gt;

&lt;p&gt;Because I haven't spent a single minute working on a "User Profile" page, I had the time to build something much more valuable: a &lt;strong&gt;Telemetry Dashboard.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Right now, this dashboard isn't for a customer; it’s for the system (and, okay, it's for me to geek out on until I go live! xD). Instead of looking at user avatars or bios, I am looking at the "System’s Efficiency." I’ve built a metric I call the &lt;strong&gt;Vault Ratio&lt;/strong&gt;. This metric tracks how many requests are served from the "Vault" (my local, optimized data) versus how many require a slow, expensive trip to an external API.&lt;/p&gt;

&lt;p&gt;By seeing these numbers move in real-time, I can tune the engine while it’s still on the workbench. I’m not guessing if my caching strategy works; I’m watching the "Vault" grow stronger every day. By the time the first user signs up, they aren't entering a construction site; they are stepping into a fully furnished house.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The core logic behind the Telemetry Dashboard
def today_snapshot
  date = Date.current
  {
    discovery_requests:  &amp;lt;Module name&amp;gt;::Metrics.read('discovery.requests', date),
    cache_hits:          &amp;lt;Module name&amp;gt;::Metrics.read('discovery.cache_hits', date), # Local Rails cache
    vault_hits:          &amp;lt;Module name&amp;gt;::Metrics.read('discovery.vault_hits', date), # Persistent optimized data
    external_calls:      &amp;lt;Module name&amp;gt;::Metrics.read('discovery.external_calls', date), # Expensive credit usage
    signals_created:     &amp;lt;Module name&amp;gt;::Metrics.read('signals.created', date)
  }
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Lesson I have learned so far: Embrace the trenches
&lt;/h2&gt;

&lt;p&gt;In the past, I was always afraid of staying in the backend trenches for too long. There is a constant pressure to "show progress" through UI and buttons. But if there is one thing I’ve learned from systems that I didn’t architect well in the past, it’s this: &lt;strong&gt;Don’t be afraid to stay in the back-end longer than it feels comfortable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Remember the story of the tortoise and the hare. The slow, intentional pace you take now is what prevents the dreaded 3:00 AM headaches down the line.&lt;/p&gt;

&lt;p&gt;When you build an engine that is self-defending and observable from day one, adding the "User" front-facing layer isn't going to be a stressful launch; it will be just a normal Tuesday. This approach gives you the confidence to sleep soundly, whether your system is handling 10 users or 10,000 users concurrently.&lt;/p&gt;

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

&lt;p&gt;As a parting shot and a last word of advice from an engineer implementing best software practices that help global businesses thrive, to build software that helps businesses truly thrive, we have to prioritize the "brain" over the "face." Focus on your system’s core logic and bulletproof it first. Make it exist, make it resilient, and then make it pretty.&lt;/p&gt;

&lt;p&gt;The users might not see the Redis locks or the background workers, but they will certainly feel the stability.&lt;/p&gt;

&lt;p&gt;Is the "Sensor &amp;amp; Suitcase" pattern something you or your team would consider for your next project? I’d love to hear how you handle the "Defensive" side of your builds in the comments!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxq3fdu6jrufc7x7z3jn.gif" 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%2Fcxq3fdu6jrufc7x7z3jn.gif" alt="Architecture Backwards: Engineering a Self-Defending System Before the UI Arrives" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Quick Side Note:&lt;/strong&gt; While I’m heads-down building, I’m also looking to pitch in on new projects. If you’re looking for a mid-level Rails engineer with solid JavaScript experience who cares about building defensive, scalable systems, I’d love to chat. You can find me on [&lt;a href="https://www.linkedin.com/in/nemwel-nyandoro/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;/&lt;a href="https://x.com/nemwel_bonie" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;] or check out my other work &lt;a href="https://github.com/Nemwel-Boniface" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>architecture</category>
      <category>observability</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Building a Custom Audit Trail in Ruby on Rails Without PaperTrail</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Thu, 21 Aug 2025 11:14:21 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/building-a-custom-audit-trail-in-ruby-on-rails-without-papertrail-klk</link>
      <guid>https://dev.to/nemwelboniface/building-a-custom-audit-trail-in-ruby-on-rails-without-papertrail-klk</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Auditability in a system is a key feature, and having a way in which you can do that in your application will definitely help a lot. In your system, you need to know who did what, who created what, updated what, deleted what, and when this was done. Please note that for deletes, it is not a hard delete but a “soft” delete option whereby you have a boolean field maybe called “Archived,” which is set to false by default and set to true when you press to delete it (Archive it).&lt;/p&gt;

&lt;p&gt;While I know there exist gems that can do this for you, such as the &lt;a href="https://github.com/paper-trail-gem/paper_trail" rel="noopener noreferrer"&gt;papertrail&lt;/a&gt; gem, you may want to reduce the number of dependencies in your application by coming up with your own custom maker checker. This involves adding fields in your database table, such as created_by, deleted_by, modified_by, etc. Look, hear me out, this may sound counterintuitive to add a created_by field, but the aim of making a maker checker is to add all the needed fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ruby v 3+&lt;/li&gt;
&lt;li&gt;Rails V 7/8&lt;/li&gt;
&lt;li&gt;Rails app with Devise for Authentication&lt;/li&gt;
&lt;li&gt;Some experience with Rails MVC&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Let's get started:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx08nu3bbsshx74su0l9h.gif" 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%2Fx08nu3bbsshx74su0l9h.gif" alt="Cat getting started to build a custom audit system" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assume that you have a table called modules. To add the auditable fields, run the following migration to create a migration file that we will update below:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rails g migration AddAuditFieldsToModules&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Update your migration file to look like the one shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AddAuditFieldsToModules &amp;lt; ActiveRecord::Migration[7.2]
  def change
    change_table :modules do |t|
      # Audit fields
      t.references :created_by, type: :uuid, foreign_key: { to_table: :users }
      t.references :modified_by, type: :uuid, foreign_key: { to_table: :users }
      t.references :deleted_by, type: :uuid, foreign_key: { to_table: :users }
      t.datetime :deleted_on

      # Archive status
      t.boolean :archive_status, default: false, null: false

      # Indexes
      t.index :archive_status
      t.index :deleted_on
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;rails db:migrate&lt;/code&gt; to update your Schema file.&lt;/p&gt;

&lt;p&gt;The next step would be to create a Current model inside &lt;code&gt;app/models/current.rb&lt;/code&gt;, which inherits from &lt;code&gt;ActiveSupport::CurrentAttributes&lt;/code&gt; and update it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Current &amp;lt; ActiveSupport::CurrentAttributes
  attribute :user
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Current class file is needed to provide a thread-safe way to access the current user (or other request-specific attributes) throughout your application, particularly in models where you normally can't access controller-level information like the current_user devise helper method. By using ActiveSupport::CurrentAttributes, it creates a globally accessible but request-specific storage that your Auditable concern can use to automatically track who created, modified, or deleted (In our case, a delete is actually an archive) records. This maintains clean separation of concerns while ensuring your audit trail always captures the correct user, whether the action originates from controllers, background jobs, or other parts of your application. The Current pattern is the Rails-recommended way to handle this cross-cutting concern without polluting your models with controller logic.&lt;/p&gt;

&lt;p&gt;Our next step will be creating an auditable concern module inside &lt;code&gt;app/models/concerns/auditable.rb&lt;/code&gt; file and add the following code to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module Auditable
  extend ActiveSupport::Concern

  included do
    # Associations
    belongs_to :created_by, class_name: 'User', optional: true
    belongs_to :modified_by, class_name: 'User', optional: true
    belongs_to :deleted_by, class_name: 'User', optional: true

    # Scopes
    scope :active, -&amp;gt; { where(archive_status: false) }
    scope :archived, -&amp;gt; { where(archive_status: true) }

    # Callbacks
    before_create :set_created_by
    before_save :set_modified_by
  end

  # Instance method for archiving
  def archive!
    update(
      archive_status: true,
      deleted_by: Current.user,
      deleted_on: Time.current,
      modified_by: Current.user
    )
  end

  # Instance method for unarchiving
  def unarchive!
    update(archive_status: false, modified_by: Current.user)
  end

  private

  def set_created_by
    self.created_by ||= Current.user
    self.modified_by ||= Current.user
  end

  def set_modified_by
    return unless !new_record? &amp;amp;&amp;amp; Current.user
    self.modified_by = Current.user
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, ok, I know that this may seem like a lot, but we are mostly done now, you can relax.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feln2kljcdgerj1xsh9r5.gif" 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%2Feln2kljcdgerj1xsh9r5.gif" alt="Cat getting started to build a custom audit system" width="500" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Auditable&lt;/code&gt; concern provides automatic tracking of record authorship and changes through a complete audit trail system. It adds three user associations (created_by, modified_by, deleted_by) to any model that includes it, along with scopes for filtering active/archived records. Using Rails callbacks, it automatically stamps records with the current user (via Current.user) during creation, updates, and soft deletion (archiving). The concern also provides explicit archive! and unarchive! methods that handle soft deletion while maintaining referential integrity and a complete change history. This creates a self-contained audit system that tracks the complete lifecycle of records while keeping the implementation DRY and consistent across different models. The soft delete functionality preserves data by setting an archive_status flag rather than physically removing records.&lt;/p&gt;

&lt;p&gt;In case you may still need a way to perform a hard delete, you can extend the functionality by adding this to your &lt;code&gt;Auditable&lt;/code&gt; concern. Please note that this may be counterintuitive, as the record will no longer be there. So, setting the "deleted_on" or "deleted_by" will not be there as the record will be gone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Callbacks
    before_destroy :set_destroyed_by

  def set_destroyed_by
    self.deleted_on = Time.current
    self.deleted_by = Current.user
    self.archive_status = true # Archive when destroyed
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside our &lt;code&gt;module.rb&lt;/code&gt; model file, we shall add the following to first include the auditable module, including all its functionality, adding it into our module model class, and using the scope to create a query shortcut that automatically filters for non-archived records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Auditability concern
  include Auditable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our setup is mostly complete, and you do not need to make any changes to your controller logic and The only place you need to make changes is in your destroy action, as shown below, which instead of performing an actual hard delete, calls the archive method, which we defined in our auditable concern earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def destroy
    @module.archive! # Soft delete / archive
    respond_to do |format|
      format.html { redirect_to modules_url, notice: "The Module '#{@module.name}' was successfully deleted." }
      format.json { head :no_content }
    end
  end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;So, why are we re-inventing the wheel?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyjxx4psugkcn4sakupt.gif" 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%2Feyjxx4psugkcn4sakupt.gif" alt="Cat getting started to build a custom audit system" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, why would someone want to use this approach as opposed to using pre-existing gems such as &lt;a href="https://github.com/paper-trail-gem/paper_trail" rel="noopener noreferrer"&gt;PaperTrail&lt;/a&gt;?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Eliminating dependencies caused by using gems. While some gems are very useful, some may stop receiving updates, which may mean, further down the line, you may be forced to migrate to a custom auditability form or move to another gem&lt;/li&gt;
&lt;li&gt;It is in line with the native Rails way of doing things; you probably don't need those gems if you can implement it custom. Such a custom approach is generally faster than using &lt;a href="https://github.com/paper-trail-gem/paper_trail" rel="noopener noreferrer"&gt;PaperTrail&lt;/a&gt; by upto 2-3 times, reducing the amount of “magic” happening in your code.&lt;/li&gt;
&lt;li&gt;There is complete transparency of what is going on, what is being collected, and how everything works. This is unlike when using a gem, and there is some “magic’ that happens behind the scenes, and you do not fully understand how it works in the first place.&lt;/li&gt;
&lt;li&gt;To have control over the data you want to collect/ track in your system. While some gems are very useful for a quick fix for data collection, some may be designed to collect an excessive amount of data, which may not be needed and may definitely increase the database costs. Customization allows you to collect the least amount of data, only the ones that you need, and nothing more.&lt;/li&gt;
&lt;li&gt;This approach incorporates query efficiency in situations where you would like to know who did what; you just get it in the table columns instead of doing complex queries, making your application data load faster.&lt;/li&gt;
&lt;li&gt;For scenarios where you are using cloud storage, this will help you to avoid using cloud offerings such as AuditLog, which charges per event. This in place saves on costs for the company.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;When it is better to use existing solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8168a6llheallhv17605.gif" 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%2F8168a6llheallhv17605.gif" alt="Cat getting started to build a custom audit system" width="480" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the pros of a custom approach are undeniable, there exist scenarios where you may need to use pre-existing gems as opposed to a custom solution like ours:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It may be a regulatory Requirement for the company. For example, healthcare systems bound by HIPAA may require complete object versioning with cryptographic signing of all changes.&lt;/li&gt;
&lt;li&gt;In situations where you need to perform cross-model Search in your system. This occurs when you need to search audit trails globally.&lt;/li&gt;
&lt;li&gt;In a situation where you need a Full Change History in your system. Gems such as PaperTrail have reify for undo functionality&lt;/li&gt;
&lt;li&gt;When a separation of concerns is needed. This is where the production database and the audit database are in two separate databases, keeping everything neat and maintainable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Areas for Improvement (What Could Be Better)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While this approach is clean and avoids extra dependencies, there are several considerations to keep in mind if you want to make it production-ready:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sometimes your system performs certain tasks, such as background jobs, where in such scenarios, there usually isn’t a current_user value, and it will be nil, which would make auditing hard. In such situations, assigning such changes to a dedicated System user account when no explicit user is available would be a safe workaround. For example:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def set_created_by
    # Assign the system user if no current user is present
    user = Current.user || User.find_by(email: 'system@yourdomain.com')
    self.created_by ||= user
    self.modified_by ||= user
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;When working with Rails APIs and you are exposing some of the audit fields, such as created_by or modified_by,  directly through JSON APIs, you may unintentionally leak personal information (e.g., user IDs, emails). From a compliance standpoint, you may need to consider whether those fields should be public, anonymized, or restricted to admin roles.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Alternatives &amp;amp; Extensibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just as Rails has some best practices for using the framework effectively, below are my best practices rules, which I highly recommend considering when choosing to have a custom audit mechanism for your application, may it be a custom audit tooling or a ready-made software:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It is best to focus on capturing the changes, and not just actors: While I am aware that my current approach only records who changed a record, but not what they changed, I would recommend extending the functionalities with a changes_log JSON column or an associated audit_logs table that stores the changes made.&lt;/li&gt;
&lt;li&gt;Embracing database-level auditing: databases such as PostgreSQL offer native auditing solutions using triggers or extensions such as &lt;a href="https://www.pgaudit.org/" rel="noopener noreferrer"&gt;pg_audit&lt;/a&gt;, which are battle-tested solutions that can help you to log all operations at the database layer.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.geeksforgeeks.org/software-engineering/kiss-principle-in-software-development/" rel="noopener noreferrer"&gt;KISS&lt;/a&gt; (Keep it simple, stupid) and choose a hybrid approach of just recording “who/when” directly in your model (just as in my demonstrated examples), combined with a dedicated audit log table or external log system for mission-critical changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Auditing may feel daunting at first. However, knowing what amount of data you want to collect is the first step. Don’t sweat it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The choice ultimately depends on your application’s specific requirements. However, using the custom approach as I have demonstrated is an excellent fit for most mid-sized applications, prioritizing maintainability and performance.&lt;/p&gt;

&lt;p&gt;If you need a lightweight, Rails-native auditing functionality, this approach works perfectly well. However, if you need full versioning, support for cryptographic trails, or cross-model searching capabilities, it would be better to stick with PaperTrail or database-level solutions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxq3fdu6jrufc7x7z3jn.gif" 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%2Fcxq3fdu6jrufc7x7z3jn.gif" alt="This is the end of the Creating custom audit logs in Ruby on Rails" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Quick Side Note:&lt;/strong&gt; While I’m heads-down building, I’m also looking to pitch in on new projects. If you’re looking for a mid-level Rails engineer with solid JavaScript experience who cares about building defensive, scalable systems, I’d love to chat. You can find me on [&lt;a href="https://www.linkedin.com/in/nemwel-nyandoro/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;/&lt;a href="https://x.com/nemwel_bonie" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;] or check out my other work &lt;a href="https://github.com/Nemwel-Boniface" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>How to Automate Daily Task Emails in Rails using Whenever and Cron</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Wed, 16 Jul 2025 06:49:51 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/how-to-automate-daily-task-emails-in-rails-using-whenever-and-cron-724</link>
      <guid>https://dev.to/nemwelboniface/how-to-automate-daily-task-emails-in-rails-using-whenever-and-cron-724</guid>
      <description>&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have been working on a task assignment system where one of the tasks I need to do is to send everyone at the end of the work day stats of what tasks they had at the start of the work day and what tasks they have been able to close and if there are any new tasks that they were assigned during that work day. Initially, I created a manual method to send emails to them by using a button that I had to click at the end of each workday. This approach worked perfectly well. However, in situations where I was not there physically to click the button to send out the daily tasks reports to all users, this quickly became an issue for me, and coming up with a way to automate this process is no longer an option, but mandatory. This is where the Ruby Whenever gem comes in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdc99f9zvhxzizq8ns2eo.gif" 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%2Fdc99f9zvhxzizq8ns2eo.gif" alt="Finding solution for automating email sending with the Whenever Gem" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introduction: Whenever Gem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whenever Gem is a Ruby DSL(domain-specific language) for managing Unix-style cron jobs. It translates your Ruby syntax into actual crontab entries that your system can use and understand. As a result, it works reliably on Unix-based systems (Linux, macOS). Still, it is not compatible with Windows unless you use a Unix-like environment (such as WSL) or adapt your jobs to Windows Task Scheduler.&lt;/p&gt;

&lt;p&gt;The Gem provides a clear syntax for writing and deploying cron jobs. A cron job is a time-based job schedule that allows users to automate tasks by scheduling commands or scripts to run at specific times/ intervals. The scheduled tasks are known as cron jobs and are all defined in a configuration file known as the crontab. A cron is a daemon that runs in the background and checks the crontab file for scheduled tasks to run. Remember that a daemon is a computer program that runs in the background, performing tasks without direct user interaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are schedulers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Schedulers are components that trigger certain tasks at specific intervals (hourly, daily, weekly, etc). Schedulers operate in the background, outside of the request/ response lifecycle (You do not run them when a user visits your website).&lt;/p&gt;

&lt;p&gt;While Rails does not have a native (inbuilt) scheduler for arbitrary cron-style jobs, we have options for using Background jobs using ActiveJobs + Sidekiq. Or asynchronous work and making use of external scheduler tools (such as whenever + sidekiq-scheduler) for repeating jobs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rails V7&lt;/li&gt;
&lt;li&gt;PostgreSQL database&lt;/li&gt;
&lt;li&gt;Ruby V 3+&lt;/li&gt;
&lt;li&gt;Preferred web browser &lt;/li&gt;
&lt;li&gt;Visual studio code&lt;/li&gt;
&lt;li&gt;Some experience with Rails MVC architecture&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Installing the Whenever Gem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx08nu3bbsshx74su0l9h.gif" 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%2Fx08nu3bbsshx74su0l9h.gif" alt="Cat setting up Whenever Gem in the project" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Installing the &lt;a href="https://github.com/javan/whenever" rel="noopener noreferrer"&gt;whenever Gem&lt;/a&gt; and configuring it to work is fairly straightforward in Ruby on Rails. To install the whenever gem, add the following to your GEMFILE and run &lt;code&gt;bundle install&lt;/code&gt; to install the package to your application.: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem 'whenever', require: false&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Optionally run the following in your terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bundle add whenever&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then run the command &lt;code&gt;wheneverize .&lt;/code&gt; to create the &lt;code&gt;schedule.rb&lt;/code&gt; file inside the &lt;code&gt;config/&lt;/code&gt; directory. This step is very crucial as the &lt;code&gt;schedule.rb&lt;/code&gt; file is where you define your cron jobs using Ruby DSL syntax (e.g., every :day, at: '5:05pm'). Without this file, the &lt;a href="https://github.com/javan/whenever" rel="noopener noreferrer"&gt;whenever gem&lt;/a&gt; has no instructions to generate crontab entries.&lt;/p&gt;

&lt;p&gt;If you would like to follow along with me through the article, I created &lt;a href="https://github.com/Nemwel-Boniface/scheduler_app" rel="noopener noreferrer"&gt;this&lt;/a&gt; GitHub repository which you can clone to test out the setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real world practice: Sending out an email every weekday at 5:05 pm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7nq6t5sxuznktjscfjp.gif" 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%2Fr7nq6t5sxuznktjscfjp.gif" alt="Getting started with a Real world setup of Whenever Gem in the project" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a relatable situation where you may want to automate the process of sending out emails every workday at 5:05 pm. To do this, we will first create a mailer. In your terminal, head over and type:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rails g mailer DailyMailer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This generates a file in the &lt;code&gt;app/mailers/&lt;/code&gt; directory called &lt;code&gt;daily_mailer.rb&lt;/code&gt;, which we are going to open so that we can edit it by adding the following to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class DailyMailer &amp;lt; ApplicationMailer
  def daily_email
    mail (to: "user@gmail.com", subject: "My update"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step will be to create a view inside the &lt;code&gt;app/views/daily_mailer/&lt;/code&gt; directory called &lt;code&gt;daily_email.html.erb&lt;/code&gt; adding this line: &lt;code&gt;&amp;lt;h1&amp;gt;Hello! This is your daily update at 5:05 pm &amp;lt;/h1&amp;gt;&lt;/code&gt; to it.&lt;/p&gt;

&lt;p&gt;We shall then create a Rake task inside the &lt;code&gt;lib/tasks/&lt;/code&gt; creating the file called &lt;code&gt;email.rake&lt;/code&gt;, using the command &lt;code&gt;touch lib/tasks/email.rake&lt;/code&gt;, and open it to edit it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace :email do
  desc "Your daily email"
  task send_daily_email: :environment do:
    DailyMailer.daily-email.deliver_now
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We shall now run the command: &lt;code&gt;rails email:send_daily_email&lt;/code&gt;, to test out our current setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the Automation Flow Works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we test locally with Letter Opener, let’s visualize the full workflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9f8binbryyzsm1phngz.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%2Fl9f8binbryyzsm1phngz.png" alt="Flowchart showing the flow from generating the cron task to when the email is sent" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Key Steps showcased in the flowchart include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;schedule.rb&lt;/strong&gt; which defines the cron schedule in Ruby (e.g., "Weekdays at 5:05 PM").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;crontab&lt;/strong&gt; system converts this into a background process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rake Task&lt;/strong&gt; which executes your custom logic (e.g., querying tasks).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mailer&lt;/strong&gt; which formats and sends the email.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Letter Opener (Dev) / SMTP (Prod)&lt;/strong&gt; which handles delivery of your email.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Integrating the email send automation with the Whenever Gem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We are now going to ensure that after every work day at 5:05 pm, users can receive an email sent to them. Open the &lt;code&gt;config/schedule.rb&lt;/code&gt; file and inside it add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;every :week, at: '5:05 pm', on: [:monday, :tuesday, :wednesday, :thursday, :friday] do
  rake "email:send_daily_email"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to check the output, run the command &lt;code&gt;whenever&lt;/code&gt; in your terminal. In case nothing happens on your end, not to worry. It means that you do not have any SMTP server configured, and this is where we will resolve it in the next section of the article, the letter_opener gem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local testing using the letter_opener gem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In case you do not have an &lt;a href="https://aws.amazon.com/what-is/smtp/" rel="noopener noreferrer"&gt;email SMTP&lt;/a&gt; setup locally, we will use the &lt;a href="https://github.com/javan/whenever" rel="noopener noreferrer"&gt;letter_opener&lt;/a&gt; Gem that catches any email that we want to send. Instead of trying to connect to an SMTP server, it renders the email in our browser and is an amazing tool for testing, as it will show your email like an HTML page for you. This means that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We do not have to configure an email server&lt;/li&gt;
&lt;li&gt;We can see and test emails quickly&lt;/li&gt;
&lt;li&gt;We do not send any actual emails over the internet&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are following along upto here, right now when we try to send the email by connecting to &lt;code&gt;localhost:25&lt;/code&gt;, it fails because we do not have an SMTP server configured locally. But with the letter opener gem configured, when our app tries to send an email using the &lt;code&gt;mail()&lt;/code&gt; or &lt;code&gt;deliver_now()&lt;/code&gt;, letter opener intercepts it and saves it as an HTML page. Then it automatically opens the page in your browser for you. The letter opener gem inline allows you to test your mailer easily, with no error messages, no ECONNREFUSED, and you can review your email layout. Amazing right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use the letter opener gem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You will add the &lt;code&gt;gem 'letter_opener'&lt;/code&gt; to your Gemfile in the &lt;strong&gt;development group&lt;/strong&gt;, and run bundle install. Open the &lt;code&gt;config/environments/development.rb&lt;/code&gt; file and edit it to add the following configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :letter_opener
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false

# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false

config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These settings configure Action Mailer to actually send emails in development mode, preview them in the browser using the letter_opener Gem, suppress delivery errors, disable caching for email templates, and generate proper URLs using localhost:3000 as the base. Amazing right?&lt;/p&gt;

&lt;p&gt;Once this has been done, run: &lt;code&gt;rails email:send_daily_email&lt;/code&gt; in your terminal. If you have followed along so far, your default email browser will be opened, and you will see something similar to what I have below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27c6jztyepwjg4s3q0el.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%2F27c6jztyepwjg4s3q0el.png" alt="HTML output when you run rails email:send_daily_email" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Up to this point, it will load the email in the browser as expected. The application will no longer attempt to connect to &lt;code&gt;localhost:25&lt;/code&gt; (SMTP port) and will instead open your browser.&lt;/p&gt;

&lt;p&gt;Up to where we are, we have been able to test out our setup, and we all validated that it works perfectly fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u5bzcku8r62o4ct7hb1.gif" 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%2F0u5bzcku8r62o4ct7hb1.gif" alt="We have been able to solve the problem and we can now comfortably test our setup locally" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes about the letter_opener gem:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is not for production, it is a tool for development testing alone&lt;/li&gt;
&lt;li&gt;Helps you test and view emails quickly without sending them explicitly over an SMTP network&lt;/li&gt;
&lt;li&gt;Fixes the connection refused error for local testing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Considerations while using the whenever and the letter_opener gems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you update the schedule.rb file, and you run the command &lt;code&gt;whenever&lt;/code&gt;, Rails will automatically assume &lt;code&gt;RAILS_ENV = production&lt;/code&gt; by default. So, when you run &lt;code&gt;whenever&lt;/code&gt; without specifying any flags, it shows you the schedule tasks for production and not for development. This may mean that you are not going to see any output and you may think that your setup is all wrong. Luckily, we have a few work a rounds for this and below are some options to use when testing out in development mode. See attached screenshot below as an example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp70bzga8d6powulss7mk.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%2Fp70bzga8d6powulss7mk.png" alt="Running Whenever in production mode will just not work" width="800" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember that running the &lt;code&gt;whenever&lt;/code&gt; command is essential because it converts your human-readable schedule written in Ruby (in schedule.rb) into raw &lt;a href="https://www.liquidweb.com/help-docs/what-is-a-cron/" rel="noopener noreferrer"&gt;cron&lt;/a&gt; syntax that your system understands. Without this step, your defined jobs won’t actually get scheduled (We mentioned that whenever doesn't run jobs itself. It simply tells your operating system when and how to run them by updating the &lt;a href="https://www.techtarget.com/searchdatacenter/definition/crontab" rel="noopener noreferrer"&gt;crontab&lt;/a&gt;). If you skip this step, your background jobs will never run, no matter how well your Rake tasks or mailers are written.&lt;/p&gt;

&lt;p&gt;To fix this, we have a few options, starting with the temporary option, which is running the command shown below which will allow you to preview the schedule for development environment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;whenever --set environment=development&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The other solution will be:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;whenever --update-crontab --set environment=development&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To explicitly set the environment in the schedule.rb file at the top of the schedule.rb file, you will add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set :environment, "development"
set :output, "log/cron.log"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding this to our &lt;code&gt;schedule.rb&lt;/code&gt; file, we will need to run the command &lt;code&gt;whenever --update-crontab&lt;/code&gt; With the flag &lt;code&gt;--update-crontab&lt;/code&gt; because running &lt;code&gt;whenever&lt;/code&gt; alone simply previews what the generated cron entries would look like but it does not install them. To actually schedule your tasks, you must run &lt;code&gt;whenever --update-crontab&lt;/code&gt;, which writes those entries into your system's active crontab.&lt;/p&gt;

&lt;p&gt;To confirm whether this was successful run &lt;code&gt;crontab -l&lt;/code&gt; in your terminal. If you were s, you should see something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figszjulhcnjial06zeii.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%2Figszjulhcnjial06zeii.png" alt="Successful write cron entries" width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always remember that:&lt;/strong&gt; the whenever Gem does not send emails itself, it just tells your OS (using cron) to run the command at a specific time. In our case, we want it to run every time at 5:05 pm every workday. The cron service will run &lt;code&gt;RAILS_ENV=development bundle exec  rake email:send_daily_email&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflcn6n35bnczxyq23zgz.gif" 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%2Fflcn6n35bnczxyq23zgz.gif" alt="We are so cool for automating sending of emails" width="500" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Technology exists to help us work faster and focus on what truly matters. Let it handle repetitive, automatable tasks like sending daily emails at specific times so you can focus on building smarter systems.&lt;/p&gt;

&lt;p&gt;By using the &lt;code&gt;whenever&lt;/code&gt; gem together with &lt;code&gt;letter_opener&lt;/code&gt;, we’ve implemented a powerful and practical scheduling pattern. With this setup, we’ve removed human dependency from a critical workflow and learned how to safely test scheduled jobs in a development environment without the need for a live email server.&lt;/p&gt;

&lt;p&gt;This one marks the end of the step by step guide on automating tasks using Whenever Gem and Cron. What will you be automating next in your systems? I would love to hear about that. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxq3fdu6jrufc7x7z3jn.gif" 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%2Fcxq3fdu6jrufc7x7z3jn.gif" alt="This is the end of the automate tasks using Whenever Gem and Cron" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Stimulus.js Explained: A Beginner’s Guide for Ruby on Rails Developers</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Mon, 10 Feb 2025 07:26:10 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/stimulusjs-explained-a-beginners-guide-for-ruby-on-rails-developers-45mb</link>
      <guid>https://dev.to/nemwelboniface/stimulusjs-explained-a-beginners-guide-for-ruby-on-rails-developers-45mb</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hello friends!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fru2iii2laoal0wbz6hn3.gif" 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%2Fru2iii2laoal0wbz6hn3.gif" alt="Welcome to the first part of the Stimulus.js Explained: A Beginner’s Guide for Ruby on Rails Developers" width="498" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We all have to agree on one thing, JavaScript is the king of the web. It is very true that it is the undisputed language that powers over 98.7% of websites, making it the dominant language for web interactivity. You can get so far with plain HTML and CSS when working on your websites. You will however want to have some reactivity on your website. Imagine without JavaScript in your website, you may not be able to implement features such as changing content on the DOM.&lt;/p&gt;

&lt;p&gt;As Ruby on Rails developers, we often prefer server-side rendering for speed and SEO benefits and at all costs, we try to reduce the reliance of JavaScript on our applications and rely on server-side rendering of content and sending it to our views to be shown to the client side. This has multiple advantages as this approach allows for pages to load quickly and be indexed by search engines without requiring JavaScript to render content. This is however still limiting and thus need for JavaScript to add that pop to our application reactivity.&lt;/p&gt;

&lt;p&gt;This is where Stimulus.js comes in. It is a lightweight JavaScript framework that adds interactivity to HTML designed to enhance static or server-rendered HTML by connecting JavaScript objects to elements on the page using simple annotations. Let this not be confused with Turbo, Turbo focuses on enhancing client-side interactivity by handling page updates, while Stimulus is more focused on managing client-side behavior and adding interactivity to HTML elements.&lt;/p&gt;

&lt;p&gt;This article will give you a general overview of Stimulus, covering its features, explain why Stimulus is useful, especially in Ruby on Rails, and give a comparison with other libraries such as React or Jquery. In a future article, we will cover how to set up and use Stimulus in your Ruby on Rails applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stimulus JS deep dive&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjuc661xb4fddoe2tias.gif" 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%2Fyjuc661xb4fddoe2tias.gif" alt="It is time to get ouselves in the mud as we learn about Stimulus JS" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We earlier mentioned about JavaScript objects. In Stimulus, these JavaScript objects are called controllers. Stimulus uses &lt;code&gt;data-controller&lt;/code&gt; attributes to continuously monitor the pages. For each attribute, Stimulus looks at the attribute’s value to find a corresponding controller class, creates a new instance of that class, and connects it to the element. I would like you to think of it this way: just like the &lt;code&gt;class attribute&lt;/code&gt; is a bridge connecting HTML to CSS, Stimulus’s &lt;code&gt;data-controller&lt;/code&gt; attribute is a bridge connecting HTML to JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stimulus JS core concepts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rb2snyyzhlb0c5c5rlg.gif" 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%2F7rb2snyyzhlb0c5c5rlg.gif" alt="We have a few more concepts to cover about Stimulus JS" width="480" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stimulus JS builds on controllers with three key concepts: actions, targets, and values. Let’s discuss the further:&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Actions&lt;/strong&gt;, which connect controller methods to DOM events using &lt;code&gt;data-action&lt;/code&gt; attributes. Actions allow you to specify what action to take when eg a button is clicked. As an example, assume that you have a controller called &lt;code&gt;hello_controller&lt;/code&gt; that has a method called &lt;code&gt;greet&lt;/code&gt;, and you would like to connect that &lt;code&gt;greet&lt;/code&gt; method to an HTML button, your action would look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button data-action="click-&amp;gt;hello#greet"&amp;gt; Greet &amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where the data action is a click event and that maps it to the &lt;code&gt;greet&lt;/code&gt; method in the hello controller.&lt;/p&gt;

&lt;p&gt;2.&lt;strong&gt;Targets&lt;/strong&gt;, which locate elements of significance within a controller. Assuming that you have an input field and you would like when you click the button, the value that is in the input field will be captured and that value will be used somewhere else, this is where the &lt;code&gt;data-&amp;lt;controllerName&amp;gt;-target&lt;/code&gt; attribute comes in. For example, we have our input field, we may want to reference this value as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Our views
&amp;lt;div data-controller="greet"&amp;gt;
  &amp;lt;input type="text" data-greet-target="name" placeholder="Enter your name"&amp;gt;
  &amp;lt;button data-action="click-&amp;gt;greet#sayHello"&amp;gt;Say Hello&amp;lt;/button&amp;gt;
  &amp;lt;p data-greet-target="output"&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

// Hello controller
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["name", "output"];

  sayHello() {
    this.outputTarget.textContent = `Hello, ${this.nameTarget.value}!`;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code shown above, the Stimulus controller &lt;code&gt;"hello"&lt;/code&gt; defines the &lt;code&gt;nameTarget&lt;/code&gt; and the &lt;code&gt;outputTarget&lt;/code&gt; to reference the HTML input and paragraph. It then updates the paragraph with a greeting message when the button is clicked.&lt;/p&gt;

&lt;p&gt;3.&lt;strong&gt;Values&lt;/strong&gt;, allow you to store and update dynamic data within a Stimulus controller using the &lt;code&gt;data-*&lt;/code&gt; attributes. They automatically sync between the HTML and JavaScript, making it easy to manage dynamic data inside Stimulus controllers without manually handling DOM updates. A good example is using values to create a counter that increments or decrements when a button is clicked.&lt;/p&gt;

&lt;p&gt;Phew! That was almost a lot of technical content on Stimulus JS, right? Ok, let's take it easier now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdg8z1odyx1m4s3mylwpk.gif" 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%2Fdg8z1odyx1m4s3mylwpk.gif" alt="Phew! That was almost a lot of technical content right?" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why anyone should be using Stimulus.js in their applications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqixzaf5w0d6vj7ye6r48.gif" 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%2Fqixzaf5w0d6vj7ye6r48.gif" alt="I know you are already asking yourself why you need Stimulus JS in your applications" width="480" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have so far seen what Stimulus is, and had an understanding of its core features. But why should you want to use it anyway? Let’s explore more below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Can help you with basic interaction enhancement. Take an example of when you want to implement a toggle button functionality in your website.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Form input handling. A great example is the sample I shared earlier when covering the stimulus js concepts (Actions, targets, values).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fast and lightweight. As compared to similar libraries such as React, it is lightweight and minimal making sure that your application does not experience any lags due to heavy packages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It has a seamless integration with Ruby on rails. Stimulus is designed to easily work with Ruby on Rails, especially alongside Turbo, and does not affect the existing backend structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Has automatic element identification - uses data-targets to reference elements within a controller, reducing the need for manual &lt;code&gt;document.querySelector&lt;/code&gt; calls and keeping JavaScript neatly organized.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stimulus allows for progressive enhancement of your web applications' client views. It works on top of server-rendered pages, thus ensuring that the core functionality remains intact even if JavaScript is disabled, This improves accessibility and SEO of the web pages.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Despite using Stimulus.js showing real tangible benefits to our applications, they also have some serious considerations that are worth factoring in when using Stimulus over other libraries such as React or Vue.&lt;/p&gt;

&lt;p&gt;Stimulus may not be well suited for applications that are very complex and need single page (SPA) functionalities. If your app requires complex state management, virtual DOM updates, or client-side routing, you may need a framework like React or Vue.&lt;/p&gt;

&lt;p&gt;The fact that Stimulus is tightly coupled with HTML and relies heavily on the &lt;code&gt;data-*&lt;/code&gt; attributes in HTML, leads to having the code become cluttered if not managed properly. This is especially true in large applications.&lt;/p&gt;

&lt;p&gt;While this might not be necessarily true for everyone, however, the syntax that Stimulus uses is not really intuitive at first glance and requires additional reading to try and have a grasp of the syntax to understand the moving parts. This extra layer of complexity might not really suit everyone and they may find it hard to start working on it quickly.&lt;/p&gt;

&lt;p&gt;One of the determining factors of the use of a tool is usually the community behind it. Unlike other libraries such as React, Stimulus has a relatively smaller community backing it which would bring a challenge in finding resources or even help when you run into a challenge. You however need not worry because I am here to bridge that gap! 😄&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just like any tool out there, it has its strengths and its weaknesses. In my own opinion, Stimulus is an amazing tool that I believe everybody should be using especially in their Rails applications to add some reactivity to their pages. Stimulus features such as reusable components make it an attractive tool allowing you to define a feature once and reuse it multiple times allowing you to stick to programming principles such as DRY and KISS. &lt;/p&gt;

&lt;p&gt;In my next article, we shall go deeper into the technical bits covering a practical demonstration of how to use Stimulus JS in your Ruby on Rails applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy806dk56b318m3m1gs2m.gif" 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%2Fy806dk56b318m3m1gs2m.gif" alt="I will be back with the part two of this technical article series" width="480" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That wraps up this introduction to Stimulus. I hope you found it insightful, and I look forward to sharing a hands-on guide in the next article.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>stimulus</category>
      <category>ruby</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Mastering Code Reviews: The Ultimate How-To Series</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Mon, 28 Aug 2023 10:43:29 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/mastering-code-reviews-the-ultimate-how-to-series-11e1</link>
      <guid>https://dev.to/nemwelboniface/mastering-code-reviews-the-ultimate-how-to-series-11e1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hello friends!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fru2iii2laoal0wbz6hn3.gif" 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%2Fru2iii2laoal0wbz6hn3.gif" alt="Welcome to the first part of the Mastering Code Reviews: The Ultimate How-To Series" width="498" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Picture this, assigned a certain amount of features to implement. how will you know that the code submitted by them is functioning according to the given project requirements?  You check it! This is where the code review process kicks in.&lt;/p&gt;

&lt;p&gt;This will be a multiple series where I aim to teach you all you need to know about code reviewing. In this article, I will teach you what we mean by code review, why code review is important, what are the qualities of a good code review, what makes up a good code reviewer, list down some drawbacks associated with the code review process, and wrap it up with a conclusion. In a later article, I will do a hands-on tutorial on how to make use of the GitHub review features available to perform a code review.&lt;/p&gt;

&lt;p&gt;At the end of the series, you will be more equipped to start making reviews for your peers and add code reviewing as a skill under your belt.&lt;/p&gt;

&lt;p&gt;Sounds interesting right? Let's dive straight in!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do we mean by Code review?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Code review in software engineering means the systematic, collaborative, and iterative process in which one or more developers examine code written by team members scanning for errors in the code to assess whether that submitted code does exactly what it is supposed to do as per the provided project requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9osfm6p0pusndlor50c.gif" 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%2Fu9osfm6p0pusndlor50c.gif" alt="Wondering what we mean by the code review process" width="300" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code review is collaborative since it involves at least two people, the author of the code and another person who reviews the code. It is systematic since to perform a meaningful code review, there needs to be a set standard followed to approach the code review process. It is also iterative since if there are errors found, the code will be sent back for revisions and when done put back into review. This process is repeated until the code satisfies the provided project requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is the code review process important?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Code review is one of the most important steps in the software development lifecycle. So what are the advantages that you will accrue from doing code reviews?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For your junior developers in your team, code review facilitates the transfer of knowledge and helps them to learn faster. This is because as they collaboratively receive feedback on their code from their peers or the seniors, they can learn new ways of doing something or why implementing features differently is better.&lt;/li&gt;
&lt;li&gt;It serves as a bug detection avenue that might have been missed by the written tests for the project. This is because the code reviewer will generally be someone who is knowledgeable and has some understanding of the future implications of not re-writing a code block and rectifying it during the code review process.&lt;/li&gt;
&lt;li&gt;We often have the "Make it work optimize later". Code review helps to eliminate code that is unreadable or unnecessarily complex making it unmaintainable. It is here where that complex code will be rewritten when you are provided with suggestions of how to better approach an issue. This will in turn make the code readerable and maintainable.&lt;/li&gt;
&lt;li&gt;Code review ensures consistency of the whole code base. This is because different organizations have different ways in which they structure their code. This is always enforced during the code review process where any code that is not up to the company's standard is returned to the author and changes are required.&lt;/li&gt;
&lt;li&gt;Code review enhances the interaction and collaboration of the different team members. This is because different people might be the ones to review your code and in this way, you get to interact with different people within the organization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What are the qualities of a good code review?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have to acknowledge that anyone can give a code review. However, we have to ask ourselves, was the review given of quality? Below are some of the pointers for a good code review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One that is provided promptly. It would not be good if you had to wait for a long time to receive a review of your code. Remember that time is of the essence when creating software and you would be required to also start working on another feature which requires that this feature be approved.&lt;/li&gt;
&lt;li&gt;One that is thorough and does not miss some of the important aspects. It would hurt if you received few changes to make in the first review and when you submit your code for a re-review, the code reviewer introduces new issues that were not mentioned in the first review.
One that is constructive and does not deviate from the project requirements. The reviewer needs to raise issues in the code that are actionable and not guessed issues.&lt;/li&gt;
&lt;li&gt;One that uses respective language and tone and acknowledges your efforts so far. We all make mistakes and the best you can do is to be respectful to someone when you are suggesting changes. This would make the one who is to make the changes respected and acknowledged and will make the changes with enthusiasm.&lt;/li&gt;
&lt;li&gt;One that offers a learning opportunity. This depends on the kind of changes that will be requested and the approach that will be used by the reviewer which will either help the junior developer learn by offering constructive feedback or break them by adding unrealistic changes to be made.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What makes up a good code reviewer?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can all see why code reviews are very important and by now, you might be itching to get your hands dirty reviewing your friend's code. But wait! Do you have the qualities required to become a good code reviewer?  Let's see if you possess any of the below qualities. A good code reviewer is one who:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Has strong communication skills. Remember that when performing a code review, you will be explaining to someone, "This code is great, but it has some issues. Here is a suggested way to fix it, and here is why you should consider making this change".&lt;/li&gt;
&lt;li&gt;Has a strong technical understanding of the underlying technologies being used. You cannot make a code review for something that you do not understand and this is why to become a good code reviewer, you need to have mastery of the underlying technologies to be used.&lt;/li&gt;
&lt;li&gt;Is empathetic and patient. The truth is making changes to your code is hard. Most of the time, the author might not be able to implement exactly how it is supposed to be implemented and this is where being empathetic and patient comes into play. You will be better able to accommodate everyone even the least experienced developers in your team.&lt;/li&gt;
&lt;li&gt;Is one who can think critically. Making a code review involves assessing the code. It is up to you as the code reviewer to assess whether this submitted code is optimum or might require changes. This is where the skill of critical thinking comes into play.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What are some drawbacks associated with the code review process?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While it is evident that the code review process does have many advantages, it is not always bliss, and like everything, there are drawbacks related to performing code reviews. They include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sometimes the code review process can be very time-consuming. This is based on the fact that there could be multiple revisions which take time to both implement changes and re-review.&lt;/li&gt;
&lt;li&gt;The code review process could lead to conflicts between team members. When one of the team members feels that the other party is intentionally requesting changes even though the project works perfectly fine that could lead to a lot of conflicts between team members.&lt;/li&gt;
&lt;li&gt;Sometimes the code review process could be subjective and might not be objective. Remember that the code review process relies on what someone thinks about a specific code and might not be as objective as someone would want it to be.&lt;/li&gt;
&lt;li&gt;With the possibility of making numerous revisions of the code, this would significantly slow down the development lifecycle making it take longer to implement features than they should have taken. This has a direct effect on cost since the longer a project takes to be completed the higher the cost involved in its development.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Conclusion.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From my discussion above, we can see why code reviews are important. We also flipped the other side of the coin and saw the cons that come with the code review process. Would you be willing to do a code review for your peer or suggest it to your team members? Please let me know in the comments section bel&lt;/p&gt;

&lt;p&gt;This brings us to the end of the first part of this series. After reading this article, are you able to define what code review is, what are the advantages of performing code reviews, how you can distinguish whether the code review you received was good or not, and for those who are already excited about code reviews, the qualities that would help make them good code reviewers?&lt;/p&gt;

&lt;p&gt;Part B of this series will include a practical tutorial teaching you how to perform reviews making use of the GitHub review features. Stay tuned! &lt;/p&gt;

&lt;p&gt;If you read till this point, I send a big thank you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpipqyqtwsp4vunl5ysz1.gif" 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%2Fpipqyqtwsp4vunl5ysz1.gif" alt="I thank my readers for reading this far" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is it for today's class. I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Mastering Technical Writing: A Step-by-Step Guide for Beginners</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Sun, 18 Jun 2023 16:43:03 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/mastering-technical-writing-a-step-by-step-guide-for-beginners-o7h</link>
      <guid>https://dev.to/nemwelboniface/mastering-technical-writing-a-step-by-step-guide-for-beginners-o7h</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hello my esteemed reader! This article is well suited for beginners in technical writing but will still benefit seasonal writers who would like to take their writing skills to the next level. This will be a multiple series where I aim to take you from point A where you know nothing about technical writing to point Z where you write meaningful articles that people actually want to read.&lt;/p&gt;

&lt;p&gt;In this short article, I will explain what technical articles are, discuss the characteristics of a good article, extensively explore steps that you can take to write a good article, and conclude with a summary as I answer the question, "are technical articles limited to software related articles alone?".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do we mean by "Technical articles"?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Technical writing. A huge word that most people get wrong and only associate with software-related articles and publications. However, today I am here to demystify this misconception and explain to you my reader why technical writing is not restricted to software-related articles alone. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiodvkt8axrnouqsegejo.gif" 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%2Fiodvkt8axrnouqsegejo.gif" alt="Woman wondering what Technical writing means" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For us to understand what this is, we need to have an understanding of what the two terms technical and writing mean. Technical means "relating to a particular subject, art, or craft, or its techniques." anywhere mentioning software? no. The term Writing encompasses the process of documenting ideas, thoughts, or facts. Therefore combining the two, technical writing is the process of conveying complex and specialized information clearly and concisely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the indicators of a good article?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you ever read an article and something about it spelled out quality? Well, quality is one subjective term. However, in technical writing, we have standardized indicators of what a quality article is that I will list below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An article with a purpose and has a target audience. This is an article that clearly defines the scope of the specific article that is being written and has an identified audience beforehand.&lt;/li&gt;
&lt;li&gt;An article that combines the written text with visual aids such as images, gifs, and screenshots to further explain a specific concept shedding more light on it by using visuals to arouse more interest.&lt;/li&gt;
&lt;li&gt;An article that makes use of relatable examples and analogies. Research has it that when hard concepts are accompanied with an analogy that the readers can relate to, they end up remembering that concept by relating it to the analogy that was used to explain it.&lt;/li&gt;
&lt;li&gt;An article that is consistent throughout and has accurate information. Nothing hurts more than an article whose flow seems inconsistent and whose content is misleading.&lt;/li&gt;
&lt;li&gt;An article that has been proofread and is free of grammatical and spelling errors.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What are the steps to follow when writing an article?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Anyone can write an article. However, not everyone is able to write a good article. Luckily enough with the right preparation, we can all be able to write resourceful artiles and this will be what will separate your article from the vast majority of sub standard articles published online following the steps highlighted below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Preparation - they say, given 10 hours to cut down a tree, use 7 hours of that time sharpening your axe and 3 hours to cut it down. This is the step where you get to understand what you are going to write it and why you actually need to write it. It is in this stage where you choose the medium which you will use to relay your thoughts through your article.&lt;/li&gt;
&lt;li&gt;Research - before you even start to write about an article, you need to get an understanding of the topic at hand and do an assessment of whether this is something that you are capable of writing about. Here is where you get your hands dirty busy going deep that topic/ domain to get an understanding of it before writing about it.&lt;/li&gt;
&lt;li&gt;Organization - with extensive research, comes a monolith of data that will not make sense as information. You need to sit back, organize your thoughts and see what bits of all this raw data combined will lead to a meaningful information for your article.&lt;/li&gt;
&lt;li&gt;Once you have your thoughts organized, Writing the article is the next logical step. It is in this step where you jot down your thoughts either on paper or on an online editor that offers grammar and spelling checks such as Grammarly.&lt;/li&gt;
&lt;li&gt;The last and final step is to proofread your work. In this step I advice sitting on your article for a few days and asking a friend to proof check your work  and this will allow you to have a third eye/ opinion that will help you see if your article has a good thought/ logical flow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From the steps listed above, it is evident that technical articles are not restricted to software related articles alone. A technical article can be any article written for a specific domain, be it hospitality, engineering or medicine all qualify as technical articles. Therefore when someone says that other articles are not technical articles, please direct them to this article where they will also get enlightened.&lt;/p&gt;

&lt;p&gt;This brings us to the end of the first part of this series. After reading this article, are you able to recognize a well researched and written article? Please do let me know in the comment section. In the next article, I shall walk you through how you can develop an appropriate writing style in technical writing and how you can structure your article. &lt;/p&gt;

&lt;p&gt;If you read till this point, I send a big thank you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpipqyqtwsp4vunl5ysz1.gif" 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%2Fpipqyqtwsp4vunl5ysz1.gif" alt="I thank my readers for reading this far" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is it for today's class. I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Implementing Custom Slugs with FriendlyID in Rails: A Step-by-Step Technical Guide</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Mon, 29 May 2023 07:38:21 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/implementing-custom-slugs-with-friendlyid-in-rails-a-step-by-step-technical-guide-4jme</link>
      <guid>https://dev.to/nemwelboniface/implementing-custom-slugs-with-friendlyid-in-rails-a-step-by-step-technical-guide-4jme</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you ever wondered how applications like Instagram or Twitter use your username as the unique identifier in your browser URL and not the ID of your account?&lt;/p&gt;

&lt;p&gt;In the development of applications, the use of eg usernames instead of the ID is called slugging. In programming, slugs are the human readable unique identifying parts of a web address typically at the end of the URL (&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Web_mechanics/What_is_a_URL" rel="noopener noreferrer"&gt;Uniform Resource Locator&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Using custom slugs for our applications offers many advantages that I will discuss later in the article. Implementing them in our applications is relatively easy and luckily for us who use Ruby on Rails, we get the advantages of using a mature stable framework where many functionalities have solutions that were already implemented for us.&lt;/p&gt;

&lt;p&gt;In Ruby on Rails, we have a gem called &lt;a href="https://github.com/norman/friendly_id" rel="noopener noreferrer"&gt;friendly_id&lt;/a&gt; that makes the process of generating human-readable slugs simple by allowing us to replace ids in our URLs with slugs. Using &lt;a href="https://github.com/norman/friendly_id" rel="noopener noreferrer"&gt;friendly_id&lt;/a&gt; gem lets our applications work with friendly URLs eg &lt;code&gt;https://www.my_app/users/nemwelboniface&lt;/code&gt; instead of &lt;code&gt;https://www.my_app/users/1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Typically, the implementation of custom slugs in Rails involves leveraging gems like &lt;a href="https://github.com/norman/friendly_id" rel="noopener noreferrer"&gt;FriendlyID&lt;/a&gt; that streamline the process and offer additional functionalities like resolving slug conflicts and automatically generating easily readable slugs.&lt;/p&gt;

&lt;p&gt;Within this article, we shall delve into a comprehensive step-by-step walk through that demonstrates how to implement custom slugs in Rails with the assistance of the &lt;a href="https://github.com/norman/friendly_id" rel="noopener noreferrer"&gt;FriendlyID&lt;/a&gt; gem. My guide will encompass essential aspects such as installation, configuration, migration, and code generation, enabling a seamless integration of custom slugs into your Rails application. By the conclusion of this tutorial, you will possess the necessary knowledge and tools to craft personalized and user-friendly URLs tailored to meet your specific needs.&lt;/p&gt;

&lt;p&gt;Let's dive in and unlock the power of custom slugs in Rails!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rails V7&lt;/li&gt;
&lt;li&gt;PostgreSQL database&lt;/li&gt;
&lt;li&gt;Ruby V 3+&lt;/li&gt;
&lt;li&gt;Visual studio code&lt;/li&gt;
&lt;li&gt;Some experience with Rails MVC architecture.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Project set-Up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx08nu3bbsshx74su0l9h.gif" 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%2Fx08nu3bbsshx74su0l9h.gif" alt="Cat setting up friendly_id for custom slugs in the project" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To set up the custom slugs functionality in our application, we need to add the &lt;a href="https://github.com/norman/friendly_id" rel="noopener noreferrer"&gt;friendly_id&lt;/a&gt; gem to our Gemfile and run bundle install.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem "friendly_id"
bundle install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you would like to follow along, I created a new branch called &lt;code&gt;friendly_id&lt;/code&gt; in &lt;a href="https://github.com/Nemwel-Boniface/activestorage_tutorial" rel="noopener noreferrer"&gt;this repository&lt;/a&gt; where you can follow along with me. If you would like to know how to create a new Rails application, kindly have a look at the first part of &lt;a href="https://dev.to/nemwelboniface/from-theory-to-practice-using-active-storage-for-file-management-in-rails-4o3f"&gt;this article&lt;/a&gt; I wrote.&lt;/p&gt;

&lt;p&gt;Our next step will involve adding a slug column to our desired table. In our case, it will be the Users table. Therefore we shall run the following line in our terminal which will create a migration file that adds a slug column to our Users table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails g migration AddSlugToUsers slug:uniq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We shall also need to generate the friendly configuration file and a new migration with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails generate friendly_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are now free to run the migrations to update the schema file with the new columns added by the migration files created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are going to head over to our Users model inside app/models/user.rb file and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User &amp;lt; ApplicationRecord
  extend FriendlyId
  friendly_id :name, use: :slugged
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the line for "extend FriendlyId" we are extending the functionalities of the User model with the methods that are provided by the FriendlyId module. This is what allows us to use the "friendly_id" method in the next line.&lt;/p&gt;

&lt;p&gt;With the line "friendly_id :name, use: :slugged" this is the line that configures the friendly_id gem for the user model. This is where we specify that the name attribute of the user model should be used as the basis for generating the slugs. The :slugged option indicates that the friendly_id gem should use the slugged module to generate the slug.&lt;/p&gt;

&lt;p&gt;Making use of the above means that when a new user record is being created, the friendly_id gem will generate a slug based on the name attribute. The slug is then stored inside the slug column in our user's table.&lt;/p&gt;

&lt;p&gt;In addition to the above, let us head back to our user's model inside app/models/user.rb file and create a to_param method. Our User model shall now be like this with all the changes made:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User &amp;lt; ApplicationRecord
  extend FriendlyId
  friendly_id :name, use: :slugged

  def to_param
    slug
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method is necessary when using the friendly_id gem to ensure that our slug is used in the URL instead of the default ID. What it does is overrides the default behavior of Rails when generating URL's for model instances.&lt;/p&gt;

&lt;p&gt;By defining the to_param method and returning the slug value, you are instructing Rails to use the slug attribute when generating the URL's for instances of our model.&lt;/p&gt;

&lt;p&gt;We are almost done now. We need to make some slight adjustments to our controllers. Head over to our users controller in app/controllers/users_controller.rb file and inside the show action and set_user method, replace this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@user = User.find(params[:id])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@user = User.friendly.find(params[:id])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is happening here is we are using the friendly_id gem to find a user's record based on its slug. The params[:id] value is passed in as the argument to the find method. Basically using the friendly.find, the user record will be found based on its slug attribute instead of the ID which is the default behavior of &lt;a href="https://guides.rubyonrails.org/active_record_basics.html" rel="noopener noreferrer"&gt;ActiveRecord&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The last step involves running the following in our Rails console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User.find_each(&amp;amp;:save)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F1t5l00omfuuxhhpvl4wt.gif" 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%2F1t5l00omfuuxhhpvl4wt.gif" alt="Note to when you need to run the line above" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that running the above line is only necessary if you're adding friendly_id to an already existing Rails application and you need to generate slugs for already existing users.&lt;/p&gt;

&lt;p&gt;What that line does is it iterates over each user's record and calls the save method on them. This triggers the saving process and generation of slugs for each user based on the friendly_id configuration in our user model.&lt;/p&gt;

&lt;p&gt;This step is not necessary if you setup friendly_id before creating users in your application. If you find having to run the line in your console application tedius, you have an option of creating a &lt;a href="https://www.jetbrains.com/help/ruby/rake.html" rel="noopener noreferrer"&gt;rake task&lt;/a&gt; that can be used to automate that process for you. However, this is beyond the scope of what we were to learn in this article. I will however consider writing an article about rake tasks upon request from someone in the comments section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjd42tsqhy1gpwdrwmx2.gif" 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%2Frjd42tsqhy1gpwdrwmx2.gif" alt="That's all you need to create custom slugs!" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros and cons of using custom slugs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have covered the technical setup and I believe that so far you have an idea of why having custom slugs in your application is a good thing. Below I will list a few advantages that I have found from using custom slugs in my own applications:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It has helped me to improve the SEO of my applications ten fold as search engines tend to give more weight to keywords in the URL when determining the relevance of a page.&lt;/li&gt;
&lt;li&gt;Using custom slugs gives you a human readable URL that gives more context and describes what thet specific webpage you are viewing contains. This rings true especially when creating blogs where you can have th title of a blog as the slug for a blogs show page.&lt;/li&gt;
&lt;li&gt;Using custom slugs has given my applications alot of flexibility as the slugs are more flexible and meaningful than numeric  ID's. Having keywords in the slugs has really helped users of my applications to identify and remember the contents of webpages that they visited from my application.&lt;/li&gt;
&lt;li&gt;Persistence and a sense of security in my application as unline numerical IDs are sequential and predictable by revealing th order in which records were created, having the custom slugs give me a sense of security as they remain consistent even if the record is moved or re-ordered in the database.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Despite using slugs showing real tangible benefits to our applications, they also have some serious considerations which are worth factoring when using custom slugs over the default ID's. You need to ensure that the slugs are unique, they can handle situations where the title or the attribute that is used for the slug is changed and also properly handling URL redirections when slugs are modified.&lt;/p&gt;

&lt;p&gt;Luckily for us, in Rails all the above concerns are already handled by the &lt;a href="https://github.com/norman/friendly_id" rel="noopener noreferrer"&gt;friendly_id&lt;/a&gt; gem where the process of handling these edge case is simplified. This is one of the many advantages we get from using a mature and stable framework like Ruby on Rails for development of our applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Would you like to use friendly_id in your applications after reading my short article? Please let me know in the comments section below.&lt;/p&gt;

&lt;p&gt;This one marks the end of the Step-by-Step Guide: Implementing Custom Slugs with FriendlyId in Rails article guide.&lt;/p&gt;

&lt;p&gt;That is it for today's class. I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>From Theory to Practice: Using Active Storage for File Management in Rails</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Thu, 04 May 2023 09:31:05 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/from-theory-to-practice-using-active-storage-for-file-management-in-rails-4o3f</link>
      <guid>https://dev.to/nemwelboniface/from-theory-to-practice-using-active-storage-for-file-management-in-rails-4o3f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Welcome all! As we learned in my &lt;a href="https://dev.to/nemwelboniface/the-ultimate-guide-to-active-storage-in-rails-2f57"&gt;previous article&lt;/a&gt;, Active Storage is an inbuilt library in Ruby on Rails that simplifies the process of file uploads in our applications.&lt;/p&gt;

&lt;p&gt;The fact that it is an inbuilt library means that we do not have to add the active storage gem in our Gemfile to use it, rather we can directly out of the box start to use it.&lt;/p&gt;

&lt;p&gt;This is the part B of my Ultimate Guide to Active Storage in Rails series where I aim to teach you all that is required to know about Active Storage. In this part of the series, I aim to walk you through the technical setup of Active Storage and how to use it in our applications. As a bonus, at the end I will show you how you can write your own custom validations to limit the file size of an uploaded file. Part A of the series can be found &lt;a href="https://dev.to/nemwelboniface/the-ultimate-guide-to-active-storage-in-rails-2f57"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rails V7&lt;/li&gt;
&lt;li&gt;PostgreSQL database&lt;/li&gt;
&lt;li&gt;Ruby V 3+&lt;/li&gt;
&lt;li&gt;Visual studio code&lt;/li&gt;
&lt;li&gt;Some experience with Rails MVC architecture.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you would like to follow along with me, I created a GitHub repository for this specific tutorial which has all the code that will be listed here. You can find the GitHub repository &lt;a href="https://github.com/Nemwel-Boniface/activestorage_tutorial" rel="noopener noreferrer"&gt;here&lt;/a&gt; and the branch name with the code is "activestorage". If you like, give me a follow!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx08nu3bbsshx74su0l9h.gif" 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%2Fx08nu3bbsshx74su0l9h.gif" alt="Cat setting up active storage in the project" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get started, we are going to create a new rails project which will use PostgreSQL as our database. (PostgreSQL is optional and you can use whichever RDMS you wish to use.) In your terminal, type and hit enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails new activestorage_tutorial --database=postgresql
cd activestorage_tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create a new rails application for us with all the dependencies required to have a fully functional fullstack application in minutes, configured to use PostgreSQL as its database.&lt;/p&gt;

&lt;p&gt;Once completed, you need to cd into the new directory and open it with the text editor of your choice. Our next steps will be to update our config for our database to ensure that rails can work with our local PostgreSQL database. Go to config -&amp;gt; database.yml file, edit the development and test blocks and add your username and password. Below is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;development:
  &amp;lt;&amp;lt;: *default
  database: activestorage_tutorial_development
  username: nemwel
  password: root

test:
  &amp;lt;&amp;lt;: *default
  database: activestorage_tutorial_test
  username: nemwel
  password: root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;our next steps will involve creating our database with &lt;code&gt;rails db:create&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Users Resource&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We want to implement and test the file upload functionality and we will need to have a User who will have the following properties: name, email, profile_photo, phone_number. In your terminal type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails generate scaffold User name email phone_number:integer profile_photo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below are the files generated by the scaffold:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjswjttwsko5xwt5xolw0.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%2Fjswjttwsko5xwt5xolw0.png" alt="The Rails scaffold of the Users resource" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In rails, all the properties for an object(User) are default type string. Therefore not defining the datatype for eg the name will give it a type of string when the columns are generated. Also, we are using string for the profile_photo field because ACtive Storage will generate urls for each uploaded file which will be of type string. I hope this explains why.&lt;/p&gt;

&lt;p&gt;We are using the scaffold to generate the users resource for us which includes a user controller, model, migration, views, test files, and an update to our routes with the user's resource. &lt;/p&gt;

&lt;p&gt;Let us update our routes to add a root route to the users(index.html.erb) view on application load. add the following line in your routes file(Config -&amp;gt; routes.rb):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root "users#index"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now run the migrations with the command below and start your server with rails s and go to &lt;a href="http://127.0.0.1:3000/" rel="noopener noreferrer"&gt;http://127.0.0.1:3000/&lt;/a&gt; in your browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Setup Active storage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our application set up, it is time to setup Active Storage. Go to your terminal and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails active_storage:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will generate three tables as we learned in my previous article, and migrate with &lt;code&gt;rails db:migrate&lt;/code&gt; to update our schema.&lt;/p&gt;

&lt;p&gt;In our application since a user should only have one profile_photo at any given period, we are going to go to our user model in app -&amp;gt; models -&amp;gt; user.rb file and add the has_one_attached association. Add the following inside our user model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User &amp;lt; ApplicationRecord
  has_one_attached :profile_photo
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our next step will be to update our views. We want to be able to upload images from our local machines, store them, and retrieve them to display it in our browser.&lt;/p&gt;

&lt;p&gt;We shall first update our file upload. Let's go to app -&amp;gt; views -&amp;gt; users -&amp;gt; new.html.erb (If you generated the Users resource with scaffold, the form will not be in this file but can be found inside the _form.html.erb partial). We ideally want to update the input field for the profile photo to a file upload field. Update this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;%= form.text_field :profile_photo %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;%= form.file_field :profile_photo %&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;that will allow us to upload a file.&lt;/p&gt;

&lt;p&gt;Once we can upload files, our next steps should be to ensure that we are now able to display the files that we uploaded. We shall go to app -&amp;gt; views -&amp;gt; users -&amp;gt; index.html.erb (If you generated the users resouce with scaffold, the markup will be found in the _user.html.erb partial instead). We will change the markup for displaying the image to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;% if user.profile_photo.attached? %&amp;gt;
  &amp;lt;image src="&amp;lt;%= (url_for(user.profile_photo))%&amp;gt;"&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are checking through the active_storage_attachments table to see if there is a profile_photo for this specific user. If such a file exists, create a custom URL for that file that is passed to the image tag.&lt;/p&gt;

&lt;p&gt;With that, we have our complete file upload and retrieval all wired up and working as expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional: some custom validations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we have learned what active Storage is, how to set it up and make it to work, we have not touched on how to ensure the integrity of the files we are uploading.&lt;/p&gt;

&lt;p&gt;Imagine a situation where you have limited storage space and you'd like to only allow images that are eg less than 1MB to be uploaded. Ideally, you'd like to have some sort of restrictions on eg the file sizes of the file we are uploading. This is where validations are king and luckily for us, rails provides a way to write our own &lt;a href="https://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations" rel="noopener noreferrer"&gt;custom methods&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In our users model, let's add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User &amp;lt; ApplicationRecord
  has_one_attached :profile_photo

  validate :valid_image

  def valid_image
    return unless profile_photo.attached?

    unless profile_photo.blob.byte_size &amp;lt;= 1.megabyte
      errors.add(:profile_photo, "The image is more than 1MB")
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a nutshell, we are using the unless a statement to check if the condition is false. If the condition is truly false, we execute the block of code(In our case, the error message and the image will not be uploaded because the validation failed)&lt;/p&gt;

&lt;p&gt;With our above validation set and you try to uload an image whose size is more than 1MB, the following error will be shown:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfz4os8l01lteckyyaih.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%2Fhfz4os8l01lteckyyaih.png" alt="Image validation example" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I hope that my short article has given you insights on how you can setup and use active storage in your applications and gave you an introduction on custom validations in Ruby on Rails.&lt;/p&gt;

&lt;p&gt;This one marks the end of the second series of The Ultimate Guide to Active Storage in Rails. Here, I walked you through the technical set up of Active storage locally in your application and in the last part of the series, I will show you how to link it with Cloudinary to upload your images to an external cloud storage service provider will be coming soon. Be on the lookout for it!&lt;/p&gt;

&lt;p&gt;That is it for today's class. I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ruby</category>
      <category>rails</category>
      <category>backend</category>
    </item>
    <item>
      <title>The Ultimate Guide to Active Storage in Rails</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Tue, 25 Apr 2023 17:39:38 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/the-ultimate-guide-to-active-storage-in-rails-2f57</link>
      <guid>https://dev.to/nemwelboniface/the-ultimate-guide-to-active-storage-in-rails-2f57</guid>
      <description>&lt;p&gt;&lt;strong&gt;What is Active storage?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When developing an application, you must have faced a situation where you want to include a file upload functionality eg allow your users to upload their profile pictures when they are signing up. Implementing this feature on your own proves to be a cumbersome process and this is where using a mature framework like Ruby on Rails comes to pay dividends.&lt;/p&gt;

&lt;p&gt;Active Storage is an inbuilt library in Ruby on Rails that provides an easy way to upload files and associate them with models in a Ruby on Rails application. &lt;/p&gt;

&lt;p&gt;It offers an easy way to integrate file uploads from your local machine and store them in both the local disk storage or in external cloud storage service providers such as Cloudinary and simplifies the retrieval and processing of the same files that were uploaded.&lt;/p&gt;

&lt;p&gt;Part B of the series is available and can be found &lt;a href="https://dev.to/nemwelboniface/from-theory-to-practice-using-active-storage-for-file-management-in-rails-4o3f"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before we continue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdf9gd5ijm0jyas1gz93.gif" 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%2Fsdf9gd5ijm0jyas1gz93.gif" alt="Before we go deep learning about active storage" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While most of us have been rushing to meet our specific needs (Making the file upload functionality work), most of us don't know what constitutes Active storage.&lt;/p&gt;

&lt;p&gt;If you're looking to take your Ruby on Rails skills to the next level and learn about working with files, then you've come to the right place. I plan to make this multiple series a one-stop guide for understanding what Active Storage is, its ins and outs, how to setup Active storage in your application covering both file uploads and retrieval to showcase the retrieved file to your screen, and in the last series, I shall walk you through using Active Storage to upload your images to &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the first part of the series and in this version of the series, I aim to give an in-depth overview of Active Storage. This article you are reading now covers the theory behind Active Storage and we shall work on the technical setup in our application in part B of the article series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Active Storage 101&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Active Storage makes use of three tables in your application's database namely: &lt;code&gt;active_storage_blobs&lt;/code&gt;, &lt;code&gt;active_storage_attachments&lt;/code&gt;, and &lt;code&gt;active_storage_variant_records&lt;/code&gt;, which are generated when running the command &lt;code&gt;rails active_storage:install&lt;/code&gt; to create a migration that will generate the above-mentioned three tables. We shall discuss the three tables in depth below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. active_storage_blobs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk24dvqswzbxrgaz8zilw.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%2Fk24dvqswzbxrgaz8zilw.png" alt="The active_storage_blobs table migration generated when running rails active_storage:install" width="699" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This table is used to store metadata for the uploaded files where each row represents a record/ file that has been uploaded to your Rails application. The blobs table contains the following columns:&lt;/p&gt;

&lt;p&gt;a. id - This is a unique identifier for the blob (file)&lt;br&gt;
b. Key - This is a unique key that is used to retrieve the blob from storage&lt;br&gt;
c. filename - This column contains the original file names of the uploaded files&lt;br&gt;
d. Content_type - This column contains the MIME type of the uploaded file&lt;br&gt;
e. Byte_size - This column contains the size of the uploaded file in bytes&lt;br&gt;
f. Checksum - This column contains a SHA256 hash of the uploaded file. The SHA256 hash is used in Active Storage to ensure the integrity of the uploaded files&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. active_storage_variant_records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhxzm3se3u5d17smyft2.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%2Fkhxzm3se3u5d17smyft2.png" alt="The active_storage_variant_records table migration generated when running rails active_storage:install" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This storage is used by Active storage to store the metadata for variant files. In simple terms, the variant files are different versions of a file that has been uploaded and processed by Active Storage to give them a different size, format, or other properties.&lt;/p&gt;

&lt;p&gt;When you use Active Storage to process an uploaded file such as resizing images, a new variant file is created and stored in the configured storage service eg local disk storage or an external cloud service such as &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thus the active_storage_variant_records is used to store the metadata about these variant files including their "variation-digest", which is a unique identifier for the specific processing that was applied to that specific file. This table contains the following columns:&lt;/p&gt;

&lt;p&gt;a. id - A unique identifier for the variant record&lt;br&gt;
b. variation-digest - the unique identifier for the processing that was applied to that file&lt;br&gt;
c. blob_id - This is the ID of the original file/ blob that a variant was created from&lt;br&gt;
d. created_at - This is a timestamp for when a variant was first created&lt;br&gt;
e. updated_at - This is a timestamp for when a variant was last updated&lt;/p&gt;

&lt;p&gt;Overall, this table allows you to create and store variant files that have been processed from the original uploaded files and make the process of retrieval of the files efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. active_storage_attachments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyrfkft9zn9qu27wbwdo.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%2Fsyrfkft9zn9qu27wbwdo.png" alt="The active_storage_attachments table migration generated when running rails active_storage:install" width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the table in active_storage that is used for associating uploaded files with records in our application's database.&lt;/p&gt;

&lt;p&gt;When you make a file upload to active_storage, you can attach it to a record in your database using Rails associations has_one_attached (here, you mean that a profile_picture belongs to only one User), or has_many_attached (here you mean a User object can have many photos associated to them). &lt;/p&gt;

&lt;p&gt;This table is used to efficiently retrieve the uploaded files associated with a particular record in our database. In case you call for eg &lt;code&gt;user.profile_picture&lt;/code&gt;, Active_storage will lookup for a corresponding record in "active_storage_attachments" table, retrieve it and display it to you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metadata. What do I mean by Metadata?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Metadata refers to information that describes other data. Confusing huh? Stay with me here. In our conext of Active-Storage, metadata refers to the information that is stored about an uploaded file or a variant file such as the filename, the content_type, byte_size and the checksum etc.&lt;/p&gt;

&lt;p&gt;Let's say I have a digital photo of myself saved in my local disk as "Nemwelheadshot.png". Metadata for my picture could include data such as: filename, date taken, camera model, aperture, shutter speed, ISO speed, etc.&lt;/p&gt;

&lt;p&gt;All the information provided above provides additional context and detail about the photo and they help to organize and simplify the retrieval process of a file. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To sum it all up:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have mentioned so much above and I would like to finalize this with a personalized summary of what each table's function is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The active_storage_blobs - this is the main engine of active storage as it stores all the core information about an uploaded file that is necessary for Active Storage to function properly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The active_storage_variant_records - this is the optimizer of the uploaded files by creating multiple versions of an uploaded file to eg optimize it to take up less space and make the process of file retrieval; faster and efficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The active_storage_attachments - this is the middle man of active storage who links a specific uploaded file to a specific object in my database.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope that my short article has given you insights on what Active Storage is, what it helps you achieve, why you should use Active Storage in your Rails projects, and most importantly what constitutes Active Storage.&lt;/p&gt;

&lt;p&gt;This one marks the end of the first series of The Ultimate Guide to Active Storage in Rails. Part B where I will walk you through the technical set up of Active storage locally in your application and later show you how to link it with &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt; to upload your images to an external cloud storage service provider will be coming soon. Be on the lookout for it!&lt;/p&gt;

&lt;p&gt;That is it for today's class. I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>activestorage</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Create an API with Rails 7 and PostgreSQL</title>
      <dc:creator>Nemwel Boniface</dc:creator>
      <pubDate>Mon, 22 Aug 2022 07:56:00 +0000</pubDate>
      <link>https://dev.to/nemwelboniface/api-with-rails-7-ngh</link>
      <guid>https://dev.to/nemwelboniface/api-with-rails-7-ngh</guid>
      <description>&lt;p&gt;&lt;u&gt;&lt;strong&gt;How to Create an API using Ruby on Rails 7 and PostgreSQL database&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;If you are new to programming like me I am sure that each time you come across this term you think of it as a very complex enigma to decipher. Luckily for you my reader today, I will try to explain to you what an API is in simple terms and walk you through how you can create your API with Ruby on Rails 7.&lt;/p&gt;

&lt;p&gt;This will be part A of a two-part series where I will cover creating an API, documenting your API with rswag gem, and deploying the API to Heroku 🚀.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So what is an API?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq134xpuo0pd9xycnrhei.jpg" 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%2Fq134xpuo0pd9xycnrhei.jpg" alt="API image icon" width="612" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An application programming interface (API) is like the messenger that takes your requests and tells your system what to do and then returns the response to you. Let's say you walk into a restaurant. You know what you want to eat, but you do not have access to the kitchen. The waiter comes and takes your request and returns your response in this case your food. Easy right? &lt;/p&gt;

&lt;p&gt;When creating your website, you might not want it to have static data, that would be very boring. You might want to give it a way of getting dynamic data from somewhere. Ruby on Rails is one of the popular and easy ways to create API and now let's get started.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our API project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We are going to create an API for a friend's application. You might want to save information about your friends and fetch them dynamically from somewhere and today we are going to do just that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rails V7&lt;/li&gt;
&lt;li&gt;PostgreSQL database&lt;/li&gt;
&lt;li&gt;Ruby V 3+&lt;/li&gt;
&lt;li&gt;postman&lt;/li&gt;
&lt;li&gt;Visual studio code&lt;/li&gt;
&lt;li&gt;Some experience with Rails MVC architecture.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Project setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We shall start by creating a new rails API application with the rails' new command in our terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails new friends-api --api --database=postgresql
cd friends-api
code .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates all our starting files and installs all the dependencies that we are going to need for this project. The &lt;code&gt;--api&lt;/code&gt; flag installs the "bare minimum" dependencies excluding the views. This will be our back end remember?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Do not forget to edit your database.yml file  to allow Rails to work with your local machine PostgreSQL. Make sure you add your postgres username and passowrd in the development and test blocks. Below is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;development:
  &amp;lt;&amp;lt;: *default
  database: friends_api_development
  username: nemwel
  password: root

test:
  &amp;lt;&amp;lt;: *default
  database: friends_api_test
  username: nemwel
  password: root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now need to create our database but do not migrate it just yet. We will do that when creating our model in a while.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rails db:create&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Now let's create our model for our API and lets call it friend and let's assume we want to store the name, phone number, twitter username, his/ her email and maybe the location of the friend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails g model friend name:string phone:integer twitter:string email:string location:string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a few files among them our model and a migration. Remember that this is a two-step process and now we need to run the migration so that the changes can persist in the database and we will have a schema.rb file created for us.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rails db:migrate&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;and now in your db folder, you should have the schema.rb file and our friend table created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create the API endpoints&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We shall now create the API endpoints. Let's go to app&amp;gt;controllers and create an api folder. Let's create another folder called v1 inside our api folder which will hold the first version of our API.&lt;/p&gt;

&lt;p&gt;Inside the app&amp;gt;controllers =&amp;gt; api =&amp;gt; v1 create our friends_controller and let's create the basic structure of our API friends controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Api::V1::FriendsController &amp;lt; ApplicationController 
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome, now we can start by creating our API endpoint that will list all the friends that are available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def index
  friends = Friend.all 

  if friends
    render json: {status: "SUCCESS", message: "Fetched all the friends successfully", data: friends}, status: :ok
  else
    render json: friends.errors, status: :bad_request
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but wait, we do not have any developers to test with! We will simplify this by going to our db/seeds.rb file and add some seed data that we shall test our API with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;friends = Friend.create([
  {name: "Nemwel Boniface", phone: 754135545, twitter: "@nemwel_bonie", email: "nemwelboniface@outlook.com", location: "Nairobi, Kenya"}
])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that everything is all wired up, we need one last thing. Routes! we need to tell our Rails application where and how to handle that request that we are passing to it.&lt;/p&gt;

&lt;p&gt;so let's go to our config/routes.rb file and let's add the routes to our friend's controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace :api do
  namespace :v1 do
    resources :friends
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run your server with &lt;code&gt;rails s&lt;/code&gt; and go to &lt;code&gt;http://127.0.0.1:3000/api/v1/friends&lt;/code&gt; and you should be able to see your friend listed there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j55clxs3p9e5ag9bdkp.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%2F8j55clxs3p9e5ag9bdkp.png" alt="show fetched friend" width="507" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Do not forget to run &lt;code&gt;rails db:seed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's move to create a new friend. we will require two methods, one friends_params, and a create method. Let's get to work now. &lt;em&gt;(Remember we will be working mostly in our friends_controller,rb file)&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def create
  friend = Friend.new(friend_params)

  if friend.save
    render json: {status: "SUCCESS", message: "Friend was created successfully!", data: friend}, status: :created
  else
    render json: friend.errors, status: :unprocessable_entity
  end
end

private

def friend_params
  params.require(:friend).permit(:name, :location, :email, :twitter, :phone)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;perfect! let's now test in our Postman. We are going to make a POST request. Make sure your server is still running. You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcyedisym29wkkowl8vw9.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%2Fcyedisym29wkkowl8vw9.png" alt="Postman Create Friend" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! our API is working very well so far. For brevity, I will now include the code for the API endpoints for deleting, editing, and showing the details of a specific friend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Show a specific friend GET request
def show
  friend = Friend.find(params[:id])

  if friend
    render json: {data: friend}, state: :ok
  else
    render json: {message: "Friend could not be found"}, status: :bad_request
  end
end

# Delete a specific friend DELETE request
def destroy
  friend = Friend.find(params[:id])

  if friend.destroy!
    render json: {message: "Friend was deleted successfully"}, status: :ok
  else
    render json: {message: "Friend does not exist"}, status: :bad_request
  end
end

# Update details for a specific friend. PATCH request
def update
  friend = Friend.find(params[:id])

  if friend.update!(friend_params)
    render json: {message: "Friend was updated successfully", data: friend}, status: :ok
  else
    render json: {message: "Friend cannot be updated"}, status: :unprocessable_entity
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, we can confidently say that we have successfully created our CRUD API endpoints. But wait, when you try to add some data, even wrong data, the API accepts it. &lt;/p&gt;

&lt;p&gt;The rule of "Garbage in, garbage out" is something we want to avoid in our API. Let's add some validations to make sure that no fields are empty and that the correct data types of the data are input. So let's go to our friends model and let's add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Friend &amp;lt; ApplicationRecord
  validates :name, length: { minimum: 2 }
  validates :location, length: { minimum: 2 }
  validates :email, length: { minimum: 2 }
  validates :twitter, length: { minimum: 2 }
  validates :phone, length: { is: 9 }, numericality: true
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if someone tries adding some wrong data or leaves a field as empty, the API will reject it and the integrity of the data we store will be upheld.&lt;/p&gt;

&lt;p&gt;This one marks the end of the first series of creating an API with Ruby on Rails 7 and PostgreSQL database. Part B of this series will cover how to document your API using the Rswag gem and then how to deploy your API to Heroku. Stay tuned!&lt;/p&gt;

&lt;p&gt;That is it for today's class. I hope this was useful information for you. See you in my next article.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>api</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
