Software Architectย Guide: Designing a RESTful API for a ๐ŸŒ Multi-Tenant Blogging Platform

Building a multi-tenant blogging platform requires thoughtful design of the API to ensure clarity, scalability, and security. In this post, we’ll explore a RESTful API design including versioning, nested resources, and authentication, using clear examples and best practices.


๐Ÿงฉ Understanding the Requirements

Before diving into endpoints, let’s break down what the platform supports:

  • Multiple tenants (e.g., organizations, teams)
  • Each tenant has users
  • Users can create blogs, and each blog has posts
  • Posts can have comments
  • Authentication is required

๐Ÿ“ Versioning

Weโ€™ll use URI-based versioning:

/api/v1/

This helps manage breaking changes cleanly.


๐Ÿ” Authentication

We’ll use token-based authentication (e.g., JWT or API keys). Each request must include:

Authorization: Bearer <token>

๐Ÿ“Œ Base URL

https://api.blogcloud.com/api/v1

๐Ÿ“š API Endpoint Design

๐Ÿ”ธ Tenants

Tenants are top-level entities.

  • GET /tenants โ€“ List all tenants (admin only)
  • POST /tenants โ€“ Create a new tenant
  • GET /tenants/:id โ€“ Show tenant details
  • PATCH /tenants/:id โ€“ Update tenant
  • DELETE /tenants/:id โ€“ Delete tenant

๐Ÿ”ธ Users (Scoped by tenant)

  • GET /tenants/:tenant_id/users โ€“ List users for tenant
  • POST /tenants/:tenant_id/users โ€“ Create user
  • GET /tenants/:tenant_id/users/:id โ€“ Show user
  • PATCH /tenants/:tenant_id/users/:id โ€“ Update user
  • DELETE /tenants/:tenant_id/users/:id โ€“ Delete user

๐Ÿ”ธ Blogs (Belong to users)

  • GET /tenants/:tenant_id/users/:user_id/blogs โ€“ List blogs
  • POST /tenants/:tenant_id/users/:user_id/blogs โ€“ Create blog
  • GET /tenants/:tenant_id/users/:user_id/blogs/:id โ€“ Show blog
  • PATCH /tenants/:tenant_id/users/:user_id/blogs/:id โ€“ Update blog
  • DELETE /tenants/:tenant_id/users/:user_id/blogs/:id โ€“ Delete blog

๐Ÿ”ธ Posts (Belong to blogs)

  • GET /blogs/:blog_id/posts โ€“ List posts
  • POST /blogs/:blog_id/posts โ€“ Create post
  • GET /blogs/:blog_id/posts/:id โ€“ Show post
  • PATCH /blogs/:blog_id/posts/:id โ€“ Update post
  • DELETE /blogs/:blog_id/posts/:id โ€“ Delete post

๐Ÿ”ธ Comments (Belong to posts)

  • GET /posts/:post_id/comments โ€“ List comments
  • POST /posts/:post_id/comments โ€“ Add comment
  • DELETE /posts/:post_id/comments/:id โ€“ Delete comment

โ“Question: what is the full url of comments?

No, the full URL for comments should not be:

https://api.blogcloud.com/api/v1/tenants/:tenant_id/users/:user_id/blogs/posts/:post_id/comments

That nesting is too deep and redundant, because:

  • By the time you’re at a post, you already implicitly know which blog/user/tenant it’s under (assuming proper authorization).
  • Posts have unique IDs across the system (or at least within blogs), so we donโ€™t need the entire hierarchy in every request.

โœ… Correct RESTful URL for Comments

If your post_id is unique (or unique within a blog), the cleanest design is:

https://api.blogcloud.com/api/v1/posts/:post_id/comments

or, if you prefer to keep blog_id context:

https://api.blogcloud.com/api/v1/blogs/:blog_id/posts/:post_id/comments

Use that second version only if post_id is not globally unique, and you need the blog context.

๐Ÿ” Recap of Comments Endpoints

ActionHTTP VerbEndpoint
List commentsGET/api/v1/posts/:post_id/comments
Create commentPOST/api/v1/posts/:post_id/comments
Delete commentDELETE/api/v1/posts/:post_id/comments/:id

๐Ÿง  Design Rule of Thumb

  • โœ… Keep URLs meaningful and shallow.
  • โŒ Don’t over-nest resources unless it’s needed to enforce scoping or clarify context.

๐Ÿ“ฅ Example: Create a Blog Post

Request:

POST /blogs/123/posts
Authorization: Bearer <token>
Content-Type: application/json

{
  "title": "Why REST APIs Still Matter",
  "body": "In this post, we explore the benefits of RESTful design..."
}

Response:

201 Created
{
  "id": 456,
  "title": "Why REST APIs Still Matter",
  "body": "In this post, we explore the benefits of RESTful design...",
  "created_at": "2025-07-03T10:00:00Z"
}


โœ… Best Practices Followed

  • Nesting: Resources are nested to show ownership (e.g., blogs under users).
  • Versioning: Prevents breaking old clients.
  • Consistency: Same verbs and JSON structure everywhere.
  • Authentication: Every sensitive request requires a token.

๐Ÿง  Final Thoughts

Designing a RESTful API for a multi-tenant app like a blogging platform requires balancing structure and simplicity. By properly scoping resources, using versioning, and enforcing auth, you build an API that’s powerful, secure, and easy to maintain.

Bonus Tip: Document your API using tools like Swagger/OpenAPI to make onboarding faster for new developers.

You are an awesome Architect ๐Ÿš€

Rails 8 App: Create an Academic software app using SQL without using ActiveRecord – Part 1 | users | products | orders

Let’s create a Rails 8 app which use SQL queries with raw SQL instead of ActiveRecord. Let’s use the full Rails environment with ActiveRecord for infrastructure, but bypass AR’s ORM features for pure SQL writing. Let me guide you through this step by step:

Step 1: Create the Rails App with ActiveRecord and PostgreSQL (skipping unnecessary components)

rails new academic-sql-software --database=postgresql --skip-action-cable --skip-jbuilder --skip-solid --skip-kamal

What we’re skipping and why:

  • –skip-action-cable: No WebSocket functionality needed
  • –skip-jbuilder: No JSON API views needed for our SQL practice app
  • –skip-solid: Skips Solid Cache and Solid Queue (we don’t need caching or background jobs)
  • –skip-kamal: No deployment configuration needed

What we’re keeping:

  • ActiveRecord: For database connection management and ActiveRecord::Base.connection.execute()
  • ActionController: For creating web interfaces to display our SQL query results
  • ActionView: For creating simple HTML pages to showcase our SQL learning exercises
  • PostgreSQL: Our database for practicing advanced SQL features

Why this setup is perfect for App with raw SQL:

  • Minimal Rails app focused on database interactions
  • Full Rails environment for development conveniences
  • ActiveRecord infrastructure without ORM usage
  • Clean setup without unnecessary overhead

=> Open config/application.rb and comment the following for now:

# require "active_job/railtie"
...
# require "active_storage/engine"
...
# require "action_mailer/railtie"
# require "action_mailbox/engine"
...
# require "action_cable/engine"

=> Open config/environments/development.rb config/environments/production.rb config/environments/test.rb comment action_mailer

๐Ÿค” Why I am using ActiveRecord (even though I don’t want the ORM):

  • Database Connection Management: ActiveRecord provides robust connection pooling, reconnection handling, and connection management
  • Rails Integration: Seamless integration with Rails console, database tasks (rails db:create, rails db:migrate), and development tools
  • Raw SQL Execution: We get ActiveRecord::Base.connection.execute() which is perfect for our raw SQL writing.
  • Migration System: Easy table creation and schema management with migrations (even though we’ll query with raw SQL)
  • Database Configuration: Rails handles database.yml configuration, environment switching, and connection setup
  • Development Tools: Access to Rails console for testing queries, database tasks, and debugging

Our Learning Strategy: We’ll use ActiveRecord’s infrastructure but completely bypass its ORM methods. Instead of Student.where(), we’ll use ActiveRecord::Base.connection.execute("SELECT * FROM students WHERE...")

Step 2: Navigate to the project directory

cd academic-sql-software

Step 3: Verify PostgreSQL setup

# Check if PostgreSQL is running
brew services list | grep postgresql
# or
pg_ctl status

