<?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: Stoyan</title>
    <description>The latest articles on DEV Community by Stoyan (@amigobg).</description>
    <link>https://dev.to/amigobg</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%2F323184%2F10f820db-c0cf-4aae-891b-37e3a7572184.jpeg</url>
      <title>DEV Community: Stoyan</title>
      <link>https://dev.to/amigobg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amigobg"/>
    <language>en</language>
    <item>
      <title>Ruby on Rails in the AI Era: Why don't need to switch to Python</title>
      <dc:creator>Stoyan</dc:creator>
      <pubDate>Tue, 02 Jun 2026 16:56:36 +0000</pubDate>
      <link>https://dev.to/amigobg/ruby-on-rails-in-the-ai-era-why-dont-need-to-switch-to-python-22dl</link>
      <guid>https://dev.to/amigobg/ruby-on-rails-in-the-ai-era-why-dont-need-to-switch-to-python-22dl</guid>
      <description>&lt;h1&gt;
  
  
  Why Ruby on Rails Developers Don't Need to Switch to Python for AI
&lt;/h1&gt;

&lt;p&gt;For the last few years, every AI conversation seemed to end with the same conclusion:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If you want to build AI applications, learn Python."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And yes, Python is currently dominating the AI landscape. Most AI frameworks, tutorials, courses, and research projects are built around Python. Tools like PyTorch, Hugging Face, LangChain, and countless AI libraries make it the default choice for machine learning and AI development.&lt;/p&gt;

&lt;p&gt;But what if you're already comfortable with Ruby and Rails (like me)?&lt;/p&gt;

&lt;p&gt;Do you really need to switch?&lt;/p&gt;

&lt;p&gt;I don't think so.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Misconception
&lt;/h2&gt;

&lt;p&gt;Many developers associate AI with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Training neural networks&lt;/li&gt;
&lt;li&gt;Building custom models&lt;/li&gt;
&lt;li&gt;Deep learning research&lt;/li&gt;
&lt;li&gt;Data science&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the reality is that most companies are not training the next GPT model.&lt;/p&gt;

&lt;p&gt;They're building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI chatbots&lt;/li&gt;
&lt;li&gt;Internal knowledge bases&lt;/li&gt;
&lt;li&gt;AI-powered search&lt;/li&gt;
&lt;li&gt;Customer support assistants&lt;/li&gt;
&lt;li&gt;Workflow automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are software engineering problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rails is already good at solving them
&lt;/h2&gt;

&lt;p&gt;A modern AI stack can look surprisingly simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails API
+
RubyLLM
+
PostgreSQL + pgvector
+
Sidekiq
+
OpenAI / Claude / Gemini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup you can build a complete RAG (Retrieval-Augmented Generation) application (I've built chatbot at &lt;a href="https://roomspilot.com/features/ai-replies" rel="noopener noreferrer"&gt;Roomspilot&lt;/a&gt; with that stack).&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload PDFs or documentation&lt;/li&gt;
&lt;li&gt;Split content into chunks&lt;/li&gt;
&lt;li&gt;Generate embeddings&lt;/li&gt;
&lt;li&gt;Store them in PostgreSQL using pgvector&lt;/li&gt;
&lt;li&gt;Retrieve relevant content through vector search&lt;/li&gt;
&lt;li&gt;Send the context to an LLM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's basically how many production AI assistants work today.&lt;/p&gt;

&lt;h2&gt;
  
  
  FastAPI vs Rails API Mode
&lt;/h2&gt;

&lt;p&gt;A common AI stack in Python looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FastAPI
+
PostgreSQL
+
Vector Database
+
OpenAI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Ruby equivalent is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails API Mode
+
PostgreSQL + pgvector
+
RubyLLM
+
OpenAI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different language.&lt;/p&gt;

&lt;p&gt;Same architecture.&lt;/p&gt;

&lt;p&gt;As Rails developers, we already know how to build APIs, background jobs, authentication systems, multi-tenant applications, and scalable products.&lt;/p&gt;

&lt;p&gt;The AI part is often just another integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Python still wins
&lt;/h2&gt;

&lt;p&gt;If your goal is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model training&lt;/li&gt;
&lt;li&gt;Deep learning&lt;/li&gt;
&lt;li&gt;Computer vision&lt;/li&gt;
&lt;li&gt;AI research&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Python is the clear winner.&lt;/p&gt;

&lt;p&gt;No debate there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Rails still shines
&lt;/h2&gt;

&lt;p&gt;If your goal is shipping AI-powered products quickly, Rails remains an excellent choice.&lt;/p&gt;

&lt;p&gt;You don't have to throw away years of experience just because AI became the hottest topic in tech.&lt;/p&gt;

&lt;p&gt;Sometimes the smartest move isn't learning an entirely new stack.&lt;/p&gt;

&lt;p&gt;It's adding new capabilities to the stack you already master.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ruby</category>
      <category>rails</category>
      <category>programming</category>
    </item>
    <item>
      <title>Deploy Rails app with PostgreSQL, Redis and Sidekiq on Production and Staging using Kamal</title>
      <dc:creator>Stoyan</dc:creator>
      <pubDate>Thu, 07 Mar 2024 08:13:09 +0000</pubDate>
      <link>https://dev.to/amigobg/deploy-rails-app-with-postgresql-redis-and-sidekiq-on-production-and-staging-using-kamal-4hme</link>
      <guid>https://dev.to/amigobg/deploy-rails-app-with-postgresql-redis-and-sidekiq-on-production-and-staging-using-kamal-4hme</guid>
      <description>&lt;p&gt;&lt;strong&gt;Step 1: Install Kamal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Article is for those who already have installed kamal with ready to use servers but if you doesn't, just take a look on &lt;a href="https://rubygems.org/gems/kamal" rel="noopener noreferrer"&gt;Kamal gem&lt;/a&gt; and make sure that is installed on your local machine:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Configure Deployment Settings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Move into the application directory and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kamal init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This process generates a deployment configuration file named &lt;strong&gt;config/deploy.yml&lt;/strong&gt;, in addition to several other files. We will now delve into the different sections of the &lt;strong&gt;deploy.yml&lt;/strong&gt; configuration file to understand their purposes and configurations.&lt;/p&gt;

&lt;p&gt;deploy.yml&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="c1"&gt;# Name of your application. Used to uniquely configure containers.&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myrailapp&lt;/span&gt;

&lt;span class="c1"&gt;# Name of the container image.&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;mydockerhubuser/myrailapp&lt;/span&gt;


&lt;span class="c1"&gt;# Credentials for your image host.&lt;/span&gt;
&lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mydockerhubuser&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KAMAL_REGISTRY_PASSWORD&lt;/span&gt;

&lt;span class="c1"&gt;# Inject ENV variables into containers (secrets come from .env).&lt;/span&gt;
&lt;span class="c1"&gt;# Remember to run `kamal env push` after making changes!&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;clear&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;RAILS_SERVE_STATIC_FILES&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;DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.0.6&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;RAILS_MASTER_KEY&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/storage:/rails/storage"&lt;/span&gt;

&lt;span class="c1"&gt;# Use accessory services (secrets come from .env).&lt;/span&gt;
&lt;span class="na"&gt;accessories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&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;postgres:15&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;66.100.100.10&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&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;clear&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;POSTGRES_ROOT_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%'&lt;/span&gt;
        &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;db_uss1_app'&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;POSTGRES_PASSWORD&lt;/span&gt;
    &lt;span class="na"&gt;directories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;data:/var/lib/postgresql/data&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&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;redis:latest&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;61.111.111.1&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6379&lt;/span&gt;
    &lt;span class="na"&gt;directories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;data:/var/lib/redis/data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our example, we utilize &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;DockerHub&lt;/a&gt; and connect to it using the DockerHub username 'mydockeruser' and the repository name 'myrailapp' (which can be private). The password is configured via the &lt;code&gt;KAMAL_REGISTRY_PASSWORD&lt;/code&gt; environment variable within the .env file, located in the root directory of the application. Additionally, it's crucial to remember to list .env files in the .gitignore to prevent them from being tracked and uploaded to version control systems.&lt;strong&gt;DB_HOST&lt;/strong&gt; using local IP of the machine but you can use your public server address as well.&lt;/p&gt;

&lt;p&gt;.env&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;KAMAL_REGISTRY_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dckr_pat_382391340045824911111&lt;/span&gt;
&lt;span class="py"&gt;RAILS_MASTER_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;25345k34j543132432m5j4234&lt;/span&gt;
&lt;span class="py"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;s0meR@andomPasw0rt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To separate the staging and production environments, we should create two additional files: &lt;strong&gt;deploy.staging.yml&lt;/strong&gt; for staging and &lt;strong&gt;deploy.production.yml&lt;/strong&gt; for production. In this scenario, the staging environment will be deployed on a single server, while the production environment will be deployed across two servers. Both environments will share a common server for the database and jobs (utilizing a Redis server), albeit with different databases to ensure separation of staging and production data.&lt;/p&gt;

&lt;p&gt;deploy.staging.yml&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;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="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;60.100.100.101&lt;/span&gt;
  &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;61.111.111.1&lt;/span&gt;
    &lt;span class="na"&gt;cmd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle exec sidekiq -e staging&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;clear&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app_staging&lt;/span&gt;
    &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;deploy.production.yml&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;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="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;60.100.100.102&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;60.100.100.103&lt;/span&gt;
  &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;61.111.111.1&lt;/span&gt;
    &lt;span class="na"&gt;cmd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle exec sidekiq -e production&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;clear&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app_production&lt;/span&gt;
    &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not forget to create &lt;strong&gt;.env.staging&lt;/strong&gt; as well which can be a copy of the &lt;strong&gt;.env&lt;/strong&gt; if the credentials are the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Deploy Application&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kamal setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Daily operations:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kamal deploy &lt;span class="nt"&gt;-d&lt;/span&gt; staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kamal deploy &lt;span class="nt"&gt;-d&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how we do in &lt;a href="https://roomspilot.com" rel="noopener noreferrer"&gt;Roomspilot&lt;/a&gt;, also check &lt;a href="https://kamal-deploy.org/docs/commands" rel="noopener noreferrer"&gt;kamal docs&lt;/a&gt; for more commands and use cases.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>kamal</category>
    </item>
  </channel>
</rss>
