<?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: Zil Norvilis</title>
    <description>The latest articles on DEV Community by Zil Norvilis (@zilton7).</description>
    <link>https://dev.to/zilton7</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%2F352433%2F2aff65b6-dba1-4f8c-8aaf-0bc84763ae23.jpg</url>
      <title>DEV Community: Zil Norvilis</title>
      <link>https://dev.to/zilton7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zilton7"/>
    <language>en</language>
    <item>
      <title>MVT vs MVC: Understanding Django Through a Rails Lens</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Fri, 05 Jun 2026 18:14:39 +0000</pubDate>
      <link>https://dev.to/zilton7/mvt-vs-mvc-understanding-django-through-a-rails-lens-4a31</link>
      <guid>https://dev.to/zilton7/mvt-vs-mvc-understanding-django-through-a-rails-lens-4a31</guid>
      <description>&lt;p&gt;If you are a Rails developer, you are used to the "Omakase" experience. Everything is chosen for you, and there is a very specific "Rails Way" to do things. &lt;/p&gt;

&lt;p&gt;But sometimes, a project requires Python - maybe because of a specific AI library or a client’s existing infrastructure. When you first open a Django project, it feels like walking into a house where all the furniture is in the wrong room. The concepts are the same, but the names are all different.&lt;/p&gt;

&lt;p&gt;Django is essentially the Python cousin of Rails. It values batteries-included development and clean architecture. Here is the translation guide to help you move between Ruby and Python without losing your mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Pattern: MVT vs. MVC
&lt;/h2&gt;

&lt;p&gt;Rails follows &lt;strong&gt;MVC&lt;/strong&gt; (Model-View-Controller). &lt;br&gt;
Django follows &lt;strong&gt;MVT&lt;/strong&gt; (Model-View-Template). &lt;/p&gt;

&lt;p&gt;This is the biggest point of confusion for Rails devs. In Django:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The Model&lt;/strong&gt; is exactly the same as Rails.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The View&lt;/strong&gt; is actually the &lt;strong&gt;Controller&lt;/strong&gt;. It’s where the logic lives.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Template&lt;/strong&gt; is the &lt;strong&gt;View&lt;/strong&gt;. It’s the HTML/ERB equivalent.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rails Concept&lt;/th&gt;
&lt;th&gt;Django Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controller&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;View (ERB)&lt;/td&gt;
&lt;td&gt;Template&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  2. The CLI: &lt;code&gt;manage.py&lt;/code&gt; is your &lt;code&gt;bin/rails&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In Rails, you use the &lt;code&gt;rails&lt;/code&gt; command for everything. In Django, every project comes with a script called &lt;code&gt;manage.py&lt;/code&gt; that handles the same tasks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Start the server:&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;  Rails: &lt;code&gt;bin/rails s&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Django: &lt;code&gt;python manage.py runserver&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Database Console:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  Rails: &lt;code&gt;bin/rails dbconsole&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Django: &lt;code&gt;python manage.py dbshell&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Interactive REPL:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  Rails: &lt;code&gt;bin/rails c&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Django: &lt;code&gt;python manage.py shell&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  3. The ORM: Django ORM vs. ActiveRecord
&lt;/h2&gt;

&lt;p&gt;ActiveRecord is famous for its "magic." You don't have to import models; they are just there. Django is more explicit. You have to import your models at the top of every file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Querying:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rails&lt;/span&gt;
&lt;span class="vi"&gt;@users&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;active: &lt;/span&gt;&lt;span class="kp"&gt;true&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&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Django
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="n"&gt;users&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="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Migrations:&lt;/strong&gt;&lt;br&gt;
Rails migrations are timestamped files where you write the change. Django does this in two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;python manage.py makemigrations&lt;/code&gt;&lt;/strong&gt;: Django looks at your &lt;code&gt;models.py&lt;/code&gt;, compares it to the last version, and &lt;em&gt;automatically&lt;/em&gt; generates the migration file for you.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;python manage.py migrate&lt;/code&gt;&lt;/strong&gt;: This actually runs the changes on the database.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  4. Routing: &lt;code&gt;urls.py&lt;/code&gt; vs. &lt;code&gt;routes.rb&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Rails uses a DSL for routing. Django uses a list of &lt;code&gt;path&lt;/code&gt; objects and regex-like patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rails routes.rb&lt;/span&gt;
&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/products/:id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'products#show'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Django urls.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;products/&amp;lt;int:id&amp;gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_detail&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. The "Superpower": Django Admin
&lt;/h2&gt;

&lt;p&gt;In Rails, we usually install a gem like &lt;code&gt;Avo&lt;/code&gt; or &lt;code&gt;ActiveAdmin&lt;/code&gt; to get an internal dashboard. In Django, this is &lt;strong&gt;built-in&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By adding two lines of code to &lt;code&gt;admin.py&lt;/code&gt;, you get a professional, secure CRUD interface for your models. This is the single biggest reason Rails developers often feel "Admin Envy" when looking at Python projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Dependency Management
&lt;/h2&gt;

&lt;p&gt;Instead of a &lt;code&gt;Gemfile&lt;/code&gt; and &lt;code&gt;Gemfile.lock&lt;/code&gt;, Django developers traditionally used a &lt;code&gt;requirements.txt&lt;/code&gt; file. However, modern Python development has moved to &lt;strong&gt;Poetry&lt;/strong&gt; or &lt;strong&gt;uv&lt;/strong&gt;, which uses a &lt;code&gt;pyproject.toml&lt;/code&gt; file—this feels much more like the Bundler experience you are used to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you can write a Rails app, you can write a Django app. You just have to remember that &lt;strong&gt;Django Views = Rails Controllers&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The transition is mostly about getting used to Python’s explicit nature. While Rails loves to hide things to make code look "pretty," Django prefers that you see exactly where everything is coming from.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Don't forget to test!&lt;/strong&gt;&lt;br&gt;
Whether you are building in Ruby or Python, the most important thing is a reliable test suite. If you want to see how I handle testing for solo projects to ensure they actually work in production, check out &lt;strong&gt;&lt;a href="https://norvilis.gumroad.com/l/wise-testing" rel="noopener noreferrer"&gt;Wise Testing: The Solo Developer's Guide to Rails Testing&lt;/a&gt;&lt;/strong&gt;. It’s the pragmatic approach to shipping fast.&lt;/p&gt;
&lt;/blockquote&gt;




</description>
      <category>django</category>
      <category>rails</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The $5 VPS Reality: My Honest Math on Moving to Kamal 2</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Thu, 04 Jun 2026 18:14:39 +0000</pubDate>
      <link>https://dev.to/zilton7/the-5-vps-reality-my-honest-math-on-moving-to-kamal-2-544k</link>
      <guid>https://dev.to/zilton7/the-5-vps-reality-my-honest-math-on-moving-to-kamal-2-544k</guid>
      <description>&lt;p&gt;I remember the day I got my first $200 bill from a PaaS provider (Platform as a Service). &lt;/p&gt;

&lt;p&gt;I only had a few side projects running. None of them were making much money, but because I needed a "Standard" database for one and an extra "Worker" for another, the costs just started stacking up. I felt like I was paying a "Laziness Tax" just to avoid managing a server.&lt;/p&gt;

&lt;p&gt;In 2026, the trend has reversed. Tools like &lt;strong&gt;Kamal 2&lt;/strong&gt; and &lt;strong&gt;Rails 8&lt;/strong&gt; have made it incredibly easy to leave the expensive "managed" clouds and host everything yourself on a cheap VPS.&lt;/p&gt;

&lt;p&gt;But is it actually cheaper? Most people only look at the monthly bill, but they forget about the "Time Tax." Here is my honest breakdown of the true cost of running your own cloud empire.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Financial Math (The "Win")
&lt;/h2&gt;

