CrackedRuby CrackedRuby

Static vs Dynamic Websites

Overview

Static and dynamic websites represent two fundamentally different approaches to delivering web content. Static websites consist of fixed HTML, CSS, and JavaScript files served directly to users without server-side processing. The content remains the same for every visitor until a developer manually updates the files. Dynamic websites generate content on-demand through server-side code, typically pulling data from databases and assembling pages in response to each request.

The distinction extends beyond simple implementation details to encompass architecture, performance, security, and maintenance patterns. A static site might serve a company homepage where content changes monthly, while a dynamic site powers a social media platform where content updates continuously and personalizes for each user.

Static websites existed first in web history. Early websites consisted entirely of hand-written HTML files uploaded to servers. As the web evolved, dynamic capabilities emerged through CGI scripts, server-side languages, and database integration. This enabled personalization, user authentication, and interactive features. More recently, static sites have experienced renewed interest through static site generators and JAMstack architectures that combine static delivery with dynamic functionality through APIs and JavaScript.

<!-- Static Website: Pre-built HTML file -->
<!DOCTYPE html>
<html>
<head><title>Products</title></head>
<body>
  <h1>Our Products</h1>
  <ul>
    <li>Product A - $29.99</li>
    <li>Product B - $39.99</li>
  </ul>
</body>
</html>
# Dynamic Website: Ruby generates HTML per request
get '/products' do
  @products = Product.all
  erb :products
end

# products.erb template
<h1>Our Products</h1>
<ul>
  <% @products.each do |product| %>
    <li><%= product.name %> - $<%= product.price %></li>
  <% end %>
</ul>

Key Principles

The core distinction between static and dynamic websites centers on when and where content generation occurs. Static websites perform all content generation at build time, producing complete HTML files before deployment. Dynamic websites perform content generation at request time, executing code and querying databases when users access pages.

Static Website Characteristics

Static sites consist of pre-generated files stored on a web server or CDN. When a browser requests a page, the server retrieves and sends the corresponding HTML file without modification. No server-side code execution occurs during the request. Changes to content require rebuilding the entire site or affected pages, then deploying the updated files.

The build process converts source content (Markdown, templates, data files) into final HTML, CSS, and JavaScript files. This happens on a developer's machine or in a CI/CD pipeline. The output is a directory structure containing all necessary files, ready for deployment to any static file host.

Dynamic Website Characteristics

Dynamic sites execute server-side code for each request. The application server receives a request, runs Ruby code (or another language), queries databases, processes business logic, and generates HTML. The same URL can return different content based on the current user, time of day, database state, or request parameters.

Database interaction forms a central component. User profiles, blog posts, product inventories, and other content reside in databases. The application queries this data during request processing and injects it into templates. Multiple users viewing the same URL simultaneously might see different content based on their authentication state or personalization settings.

Request Processing Flow

For static sites: Browser requests URL → Web server locates file → Server sends file → Browser renders HTML. Processing time typically measures in single-digit milliseconds.

For dynamic sites: Browser requests URL → Application server receives request → Server executes Ruby code → Code queries database → Database returns data → Code processes data → Template engine generates HTML → Server sends HTML → Browser renders. Processing time ranges from tens to hundreds of milliseconds depending on complexity.

Content Update Mechanisms

Static sites require a build-and-deploy cycle. A developer or content editor updates source files, triggers a build process that regenerates affected pages, and deploys the new files to hosting. The entire site or changed pages become available atomically after deployment completes.

Dynamic sites update content through database modifications. An administrator logs into a CMS, updates a post, saves changes, and the new content appears immediately on the next page request. No deployment or build process occurs.

Hybrid Approaches

Modern architectures blur these boundaries. Server-side rendering (SSR) generates static HTML at request time but caches results. Incremental static regeneration (ISR) rebuilds individual pages on-demand when content changes. Client-side rendering loads minimal HTML then uses JavaScript to fetch data from APIs and build the page in the browser. These approaches combine static delivery speed with dynamic content capabilities.

