Showing posts with label Reflexiones. Show all posts
Showing posts with label Reflexiones. Show all posts

Thursday, May 7, 2026

Ya no programamos. Ahora dirigimos.

 Andrej Karpathy, uno de los fundadores de OpenAI y quien hizo funcionar el piloto automático de Tesla, dijo hace unos meses algo que me resultó sorprendente viniendo de él: que nunca se había sentido tan rezagado como programador.

No lo dijo con angustia. Lo dijo con la curiosidad de alguien que acaba de ver algo que cambia las reglas del juego. Y creo que vale la pena detenerse en eso, porque lo que describe no es una anécdota personal. Es una señal de que el paradigma cambió.


Software 1.0, 2.0, 3.0

Durante décadas programamos de la misma manera: le decíamos a la máquina exactamente qué hacer, línea a línea. Eso es el Software 1.0. Instrucciones explícitas, deterministas, sin ambigüedad.

Luego vino el Software 2.0: en lugar de escribir reglas, organizábamos datos y entrenábamos redes neuronales. Ya no programabas la lógica directamente, sino que la aprendías de los ejemplos. El código se volvió implícito.

Ahora estamos en el Software 3.0. Y aquí la palanca de programación es el lenguaje natural. Lo que pones en el contexto de un modelo de lenguaje es tu forma de instruir al intérprete. El LLM es ese intérprete. Y la pregunta ya no es qué función escribo, sino qué texto le doy a mi agente.

"Puedes externalizar tu forma de pensar, pero no puedes externalizar tu comprensión."

Eso no es solo una metáfora. Es un cambio en cómo concebimos la construcción de software. Y la mayoría de nosotros todavía está usando la mentalidad del paradigma anterior.


El momento en que algo hizo click

Karpathy describe que en diciembre pasado, durante sus vacaciones, empezó a notar algo distinto. Los fragmentos de código que pedía simplemente salían bien. Ya no tenía que corregirlos. Y siguió pidiendo más. Y seguían saliendo bien. Hasta que se dio cuenta de que no recordaba la última vez que había tenido que editar algo manualmente.

Lo que vivió no es solo una mejora cuantitativa en las herramientas. Es una transición cualitativa en el flujo de trabajo. Y creo que muchos developers estamos justo en ese borde, sin haberlo atravesado todavía, o habiéndolo cruzado sin nombrarlo.

Hay un agujero de conejo ahí adentro. Una vez que confías en el sistema, tus proyectos personales se multiplican. Las cosas que antes parecían demasiado grandes para empezarlas solas, de repente son abordables. Eso tiene un costo, claro, porque también tienes que aprender a dirigir, no solo a ejecutar.


El problema de la inteligencia irregular

Pero seamos honestos. Estos modelos son notablemente inconsistentes. Karpathy los llama "irregulares". Un modelo puede refactorizar una base de código de 100,000 líneas y encontrar vulnerabilidades de día cero, y en el mismo día decirte que camines 50 metros para lavar tu carro porque está "muy cerca".

Eso no es un error menor. Es una señal de cómo funcionan por dentro. Los modelos son entrenados con aprendizaje por refuerzo sobre dominios donde la verificación es posible: código, matemáticas, lógica formal. En esos dominios, avanzan rápidísimo. En dominios donde no hay señal de recompensa clara, el modelo navega a ciegas.

La conclusión práctica es importante: si tu problema vive dentro de los circuitos que el modelo entrenó bien, vas a volar. Si estás fuera de esos circuitos, vas a pelear. Y la única forma de saber en cuál estás es explorando. No hay manual. Hay que probar.


Vibe coding vs. ingeniería agéntica

Hay una distinción que me parece clave y que a menudo se mezcla: el vibe coding y la ingeniería agéntica no son lo mismo.

El vibe coding sube el piso. Cualquier persona, con conocimiento técnico limitado, puede construir algo funcional. Eso es enorme. Democratiza la creación de software de maneras que hace cinco años eran impensables.

Pero la ingeniería agéntica busca otra cosa: mantener los estándares de calidad del software profesional mientras usas agentes para ir más rápido. No introduces vulnerabilidades porque "el agente lo hizo". Sigues siendo responsable. Lo que cambia es la velocidad a la que puedes operar.

"El ingeniero 10x de antes se magnifica mucho más ahora. Y 10x ya no es la velocidad que se obtiene."

El techo sube, no baja. Y quienes aprenden a coordinar agentes con criterio de ingeniería son los que van a operar en ese techo. Eso requiere algo que ningún modelo tiene todavía de forma confiable: gusto, juicio, diseño.


Qué sigue valiendo la pena

Si los modelos se encargan de los detalles de las APIs, de los parámetros de las funciones, de la sintaxis específica de cada framework, ¿qué queda para nosotros?

Queda lo más difícil: entender qué estamos construyendo y por qué. Saber que hay un tensor subyacente y qué implica manipular su vista. Diseñar que los usuarios tengan un ID único y que todos los sistemas lo usen, en lugar de intentar cruzar correos electrónicos de distintas plataformas. Eso es diseño de sistemas. Eso es criterio. Y ningún modelo lo va a sustituir en el corto plazo, no porque sea imposible, sino porque nadie ha puesto esa señal de recompensa todavía.

La comprensión no se puede externalizar. Puedes delegar el pensamiento, pero no el entendimiento. Y si no entiendes qué le estás pidiendo al agente, estás firmando en blanco.


El momento en que estamos

Estamos en un punto raro. Las herramientas cambiaron de forma suficientemente profunda como para que los flujos de trabajo viejos ya no sean los óptimos, pero la mayoría del ecosistema todavía está organizado para humanos. La documentación está escrita para que la lea una persona. Los procesos de contratación evalúan si resuelves puzzles algorítmicos. Las empresas miden productividad con métricas diseñadas antes de que existieran los agentes.

Todo eso va a cambiar. Ya está cambiando. Y quienes se adapten primero no son los que abandonen el rigor técnico, sino los que aprendan a aplicarlo en este nuevo paradigma: dirigiendo agentes con la misma exigencia con la que antes escribían código.

La pregunta ya no es si sabes programar. Es si sabes dirigir.

Este artículo está basado en ideas expresadas por Andrej Karpathy en una conversación reciente sobre el estado actual del desarrollo de software y la inteligencia artificial.

Fuente: https://x.com/stephzhan/status/2049518659513852109


Sunday, April 12, 2026

El futuro de la programación es no programar en absoluto

En 2017, el CEO de GitHub dijo que la programación iba a desaparecer. Tenía razón en algo. Se equivocó en lo que importa.


"El futuro de la programación es no programar en absoluto." — Chris Wanstrath, ex-CEO de GitHub, 2017

Business Insider lo publicó en portada. El CEO de GitHub diciendo que la automatización acabaría con la programación tradicional.

En 2017 no existía ChatGPT. No existía Copilot. No existía nada de lo que hoy usamos a diario.

Y aun así, ya lo estaba diciendo.

¿Tenía razón?

Sí y no.

En lo que tenía razón

La forma de programar cambió. Eso es un hecho.

Hoy cualquier persona puede abrir una interfaz, escribir un prompt, y obtener un componente funcional en segundos. Código que hace cinco años habría requerido horas de trabajo aparece en la pantalla antes de que termines el café.

Sobre esto no hay debate. La barrera para generar código se desplomó.

En lo que se equivocó

Programar nunca fue solo escribir código.

Programar es pensar en sistemas. Es decidir qué construir y cómo. Es diseñar soluciones que funcionen hoy y que se puedan modificar mañana sin que todo explote. Es entender por qué una arquitectura aguanta la carga y otra no. Es saber cuándo una abstracción ayuda y cuándo está creando un problema que alguien va a sufrir en seis meses.

Eso no lo hace un prompt.

Un modelo de lenguaje genera código. No razona sobre el sistema. No conoce las restricciones reales del negocio. No tiene que mantener lo que produce ni responder cuando algo falla en producción a las dos de la mañana.

El criterio de ingeniería no se automatiza. Se aplica sobre la automatización.

La ventaja que nadie está viendo del todo

El programador que sabe diseñar software y además sabe usar herramientas de IA como un profesional tiene una ventaja que hoy todavía no está completamente valorada. Pero lo va a estar.

Y acá importa ser preciso, porque hay mucha confusión sobre qué significa "usar IA como profesional".

No hablo de pedirle a ChatGPT que te escriba un componente. Eso ya lo hace cualquiera. Eso es el piso, no el techo.

