<?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: Niko</title>
    <description>The latest articles on DEV Community by Niko (@nikom).</description>
    <link>https://dev.to/nikom</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%2F3582570%2F6d4fdc9f-184e-4109-bed2-49a8f7b1dd83.jpg</url>
      <title>DEV Community: Niko</title>
      <link>https://dev.to/nikom</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikom"/>
    <language>en</language>
    <item>
      <title>Contributing Upstream Instead of Forking: My grape-swagger-rails Story</title>
      <dc:creator>Niko</dc:creator>
      <pubDate>Tue, 26 May 2026 04:01:17 +0000</pubDate>
      <link>https://dev.to/nikom/contributing-upstream-instead-of-forking-my-grape-swagger-rails-story-18k7</link>
      <guid>https://dev.to/nikom/contributing-upstream-instead-of-forking-my-grape-swagger-rails-story-18k7</guid>
      <description>&lt;p&gt;Recently I had a chance to contribute to grape-swagger-rails, helping modernize the project and release v1.0.0.&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%2Frjq7pbhdxdc5b8sljje6.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%2Frjq7pbhdxdc5b8sljje6.png" alt=" " width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What started as a small fix turned into one of my favorite open source experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;At the time, &lt;code&gt;grape-swagger-rails&lt;/code&gt; was still using a very old Swagger UI (v0.7.0).&lt;/p&gt;

&lt;p&gt;The UI looked outdated and lacked many modern Swagger/OpenAPI features developers expect today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;

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

&lt;p&gt;Even the interaction model and styling clearly showed its age.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Typical Company Solution: Fork It
&lt;/h2&gt;

&lt;p&gt;In many companies I worked with, the usual approach for issues like this was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create an internal fork,&lt;/li&gt;
&lt;li&gt;patch the problem locally,&lt;/li&gt;
&lt;li&gt;never contribute changes upstream.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honestly, that's often the fastest short-term solution.&lt;/p&gt;

&lt;p&gt;But this time I wanted to try something different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing Upstream
&lt;/h2&gt;

&lt;p&gt;Instead of maintaining another private fork, I decided to contribute directly to the project.&lt;/p&gt;

&lt;p&gt;I reached out to the grape community and started working with maintainers, especially @dblock.&lt;/p&gt;

&lt;p&gt;Together we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;upgraded the Swagger UI integration,&lt;/li&gt;
&lt;li&gt;improved compatibility,&lt;/li&gt;
&lt;li&gt;modernized the frontend,&lt;/li&gt;
&lt;li&gt;fixed bugs,&lt;/li&gt;
&lt;li&gt;and prepared a major release.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That work became:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grape-swagger-rails v1.0.0&lt;/li&gt;
&lt;li&gt;followed by two additional bug-fix releases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;The project now ships with a much more modern Swagger experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  After
&lt;/h3&gt;

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

&lt;p&gt;Compared to the old endpoint rendering:&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%2F28fwphxrs700yjpnu8ll.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%2F28fwphxrs700yjpnu8ll.png" alt=" " width="800" height="978"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Becoming Part of the Community
&lt;/h2&gt;

&lt;p&gt;One thing I definitely didn't expect when opening my first contribution was eventually becoming part of the grape team.&lt;/p&gt;

&lt;p&gt;That was honestly a very happy moment for me.&lt;/p&gt;

&lt;p&gt;This experience reminded me that contributing upstream can have a much bigger impact than solving problems only locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Project: &lt;a href="https://github.com/ruby-grape/grape-swagger-rails" rel="noopener noreferrer"&gt;https://github.com/ruby-grape/grape-swagger-rails&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;v1.0.0 Release: &lt;a href="https://github.com/ruby-grape/grape-swagger-rails/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;https://github.com/ruby-grape/grape-swagger-rails/releases/tag/v1.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Changelog: &lt;a href="https://github.com/ruby-grape/grape-swagger-rails/blob/master/CHANGELOG.md#100-20260509" rel="noopener noreferrer"&gt;https://github.com/ruby-grape/grape-swagger-rails/blob/master/CHANGELOG.md#100-20260509&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Big thanks to the grape community and especially to @dblock for the collaboration and support throughout the releases.&lt;/p&gt;

</description>
      <category>api</category>
      <category>opensource</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>SafeMigrations: A Rails Gem for Easy Migrations</title>
      <dc:creator>Niko</dc:creator>
      <pubDate>Sat, 25 Oct 2025 01:35:55 +0000</pubDate>
      <link>https://dev.to/nikom/safemigrations-a-rails-gem-for-easy-migrations-34h4</link>
      <guid>https://dev.to/nikom/safemigrations-a-rails-gem-for-easy-migrations-34h4</guid>
      <description>&lt;p&gt;I've spent years working on Rails projects, and migrations have often been a source of frustration. Have you ever had a migration fail because a table or column already existed? Or seen a rollback cause issues because Rails' change method couldn't handle custom logic?&lt;br&gt;