&lt;p&gt;Let’s look at a typical Rails setup for a small SaaS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Heroku/Render Way:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Web Service (2GB RAM): $25&lt;/li&gt;
&lt;li&gt;  Postgres Database (Managed): $30&lt;/li&gt;
&lt;li&gt;  Redis (for Sidekiq): $15&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Total: $70/month&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Self-Hosted Way (Hetzner/DigitalOcean):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  One VPS (ARM, 8GB RAM, 4 vCPUs): $10&lt;/li&gt;
&lt;li&gt;  Backups (S3/R2): $1&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Total: $11/month&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Savings: $59 per month / $708 per year.&lt;/strong&gt; &lt;br&gt;
If you have 5 different apps, you are saving over &lt;strong&gt;$3,500 a year&lt;/strong&gt;. For a solo developer living in Europe, that is a huge amount of money that can be spent on marketing or better coffee.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Maintenance Tax (The "Hidden Cost")
&lt;/h2&gt;

&lt;p&gt;This is where the PaaS providers justify their high prices. When you self-host, you are the CEO, the Developer, and now the &lt;strong&gt;System Administrator&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Security:&lt;/strong&gt; You are responsible for OS updates. If a new Linux vulnerability comes out, you have to run &lt;code&gt;apt-get upgrade&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Backups:&lt;/strong&gt; On Heroku, you click a button. On a VPS, you have to ensure your database is being streamed to S3 (using Litestream or a cron job). If your script fails and the server dies, your business is gone.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Monitoring:&lt;/strong&gt; If your server runs out of disk space at 3:00 AM, there is no support team to fix it. You need to set up your own alerts (like UptimeRobot or health checks).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;True Cost:&lt;/strong&gt; About 1–2 hours of "Server Maintenance" per month.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The "One-Person" Secret: Docker &amp;amp; Kamal 2
&lt;/h2&gt;

&lt;p&gt;The reason self-hosting is viable in 2026 is because we stopped managing servers manually. &lt;/p&gt;

&lt;p&gt;In the old days, you had to install Ruby, Nginx, and Postgres on the server. It was a nightmare. Today, we use &lt;strong&gt;Docker&lt;/strong&gt;. Your server is just a "dumb" box that runs containers. &lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Kamal 2&lt;/strong&gt;, deploying to a $5 VPS feels exactly like deploying to Heroku. You type &lt;code&gt;kamal deploy&lt;/code&gt;, and it handles the SSL, the zero-downtime switch, and the health checks. &lt;/p&gt;

&lt;p&gt;The "Learning Tax" to master Kamal takes about a weekend. Once you pay that tax, you never have to pay the "PaaS Tax" again.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Stability Myth
&lt;/h2&gt;

&lt;p&gt;People fear that a cheap VPS is less stable than AWS or Heroku. &lt;/p&gt;

&lt;p&gt;The truth? Modern VPS providers like Hetzner or Akamai have 99.9% uptime. Most "crashes" in a Rails app aren't caused by the hardware; they are caused by &lt;strong&gt;bad code&lt;/strong&gt; (N+1 queries, memory leaks, or unhandled exceptions). &lt;/p&gt;

&lt;p&gt;Whether you pay $7 or $700, a bug in your Ruby code will still crash the app. &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary: Should You Switch?
&lt;/h2&gt;

&lt;p&gt;If you are a solo developer, here is my recommendation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Stay on a PaaS (Render/Fly.io)&lt;/strong&gt; if you are in the first 48 hours of an idea. Don't waste time on infrastructure until you know the idea is worth it.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Switch to Self-Hosting (Kamal + VPS)&lt;/strong&gt; the moment your app starts making even $10 a month. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The $50+ you save every month covers your AI subscription (Cursor/ChatGPT), your domain names, and your email marketing tools. &lt;/p&gt;

&lt;p&gt;Building your own "Cloud Empire" isn't just about saving money; it’s about &lt;strong&gt;Ownership&lt;/strong&gt;. When you own the server, you have total control. And in the world of indie hacking, control is freedom.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>rails</category>
      <category>kamal</category>
      <category>startup</category>
    </item>
    <item>
      <title>From Coder to Architect: How to Learn Rails When AI Writes the Code</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Wed, 03 Jun 2026 18:14:28 +0000</pubDate>
      <link>https://dev.to/zilton7/from-coder-to-architect-how-to-learn-rails-when-ai-writes-the-code-3ljf</link>
      <guid>https://dev.to/zilton7/from-coder-to-architect-how-to-learn-rails-when-ai-writes-the-code-3ljf</guid>
      <description>&lt;p&gt;I recently realized that the way I learned Ruby on Rails ten years ago is now completely obsolete. &lt;/p&gt;

&lt;p&gt;Back then, I spent hundreds of hours watching tutorials like Railscasts or reading books to memorize syntax. I had to learn exactly how to write a &lt;code&gt;has_many :through&lt;/code&gt; association or how to structure a nested form. I was a &lt;strong&gt;Writer&lt;/strong&gt;. My value was my ability to produce correct code.&lt;/p&gt;

&lt;p&gt;In 2026, syntax is a commodity. With tools like Cursor and GitHub Copilot, the AI is the writer. I can prompt: &lt;em&gt;"Add a tagging system to my posts with a many-to-many relationship,"&lt;/em&gt; and the code is written in seconds. &lt;/p&gt;

&lt;p&gt;But here is the problem: &lt;strong&gt;AI is a great writer, but a terrible architect.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;If you don't know where the code &lt;em&gt;should&lt;/em&gt; go, the AI will dump it in the easiest place, usually making your controllers and models huge and messy. To survive in this "Post-Tutorial Era," you need to stop focusing on syntax and start focusing on &lt;strong&gt;Architecture&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Here is how to learn the "Rails Way" when you aren't the one typing the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Move from "How" to "Where"
&lt;/h2&gt;

&lt;p&gt;When you follow an old-school tutorial, it teaches you &lt;strong&gt;how&lt;/strong&gt; to write a method. In 2026, you should be asking &lt;strong&gt;where&lt;/strong&gt; that method belongs.&lt;/p&gt;

&lt;p&gt;If the AI suggests putting logic that talks to the Stripe API inside your &lt;code&gt;User&lt;/code&gt; model, you need to be able to say: &lt;em&gt;"No, that belongs in a Service Object in &lt;code&gt;app/services&lt;/code&gt;."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to learn this:&lt;/strong&gt; Study the &lt;strong&gt;Rails Sidecar&lt;/strong&gt; pattern. Look at how modern apps use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Service Objects:&lt;/strong&gt; For third-party API logic.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;ViewComponents:&lt;/strong&gt; For complex UI logic.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jobs:&lt;/strong&gt; For anything that takes more than 100ms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Audit the AI (The "Why" Test)
&lt;/h2&gt;

&lt;p&gt;Don't just hit "Tab" to accept AI suggestions. Every time the AI generates a block of code, perform a manual audit. Ask yourself: &lt;em&gt;"Why did it use a callback instead of a background job?"&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;If you don't know the answer, ask the AI to explain the trade-offs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why is an &lt;code&gt;after_create&lt;/code&gt; callback dangerous here compared to a background job?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is how you learn architecture today. You treat the AI as a junior developer and yourself as the &lt;strong&gt;Editor-in-Chief&lt;/strong&gt;. By forcing the AI to justify its architectural choices, you learn the underlying principles of the framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Learn "Omakase" Constraints
&lt;/h2&gt;

&lt;p&gt;Rails is an "Omakase" framework (the chef chooses the ingredients). It is built on &lt;strong&gt;Convention over Configuration&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The reason AI is so good at writing Rails code is because Rails has very strict conventions. If you try to be "clever" and invent your own folder structure or naming patterns, the AI will get confused and start hallucinating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Strategy:&lt;/strong&gt; Lean into the defaults. The more you follow the standard Rails 8 patterns (Solid Queue, Hotwire, etc.), the more the AI becomes a superpower. Architecture in 2026 is about keeping the "wiring" of your app so standard that the AI can perfectly navigate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Read the Rails Source Code
&lt;/h2&gt;

&lt;p&gt;Since you aren't spending time memorizing syntax anymore, use that extra brainpower to read the actual &lt;code&gt;rails/rails&lt;/code&gt; source code on GitHub. &lt;/p&gt;

&lt;p&gt;If you understand how &lt;code&gt;ActiveJob&lt;/code&gt; actually works under the hood, you will know exactly how to architect your background tasks. You don't need a tutorial to show you a "To-Do List" app; you need to understand the &lt;strong&gt;Core Patterns&lt;/strong&gt; of the framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The era of the "Tutorial-Driven Developer" is over. If you only know how to follow instructions, an AI can already do your job.&lt;/p&gt;