Design Considerations

Selecting between static and dynamic architectures depends on content update frequency, personalization requirements, scale, team capabilities, and budget constraints.

Content Update Frequency

Static sites excel when content changes infrequently. Marketing pages, documentation, portfolios, and company websites often update daily at most. The build-and-deploy cycle adds minimal friction. A documentation site might rebuild nightly to incorporate the day's changes.

Dynamic sites handle frequent updates without friction. Social networks, e-commerce inventory, news sites, and collaborative platforms require immediate content visibility. A news site publishing articles every hour cannot wait for builds and deploys.

Edge case: High-frequency updates with known timing can work with static sites. A sports site might rebuild when games end, incorporating final scores. Scheduled builds can refresh content every 15 minutes for near-real-time updates without dynamic infrastructure.

Personalization and User-Specific Content

Static sites serve identical content to all visitors. Personalization requires client-side JavaScript fetching data from APIs. A static site cannot show user-specific information in initial HTML without additional architecture.

Dynamic sites personalize every request. User dashboards, recommendation engines, access-controlled content, and shopping carts require server-side knowledge of the requesting user. The application queries user-specific data and generates appropriate HTML.

Workaround: Static sites can implement authentication and personalization through JavaScript and APIs. The initial HTML loads unpersonalized, then JavaScript fetches user data and updates the page. This creates a slower initial experience but maintains static infrastructure benefits.

Scale and Traffic Patterns

Static sites scale effortlessly. CDNs cache files at edge locations worldwide. A static site can handle millions of requests per hour with minimal infrastructure. Costs remain low regardless of traffic spikes. A viral blog post generates the same infrastructure cost as zero traffic.

Dynamic sites scale vertically (larger servers) or horizontally (more servers), both increasing costs. Each request executes code and queries databases. High traffic requires load balancers, application server clusters, database replication, and caching layers. A traffic spike can overwhelm infrastructure or generate massive cloud bills.

Cache effectiveness differs dramatically. Static sites achieve near-100% cache hit rates since content never changes between deploys. Dynamic sites struggle with cache invalidation—determining when cached content becomes stale. User-specific content often cannot be cached at all.

Development Complexity

Static sites require build tooling. Developers configure static site generators, define templates, and create build pipelines. Initial setup involves learning a new tool ecosystem. Ongoing development focuses on templates and content rather than server-side logic.

Dynamic sites require full-stack infrastructure. Developers set up application servers, databases, background job processors, and monitoring. They implement business logic, database migrations, authentication systems, and error handling. Maintenance includes security patches, database backups, and performance optimization.

Team skills influence this decision. A team comfortable with Ruby and databases can build dynamic sites efficiently. A team focused on frontend development and content might prefer static sites with external services (forms, comments, search) handled through third-party APIs.

Database Requirements

Static sites avoid database operational overhead. No database to maintain, backup, monitor, or secure. Content comes from files in version control. Rollbacks involve deploying previous commits.

Dynamic sites depend on database availability and performance. Database outages take the site offline. Backups require careful planning. Schema migrations need coordination with application code. Database optimization becomes crucial at scale.

Exception: Dynamic sites can use databases solely for administrative interfaces while serving pre-generated content to visitors. An article database feeds a static site generator that rebuilds when content changes. This provides editorial convenience without runtime database dependencies.

Cost Structure

Static hosting costs approach zero for low to medium traffic. Services like Netlify, Vercel, and GitHub Pages offer free hosting for static sites. Even paid CDN services cost less than dynamic hosting since no compute resources are consumed per request.

Dynamic hosting requires always-running servers or serverless compute that charges per request. Minimum monthly costs start at $5-10 for simple sites, rising to hundreds or thousands for high-traffic applications. Database hosting adds additional costs.

Content Management