Database Foundation: PostgreSQL gives us advanced SQL features:

  • Complex JOINs (INNER, LEFT, RIGHT, FULL OUTER)
  • Window functions (ROW_NUMBER, RANK, LAG, LEAD)
  • Common Table Expressions (CTEs)
  • Advanced aggregations and subqueries

Step 4: Install dependencies

bundle install

What this gives us:

  • pg gem: Pure PostgreSQL adapter (already included with --database=postgresql)
  • ActiveRecord: For connection management only
  • Rails infrastructure: Console, generators, rake tasks

Step 5: Create the PostgreSQL databases

โœ— rails db:create
Created database 'academic_sql_software_development'
Created database 'academic_sql_software_test

Our Development Environment:

  • Creates academic_sql_software_development and academic_sql_software_test
  • Sets up connection pooling and management
  • Enables us to use Rails console for testing queries: rails console then ActiveRecord::Base.connection.execute("SELECT 1")

Our Raw SQL Approach:

# We'll use this pattern throughout our app:
connection = ActiveRecord::Base.connection
result = connection.execute("SELECT s.name, t.subject FROM students s INNER JOIN teachers t ON s.teacher_id = t.id")

Why not pure pg gem:

  • Would require manual connection management
  • No Rails integration (no console, no rake tasks)
  • More boilerplate code for connection handling
  • Loss of Rails development conveniences

Why not pure ActiveRecord ORM:

  • We want to do SQL query writing, not ActiveRecord methods.
  • Need to understand database performance implications.
  • Want to practice complex queries that might be harder to express in ActiveRecord.

Step 6: Create Users table

mkdir -p db/migrate
class CreateUsers < ActiveRecord::Migration[8.0]
  def up
    # create users table
    execute <<~SQL
      CREATE TABLE users (
        id INT,
        username VARCHAR(200),
        email VARCHAR(150),
        phone_number VARCHAR(20)
      );
    SQL
  end

  def down
    execute <<~SQL
      DROP TABLE users;
    SQL
  end
end

class CreateOrders < ActiveRecord::Migration[8.0]
  def up
    # create table orders
    execute <<~SQL
    SQL
  end

  def down
    execute <<~SQL
    SQL
  end
end

execute <<~SQL is a Rails migration method that allows you to run raw SQL statements. Let me break it down:

Components:

  1. execute – A Rails migration method that executes raw SQL directly against the database
  2. <<~SQL – Ruby’s “squiggly heredoc” syntax for multi-line strings that automatically strips leading whitespace (read: https://www.rubyguides.com/2018/11/ruby-heredoc/)

Usage:

class SomeMigration < ActiveRecord::Migration[8.0]
  def change
    execute <<~SQL
      CREATE INDEX CONCURRENTLY idx_users_email_lower 
      ON users (LOWER(email));
    SQL
  end
end

Why use it?

  • Database-specific features: When you need PostgreSQL-specific syntax, MySQL features, etc.
  • Complex SQL: For operations that Rails DSL doesn’t support easily
  • Performance: Direct SQL can sometimes be more efficient
  • Raw SQL requirements: When you specifically need to write SQL (as mentioned in our requirement)

Example for creating a users table with raw SQL:

class CreateUsers < ActiveRecord::Migration[8.0]
  def up
    execute <<~SQL
      CREATE TABLE users (
        id BIGSERIAL PRIMARY KEY,
        name VARCHAR(255) NOT NULL,
        email VARCHAR(255) NOT NULL,
        created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
      );

      CREATE UNIQUE INDEX idx_users_email ON users(email);
    SQL
  end

  def down
    execute <<~SQL
      DROP TABLE IF EXISTS users;
    SQL
  end
end

The <<~SQL heredoc ends when it encounters the closing SQL keyword, and the ~ strips indentation so your SQL can be properly formatted within the Ruby code.

Q) ๐Ÿค” Is it a best practise to repeat the database constraints in model validation in Rails? DB level vs App level? or both?

Best Practice: Use BOTH ๐ŸŽฏ

Why both levels are recommended:

Database Level (Last Line of Defense)

-- Database constraints
price DECIMAL(10,2) NOT NULL CHECK (price > 0),
category product_category NOT NULL,
title VARCHAR(250) NOT NULL

Pros:

  • โœ… Data integrity guarantee – No bad data can enter, ever
  • โœ… Performance – Database enforces constraints efficiently
  • โœ… Bulletproof – Protects against bulk operations, direct SQL, data imports
  • โœ… Concurrency safe – Prevents race conditions
  • โœ… Multi-application protection – If other apps access same DB

Application Level (User Experience)

validates :price, presence: true, numericality: { greater_than: 0 }
validates :category, inclusion: { in: %w[men women kids infants] }

Pros:

  • โœ… User-friendly errors – “Price must be greater than 0” vs “CHECK constraint violated”
  • โœ… Better UX – Validation before form submission
  • โœ… Easier testing – Can unit test validations
  • โœ… Conditional logic – Complex business rules
  • โœ… Framework features – Callbacks, custom validators

Real-world scenarios where each matters:

Database saves you when:

# Bulk operations bypass Rails validations
Product.update_all(price: -10)  # DB constraint prevents this

# Direct SQL injection attempts
# DB constraints are your last line of defense

App validations save you when:

# User gets friendly error instead of:
# PG::CheckViolation: ERROR: new row violates check constraint
@product = Product.new(price: -5)
@product.valid? # => false
@product.errors.full_messages # => ["Price must be greater than 0"]

Practical Implementation:

class Product < ApplicationRecord
  # App-level validations for UX
  validates :title, presence: true, length: { maximum: 250 }
  validates :price, presence: true, numericality: { greater_than: 0 }
  validates :category, inclusion: { in: %w[men women kids infants] }

  # Don't duplicate precision validation if DB handles it
  # The DECIMAL(10,2) constraint is sufficient at DB level
end
-- DB-level constraints for data integrity
CREATE TABLE products (
  id BIGSERIAL PRIMARY KEY,
  title VARCHAR(250) NOT NULL,
  price DECIMAL(10,2) NOT NULL CHECK (price > 0),
  category product_category NOT NULL,
  -- DB handles precision automatically with DECIMAL(10,2)
);

What NOT to duplicate:

  • โŒ Precision constraintsDECIMAL(10,2) handles this perfectly
  • โŒ Data type validation – DB enforces INTEGER, BOOLEAN, etc.
  • โŒ Complex regex patterns – Better handled in app layer

Conclusion:

Use both, but strategically:

  • Database: Core data integrity, type constraints, foreign keys
  • Application: User experience, business logic, conditional rules
  • Don’t over-duplicate simple type/precision constraints that DB handles well

This approach gives you belt and suspenders protection with optimal user experience.

to be continued … ๐Ÿš€

Design Studio v0.9.5: A Visual Improvement in E-commerce Experience ๐ŸŽจ

Published: June 25, 2025

I am thrilled to announce the release of Design Studio v0.9.5, a major milestone that transforms our online shopping platform into a truly immersive visual experience. This release focuses heavily on user interface enhancements, performance optimizations, and creating a more engaging shopping journey for our customers.

๐Ÿš€ What’s New in v0.9.5

1. Stunning 10-Slide Hero Carousel

The centerpiece of this release is our brand-new interactive hero carousel featuring 10 beautifully curated slides with real product imagery. Each slide tells a story and creates an emotional connection with our visitors.

Dynamic Gradient Themes

Each slide features its own unique gradient theme:

<!-- Hero Slide Template -->
<div class="slide relative h-screen flex items-center justify-center overflow-hidden"
     data-theme="<%= slide[:theme] %>">
  <!-- Dynamic gradient backgrounds -->
  <div class="absolute inset-0 bg-gradient-to-br <%= slide[:gradient] %>"></div>

  <!-- Content with sophisticated typography -->
  <div class="relative z-10 text-center px-4">
    <h1 class="text-6xl font-bold text-white mb-6 leading-tight">
      <%= slide[:title] %>
    </h1>
    <p class="text-xl text-white/90 mb-8 max-w-2xl mx-auto">
      <%= slide[:description] %>
    </p>
  </div>
</div>

Smart Auto-Cycling with Manual Controls

// Intelligent carousel management
class HeroCarousel {
  constructor() {
    this.currentSlide = 0;
    this.autoInterval = 4000; // 4-second intervals
    this.isPlaying = true;
  }