Hablo de otra cosa. De montar agentes que siguen tus reglas de arquitectura. De encadenar subagentes que manejan partes específicas de un flujo. De definir skills reutilizables y conectar herramientas externas vía MCP. De hacer que la IA trabaje para vos de forma estructurada, con criterio, con guardarraíles de ingeniería reales.

La diferencia entre generar código con un prompt y montar un sistema de IA que hace ingeniería de software es la misma diferencia que hay entre ejecutar un script y diseñar una arquitectura.

Dos caminos

Podés seguir haciendo vibe coding. Generar código, pegarlo, rezar para que funcione, repetir. Muchos lo hacen. El resultado es velocidad aparente y deuda técnica real.

O podés aprender a montar sistemas de IA que apliquen ingeniería de software de verdad. Que sigan principios de diseño. Que generen tests. Que respeten las convenciones del proyecto. Que escalen.

Wanstrath tenía razón en que escribir código manualmente iba a dejar de ser el centro del trabajo. Se equivocó en pensar que eso hacía irrelevante al programador. Lo que hace irrelevante al programador es no entender que su rol cambió.

El rol ahora es dirigir sistemas que producen software. Y para dirigirlos bien, necesitás saber exactamente qué es buen software.

El que no sabe eso, con o sin IA, sigue construyendo lo mismo de siempre.

Solo más rápido.

Tu código limpio ya no te diferencia

Todo lo que aprendiste sigue valiendo. El problema es que ya no es suficiente.


Años aplicando SOLID. Code reviews serias. Tests, refactoring, arquitectura limpia. Has pagado el precio que hay que pagar para escribir software de verdad.

Y ahora hay alguien con menos experiencia que está entregando más rápido que tú.

Antes de que eso te moleste, lee: no es porque sea mejor programador. Probablemente ni sabe por qué funciona la mitad de lo que escribe. Pero aprendió algo que tú todavía no has incorporado.

Aprendió a dirigir agentes.

El código limpio sigue siendo necesario. Ya no es suficiente.

Esto no es un ataque a los fundamentos. El código sostenible, los principios de diseño, la capacidad de detectar un code smell antes de que se convierta en un desastre — todo eso sigue importando. Mucho.

Pero dejó de ser lo que te diferencia.

Antes, ser senior era saber escribir buen código. Conocer los patrones. Hacer buenas abstracciones. Eso era el nivel al que llegabas después de años de trabajo real.

Ahora eso es el piso. El punto de partida. Lo mínimo esperado.

Lo que está cambiando la ecuación no es el conocimiento. Es quién está aplicando ese conocimiento, y a qué velocidad.

Lo que está pasando en los equipos ahora mismo

Hay developers que llevan meses trabajando con agentes. No como juguete. Como parte real de su flujo de trabajo.

Le dan al agente sus reglas de arquitectura. Sus convenciones de naming. Sus criterios de testing. Las mismas reglas que tú aplicas en cada PR. Y el agente las ejecuta de forma consistente, en cada archivo, sin que nadie tenga que repetirlas.

El resultado: resuelven los mismos problemas que tú. En menos tiempo.

No porque tengan mejor criterio. Porque aprendieron a convertir su criterio en instrucciones que escalan.

No va a ser la IA la que te desplace. Va a ser un developer que sabe lo mismo que tú pero que lleva meses haciendo ingeniería con agentes mientras tú segues trabajando igual que hace un año.

Y la distancia se alarga cada semana.

Por qué los seniors tienen la mejor posición para esto

Acá está la parte que la mayoría no está viendo.

Los agentes sin criterio son peligrosos. Un developer que no distingue buen diseño de malo, que no entiende por qué una abstracción existe, que no sabe cuándo un test agrega valor y cuándo solo agrega ruido — ese developer con agentes genera deuda técnica a velocidad industrial.

El criterio que tardaste años en construir es exactamente lo que convierte un agente en una herramienta de ingeniería seria. Sin ese criterio, el agente es solo ruido rápido.

Las convenciones que aplicas en cada code review. Los límites de responsabilidad que defiendes en cada diseño. Los patrones que exiges antes de aprobar un PR. Todo eso, que hoy vive en tu cabeza, puede convertirse en reglas que un agente sigue de forma consistente.

Solo tienes que dar ese paso.

La pregunta que vale la pena hacerse

¿Cuánto de lo que sabes está documentado de una forma que un agente pueda seguir? ¿Cuánto sigue atrapado en tu cabeza, disponible solo cuando tú estás en la conversación?

Eso es lo que hay que cambiar. No el conocimiento. La forma en que ese conocimiento se aplica.

El developer que va a seguir siendo relevante en los próximos años no es el que abandona lo que sabe. Es el que aprende a multiplicarlo.

El que no lo haga se va a quedar atrás. No por falta de talento. Por falta de movimiento.

Y eso sí es una decisión.

Refactorizar sin tests es apostar a ciegas

El código limpio que nadie puede verificar es solo código que todavía no ha roto nada.


El buen código no es opcional. Eso ya lo sabes. Pero hay algo que mucha gente se deja en el camino.

¿Cómo sabes que tu código "limpio" sigue funcionando después de refactorizarlo?

Imagina esto: es un viernes por la tarde. Te sientas a refactorizar un servicio que lleva meses pidiendo a gritos una limpieza. Renombras métodos, los extraes a un nuevo módulo, introduces abstracciones que antes no existían. Cuando terminas, el código brilla. Es una obra de arte.

Y entonces viene la pregunta incómoda.

¿Cómo sabes que no has roto nada? ¿Que el flujo de pedidos sigue funcionando de extremo a extremo? ¿Que la integración de pagos no se ha roto silenciosamente? ¿Que un cliente todavía puede completar una compra?

No lo sabes. No puedes saberlo, a menos que tengas tests que verifiquen el comportamiento del sistema, no la implementación.

El código sostenible necesita una red de seguridad

Los tests unitarios son rápidos. Te dan feedback inmediato y hacen que refactorizar se sienta seguro. Son el pan de cada día de cualquier desarrollador disciplinado.

Pero incluso los mejores tests unitarios tienen un punto ciego enorme.

Verifican piezas en aislamiento. Mockean la base de datos. Stubean la API externa. Se saltan la serialización. Cuando todos los tests pasan, sabes que la lógica de tu unidad funciona correctamente. Pero no sabes si la lógica del sistema funciona.

Los problemas aparecen en los límites. La consulta que devuelve datos con una forma diferente a la que asumías. La capa HTTP que silenciosamente se come un campo. La integración que funcionaba aislada pero se rompe cuando se encuentra con la realidad.

Los tests unitarios no pueden detectar estos problemas, no porque estén mal escritos, sino porque no están diseñados para eso. Nadie les pidió que lo hicieran.

Los tests de aceptación llenan ese hueco

Los tests de aceptación ejercitan el sistema de extremo a extremo, a través de los mismos límites que tocan tus usuarios reales.

La diferencia con los unitarios no es solo técnica, es conceptual:

  • Un test unitario dice: "Esta función calcula el descuento correctamente."
  • Un test de aceptación dice: "Cuando un cliente aplica un cupón en el checkout, el precio total se reduce y el pedido se completa."

Uno verifica una pieza. El otro verifica una historia. Uno te dice que la función hace lo que se supone. El otro te dice que el usuario puede hacer lo que necesita.

Ambos son necesarios. Son complementarios, no competidores. Pero el segundo es el que te deja dormir tranquilo después de un viernes de refactoring.

¿Y si tienes un proyecto legacy sin un solo test?

Esta es la pregunta real. La que tienen la mayoría de desarrolladores .NET que trabajan en proyectos con años de historia encima.

Aquí está la trampa en la que caen muchos: intentan escribir tests unitarios primero en código legacy. Es una batalla perdida. El código legacy no fue diseñado para ser testeado en aislamiento. Tiene dependencias estáticas, clases enormes, métodos que hacen diez cosas a la vez.

La estrategia más efectiva es la contraria: empieza por los tests de aceptación.

1. Escribe un test de aceptación que describa un flujo crítico.
2. Hazlo pasar tal cual está el código (sin tocar nada).
3. Ahora tienes una red de seguridad.
4. Refactoriza con confianza.
5. Añade tests unitarios a medida que extraes lógica.

Los tests de aceptación sobre código legacy son feos, lentos, a veces frágiles. No importa. Su trabajo no es ser elegantes. Su trabajo es atrapar regresiones mientras tú limpias el desastre heredado.

Primero pon la red. Luego haz el salto.

La conclusión que no te puedes saltar

Refactorizar código sin cobertura de tests de comportamiento no es "limpiar". Es reorganizar la deuda técnica sin garantías. Puede quedar bonito en el repositorio y estar completamente roto en producción.

