<?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: Luis Donis</title>
    <description>The latest articles on DEV Community by Luis Donis (@ldonis).</description>
    <link>https://dev.to/ldonis</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%2F91674%2F3996af64-3028-41c6-a094-d7ff8e5d2aef.jpg</url>
      <title>DEV Community: Luis Donis</title>
      <link>https://dev.to/ldonis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ldonis"/>
    <language>en</language>
    <item>
      <title>How I Structure a Modular Rails SaaS Application</title>
      <dc:creator>Luis Donis</dc:creator>
      <pubDate>Wed, 11 Mar 2026 03:00:00 +0000</pubDate>
      <link>https://dev.to/ldonis/how-i-structure-a-modular-rails-saas-application-1l74</link>
      <guid>https://dev.to/ldonis/how-i-structure-a-modular-rails-saas-application-1l74</guid>
      <description>&lt;h2&gt;
  
  
  How I Structure a Modular Rails SaaS Application
&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%2Flooqkhlp5zf06kpwk8i6.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%2Flooqkhlp5zf06kpwk8i6.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my previous post, I wrote about the Rails SaaS architecture I wish I had 5 years ago.&lt;/p&gt;

&lt;p&gt;The main idea was simple: once a Rails application grows into a real SaaS product, the problem is no longer just writing features quickly.&lt;/p&gt;

&lt;p&gt;The real challenge becomes &lt;strong&gt;keeping the system understandable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That naturally leads to the next question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What does that structure actually look like in practice?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post is my attempt to answer that.&lt;/p&gt;

&lt;p&gt;It is not the only way to organize a Rails app.&lt;br&gt;
It is just the structure that currently makes the most sense to me for products with multiple business capabilities, internal tooling, and long-term growth.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem With the Default Growth Path
&lt;/h2&gt;

&lt;p&gt;Most Rails applications begin with a very reasonable structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;app/
config/
db/
lib/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works well at the beginning.&lt;/p&gt;

&lt;p&gt;But as the product grows, many business capabilities start living side by side in the same application layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;roles and permissions&lt;/li&gt;
&lt;li&gt;notifications&lt;/li&gt;
&lt;li&gt;dashboards&lt;/li&gt;
&lt;li&gt;auditing&lt;/li&gt;
&lt;li&gt;support tickets&lt;/li&gt;
&lt;li&gt;file management&lt;/li&gt;
&lt;li&gt;billing logic&lt;/li&gt;
&lt;li&gt;admin tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, the issue is not that Rails is bad.&lt;/p&gt;

&lt;p&gt;The issue is that everything starts competing for space inside the same app boundaries.&lt;/p&gt;

&lt;p&gt;Models become aware of too much.&lt;br&gt;
Controllers start coordinating unrelated concerns.&lt;br&gt;
Helpers grow in weird directions.&lt;br&gt;
Concerns multiply.&lt;br&gt;
And eventually, the application feels large even when individual features are not that complex.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Shift: Structure by Capability
&lt;/h2&gt;

&lt;p&gt;What has worked better for me is structuring the system by &lt;strong&gt;business capability&lt;/strong&gt; instead of only by technical layer.&lt;/p&gt;

&lt;p&gt;So instead of thinking only in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;models&lt;/li&gt;
&lt;li&gt;controllers&lt;/li&gt;
&lt;li&gt;views&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;support&lt;/li&gt;
&lt;li&gt;audit&lt;/li&gt;
&lt;li&gt;admin&lt;/li&gt;
&lt;li&gt;accounts&lt;/li&gt;
&lt;li&gt;users&lt;/li&gt;
&lt;li&gt;dashboards&lt;/li&gt;
&lt;li&gt;billing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of those capabilities gets its own boundary.&lt;/p&gt;

&lt;p&gt;In Rails, the cleanest way I have found to do that is with &lt;strong&gt;engines&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  A Simple High-Level Structure
&lt;/h2&gt;

&lt;p&gt;A modular Rails SaaS application might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;my_app/
├── app/
├── config/
├── db/
├── lib/
├── engines/
│   ├── lesli_core/
│   ├── lesli_admin/
│   ├── lesli_audit/
│   ├── lesli_billing/
│   ├── lesli_dashboard/
│   ├── lesli_shield/
│   └── lesli_support/
└── Gemfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main application still exists.&lt;br&gt;
But it is no longer the place where every feature gets dumped.&lt;/p&gt;

&lt;p&gt;Instead, the main app acts more like the integration layer.&lt;/p&gt;

&lt;p&gt;The engines contain the actual business capabilities.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Belongs in the Main App?
&lt;/h2&gt;