  startAutoPlay() {
    this.autoPlayTimer = setInterval(() => {
      if (this.isPlaying) {
        this.nextSlide();
      }
    }, this.autoInterval);
  }

  pauseOnInteraction() {
    // Pause auto-play when user interacts
    this.isPlaying = false;
    setTimeout(() => this.isPlaying = true, 10000); // Resume after 10s
  }
}

2. Modular Component Architecture

We’ve completely redesigned our frontend architecture with separation of concerns in mind:

<!-- Main Hero Slider Component -->
<%= render 'home/hero_slider' %>

<!-- Individual Components -->
<%= render 'home/hero_slide', slide: slide_data %>
<%= render 'home/hero_slider_navigation' %>
<%= render 'home/hero_slider_script' %>
<%= render 'home/category_grid' %>
<%= render 'home/featured_products' %>

Component-Based Development Benefits:

  • Maintainability: Each component has a single responsibility
  • Reusability: Components can be used across different pages
  • Testing: Isolated components are easier to test
  • Performance: Selective rendering and caching opportunities

3. Enhanced Visual Design System

Glass Morphism Effects

We’ve introduced subtle glass morphism effects throughout the application:

/* Modern glass effect implementation */
.glass-effect {
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 16px;
  box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
}

/* Category cards with gradient overlays */
.category-card {
  @apply relative overflow-hidden rounded-xl;

  &::before {
    content: '';
    @apply absolute inset-0 bg-gradient-to-t from-black/60 to-transparent;
  }
}

Dynamic Color Management

Our new helper system automatically manages theme colors:

# app/helpers/application_helper.rb
def get_category_colors(gradient_class)
  case gradient_class
  when "from-pink-400 to-purple-500"
    "#f472b6, #8b5cf6"
  when "from-blue-400 to-indigo-500"  
    "#60a5fa, #6366f1"
  when "from-green-400 to-teal-500"
    "#4ade80, #14b8a6"
  else
    "#6366f1, #8b5cf6" # Elegant fallback
  end
end

def random_decorative_background
  themes = [:orange_pink, :blue_purple, :green_teal, :yellow_orange]
  decorative_background_config(themes.sample)
end

4. Mobile-First Responsive Design

Every component is built with mobile-first approach:

<!-- Responsive category grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
  <% categories.each do |category| %>
    <div class="group relative h-64 rounded-xl overflow-hidden cursor-pointer
                hover:scale-105 transform transition-all duration-300">
      <!-- Responsive image handling -->
      <div class="absolute inset-0">
        <%= image_tag category[:image], 
            class: "w-full h-full object-cover group-hover:scale-110 transition-transform duration-500",
            alt: category[:name] %>
      </div>
    </div>
  <% end %>
</div>

5. Public Product Browsing

We’ve opened up product browsing to all visitors:

# app/controllers/products_controller.rb
class ProductsController < ApplicationController
  # Allow public access to browsing
  allow_unauthenticated_access only: %i[index show]

  def index
    products = Product.all

    # Smart category filtering
    if params[:category].present?
      products = products.for_category(params[:category])
      @current_category = params[:category]
    end

    # Pagination for performance
    @pagy, @products = pagy(products)
  end
end

๐Ÿ”ง Technical Improvements

Test Coverage Excellence

I’ve achieved 73.91% test coverage (272/368 lines), ensuring code reliability:

# Enhanced authentication test helpers
module AuthenticationTestHelper
  def sign_in_as(user)
    # Generate unique IPs to avoid rate limiting conflicts
    unique_ip = "127.0.0.#{rand(1..254)}"
    @request.remote_addr = unique_ip

    session[:user_id] = user.id
    user
  end
end

Asset Pipeline Optimization

Rails 8 compatibility with modern asset handling:

# config/application.rb
class Application < Rails::Application
  # Modern browser support
  config.allow_browser versions: :modern

  # Asset pipeline optimization
  config.assets.css_compressor = nil # Tailwind handles this
  config.assets.js_compressor = :terser
end

Security Enhancements

# Role-based access control
class ApplicationController < ActionController::Base
  include Authentication

  private

  def require_admin
    unless current_user&.admin?
      redirect_to root_path, alert: "Access denied."
    end
  end
end

๐Ÿ“Š Performance Metrics

Before vs After v0.9.5:

MetricBeforeAfter v0.9.5Improvement
Test Coverage45%73.91%+64%
CI/CD Success23 failures0 failures+100%
Component Count3 monoliths8 modular components+167%
Mobile Score72/10089/100+24%

๐ŸŽจ Design Philosophy

This release embodies our commitment to:

  1. Visual Excellence: Every pixel serves a purpose
  2. User Experience: Intuitive navigation and interaction
  3. Performance: Fast loading without sacrificing beauty
  4. Accessibility: Inclusive design for all users
  5. Maintainability: Clean, modular code architecture

๐Ÿ”ฎ What’s Next?

Version 0.9.5 sets the foundation for exciting upcoming features:

  • Enhanced Search & Filtering
  • User Account Dashboard
  • Advanced Product Recommendations
  • Payment Integration
  • Order Tracking System

๐ŸŽ‰ Try It Today!

Experience the new Design Studio v0.9.5 and see the difference visual design makes in online shopping. Our hero carousel alone tells the story of modern fashion in 10 stunning slides.

Key Benefits for Users:

  • โœจ Immersive visual shopping experience
  • ๐Ÿ“ฑ Perfect on any device
  • โšก Lightning-fast performance
  • ๐Ÿ”’ Secure and reliable

For Developers:

  • ๐Ÿ—๏ธ Clean, maintainable architecture
  • ๐Ÿงช Comprehensive test suite
  • ๐Ÿ“š Well-documented components
  • ๐Ÿš€ Rails 8 compatibility

Design Studio v0.9.5 – Where technology meets artistry in e-commerce.

Download: GitHub Release
Documentation: GitHub Wiki
Live Demo: Design Studio – coming soon!


Enjoy Rails 8 with Hotwire! ๐Ÿš€

Rails 8 Tests: ๐Ÿ”„ TDD vs ๐ŸŽญ BDD | System Tests

Testโ€‘Driven Development (TDD) and Behaviorโ€‘Driven Development (BDD) are complementary testing approaches that help teams build robust, maintainable software by defining expected behaviour before writing production code. In TDD, developers write small, focused unit tests that fail initially, then implement just enough code to make them pass, ensuring each component meets its specification. BDD extends this idea by framing tests in a global language that all stakeholdersโ€”developers, QA, and product ownersโ€”can understand, using human-readable scenarios to describe system behaviour. While TDD emphasizes the correctness of individual units, BDD elevates collaboration and shared understanding by specifying the “why” and “how” of features in a narrative style, driving development through concrete examples of desired outcomes.

๐Ÿ”„ TDD vs ๐ŸŽญ BDD: Methodologies vs Frameworks

๐Ÿง  Understanding the Concepts

๐Ÿ”„ TDD (Test Driven Development)
  • Methodology/Process: Write test โ†’ Fail โ†’ Write code โ†’ Pass โ†’ Refactor
  • Focus: Testing the implementation and correctness
  • Mindset: “Does this code work correctly?”
  • Style: More technical, code-focused
๐ŸŽญ BDD (Behavior Driven Development)
  • Methodology/Process: Describe behavior โ†’ Write specs โ†’ Implement โ†’ Verify behavior
  • Focus: Testing the behavior and user requirements
  • Mindset: “Does this behave as expected from user’s perspective?”
  • Style: More natural language, business-focused

๐Ÿ› ๏ธ Frameworks Support Both Approaches

๐Ÿ“‹ RSpec (Primarily BDD-oriented)
# BDD Style - describing behavior
describe "TwoSum" do
  context "when given an empty array" do
    it "should inform user about insufficient data" do
      expect(two_sum([], 9)).to eq('Provide an array with length 2 or more')
    end
  end
end
โš™๏ธ Minitest (Supports Both TDD and BDD)
๐Ÿ”ง TDD Style with Minitest
class TestTwoSum < Minitest::Test
  # Testing implementation correctness
  def test_empty_array_returns_error
    assert_equal 'Provide an array with length 2 or more', two_sum([], 9)
  end

  def test_valid_input_returns_indices
    assert_equal [0, 1], two_sum([2, 7], 9)
  end