El código limpio sin tests es código que todavía no ha fallado en el peor momento. El código con una buena cobertura de aceptación es código que puedes cambiar sin miedo.

Y en este trabajo, cambiar código sin miedo no es un lujo. Es la habilidad que te hace relevante.

Sunday, March 8, 2026

5 Tips para seguir siendo relevante en la era de la IA

Cada vez se escribe menos código a mano. La pregunta no es si la IA va a cambiar tu trabajo — ya lo está haciendo. La pregunta es: ¿qué valor vas a aportar tú?


1. Aprende a usar la IA — pero sobre todo, entiende lo que genera

Usar GitHub Copilot o ChatGPT para escribir código es fácil. Cualquiera puede hacerlo hoy. El problema es que la IA se equivoca, y se equivoca con mucha confianza. Genera código que compila, que se ve correcto, pero que tiene bugs sutiles, vulnerabilidades de seguridad o que simplemente no escala.

Tu valor como developer ya no está en escribir la línea de código — está en saber si esa línea debería existir, y si está bien escrita. Eso requiere criterio. Y el criterio solo viene de entender profundamente lo que estás construyendo.

Si no tienes el conocimiento para detectar cuando la IA se equivoca, entonces básicamente eres un copypaste humano. Y eso sí lo puede hacer la IA sola.

→ La IA es un amplificador: amplifica a los buenos developers y expone a los que no saben lo que hacen.


2. El conocimiento profundo en tecnología nunca fue tan importante


Paradójicamente, en la era donde la IA "escribe el código por ti", entender los fundamentos de la tecnología se vuelve más importante, no menos. Cuando la IA genera una solución con O(n²) de complejidad donde necesitabas O(log n), ¿lo vas a detectar?

Estructuras de datos, algoritmos, arquitectura de sistemas, patrones de diseño, redes, sistemas operativos. Todo eso que quizás sentiste que podías saltarte porque "igual puedo buscar en Stack Overflow" — ahora es exactamente lo que te diferencia.

La IA puede completar código, pero no puede reemplazar la intuición que te dice que algo está mal diseñado desde la base. Esa intuición se construye con años de estudio y práctica real.

→ Invierte tiempo en entender cómo funcionan las cosas por dentro. Ese conocimiento no se puede generar con un prompt.


3. Sé Full-Stack en el sentido amplio del término

Full-Stack ya no significa solo "frontend y backend". Significa entender el stack completo: desde el código hasta la infraestructura donde corre.

Los developers que más valor aportan hoy son los que pueden razonar sobre DevOps, Cloud, Seguridad, Performance, Bases de Datos, Costos de infraestructura — y cómo todo eso interactúa. La IA puede generar una función, pero no puede diseñar un sistema resiliente con tus restricciones específicas de negocio.

Un developer que entiende que su código va a correr en Kubernetes, que necesita ser idempotente, que tiene un SLA de 99.9% y que debe cumplir con GDPR — ese developer es muy difícil de reemplazar.


→ El developer del futuro es un generalista profundo: sabe de todo y profundiza donde hace falta.


4. No hay sustituto para construir cosas reales

Puedes ver todos los tutoriales del mundo. Puedes tomar todos los cursos de Udemy. Pero hasta que no hayas debuggeado tu propio servidor a las 2am porque se cayó en producción, no entiendes realmente lo que significa construir software.

Empieza a construir proyectos por tu cuenta. No importa que sean pequeños, no importa que nadie los use. Construye una CLI tool, un bot, una API, un side project que resuelva un problema que tú tengas. La experiencia real de tomar decisiones de diseño, gestionar deuda técnica, manejar datos reales — eso no se puede simular.

Además, los proyectos reales te van a enseñar cosas que ningún tutorial menciona: los edge cases, los problemas de integración, los tradeoffs reales entre velocidad y calidad.

→ La experiencia acumulada es un moat que la IA no puede cruzar. Empieza a construirla hoy.


5. Muestra lo que haces y por qué importa

En un mercado donde cada vez más gente puede generar código con IA, la diferencia va a estar en quién puede demostrar que sabe lo que está haciendo y por qué sus decisiones son correctas.

Escribe sobre lo que aprendes. Documenta tus proyectos. Explica las decisiones técnicas que tomaste y las alternativas que descartaste. Comparte lo que construyes en GitHub. Habla en meetups locales. Crea una presencia técnica real que respalde tu trabajo.

No se trata de hacer personal branding vacío. Se trata de que cuando alguien vea tu trabajo, quede claro que detrás hay un developer que piensa, que razona, y que tiene criterio — y que eso vale mucho más que alguien que solo sabe invocar un LLM.

→ Tu portafolio, tu blog, tus contribuciones open source: eso es evidencia de que eres el tipo de developer que la IA no puede reemplazar.



La IA cambia el cómo, no el qué

Seguimos necesitando software que resuelva problemas reales, que escale, que sea seguro, que sea mantenible. Los developers que entiendan eso — y que usen la IA como herramienta, no como muleta — van a ser los más valiosos de la próxima década.


Wednesday, November 5, 2025

Por qué la documentación debe vivir junto al código

Esa idea proviene de una práctica común en ingeniería de software y desarrollo ágil.

Martin Fowler — En sus artículos sobre "Self-Documenting Code" y "Continuous Integration", Fowler sugiere que la documentación debe estar “lo más cerca posible del código” para mantenerse actualizada y  Fowler promueve documentar dentro del código (por ejemplo, con comentarios o docstrings), o en el mismo repositorio (por ejemplo, usando README o Markdown junto al código fuente).

Kent Beck — En Extreme Programming Explained, Beck también insiste en que la mejor documentación es el código mismo, y que cualquier documentación adicional debe estar “junto al código que describe” para evitar que quede obsoleta.

Joel Spolsky — En su ensayo “Where does documentation live?”, Joel recomienda mantener la documentación cerca del código en el control de versiones, por la misma razón: coherencia y mantenimiento.



Wednesday, September 24, 2025

La IA no arregla lo que no existe: lecciones sobre profesionalidad y código

 Hace poco me llegó la historia de un developer con mucha experiencia que me hizo reflexionar. No hablaba de frameworks, ni de arquitecturas modernas. Hablaba de algo más profundo: la profesionalidad.

Este developer forma parte de un equipo que revisa pruebas técnicas de candidatos en una empresa que está creciendo a buen ritmo. Nada fuera de lo normal… hasta que le tocó analizar un code challenge que, en apariencia, era impecable.

Había mucho código, la documentación era extensa y, a primera vista, todo parecía sólido. Pero cuando intentó ejecutar el proyecto, las señales empezaron a aparecer:

  • La aplicación no levantaba.
  • La documentación mencionaba migraciones y scripts que no existían.
  • Había sobreingeniería absurda.
  • El código estaba lleno de sobreingeniería que complicaba lo básico.

Tras un rato de debugging, la conclusión era evidente: aquello no era el resultado del trabajo de alguien con fundamentos sólidos. Lo más probable es que hubiera sido generado con IA sin criterio.

El jefe de este developer resumió la situación con una frase que, sinceramente, me parece oro puro:

“No importa si la solución funciona o no, simplemente no nos gustaría trabajar con esta persona.”

La IA como espejo de lo que eres

Aquí está la lección importante: la IA no es la villana de esta historia.

La inteligencia artificial es un potenciador.

  • Si tienes fundamentos sólidos, multiplica tu productividad.
  • Si careces de ellos, amplifica el caos.

Es un espejo que refleja lo que ya eres como profesional.

Un developer con criterio puede usar IA para avanzar más rápido, aprender o incluso mejorar la calidad de su trabajo. Pero alguien sin bases claras solo construirá castillos en el aire, y la IA hará que esos castillos sean más grandes y más frágiles.

Profesionalidad antes que herramientas

Lo que realmente se evalúa en un proceso de selección (y en el día a día del trabajo) no es solo la calidad del código. Es cómo entiendes tu rol como profesional:

  • ¿Respetas tu propio trabajo y el de los demás?
  • ¿Eres honesto con tus habilidades y tus límites?
  • ¿Entregas algo que otros puedan mantener y mejorar?

Un proyecto perfecto en apariencia, pero imposible de sostener, dice más del profesional que lo presenta que de la herramienta con la que fue creado.

Mi reflexión personal

Yo uso IA en mi día a día como developer. Me ayuda a salir de bloqueos, a generar ejemplos rápidos y a tener otra perspectiva cuando busco soluciones. Pero no le entrego el timón de mi trabajo.

Porque el código, al final, es un reflejo de cómo piensas. Y la IA, por más sofisticada que sea, no puede pensar por ti.

