Background jobs with Sidekiq and reliable queues

Moving slow operations to background jobs keeps API response times fast and improves perceived performance. Sidekiq with Redis provides a robust, production-proven job queue that handles millions of jobs per day. I organize workers into separate queue

N+1 query detection with Bullet gem

N+1 queries are the silent performance killer in Rails apps—they're easy to introduce during rapid development and expensive to diagnose in production. The Bullet gem monitors queries during development and test runs, raising alerts when it detects mi

Pagination with cursor-based approach

Traditional offset-based pagination becomes unreliable and slow for large datasets when records are frequently inserted or deleted—users can miss items or see duplicates across pages. Cursor-based pagination solves this by using an opaque token that e

Structured JSON error responses

Consistent error handling transforms debugging from guesswork into systematic troubleshooting. I use a rescue handler that catches exceptions globally and transforms them into a standard JSON structure containing an error code, human-readable message,

Rate limiting with Redis and Rack::Attack

Rate limiting is essential protection against abuse and ensures fair resource distribution across API consumers. Rack::Attack with Redis backing provides a robust, shared state solution that works across multiple application servers. I define differen

JWT authentication with refresh tokens

Stateless authentication with JWT tokens simplifies horizontal scaling but introduces security concerns around token lifetime and revocation. I use short-lived access tokens (15 minutes) combined with longer-lived refresh tokens stored in an encrypted

API versioning with namespace routing

API versioning is critical for maintaining backward compatibility while evolving your endpoints. I use Rails namespace routing to organize versions cleanly within the app/controllers structure. Each version lives in its own module like Api::V1 or Api:

Panic recovery middleware that fails closed and logs context

Even well-tested services panic occasionally: a nil pointer from an unexpected edge case, a slice bounds bug, or a library doing something surprising. If you don’t recover, one request can crash the entire process and turn a small bug into an outage.

Retry Postgres serialization failures with bounded attempts

For workloads that run at SERIALIZABLE isolation (or that hit serialization conflicts under load), retries are part of the contract. The important part is to retry only the safe errors (typically SQLSTATE 40001) and to keep the loop bounded so you don

errors.Join for cleanup (surface multiple close failures)

Cleanup paths are easy to under-test, and it’s common to ignore close errors because you only have one return value. With Go 1.20+, errors.Join gives you a clean way to accumulate multiple cleanup errors and return them as a single error value. This i

Capture status code and bytes written via ResponseWriter wrapper

When you need accurate request logs or metrics, you can’t rely on “what you intended to write” — you need what actually got written. Wrapping http.ResponseWriter to capture WriteHeader and count bytes is a simple way to record status codes and respons

WriteJSON helper with consistent headers and status

I like explicit response helpers because they prevent subtle inconsistencies: missing Content-Type, forgetting Cache-Control, or writing headers after the body has started. A WriteJSON function centralizes the “happy path” and makes error handling con