Ruff combina lint rápido y formato de Python. El linter encuentra imports sin usar, errores probables y reglas seleccionadas; el formateador controla la presentación. Ninguno sustituye pruebas ni revisión.

Instalar y configurar

python -m pip install ruff
ruff check .
ruff format --check .
[tool.ruff]
target-version = "py312"
line-length = 88

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I", "B"]

Lee la referencia oficial antes de activar familias amplias. Para aplicar cambios:

ruff check --fix .
ruff format .

El formateador no ordena imports por sí solo. La documentación del formateador enumera reglas incompatibles.

En código existente, ejecuta primero sin --fix, corrige errores objetivos y amplía reglas gradualmente. No mezcles un formato masivo con cambios funcionales.

En CI usa verificaciones sin modificar archivos y combínalas con pytest:

ruff check .
ruff format --check .
pytest

Define target-version según el Python realmente compatible, comparte la misma configuración entre editor y CI, y revisa cada corrección automática. Una política corta y obligatoria aporta más que cientos de reglas ignoradas.

Diferenciar lint y formato

ruff check analiza el código y señala las reglas seleccionadas: nombres no definidos, imports sin uso, comparaciones sospechosas o imports desordenados. ruff format reescribe decisiones de presentación, como espacios y saltos de línea. No corrige todos los diagnósticos ni valida la lógica de negocio.

Esta separación define el orden. Con la familia I activa, ruff check --fix organiza imports y después ruff format estabiliza el diseño. Para comprobar sin modificar archivos, ejecuta ruff check . y ruff format --check .; son las variantes apropiadas para CI.

Un informe limpio no demuestra que la aplicación funcione. Las pruebas verifican comportamiento y un comprobador de tipos examina contratos. Ruff cubre otra clase de errores y complementa esas herramientas.

Seleccionar reglas con intención

Los códigos se agrupan por origen. E4, E7 y E9 activan errores seleccionados de pycodestyle; F corresponde a Pyflakes; I ordena imports; B añade comprobaciones de flake8-bugbear. La referencia oficial de reglas explica cada diagnóstico.

No copies una selección enorme de otro repositorio. Un servicio web, una biblioteca pública y una colección de notebooks tienen necesidades distintas. Empieza por errores de alta confianza y añade familias en cambios pequeños. Una regla aporta valor cuando el equipo la entiende y la aplica.

Prefiere excepciones específicas a un ignore global:

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101"]

Si está activa la familia de seguridad, el ejemplo permite assert únicamente en pruebas. Para una línea intencional, # noqa: CODIGO es mejor que un # noqa genérico, que podría ocultar otros problemas.

Tratar archivos generados y notebooks

Entornos virtuales, cachés y builds no deben analizarse. Ruff reconoce exclusiones comunes y extend-exclude permite añadir rutas propias. No excluyas código mantenido solamente para conseguir una CI verde; registra una limpieza gradual.

Si un archivo siempre se recrea con otra herramienta, reformatearlo puede producir ruido que desaparecerá en la próxima generación. Exclúyelo o configura el generador. Si el archivo generado se edita y mantiene, aplica la política normal.

Ruff puede procesar notebooks, pero el orden de las celdas cambia el significado de algunos diagnósticos. Prueba las correcciones sobre una copia y decide explícitamente si forman parte del alcance.

Revisar las correcciones automáticas

--fix aplica correcciones disponibles según la política de seguridad de Ruff. Algunas transformaciones se clasifican como inseguras porque pueden cambiar el comportamiento o eliminar información. No habilites --unsafe-fixes de forma general; comprende primero el diagnóstico.

Incluso una corrección segura merece revisión. Un import aparentemente sin uso podría existir por un efecto secundario, que debería expresarse de forma clara. Reordenar imports puede descubrir una dependencia circular. Ejecuta las pruebas después de cambios amplios.

ruff check --diff permite observar propuestas sin tocar archivos. En una adopción grande, activa una familia cada vez y separa la formateada masiva de los cambios funcionales. Así se conserva una revisión y un historial útiles.

Alinear versión de Python y longitud

target-version indica qué sintaxis y modernizaciones son válidas. Debe coincidir con requires-python y la matriz de CI. Configurar py312 mientras el proyecto promete Python 3.10 puede generar sugerencias incompatibles.

line-length orienta linter y formateador, pero no garantiza que toda línea se divida. URLs, cadenas y construcciones indivisibles pueden superar el límite. No añadas reglas incompatibles con el formateador solo para imponer una medida visual.

Al sustituir Black, compara el resultado completo en una rama dedicada. Hay diferencias documentadas. Una vez aceptada la migración, elimina el comando, la integración del editor y la configuración anteriores. Dos formateadores sobre los mismos archivos producen diffs alternos.

Editor, hooks y CI

El editor puede formatear al guardar, pero la configuración versionada sigue siendo la fuente de verdad. Los hooks locales aceleran el feedback, aunque no sustituyen CI porque pueden omitirse. Si usas pre-commit, fija la revisión y alinea sus argumentos con los comandos documentados.

En CI ejecuta verificaciones sin reescribir:

ruff check --output-format=github .
ruff format --check --diff .
python -m pytest

Fija la versión de Ruff. Una actualización puede añadir diagnósticos, corregir falsos positivos o cambiar formato, por lo que debe llegar en un cambio revisable.

Si CI falla y local pasa, compara versión, directorio de ejecución y archivos analizados. Busca también configuraciones anidadas y exclusiones. Ejecutar exactamente el comando de CI desde la raíz suele mostrar la diferencia.

Antes de aprobar una política, prueba un checkout limpio, confirma que el formateador no genera diff y ejecuta la suite después de las correcciones. De ese modo, Ruff se convierte en un contrato pequeño, explícito y reproducible para el repositorio.

Introducir Ruff en un repositorio con deuda

Un proyecto antiguo puede tener cientos de avisos legítimos. Desactivarlos todos para empezar con una pantalla verde pierde información, mientras que exigir la corrección completa en el primer cambio puede bloquear trabajo urgente. Una transición útil separa la línea base de la política futura.

Primero guarda la configuración mínima y ejecuta el informe sin correcciones. Clasifica problemas que pueden ocultar fallos, como nombres indefinidos e imports inválidos, y corrígelos antes que las preferencias de estilo. Después aplica formato en un commit aislado. A partir de ese punto, la CI puede impedir nuevas infracciones en el alcance acordado.

Para carpetas que todavía no pueden cumplir, usa excepciones por archivo o una selección más limitada con un comentario y una tarea de seguimiento. Reduce las excepciones en cambios pequeños. No ignores códigos desconocidos: consulta la regla y conserva un ejemplo de por qué resulta inaplicable al proyecto.

También conviene definir responsabilidades. Quien actualiza Ruff revisa el changelog y el nuevo diff; quien modifica código ejecuta check y format --check; la CI confirma el estado limpio. Si un aviso revela un posible defecto, corrige el comportamiento y añade una prueba de regresión, en lugar de limitarse a silenciar la línea.

La adopción termina cuando una persona nueva puede clonar el repositorio, instalar las dependencias documentadas y obtener el mismo resultado. Comprueba el comando desde Windows y Linux si ambos son plataformas de desarrollo, evita patrones de rutas dependientes del shell y mantén toda decisión compartida en pyproject.toml. La rapidez de Ruff hace viable ejecutar estas verificaciones con frecuencia, pero la claridad de la política es lo que mantiene su valor.