🧪 Problem-Driven Systems Lab

🧬 Perfiles de lenguaje#

Qué es cada runtime, para qué sirve, qué primitivas usa el laboratorio, qué no puede resolver, y qué hay que revisar cuando publique una versión nueva.


🎯 Por qué existe esta carpeta#

El laboratorio resuelve los mismos 20 problemas en 7 lenguajes. Eso obliga a una pregunta que un repositorio de un solo stack nunca se hace:

¿La primitiva que este caso enseña sigue siendo la forma idiomática actual, o el lenguaje ya incorporó algo que la reemplaza?

Un perfil por lenguaje es donde esa pregunta tiene respuesta escrita. Sin esto, cuando Java 25 saque ScopedValue de preview, el caso 03 seguiría enseñando ThreadLocal como si nada hubiera pasado — y nadie sabría dónde mirar.

Cada perfil documenta seis cosas:

SecciónQué responde
🪪 IdentidadQué es el lenguaje y para qué se usa fuera de este laboratorio
⚙️ Modelo de ejecuciónCómo corre el código, porque de ahí sale qué primitiva es la correcta
🧰 Primitivas en el labQué usa cada uno de los 20 casos, con enlace al código
📈 RendimientoQué mide el laboratorio en este stack y cómo reproducirlo
🚧 Límites y problemas sin soluciónLo que este runtime no puede hacer, y qué caso lo deja visible
🔄 Ciclo de versionesVersión fijada, cadencia upstream y qué revisar en el próximo salto

🗺️ Los siete perfiles#

StackVersión fijadaModelo de ejecuciónPerfil
🐘 PHP8.3Proceso por petición, sin estado compartidophp.md
🐍 Python3.12Threads reales con GILpython.md
🟢 Node.js22Event loop de un solo hilonode.md
Java21Threads del SO, paralelismo real, JVMjava.md
🔵 .NET8.0ThreadPool con async/awaitdotnet.md
🐹 Go1.23Goroutines multiplexadas por el runtimego.md
🦀 Rust1.83Threads del SO sin GC, Drop deterministarust.md

La versión fijada no se escribe a mano en esta tabla ni en los perfiles: sale de shared/catalog/cases.json y scripts/check-language-versions.sh falla el PR si algún Dockerfile dice otra cosa.


⚙️ Un problema, siete modelos de ejecución#

Modelos de ejecución comparados por stack

El modelo de ejecución no es trivia: es lo que decide qué primitiva es correcta. El caso 11 —reportes pesados que bloquean la operación— lo deja claro:

  • En Java y .NET el problema es el pool de threads, así que la solución es separar pools y observar getActiveCount().
  • En Go no hay pool que agotar, así que la solución es un semáforo de concurrencia con un canal.
  • En Node.js el trabajo CPU-bound bloquea el proceso entero, así que la única salida real es worker_threads.
  • En PHP los procesos FPM están aislados: no hay nada que aislar, y tampoco nada que observar desde adentro.

Cuatro soluciones distintas, un solo problema. Eso es lo que este laboratorio intenta hacer visible.


🏆 Dónde gana cada lenguaje (evidencia, no opinión)#

Ranking de fit por caso y stack

Este mapa de calor no se escribe a mano: scripts/generate_diagrams.py lo deriva de la sección Veredicto de los once comparison.md que la tienen. Si mañana un veredicto cambia, el diagrama cambia con él.

Cómo leerlo, y cómo no leerlo:

⚠️ Es un ranking de fit con el problema, no de calidad de lenguaje. Mide qué tan directamente las primitivas nativas del runtime expresan la solución de ese caso concreto. El orden cambia — a veces se invierte — de un caso a otro.

Tres lecturas que el agregado deja a la vista:

  • Go promedia mejor que Rust (1.7 vs 1.9) pero Rust gana más casos (6 oros vs 4). Go es consistentemente bueno; Rust es excepcional donde el sistema de tipos aporta, y flojo donde no (caso 04: mpsc::recv_timeout tiene la misma limitación que Java — corta la espera, no el trabajo).
  • Java y .NET empatan en 3.3 y ganan exactamente el mismo caso: el 11, donde el problema es el pool de threads. Cuando el problema es el pool, tener pool explícito deja de ser ceremonia y pasa a ser la herramienta.
  • PHP promedia último (5.5) y aun así es 🥉 en el caso 02, porque es el único stack donde el N+1 cruza un socket real contra PostgreSQL. El promedio esconde el caso donde el stack "peor rankeado" es el que produce la mejor evidencia.

Si un solo lenguaje ganara todos los casos, el laboratorio no tendría nada que enseñar.


🔄 Cuando un lenguaje publica versión nueva#

Protocolo de actualización por versión de lenguaje

La detección es automática y semanal; la decisión es humana y queda escrita. El procedimiento completo —qué revisar, en qué orden, y cómo cerrar el issue cuando la respuesta es "no aplica"— está en docs/language-upgrade-protocol.md.

Cada perfil termina con una sección 🔄 Ciclo de versiones que dice, para ese lenguaje, qué está en juego en el próximo salto.


📚 Documentación relacionada#

DocumentoQué agrega sobre esto
docs/language-upgrade-protocol.mdEl procedimiento de actualización, paso a paso
docs/stack-map.mdPor qué hay múltiples lenguajes y qué se estudia al comparar
docs/case-catalog.mdLos 20 casos con su estado real y stacks operativos
docs/case-methodology.mdCómo se construye un caso antes de escribir código
cases/NN-*/comparison.mdLa comparativa de los 7 stacks para un caso concreto

Ver esta carpeta en GitHub ↗