Parte 9 — Ingeniería de software políglota · ⏱️ Duración estimada: 90 min · Nivel: Intermedio ✅ Clase construida — 10 implementaciones del núcleo verificadas contra
casos.json.
Si la CI (clase 147) contesta "¿es correcto este cambio?", esta clase contesta la pregunta siguiente: "¿cómo lo llevamos a manos de los usuarios sin miedo?". Aquí conviene separar dos palabras que se confunden a diario. La entrega (delivery) es tener siempre un artefacto probado, versionado y listo para desplegar; el despliegue (deployment) es el acto concreto de ponerlo en producción. Puedes tener entrega continua —cada commit verde produce un artefacto publicable— y aun así decidir cuándo desplegarlo con un botón humano. El despliegue continuo va un paso más allá y automatiza también ese último salto. Hunt y Thomas resumen el principio subyacente en The Pragmatic Programmer: automatiza todo lo repetible, porque lo que se hace a mano se hace mal, tarde y de formas distintas cada vez.
El hilo conductor de la clase es la trazabilidad: saber exactamente qué código corre en producción. La herramienta más básica para ello es la etiqueta de versión (v1.2.3), una marca inmutable en el historial que ancla un artefacto a un punto preciso del código. El programa de casos.json reduce toda la ceremonia a su gesto mínimo —tomar una versión mayor.menor.parche y prefijarla con v para producir desplegado=v1.2.3—, pero detrás de ese prefijo está la diferencia entre poder decir "en producción corre exactamente el commit etiquetado v1.2.3" y encogerse de hombros cuando algo falla.
Entender esto te prepara para razonar sobre lo que de verdad importa en producción: entornos separados, estrategias que reducen el riesgo del cambio, contenedores que hacen el artefacto reproducible, y —sobre todo— la capacidad de volver atrás (rollback) cuando una versión resulta mala.
Al finalizar, podrás:
mayor.menor.parche.| # | Tema | Por qué importa |
|---|---|---|
| 1 | Entrega vs. despliegue | Listo para publicar ≠ publicado |
| 2 | Artefacto y etiqueta de versión | Trazabilidad: qué corre en prod |
| 3 | Entornos dev/staging/prod | Probar antes de exponer a usuarios |
| 4 | Estrategias y rollback | Cambiar con red y poder volver atrás |
.jar, una rueda de Python), identificado por una versión. Inmutable es la palabra clave: el mismo artefacto que pasó staging es el que llega a prod, bit a bit.v1.2.3 en el historial de Git que fija un artefacto a un commit exacto. Sigue el versionado semántico: mayor para cambios incompatibles, menor para funciones nuevas compatibles, parche para correcciones. Es la ancla de la trazabilidad.dev para desarrollar, staging para ensayar en condiciones casi reales, prod para los usuarios. El cambio avanza por ellos como por esclusas, ganando confianza antes de tocar a nadie.Tu equipo acaba de fusionar un cambio que pasó toda la CI. El pipeline de CD construye una imagen de contenedor, la etiqueta como v1.2.3 y la publica en el registro: eso es la entrega, el artefacto ya existe y es trazable. Un ingeniero de guardia decide desplegar por canary: el 5 % del tráfico va a v1.2.3 mientras el resto sigue en v1.2.2. Los tableros muestran latencia y errores estables durante quince minutos, así que se amplía al 50 % y luego al 100 %. Si en cualquier momento los errores se hubieran disparado, el rollback habría sido inmediato —redirigir el tráfico de vuelta a v1.2.2, la última versión conocida-buena— porque la etiqueta permite saber exactamente a qué volver. Ese pequeño desplegado=v1.2.3 que produce el programa de esta clase es, literalmente, el eslabón que hace posible toda esa maniobra.
mayor.menor.parchedesplegado=v<versión>Especificación y verificación en casos.json:
| stdin | esperado |
|---|---|
1.2.3 |
desplegado=v1.2.3 |
0.9.0 |
desplegado=v0.9.0 |
2.1.5 |
desplegado=v2.1.5 |
El gesto es mínimo pero cargado de sentido: tomar la versión y anteponerle la marca que la vuelve una etiqueta reconocible.
LEER version ; ESCRIBIR 'desplegado=v' + version
Mismo algoritmo, forma idiomática en cada lenguaje. Todas producen la salida de casos.json.
Cada bloque es el archivo real de implementaciones/: el enlace de cada lenguaje abre su fuente, y el comando de al lado lo ejecuta.
python/main.py · python main.pyEl problema es, en el fondo, una concatenación con formato, y ahí Python brilla con las f-strings. sys.stdin.readline().strip() lee la única línea y le quita el salto final; la f-string f"desplegado=v{version}" inserta la versión ya limpia justo después del prefijo v. Ramalho, en Fluent Python, presenta las f-strings como la forma preferente de interpolar en Python moderno por ser legibles y eficientes: el texto plantilla y los valores conviven a la vista, sin + ni % que estorben.
import sys
version = sys.stdin.readline().strip()
print(f"desplegado=v{version}")
🧬 El mismo programa en la familia Scripting dinámico: Ruby · Perl · Lua · Tcl · R
Para 1.2.3, version vale "1.2.3" y la salida es desplegado=v1.2.3. El strip() es la parte silenciosamente importante: sin él, el \n de stdin viajaría dentro de la etiqueta y rompería la comparación con casos.json.
java/Main.java · java Main.javaJava contrasta por su ceremonia: no hay lectura de una línea "de una", sino el trío BufferedReader + InputStreamReader + System.in, la escalera clásica de la E/S de la plataforma. La concatenación usa el operador + sobre String, que Bloch analiza en Effective Java: es cómodo para unir dos piezas como aquí, aunque desaconseje encadenar muchas en bucle por el coste. br.readLine().trim() cumple el mismo papel que el strip() de Python.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String version = br.readLine().trim();
System.out.println("desplegado=v" + version);
}
}
🧬 El mismo programa en la familia JVM: Kotlin · Scala · Groovy · Clojure
sql/main.sql · sqlite3 :memory: < main.sqlSQL hace explícito el otro rostro de la misma operación: la concatenación como cómputo declarativo. El operador estándar || une el literal 'desplegado=v' con la columna v de una tabla de una sola fila. No hay "leer una línea"; hay una relación con un valor y una proyección que lo transforma. Date, en SQL and Relational Theory, recuerda que en el modelo relacional todo es evaluación de expresiones sobre relaciones, y algo tan mundano como añadir un prefijo se piensa como un SELECT que proyecta una nueva columna.
-- SQL: concatena el prefijo con ||.
WITH t(v) AS (VALUES ('1.2.3'))
SELECT 'desplegado=v' || v AS resultado FROM t;
🧬 El mismo programa en la familia Lógica y declarativa: Prolog · Datalog
javascript/main.mjs · node main.mjsimport { readFileSync } from "node:fs";
const version = readFileSync(0, "utf8").trim();
console.log(`desplegado=v${version}`);
🧬 El mismo programa en la familia JavaScript / web: Dart · ActionScript
typescript/main.ts · pnpm exec tsx main.tsimport { readFileSync } from "node:fs";
const version: string = readFileSync(0, "utf8").trim();
console.log(`desplegado=v${version}`);
🧬 El mismo programa en la familia JavaScript / web: Dart · ActionScript
csharp/Program.cs · dotnet runusing System;
string version = Console.In.ReadToEnd().Trim();
Console.WriteLine($"desplegado=v{version}");
🧬 El mismo programa en la familia .NET: F# · VB.NET
go/main.go · go run main.gopackage main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
line, _ := bufio.NewReader(os.Stdin).ReadString('\n')
version := strings.TrimSpace(line)
fmt.Printf("desplegado=v%s\n", version)
}
🧬 El mismo programa en la familia Sistemas: Zig · Nim · D
rust/main.rs · rustc main.rs -o main && ./mainuse std::io::Read;
fn main() {
let mut s = String::new();
std::io::stdin().read_to_string(&mut s).unwrap();
let version = s.trim();
println!("desplegado=v{version}");
}
🧬 El mismo programa en la familia Sistemas: Zig · Nim · D
c/main.c · cc main.c -o main && ./main#include <stdio.h>
int main(void) {
char version[64];
if (scanf("%63s", version) != 1) return 1;
printf("desplegado=v%s\n", version);
return 0;
}
🧬 El mismo programa en la familia C / llaves: C++ · Objective-C
php/main.php · php main.php<?php
$version = trim(fgets(STDIN));
echo "desplegado=v$version\n";
🧬 El mismo programa en la familia Scripting dinámico: Ruby · Perl · Lua · Tcl · R
SQL es declarativo: no lee de stdin como los demás; su implementación muestra la misma idea sobre una tabla de casos, y el verificador la marca como ilustrativa.
El ejercicio es una concatenación en todos, pero el artefacto que cada ecosistema publica al desplegar difiere de raíz, y con él la herramienta de versionado y el registro donde acaba.
| Lenguaje | Artefacto típico | Se publica en |
|---|---|---|
| Python | rueda .whl / imagen |
PyPI, registro de contenedores |
| JavaScript/TS | paquete npm / bundle | npm registry, CDN |
| Java | .jar / .war |
Maven Central, Nexus/Artifactory |
| C# | paquete NuGet / binario | NuGet, registro |
| Go | binario estático | release de GitHub, imagen |
| Rust | crate / binario | crates.io, imagen |
| C | binario / biblioteca | paquete del SO |
| SQL | scripts de migración | control de versiones |
| PHP | paquete Composer | Packagist |
| Clase de diferencia | Observación entre lenguajes |
|---|---|
| Sintáctica | Concatenación con f-string, +, \|\| o interpolación. |
| Semántica | La etiqueta identifica de forma única la versión desplegada. |
| Paradigmática | SQL concatena con \|\| sobre una relación, no sobre una línea leída. |
La entrega y el despliegue versionados no son idea de una herramienta concreta: los git tag y las releases de GitHub anclan artefactos a commits; los registros de contenedores guardan imágenes por etiqueta; herramientas de CD como Argo CD o Spinnaker orquestan blue-green y canary sobre Kubernetes; y en móvil, las tiendas gestionan despliegues escalonados por porcentaje de usuarios. En todas, dos constantes se repiten: la versión inmutable como fuente de verdad y el rollback como derecho irrenunciable.
Los mismos casos para todas las implementaciones: casos.json. Verifica la equivalencia:
python scripts/verificar_equivalencia.py 148
Detalle en reto.md.
v1.2.3) de otras etiquetas del historial.Libros de la parte:
Libros de los lenguajes del núcleo:
⏮️ Clase 147 · 📂 Parte · 📚 Índice · 🌐 Atlas · Clase 149 ⏭️