Static sites traditionally require developer involvement for content changes. Editors modify Markdown files in Git repositories and trigger builds. This technical barrier prevents non-developer content updates.

Dynamic sites offer browser-based content management systems. Editors log in, see WYSIWYG interfaces, and publish changes immediately without developer intervention. This reduces bottlenecks and enables non-technical team members to manage content.

Headless CMS solutions bridge this gap for static sites. Editors use a CMS interface that stores content in a database or API. The static site generator fetches content from the CMS during builds. This provides editor-friendly interfaces while maintaining static delivery.

Ruby Implementation

Ruby supports both static and dynamic website development through multiple frameworks and tools. The dynamic web framework ecosystem matured first, with static site generators emerging later as the static approach gained popularity.

Dynamic Sites with Sinatra

Sinatra provides a lightweight framework for dynamic Ruby websites. It defines routes that execute Ruby code when requested.

require 'sinatra'
require 'sqlite3'

configure do
  set :db, SQLite3::Database.new('blog.db')
  settings.db.results_as_hash = true
end

get '/' do
  posts = settings.db.execute('SELECT * FROM posts ORDER BY created_at DESC LIMIT 10')
  erb :index, locals: { posts: posts }
end

get '/post/:id' do
  post = settings.db.execute('SELECT * FROM posts WHERE id = ?', params[:id]).first
  halt 404 unless post
  erb :post, locals: { post: post }
end

Every request executes Ruby code, queries the database, and generates HTML through ERB templates. The application runs continuously on a server.

Dynamic Sites with Rails

Rails provides a full-featured framework for complex dynamic websites with conventions for database models, controllers, and views.

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.published.order(created_at: :desc).limit(10)
  end
  
  def show
    @post = Post.find(params[:id])
  end
end

# app/models/post.rb
class Post < ApplicationRecord
  scope :published, -> { where(published: true) }
  
  validates :title, presence: true
  validates :content, presence: true
end

# app/views/posts/show.html.erb
<article>
  <h1><%= @post.title %></h1>
  <time><%= @post.created_at.strftime('%B %d, %Y') %></time>
  <div><%= sanitize @post.content %></div>
</article>

Rails includes Active Record for database interaction, Action View for templating, and extensive middleware for authentication, caching, and asset management. Applications grow to include background jobs, API endpoints, and complex business logic.

Static Sites with Jekyll

Jekyll generates static websites from templates and content files. It runs as a build tool, producing HTML files for deployment.

# _config.yml
title: My Blog
description: A static blog
url: https://example.com

# _posts/2025-10-01-first-post.md
---
layout: post
title: "First Post"
date: 2025-10-01
---

Post content in Markdown.

# _layouts/post.html
<!DOCTYPE html>
<html>
<head>
  <title>{{ page.title }} - {{ site.title }}</title>
</head>
<body>
  <article>
    <h1>{{ page.title }}</h1>
    <time>{{ page.date | date: "%B %d, %Y" }}</time>
    <div>{{ content }}</div>
  </article>
</body>
</html>

Run jekyll build to generate the site in the _site directory. Deploy these static files to any web host. Jekyll supports plugins for extended functionality:

# _plugins/reading_time.rb
module Jekyll
  module ReadingTimeFilter
    def reading_time(input)
      words_per_minute = 200
      words = input.split.size
      minutes = (words / words_per_minute).ceil
      "#{minutes} min read"
    end
  end
end

Liquid::Template.register_filter(Jekyll::ReadingTimeFilter)

# Usage in templates: {{ content | reading_time }}

Static Sites with Middleman

Middleman offers more flexibility than Jekyll with Ruby-based configuration and asset pipeline integration.

# config.rb
set :css_dir, 'stylesheets'
set :js_dir, 'javascripts'
set :images_dir, 'images'

activate :blog do |blog|
  blog.prefix = 'blog'
  blog.permalink = '{year}/{month}/{title}.html'
end

