<?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: Daniele</title>
    <description>The latest articles on DEV Community by Daniele (@danig2k).</description>
    <link>https://dev.to/danig2k</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%2F322100%2F85a92a51-2ceb-45fd-a39d-08cd37d2d0e6.jpeg</url>
      <title>DEV Community: Daniele</title>
      <link>https://dev.to/danig2k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danig2k"/>
    <language>en</language>
    <item>
      <title>Facade Pattern in Rails for Performance and Maintainability</title>
      <dc:creator>Daniele</dc:creator>
      <pubDate>Thu, 02 Apr 2020 10:50:24 +0000</pubDate>
      <link>https://dev.to/appsignal/facade-pattern-in-rails-for-performance-and-maintainability-3i26</link>
      <guid>https://dev.to/appsignal/facade-pattern-in-rails-for-performance-and-maintainability-3i26</guid>
      <description>&lt;p&gt;In today's post, we will be looking into a software design pattern called Facade. When I first adopted it, it felt a little bit awkward, but the more I used it in my Rails apps, the more I started to appreciate its usefulness. More importantly, it allowed me to test my code more thoroughly, to clean out my controllers, to reduce the logic within my views and to make me think more clearly about an application's code's overall structure.&lt;/p&gt;

&lt;p&gt;Being a software development pattern, facade is framework agnostic but the examples I will provide here are for Ruby on Rails. However, I encourage you to read through this article and try them out regardless of the framework you are using. I'm sure that once you become familiar with this pattern, you will start seeing opportunities to use it in many parts of your codebase.&lt;/p&gt;

&lt;p&gt;Without further ado, let's dive right in!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with the MVC Pattern
&lt;/h2&gt;

&lt;p&gt;The MVC (Model-View-Controller) pattern is a software development pattern that dates back to the 1970s. It's a battle-tested solution for designing software interfaces, separating programming concerns into three main groups that communicate amongst each other in a unique way.&lt;/p&gt;

&lt;p&gt;Many large web frameworks emerged in the early 2000s with the MVC pattern as their foundation. Spring (for Java), Django (for Python) and Ruby on Rails (for Ruby), were all forged with this trinity of interconnected elements at their core. Compared to the spaghetti-code resulting from software that did not make use of it, the MVC pattern was a huge achievement and turning point in the evolution of both software development and the internet.&lt;/p&gt;

&lt;p&gt;In essence, the Model-View-Controller pattern allows for the following: a user performs an action on the View. The View triggers a request to a Controller which can potentially create/read/update or delete a Model. The Model transaction responds back to the Controller, which in turn renders some change that the user will see reflected in the View.&lt;/p&gt;

&lt;p&gt;There are plenty of pros to this programming pattern. To list some:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It improves code maintainability by separating concerns&lt;/li&gt;
&lt;li&gt;It allows for greater testability (the Models, Views and Controllers can be tested in isolation)&lt;/li&gt;
&lt;li&gt;It encourages good coding practices by enforcing the Single Responsibility Principle of SOLID: "A class should have only one reason to change."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A phenomenal achievement for its time, developers soon realized that the MVC pattern was also somewhat limiting. Variants started to emerge, such as HMVC (hierarchical model–view–controller), MVA (model–view–adapter), MVP (model–view–presenter), MVVM (model–view–viewmodel) and others, which all sought to address the limitations of the MVC pattern.&lt;/p&gt;

&lt;p&gt;One of the problems that the MVC pattern introduces, and the topic of today's article, is the following: who is responsible for handling complex view logic? The view should simply be concerned with presenting the data, the controller is just relaying the message it received from the model, and the model should not be concerned with any view logic.&lt;/p&gt;

&lt;p&gt;To help with this common conundrum, all Rails applications get initialized with a &lt;code&gt;helpers&lt;/code&gt; directory. The &lt;code&gt;helper&lt;/code&gt; directory can contain modules with methods that assist in complex View logic.&lt;/p&gt;

&lt;p&gt;Here is an example of a helper within a Rails application:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app/helpers/application_helper.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ApplicationHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display_ad_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;advertisement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;advertisement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ad_type&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'foo'&lt;/span&gt;
      &lt;span class="n"&gt;content_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:span&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"foo ad-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'bar'&lt;/span&gt;
      &lt;span class="n"&gt;content_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bar advertisement'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;content_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:span&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"badge ads-badge badge-pill ad-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This example is simple but demonstrates the fact that you would want to extract this kind of decision making from the template itself in order to reduce its complexity.&lt;/p&gt;

&lt;p&gt;Helpers are nice, but there is yet another pattern for handling complicated View logic that has become accepted through the years, and that is the Facade pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to the Facade Pattern
&lt;/h2&gt;

&lt;p&gt;In a Ruby on Rails application, facades are usually placed within the &lt;code&gt;app/facades&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;While similar to &lt;code&gt;helpers&lt;/code&gt;, &lt;code&gt;facades&lt;/code&gt; are not a group of methods within a module. A Facade is a PORO (Plain Old Ruby Object) that is instantiated within the controller, but one that handles elaborate View business logic. As such, it allows the following benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rather than having a single module for &lt;code&gt;UsersHelper&lt;/code&gt; or &lt;code&gt;ArticlesHelper&lt;/code&gt; or &lt;code&gt;BooksHelper&lt;/code&gt;, each controller action can have its own Facade: &lt;code&gt;Users::IndexFacade&lt;/code&gt;, &lt;code&gt;Articles::ShowFacade&lt;/code&gt;, &lt;code&gt;Books::EditFacade&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;More so than modules, facades encourage good coding practices by allowing you to nest facades to ensure the Single Responsibility Principle is enforced. While you probably don't want facades that are nested hundreds of levels deep, having one or two layers of nesting for improved maintainability and test coverage can be a good thing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a contrived example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Books&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IndexFacade&lt;/span&gt;
    &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:books&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
      &lt;span class="vi"&gt;@params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;
      &lt;span class="vi"&gt;@user&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
      &lt;span class="vi"&gt;@books&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;books&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;filtered_books&lt;/span&gt;
      &lt;span class="vi"&gt;@filtered_books&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
                  &lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name ILIKE ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;isbn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
                  &lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;isbn: &lt;/span&gt;&lt;span class="n"&gt;isbn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                  &lt;span class="n"&gt;books&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;created_at: :desc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;recommended&lt;/span&gt;
      &lt;span class="c1"&gt;# We have a nested facade here.&lt;/span&gt;
      &lt;span class="c1"&gt;# The `Recommended Books` part of the view has a&lt;/span&gt;
      &lt;span class="c1"&gt;# single responsibility so best to extract it&lt;/span&gt;
      &lt;span class="c1"&gt;# to improve its encapsulation and testability.&lt;/span&gt;
      &lt;span class="vi"&gt;@recommended&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Books&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RecommendedFacade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;books: &lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;
      &lt;span class="vi"&gt;@query&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:query&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;isbn&lt;/span&gt;
      &lt;span class="vi"&gt;@isbn&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:isbn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  When Not to Use the Facade Pattern
&lt;/h2&gt;

&lt;p&gt;Let's take a moment to also reflect on what facades are not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Facades should not be placed in classes that live, for example, in the &lt;code&gt;lib&lt;/code&gt; directory for code that needs to be displayed in the View. The facade's lifecycle should be generated in the Controller action and be used in its associated View.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Facades are not meant to be used for business logic to perform CRUD actions (there are other patterns for that, such as Services or Interactors—but that is a subject for another day.) In other words, facades should not be concerned with creating, updating or deleting. Their aim is to extract intricate presentation logic from the View or Controller and offer a single interface to access all that information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Last but not least, Facades are not a silver bullet. They do not allow you to bypass the MVC pattern, but rather, they play along with it. If a change occurs in a Model, it will not be immediately reflected in the View. As is always the case with MVC, the controller action would have to be re-rendered in order for the Facade to display changes on the View.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Controller Benefits
&lt;/h2&gt;

&lt;p&gt;One of the main, obvious benefits of Facades is that they will allow you to dramatically reduce the controller logic.&lt;/p&gt;

&lt;p&gt;Your controller code will be reduced from something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;BooksController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@books&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:query&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
                &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name ILIKE ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:query&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:isbn&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
                &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;isbn: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:isbn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
              &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;books&lt;/span&gt;
              &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="vi"&gt;@books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;created_at: :desc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="vi"&gt;@recommended&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;some_complex_query: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;BooksController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@index_facade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Books&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IndexFacade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  View Benefits
