dev.to is one of the largest developer communities on the web, serving millions of technical articles, discussions, and feeds each month. The platform runs on Forem, an open-source software stack built on Ruby on Rails, PostgreSQL, Redis, and Sidekiq.
Forem's Rails architecture provides productive developer tooling and clean domain abstractions. However, serving dynamic server-rendered HTML through Puma workers introduces measurable latency, especially under high concurrency or traffic surges.
We ran a performance and infrastructure audit against production dev.to endpoints using an automated benchmarking suite. Here is a breakdown of origin bottlenecks, empirical benchmark results, and how an in-memory edge proxy like ApexCache drops time to first byte (TTFB) from 640ms down to 11ms while offloading 100% of read traffic from origin Rails workers.
1. The Rails request lifecycle on Forem
When a user requests a public page like https://dev.to/videos or https://dev.to/challenges, the request travels through several layers:
HTTP Request ──► Cloudflare / CDN ──► Origin Load Balancer ──► Puma Worker (Ruby)
├── Controller action & params validation
├── PostgreSQL query (articles, user relations)
├── Fragment cache checks (Redis)
└── ERB / ViewComponent HTML rendering
Even with Redis fragment caching and database indexing, every request hitting the Rails origin must:
- Allocate an execution thread inside a Puma worker process.
- Initialize request context, parse cookies, and evaluate session state.
- Fetch missing cache fragments or execute database queries via ActiveRecord.
- Render string templates and construct the complete HTTP payload.
Under light traffic, a modern Rails server responds in 300ms to 600ms. But because Ruby is interpreted and execution threads hold memory, Puma worker pools quickly saturate when hundreds of concurrent requests arrive simultaneously.
2. Benchmark methodology: direct origin vs edge acceleration
We audited dev.to across 8 representative public routes:
-
/(Homepage feed) -
/challenges(Community coding challenges) -
/++(Community member index) -
/videos(Video content feed) -
/deved(Developer Education listings) -
/help(Help & FAQ center) -
/advertise(Advertising information) -
/organizations(Developer organization index)
The test ran in two phases:
-
Phase 1 (Direct Origin Baseline): Paced baseline queries (concurrency 2, 100ms pacing delay) directly to
https://www.dev.toto measure native origin response times without triggering rate limits. - Phase 2 & 3 (In-Memory Edge Acceleration): The same endpoints routed through an ApexCache edge proxy instance, warmed with a single pass, then stress-tested across 25,000 requests at a concurrency of 40 worker threads.
3. Empirical benchmark results
The benchmark measured TTFB, end-to-end latency, throughput capacity, and tail latencies.
Telemetry comparison matrix
| Evaluation parameter | Before (Direct origin) | After (ApexCache edge) | Delta / Improvement |
|---|---|---|---|
| Sampled request load | 40 requests | 25,000 requests | 625x load scale |
| Worker concurrency | 2 workers | 40 workers | High-concurrency burst |
| TTFB (Mean) | 639.9 ms | 11.0 ms | 58.3x faster (-98.3%) |
| TTFB (Median P50) | 420.0 ms | 10.5 ms | -409.5 ms |
| TTFB (P95 Tail) | 1.85 s | 18.1 ms | -1.83 s |
| TTFB (P99 Tail) | 2.29 s | 23.1 ms | -2.27 s |
| E2E Latency (Mean) | 660.0 ms | 15.6 ms | 42.3x faster (-97.6%) |
| E2E Latency (Median P50) | 434.6 ms | 14.7 ms | -419.9 ms |
| E2E Latency (P95 Tail) | 1.85 s | 27.1 ms | -1.82 s |
| E2E Latency (P99 Tail) | 2.51 s | 34.2 ms | -2.48 s |
| Origin offload ratio | 0.0% (All hit origin) | 100.0% | 100% backend offload |
| Throughput capacity | 2.4 req/s | 2,556.1 req/s | 1,086x capacity |
Per-endpoint latency breakdown
Origin response times varied substantially by page complexity:
| Endpoint path | Origin TTFB | Edge TTFB | Origin E2E | Edge E2E | Mean payload |
|---|---|---|---|---|---|
/ (Homepage) |
1,090.0 ms | 14.3 ms | 1,200.0 ms | 23.7 ms | 253.7 KB |
/challenges |
689.1 ms | 11.7 ms | 703.8 ms | 18.7 ms | 171.5 KB |
/++ |
767.2 ms | 12.9 ms | 780.7 ms | 18.6 ms | 141.1 KB |
/videos |
921.2 ms | 10.3 ms | 924.7 ms | 14.1 ms | 64.8 KB |
/organizations |
538.9 ms | 9.9 ms | 547.4 ms | 13.0 ms | 56.9 KB |
/help |
463.1 ms | 9.4 ms | 467.4 ms | 12.5 ms | 48.8 KB |
/advertise |
337.8 ms | 9.5 ms | 339.3 ms | 12.0 ms | 42.3 KB |
/deved |
310.7 ms | 9.6 ms | 321.5 ms | 12.3 ms | 43.2 KB |
On the homepage (/), origin TTFB was 1.09 seconds. Edge acceleration delivered the same fully-rendered HTML document in 14.3ms.
4. Why tail latencies matter for dev.to
While average latency paints a broad picture, tail latency (P95 and P99) dictates real user experience.
During the direct origin test, P99 TTFB reached 2.29 seconds, and P99 end-to-end latency reached 2.51 seconds. When a user on a mobile device or high-latency connection visits a page with a 2.5-second initial document delay, browser parsing is stalled. CSS, JavaScript, and fonts cannot start downloading until the initial HTML document arrives.
With edge acceleration:
- P95 TTFB dropped from 1,850ms to 18.1ms.
- P99 E2E latency dropped from 2,510ms to 34.2ms.
The edge proxy truncates the latency tail because requests are served directly from RAM without database lookups, lock contention, or garbage collection pauses.
5. Business and user experience impact
Using established web performance research from Google, Akamai, and Deloitte Digital, we evaluated what saving 644ms per request means for dev.to:
1. Cumulative user wait time eliminated
At 10,000,000 monthly page views, reducing mean response time by 644.4ms eliminates 1,790 hours of human waiting time every month.
$$\text{Hours Saved} = \frac{10,000,000 \times 0.6444\text{s}}{3,600} \approx 1,790\text{ hours/month}$$
2. Projected conversion lift (+6.4%)
Deloitte's study on retail and publishing sites established that every 100ms improvement in page speed increases conversion events (signups, newsletter subscriptions, and click-throughs) by ~1.0%. A 644ms speedup yields a projected +6.4% lift in user engagement and registration completion.
3. Bounce rate reduction (-12.9%)
Google's Core Web Vitals research shows that reducing initial page load times below 1 second cuts immediate abandonment. Faster page responses across community feeds prevent users from bouncing before articles render.
4. Core Web Vitals compliance
Google's search algorithm classifies TTFB into three tiers:
- Good: Under 200ms
- Needs Improvement: 200ms to 800ms
- Poor: Over 800ms
dev.to's origin average of 639.9ms falls into the "Needs Improvement" bracket, with pages like / (1,090ms) and /videos (921ms) falling into "Poor". Edge acceleration moves all audited routes into the Good (<20ms) bracket, improving search engine crawl efficiency and mobile search placement.
6. Financial and infrastructure savings (TCO)
Running dynamic Rails applications at scale requires substantial server resources. Modeling dev.to's infrastructure requirements at 10,000,000 monthly requests demonstrates the cost difference between origin compute and edge caching:
| Cost category | Self-managed origin | ApexCache managed edge | Monthly savings |
|---|---|---|---|
| Application compute (vCPUs) | $131.40 (4 vCPUs) | $32.85 (1 vCPU) | $98.55 |
| Outbound egress bandwidth | $78.43 (980 GB) | $0.00 (Served at edge) | $78.43 |
| Database read replicas | $350.00 (1-2 RDS instances) | $0.00 (DB reads offloaded) | $350.00 |
| DevOps engineering upkeep | $2,000.00 (0.25 FTE labor) | $0.00 (Automated rules) | $2,000.00 |
| ApexCache platform fee | $0.00 | $449.00 (Growth Tier) | -$449.00 |
| Total monthly operating TCO | $2,559.83 | $481.85 | $2,077.98 / month |
- Annual net savings: $24,935.75 per year
- Total cost reduction: -81.2%
- Return on investment multiple: 5.6x net value multiple
The largest financial saving is not raw server compute; it is developer labor. Managing custom Redis caching layers, writing custom cache sweepers, and debugging cache synchronization issues consumes approximately 10 hours per week of senior platform engineering time (~0.25 FTE). Shifting caching to an automated edge layer frees engineers to focus on product development.
7. Deployment options: zero-code CNAME vs tagged Rails purging
Teams can introduce edge acceleration through two paths: a zero-code DNS change, or precision application-level cache tagging.
Option A: Zero-code CNAME DNS routing (Fast path)
The fastest deployment requires no Rails code changes. You route incoming traffic through ApexCache at the DNS level:
-
Add your domain in the ApexCache dashboard: Enter
www.dev.toand specify your origin server (for example,origin.dev.toor your AWS Application Load Balancer endpoint). - Add DNS records at your DNS provider (Route 53, Cloudflare, or NS1):
| Record type | Name / Host | Target / Value | TTL | Notes |
|---|---|---|---|---|
| TXT | _apexcache-challenge.www |
apex-verify=YOUR_TOKEN |
300s | Domain ownership verification |
| CNAME | www |
cname.getapexcache.com |
300s | Set to DNS Only (grey cloud on Cloudflare) |
- Automated TLS termination: ApexCache provisions and renews Let's Encrypt TLS 1.3 certificates automatically.
-
Configure default path rules: In the dashboard, configure micro-caching rules for public routes (
/,/challenges,/videos,/organizations,/help) with 60s to 300s TTLs. -
Session bypass: ApexCache automatically bypasses the cache when requests carry authentication cookies (such as
_forem_sessionorremember_user_token), passing logged-in requests directly to origin Puma workers.
Option B: Application-level surrogate tagging in Rails (Precision path)
For instant cache invalidation upon post publication, configure your Rails controllers to return surrogate keys (X-Cache-Tags) and fire asynchronous webhooks on model updates.
Step 1: Return surrogate keys in Rails controllers
In your Rails controllers, return standard Cache-Control headers along with an X-Cache-Tags header identifying the entities rendered on the page:
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def index
@articles = Article.published.order(created_at: :desc).limit(20)
# Set public cache header for anonymous visitors
if current_user.nil?
response.headers['Cache-Control'] = 'public, max-age=300, stale-while-revalidate=60'
# Tag the response with collection and individual article identifiers
tags = ['articles', 'feed:home'] + @articles.map { |a| "article:#{a.id}" }
response.headers['X-Cache-Tags'] = tags.join(',')
else
# Bypass caching for authenticated sessions
response.headers['Cache-Control'] = 'private, no-cache, no-store'
end
end
def show
@article = Article.friendly.find(params[:id])
if current_user.nil?
response.headers['Cache-Control'] = 'public, max-age=3600, stale-while-revalidate=120'
response.headers['X-Cache-Tags'] = "article:#{@article.id},user:#{@article.user_id}"
else
response.headers['Cache-Control'] = 'private, no-cache, no-store'
end
end
end
Step 2: Invalidate tags when articles update
Whenever an author edits an article or publishes a new post, trigger a single webhook to purge the affected tag:
# app/models/article.rb
class Article < ApplicationRecord
after_commit :purge_edge_cache, on: [:create, :update, :destroy]
private
def purge_edge_cache
# Async background job using Sidekiq
PurgeEdgeCacheJob.perform_later("article:#{id}")
PurgeEdgeCacheJob.perform_later("feed:home") if saved_change_to_published?
end
end
The background worker sends a POST request to the ApexCache purge endpoint:
# app/jobs/purge_edge_cache_job.rb
class PurgeEdgeCacheJob < ApplicationJob
queue_as :default
def perform(tag)
uri = URI('https://api.getapexcache.com/api/v1/cache/invalidate')
req = Net::HTTP::Post.new(uri)
req['Authorization'] = "Bearer #{ENV['APEXCACHE_API_KEY']}"
req['Content-Type'] = 'application/json'
req.body = { tags: [tag] }.to_json
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req)
end
end
end
When an article is modified, the edge cache clears only the pages associated with article:123 across all global edge points of presence in under 150 milliseconds. The rest of the site cache remains completely intact.
8. Summary
Rails is a productive framework for building rich web platforms, but server-rendered architectures naturally encounter latency and concurrency constraints when scaling public feeds.
By placing an in-memory edge caching gateway in front of Forem:
- Mean TTFB drops from 639.9ms to 11.0ms (58x faster).
- P99 tail latency drops from 2.51s to 34.2ms.
- Throughput capacity expands from 2.4 req/s to 2,556 req/s.
- Origin server resources are offloaded 100% on cached routes.
- Platform operating costs decline by 81.2%, saving over $24,900 annually.
Offloading read traffic to edge memory lets Rails applications scale smoothly while preserving backend database and compute capacity for authenticated user interactions.