configure :build do
  activate :minify_css
  activate :minify_javascript
  activate :asset_hash
end

# Custom helper
helpers do
  def format_date(date)
    date.strftime('%B %d, %Y')
  end
end

# source/blog/2025-10-01-post.html.md
---
title: Blog Post
date: 2025-10-01
---

Content here.

# source/layouts/layout.erb
<!DOCTYPE html>
<html>
<head>
  <title><%= current_page.data.title || 'My Site' %></title>
  <%= stylesheet_link_tag 'site' %>
</head>
<body>
  <%= yield %>
  <%= javascript_include_tag 'site' %>
</body>
</html>

Middleman excels at sites with custom build requirements, data files, and complex templates. The middleman build command generates production files.

Hybrid Approach: Rails with Static Caching

Rails applications can cache entire pages as static files, serving them like a static site until cache invalidation:

class PostsController < ApplicationController
  caches_page :show
  
  def show
    @post = Post.find(params[:id])
  end
  
  def update
    @post = Post.find(params[:id])
    if @post.update(post_params)
      expire_page action: :show, id: @post.id
      redirect_to @post
    else
      render :edit
    end
  end
end

First requests generate HTML and save it to public/posts/123.html. Subsequent requests for that URL receive the static file without executing Ruby code. Updates expire cached pages, forcing regeneration on the next request.

API-Backed Static Sites

Static sites can fetch data from dynamic APIs during build time:

# Rakefile for build process
require 'net/http'
require 'json'
require 'erb'

task :build do
  # Fetch data from API
  uri = URI('https://api.example.com/posts')
  response = Net::HTTP.get(uri)
  posts = JSON.parse(response)
  
  # Generate HTML from template
  template = ERB.new(File.read('templates/index.html.erb'))
  html = template.result_with_hash(posts: posts)
  
  # Write to output directory
  File.write('public/index.html', html)
end

This pattern fetches live data during builds while serving static HTML to visitors. Scheduled builds keep content fresh without runtime API calls.

Performance Considerations

Performance differences between static and dynamic websites manifest in response time, server load, bandwidth efficiency, and scaling characteristics.

Response Time

Static websites serve files directly from disk or memory. Web servers like Nginx can serve tens of thousands of static files per second from a single machine. Response times typically range from 1-10 milliseconds for the server processing, plus network latency.

Dynamic websites execute code for each request. Even simple Rails or Sinatra routes take 10-50 milliseconds for Ruby execution and template rendering. Database queries add 5-100 milliseconds depending on complexity and database performance. Complex pages with multiple queries can reach 200-500 milliseconds.

# Static site: Pre-generated HTML (1-10ms server time)
# File served directly by Nginx

# Dynamic site: Ruby execution (50-200ms typical)
def show
  @post = Post.find(params[:id])                    # 10-30ms database query
  @comments = @post.comments.includes(:user).limit(50)  # 20-50ms with join
  @related = Post.where(category: @post.category).limit(5)  # 10-20ms
  render :show                                       # 10-50ms template
end

CDN edge caching dramatically improves static site performance. Files cached at edge locations worldwide serve in 10-50 milliseconds total including network time. Dynamic sites benefit less from CDN caching since personalized content cannot be cached.

Server Resource Usage

Static file serving consumes minimal CPU and memory. A server handling 10,000 requests per second for static content might use 10% CPU. Memory usage remains constant regardless of traffic.

Dynamic applications consume CPU for code execution and memory for application instances. A Rails application might use 200-500MB of memory per worker process. Under load, CPU usage scales linearly with requests. A server handling 100 dynamic requests per second might reach 80-90% CPU utilization.

Database connections represent a critical resource. Each application worker maintains database connections. Connection pool exhaustion causes requests to queue or fail. Static sites eliminate database connections entirely for visitor-facing traffic.

Caching Strategies

