Una Intermediate Representation (IR) es una representación de un programa entre el código fuente de alto nivel y el código máquina de bajo nivel. Su propósito principal es desacoplar las fases de frontend (análisis léxico, sintáctico y semántico) y backend (generación de código y optimización) de un compilador. La IR abstrae los detalles específicos del lenguaje fuente y de la arquitectura de destino, permitiendo que las optimizaciones se realicen de manera independiente y que un mismo frontend pueda generar código para múltiples backends, o viceversa. Las IR pueden variar en su nivel de abstracción, desde representaciones de alto nivel cercanas al código fuente (como árboles de sintaxis abstracta - AST) hasta representaciones de bajo nivel cercanas al código máquina (como Three-Address Code o Static Single Assignment - SSA form).

La implementación de IRs es fundamental en el mundo de los compiladores y runtimes modernos. Ejemplos prominentes incluyen LLVM IR, que es la base del compilador LLVM y es utilizada por lenguajes como C++, Rust, Swift y Kotlin/Native para optimizar y generar código para diversas arquitecturas. GraalVM, una máquina virtual universal, utiliza una IR propia para compilar código Java y otros lenguajes a código máquina de alto rendimiento. WebAssembly (Wasm) puede considerarse una forma de IR binaria de bajo nivel, diseñada para ser un objetivo de compilación portátil para la web. Las bases de datos distribuidas y motores de procesamiento de datos como Apache Spark también utilizan IRs internas para representar planes de consulta, permitiendo optimizaciones antes de la ejecución distribuida.

Para un Arquitecto de Sistemas, comprender las IRs es crucial para diseñar plataformas que requieran flexibilidad en la ejecución de código, optimización de rendimiento o interoperabilidad entre lenguajes. La elección o diseño de una IR impacta directamente en la capacidad de extender un sistema a nuevos lenguajes o arquitecturas (portabilidad), la facilidad de implementar optimizaciones complejas (rendimiento) y la complejidad del compilador o runtime. Un arquitecto debe considerar el nivel de abstracción de la IR: una IR de alto nivel facilita el análisis semántico y las optimizaciones de alto nivel, mientras que una IR de bajo nivel permite un control más fino sobre la generación de código máquina. La adopción de una IR bien establecida como LLVM IR puede reducir significativamente el tiempo de desarrollo y aprovechar un ecosistema de optimizaciones ya existente, mientras que el diseño de una IR personalizada ofrece control total pero conlleva una mayor carga de mantenimiento y desarrollo.