&lt;p&gt;This is the part that matters most.&lt;/p&gt;

&lt;p&gt;A modular structure only works if the main app stays disciplined.&lt;/p&gt;

&lt;p&gt;In my case, the main Rails app is usually responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;environment configuration&lt;/li&gt;
&lt;li&gt;deployment configuration&lt;/li&gt;
&lt;li&gt;boot process&lt;/li&gt;
&lt;li&gt;mounting engines&lt;/li&gt;
&lt;li&gt;app-specific branding and overrides&lt;/li&gt;
&lt;li&gt;product-specific custom logic&lt;/li&gt;
&lt;li&gt;final composition of the system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the app is still important.&lt;br&gt;
It just stops pretending to own every domain directly.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Belongs in an Engine?
&lt;/h2&gt;

&lt;p&gt;Each engine owns a clear capability.&lt;/p&gt;

&lt;p&gt;For example, a support engine might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;engines/lesli_support/
├── app/
│   ├── controllers/
│   ├── models/
│   ├── views/
│   └── components/
├── config/
│   └── routes.rb
├── db/
│   └── migrate/
├── lib/
│   └── lesli_support/
├── lesli_support.gemspec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That engine can own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tickets&lt;/li&gt;
&lt;li&gt;comments or discussions&lt;/li&gt;
&lt;li&gt;statuses&lt;/li&gt;
&lt;li&gt;priorities&lt;/li&gt;
&lt;li&gt;assignment flows&lt;/li&gt;
&lt;li&gt;support-specific dashboards&lt;/li&gt;
&lt;li&gt;notifications related to support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That creates something extremely valuable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;local reasoning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When I need to work on support, I go to the support engine.&lt;br&gt;
I am not hunting through a giant application trying to remember where ticket logic leaked into other layers.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Role of a Core Layer
&lt;/h2&gt;

&lt;p&gt;I still like having a shared core layer.&lt;/p&gt;

&lt;p&gt;But I think it should stay small and intentional.&lt;/p&gt;

&lt;p&gt;For me, a core engine usually contains things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shared concerns that are truly cross-cutting&lt;/li&gt;
&lt;li&gt;shared UI primitives&lt;/li&gt;
&lt;li&gt;base classes&lt;/li&gt;
&lt;li&gt;common helpers&lt;/li&gt;
&lt;li&gt;platform configuration helpers&lt;/li&gt;
&lt;li&gt;common interfaces between engines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I try to avoid is turning the core layer into a second monolith.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;core&lt;/code&gt; becomes the place where every engine dumps shared shortcuts, then the architecture slowly collapses back into the same problem.&lt;/p&gt;

&lt;p&gt;So the question I ask is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this really cross-cutting, or am I just avoiding a cleaner boundary?&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Routing and Composition
&lt;/h2&gt;

&lt;p&gt;One thing I like about this approach is that composition stays explicit.&lt;/p&gt;

&lt;p&gt;The main application decides what gets mounted.&lt;/p&gt;

&lt;p&gt;A simplified example might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;LesliAdmin&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;at: &lt;/span&gt;&lt;span class="s2"&gt;"/admin"&lt;/span&gt;
  &lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;LesliSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;at: &lt;/span&gt;&lt;span class="s2"&gt;"/support"&lt;/span&gt;
  &lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;LesliAudit&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;at: &lt;/span&gt;&lt;span class="s2"&gt;"/audit"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the final application shape easy to understand.&lt;/p&gt;

&lt;p&gt;The product is not a mystery.&lt;br&gt;
It is a composition of capabilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  Boundaries Matter More Than Reuse
&lt;/h2&gt;

&lt;p&gt;A nice side effect of engines is reuse.&lt;/p&gt;

&lt;p&gt;But honestly, that is not the main reason I like them.&lt;/p&gt;

&lt;p&gt;The bigger benefit is &lt;strong&gt;enforced boundaries&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Without boundaries, every feature eventually reaches into everything else.&lt;/p&gt;

&lt;p&gt;With boundaries, integration has to be intentional.&lt;/p&gt;

&lt;p&gt;That changes how the codebase grows.&lt;/p&gt;

&lt;p&gt;You start asking better questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should this dependency exist?&lt;/li&gt;
&lt;li&gt;Does support really need to know about billing internals?&lt;/li&gt;
&lt;li&gt;Is this concern shared, or just misplaced?&lt;/li&gt;
&lt;li&gt;Should this logic live in the app, the engine, or the core layer?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those questions improve architecture more than any naming convention ever will.&lt;/p&gt;