Static sites achieve perfect cache efficiency. Every file can be cached indefinitely at the CDN edge using far-future expires headers. Cache hit rates approach 100%. Only the first request after deployment hits the origin server.

# Nginx configuration for static files
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

Dynamic sites struggle with cache invalidation. Caching user-specific content is impossible. Public content can be cached but requires invalidation when data changes. Fragment caching helps by caching template portions:

# Rails fragment caching
<% cache @post do %>
  <h1><%= @post.title %></h1>
  <div><%= @post.content %></div>
<% end %>

# Expires automatically when @post updates

Russian doll caching nests cached fragments, invalidating only changed portions:

<% cache @post do %>
  <h1><%= @post.title %></h1>
  <% cache ['comments', @post] do %>
    <% @post.comments.each do |comment| %>
      <% cache comment do %>
        <p><%= comment.body %></p>
      <% end %>
    <% end %>
  <% end %>
<% end %>

Bandwidth and Cost

Static files compress effectively with gzip or Brotli. HTML, CSS, and JavaScript shrink to 15-30% of original size. CDNs serve compressed files automatically.

Dynamic sites generate uncompressed HTML that requires middleware compression. Rails applications use Rack::Deflater:

# config/application.rb
config.middleware.use Rack::Deflater

CDN costs for static sites remain low since origin requests are rare. Dynamic sites generate more origin traffic since personalized content cannot be cached, increasing bandwidth costs.

Build Time vs. Runtime Performance

Static sites shift work from request time to build time. A complex documentation site might take 5-10 minutes to build but serve pages in milliseconds. Large static sites with thousands of pages can take 30+ minutes to build.

Incremental builds optimize this by regenerating only changed pages:

# Jekyll incremental build
jekyll build --incremental

# Custom build tool with change detection
changed_files = `git diff --name-only HEAD~1`.split("\n")
pages_to_rebuild = changed_files.select { |f| f.end_with?('.md') }
rebuild_pages(pages_to_rebuild)

Dynamic sites avoid build time entirely but pay the performance cost on every request. For frequently changing content, this trade-off favors dynamic generation. For stable content with high traffic, static generation wins.

Security Implications

Static and dynamic websites present different security profiles, with static sites offering a significantly reduced attack surface while dynamic sites require active security measures.

Attack Surface Reduction

Static sites eliminate entire categories of vulnerabilities. No server-side code execution means no code injection, SQL injection, or command injection vulnerabilities. No database means no database breaches or connection string exposure. No user authentication in the static layer eliminates session hijacking and authentication bypass vulnerabilities.

The attack surface consists primarily of the web server configuration and any client-side JavaScript. Misconfigured CORS headers or exposed .git directories represent the main risks. Static files cannot execute malicious payloads on the server.

# Dynamic site vulnerability example
get '/user/:id' do
  # SQL injection vulnerability
  user = db.execute("SELECT * FROM users WHERE id = #{params[:id]}").first
  erb :user, locals: { user: user }
end

# Static site: No SQL, no injection
# Pre-generated HTML files served directly

Injection Attack Prevention

Dynamic sites must defend against multiple injection types. SQL injection exploits occur when user input enters SQL queries without sanitization. Cross-site scripting (XSS) allows attackers to inject malicious JavaScript. Command injection executes system commands through application vulnerabilities.

Ruby frameworks provide protection mechanisms:

# Rails: Parameterized queries prevent SQL injection
User.where('email = ?', params[:email])
User.where(email: params[:email])  # Hash notation also safe

# ERB escaping prevents XSS
<p><%= @user.bio %></p>  # Automatically escaped
<p><%== @user.bio %></p>  # Dangerous: disables escaping

# Sinatra: Manual escaping required
require 'erb'
<p><%= ERB::Util.html_escape(@user.bio) %></p>

Static sites sidestep these concerns since no user input enters server-side processing during requests. User input only matters during the build process, where it comes from trusted sources like content management systems or version control.

Authentication and Authorization