&lt;p&gt;To be a valuable developer in 2026, you must become an &lt;strong&gt;Architect&lt;/strong&gt;. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Stop memorizing syntax.&lt;/strong&gt; The AI handles that.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Learn patterns.&lt;/strong&gt; Know when to use a Concern vs. a Service.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Enforce standards.&lt;/strong&gt; Use &lt;code&gt;.cursorrules&lt;/code&gt; or RuboCop to keep the AI on track.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Understand the "Why."&lt;/strong&gt; Always ask the AI for the trade-offs of its suggestions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code is now free. The &lt;strong&gt;Taste&lt;/strong&gt; and &lt;strong&gt;Vision&lt;/strong&gt; of how that code fits together is where the money is.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ai</category>
      <category>architecture</category>
      <category>career</category>
    </item>
    <item>
      <title>Stop Using JS for Everything: Harnessing the Power of Pure CSS in 2026</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Tue, 02 Jun 2026 18:14:28 +0000</pubDate>
      <link>https://dev.to/zilton7/stop-using-js-for-everything-harnessing-the-power-of-pure-css-in-2026-3ipm</link>
      <guid>https://dev.to/zilton7/stop-using-js-for-everything-harnessing-the-power-of-pure-css-in-2026-3ipm</guid>
      <description>&lt;p&gt;I remember when building a simple dropdown menu or a sticky header required a library like jQuery. Later, we moved to writing Stimulus controllers or React hooks for every single tiny interaction on the screen. &lt;/p&gt;

&lt;p&gt;As a developer, my instinct was always: &lt;em&gt;"If it moves, write JavaScript."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But in 2026, the browser has changed. CSS has evolved so much that many of the things we used to do in JS are now built natively into the stylesheet. Moving this logic to CSS isn't just about being "cool" - it makes your site significantly faster, reduces "layout shift," and means you have less code to maintain.&lt;/p&gt;