&lt;h2&gt;
  
  
  This Is Not Free
&lt;/h2&gt;

&lt;p&gt;To be fair, this kind of structure also adds some overhead.&lt;/p&gt;

&lt;p&gt;You have to think more about things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;engine naming&lt;/li&gt;
&lt;li&gt;domain boundaries&lt;/li&gt;
&lt;li&gt;dependency direction&lt;/li&gt;
&lt;li&gt;migrations across engines&lt;/li&gt;
&lt;li&gt;shared conventions&lt;/li&gt;
&lt;li&gt;local development workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I would not use this for every project.&lt;/p&gt;

&lt;p&gt;If you are building a small internal tool, an MVP, or something you need to ship very quickly, this can feel like too much structure too early.&lt;/p&gt;

&lt;p&gt;But when the product starts growing across multiple capabilities, that extra structure stops feeling heavy and starts feeling useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Like Most About This Approach
&lt;/h2&gt;

&lt;p&gt;What I like most is not that it feels more “advanced.”&lt;/p&gt;

&lt;p&gt;It is that it makes the system easier to work with.&lt;/p&gt;

&lt;p&gt;When I add a feature, I have a better idea of where it belongs.&lt;br&gt;
When I debug something, I am not jumping across unrelated parts of the app.&lt;br&gt;
When I refactor, I feel a little more confident that I am not breaking everything around it.&lt;/p&gt;

&lt;p&gt;For me, that is what good architecture should do.&lt;/p&gt;

&lt;p&gt;Not win abstraction points.&lt;br&gt;
Not look impressive on a diagram.&lt;/p&gt;

&lt;p&gt;Just make the app easier to understand as it grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  How This Connects to Lesli
&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%2Flooqkhlp5zf06kpwk8i6.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%2Flooqkhlp5zf06kpwk8i6.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the same general direction I have been exploring while building Lesli.&lt;/p&gt;

&lt;p&gt;I am not trying to make Rails more complicated.&lt;br&gt;
I am mostly trying to give larger SaaS-style applications a cleaner place to grow.&lt;/p&gt;

&lt;p&gt;Lesli is still evolving, but this modular approach is one of the ideas behind it.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>saas</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Rails SaaS Architecture I Wish I Had 5 Years Ago</title>
      <dc:creator>Luis Donis</dc:creator>
      <pubDate>Sun, 22 Feb 2026 02:24:00 +0000</pubDate>
      <link>https://dev.to/ldonis/the-rails-saas-architecture-i-wish-i-had-5-years-ago-3nph</link>
      <guid>https://dev.to/ldonis/the-rails-saas-architecture-i-wish-i-had-5-years-ago-3nph</guid>
      <description>&lt;h2&gt;
  
  
  The Rails SaaS Architecture I Wish I Had 5 Years Ago
&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%2Fy4e95ynr5yvpb0ey6pqk.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%2Fy4e95ynr5yvpb0ey6pqk.png" alt="Modular rails app diagram" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Five years ago, I started building SaaS applications with Ruby on Rails.&lt;/p&gt;

&lt;p&gt;Like most Rails apps, everything started beautifully simple.&lt;/p&gt;

&lt;p&gt;A few models. &lt;br&gt;
A few controllers. &lt;br&gt;
Some concerns. &lt;br&gt;
Some service objects. &lt;/p&gt;

&lt;p&gt;It felt clean.&lt;/p&gt;

&lt;p&gt;Six months later?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business logic everywhere&lt;/li&gt;
&lt;li&gt;“Temporary” modules that became permanent&lt;/li&gt;
&lt;li&gt;Cross-cutting features tangled across the app&lt;/li&gt;
&lt;li&gt;Shared helpers doing too much&lt;/li&gt;
&lt;li&gt;Growing fear of refactoring core features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The app still worked — but the architecture wasn’t intentional anymore.&lt;/p&gt;

&lt;p&gt;And that’s the real problem.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Pattern I Kept Repeating
&lt;/h2&gt;

&lt;p&gt;Every SaaS I built ended up having the same core capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Roles &amp;amp; permissions&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;li&gt;Audit logs&lt;/li&gt;
&lt;li&gt;Dashboards&lt;/li&gt;
&lt;li&gt;Support tickets&lt;/li&gt;
&lt;li&gt;Document management&lt;/li&gt;
&lt;li&gt;Account scoping / multi-tenancy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I kept rebuilding them inside the main app.&lt;/p&gt;

&lt;p&gt;Every. Single. Time.&lt;/p&gt;