Si quieres crecer como programador —y que la IA sea una aliada real— hay tres bases que no puedes saltarte:

  1. Aprende y refuerza los fundamentos.
  2. Sé honesto con lo que entregas.
  3. Usa la IA para potenciar, no para esconder tus carencias.

La moraleja de esta historia es clara: la tecnología cambia, pero la profesionalidad se nota siempre.



El programador que sabe vender: la soft skill más infravalorada

Muchos desarrolladores me dicen que, además de aprender hard skills (ya sabes, lo técnico), están metiendo tiempo en soft skills.

Y eso está muy bien.

De hecho, las soft skills son, paradójicamente, las más hard de aprender. No sé por qué las llaman “blandas”…

Entre todas, hay una que sobresale: saber vender.

Vender no es lo que piensas

En España y en Latinoamérica solemos tener una imagen anticuada del vendedor: el comercial pesado, con carpeta en mano, tocando puertas y forzando conversaciones.

Pero la realidad es otra.

Vender no tiene nada de malo. Más bien, todo lo contrario.

Si tu producto es bueno, tienes casi una obligación moral de venderlo. Porque significa que puede mejorar la vida de las personas, y el mundo necesita enterarse de que existe.

El vendedor desafiante

Hay un libro clave que explica esto: El vendedor desafiante. Su tesis es simple: los mejores vendedores no son los más simpáticos, ni los que más invitan a cafés.

Son los que cuestionan.

Los que hacen que el cliente vea oportunidades que tenía delante, pero que no sabía reconocer.

¿Qué tiene que ver esto con programar?

Mucho más de lo que parece.

Un buen programador hace lo mismo. No se limita a escribir código:

  • Pregunta por el contexto.
  • Cuestiona lo superficial.
  • Diferencia entre problema y solución.
  • Detecta si algo realmente aporta valor.

Ese tipo de dev no se obsesiona con presumir de tecnología ni con perderse en sobreingeniería. Se obsesiona con entregar valor.

Y, sobre todo, entiende el negocio. Sabe traducir las reglas del dominio al código. Y mantiene el legacy code bajo control, porque comprende que la deuda técnica también impacta en el valor que recibe el cliente.

La soft skill más estratégica

Por eso aprender a vender es tan potente para un programador.

No hablamos de aprender técnicas de persuasión vacías, sino de internalizar el verdadero sentido de aportar valor:

  • A tu equipo.
  • A tu empresa.
  • A tus clientes.

Al final, el mejor dev no es el que sabe más lenguajes o frameworks, sino el que sabe poner la tecnología al servicio del negocio.

Ese, créeme, siempre “vende” mejor su trabajo.

Wednesday, September 17, 2025

Figuras más influyentes del desarrollo de software

  • Kent Beck
  • Bjarne Stroustrup
  • Grady Booch
  • Dave Thomas
  • Michael Feathers
  • Ron Jeffries
  • Ward Cunningham
  • Uncle Bob

Wednesday, June 11, 2025

Cuando el código funciona, pero no tiene tests: ¿y ahora qué?

Seguramente te ha pasado alguna vez. Te dan acceso al repositorio de un nuevo proyecto. Lo abres con curiosidad, esperas encontrar una estructura bien organizada, quizás alguna carpeta de tests. Comienzas a revisar los archivos y, para tu sorpresa, no hay ni una sola prueba. Ni unitarias. Ni de integración. Ni siquiera una prueba olvidada en un rincón del código. Nada. Pero lo más curioso es que ese sistema lleva tres años en producción. Funciona. Se usa a diario. Y sin embargo, todos en el equipo le tienen miedo. Nadie quiere tocar nada porque el más mínimo cambio puede romper algo importante… y nadie sabría con certeza qué fue.

Frente a este escenario, la pregunta es inevitable: ¿por dónde empiezo? ¿Cómo puedo empezar a testear sin volverme loco ni romper lo que ya está funcionando? Esta duda no solo la tienen los desarrolladores que recién se suman a un proyecto. Muchas empresas se enfrentan a este dilema en algún momento de su vida tecnológica. Y aunque cada contexto tiene sus particularidades, la respuesta de fondo suele ser la misma: si no puedes testear, probablemente es porque el código no fue diseñado para ser testeado.

Esto es algo más común de lo que parece. Cuando un sistema crece sin una estrategia clara de pruebas, lo que suele pasar es que se acumula lo que se conoce como complejidad accidental. Hay demasiado acoplamiento entre clases y módulos, poca cohesión interna, muchas responsabilidades mezcladas en los mismos componentes. El código se vuelve rígido, difícil de dividir y casi imposible de probar en pequeñas unidades. Y aquí viene una verdad incómoda: no hay atajos mágicos que te permitan hacer testing si el diseño del sistema no lo permite. Puedes intentarlo, claro, pero es como querer medir la temperatura con una regla: simplemente no va a funcionar.

Entonces, ¿qué hacer en estos casos? La mejor forma de empezar es con pruebas de extremo a extremo, también conocidas como tests end-to-end (E2E). Estas pruebas se enfocan en validar que el sistema funciona correctamente desde el punto de vista del usuario, sin importar cómo está construido por dentro. Piensa en ellas como una caja negra: no necesitas saber cómo están organizadas las clases, qué métodos se llaman, ni qué librerías usa el sistema. Solo te importa que, si ingresas ciertos datos por la interfaz, el resultado sea el esperado.

Los tests end-to-end te dan una ventaja enorme al inicio. Aunque suelen ser más lentos y costosos de mantener, en este contexto cumplen una función clave: crear una red de seguridad. Con ellos puedes detectar si rompiste algo importante al hacer un cambio. Te dan confianza para empezar a refactorizar, para mover piezas internas del sistema sin miedo a dejarlo inutilizable.

Una vez que tienes esa base de pruebas automáticas funcionando, el siguiente paso es comenzar a mejorar el diseño del sistema. No hace falta reescribir todo desde cero. De hecho, eso sería un error. Lo que sí puedes hacer es aplicar pequeñas mejoras: dividir responsabilidades, separar lógicas en distintas clases, introducir interfaces, eliminar dependencias directas, aplicar patrones de diseño donde tenga sentido. Estos cambios van haciendo que el código se vuelva cada vez más modular, más limpio… y, lo más importante, más testeable.

Cuando alcanzas un punto en el que puedes aislar comportamientos o componentes, entonces puedes empezar a escribir pruebas unitarias y de integración. Estos tipos de tests son más rápidos, más baratos de mantener, y te permiten obtener feedback casi inmediato. Ya no necesitas levantar toda la aplicación para probar algo; basta con ejecutar unos pocos tests y ver si todo sigue en orden. Este es el momento en el que verdaderamente comienza a cambiar la cultura del proyecto: pasas de tener miedo a tocar el código, a tener confianza para mejorarlo continuamente.

En resumen, no hay más ciencia que esta: empieza por donde puedas, construye una base segura con pruebas de extremo a extremo, refactoriza con cuidado hacia un diseño más limpio, y luego introduce tests más pequeños y específicos. Es un camino gradual, pero muy poderoso. No se trata de hacer testing por cumplir, sino de transformar la forma en que desarrollas y mantienes el software.

Así que si estás frente a un proyecto sin pruebas y con años de historia encima, no te frustres. No estás solo. Muchos hemos estado ahí. Empieza poco a poco, con paciencia, y verás cómo el sistema se vuelve cada vez más mantenible… y tú, cada vez más tranquilo al trabajar en él.

Friday, April 4, 2025

Querido Developer, ¿estás probando lo suficiente tu código?

Como developers, estamos entrenados para resolver problemas, construir funcionalidades y entregar valor rápido. Pero hay un aspecto esencial del desarrollo que muchas veces ignoramos o subestimamos: las pruebas de software.

En este artículo, quiero hablarte como colega. Vamos a reflexionar juntos sobre por qué las pruebas no son una carga, sino una de las mejores herramientas que tenemos para escribir mejor código, evitar dolores de cabeza y ganar confianza en nuestras entregas.


La importancia de las pruebas dentro del proceso de desarrollo de software

Las pruebas no son una fase final ni un "extra" que solo se hace si queda tiempo. Son una parte fundamental del ciclo de desarrollo.

Cuando probamos nuestro código, no solo estamos verificando que una función devuelva el valor esperado. Estamos:

  • Confirmando que el sistema cumple con los requisitos.
  • Validando que los cambios no rompan lo que ya funcionaba.
  • Previniendo bugs antes de que lleguen a producción.
  • Reduciendo el costo de mantenimiento a futuro.

Probar es una forma de cuidar nuestro trabajo. De blindar nuestras soluciones frente a cambios, errores humanos y condiciones inesperadas. Es escribir con visión de futuro.

