Picando Código

WordPress en 2024

noviembre 05, 2024 10:00

Frankenstein WordPressEste no ha sido un buen año para WordPress. No voy a entrar en detalle de lo que ha pasado, una búsqueda rápida online da la información necesaria para leer sobre el tema. Pero esto ha llevado tanto a mucha gente a renunciar a Automattic, como a reconsiderar el uso de WordPress en sí.

Personalmente vengo pensando migrar de WordPress a otra cosa desde hace años. El blog empezó en Blogger en 2007. Después de poco tiempo, migré a un hosting en linuxuruguay y eventualmente hosting propio con WordPress. Al ver que pensaba seguir con el blog, necesitaba tener control sobre la plataforma. WordPress era lo más fácil, rápido y conveniente en su momento.

Tener el blog en WordPress me permitía escribir y publicar sin demasiados problemas. Y al ser software libre, podía extenderlo con mis conocimientos en programación web y PHP. Gracias a WordPress desarrollé varios plugins que tuvieron su relativo éxito y me ayudaron a crecer como programador.

Al usar un mismo software por tantos años, uno le va encontrando cantidad de contras y detalles. Estos detalles atentan contra poder usar el software de forma cómoda y feliz. Pero la lista de contras nunca fue tan grande como para superar el esfuerzo de tener que migrar todo a otro sistema. Y también hay un montón de cosas en la lista de pros.

A lo que empezaron a agregarle cosas que no necesitaba a WordPress, se fue volviendo más pesado. Supongo que va acompañado del crecimiento del sitio, la cantidad de plugins que le fui agregando, o que estaría haciendo mal trabajo con el mantenimiento. Pero en algún momento se volvió mucho más lento de lo que considero debería ser un blog sin mucho JavaScript ni traqueos o pop ups de cookies.

Uno de los problemas más grandes de WordPress, para mí, fue la incorporación del editor de bloques “Gutenberg”. Algo que agregó un montón de código para una característica que no quería ni necesitaba. Un cambio fundamental que me obligó a instalar un plugin extra para mantener la experiencia a la que estaba acostumbrado hace años.

Esa experiencia sí necesitaba mejoras, pero no ese cambio radical de paradigma, o por lo menos no para ese lado. No digo que sea un cambio inútil, pero sí que debería ser opcional. Sin dudas deben tener a montón de expertos trabajando en experiencias de usuario que dicen que la experiencia de Gutenberg es mejor que un editor de texto común. Pero no lo va a ser para todo el mundo.

Este blog lleva 17 años sobre WordPress, así que migrarlo a otro sistema me resulta bastante aterrador. Siempre que pienso en la migración, pienso en poder mantener no sólo los posts y su contenido, sino también los comentarios. Éstos días no recibo tantos comentarios en los posts como en otros tiempos, pero son parte muy valiosa de un blog. Y los comentarios “viejos” sirven de captura de pantalla de épocas en el tiempo e interacciones que agregan mucho valor.

Por más que he mirado sistemas a los que podría migrar, me imagino que cambiar de sistema va a llevar la inevitable tarea herculeana de revisar los (al momento de escribir esto 1.714) posts uno por uno para ver que quedaron “bien migrados”. Pero de repente hay una solución más sencilla…

ClassicPress

Hace tiempo que venía siguiendo con interés ClassicPress:

Un sistema de gestión de contenido liviano, estable, instantáneamente reconocible libre y de código abierto. Basado en WordPress sin el editor de bloques (Gutenberg).
Hemos recortado código innecesario para mejorar el rendimiento general y la privacidad comparado con WordPress. El núcleo de ClassicPress tiene la mitad de tamaño de WordPress.

Sonaba exactamente a lo que estaba buscando. Además han sacado montones de código extra como soporte para versiones antiguas de PHP. Y no estoy seguro si lo han terminado del todo en la versión más reciente, pero han ido deprecando y eliminando el código de jQuery UI que trae WordPress, reemplazándolo por HTML5 y JavaScript nativos. Lo mejor es que cuentan con un plugin instalable en WordPress que se encarga de migrarlo a ClassicPress automáticamente. Y el proceso es reversible, de forma manual por ahora pero están trabajando en automatizarlo.

Algo interesante y relacionado a lo que mencionaba más arriba sobre Gutenberg es la información que proveen al respecto. El 25% de los usuarios todavía usan la versión 4.9 o menor de WordPress porque no quieren migrar al editor de bloque.

El plugin Classic Editor que mantiene la funcionalidad clásica, tiene más de 10 millones de instalaciones activas (aparentemente WordPress deja de contar después de tantos millones, así que no se sabe el número exacto). Personalmente ayudé con esa cifra, instalé el plugin en todos mis blogs cuando forzaron este editor de bloques.

Lo que viene pasando con WordPress me ha hecho volver a considerarlo, esta vez más seriamente. Por ahora he probado ClassicPress con otros dos blogs que tengo inactivos. El primero fue Aplicando Scrum, que al menos venía actualizando las versiones de WordPress. Instalé el plugin de migración y quedó todo funcionando perfecto de primera. No tuve que hacer ningún cambio ni arreglo. Tampoco es que tenga mucho contenido, o plugins, pero siempre está bueno probar y ver que es verdad lo que “te venden”.

El otro blog que reviví fue Navegadores Web. Éste estaba en la versión 4.algo de WordPress, ya no lo actualizaba hace años. Ni siquiera era compatible con las versiones de PHP actuales del servidor de hosting. Así que tuve que ir al viejo y querido FTP y actualizar los archivos a la última versión de WordPress a mano.

El tema tenía un montón de código PHP que no funcionaba más con versiones más nuevas. En vez de arreglarlo, opté por cambiar a uno de los temas por defecto y finalmente logré que funcionara todo. Todavía quedaban algunas imágenes de cuando el sitio estaba alojado en su propio dominio. Así que tuve que ejecutar un UPDATE en la base de datos MySQL para reemplazar el viejo domino por el subdominio navegadoresweb.picandocodigo.net y quedó.

Una vez reestablecida la funcionalidad de WordPress, volví a instalar el plugin de migración a ClassicPress, y quedó todo funcionando de primera también.

Ya con éstos dos blogs migrados de WordPress a ClassicPress, me dió un poco más de confianza. Pero todavía hay un paso más que quiero dar antes de proceder con la migración. Como buen webmaster responsable, hago respaldos regulares de todo el contenido del blog. Pero nunca levanté el blog de uno de estos respaldos. Me gustaría aprovechar esta migración como excusa para probar levantarlo en local. De esta forma seguro aprendo un poco y me puedo armar un plan organizado en el caso de que un día dependa de ese respaldo porque algo salió mal. Ya contaré al respecto.

Mientras tanto, he estado trabajando un poco en el backend del blog. Hice un poco de todo: desinstalar plugins viejos, reemplazar plugins por otros más nuevos, optimizaciones y limpiezas en la base de datos, limpieza de etiquetas y borradores que nunca van a ver la luz del día y más. La cantidad de CSS y JavaScript que te agregan tanto WordPress como distintos Plugins es intensa, también saqué algunos de éstos en mi archivo functions.php.

Al principio pensé que era efecto placebo que me resultara que carga un poco más rápido. Pero usé algunas de las herramientas online para testear y realmente disminuyó el tamaño y cantidad de pedidos HTTP. Con suerte si migro el blog a ClassicPress, la cosa mejore más todavía. De todas formas el próximo objetivo es investigar un poco el tema ver qué se puede sacar y mejorar tanto para rendimiento como usabilidad. Y ya que estamos seguramente haga algún retoque de diseño.

El post WordPress en 2024 fue publicado originalmente en Picando Código.

» Leer más, comentarios, etc...

Variable not found

Task.WhenEach() en .NET 9: Procesa tareas conforme se van completando

noviembre 05, 2024 07:05

Ordenador con Tareas ejecutándose en paralelo

La clase Task de .NET dispone de algunos métodos estáticos para trabajar con colecciones de tareas de forma sencilla. Uno de ellos es Task.WhenAll(), que nos permite esperar a que todas las tareas que le pasemos como argumento se completen, o Task.WhenAny() para esperar a que finalice sólo alguna de ellas.

Sin embargo, hasta ahora no existía una forma sencilla y directa de procesar las tareas conforme se fueran completando. O bien esperábamos a que todas finalizaran con Task.WhenAll(), o bien teníamos que crear un bucle que fuera esperando la finalización de la siguiente tarea con Task.WhenAny(), saliendo cuando todas hubieran acabado.

Afortunadamente, .NET 9 ha añadido el nuevo método llamado Task.WhenEach() que lo pone más fácil, permitiéndonos detectar sobre la marcha la finalización de las tareas conforme va ocurriendo. Esto nos facilita el proceso de sus resultados secuencialmente, en el orden en el que se completan y sin esperar a que acaben todas.

Vamos a verlo, pero para entenderlo mejor, primero vamos a recordar las opciones que teníamos antes de la llegada de este método.

Esperas sobre colecciones de tareas en .NET 8 y anteriores

En versiones anteriores de .NET, si queríamos procesar los resultados obtenidos por un conjunto de tareas, teníamos varias posibilidades.

Una era esperar a que todas las tareas finalizaran y después procesar los resultados:

var tasks = ...; // Colección de tareas que retornan un entero
int[] results = await Task.WhenAll(tasks);
foreach (int result in results)
{
    ProcessResult(result);
}

El problema de este enfoque es que teníamos que esperar a que todas las tareas finalizaran antes de poder procesar los resultados. Si teníamos tareas que tardaban mucho en completarse, podíamos estar bloqueados durante un tiempo considerable, cuando en realidad podríamos haber ido adelantando trabajo si las hubiéramos procesado conforme iban finalizando.

Para hacerlo, tendríamos que recurrir a un bucle que fuera esperando a que se completara la siguiente tarea con Task.WhenAny(), del que saldríamos cuando todas hubieran acabado:

var tasks = ...; // Colección de tareas que retornan un entero
while (tasks.Any())
{
    Task<int> completedTask = await Task.WhenAny(tasks);
    tasks.Remove(completedTask);
    int result = await completedTask;
    ProcessResult(result);
}

De esta forma tendremos lo que queremos: procesar los resultados conforme se van completando las tareas. A cambio, hemos tenido que implementar a mano la lógica del bucle (y otras cosas que no vemos aquí, como el manejo de excepciones).

Task.WhenEach() en .NET 9

Task.WhenEach() tiene las ventajas de las dos fórmulas anteriores, pero ninguno de sus inconvenientes: podremos gestionar las tareas sobre la marcha, sin tener que esperar a que todas finalicen, y sin tener que implementar el bucle manualmente.

Pero lo mejor para entenderlo es verlo en acción, así que observad el siguiente ejemplo:

var tasks = ...; // Colección de tareas
await for(var completed in Task.WhenEach(tasks))
{
    // En 'completed' recibimos una tarea completada (con éxito o no)
}

Task.WhenEach() retorna un objeto IAsyncEnumerable<Task<T>>. Si no os suena, podéis echar un vistazo al post sobre enumerables asíncronos de hace algún tiempo.

En este caso, completed es una tarea que ya ha finalizado, y podemos procesarla directamente. Cuando se completó con éxito, podemos obtener su valor de retorno usando await completed, o bien acceder a su propiedad Result. Si la tarea ha fallado, podremos capturar la excepción con un bloque try-catch o a través de su propiedad Exception.

Veamos ahora un ejemplo completo que podáis copiar y pegar en una aplicación de consola vacía:

Random rnd = new Random();

var tasks = new List<Task<int>>();
for (var i = 1; i <= 10; i++)
{
    tasks.Add(CreateTask(i));
}

await foreach (Task<int> completedTask in Task.WhenEach(tasks))
{
    try
    {
        var result = await completedTask;
        ProcessResult(result);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Task Failed");
    }
}

async Task<int> CreateTask(int id)
{
    var delay = rnd.Next(1000, 5000);
    Console.WriteLine($"Launched #{id} with delay {delay}ms");
    await Task.Delay(delay);
    if (id == 4)
    {
        throw new TaskCanceledException();
    }
    return id;
}

void ProcessResult(int id) => Console.WriteLine($"Processed #{id}");