I faced these problems repeatedly, which led me to create &lt;a href="https://github.com/moskvin/safe_migrations" rel="noopener noreferrer"&gt;safe_migrations&lt;/a&gt;, a gem that makes Rails migrations idempotent and reversible while preserving the change method's simplicity. Here's the story of how I built it and why you should try it.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Migration Problem
&lt;/h3&gt;

&lt;p&gt;In one Rails 4.2 project, I encountered migrations like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddUseLogoutPageToAccounts&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;4.2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;column_exists?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:use_logout_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:use_logout_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;column_exists? || add_column&lt;/code&gt; check was necessary to prevent errors when re-running migrations, but it was verbose and error-prone. Worse, rollbacks (&lt;code&gt;rails db:rollback&lt;/code&gt;) were problematic. Rails' change method is designed to automatically reverse operations, but it didn't understand my custom checks. If the column existed before the migration, &lt;code&gt;add_column&lt;/code&gt; would skip, but the rollback might still call remove_column, risking unintended schema changes.&lt;br&gt;
To address this, I created a helper module with a &lt;code&gt;safe_add_column&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;SafeMigrationHelper&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;safe_add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;column_exists?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allowed cleaner migrations:&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;AddUseLogoutPageToAccounts&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;4.2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;safe_add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:use_logout_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="kp"&gt;false&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;Running &lt;code&gt;rails db:migrate&lt;/code&gt; or &lt;code&gt;VERSION=20180801004438 rails db:migrate:up&lt;/code&gt; worked well - no errors if the column existed. However, rollbacks remained an issue. Rails' CommandRecorder didn't recognize &lt;code&gt;safe_add_column&lt;/code&gt;, so &lt;code&gt;rails db:rollback&lt;/code&gt; wouldn’t invert it correctly. If the column pre-existed, the migration skipped adding it, but the rollback might still attempt to remove it.&lt;br&gt;
With time constraints, we used explicit up and down methods:&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;AddUseLogoutPageToAccounts&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;4.2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;
    &lt;span class="n"&gt;safe_add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:use_logout_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="kp"&gt;false&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;down&lt;/span&gt;
    &lt;span class="n"&gt;safe_remove_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:use_logout_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was reliable but lost the elegance of the change method. I wanted a solution that combined idempotency with automatic rollbacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing safe_migrations
&lt;/h3&gt;

&lt;p&gt;After further development, I created the safe_migrations gem to solve these issues. It provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Idempotent Methods&lt;/strong&gt;: Methods like &lt;code&gt;safe_create_table&lt;/code&gt;, &lt;code&gt;safe_add_column&lt;/code&gt;, and &lt;code&gt;safe_add_index&lt;/code&gt; check for existing schema elements before executing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CommandRecorder Integration&lt;/strong&gt;: Extends &lt;code&gt;ActiveRecord::Migration::CommandRecorder&lt;/code&gt; to register &lt;code&gt;safe_&lt;/code&gt; methods and their inverses (e.g., &lt;code&gt;safe_add_column&lt;/code&gt; to &lt;code&gt;safe_remove_column&lt;/code&gt;), enabling automatic rollbacks in change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rails 7 Support&lt;/strong&gt;: Built for Rails 7.0+ and Ruby 3.2, with RSpec tests for reliability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddUseLogoutPageToAccounts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;7.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;safe_add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:use_logout_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;safe_add_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:use_logout_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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;rails db:migrate&lt;/code&gt;, and it adds the column and index only if needed. Run &lt;code&gt;rails db:rollback&lt;/code&gt;, and &lt;code&gt;CommandRecorder&lt;/code&gt; inverts to &lt;code&gt;safe_remove_column&lt;/code&gt; and &lt;code&gt;safe_remove_index&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Important Caveat
&lt;/h3&gt;

&lt;p&gt;One limitation: CommandRecorder inverts all commands in change, even if they didn’t execute. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateAccounts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;7.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;safe_create_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:accounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;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;:name&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;ul&gt;
&lt;li&gt;
&lt;code&gt;up&lt;/code&gt;: &lt;code&gt;safe_create_table&lt;/code&gt; skips if &lt;code&gt;:accounts&lt;/code&gt; exists.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;down&lt;/code&gt;: CommandRecorder calls safe_drop_table, which may drop a pre-existing table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I believe this is a rare scenario in most projects, and &lt;code&gt;safe_migrations&lt;/code&gt; is reliable for typical use cases, but I'm eager to hear your ideas for further improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try safe_migrations
&lt;/h3&gt;

&lt;p&gt;If migrations have caused you headaches, safe_migrations can help. Install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;safe_migrations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or add to your Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'safe_migrations'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 1.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the GitHub repo for docs and examples. It's open-source (MIT license), so contributions are welcome!&lt;/p&gt;

&lt;h3&gt;
  
  
  Your Feedback Matters
&lt;/h3&gt;

&lt;p&gt;I built &lt;code&gt;safe_migrations&lt;/code&gt; to simplify Rails migrations, but I'd love to hear your experiences. Have you faced similar migration challenges? How does the gem work for you? Have ideas to improve it? Comment below, open a GitHub issue, or reach out. Let's make migrations reliable together!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/moskvin/safe_migrations" rel="noopener noreferrer"&gt;safe_migrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>rubygems</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