&lt;/h2&gt;

&lt;p&gt;For the Views, there are two main benefits when using Facades:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Conditional checks, inline queries and other logic can be neatly extracted from the template itself making the code far more readable. For instance, you could use it in a form:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;%= f.label :location %&amp;gt;
&amp;lt;%= f.select :location, options_for_select(User::LOCATION_TYPES.map { |type| [type.underscore.humanize, type] }.sort.prepend(['All', 'all'])), multiple: (current_user.active_ips.size &amp;gt; 1 &amp;amp;&amp;amp; current_user.settings.use_multiple_locations?) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Could just become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;%= f.label :location %&amp;gt;
&amp;lt;%= f.select :location, options_for_select(@form_facade.user_locations), multiple: @form_facade.multiple_locations? %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Variables that get called multiple times can be cached. This can offer significant performance improvements to your app and help remove pesky N+1 queries:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Somewhere in the view, a query is performed.
&amp;lt;% current_user.books.where(isbn: params[:isbn]).each do |book| %&amp;gt;
  // Do things
&amp;lt;% end %&amp;gt;

// Somewhere else in the view, the same query is performed again.
&amp;lt;% current_user.books.where(isbn: params[:isbn]).each do |book| %&amp;gt;
  // Do things
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;would become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Somewhere in the view, a query is performed.
&amp;lt;% @index_facade.filtered_books.each do |book| %&amp;gt;
  // Do things
&amp;lt;% end %&amp;gt;

// Somewhere else in the view.
// Second query is not performed due to instance variable caching.
&amp;lt;% @index_facade.filtered_books.each do |book| %&amp;gt;
  // Do things
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing Benefits
&lt;/h2&gt;

&lt;p&gt;A major benefit of Facades is that they allow you to test singular bits of business logic without having to write an entire controller test, or worse, without having to write an integration test that goes through a flow and reaches a page just to ensure that the data presentation is as expected.&lt;/p&gt;

&lt;p&gt;As you will be testing single POROs, this will help maintain a fast test suite.&lt;/p&gt;

&lt;p&gt;Here is a simple example of a test written in Minitest for demonstration purposes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'test_helper'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Books&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IndexFacadeTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
    &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:params&lt;/span&gt;

    &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'Bob'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Dylan'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"#filtered_books returns all user's books when params are empty"&lt;/span&gt;
      &lt;span class="n"&gt;index_facade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Books&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IndexFacade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;expectation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;created_at: :desc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

      &lt;span class="c1"&gt;# Without writing an entire controller test or&lt;/span&gt;
      &lt;span class="c1"&gt;# integration test, we can check whether using the facade with&lt;/span&gt;
      &lt;span class="c1"&gt;# empty parameters will return the correct results&lt;/span&gt;
      &lt;span class="c1"&gt;# to the user.&lt;/span&gt;
      &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="n"&gt;expectation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index_facade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filtered_books&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"#filtered_books returns books matching a query"&lt;/span&gt;
      &lt;span class="vi"&gt;@params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;query: &lt;/span&gt;&lt;span class="s1"&gt;'Lord of the Rings'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;index_facade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Books&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IndexFacade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;expectation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;books&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name ILIKE ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:query&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;created_at: :desc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

      &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="n"&gt;expectation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index_facade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filtered_books&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Unit testing facades considerably improves test suite performance, and every large company will eventually encounter slow test suites unless problems like these aren’t addressed with some level of seriousness.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Facade, Two Facades, Three Facades, More?
&lt;/h2&gt;

&lt;p&gt;You might encounter a scenario where a View renders a partial that outputs some data. In that case, you have the option of either using the parent facade or using a nested facade. That largely depends on how much logic is involved, whether you want to test it separately and whether it makes sense to extract the functionality.&lt;/p&gt;

&lt;p&gt;There is no golden rule for how many facades to use or how many facades to nest within each other. That is to the developer's discretion. I generally prefer to have a single facade for the controller action and I limit nesting to a single level to make the code easier to follow.&lt;/p&gt;

