Blog / La Universidad No Te Enseña a Programar (Y Eso Está Bien)
7 de abril de 2026
csingenieria-de-softwareopinion

La Universidad No Te Enseña a Programar (Y Eso Está Bien)

Pasé años creyendo que la teoría y la industria eran mundos distintos. Me equivoqué. Esto es lo que la carrera de Ciencias de la Computación me enseñó sin que yo me diera cuenta.

La Universidad No Te Enseña a Programar (Y Eso Está Bien)

Cuando entré a la carrera de Ciencias de la Computación, esperaba aprender a construir cosas. En cambio, me encontré demostrando propiedades matemáticas a mano, resolviendo fórmulas de recurrencia y escribiendo analizadores de lenguaje con símbolos que parecían sacados de un libro de lógica filosófica. No había interfaces de usuario. No había servidores web. No había nada “impresionante” que mostrarle a nadie en una reunión. Todo ocurría en la terminal, en el pizarrón, o en hojas de examen que nunca iba a volver a ver.

No lo voy a romantizar: me aburrí. O más bien, no veía el puente. Mientras mis profesores me pedían demostrar propiedades formales de gramáticas y lenguajes, yo me quedaba despierto hasta las dos de la mañana aprendiendo React, construyendo APIs, explorando Flutter. Aprendí a ganar dinero como desarrollador mucho antes de graduarme, y por un tiempo creí que la universidad y la industria eran dos mundos completamente distintos que simplemente coexistían en mi calendario.

Mis profesores me lo decían. Recuerdo sus palabras con una claridad que en ese momento no merecían: “esto que están aprendiendo no es para resolver ejercicios, es para pensar”. Lo escuchaba, asentía, y volvía a mis proyectos personales convencido de que era una justificación elegante para enseñar cosas que nadie usaba en el mundo real. Les debo una disculpa, y también un agradecimiento enorme. Tenían razón. Solo que la razón de ese tipo tarda años en volverse evidente.

Estaba equivocado. Los cimientos que me puso la carrera están presentes en todo lo que construyo hoy. Solo que no de la manera obvia. Este artículo es sobre esas conexiones invisibles: las que no te enseñan en ningún curso rápido de programación, y que marcan la diferencia entre alguien que escribe código y alguien que diseña sistemas.

El Problema Real No Es Técnico, Es de Pensamiento

Hay una diferencia enorme entre saber usar una herramienta y entender el problema que esa herramienta resuelve. Durante años trabajé con bases de datos relacionales (PostgreSQL, MySQL) sin pensar demasiado en por qué estaban diseñadas de esa manera. Simplemente las usaba: creaba tablas, escribía consultas, resolvía el problema inmediato.

El cambio llegó cuando tuve que diseñar desde cero el modelo de datos para un motor de promociones. Un motor de promociones es un sistema que gestiona reglas de descuento: qué productos aplican, bajo qué condiciones, con qué límites de uso, durante qué periodos. También necesita registrar el historial de aplicación de cada promoción para evitar usos duplicados o fraudulentos, y tiene que ser lo suficientemente flexible para que el negocio pueda crear nuevos tipos de descuento sin que el equipo de ingeniería tenga que reescribir todo cada vez.

A primera vista parece simple. En la práctica, es un problema de modelado complejo donde las decisiones que tomas al principio determinan si el sistema va a poder crecer o si vas a estar reescribiéndolo en seis meses.

Fue ahí cuando entendí, de golpe, para qué habían servido esos exámenes donde teníamos que demostrar a mano que una base de datos estaba bien estructurada. En la academia eso se llama “normalización”, que es el proceso de organizar los datos para eliminar redundancias y evitar inconsistencias. No es magia: es álgebra. Es razonar sobre dependencias entre datos y garantizar que cuando algo cambia, no tienes que actualizar la misma información en veinte lugares distintos.

En el motor de promociones eso se traduce en decisiones muy concretas. Si una promoción puede aplicar a múltiples productos y un producto puede pertenecer a múltiples promociones, esa relación tiene que modelarse de una manera específica o terminas con datos duplicados que eventualmente se contradicen entre sí. Si el historial de uso almacena una copia de los datos de la promoción en lugar de una referencia a ella, cualquier cambio en la promoción original genera inconsistencias en el historial. Si las condiciones de aplicación están codificadas directamente en la estructura de la tabla en lugar de ser configurables, agregar un nuevo tipo de condición requiere modificar el esquema completo de la base de datos.

Cada una de esas decisiones tiene una respuesta correcta que se deriva de la teoría formal. No de “buenas prácticas” aprendidas de un tutorial, sino de razonamiento matemático sobre dependencias entre datos. Eso lo aprendí a mano, en papel, demostrando propiedades que en su momento me parecían completamente desconectadas de cualquier aplicación práctica.