end
๐ŸŽญ BDD Style with Minitest
describe "TwoSum behavior" do
  describe "when user provides empty array" do
    it "guides user to provide sufficient data" do
      _(two_sum([], 9)).must_equal 'Provide an array with length 2 or more'
    end
  end

  describe "when user provides valid input" do
    it "finds the correct pair indices" do
      _(two_sum([2, 7], 9)).must_equal [0, 1]
    end
  end
end

๐ŸŽฏ Key Differences in Practice

๐Ÿ”„ TDD Approach
# 1. Write failing test
def test_two_sum_with_valid_input
  assert_equal [0, 1], two_sum([2, 7], 9)  # This will fail initially
end

# 2. Write minimal code to pass
def two_sum(nums, target)
  [0, 1]  # Hardcoded to pass
end

# 3. Refactor and improve
def two_sum(nums, target)
  # Actual implementation
end
๐ŸŽญ BDD Approach
# 1. Describe the behavior first
describe "Finding two numbers that sum to target" do
  context "when valid numbers exist" do
    it "returns their indices" do
      # This describes WHAT should happen, not HOW
      expect(two_sum([2, 7, 11, 15], 9)).to eq([0, 1])
    end
  end
end

๐Ÿ“Š Summary Table

AspectTDDBDD
FocusImplementation correctnessUser behavior
LanguageTechnicalBusiness/Natural
FrameworksAny (Minitest, RSpec, etc.)Any (RSpec, Minitest spec, etc.)
Test Namestest_method_returns_value"it should behave like..."
AudienceDevelopersStakeholders + Developers

๐ŸŽช The Reality

  • RSpec encourages BDD but can be used for TDD
  • Minitest is framework-agnostic – supports both approaches equally
  • Your choice of methodology (TDD vs BDD) is independent of your framework choice
  • Many teams use hybrid approaches – BDD for acceptance tests, TDD for unit tests

The syntax doesn’t determine the methodology – it’s about how you think and approach the problem!

System Tests ๐Ÿ’ปโš™๏ธ