Entonces… si sabemos que las pruebas son clave, ¿por qué a veces las evitamos?


¿Por qué los developers no siempre invierten suficiente tiempo en pruebas?

La realidad es que sí hacemos pruebas, pero muchas veces de forma informal o superficial. Hacemos una prueba manual, o ejecutamos el programa “a ver si funciona”, y seguimos adelante. ¿Te suena?

Las razones por las que no escribimos suficientes pruebas son conocidas:

  • Presión por entregar rápido.
  • Plazos ajustados que nos hacen pensar que “ya habrá tiempo para testear después”.
  • Falta de cultura de calidad dentro del equipo o la empresa.
  • Desconocimiento o poca práctica con herramientas de testing.

El problema es que esta lógica termina saliendo cara. Porque lo que no se prueba hoy, se rompe mañana. Y cuando se rompe en producción, el costo es mayor: horas extra, bugs urgentes, pérdida de confianza, estrés...

Por eso es importante entender qué tipo de pruebas existen y cuál es nuestro rol como developers en cada una.


Tipos de pruebas en el desarrollo de software

No todas las pruebas son iguales. Cada tipo cumple una función específica dentro del desarrollo:

Cada una de estas pruebas aporta valor en un nivel distinto del sistema. Como developers, no podemos esperar que QA cubra todo. Tenemos una responsabilidad clara sobre las pruebas unitarias e integración, que son las que nos permiten desarrollar con confianza.

Y si además logramos automatizarlas, el beneficio se multiplica.


¿Qué es la automatización de pruebas y por qué deberías aplicarla?

La automatización de pruebas consiste en escribir scripts o casos de prueba que se ejecutan automáticamente, en lugar de hacerlos a mano cada vez.

Esto permite:

  • Ejecutar pruebas en segundos (ideal para integración continua).
  • Detectar errores al instante después de cada cambio.
  • Asegurar que el código nuevo no rompa el sistema existente.
  • Ahorrar tiempo en tareas repetitivas.

Antes, todas las pruebas eran manuales. Hoy, con herramientas como xUnit, NUnit, Selenium o Postman, podemos automatizar casi todo.

Pero cuidado: automatizar no significa automatizar todo. Acá es donde entra un concepto clave en el mundo del testing...


La pirámide de testing: cómo decidir qué pruebas automatizar

La pirámide de testing es un modelo que nos guía sobre cuántas pruebas hacer y de qué tipo. Su idea central es: las pruebas más baratas y rápidas deben ser las más numerosas.


  • Las pruebas unitarias (base de la pirámide) son fáciles de escribir, rápidas de ejecutar y muy efectivas para detectar errores en funciones pequeñas.
  • Las pruebas de servicio o integración validan cómo interactúan los módulos, APIs, o servicios entre sí.
  • Las pruebas de UI son necesarias, pero frágiles, lentas y difíciles de mantener. Se deben hacer con moderación.

Este enfoque no solo mejora la eficiencia, también reduce el tiempo de feedback y mantiene tu sistema saludable en el tiempo.

Y aquí viene otra gran pregunta...


¿Dónde terminan las pruebas del developer y empiezan las del tester?

En realidad, no hay una línea tan clara. QA y desarrollo deberían trabajar juntos, no como dos mundos separados.

  • Los developers escribimos pruebas que aseguran que el código hace lo que esperamos.
  • El equipo de QA verifica que el sistema responde correctamente ante escenarios reales, errores de usuario, condiciones extremas.

Si no escribimos nuestras pruebas, el equipo QA pasa más tiempo encontrando errores básicos en vez de enfocarse en casos más complejos y valiosos.

Colaborar con QA desde el inicio del proyecto mejora los entregables, evita malentendidos y crea una cultura de calidad compartida.


¿Qué es una estrategia de pruebas y por qué deberías tener una?

Una estrategia de pruebas es el plan que define cómo vamos a probar nuestro software. Y no debería ser un documento oculto o algo que solo define QA.

Definir una buena estrategia de pruebas implica responder a estas preguntas:

  • ¿Qué tipos de pruebas vamos a hacer? (unitarias, integración, UI…)
  • ¿Cuántas pruebas vamos a tener? (basado en riesgo o cobertura)
  • ¿Quién las va a hacer? (Dev, QA, ambos)
  • ¿Cuándo se van a ejecutar? (CI/CD, antes del release, por sprint)
  • ¿Con qué herramientas? (xUnit, Cypress, Postman, Playwright, etc.)
  • ¿Cuál será la arquitectura de pruebas? (estructura de carpetas, mocks, inyección de dependencias, base de datos en memoria, etc.)

Una estrategia clara nos da foco, reduce el retrabajo y permite mantener una cobertura saludable sin duplicar esfuerzos.

Lectura recomenda: Definición de la estrategia de pruebas

Conclusión: probar es parte de tu trabajo, no una tarea secundaria

Como developers, no solo estamos para escribir código que compila. Estamos para crear soluciones robustas y mantenibles.

Probar tu código no es una pérdida de tiempo. Es la mejor forma de proteger tu trabajo, tu tiempo y la calidad del producto.


✅ Acciones concretas que puedes aplicar hoy

  • Empieza por lo básico: agrega pruebas unitarias a tu próxima función.
  • Incorpora las pruebas en tu flujo diario: que no sean una fase separada.
  • Automatiza con herramientas simples: empieza con xUnit o NUnit.
  • Habla con QA: alinear expectativas y compartir estrategia de pruebas.
  • Construye o mejora la estrategia de testing de tu equipo.


¡Gracias por llegar hasta aquí!


Monday, March 3, 2025

Integrando el Versionamiento en la Arquitectura Empresarial

En el ámbito empresarial moderno, la Arquitectura Empresarial (AE) se presenta como un marco fundamental que alinea los procesos de negocio con las capacidades tecnológicas, asegurando que las inversiones en tecnología respalden eficazmente los objetivos estratégicos de la organización. Dentro de este contexto, la gestión adecuada de los activos tecnológicos es esencial para mantener la competitividad y eficiencia operativa.​


Activos Tecnológicos en la Arquitectura Empresarial

Los activos tecnológicos comprenden todos los recursos de tecnología de la información que una organización utiliza para operar y alcanzar sus metas. Estos incluyen:​
  • Hardware: Servidores, computadoras, dispositivos de red y otros equipos físicos.​
  • Software: Aplicaciones internas, sistemas operativos y herramientas de productividad.​
  • Datos: Bases de datos, registros de clientes y cualquier información digital relevante.​
  • Infraestructura de Red: Conectividad interna y externa, incluyendo internet y redes privadas.​
  • Propiedad Intelectual Digital: Patentes, licencias y derechos de autor en formato digital.
La AE proporciona una visión integral de cómo estos activos interactúan y se integran con los procesos de negocio, facilitando una gestión más efectiva y alineada con la estrategia corporativa. ​

La Práctica del Versionamiento en la Gestión de Activos Tecnológicos

El versionamiento es una práctica técnica que implica el seguimiento y control de las diferentes versiones de los artefactos de software o ítems de configuración dentro de un sistema. Esta práctica es esencial para mantener la integridad y continuidad operativa, permitiendo a las organizaciones:​
  • Rastrear Cambios: Identificar qué modificaciones se realizaron, cuándo y por quién.​
  • Revertir Errores: Volver a versiones anteriores en caso de fallos o problemas.​
  • Facilitar la Colaboración: Permitir que múltiples equipos trabajen simultáneamente sin conflictos.
El control de versiones coordina todos los cambios en un proyecto de software, al rastrear de manera efectiva los cambios en los archivos de origen, los diseños y todos los activos digitales necesarios para un proyecto y los metadatos relacionados. ​


Artefactos que un Desarrollador debe Considerar para el Versionamiento

Para implementar un control de versiones efectivo, los desarrolladores deben considerar una variedad de artefactos, incluyendo:​
  1. Código Fuente: Los archivos que contienen las instrucciones de programación de las aplicaciones.​
  2. Archivos de Configuración: Documentos que establecen parámetros y ajustes para diferentes entornos (desarrollo, pruebas, producción).​
  3. Documentación: Manuales de usuario, guías de instalación y cualquier material que acompañe al software.​
  4. Scripts de Base de Datos: Instrucciones para la creación, modificación y gestión de bases de datos.​
  5. Pruebas Automatizadas: Casos de prueba que aseguran la funcionalidad y calidad del software.​
  6. Infraestructura como Código (IaC): Archivos que describen la configuración de la infraestructura, permitiendo su gestión y despliegue automatizado.​
  7. Dependencias y Librerías: Módulos externos o paquetes que la aplicación requiere para funcionar correctamente.