Cuando tu Sistema es un Mapa

Una de las ideas más poderosas que me dejó la carrera, y que tardé años en reconocer como tal, viene de una rama de las matemáticas llamada “teoría de grafos”. Un grafo, en términos simples, es un conjunto de nodos conectados por aristas, como un mapa donde los puntos son ciudades y las líneas son rutas entre ellas.

Parece abstracto. Pero resulta que casi cualquier sistema de software complejo es un grafo si lo miras de la manera correcta. Los servicios de una aplicación son nodos. Las dependencias entre ellos (quién llama a quién, quién necesita que quién esté disponible para funcionar) son las aristas. Y las propiedades matemáticas de esos grafos tienen consecuencias directas en cómo se comporta tu sistema en producción.

Una propiedad en particular es fundamental: los grafos que no tienen ciclos (es decir, donde no puedes partir de un punto y volver al mismo siguiendo las conexiones hacia adelante) tienen propiedades de ordenamiento que los hacen predecibles y manejables. En la academia se llaman DAGs (del inglés “Directed Acyclic Graphs”, o grafos dirigidos acíclicos). En arquitectura de software, la regla práctica es la misma: si tus servicios tienen dependencias circulares (A depende de B, B depende de C, C depende de A) tu sistema se vuelve frágil, difícil de desplegar en orden correcto, y un verdadero problema para diagnosticar cuando algo falla.

Más allá de las dependencias entre servicios, la teoría de grafos aparece en contextos sorprendentes. El proceso de construir y desplegar software moderno (compilar código, resolver dependencias entre bibliotecas, determinar en qué orden ejecutar las tareas de un pipeline de integración continua) es esencialmente un problema de ordenamiento topológico sobre un grafo dirigido acíclico. Los algoritmos que aprendemos en la universidad para recorrer grafos (búsqueda en profundidad, búsqueda en anchura, algoritmos de camino más corto) aparecen bajo distintos disfraces en sistemas de enrutamiento, motores de recomendación, análisis de redes sociales y resolución de conflictos de dependencias.

Hoy, cuando diseño una arquitectura, mentalmente estoy trazando ese mapa y verificando sus propiedades. No porque recuerde la clase específica, sino porque esa manera de pensar quedó incorporada como un instinto.

Diseñar Sistemas que Reaccionan a lo que Ocurre

Gran parte de mi trabajo en los últimos años ha girado alrededor de lo que en la industria se llama “arquitecturas orientadas a eventos”. En lugar de que los componentes de un sistema se comuniquen directamente (el servicio A le dice al servicio B “ejecuta esta operación ahora”), cada componente simplemente publica registros de cosas que ocurrieron (“se creó una orden”, “un usuario completó su perfil”, “una promoción fue aplicada”) y cualquier otro componente interesado en esos eventos reacciona de manera independiente.

Este modelo tiene propiedades muy valiosas. Los servicios están desacoplados: puedes agregar nuevos componentes que reaccionen a los mismos eventos sin modificar los existentes. Tienes un historial natural de todo lo que ocurrió en el sistema, lo que facilita auditoría, debugging y reconstrucción de estados. Y el sistema puede escalar más fácilmente porque los componentes no se bloquean esperando respuestas de otros.

Pero también introduce complejidad que no es obvia hasta que tienes que razonar formalmente sobre ella. ¿En qué orden deben procesarse los eventos? ¿Qué garantías de consistencia ofrece el sistema cuando múltiples servicios reaccionan al mismo evento de manera simultánea? ¿Cómo aseguras que un evento procesado dos veces (por un reintento de red, por ejemplo) no genera efectos duplicados?

Responder esas preguntas requiere exactamente el tipo de pensamiento que entrena la teoría de compiladores y la lógica matemática. La estructura de un evento (su tipo, sus campos, sus restricciones de valores válidos) es en esencia una gramática: un conjunto de reglas que define qué formas son válidas y cuáles no. El sistema que valida y enruta eventos según su tipo es un parser, un componente que analiza estructuras y toma decisiones basadas en ellas. Los contratos entre servicios (lo que uno promete publicar y lo que el otro espera recibir) son especificaciones formales de un lenguaje compartido.

Cuando diseño estos sistemas, aplico ese razonamiento casi sin notarlo. Defino los tipos de eventos con precisión. Establezco invariantes (condiciones que deben mantenerse verdaderas sin importar en qué orden lleguen los eventos). Razono sobre qué propiedades del sistema deben garantizarse siempre y cuáles pueden ser eventuales. Todo eso es lógica formal aplicada, aunque rara vez lo llame por ese nombre.

El Superpower Silencioso: Entender los Números