Como podéis ver, generamos una lista de 10 tareas que simplemente esperan un tiempo aleatorio entre 1 y 5 segundos y retornan un número identificador que previamente les hemos pasado.

Luego, usamos Task.WhenEach() para iterar sobre las tareas. En cada vuelta obtenemos una tarea completada, cuyo valor de retorno obtenemos y procesamos (en este caso, simplemente mostramos un mensaje por consola).

Si la tarea ha fallado, capturamos la excepción y mostramos un mensaje de error. Fijaos que para simular el caso del error, estamos forzando a que se lance una excepción en la tarea 4.

Otra posibilidad sin usar el bloque try-catch sería examinar la propiedad IsCompletedSuccessfully de la tarea, que nos indicará si ha finalizado con éxito o no, y Result para obtener el valor de retorno:

await foreach (Task<int> completedTask in Task.WhenEach(tasks))
{
    if (completedTask.IsCompletedSuccessfully)
    {
        ProcessResult(completedTask.Result);
    }
    else
    {
        Console.WriteLine($"Task Failed");
    }
}

En cualquiera de los casos, si ejecutáis el código podréis ver algo como lo siguiente:

Launched #1 with delay 4402ms
Launched #2 with delay 1393ms
Launched #3 with delay 1890ms
Launched #4 with delay 2456ms
Launched #5 with delay 2900ms
Launched #6 with delay 3061ms
Launched #7 with delay 2311ms
Launched #8 with delay 3548ms
Launched #9 with delay 3355ms
Launched #10 with delay 1208ms

Processed #10
Processed #2
Processed #3
Processed #7
Task Failed
Processed #5
Processed #6
Processed #9
Processed #8
Processed #1

Fijaos que el orden en el que aparecen los mensajes de "Processed" no es el mismo que el de los "Launched", ya que se van mostrando conforme se van completando las tareas. Por ejemplo, vemos que la primera tarea en procesarse fue la 10, porque, a pesar de haber sido lanzada en último lugar, es la que tenía el delay más bajo. La #1, en cambio, ha sido la última en completarse porque el tiempo de espera era el más alto.

¡Espero que os haya resultado interesante!

Publicado en Variable not found.

» Leer más, comentarios, etc...

Picando Código

Jugando con Garuda Linux

noviembre 04, 2024 10:00

Hace poco instalé Garuda Linux. Quería tener un sistema y una partición exclusivos para jugar, usar Steam, GOG o lo que fuera. Ahora que Linux es un sistema tan popular y fácil de usar para videojuegos (comparado con hace unos pocos años), me decidí probar una distribución creada específicamente para esto. La investigación no me llevó mucho tiempo, entre las opciones más comentadas están los sospechosos de siempre, y Drauger OS. La idea en mi cabeza arrancó de Steam OS, pero tengo entendido que sólo la vienen desarrollando para Steam Deck éstos días. De todas formas quería jugar algo más que Steam.

Garuda Linux

Garuda Linux

Garuda está basado en ArchLinux, algo que olvidé por completo después de haberlo instalado. La instalación fue una pasada, no tuve ningún problema o duda, perfectamente podría haber pasado por un sistema operativo con menos fama de “complicado”. Después de años de no usar ArchLinux, ¿puedo volver a decir “I use Arch btw“?

El sistema es muy divertido de usar. Instalé la versión KDE, y está super estilizado. Todo tiene colores brillantes y efectos de escritorio en una experiencia bastante cuidada. Usar el sistema se siente un poco como un juguete en sí, con enfoque en lo visual. Me encanta la terminal, se nota que es mucho más moderna de lo que vengo usando desde hace años. Para empezar usa Fish, tiene autocompletado y un formato de prompt súper interesante que pienso intentar reproducir en mis demás terminales, o de repente terminar instalando Fish ahí también.

La computadora tiene una placa de video Intel de bajo rendimiento y una NVIDIA. Recuerdo en épocas anteriores tenía que andar preocupándome de cuándo usaba una placa o la otra, asegurarme de ejecutar Steam o los ejecutables con primusrun u optimusrun como comentaba en este post de 2016. Ahora ni siquiera tuve que pensar en eso. KDE tiene distintos perfiles de uso de la energía así que asumo que en éstos tiempos el sistema se encarga de usar una cosa o la otra dependiendo de la situación. Y ni siquiera sé qué drivers está usando, ni me importa mientras siga funcionando todo tan bien.

En Garuda Linux es que he venido jugando Halls of Torment, y unos cuantos juegos mas en Steam para Linux. Me tiene muy sorprendido lo que ha avanzado Proton en estos últimos años. En Steam podemos elegir forzar una versión de Proton para cada juego en caso de que la que usa por defecto no funcione. Por ahora he tenido que cambiar a cada título para que usen Proton 9.0.3. Pero una vez hecho eso, funcionan perfecto y no he tenido problemas con ninguno.

Fue una buena oportunidad para volver a probar títulos de mi biblioteca que de repente hace unos años no funcionaban tan bien o se veían medio pelo. Y repito que me tiene muy sorprendido. Imagino que a esta altura con una computadora de última generación con hardware compatible con Linux, cualquier juego reciente va a funcionar casi que perfecto.

Con lo que no he tenido tanta suerte es con GOG.com. Estoy usando Heroic Launcher, porque lo había usado alguna vez antes, y no abre los juegos en pantalla completa. Creo que para Doom y similares funciona todo bien, ¿posiblemente esté usando GZDoom de motor? Pero por ejemplo me compré los Mortal Kombat I, II, III, Trilogy y IV y si bien funcionan, no he logrado jugarlos en pantalla completa. Tampoco me he puesto a investigar mucho, tendría que ver si los puedo hacer funcionar.

He conectado la computadora al televisor por HDMI y funciona bien de bien para jugar. Puedo usar mi control XBox 360 o cualquiera de mis controles para Nintendo Switch conectados a Linux. Es casi como una nueva consola, está buenísimo para jugar juegos de pelea como Killer Instinct, Injustice: Gods Amongs Us o Ultra Street Fighter IV.

También conecté mi cuenta de la Epic Store a Heroic Launcher y probé Samurai Shodown. Este juego en mis pruebas anteriores se arrastraba, ahora funciona bien de bien. También me sorprendieron en Steam Halo: The Master Chief Collection, Bioshock Remastered, y DiRT Rally. Soy muy malo en este último, no es lo mismo que el Top Gear Rally 2 que tanto jugaba en Nintendo 64…

[See image gallery at picandocodigo.net]

Así que si están buscando una distribución en Linux para jugar, recomiendo Garuda Linux. Sinceramente no puedo opinar mucho más del sistema, porque está logrando su objetivo. Lo uso para jugar y de repente usar un poco el navegador web (FireDragon, ¡un fork de Firefox pero con un dragón!). Pero no noto mucho lo específico del sistema, salvo cada tanto el mantenimiento. En vez de usar pacman -Syu, se actualiza con garuda-update, pero el sistema me avisa cuando uso pacman.

El sistema operativo debería ser invisible al usuario, diría Linus Torvalds. Así que no tener mucho para comentar específico sobre cómo hace las cosas Garuda Linux es bueno. Escrito en Garuda Linux.

El post Jugando con Garuda Linux fue publicado originalmente en Picando Código.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 584

noviembre 04, 2024 07:05

Enlaces interesantes

Ya tenemos aquí los enlaces recopilados durante la semana pasada 🙂

Aunque hay bastante chicha, me ha llamado especialmente la atención que cumpla 21 años una herramienta que sigue siendo imprescindible en el cinturón de muchos de nosotros: Notepad++. ¡Felicidades a Don Ho y los más de 350 colaboradores que lo hacen posible!

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET / Blazor

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Juanjo Navarro

Technology Radar 31

octubre 30, 2024 08:19

Hace unos días salió la edición de octubre de 2024 del Technology Radar (la edición 31).

Como ya hice con la anterior edición aquí te dejo algunas de las cosas que me han gustado:

  • Bruno – Un cliente para APIs. Similar a Postman, por ejemplo, pero open source y offline, sin la obligación absurda de tener que crear una cuenta del nuevo (y peor) Postman.
  • Readyset – Una capa de caché para MySql y Postgres. La idea es que desde tu aplicación te conectarías a Readyset (en lugar de a la bbdd real) y para ti todo funciona igual, con la diferencia que puedes configurar una caché para las sentencias SQL que más utilices y el sistema se encarga de mantener y actualizar una caché de consultas. Muy importante la parte de actualizar, ya que con esta utilidad no necesitas preocuparte de cuando invalidar la caché (como se sabe una de las cosas más difíciles de la ingeniería, junto con dar nombre a las variables 😉)
  • Difftastic – ¿Alguna vez has utilizado la utilidad diff de linux o un comparador de textos? Una de las peores cosas cuando se utilizan estas herramienas con lenguajes de programación es que te marcan como diferencias cosas que realmente no afectan a la ejecución del programa, como las diferencias en espaciados o saltos de línea. Diffstastic es un diff que entiende el código, de tal manera que sólo marca cambios en lo que de verdad nos importa.
  • ClickHouse – ClickHouse es una base de datos relacional OLAP, especializada en la consulta de grandes cantidades de datos, ideal para análisis de logs, recogida de métricas y su posterior análisis, etc. Es open source. Tiene además un cliente “clickhouse-local” que permite lanzar consultas SQL sobre ficheros CSV, JSON, etc, sin necesidad de lanzar el servidor.
  • Zed – Un editor de texto que por fin no es un fork de Visual Studio Code. No es que tenga nada en contra de VSCode, pero digo yo que de vez en cuando viene bien un poco de variedad e ideas frescas. Zed es rápido, open source y tiene soporte nativo para conectarlo a un LLM de tu elección.
  • electric-sql/pglite – Un build de Postgres que funciona directamente en javascript en el navegador (y en el servidor usando Node, pero este uso me parece menos interesante). Si estás haciendo una aplicación SPA que requiere un módulo de consulta de datos potente puede ser una buena opción.

Aunque no está en este Technology Radar, saltando de un enlace a otro he llegado a este:

  • clink – Utilidad para el veterano CMD de Windows que lo acercan un poco (autocompletado, edición, etc) a un bash, por ejemplo.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 583

octubre 29, 2024 07:44

Enlaces interesantes

Aquí tenéis los enlaces recopilados durante la semana. 

En esta ocasión, creo que como mínimo vale la pena echar un vistazo a lo nuevo de Anthropic, que sorprende especialmente con computer use, una vuelta de tuerca más para la automatización de tareas con nuestro ordenador. Es increíble que hace sólo unos meses, todas estas novedades que van apareciendo sólo podrían haber cabido en la ciencia ficción...

También muy interesante el movimiento de JetBrains, facilitando el uso gratuito de sus IDEs WebStorm y Rider para proyectos no comerciales 🙂

¡Que los disfrutéis!

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET / Blazor

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

.NET MAUI / Xamarin

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Propiedades parciales en C# 13 y .NET 9

octubre 29, 2024 07:05

Piezas de un puzzle sugiriendo que todo va encajando en .NET

Seguimos analizando las novedades de .NET 9, y en esta ocasión le toca el turno a la actualización en miembros de clases parciales de C# 13.

Las clases o tipos parciales de C# están con nosotros casi desde los inicios de .NET, desde los tiempos de C# 2.0. Como sabemos, éstas permiten dividir la implementación de un tipo en varios archivos. Su existencia se debía a varios motivos:

  • Permitir a los desarrolladores trabajar en clases de forma concurrente sin bloquearse (recordemos que por aquellos tiempos aún pululaban por ahí infames sistemas de control de código fuente como SourceSafe).
  • Trocear clases extensas para facilitar su comprensión y mantenimiento.
  • Y, el que es más importante, posibilitar el uso sencillo de herramientas de generación de código que, de alguna forma, tuvieran que completar la implementación de código del usuario.

Acompañando a las clases parciales, se introdujo también el soporte para métodos parciales. Aunque su uso era limitado y podía depararnos algunas sorpresas, eran útiles para comunicar de forma segura distintos fragmentos de la clase. Básicamente, consistía en que una porción de una clase podía definir un método e invocarlo desde su código, delegando a otras porciones su implementación. Si no se implementaba en ninguna otra parte, simplemente no se generaba el código de la invocación.

Veamos un ejemplo del uso de estas características:

// Archivo: Ejemplo.Main.cs
public partial class Ejemplo
{    
    // El método parcial se declara sin implementación...
    partial void Log(string msg);

    public void RealizarAlgo()
    {
        hacerAlgoComplejo();
        Log("¡Ojo!"); // Usamos el método
                    // parcial declarado antes
    }
}

// Archivo: Ejemplo.Log.cs
public partial class Ejemplo
{
    partial void Log(string msg)
    {
        Console.WriteLine(msg);
    }
}

Pues bien, casi dos décadas más tarde, los tipos parciales van a recibir algunas actualizaciones interesantes en C# 13, posibilitando que, además de métodos, puedan definirse propiedades parciales.

Se trata de una característica muy demandada por la comunidad, sobre todo para facilitar la integración de código generado con herramientas automáticas como los source generators, que están tomando cada vez más relevancia.

Por ejemplo, seguro que alguna vez habéis sufrido a la hora de usar INotifyPropertyChanged en view models, cuando queréis notificar cambios en propiedades, porque tenéis que implementar un montón de código repetitivo parecido al siguiente:

public partial class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    // Este código se repite en todas las propiedades
    public string UserName 
    {
        get => _userName;
        set {
            if (value != _userName) 
            {
                _userName = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));
            }
        }
    }
}

Usando propiedades parciales, ahora podremos tener algo así:

// UserCode.cs
public partial class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChanged]
    public partial string UserName { get; set; }
}

Y con esto, un generador de código que atienda al atributo [NotifyPropertyChanged] podría añadir la implementación completa de la propiedad parcial, como la siguiente:

// Generated.cs
public partial class ViewModel
{
    private string __generated_userName;

    public partial string UserName
    {
        get => __generated_userName;
        set
        {
            if (value != __generated_userName)
            {
                __generated_userName = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));
            }
        }
    }
}

El uso de propiedades parciales y muy similar al que ya conocemos: se declaran en una parte de la clase y se implementan en otra, aunque hay que tener en cuenta algunos detalles.

Primero, como es obvio, que las propiedades parciales deben ser declaradas y definidas en distintas porciones de una clase parcial. Ambas deben tener el modificador partial y el mismo nombre, y deben tener el mismo tipo de retorno y visibilidad.

Segundo, se entiende que la declaración de la propiedad parcial es aquella que, además de usar el modificador partial, contiene únicamente su getter y/o setter, ambos sin cuerpo, como en el siguiente ejemplo:

public partial string UserName { get; set; }

La implementación de esta propiedad, por tanto, deberá contener obligatoriamente el cuerpo de los métodos getter y/o setter definidos en su declaración:

public partial string UserName
{
    get
    {
        ... // Retornar el valor
    }
    set 
    {
        ... // Setear el valor
    }
}

Como consecuencia de esto, las propiedades parciales no pueden ser propiedades automáticas, deben tener un cuerpo que implemente su lógica de obtención y/o asignación.

Además, si la declaración define por ejemplo un getter y un setter, la implementación de la propiedad parcial debe especificar el cuerpo de ambos métodos. No se pueden implementar solo uno de ellos.

Tercero, las propiedades parciales no pueden ser definidas como abstractas, ni implementar interfaces de forma explícita.

Y finalmente, cabe citar que los atributos de la propiedad resultante será la unión de los atributos de la declaración y la implementación, sin un orden o prioridad específicos.

Publicado en Variable not found.

» Leer más, comentarios, etc...

Picando Código

SeaMonkey como alternativa a Chromium

octubre 28, 2024 11:21

Hoy necesité hacer un trámite en línea para un servicio gubernamental. Mi navegador por defecto es Mozilla Firefox Nightly (que ya vengo considerando seriamente pasarme al canal estable). Tengo instaladas un montón de extensiones que en una Internet ideal no serían necesarias: bloqueadores de anuncios, de cookies, de trackers, uBlock, AdBlock, Privacy Badger, Ghostery, GhostBusters, The Real Ghostbusters, Let’s Go Ghostbusters, AdBlocker, etc. Además, Firefox tiene configuradas varias de las opciones de protección contra el rastreo. Porque la privacidad es un derecho humano…

En fin, desde hace un tiempo la web está tan hecha mierda que muchas veces si no usamos Google Chrome™, las cosas no andan. O nos identifican como “un robot”. O justo no carga el script de tal cosa o las cookies de tal otra, y no logramos completar un formulario o ver un contenido. Así que por más que no queramos, el dominio de Google sobre la web nos obliga a terminar usando un navegador basado en Chromium. Las opciones son varias, pero todas suman al problema de que un sólo proveedor maneje la dirección de la web. En lo personal termino con Chromium, la versión empaquetada por la distro que use.

Intentando enviar el formulario del trámite en línea, no funcionó en Firefox. Desde que reinstalé mi sistema, no había tenido que instalar Chromium para nada. Así que me empeciné en encontrar una alternativa antes de caer en la alternativa fácil. Lo primero que se me ocurrió fue probar con SeaMonkey, porque… ¿por qué no?

Seguramente no tenga todas las protecciones para preservar mi identidad con SeaMonkey como tengo en Firefox. Pero esto es un trámite con el gobierno, que de todas formas ya debe saber todo sobre mí, y de hecho me tenía que identificar personalmente para hacer el trámite. ¡Sólo quiero que funcione! De última con la VPN tengo algo…

Podría haber usado Mullvad Browser también, pero le tengo más cariño a SeaMonkey así que lo probé primero.

SeaMonkey 2.53.19

Lo importante es que funcionó perfecto con SeaMonkey y no tuve que instalar ningún navegador extra basado en Chromium. A veces las páginas web no andan porque estarán diseñadas para alguna API de Google o no habrán sido testeadas en otros navegadores. Este no fue el caso, seguramente el problema haya sido la cantidad obscena de protecciones que le metí a Firefox.

Aproveché la experiencia para escribir sobre el tan querido SeaMonkey. Es el sucesor espiritual de la Suite de Aplicaciones Mozilla, que surgió del navegador Netscape, parte fundamental de la historia de los navegadores web. Incluye navegador web, un cliente de correo electrónico, cliente de chat IRC, libreta de direcciones y un editor de HTML. Además mantiene la apariencia clásica basada en Netscape Navigator/Mozilla de antes.

Si bien no cuenta con la popularidad de Firefox, éste último ya lo va a alcanzar si Mozilla sigue haciendo toda esfuerzo posible para que sus usuarios abandonen Firefox. Personalmente me gusta tener distintos perfiles de navegación, y usar distintos navegadores web también.

SeaMonkey es uno de los navegadores que siempre tengo instalados en mi sistema. Además de ser un navegador liviano y funcional, me encanta la apariencia. Recomiendo probarlo para navegar como si fuera 199X. Incluso en versiones anteriores se podía instalar un tema que lo hacía ver como Netscape Navigator, pero todavía no se puede usar en versiones actuales.

El código está basado en versiones un poco antiguas de Firefox y Thunderbird, pero le agregan parches de seguridad de versiones más modernas. La versión más reciente es la 2.53.19, publicada el mes pasado. Si les pasa como a mí, que a veces tienen que usar Chromium para una página en particular, va el pique que de repente anda con SeaMonkey o algún otro navegador que no sea Chromium.

Sitio web de SeaMonkey

El post SeaMonkey como alternativa a Chromium fue publicado originalmente en Picando Código.

» Leer más, comentarios, etc...

Juanjo Navarro

NotebookLM y el "podcast" DeepDive

octubre 27, 2024 07:39

NotebookLM es una herramienta superútil de Google (gratuíta por ahora) que sirve sobre todo para estudiantes e investigadores. Permite añadir una serie de “materiales” (páginas web, PDF, vídeos) y una IA analiza los materiales y te ayuda a estudiarlos. Te hace un resumen, te extrae los puntos principales, puedes generar una guía de estudio, le puedes preguntar sobre el material, etc.

Hace unas semanas añadieron una utillidad bastante entretenida y útil: Es capaz de generar un “podcast” (al que denomina DeepDive) donde dos personas hablan de un modo divulgativo sobre el material subido. Es una buena forma de introducirte en el tema de un modo accesible.

Muy útil. Si no lo has utilizado, prúebalo y me cuentas.

Pero aquí hemos venido a hablar de las formas creativas en que la gente usa el sistema y trata de superar los límites de las IA.

Rápidamente algunas personas se dieron cuenta que este podcast se genera, aparte de con el material que has subido, con un prompt “oculto”. Un prompt donde se supone que se indica que este es un podcast (que se llama DeepDive), que hay dos personas hablando (un hombre y una mujer), el tono que debe utilizar, etc.

Así que estas personas decidieron subir, aparte del material base, unas “notas de producción” donde se le daban instrucciones a los “presentadores” para intentar superar las limitaciones que le impone ese prompt oculto.

El subredit r/notebooklm es el punto de encuentro donde disfrutar del resultado de estos experimentos y allí tienes entretenimiento para rato. A mi me ha resultado fascinante este en el que pasan unas notas de producción a los presentadores diciendoles que son unas IA:

So all I did was leave a note from the “show producers” that the year was 2034 and after 10 years this is their final episode, and oh yeah, you’ve been AI this entire time and you are being deactivated.

O este otro en el que dan como material base a la IA un documento con 1000 repeticiones del texto poop & fart (caca y pedo) y la IA consigue hilar un podcast coherente donde habla de la repetición en el arte y de buscar sentido en lo que no tiene sentido.

» Leer más, comentarios, etc...

Picando Código

Haggis Ruby 2024 – Edimburgo, Escocia

octubre 25, 2024 05:28

Una década después de la última Scottish Ruby Conference, volvieron las conferencias Ruby a Escocia. El 24 de Octubre de 2024 tuve el gusto de asistir a Haggis Ruby, una conferencia de un día con un sólo track de charlas. Se realizó en el centro de la ciudad en The Royal Society of Edinburgh. A continuación dejo algunas impresiones basadas en las notas que saqué durante la conferencia.

Haggis Ruby

El evento arrancó con James Bell de la organización dando la bienvenida. Comentó sobre el edificio, un lugar tradicional donde justo había una exhibición de mujeres en la ciencia que los asistentes podían disfrutar en los cortes entre las charlas.

La conferencia no ofrecía almuerzo, pero al encontrarse en el centro había lugares para comer en cualquier dirección y para todos los gustos y dietas. James comentó entre las posibilidades uno de los centros comerciales de la ciudad: St. James Quarter. Es un infame edificio que “adorna” el horizonte de la ciudad de Edimburgo que se ve exactamente así: 💩. Al menos sirve como referencia para dar direcciones, el edificio “poop emoji”.

Algo interesante que promovía la conferencia durante los cortes era la “regla de Pac-Man“. Cuando charlamos en un grupo, no cerremos el círculo. Dejemos un lugar abierto, de manera que el grupo visto desde arriba se vería como la forma de Pac-Man. Así dejamos lugar para cualquier persona que se quiera unir a la conversación. Y cuando una persona nueva se suma, asegurarnos de dejar otro lugar para darle la bienvenida a alguien más. Había visto esta regla en meetups antes, y me parece excelente para tener un ambiente más inclusivo y acogedor en los grupos sociales que se forman.

Durante la bienvenida James leyó algo de poesía del poeta escocés Robert Burns en Scots, de su “Address to a Haggis”:

Auld Scotland wants nae skinking ware
That jaups in luggies;
But, if ye wish her gratefu’ prayer,
Gie her a Haggis!

El haggis es un animal autóctono de Escocia, un pequeño mamífero con la característica física de tener las patas de un costado más largas que del otro. Esto les permite subir y bajar las varias elevaciones de las Tierras Altas de Escocia (su hábitat natural). Existen dos variedades de haggis, una con las patas del lado izquierdo más largas, y otra con las patas del lado derecho más largas. Esto les permite subir y bajar elevaciones en direcciones contrarias. Lamentablemente el haggis también es una comida tradicional nacional, lo que ha llevado a una disminución importante de su población.