Estos artefactos, al ser versionados, garantizan que cualquier cambio sea registrado y pueda ser gestionado de manera eficiente, reduciendo riesgos y mejorando la calidad del producto final.​

Conclusión

La integración de prácticas de versionamiento en la gestión de activos tecnológicos es fundamental para el éxito de la Arquitectura Empresarial. Al asegurar que cada componente tecnológico esté debidamente controlado y alineado con los objetivos estratégicos, las organizaciones pueden adaptarse mejor a los cambios del mercado, mejorar su eficiencia operativa y mantener una ventaja competitiva sostenible.​

¡Gracias por llegar hasta aquí!

Friday, February 28, 2025

Control de versiones en scripts de migración de bases de datos

Introducción

El desarrollo de software es un proceso en constante evolución, donde los cambios son esenciales para mejorar los sistemas y adaptarse a un entorno tecnológico dinámico. Sin embargo, gestionar estas modificaciones de manera efectiva es un desafío, ya que cualquier alteración no planificada puede comprometer la estabilidad, seguridad o eficiencia de una aplicación. Para enfrentar este reto, el control de versiones se ha convertido en una práctica fundamental, permitiendo a los equipos registrar cambios, mantener un historial preciso y colaborar de forma estructurada mediante herramientas como Git. Esto asegura la coherencia del código fuente y facilita su mantenimiento y evolución.

En el caso de las bases de datos, la gestión del cambio presenta desafíos únicos, ya que almacenan información crítica y cualquier modificación puede afectar la integridad de los datos. A diferencia del código fuente, que puede reemplazarse fácilmente, las bases de datos requieren estrategias bien planificadas para evitar pérdidas de información, garantizar la compatibilidad con versiones anteriores y sincronizar cambios sin interrumpir la operatividad del sistema. En este artículo, se analizará la importancia del control de versiones en los scripts de migración de bases de datos, los principales desafíos que implica y las mejores prácticas para asegurar estabilidad, seguridad y escalabilidad.


Versionado de Base de Datos mediante scripts de migración

Los scripts de migración de bases de datos son archivos que contienen instrucciones SQL diseñadas para modificar la estructura, los datos o la configuración de una base de datos de manera incremental y controlada. Estos scripts permiten realizar cambios como:

  • Creación, modificación o eliminación de tablas.
  • Adición o eliminación de columnas.
  • Alteración de índices, restricciones y llaves foráneas.
  • Inserción, actualización o eliminación de datos estáticos.
  • Cambios en procedimientos almacenados, funciones y triggers.
  • Creación de Jobs o Tareas Programadas.
  • Cambios en permisos y seguridad.
  • Creación de scripts de reversión para cada migración.

Por ejemplo, un script de migración que define la creación de una nueva tabla Users podría ser de la siguiente manera:

-- V1__Create_Users_Table.sql
CREATE TABLE Users (
    Id INT PRIMARY KEY,
    Name VARCHAR(100),
    Email VARCHAR(100) UNIQUE,
    CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
);

El control de versiones en bases de datos busca gestionar cambios de manera estructurada, evitando inconsistencias entre entornos (desarrollo, prueba, producción). Los scripts de migración son una parte clave de este proceso, ya que:

  1. Documentan los cambios: Cada script representa una versión específica de la base de datos, facilitando el seguimiento del historial de modificaciones.
  2. Permiten la replicación de cambios en distintos entornos: Aseguran que todos los desarrolladores y servidores utilicen la misma versión de la base de datos.
  3. Facilitan el rollback: En caso de errores, los scripts de migración pueden incluir scripts de reversión para volver a una versión anterior.
  4. Se integran con herramientas de versionado y CI/CD: Herramientas como Flyway, Liquibase, Entity Framework Core Migrations, entre otras, permiten aplicar estos scripts de manera ordenada y automatizada.

El uso de scripts de migración es una estrategia esencial para mantener la coherencia y estabilidad de las bases de datos a medida que evolucionan junto con el software. Sin embargo, gestionar estos cambios manualmente puede volverse complejo, especialmente en proyectos grandes con múltiples desarrolladores y entornos.

Para abordar este desafío, existen herramientas especializadas que automatizan la gestión de migraciones y garantizan que los cambios se apliquen de manera ordenada y reproducible. Entre las soluciones más utilizadas en el ecosistema de desarrollo encontramos Flyway y Entity Framework Core Migrations, dos enfoques que facilitan el versionado de bases de datos, cada uno con características y beneficios específicos.


Flyway y Entity Framework Core Migrations: Herramientas para el versionado de bases de datos

El control de versiones en bases de datos no solo requiere una estrategia bien definida, sino también herramientas que faciliten la aplicación y gestión de cambios de manera eficiente y segura. Flyway y Entity Framework Core Migrations (EF Core Migrations) son dos de las soluciones más utilizadas en el mundo del desarrollo, cada una con un enfoque distinto para gestionar la evolución de la base de datos.

Flyway: Migraciones mediante scripts SQL

Flyway es una herramienta de control de versiones de bases de datos que permite gestionar cambios mediante scripts SQL escritos manualmente. Se basa en un enfoque migration-based, lo que significa que los cambios se aplican secuencialmente a través de scripts versionados.

¿Cómo funciona Flyway?
  1. Los desarrolladores crean scripts de migración con cambios específicos en SQL.
  2. Flyway detecta y aplica los scripts en orden secuencial, registrando cada cambio en una tabla especial (flyway_schema_history).
  3. Los cambios se propagan automáticamente a diferentes entornos (desarrollo, prueba, producción).

Ejemplo de script de migración en Flyway:
-- V1__Create_Users_Table.sql
CREATE TABLE Users (
    Id INT PRIMARY KEY,
    Name VARCHAR(100),
    Email VARCHAR(150) UNIQUE,
    CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
);

Ejemplo de comando para ejecutar la migración:
flyway migrate

Ventajas de Flyway
  • Independiente del lenguaje de programación (compatible con .NET, Java, Python, etc.).
  • Permite escribir migraciones en SQL puro, dando control total sobre los cambios.
  • Historial de cambios claro y replicable en múltiples entornos.
  • Integración sencilla con CI/CD (Jenkins, GitHub Actions, Azure DevOps, etc.).
Desventajas de Flyway
  • Requiere escribir los scripts SQL manualmente, lo que puede ser más tedioso para equipos sin experiencia en bases de datos.
  • No genera automáticamente los scripts a partir del modelo de datos, como lo hace EF Core Migrations.
¿Cuándo usar Flyway?
  • Cuando se necesita control total sobre los cambios en la base de datos.
  • Si la base de datos es compartida entre múltiples aplicaciones o equipos.
  • En proyectos que no dependen exclusivamente de .NET y requieren una solución universal.

Entity Framework Core Migrations: Migraciones Code-First

Entity Framework Core Migrations (EF Core Migrations) es la herramienta nativa de .NET y Entity Framework Core para gestionar cambios en la base de datos mediante el enfoque Code-First. En este caso, los cambios en la base de datos se generan a partir del modelo de datos en C#, sin necesidad de escribir scripts SQL manualmente.

¿Cómo funciona EF Core Migrations?
  1. El desarrollador modifica las clases del modelo de datos en C# (ejemplo: agregar una nueva propiedad a una entidad).
  2. Se genera automáticamente un script de migración con el siguiente comando: 
    dotnet ef migrations add AddBirthdateToUsers

  3. Se aplica la migración a la base de datos con el comando:
    dotnet ef database update

  4. EF Core se encarga de ejecutar los cambios en SQL, sin necesidad de escribir los scripts manualmente.

Ejemplo de modelo de datos en C#:
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public DateTime Birthdate { get; set; } // Nuevo campo agregado
}

Ejemplo de migración generada automáticamente por EF Core:
ALTER TABLE Users ADD COLUMN Birthdate DATE;


Ventajas de EF Core Migrations
  • Automatiza la generación de scripts SQL, reduciendo la carga de trabajo del desarrollador.
  • Se integra perfectamente con .NET y Entity Framework Core.
  • Permite realizar migraciones sin escribir SQL manualmente, facilitando la gestión del esquema de la base de datos.
Desventajas de EF Core Migrations
  • Está altamente acoplado a .NET y Entity Framework Core.
  • Puede generar scripts SQL no óptimos en bases de datos grandes o complejas.
  • No es ideal para bases de datos compartidas con otras aplicaciones que no usan EF Core.
¿Cuándo usar EF Core Migrations?
  • En proyectos .NET que usan Entity Framework Core y siguen el enfoque Code-First.
  • Cuando se quiere minimizar la necesidad de escribir SQL manualmente.
  • Si la base de datos solo es utilizada por la aplicación .NET y no requiere cambios gestionados por otros equipos.


