Rechaza los callbacks de Rails generados por IA que hacen trabajo lento
Ese callback after_save es la razón por la que tu suite de tests toca la red. Pídele a un asistente de IA que "sincronice el registro con el CRM después de guardar" y con gusto te entregará un after_save que hace una llamada HTTP — dentro de tu save, en cada save, en cada test. Compila, pasa el spec del camino feliz, y planta una dependencia lenta y frágil en la ruta más caliente de tu aplicación. Es uno de los patrones generados por IA más fáciles de rechazar en revisión, porque la regla cabe en una sola frase.
Por qué el callback generado es una trampa
Un callback no es un gancho que corre "después de la parte importante". Es parte de la parte importante:
- Corre en cada save — el formulario, la edición del admin, la migración de datos, la factory en cada uno de tus 4,000 tests. Una llamada de API de 300 ms convierte
user.saveen una operación de 300 ms en todas partes. - Corre dentro de la transacción. Las guías de Rails son explícitas: toda la cadena de callbacks está envuelta en una transacción, y si un callback lanza una excepción, la cadena se detiene y se emite un rollback. Un timeout de un tercero no solo falla la sincronización: revierte tu save y mantiene abierta una transacción de base de datos mientras dura la llamada HTTP.
- No tiene historia de reintentos. Si la llamada de red falla, el trabajo simplemente se pierde — o peor, quedó a medias y el registro se revirtió.
La regla de revisión en una frase
Los callbacks son para invariantes baratos. El trabajo de red, lento o reintentable va a background jobs.
| Trabajo en el callback | Veredicto |
|---|---|
| Normalizar un email, fijar un default, calcular un slug | Callback — está bien |
| Actualizar un contador o columna desnormalizada | Callback — está bien |
| Llamada HTTP a cualquier API externa | Rechazar — background job |
| Enviar email, push o webhooks | Rechazar — background job |
| Cualquier cosa que pueda fallar transitoriamente y deba reintentarse | Rechazar — background job |
| Cualquier cosa más lenta que ~1 ms | Rechazar — background job |
La versión correcta es apenas más larga que la generada:
# AI-generated — reject in review
after_save :sync_to_crm
def sync_to_crm
CrmClient.new.upsert(self) # network call inside your save
end
# What to ask for instead
after_commit :enqueue_crm_sync, on: [:create, :update]
def enqueue_crm_sync
CrmSyncJob.perform_later(id)
end
class CrmSyncJob < ApplicationJob
retry_on Net::OpenTimeout, wait: 3.seconds, attempts: 5
def perform(record_id)
CrmClient.new.upsert(Record.find(record_id))
end
end
Fíjate en after_commit, no after_save: se dispara solo después de que la transacción realmente hace commit, así que nunca encolas un job para un registro que se revirtió — y a esa altura una excepción ya no puede revertir nada.
Lo que ganas
- Los saves siguen rápidos. El save hace trabajo de base de datos y encola un job — microsegundos, no viajes de ida y vuelta a una API.
- Los tests siguen sin red. No más stubs de HTTP en cada spec de modelo solo para poder llamar
save. - El trabajo lento se reintenta de forma segura. El
retry_onde Active Job da reintentos declarativos (por defecto: 5 intentos, esperas de 3 segundos). Una sincronización fallida se vuelve un job reintentado, no un save revertido.
Movidas de usuario avanzado
- Automatiza el rechazo. Pon
WebMock.disable_net_connect!(allow_localhost: true)en el setup de tus tests. El próximo callback de red generado por IA rompe la suite al instante con un sonoro "Real HTTP connections are disabled" — no tienes que cazarlo en revisión. - Diseña los jobs para at-least-once. Las mejores prácticas de Sidekiq advierten que un job corre "al menos una vez, no exactamente una vez". Pasa IDs (no objetos), vuelve a leer el estado dentro de
perform, y haz idempotente la llamada externa (upsert, no create). - Pon la regla en el contexto de tu asistente. Una línea en
CLAUDE.md/ tu archivo de reglas — "Nunca hagas trabajo de red o lento en callbacks de Active Record; usaafter_commit+ un job" — y el modelo deja de generar el patrón en lugar de que lo rechaces cada semana.
Recursos
- Active Record Callbacks — Rails Guides
- Active Job Basics — Rails Guides
- Sidekiq Best Practices — jobs idempotentes, at-least-once
- WebMock — desactivar HTTP real en tests
¿Construyendo con asistentes de código con IA? Yeda AI diseña, audita y entrega sistemas LLM de producción.