&lt;p&gt;Here are some general questions you can ask yourself during development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the facade encapsulate the logic I am trying to present on the view?&lt;/li&gt;
&lt;li&gt;Does the method within the facade make sense in this context?&lt;/li&gt;
&lt;li&gt;Is the code easier to follow now, or harder to follow?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When in doubt, always strive to make your code as easy to follow as possible.&lt;/p&gt;

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

&lt;p&gt;In conclusion, facades are a fantastic pattern to keep your controllers and views lean, while improving code maintainability, performance and testability.&lt;/p&gt;

&lt;p&gt;However, like any programming paradigm, there is no silver bullet. Even the multitude of patterns that have emerged in more recent years (HMVC, MVVM, etc.) are not be-all-end-all solutions to the complications of software development.&lt;/p&gt;

&lt;p&gt;Similar to the second law of thermodynamics, which states that the state of entropy in a closed system will always increase, so too in any software project does the complexity increase and evolve over time. In the long run, the goal is to write code that is as easy to read, test, maintain and follow as possible; facades offer exactly this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/#ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Guest author Daniele is a full-stack developer from Italy with an eye for clean, elegant code. He has lived and worked on three continents, and as of late, splits his time working as a digital nomad between Italy and East Asia.&lt;br&gt;
Besides coding, Daniele enjoys reading, drawing and playing guitar.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Pros and Cons of Using structure.sql in Your Ruby on Rails Application</title>
      <dc:creator>Daniele</dc:creator>
      <pubDate>Wed, 22 Jan 2020 14:52:31 +0000</pubDate>
      <link>https://dev.to/appsignal/pros-and-cons-of-using-structure-sql-in-your-ruby-on-rails-application-12jn</link>
      <guid>https://dev.to/appsignal/pros-and-cons-of-using-structure-sql-in-your-ruby-on-rails-application-12jn</guid>
      <description>&lt;p&gt;In today's post, we'll cover the significant differences and benefits of using &lt;code&gt;structure.sql&lt;/code&gt; versus the default &lt;code&gt;schema.rb&lt;/code&gt; schema formats in your Ruby on Rails application. In a data-driven world, knowing how to exploit all of your database's rich features can make the difference between a successful and unsuccessful enterprise. &lt;/p&gt;

&lt;p&gt;After evincing the main differences between the two formats, we'll outline how to switch to &lt;code&gt;structure.sql&lt;/code&gt; and demonstrate how it can help with ensuring data integrity as well as database functionality that you might otherwise not be able to preserve.&lt;/p&gt;

&lt;p&gt;In the post, I'll give examples of a Rails app that makes use of &lt;code&gt;structure.sql&lt;/code&gt; with a PostgreSQL database, but the underlying concepts can be transposed to other databases as well. No real-world web application is truly complete without a reliable database to support it.&lt;/p&gt;

&lt;p&gt;Without further ado, let's dive right in!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Difference Between schema.rb and structure.sql
&lt;/h2&gt;

&lt;p&gt;One of the first things you need to do when starting a Ruby on Rails project is to run database migrations. If you generate a User model, for instance, Rails will inevitably ask you to run migrations, which will create a &lt;code&gt;schema.rb&lt;/code&gt; file accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g model User first_name:string last_name:string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Rails will generate the following migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;CreateUsers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:first_name&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;

      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once the migration is executed, you will find that Rails generated a &lt;code&gt;schema.rb&lt;/code&gt; file for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;version: &lt;/span&gt;&lt;span class="mi"&gt;2019_12_14_074018&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="c1"&gt;# These are extensions that must be enabled in order to support this database&lt;/span&gt;
  &lt;span class="n"&gt;enable_extension&lt;/span&gt; &lt;span class="s2"&gt;"plpgsql"&lt;/span&gt;

  &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: :cascade&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="s2"&gt;"first_name"&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="s2"&gt;"last_name"&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt; &lt;span class="s2"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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



&lt;p&gt;This &lt;code&gt;schema.rb&lt;/code&gt; file is fantastic for relatively basic applications and use cases.&lt;/p&gt;

&lt;p&gt;There are two main things to notice here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It is a Ruby representation of your database; &lt;code&gt;schema.rb&lt;/code&gt; is created by inspecting the database and expressing its structure using Ruby.&lt;/li&gt;
&lt;li&gt;It is database-agnostic (i.e. whether you use SQLite, PostgreSQL, MySQL or any other database that Rails supports, the syntax and structure will remain largely the same)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, there may come a time when this strategy becomes too limiting for your growing app.&lt;/p&gt;