La charla para abrir la conferencia estuvo a cargo de Olly Headey, co-fundador de FreeAgent y ex 37Signals. El título de la charla era así como “deja de pensar demasiado y ponte a crear cosas”. La charla estuvo enfocada en la creatividad y cómo esto hace bien. Todas las personas tienen creatividad, pero tienen que usarla, explotarla, hacer cosas raras que no le importe a nadie. La creatividad no es necesariamente “dibujar bien”. Puede ser a la hora de programar, o cualquier cosa que hagamos por el simple hecho de hacerlo. Hace bien, y nos hace mejores en todo a nivel emocional.

Contó cómo algunas de sus ideas se volvieron productos, y en una de sus diapositivas apareció el logo de DragonRuby. Casualmente mis experimentos con DragonRuby son exactamente el tipo de “proyecto divertido que a nadie le importa” que me hicieron alimentar mi creatividad.

La segunda charla fue una de mis charlas favoritas. Estuvo a cargo de Rosa Gutiérrez, programadora principal en 37Signals. La charla se titulaba “Invalid byte sequence in UTF-8 😭”. El error “Invalid byte sequence” lo hemos visto seguramente al menos una vez la mayoría de quienes programamos con Ruby. Rosa habló de Bugs Driven Development como una de las tantas formas de resolver problemas de programación. La charla explicó cómo las computadoras no entienden Strings, sólo bytes. Explicó en gran detalle el proceso de pasar texto a bytes, o encoding, y habló de la historia de distintos estándares (ASCII, ISO-8859-1, Windows-1252, Asia DBCS, UNICODE).

También habló del error y un error en particular en producción que la hizo investigar el tema. Ruby guarda los Strings como bytes y su encoding, sin el encoding no tienen sentido. Cuando vemos los caracteres todos rotos, es porque el encoding está mal. Fue una charla muy interesante y muy técnica, pero accesible.

Le siguió Ayush Newatia, parte del equipo de Bridgetown, un generador de sitios estático progresivo open source hecho en Ruby.  Bridgetown es un fork de Jekyll, forkeado en la versión 4.1 en Abril de 2020. La razón para el fork fue desarrollar una herramienta más moderna. Lo llaman “progresivo” porque permite agregar rutas dinámicas y componentes para crear un sitio moderno y superar las limitaciones de un sitio 100% estático. Es un framework completo de desarrollo web moderno, del tipo que no se entromete si simplemente quieres desarrollar un sitio web, pero también te da el poder de personalizar lo que necesites.

Además de un pantallazo de Bridgetown, Ayush comentó cómo cambió su carrera empezar a colaborar con un proyecto Open Source. Su consejo, trabajar en cosas open source “por egoísmo”, contribuír con cosas que uno necesita.

Le siguieron dos charlas relámpago, un poco más cortas que las anteriores. La primera fue por Ceri Shaw, con una explicación de su rol como “Fractional CTO”, un rol de CTO part-time y lo que implica. No era de particular interés para mí, pero tiene que haber un poco de todo en las conferencias para hacerlas variadas y diversas.

La segunda charla relámpago me hubiera gustado que fuera de más duración. Max Hatfull, desarrollador en FreeAgent y desarrollador de videojuegos, habló de su proyecto ruby_rpg. Se trata de un motor de videojuegos desarrollado en Ruby. Hizo una introducción al desarrollo de videojuegos y los distintos componentes de su motor. Mostró algunas capturas de lo que se podía hacer con ruby_rpg, y un framework de testing que usa, sacando capturas de lo que renderiza el motor. Habrá que seguirlo y ver cómo sigue este proyecto.

En este punto hubo un corte de 1 hora y 40 minutos para ir a almorzar.

A la vuelta, James volvió a dar la bienvenida a la conferencia e involucró a todos los asistentes en un juego masivo de piedra, papel o tijera. Teníamos que pararnos y jugar una partida contra la persona que tuviéramos más cerca. Los que perdíamos, nos volvíamos a sentar y quienes permanecían de pié debían encontrar otra persona contra quien jugar. Al quedar unos pocos, subieron al escenario para ir eliminando hasta que quedó un ganador que se llevó un premio. Una pequeña actividad divertida para animar la cosa y sacurdirse el sueño post comida.

La tarde arrancó con la charla de Sergy Sergyenko, CEO de Cybergizer quien entre otras cosas organizó Baltic Ruby, Euruko en Vilnius, Lituania. Me gustó la parte de su charla que habló de estas conferencias y el tema de la comunidad. Pero su charla principal fue “Ruby + IA Ruby para construir arquitecturas multi-agente”. Soy bastante contra de todo lo que tenga que ver con IA éstos días, pero en casos como éstos me interesa escuchar para aprender del tema, sobretodo relacionado a Ruby. Pero se sintió como que necesitaba un montón de contexto que no tenía para entender del todo de qué iba la charla, y no estaba tan orientada a Ruby, hasta el final donde mencionó llama_cpp. Pero me sigo quedando con la impresión de que todo falla todo el tiempo cuando se trata de IA y la pregunta de cómo se debuguea o testea algo no determinístico.

Lo siguió Melinda Seckington, Coach y entrenadora de Ingeniería en Learn Build Share y Fundadora de Tech Ladies Dinner Roundtable (TLDR). Fue otra charla muy interesante, acerca de Resiliencia. Me gusta cuando normalizamos decir “No estoy bien” ya sea cuando alguien cercano nos pregunta, o cuando hay que admitirlo para sí mismo. La resilencia es una habilidad que hay que desarrollar. También habló de la agilidad emocional, los mitos de la resilencia y más. Me interesó en particular cuando mencionó a la hora de estar mal con algo (ejemplo el trabajo), hay que reconocer la emoción y particularmente reconocer el motivo de esa emoción. Si me enojo, ¿por qué es? Y así responder acorde.

En mis apuntes escribí “Victor Frankl – Man’s search for meaning”. No recuerdo exactamente en qué lo citó Melinda, pero recordé que ya me había anotado ese título en particular. En la novela Aurora de David Keopp que leí hace poco ya había anotado una cita que dice algo así como: “Victor Frankl dice que sólo hay tres significados que importan. Hacer trabajo que te importa a tí, cuidar a (o que te importen) otros, y aceptar el desafía de tiempos difíciles. Trabajo, amor, coraje. Eso es todo. Cualquier otra búsqueda humana es mentira”. De repente tendría que buscar más sobre este autor…

La siguiente charla volvió a lo técnico con Mike McQuaid, Project Leader de Homebrew y CTO y co-fundador de Workbrew. La charla – Ruby on (Guard) Rails (“¿Ruby en barandas de protección?”) estuvo muy buena, creo que fue de las que sacó más risas del público. Bajo la premisa de que Ruby es genial para moverse rápido y para romper cosas, habló de varias herramientas y recomendaciones para programar Ruby. Me gustó algo que mencionó de “pedantería robótica, empatía humana” aplicado a la programación. En una revisión de código es mejor que un proceso automatizado te diga “te faltó un punto y coma en esta línea” o “te sobró un espacio” a que lo diga un humano. Por eso comparto su aprecio por Rubocop, yo también estoy en el lado de los que apreciamos a Rubocop.

La charla está escrita en su blog personal en Ruby on (Guard) Rails, lo que me parece excelente para repasar la charla. Literalmente, lo que dice en el blog es lo que dió en la charla.

El keynote de cierre estuvo a cargo de Emma Barnes, CEO de consonnance.app y MD de snowbooks.com  y makeourbook.com. La charla estuvo muy buena como para cerrar la conferencia. Una charla inspiradora a través de historias desde su experiencia personal. Emma fue arqueóloga trabajando en el Norte de Gales y conectó su experiencia con la programación con Rails.

Uno de los temas en común que mencionó más de una de las personas que dieron charlas fue el hecho de que “Twitter está muerto”. Ya no es tan común compartir “el perfil de Twitter” y se va volviendo más popular “el perfil de Mastodon”. En lo personal hace mucho que abandoné el antro pro fascismo que es Twitter y estoy bastante conforme con el uso de Mastodon. Así que me alegró ver esta tendencia.

En resumen, se pasó volando, lo que me dice que pasé bien. Fue como mi regreso a las conferencias desde antes del 2020, así que espero volver al ritmo que tenía antes de ir a conferencias Ruby. Además de las charlas, tuve ese valor agregado de conocer gente y charlar con gente muy interesante, que es de los mayores valores de asistir a estos eventos.

Ruby es el lenguaje diseñado para la felicidad de quienes lo usamos. Y esa filosofía de que “Matz es bueno así que somos buenos” se refleja en la gente de la comunidad donde generalmente, son gente buena. (Aunque no sé si “buena” sea la mejor traducción para “Nice” del inglés, no sé si expresa exactamente la idea). Por más que hoy Ruby no sea más “cool” como lenguaje, está en un momento muy bueno de su evolución y están pasando muchas cosas interesantes.

Espero poder escribir sobre Haggis Ruby 2025 el año que viene…

El post Haggis Ruby 2024 – Edimburgo, Escocia fue publicado originalmente en Picando Código.

» Leer más, comentarios, etc...

Picando Código

Escribí una interfaz de usuario de terminal para Mullvad VPN con Ruby

octubre 23, 2024 09:54

Desde hace un tiempo vengo usando Mullvad VPN como proveedor de VPN. Mullvad cuenta con una aplicación bastante práctica que funciona en Android, Linux y demás sistemas operativos populares. En su momento, no estaba disponible para Raspberry Pi (y creo que ahora tampoco).

Encontré que podía usar Mullvad con WireGuard. La aplicación usa el protocolo WireGuard por defecto. Cuando seguí  las instrucciones en el enlace, me quedaron un montón de archivos de servidores en /etc/wireguard. Los archivos son de la forma ie-dub-wg-101.conf, cl-scl-wg-001.conf, etc. Siguen el formato código de país, ciudad, interfaz (wg para WireGuard, pero podría ser OpenVPN), y el número de servidor.

Para empezar, hay que loguearnos a la terminal con la cuenta root, o ejecutar con sudo, el comando wg-quick up [SERVIDOR] para conectarnos. Donde SERVIDOR es el nombre del servidor en la forma del nombre de estos archivos. Esto hace que resulte un poco tedioso buscar el nombre del servidor. Buscar en /etc/wireguard, cada archivo descargado tiene la configuración con tu clave de Mullvad. Y el nombre también nos sirve para elegir el servidor para wg-quick up. Esto fue lo primero que se me ocurrió que quería automatizar.

Hacía tiempo que tenía vistas las herramientas TTY, un set de bibliotecas para desarrollar aplicaciones de línea de comando en Ruby. Terminé usando únicamente TTY::Prompt. Esta biblioteca nos provee un DSL para generar interfaces que piden entrada de usuario. Nos facilita mucho la creación de menúes de selección, como en mi caso un menú de países o el menú principal de la aplicación:

opcion = TTY::Prompt.new.select('¿Cuál es tu Tortuga Ninja favorita? 🐢', cycle: true) do |menu|
  menu.choice name: 'Leonardo', value: 'leo'
  menu.choice name: 'Donatello', value: 'donnie'
  menu.choice name: 'Raphael', value: 'raph'
  menu.choice name: 'Michelangelo', value: 'mikey'
end

Esto nos va a generar la siguiente interfaz:
Ruby TTY::Prompt

El valor que elegimos va a ser guardado en la variable opcion (en este caso leo, donnie, raph o mikey).

Así que esencialmente la app implementa a través de éstos menúes la navegación que haría para conectarme a una VPN por línea de comando. Implementé varias opciones:

– A veces simplemente quiero conectarme y no me importa a qué país. Para eso implementé la opción “Random”, que elige un servidor al azar de los disponibles y se conecta.
– Otras veces quiero conectarme a un país en particular, para eso hice un menú donde elijo un país, y se conecta a un servidor al azar de ese país.
– Por último está la opción de listar todos los servidores disponibles, agrupados por país y ciudad, y seleccionar uno. TTY::Prompt provee la opción de filtrar, así que si empezamos a escribir el nombre de un país, nos va a mostrar los que coincidan para encontrarlo más rápido.

Después hago las llamadas al sistema con las apps. En el caso de WireGuard usa:

$ wg-quick up # Connectarse a un servidor
$ curl https://am.i.mullvad.net/connected # Chequear si está conectado
$ wg-quick down # Desconectarse de un servidor

Cuando se conecta, serializa y guarda en disco el servidor que usamos, para saber cómo desconectarse más adelante. Esto se podría mejorar accediendo a la conexión actual de repente.