Dynamic sites implement authentication systems that introduce security complexity. Password storage, session management, token generation, and permission checking create vulnerability opportunities.

# Rails authentication implementation
class SessionsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])
    if user && user.authenticate(params[:password])
      session[:user_id] = user.id
      redirect_to root_path
    else
      flash[:error] = 'Invalid credentials'
      render :new
    end
  end
end

# Authorization in controllers
class PostsController < ApplicationController
  before_action :require_login
  before_action :require_ownership, only: [:edit, :update, :destroy]
  
  def require_ownership
    @post = Post.find(params[:id])
    redirect_to root_path unless @post.user_id == current_user.id
  end
end

Common vulnerabilities include weak password requirements, missing CSRF protection, insecure session storage, and authorization bypass through parameter manipulation. Static sites defer authentication to external services (OAuth providers, Auth0) or implement it client-side through JavaScript.

Content Security Policies

Both static and dynamic sites benefit from Content Security Policy (CSP) headers that restrict resource loading:

# Rails: Setting CSP headers
class ApplicationController < ActionController::Base
  before_action :set_csp_header
  
  private
  
  def set_csp_header
    response.headers['Content-Security-Policy'] = 
      "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'"
  end
end

# Static site: Web server configuration (Nginx)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com";

Static sites have simpler CSP configurations since all resources are known at build time. Dynamic sites must account for runtime-generated inline scripts and styles.

Dependency Vulnerabilities

Dynamic applications depend on numerous gems that may contain security vulnerabilities. Rails applications typically require 50-200 gems. Each represents a potential vulnerability.

# Gemfile.lock inspection
bundle audit check --update

# Example vulnerability output
Name: actionpack
Version: 6.1.4
Advisory: CVE-2021-22885
Criticality: High
URL: https://groups.google.com/g/rubyonrails-security/c/...

Regular dependency updates and security monitoring become critical. Static site generators have dependencies but they affect only build-time security, not production runtime.

File Upload Vulnerabilities

Dynamic sites accepting file uploads face validation challenges. Uploaded files might contain malicious code, overwrite system files, or consume excessive storage.

# Unsafe file upload
post '/upload' do
  file = params[:file]
  File.open("uploads/#{file[:filename]}", 'wb') do |f|
    f.write(file[:tempfile].read)
  end
end

# Safer implementation
post '/upload' do
  file = params[:file]
  
  # Validate file type
  unless ['image/jpeg', 'image/png'].include?(file[:type])
    halt 400, 'Invalid file type'
  end
  
  # Generate safe filename
  ext = File.extname(file[:filename])
  filename = "#{SecureRandom.uuid}#{ext}"
  
  # Store outside web root
  File.open("private/uploads/#{filename}", 'wb') do |f|
    f.write(file[:tempfile].read)
  end
end

Static sites typically avoid file uploads in the static layer, delegating to separate services or implementing uploads through JavaScript to APIs.

Tools & Ecosystem

The Ruby ecosystem provides diverse tools for both static and dynamic website development, from full-featured frameworks to specialized utilities.

Dynamic Web Frameworks

Rails remains the dominant Ruby web framework. It provides conventions for MVC architecture, Active Record ORM, Asset Pipeline, Action Cable for WebSockets, and Active Storage for file uploads. Rails applications handle millions of requests for companies like GitHub, Shopify, and Airbnb.

# Rails application structure
rails new myapp --database=postgresql
cd myapp
rails generate scaffold Post title:string content:text published:boolean
rails db:migrate
rails server

# Full CRUD operations generated automatically
# Routes, controller, model, views, tests created

Sinatra offers a lightweight alternative for smaller applications. It provides routing and templating without extensive conventions or dependencies.

# Sinatra application
require 'sinatra'

get '/' do
  'Hello world'
end

get '/posts/:id' do
  @post = Post.find(params[:id])
  erb :post
end