&lt;p&gt;Say, for instance, you have hundreds or thousands of migration files.&lt;/p&gt;

&lt;p&gt;If you need to rapidly spin up a new production system, you might encounter a scenario where running them all in sequence takes too long. Or you might face a situation where some migrations contain code that was meant to be executed on an older version of your database, but that is no longer executable on the current version. You might have a situation where migrations were written with certain data assumptions that are no longer valid, which would cause the migrations to fail.&lt;/p&gt;

&lt;p&gt;All these scenarios prevent efficiently setting up a new instance of your application⁠—be it in production or for a new team member⁠—with a simple &lt;code&gt;rails db:create db:migrate&lt;/code&gt; command. If this were the case, how would you go about getting up to speed with a correct database schema?&lt;/p&gt;

&lt;p&gt;Certainly, one way would be to go back and fix all the broken migrations. That's never a bad idea!&lt;/p&gt;

&lt;p&gt;If going back and fixing a bunch of migrations is too costly, another way would be to run the &lt;code&gt;rails db:setup&lt;/code&gt; task. This task will generate a database schema from your &lt;code&gt;schema.rb&lt;/code&gt; file. However, what if your database contained complex logic that is not represented in the &lt;code&gt;schema.rb&lt;/code&gt; representation of your database?&lt;/p&gt;

&lt;p&gt;Luckily, Rails offers an alternative: &lt;code&gt;structure.sql&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;structure.sql&lt;/code&gt; differs from &lt;code&gt;schema.rb&lt;/code&gt; in the following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It allows for an exact copy of the database structure. This is important when working with a team, as well as if you need to rapidly generate a new database in production from a &lt;code&gt;rails db:setup&lt;/code&gt; task.&lt;/li&gt;
&lt;li&gt;It allows preserving information of advanced database features. For example, if you are using PostgreSQL, it enables the use of views, materialized views, functions, constraints and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once an application reaches a certain maturity level, we have to use every trick in the book to boost efficiency, preserve data correctness, and ensure blazing-fast performance. Using &lt;code&gt;structure.sql&lt;/code&gt; to manage the Rails database's behavior allows users to do so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switching From &lt;code&gt;schema.rb&lt;/code&gt; to &lt;code&gt;structure.sql&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Making the change from &lt;code&gt;schema.rb&lt;/code&gt; to &lt;code&gt;structure.sql&lt;/code&gt; is a relatively straightforward process. All you need to do is set the following line in &lt;code&gt;config/application.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;YourApp&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_defaults&lt;/span&gt; &lt;span class="mf"&gt;6.0&lt;/span&gt;

    &lt;span class="c1"&gt;# Add this line:&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema_format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:sql&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, run &lt;code&gt;rails db:migrate&lt;/code&gt; and you should see the file in &lt;code&gt;db/structure.sql&lt;/code&gt;. Voilà! Rails will dump the database structure using the tool specific to the database you are using (in PostgreSQL's case, that tool is &lt;code&gt;pg_dump&lt;/code&gt;, for MySQL or MariaDB, it will contain the output of &lt;code&gt;SHOW CREATE TABLE&lt;/code&gt; for each table, etc). It is advisable to ensure this file is under version control so that the rest of your team will have the same database structure.&lt;/p&gt;

&lt;p&gt;A first glance at that file may be daunting: the &lt;code&gt;schema.rb&lt;/code&gt; file was only 25 lines, whereas the &lt;code&gt;structure.sql&lt;/code&gt; file is a &lt;a href="https://gist.github.com/tombruijn/8ec144daed3e4993d73226bb24f035c6"&gt;whopping 109 lines&lt;/a&gt;! What benefits could such a large file add to the app development?&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Database-level Constraints
&lt;/h2&gt;