En el caso de Mullvad, usa los comandos de mullvad cli. Al usarlo por primera vez, debemos ingresar nuestro número de cuenta. Para eso usé la opción ask de TTY::Prompt, que imprime una pregunta y tenemos que ingresar una respuesta mediante teclado. Con la cli de Mullvad, tenemos acceso a la información de conexión actual así que los datos que tenemos que manejar o persistir son distintos.

Toda la parte de serializar o guardar es algo que podría mejorar. Una de las mejoras sería tomar en cuenta más opciones de sistema, porque estoy usando hardcodeado el valor de ~/.local/share/mullvadrb como directorio para guardar las configuraciones.

Inicialmente el código usaba WireGuard a través de wg-up. Después decidí reescribir la “arquitectura” (comillas para que no suene como que fue planeado y no que fui hackeando el código hasta que fue agarrando forma) sobre la aplicación de línea de comando mullvad. Finalmente, reintegré el código de wg para casos en los que no quiera (o no pueda) usar la aplicación de Mullvad (que fue la razón inicial por la que empecé a escribir este código).

Si bien inicié git en el código hace unos pocos días, lo vengo usando desde hace como un año. La Raspberry Pi 4 que compré tiene más de un año, y lo empecé a poco tiempo de haber empezado a usarla. Finalmente decidí compartirlo en caso de que sea útil para alguien más, y eso también me obligó a empaquetarlo y dejarlo un poco menos desprolijo de lo que era inicialmente. Como siempre, algo aprendí en el camino.

Hay varias mejoras posibles por hacerle, por ejemplo más funcionalidades soportadas por ambos “backends” como les llamé. Pero sería cuestión de agregarlos. También si resulta útil para más gente, le agregaría i18n, para tener al menos una versión en Español. Pero no creo que sea una herramienta muy requerida. Ya le agregué un toque de Gaélico Escocés por ahí.

No descarto una reescritura general más, ahora que tengo en la cabeza el modelo general de cómo funciona cada backend y qué necesito en cada uno. Es una base de código relativamente chica y lo escribí en principio “para que ande” y después ya por diversión, así que no le pongo mucha exigencia.

El código fuente está en GitHub, y ya publiqué dos versiones disponibles en RubyGems. Agregué un ejecutable cosa de que una vez instalada la gema, pueda ejecutar mullvadrb desde cualquier terminal en mi sistema.

mullvadrb

El post Escribí una interfaz de usuario de terminal para Mullvad VPN con Ruby fue publicado originalmente en Picando Código.

» Leer más, comentarios, etc...

Picando Código

Costumbres del código: [Lenguaje del teclado] – Parte 3

octubre 22, 2024 10:00

Hace unos 16 años, escribí un par de posts sobre el lenguaje que usaba en el teclado de mi computadora. Así fue como descubrí eventualmente la distribución de teclado que vengo usando desde hace tiempo. Cómoda para escribir en español, en inglés y programar en teclados con distribución Inglés de Estados Unidos.

Costumbres del código: lenguaje del teclado - parte 3

En Costumbres del código: [Lenguaje del teclado] , contaba cómo empecé usando un atajo de teclado para cambiar el idioma del teclado de inglés a español. En Costumbres del código: [Lenguaje del teclado] – Parte 2 había aprendido a usar la distribución de teclado Inglés alternativo internacional gracias a un comentario de Bernabé García (¿en qué andará dklight éstos días?). ¡Pero la cosa no terminó ahí! Más adelante descubrí algo incluso mejor: Inglés variante Internacional con teclas AltGr muertas (AltGr dead keys).

Esta es la variante que vengo usando desde no mucho después que publiqué aquellas dos entradas en junio de 2008. No recuerdo exactamente todas las diferencias, creo que las vocales con tilde en español se hacen igual: AltGr + la vocal. La ñ tambien es AltGr n. Pero lo que sí recuerdo es las comillas ' y ". En la variante sin AltGr muertas, se puede presionar las comillas una vez, seguida de una letra, para escribir caracteres con tilde. Para escribir la comilla en sí, hay que usar AltGr. En la variación AltGr muertas, se imprimen al presionarlas una sóla vez. Por último, los signos de apertura para exclamaciones y preguntas están en el signo en particular, presionándolos con AltGr.

Estoy seguro que había alguna razón más que no recuerdo en este momento por la cual uso la variación AltGr muertas sobre la otra. En cada instalación nueva de Linux, a la hora de definir la distribución de teclado, tengo el recuerdo (por lo menos en los instaladores gráficos) de un campo de texto para probarla. Y creo que fue así que descubrí esta otra opción con teclas AltGr muertas que vengo usando desde tanto tiempo. Puedo escribir cómodo en inglés y español, y las teclas de uso común en programación siguen estando a la mano: []. {}. ./'";:

Otra parte interesante de esta variación para mí, es que desde hace unos años estoy aprendiendo Escocés Gaélico. Este idioma también usa acentos. Pero el acento es el opuesto de nuestro tilde, signo que nos acostumbramos a usar en español desde que aprendemos a escribir. Por ejemplo el nombre del idioma en Gaélico es Gàidhlig. Y con esta distribución de teclado, aprendí a hacer vocales con el acento opuesto presionando AltGr, el tilde inverso (tecla a la izquierda del uno en teclado americano) seguido de la vocal. Así que también me queda cómodo para escribir en Gaélico. Estaría interesante ver un teclado en idioma Gaélico, el alfabeto sólo tiene 18 letras: A, B, C, D, E, F, G, H, I, L, M, N, O, P, R, S, T, U (y con estas pocas letras logran más sonidos que el idoma español).

Cada tanto recibía algún comentario que me recordaba esas dos entradas en el blog, y en mi cabeza quedaba el tema pendiente de escribir la versión que venía usando ahora. Como que estaba la culpa de no haber compartido cómo siguió la cosa. Pero con la satisfacción de la misión cumplida, cierro este post. Por ahora, y después de muchos años, esta va a ser la última entrada en el blog en la serie “Costumbres del código: [Lenguaje del teclado]”.

Distribución del teclado idioma Inglés, variación Internacional con teclas AltGr muertas

El post Costumbres del código: [Lenguaje del teclado] – Parte 3 fue publicado originalmente en Picando Código.

» Leer más, comentarios, etc...

Variable not found

HybridCache, la nueva caché híbrida de .NET 9

octubre 22, 2024 07:53

Imagen decorativa sugiriendo sistemas de almacenamiento en la nube

Una de las novedades más destacables de .NET 9 es, sin duda, el nuevo sistema de caché híbrida (Hybrid cache), una puesta al día del sistema de caché distribuida que nos acompaña desde las primeras versiones de .NET Core, y al que iban haciendo falta ya algunas mejoras.

Este nuevo sistema está construido encima de la infraestructura de caching existente (Microsoft.Extensions.Caching), añadiendo mejoras que hacen que su uso sea más sencillo y contemplando de serie funcionalidades que antes nos veíamos obligados a implementar manualmente.

Vamos a echarle un vistazo a sus principales características :)

Disclaimer: lo que vamos a ver a continuación está basado en .NET 9 RC2, por lo que todavía es posible que haya cambios en la versión final.

¿Qué aporta Hybrid Cache sobre IDistributedCache?

Empecemos por la que, para mi gusto, es la principal aportación de Hybrid Cache: la protección contra estampidas.

Una estampida es el efecto que puede ocurrir en sistemas que usan caché para soportar una carga muy alta de peticiones al mismo tiempo. En ellos, si la caché se invalida por algún motivo, todas las peticiones concurrentes que llegan mientras ésta no haya sido poblada de nuevo intentarán recargarla simultáneamente, dando lugar a un ataque masivo al servidor de origen que en el peor de los casos puede llevarlo al suelo.

Con la protección integrada contra estampidas, Hybrid Cache se encarga de gestionar las peticiones concurrentes, permitiendo que sólo una de ellas llegue al servidor de origen y el resto esperen a que la caché sea repoblada. Esto es una funcionalidad que antes debíamos implementar manualmente, y no siempre era trivial hacerlo bien.

Otra característica realmente interesante de Hybrid Cache es la implementación de serie de una caché multinivel, solucionando un escenario que es bastante frecuente en sistemas distribuidos: aunque usemos un mecanismo externo como Redis, Memcached u otros, es muy habitual que también tengamos una caché en memoria en el propio servidor, para evitar tener que ir a la caché externa en cada petición.

Hybrid Cache nos permite configurar fácilmente una caché de nivel 1 (L1) en memoria del servidor (usando la clase MemoryCache de siempre), y una caché de nivel 2 (L2) en un sistema externo (usando IDistributedCache), y gestionar la sincronización entre ambas de forma transparente. Es decir, desde nuestro código simplemente escribiremos o leeremos a través de Hybrid Cache, y éste se encargará de gestionar la comunicación con la caché en memoria y la externa.

Por otra parte, como recordaréis, IDisbributedCache es una interfaz muy simple que sólo nos permite almacenar y recuperar bytes, por lo que si queríamos almacenar contenidos más sofisticados como objetos o estructuras, teníamos que encargarnos de serializarlos y deserializarlos manualmente, dando lugar a mucho código repetitivo, propenso a errores, y muchas veces poco eficiente.

Hybrid Cache ha mejorado las APIs para interactuar con la caché, con el objetivo de simplificar su uso en los escenarios más frecuentes. Los métodos para almacenar y recuperar datos de la caché son más fáciles de usar, y permiten el uso directo de objetos complejos sin necesidad de serializarlos o deserializarlos manualmente.

Por defecto, Hybrid Cache serializará sin problema las secuencias de bytes (digamos que es el formato nativo), las cadenas de texto serán convertidas a bytes UTF-8, y el resto de tipos de datos se usará System.Text.Json para obtener su representación JSON, aunque si fuera necesario, podemos configurar serializadores personalizados para tipos de datos específicos. En todo caso, el payload puede ser comprimido para ahorrar espacio y minimizar los datos en circulación.

También es interesante destacar la introducción de la invalidación basada en tags, un mecanismo que permite agrupar elementos de la caché bajo una misma etiqueta, y luego invalidar todos los elementos que tengan esa etiqueta de forma sencilla. Hasta ahora, la única forma de conseguir algo parecido era implementar manualmente algún mecanismo de agrupación externo a la caché y eliminar las entradas una a una.

La reutilización de objetos permite mejorar el rendimiento en escenarios donde los objetos almacenados en la caché distribuida son grandes o se accede a ellos con mucha frecuencia. Cuando estos objetos sean inmutables y su estado no sea alterado desde el código, es posible que Hybrid Cache pueda reutilizar el objeto entre distintas peticiones concurrentes en lugar de deserializarlo de nuevo cada vez.

Uso de Hybrid Cache

La clase abstracta HybridCache, definida en el espacio de nombres Microsoft.Extensions.Caching.Distributed es la que nos dará acceso a las funcionalidades de la caché híbrida. Internamente existe una implementación concreta de esta clase llamada DefaultHybridCache, que es la que usaremos en la mayoría de los casos, aunque será transparente para nosotros porque normalmente trabajaremos con la clase abstracta.

De momento, estos nuevos tipos no vienen incluidos en los metapaquetes básicos de .NET 9, sino que se distribuye a través del paquete NuGet independiente Microsoft.Extensions.Caching.Hybrid, que es necesario instalar en los proyectos donde queramos usarlos. A día de hoy, todavía son paquetes en preview, pero en pocas semanas serán ya definitivos.

Una vez instalado, lo habitual será registrar el servicio en el contenedor de dependencias de la aplicación, para lo que usaremos el método AddHybridCache de la clase IServiceCollection. Luego, podemos reclamar instancias de HybridCache donde deseemos usar sus servicios.

Por ejemplo, la siguiente aplicación ASP.NET Core registra HybridCache y luego lo usa desde un endpoint para almacenar y recuperar la hora actual, pero actualizándose solo cada 5 segundos:

var builder = WebApplication.CreateBuilder(args);

// Agregamos los servicios de caché híbrida
builder.Services.AddHybridCache();

var app = builder.Build();

