- Con la tendencia reciente del desarrollo con IA, comenzó a aprender y usar Python de forma seria, y ahora siente una gran satisfacción con su ecosistema
- Python ha evolucionado hasta convertirse en un lenguaje mucho más rápido y moderno que antes, y percibe avances importantes como las mejoras de rendimiento mediante Cython
- Ha incorporado activamente herramientas y bibliotecas modernas de desarrollo como uv, ruff, pytest y Pydantic en su flujo de trabajo para aumentar la productividad
- También aplica estructuras de proyecto y automatización para reducir la brecha entre los entornos de producción y el desarrollo basado en notebooks de Jupyter/scripts
- Usa GitHub Actions, Docker y otras herramientas para construir de forma eficiente CI/CD, pruebas y gestión de infraestructura
Resumen de I’m Switching to Python and Actually Liking It
Por qué se cambió a Python
- En entornos de desarrollo centrados en IA, Python se ha consolidado como el lenguaje estándar de facto
- Antes lo usaba solo para scripts simples, pero recientemente comenzó a usarlo seriamente para crear apps “listas para producción” como RAG, agentes e IA generativa
- En ese proceso, notó que el ecosistema de Python ha evolucionado muchísimo en comparación con el pasado
Tres fortalezas de Python
- Ecosistema abundante de bibliotecas y herramientas: especializado en procesamiento de datos, análisis, web e IA
- Mejoras de rendimiento gracias a Cython y similares: permite optimización basada en compilación
- Legibilidad mejorada de la sintaxis: la sintaxis legacy como
__init__ y __new__ queda más oculta, y se ofrece una sintaxis más intuitiva
Estructura del proyecto (basada en monorepo)
Herramientas y configuraciones principales
-
uv
- Administrador de paquetes y herramienta de build moderna para Python ofrecida por Astral
- Maneja rápidamente la mayoría de las tareas, como gestión de dependencias, creación de entornos virtuales e inicialización del proyecto
pyproject.toml es el archivo de configuración central, donde se integran todos los metadatos y dependencias
- Se puede configurar rápidamente el entorno del proyecto con los comandos
uv init, uv add y uv sync
-
ruff
- Linter y formateador de código para Python ultrarrápido
- Integra herramientas como
isort, flake8 y autoflake
- Permite hacer linting y corrección automática con
ruff check y ruff format
- Soporte nativo para la guía de estilo PEP 8
-
ty
- Verificador estático de tipos para Python creado por Astral
- Combinado con
typing, es eficaz para el análisis estático y la prevención temprana de bugs
- Aunque está en una etapa inicial de desarrollo, ya es lo bastante estable para usarse
-
pytest
- Framework de pruebas de Python representativo para pruebas unitarias y entornos de pruebas extensibles
- Permite integrar pruebas rápidamente con una convención simple de nombres de archivos y un solo comando
- Se organizan en
test_*.py y se ejecutan con uv run pytest
- Sintaxis concisa y un ecosistema abundante de plugins
-
Pydantic
- Biblioteca para validación de datos y gestión de configuración de entorno
- Carga configuraciones basadas en variables de entorno
.env y valida tipos
- Permite gestionar de forma segura claves API, URL de bases de datos y más mediante la clase
BaseSettings
-
MkDocs
- Facilita la generación de sitios web estáticos y documentación para proyectos en Python
- Permite aplicar rápidamente diseños atractivos al estilo de proyectos open source
- También se integra fácilmente con GitHub Pages
-
FastAPI
- Framework para construir APIs RESTful rápidas
- Destaca por su validación y documentación automáticas, alto rendimiento e integración sencilla con Pydantic
- Basado en Starlette y Pydantic, ofrece gran seguridad de tipos y buen rendimiento
-
Dataclasses
- Funcionalidad estándar de Python para definir fácilmente clases centradas en datos
- La generación automática de métodos especiales reduce mucho el código boilerplate
Control de versiones y automatización
-
GitHub Actions
- Configura pipelines de CI separados para
project-api y project-ui
- Ofrece workflows optimizados para construir pipelines de CI en varios sistemas operativos
- Con un entorno de pruebas basado en Docker, es posible probar en el mismo entorno que producción
-
Dependabot
- Automatiza la actualización de dependencias y la gestión de parches de seguridad
-
Gitleaks
- Herramienta para evitar fugas de información sensible (contraseñas, claves API, etc.) mediante revisiones de seguridad antes de los commits en git
-
Pre-commit Hooks
- Herramienta para linting, formateo y revisiones de seguridad automáticas antes del commit
- Se usa junto con ruff, gitleaks y otros para mantener la consistencia y la calidad del código
Automatización de infraestructura
-
Make
- Brinda un flujo de trabajo de desarrollo consistente con comandos como
make test y make infrastructure-up
- Hay un Makefile tanto en la raíz del proyecto como en
project-api
-
Docker & Docker Compose
project-api y project-ui se ejecutan por separado como contenedores
- Toda la app puede levantarse con una sola línea:
docker compose up --build -d
- El
Dockerfile incluye la instalación de uv y el comando para ejecutar la app de FastAPI
Cierre
- Como se ve arriba, el entorno moderno de desarrollo en Python permite construir flujos de trabajo de producción eficientes y robustos
- Es posible obtener muchos beneficios del crecimiento del ecosistema de Python y de la evolución de sus herramientas en áreas como IA, datos y desarrollo web
- Se puede implementar una cultura de desarrollo integrada que abarque estructura monorepo, herramientas de automatización, linters y verificadores de tipos, entornos de prueba inmediatos, documentación y orquestación de infraestructura
6 comentarios
Python tiene una gran variedad de bibliotecas y frameworks, pero la desventaja es que la gestión de versiones de paquetes no está muy bien resuelta y los conflictos son frecuentes.
Se parece a Java de antes en la tendencia de sus ventajas y desventajas.
El
uvque también aparece en el artículo es realmente una joya. Además de ser rapidísimo, maneja muy bien las versiones y las dependencias, casi comonpm, así que me estoy quedando conuv.Pero aun así, últimamente parece que con
uvypoetryla gestión de versiones y los conflictos en su mayoría ya se han resuelto.¿También es adecuado para abarcar el ecosistema, incluso partes como React?
Como el lenguaje es distinto, hay partes difíciles para integrarlo directamente con React, pero dependiendo de lo que quieran hacer, creo que también hay aspectos que sí serían posibles.
Personalmente, creo que el desarrollo frontend con Python es un área que todavía no está muy extendida.
Opiniones de Hacker News
En el código, mostrar un mensaje con OR como “Falta YOUTUBE_API_KEY o YOUTUBE_CHANNEL_ID” cuando falta una variable de entorno termina molestando al usuario en una situación donde ni siquiera hace falta usar OR. Es mucho mejor revisar cada valor por separado y decir con claridad cuál falta. La diferencia en tiempo de desarrollo es casi nula, así que recomendaría hacerlo así.
Es meterse mucho en un detalle pequeño, pero creo que este caso es ideal para usar el operador
:=(walrus). Por ejemplo, se puede usar directo así:if not (API_KEY := os.getenv("API_KEY")):. En lo personal, en herramientas internas dejo queos.environ["API_KEY"]lanceKeyErrorsin atraparlo. Eso también me parece suficientemente claro.Yendo más allá, me parece mucho mejor revisar las condiciones una por una y, si falta al menos una, mostrar todas de una vez. Así se evita la molestia de correr el programa por una variable faltante y luego encontrarse con otro error por otra variable distinta. A veces no queda otra que hacerlo más incómodo, pero si se puede, es mejor mostrar todo en una sola pasada.
Lo mejor es leer todas las variables de entorno y reportar de una vez cuáles faltan.
También se puede usar un flag booleano y hacer
exit(1)una sola vez al final. Así puedes mostrar juntas todas las variables de entorno faltantes.También se puede terminar con código 1 imprimiendo el mensaje directamente, como
exit("Missing ...").Si estás buscando una herramienta que genere automáticamente la estructura de un proyecto, recomiendo cookiecutter. Tengo varias plantillas que uso seguido, como python-lib, click-app, datasette-plugin y llm-plugin. Se usa así:
uvx cookiecutter gh:simonw/python-libHice algo llamado baker en Ruby. baker no copia el repo de plantillas, sino que genera una lista de tareas por hacer (una lista de pasos imperativos), y permite mezclar tareas manuales (como obtener y configurar una API key) con tareas automáticas (como
uv init). Se puede usar sintaxis Markdown, interpolación de strings de Ruby y también bash. Lo hice porque ya estaba muy cansado de las configs basadas en yml.Lo que está sonando últimamente es una herramienta llamada Copier. Para más detalles, revisa la documentación de Copier.
A mí más bien me gusta configurar proyectos nuevos. No siento necesidad de automatizar eso.
Creo que este tipo de herramientas de automatización de estructura encaja perfecto con los workflows de desarrollo con LLMs basados en agentes que están surgiendo ahora.
La idea de que “Python es más amigable para humanos porque viene preinstalado en la mayoría de Unix” me parece una lectura algo optimista. Apenas pasas de
import json, entras muy rápido al infierno devirtualenv. Si quieres correr algo en Python 3.13.x sobre Ubuntu 22.04 o 24.04, Rocky 9, etc., al finalvenv, contenedores y gestores de versión se vuelven obligatorios.Incluso una librería básica como
import json, si el lenguaje no la incluyera, habría que instalarla aparte; en Python, la librería estándar da mucha productividad al inicio. Claro, en proyectos grandes no alcanza con la librería estándar, pero en la práctica he desplegado varios códigos reales usando solo eso, sin problemas de despliegue ni de gestión de seguridad. Además, manejarvenvya no es tan difícil como antes, y los package managers también han mejorado.Una de mis teorías medio en broma es que la mitad de la razón por la que Docker/contenedores se expandieron tan rápido fue que ayudaron a superar el infierno de dependencias de Python. Mi primera experiencia con Python fue instalar un servicio en un servidor en 2012, y fue terrible: infierno de dependencias, comandos de
venv, entornos difíciles de manejar, todo espantoso. También me la pasé sufriendo conpip,brewy entornos de macOS, y terminaba evitando Python apenas lo veía. Pero últimamente, gracias auv, siento que Python mejoró bastante incluso para principiantes. Conuv init,uv addyuv runya alcanza.Creo que siempre hay que usar
virtualenv. Al final no es más que un directorio, y ahora hasta salen advertencias si intentas instalar globalmente conpip, así que ya no es tan complicado como antes.Es buena idea usar sí o sí
virtualenvo contenedores. Aunque parezca más difícil de manejar, así evitas que actualizaciones o cambios de versión de librerías afecten todo el sistema.Antes era común que el sistema trajera solo Python 2 por defecto, y a veces el propio sistema dependía de ese Python 2, así que en realidad era todavía más riesgoso.
Siento que Python es al mismo tiempo verboso e insuficiente. Para hacer algo simple, o terminas metiendo 500 dependencias, o para una tontería acabas escribiendo desde decenas hasta cientos de líneas. Por eso evito Python: hay demasiado trabajo innecesario. Prefiero Perl porque puedo terminar mucho más rápido y con más concisión. Python termina sintiéndose como programación por la programación, más que como una herramienta para hacer el trabajo.
Yo hago muchos proyectos sin dependencias. Con la librería estándar y un solo archivo se puede hacer muchísimo. Si tienes Python instalado, puedes bajarlo con
curly correrlo directo. Por ejemplo, tengo una herramienta CLI de manejo de dinero de 2000 líneas, plutus, que usa como 12 módulos estándar. Aproximadamente 25% del código es la parte de parsear comandos conargparse. Me gusta escribirlo de forma clara, con una línea por parámetro.Dijiste que Perl es más rápido y más potente que Python; me da curiosidad qué ejemplos concretos tendrías.
En Python me resulta cómodo poder anidar estructuras de datos sin pensarlo demasiado. Puedes mezclar libremente listas, tuplas, diccionarios, etc., y acceder a todo con una sintaxis uniforme. Perl sin duda es más listo y más divertido, pero justo por eso a mí se me enreda más la cabeza. Python es más aburrido, pero tiene muchísima claridad y puedo volver a verlo cinco años después y entenderlo.
Creo que Python ya es bastante útil solo con la librería estándar.
A mí me gusta la estructura de monorepo, pero en una empresa donde trabajé antes eso terminó creando una estructura gigantesca y obesa que nadie quería tocar por miedo a romper código de otros equipos. El problema de fondo no era tanto el repo en sí, sino que se manejaba un solo
requirements.txtpara todo el repo o los scripts de build estaban enredados. En teoría, debería bastar con actualizar dependencias una vez para que todo el código quede seguro con los últimos parches, pero en la práctica nadie podía tocar eso. Los monorepos solo funcionan bien cuando la organización tiene una cultura muy NIH (como Google, con tendencia a construir todo por cuenta propia). Por experiencias así, empecé a valorar más una estructura de microservicios donde cada servicio coincide con la estructura de equipos de la organización. También vale la pena mirar Conway's law.Python es el lenguaje que más se comporta como el pseudocódigo que escribo. Cada parte que en mi cabeza doy por obvia, Python también la resuelve con abstracciones intuitivas. Como vengo de una formación más matemática, eso me resultó muy satisfactorio. Claro que ahora también me gustan otros lenguajes, pero sigue teniendo encanto.
Estoy armando proyectos casi siempre con exactamente el mismo patrón. Se parece tanto que hasta da miedo. Me pregunto si el ecosistema de desarrolladores Python no estará convergiendo cada vez más a un mismo estilo. Antes pensaba que mis elecciones eran originales, pero si todos terminamos haciendo lo mismo, me pregunto dónde quedó mi libre albedrío. Me recuerda al fenómeno de elegir nombres de bebé: creías que habías escogido algo único y en realidad era el nombre popular #2.
Esta estructura ya era popular en Python desde hace 10 años. Al final, parece que muchos ingenieros razonables llegan naturalmente al mismo patrón después de pensarlo bien.
Siento que el ego humano está extendido por todo el espectro como una onda piloto cuántica, y desde ahí se transforma en existencia. becoming-being, me hace reír.
Me da gusto ver que a más gente le está empezando a gustar Python. Yo originalmente prefería Ruby, pero por requerimientos de clientes terminé usándolo a la fuerza. Antes Ruby era mucho más lento, pero al obligarme a aprender Python me fui acostumbrando y ahora hasta lo disfruto. Sí tengo algunas diferencias de opinión sobre cómo usar Make: si no usas dependencias en absoluto, no es muy distinto de un script con un
case... lo digo medio en broma, pero me da cierta nostalgia ver que la generación de ahora ya no está familiarizada con Make. Es un poco ese sentimiento de “en mis tiempos...”.Ruby tiene una sintaxis mucho más bonita. Python, con eso de delimitar el scope solo por indentación, no es mi estilo.
Yo también empecé con un script lleno de
case, pero al final evolucionó hacia unMakefileplano. UnMakefilees más estándar y más fácil de entender que un script cualquiera.Me pregunto cuándo conviene usar Dataclass y cuándo Pydantic Basemodel. Si ya estás usando Pydantic, casi parece que podrías unificar todo con Pydantic, así que dudo si realmente hay una buena razón para seguir usando Dataclass.
El proyecto attrs tiene una comparación muy bien explicada. Está esta comparación oficial de attrs; claro, puede tener algo de sesgo, pero me parece que los argumentos lógicos son sólidos. También ayuda este blog.
Dataclass no soporta validación de objetos anidados. Por eso, para estructuras planas simples que solo sirven para pasar argumentos entre funciones, conviene más
dataclass. Es más claro que recibir demasiados argumentos sueltos.Hay un costo de rendimiento por la validación de datos al crear objetos. También hay alternativas mucho más ligeras y rápidas, como
msgspec.Si no necesitas validación ni serialización, Pydantic más bien es overhead innecesario. Mi regla es: si necesitas serialización, Pydantic; si no,
dataclass.Puedes usar directamente un dataclass existente con algo como
TypeAdapter(MyDataclass), así que no veo por qué habría que crear además un modelo Pydantic aparte.Últimamente, de hecho, me he mudado a otros lenguajes y estoy más satisfecho así. Resumí mis ideas sobre Python en este texto. Si más adelante me toca volver a usar Python, sí quiero probar cosas como
uv,ruffyty.async. Python tieneasyncio, pero hay varias formas en competencia y no se ha asentado un estándar claro. En JS todo converge a una sola forma, y eso me resultó mucho más cómodo. Incluso los detalles chicos, sumados, terminan haciendo una gran diferencia: en Python, el scope por indentación, los problemas de rutas relativas en imports, la sintaxis de objetos en JS, etc., se sienten más agradables. Ver también esta explicación relacionada.