Clase 148 — Entrega y despliegue

Parte 9 — Ingeniería de software políglota · ⏱️ Duración estimada: 90 min · Nivel: IntermedioClase construida — 10 implementaciones del núcleo verificadas contra casos.json.


🎯 Objetivo

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.

📚 Resultados de aprendizaje

Al finalizar, podrás:

  1. Distinguir entrega (tener listo) de despliegue (poner en producción) y explicar por qué la separación importa.
  2. Etiquetar una versión de forma trazable y justificar la convención mayor.menor.parche.
  3. Comparar estrategias de despliegue (blue-green, canary, rolling) según su riesgo y su coste de rollback.
  4. Reconocer el papel de los artefactos versionados, los entornos (dev/staging/prod) y los contenedores en un despliegue seguro.

🗺️ Temas

# 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

📖 Definiciones y características

🧩 Situación

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.

🧮 Modelo

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

📐 Algoritmo (pseudocódigo neutral)

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

🌐 Implementaciones idiomáticas — el código a la vista

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 · python/main.py · python main.py

El 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 · java/Main.java · java Main.java

Java 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 · sql/main.sql · sqlite3 :memory: < main.sql

SQL 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 · javascript/main.mjs · node main.mjs

import { 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 · typescript/main.ts · pnpm exec tsx main.ts

import { 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

C# · csharp/Program.cs · dotnet run

using System;

string version = Console.In.ReadToEnd().Trim();
Console.WriteLine($"desplegado=v{version}");

🧬 El mismo programa en la familia .NET: F# · VB.NET

Go · go/main.go · go run main.go

package 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 · rust/main.rs · rustc main.rs -o main && ./main

use 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 · 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 · 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.

🔬 Comparación

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.

🧬 El concepto en la familia

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.

✅ Prueba común

Los mismos casos para todas las implementaciones: casos.json. Verifica la equivalencia:

python scripts/verificar_equivalencia.py 148

🧪 Reto de transferencia

Detalle en reto.md.

⚠️ Errores comunes

❓ Preguntas frecuentes

🔗 Referencias

Libros de la parte:

Libros de los lenguajes del núcleo:


⏮️ Clase 147 · 📂 Parte · 📚 Índice · 🌐 Atlas · Clase 149 ⏭️