Conclusión

El desarrollo de software requiere que tanto el código como la base de datos evolucionen de manera estructurada para adaptarse a nuevas necesidades sin comprometer la integridad del sistema. A diferencia del código fuente, donde los cambios son más manejables, cualquier modificación en una base de datos debe gestionarse cuidadosamente mediante un sistema de versionado. Herramientas como Flyway y Entity Framework Core Migrations facilitan este proceso, ofreciendo enfoques distintos según el contexto del proyecto. Más allá de la elección de herramientas, es crucial adoptar una cultura de disciplina y control en la gestión de cambios, ya que un sistema de versionado adecuado no solo garantiza estabilidad y seguridad, sino que también permite escalar y evolucionar el software de manera eficiente.


¡Gracias por llegar hasta aquí!


Webgrafía:


Saturday, February 15, 2025

Dedicación Industria vs. Academia

Cuando terminé la carrera de Analista de Sistemas en ORT tuve una fuerte disyuntiva sobre mi dedicación industria/academia. En ese momento, me encantaba los temas de investigación y la escritura de artículos relacionados al desarrollo de software. Me atraía bastante la idea de dedicarme a la docencia, sin embargo al final terminé dedicándome de lleno a la industria, muchas veces participando en proyectos que no eran de mi interés. Antes de seguir con esta reflexión ¿a qué me refiero con Academia e Industria? 

Academia

Hace referencia al mundo de la enseñanza, la investigación y el desarrollo del conocimiento en instituciones educativas como universidades, institutos o centros de investigación.

Características de la academia:

  • Se centra en la generación, transmisión y validación del conocimiento.
  • Se trabaja en universidades, institutos o centros de investigación.
  • Se investiga sobre nuevas tecnologías, metodologías o modelos teóricos.
  • Se requiere una formación avanzada como maestrías o doctorados en muchos casos.
  • Puede incluir actividades de docencia, escritura de artículos científicos o supervisión de tesis.

Ejemplo: Ser profesor universitario en una facultad de informática y escribir artículos sobre inteligencia artificial en conferencias académicas.

Industria

Se refiere al ámbito profesional en el que se aplican conocimientos y habilidades para desarrollar productos, servicios o soluciones tecnológicas en empresas u organizaciones. En el caso del desarrollo de software, la industria abarca roles como programador, arquitecto de software, ingeniero de datos, DevOps, entre otros.

Características de la industria:

  • Se centra en la aplicación práctica del conocimiento para resolver problemas reales.
  • Se trabaja en empresas privadas o públicas que buscan rentabilidad, eficiencia o innovación.
  • Hay una fuerte orientación hacia plazos, clientes y requerimientos del negocio.
  • Se valora la experiencia práctica, certificaciones y habilidades técnicas aplicadas.

Ejemplo: Trabajar como desarrollador .NET en una empresa de software que crea sistemas para el sector financiero.

Con esta aclaración sobre los términos academia/industria espero dar más claridad sobre este dilema que aún me persigue...


Como comenté al principio, terminé elegiendo la industria, principalmente por motivos económicos, sin embargo la academia me sigue interesando bastante. Siento que es momento de poner un poco de orden a todo esto y combinar ambas, dando cursos o escribiendo contenido técnico. Es posible seguir una carrera combinando la academia e industria, todo es cuestión de saber organizarse. Para aquellos que quieran meterse de lleno a uno u otro, le rescomiendo ver el video Academia or Industry? How do I choose? donde recomiendan responderse a las siguientes tres preguntas:

  1. Do you often think about the applications for your research?
  2. Do you like variety in your work?
  3.  Is work-life balance important?

Mirando hacia atrás, a veces me pregunto qué habría pasado si hubiera seguido el rumbo de la academia por completo. Sin embargo, también valoro todo lo que aprendí en la industria y las oportunidades que surgieron en el camino. Al final, creo que no se trata tanto de “escoger un lado” como de escuchar la voz interior que nos impulsa a explorar. Esa voz puede orientarnos hacia la docencia, la investigación o la creación de proyectos que realmente nos apasionen, incluso si eso significa encontrar espacios para ambas cosas. Más que un dilema, es la oportunidad de construir un sendero propio donde la curiosidad, el aprendizaje continuo y el deseo de compartir conocimiento puedan coexistir con la experiencia profesional y las demandas de la vida cotidiana.

¡Gracias por llegar hasta aquí!


Sunday, January 26, 2025

OAuth2, OpenID Connect (OIDC) y JWT

Marco Teórico

OAuth2, OpenID Connect (OIDC), y JWT se combinan comúnmente porque juntos ofrecen una solución integral, segura y eficiente para autenticación y autorización en aplicaciones modernas. La razón de esta cambinación es la siguiente: 

  • Completo: OAuth2 + OIDC cubren tanto autorización como autenticación.
  • Eficiente: JWT permite transportar tokens de manera rápida y sin depender de almacenamiento adicional en el servidor.
  • Compatible: Es ampliamente utilizado en aplicaciones web, móviles y APIs debido a su facilidad de integración.

Poniéndolo en contexto con el esquema de la Seguridad Informática:


En el control de riesgos definimos las acciones, estrategias y mecanismos implementados para mitigar, minimizar o eliminar los riesgos identificados durante el análisis de riesgos. En función de la clásica combinación de estos estándares y mecanismos de seguridad para identidad y acceso (OAuth2, OIDC y JWT) podemos decir lo siguiente: 

  • Servicios de seguridad:  Requisitos generales que se necesitan para proteger la información.
    • Autenticación: Verificar que un usuario sea quien dice ser.
    • Autorización: Controlar qué recursos y acciones están permitidos para un usuario autenticado.
    • Confidencialidad: Proteger la información transmitida.
    • Integridad: Asegurar que los datos no sean manipulados en el proceso.
  • Mecanismos de seguridad: Formas concretas de implementar esos requisitos en la práctica.
    • OAuth2: Cómo se implementa la autorización mediante flujos como el Authorization Code Flow para delegar el acceso a recursos de manera segura.
    • OpenID Connect (OIDC): Cómo se implementa la autenticación mediante una extensión de OAuth2 que utiliza el ID Token para identificar al usuario.
    • JWT (JSON Web Token): Cómo se representa y transporta la información de manera segura mediante un token firmado digitalmente que asegura integridad y confidencialidad.

Implementación en .NET

Implementar OAuth2, OpenID Connect (OIDC) y JWT en un proyecto .NET implica integrar los conceptos de autenticación y autorización utilizando bibliotecas y configuraciones específicas. A continuación, vemos un ejemplo:

1. Configuración del Proveedor de Identidad

Primero necesitas un proveedor de identidad que soporte OAuth2 y OpenID Connect, como Azure AD, Auth0, Okta, Keycloak o Google.

Obtener Credenciales del Proveedor:
  • ID del Cliente (Client ID).
  • Secreto del Cliente (Client Secret).
  • URL de autorización y token (Authorization Endpoint, Token Endpoint).

2. Configuración del Proyecto en .NET

Usaremos ASP.NET Core para esta implementación.

a) Instalar paquetes necesarios:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.IdentityModel.Protocols.OpenIdConnect
dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect


b) Configurar los servicios en Program.cs o Startup.cs:
En el archivo Program.cs, añade los servicios para autenticación y validación de JWT.

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.Authority = "https://<your-provider-domain>"; // URL del proveedor OIDC (p.ej., Auth0, Azure AD)
    options.Audience = "<your-audience>"; // Identificador del API configurado en el proveedor
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true
    };
});


c) Habilitar autenticación en el pipeline de la aplicación:

Agrega el middleware de autenticación en el pipeline:

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.Run();


3. Proteger Endpoints en la API

Utiliza el atributo [Authorize] en los controladores o acciones específicas que requieren autenticación.

[ApiController]
[Route("api/[controller]")]
[Authorize] // Protege este endpoint
public class SecureController : ControllerBase
{
    [HttpGet]
    public IActionResult GetSecureData()
    {
        return Ok("This is a secure endpoint");
    }
}

4. Flujo de Autenticación y Autorización

  • El cliente (como una SPA o aplicación móvil) redirige al usuario al endpoint de autorización del proveedor.
  • Una vez autenticado, el proveedor devuelve:
    • Un ID Token (si usas OIDC) en formato JWT con información sobre el usuario.
    • Un Access Token (OAuth2) en formato JWT para acceder a los recursos de la API.
  • El cliente incluye el Access Token en las peticiones a la API:
Authorization: Bearer <access_token>
  • La API valida el token utilizando el middleware configurado.

5. Validar el Token JWT

El middleware de ASP.NET Core se encarga de la validación automáticamente. Sin embargo, si necesitas validarlo manualmente, puedes usar la biblioteca System.IdentityModel.Tokens.Jwt:

var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken("<access_token>");
var claims = token.Claims; // Obtener claims del token


6. Flujo Adicional: OpenID Connect para Login

Si estás desarrollando una aplicación que necesita autenticación de usuario, puedes usar OpenIdConnect:

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
    options.Authority = "https://<your-provider-domain>";
    options.ClientId = "<your-client-id>";
    options.ClientSecret = "<your-client-secret>";
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
});

Esto permite iniciar sesión mediante el proveedor configurado.


Conclusión

La combinación de OAuth2, OIDC y JWT es muy popular porque equilibra seguridad, escalabilidad y compatibilidad con aplicaciones modernas. Sin embargo, dependiendo del caso de uso, puedes optar por tecnologías más simples (como API Keys o sesiones) o más avanzadas (como autenticación biométrica o blockchain).
  • Token-Based Authentication con Simple Tokens (Sin JWT): Utiliza tokens simples (generalmente UUIDs o cadenas aleatorias) en lugar de JWT. Estos tokens se almacenan en una base de datos en el servidor para validarlos. Ejemplo: Sesiones tradicionales usando cookies con un token de sesión generado.
  • API Key Authentication: Las aplicaciones utilizan claves API para autenticar solicitudes. Estas claves son secretas y se pasan en cada solicitud (por ejemplo, en la cabecera HTTP). Ejemplo: Uso de claves API en servicios como Google Maps o servicios REST.
  • LDAP (Lightweight Directory Access Protocol): Utilizado principalmente en entornos empresariales para autenticar usuarios a través de un directorio centralizado. Ejemplo: Aplicaciones internas en organizaciones que usan Active Directory.
  • SAML (Security Assertion Markup Language): Un estándar basado en XML utilizado para autenticación y autorización en aplicaciones empresariales. Ejemplo: Integraciones con servicios empresariales como Salesforce o aplicaciones on-premise.
  • Session-Based Authentication: Una técnica tradicional donde el servidor genera una sesión para el usuario después de autenticarse, y el cliente almacena un identificador de sesión (generalmente en una cookie). Ejemplo: Aplicaciones web que no utilizan APIs modernas.
  • Biometric Authentication: Utiliza características biométricas como huellas digitales, reconocimiento facial o escaneo de retina para autenticar al usuario. Ejemplo: Aplicaciones móviles con autenticación por huella digital (ej.: Face ID, Touch ID).
  • Passwordless Authentication: Elimina el uso de contraseñas y utiliza métodos alternativos como enlaces mágicos enviados por correo electrónico, códigos de verificación, o autenticación con dispositivos confiables. Ejemplo: Aplicaciones que envían un enlace para iniciar sesión (como Slack o Notion).
  • Blockchain-Based Authentication: Utiliza tecnologías de blockchain para verificar identidades descentralizadas y gestionar autorizaciones. Ejemplo: Identidades descentralizadas con proyectos como uPort o Sovrin.

¡Gracias por llegar hasta aquí!

Thursday, October 10, 2024

¿Por qué es tan demandado el perfil de Full Stack Developer?


El perfil de Full-Stack Developer ha ganado una gran popularidad en los últimos años, y con justa razón. Las empresas valoran a los desarrolladores que tienen la capacidad de gestionar tanto el front-end (interfaz de usuario) como el back-end (lógica del servidor y bases de datos). Sin embargo, aunque este perfil es altamente demandado, también conlleva ciertos desafíos que conviene analizar. A continuación, exploramos tanto las ventajas que hacen tan atractivo este rol, como las desventajas que pueden surgir al desempeñarlo.

Ventajas

  • Versatilidad y Flexibilidad: La habilidad de un Full-Stack Developer para trabajar en ambas partes de una aplicación lo convierte en un profesional sumamente versátil. Puede moverse fácilmente entre el desarrollo de la interfaz de usuario y la lógica del servidor, lo que lo hace adecuado para una amplia gama de proyectos. En un mercado que demanda adaptabilidad, esta es una ventaja crucial que posiciona al desarrollador Full-Stack como una pieza valiosa en cualquier equipo.
  • Reducción de Costos para las Empresas: Contratar a un solo desarrollador Full-Stack, en lugar de varios especialistas en front-end y back-end, puede representar una importante reducción de costos para las empresas, especialmente las startups o pequeñas empresas que no pueden permitirse equipos grandes. El Full-Stack Developer puede abordar todas las capas de una aplicación, reduciendo la necesidad de múltiples contrataciones y facilitando la coordinación de tareas.
  • Visión Integral del Proyecto: Gracias a su conocimiento de todas las capas del desarrollo, un Full-Stack Developer tiene una visión completa de cómo funciona una aplicación. Esto le permite tomar decisiones informadas sobre la arquitectura y el diseño del sistema, lo que facilita la creación de soluciones más coherentes y optimizadas. Además, puede identificar y solucionar problemas más rápidamente, ya que comprende tanto la interacción entre el front-end y el back-end como los puntos críticos de cada área.
  • Adaptabilidad a Metodologías Ágiles: Los Full-Stack Developers encajan bien en equipos que siguen metodologías ágiles, donde la flexibilidad y la rapidez en la entrega de resultados son clave. Al ser capaces de abordar distintas tareas y cambiar de rol dentro del ciclo de vida del desarrollo, contribuyen a mantener los proyectos ágiles y en constante progreso.
  • Mayor Autonomía y Responsabilidad: Los Full-Stack Developers tienen la capacidad de llevar proyectos desde la concepción hasta la entrega final. Esta autonomía les permite asumir mayor responsabilidad en las decisiones técnicas y estratégicas. Además, su perfil es ideal para liderar equipos pequeños o startups, donde se necesitan desarrolladores multifacéticos que puedan enfrentar diversos desafíos sin depender de otros especialistas.

Desventajas

  • Riesgo de Superficialidad: Aunque un Full-Stack Developer domina múltiples áreas del desarrollo, existe el riesgo de que su conocimiento sea superficial en comparación con un especialista que se enfoque solo en el front-end o el back-end. En proyectos que requieren un alto grado de especialización en una tecnología específica (por ejemplo, optimización avanzada de bases de datos o interfaces gráficas muy complejas), un Full-Stack Developer podría enfrentar dificultades para profundizar en áreas especializadas.
  • Sobrecarga de Tareas: Debido a que los Full-Stack Developers manejan varias partes del proyecto, a menudo se ven abrumados por la cantidad de responsabilidades que asumen. Esta sobrecarga puede llevar a tiempos de entrega más largos o una calidad de código inferior si no se gestionan correctamente las expectativas y los plazos. Para algunos desarrolladores, el hecho de estar constantemente cambiando entre front-end y back-end puede ser agotador y afectar la productividad a largo plazo.
  • Dificultad para Mantenerse al Día con las Tecnologías: El desarrollo tecnológico avanza rápidamente, tanto en el front-end como en el back-end, lo que exige a los Full-Stack Developers mantenerse actualizados en una amplia gama de tecnologías. Esto puede ser un reto, ya que es difícil estar al día con todas las innovaciones en ambas áreas. Por ejemplo, mientras que un especialista front-end puede estar completamente actualizado con los últimos cambios en Angular o React, un Full-Stack Developer tiene que dividir su tiempo entre esas tecnologías y las novedades en bases de datos, servidores, o frameworks de back-end.
  • Falta de Profundización en Roles Complejos: En entornos de desarrollo más complejos, como grandes corporaciones o proyectos que requieren una infraestructura muy robusta, puede que el perfil Full-Stack no sea suficiente. A menudo, estas organizaciones necesitan especialistas que tengan un conocimiento profundo en áreas críticas como la escalabilidad de bases de datos, la optimización de servidores o la experiencia de usuario avanzada, donde los Full-Stack Developers pueden quedar rezagados frente a los especialistas.
  • Expectativas altas por parte de las empresas: Aunque las empresas valoran la versatilidad de un Full-Stack Developer, esto a menudo viene con expectativas elevadas. A veces se espera que estos desarrolladores sean expertos en todas las áreas del desarrollo, lo que puede ser irrealista. Si bien es posible que tengan un buen nivel de competencia en diversas tecnologías, es poco probable que puedan igualar el nivel de profundidad que un especialista puede ofrecer en un área específica. Esto puede generar presión y estrés, ya que el desarrollador podría sentir que nunca alcanza las expectativas del empleador.