# Modular style for larger apps
class MyApp < Sinatra::Base
  configure do
    set :database, Sequel.connect(ENV['DATABASE_URL'])
  end
  
  get '/' do
    erb :index
  end
end

Hanami provides an alternative to Rails with explicit architecture and faster performance. It emphasizes objects over magic, multiple sub-applications within one codebase, and repository pattern for database access.

Static Site Generators

Jekyll dominates Ruby static site generation. GitHub Pages uses Jekyll, making it the most deployed static site generator. It converts Markdown and Liquid templates into HTML, supports plugins through Ruby gems, and integrates with GitHub's infrastructure.

# Jekyll plugin example: Custom tag
module Jekyll
  class RenderPartial < Liquid::Tag
    def initialize(tag_name, markup, tokens)
      super
      @partial_name = markup.strip
    end
    
    def render(context)
      partial_path = "_includes/#{@partial_name}.html"
      partial = File.read(partial_path)
      Liquid::Template.parse(partial).render(context)
    end
  end
end

Liquid::Template.register_tag('render_partial', Jekyll::RenderPartial)

Middleman offers more programmatic control through Ruby configuration files. It supports multiple template engines (ERB, Haml, Slim), data files for dynamic content, and a proxy system for generating pages from data.

# Middleman: Generate pages from data
# data/products.yml
products:
  - slug: product-a
    name: Product A
    price: 29.99
  - slug: product-b
    name: Product B
    price: 39.99

# config.rb
data.products.products.each do |product|
  proxy "/products/#{product[:slug]}.html", 
        "/templates/product.html", 
        locals: { product: product }
end

Bridgetown emerged as a modernized Jekyll alternative with Webpack integration, component-based architecture, and improved performance. It maintains Jekyll compatibility while adding contemporary features.

Headless CMS Integration

Contentful, Sanity, and Strapi provide headless CMS capabilities that separate content management from presentation. Static site generators fetch content during builds:

# Jekyll plugin to fetch from Contentful
require 'contentful'

module Jekyll
  class ContentfulGenerator < Generator
    def generate(site)
      client = Contentful::Client.new(
        access_token: ENV['CONTENTFUL_TOKEN'],
        space: ENV['CONTENTFUL_SPACE']
      )
      
      entries = client.entries(content_type: 'post')
      entries.each do |entry|
        site.pages << ContentfulPage.new(site, entry)
      end
    end
  end
  
  class ContentfulPage < Page
    def initialize(site, entry)
      @site = site
      @base = site.source
      @dir = 'posts'
      @name = "#{entry.slug}.html"
      
      self.process(@name)
      self.read_yaml(File.join(base, '_layouts'), 'post.html')
      self.data['title'] = entry.title
      self.data['content'] = entry.content
    end
  end
end

Hosting and Deployment

Heroku pioneered platform-as-a-service for Ruby applications. It deploys Rails and Sinatra apps through Git, manages databases and add-ons, and scales automatically.

# Heroku deployment
git push heroku main
heroku run rails db:migrate
heroku ps:scale web=2

# Procfile
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq

Netlify and Vercel excel at static site hosting. They detect static site generators, build automatically on Git commits, provide CDN distribution, and offer serverless functions for dynamic features.

# netlify.toml configuration
[build]
  command = "jekyll build"
  publish = "_site"

[[plugins]]
  package = "@netlify/plugin-cache-jekyll"

Render offers a middle ground with static site hosting and full application hosting on the same platform.

Ruby Gems for Web Development

Rack provides the foundation for Ruby web frameworks. It defines a simple interface between web servers and Ruby applications:

# Basic Rack application
class MyApp
  def call(env)
    [200, {'Content-Type' => 'text/html'}, ['Hello World']]
  end
end

# Middleware example
class Timer
  def initialize(app)
    @app = app
  end
  
  def call(env)
    start = Time.now
    status, headers, body = @app.call(env)
    duration = Time.now - start
    puts "Request took #{duration}s"
    [status, headers, body]
  end
