En ingeniería de software existe un compromiso difícil de resolver: no se pueden mantener a la vez la concurrencia, la interactividad y la mutabilidad de los datos sin pagar un precio considerable. Cada una de estas propiedades resuelve un problema real. La concurrencia permite ejecutar varios hilos de progreso en la misma unidad de tiempo, lo que acelera el procesamiento. La interactividad facilita inspeccionar y modificar el estado de un programa en tiempo de ejecución, algo muy útil para depurar sistemas complejos. La mutabilidad, por su parte, hace que modificar datos sea directo y práctico. Sin embargo, combinarlas genera conflictos: un cambio interactivo en una estructura de datos compartida puede provocar condiciones de carrera, corrupción de memoria o bloqueos cuando otro hilo accede a la misma estructura. El artículo analiza las tres estrategias que adoptan los lenguajes de programación para resolver este dilema. La más extendida es renunciar a la interactividad, como hacen C, Rust o Go: el programa se compila y se ejecuta sin permitir accesos interactivos al estado interno, lo que elimina la mayoría de los problemas de concurrencia pero dificulta la depuración en vivo. La segunda opción es sacrificar la concurrencia efectiva: lenguajes como Python o Ruby recurren al GIL (Global Interpreter Lock), un cerrojo global que serializa los accesos a los datos. Esto preserva la interactividad, pero impone una penalización de rendimiento notable. La tercera vía es abandonar la mutabilidad, siguiendo el modelo de Erlang: los procesos se comunican intercambiando copias inmutables de los valores. Esto evita condiciones de carrera y permite interactuar con el sistema, aunque la copia sistemática de datos resulta costosa. Common Lisp presume de ofrecer las tres propiedades, pero cualquier modificación hecha desde el REPL puede desestabilizar el programa si otro hilo accede simultáneamente a los mismos datos. La conclusión es clara: elegir un lenguaje implica elegir un conjunto de renuncias.