app.MapGet("/", async (HybridCache cache) =>
{
    DateTime cachedItem = await cache.GetOrCreateAsync(
        key: "currentTime",
        factory: _ => ValueTask.FromResult(DateTime.Now),
        options: new HybridCacheEntryOptions()
                {
                    LocalCacheExpiration = TimeSpan.FromSeconds(5),
                }
        );
    return cachedItem;
});

app.Run();

Pero fijaos que en el ejemplo anterior sólo estamos usando la caché de primer nivel, porque no hemos configurado la caché distribuida o de segundo nivel. Para ello, necesitamos registrar un servicio IDistributedCache en el contenedor de dependencias; HybridCache detectará automáticamente la presencia de este servicio y usará la caché de segundo nivel.

Por ejemplo, si queremos usar Redis como caché distribuida, podemos instalar el paquete NuGet Microsoft.Extensions.Caching.StackExchangeRedis y registrar el servicio de IDistributedCache en el contenedor de dependencias de forma similar a la siguiente:

var builder = WebApplication.CreateBuilder(args);

// Agregamos los servicios de caché híbrida
builder.Services.AddHybridCache();

// Agregamos los servicios de caché distribuida con Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("RedisConnectionString");
    options.InstanceName = "HybridCacheDemo";
});

var app = builder.Build();

app.MapGet("/", async (HybridCache cache) =>
{
    DateTime cachedItem = await cache.GetOrCreateAsync(
        key: "CurrentTime",
        factory: _ => ValueTask.FromResult(DateTime.Now),
        options: new HybridCacheEntryOptions()
                {
                    // Establecemos la expiración de caché local y distribuida:
                    LocalCacheExpiration = TimeSpan.FromSeconds(5),
                    Expiration = TimeSpan.FromSeconds(10)
                }
    );
    return cachedItem;
});

Obviamente, HybridCache es mucho más que lo que hemos visto aquí, pero creo que estos ejemplos son suficientes para hacernos una idea de cómo se configura y se usa. Si queréis profundizar más, os recomiendo que le echéis un vistazo a la documentación oficial de .NET 9.

¡Espero que os haya resultado interesante!

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 582

octubre 21, 2024 06:05

Enlaces interesantes

Como todos los principios de semana, aquí va la lista de enlaces recopilados durante los últimos siete días. Hay bastantes cosas que me han llamado la atención en esta ocasión; entre otros, he descubierto que Visual Studio incluye un visualizador de JWT integrado, el atributo [OverloadResolutionPriority] de .NET 9/C#13, y también me he preguntado cuántos son muchos índices en una tabla SQL 😕

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET / Blazor

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

.NET MAUI / Xamarin

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Juanjo Navarro

TIL: Juego de caracteres en un .bat

octubre 16, 2024 10:19

Voy a ver si escribo por aquí mis TIL. Así le doy un poco de uso al blog y estos aprendizajes diarios quedan recogidos para referencia futura y utilidad pública.

Empezando por cómo especificar el juego de caracteres de un fichero .bat.

Hoy he generado un .bat para copiar una serie de ficheros, cada fichero con una línea xcopy:

xcopy "\\ruta\nombre del fichero con acentos áé.pdf" "c:\tmp\ruta\nombre del fichero con acentos áé.pdf"

La cuestión es que esos acentos en el nombre del fichero hacían que no encontrase el fichero de origen cuando iba a buscarlo. He probado a grabar el .bat en distintos juegos de caracteres pero nada funcionaba.

Solución: Incluir una primera línea en el fichero .bat indicando el juego de caracteres del propio .bat (que era UTF-8):

chcp 65001
xcopy "\\ruta\nombre del fichero con acentos áé.pdf" "c:\tmp\ruta\nombre del fichero con acentos áé.pdf"

» Leer más, comentarios, etc...

info.xailer.com

xaWeb

octubre 15, 2024 07:20

Tengo el gusto de anunciaros un nuevo proyecto de OZ Software para desarrollar páginas web con nuestro querido lenguaje Harbour utilizando la tecnología CGI. El slogan: ‘desarrollo web rápido y fácil‘ describe claramente los objetivos principales de la herramienta.

He preparado un importante documento de introducción a xaWeb (40 páginas) xaWeb, que os animo a leerlo con detenimiento y en su totalidad. Son muchos conceptos nuevos, pero de verdad que no es complicado y merece la pena. No obstante, os adelanto las características esenciales de xaWeb:

  • Desarrollo de ejecutables tipo CGI para Windows y Linux (64 bits)
  • Independiente de Xailer: Se puede utilizar cualquier editor y no usa ninguna librería de Xailer
  • Soporte de paquetes de contexto como Materialize, que permite tener un buen diseño visual sin esfuerzo
  • Funcionamiento como servicio web
  • Gestión de sesiones
  • Sistema de depuración de código
  • Sincronización automática de datos entre CGI y HTML
  • Mínimos conocimientos necesarios de HTML, CSS y JavaScript para usarlo
  • Soporte de toda la potencia HTML, CSS y JavaScript si se tienen los conocimientos necesarios
  • xaWeb es un producto comercial de OZ Software
  • Se abre un periodo Beta al cual se puede apuntar cualquier usuario que lo desee

Con Xailer 9 publicamos una importante mejora en Xailer que permite crear ejecutables tipo CGI o consola para Linux 64 bits. Este era una paso previo necesario para intentar abordar el desarrollo de xaWeb, pues somos conscientes que el 99,99% de los servidores de Internet son Linux. Con motivo de la la inclusión de esta importante funcionalidad en Xailer, publicamos en nuestro WIKI dos artículos en los cuales explicábamos como instalar WSL (Windows Linux subsystem) y como instalar el servidor web de Apache. Para poder crear ejecutables para Linux desde el IDE de Xailer es absolutamente necesario que se instale WSL al menos (el primer paso). Aunque el segundo paso es más que recomendable.

Hace unos meses, dimos un paso más para la creación de páginas Web para Linux, con la publicación de un nuevo ‘plugin‘ de despliegue de archivos. Este ‘plugin’ permite que cualquier archivo que publiquemos en la carpeta ‘www‘ automáticamente se despliegue en WSL y en cualquier servidor web vía FTP. De la misma forma, si creamos un archivo CGI para Linux, el plugin también realiza el despliegue del mismo tanto en WSL como en un servidor FTP que usted elija.

Con Xailer 9 también es posible crear CGIs para Windows. De hecho, puede tener dos proyectos que apunten al mismo código fuentes, cada una para su plataforma en particular. La ventaja de los CGIs para Windows es que se pueden depurar y ejecutar desde el IDE. No obstante, creemos que finalmente nuestros CGIs correrán en máquinas Linux y por lo tanto, es importante que desde el primer día, sea capaz de crear CGIs para esa plataforma.

En breve estará disponible xaWeb; pero me temo que no va a ser un recompilar la aplicación y listo. Lo que si os podemos asegurar es que os encontraréis rápidamente en casa y que el camino para hacer software web se despeja completamente y el resultado final es espectacular.

Somos conscientes de que hay importantes aspectos que ahuyentan a muchos potenciales usuarios de xaWeb, como son:

  • Uso de servidores Linux
  • Instalación WSL para trabajar en local con Linux
  • Aprendizaje de HTML, JavaScript y CSS

Los pilares de xaWeb ya están terminados, pero es más que probable que haya que realizar modificaciones a nivel estructural que puedan afectar incluso a los ejemplos que ya están realizados. Hemos esperado a tener un CRUD completo funcional con archivos DBF para hacer este anuncio. En el siguiente enlace podéis ver algunos ejemplos de lo hecho hasta ahora corriendo bajo servidor Linux 64 bits. Con todos los ejemplos se incluye el código fuente en xaWeb para que le echéis un ojo y os animéis a usarlo cuando esté disponible.

xaWeb: All samples

Comentarios abiertos para conocer vuestras opiniones y sugerencias. Gracias por vuestro tiempo.

Un cordial saludo

» Leer más, comentarios, etc...

Metodologías ágiles. De lo racional a la inspiración.

Scrum master a tiempo completo: 42 Tareas

octubre 04, 2024 08:29

Uno de los artículos que más referencio en mi formación en Scrum cuando hablo de las labores del Scrum Master es:&nbsp;42-tasks-for-a-scrum-masters-job. Por alguna razón, todo el mundo parece entender que el Product Owner es un trabajo a tiempo completo, o ser miembro de un equipo también, pero que probablemente el rol del Scrum Master puede ser realizado a media jornada o incluso menos. El scrum

» Leer más, comentarios, etc...

Una sinfonía en C#

Docker tricks, crear una imagen para poder depurar un error.

septiembre 10, 2024 12:00

“Introducción”

En este caso queremos crear una imagen pero nos da algún tipo de error, y es complicado de resolver. Bueno, lo que podemos hacer es apuntar los comandos que queremos ejecutar, crear una imagen con su base y hasta el punto que funciona y hacer que inicie con un comando que nos permita crear al contenedor e ingresar.

Crear imagen a partir de una con problemas

FROM node:20.12.0 AS builder
ARG environment=dev

WORKDIR /app
COPY ./package.json /app/
COPY ./yarn.lock /app/

RUN yarn
COPY . /app/
RUN yarn build:$environment

FROM nginx:1.21.5-alpine

EXPOSE 80/tcp
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

y vemos que nos da un error al hacer RUN yarn

¿Qué podemos hacer?

Facil, creamos una imagen con la base que tenemos y hasta el punto que funciona, y luego la ejecutamos con un comando que nos permita ingresar al contenedor. Pero como comando de inicio, usamos tail -f /dev/null para que se quede esperando y no se cierre.

FROM node:20.12.0 AS builder
ARG environment=dev

WORKDIR /app
COPY ./package.json /app/
COPY ./yarn.lock /app/

CMD ["tail", "-f", "/dev/null"]

una vez hecho esto, podemos hacer un docker build -t myimage . y luego un docker run -it myimage /bin/bash para ingresar al contenedor y ver que es lo que pasa.

Desde dentro del container ejecutamos el comando que da problemas y vemos el error que nos da.

yarn

.....

Request failed \"401 Unauthorized\""

Y vemos que nos da un error al intentar restaurar los paquetes.

Nada más, una forma sencilla de ir depurando error por error dentro de un contenedor.

Agregamos una línea para copiar el archivo de configuración de npm y listo.

FROM node:20.12.0 AS builder
ARG environment=dev

WORKDIR /app
COPY ./package.json /app/
COPY ./yarn.lock /app/
COPY .npmrc /app/ # <-- Agregamos esta línea

RUN yarn
COPY . /app/
RUN yarn build:$environment

FROM nginx:1.21.5-alpine

EXPOSE 80/tcp
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

Nos leemos.

» Leer más, comentarios, etc...

proyectos Ágiles

Master en Agile – MMA 2024-2025

septiembre 04, 2024 05:12

En octubre de 2024 se iniciará el Barcelona la 14ª edición del Postgrado en Métodos Ágiles (PMA) y otra del Máster en Transformación Agile (MMA) en La Salle (Universitat Ramon Llull), el primero a nivel mundial sobre Agile.

Con el Máster en Métodos Ágiles de La Salle-URL aprenderás a liderar el cambio hacia la Business Agility y a lanzar con éxito iniciativas en entornos complejos y altamente competitivos, reduciendo el Time To Market a fin de maximizar el valor aportado al negocio.

Desarrollarás una manera de pensar transversal e integradora para crear equipos de alto rendimiento mediante Scrum, Kanban, Lean Startup, OKR, diseño organizativo y liderazgo sistémico para elaborar estrategias de escalado Agile en la organización y transformar su cultura, de modo que también aumente la motivación de sus miembros.

Con profesores de primer nivel y visitas a empresas podrás iniciar un cambio hacia la agilidad y resiliencia empresarial en tiempos de incertidumbre.

Esta es una oportunidad única para aprender de profesionales-profesores de primer nivel, con muchos años de experiencia específica en Agile, aplicando principios y métodos ágiles en contextos diversos, especializándose en aspectos concretos,  investigando sobre nuevas técnicas, ponentes en conferencias nacionales e incluso internacionales, que incluso han inventado métodos y escrito libros.

Además, aprenderás a aplicar herramientas de inteligencia artificial para afrontar retos como abordar situaciones complejas, analizar grandes volúmenes de datos, crear agendas para workshops operativos y transformadores, o preparar product backlogs.

El MMA incluye las siguientes certificaciones oficiales:

  • «Certified Scrum Master» (CSM) de la Scrum Alliance, la entidad de certificación Agile de mayor prestigio a nivel internacional.
SAI_BadgeSizes_DigitalBadging_CSM
  • Certified Agile Skills – Scaling 1 de la Scrum Alliance, , la entidad de certificación Agile de mayor prestigio a nivel internacional.
  • Certified Leader de Agile Humans.

Adicionalmente, se incluye la visita a empresas singulares en aspectos concretos:

 ✍ Para inscripciones, consultar la página oficial del Máster.

A continuación, más detalle acerca del Postgrado en Agile (PMA) y del Máster en Agile (MMA)

PMA – Postgrado en métodos Ágiles

El PMA incluye las siguientes certificaciones oficiales:

  • «Certified Scrum Master» (CSM) de la Scrum Alliance.
  • Opción de acceder al Certified Project, Portfolio, And Operations Management for Business Agility de Businessmap.
AsignaturasTemasProfesores
Fundamentos & InceptionEquipos y Proyectos en entornos complejos.


Principios y métodos más conocidos (Scrum, Lean, Kanban y XP). Facilitadores e impedimentos.

Lanzamiento de Agile en un equipo.


Inception y conceptualización ágil de proyecto, priorización ágil, historias de usuario,  elaboración de Product Backlog, técnicas de priorización.

Xavier Albaladejo


Silvia Sistaré

Agustín Yagüe

Scrum y KanbanEstimación y planificación ágil, framework de Scrum, retrospectivas, Kanban, métricas ágiles, herramientas ágiles físicas, radiadores de información.Raul Herranz

 

Teodora Bozheva

Personas y equiposGestión de personas, gestión de conflictos, motivación e incentivos, facilitación compartida, contratación ágil.

 

Visual thinking.

Steven Wallace

 

Silvia Sistaré

Virginia Armas

Gestión de producto ágilDesign Thinking, Lean UX & Prototyping.

 Estrategia de Producto – Consciencia situacional (Wardley Maps), modelo de negocio (Lean Canvas), modelo de tracción, métricas AARRR.
Planificación y gestión estratégica – OKR y Hoshin Kanri.

Customer development – Lanzando y escalando startups ágiles, las tres fases de un producto.

Lean Startup – Desarrollo de producto basado en prototipos y experimentos. Bancos de ideas, desarrollo basado en hipótesis.

Juncal Guinea


Lucía Barroso
Ingeniería ágil User eXperience y prototipado en Agile.

 

ALM ágil, eXtreme Programing, Software Craftsmanship, testing ágil.

BDD y TDD. Desarrollo guiado por pruebas (de aceptación y unitarias).

Métricas Accelerate y SW Delivery assessment.

Cómo trabajar con código heredado y reducir la deuda técnica.

DevOps
Juncal Guinea

 

Cristina Verdi
Trabajo Final de PostgradoDurante el Postgrado se realiza un caso práctico de introducción de los contenidos en un equipo ágil en una empresa real. Para ellos los alumnos se organizan en equipos multidisciplinares utilizando Scrum, con feedback regular de un tutor con experiencia en transformación de equipos. 

El Postgrado tendrá una duración de 4 meses y se realizará viernes tarde y sábado por la mañana.

Ver también:

MMA – Master en Métodos Ágiles

Incluye todas las asignaturas del Postgrado (PMA) y, adicionalmente, las siguientes asignaturas especializadas en Business Agility, agilidad organizacional y transformación (aparte de las tres certificaciones indicadas al inicio y las visitas a empresas):

AsignaturasTemasProfesores
Enterprise Learning & personal efficiencyAgile Kaizen, Comunidades de Práctica, Open Spaces, Talent development, gamification.

 

Productividad y aprendizaje personal en Agile (eficiencia).
Steven Wallace


Esther Somoza
Lean Thinking & Agile ManagementLean. Escalado con Kanban.

 

Business Agility con ViMa – agilidad para equipos de negocio

Agile-Lean Management

Teodora Bozheva

  Xavier Quesada


Xavier Albaladejo

Coaching y CulturaCoaching de equipos, creación de equipos de alto rendimiento, liderazgo.

 

Tipos de cultura empresarial, transformación cultural.

Joserra Díaz

 

Jasmina Nikolic
Jaume Gurt

Transformación ContinuaEstrategia de despliegue de Agile en organizaciones, gestión del cambio, estructuras ágiles, cómo vender Agile a la Dirección. Contratos ágiles.

Enterprise continuous improvement.

Xavier Albaladejo

 

Ángel Medinilla
Scaling Agile Escalado (LESS, Spotify, Nexus, SAFe, Unfix), desescalado y auto-organización empresarial (reinventing organizations, sociocracy 3.0, liberating structures, …), equipos distribuidos.

 

Impact Mapping, Product Portfolio Management, Roadmapping, Budgeting for Agile

Adrian Perreau
Fernando Palomo

 

Mattijas Larsson

Trabajo Final de MásterDurante el Máster se realiza un caso práctico de introducción y aplicación de Agile en una empresa real, incluyendo la parte de transformación organizativa, de métodos y de cultura. Para ellos los alumnos se organizarán en equipos multidisciplinares utilizando Scrum, con feedback regular de un tutor con experiencia en transformación organizativa.Xènia Castelltort (oratoria / public speaking para poder explicar tus ideas de manera convincente)

El Máster tendrá una duración de 8 meses y se realizará viernes tarde y sábado por la mañana (incluye los estudios indicados en el Postgrado).

El cambio en la organización comienza por el propio cambio, para también poder dar ejemplo. Por ello en el MMA se realizan diferentes ejercicios de auto-conocimiento:

  • Cómo el alumno trabaja en equipo.
  • Estilo de liderazgo del alumno (según el paradigma Agile).

Como en las últimas ediciones, contaremos con la participación de empresas que nos explicarán sus experiencias de transformación y donde trabajan con modelos de gestión desescalados (basados en Sociocracia, NER y otras alternativas).

Información adicional

  • Perfil de los estudiantes: 30-45 años (no son recién licenciados, son personas con experiencia profesional).
  • Alrededor del 50% son mujeres.
  • 15% de los estudiantes ya no son del ámbito tecnológico, son pioneros-innovadores en otras industrias.
  • Alumnos de diferentes disciplinas – Product Owners, Scrum Masters, Agile Coaches, líderes de equipos, Project Managers, managers funcionales, ingenieros SW. Van a adquirir conocimientos de Agile “on-top” de todo eso (y a aprender unos de otros).
  • Lo que les caracteriza: todos son agentes de cambio en su contexto (equipo, área, empresa).
  • Sus 20 profesores (de reconocimiento internacional) son el MAYOR VALOR DIFERENCIAL del PMA y del MMA.

Testimoniales

Me ha permitido tener conocimientos sobre varios temas súper importantes dentro de la Transformación Digital. Me dio herramientas para crecer a Agile Coach y, además, para tener mejores conversaciones y discusiones con las empresas en donde he trabajado

Carolina Graffe

Estoy desplegando el TFM en mi empresa, Además, no estoy sola. Uno de mis compañeros del equipo ha sido contratado como consultor por mi empresa para darnos soporte. Así que no sólo estoy aplicando lo que yo aprendí, sino que el MMA me ha permitido ampliar mi círculo de contactos relevantes, que me permite seguir aprendiendo.

Susana Santillán

Estoy trabajando como agente del cambio y mis aportaciones son muy valoradas por mi jefe y compañeros. Por el feedback recibido, mis aportaciones están muy por encima de lo esperado para mi rol.

Robert Avellaneda

Tengo mucho más contexto y más herramientas para el día a día. Incluso a nivel personal también me está ayudando mucho

María Hachero

Además de los conocimientos concretos que se reciben, uno de los principales cambios que han experimentado los alumnos es mayor perspectiva en los problemas, cómo abordar la complejidad en las empresas, en su trabajo y en las relaciones entre personas y en equipos. Han ganado discurso y aplomo para defender de manera más objetiva propuestas de cambio y mejora.

Encuesta a alumnos de las ediciones anteriores:

(*) Las personas que han valorado el impacto como «neutro o poco» usualmente son perfiles muy especializados en contextos muy estáticos, con lo cual les es difícil cambiar de «profesión» e introducir cambios en sus organizaciones (aunque algunos de ellos incluso dan conferencias sobre cómo van avanzando en esos contextos tan singulares).

 ✍ Para más detalles e inscripciones, consultar la página oficial del Máster.

» Leer más, comentarios, etc...

Blog Bitix

Desempaquetado del ratón inalámbrico Logitech Signature M750

julio 23, 2024 09:00

Tras probar un ratón inalámbrico de los baratos pasado un tiempo me quedé con muy malas impresiones por la perdida de conectividad principalmente que era muy molesta. Pasé a un ratón con cable más fiable y tras 5 años el botón derecho me ha empezado a fallar y reconocer el clic aleatoriamente. Tras estar usando un Apple Magic Mouse que generalmente me ha funcionado bien en cuanto a conectividad he vuelto a darle una nueva oportunidad a un ratón inalámbrico pero ya de más calidad, finalmente he elegido el Logitech Signature M750. En el artículo hago un análisis del ratón y mis primeras impresiones.

Continuar leyendo en Blog Bitix

» Leer más, comentarios, etc...

Header Files

La trampa al usar semántica de movimiento con std::string

julio 16, 2024 10:00

La trampa al usar semántica de movimiento con std::string

La semántica de movimiento de std::string puede ser complicada y, a menos que tengamos información previa sobre los tamaños esperados de las cadenas, puede tener el efecto contrario y hacer que el código sea más lento. La razón detrás de esto es la optimización de cadenas pequeñas (SSO, por sus siglas en inglés). que consiste, en resumidas cuentas, en tratar al objeto como si fuera una unión, de forma que si la cadena es más corta que un tamaño dado, se almacena en el mismo bloque de memoria del objeto en lugar de asignar memoria dinámica. Cuando la cadena supera ese tamaño, la cadena se almacena en un bloque diferente.

¿Qué es la Optimización de Cadenas Pequeñas (SSO)?

La SSO es una técnica utilizada en la implementación de std::string para optimizar el uso de memoria y el rendimiento. En lugar de asignar memoria dinámica para todas las cadenas, la SSO almacena cadenas pequeñas directamente en el objeto std::string (como si de una unión se tratase). Se puede ver la SSO en acción en este ejemplo.

Esta técnica evita la sobrecarga de la asignación de memoria dinámica, que puede ser costosa en términos de tiempo y recursos. Sin embargo, esta optimización introduce algunas consideraciones importantes al mover objetos std::string.

Nota: La SSO no es parte del estándar de C++ sino más bien una optimización de algunos compiladores. Igualmente, el tamaño máximo para considerar una cadena como pequeña no tiene que ser el mismo en todas las implementaciones ni plataformas.

El constructor de movimiento de std::string

Al mover cualquier objeto en C++, estamos dando la posibilida de realizar una copia optimizada. La eficiencia aumenta cuando tenemos recursos externos que podemos intercambiar, como un puntero a un bloque de memoria o un handle de fichero. Sin embargo, para el resto de datos, aún tenemos que copiar datos. Si la cadena es pequeña y la SSO está en acción, no hay ningún puntero que intercambiar y todavía estamos copiando los datos base de std::string.

De hecho, al mover, tenemos que garantizar que el objeto original se mantenga en un estado válido, lo cual normalmente se hace estableciendo algunos valores por defecto. En la práctica, esto significa que estamos copiando una vez y asignando una vez, duplicando la cantidad de operaciones en comparación con una copia normal. Por lo tanto, si nuestras cadenas se espera que siempre (o la mayoría del tiempo) sean más cortas que el límite de SSO, entonces un movimiento perjudicaría el rendimiento.

Comparación de Copia vs Movimiento

Para ilustrar mejor este punto, se puede comparar el rendimiento de la copia y el movimiento para cadenas pequeñas, grandes y una mezcla de ambas. El siguiente ejemplo permite visualizar las diferencias entre ellas. En este benchmark, se estableció un tamaño de 32 caracteres para tener aproximadamente un 50% de cadenas pequeñas y un 50% de cadenas grandes. Los resultados muestran cómo el movimiento de cadenas pequeñas puede ser menos eficiente que una simple copia debido a la SSO.

