← maurobernal.com.ar

Etiqueta: migration

  • Guía práctica: cómo migré mis proyectos de .NET 8 a .NET 10 sin romper producción

    Migrar un proyecto de .NET 8 a .NET 10 es el tipo de tarea que parece riesgosa pero en la práctica es incremental y reversible. Lo hice en varios proyectos de producción sin downtime. El proceso siempre es el mismo: actualizar el TFM, correr los tests, adoptar las nuevas features de a poco.

    Paso 1: Actualizar el Target Framework

    # Verificar SDK instalado
    dotnet --list-sdks
    
    # Instalar .NET 10 SDK si no está
    # Linux/WSL:
    wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh
    bash dotnet-install.sh --channel 10.0
    
    # En cada .csproj de la solución:
    # <TargetFramework>net10.0</TargetFramework>
    
    # Para actualizar todos los .csproj de una solución en Linux:
    find . -name "*.csproj" -exec sed -i 's/net8.0/net10.0/g; s/net9.0/net10.0/g' {} \;
    
    # Restaurar y compilar
    dotnet restore
    dotnet build

    Paso 2: Actualizar paquetes NuGet

    # Ver paquetes desactualizados
    dotnet list package --outdated
    
    # Actualizar todos los paquetes de Microsoft.* a versiones compatibles con .NET 10
    dotnet add package Microsoft.AspNetCore.OpenApi
    dotnet add package Microsoft.Extensions.Http.Resilience
    
    # Paquetes que pueden desinstalarse en .NET 10:
    # - Swashbuckle.AspNetCore (reemplazado por OpenAPI nativo)
    # - Microsoft.AspNetCore.Mvc.NewtonsoftJson (System.Text.Json mejorado)
    
    # Remover Swashbuckle si usabas OpenAPI:
    dotnet remove package Swashbuckle.AspNetCore

    Paso 3: Adoptar las nuevas features de C# 14

    No hay que cambiar nada para que el proyecto compile. Las nuevas features son opt-in. La estrategia es adoptarlas gradualmente donde aporten más valor.

    // Prioridad 1: field — máximo impacto con mínimo riesgo
    // Buscar propiedades con backing fields privados y migrarlas
    
    // Antes:
    private string _nombre = string.Empty;
    public string Nombre
    {
        get => _nombre;
        set => _nombre = value?.Trim() ?? throw new ArgumentNullException();
    }
    
    // Después:
    public string Nombre
    {
        get => field;
        set => field = value?.Trim() ?? throw new ArgumentNullException();
    }
    
    // Prioridad 2: Primary Constructors en servicios con DI
    // Especialmente útil en proyectos con muchos servicios inyectados
    
    // Prioridad 3: params ReadOnlySpan en métodos variádicos de uso frecuente
    // Buscar: params int[], params string[], params object[]
    // Reemplazar por: params ReadOnlySpan<T> donde sea posible
    
    // Prioridad 4: Lock para thread safety
    // Buscar: private readonly object _lockObj = new object();
    // Reemplazar por: private readonly Lock _lockObj = new();

    Paso 4: Migrar a OpenAPI nativo (si usabas Swashbuckle)

    // Program.cs — reemplazar Swashbuckle por OpenAPI nativo
    
    // ❌ Antes con Swashbuckle:
    // builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", ...); });
    // app.UseSwagger();
    // app.UseSwaggerUI();
    
    // ✅ .NET 9/10 nativo:
    builder.Services.AddOpenApi(options =>
    {
        options.AddDocumentTransformer((document, context, ct) =>
        {
            document.Info = new()
            {
                Title = "Mi API",
                Version = "v1",
                Description = "API de gestión interna"
            };
            return Task.CompletedTask;
        });
    });
    
    // En el pipeline:
    if (app.Environment.IsDevelopment())
    {
        app.MapOpenApi();
        // Opcional: seguir usando Swagger UI solo para dev
        app.UseSwaggerUI(options =>
            options.SwaggerEndpoint("/openapi/v1.json", "Mi API v1"));
    }

    Checklist de migración completo

    PasoAcciónImpacto
    1Cambiar TFM a net10.0 en todos los .csprojObligatorio
    2dotnet restore && dotnet buildObligatorio
    3Ejecutar suite de tests completaObligatorio
    4Actualizar paquetes Microsoft.* a versión 10.xRecomendado
    5Adoptar field en propiedades con backing fieldGradual
    6Primary Constructors en servicios con DIGradual
    7Reemplazar object _lock por Lock _lockGradual
    8Migrar OpenAPI de Swashbuckle a nativoOpcional
    9Adoptar params ReadOnlySpan<T> en hot pathsOpcional
    10Evaluar Native AOT para Workers/APIs simplesOpcional avanzado

    Errores comunes y cómo resolverlos

    // ERROR 1: Paquete no compatible con net10.0
    // "Package X is not compatible with net10.0"
    // Solución: actualizar a la versión más reciente del paquete
    // Si no hay versión compatible, usar <TargetFrameworks>net8.0;net10.0</TargetFrameworks>
    
    // ERROR 2: Reflection en Native AOT
    // "System.InvalidOperationException: Type ... is not reflection-enabled"
    // Solución: agregar source generator o usar [DynamicDependency]
    [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(MiClase))]
    static void MetodoConReflection() { ... }
    
    // ERROR 3: field en C# 14 — feature no disponible
    // "error CS8652: The feature 'field keyword' is currently in Preview"
    // Solución: habilitar preview features en .csproj
    // <LangVersion>preview</LangVersion>
    // (o esperar a que salga de preview en la versión final de .NET 10)
    
    // ERROR 4: Cambios breaking en serialización JSON
    // System.Text.Json en .NET 10 es más estricto con algunos tipos
    // Solución: revisar JsonSerializerOptions y ajustar si es necesario

    Mi experiencia: cuánto tardó cada proyecto

    • API Minimal con 15 endpoints: 30 minutos (cambiar TFM, actualizar paquetes, correr tests)
    • Worker Service con threading complejo: 2 horas (migración de locks + tests de concurrencia)
    • Aplicación ASP.NET MVC grande (80 controllers): medio día (TFM + paquetes + resolver warnings)
    • Adopción gradual de C# 14 features: 2-3 semanas en paralelo con desarrollo normal

    La migración del TFM en sí es rápida. Lo que toma tiempo es la adopción de las nuevas features, y eso es completamente optativo. Podés migrar a .NET 10 hoy y adoptar field o Primary Constructors a tu ritmo, archivo por archivo, cuando tocás esa parte del código.


    ← dotnet run script.cs y las nuevas herramientas de .NET 10: OpenAPI, HttpClient y más | Fin de la Serie .NET 8 → .NET 10

  • Guía práctica: cómo migré un proyecto de Angular 19/20 a Angular 21.2 sin morir en el intento

    Migrar un proyecto Angular de v19/20 a v21 puede sonar intimidante. En la práctica, si seguís el proceso correcto, es incremental: cada paso es reversible y el proyecto sigue funcionando en todo momento. Esta es la guía que apliqué en proyectos reales.

    Antes de empezar: el inventario

    # Ver versión actual y dependencias desactualizadas
    ng version
    ng update
    
    # Verificar compatibilidad antes de actualizar
    npx npm-check-updates -u --target minor

    Paso 1: Actualizar el core

    # Siempre de a una versión mayor a la vez
    # Si estás en v19, primero actualizar a v20, luego a v21
    
    # De v20 a v21:
    ng update @angular/core@21 @angular/cli@21
    
    # El comando aplica schematics automáticos:
    # - Actualiza imports deprecated
    # - Adapta APIs que cambiaron
    # - Avisa sobre cambios manuales necesarios
    
    # Verificar que compila
    ng build --configuration production

    Paso 2: Adoptar Zoneless

    // app.config.ts
    import { provideZonelessChangeDetection } from '@angular/core';
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideZonelessChangeDetection(),
        provideRouter(routes)
      ]
    };
    
    // angular.json: "polyfills": []  // eliminar zone.js
    // npm uninstall zone.js

    Paso 3: Migrar de Karma a Vitest

    ng generate @angular/build:vitest
    ng test  # verificar que los tests pasan
    npm uninstall karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter

    Paso 4: Standalone components

    # Angular 21 asume standalone por defecto
    # Migración automática:
    ng generate @angular/core:standalone --mode=convert-to-standalone
    ng generate @angular/core:standalone --mode=prune-ng-modules
    ng generate @angular/core:standalone --mode=standalone-bootstrap

    Checklist completo

    PasoAcciónObligatorio
    1ng update @angular/core@21 @angular/cli@21✅ Sí
    2Verificar que la app compila y los tests pasan✅ Sí
    3Activar provideZonelessChangeDetection()Recomendado
    4Eliminar Zone.js de polyfillsCon paso 3
    5Migrar a VitestRecomendado
    6Nuevos formularios con Signal FormsGradual
    7Componentes de nav con funciones standalone del RouterGradual
    8Configurar ng mcp para integración con IAOpcional

    Errores comunes y cómo resolverlos

    // ERROR: El componente no se actualiza después de activar Zoneless
    // → Convertir estado a Signals o llamar markForCheck()
    
    // ERROR: Tests fallan con "No current Angular test" después de Vitest
    // → Verificar imports de @angular/core/testing
    
    // ERROR: "Cannot find module zone.js"
    // → Buscar y eliminar import 'zone.js' en el proyecto:
    // grep -r "import 'zone.js'" src/
    
    // ERROR: ExpressionChangedAfterItHasBeenCheckedError en Zoneless
    // → Usar signal.update() o signal.set() en lugar de mutación directa

    Mi experiencia: cuánto tardó la migración

    En un proyecto de tamaño medio (45 componentes, 12 servicios, 80 tests): el ng update tardó 10 minutos. Revisar avisos y corregir deprecaciones: media jornada. Migración a Zoneless con todos los tests adaptados a Vitest: un día. Adopción gradual de Signal Forms en los formularios principales: dos semanas, a medida que tocábamos cada módulo.

    No es un proceso de un fin de semana, pero tampoco es una reescritura. Es una evolución incremental que podés hacer en paralelo con el trabajo normal del equipo.


    Artículo anterior: Router Signals en Angular 21: navegación standalone sin cargar todo el Router | Fin de la Serie Angular 20 → 21.2

    ¿Tenés preguntas sobre la migración en tu proyecto específico? Dejá tu caso en los comentarios. 👇

Tags

tsql (27)mssql (26)sql (20)devops (20)dotnet (18)docker (15)performance (14)contenedores (11)dotnet10 (10)linux (9)csharp (8)microservicios (7)angular (7)angular21 (7)sql server (6)issabel (6)docker-compose (6)typescript (6)mysql (5).NET (5)