Hay una habilidad que separa a los ingenieros que realmente entienden sus sistemas de los que simplemente los operan: saber leer métricas. Y no me refiero a saber navegar un dashboard, sino a entender qué significa estadísticamente lo que estás viendo.

La industria está llena de ingenieros que miran el tiempo de respuesta promedio de su API y concluyen que “todo está bien”. El problema es que el promedio es una de las métricas más engañosas que existen cuando hay variabilidad en los datos. Si noventa personas esperan un segundo y diez esperan treinta segundos, el promedio te dice que el tiempo de respuesta es “cuatro segundos”, lo cual no describe la experiencia de nadie en particular. Los percentiles, en cambio (“el 95% de las solicitudes se responde en menos de X tiempo”) cuentan una historia mucho más honesta sobre cómo se comporta el sistema para la mayoría de los usuarios, y también para los que tienen la peor experiencia.

Eso es estadística aplicada. Entender distribuciones, varianza, percentiles, correlaciones y sus limitaciones es la diferencia entre detectar un problema antes de que afecte a los usuarios o enterarte por los reclamos. También es la diferencia entre hacer “capacity planning” real (estimar cuántos recursos va a necesitar un sistema bajo distintos escenarios de carga) y simplemente adivinar con números que suenan razonables.

El curso de probabilidad y estadística es, en la experiencia de muchos estudiantes de computación, el que se siente más alejado de “programar”. En la práctica, es uno de los más útiles para operar sistemas reales a escala. Los ingenieros que saben estadística ven cosas en los datos que los demás no ven.

El Costo Oculto de No Entender la Máquina

Hay un nivel de comprensión que ningún framework te da: entender qué está pasando debajo de las abstracciones. La arquitectura de computadoras (cómo funciona la memoria, cómo el procesador ejecuta instrucciones, cómo se organiza la jerarquía de caché) parece completamente irrelevante cuando escribes una API web. Y en muchos casos lo es. Hasta que no lo es.

La memoria caché del procesador, por ejemplo, funciona mejor cuando los datos que se acceden juntos están almacenados cerca en memoria. Eso parece un detalle de hardware, pero tiene implicaciones directas en cómo estructuras tus datos en código. Un sistema que procesa millones de eventos por segundo se comporta de manera radicalmente distinta dependiendo de si los datos que necesita para cada operación están en caché o tienen que buscarse en memoria principal, una operación que puede ser cien veces más lenta.

Los ingenieros que nunca entendieron lo que está debajo de la abstracción son los que escriben código “correcto” que misteriosamente se degrada bajo carga. La máquina no miente, y haber estudiado su arquitectura te da el instinto para saber cuándo una decisión de diseño va a tener consecuencias de rendimiento, antes de que el sistema en producción te lo demuestre de la peor manera.

Lo que la Universidad Realmente Te Enseña

Mirando hacia atrás, la carrera no me enseñó a construir software. Me enseñó a pensar en estructuras, en propiedades que se preservan o se rompen bajo determinadas condiciones, en invariantes que deben mantenerse verdaderas sin importar qué pase. Me enseñó a razonar con precisión sobre cosas complejas, a descomponer problemas en sus partes fundamentales, a buscar la propiedad matemática detrás del síntoma visible.

Eso es exactamente lo que haces cuando diseñas sistemas de software a escala. Trabajas con algo demasiado complejo para tenerlo en la cabeza al mismo tiempo, y necesitas modelos mentales sólidos para garantizar que lo que construyes va a funcionar cuando las cosas se compliquen, cuando el tráfico suba, cuando un componente falle, cuando los requisitos cambien y el sistema tenga que evolucionar sin romperse.

A mis profesores, que me repetían pacientemente que el valor estaba en aprender a pensar y no en memorizar soluciones: tenían razón. En ese momento yo era demasiado impaciente para entenderlo. La industria recompensa rápido la habilidad de construir cosas visibles, y eso hace que sea muy fácil subestimar el valor de lo que no se ve. Pero con los años, la diferencia entre los ingenieros que simplemente saben usar herramientas y los que entienden los principios detrás de ellas se vuelve muy evidente.

Si estás estudiando y sientes que la teoría no tiene nada que ver con la industria, te entiendo perfectamente. Yo lo sentí igual. Pero hay una diferencia importante entre “no veo la aplicación ahora” y “esto no tiene aplicación”. Con la experiencia, los puentes aparecen, y cuando aparecen, lo hacen de golpe, en medio de un problema real, y agradeces haber pasado por esos exámenes que en su momento te parecían una pérdida de tiempo.

El conocimiento teórico no te da respuestas directas. Te da algo más valioso: la capacidad de formular las preguntas correctas. Y en ingeniería de software, formular la pregunta correcta es, con frecuencia, la mejor parte del trabajo.