&lt;p&gt;And every time, they slowly leaked into everything else.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Architecture Shift
&lt;/h2&gt;

&lt;p&gt;At some point I started asking myself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if SaaS capabilities weren’t just folders inside &lt;code&gt;/app&lt;/code&gt;?&lt;br&gt;
What if they were isolated systems?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of structuring only by MVC, I started structuring by &lt;strong&gt;capability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each major SaaS feature became its own Rails engine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth engine&lt;/li&gt;
&lt;li&gt;Support engine&lt;/li&gt;
&lt;li&gt;Audit engine&lt;/li&gt;
&lt;li&gt;Dashboard engine&lt;/li&gt;
&lt;li&gt;Admin engine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each engine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Owns its models&lt;/li&gt;
&lt;li&gt;Owns its controllers&lt;/li&gt;
&lt;li&gt;Owns its routes&lt;/li&gt;
&lt;li&gt;Owns its views&lt;/li&gt;
&lt;li&gt;Owns its database tables&lt;/li&gt;
&lt;li&gt;Has explicit integration points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No leaking constants.\&lt;br&gt;
No random cross-feature helpers.\&lt;br&gt;
No accidental coupling.&lt;/p&gt;

&lt;p&gt;Just clear boundaries.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Engines?
&lt;/h2&gt;

&lt;p&gt;Rails engines are often misunderstood.&lt;/p&gt;

&lt;p&gt;Many developers think they’re only for gems or mountable admin panels.&lt;/p&gt;

&lt;p&gt;But for SaaS architecture, they provide something incredibly valuable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structural isolation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When features live in engines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can reason about them independently&lt;/li&gt;
&lt;li&gt;You can test them in isolation&lt;/li&gt;
&lt;li&gt;You can disable or replace them&lt;/li&gt;
&lt;li&gt;You reduce accidental coupling&lt;/li&gt;
&lt;li&gt;You avoid the “god app” problem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main Rails app becomes the orchestrator — not the dumping ground.&lt;/p&gt;


&lt;h2&gt;
  
  
  What This Changes in Practice
&lt;/h2&gt;

&lt;p&gt;Instead of 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="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&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;rb&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;audit_log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get something 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="n"&gt;engines&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;auth&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;rb&lt;/span&gt;
&lt;span class="n"&gt;engines&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;support&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;support&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;span class="n"&gt;engines&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;audit&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;audit&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clear ownership.&lt;/p&gt;

&lt;p&gt;Clear responsibility.&lt;/p&gt;

&lt;p&gt;Clear boundaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Benefit: Cognitive Load
&lt;/h2&gt;

&lt;p&gt;The biggest improvement wasn’t performance.&lt;/p&gt;

&lt;p&gt;It wasn’t scalability.&lt;/p&gt;

&lt;p&gt;It was &lt;strong&gt;mental clarity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a bug appears in support logic, I know exactly where to go.&lt;/p&gt;

&lt;p&gt;When I need to extend auditing, I don’t fear breaking unrelated features.&lt;/p&gt;

&lt;p&gt;Architecture stops being accidental.&lt;/p&gt;

&lt;p&gt;It becomes intentional.&lt;/p&gt;




&lt;h2&gt;
  
  
  Is This Over-Engineering?
&lt;/h2&gt;

&lt;p&gt;For small apps?&lt;/p&gt;

&lt;p&gt;Probably.&lt;/p&gt;

&lt;p&gt;For serious SaaS products?&lt;/p&gt;

&lt;p&gt;Not really.&lt;/p&gt;

&lt;p&gt;Once your application has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple accounts&lt;/li&gt;
&lt;li&gt;Role systems&lt;/li&gt;
&lt;li&gt;Permissions&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;li&gt;Background processing&lt;/li&gt;
&lt;li&gt;Operational tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’re no longer building “just a Rails app.”&lt;/p&gt;

&lt;p&gt;You’re building infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I’m Doing Now
&lt;/h2&gt;

&lt;p&gt;I’m currently building an open-source Rails framework based on this idea.&lt;/p&gt;

&lt;p&gt;Each SaaS capability lives in its own engine.&lt;/p&gt;

&lt;p&gt;The goal isn’t to abstract everything.&lt;/p&gt;

&lt;p&gt;The goal is to start with intentional structure — instead of cleaning up chaos later.&lt;/p&gt;

&lt;p&gt;It’s still evolving.&lt;/p&gt;

&lt;p&gt;I’m still learning.&lt;/p&gt;

&lt;p&gt;But it already feels more sustainable than anything I built five years ago.&lt;/p&gt;

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