System tests in Rails (located in test/system/*) are full-stack integration tests that simulate real user interactions with your web application. They’re the highest level of testing in the Rails testing hierarchy and provide the most realistic testing environment.

System tests actually launch a real web browser (or headless browser) and interact with your application just like a real user would. Looking at our Rails app’s configuration: design_studio/test/application_system_test_case.rb

driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ]

This means our system tests run using:

  • Selenium WebDriver (browser automation tool)
  • Headless Chrome (Chrome browser without UI)
  • 1400×1400 screen size for consistent testing

Code Snippets from:actionpack-8.0.2/lib/action_dispatch/system_test_case.rb

# frozen_string_literal: true

# :markup: markdown

gem "capybara", ">= 3.26"

require "capybara/dsl"
require "capybara/minitest"
require "action_controller"
require "action_dispatch/system_testing/driver"
require "action_dispatch/system_testing/browser"
require "action_dispatch/system_testing/server"
require "action_dispatch/system_testing/test_helpers/screenshot_helper"
require "action_dispatch/system_testing/test_helpers/setup_and_teardown"

module ActionDispatch
  # # System Testing
  #
  # System tests let you test applications in the browser. Because system tests
  # use a real browser experience, you can test all of your JavaScript easily from
  # your test suite.
  #
  # To create a system test in your application, extend your test class from
  # `ApplicationSystemTestCase`. System tests use Capybara as a base and allow you
  # to configure the settings through your `application_system_test_case.rb` file
  # that is generated with a new application or scaffold.
  #
  # Here is an example system test:
  #
  #     require "application_system_test_case"
  #
  #     class Users::CreateTest < ApplicationSystemTestCase
  #       test "adding a new user" do
  #         visit users_path
  #         click_on 'New User'
  #
  #         fill_in 'Name', with: 'Arya'
  #         click_on 'Create User'
  #
  #         assert_text 'Arya'
  #       end
  #     end
  #
  # When generating an application or scaffold, an
  # `application_system_test_case.rb` file will also be generated containing the
  # base class for system testing. This is where you can change the driver, add
  # Capybara settings, and other configuration for your system tests.
  #
  #     require "test_helper"
  #
  #     class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  #       driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
  #     end
  #
  # By default, `ActionDispatch::SystemTestCase` is driven by the Selenium driver,
  # with the Chrome browser, and a browser size of 1400x1400.
  #
  # Changing the driver configuration options is easy. Let's say you want to use
  # the Firefox browser instead of Chrome. In your
  # `application_system_test_case.rb` file add the following:
  #
  #     require "test_helper"
  #
  #     class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  #       driven_by :selenium, using: :firefox
  #     end
  #
  # `driven_by` has a required argument for the driver name. The keyword arguments
  # are `:using` for the browser and `:screen_size` to change the size of the
  # browser screen. These two options are not applicable for headless drivers and
  # will be silently ignored if passed.
  #
  # Headless browsers such as headless Chrome and headless Firefox are also
  # supported. You can use these browsers by setting the `:using` argument to
  # `:headless_chrome` or `:headless_firefox`.
  #
  # To use a headless driver, like Cuprite, update your Gemfile to use Cuprite
  # instead of Selenium and then declare the driver name in the
  # `application_system_test_case.rb` file. In this case, you would leave out the
  # `:using` option because the driver is headless, but you can still use
  # `:screen_size` to change the size of the browser screen, also you can use
  # `:options` to pass options supported by the driver. Please refer to your
  # driver documentation to learn about supported options.
  #
  #     require "test_helper"
  #     require "capybara/cuprite"
  #
  #     class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  #       driven_by :cuprite, screen_size: [1400, 1400], options:
  #         { js_errors: true }
  #     end
  #
  # Some drivers require browser capabilities to be passed as a block instead of
  # through the `options` hash.
  #
  # As an example, if you want to add mobile emulation on chrome, you'll have to
  # create an instance of selenium's `Chrome::Options` object and add capabilities
  # with a block.
  #
  # The block will be passed an instance of `<Driver>::Options` where you can
  # define the capabilities you want. Please refer to your driver documentation to
  # learn about supported options.
  #
  #     class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  #       driven_by :selenium, using: :chrome, screen_size: [1024, 768] do |driver_option|
  #         driver_option.add_emulation(device_name: 'iPhone 6')
  #         driver_option.add_extension('path/to/chrome_extension.crx')
  #       end
  #     end
  #
  # Because `ActionDispatch::SystemTestCase` is a shim between Capybara and Rails,
  # any driver that is supported by Capybara is supported by system tests as long
  # as you include the required gems and files.
  class SystemTestCase < ActiveSupport::TestCase
    include Capybara::DSL
    include Capybara::Minitest::Assertions
    include SystemTesting::TestHelpers::SetupAndTeardown
    include SystemTesting::TestHelpers::ScreenshotHelper

    ..........

How They Work

System tests can:

  • Navigate pages: visit products_url
  • Click elements: click_on "New product"
  • Fill forms: fill_in "Title", with: @product.title
  • Verify content: assert_text "Product was successfully created"
  • Check page structure: assert_selector "h1", text: "Products"

Examples From Our Codebase

Basic navigation test (from products_test.rb):

test "visiting the index" do
  visit products_url
  assert_selector "h1", text: "Products"
end

Complex user workflow (from profile_test.rb):

def sign_in_user(user)
  visit new_session_path
  fill_in "Email", with: user.email
  fill_in "Password", with: "password"
  click_button "Log In"

  # Wait for redirect and verify we're not on the login page anymore
  # Also wait for the success notice to appear
  assert_text "Logged in successfully", wait: 10
  assert_no_text "Log in to your account", wait: 5
end

Key Benefits

  1. End-to-end testing: Tests the complete user journey
  2. JavaScript testing: Can test dynamic frontend behavior
  3. Real browser environment: Tests CSS, responsive design, and browser compatibility
  4. User perspective: Validates the actual user experience

When to Use System Tests

  • Critical user workflows (login, checkout, registration)
  • Complex page interactions (forms, modals, AJAX)
  • Cross-browser compatibility
  • Responsive design validation

Our profile_test.rb is a great example – it tests the entire user authentication flow, profile page navigation, and various UI interactions that a real user would perform.

Happy Testing ๐Ÿš€

๐Ÿ“‹ Software Architect Guide: Designs | Patterns | Coding | Architectures

๐Ÿ“ About

Technical assessment platforms used by employers to screen candidates via timed online tests. Key characteristics include:

  • Mixed question formats: multiple-choice, fill-in-the-blank, coding tasks, and design scenarios.
  • Language & framework coverage: supports Ruby, Rails, React, Angular, AWS, SQL, DevOps and more.
  • Time-boxed: each test typically runs 30โ€“60 minutes, depending on role complexity. Each tests (1/12) allocated 2-8 mins depending on complexity.
  • Auto-grading + manual review: coding tasks are auto-checked against test cases; system-design answers may be manually reviewed.
  • Real-world scenarios: questions often mimic production challenges rather than classic white-board puzzles.

๐Ÿ“‹ Software Architect Test Structure

On these type of platforms, a Software Architect assessment usually combines:

  1. System & API Design
  2. Coding & Code Review
  3. Architecture Patterns
  4. DevOps & Cloud
  5. Database & Performance
  6. Front-end Integration

Below is a breakdown with sample questions you can practice.

๐Ÿ”ง 1. System & API Design

1.1 Design a RESTful API

  • Prompt: Sketch endpoints (URL, HTTP verbs, request/response JSON) for a multi-tenant blogging platform.
  • Whatโ€™s tested: URI naming, resource nesting, versioning, authentication.

https://railsdrop.com/2025/07/02/software-architect-guide-designing-a-restful-api-for-a-multi-tenant-blogging-platform/

1.2 High-Level Architecture Diagram

  • Prompt: Draw (in ASCII or pseudo-UML) a scalable order-processing system that integrates with payment gateways, queues, and microservices.
  • What’s tested: service decomposition, message brokering, fault tolerance.

Premium: https://railsdrop.com/2025/07/03/software-architect-guide-designing-a-scalable-order-processing-architecture-with-micro-services/


โ“Questions

Q) Why is layering an application important while executing a project?

https://railsdrop.com/2025/07/04/software-architect-guide-layering-an-app/

๐Ÿ’ป 2. Coding & Code Review

2.1 Ruby/Rails Coding Task

2.2 Code Quality Review


๐Ÿ—๏ธ 3. Architecture Patterns

3.1 Design Patterns Identification

3.2 Event-Driven vs Request-Driven


โ˜๏ธ 4. DevOps & Cloud (AWS)

4.1 AWS CI/CD Pipeline

4.2 Server Scaling Strategy


๐Ÿ—„๏ธ 5. Database & Performance (SQL)

5.1 Query Optimization

5.2 Schema Design

  • Prompt: Design a relational schema for a multi-language product catalog, ensuring easy localization and high read throughput.
  • Whatโ€™s tested: normalization, partitioning/sharding strategies.

๐ŸŒ 6. Front-end Integration

6.1 React Component Design

  • Prompt: Build a stateless React component that fetches and paginates API data. Outline props, state hooks, and error handling.
  • Whatโ€™s tested: Hooks, prop-drilling vs context, error boundaries.

6.2 Angular vs React Trade-offs

  • Prompt: Compare how you’d structure a large-scale dashboard in Angular vs React. Focus on modules, lazy loading, and state management.
  • What’s tested: NgModules, Redux/MobX/NgRx, bundle splitting.

6.3 React Native Considerations

  • Prompt: You don’t have React Native experienceโ€”explain how you’d architect code-sharing between web (React) and mobile (React Native).
  • What’s tested: Monorepo setups (e.g. Yarn Workspaces), shared business logic, native module stubs.

๐Ÿ’กPreparation Tips

  • Hands-on practice: Spin up mini-projects (e.g. Rails API with React front-end, deployed on AWS).
  • Mock interviews: Time yourself on similar Platform problemsโ€”aim for clarity and brevity.
  • Review fundamentals: Brush up on design patterns, AWS core services, SQL indexing strategies, and front-end state management.
  • Document trade-offs: Always justify architecture decisions with pros/cons.

Good luck, Architect to Greatness! ๐Ÿš€

Ruby Enumerable ๐Ÿ“š Module: Exciting Methods

Enumerable is a collection of iteration methods, a Ruby module, and a big part of what makes Ruby a great programming language.

# count elements that evaluate to true inside a block
[1,2,34].count
=> 3

# Group enumerable elements by the block return value. Returns a hash
[12,3,7,9].group_by {|x| x.even? ? 'even' : 'not_even'}
=> {"even" => [12], "not_even" => [3, 7, 9]}

# Partition into two groups. Returns a two-dimensional array
> [1,2,3,4,5].partition { |x| x.even? }
=> [[2, 4], [1, 3, 5]]

# Returns true if the block returns true for ANY elements yielded to it
> [1,2,5,8].any? 4
=> false

> [1,2,5,8].any? { |x| x.even?}
=> true

# Returns true if the block returns true for ALL elements yielded to it
> [2,5,6,8].all? {|x| x.even?}
=> false

# Opposite of all?
> [2,2,5,7].none? { |x| x.even?}
=> false

# Repeat ALL the elements n times
> [3,4,6].cycle(3).to_a
=> [3, 4, 6, 3, 4, 6, 3, 4, 6]

# select - SELECT all elements which pass the block
> [18,4,5,8,89].select {|x| x.even?}
=> [18, 4, 8]
> [18,4,5,8,89].select(&:even?)
=> [18, 4, 8]

# Like select, but it returns the first thing it finds
> [18,4,5,8,89].find {|x| x.even?}
=> 18

# Accumulates the result of the previous block value & passes it into the next one. Useful for adding up totals
> [4,5,8,90].inject(0) { |x, sum| x + sum }
=> 107
> [4,5,8,90].inject(:+)
=> 107
# Note that 'reduce' is an alias of 'inject'.

# Combines together two enumerable objects, so you can work with them in parallel. Useful for comparing elements & for generating hashes

> [2,4,56,8].zip [3,4]
=> [[2, 3], [4, 4], [56, nil], [8, nil]]

# Transforms every element of the enumerable object & returns the new version as an array
> [3,6,9].map { |x| x+89-27/2*23 }
=> [-207, -204, -201]

What is :+ in [4, 5, 8, 90].inject(:+) in Ruby?

๐Ÿ”ฃ :+ is a Symbol representing the + method.

In Ruby, every operator (like +, *, etc.) is actually a method under the hood.

  • inject takes a symbol (:+)
  • Ruby calls .send(:+) on each pair of elements
  • It’s equivalent to:
    (((4 + 5) + 8) + 90) => 107

๐Ÿ”ฃ &: Explanation:

  • :even? is a symbol representing the method even?
  • &: is Ruby’s “to_proc” shorthand, converting a symbol into a block
  • So &:even? becomes { |n| n.even? } under the hood

Enjoy Enumerable ๐Ÿš€

๐Ÿ” Ruby Programming Language Loops: A Case Study

Loops are an essential part of any programming languageโ€”they allow developers to execute code repeatedly without redundant repetition. Ruby, being an elegant and expressive language, offers several ways to implement looping constructs. This blog post explores Ruby loops through a real-world case study and demonstrates best practices for choosing the right loop for the right situation.


๐Ÿง  Why Loops Matter in Ruby

In Ruby, loops help automate repetitive tasks and iterate over collections (arrays, hashes, ranges, etc.). Understanding the different loop types and their use cases will help you write more idiomatic, efficient, and readable Ruby code.

๐Ÿงช The Case Study: Daily Sales Report Generator

Imagine you’re building a Ruby application for a retail store (like our Design studio) that generates a daily sales report. Your data source is an array of hashes, where each hash represents a sale with attributes like product name, category, quantity, and revenue.

sales = [
  { product: "T-shirt", category: "Apparel", quantity: 3, revenue: 900 },
  { product: "Laptop", category: "Electronics", quantity: 1, revenue: 50000 },
  { product: "Shoes", category: "Footwear", quantity: 2, revenue: 3000 },
  { product: "Headphones", category: "Electronics", quantity: 4, revenue: 12000 }
]

We’ll use this dataset to explore various loop types.

In Ruby:

  • Block-based loops like each, each_with_index, and loop do do create a new scope, so variables defined inside them do not leak outside.
  • Keyword-based loops like while, until, and for do not create a new scope, so variables declared inside are accessible outside.

๐Ÿ”„ each Loop โ€“ The Idiomatic Ruby Way

sales.each do |sale|
  puts "Sold #{sale[:quantity]} #{sale[:product]}(s) for โ‚น#{sale[:revenue]}"
end
Sold 3 T-shirt(s) for โ‚น900
Sold 1 Laptop(s) for โ‚น50000
Sold 2 Shoes(s) for โ‚น3000
Sold 4 Headphones(s) for โ‚น12000
=>
[{product: "T-shirt", category: "Apparel", quantity: 3, revenue: 900},
 {product: "Laptop", category: "Electronics", quantity: 1, revenue: 50000},
 {product: "Shoes", category: "Footwear", quantity: 2, revenue: 3000},
 {product: "Headphones", category: "Electronics", quantity: 4, revenue: 12000}]

Why use each:

  • Readable and expressive
  • Doesn’t return an index (cleaner when you donโ€™t need one)
  • Scope-safe: variables declared inside the block do not leak outside
  • Preferred for iterating over collections in Ruby

๐Ÿ”ข each_with_index โ€“ When You Need the Index

sales.each_with_index do |sale, index|
  puts "#{index + 1}. #{sale[:product]}: โ‚น#{sale[:revenue]}"
end
1. T-shirt: โ‚น900
2. Laptop: โ‚น50000
3. Shoes: โ‚น3000
4. Headphones: โ‚น12000
=>
[{product: "T-shirt", category: "Apparel", quantity: 3, revenue: 900},
 {product: "Laptop", category: "Electronics", quantity: 1, revenue: 50000},
 {product: "Shoes", category: "Footwear", quantity: 2, revenue: 3000},
 {product: "Headphones", category: "Electronics", quantity: 4, revenue: 12000}]

Use case: Numbered lists or positional logic.

  • Scope-safe like each

๐Ÿงฎ for Loop โ€“ Familiar but Rare in Idiomatic Ruby

for sale in sales
  puts "Product: #{sale[:product]}, Revenue: โ‚น#{sale[:revenue]}"
end
Product: T-shirt, Revenue: โ‚น900
Product: Laptop, Revenue: โ‚น50000
Product: Shoes, Revenue: โ‚น3000
Product: Headphones, Revenue: โ‚น12000
=>
[{product: "T-shirt", category: "Apparel", quantity: 3, revenue: 900},
 {product: "Laptop", category: "Electronics", quantity: 1, revenue: 50000},
 {product: "Shoes", category: "Footwear", quantity: 2, revenue: 3000},
 {product: "Headphones", category: "Electronics", quantity: 4, revenue: 12000}]

Caution:

  • โŒ Not scope-safe: Variables declared inside remain accessible outside the loop.
  • Though valid, for loops are generally avoided in idiomatic Ruby

๐Ÿชœ while Loop โ€“ Controlled Repetition

index = 0
while index < sales.size
  puts sales[index][:product]
  index += 1
end
T-shirt
Laptop
Shoes
Headphones
=> nil

Use case: When you’re manually controlling iteration.

  • โŒ Not scope-safe: variables declared within the loop (like index) remain accessible outside the loop.

๐Ÿ” until Loop โ€“ The Inverse of while

index = 0
until index == sales.size
  puts sales[index][:category]
  index += 1
end
Apparel
Electronics
Footwear
Electronics
=> nil

Use case: When you want to loop until a condition is true.

Similar to while, variables persist outside the loop (not block scoped).

๐Ÿงจ loop do with break โ€“ Infinite Loop with Manual Exit

index = 0
loop do
  break if index >= sales.size
  puts sales[index][:quantity]
  index += 1
end
3
1
2
4
=> nil

Use case: Custom control with explicit break condition.

Scope-safe: like other block-based loops, variables inside loop do blocks do not leak unless declared outside.

๐Ÿงน Bonus: Filtering with Loops vs Enumerable

#--- Loop-based filter
electronics_sales = []
sales.each do |sale|
  electronics_sales << sale if sale[:category] == "Electronics"
end
=>
[{product: "T-shirt", category: "Apparel", quantity: 3, revenue: 900},
 {product: "Laptop", category: "Electronics", quantity: 1, revenue: 50000},
 {product: "Shoes", category: "Footwear", quantity: 2, revenue: 3000},
 {product: "Headphones", category: "Electronics", quantity: 4, revenue: 12000}]

#--- Idiomatic Ruby filter
> electronics_sales = sales.select { |sale| sale[:category] == "Electronics" }
=>
[{product: "Laptop", category: "Electronics", quantity: 1, revenue: 50000},
...
> electronics_sales
=>
[{product: "Laptop", category: "Electronics", quantity: 1, revenue: 50000},
 {product: "Headphones", category: "Electronics", quantity: 4, revenue: 12000}]

Takeaway: Prefer Enumerable methods like select, map, reduce when working with collections. Loops are useful, but Ruby’s functional approach often leads to cleaner code.


โœ… Summary Table: Ruby Loops at a Glance

Loop TypeScope-safeIndex AccessBest Use Case
eachโœ…โŒSimple iteration
each_with_indexโœ…โœ…Need both element and index
forโŒโœ…Familiar syntax, but avoid in idiomatic Ruby
whileโœ…โœ… (manual)When condition is external
untilโœ…โœ… (manual)Inverted while, clearer for some logic
loop do + breakโœ…โœ… (manual)Controlled infinite loop

๐Ÿ Conclusion

Ruby offers a wide range of looping constructs. This case study demonstrates how to choose the right one based on context. For most collection traversals, each and other Enumerable methods are preferred. Use while, until, or loop when finer control over the iteration process is required.

Loop mindfully, and let Ruby’s elegance guide your code.

Enjoy Ruby ๐Ÿš€

Hotwire ใ€ฐ in Rails 8 World โ€“ And How My New Rails App Puts this into Work ๐Ÿš€

When you create a brand-new Rails 8 project today you automatically get a super-powerful front-end toolbox called Hotwire.

Because it is baked into the framework, it can feel a little magical (“everything just works!”). This post demystifies Hotwire, shows how its two core librariesโ€”Turbo and Stimulusโ€”fit together, and then walks through the places where the design_studio codebase is already using them.


1. What is Hotwire?

Hotwire (HTML Over The Wire) is a set of conventions + JavaScript libraries that lets you build modern, reactive UIs without writing (much) custom JS or a separate SPA. Instead of pushing JSON to the browser and letting a JS framework patch the DOM, the server sends HTML fragments over WebSockets, SSE, or normal HTTP responses and the browser swaps them in efficiently.

Hotwire is made of three parts:

  1. Turbo โ€“ the engine that intercepts normal links/forms, keeps your page state alive, and swaps HTML frames or streams into the DOM at 60fps.
  2. Stimulus โ€“ a “sprinkle-on” JavaScript framework for the little interactive bits that still need JS (dropdowns, clipboard buttons, etc.).
  3. (Optional) Strada โ€“ native-bridge helpers for mobile apps; not relevant to our web-only project.

Because Rails 8 ships with both turbo-rails and stimulus-rails gems, simply creating a project wires everything up.


2. How Turbo & Stimulus complement each other

  • Turbo keeps pages fresh โ€“ It handles navigation (Turbo Drive), partial page updates via <turbo-frame> (Turbo Frames), and real-time broadcasts with <turbo-stream> (Turbo Streams).
  • Stimulus adds behaviour โ€“ Tiny ES-module controllers attach to DOM elements and react to events/data attributes. Importantly, Stimulus plays nicely with Turboโ€™s DOM-swapping because controllers automatically disconnect/re-connect when elements are replaced.

Think of Turbo as the transport layer for HTML and Stimulus as the behaviour layer for the small pieces that still need JavaScript logic.

# server logs - still identify as HTML request, It handles navigation through (Turbo Drive)

Started GET "/products/15" for ::1 at 2025-06-24 00:47:03 +0530
Processing by ProductsController#show as HTML
  Parameters: {"id" => "15"}
.......

Started GET "/products?category=women" for ::1 at 2025-06-24 00:50:38 +0530
Processing by ProductsController#index as HTML
  Parameters: {"category" => "women"}
.......

Javascript and css files that loads in our html head:

    <link rel="stylesheet" href="/assets/actiontext-e646701d.css" data-turbo-track="reload" />
<link rel="stylesheet" href="/assets/application-8b441ae0.css" data-turbo-track="reload" />
<link rel="stylesheet" href="/assets/tailwind-8bbb1409.css" data-turbo-track="reload" />
    <script type="importmap" data-turbo-track="reload">{
  "imports": {
    "application": "/assets/application-3da76259.js",
    "@hotwired/turbo-rails": "/assets/turbo.min-3a2e143f.js",
    "@hotwired/stimulus": "/assets/stimulus.min-4b1e420e.js",
    "@hotwired/stimulus-loading": "/assets/stimulus-loading-1fc53fe7.js",
    "trix": "/assets/trix-4b540cb5.js",
    "@rails/actiontext": "/assets/actiontext.esm-f1c04d34.js",
    "controllers/application": "/assets/controllers/application-3affb389.js",
    "controllers/hello_controller": "/assets/controllers/hello_controller-708796bd.js",
    "controllers": "/assets/controllers/index-ee64e1f1.js"
  }
}</script>
<link rel="modulepreload" href="/assets/application-3da76259.js">
<link rel="modulepreload" href="/assets/turbo.min-3a2e143f.js">
<link rel="modulepreload" href="/assets/stimulus.min-4b1e420e.js">
<link rel="modulepreload" href="/assets/stimulus-loading-1fc53fe7.js">
<link rel="modulepreload" href="/assets/trix-4b540cb5.js">
<link rel="modulepreload" href="/assets/actiontext.esm-f1c04d34.js">
<link rel="modulepreload" href="/assets/controllers/application-3affb389.js">
<link rel="modulepreload" href="/assets/controllers/hello_controller-708796bd.js">
<link rel="modulepreload" href="/assets/controllers/index-ee64e1f1.js">
<script type="module">import "application"</script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">

3. Where Hotwire lives in design_studio

Because Rails 8 scaffolded most of this for us, the integration is scattered across a few key spots:

3.1 Gems & ES-modules are pinned

# config/importmap.rb

pin "@hotwired/turbo-rails",  to: "turbo.min.js"
pin "@hotwired/stimulus",     to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"

The Gemfile pulls the Ruby wrappers:

gem "turbo-rails"
gem "stimulus-rails"

3.2 Global JavaScript entry point

# application.js 

import "@hotwired/turbo-rails"
import "controllers"   // <-- auto-registers everything in app/javascript/controllers

As soon as that file is imported (it’s linked in application.html.erb via
javascript_include_tag "application", "data-turbo-track": "reload"
), Turbo intercepts every link & form on the site.

3.3 Stimulus controllers

The framework-generated controller registry lives at app/javascript/controllers/index.js; the only custom controller so far is the hello-world example:

connect() {
  this.element.textContent = "Hello World!"
}

You can drop new controllers into app/javascript/controllers/anything_controller.js and they will be auto-loaded thanks to the pin_all_from line above.

pin_all_from "app/javascript/controllers", under: "controllers"

3.4 Turbo Streams in practice โ€“ removing a product image

The most concrete Hotwire interaction in design_studio today is the “Delete image” action in the products feature:

  1. Controller action responds to turbo_stream:
respond_to do |format|
  ...
  format.turbo_stream   # <-- returns delete_image.turbo_stream.erb
end
  1. Stream template sent back:
# app/views/products/delete_image.turbo_stream.erb

<turbo-stream action="remove" target="product-image-<%= @image_id %>"></turbo-stream>
  1. Turbo receives the <turbo-stream> tag, finds the element with that id, and removes it from the DOMโ€”no page reload, no hand-written JS.
# app/views/products/show.html.erb
....
<%= link_to @product, 
    data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete this product?" }, 
    class: "px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors duration-200" do %>
    <i class="fas fa-trash mr-2"></i>Delete Product
<% end %>
....

3.5 “Free” Turbo benefits you might not notice

Because Turbo Drive is on globally:

  • Standard links look instantaneous (HTML diffing & cache).
  • Form submissions automatically request .turbo_stream when you ask for format.turbo_stream in a controller.
  • Redirects keep scroll position/head tags in sync.

All of this happens without any code in the repoโ€”Rails 8 + Turbo does the heavy lifting.


4. Extending Hotwire in the future

  1. More Turbo Frames โ€“ Wrap parts of pages in <turbo-frame id="cart"> to make only the cart refresh on โ€œAdd to cartโ€.
  2. Broadcasting โ€“ Hook Product model changes to turbo_stream_from channels so that all users see live stock updates.
  3. Stimulus components โ€“ Replace jQuery snippets with small controllers (dropdowns, modals, copy-to-clipboard, etc.).

Because everything is wired already (Importmap, controller autoloading, Cable), adding these features is mostly a matter of creating the HTML/ERB templates and a bit of Ruby.


Questions

1. Is Rails 8 still working with the real DOM?

  • Yes, the browser is always working with the real DOMโ€”nothing is virtualized (unlike Reactโ€™s virtual DOM).
  • Turbo intercepts navigation events (links, form submits). Instead of letting the browser perform a โ€œhardโ€ navigation, it fetches the HTML with fetch() in the background, parses the response into a hidden document fragment, then swaps specific pieces (usually the whole <body> or a <turbo-frame> target) into the live DOM.
  • Because Turbo only swaps the changed chunks, it keeps the rest of the page alive (JS state, scroll position, playing videos, etc.) and fires lifecycle events so Stimulus controllers disconnect/re-connect cleanly.

“Stimulus itself is a tiny wrapper around MutationObserver. It attaches controller instances to DOM elements and tears them down automatically when Turbo replaces those elementsโ€”so both libraries cooperate rather than fighting the DOM.”


2. How does the HTML from Turbo Drive get into the DOM without a full reload?

Step-by-step for a normal link click:

  1. turbo-railsย JSย (loadedย viaย importย “@hotwired/turbo-rails”) cancelsย the browser’sย defaultย navigation.
  2. Turbo sends anย AJAXย request (actuallyย fetch()) forย theย new URL, requesting full HTML.
  3. The response text is parsed into an off-screen DOMParser document.
  4. Turboย comparesย theย <head>ย tags, updatesย <title>ย andย anyย changed assets, thenย replaces theย <body>ย of the currentย page withย theย new oneย (or, forย <turbo-frame>, just thatย frame).
  5. Itย pushesย aย history.pushStateย entry soย Back/Forwardย work, andย firesย events likeย turbo:load.

Because no real navigation happened, the browser doesnโ€™t clear JS state, WebSocket connections, or CSS; it just swaps some DOM nodesโ€”visually it feels instantaneous.


3. What does pin mean in config/importmap.rb?

Rails 8 ships with Importmapโ€”a way to use normal ES-module import statements without a bundler.pin is simply a mapping declaration:

pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus",    to: "stimulus.min.js"

Meaning:

  • When the browser sees import "@hotwired/turbo-rails", fetch โ€ฆ/assets/turbo.min.js
  • When it sees import “controllers”, look at 
    pin_all_from "app/javascript/controllers" 
    which expands into individual mappings for every controller file.

Thinkย ofย pinย as theย importmap equivalentย ofย aย requireย statement in a bundler configโ€”justย declarative andย handled at runtime by theย browser. That’sย all there is to it: real DOM, no pageย reloads, and a lightweightย wayย to load JS modules without Webpack.

Take-aways

  • Hotwire is not one big library; it is a philosophy (+ Turbo + Stimulus) that keeps most of your UI in Ruby & ERB but still feels snappy and modern.
  • Rails 8 scaffolds everything, so you may not even realize you’re using itโ€”but you are!
  • design_studio already benefits from Hotwire’s defaults (fast navigation) and uses Turbo Streams for dynamic image deletion. The plumbing is in place to expand this pattern across the app with minimal effort.

Happy hot-wiring! ๐Ÿš€

๐ŸŒ€ Event Loops Demystified: The Secret Behind High-Performance Applications

Ever wondered how a single Redis server can handle thousands of simultaneous connections without breaking a sweat? Or how Node.js can serve millions of requests with just one thread? The magic lies in the event loop – a deceptively simple concept that powers everything from your web browser to Netflix’s streaming infrastructure.

๐ŸŽฏ What is an Event Loop?

An event loop is like a super-efficient restaurant manager who never stops moving:

๐Ÿ‘จโ€๐Ÿ’ผ Event Loop Manager:
"Order ready at table 3!" โ†’ Handle it
"New customer at door!" โ†’ Seat them  
"Payment needed at table 7!" โ†’ Process it
"Kitchen needs supplies!" โ†’ Order them

Traditional approach (blocking):

# One waiter per table (one thread per request)
waiter1.take_order_from_table_1  # Waiter1 waits here...
waiter2.take_order_from_table_2  # Waiter2 waits here...  
waiter3.take_order_from_table_3  # Waiter3 waits here...

Event loop approach (non-blocking):

# One super-waiter handling everything
loop do
  event = get_next_event()
  case event.type
  when :order_ready then serve_food(event.table)
  when :new_customer then seat_customer(event.customer)
  when :payment then process_payment(event.table)
  end
end

๐Ÿ—๏ธ The Anatomy of an Event Loop

๐Ÿ”„ The Core Loop

// Simplified event loop (Node.js style)
while (true) {
  // 1. Check for completed I/O operations
  let events = pollForEvents();

  // 2. Execute callbacks for completed operations
  events.forEach(event => {
    event.callback();
  });

  // 3. Execute any scheduled timers
  runTimers();

  // 4. If no more work, sleep until next event
  if (noMoreWork()) {
    sleep();
  }
}

๐Ÿ“‹ Event Queue System

โ”Œโ”€ Timer Queue โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€ I/O Queue โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€ Check Queue โ”€โ”€โ”
โ”‚ setTimeout()       โ”‚    โ”‚ File operations    โ”‚    โ”‚ setImmediate() โ”‚
โ”‚ setInterval()      โ”‚    โ”‚ Network requests   โ”‚    โ”‚ process.tick() โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚ Database queries   โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                     โ†“
                          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                          โ”‚   Event Loop Core   โ”‚
                          โ”‚  "What's next?"     โ”‚
                          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โšก Why Event Loops Are Lightning Fast

๐Ÿšซ No Thread Context Switching

# Traditional multi-threading overhead
Thread 1: [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ] Context Switch [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ] Context Switch
Thread 2:           [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ] Context Switch [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ]
Thread 3:                     [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ] Context Switch [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ]
# CPU wastes time switching between threads!

# Event loop efficiency  
Single Thread: [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ]
# No context switching = pure work!

๐ŸŽฏ Perfect for I/O-Heavy Applications

# What most web applications do:
def handle_request
  user = database.find_user(id)     # Wait 10ms
  posts = api.fetch_posts(user)     # Wait 50ms  
  cache.store(posts)                # Wait 5ms
  render_response(posts)            # Work 1ms
end
# Total: 66ms (65ms waiting, 1ms working!)

Event loop transforms this:

# Non-blocking version
def handle_request
  database.find_user(id) do |user|           # Queue callback
    api.fetch_posts(user) do |posts|         # Queue callback  
      cache.store(posts) do                  # Queue callback
        render_response(posts)               # Finally execute
      end
    end
  end
  # Returns immediately! Event loop handles the rest
end

โ˜๏ธ Nextflix: Cloud Architecture

NextFlix Cloud Architecture

ย High-Level Design of Netflix System Design:

Microservices Architectureย of Netflix:

Netflix Billing Migration To Aws

Application-data-caching-using-ssds

Read System Design Netflix | A Complete Architecture: System-design-netflix-a-complete-Architecture

Reference: NetFlix Blog, GeeksForGeeks Blog

๐Ÿ”ง Event Loop in Action: Redis Case Study

๐Ÿ“ก How Redis Handles 10,000 Connections

// Simplified Redis event loop (C code)
void aeMain(aeEventLoop *eventLoop) {
    while (!eventLoop->stop) {
        // 1. Wait for file descriptor events (epoll/kqueue)
        int numEvents = aeApiPoll(eventLoop, tvp);

        // 2. Process each ready event
        for (int i = 0; i < numEvents; i++) {
            aeFileEvent *fe = &eventLoop->events[fired[i].fd];

            if (fired[i].mask & AE_READABLE) {
                fe->rfileProc(eventLoop, fired[i].fd, fe->clientData, fired[i].mask);
            }
            if (fired[i].mask & AE_WRITABLE) {
                fe->wfileProc(eventLoop, fired[i].fd, fe->clientData, fired[i].mask);
            }
        }
    }
}

๐ŸŽญ The BRPOP Magic Revealed

# When you call redis.brpop()
redis.brpop("queue:default", timeout: 30)

# Redis internally does:
# 1. Check if list has items โ†’ Return immediately if yes
# 2. If empty โ†’ Register client as "blocked"  
# 3. Event loop continues serving other clients
# 4. When item added โ†’ Wake up blocked client
# 5. Return the item

# Your Ruby thread blocks, but Redis keeps serving others!

๐ŸŒ Event Loops in the Wild

๐ŸŸข Node.js: JavaScript Everywhere

// Single thread handling thousands of requests
const server = http.createServer((req, res) => {
  // This doesn't block other requests!
  database.query('SELECT * FROM users', (err, result) => {
    res.json(result);
  });
});

๐Ÿ Python: AsyncIO

import asyncio

async def handle_request():
    # Non-blocking database call
    user = await database.fetch_user(user_id)
    # Event loop serves other requests while waiting
    posts = await api.fetch_posts(user.id)  
    return render_template('profile.html', user=user, posts=posts)

# Run multiple requests concurrently
asyncio.run(handle_many_requests())

๐Ÿ’Ž Ruby: EventMachine & Async

# EventMachine (older)
EventMachine.run do
  EventMachine::HttpRequest.new('http://api.example.com').get.callback do |response|
    puts "Got response: #{response.response}"
  end
end

# Modern Ruby (Async gem)
require 'async'

Async do |task|
  response = task.async { Net::HTTP.get('example.com', '/api/data') }
  puts response.wait
end

Read More (Premium content): RailsDrop: Rubys-async-revolution

Message from the post: “๐Ÿ’ช Ruby is not holding back with the JS spread of the WORLD! ๐Ÿ’ช Rails isย NOT DYING in 2025!! ITS EVOLVING!! ๐Ÿ’ช We Ruby/Rails Community Fire with BIG in Future! ๐Ÿ•บ”


โš™๏ธ The Dark Side: Event Loop Limitations

๐ŸŒ CPU-Intensive Tasks Kill Performance

// This blocks EVERYTHING!
function badCpuTask() {
  let result = 0;
  for (let i = 0; i < 10000000000; i++) {  // 10 billion iterations
    result += i;
  }
  return result;
}

// While this runs, NO other requests get served!

๐Ÿฉน The Solution: Worker Threads

// Offload heavy work to separate thread
const { Worker } = require('worker_threads');

function goodCpuTask() {
  return new Promise((resolve) => {
    const worker = new Worker('./cpu-intensive-task.js');
    worker.postMessage({ data: 'process this' });
    worker.on('message', resolve);
  });
}

๐ŸŽฏ When to Use Event Loops

โœ… Perfect For:

  • Web servers (lots of I/O, little CPU)
  • API gateways (routing requests)
  • Real-time applications (chat, games)
  • Database proxies (Redis, MongoDB)
  • File processing (reading/writing files)

โŒ Avoid For:

  • Heavy calculations (image processing)
  • Machine learning (training models)
  • Cryptographic operations (Bitcoin mining)
  • Scientific computing (physics simulations)

๐Ÿš€ Performance Comparison

Handling 10,000 Concurrent Connections:

Traditional Threading:
โ”œโ”€โ”€ Memory: ~2GB (200KB per thread)
โ”œโ”€โ”€ Context switches: ~100,000/second  
โ”œโ”€โ”€ CPU overhead: ~30%
โ””โ”€โ”€ Max connections: ~5,000

Event Loop:
โ”œโ”€โ”€ Memory: ~50MB (single thread + buffers)
โ”œโ”€โ”€ Context switches: 0
โ”œโ”€โ”€ CPU overhead: ~5%  
โ””โ”€โ”€ Max connections: ~100,000+

๐Ÿ”ฎ The Future: Event Loops Everywhere

Modern frameworks are embracing event-driven architecture:

  • Rails 7+: Hotwire + ActionCable
  • Django: ASGI + async views
  • Go: Goroutines (event-loop-like)
  • Rust: Tokio async runtime
  • Java: Project Loom virtual threads

๐Ÿ’ก Key Takeaways

  1. Event loops excel at I/O-heavy workloads – perfect for web applications
  2. They use a single thread – no context switching overhead
  3. Non-blocking operations – one slow request doesn’t block others
  4. CPU-intensive tasks are kryptonite – offload to worker threads
  5. Modern web development is moving toward async patterns – learn them now!

The next time you see redis.brpop() blocking your Ruby thread while Redis happily serves thousands of other clients, you’ll know exactly what’s happening under the hood! ๐ŸŽ‰