rails-37-style-backend-performance

star 0

Preloading, N+1 prevention, memoization

Chwistophe By Chwistophe schedule Updated 3/6/2026

name: rails-37-style-backend-performance description: Preloading, N+1 prevention, memoization license: MIT

Performance Patterns

Database, CSS, and rendering optimizations from 37signals.


CSS Performance

Avoid Complex :has() Selectors

Safari freezes on complex nested :has() selectors (#1089). Prefer simpler selectors over clever CSS.

View Transitions

Remove unnecessary view-transition-name causing navigation jank.

Database Performance

N+1 → JOINs

Replace find_each loops with JOINs for bulk operations (#1129).

Accept "unmaintainable" SQL when performance requires it:

"Way way way faster but feels unmaintainable"

Counter Caches

Fast reads, but callbacks are bypassed. Consider manual approach if you need side effects.

Pagination

  • Start with reasonable page sizes (25-50)
  • Reduce if initial render is slow (#1089: 50 → 25)
  • Use "Load more" buttons or intersection observer
  • Separate pagination per column/section

Active Storage

Read Replicas

Use preprocessed: true - lazy generation fails on read-only replicas (#767)

Slow Uploads

Extend signed URL expiry from default 5 min to 48 hours (#773). Cloudflare buffering can exceed default timeout.

Large Files

Skip previews above size threshold (e.g., 16MB) to avoid timeouts (#941)

Avatars

  • Redirect to blob URL instead of streaming through Rails
  • Define thumbnail variants for consistent sizing
  • Faster than proxying through the app

Rendering Performance

Lazy Loading

  • Convert expensive menus to turbo frames
  • Load on interaction, not page load
  • Reduces initial render time significantly

Debouncing

100ms debounce on filter search feels responsive (#567)

Puma/Ruby Tuning (#1283)

# config/puma.rb
workers Concurrent.physical_processor_count
threads 1, 1

before_fork do
  Process.warmup  # GC, compact, malloc_trim for CoW
end

Use autotuner gem to collect data and suggest tuning.

N+1 Prevention (#1747)

Use prosopite gem for detection. Replace:

# Bad - extra query
assignments.exists? assignee: user

# Good - in-memory
assignments.any? { |a| a.assignee_id == user.id }

Create preloaded scopes:

scope :preloaded, -> {
  includes(:column, :tags, board: [:entropy, :columns])
}

Optimistic UI for D&D (#1927)

Insert immediately, request async:

#insertDraggedItem(container, item) {
  // Insert at correct position respecting priority
  const topItems = container.querySelectorAll("[data-drag-and-drop-top]")
  // ... insert logic
}

await this.#submitDropRequest(item, container)

Batch SQL Over N+1 Loops (#1129)

Replace find_each with JOINs:

# Single query with JOINs instead of N queries
user.mentions
  .joins("LEFT JOIN cards ON ...")
  .joins("LEFT JOIN comments ON ...")
  .where("cards.collection_id = ?", id)
  .destroy_all
Install via CLI
npx skills add https://github.com/Chwistophe/agent-skills-unofficial-37-signals-rails-way-fizzy --skill rails-37-style-backend-performance
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator