patterns

Shallow Controller, Deep Params: Form Object Pattern

When controllers become parameter jungles, use a form object. It centralizes coercion, validations, and save logic. This pattern is extremely effective for admin panels and multi-step flows.

Custom Validator for “At Least One of” Fields

A tiny custom validator keeps complex presence rules out of controllers. This example enforces that at least one of two fields is present (e.g., text content or an attachment).

Graceful Degradation: Feature-Based Rescue

Not every failure should be a 500. If a non-critical dependency fails (e.g., recommendations), rescue narrowly, emit a metric/log, and serve a baseline response.

Validated JSON Schema with dry-validation-style contract (lightweight)

Even without extra gems, you can validate incoming JSON payloads with small “contracts” that coerce and validate keys. It’s a strong reliability upgrade for webhook and API ingestion.

Keep Controllers Thin: Use Command Objects

Command objects (a.k.a. “actions”) make controllers boring. They’re easy to test, easy to instrument, and they produce a stable API for the rest of your app. This is the kind of structure that makes large Rails apps maintainable.

Composable “Policy Scope” without a Gem

Authorization libraries are great, but you can also build a lightweight policy scope. The key is to keep it composable: a single public method that returns an ActiveRecord::Relation and nothing else.

ActiveRecord::Relation as a Boundary (No Arrays)

Return relations from query objects, not arrays. It keeps composition possible (additional filters, pagination, eager loading) and avoids loading huge result sets accidentally.

Avoid Callback Chains: Use Domain Events (In-App)

Callback chains become spooky action at a distance. A simple in-app event bus keeps side effects explicit and testable. This isn’t about Kafka—it’s about clarity and seams.

Django model soft delete pattern

Soft deletes mark records as deleted without removing them from the database. I add a deleted_at field and override the delete() method. A custom manager excludes soft-deleted records by default. I provide a hard_delete() method for permanent removal.

Service-Level “Circuit Breaker” (Simple)

When a dependency is failing, you don’t want to keep hammering it. A simple circuit breaker trips after N failures and short-circuits for a cooldown window. It protects your app and your vendor.