&lt;p&gt;ActiveRecord is one of my favorite parts of using Rails. It allows you to query the database in a way that feels natural, almost like in a spoken language. For instance, if you want to find all a company's users named Dan, then ActiveRecord allows you to simply run a query like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Some Company'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Reads just like in a natural language!&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'Dan'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are some cases in which ActiveRecord falls short though. For instance, say you have the following validation on your User model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;validate&lt;/span&gt; &lt;span class="ss"&gt;:name_cannot_start_with_d&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name_cannot_start_with_d&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'d'&lt;/span&gt;
      &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"cannot start with the letter 'D'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you try to create a user with the name 'Dan', you should see an error when the validation runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'Dan'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RecordInvalid&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Validation&lt;/span&gt; &lt;span class="ss"&gt;failed: &lt;/span&gt;&lt;span class="no"&gt;First&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="n"&gt;cannot&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="s1"&gt;'D'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is fine, but suppose you or one of your team members changed the data by bypassing ActiveRecord's validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'Pan'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The update_attribute method bypasses ActiveRecord validations&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attribute&lt;/span&gt; &lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Dan'&lt;/span&gt;
&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Dan"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As demonstrated, it is very easy to bypass the validation.&lt;/p&gt;

&lt;p&gt;This can have disastrous consequences for our application. ActiveRecord can be a blessing as well as a curse⁠—while it has a very clean and natural DSL that makes it a pleasure to work with, it is often overly permissive when enforcing model-level validations. The solution, as you may already know, is to add database-level constraints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g migration AddFirstNameConstraintToUser
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will generate a file that you can edit with the logic to disallow first names that start with the letter 'D':&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;AddFirstNameConstraintToUser&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="s2"&gt;"ALTER TABLE users ADD CONSTRAINT name_cannot_start_with_d CHECK (first_name !~* '^d')"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="s2"&gt;"ALTER TABLE users DROP CONSTRAINT IF EXISTS name_cannot_start_with_d"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that it is &lt;em&gt;very&lt;/em&gt; important to add code that successfully reverts the migration. In the above example, I have &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; directives. The &lt;code&gt;up&lt;/code&gt; method gets executed when the migration runs, &lt;code&gt;down&lt;/code&gt; gets executed when the migration is rolled back. Without properly reverting your database structure, you may have to do some manual house-cleaning later. I'd recommend always having a migration file that can be executed both &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; to avoid future headaches.&lt;/p&gt;

&lt;p&gt;Now, run the migration and check whether you can bypass that constraint:&lt;br&gt;
&lt;/p&gt;

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





&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'Pan'&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attribute&lt;/span&gt; &lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Dan'&lt;/span&gt;

&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StatementInvalid&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CheckViolation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;relation&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="n"&gt;violates&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="n"&gt;constraint&lt;/span&gt; &lt;span class="s2"&gt;"name_cannot_start_with_d"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;DETAIL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="no"&gt;Failing&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="n"&gt;contains&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Dan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;11.809358&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;41.658974&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Perfect! Our constraint is working as intended. Even if, for whatever reason, we bypass ActiveRecord's validation, we can still rely on the database⁠—our ultimate goalkeeper⁠—to preserve our data integrity.&lt;/p&gt;

&lt;p&gt;What does this have to do with &lt;code&gt;structure.sql&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;If you take a look at it, you'll see that the following was added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="nb"&gt;character&lt;/span&gt; &lt;span class="nb"&gt;varying&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="nb"&gt;character&lt;/span&gt; &lt;span class="nb"&gt;varying&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;without&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;without&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;name_cannot_start_with_d&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="o"&gt;!~*&lt;/span&gt; &lt;span class="s1"&gt;'^d'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Your constraint is within the schema itself!&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;schema.rb&lt;/code&gt; also supports database-level constraints, it is important to remember that it does not express everything your database may support such as triggers, sequences, stored procedures or check constraints. For example, this is what would happen to your schema file with the same exact migration (&lt;code&gt;AddFirstNameConstraintToUser&lt;/code&gt;) if you were just to use &lt;code&gt;schema.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;version: &lt;/span&gt;&lt;span class="mi"&gt;2019_12_14_074018&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="c1"&gt;# These are extensions that must be enabled in order to support this database&lt;/span&gt;
  &lt;span class="n"&gt;enable_extension&lt;/span&gt; &lt;span class="s2"&gt;"plpgsql"&lt;/span&gt;

  &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: :cascade&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="s2"&gt;"first_name"&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="s2"&gt;"last_name"&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt; &lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt; &lt;span class="s2"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;precision: &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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



&lt;p&gt;The file has not changed! The constraint was not added.&lt;/p&gt;

&lt;p&gt;If you were to onboard a new developer to work on your project, you could potentially be operating under different database regulations.&lt;/p&gt;

&lt;p&gt;Committing &lt;code&gt;structure.sql&lt;/code&gt; to version control would help ensure that your team is on the same page. If you were to run &lt;code&gt;rails db:setup&lt;/code&gt; having a &lt;code&gt;structure.sql&lt;/code&gt; file, your database's structure will contain the above constraint. With &lt;code&gt;schema.rb&lt;/code&gt; there is no such guarantee.&lt;/p&gt;

&lt;p&gt;The same can be said about a production system. If you needed to rapidly spin up a new instance of your application with a fresh database⁠—and running all migrations sequentially takes a long time⁠—setting up the database from the &lt;code&gt;structure.sql&lt;/code&gt; file would be a lot quicker. We can rest assured that the &lt;code&gt;structure.sql&lt;/code&gt; will create our database with the exact same structure as in other instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Growing Pains
&lt;/h2&gt;

&lt;p&gt;Managing the concise &lt;code&gt;schema.rb&lt;/code&gt; file across a team is a far easier task than managing the verbose &lt;code&gt;structure.sql&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;One of the biggest growing pains when migrating to &lt;code&gt;structure.sql&lt;/code&gt; is ensuring that only the required changes get committed to that file, which can sometimes be difficult to do.&lt;/p&gt;

&lt;p&gt;Say, for instance, you pull someone's branch and run the migrations specific to that branch. Your &lt;code&gt;structure.sql&lt;/code&gt; will now contain some changes. You then go back to working on your own branch and generate a new migration. Your &lt;code&gt;structure.sql&lt;/code&gt; file will now contain both your branch's and the other branch's changes. This can be a bit of a hassle to deal with, and there is undoubtedly a bit of a learning curve when it comes to managing these conflicts.&lt;/p&gt;

&lt;p&gt;By using this approach, we're making a tradeoff. We have to deal with a bit of code complexity upfront that allows us to preserve our database's advanced functionality. In turn, we also have to deal with a simpler schema representation as well as not having all the power of the database at our fingertips, e.g. if we want to set a backup from a &lt;code&gt;db:setup&lt;/code&gt; task. I posit that it's best to put up with a bit of version-control hassle than to suffer through fixing corrupt/incorrect data in a production system, or to not be able to make use of all the advanced functionality that your database offers.&lt;/p&gt;

&lt;p&gt;Generally speaking, there are two strategies I've used to ensure my &lt;code&gt;structure.sql&lt;/code&gt; file only contains the necessary changes to a specific branch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once you are done working on a branch that contains migrations, make sure you run &lt;code&gt;rails db:rollback STEP=n&lt;/code&gt; where &lt;code&gt;n&lt;/code&gt; is the number of migrations in that branch. This will ensure your database structure reverts to its original state.&lt;/li&gt;
&lt;li&gt;You might forget to rollback after working on a branch. In that case, when working on a new branch, make sure you pull a pristine &lt;code&gt;structure.sql&lt;/code&gt; file from master before creating any new migrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a rule of thumb, your &lt;code&gt;structure.sql&lt;/code&gt; file should only contain the changes relevant to your branch before being merged into master.&lt;/p&gt;

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

&lt;p&gt;Generally speaking, when Rails applications are small or don't need some of the more advanced features that a database offers then it's safe to use &lt;code&gt;schema.rb&lt;/code&gt;, which is very readable, concise and easy to manage.&lt;/p&gt;

&lt;p&gt;However, as an application grows in size and complexity, an accurate reflection of the database structure is of the essence. It will allow a team to maintain the right constraints, database modules, functions and operators that otherwise wouldn't be possible. Learning to use Rails with a well-maintained &lt;code&gt;structure.sql&lt;/code&gt; file will offer an edge that the simpler &lt;code&gt;schema.rb&lt;/code&gt; simply cannot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/#ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Guest author Daniele is a full-stack developer from Italy with an eye for clean, elegant code. He has lived and worked on three continents, and as of late splits his time working as a digital nomad between Italy and East Asia.&lt;br&gt;
Besides coding, Daniele enjoys reading, drawing and playing guitar.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
