validation

Soft Validation: Normalize + Validate Email

Normalize before validation to avoid “same email, different casing/whitespace” bugs. Keep normalization deterministic and small; put it in the model so imports, consoles, and controllers all behave the same.

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).

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.

Django model validation with clean method

The clean() method validates model instances before saving. I raise ValidationError for invalid data. This runs on form submission and can be called explicitly with full_clean(). I validate cross-field constraints that can't be expressed as field vali

Runtime validation for request bodies (Zod)

TypeScript only protects you at compile time; your API still receives untrusted JSON from the internet. I lean on Zod as the source of truth for parsing + validation so runtime and types stay aligned. The big win is that I don’t try to validate ‘every

Turbo Stream form errors: replace only the form frame

Hotwire forms feel “native” when invalid submissions keep you in context. Replace just the form frame with errors and keep the rest of the page intact. Return 422 so clients and caches behave correctly.

API input coercion for query params (Zod preprocess)

Query params arrive as strings, and ad-hoc parsing logic tends to drift across endpoints. I use Zod preprocessors to coerce values like page size and booleans, then validate the result. This keeps the handler readable and makes parsing rules shareable

Django form validation with clean methods

I use clean_<fieldname>() to validate individual fields and clean() to validate field combinations. Raising ValidationError shows the message to the user near the appropriate field. For cross-field validation (like 'end date must be after start