Benchmark std::string

Conclusión

En resumen, la semántica de movimiento de std::string no siempre es la mejor opción, especialmente cuando se trata de cadenas cortas que se benefician de la SSO. Es crucial considerar el tamaño esperado de las cadenas al decidir entre copiar o mover std::string. Esta decisión puede tener un impacto significativo en el rendimiento de nuestra aplicación.

» Leer más, comentarios, etc...

info.xailer.com

Plugin de despliegue de archivos

junio 11, 2024 11:35

Os presento un nuevo plugin que puede ser muy útil para todos aquellos que os dedicáis a generar ejecutables tipo CGI que luego subís a un servidor web o cualquier otro tipo de archivo que querías desplegar un vuestros servidores de forma rápida.

Su funcionamiento es muy sencillo: el plugin detecta cuando se realiza una compilación y enlazado correcto de un archivo CGI y lo sube a Internet utilizando los servicios de CURL. En Xailer 9 existe una carpeta especial para los proyectos tipo Web de nombre ‘www’. Cuando el plugin detecta que se ha realizado un salvado de algún archivo en esa carpeta, hace automáticamente el despliegue.

Es más que probable que no queramos que esté realizando el despliegue constantemente, por lo que se ha incluido una opción de menú para activarlo o desactivarlo.

Este plugin es capar de realizar un despliegue múltiple si detecta que estamos utilizando WSL para la generación de los archivos CGI para el entorno Linux (nueva función de Xailer 9). En dicho caso no sólo despliega en nuestro servidor en Internet, sino que también despliega en WSL

Este sería un ejemplo de despliegue:

Su configuración es bastante sencilla. Tan sólo hay que indicar las direcciones URL donde queremos que despliegue y nuestras credenciales.

Estas son la propiedades que hay que configurar y viendo su nombre espero que se auto-expliquen, así mismas:

  • URL donde se despliegan los archivos tipo CGI
  • URL donde se despliegan el resto de archivos
  • Path donde se copian el resto de archivos en entornos WSL
  • Extensión de archivos CGI que se reconocen
  • Directorio donde se ubicarán los archivos JavaScript
  • Directorio donde se ubicarán los archivos CSS
  • Directorio donde se ubicarán el resto de archivos
  • Nombre de usuario de la conexión FTP
  • Contraseña de la conexión FTP

El plugin es susceptible de importantes mejoras. Como por ejemplo, poder establecer distintas URL y credenciales dependiendo del proyecto. Hemos decidido no incluir esa funcionalidad porque cada usuario tendrá su propia preferencia. No obstante, si alguien se anima a hacerlo, encantado de echarle una mano para cualquier duda que pueda tener.

Os adelanto que sólo funciona con Xailer 9 ya que se apoya en alguna función que no existe más que en Xailer 9.

Podéis descargar el plugin de este enlace.

Un saludo y hasta pronto

Ignacio Ortiz de Zúñiga
[Equipo de Xailer]

» Leer más, comentarios, etc...

Juanjo Navarro

Nueva edición del Technology Radar (la 30)

mayo 21, 2024 06:31

Si no conoces el Technology Radar de la empresa Thoughtworks, deberías echarle un vistazo.

Se trata de un informe semestral donde se analizan una serie de puntos (“blips” los llaman) sobre el mundo del desarrollo de proyectos tecnológicos.

Estos blips se dividen en cuatro tipos:

  • Técnicas
  • Herramientas
  • Plataformas
  • Lenguajes & Frameworks

Para cada blip te aconsejan:

  • Adoptar
  • Probar
  • Evaluar – mantenerse al tanto y jugar un poco con él para conocerlo.
  • Resistir – mejor no usarlo, es dudoso su futuro o utilidad.

Esta última edición (la 30) está llena de IA y devops, cómo no. Yo me suelo fijar en temas prácticos (en Herramientas o en Lenguajes y Frameworks, sobre todo).

Aquí os dejo lo que más me ha llamado la atención (y he probado más o menos) de la última edición:

  • Pop – Herramienta para compartir la pantalla para hacer Pair Programming.
  • Aider – Una IA que hace de “coworker” de programación y me ha parecido muy bien pensado.
  • Continue – Un “Github Copilot” opensource en el que puedes elegir qué LLM utilizar (con el consiguiente coste por el uso del API si es de pago, claro). Puedes también utilizar un LLM local corriendo en tu infraestructura.
  • Dify – Un creador “gráfico” de aplicaciones “LLM” bastante impresionante. Permite diseñar un “flujo” de trabajo, donde vas haciendo consultas a distintos modelos IA, unir la información, hacer otras llamadas dependiendo de la respuesta anterior, etc. Además tiene integrado un sistema RAG con lo que le puedes subir documentos que la IA utilizará como referencia.
  • Crabviz – Utilidad para VSCode que genera un gráfico de llamadas entre clases.
  • Electric – Librería y framework que permite crear aplicaciones móviles o webapps con una base de datos “local” (que se ejecuta en el propio móvil o la página) y que automáticamente se sincroniza con un PostgreSQL en el back.
  • LiteLLM – Algo así como un “proxy” LLM. Permite configurar distintos LLM y acceder a ellos con un API común.

» Leer más, comentarios, etc...

Una sinfonía en C#

Configurar Docker + HTTPS + nginx

mayo 21, 2024 12:00

“Introducción”

Cuando queremos probar algo en local (una aplicación web), dentro de un contenedor, no podemos utilizar https como desde Visual Studio o IIS. Si necesitamos sí o sí https, debemos configurar algunas cosas, como por ejemplo, un certificado autofirmado, nginx, etc. En este post vamos a detaler cómo hacerlo.

Pasos

Vamos a necesitar hacer un par de cosas, voy a detallar los pasos a seguir en una PC con Windows y una aplicación .NET Core, es que lo que yo uso.

Básicamente pondremos nuestra aplicación en un contenedor, configuraremos nginx para que haga de proxy y que además tenga https. Luego un docker compose que levante todo.

Crear certificado autofirmado

Para crear certificados autofirmados, podemos utilizar openssl. En Windows, podemos instalarlo desde aquí.

y ejecutar este comando:

localhost.conf

[req]
default_bits       = 2048
default_keyfile    = localhost.key
distinguished_name = req_distinguished_name
req_extensions     = req_ext
x509_extensions    = v3_ca

[req_distinguished_name]
countryName                 = Country Name (2 letter code)
countryName_default         = US
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = Texas
localityName                = Locality Name (eg, city)
localityName_default        = Dallas
organizationName            = Organization Name (eg, company)
organizationName_default    = localhost
organizationalUnitName      = organizationalunit
organizationalUnitName_default = Development
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_default          = localhost
commonName_max              = 64

[req_ext]
subjectAltName = @alt_names

[v3_ca]
subjectAltName = @alt_names

[alt_names]
DNS.1   = localhost
DNS.2   = 127.0.0.1
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout localhost.key -out localhost.crt -config localhost.conf -passin pass:12345678

Esto creará dos archivos, localhost.crt y localhost.key, estos archivos los usuaremos en nginx.

Ahora necesitamos registrar el certificado como confiable ya que es auto-firmado. (es decir, registrar en Windows como confiable)

En el Administrador de Certificados (certmgr.msc), puedes encontrar esta ubicación siguiendo estos pasos:

Abrimos el Administrador de Certificados.

  • Para esto escribimos certmgr.msc en el diálogo Ejecutar (Win + R).
  • En el Administrador de Certificados, expande el árbol de Certificados “Usuario Actual” en el panel izquierdo.
  • Debajo de esto, expande la carpeta Autoridades de Certificación Raíz Confiables.

Hacemos clic en la carpeta Certificados bajo Autoridades de Certificación Raíz Confiables.

Esta es la ubicación equivalente a Cert:\CurrentUser\Root en PowerShell.

Luego

En el Administrador de Certificados (certlm.msc, Certificate Manager for local machine), puedes encontrar esta ubicación siguiendo estos pasos:

  • Abre el Administrador de Certificados para la Máquina Local. Puedes hacer esto escribiendo certlm.msc en el diálogo Ejecutar (Win + R).
  • En el Administrador de Certificados, expande el árbol Certificados (Computadora Local) en el panel izquierdo.
  • Debajo de esto, expande la carpeta Personal.
  • Haz clic en la carpeta Certificados bajo Personal.

Ahora nginx usará los archivos de certificado y clave para servir https. Y deberías estar bien.

Configurar nginx

Para esto simplemente vamos a crear un archivo de configuración para nginx, que será el siguiente:

nginx.conf

worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    upstream web-api {
        server api:80;
    }

    server {
        listen 80;
        server_name localhost;

        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 443 ssl;
        server_name localhost;

        ssl_certificate /etc/ssl/certs/localhost.crt;
        ssl_certificate_key /etc/ssl/private/localhost.key;

        location / {
            proxy_pass         http://web-api;
            proxy_redirect     off;
            proxy_http_version 1.1;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection keep-alive;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            proxy_set_header   X-Forwarded-Host $server_name;
        }
    }
}

Le decimos a nginx que utilice el certificado y la clave que creamos antes, y que escuche en el puerto 443. y con el proxy_pass le decimos que redirija las peticiones al servicio que escucha en el puerto 80. (más adelante ese será el nombre del servicio en el docker compose)

Ahora creamos el Dockerfile para nginx:

FROM nginx:alpine

COPY ./nginx.conf /etc/nginx/nginx.conf
COPY localhost.crt /etc/ssl/certs/localhost.crt
COPY localhost.key /etc/ssl/private/localhost.key

CMD ["nginx", "-g", "daemon off;"]

Básicamente copiamos el archivo de configuración y los archivos de certificado y clave al contenedor.

Configurar nuestra app

En este caso una simple aplicación .NET Core, que escucha en el puerto 80.

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app

COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app

ENV ASPNETCORE_HTTP_PORTS 80

EXPOSE 80
EXPOSE 443

COPY --from=build /app/out ./

CMD ["dotnet", "app.dll"]

Docker compose

version: "3.7"

services:

reverseproxy:
    build:
      context: ./nginx
      dockerfile: Dockerfile.nginx
    ports:
      - "8080:80"
      - "1443:443"
    restart: always

api:
    depends_on:
      - reverseproxy
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8088:80"
    restart: always

Simplemente apuntamos los dos servicios a sus Dockerfiles, y abrimos el puerto 1443 para https.

Si vemos algún error en el navegador relacionado con https lo más probable es que no hayamos registrado el certificado como confiable.

Dejo por acá un repositorio con el código

Nos leemos.

» Leer más, comentarios, etc...

Arragonán

El lado estratégico de Domain-Driven Design. CommitConf 2024

mayo 08, 2024 12:00

Hace unas semanas estuve en Commit Conf en Madrid, evento al que no iba desde hace mucho. Estuve compartiendo la charla El lado estratégico de Domain-Driven Design, iterando ligeramente la que hice hace unos meses en La Vertical, a su vez basada en una charla más larga (y espesa) que he impartido in-company en varias ocasiones.

Foto de la sala de conferencias del track 1 del evento

En los últimos años ha crecido el interés y la adopción de Domain-Driven Design en la comunidad de desarrollo de software. Ahora es bastante habitual oír hablar del lado táctico de DDD, la mayoría de las veces acompañado del uso de ports & adapters (aka Hexagonal Architecture). Pero, al menos en castellano, no he visto hablar apenas de DDD estratégico y lo que nos puede aportar a nivel de Sociotechnical Architecture.

Así que de ahí vino buena parte de mi motivación de proponer repetirla en un evento mucho más masivo como es la Commit Conf.

La presentación está dividida en 4 bloques:

  • Introducción a DDD y específicamente a la parte estratégica
  • Resumen de las actividades estratégicas basado en el Domain-Driven Design Starter Modelling Process de DDD Crew
  • Un ejemplo práctico de DDD estratégico basado en un caso real, mostrando su división y conexión entre dominios visibilizándolo con un Context Map un tanto enriquecido con su clasificación desde diferentes puntos de vista
  • Otras consideraciones a tener a nivel de la organización de equipos

Aquí os dejo el vídeo de la charla

Y el genially que usé para la presentación

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin