sábado, 27 de diciembre de 2025

Ballerina: una comparativa con Rust

Como mencionamos en la entrega anterior, Ballerina es un lenguaje de programación en crecimiento. No posee una comunidad tan grande como otros lenguajes (Java, C#, C/C++, Python, etc.), pero así empiezan todos. Ballerina junto otros lenguajes como Go y Rust han cobrado cierto interés en el mundo de la programación, servicios Cloud, servicios REST y hasta la I.A.

En esta ocasión haremos una comparativa con Rust, un lenguaje de programación que tiene inspiración en lenguajes como C y C++, tomando lo mejor de ellos y descartando lo que no es necesario y Ballerina, un lenguaje que ha cobrado importancia en el desarrollo en la nube. Ambos lenguajes han aprendido de los mejores e incluso han ido superándolos incluso en velocidad y rendimiento.

Ballerina y Rust son lenguajes muy distintos: Ballerina está orientado a integración y microservicios, mientras que Rust se centra en sistemas de alto rendimiento y seguridad de memoria.

Veamos una tabla comparativa:

Comparativa: Ballerina vs Rust
Aspecto Ballerina Rust
Paradigmas Multiparadigma con foco en integración, concurrencia y servicios distribuidos; código con visualización de flujos y llamadas. Multiparadigma (imperativo y funcional) orientado a sistemas, control de bajo nivel y seguridad de memoria.
Casos de uso Integración de APIs, microservicios, orquestación de endpoints, aplicaciones cloud-native y seguridad en comunicaciones. Sistemas operativos, motores de juegos, programación embebida, CLI y servicios de alto rendimiento.
Compilación y ejecución Compila con bal build; ejecuta con bal run archivo.bal. Soporte integrado para Docker/Kubernetes. Compila con rustc archivo.rs; ejecuta con ./archivo. En proyectos se usa Cargo (cargo build, cargo run).
Tipos de variables Tipado estático: int, float, boolean, string; estructurados: record, map, array, table. Tipado estático con inferencia: primitivos (i32, f64, bool, char) y avanzados (struct, enum, tuple).
Estructuras de control if, while, foreach, match; concurrencia con workers y fork/join. if, loop, while, for, match con patrones; concurrencia segura con async/await y ownership.
Estructuras de datos record (tipo estructurado), map, array, table. struct, enum, tuple, array, Vec<T>, HashMap<K,V>.
Concurrencia Modelo de trabajadores, transacciones y flujos de integración; primitivas para servicios distribuidos. Seguridad de datos en concurrencia mediante ownership, borrowing y tipos como Arc/Mutex.
Ecosistema Enfocado en integración (HTTP, gRPC, GraphQL, Kafka) con librerías estándar orientadas a servicios. Amplio ecosistema de crates para sistemas, redes, WebAssembly, embebidos y desarrollo multiplataforma.
Curva de aprendizaje Suave para integraciones y microservicios; diseño opinado para flujos de red. Pronunciada por el ownership y el borrow checker; recompensa con rendimiento y seguridad.

Puntos clave entre Ballerina y Rust

Ambos lenguajes tienen similitudes y diferencias, aquí los puntos claves:

  • Enfoque distinto: Ballerina es un lenguaje de integración; Rust es un lenguaje de sistemas. 
  • Compilación: Rust ofrece control de bajo nivel y optimización; Ballerina simplifica despliegues en entornos distribuidos. 
  • Datos y control: Rust tiene estructuras más ricas y flexibles; Ballerina prioriza simplicidad y seguridad en datos de red. 
  • Concurrencia: Ambos soportan concurrencia, pero Rust lo hace con un modelo de ownership y Ballerina con workers y diagramas visuales.

Ejemplo 1. Crear un sencillo "Hola, mundo" en Ballerina y Rust. Esto nos servirá para ver cómo se compila y ejecuta un programa en cada lenguaje.

Empecemos con Ballerina. Compilaremos y ejecutaremos un archivo llamado holamundo.bal:

import ballerina/io;

public function main(string... args) {
    io:println("\t Hola, mundo en Ballerina");
}

Compilamos y ejecutamos:

$ bal run holamundo.bal

El código se compilará y se ejecutará en la terminal, la salida será:

Compiling source
        holamundo.bal

Running executable

         Hola, mundo en Ballerina

Ahora con Rust, compilaremos y ejecutaremos el archivo holamundo.rs:

fn main() {
    println!("\t Hola mundo en Rust");
}

Compilamos:

$ rustc holamundo.rs

Se creará un ejecutable:

$ holamundo.exe

Salida:

         Hola mundo en Rust

¿Qué podemos notar?

  • Ambos lenguajes son compilados. 
  • Ballerina es más similar Java, es dependiente de la JVM y genera bytecodes
  • Rust es más similar a C/C++, genera ejecutables nativos (*exe). 
  • La extensión de un programa Ballerina es *bal.
  • La extensión de un programa Rust es *rs.

Tipos de datos en Ballerina y Rust

Como mencioanmos, ambos lenguajes son compilados. También comparten la característica de ser de tipado estático. Eso quiere decir que debes especificar el tipo de datos a usar. No como lenguajes como Python o Ruby donde "una variable es una variable".

Tipos de datos en Ballerina vs Rust
Categoría Ballerina Rust
Primitivos numéricos int, float, decimal i8, i16, i32, i64, u8, u16, u32, u64, f32, f64
Booleanos boolean bool
Texto y caracteres string char, String (tipo de colección)
Compuestos record, map, array, table struct, enum, tuple, array, Vec<T>, HashMap<K,V>
Especiales nil (valor nulo), error, any Option<T>, Result<T,E>, unit (())
Concurrencia Workers y canales integrados para paso de mensajes Tipos seguros para concurrencia: Arc, Mutex, RwLock

Ejemplo 2. Crear un programa en ambos lenguajes con el objetivo de ver los tipos de datos en cada lenguaje.

En Ballerina:

tipos.bal

import ballerina/io;

type Persona record {|
    string nombre;
    int edad;
|};

enum Estado {
    ACTIVO,
    INACTIVO
}

public function main() {
    int edad = 30;
    float altura = 1.75;
    boolean activo = true;
    string inicial = "C"; // no existe 'char'
    string nombre = "Thomas Muller.";

    int[] numeros = [1, 2, 3, 4];
    [int, float, string] tupla = [25, 1.80, "Ana"];

    Persona p = { nombre: "Luis", edad: 40 };
    Estado estado = ACTIVO;

    map<string> capitales = { "MX": "Ciudad de México", "US": "Washington" };

    int|error resultado = 10; // Result
    float? opcion = 3.14;     // Option

    
    int contador = 0;
    fork {
        worker w1 {
            int local = contador;
            local += 1;
            io:println("Worker 1 contador: " , local.toString());
        }
        worker w2 {
            int local = contador;
            local += 1;
            io:println("Worker 2 contador: " , local.toString());
        }
    }

	io:println("Nombre: " + nombre + ", Edad: " + edad.toString());
    string capitalMX = capitales["MX"] ?: "Desconocido";
    io:println("Capital de MX: " + capitalMX);
    io:println("Persona: " + p.toString());
    io:println("Contador final (simulado): " + contador.toString());
}

Compilamos y ejecutamos:

$ bal run tipos.bal

Salida:

Nombre: Thomas Muller., Edad: 30
Capital de MX: Ciudad de México
Persona: {"nombre":"Luis","edad":40}
Contador final (simulado): 0
Worker 2 contador: 1
Worker 1 contador: 1

En Rust:

tipos.rs

fn main() {
    
    let edad: i32 = 30;
    let altura: f64 = 1.75;
    let activo: bool = true;
    let inicial: char = 'C';
    let nombre: String = String::from("Thomas Muller.");

    
    let numeros: [i32; 4] = [1, 2, 3, 4];
    let tupla: (i32, f64, &str) = (25, 1.80, "Ana");

    struct Persona {
        nombre: String,
        edad: i32,
    }
    let p = Persona { nombre: String::from("Luis"), edad: 40 };

    enum Estado {
        Activo,
        Inactivo,
    }
    let estado = Estado::Activo;

    use std::collections::HashMap;
    let mut capitales = HashMap::new();
    capitales.insert("MX", "Ciudad de México");
    capitales.insert("US", "Washington");

    let resultado: Result<i32, &str> = Ok(10);
    let opcion: Option<f64> = Some(3.14);

   
    use std::sync::{Arc, Mutex};
    use std::thread;

    let contador = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..2 {
        let contador = Arc::clone(&contador);
        let handle = thread::spawn(move || {
            let mut num = contador.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for h in handles {
        h.join().unwrap();
    }

    println!("Nombre: {}, Edad: {}", nombre, edad);
    println!("Capital de MX: {}", capitales["MX"]);
    println!("Persona: {} ({})", p.nombre, p.edad);
    println!("Contador final: {}", *contador.lock().unwrap());
}

Compilamos:

$ rustc tipos.rs

Se creará un ejecutable:

$ tipos.exe

Salida:

Nombre: Thomas Muller., Edad: 30
Capital de MX: Ciudad de México
Persona: Luis (40)
Contador final: 2

Podríamos continuar con las comparaciones con estructuras de datos, acceso a archivos, acceso a base de datos y hasta servicios REST. Sin embargo, lo haremos en otra ocasión.

Conclusiones

Es importante hacer notar que:

  • Rust: curva de aprendizaje más pronunciada por su sistema de ownership y borrow checker
  • Ballerina: menos comunidad y ecosistema que Rust; puede ser limitante fuera del ámbito de integración.

Ambos lenguajes tienen un propósito, resuelven problemas distintos. No son competidores.

Enlaces:

https://rust-lang.org/
https://ballerina.io/
https://www.infoq.com/articles/ballerina-integration-tutorial-part-2/
https://www.infoq.com/articles/ballerina-microservices-language-part-1/



jueves, 25 de diciembre de 2025

Ballerina: un lenguaje en crecimiento

En una entrega anterior vimos un breve vistazo a Ballerina, un lenguaje de programación dependiente de la JVM. Diseñado por WSO2 para programadores de aplicaciones de la era de la nube.

Lanzado en 2017, con versión estable en 2022, Ballerina aún es joven y no tiene la misma comunidad que Java o Go.

Empresas como WSO2, MOSIP y RAAPID.AI usan Ballerina principalmente para integración de sistemas, microservicios y manejo de datos sensibles en entornos distribuidos. Su enfoque está en la orquestación de APIs, seguridad en comunicaciones y procesamiento de datos en la nube.

Empresas que usan Ballerina y casos de uso
Empresa / Proyecto Uso principal de Ballerina Beneficio clave
WSO2 Integración de sistemas empresariales y orquestación de APIs Resuelve el “integration gap” entre microservicios y productos de integración
MOSIP Integración dirigida por eventos con WebSubHub Manejo eficiente de eventos y flujos de datos en sistemas de identidad nacional
RAAPID.AI Integración de datos clínicos Interoperabilidad y unificación de datos médicos dispersos
Localazy Hub Aplicaciones en red y microservicios Seguridad integrada (TLS/SSL) y soporte nativo para Docker/Kubernetes

¿Por qué Ballerina?

  • Diseñado para integración: A diferencia de lenguajes genéricos, Ballerina entiende protocolos, formatos y conectores desde el núcleo. 
  • Microservicios y APIs: Ideal para arquitecturas distribuidas, con soporte nativo para REST, gRPC y mensajería. 
  • Seguridad embebida: TLS/SSL y políticas de seguridad por diseño. 
  • Cloud-native: Integración directa con Docker y Kubernetes, lo que lo hace atractivo para DevOps y despliegues modernos.

Consideraciones y retos

  • Adopción limitada: Aunque potente, Ballerina aún es joven (lanzado en 2017, con versión estable en 2022) y no tiene la misma comunidad que Java o Go. 
  • Nicho de integración: Su mayor valor está en proyectos donde la orquestación de servicios y APIs es crítica; fuera de ese ámbito, otros lenguajes pueden ser más prácticos. 
  • Curva de aprendizaje: Su enfoque visual (diagramas de secuencia y flujos) puede ser novedoso pero requiere adaptación.

Creando un proyecto sencillo con Ballerina

Para esto requerimos instalar Ballerina:

https://ballerina.io/downloads/

1. Una vez instalado comprobamos la versión instalada:

$ bal version

2. Creamos el proyecto:

$ bal new hello_world
$ cd hello_world

3. Editamos el archivo main.bal:

import ballerina/io;

public function main() {
    io:println("Hola, mundo");
}

4. Ejecutamos el código:

$ bal run

Salida:

Hola, mundo

Revisemos la estructura del proyecto:

  • Ballerina.toml: Metadatos del paquete (nombre, versión). 
  •  hello_world/main.bal: Punto de entrada (main) del programa. 
  •  target/ (se crea al compilar): Artefactos y compilados. 
  •  Module.md: Documentación del módulo.

También tenemos la opción de crear un ejecutable (como los JARS):

$ bal build

Y ejecutarlo:

$ bal run target/bin/hello_world.jar

Y si preguntan ¿Por qué se crea un JAR? Es porque Ballerina es un lenguaje dependiente de la JVM.

Ahora veamos algo más interesante: crear un servicio HTTP.

Editemos el archivo main.bal:

import ballerina/http;

service / on new http:Listener(8080) {
    resource function get hello() returns string {
        return "Hola, mundo";
    }
}

Ejecutemos:

$ bal run

Abrimos el navegador en la ruta: http://localhost:8080/hello

Salida:

Hola, mundo

Como se puede observar, Ballerina tiene mucho que ofrecer para los desarrolladores. Desde los que vienen de C, C++, PHP, Python, Java, C#, Go y Rust.

Enlaces:

https://alquimistadecodigo.blogspot.com/2022/09/ballerina-en-un-vistazo.html


Websharper

Websharper, según la documentación oficial es: un framework y un conjunto de herramientas para desarrollar aplicaciones web/móviles y servicios web íntegramente en C# o F# (o una combinación de ambos lenguajes) con comunicación cliente-servidor y navegación web fuertemente tipadas.

Características principales:

Veremos las sus características principales.

  • Sitelets: funcionalidad del lado del servidor. 
  • Compilador de JavaScript y abstracciones del lado del cliente. 
  • Facilidades de interacción cliente-servidor. 
  • Funciones adicionales.

Empezando con Websharper

Por default se usa C#, veamos un ejemplo paso a paso.

1. Creamos un directorio y nos ubicamos en el:

$ mkdir proyecto-websharper
$ cd proyecto-websharper

2. Instalamos los templates de WebSharper:

$ dotnet new -i WebSharper.Templates

3. Creamos un proyecto WebSharper:

$ dotnet new websharper-web

4. Ejecutamos la aplicación:

$ dotnet run

Abrimos el navegador en la ruta: http://localhost:5000

Con esto hemos creado nuestra primera aplicación con WebSharper.

Ahora miremos el contenido del proyecto:

Como se puede observar hay directorios que llaman la atención, describiremos las carpetas:

  • bin: Carpeta estándar de .NET donde se colocan los archivos compilados finales (DLLs, ejecutables, etc.) después de construir el proyecto. En proyectos WebSharper, también puede contener subcarpetas con archivos generados para despliegue (como HTML/JS en versiones antiguas). 
  • Generated: Carpeta generada automáticamente por WebSharper. Contiene código JavaScript (y posiblemente otros artefactos) traducido desde el código C# marcado con atributos como [JavaScript]. Este código es el resultado de la compilación client-side de WebSharper. 
  • node_modules: Carpeta creada por npm (Node.js package manager). Contiene todas las dependencias JavaScript externas instaladas vía package.json. En proyectos WebSharper modernos, se usa para herramientas de bundling como Vite o esbuild, y para librerías frontend. 
  • obj: Carpeta estándar de compilación .NET. Almacena archivos intermedios temporales durante el proceso de build (como objetos compilados, recursos embebidos, etc.). No se debe modificar manualmente ni incluir en control de versiones. 
  • wwwroot: Carpeta raíz para archivos estáticos en proyectos web .NET (como ASP.NET Core). Aquí se colocan los archivos que se sirven directamente al navegador: HTML, CSS, JS bundlado, imágenes, etc. En WebSharper, el output final del cliente (JS generados, bundles) suele configurarse para ir aquí (a menudo vía wsconfig.json con "outputDir": "wwwroot").

Estas carpetas combinan elementos estándar de proyectos .NET/ASP.NET Core con específicas de WebSharper (como Generated) y modernas herramientas frontend (como node_modules y configuración para bundlers como Vite/esbuild, visibles en archivos como vite.config.js y esbuild.config.mjs).

Ahora miremos los achivos:

  • proyecto-websharper.csproj: Archivo de proyecto principal de .NET (formato SDK-style). Define la configuración del proyecto: tipo de salida (generalmente una aplicación web ASP.NET Core), referencias a paquetes NuGet (como WebSharper, WebSharper.CSharp, etc.), propiedades de compilación y opciones específicas de WebSharper (por ejemplo, vía propiedades como o referencias a targets). Es esencial para que dotnet build o Visual Studio compile el proyecto correctamente. 
  • appsettings.json: Archivo de configuración estándar de ASP.NET Core. Contiene settings de la aplicación, como cadenas de conexión a bases de datos, opciones de logging, URLs de APIs externas o configuraciones específicas de WebSharper (por ejemplo, sección "websharper" para redirección de scripts en modo debug). Se carga en tiempo de ejecución y puede tener versiones por entorno (como appsettings.Development.json). 
  • Client.cs: Archivo de código C# que contiene la lógica client-side (lado del navegador). Aquí se escribe código marcado con atributos como [JavaScript] o [SPAEntryPoint], que WebSharper traduce automáticamente a JavaScript. Es el punto principal para definir la interfaz de usuario interactiva (UI con WebSharper.UI, eventos, etc.). 
  • esbuild.config.mjs: Archivo de configuración para esbuild (un bundler/minificador rápido de JavaScript). En proyectos WebSharper modernos, se usa para procesar el output prebundled generado por WebSharper (cuando "prebundle": true en wsconfig.json), creando un bundle final optimizado para producción (minificación, tree-shaking, etc.). 
  • Main.html: Archivo HTML principal (plantilla o index). Sirve como página base para la aplicación (SPA o sitelet). Contiene placeholders especiales de WebSharper (como
    o atributos ws-replace) donde se inyecta el contenido dinámico generado desde el lado server o client. En modo desarrollo, Vite lo sirve con hot-reload. 
  • package.json: Archivo estándar de Node.js/npm. Define las dependencias JavaScript externas (como librerías UI, polyfills o herramientas) y scripts de build (por ejemplo, "dev" para Vite, "build" para producción). WebSharper lo usa para integrar con bundlers modernos como Vite o esbuild. 
  • Remoting.cs: Archivo C# que define funciones RPC (Remote Procedure Calls). Contiene métodos marcados con [Rpc] que permiten llamar código server-side desde el client de forma segura y tipada (sin escribir AJAX manual). Es clave para la comunicación client-server en aplicaciones WebSharper
  • Site.cs: Archivo C# que define el sitelet (estructura de rutas y páginas de la aplicación). Usa WebSharper.Sitelets para declarar endpoints (acciones, páginas, etc.) y cómo renderizar el contenido (integrando templates como Main.html y código client). Es el núcleo de la lógica de enrutamiento server-side. 
  • Startup.cs: Archivo estándar de ASP.NET Core para configurar la aplicación. Aquí se registra el pipeline (middleware), se integra WebSharper (por ejemplo, services.AddWebSharper() o configuración de sitelets/RPC), se carga appsettings.json y se define el hosting (Kestrel, IIS, etc.). 
  • vite.config.js: Archivo de configuración para Vite (servidor de desarrollo y bundler moderno). Se usa en modo debug para hot-module-replacement (HMR), proxy de APIs y servir el proyecto rápidamente. En producción, puede delegar a esbuild. Los templates WebSharper recientes lo incluyen para un desarrollo fluido. 
  • wsconfig.json: Archivo de configuración específico de WebSharper. Define opciones del compilador como tipo de proyecto ("site", "spa", etc.), output directory (ej. "wwwroot"), prebundling, source maps, descarga de recursos remotos y más. Es crucial para personalizar cómo WebSharper genera el JavaScript.

Continuaremos con esta serie de entregas sobre .NET.

Enlaces:

https://websharper.com/

martes, 23 de diciembre de 2025

Coursera compra Udemy

Hace unos días estalló la noticia de que Coursera adquiría a Udemy. La plataforma Udemy, donde puedes comprar cursos de todo tipo y a "bajo costo" (con promociones), lo confirmaba en su blog. Coursera por su parte también lo hacía.

Era un hecho y no una suposición. La noticia era real.

Muchos estudiantes e instructores daban un grito ensordecedor.

¿Qué pasará con la plataforma? 
¿Coursera eliminará a Udemy o se fusionará?
¿Qué pasará con los cursos de los instructores? 
¿Qué pasa con los cursos que los estudiantes ya compraron? 
¿Darán algún reembolso o se deslindarán de todo?

Mil y un preguntas eran lanzadas al aire con la esperanza que alguien, quien sea, las respondiera.

2025 fue un año caótico gracias en gran parte al auge de las plataformas de Inteligencia Artificial. ChatGPT, Grok, Gemini, DeepSeek, etc. se volvieron noticia a nivel mundial. Su uso cada vez más extenso ha vuelto loca a la la comunidad de tecnología de la información.

Lo que parecía una cosa de ciencia ficción donde el humano dicta a una máquina lo que quiere hacer y ésta lo hace ya no es una locura, es el pan de cada día.

Plataformas educativas como Pluralsight, Coursera, Udemy, Platzi, etc. veían mermadas sus ganancias.

"¿Para qué pagar una subscripción o curso si con una IA como ChatGPT puede ayudarme a aprender todo tipo de cosas y además "gratis"?"

Se volvía el lema de las generaciones actuales.

Una generación dependiente de las herramientas IA. Una generación donde leer, escribir y pensar por uno mismo se ha vuelto opcional.

"¿Para qué me esfuerzo? Le pregunto a ChatGPT cómo resolver una ecuación diferencial y hasta la gráfica me da".

"Hace mi tarea, mis presentaciones y vídeos en minutos. Mejor a ver Netflix y terminar un maratón de Strange Things."

Y no es broma, la tendencia de uso de herramientas IA indica que los más jóvenes saben cada vez menos. A pesar de contar con ventajas, como buscar miles o millones de recursos en la web y obtenerlo en segundos, el joven actual es más ignorante que los jóvenes de generaciones pasadas.

Salgan a la calle y compruebenlo.

Volviendo al tema. Las plataformas han sido afectadas por el constante uso de la IA. Stackoverflow tuvo que enfrentar una caída severa de sus visitas y consultas. Pues es difícil competir con una herramienta que puede resolver problemas relacionadas al mundo de la programación e informática en minutos o segundos.

Udemy/Coursera han tenido que implementar servicios de IA en sus cursos para estar a la moda y las tendencias.

Aún no se sabe que pasará. Si habra una fusión en una sola plataforma. Si ambas coexistirán o alguna de ellas desaparecerá.

Es hora de desempolvar el cerebro y empezar a usarlo. No vaya ser que los humanos seamos los próximos. Todos somos sustituibles, pues vivimos en un mundo donde la humanidad está desunida y solo busca el placer inmediato. Un mundo donde tener es más importante que ser.

Salgan a la calle y compruebenlo.

Poco a poco la gente comprenderá el verdadero impacto de las herramientas IA. Nada es gratis y si lo es, es porque el humano es el producto y el pago mismo.

Enlaces:

https://blog.udemy.com/udemy-coursera-combine/
https://www.coursera.org/

sábado, 20 de diciembre de 2025

¿Por qué Erlang sigue vivo en 2025 ... ?

¿Por qué Erlang sigue vivo en 2025? Una pregunta que muy pocos hacen, es la verdad, admitámoslo. 

En un mundo donde cada año aparecen nuevos lenguajes, frameworks y paradigmas, sorprende que Erlang, creado en los años 80 por Ericsson, siga siendo una pieza clave en sistemas modernos de telecomunicaciones, mensajería, fintech e infraestructura crítica. En 2025, Erlang no solo sigue vivo… es insustituible.


1. Erlang nació para problemas que siguen vigentes

Erlang/OTP se diseñó para tolerancia a fallos, concurrencia masiva y actualizaciones en caliente. Hoy esos retos son más comunes: millones de conexiones simultáneas, sistemas distribuidos globales, microservicios que no pueden caerse, plataformas con latencia estable y operación 24/7 sin downtime.


2. Industrias donde Erlang domina en 2025

Telecomunicaciones

  • Origen: conmutadores telefónicos que no pueden fallar.
  • Usos actuales: VoIP, gateways de señalización, softswitches, sistemas de mensajería en tiempo real.

Mensajería y tiempo real

  • Escala: millones de conexiones concurrentes con latencia mínima.
  • Proyectos: Ejabberd (XMPP), RabbitMQ, CouchDB.

Fintech y trading

  • Requisitos: baja latencia, alta disponibilidad, resiliencia ante fallos.
  • Encaje: procesamiento distribuido y tolerante a fallos.

IoT y sistemas distribuidos

  • Modelo: procesos ligeros + supervisión automática.
  • Casos: sensores distribuidos, gateways IoT, sistemas de monitoreo.

3. ¿Por qué no ha sido reemplazado?

Ningún otro lenguaje combina al mismo nivel: procesos ultraligeros, aislamiento entre procesos, paso de mensajes seguro, supervisores que reinician fallos automáticamente, hot code swapping y escalabilidad horizontal nativa. Muchos lenguajes modernos intentan imitarlo; pocos lo logran.


4. Comparativa: Erlang vs Elixir vs Haskell (2025)

Característica Erlang Elixir Haskell
Paradigma Funcional, concurrente Funcional, concurrente Funcional puro
VM BEAM BEAM GHC
Concurrencia ★★★★★ ★★★★★ ★★★
Tolerancia a fallos ★★★★★ ★★★★★ ★★
Hot code reload No
Ecosistema web Limitado Phoenix (muy fuerte) Limitado
Curva de aprendizaje Media Baja Alta
Uso típico Telecom, mensajería, sistemas críticos Web, APIs, tiempo real Compiladores, investigación, sistemas puros
Comunidad Pequeña pero estable Creciente Académica
Conclusión comparativa: Erlang sigue siendo el rey de la resiliencia; Elixir es la puerta de entrada moderna al ecosistema BEAM; Haskell brilla en lógica pura, no en sistemas distribuidos de alta disponibilidad.

5. Ejemplos sencillos para entender su poder

Ejemplo 1: Un proceso que puede fallar (y un supervisor lo traería de vuelta)

ejemplo.erl

-module(ejemplo).
-export([start/0, loop/0]).

start() -&gt;
    spawn(fun loop/0).

loop() -&gt;
    receive
        crash -&gt;
            exit(crashed);
        _ -&gt;
            io:format("Mensaje recibido~n")
    end,
    loop().

Idea clave: en Erlang los procesos están aislados y los supervisores de OTP reinician los que fallan para mantener el sistema saludable.

Ejemplo 2: Concurrencia masiva en una línea

spawn(fun() -> io:format("Hola desde un proceso~n") end).

Punto fuerte: puedes crear millones de procesos ligeros gracias a la VM BEAM y su planificación cooperativa.

Ejemplo 3: Comunicación entre procesos

Shell de Erlang:

Pid = spawn(fun() ->
    receive
        {hola, From} ->
            From ! "Hola desde Erlang!"
    end
end),

Pid ! {hola, self()},
receive
    Respuesta -> io:format("~p~n", [Respuesta])
end.

Modelo mental: todo se coordina por paso de mensajes; no hay memoria compartida, lo que evita condiciones de carrera y simplifica la concurrencia.


6. ¿Deberías aprender Erlang en 2025?

Si te atraen sistemas distribuidos, alta disponibilidad, telecomunicaciones, mensajería, backend resiliente y microservicios, la respuesta es . Si prefieres una entrada más suave, comienza con Elixir: sintaxis moderna, misma potencia de BEAM.


7. Conclusión

Erlang no compite por ser trendy; compite por ser confiable. Sigue vivo porque fue diseñado para problemas que persisten, su modelo de concurrencia es superior, su tolerancia a fallos es incomparable y el ecosistema OTP/BEAM continúa sólido. Industrias enteras dependen de él, y por eso, en 2025, Erlang sigue ganando.

Enlaces:

https://codemonkeyjunior.blogspot.com/2021/06/erlang-sitios-para-aprender.html
https://codemonkeyjunior.blogspot.com/2021/05/erlang-beam.html
https://codemonkeyjunior.blogspot.com/2021/07/elixir-sitios-para-aprender.html

sábado, 13 de diciembre de 2025

Equivalencias entre un proyecto Spring Boot y ASP .NET Core

En esta ocasión veremos una equivalencia entre un proyecto desarrollado con Spring Boot y ASP .NET Core.

Digamos que tenemos esta estructura de carpetas:

auditor/
  pom.xml
  src/resources/application.properties
  src/main/java/com/inforhomex/CursoSpring/AuditorSpringApplication.java
  src/main/java/com/inforhomex/CursoSpring/component/AuditorComponent.java
  src/main/java/com/inforhomex/CursoSpring/config/AuditorConfig.java
  src/main/java/com/inforhomex/CursoSpring/controller/AuditorController.java
  src/main/java/com/inforhomex/CursoSpring/model/Auditor.java
  src/main/java/com/inforhomex/CursoSpring/services/AuditorService.java
  src/main/java/com/inforhomex/CursoSpring/services/AuditorServiceImpl.java
  src/main/java/com/inforhomex/CursoSpring/repository/AuditorRepository.java

Su equivalente en ASP .NET Core sería:

Auditor/
  Auditor.csproj
  appsettings.json
  Program.cs
  Controllers/
      AuditorController.cs
  Models/
      Auditor.cs
  Services/
      IAuditorService.cs
      AuditorService.cs
  Repositories/
      IAuditorRepository.cs
      AuditorRepository.cs
  Data/
      ApplicationDbContext.cs
  Configuration/
      AuditorConfig.cs (opcional)

Explicación archivo por archivo

Spring usa Maven; .NET usa un archivo XML similar.

Auditor.csproj (equivale al pom.xml)

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
  </ItemGroup>
</Project>

El appsettings.json de ASP .NET Core sería el application.properties de Spring Boot.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=AuditorDb;Trusted_Connection=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}

Ahora veamos el programa principal llamado Program.cs que equivaldría a AuditorSpringApplication.java de Spring Boot.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<ApplicationDbContext>();
builder.Services.AddScoped<IAuditorService, AuditorService>();
builder.Services.AddScoped<IAuditorRepository, AuditorRepository>();

var app = builder.Build();

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

Quien ha usado Spring Boot sabrá lo que es la Inyección de dependencia.

@Autowired
AuditorService service;

En C# también existe ese concepto:

builder.Services.AddSingleton<AuditorComponent>();

¿Y qué con las configuraciones? Con Spring Boot tenemos clases para configuraciones especiales.

@Configuration
public class AuditorConfig { ... }

En ASP .NET Core tenemos esto:

public static class AuditorConfig {
    public static void AddAuditorServices(this IServiceCollection services) {
        services.AddScoped<IAuditorService, AuditorService>();
    }
}

Y en el programa principal Program.cs:

builder.Services.AddAuditorServices();

Las clases Controllers de Spring Boot también tendrían su equivalente:

AuditorController.cs

[ApiController]
[Route("auditor")]
public class AuditorController : ControllerBase {
    private readonly IAuditorService _service;

    public AuditorController(IAuditorService service) {
        _service = service;
    }

    [HttpGet]
    public IActionResult GetAll() => Ok(_service.GetAll());
}

Con las entidades Java pasaría lo mismo:

Auditor.java

@Entity
public class Auditor { ... }

Su equivalente sería:

Auditor.cs

public class Auditor {
    public int Id { get; set; }
    public string Nombre { get; set; }
}

Con las clases servicio de Java/SpringBoot también aplica:

public interface AuditorService { ... }

@Service
public class AuditorServiceImpl implements AuditorService { ... }

Con C#:

IAuditorService.cs

public interface IAuditorService {
    IEnumerable<Auditor> GetAll();
}

AuditorService.cs

public class AuditorService : IAuditorService {
    private readonly IAuditorRepository _repo;

    public AuditorService(IAuditorRepository repo) {
        _repo = repo;
    }

    public IEnumerable<Auditor> GetAll() => _repo.GetAll();
}

Y continuamos con las clases Repository:

AuditorRepository.java

@Repository
public interface AuditorRepository extends JpaRepository<Auditor, Long> { }

Y su equivalente:

Interface:

public interface IAuditorRepository {
    IEnumerable<Auditor> GetAll();
}

Implementación:

public class AuditorRepository : IAuditorRepository {
    private readonly ApplicationDbContext _context;

    public AuditorRepository(ApplicationDbContext context) {
        _context = context;
    }

    public IEnumerable<Auditor> GetAll() => _context.Auditors.ToList();
}

ApplicationDbContext.cs

public class ApplicationDbContext : DbContext {
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }

    public DbSet<Auditor> Auditors { get; set; }
}

Con esto tendríamos la estructura de directorios equivalente a Spring Boot con ASP .NET Core :

- Controllers
- Models
- Services
- Repositories
- DbContext
- Configuración en Program.cs
- appsettings.json

Esta estructura no necesariamente la tendría un proyecto real. Sin embargo, es un acercamiento bastante formal.

Ahora veamos una tabla comparativa.

Comparativa ASP .NET Core vs Spring Boot


Criterio ASP.NET Core (.NET) Spring Boot (Java)
Lenguaje principal C#, F#, VB.NET Java, Kotlin, otros JVM
Plataformas soportadas Windows, Linux, macOS (cross-platform) Windows, Linux, macOS (via JVM)
Modelo de ejecución .NET runtime (Core/5+) con JIT y opciones AOT JVM (HotSpot, OpenJ9) con JIT
Tipo de framework Framework web moderno, modular, orientado a middleware Framework de arranque rápido sobre Spring, opinionated
Arquitectura y patrón principal Soporta MVC, Razor Pages, Minimal APIs, gRPC, SignalR Soporta MVC, WebFlux (reactivo), REST, SOAP, gRPC (via librerías)
Configuración appsettings.json, configuración fuertemente tipada application.properties / application.yml, Profiles
Inyección de dependencias Contenedor DI integrado en el framework Spring IoC Container muy maduro y extensible
Rendimiento en APIs Muy alto; entre los más rápidos en benchmarks Alto, generalmente por debajo de ASP.NET Core en pruebas crudas
Servidor embebido Kestrel (servidor web propio, muy eficiente) Tomcat por defecto; Jetty/Undertow opcionales
ORM / acceso a datos Entity Framework Core, Dapper, otros ORMs Spring Data JPA, Hibernate, MyBatis, etc.
Soporte cloud Excelente integración con Azure; buen soporte AWS/GCP Excelente integración con AWS, GCP, Azure (Spring Cloud)
Ecosistema y librerías Ecosistema .NET, NuGet, fuerte integración con herramientas Microsoft Ecosistema Java, gran cantidad de librerías, Spring Cloud, Spring Security, etc.
Curva de aprendizaje Amigable si vienes de C# o stack Microsoft Puede ser más compleja por la profundidad del ecosistema Spring
Herramientas y IDEs Visual Studio, Rider, VS Code IntelliJ IDEA, Eclipse, VS Code
Madurez en entornos enterprise Muy usado en enterprise, fuerte en empresas Microsoft-centric Dominante histórico en enterprise Java, ecosistema muy maduro
Licencia y open source Open source (MIT), desarrollado por Microsoft y comunidad Open source (Apache 2.0), desarrollado por VMware/Spring y comunidad
Casos de uso típicos APIs de alto rendimiento, microservicios, backends corporativos, integraciones con Azure Microservicios enterprise, sistemas bancarios, telecom, SaaS sobre JVM

Conclusiones:

ASP.NET Core y Spring Boot representan dos de los ecosistemas más sólidos, modernos y maduros para construir aplicaciones backend y microservicios. Ambos ofrecen rendimiento, modularidad, herramientas de desarrollo avanzadas y una comunidad activa, pero cada uno destaca en áreas distintas.

ASP.NET Core sobresale por su alto rendimiento, su servidor web Kestrel extremadamente eficiente y su integración natural con el ecosistema Microsoft, especialmente Azure. Su diseño minimalista, su DI nativo y su enfoque en middleware lo hacen muy directo y consistente.

Para equipos que ya trabajan con C#, Windows o Azure, ASP.NET Core suele ser la opción más natural y productiva.

Spring Boot, por otro lado, brilla por la amplitud y madurez de su ecosistema, especialmente en entornos enterprise donde Java ha sido dominante durante décadas.

Su integración con Spring Cloud, su flexibilidad para trabajar con múltiples servidores embebidos y su enorme catálogo de librerías lo convierten en una plataforma muy poderosa para arquitecturas complejas y distribuidas.

En términos prácticos, ninguno es “mejor” de forma absoluta. La elección depende del contexto:

  • Si buscas máximo rendimiento, integración con Azure, o ya trabajas con C#, ASP.NET Core es una apuesta excelente. 
  • Si necesitas ecosistema enterprise, herramientas maduras para microservicios distribuidos o ya tienes experiencia en Java, Spring Boot sigue siendo un estándar de oro.

En resumen, ambos frameworks son tecnologías de primera línea. La decisión final suele depender más del ecosistema, la experiencia del equipo y las necesidades del proyecto que de diferencias técnicas absolutas.

Enlaces:

https://blog.vtutor.com/es/cursos-en-linea/principales-razones-por-las-que-asp-net-core-es-el-mejor-framework-para-el-desarrollo-de-aplicaciones-web/
https://www.swiftorial.com/matchups/backend_framework/aspnet-core-vs-spring-boot-dotnet-vs-java
https://medium.com/@solomongetachew112/spring-boot-vs-net-core-which-backend-framework-wins-in-2024-2025-de9395008244
https://dev.to/umesh_kushwaha_6655ba4c0d/spring-boot-vs-net-core-complete-developer-migration-guide-4mfk

Un vistazo a JakartaEE

Los que vienen programando en Java algún tiempo sabrán que trabajar con este lenguaje tiene sus pros y contras. No todo ha sido fácil, pe...

Etiquetas

Archivo del blog