&lt;p&gt;Here is how I’ve started replacing JavaScript with pure CSS in my Rails 8 projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The "Parent" Selector (&lt;code&gt;:has&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;For decades, we wanted a way to style an element based on what was &lt;em&gt;inside&lt;/em&gt; it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Old Way (JS):&lt;/strong&gt;&lt;br&gt;
You would write a script to check if a checkbox was ticked, then add a class like &lt;code&gt;.is-active&lt;/code&gt; to the parent container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 2026 Way (CSS):&lt;/strong&gt;&lt;br&gt;
We now have the &lt;code&gt;:has()&lt;/code&gt; selector. It is a game-changer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Style the card only if it contains a checked checkbox */&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"checkbox"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f0fdf4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#22c55e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This replaces hundreds of lines of "state-toggling" JavaScript. You can use it for form validation, menu states, and complex grid layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Native Popovers (The &lt;code&gt;popover&lt;/code&gt; Attribute)
&lt;/h2&gt;

&lt;p&gt;Tooltips and dropdowns are usually the first things people use JavaScript for. In 2026, we don't need a JS library for this anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Modern Way:&lt;/strong&gt;&lt;br&gt;
You use the HTML &lt;code&gt;popover&lt;/code&gt; attribute and target it with CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"my-menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Open Menu&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"my-menu"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"p-4 rounded-lg shadow-xl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is a pure CSS/HTML dropdown!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With zero lines of JavaScript, the browser automatically handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Showing/hiding the element.&lt;/li&gt;
&lt;li&gt;  "Light dismiss" (closing when you click outside).&lt;/li&gt;
&lt;li&gt;  Putting the menu on the "top layer" so it isn't cut off by parent containers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Container Queries (No more JS Resize Listeners)
&lt;/h2&gt;

&lt;p&gt;We used to use JavaScript &lt;code&gt;ResizeObserver&lt;/code&gt; to change a component's layout if its container got too small (like a sidebar moving to the bottom).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 2026 Way:&lt;/strong&gt;&lt;br&gt;
Container queries allow an element to style itself based on its &lt;strong&gt;own size&lt;/strong&gt;, not the size of the whole browser window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;container-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex-direction&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="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&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 perfect for Rails developers using &lt;strong&gt;ViewComponents&lt;/strong&gt;. Your component can now be "smart" and responsive no matter where you drop it in your layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Scroll-Driven Animations
&lt;/h2&gt;

&lt;p&gt;I used to hate writing JS scroll listeners. They are terrible for performance and often feel "janky" on mobile phones. &lt;/p&gt;

&lt;p&gt;In 2026, we can link animations directly to the scroll position using pure CSS. Want a progress bar at the top of your blog post?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;grow-progress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scaleX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scaleX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.progress-bar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grow-progress&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;animation-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser handles the math. It is buttery smooth and consumes zero CPU cycles compared to a JS &lt;code&gt;scroll&lt;/code&gt; event.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Native Smooth Scrolling and Snap
&lt;/h2&gt;

&lt;p&gt;If you are building a landing page with a carousel or a "back to top" button, you might be tempted to use a JS library. &lt;/p&gt;

&lt;p&gt;Don't.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;scroll-behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;smooth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.carousel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;scroll-snap-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="n"&gt;mandatory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.carousel-item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;scroll-snap-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you that premium, "app-like" sliding feel natively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary: Why this matters for Rails 8
&lt;/h2&gt;

&lt;p&gt;When we use &lt;strong&gt;Hotwire and Turbo&lt;/strong&gt;, we want to keep the "State" on the server as much as possible. Every time we add a custom Stimulus controller for a tiny UI animation, we are adding "Client-side State" that we have to manage.&lt;/p&gt;

&lt;p&gt;By using these 2026 CSS features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Your &lt;strong&gt;HTML is cleaner&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Your &lt;strong&gt;JavaScript bundle is smaller&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Your &lt;strong&gt;UX is more resilient&lt;/strong&gt; (CSS doesn't "crash" like JS does).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next time you are about to run &lt;code&gt;rails generate stimulus&lt;/code&gt;, ask yourself: &lt;em&gt;"Can I do this with &lt;code&gt;:has()&lt;/code&gt; or a container query instead?"&lt;/em&gt; Most of the time, the answer is now yes.&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>The Developer's Way to Track Traffic: Self-Hosting Ackee or Plausible</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Mon, 01 Jun 2026 18:14:41 +0000</pubDate>
      <link>https://dev.to/zilton7/the-developers-way-to-track-traffic-self-hosting-ackee-or-plausible-583i</link>
      <guid>https://dev.to/zilton7/the-developers-way-to-track-traffic-self-hosting-ackee-or-plausible-583i</guid>
      <description>&lt;p&gt;I recently took a long look at my Rails projects and realized I was carrying around a piece of technical debt that was actually hurting my user experience: &lt;strong&gt;Google Analytics (GA4).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a developer living in the EU (Lithuania), using Google Analytics is a headache. Between the complex GDPR compliance rules, the legal uncertainty of sending data to US servers, and that massive, ugly cookie banner I was forced to show every visitor, it just didn't feel "clean" anymore. &lt;/p&gt;

&lt;p&gt;Plus, let’s be honest: GA4 is bloated. I don't need 500 different reports to tell me how many people visited my pricing page today. I just want to see my traffic, my referrers, and my conversion rate without selling my users' souls to an advertising giant.&lt;/p&gt;

&lt;p&gt;In 2026, the best move for a solo developer is to self-host your own privacy-first analytics. Here is how I moved to &lt;strong&gt;Ackee&lt;/strong&gt; and &lt;strong&gt;Plausible&lt;/strong&gt;, and why you should too.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why Self-Host?
&lt;/h2&gt;

&lt;p&gt;When you self-host your analytics, you own the data. It lives on your server, in your database. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;No Cookie Banners:&lt;/strong&gt; Tools like Plausible and Ackee don't use cookies and don't track personal data. Under GDPR, this means you can often remove those annoying "Accept Cookies" popups entirely.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Performance:&lt;/strong&gt; The Google Analytics script is heavy. Self-hosted alternatives are usually tiny (under 1KB), which helps your site load faster and score higher on Google Lighthouse.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ad-Blocker Friendly:&lt;/strong&gt; Because you are serving the script from your own subdomain (like &lt;code&gt;stats.yourdomain.com&lt;/code&gt;), many ad-blockers won't catch it, giving you more accurate data than Google.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. The Contenders: Ackee vs. Plausible
&lt;/h2&gt;

&lt;p&gt;There are two main players I recommend for the Rails community.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ackee (The Minimalist)
&lt;/h3&gt;

&lt;p&gt;Ackee is built on Node.js and is incredibly beautiful. It is perfect if you just want the "vibe" of your traffic. It shows you views, referrers, and duration in a very clean, dark interface.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Pros:&lt;/strong&gt; Very lightweight, easy to run in a Docker container.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cons:&lt;/strong&gt; Doesn't track "Goals" (like button clicks) as easily as Plausible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Plausible (The Professional)
&lt;/h3&gt;

&lt;p&gt;Plausible is the gold standard for privacy-first analytics. It is written in Elixir and is incredibly fast.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Pros:&lt;/strong&gt; Supports goal tracking, custom events, and even integrates with Google Search Console so you can see your keywords.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cons:&lt;/strong&gt; A bit more complex to self-host because it requires a ClickHouse database for the stats.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Deployment with Kamal 2
&lt;/h2&gt;

&lt;p&gt;If you followed my previous articles, you know I use &lt;strong&gt;Kamal 2&lt;/strong&gt; to build my "Cloud Empire" on cheap VPS servers. Deploying a self-hosted analytics tool is just another Docker container in your cluster.&lt;/p&gt;

&lt;p&gt;Here is a simplified &lt;code&gt;deploy.yml&lt;/code&gt; example for running Ackee on a subdomain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-stats&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;electerious/ackee&lt;/span&gt;

&lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;123.45.67.89&lt;/span&gt; &lt;span class="c1"&gt;# Your VPS IP&lt;/span&gt;

&lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stats.zil.com&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ACKEE_PASSWORD&lt;/span&gt;
  &lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ACKEE_MONGODB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongodb://db_user:pass@your-db-ip:27017/ackee&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once deployed, you get a small JavaScript snippet to paste into your Rails layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Integrating with Rails
&lt;/h2&gt;

&lt;p&gt;You don't need a heavy gem for this. Just put the script in your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/layouts/application.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;production?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script 
    &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; 
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://stats.zil.com/tracker.js"&lt;/span&gt; 
    &lt;span class="na"&gt;data-ackee-server=&lt;/span&gt;&lt;span class="s"&gt;"https://stats.zil.com"&lt;/span&gt; 
    &lt;span class="na"&gt;data-ackee-domain-id=&lt;/span&gt;&lt;span class="s"&gt;"your-uuid-here"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to track custom events (like when someone clicks "Subscribe"), you can trigger it from a &lt;strong&gt;Stimulus&lt;/strong&gt; controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/javascript/controllers/analytics_controller.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hotwired/stimulus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;trackClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Ackee custom action&lt;/span&gt;
    &lt;span class="nx"&gt;ackeeTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button_clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Join&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Moving away from Google Analytics was one of the best decisions I made for my side projects. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; My sites load faster.&lt;/li&gt;
&lt;li&gt; My users' privacy is respected.&lt;/li&gt;
&lt;li&gt; I don't have to deal with complex GDPR legal paperwork.&lt;/li&gt;
&lt;li&gt; My dashboard is actually fun to look at again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are a solo developer, stop giving your data away for free. Rent a $5 VPS, spin up a Docker container, and take control of your own metrics.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>privacy</category>
      <category>devops</category>
      <category>rails</category>
    </item>
    <item>
      <title>Count, Length, or Size? Avoiding ActiveRecord Performance Traps</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Sun, 31 May 2026 18:15:47 +0000</pubDate>
      <link>https://dev.to/zilton7/count-length-or-size-avoiding-activerecord-performance-traps-784</link>
      <guid>https://dev.to/zilton7/count-length-or-size-avoiding-activerecord-performance-traps-784</guid>
      <description>&lt;p&gt;I remember when I first started with Rails, I thought &lt;code&gt;.count&lt;/code&gt;, &lt;code&gt;.length&lt;/code&gt;, and &lt;code&gt;.size&lt;/code&gt; were exactly the same thing. I used them interchangeably in my views and controllers. If I wanted to know how many users were in the database, I would just pick one and move on.&lt;/p&gt;

&lt;p&gt;But then, as my app grew and I started checking my server logs, I realized I was making a huge performance mistake. While these three methods look identical, they behave completely differently under the hood. One of them hits your database every single time, one of them might crash your server’s memory, and one of them is "smart" enough to choose the best path.&lt;/p&gt;

&lt;p&gt;Here is the breakdown of when to use which so you can keep your Rails app fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;.count&lt;/code&gt; (The Database Hitter)
&lt;/h2&gt;

&lt;p&gt;When you call &lt;code&gt;.count&lt;/code&gt;, ActiveRecord ignores any records you might already have loaded in memory. It goes straight to the database and runs a specific SQL query: &lt;code&gt;SELECT COUNT(*) FROM users&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;users&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;active: &lt;/span&gt;&lt;span class="kp"&gt;true&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="nf"&gt;count&lt;/span&gt; &lt;span class="c1"&gt;# SQL: SELECT COUNT(*) FROM users WHERE active = true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use it:&lt;/strong&gt; Use this when you &lt;em&gt;only&lt;/em&gt; need the number and you have no intention of using the actual data.&lt;br&gt;
&lt;strong&gt;The Trap:&lt;/strong&gt; If you call &lt;code&gt;.count&lt;/code&gt; and then immediately loop through the records with &lt;code&gt;.each&lt;/code&gt;, you are doing double work. You hit the DB for the count, and then you hit it again to get the users.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. &lt;code&gt;.length&lt;/code&gt; (The Memory Loader)
&lt;/h2&gt;

&lt;p&gt;Calling &lt;code&gt;.length&lt;/code&gt; is the equivalent of saying: "Give me everything." &lt;/p&gt;

&lt;p&gt;ActiveRecord will fetch every single column for every single record in that query and load them all into your computer's RAM. Only after everything is loaded does it count how many items are in the array.&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;users&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;active: &lt;/span&gt;&lt;span class="kp"&gt;true&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="nf"&gt;length&lt;/span&gt; &lt;span class="c1"&gt;# SQL: SELECT * FROM users WHERE active = true&lt;/span&gt;
&lt;span class="c1"&gt;# Every user object is now sitting in your RAM.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use it:&lt;/strong&gt; Use this only if you have &lt;strong&gt;already&lt;/strong&gt; loaded the records (for example, if you already called &lt;code&gt;@users.to_a&lt;/code&gt;).&lt;br&gt;
&lt;strong&gt;The Trap:&lt;/strong&gt; If you have 100,000 users and you just want to show a number in the navbar, calling &lt;code&gt;.length&lt;/code&gt; will try to load all 100,000 users at once. This is the fastest way to get an "Out of Memory" error and crash your production server.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;.size&lt;/code&gt; (The "Smart" Manager)
&lt;/h2&gt;

&lt;p&gt;This is the method I recommend for 90% of use cases. It is the "omakase" choice because it adapts to the situation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  If the records are &lt;strong&gt;already loaded&lt;/strong&gt; in memory, it acts like &lt;code&gt;.length&lt;/code&gt; (it just counts the items in the array without touching the database).&lt;/li&gt;
&lt;li&gt;  If the records are &lt;strong&gt;not loaded&lt;/strong&gt;, it acts like &lt;code&gt;.count&lt;/code&gt; (it runs the efficient &lt;code&gt;SELECT COUNT(*)&lt;/code&gt; query).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;users&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;active: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Records aren't loaded yet, so it runs a COUNT query&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; 

&lt;span class="c1"&gt;# Later in the view...&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# Data is loaded here&lt;/span&gt;

&lt;span class="c1"&gt;# Records are now loaded, so .size doesn't hit the DB again!&lt;/span&gt;
&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use it:&lt;/strong&gt; This should be your &lt;strong&gt;default&lt;/strong&gt; choice. It protects you from making mistakes in your views and ensures you aren't hammering the database with redundant queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary: The Decision Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;th&gt;Best for...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;.count&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always runs &lt;code&gt;SELECT COUNT(*)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;One-off checks when you don't need the data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;.length&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always runs &lt;code&gt;SELECT *&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;When data is already loaded in an array.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;.size&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Smart:&lt;/strong&gt; Choice depends on state&lt;/td&gt;
&lt;td&gt;Almost everything. It’s the safest default.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As a solo developer, you want to spend your time building features, not debugging slow database queries. Switching your habit from &lt;code&gt;.count&lt;/code&gt; to &lt;code&gt;.size&lt;/code&gt; is a 1-second change that can save you a lot of headache in the future.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>activerecord</category>
      <category>performance</category>
    </item>
    <item>
      <title>From ERB to Phlex: 5 Steps to Pure Ruby Views</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Sat, 30 May 2026 18:15:47 +0000</pubDate>
      <link>https://dev.to/zilton7/from-erb-to-phlex-5-steps-to-pure-ruby-views-4hcf</link>
      <guid>https://dev.to/zilton7/from-erb-to-phlex-5-steps-to-pure-ruby-views-4hcf</guid>
      <description>&lt;p&gt;I love Rails, but I’ve always found ERB to be a bit messy. You spend your whole day jumping between the "Backend" (clean Ruby code) and the "Frontend" (a mixture of HTML and weird &lt;code&gt;&amp;lt;% %&amp;gt;&lt;/code&gt; tags). &lt;/p&gt;

&lt;p&gt;It is annoying to manage indentation, and if you make a typo inside an ERB tag, you usually don't find out until the page crashes in the browser. &lt;/p&gt;

&lt;p&gt;In 2026, many solo developers are moving to &lt;strong&gt;Phlex&lt;/strong&gt;. Phlex is a view framework that lets you write your HTML in &lt;strong&gt;Pure Ruby&lt;/strong&gt;. It is 10x faster than ERB and allows you to use standard Ruby tools (like Mixins and inheritance) to build your UI.&lt;/p&gt;

&lt;p&gt;If you want to try it out, here is how to move from ERB to Phlex in 5 easy steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  STEP 1: Installation
&lt;/h2&gt;

&lt;p&gt;First, add the gem to your project:&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;bundle&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="n"&gt;phlex&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rails&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, run the generator to create the basic folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/rails g phlex:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates an &lt;code&gt;app/views/layouts/application_view.rb&lt;/code&gt; file which acts as the base for all your new views.&lt;/p&gt;

&lt;h2&gt;
  
  
  STEP 2: The Mental Model (Syntax Translation)
&lt;/h2&gt;

&lt;p&gt;Before we convert a file, you need to understand the translation. In Phlex, every HTML tag is just a Ruby method.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;ERB:&lt;/strong&gt; &lt;code&gt;&amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Phlex:&lt;/strong&gt; &lt;code&gt;h1 { "Hello" }&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ERB:&lt;/strong&gt; &lt;code&gt;&amp;lt;div class="container"&amp;gt;...&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Phlex:&lt;/strong&gt; &lt;code&gt;div(class: "container") { ... }&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need to worry about closing tags or escaping HTML. Ruby handles it all automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  STEP 3: Converting a Simple Template
&lt;/h2&gt;

&lt;p&gt;Let's take a standard "Show" page and convert it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Old ERB (&lt;code&gt;app/views/posts/show.html.erb&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"p-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;"Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edit_post_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The New Phlex View (&lt;code&gt;app/views/posts/show_view.rb&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Views::Posts::ShowView&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationView&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;post&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&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;view_template&lt;/span&gt;
    &lt;span class="n"&gt;div&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;"p-8"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;# You can still use Rails helpers!&lt;/span&gt;
      &lt;span class="n"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;"Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;edit_post_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@post&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;
  
  
  STEP 4: Handling Loops and Logic
&lt;/h2&gt;

&lt;p&gt;The cool thing about Phlex is that loops are just... Ruby loops. You don't need special syntax.&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;def&lt;/span&gt; &lt;span class="nf"&gt;view_template&lt;/span&gt;
  &lt;span class="n"&gt;ul&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vi"&gt;@posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&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="c1"&gt;# Standard Ruby IF statement&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;admin?&lt;/span&gt;
    &lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Delete Everything"&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;
  
  
  STEP 5: Rendering from the Controller
&lt;/h2&gt;

&lt;p&gt;Now that your view is a Ruby class, you need to tell your controller to use it. In Rails 8, this is very clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/posts_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
  &lt;span class="vi"&gt;@post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&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="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="c1"&gt;# Instead of 'render :show', we render the Ruby object&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="no"&gt;Views&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Posts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ShowView&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;post: &lt;/span&gt;&lt;span class="vi"&gt;@post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why is this better?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Speed:&lt;/strong&gt; Phlex doesn't have to "parse" a text file like ERB does. It just executes Ruby methods. It is incredibly fast.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Organization:&lt;/strong&gt; You can use Ruby &lt;strong&gt;Modules&lt;/strong&gt; to share pieces of UI. No more messy partials.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Testing:&lt;/strong&gt; Since your view is a class, you can test it like a normal Ruby object without booting a whole browser.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;No Context Switching:&lt;/strong&gt; You stay in "Ruby Mode" all day. Your brain doesn't have to switch between HTML rules and Ruby rules.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Moving to Phlex feels a bit strange for the first hour, but once you realize that you have the full power of the Ruby language in your views, you won't want to go back. &lt;/p&gt;

&lt;p&gt;Start small. Convert one simple "Settings" page or a "Profile" page. You can run ERB and Phlex side-by-side in the same app without any problems. &lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>frontend</category>
      <category>performance</category>
    </item>
    <item>
      <title>UUID v7 vs. ULID: Which Sortable ID Should You Choose?</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Fri, 29 May 2026 19:58:40 +0000</pubDate>
      <link>https://dev.to/zilton7/uuid-v7-vs-ulid-which-sortable-id-should-you-choose-1cdd</link>
      <guid>https://dev.to/zilton7/uuid-v7-vs-ulid-which-sortable-id-should-you-choose-1cdd</guid>
      <description>&lt;p&gt;For a long time, if you wanted a primary key that was both unique and chronological, you reached for &lt;strong&gt;ULID&lt;/strong&gt;. It was the perfect "indie" solution to the randomness of UUID v4. It gave us fast database inserts and clean, copy-pasteable URLs.&lt;/p&gt;

&lt;p&gt;But the world of standards moves fast. In 2024, the IETF officially published RFC 9562, which introduced &lt;strong&gt;UUID v7&lt;/strong&gt;. It does almost exactly what ULID does: it embeds a timestamp into the first part of the ID.&lt;/p&gt;

&lt;p&gt;If you are starting a new Rails 8 app today, you might be wondering: &lt;em&gt;"Is ULID still relevant, or should I switch to the 'official' UUID v7?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve looked at both, and while they serve the same purpose, the choice usually comes down to a battle between &lt;strong&gt;Native Performance&lt;/strong&gt; and &lt;strong&gt;Developer Experience (Readability)&lt;/strong&gt;. Here is the breakdown.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Side-by-Side Look
&lt;/h2&gt;

&lt;p&gt;First, let's look at how they appear in your logs or your browser address bar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;ULID:&lt;/strong&gt; &lt;code&gt;01J7Z3P7W9K2Q4R5S6T7V8W9X0&lt;/code&gt; (26 characters, Case-insensitive)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;UUID v7:&lt;/strong&gt; &lt;code&gt;0191eb5d-79e0-7174-8b01-382a937a4697&lt;/code&gt; (36 characters, Hexadecimal)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both represent the exact same amount of data (128 bits). Both are "lexicographically sortable," meaning your database can sort them by time without a separate &lt;code&gt;created_at&lt;/code&gt; column.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The "Native" Win (Why UUID v7 wins on Performance)
&lt;/h2&gt;

&lt;p&gt;This is the biggest argument for UUID v7. Because it is an official standard, database engines like &lt;strong&gt;PostgreSQL 17+&lt;/strong&gt; and &lt;strong&gt;MariaDB&lt;/strong&gt; are building native support for it.&lt;/p&gt;

&lt;p&gt;When you use UUID v7 in Postgres:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Storage:&lt;/strong&gt; It is stored as a native &lt;code&gt;uuid&lt;/code&gt; type (16 bytes). &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Indexing:&lt;/strong&gt; The database knows exactly how to optimize the B-Tree index for this specific format.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Interoperability:&lt;/strong&gt; Almost every language (Python, Go, Java) now has a native UUID v7 generator in its standard library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In contrast, ULID is usually stored as a &lt;code&gt;string&lt;/code&gt; or &lt;code&gt;text&lt;/code&gt; in Postgres. A string takes up more space and is slower to query than a native &lt;code&gt;uuid&lt;/code&gt; type.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The "Copy-Paste" Win (Why ULID wins on DX)
&lt;/h2&gt;

&lt;p&gt;If you are a solo developer who spends all day looking at your own URLs and database rows, &lt;strong&gt;ULID is much nicer to work with.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Double-Click Rule:&lt;/strong&gt; &lt;br&gt;
If you try to copy a UUID from a terminal or a text editor, a double-click usually only selects one "chunk" between the dashes. You have to carefully click and drag to copy the whole thing.&lt;br&gt;
With ULID, a double-click selects the entire string every time. It sounds small, but when you do it 100 times a day, it matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Crockford Base32 Alphabet:&lt;/strong&gt;&lt;br&gt;
ULIDs use a specific set of characters that exclude confusing letters like &lt;code&gt;I&lt;/code&gt;, &lt;code&gt;L&lt;/code&gt;, &lt;code&gt;O&lt;/code&gt;, and &lt;code&gt;U&lt;/code&gt;. This makes them much safer to read over the phone or type manually if you ever have to do customer support.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Implementation in Rails 8
&lt;/h2&gt;

&lt;p&gt;Rails 8 has made switching to UUIDs (including v7) very easy. You don't even need an external gem anymore if your database supports it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Migration
&lt;/h3&gt;

&lt;p&gt;When creating your table, just specify the type.&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;create_table&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;id: :uuid&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;:title&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Model Configuration
&lt;/h3&gt;

&lt;p&gt;If your database doesn't generate v7 by default, you can tell Rails to do it using the &lt;code&gt;uuid&lt;/code&gt; gem (which now supports v7).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/post.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="c1"&gt;# Rails 8 can handle this via default_random_uuid() in PG &lt;/span&gt;
  &lt;span class="c1"&gt;# or you can set it manually:&lt;/span&gt;
  &lt;span class="n"&gt;before_create&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;UUID7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary: Which one should you pick?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Choose UUID v7 if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You want the highest possible performance in PostgreSQL.&lt;/li&gt;
&lt;li&gt;  You are building a high-volume app with millions of writes per day.&lt;/li&gt;
&lt;li&gt;  You want to follow "Official Standards" so your data plays nice with other systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose ULID if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You value &lt;strong&gt;Readability&lt;/strong&gt; and &lt;strong&gt;Developer Happiness&lt;/strong&gt; above all else.&lt;/li&gt;
&lt;li&gt;  Your app is an internal tool or a standard SaaS where the "string vs native uuid" performance gap is too small to notice.&lt;/li&gt;
&lt;li&gt;  You hate dashes in your URLs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My Verdict:&lt;/strong&gt; &lt;br&gt;
I’m a pragmatist. For my core business data (Users, Invoices, Orders), I’ve switched to &lt;strong&gt;UUID v7&lt;/strong&gt; because I want that native Postgres efficiency. But for things like "Slug" replacements or public-facing tokens, I still use &lt;strong&gt;ULID&lt;/strong&gt; because it just looks better.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>postgres</category>
      <category>database</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Stop Making Ugly Apps: Simple Tailwind Tricks for Backend Developers</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Thu, 28 May 2026 18:27:55 +0000</pubDate>
      <link>https://dev.to/zilton7/stop-making-ugly-apps-simple-tailwind-tricks-for-backend-developers-12ep</link>
      <guid>https://dev.to/zilton7/stop-making-ugly-apps-simple-tailwind-tricks-for-backend-developers-12ep</guid>
      <description>&lt;p&gt;I know the feeling of opening a blank &lt;code&gt;index.html.erb&lt;/code&gt; file and having no idea how to make it look "premium." You have the Rails backend working perfectly, but the frontend looks like a website from 2005. &lt;/p&gt;

&lt;p&gt;You don't need to be an artist to build a professional-looking SaaS. Professional design is mostly about &lt;strong&gt;consistency, spacing, and subtle details.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After building dozens of projects with Tailwind, I’ve found five patterns that work every single time. If you follow these, your app will instantly look like it was built by a real design team.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The "Subtle Shell" (The Card Pattern)
&lt;/h2&gt;

&lt;p&gt;The most common mistake is making borders too dark or shadows too heavy. Professional UI is all about being "subtle." &lt;/p&gt;

&lt;p&gt;If you need a container for a form or a list, use this combination:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white border border-slate-200 rounded-xl shadow-sm p-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Your content here --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Trick:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use &lt;code&gt;rounded-xl&lt;/code&gt; (or &lt;code&gt;2xl&lt;/code&gt;) instead of just &lt;code&gt;rounded&lt;/code&gt;. Large radius corners make an app feel modern and "soft."&lt;/li&gt;
&lt;li&gt;  Use &lt;code&gt;shadow-sm&lt;/code&gt;. If the shadow is too big, it looks like a cheap 90s website.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Never Use Pure White on Pure White
&lt;/h2&gt;

&lt;p&gt;If your website background is white (&lt;code&gt;bg-white&lt;/code&gt;), and your cards are also white, the page looks "flat" and confusing.&lt;/p&gt;

&lt;p&gt;The secret to depth is using a very light gray for the page background.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- In your application layout --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-slate-50 text-slate-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Inside your view --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white border border-slate-200 ..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    This card now "pops" off the background.
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Trick:&lt;/strong&gt; &lt;code&gt;bg-slate-50&lt;/code&gt; is just enough to create a contrast that makes your white cards look clean and intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Hierarchy through Color, Not Just Size
&lt;/h2&gt;

&lt;p&gt;When developers want to emphasize text, they usually just make it &lt;strong&gt;BOLD&lt;/strong&gt; or BIG. Designers use color to create hierarchy.&lt;/p&gt;

&lt;p&gt;If you have a list of items, don't make everything black. Make the secondary information lighter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-slate-900 font-semibold"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;User Name&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-slate-500 text-sm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Joined 2 days ago&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Trick:&lt;/strong&gt; By using &lt;code&gt;text-slate-500&lt;/code&gt; for the date, you tell the user’s brain: &lt;em&gt;"Look at the name first, the date is secondary."&lt;/em&gt; This makes the interface much easier to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The "Soft" Action (Secondary Buttons)
&lt;/h2&gt;

&lt;p&gt;Every app has a "Delete" or "Cancel" button. Usually, developers make these bright red or blue. This creates too much "visual noise."&lt;/p&gt;

&lt;p&gt;If an action isn't the primary goal of the page, use a "Ghost" or "Soft" button style.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Primary Action --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-indigo-600 text-white px-4 py-2 rounded-lg font-medium hover:bg-indigo-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Save Changes
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Secondary Action --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-slate-100 text-slate-700 px-4 py-2 rounded-lg font-medium hover:bg-slate-200"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Cancel
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Trick:&lt;/strong&gt; Using a light gray background (&lt;code&gt;bg-slate-100&lt;/code&gt;) for secondary actions makes your primary button (the "Money" button) stand out much more.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Pro-Level Inputs (The Focus Ring)
&lt;/h2&gt;

&lt;p&gt;The default browser focus (the blue outline when you click an input) looks terrible. Tailwind makes it easy to create a professional focus state that feels like a premium app (like Stripe or Linear).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; 
       &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full border-slate-300 rounded-lg shadow-sm focus:border-indigo-500 focus:ring-4 focus:ring-indigo-500/10 outline-none transition-all"&lt;/span&gt; 
       &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"you@example.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Trick:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;focus:ring-4&lt;/code&gt;: This creates a large, soft outer glow.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;focus:ring-indigo-500/10&lt;/code&gt;: The &lt;code&gt;/10&lt;/code&gt; makes the ring 10% transparent. This is the secret to that "premium" glowing effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You don't need to reinvent the wheel. Just stick to these five rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Big rounding (&lt;code&gt;xl&lt;/code&gt;), small shadows.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Light gray background (&lt;code&gt;slate-50&lt;/code&gt;) for the whole page.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Light gray text (&lt;code&gt;slate-500&lt;/code&gt;) for secondary info.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Soft gray buttons for secondary actions.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Transparent rings (&lt;code&gt;/10&lt;/code&gt;) for input focus states.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you apply these to your next Rails project, you will be surprised at how "designed" it feels, even if you never opened Figma.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>tailwindcss</category>
      <category>frontend</category>
    </item>
    <item>
      <title>The Better Primary Key: A Guide to ULIDs for Rails Developers</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Wed, 27 May 2026 18:06:27 +0000</pubDate>
      <link>https://dev.to/zilton7/the-better-primary-key-a-guide-to-ulids-for-rails-developers-424h</link>
      <guid>https://dev.to/zilton7/the-better-primary-key-a-guide-to-ulids-for-rails-developers-424h</guid>
      <description>&lt;p&gt;In a previous article, I talked about &lt;strong&gt;Snowflake IDs&lt;/strong&gt;. They are great, but they require a bit of configuration because you need to manage "Worker IDs." &lt;/p&gt;

&lt;p&gt;If you want something simpler that gives you the same benefits - secure URLs and fast database sorting—you should look at &lt;strong&gt;ULIDs&lt;/strong&gt; (Universally Unique Lexicographically Sortable Identifiers).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use UUIDs?
&lt;/h2&gt;

&lt;p&gt;Most developers switch from auto-incrementing integers to &lt;strong&gt;UUIDs&lt;/strong&gt; because they want to hide their business volume. If your order ID is &lt;code&gt;order/550e8400-e29b...&lt;/code&gt;, no one knows if you have 1 customer or 1 million.&lt;/p&gt;

&lt;p&gt;But UUIDs have a major problem: &lt;strong&gt;they are completely random.&lt;/strong&gt; &lt;br&gt;
When you insert millions of random UUIDs into a database, the "B-Tree" index gets fragmented and slow. Your database has to jump all over the hard drive to find where to put the new data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ULIDs fix this.&lt;/strong&gt;&lt;br&gt;
A ULID is 128 bits (just like a UUID), but the first part of the ID is a &lt;strong&gt;timestamp&lt;/strong&gt;. This means ULIDs are chronological. They are unique like a UUID, but they sort like an Integer.&lt;/p&gt;

&lt;p&gt;Here is how to implement ULIDs in your Rails 8 app in 4 easy steps.&lt;/p&gt;
&lt;h2&gt;
  
  
  STEP 1: Install the Gem
&lt;/h2&gt;

&lt;p&gt;We will use the &lt;code&gt;ulid&lt;/code&gt; gem to generate the strings. Add this 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="s2"&gt;"ulid"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;bundle install&lt;/code&gt; in your terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  STEP 2: The ULID Concern
&lt;/h2&gt;

&lt;p&gt;We want to make it very easy to add ULIDs to any model. The best way to do this is with a &lt;strong&gt;Concern&lt;/strong&gt;. This code will ensure that every time we create a new record, a ULID is generated and assigned to the ID.&lt;/p&gt;

&lt;p&gt;Create a new file at &lt;code&gt;app/models/concerns/has_ulid.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/concerns/has_ulid.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;HasUlid&lt;/span&gt;
  &lt;span class="kp"&gt;extend&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;Concern&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Before we save to the DB, generate the ULID&lt;/span&gt;
    &lt;span class="n"&gt;before_create&lt;/span&gt; &lt;span class="ss"&gt;:set_ulid&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;set_ulid&lt;/span&gt;
    &lt;span class="c1"&gt;# ULID.generate creates a string like: 01ARZ3NDEKTSV4RRFFQ69G5FAV&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;ULID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&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;
  
  
  STEP 3: The Migration
&lt;/h2&gt;

&lt;p&gt;When you create a new model, you need to tell Rails that the &lt;code&gt;id&lt;/code&gt; is a &lt;code&gt;string&lt;/code&gt;, and you must disable the default auto-increment logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g model Product name:string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the migration file and modify it 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="c1"&gt;# db/migrate/XXXXXXXXXXXXXX_create_products.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateProducts&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;8.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="c1"&gt;# id: false stops the automatic integer ID&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="kp"&gt;false&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="c1"&gt;# We use string for ULID primary key&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;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;primary_key: &lt;/span&gt;&lt;span class="kp"&gt;true&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="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;h2&gt;
  
  
  STEP 4: Update the Model
&lt;/h2&gt;

&lt;p&gt;Now, just include the concern we wrote in Step 2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/product.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&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="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;HasUlid&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  STEP 5: See it in action
&lt;/h2&gt;

&lt;p&gt;Open your Rails console (&lt;code&gt;bin/rails c&lt;/code&gt;) and create a few products:&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;Product&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;name: &lt;/span&gt;&lt;span class="s2"&gt;"Laptop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Monitor"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Keyboard"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Check the IDs&lt;/span&gt;
&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; ["01HQV...", "01HQV...", "01HQV..."]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look closely, the IDs all start with the same characters because they were created in the same minute. Because they are sortable, you can still run &lt;code&gt;Product.order(:id)&lt;/code&gt; and they will be in the correct chronological order!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I like ULIDs for Rails
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Better Performance:&lt;/strong&gt; Because the IDs are sortable, PostgreSQL (or SQLite) can append them to the end of the index. This is much faster for "Write-Heavy" apps than random UUIDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readability:&lt;/strong&gt; ULIDs use a special alphabet (Crockford's Base32) that excludes confusing letters like "I", "L", and "O". This makes them easier to read if a user has to type one in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Setup:&lt;/strong&gt; Unlike Snowflake IDs, you don't need to configure server IDs or worker nodes. You just install the gem and go.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's pretty much it. It’s a small architectural change that makes your app feel much more professional and scalable.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Chasing Hype: Why I Chose Postgres Over 'New-Age' Databases</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Tue, 26 May 2026 22:24:42 +0000</pubDate>
      <link>https://dev.to/zilton7/stop-chasing-hype-why-i-chose-postgres-over-new-age-databases-40pg</link>
      <guid>https://dev.to/zilton7/stop-chasing-hype-why-i-chose-postgres-over-new-age-databases-40pg</guid>
      <description>&lt;p&gt;I’ve lost count of how many times I’ve been tempted by a "revolutionary" new database. &lt;/p&gt;

&lt;p&gt;Every few months, a new tool pops up on Hacker News promising infinite scalability, a schema-less paradise, or native AI capabilities that make everything else look like a calculator from the 80s. For a solo developer, these promises are like siren songs. You think: &lt;em&gt;"If I use this new DB, I'll never have to worry about migrations again!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But after a decade of building Rails apps, I’ve learned a hard lesson: &lt;strong&gt;Boring tech is a competitive advantage.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;In 2026, my default choice for every single project is &lt;strong&gt;PostgreSQL&lt;/strong&gt;. While everyone else is busy debugging edge cases in a database that’s only two years old, I’m shipping features. Here is why "boring" Postgres is actually the most exciting tool in my stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. It is 10 Databases in One
&lt;/h2&gt;

&lt;p&gt;The biggest myth about Postgres is that it is "just" a relational database for tables and rows. In reality, Postgres has eaten the features of almost every trending niche database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Need Document Storage?&lt;/strong&gt; Use &lt;code&gt;JSONB&lt;/code&gt;. It’s faster and more reliable than MongoDB for 99% of use cases.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Need Full-Text Search?&lt;/strong&gt; Use GIN indexes and TSVector. You can build a great search engine without installing Elasticsearch.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Need AI Vector Search?&lt;/strong&gt; Use the &lt;code&gt;pgvector&lt;/code&gt; extension. You don't need a separate subscription for Pinecone.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Need a Key-Value Store?&lt;/strong&gt; Use unlogged tables or just a simple HSTORE.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By sticking to Postgres, my "infrastructure map" stays tiny. I don't have to manage five different services; I just manage one very powerful elephant.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. ACID Compliance and Peace of Mind
&lt;/h2&gt;

&lt;p&gt;There is a specific kind of stress that only developers know: the "Data Corruption" stress. &lt;/p&gt;

&lt;p&gt;Many trending databases trade off &lt;strong&gt;ACID compliance&lt;/strong&gt; (Atomicity, Consistency, Isolation, Durability) for speed or "flexibility." They tell you that "eventual consistency" is fine.&lt;/p&gt;

&lt;p&gt;It’s not fine. Not when you are handling a user's money, their private notes, or their business records. Postgres is built like a tank. It is designed to never lose your data, even if the power goes out in the middle of a write. As a solo dev, I want to spend my nights sleeping, not manually repairing corrupted database clusters.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Rails 8 "Solid" Movement
&lt;/h2&gt;

&lt;p&gt;If you are following the Rails 8 era, you know that the community is moving toward &lt;strong&gt;Database-Backed everything&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Solid Queue&lt;/strong&gt; moves your background jobs into Postgres.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Solid Cache&lt;/strong&gt; moves your caching into Postgres.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Solid Cable&lt;/strong&gt; moves your WebSockets into Postgres.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Rails team realized that for a "One-Person Framework" to work, we need to stop adding external dependencies like Redis. Because Postgres is so fast and reliable on modern NVMe drives, it can handle all these tasks easily. My entire production stack is now just &lt;strong&gt;Linux + Postgres&lt;/strong&gt;. That is incredibly beautiful.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The "Ask Anyone" Ecosystem
&lt;/h2&gt;

&lt;p&gt;When you use a trending database that was released last year, and you hit a weird bug at 2:00 AM, you are in trouble. The only place to find help is a small Discord channel or a half-finished documentation page.&lt;/p&gt;

&lt;p&gt;When you have a problem with Postgres, the answer has already been written ten times on StackOverflow. Every AI model (Cursor, ChatGPT, Claude) was trained on decades of Postgres knowledge. Every tool - from BI dashboards to backup utilities—supports Postgres on Day One. &lt;/p&gt;

&lt;p&gt;You aren't just buying a database; you are buying into the largest knowledge base in the software world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary: Shipping is the Only Metric
&lt;/h2&gt;

&lt;p&gt;As an indie hacker or solo developer, you are not a "Database Researcher." You are a &lt;strong&gt;Product Builder&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Every hour you spend learning a new query language or figuring out how to back up a "revolutionary" new NoSQL store is an hour you didn't spend talking to users or polishing your UI. &lt;/p&gt;

&lt;p&gt;Postgres is "boring" because it works. It’s boring because it’s predictable. And in the chaotic world of startups, &lt;strong&gt;predictability is a superpower.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>database</category>
      <category>postgres</category>
      <category>architecture</category>
    </item>
    <item>
      <title>n8n vs ByteChef: Which Automation Engine Should You Self-Host?</title>
      <dc:creator>Zil Norvilis</dc:creator>
      <pubDate>Sun, 24 May 2026 18:10:41 +0000</pubDate>
      <link>https://dev.to/zilton7/n8n-vs-bytechef-which-automation-engine-should-you-self-host-1i1m</link>
      <guid>https://dev.to/zilton7/n8n-vs-bytechef-which-automation-engine-should-you-self-host-1i1m</guid>
      <description>&lt;p&gt;Automating the "glue work" of a SaaS business is a survival skill for a solo developer. You need to sync Stripe payments to your database, send Discord alerts for new sign-ups, and maybe trigger AI summaries for incoming emails. &lt;/p&gt;

&lt;p&gt;For a long time, &lt;strong&gt;n8n&lt;/strong&gt; has been the undisputed king of self-hosted automation. It’s the tool I’ve recommended for years because it lets you escape the high costs of Zapier while keeping your Rails monolith clean. &lt;/p&gt;

&lt;p&gt;But recently, a new challenger called &lt;strong&gt;ByteChef&lt;/strong&gt; has started gaining traction in the open-source community. It promises a more "developer-first" approach to low-code integrations. &lt;/p&gt;

&lt;p&gt;If you are looking to set up an automation server on a $5 VPS this weekend, which one should you choose? Here is the breakdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. n8n: The Versatile Giant
&lt;/h2&gt;

&lt;p&gt;n8n is the "Photoshop" of automation. It is built on Node.js and uses a beautiful, flexible node-based interface where you can drag and drop connections in any direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Strength: The Ecosystem&lt;/strong&gt;&lt;br&gt;
In 2026, n8n has over 400 native integrations. Whether you need to talk to a niche Lithuanian shipping API or a major player like OpenAI, n8n probably already has a node for it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Coder’s Edge: JavaScript Nodes&lt;/strong&gt;&lt;br&gt;
If a built-in node doesn't do exactly what you want, you can drop in a "Code Node" and write raw JavaScript. Since n8n runs on Node.js, you have access to the entire NPM ecosystem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A simple n8n code node example&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/ /g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Catch:&lt;/strong&gt; Because n8n is so flexible, complex workflows can eventually look like a "bowl of spaghetti" with wires crossing everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. ByteChef: The Structured Alternative
&lt;/h2&gt;

&lt;p&gt;ByteChef is the newer kid on the block. While n8n focuses on the "Canvas" feel, ByteChef is built with a focus on &lt;strong&gt;Integration Management&lt;/strong&gt;. It’s designed for people who want to build and manage hundreds of different integrations without losing their mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Strength: Multi-Step Logic and Performance&lt;/strong&gt;&lt;br&gt;
ByteChef is built on a Java-based stack (Spring Boot), which makes it incredibly performant for heavy data processing. It feels more "rigid" than n8n, but in a way that encourages better organization. It’s less of a "playground" and more of an "engine."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Coder’s Edge: Component Building&lt;/strong&gt;&lt;br&gt;
ByteChef allows you to build your own components using a very structured schema. If you are an engineer who likes to define strict inputs and outputs for your automations, ByteChef's approach will feel very professional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Catch:&lt;/strong&gt; The community is much smaller. You might find yourself having to write custom connectors for services that n8n already supports out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Side-by-Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;n8n&lt;/th&gt;
&lt;th&gt;ByteChef&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node.js (JavaScript)&lt;/td&gt;
&lt;td&gt;Java / TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI Vibe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infinite Canvas (Freeform)&lt;/td&gt;
&lt;td&gt;Structured Workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integrations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;400+ (Huge)&lt;/td&gt;
&lt;td&gt;Growing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Self-Hosting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very Easy (Docker)&lt;/td&gt;
&lt;td&gt;Easy (Docker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JavaScript everywhere&lt;/td&gt;
&lt;td&gt;TypeScript / Java&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why I’m Sticking with n8n (For Now)
&lt;/h2&gt;

&lt;p&gt;As a solo developer using the "One-Person Framework" philosophy, &lt;strong&gt;n8n is still the winner for 2026.&lt;/strong&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Community Support:&lt;/strong&gt; If you have a weird error in an n8n workflow, a quick Google search usually finds a forum post with the solution. ByteChef is still building that knowledge base.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The "Vibe" Factor:&lt;/strong&gt; n8n's visual debugging is incredible. You can see the data moving through the wires in real-time. When you're "Vibe Coding" with AI assistance, being able to &lt;em&gt;see&lt;/em&gt; the failure point instantly is a huge time-saver.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Low Overhead:&lt;/strong&gt; If you are already a web developer, you probably know enough JavaScript to fix anything in n8n. Learning the ByteChef component structure feels like a bigger "homework" assignment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Choose n8n&lt;/strong&gt; if you want the most reliable, well-documented, and flexible tool available. It is perfect for 99% of solo-dev automation needs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Choose ByteChef&lt;/strong&gt; if you are building an automation-heavy product where you need to manage complex, high-performance data pipelines and you prefer a more "engineered" feel over a "drawing" feel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regardless of which tool you pick, the most important thing is to &lt;strong&gt;stop writing custom Ruby scripts for third-party integrations.&lt;/strong&gt; Get those tasks out of your Rails app and into a dedicated automation engine.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>automation</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