end

use Timer
run MyApp.new

Puma serves as the default web server for Rails. It handles concurrent requests through threads and workers, supports SSL, and includes hot restart capabilities.

Sequel and ROM provide alternatives to Active Record for database access. They offer more explicit query building and reduced magic:

# Sequel example
require 'sequel'

DB = Sequel.connect(ENV['DATABASE_URL'])

posts = DB[:posts].where(published: true).order(:created_at).all
post = DB[:posts].where(id: params[:id]).first

DB[:posts].insert(
  title: params[:title],
  content: params[:content],
  created_at: Time.now
)

Reference

Architecture Comparison

Characteristic Static Websites Dynamic Websites
Content Generation Build time Request time
Server Requirements File server only Application server + database
Response Time 1-10ms typical 50-200ms typical
Database Dependency None for serving Required for serving
Scalability Excellent, CDN-based Requires horizontal scaling
Cost at Scale Minimal Increases with traffic
Update Mechanism Rebuild and deploy Database modification
Personalization Client-side only Server-side per request
Cache Effectiveness Near 100% Variable, user-dependent

Ruby Framework Selection

Framework Type Complexity Best For
Jekyll Static Low Blogs, documentation, marketing sites
Middleman Static Medium Custom static sites with complex builds
Bridgetown Static Medium Modern static sites with component architecture
Sinatra Dynamic Low APIs, microservices, simple web apps
Rails Dynamic High Full-featured applications, SaaS platforms
Hanami Dynamic Medium Explicit architecture, multiple sub-apps

Security Comparison

Vulnerability Type Static Site Risk Dynamic Site Risk
SQL Injection None High without parameterization
XSS Low, client-side only High without output escaping
CSRF None in static layer High without token validation
Authentication Bypass N/A Medium to high
File Upload Exploits None in static layer High without validation
Session Hijacking None in static layer Medium without HTTPS and secure cookies
Dependency Vulnerabilities Build-time only Runtime impact
Server-Side Code Execution None High without input validation

Performance Metrics

Metric Static Site Dynamic Site
Server Processing Time 1-10ms 50-500ms
Database Query Time 0ms 5-100ms per query
Memory per Request <1MB 10-50MB
Concurrent Requests per Server 10,000+ 50-500
CDN Cache Hit Rate 95-100% 20-80%
Time to First Byte 10-50ms 100-500ms
Build Time 1-30 minutes N/A

Deployment Commands

Operation Static (Jekyll) Dynamic (Rails)
Local Development jekyll serve rails server
Production Build jekyll build RAILS_ENV=production bundle exec rake assets:precompile
Deploy git push netlify main git push heroku main
Database Migration N/A heroku run rails db:migrate
View Logs N/A heroku logs --tail
Scale Application Automatic via CDN heroku ps:scale web=3
Environment Variables netlify env:set KEY value heroku config:set KEY=value

Common Patterns

Pattern Static Implementation Dynamic Implementation
Blog with Comments Static HTML + JavaScript API calls Server-side rendering with database
User Authentication OAuth via JavaScript Session-based with Devise or custom
Search Functionality Lunr.js client-side index Full-text database search
Form Submission Third-party service or API Controller action with database insert
Real-time Updates WebSocket connection to API Action Cable or Server-Sent Events
Content Versioning Git history Database records with timestamps
Multi-language Support Separate build per language Dynamic locale switching

Hybrid Architecture Options

Approach Description Use Case
Static with API Static HTML, JavaScript fetches dynamic data Content-heavy sites with limited dynamic features
Incremental Static Regeneration Rebuild individual pages on-demand Large sites with occasional content updates
Edge Functions Serverless functions at CDN edge Personalization without full server infrastructure
Static Export from Dynamic Generate static files from dynamic app Documentation from database content
Static Front, Dynamic Admin Public site static, admin interface dynamic Marketing sites with internal content management