Quantcast
Channel: campusMVP.es
Viewing all 776 articles
Browse latest View live

Los 5 puntos clave a la hora de elegir un curso de programación online

$
0
0

Nunca ha habido un mejor momento para aprender a programar; ya desde antes de la crisis actual. Existen un gran número de cursos y plataformas de aprendizaje online, tanto gratuitos como de pago.

Para elegir un buen curso de desarrollo en una buena plataforma de formación es importante dedicar un tiempo a considerar qué curso será el mejor para ti en función del tipo de persona que eres y de tus circunstancias.

Sin una buena evaluación, puede que te encuentres a la deriva yendo de un curso a otro, en un perpetuo estado de confusión, perdiendo dinero o, lo que es más importante, tiempo (a veces un curso gratuito sale muy caro). Esto provoca frustración y puede llevar a que uno se dé por vencido y abandone la formación en esa materia por completo.

Una adecuada valoración de cada curso es fundamental para que el proceso resulte óptimo y satisfactorio. En este artículo repasaremos cinco puntos de evaluación basados en la psicología educativa, que son muy importantes a la hora de establecer un criterio para elegir un curso y una plataforma de formación online.

Sí, en el mercado hay un gran número de cursos para aprender a programar, pero al evaluarlos teniendo en cuenta estos cinco criterios, se eliminarán muchos de la lista.

Dicho esto: no todos los cursos son para todo el mundo. Dependerá de quién eres, de dónde partas en el aprendizaje, de cuáles son tus capacidades, del tiempo del que dispones, de si tienes mucha experiencia o no... No existe una "bala de plata", y una misma formación o método no tiene por qué ser válida para todo el mundo. Pero estos cinco puntos garantizan que, para la mayor parte de la gente que tiene el perfil apropiado para programar, la formación tienda a ser la más adecuada.

Vamos a verlos.

1.- El estilo de enseñanza online

No vamos a entrar aquí a repasar todos los estilos de aprendizaje, que se refieren a la forma preferida que tiene cada mente para asimilar conceptos, pero sí diremos que, la mayoría de las personas tienen un estilo dominante de aprendizaje o una combinación de estilos dominantes que son fundamentalmente estos tres:

  • El aprendizaje verbal (lectura)
  • El aprendizaje visual (vídeos e imágenes)
  • El aprendizaje auditivo (escucha)

Esto significa que los cursos con vídeo + audio que además ofrecen material con teoría para su lectura acompañada de esquemas, imágenes y capturas, aseguran que más participantes puedan tener éxito en la realización de los mismos.

En cualquier curso de campusMVP hay contenidos verbales, visuales y auditivos, no sólo una sucesión de vídeos uno tras otro, sin pausa para reflexionar y asentar conceptos. Todos los módulos se componen de teoría para su lectura, que se refuerza con vídeos con voz centrados en lo importante. Con la teoría también se incluyen esquemas e imágenes para ayudar a aclarar los conceptos más complejos.

La combinación de los tres estilos de aprendizaje facilita la comprensión de los conceptos. Pero es mucho más costosa de crear que una simple sucesión de vídeos grabados como si fuese una clase en directo.

Cuando se busca un curso de programación, se debe elegir uno en el que el material combine múltiples formas de transmitir las ideas, en cada caso la más apropiada, para aumentar las posibilidades de comprender y retener la información.

El curso online que tienes en mente adquirir para formarte, ¿qué tipo de material te ofrece? ¿Vídeo tan solo? ¿Una buena teoría para los conceptos importantes? ¿Te enseña "recetas" o te enseña conceptos? ¿Te ofrece material de consulta y repaso posteriores para cuando termine la formación?

2.- Aprendizaje basado en proyectos

Imagen ornamental, una persona llevando un pan que ha hecho con sus manos. Foto por Toa Heftiba, CC0

Tratándose de temas de formación técnica, el aprendizaje kinestésico - aprender haciendo - es la forma más efectiva y eficiente de asimilar la materia.

El aprendizaje se produce cuando hay una transformación de la información en un producto de trabajo. Durante este proceso de aprendizaje es natural que las tareas de programación se compliquen y que no funcionen las cosas, porque así se aprende de forma contextual. La máxima de campusMVP es que la mejor forma de aprender a programar es programando. Pero no de cualquier manera.

La formación siempre debe aspirar a una proporción de 80/20. El 80% del tiempo se debería estar programando o pensando en cómo resolver tareas de programación, revisando tu propio código hasta dar con la solución más adecuada. El 20% restante se debería estar leyendo y viendo vídeos.

Siempre hay que tener en cuenta si el curso que se quiere realizar tiene un proyecto o una aplicación que se desarrolla a lo largo del mismo. La programación en casos reales desarrolla la memoria para adquirir habilidades básicas. Y estos proyectos no deben ser los típicos laboratorios paso a paso en los que te van diciendo lo que tienes que hacer, sino ofrecer un punto de partida y uno de destino, unas pocas indicaciones para el camino, y que los pasos adecuados los decidas e implementes tú, con apoyo de un tutor, y basándose en todo lo aprendido hasta ese momento en el curso.

"Pegarse" con el código es la única forma de aprender. Lo otro son "recetas" que no servirán para la vida real, cuando no estés bajo condiciones de laboratorio.

Eso es realmente el aprendizaje kinestésico: aprender haciendo. Averigüa si la formación que te interesa te da "mascado" cómo hacer las cosas o si te obliga a decidir y trabajar con apoyo de un tutor experimentado. Si te dicen que "aprendes sin esfuerzo": o no aprenderás nada útil o te mienten.

3.- Posibilidad de resolución de problemas

Esto está muy relacionado con lo anterior, pero va un poco más allá...

Desafortunadamente, muchos cursos programación no dan la opción a que los estudiantes resuelvan problemas. La mayoría de los cursos enlatados basados en vídeo muestran a alguien programando para que los alumnos lo imiten sin pensar. El resultado siempre sale.

Y, aunque es importante replicar también las "recetas" que te explican cuando estás empezando en una materia, sin el entrenamiento adecuado para resolver problemas, será muy difícil encontrar una salida profesional en el mundo de la programación. Un buen curso debe ofrecer retos que se sepa que te van a provocar problemas cuando los resuelvas.

Esto se debe a que se necesita práctica en el análisis causa/efecto y en la comprensión de la jerarquía, la depuración de errores y la refactorización. A veces, el objetivo que se persigue con los problemas es secundario. Lo importante es el aprendizaje que surge al intentarlo, al tropezar, al encontrarse con problemas y no tanto el hecho de conseguir un resultado concreto.

Esta es probablemente una de las mayores fortalezas de campusMVP: siempre se están resolviendo problemas y, esto es muy relevante, aprendiendo a gestionar cierto grado de frustración.

Siendo esto importante, tampoco hay que abusar. Para prevenir la fatiga, es aconsejable que se establezcan objetivos para la resolución de un cierto número de problemas a la semana o en cada módulo.

La formación que estás considerando ¿te ofrece la oportunidad de resolver problemas (no proyectos concretos: problemas)? ¿Te obligará a "pelearte" con ellos y a gestionar un poco la frustración? ¿Te apoyarán cuando no puedas resolverlos?

4.- La figura del tutor

No hay nada peor que estar trabajando en un problema dentro de un curso y toparse con un muro de hormigón y no tener a dónde acudir en busca de ayuda. Se intenta hacer todo lo que se puede, incluyendo un montón de búsquedas en Google, pero nada. Esto hace que el aprendizaje se frene en seco, y por eso es tan importante que el curso disponga de un tutor para ayudar al alumno a "desbloquearse".

Un tutor con contacto directo, no un foro donde a lo mejor te contesta un compañero mañana, dentro de una semana o a lo mejor nunca.

Esto es crucial. Si no se tiene una manera de obtener respuestas a las preguntas de manera oportuna y rápida, uno se olvida de lo que ha aprendido, hay ciertas cosas que no va a ver nunca o se siente desmotivado para seguir avanzando.

¿La formación que estás sopesando hacer te ofrece un tutor con contacto directo y tiempo de respuesta garantizado? ¿Quién es ese tutor? ¿Quizá el mismo profesional reconocido que ha diseñado el curso?, como en campusMVP.

5.- El mapa del tesoro: hitos y plazos

Imagen ornamental. Un camino de madera en el bosque, por Erik Mclean, CC0

Aunque un buen marketing puede hacer pensar que puedes aprender a programar en un mes, sin esfuerzo y conseguir un trabajo increíble, lo más probable es que no sea así. Y eso está bien. Aprender a programar o una nueva tecnología lleva tiempo.

Cada uno aprende a su propio ritmo, pero es importante que se fijen objetivos realistas para saber en cada momento dónde deberíamos estar, tener hitos a corto, medio y largo plazo para ayudarnos a planificar y no desanimarnos por el camino.

Los grandes cursos proporcionan puntos de referencia que ayudan a motivarte y a animarte a seguir adelante. En cualquier curso de campusMVP siempre tienes claros los hitos y objetivos en cada momento:

Ejemplo de información sobre el alumno en la portada de un curso de campusMVP

Desde el instante en el que accedes al curso conoces perfectamente en dónde estás respecto al aprendizaje total, qué llevas bien y qué llevas mal, cuál es el próximo hito que tienes en el futuro inmediato, cuánto te queda y las estadísticas clave de tu aprendizaje.

Esto no está reñido con que tengas libertad para ir a tu ritmo y decidir cómo estudiar. Solamente te ayuda a tener una brújula por la que guiarte. Que no es poco.

En formación online esto es especialmente importante o corres el riesgo de no terminar la formación nunca. Por eso también entra dentro de nuestra filosofía el hecho de que todas las formaciones tengan una fecha de fin concreta y cerrada. Y por eso cerca del 90% de nuestros alumnos finalizan la formación con éxito.

Aunque esto es algo que muchas veces se pasa por alto al valorar un curso, es sumamente importante para la salud mental mientras se aprende y para lograr aprovechar y terminar la formación online. La sensación de logro hace maravillas para en la psique humana.

¿Cómo estás de fuerza de voluntad pra estudiar a pesar del día a día de tu trabajo? La formación que estás considerando ¿ofrece hitos claros o deja a tu libre albedrío qué haces y cuándo lo haces? Si tienes toda la vida para hacerlo, es probable que no lo hagas en tu vida. Si te cobran una cuota mensual, les importa que pagues todos los meses, no que aprendas en el tiempo adecuado.


Cómo manejar trazas en .Net Core con Serilog

$
0
0

Imagen ornamental con el título del post y el logo de Serilog

En el mundo del desarrollo de software estamos muy acostumbrados a depurar código mientras desarrollamos un producto o una nueva característica. Para ello, los entornos de desarrollo (IDE por sus siglas en inglés) van siempre acompañados de herramientas de depuración que nos permiten recolectar información sobre la ejecución, el valor de las variables, interrumpir el programa en puntos concretos y mover el puntero de ejecución hacia atrás y hacia adelante.

Esto es indispensable durante la fase de desarrollo y mantenimiento, pero ¿qué podemos hacer si el software está ya en producción y se produce un fallo catastrófico? En producción no tenemos un IDE con su depurador asociado para poder ver qué está pasando... Por ello es de vital importancia instrumentar el código de manera que tengamos métricas y trazas que aporten esa información que nos falta cuando algo no va bien en producción.

¿Por qué utilizar un logger?

Existen diferentes métodos y destinos para poder registrar toda esa información de manera que sea posible consultarla a posteriori: desde herramientas básicas integradas en la plataforma .NET, hasta utilidades específicas de proveedores cloud, como Azure, para instrumentar aplicaciones y obtener información en tiempo real sobre cómo se están ejecutando.

Parafraseando a Arquímedes*: dadme un IDE y desarrollaré lo necesario. Es posible añadir todas esas funciones y características que nos resultarían ideales pero hay que plantearse si merece la pena.

* Como dato histórico, la frase que se atribuye a Arquímedes es "Dadme un punto de apoyo y moveré el mundo".

Por ejemplo, podríamos crear con facilidad nosotros mismos una clase estática que guarde la información en un fichero:

public static class FileLogger
{
    private const string FilePath = "log.txt";
    public static void Log(string messsage)
    {
        using var fileStream = new FileStream(FilePath, FileMode.Append);
        using var writter = new StreamWriter(fileStream);
        writter.WriteLine(messsage);
    }
}
//...

FileLogger.Log("Mensaje 1");
FileLogger.Log("Mensaje 2");
FileLogger.Log("Mensaje 3");

Como se puede comprobar, es una implementación muy sencilla que simplemente registra cada mensaje en un fichero llamado log.txt, junto al ejecutable. De hecho, si colocamos ese código en una aplicación de consola y tras ejecutarla abrimos el fichero nos encontrarémos algo como esto:

La imagen muestra el fichero generado con 3 líneas con los valores Mensaje 1, Mensaje 2 y Mensaje 3, uno en cada línea

Hay que reconocer que aunque funcional, el ejemplo anterior es poco práctico y nada recomendable. Sí que es cierto que puede haber algún caso muy particular en el que esto sea suficiente. Pero por lo general vamos a necesitar más información (como la hora, los datos internos de una excepción...), un sistema más robusto de guardar la información y de lidiar con la concurrencia o, simplemente, que los ficheros se vayan purgando periódicamente para evitar acumular un montón de gigabytes con información antigua.

Existen varias herramientas disponibles para poder resolver la situación y que están ya preparadas con mucha funcionalidad extra que nos va a facilitar la vida. Por citar algunas podríamos hablar de NLog, Apache log4net o el que vamos a revisar en más profundidad en el artículo de hoy: Serilog.

Todos ellos trabajan de manera "similar": nos ofrecen un mecanismo para configurar un logger y después vamos a trabajar con él para registrar las trazas según lo configurado. Es por esto que aunque aquí vayamos a hablar de Serilog, utilizar cualquier otro logger no debería ser muy complicado una vez se conoce alguno.

Y ahora que ya hemos hecho las presentaciones, vamos con lo que realmente nos interesa. ¿Cómo se pone en marcha Serilog en .Net Core? Depende un poco del tipo de aplicación que estemos creando, así que empecemos con una aplicación de consola y después veremos cómo aprovechar la inyección de dependencias.

Creando un logger con Serilog

Para poder utilizar Serilog, lo primero que vamos a necesitar es añadir a nuestro proyecto el paquete Serilog. Este es el paquete básico que contiene toda la infraestructura necesaria para utilizar Serilog y sobre este paquete se van a ir añadiendo diferentes salidas como pueden ser ficheros, consola, una base de datos, etc... Estos diferentes destinos se conocen como Sinks y la lista de paquetes ya listos para utilizar diferentes Sinks es muy larga.

Puedes comprobar la lista completa de Sinks, así como sus enlaces en la wiki del proyecto.

En este caso, el ejemplo más básico sería utilizar el Sink de consola, para que todas las trazas que registremos se muestren por consola. Para esto, vamos a añadir también una referencia al paquete Serilog.Sinks.Console.

Teniendo ya en el proyecto los paquetes de Serilog y del Sink de consola, vamos a poder crear un logger con un código tan simple como este:

var logger = new LoggerConfiguration()
                   .WriteTo.Console()
                   .CreateLogger();

Una vez creado el logger, basta con mantener la instancia en memoría para registrar mensajes en consola desde cualquier punto donde tengamos acceso a ésta. Para conseguir registrar el mensaje, sólo hay que llamar a cualquiera de los métodos a nuestra disposición, a saber:

  • Verbose(string)
  • Debug(string)
  • Information(string)
  • Warning(string)
  • Error(string)
  • Fatal(string)

Con ellos vamos a poder registrar trazas con diferentes niveles de importancia e información a lo largo del código de la aplicación. Posteriormente podremos filtrarlos de manera global o simplemente indicándole el nivel mínimo que nos interese, al registrar el Sink. Por ejemplo, si ejecutamos el siguiente código:

var logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console(LogEventLevel.Information)
                .CreateLogger();

logger.Verbose("Mensaje Verbose");
logger.Debug("Mensaje Debug");
logger.Information("Mensaje Information");
logger.Warning("Mensaje Warning");
logger.Error("Mensaje Error");
logger.Fatal("Mensaje Fatal");

Podemos comprobar que todos loe mensajes con un nivel inferior a Information se filtran directamente y no llegan a la salida del Sink:

La imagen muestra el resultado de la ejecución donde se ve que los mensajes Verbose y Debug no están

Añadiendo diferentes Sink a nuestro logger

Ahora que tenemos estos conceptos básicos claros, vamos a replicar la misma funcionalidad que tenía nuestro logger hecho a mano del principio, añadiendo el Sink de salida a archivos. Para eso añadimos el paquete "Serilog.Sinks.File" y configuramos el Sink de la misma manera que antes:

var logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console(LogEventLevel.Information)
                .WriteTo.File("log.txt", LogEventLevel.Fatal) //Con esta línea configuramos la salida a fichero
                .CreateLogger();

Con este pequeño cambio, ya hemos añadido una nueva salida al logger, de manera que vamos a mostrar toda la información superior a Information en la consola y, además, guardaremos todas las trazas de nivel igual o superior a Fatal (que en la práctica es solo Fatal) en el fichero log.txt.

De todos modos, respecto a nuestro logger hecho a mano no tenemos una gran ventaja. Simplemente hemos conseguido un par de filtros y una salida adicional por consola. Nada que no se pueda hacer escribiendo un poco más de código en nuestra pequeña clase.

La verdadera potencia se hace patente cuando necesitamos esa funcionalidad extra como poder rotar ficheros. Imaginemos que queremos que las trazas se agrupen por día y que además se retengan durante un máximo de 10 días. Para conseguir eso ya tendríamos que empezar a añadir bastante código extra a nuestra clase. Pero utilizando el Sink de Serilog lograrlo se reduce a algo como esto:

var logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console(LogEventLevel.Information)
                .WriteTo.File("log.txt", LogEventLevel.Fatal, rollingInterval: RollingInterval.Day, retainedFileCountLimit:10)
                .CreateLogger();

¿Sencillo verdad? Pues aún hay (mucho) más. Utilizando diferentes Sink las posibilidades aumentan para poder cubrir la gran mayoría de los casos. Sin ir más lejos, un escenario habitual es escribir registros en una base de datos, y eso es algo tan simple como esto:

//Cadena de conexión de la base de datos
var connectionString = "Server=(localdb)\\MSSQLLocalDB;Database=Logger;Integrated Security=true";
//Configuración del Sink de MSSqlServer
var sqlLoggerOptions = new SinkOptions
{
    AutoCreateSqlTable = true,
    SchemaName = "Logger",
    TableName = "Logs",
    BatchPostingLimit = 1
};

var logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console(LogEventLevel.Information)
                .WriteTo.File("log.txt", LogEventLevel.Fatal,rollingInterval: RollingInterval.Day,retainedFileCountLimit:10)
                .WriteTo.MSSqlServer(connectionString, sqlLoggerOptions) //Aquí se añade el Sink
                .CreateLogger();

Como es de suponer, para poder utilizar el Sink de MSSqlServer hay que añadir el paquete "Serilog.Sinks.MSSqlServer"

Enriqueciendo las trazas con información adicional

En este punto ya está claro que, salvo que nuestro logger sea algo extremadamente simple, utilizar un logger especializado es siempre la mejor opción.

Otra de las opciones muy interesantes que ofrece Serilog es la posibilidad de añadir información útil a las trazas de manera automática. A este concepto se le conoce como enriquecer las trazas.

Esto se consigue gracias a la gran capacidad de extensión que ofrece Serilog. Aunque podemos crear nuestros propios enriquecedores para Serilog, ya existen algunos disponibles como paquete NuGet.

Por ejemplo, añadiendo el identificador del proceso:

var logger = new LoggerConfiguration()
                .Enrich.WithProcessId() //Aquí se añade el enriquecedor
                .WriteTo.Console(LogEventLevel.Information)
                .WriteTo.File("log.txt", LogEventLevel.Fatal,rollingInterval: RollingInterval.Day,retainedFileCountLimit:10)
                .WriteTo.MSSqlServer(connectionString, sqlLoggerOptions)
                .CreateLogger();

Vamos a obtener una salida parecida a esta:

La imagen muestra los mensajes enriquecidos registrados en la base de datos donde se ve el identificador del proceso

Para poder utilizar el método WithProcessId hay que añadir el paquete Serilog.Enrichers.Process

Configurando Serilog desde un fichero de configuración

Si bien es cierto que se pueden configurar todos los aspectos de Serilog mediante código, esto no deja de ser una solución muy rígida, que impide poder cambiar la configuración a menos que recompilemos el proyecto. No son pocas las veces donde necesitamos que una aplicación registre absolutamente toda la información disponible para arreglar algún error, pero nadie quiere tener ficheros de varios gigas en producción.

Para poder manejar esta situación los loggers suelen ofrecer la posibilidad de configurarse mediante un archivo, que es fácilmente editable. Serilog no es menos, y permite que configuremos todas sus opciones mediante un fichero app.config/web.config para .NET "tradicional", o un fichero appsettings.json en .NET Core.

Ya que la finalidad en este artículo no es entrar en profundidad en Serilog, sino darte una visión general sobre sus posibilidades, vamos a plantear solamente la opción de appsettings.json, ya que previsiblemente es la que mayor recorrido tiene. Serilog puede tomar su configuración desde cualquier origen de Microsoft.Extensions.Configuration siempre que añadamos el paquete Serilog.Settings.Configuration, por lo que podemos crear un archivo de configuración y pasárselo a Serilog para inicializarlo con las opciones contenidas en éste:

var configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();

var logger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .CreateLogger();

Pero no solo llega con cambiar el código. También es necesario escribir la configuración en el fichero. Para ello creamos un objeto en el JSON y le indicamos todas las configuraciones que habíamos puesto antes en el código:

{
  "Serilog": {
    "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.MSSqlServer" ],
    "WriteTo": [
      {
        "Name": "Console",
        "restrictedToMinimumLevel": "Information"
      },
      {
        "Name": "File",
        "Args": {
          "path": "log.txt",
          "rollingInterval": 3,
          "retainedFileCountLimit": 10
        },
        "restrictedToMinimumLevel": "Fatal"
      },
      {
        "Name": "MSSqlServer",
        "Args": {
          "connectionString": "Server=(localdb)\\MSSQLLocalDB;Database=Logger;Integrated Security=true",
          "sinkOptionsSection": {
            "tableName": "Logs",
            "schemaName": "Logger",
            "autoCreateSqlTable": true,
            "batchPostingLimit": 1
          }
        }
      }
    ],
    "Enrich": [ "WithThreadId" ]
  }
}

Si analizamos en detalle el contenido de este archivo podemos comprobar que se parece mucho a lo que teníamos por código, pero de manera declarativa. ¡Genial!

Registrando Serilog en el inyector de dependencias

Lo que hemos planteado hasta ahora está muy bien, pero para los que estamos habituados a trabajar con inyección de dependencias y la interfaz ILogger de .Net Core, esto se queda un poco corto. Eso de crear instancias de objetos por aquí y por allá va totalmente en contra del principio de inyección de dependencias. Es por eso que, Serilog está preparado también para registrarse en el contenedor de inyección de dependencias.

¿A estas alturas ya no hay dudas de cómo añadir ese soporte verdad? En efecto... Utilizando un paquete NuGet.

Esta vez vamos a necesitar incorporar Serilog.AspNetCore, y con esto podremos ir a Program.cs y añadir las únicas 9 líneas que necesitamos para que Serilog trabaje debajo del capó de ILogger:

public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        })
        //Borramos todos los registros de los loggers que vienen prerregistrados
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.SetMinimumLevel(LogLevel.Debug);
        })
        //Añadimos Serilog obteniendo la configuración desde Microsoft.Extensions.Configuration
        .UseSerilog((HostBuilderContext context, LoggerConfiguration loggerConfiguration) =>
        {
            loggerConfiguration.ReadFrom.Configuration(context.Configuration);
        });

Et voilà!, ya tenemos listo nuestro Serilog trabajando por debajo de ILogger. Allá donde utilicemos ILogger realmente por debajo estaremos llamando a Serilog y con todas las configuraciones que le hayamos indicado.

Conclusión

Después de todo lo planteado, crear nuestros propios loggers debería ser algo que ni se nos pase por la cabeza. Al alcance de unos pocos clics tenemos la potencia de loggers tan completos como Serilog.

En esta entrada hemos planteado algunas de las principales opciones de Serilog, aunque no son ni mucho menos las únicas. Te recomiendo encarecidamente que, si no tienes costumbre de trabajar con herramientas como Serilog, Nlog u otros loggers, le eches un vistazo a la documentación de Serilog, ya que un mundo de posibilidades te está esperando.

Ahora que ya conoces lo interesante que puede resultar un sistema de logging como Serilog, seguramente te interesará también aprender a utilizarlo junto a Seq, un sistema de ingesta de logs que permite hacer analítica en tiempo real de los mismos.

Y tú, ¿conocías Serilog? ¿crees que puede ayudarte en el día a día de tus proyectos? Puedes dejarnos tus experiencias e impresiones en los comentarios.

.NET MAUI: Construir aplicaciones multiplataforma de escritorio con Xamarin.Forms

$
0
0

En el transcurso del evento BUILD esta madrugada, Microsoft ha anunciado MAUI, que es el futuro framework de desarrollo de interfaces multiplataforma que formará parte de .NET unificado.

Aunque la información oficial es la que está en el primer enlace, vamos a tratar de resumir y contestar a las preguntas más importantes sobre este anuncio y esta tecnología.

¿Qué es .NET MAUI?

Es el acrónimo (un poco pillado por los pelos) de .NET Multi-platform App UI. Se trata de una evolución de Xamarin.Forms que permitirá crear aplicaciones más allá de Android e iOS y otras plataformas que soporta actualmente esta tecnología. Con MAUI será posible utilizar Xamarin.Forms para crear también aplicaciones de escritorio para Windows (x32, x64 y ARM) y macOS, sacando partido de los conocimientos que ya tienes sobre esta tecnología y dándole más amplitud de miras.

Es importante señalar que en la actualidad ya es posible construir aplicaciones de escritorio con Xamarin.Forms, pero no están oficialmente soportadas. Las aplicaciones construidas con MAUI sí que estarán soportadas oficialmente por Microsoft y formarán parte integral de .NET 6 cuando esté disponible. Linux seguirá siendo posible, pero como ahora: apoyados por la comunidad, sin soporte oficial, lo cual le resta un poco de fuerza a la propuesta.

Este gráfico, sacado del anuncio oficial, define muy bien qué es MAUI:

Resumen de MAUI - Está explicado en el texto

Además simplifican el actual desarrollo existente en Xamarin, unificando el tooling, simplificando la estructura de proyectos, facilitando la reutilización, etc. (todos los detalles en el primer enlace).

Seguirá soportando el patrón MVVM (Model-View-ViewModel) y también la nueva estrella que parece que se está haciendo un hueco en Xamarin, el patrón MVU (Model-View-Update). Por supuesto, como ahora, podrás sacar partido a las APIs nativas cuando sea necesario, pero en lugar de tener que estar creando un nuevo proyecto por cada sistema operativo, ahora irán todos bajo un único proyecto, simplificando la gestión del desarrollo.

¿Cuándo estará disponible?

El proyecto es Open Source, por lo que si te interesa puedes ir a su repositorio en GitHub e ir viendo el código o experimentando. Pero de momento está en pañales.

Según el roadmap y el anuncio , las primeras versiones preliminares se empezarán a ver a finales de este año, probablemente con .NET 5 en noviembre. A lo largo de 2021 se irán sacando más previews y la versión definitiva se espera que esté disponible junto con .NET 6 en noviembre de 2021, dentro de año y medio.

¿Qué pasa con Xamarin?

Pues no pasa nada de momento. Seguirá siendo la plataforma oficial para crear aplicaciones móviles y podrás crear aplicaciones de escritorio también usando los proyectos de la comunidad. Sacarán nuevas versiones cada 6 semanas como hasta ahora.

Una vez que salga .NET MAUI, a finales de 2021, darán soporte oficial a Xamarin durante 1 año, pero será aconsejable que migres las aplicaciones al nuevo framework.

Por suerte, la migración será fácil y probablemente automática, así que no hay que agobiarse en exceso.

¿Debo aprender Xamarin ahora?

Pues si quieres crear aplicaciones móviles nativas multiplataforma para Android e iOS usando C# y XAML, la respuesta es un rotundo sí. Sigue siendo una plataforma muy interesante que te permitirá sacar partido a lo que ya sabes de .NET para crear aplicaciones móviles de manera rápida y que además son nativas, no funcionando en una vista Web.

Una vez que salga MAUI no debería resultarte difícil adaptarte y migrar las aplicaciones al nuevo framework, obteniendo de paso más opciones para explotarlas, dando a tus clientes el extra de tener aplicaciones de escritorio con muy poco esfuerzo adicional.

En resumen

Aunque el nombre pueda confundir a algunos y lo hayan anunciado con tanta antelación, MAUI es una gran noticia para todos los desarrolladores de Xamarin. Podrán seguir invirtiendo en sus conocimientos desarrollando aplicaciones móviles como hasta ahora y, cuando esté disponible .NET 6, tendrán a Xamarin integrado dentro de .NET en lugar de ser un producto aparte, con el añadido de poder crear más tipos de aplicaciones que ahora, y seguramente pudiendo ampliar en el futuro el alcance a otros nuevos tipos de aplicaciones que puedan surgir. Una combinación ganadora.

Introducción a Blazor a través de 7 preguntas (y sus respuestas)

$
0
0

Imagen de portada, ornamental

Los desarrolladores que trabajamos con tecnologías web de Microsoft estamos viviendo un terremoto con la llegada de Blazor. No en vano, Blazor se está postulando como una alternativa real a frameworks para el desarrollo de aplicaciones web ya consolidados, como MVC o Razor Pages, e incluso como sustituto de soluciones SPA como Angular o React.

Sin embargo, aún hay muchos desarrolladores que no han oído hablar de este nuevo marco de trabajo o que tienen algunas dudas sobre él. En este post vamos a intentar responder a algunas preguntas que, aunque básicas, creo que serán interesantes para todos los que queráis echar un primer vistazo a este framework que nos puede cambiar la forma de desarrollar aplicaciones web.

1.- ¿Qué es exactamente Blazor?

Si tuviéramos que definirlo en una única frase corta, quizás podría ser algo como:

Blazor es un framework para la construcción de aplicaciones SPA.

Pero obviamente, hay mucho por detrás de esta escueta descripción. Blazor incluye todo lo que necesitamos para construir aplicaciones profesionales en una única página (SPA) y de calidad:

  • Sistema de componentes
  • Implementación de componentes basada en MVVM
  • Sistema de bindings
  • Sistema de routing
  • Sistema de inyección de dependencias
  • Sistema de gestión de errores
  • Sistema de plantillas para componer la UI
  • Herramientas de compilación
  • Herramientas de depuración
  • ... etc.

Por otra parte, se trata de un proyecto liderado por Microsoft (de hecho, forma parte de ASP.NET Core), pero abierto a la comunidad. Es open source (licencia Apache 2.0) y es posible colaborar en el desarrollo mediante pull requests, reportando issues, ideas, etc.

Si vienes de otros frameworks que tienen el mismo objetivo, como Angular, Vue o React, todas estas características te sonarán porque ya las has visto antes. ¿Nada nuevo bajo el sol? 😉

2.- ¿En qué se diferencia Blazor de otros frameworks SPA como Angular, React o Vue?

Visto el punto anterior, no parece que Blazor aporte mucho sobre los numerosos frameworks existentes que persiguen el mismo fin, la construcción de aplicaciones SPA y que, de hecho, se parecen también bastante entre ellos.

Pero Blazor añade un objetivo, tan ambicioso como atractivo, que es lo que lo hace único: los desarrolladores no tendrán que utilizar JavaScript, sino C#, lo cual aporta ventajas interesantes:

  • Reducir la dificultad inicial con la que se encuentran los profesionales .NET a la hora de empezar a construir aplicaciones SPA.
  • Seguir utilizando el fantástico tooling existente en el ecosistema .NET.
  • Aprovechar y reutilizar código existente también en las capas de presentación.
  • Compartir directamente código entre cliente y servidor.
  • O, por supuesto, las derivadas de la utilización de un lenguaje de primer nivel como C#.

3.- Pero si uso C# para programar mi SPA, ¿ya no puedo usar bibliotecas o código JS existente?

No he dicho eso 😉 De hecho, sería una pena desaprovechar la gran cantidad de bibliotecas, componentes y código que la comunidad JavaScript ha estado aportando a la humanidad durante tantos años.

Aunque Blazor está diseñado para que todo lo implementemos con C#, existen "puentes" que permiten utilizar JavaScript en dos direcciones, de forma que podemos aprovechar lo mejor de ambos mundos:

  • Desde código Blazor (C#) podemos invocar funciones JS.
  • Desde código JS podemos invocar métodos Blazor (C#)

Por ejemplo, si nos gusta Bootstrap, resulta sencillo crear componentes Blazor que encapsulen los componentes de este framework, tanto en lo relativo a presentación (marcado HTML) como el código de métodos y eventos JS que presentan ciertos elementos visuales, como los diálogos modales, pestañas, etc. De esta forma, los consumidores de estos componentes Blazor no tendrán que ver JS en absoluto, interactuarán con Bootstrap directamente desde C#, dando lugar a "componentes nativos Blazor".

4.- ¿Existen bibliotecas de componentes nativos para Blazor?

En efecto, la comunidad Blazor lleva ya tiempo avanzando en el desarrollo de componentes de todo tipo, tanto gratuitos como de pago. Aunque este ecosistema obviamente no es comparable en tamaño al de JavaScript, sí que hay ya un buen número de bibliotecas que prácticamente comprenden las necesidades más habituales.

Es fácil encontrar componentes creados por desarrolladores independientes, fácilmente localizables a través de Google. Por ejemplo, es inmediato descubrir que existen ya varias alternativas para utilizar Bootstrap de forma nativa en Blazor, que ocurre lo mismo con los clásicos grids o rejillas de datos o componentes para lanzar ventanas modales entre muchos otros.

También existen colecciones y suites de componentes, como la gratuita (y extensa) Blazored (+10 componentes), MatBlazor (+40 componentes material design), o la biblioteca de Radzen(+40 componentes), también de uso gratuito.

Si tenemos acceso a soluciones profesionales, también los grandes proveedores de componentes hace tiempo que comenzaron a ofrecer soluciones para Blazor, como por ejemplo:

Componentes DevExpress para Blazor

5.- Espera, C# es un lenguaje de servidor, ¿dónde se ejecuta una aplicación Blazor?

Una de las piezas clave de los frameworks SPA es su capacidad para interactuar con el DOM, que son los elementos que componen las páginas que vemos en nuestro navegador. A priori, esto podría limitar las opciones de Blazor, puesto que para que pudiéramos interactuar con el DOM desde C# haría falta:

  • o bien acercar el DOM al servidor, que es donde tradicionalmente "vive" C#,
  • o buscar una fórmula para que C# vaya al cliente.

Blazor soporta ambas opciones y les da un nombre: Blazor Server y Blazor WebAssembly, respectivamente.

Blazor hosting models

En el primer caso, la aplicación correrá en el lado servidor, sobre ASP.NET Core. El servidor manipulará una representación del DOM en memoria, que mantendrá sincronizada con el browser gracias a una conexión SignalR.

En el segundo, la aplicación Blazor se ejecuta directamente en el navegador sobre un runtime de .NET basado en Mono, que se ejecuta sobre WebAssembly. No necesitas servidor y puedes desplegarla incluso desde almacenamiento (como GitHub pages o Azure Blob Storage).

La primera release oficial de Blazor Server se lanzó junto a .NET Core 3 (septiembre de 2019). La primera versión de Blazor WebAssembly fue liberada algunos meses más tarde, en mayo de 2020.

Lo más interesante es que ambas opciones utilizan el mismo modelo de programación. Es decir, la forma de programar las aplicaciones no cambia, independientemente de si vamos a alojarlas en el cliente o en el servidor. Por tanto, al menos en teoría, podríamos ir cambiando entre uno y otro modelo de forma (casi) transparente... aunque más adelante puntualizaremos este tema 😉

6. Hey, y esto de llevar C# al navegador, ¿no lo he oído antes? 😒

Pues si llevas algunos años en este mundillo, probablemente...

Aunque no tiene que ver directamente con C#, recuerdo que uno de los primeros intentos de Microsoft de llevar uno de sus lenguajes al browser fue con VBScript, allá por el año 1996 (¡sí, Internet existía por entonces!). Para los que no lo hayáis visto, la idea consistía básicamente en introducir código VB en etiquetas <script> de la siguiente forma:

<script language="vbscript">
    MsgBox "Hola, qué tal"</script>

Esta tecnología, nacida en plena guerra de los navegadores sólo era soportada por Internet Explorer, y sobra decir que perdió la batalla contra JavaScript. Aun así, VBScript ha estado activo y soportado en IE hasta 2019.

Seguro que algunos también recordaréis Silverlight 😰 Fue lanzado en 2007, en pleno apogeo del concepto RIA (rich Internet Applications) como la panacea y el futuro de la web, empujados fuertemente por tecnologías como Flash, Flex, JavaFX y otras opciones similares.

En todos los casos, la idea era "incrustar" en el browser aplicaciones pesadas que corrían sobre un runtime propietario. A día de hoy, podríamos considerar todas estas opciones como intentos frustrados de romper la web tal y como la conocemos en estos momentos. Afortunadamente, la cordura devolvió las aguas a los cauces originales, y los estándares triunfaron. Silverlight fue discontinuado en 2015.

¿Pero, por qué cuento todo esto? Pues para que ahora veáis claro que el caso de Blazor es totalmente diferente. Las dos tecnologías de las que hemos hablado eran completamente cerradas, propietarias y alejadas de estándares, mientras que Blazor WebAssembly se basa en estándares aceptados por la industria y la comunidad, tales como C#, .NET (sí, son estándares del ECMA, igual que JavaScript) y WebAssembly. Esto, junto con el hecho de que Blazor sea un proyecto open source desde sus orígenes, hacen que no exista una dependencia respecto a un proveedor, como ocurría con VBScript o Silverlight.

También ASP.NET Web Forms, tecnología nacida en 2002 junto con .NET, de alguna forma, ofrecía la ilusión de un C# corriendo en el navegador, gracias a la facilidad con la que podíamos implementar en el servidor el handler del evento click de un botón que se encontraba en el browser y las abstracciones sobre el DOM que sustentaban esta tecnología. El problema es que estos mecanismos, además de ser propietarios, nos alejaban de la realidad de la web, algo que poco a poco también fue descartado por la comunidad de desarrolladores.

A nivel funcional, Blazor Server podría parecerse conceptualmente a Web Forms, pues el código C# se encuentra en el servidor, que es donde realmente se procesa la lógica de presentación, para luego enviar de vuelta las modificaciones a realizar en el DOM. De hecho, Blazor Server es la tecnología que propone Microsoft como sustituta de Web Forms, dado que el estilo de desarrollo puede ser conceptualmente muy parecido.

Blazor Server, además de ser código abierto y estar montado sobre estándares, propone un modelo de desarrollo muy respetuoso son la web: maquetamos las páginas con HTML puro, y no se introduce en las páginas código que no queramos (algo que en Web Forms era muy frecuente), ni Viewstates ni nada parecido. También, el hecho de que Blazor sea un framework SPA aporta un extra de calidad bastante importante en la interfaz de usuario.

7.- ¿Qué es esto de WebAssembly?

Vale, pero, ¿qué clase de invento es este de WebAssembly? ¿No será algo parecido a los applets de Java o el antiguo y peligroso Flash? 😱

No. Nada que ver. WebAssembly o Wasm es una especificación estándar del W3C desde diciembre de 2019, y define un formato portátil de código binario para programas ejecutables directamente en el navegador. ¿Y esto qué significa? Pues se trata de una serie de instrucciones de bajo nivel parecidas al lenguaje ensamblador (de ahí su nombre), que permiten que el navegador ejecute aplicaciones binarias del mismo modo que las ejecuta un sistema operativo.

Si te suena qué es el ByteCode de Java, el CIL/MSIL de .NET o el p-code de muchos otros como el de Visual Basic clásico, esto es más o menos lo mismo pero en el entorno del navegador.

Pero esto no quiere decir que te tengas que complicar la vida. Lo que significa es que si tu lenguaje favorito, en este caso C#, tiene un compilador que genere módulos de Wasm, podrás utilizarlo directamente en el navegador con un rendimiento mucho mayor que si usases JavaScript, y sin sus problemas.

De hecho WebAssembly, tras JavaScript, es el segundo lenguaje de programación nativo que entienden y soportan los navegadores sin necesidad de extensiones, plugins ni nada. Todos los navegadores modernos (Google Chrome, Mozilla Firefox, Microsoft Edge, Safari...) lo llevan incluido de serie y no es necesario instalar nada para poder sacarle partido.

Además, permite hacer cosas imposibles o limitadas en JavaScript, como hilos o SIMD (Single Instruction, Multiple Data, que permite ejecutar una instrucción única en varios datos a la vez), lo cual dota de una potencia inédita a los navegadores para llevar a cabo tareas pesadas como edición de vídeo en paralelo, juegos, realidad virtual y otras cosas de bajo nivel o muy demandantes.

Para que te hagas una idea, un caso famoso de implementación ha sido el de la herramienta de diseño actualmente más de moda, Figma, que fueron de los primeros en migrar una aplicación grande a WebAssembly y lograron mejorar el rendimiento en un 300% en una aplicación que ya era mucho más rápida que las creadas con JavaScript. En Figma ya programaban la aplicación en C++ y la compilaban a un precursor de Wasm llamado asm.js, que era mucho más rápido que JavaScript. Pero con Wasm la ganancia todavía es mucho mayor y nos da una idea de la potencia del nuevo lenguaje.

Blazor compila C# a ensamblados, como siempre, y éstos se ejecutan en el navegador sobre el runtime .NET que está previamente compilado para Wasm.

Cerrando preguntas (por ahora)

Con estas 7 preguntas te puedes hacer una composición de lugar de qué es Blazor y cómo se relaciona con la plataforma .NET, con los navegadores y con los servidores, además de tener una idea de la potencia que te puede otorgar para crear aplicaciones Web.

La semana que viene volveré al ataque con otras 7 preguntas y sus respuestas para aprender un poco mejor qué es Blazor desde un punto de vista práctico, y cómo se utiliza.

Mientras tanto, ¿qué te parece Blazor? ¿Lo conocías ya? ¿Has podido probarlo? Déjanos tus impresiones en los comentarios de debajo.

¡Hasta pronto!

Qué es Git, ventajas e inconvenientes y por qué deberías aprenderlo (bien)

$
0
0

Si te dedicas a la programación hoy en día, debes dominar multitud de lenguajes y plataformas, herramientas, conceptos y patrones. Pero si vamos a lo esencial ¿qué herramientas dirías tú que son realmente indispensables? Si te dijeran que sólo te puedes quedar con 3 herramientas de todo lo que utilizas para programar ¿cuáles serían?

Cualquier programador profesional te diría seguramente estas tres:

  1. Un editor de código potente que te ayude a escribir código rápido, productivo y con menos errores.
  2. Un buen navegador Web para poder consultar documentación y solucionar errores con la comunidad.
  3. Un gestor de código fuente.

Sí, hay muchas otras, sin duda, pero estas 3 son esenciales. Sin ellas el trabajo sería casi imposible.

¿Qué es un gestor de código fuente?

En esencia, un gestor de código fuente, también llamado gestor de versiones, es una herramienta que se encarga de registrar todos los cambios por los que van pasando nuestros archivos (de código o de otro tipo), de modo que podamos consultar estos cambios, volver hacia atrás, ver quién hizo qué y cuándo, etc. En realidad van mucho más allá todavía, ya que permiten también colaborar con otras personas, trabajar en paralelo en diferentes características sin molestarse (a otros o a nosotros mismos), resolver conflictos, tener copias de seguridad de los proyectos sin riesgo de perderlos, y muchas otras cosas. Además, muchas otras herramientas se apoyan en el control de código para darnos más capacidades: flujos de trabajo en equipo, revisión del código, testeo automático, comentarios sobre el código, integración y despliegue continuos de sistemas...

El principal producto de tu trabajo es el código. Y como tal debes protegerlo. El código no son solo instrucciones escritas en un archivo de texto: es conocimiento sobre un problema y sus sutilezas, que además va evolucionando y creciendo con el tiempo. Es indispensable proteger el fruto de nuestro trabajo contra desastres, pérdidas, errores humanos y la inevitable entropía que va a ocurrir con el paso del tiempo.

Por ello, aunque trabajes en solitario, usar un sistema de control de versiones es indispensable (y no, no sirve sacar copias a mano ni usar un disco Cloud estilo OneDrive o Dropbox: olvídate). Y una empresa de desarrollo en la que no se utilice control de código, no merece llamarse así y está condenada a desaparecer. Así de duro, así de real.

La cuestión no es si debes utilizar un sistema de control de código o no, sino cuál de los existentes debes elegir.

Git: el gestor de código fuente más utilizado del mundo

Si te preguntan quién es Linus Torvalds, seguro que no tardas nada en responder: "El creador de Linux". Lo que no todo el mundo sabe es que, además, es el creador de un producto incluso más importante para nuestra industria: Git.

Git es el gestor de código fuente que domina el panorama desde hace años y que la práctica mayoría de desarrolladores y empresas utilizan hoy en día.

Git ofrece varias ventajas frente a otros sistemas tradicionales:

  • Sistema distribuido, sin un punto central de fallo, que permite el trabajo incluso sin conexión.
  • Superrápido y ligero, optimizado para hacer operaciones de control muy rápidas.
  • Crear ramas y mezclarlas es rápido y poco propenso a problemas, al contrario que en otros sistemas tradicionales.
  • La integridad de la información está asegurada gracias a su modelo de almacenamiento, que permite predecir este tipo de problemas. En sistemas tradicionales este era un problema grave.
  • Permite flujos de trabajo muy flexibles.
  • El concepto de área de preparación o staging permite versionar los cambios como nos convenga, no todo o nada.
  • ¡Es gratis! y de código abierto.

Pero, como todo, también tiene sus problemas:

  • Es más complejo que los sistemas centralizados tradicionales porque entran en juego más repositorios, más operaciones y más posibilidades para trabajar en equipo, que hay que decidir.
  • La curva de aprendizaje es empinada. Lo básico lo aprendes enseguida, pero la realidad te demuestra que no es suficiente "tocar de oído" con él. La documentación es tan compleja que muchas veces no resulta de ayuda.
  • Los comandos y algunos conceptos que usa pueden llegar a ser confusos, al igual que algunos mensajes que muestra.
  • Por defecto, se lleva mal con archivos binarios muy grandes, como vídeos o documentos gráficos muy pesados. Por suerte existen soluciones para ello (Git LFS).

Y es que, como decía el tío Ben: "Un gran poder conlleva una gran responsabilidad". Personalmente me gustan mucho estos dos diagramas del clásico artículo de Steve Bennet"10 things I hate about Git" y que le tomo prestados.

El primero muestra el flujo típico de trabajo con Subversion, el sistema gestor de código clásico por antonomasia:

Como vemos, las operaciones son muy pocas y cualquiera lo puede dominar muy rápido.

El siguiente muestra el típico flujo de trabajo con Git, que involucra además de nuestro repositorio local, su área de trabajo, el área de preparación y dos remotos:

Mucho más complejo, pero mucho más poderoso 😱

Aprender Git bien

Personalmente pienso que el mayor problema de Git, y su mayor fuente de frustración, proviene de que en muchas ocasiones se utiliza sin tener los conocimientos apropiados. Los cuatro comandos básicos son muy sencillos y enseguida puedes estar utilizándolo en un proyecto. Pero luego, la realidad es mucho más compleja. Si trabajas con más gente, no hay unas pautas claras de cómo gestionar el código, y el proyecto empieza a crecer, es cuando empiezan los problemas.

Es por ello que en campusMVP hemos lanzado un curso para aprender Git bien. La formación parte de cero y se preocupa no sólo de enseñarte cuatro recetas para salir del paso, sino que te explica bien los conceptos involucrados para que comprendas lo que estás haciendo, te enseña técnicas más avanzadas, flujos de trabajo, gestión de remotos... e incluso aprenderás a lidiar con archivos binarios de gran tamaño, uno de los puntos débiles de la herramienta. Encima, su autor David García, ha sido capaz de hacerlo muy enfocado y directo al grano para que puedas dominarlo en poco tiempo.

¿Qué más se puede pedir? Pues que el propio David sea el que esté pendiente de tus dudas durante el curso para responderlas y ayudarte en el camino mientras aprendes.

Si tú o tu equipo no usáis un gestor de código fuente, ya estáis tardando en aprender😊

Cómo funciona Blazor: otras 7 preguntas y respuestas para conocerlo mejor

$
0
0

Esquema de funcionamiento a alto nivel de Blazor

En mi anterior artículo presenté Blazor, la nueva tecnología de Microsoft para crear aplicaciones Web basadas en servidor o en navegador usando .NET y C#.

Ese artículo proporcionaba una visión general de Blazor, cómo se relaciona con .NET y con los navegadores, qué es WebAssembly y cuáles son las posibilidades alucinantes que nos facilita esta tecnología.

En el artículo de hoy voy a profundizar un poco más con otras 7 preguntas y sus respuestas de modo que puedas conocer mejor cómo funciona, qué implica en cuanto a conocimientos y aprendizaje y que veas si te puede encajar la tecnología o no. Seguiré numerando las preguntas desde donde lo dejé y así tendrás una visión mejor.

¡Vamos allá!

8.- Hace algún tiempo oí hablar de Razor Components... ¿es lo mismo que Blazor, o se trata de algo distinto?

Bueno, aquí es que hubo un poco de lío al principio 😆

En enero de 2019, con la llegada de la preview 2 de .NET Core 3.0, se anunció la inclusión de Razor Components, que materializaba la capacidad de alojar componentes Blazor en el lado servidor. Es decir, antes de tomar su nombre definitivo, Blazor Server se llamó Razor Components durante algún tiempo.

Meses más tarde, ya en abril del mismo año, decidieron dar marcha atrás y renombraron definitivamente la tecnología como Blazor Server al lanzar la preview 4, porque pensaron que era bastante confuso, dado que Razor Components era una denominación que también era utilizada para referirse a los componentes de Blazor, los building blocks de las aplicaciones Blazor que se implementan en archivos .razor.

Hoy en día, la denominación "Razor Component" ha quedado exclusivamente para esta segunda acepción. Es decir, los componentes Blazor se implementan en archivos de Razor Components (archivos .razor), aunque en realidad creo que acabaremos llamándoles "Componentes Blazor" a secas la mayoría de las veces.

9.- ¿Y tiene esto algo que ver con Razor Pages? ¿O con Razor?

Un aire de familia sí que tienen, aunque en realidad se trata de cosas distintas.

Aunque coincida en parte el nombre con Razor Components, Razor Pages es una tecnología de servidor pura, construida sobre ASP.NET Core, y lanzada hace ya unos años, al abrigo de ASP.NET Core 2.0, como una actualización y puesta al día de lo que antes habíamos conocido como ASP.NET Web Pages.

El objetivo de este pequeño framework era permitir a los desarrolladores crear aplicaciones web adoptando un enfoque "por página", más simple y directo en muchos escenarios que la alternativa MVC. Es decir, con esta tecnología podemos hacer que una petición sea procesada directamente por una página, al estilo de cómo se hacen en otros frameworks y lenguajes de scripting para la web, en lugar de tener que crear controladores y vistas.

Por otro lado, Razor es la sintaxis que permite mezclar código de marcado (HTML) con C# de una forma bastante clara y productiva, y es precisamente el nexo de unión entre estas tecnologías: Blazor Componentes, Razor Pages e incluso las vistas MVC utilizan la sintaxis Razor, aunque cada una de ellas introduce algunas características propias que es necesario conocer.

10.- ¿Cómo se estructuran las aplicaciones en Blazor?

Estructura basada en componentes

La forma de estructurar una aplicación Blazor se parece mucho a otros frameworks SPA. Muy a vista de pájaro, consiste en un Razor Component raíz, que normalmente aloja el sistema de routing. Éste se encargará de analizar los cambios en las rutas del navegador para mostrar las páginas de la aplicación.

Las páginas, que también se implementan como Razor Components, pueden utilizar dentro, a su vez, otros componentes, que pueden estar compuestos a su vez por otros componentes, y así hasta el infinito. Lo único que hace diferente a una página de un componente es que ésta incluye la directiva @page para indicar al sistema de routing cómo puede ser localizada (algo bastante similar a como lo hacemos al desarrollar con Razor Pages en MVC).

Por tanto, para crear la interfaz de una aplicación Blazor, lo único que tendremos que hacer es implementar Componentes Razor. Aparte, claro está, tendremos que implementar otro tipo de clases y estructuras habituales: servicios de aplicación, acceso a datos, lógica de negocio, etc., que pueden ser utilizadas desde los archivos .razor gracias a la inyección de dependencias.

11.- ¿Qué pinta tiene por dentro un Componente Razor?

Pues si has utilizado alguna vez Razor Pages o MVC, la pinta es muy parecida a lo que ya conoces para el desarrollo de páginas o vistas.

Por ejemplo, el siguiente código podría ser un Componente Razor que actúa como página y que en su interior utiliza otro componente:

@* File: Pages/Index.razor *@
@page "/"<h1>Hello, world!</h1>
Welcome to your new app.<hr /><Multiplication Number="5"></Multiplication>

El componente Razor en ejecución

Fíjate en que con la directiva @page indicamos al sistema de routing mediante qué ruta debe ser accesible el contenido. Ya en su interior, podemos observar que utilizamos un Componente Razor llamado <Multiplication>, que mostrará tablas de multiplicar. Puedes ver el funcionamiento en la animación anterior.

A continuación, veamos el código del componente <Multiplication>, definido en el archivo Multiplication.razor de la siguiente forma:

@* File: Pages/Multiplication.razor *@<h3>Multiplication</h3><div><label>Select a number:</label><select @bind="Number">
        @for (var i = 1; i < 10; i++)
        {<option>@i</option>
        }</select></div><div><h5>Multiplication table of @Number,
        using font-size @(FontSize)pt:</h5><ul style="font-size: @(FontSize)pt">
        @for (var i = 1; i <= 10; i++)
        {<li>@Number * @i = @(Number * i)</li>
        }</ul><button @onclick="ZoomIn">Zoom in</button><button @onclick="ZoomOut">Zoom out</button></div>

@code {
    [Parameter]
    public int Number { get; set; }
    int FontSize { get; set; } = 10;

    void ZoomIn() => FontSize++;
    void ZoomOut() => FontSize--;
}

En este componente destacan varios aspectos:

  • El uso de sintaxis Razor, esa mezcla de HTML y C# que encontramos a lo largo del contenido: expresiones de salida tipo @var o @(var) para mostrar valores de variables, o incluso los clásicos bucles @for para mostrar listas o colecciones.
  • La existencia del bloque @code para definir miembros e implementar comportamientos en C#, que podría asimilarse a un típico view-model en otros frameworks MVVM.
  • El uso de la directiva @bind para bindear el valor actual del desplegable a la propiedad Number, que además actúa como parámetro para que las páginas o componentes que lo utilicen puedan establecerlo (nosotros lo habíamos hecho con el valor "1" desde la página).
  • Podemos ver también el uso de @onclick para asociar eventos "click" del DOM a handlers escritos en C# en la sección @code.

12.- ¿Los componentes que desarrolle para Blazor Server podrán funcionar luego en Blazor WebAssembly y viceversa?

El modelo de programación es el mismo en ambos tipos de hosting. Es decir, en principio, un Razor Component escrito sobre Blazor Server podría funcionar sin cambiar ni una sola línea de código en Blazor WebAssembly y viceversa.

Vuelve un momento al código que te mostraba en el punto anterior y analízalo desde el punto de vista de ambos modelos de hosting. Verás que no hay nada que lo ate especialmente al servidor o al cliente:

  • Si lo ejecutamos en Blazor Server, el evento "change" del desplegable será enviado al servidor por el canal SignalR. En éste, se actualizará el valor de la propiedad Number y se detectarán los cambios en el DOM que este cambio produce, enviándose de vuelta al cliente las porciones de HTML modificadas. Lo mismo ocurre con los eventos "click": serán enviados al servidor, procesados allí, y los cambios en el DOM enviados de vuelta al cliente.
  • Si lo ejecutamos en Blazor WebAssembly, el evento "change" será procesado directamente desde el código .NET presente en el navegador.

En efecto, es el mismo código ejecutándose en dos escenarios totalmente distintos.

Sin embargo, la realidad es algo más compleja y hace que debamos tener en cuenta dónde se ejecutan los componentes para implementarlos de forma correcta.

Por ejemplo, imagina que en el handler de un evento click necesitamos acceder a recursos del servidor, como su sistema de archivos, una base de datos o cualquier otra cosa. Esto podremos hacerlo sin problema al hostear el componente en Blazor Server, puesto que esta ejecución se llevará a cabo precisamente en el servidor. Sin embargo, si el mismo código lo llevamos a Blazor WebAssembly, fallará estrepitosamente porque el código se ejecutará en el navegador.

¿Y cómo solucionamos estos escenarios? Pues es el momento en el que la inyección de dependencias llega a nuestro rescate :wink:

La forma correcta de implementar un requisito de ese tipo sería creando una abstracción que puedas implementar de forma personalizada en cada tipo de aplicación. Por ejemplo, si necesitamos manipular un archivo del servidor, podríamos crear una interfaz IServerFileAccessor y luego implementarla de forma diferente en cada host:

  • En Blazor Server, la implementación simplemente accedería al archivo local para manipular el archivo de forma directa.
  • En Blazor WebAssembly, la implementación podría usar una llamada HTTP para manipular el archivo a través de un endpoint HTTP publicado en el servidor.

De esta forma, el código de nuestro componente seguiría siendo el mismo y solo cambiaría la implementación de la interfaz, ya en función del hosting utilizado:

@inject IServerFileAccesor FileAccessor<button @onclick="DoSomethingClick">
...
@code
{
    async Task DoSomethingClick()
    {                                         // Este código es el mismo para
        await FileAccesor.DoSomethingAsync(); // Razor Server y Razor WebAssembly
    }
}

Si conoces Xamarin Forms esto podría resultarte familiar, porque es lo mismo que estás haciendo cada vez que implementas servicios o custom renderers nativos que luego utilizas desde tu proyecto compartido.

13.- ¿Necesito herramientas especiales para programar con Blazor?

En absoluto. Puedes programar aplicaciones Blazor desde Windows, Mac o Linux, porque se trata de una tecnología basada en .NET Core y, por tanto, cross-platform. También puedes utilizar tu editor o entorno de desarrollo favorito (Visual Studio o cualquier otro).

Desde Visual Studio, como es habitual, tendremos bastantes ayudas a la hora de crear proyectos de este tipo, porque ya viene equipado de serie con plantillas, como también disfrutaremos de intellisense, depuración integrada y todas las ayudas a las que nos tiene acostumbrados.

Pero si preferimos otras opciones, somos libres de hacerlo :smile: Por ejemplo, podríamos crear un proyecto Blazor desde línea de comandos sin problema y, a partir de ahí, abrir el proyecto con Visual Studio Code sin demasiado esfuerzo:

C:\Test>dotnet new | find "blazor"
Blazor Server App            blazorserver          [C#]           Web/Blazor
Blazor WebAssembly App       blazorwasm            [C#]           Web/Blazor/WebAssembly

C:\Test>dotnet new blazorserver
The template "Blazor Server App" was created successfully.
This template contains technologies from parties other than Microsoft, see https://aka.ms/aspnetcore/3.1-third-party-notices for details.

Processing post-creation actions...
Running 'dotnet restore' on C:\Test\Test.csproj...
  Restauración realizada en 85,39 ms para C:\Test\Test.csproj.

Restore succeeded.

C:\Test>code .

C:\Test>_

Proyecto Blazor con Visual Studio Code

14.- Como desarrollador .NET, ¿me valen mis conocimientos actuales?

Por supuesto, y la idea de fondo de Blazor es precisamente esa, que desarrolladores con experiencia en C# y .NET puedan dar el salto a la Web sin demasiados problemas porque aprovechan al máximo sus conocimientos:

  • Aprovecharás a tope tus conocimientos de C#, pues lo utilizarás para implementar la lógica de presentación en páginas y componentes de tus aplicaciones.
  • Si ya conoces ASP.NET Core, te sentirás cómodo con Blazor Server, pues este tipo de hosting es, al fin y al cabo, una aplicación ASP.NET Core a la que se insertan algunos servicios y endpoints especiales. Toda la infraestructura (inyección de dependencias, logging, settings, etc.) es la misma. Con Blazor WebAssembly podrás aprovechar todos esos conocimientos para implementar backends de tus componentes de presentación.
  • Si has trabajado con Razor en ASP.NET MVC, ASP.NET Web Pages, ASP.NET Core MVC o ASP.NET Razor Pages, podrás aprovechar estos conocimientos y simplemente aprender las diferencias existentes entre la anterior sintaxis y la usada en los Razor Components.
  • Si conoces Web Forms, el modelo de programación de Blazor Server te resultará familiar y podrás adaptarte con relativa facilidad. Pero ojo, si lo único que has hecho en el mundo Web es de la mano de Web Forms, quizás te cueste más acostumbrarte a trabajar cerca de la web real (HTML, CSS) que a Blazor.
  • Si llevas tiempo con .NET, podrás aprovechar conocimientos y reutilizar componentes para implementar las aplicaciones Blazor usando bibliotecas y código que ya conoces o puedes reutilizar. Incluso en el caso de Blazor WebAssembly, lo que llevamos al browser son ensamblados .NET Standard, por lo que probablemente puedas aprovechar cosas.
  • Si has trabajado con otros frameworks SPA no tendrás demasiado problema para "mapear" tus conocimientos a Blazor porque los conceptos son muy similares, acelerando así tu proceso de aprendizaje.
  • Conocer JavaScript te vendrá de perlas para implementar escenarios interop y reutilizar componentes o bibliotecas existentes.
  • Si has trabajado con Visual Studio podrás utilizar tus habilidades y herramientas habituales para crear, desarrollar y depurar aplicaciones Blazor.

En resumen, si eres un desarrollador .NET con cierta experiencia en el mundo Web moderno, el salto a Blazor te resultará bastante cómodo. Obviamente habrá que aprender las peculiaridades de este framework, pero el esfuerzo requerido es relativamente moderado, y diría que incluso mucho más leve que el requerido si pretendemos dar un salto desde cero a otros como Angular.

Cómo arruinar tu carrera profesional en 8 sencillos pasos

$
0
0

Nota: este artículo es una traducción de "How to Ruin Your Career in 8 Easy Steps" escrito por Rina Artstain, ingeniera de Software en Dropbox, y traducido y publicado con su permiso expreso. El énfasis es nuestro. ¡Gracias Rina!

Quizá pienses que el concepto "carrera profesional" es anticuado, de los años 80, y que hoy en día podría ser completamente obviado. Yo antes también lo hacía... ¡Pensaba que eso de "hacer carrera" era para los trajeados profesionales de la banca, no para mí! Era joven y guay y quería trabajar para ganarme la vida, por supuesto, pero no me importaba los más mínimo subir de escalafón profesional. Pues bien, he cambiado de opinión.

No digo que la trayectoria profesional deba ser el foco de todo el mundo en todo momento. Si te conformas con tener un trabajo, se trata de una elección totalmente legítima. Pero si quieres tener una trayectoria profesional, en el sentido de progresar a lo largo de tu vida laboral, ampliando tus horizontes y oportunidades de impacto personal y en el trabajo, aquí tienes una lista de 8 cosas que debes evitar hacer.

1.- No planifiques con antelación

Es fácil simplemente dejarse llevar por las oportunidades que se presentan, ir a unas cuantas entrevistas, aceptar la mejor oferta que se te presente, quedarte unos años, y vuelta a empezar. Eso probablemente servirá para conseguir pequeños aumentos y algo de variedad en tu trabajo diario.

Imagen ornamental que muestra una serie de cajas enlazadas por líneas pintadas en unas paredes, por Hanna Morris en Unsplash, CC0

Pero si quieres hacer carrera debes tener un plan a corto, medio y largo plazo (2, 5, 10 años por adelantado) y tener la intención de alcanzar metas que te hagan avanzar en la dirección adecuada. Imagina lo que quieres ser "cuando seas mayor", date espacio para experimentar y probar diferentes cosas para lograr esa visión a largo plazo, pero siempre mantén los ojos en la pelota. Recuerda que siempre puedes cambiar tus planes de acuerdo a tus deseos y necesidades cambiantes, pero si no tienes ningún plan, es poco probable que seas efectivo a la hora de conseguir algo.

A veces "salir y dar una vuelta" es perfectamente válido, si quieres concentrarte en otras cosas además del trabajo por un tiempo, pero es importante hacerlo de manera consciente. De lo contrario, te encontrarás mirando hacia atrás y viendo el tiempo perdido en lugar del tiempo bien empleado en otras cosas.

Sea cual sea el camino que elijas, pon tu plan de 2, 5, 10 años por escrito para que puedas reflexionar sobre él cada año más o menos, y ajustar el plan o los objetivos de acuerdo con los avances que hayas hecho o lo que esté pasando en tu vida.

Una vez, no hace mucho, me fijé un objetivo a 7 años y terminé lográndolo en menos de 2 años. Fue divertido, emocionante y alentador y me permitió poner la mira en el siguiente objetivo.

2.- Haz tu trabajo y espera por el reconocimiento

Esta es una idea equivocada muy habitual... asumimos que si hacemos bien nuestro trabajo seremos reconocidos y ascendidos por ello. Tiene sentido a primera vista, ¿verdad? Nuestro trabajo hablará por nosotros, no deberíamos tener que divulgarlo, se hará evidente por si mismo, ¿no?

Imagen ornamental que muestra unas escaleras de madera apoyadas en una pared vieja, por Biao Xie en Unsplash - CC0

Por desgracia, las cosas no funcionan así. Dando por sentado que (esperemos) el nivel de competencia es un requisito indispensable para ascender e, incluso, en el mejor de los sistemas de evaluación del rendimiento, todavía tendrás que ayudar a tu superior a apreciar lo que has logrado. De hecho, a veces nuestro mejor trabajo es algo totalmente invisible: si el proyecto en el que has trabajado ha tenido éxito sin grandes interrupciones o errores, es posible que sea menos visible y por lo tanto menos apreciado que los proyectos con grandes contratiempos y fracasos que crean mucho "ruido".

Trata de sentirte cómodo contándole a las demás personas tus logros. Incluso compartir fracasos y cómo los has superado es un poderoso mensaje. Si te resulta difícil hacerlo, intenta construir tus mensajes de manera que beneficien a tus lectores/oyentes - enmáscaralo como una "lección aprendida" o como una historia de éxito colectivo (sin dar todo el mérito al equipo). Así no parecerá que estás fanfarroneando y obtendrás la visibilidad que necesitas.

Si no consigues hacerlo, inténtalo de nuevo la próxima vez. Pero si simplemente no eres capaz de lograr hacer esto, otra estrategia eficaz es la de formar un grupo de aliados que se elogien mutuamente en público. Puedes pensar que hacer esto es algo muy evidente, pero no lo es, la gente no lo notará. Y si se dan cuenta... a quién le importa: tu mensaje ya habrá salido a la luz de todos modos.

Recomiendo llevar un registro semanal de las cosas que has hecho en el trabajo, buenas o malas. Lo hago desde hace tiempo y me ayuda a llevar un registro semiobjetivo de lo que he conseguido y todos los puntos y datos que necesito para mi autoevaluación y para las conversaciones 1 a 1 con mi jefe. También lo uso para los retros de sprint, resúmenes de trabajo con otros miembros del equipo para sus revisiones de 360º, etc. No tendría forma de recordar todo lo que necesito para dar feedback de calidad para mí o para otros sin mi registro semanal.

Además del esfuerzo para lograr que tu trabajo sea visible, recuerda pedir todo lo que ansías. Incluso el mejor de los jefes puede no ser consciente de que todo lo que te interesa. A otros les puede resultar más fácil reaccionar sólo cuando se les pide algo específicamente o se les pone un ultimátum. No dejes que se salgan con la suya. Dile a quien esté dispuesto a escuchar que quieres un ascenso o un aumento. Dile a tu jefe que sientes que necesitas un reconocimiento. Si no consigues lo que has pedido, al menos sabrás que lo has hecho lo mejor que has podido (y quizás sea el momento de pasar a otra empresa que reconozca tu valía).

3.- Perspectiva de todo o nada

A veces mirarás a tus colegas y pensarás que son mucho más inteligentes que tú o que algo de sus circunstancias les permite trabajar con más ganas, más horas o con menos interrupciones (por ejemplo, si tienes hijos y ellos no). No hay forma de que puedas ganar contra ellos, siempre tendrán más éxito que tú, así que, ¿qué sentido tiene intentarlo?

Imagen ornamental que muestra unos islotes rodeados por el mar, por Emilie Farris en Unsplash, CC0

Caí en este tipo de cosas después de tener hijos. Quería pasar tiempo con ellos y me di cuenta de que no podía "vencer" a los colegas solteros o sin hijos, así que debía "rendirme". No lo pensé de esa manera exactamente, pero, en resumidas cuentas, esto es lo que hice en la práctica.

No voy a suavizar esto: sí, es probable que no puedas "vencerles". Siempre habrá gente más inteligente que tú y que tenga más tiempo, menos responsabilidades, más ambición... Pero una carrera profesional no es un juego de suma cero. Sólo porque otras personas avancen más rápido que tú en ciertos momentos de tu/su vida, no significa que tú no puedas avanzar en absoluto. No te rindas, ajusta tus expectativas a lo que puedes lograr y no te compares con los demás (demasiado), eso probablemente te desalentará. A largo plazo, no importará si obtienes un ascenso un año o dos años más tarde. Sin embargo, puede marcar una gran diferencia si te esperas 10 años porque crees que el éxito de otra persona te impide conseguir algo por ti mismo.

4.- Venderse poco y mal

Esto puede sonar algo parecido al segundo punto de esta lista, pero tiene una sutil diferencia. El punto 2 es hacer un buen trabajo, darte cuenta de que es un buen trabajo, pero no decírselo a nadie. Este punto 4 es acerca de tu discurso. Puede que no te des cuenta de que tu trabajo es bueno o que no sepas cómo expresarlo, pero el efecto es el mismo: la gente se llevará de ti una impresión equivocada.

Imagen ornamental que muestra un libro abierto con una ola marina en blanco y negro, por Andrew Neel en Unsplash, CC0

Aquí va una historia:

Trabajé prácticamente en solitario durante 6 años, con algunos desarrolladores independientes y un arquitecto ayudándome. Tenía que hacerlo todo yo misma, incluyendo trabajo de gestión del producto. Después de unos años la compañía se vendió a una empresa más grande y decidí que era hora de cambiar.

Aquí va otra historia:

Durante 6 años lideré el desarrollo de un producto de éxito, contando con la ayuda de desarrolladores freelance y consultando con un arquitecto de software con el que trabajé estrechamente. Trabajé directamente con el equipo directivo, proporcioné valiosos aportes al producto y coordiné el trabajo de diseño. En parte debido a la excelente calidad técnica del producto, la compañía fue vendida a una gigantesca corporación europea. Sentí que había empleado muy bien mi tiempo allí, pero era hora de afrontar nuevos retos.

Ambas son (mi) historia real. Pero la primera subestima lo que realmente hice. Es la historia de un lobo solitario pasivo (que yo no soy). La segunda es una versión mejorada, destacando mis fortalezas e impactos.

En mi experiencia como mentora, he encontrado a muchos que toman el primer enfoque al construir su historia (yo sé que he cometido este error). Simplemente no entienden cómo suena.

No subestimes el impacto de construir un relato potente. Si estás buscando trabajo, haz que la gente escuche tu discurso de 3 a 5 minutos en el apartado "Háblame de ti" y escucha sus comentarios sobre la forma en que te perciben. Lo mismo para la sección de "quién soy y qué estoy buscando" de tu currículum o carta de presentación.

De hecho, es mejor si haces lo mismo para toda la parte "blanda" del proceso de entrevista. La misma serie de eventos pueden ser contados de manera que te retraten como un resentido o un proactivo, como un fracasado o como una persona que aprende de sus errores. Debes tomarte el tiempo necesario para prepararte para las entrevistas de la parte "blanda" escribiendo tus historias y haciéndolas llegar a los oídos del resto, reescribiéndolas para transmitir la mejor versión de ti mismo. Recuerda: no se trata de distorsionar la verdad, sino de construir una narrativa que te sirva de ayuda.

Si no estás buscando trabajo ahora mismo, vale la pena pensar en tu historia y en cómo encaja en tu imagen y en tus objetivos a largo plazo. Tal vez no te aprecias lo suficiente a ti mismo y eso te está frenando.

5.- Quién necesita una red de contactos

Hacer contactos puede sonar a algo artificial, algo estilo ir a un montón de eventos y entablar una pequeña charla inútil sólo para pasar tarjetas de visita anticuadas que se perderán segundos después de dejar el evento. Si es así como lo haces, lo estás haciendo mal y sí, es bastante inútil.

Imagen ornamental, un montón de hilos creando redes entre ellos, por Omar Flores en Unsplash, CC0

No obstante, la creación de una auténtica red profesional es algo que dista mucho de resultar inútil. Tener una sólida red de contactos puede darte acceso a información, orientación y oportunidades difíciles de conseguir de otras maneras. Ya sea si estás buscando trabajo o consejo, un socio fundador u otro tipo de apoyo, seguramente habrá alguien en tu red que pueda ayudarte, si no directamente, poniéndote en contacto con alguien que sí pueda. A veces, son ellos los que buscan trabajo, consejo, etc. y tú puedes ser el que aporte valor. Incluso, si no hay una recompensa inmediata (como encontrar a la persona adecuada para un puesto vacante), a largo plazo probablemente te beneficiarás de esa relación de una manera u otra. Y si no es así, al menos has hecho algo bueno.

Cuanto más grande sea tu red, cuanta más gente conozcas, mayor será la probabilidad de que encuentres lo que necesitas, cuando lo necesites. En cierto modo, crear una red es como construir suerte para tu vida profesional.

Mientras que la construcción de una red puede ocurrir orgánicamente si vas a la universidad "indicada" y trabajas en los lugares "apropiados", es mejor si pones algún esfuerzo consciente en ello, particularmente si eres menos privilegiado en ese sentido. Ir a, o incluso organizar, meetups y eventos de networking es una manera de hacerlo, y construir una presencia online es otra. Aprovecha al máximo las conexiones que haces mientras trabajas. Intenta hacer una conexión relevante, no te limites a charlar superficialmente, eso no dejará una impresión duradera. Acércate a la gente que te parezca interesante. Empieza por pensar en lo que puedes hacer por ellos, no en lo que ellos pueden hacer por ti. El resto vendrá solo con el tiempo.

6.- Culpar a los demás por cosas que puedes controlar

No podías haberlo sabido. No recibiste los requisitos pertinentes. El otro grupo no cumplió con lo que tú necesitabas. No conseguiste ese ascenso porque te dieron tareas de poco impacto. No obtuviste un aumento porque no le gustas a tu jefe. No es culpa tuya.

Imagen ornamental, unas flechas blancas de papel clavadas con chinchetas en una madera, por Jungwoo Hong en Unsplash, CC0

En cualquier fracaso siempre hay algo que podría haberse hecho para impedirlo. Tal vez no se podría haber evitado el fracaso por completo, pero probablemente hay cosas que se podrían haber hecho de manera diferente. Y en retrospectiva, hay cosas que aprender de ello para evitar el próximo fracaso. Culpar a los demás es algo que puede hacerte sentir bien, pero erosiona la confianza y no te ayudará a crecer.

En vez de eso, acepta el fracaso. En lugar de esquivar las culpas, piensa en las formas en que podrías haber hecho algo diferente para lograr un resultado diferente: poner más claras las expectativas, comunicarte mejor, tomar la iniciativa, etc. No sólo crecerás personalmente, sino que también ganarás credibilidad. A la mayoría de la gente le encanta oír cómo los demás admiten el fracaso y comparten las lecciones aprendidas, es mucho más atractivo e interesante oír a alguien asumir la responsabilidad de sus acciones que despotricar sobre cómo todos los demás son simplemente unos idiotas.

Recuerdo que una vez hice una gran refactorización de una enorme cantidad de código duplicado y se me pasó un "if" que causó una degradación de rendimiento medio-seria. En una reunión mucho más tarde, cuando estábamos discutiendo el rendimiento en general, ese ejemplo salió a colación sin mencionar mi nombre, probablemente para no avergonzarme. No tuve problemas en admitir que había cometido un error y compartí la historia de cómo sucedió y cómo investigué, encontré y solucioné el problema, para beneficio de todos. Estoy seguro de que nadie me hace de menos por haber compartido esa historia.

El final de esta saga es que alguien más estaba haciendo algo de refactorización en esa área y vi la oportunidad de deshacerme de ese "if" por completo. Si no hubiera sabido del riesgo debido a mi error anterior, no habría notado esa oportunidad de mejorar nuestro código base. Con todo, una victoria (y lo siento a cualquiera que hubiera tenido que esperar 200 milisegundos extra cargando aquella página durante un par de días).

7.- Culparte a ti mismo por cosas que no puedes controlar

No podías haberlo sabido. No recibiste los requisitos pertinentes. El otro grupo no cumplió con lo que tú necesitabas. No conseguiste ese ascenso porque te dieron tareas de poco impacto. No obtuviste un aumento porque no le gustas a tu jefe. No es culpa tuya.

Imagen ornamental, un hombre caminando bajo la lluvia con paraguas, por Cristophe Dutour, en Unsplash, CC0

Sé que esta es exactamente la misma historia que la del punto 6, el caso es que a veces de verdad no es culpa tuya. A veces sí estás realmente en un ambiente tóxico que te infravalora e incluso te perjudica. Si eres el tipo de persona que siempre trata de aprender y crecer a partir del fracaso, puedes encontrarte en un ambiente de trabajo que no hace nada por ti e incluso te hace daño. A veces es difícil notar la diferencia. Pero si haces el trabajo y no consigues nada, y si tienes una sensación de malestar en el estómago cada vez que vas a trabajar, quizás no haya nada más que puedas hacer y sea hora de irte.

Existen ambientes tóxicos más que suficientes a los que cualquiera puede ir a trabajar por ahí, pero creo que las mujeres y las otras minorías se enfrentan a este tipo de problemas más a menudo. Cuando hay comportamientos sutiles y difíciles de precisar, es fácil culparse a sí mismo y procurar resolverlos, en lugar de darse cuenta de que es algo sistémico y que en realidad no es culpa tuya. Si no te consideran para un ascenso a pesar de que eres más hábil que un colega masculino, si te pasan por encima en las reuniones, te dicen que no eres lo suficientemente técnica y te ocurren otros incidentes sesgados, es muy posible que no puedas ganar. Es probable que te encuentres con algo de esto dondequiera que vayas, así que intenta hacer lo que puedas para mejorar la situación, pero hagas lo que hagas, ten en cuenta: no es culpa tuya.

8.- Acomódate

Cuando empiezas en un sitio nuevo, tienes ansias por probarte a ti mismo. Es difícil, tienes que aprenderlo todo, conocer a todo el mundo, forjarte una reputación. Después de un tiempo, las cosas se vuelven más fáciles. Entiendes los procesos, sabes quién es quién, ganas conocimiento profesional y estatus. Es agradable sentirse así, la presión desaparece por un tiempo.

Imagen ornamental, un cachorrillo tumbado encima de una manta en cama, por Roberto Nickson en Unsplash, CC0

No tiene nada de malo sentirse algo cómodo, el trabajo no debería ser una lucha constante. Pero si usas "cómodo" para definir tu trabajo, tal vez sea hora de agitar un poco las cosas. No querrías perder todo el camino recorrido quedándote en el mismo lugar (profesionalmente) por mucho tiempo.

La búsqueda constante de "lo siguiente" no es probablemente la mejor estrategia. Es estresante y te verán como inestable o poco fiable. Pero una vez cada uno o dos años es probablemente una buena cadencia para comprobar cuál debería ser tu próximo reto, dentro o fuera de la organización para la que ya trabajas. Es posible que descubras que estás mal pagado o "atascado" profesionalmente, y sería mejor que lo supieras más pronto que tarde, cuando quizás sea más difícil de arreglar.

···

Espero haber puesto mi granito de arena para ayudarte en tu camino hacia una carrera increíble. Vuelve dentro de unos años, puede que tenga unos cuantos errores más que añadir a esta lista... Pero puedes estar seguro de que no dejaré que eso me detenga, y tú tampoco deberías.

Plataforma .NET: Cómo fusionar exe y dlls en un único ejecutable para distribuir

$
0
0

Imagen ornamental por Bilal O. en Unsplash, CC0

La plataforma .NET viene incluida con todas las versiones de Windows desde hace años y, por suerte, puedes crear una aplicación y distribuirla a tus usuarios sin necesidad de distribuir también la plataforma completa, ya que puedes partir de la base de que la tienen disponible, salvo quizá las versiones más recientes. Por ejemplo, si el target de tu aplicación es .NET 4.5 puedes casi garantizar que todos tus usuarios con un ordenador de los últimos 7 u 8 años lo tendrán preinstalado en el sistema. Si es la 4.7 o 4.8 y tienen Windows 10 casi seguro que también la tienen porque se habrán actualizado, no así si usan Windows 8, por ejemplo, un Windows Server antiguo.

En cualquier caso, y aunque no tengas que distribuir la plataforma .NET completa junto a tu aplicación, muchas de estas aplicaciones acaban haciendo uso de multitud de DLLs de terceros: desde paquetes NuGet hasta DLLs propias que creamos en proyectos separados si la aplicación es compleja. En ocasiones, sin embargo, lo que te interesaría es poder distribuir un único ejecutable a tus usuarios, sin ninguna otra dependencia más allá de la propia plataforma. Esto es lo que vamos a ver en el artículo de hoy.

Nota: lo explicado en este artículo es válido tan solo para la plataforma .NET "clásica", o sea, hasta .NET 4.8.x. No sirve para .NET Core, pero en un próximo artículo explicaré cómo hacerlo con esta otra plataforma también.

El proyecto de ejemplo

Para ilustrar cómo hacerlo voy a crear un pequeño proyecto de ejemplo que nos sirva para hacer una prueba de concepto. Se trata de una aplicación de consola llamada singleexe que lo único que hace es mostrar por consola la hora actual. Por defecto, la app muestra la hora en una línea y luego un mensaje de "Pulse una tecla para continuar...", para evitar que se cierre la consola si hacemos doble-clic sobre el ejecutable. Pero, por si acaso queremos usarlo en un archivo de script, permite también el uso de un parámetro -b o --batch para mostrar tan solo la hora y continuar automáticamente.

Para facilitar la gestión de parámetros existe un paquete NuGet muy útil llamado CommandLineParser que facilita mucho el trabajo y que, además, por defecto, añade dos parámetros extra, --help para mostrar la ayuda de los parámetros y --version para mostrar la versión actual de la aplicación. Aquí tienes la documentación, pero lo único que hay que hacer es crear una clase para definir los parámetros y luego procesarlos en main, facilitándonos mucho las cosas en aplicaciones con muchos parámetros y sin preocuparnos del orden o de tener dos opciones (corta y larga, y con - o --). En este caso es, obviamente, excesivo para lo que queremos hacer, pero nos sirve para el objetivo que buscamos de mezclar ensamblados.

Este es el código de nuestra aplicación de ejemplo, que te dejo en este ZIP (4,07KB) para que puedas examinarla (recuerda restaurar los paquetes NuGet antes de compilarla):

El código fuente de nuestra mini-app

Ahora podemos ejecutar nuestra app con -b o --batch y obtener el resultado esperado (además de con -v o --version y -h o --help para ver la versión u obtener ayuda).

Si vamos a la carpeta bin a ver el resultado de compilarlo veremos que además del ejecutable tenemos que distribuir una DLL, CommandLine.dll, que pesa 212Kb. Vale, no es un problema comparado con cuando debemos distribuir decenas de ellas, pero nos sirve para nuestro ejemplo.

Fusionando ejecutables con ILMerge

Mike Barnett es Principal Research Software Design Engineer en Microsoft. Hace años creó una herramienta llamada ILMerge que sirve precisamente para lo que necesitamos: combinar varios ensamblados en uno solo, facilitando además otras cuestiones, como firmarlo digitalmente, convertir en privados los tipos que no se expongan al exterior, etc..

Aunque hace años se incluía con el SDK de .NET 2.0 y por lo tanto con Visual Studio, si lo tienes ya en disco no te servirá: necesitas la versión más moderna para poder trabajar con todas las versiones de .NET.

Como no, para instalarlo necesitarás hacerlo a través de un paquete NuGet. Así que debes abrir un proyecto en Visual Studio Code y agregarlo usando la siguiente instrucción en la línea de comandos de NuGet:

Install-Package ilmerge

La consola de NuGet en Visual Studio tras instalar el paquete ilmerge

Esto instalará la aplicación en la ruta: C:\Users\<TuUsuario>\.nuget\packages\ilmerge\<Version>\tools\net452. Desde ahí puedes utilizarlo para combinar los ensamblados de cualquier aplicación como veremos enseguida.

Aunque lo instalas como una dependencia de tu proyecto con NuGet, realmente no lo es y no lo verás en el nodo de dependencias del Solution Explorer de Visual Studio. De hecho, una vez instalado, puedes editar el packages.config para eliminar la línea de este paquete, pues no le hace falta a tu proyecto en realidad y una vez lo hayas instalado en algún proyecto ya lo tienes disponible en la ruta anterior para usar cuando quieras:

El archivo packages.config con la linea de ilmerge destacada para aeliminar

Nota: yo lo he dejado en el proyecto de ejemplo para que se te instale nada más intentes compilarlo y se restauren las dependencias. Así que, si abres el proyecto y lo ejecutas, ya no te hace falta instalarlo a mano.

Vale. Ya tenemos la utilidad instalada. Ahora solo queda utilizarla. Para ello lo más sencillo es ir a la carpeta donde se ha instalado (o añadirla al PATH del sistema para que esté siempre disponible) y escribir:

ILMerge <ruta del ejecutable> <rutas de las DLLs, separadas con comas> /out:<ruta nuevo ejecutable>

Por ejemplo, en mi app ejemplo, escribiré:

ILMerge E:\singleexe\bin\Release\singleexe.exe E:\singleexe\bin\Release\CommandLine.dll /out:E:\singleexe.exe

Y lo que hará será mezclar en singleexe.exe la DLL CommandLine.dll y generar un nuevo ejecutable llamado singleexe.exe (podría ser cualquier otro nombre) en la carpeta raíz de mi unidad E:\ que contiene la mezcla de ambos.

Este nuevo singleexe.exe pesa mucho más que el original (230Kb frente a las 5,5KB del original) porque ya tiene dentro todo el código compilado de las DLLs de soporte que estábamos utilizando.

Ahora puedo distribuir ese .exe con un solo archivo, el cual funcionará sin ningún problema pues tiene toda la funcionalidad embebida:

La app de consola de ejemplo ejecutándose con varias opciones de línea de comandos

Podemos usar algunas opciones más que pueden ser interesantes.

Por ejemplo, cuando hay muchas DLLs que necesitamos incluir, meterlas una a una en la línea de comandos es tedioso y propenso a errores. Así que podemos sacar partido al parámetro /wildcards:true de modo que nos permita usar * y ? en los nombres de los ensamblados a mezclar. Gracias a esto nuestro comando anterior se convierte en:

ILMerge E:\singleexe\bin\Release\singleexe.exe /wildcards:true E:\singleexe\bin\Release\*.dll /out:E:\singleexe.exe

Fíjate en cómo ahora, en lugar de especificar el nombre de la DLL he puesto un *.dll de modo que todas las DLL que haya en esa ruta se mezclarán con mi ejecutable. Así, si en el futuro le añado nuevas dependencias a la aplicación no me tengo que preocupar de añadirlas a la operación: se añadirán automáticamente.

Otra opción interesante es /internalize. Como sabes, en .NET cualquier clase pública que tenga tu aplicación, incluso en un .exe, se puede utilizar desde cualquier otra aplicación .NET con tan solo añadir una referencia al ensamblado. Si utilizas /internalize lo que consigues es que todas las clases de los ensamblados que mezclas se conviertan al ámbito interno (internal), de modo que no se puedan utilizar desde otras aplicaciones. Fíjate, por ejemplo, cómo se ha convertido la clase Parse del ensamblado CommandLine.dll en una clase interna en el ejecutable final con todo mezclado y esta opción activada:

Detalle de la clase Parser de CommandLine en ILSpy: se ve que es privada tras la mezcla

Si hay alguna clase o espacio de nombres que no queramos internalizar y permitir que sea visible desde el exterior podemos usar una expresión regular como valor para el parámetro /internalize de modo que se excluirán de este proceso, por ejemplo:

ILMerge <ruta> /internalize:CommandLine.Parser

En el valor de este argumento puede ir cualquier cosa que sea interpretable correctamente por el analizador de expresiones regulares de .NET.

Aquí tienes todas las opciones, pero algunas que pueden interesarte son:

  • /lib:carpeta: para especificar en qué carpetas debe buscar los ensamblados. Así te ahorras la ruta completa, como he puesto en mi ejemplo, y puedes poner tan solo el nombre de los archivos.
  • /ndebug:false: si no especificamos nada, ILMerge negera un archivo .dbg de depuración para el nuevo ensamblado (como Visual studio, de hecho), de modo que lo podamos usar para depurar en producción en caso necesario. Si no lo queremos, podemos especificar este parámetro y se generará sólo el ensamblado final fusionado.
  • /target: el tipo de ensamblado que quieres generar. Puede ser library, exe o winexe para, respectivamente, DLLs, aplicaciones de consola o aplicaciones de escritorio para Windows (incluyendo WPF). Si no indicamos nada, genera el mismo tipo de ensamblado que el archivo inicial.
  • /union: si hay dos o más clases con el mismo nombre en los ensamblados que se mezclan, con este switch le indicamos que queremos que los fusione en una sola clase con ese nombre, en vez de tener varias diferentes o que se produzca un conflicto.
  • /version: por defecto, pone la misma versión que tenga nuestro ensamblado principal (el que indicamos de primero), pero si usamos esto le podemos indicar una versión diferente, por ejemplo /version:1.5.0.
  • /log:ruta_a_archivo.txt: por derecto ILMerge no muestra nada por pantalla cuando hace su labor. Sabremos que ha terminado porque simplemente no da un error. Si queremos ver todo lo que ocurre durante el proceso, con esto podemos establecer la ruta a un archivo de texto (con la extensión que queramos) en el que se guardarán los detalles.

Esta utilidad funciona con todo tipo de ensamblados, no sólo ejecutables. Por ejemplo, si una DLL depende de otras (incluso una compilada como aplicación Web), con esta técnica podríamos combinarlas en una única DLL para distribuirla de manera más "empaquetada".

Desde luego no es algo que vayas a usar todos los días, pero sí que es muy interesante para algunas situaciones especiales, en cuyo caso te puede salvar el día.

¡Espero que te resulte útil!


Git: Cómo evitar problemas con cambios de línea al trabajar en equipo

$
0
0

Como seguramente sabrás, cada vez que en tu teclado pulsas la tecla ENTER para cambiar de línea en tu código o en cualquier documento de texto, lo que ocurre es que se inserta un carácter de control que representa ese fin de línea. Estos caracteres de control no se ven, pero están ahí y ocupan memoria (o lo que es lo mismo, espacio en disco).

Como en muchas otras cosas, existen diferencias entre los sistemas operativos a la hora de tratar este tipo de caracteres de control. Y en concreto, Windows, macOs y Linux utilizan caracteres de control diferentes para hacerlo.

Si tienes acceso a dos sistemas de estos, es muy fácil de comprobar. Por ejemplo, abre el bloc de notas en Windows y escribe unas cuantas líneas de texto con un solo carácter en cada una de ellas. Ahora haz lo mismo en un editor de texto plano de Linux o de macOS, escribiendo exactamente las mismas líneas:

 Dos archivos de texto con el mismo contenido en Windows y en macOS

Si te fijas en sus tamaños, resaltados en rojo en la figura, el de Windows ocupa un poco más. En concreto 5 bytes más que el de macOS.

El motivo de esto es que en macOs y Linux se utiliza como carácter de control para cambio de línea un Avance de línea (Line Feed o LF). En Windows, por otro lado, se utilizan dos caracteres: el mismo LF y además, delante de éste, otro llamado Retorno de Carro (en inglés Carriage Return o CR), es decir, Windows usa CRLF, dos caracteres, como control para el cambio de línea. Por eso ocupa 1 byte más por línea y por tanto un 50% más en este caso extremo.

Nota friki #1: esta terminología viene de los tiempos de las máquinas de escribir 😯 El carro era un cilindro que agarraba el papel y que se movía en horizontal con cada letra y hacia arriba por cada línea. Cuando en una de estas máquinas terminabas de escribir una línea, le dabas un golpe al carro con la mano izquierda, que lo que hacía eran dos cosas: moverlo a la derecha (retorno de carro) y girar el carro un poco hacia arriba para subir el papel una línea (avance de línea). En este vídeo se pueden ver ambas cosas en acción: primero el avance de línea y luego el retorno de carro con un golpe más fuerte.

Nota friki #2: en los antiguos sistemas Mac "clásicos" que se comercializaron hasta 2001, el cambio de línea se hacía con un retorno de carro, CR, solamente, al igual que ocurría con los clásicos ZX Spectrum o Commodore 64. El CRLF de Windows se usaba también en sistemas clásicos como los Amstrad CPC, en OS/2, sistemas Atari y muchos otros. Y muchos sistemas embebidos o para mainframes todavía en uso en bancos y grandes aseguradoras utilizan sus propios caracteres de control para cambio de línea. De hecho, el estándar Unicode recoge hasta 8 caracteres de control que se deberían reconocer como terminadores de línea.

Si abrimos los 3 archivos (Windows, Mac clásico y Linux) con Notepad++, que es capaz de mostrar los caracteres de control, vemos las diferencias claramente:

 Los carácteres de control en cada uno de los sistemas

Problemas de diferencias de archivos en Git debidos a retornos de carro

Cuando trabajamos en equipo con el sistema de control de código fuente Git, podemos llegar a tener problemas debido a estas diferencias.

Dependiendo de cómo tengamos configurado Git, si en el equipo tenemos a unos en Windows, a otros en macOS o en Linux, según quién edite en cada momento los archivos, pueden registrarse cambios que son debidos tan solo a estos elementos de control para cambio de línea.

Por regla general, los programas de comparación de archivos hacen caso omiso de los caracteres de control, por lo que no notaremos la diferencia. Pero sí que veremos archivos cambiados en los commits, y a la hora de copiar archivos a otro lado podremos sobrescribir algunos que realmente no han cambiado. Si, por ejemplo, copiamos archivos a otro servidor por la red local o por FTP y el programa de copiado decide, si debe sobrescribir o no, en función de si ha variado la fecha de modificación del archivo o su tamaño, podríamos copiar muchos más archivos de los necesarios solo por esa diferencia en los cambios de línea. O si hacemos despliegue continuo y se van a desplegar en el servidor mediante el propio Git, se podrían cambiar archivos sin querer sólo por estos cambios de línea.

En el asistente de instalación de Git en Windows y Linux se nos muestra una opción específica para controlar este comportamiento:

 Opciones de cambio de línea en la instalación de Git

Se nos ofrecen 3 opciones que ya vienen bien explicadas en la propia pantalla pero que detallo a continuación en español:

  1. Checkout Windows-Style, commit Unix-style: esto implica que cuando nos traigamos los archivos desde el repositorio remoto, Git convertirá todos los cambios de línea en CRLF, es decir, en el estilo para Windows. Esto es lo que tendremos para trabajar localmente. A la hora de enviarlos al repositorio de nuevo, Git los convertirá de nuevo al estilo UNIX, es decir, a LF.
  2. Checkout as is, commit Unix-style: como en la opción anterior, en este caso se convertirán todos los cambios de línea a LF al enviarlos al repositorio remoto y no se hará conversión alguna al traerse los archivos, es decir, se obtendrán tal cual están en el repositorio remoto.
  3. Checkout as-is, commit as-is: con esta opción, Git no hace cambio alguno sobre los cambios de línea.

La última opción es la peor de todas y la que no deberíamos elegir casi nunca. Si en el proyecto trabajan personas con diferentes sistemas o, si nosotros mismos trabajamos con más de un sistema operativo, acabaremos con el problema que describía antes, con varios tipos de cambios de línea mezclados.

Lo que deberíamos hacer es, marcar la opción 1 si trabajamos en Windows y la opción 2 si trabajamos en Linux o Mac. De este modo tendríamos siempre el tipo de cambio de línea apropiado para nuestro sistema, y al mismo tiempo se guardaría de manera homogénea en los repositorios remotos.

Puedes saber qué opción tienes establecida si abres una terminal y escribes:

git config --global core.autocrlf

Por ejemplo, en Windows yo tengo esta opción establecida a true, que es lo correcto según hemos visto antes:

 Opción autocrlf establecida a true

Y los compañeros que trabajen con macOS o Linux la deben establecer como input, para que siempre se use LF como cambio de línea.

Si tienes otro valor, puedes cambiarlo globalmente usando la misma instrucción que antes, pero indicando el valor que le vas a ajustar, por ejemplo:

git config --global core.autocrlf true

para Windows si no la tuvieras correctamente establecida, o con input para Linux/macOS.

No dejar nada al azar: establecer por repositorio

El problema de lo anterior es que dejamos a la responsabilidad de cada desarrollador el establecer la opción correcta, lo cual es propenso a errores. Si no conocen la problemática o cuál es la opción correcta podríamos tener problemas.

La solución a esto es no dar la libertad de elegir y establecer esta opción directamente en todos los repositorios que tengamos. Para ello podemos hacer uso del archivo .gitattributes.

Este archivo se crea en la raíz del repositorio y permite incluir configuración de Git que afectará tan solo al repositorio actual y que, por lo tanto, prevalece sobre lo que haya en la configuración global de Git.

En el caso que nos ocupa podemos establecer el modo en que gestionará Git cada archivo para los cambios de línea añadiendo patrones de archivos y un valor de configuración concreto.

Si queremos la configuración correcta equivalente a autocrlf, lo que debemos hacer es escribir esto en ese archivo:

* text=auto

Esto significa que, para todos los archivos de texto, Git decidirá de manera automática qué hacer respecto de los cambios de línea, lo cual es equivalente a la situación ideal que mencionaba antes: que en Windows usará CRLF y en macOS/Linux usará LF. Además, al enviar los cambios a un repositorio remoto los homogeneizará a LF.

Esto asegura consistencia para los cambios de línea entre todos los desarrolladores, sin importar en qué sistema operativo estén trabajando.

Es una gran idea tener un archivo .gitattributes con configuraciones como esta como parte de las plantillas de proyectos que usemos. Como se mete bajo control de código, aparecerá en los repositorios locales de todos los desarrolladores.

Si queremos configurarlo de manera distinta para algún tipo de archivo concreto podemos hacerlo también. Por ejemplo, los archivos .sln de Visual Studio que guardan información sobre los proyectos que forman parte de una solución son archivos de texto, y dado que Visual Studio sólo funciona en Windows, podríamos forzar a que siempre utilicen CRLF como separador de línea escribiendo esto en el archivo .gitattributes:

* text=auto

# Archivos de soluciones de Visual Studio
*.sln text eol=crlf

De este modo le estamos indicando a Git que lo archivos con la extensión .sln son de texto, pero que debe utilizar siempre CRLF para los cambios de línea, independientemente del sistema operativo en el que se clonen. Así evitamos que si lo abre un colaborador en macOS, cambie ese ajuste. No es que sea necesario, pero podríamos forzarlo así.

Arreglar un repositorio que no esté con cambios de línea homogéneos

Bien, con lo que hemos visto ya podemos crear una configuración homogénea para todo nuestro equipo (o para nosotros si trabajamos en varias máquinas con sistemas operativos diferentes).

Pero, ¿qué pasa si el mal ya está hecho y cuando hemos puesto este remedio en marcha ya tenemos centenares de archivos de código con diferentes estilos de cambio de línea?

Por suerte, Git ya tiene este problema contemplado también, así que podemos hacer lo siguiente:

git add --renormalize .

(¡Fíjate en el punto del final, es importante!)

Al hacer esto, Git recorrerá todos los archivos de contenido textual que haya en nuestro repositorio y se asegurará de que tienen los cambios de línea homogéneos, según la configuración que corresponda.

Tras hacerlo, tendremos en el área de preparación (staging) de Git todos los archivos que se han modificado y sólo tendríamos que hacer un commit y un push al repositorio remoto para persistir los cambios:

git commit -m "Homogeneizados los cambios de línea"
git push

Y listo.

Con estas técnicas sencillas podremos tener la seguridad de que no vamos a tener problemas por culpa de algo tan aparentemente inofensivo como unos cambios de línea.

¡Espero que te resulte útil!

.NET Core / .NET 5: Cómo publicar aplicaciones en un único ejecutable

$
0
0

Cuando compilas una aplicación con .NET Core de cualquier tipo, tienes dos formas básicas de hacerlo:

  • Generando tan solo los ensamblados propios de tu aplicación y de los componentes que utilicen. En este caso debes estar seguro de que los equipos que van a ejecutar la aplicación tienen instalado el runtime de .NET. Esto es lo que hace por defecto Visual Studio cuando compilas.
  • Incluyendo en el resultado todos los archivos propios de .NET, de modo que el runtime se distribuye con tu aplicación y no es necesario que los usuarios instalen nada. Esto facilita mucho el despliegue a costa de tener que mover un montón de archivos (y "megas").

Da igual la modalidad que escojas: al final acabas con un ejecutable y un número mayor o menor de ensamblados relacionados que vas a necesitar según lo que elijas.

Por ejemplo, para publicar la aplicación sólo con los ensamblados propios basta con que escribas en la línea de comandos (dentro de la carpeta del proyecto) lo siguiente:

dotnet publish -c release -r win10-x64 --self-contained false

Esto generará un ejecutable de tu aplicación para Windows de 64 bits (opción -r) con la configuración "Release" (o sea, para producción: opción -c) y de modo que no sea autosuficiente, por lo que necesitará tener .NET instalado para funcionar. Así que obtendrás tan solo tu ejecutable con los ensamblados que necesite (paquetes NuGet y bibliotecas propias que la acompañen).

Si hubiésemos utilizado --self-contained true se hubiesen incluido también todos los ensamblados de tiempo de ejecución de .NET (runtime), por lo que en nuestra carpeta de publicación tendríamos literalmente cientos de archivos aparte del nuestro.

¿Cómo podemos conseguir un único ejecutable con todo lo necesario?

De esta forma podríamos obtener un único archivo que podríamos enviar con confianza a cualquiera, de modo que, sin necesidad de instalar nada, pueda ejecutar nuestra aplicación. Esto es fenomenal para algunos casos de uso, ya que facilitamos mucho la distribución de la aplicación para que la use cualquiera, basta con descargar un archivo y hacer doble-clic.

Hace poco explicaba cómo puedes conseguir esto mismo con .NET "clásico". Para lograrlo hay que emplear una herramienta especial, ajena a la propia plataforma. Además no se incluyen los propios de .NET, sino tan solo los tuyos.

Sin embargo, en .NET Core ya se ha tenido en cuenta esta posibilidad de serie y no necesitamos nada especial: tan sólo el comando dotnet y usar sus opciones con un poco de habilidad.

En esta ocasión, en lugar de contártelo por escrito, he grabado un vídeo práctico en el que te enseño cómo puedes hacerlo no sólo con aplicaciones de consola o escritorio sino incluso para poder distribuir aplicaciones web autocontenidas, con un único .exe a distribuir para que funcionen:

[youtube:f-HP8yOfGxg]

¡Espero que te resulte útil!

Trabajar como programador: 5 "extras" que puedes pactar con la empresa en lugar del salario

$
0
0

Imagen ornamental de una oficina con muchos ordenadores, por Giu Vicente, CC0

Muchos profesionales, a la hora de valorar las oportunidades laborales, tienden a centrarse tan solo en la parte salarial, olvidándose de "los extras". Y es que, cuando recibes una oferta de trabajo, es importante mirar más allá del salario, ya que hay otro tipo de cuestiones que te permiten obtener mejores condiciones y una vida profesional más satisfactoria. No todo en la vida es dinero...

Hace unos años estas cosas parecían propias de otros países y culturas, pero cada vez más empresas y departamentos de recursos humanos en el sector de la programación están abiertas a este tipo de propuestas, incluso a medida para cada persona.

Unos extras adecuados pueden incluso conseguir que un puesto con un salario más bajo sea más satisfactorio que otro mejor pagado pero con unos extras limitados (o sin ellos). Por eso es importante que tomes tus decisiones de forma inteligente.

Los beneficios extra son una forma de remuneración que no se contabilizan como parte del salario, pero que contribuyen a la retribución total. Las empresas utilizan los extras para atraer a nuevos empleados. Algunas empresas pueden tratar de tentarte con pequeñas cosas, como refrigerios en la sala de descanso o la hora feliz del viernes. Pero eso no son más que cuestiones sin importancia. Conviene que consideres cuidadosamente lo que puedes pedir cuando decidas aceptar un trabajo.

Entonces, ¿qué extras merecen ser considerados? Cada empresa tendrá sus propios incentivos, y vale la pena investigar cuáles son los más importantes para ti.

1.- Tus herramientas tecnológicas favoritas

La programación es una tarea intensiva en tecnología. Programar código para la empresa requiere de herramientas especiales a las que también se debería tener acceso desde casa y en los desplazamientos. Estas herramientas incluyen ordenadores portátiles de gama alta que se cambien cada poco tiempo, Internet de alta velocidad y herramientas de otro tipo (por ejemplo, espacio de almacenamiento de datos en la nube) que, aunque no estén relacionadas 100% con tu trabajo puede pagarte la empresa.

Por ejemplo, es posible que necesites un punto de acceso móvil para los momentos en que viajas o te desplazas al trabajo. Es posible que necesites una conexión de fibra rápida en casa para conectarte de forma segura a la oficina. Habla con tu empleador sobre cómo cubrir los costes.

2.- Trabajo en remoto

Cada vez más programadores no encajan en el molde estándar de horarios de 9 a 6 desde la oficina. La inspiración puede llegarte a medianoche mientras estás en la cama. Cuando te entra en la cabeza la forma de programar algo o la solución a un problema, puedes perder la noción del tiempo y terminar llegando tarde a casa o al trabajo. A veces, el hecho de tener un puesto de trabajo físico en la oficina para ir a casa puede hacer que pierdas la pista de tu progreso y tus ideas. Por no mencionar las pérdidas de tiempo en transporte, comer, etc. Además, si tienes hijos o personas que dependen de ti, es una manera indispensable de poder conciliar la vida laboral y familiar.

Algo que puede ser muy interesante, por tanto, es negociar la posibilidad de trabajar a distancia, ya sea a tiempo completo o en horario flexible.

Otra opción sería trabajar días condensados. Por ejemplo, puedes elegir trabajar turnos de 10 horas durante cuatro días en lugar de turnos de ocho horas durante cinco días a la semana.

Si vives lejos de la oficina o esta es muy ruidosa y te distrae, una opción muy extendida es trabajar desde tu casa. Un empleador inteligente debería alegrarse si tiene teletrabajadores productivos y felices.

3.- Proyectos personales en horario laboral

Ser desarrollador te recompensa con las habilidades para llevar a cabo proyectos increíbles en tu tiempo libre, pero no solo eso.

Si tienes un perfil emprendedor puedes negociar con tu empresa para que te permita invertir algunas horas de trabajo a la semana en proyectos personales a cambio de que les informes de tus progresos y acuerdes formas de comercializar esos desarrollos de forma conjunta con tu empresa.

Por ejemplo, si utilizas Swift para crear aplicaciones de iOS para tu trabajo diario, también puedes lanzar tus propias aplicaciones para móviles como un medio de obtener ingresos adicionales para ti o para tu empresa, lanzando un producto mínimo viable propio para ver si hay un mercado para tus ideas.

Google siempre ha sido famosa por sus famosos "proyectos del 20%", que son proyectos personales de cualquier índole que puedes realizar en tu horario laboral, dedicándoles hasta un 20% de tu tiempo. Hay quien los utiliza para proyectos totalmente personales, para colaborar con sus conocimientos con una ONG... pero también hay productos exitosos que han salido de eso, como por ejemplo GMail 😯

Lo bueno de ser desarrollador es que tus habilidades son fácilmente transferibles y permite lanzar proyectos para ti mismo, o de forma conjunta con la empresa, con amigos, o incluso sólo por diversión o con fines formativos. Y esto es también una gran parte de tu satisfacción personal.

4.- Compensación de las horas extra

La carrera profesional de un programador está llena de largos períodos de cierto hastío entremezclados con breves ráfagas de acción frenética. Para la mayoría de los programadores, hay períodos de trabajo muy intensos, como por ejemplo durante la salida de un software o de una versión. Se espera de ti que trabajes muchas horas y días extras durante estas temporadas de puntas de trabajo. Y, claro está, legalmente las horas extra es obligatorio pagarlas, pero muchas empresas ya se sabe...

Si crees que el trabajo tendrá temporadas muy ajetreadas de vez en cuando, intercaladas con otras menos intensas, negocia para que te paguen todas las horas extras o pide un tiempo de compensación en forma de vacaciones (en inglés este término se conoce como "comp time"). El tiempo de compensación es el tiempo libre remunerado que proporciona el empleador a cambio de las horas extras trabajadas.

En cualquier caso no te quedes en una empresa en la que hacer más horas que el reloj sea la norma y no la excepción.

5.- Acciones y participación en capital

En empresas que están empezando, que no tienen claro del todo el modelo de negocio y que han logrado algo de financiación, es habitual que no puedan pagarte tanto como en una empresa consolidada.

Al mismo tiempo, en estas "startup", a veces como desarrollador tus creaciones son las que forman la columna vertebral de los productos y servicios de la nueva empresa. En esos casos, podrías considerar la negociación de opciones de compra o el acceso de alguna forma a las acciones. Aunque con la legislación fiscal y societaria española es mucho más complicado de llevar a cabo que en otros países, esto es algo cada vez más habitual en este tipo de empresas.

Pero claro, una advertencia: un alto porcentaje de estas empresas de nueva creación fracasan. Si la empresa se hunde sin lograr una alta rentabilidad o sin ser adquirida por otro actor de mayor tamaño, cualquier empleado que tenga acciones se irá a casa sin nada. Sin embargo, si crees en la viabilidad del producto y en el equipo, una participación en el capital te da una porción del éxito a largo plazo de la empresa a cambio de renunciar a un salario de mercado al principio. Calcula la capitalización de acciones de tu empleador antes de considerar la negociación de las acciones y el capital social.

Conclusión

No importa lo bien pagado que esté un puesto de trabajo. Si hay aspectos del mismo que disminuyen la productividad y la creatividad, es posible que sientas insatisfacción en tu rol en la empresa. Extras como las herramientas especiales de tu pila tecnológica y los acuerdos de trabajo flexible te permiten seguir trabajando en tus condiciones, con el equipamiento adecuado. Una empresa que ofrece extras en temas de formación te permitirá seguir desarrollando tus habilidades para no estancarte en tu trabajo.

Hay muchas otras fórmulas que te pueden servir. Lo importante es que dediques un tiempo a pensar qué cosas son importantes para ti y las formules en forma de extras de cara a negociarlas con tu empleador.

Negociar los extras adecuados puede marcar la diferencia para tu éxito y la felicidad en tu trabajo.

Cuestiones como la formación y el trabajo flexible significan que puedes mantenerte al día en tus habilidades. Puedes trabajar de una manera que promueva tu productividad. Tener participaciones en una empresa de nueva creación es una forma de trabajar para ti mismo sin arriesgarte tanto como si fueses emprendendor.

Tu salario es importante, pero los extras que negocies pueden tener la misma importancia que tu salario total, así que no los pases por alto cuando aceptes un nuevo trabajo.

Docker vs Kubernetes ¿En qué se diferencian?

$
0
0

imagen ornamental

Dicho de manera muy simplificada, la tecnología de contenedores permite empaquetar y distribuir software aislándolo del sistema operativo subyacente y asegurándonos que se van a ejecutar siempre igual en cualquier sitio. Gracias a ellos podemos desplegar aplicaciones rápidamente sin miedo a que algo falle aunque cambiemos de entorno, arrancarán rapidísimo y podremos usarlos para desarrollar, probar y poner en producción cualquier aplicación o conjunto de ellas sin sorpresas.

Si esto te suena a una máquina virtual, te recomiendo que leas mi artículo ¿Qué diferencia hay entre Docker (Contenedores) y Máquinas virtuales (VMWare, VirtualBox...)?, porque verás que no son lo mismo en absoluto.

Cuando estás empezando con la tecnología de contenedores existen dos productos que aparecen todo el tiempo: Docker y Kubernetes, lo que da lugar a confusión. Vamos a aclarar qué son y en qué se parecen o se diferencian ambos productos.

¿Qué es Docker?

Docker es la principal tecnología para la creación y ejecución de contenedores. Se trata de un conjunto de herramientas Open Source que nos permiten definir, crear y ejecutar imágenes de software y contenedores que las ejecutan en cualquier sistema operativo.

Con Docker puedes definir cómo será el entorno en el que se ejecutará tu aplicación, que tú y todo tu equipo podáis desarrollar sobre la imagen correspondiente, que otros la prueben e incluso llevártela a un servidor, a producción, con la garantía de que siempre va a funcionar exactamente igual. Esto es una maravilla, y termina de una vez por todas con la clásica letanía de "¡En mi máquina funciona!" cuando algo falla en producción.

Las imágenes de tus aplicaciones se pueden compartir con todo el equipo (e incluso externamente), las puedes versionar, combinar... y puede facilitar mucho el desarrollo, prueba y despliegue de las aplicaciones, incluso en equipos de trabajo pequeños.

Aquí desarrollamos más todos los conceptos básicos sobre Docker.

¿Qué es Kubernetes?

Tras una temporada trabajando con Docker seguro que no quieres volver atrás. Sin embargo, si no desarrollas o pruebas únicamente, sino que tienes que poner aplicaciones en producción, probablemente tendrás una sensación agridulce con él. Y es que para eso se queda corto si tienes que gestionar más de uno o dos contenedores o varios servidores.

Cuando debes desplegar una aplicación hay muchas cosas que puedes necesitar, más allá de simplemente "levantar" un contenedor y de que la app funcione. Algunos ejemplos (hay muchos más) son:

  • Ejecutar contenedores/apps diferentes en diferentes servidores sin tener que ponerlos a funcionar uno a uno, a mano.
  • Asegurarte de que la aplicación siempre funciona y, si algo falla o "se cae", que se vuelva a levantar automáticamente.
  • Relacionado con lo anterior, monitorizar tus contenedores para saber en qué estado se encuentran en todo momento.
  • Si la aplicación se divide en capas y cada una en diferentes contenedores, algo muy habitual, que éstos sepan cómo comunicarse entre sí sin que tengas que hacerlo todo manualmente.
  • Cuando la aplicación crezca que pueda escalar automáticamente, levantando nuevas instancias para atender a más usuarios, desplegando más contenedores en otras máquinas, etc... Es decir, lograr que tu desarrollo no muera de éxito.

En definitiva, cuando empiezas a desplegar en serio aplicaciones "contenedorizadas" vas a tener que desplegar los contenedores, arrancar cada uno cuando le corresponda, comunicarlos entre sí, gestionar su almacenamiento, lidiar con sus fallos y caídas, y escalarlos cuando se necesite. Y hacer esto a mano puede llegar a ser una pesadilla y algo totalmente inviable en algunos entornos.

Es ahí en donde entra en juego Kubernetes, una tecnología Open Source desarrollada por Google que se encarga de todas estas cosas y muchas más. Es lo que se denomina un orquestador de contenedores, ya que los dirige y coordina, como el director de una orquesta, para que trabajen adecuadamente todos juntos.

Esquema básico de la relación entre Docker y Kubernetes

Puedes profundizar más en las utilidades de Kubernetes en este estupendo artículo de Eduard Tomás, el autor y tutor del mejor curso online que existe sobre Docker y Kubernetes.

Entonces ¿en qué se parecen o diferencian Docker y Kubernetes?

Como podemos deducir de lo anterior, Docker y Kubernetes son tecnologías relacionadas, pero no tienen nada que ver.

Docker es la tecnología que nos va a permitir hacer lo básico del trabajo con contenedores (definirlos, ejecutarlos y que funcionen), mientas que Kubernetes saca partido a esta tecnología para llevarla un nivel más allá, potenciando a Docker para permitirnos crear algo mucho más complejo a partir de esa tecnología de base.

Conviene aclarar que Docker no es la única tecnología de contenedores que existe ni la única que puede utilizar Kubernetes. Y Kubernetes no es el único orquestador de contenedores Docker que hay en el mercado, ni mucho menos. De hecho, Docker tiene su propio orquestador llamado Swarm que puedes utilizar de serie con esta tecnología.

Sin embargo, ambas son las tecnologías dominantes en su especialidad (Docker la principal tecnología de contenedores, y Kubernetes el principal orquestador de contenedores), y juntos forman una pareja imbatible para construir aplicaciones basadas en contenedores, modernas, escalables y con toda la complejidad que necesitemos.

Cómo manejar correctamente fechas en Java: el paquete java.time

$
0
0

Desde el inicio de los tiempos, la edición estándar de Java incluye un par de clases para manejo de fechas, las conocidas java.util.Date y java.util.Calendar. Si haces una búsqueda rápida sobre manejo de fechas en Java, verás que la mayoría de los artículos y tutoriales se centran en el uso de estas clases obsoletas. ¡Error!

Estas clases de manejo de fechas no son las más adecuadas para casi nada. Están mal diseñadas, abusan del uso de enumeraciones y constantes para indicar cosas, y además no son seguras para uso multihilo (no son thread-safe, en la jerga). La posterior adición de la clase java.sql.Date, pensada para facilitar su uso con bases de datos JDBC no mejoró la cosa, ya que no es más que una subclase de java.util.Date y por lo tanto adolece de los mismos problemas.

Entonces, ¿cuál es la mejor manera de gestionar fechas en java?

En este artículo veremos las clases apropiadas y algunas "recetas" para hacer tareas comunes con fechas con ellas.

El paquete java.time

Dado que las clases base de manejo de fechas en Java son tan problemáticas, surgieron alternativas por parte de la comunidad. La más conocida y utilizada siempre ha sido la biblioteca Joda-Time, que es gratuita y Open Source. Tan popular era que sus desarrolladores participaron junto a Oracle en la definición de las nuevas clases oficiales para manejo de fechas en Java: las incluidas en el paquete java.time.

En marzo de 2014 apareció la versión estándar de Java 8 y con ella estas nuevas clases especializadas que solucionan la mayor parte de los problemas de las clases tradicionales, e incluyen soporte automático para cosas como años bisiestos, zonas horarias y cambio automático de zona horaria.

A pesar de la cantidad de años que han pasado, muchos programadores siguen usando las clases antiguas, y es sorprendente ver la cantidad de recursos en la Web, incluso recientes, que siguen haciendo referencia a estas clases obsoletas.

Este paquete java.time incluye muchas clases, pero las básicas son:

  • LocalDate: representa a fechas sin la hora y nos facilita su manejo para declararlas, sumar y restar fechas y compararlas.
  • LocalTime: es idéntica a la anterior pero para el manejo de horas, sin ninguna fecha asociada, pudiendo así compararlas, sumar o restar tiempo a las mismas...
  • LocalDateTime: como puedes suponer, es una combinación de las dos anteriores, que permite hacer lo mismo con fechas y horas simultáneamente.
  • Instant: es muy parecida a la anterior pero a la vez muy diferente. Se usa para almacenar un punto determinado en el tiempo, o sea con fecha y hora, pero guarda su valor como un timestamp de UNIX, es decir, en nanosegundos desde el epoch de UNIX (1/1/1970 a las 00:00) y usando la zona horaria UTC. Es muy útil para manejar momentos en el tiempo de manera neutra e intercambiarlo entre aplicaciones y sistemas, por lo que lo verás utilizado muy a menudo.
  • ZonedDateTime: esta clase es como la LocalDateTime pero teniendo en cuenta una zona horaria concreta, ya que las anteriores no la tienen en cuenta.
  • Period: esta clase auxiliar nos ayuda a obtener diferencias entre fechas en distintos periodos (segundos, minutos, días...) y también a añadir esas diferencias a las fechas.
  • Duration: esta es muy parecida a la anterior pero para manejo de horas exclusivamente.

Construyendo fechas y horas con java.time

Estas clases producen instancias inmutables, al contrario de lo que pasaba con las antiguas clases Date de Java, por lo que son thread-safe. Dado que carecen de constructores públicos, se instancian usando métodos de tipo "factoría", es decir, tienen métodos que construyen estas clases a partir de posibles parámetros que le pasemos.

En concreto, todas las de manejo de fechas y horas disponen de tres métodos importantes, que son:

  • now(): crean instancias nuevas a partir de la fecha y hora actual.
  • of(): construyen fechas y horas a partir de sus partes.
  • with(): modifican la fecha u hora actual en función del parámetro que se le pase, con alguna cantidad (años, días, horas...) o alguna clase de ajuste que enseguida estudiaremos.

Vamos a ver now() en acción con algunas de estas clases:

System.out.println("La fecha actual es: " + LocalDate.now());
System.out.println( "La hora actual es: " + LocalTime.now() );
System.out.println( "La fecha y hora actuales son: " + LocalDateTime.now() );
System.out.println( "El instante actual es: " + Instant.now() );
System.out.println( "La fecha y hora actuales con zona horaria son: " + ZonedDateTime.now() );

Que nos mostrarían por pantalla algo similar a esto:

Las fechas generadas, mostradas por pantalla

Fíjate en cómo cada tipo de clase genera un tipo de dato ligeramente diferente.

Al convertirlas a cadena para mostrarlas se generan en el formato ISO 8601, que es un estándar ampliamente aceptado. Luego veremos cómo formatearlas de otro modo que nos interese más.

Para controlar qué fechas y horas generamos podemos usar el método factoría of() que admite ciertos parámetros en función del tipo de dato utilizado. Por ejemplo:

System.out.println( "Fecha de mi cumpleaños: " + LocalDate.of(1972, Month.MAY, 23) );

Fíjate en que para el mes, aunque podría haber utilizado los números del 1 al 12 para indicarlo, he usado una enumeración específica que existe para ello llamada Month, cuyos miembros son los nombres de los meses en inglés. Así que mayo, que sería el mes 5, se convierte en Month.MAY.

Importante: si estás acostumbrado a usar la clase Calendar tradicional, sabes que existe algo parecido ya que es posible escribir Calendar.MAY para lograr lo mismo. La diferencia está que, en ese caso, Calendar.JANUARY es el número 0, mientras que Month.JANUARY es el número 1. Si no tienes cuidado y usas los números en lugar de la enumeración puedes cometer errores por 1 mes fácilmente.

En el caso de LocalDateTime también le podríamos haber pasado la hora llegando hasta los nanosegundos, en sucesivos parámetros:

System.out.println( "Con la hora exacta: " + LocalDateTime.of(1972, Month.MAY, 23, 20, 01, 15, 0023) );

que incluiría la hora exacta. Este método tiene varias sobrecargas, por lo que, por ejemplo, le podríamos haber pasado también dos parámetros, uno con la fecha y otro con la hora usando sendas clases LocalDate y LocalTime. Te refiero a la documentación para ver las posibilidades.

Si intentas pasar una fecha incorrecta al método factoría se produce una excepción. Por ejemplo, si quisieras usar el día 29 de febrero de 2019:

System.out.println( "Día bisiesto de 2019: " + LocalDate.of(2019, Month.FEBRUARY, 29) );

Dado que ese año no fue bisiesto, se produciría una excepción:

Excepción al intentar instanciar una fecha incorrecta

Partes de una fecha o una hora

Es posible extraer cualquier parte de una fecha o una hora a través de los métodos getXXX() que ofrecen estas clases. Por ejemplo, getHour(), getMinute(), getMonth() o getDayOfWeek().

Miembros de la clase LocalDateTime

Gracias a ellos podemos extraer cualquier parte de una fecha para trabajar con ella.

Transformando fechas y horas

O lo que es lo mismo: construyendo unas fechas y horas a partir de otras. Según la clase que manejemos tendremos una serie de métodos para añadir o quitar intervalos al dato.

Por ejemplo, la clase LocalDate dispone de los métodos:

  • plusDays() / minusDays(): para sumar o restar días a la fecha.
  • plusWeeks() / minusWeeks(): ídem con semanas.
  • plusMonths() / minusMonths(): para añadir o quitar meses.
  • plusYears() / minusYears(): para sumar o restar años.

Del mismo modo LocalTime ofrece los métodos plusNanos(), plusSeconds(), plusMinutes() y plusHours() para sumar nanosegundos, segundos, minutos y horas a la hora actual respectivamente. Del mismo modo, tiene los mismos métodos, pero con el prefijo minus, para restarlas: minusNanos(), etc. Intuitivo y directo:

System.out.println("La fecha dentro de 10 días: " + LocalDate.now().plusDays(10) );
System.out.println("La fecha y hora de hace 32 horas: " + LocalDateTime.now().minusHours(32) );

Dado que son clases inmutables, recuerda, lo que se devuelve en todos los casos son instancias nuevas del nuevo dato, no versiones modificadas de los datos originales.

Truco: en realidad, si a plusXXX() le pasas un número negativo estarás consiguiendo el mismo efecto que si usas el método correspondiente minusXXX().

Ajustadores temporales

Aparte de los métodos de suma y resta de fechas que acabamos de ver, existe una clase especializada llamada TemporalAdjuster que nos permite definir ajustes para las fechas para obtener nuevas fechas a partir de una existente.

Existe una clase factoría llamada TemporalAdjusters (en plural) cuyos métodos permiten obtener ajustes de fecha (de la clase TemporalAdjuster) de manera sencilla para hacer muchas cosas.

Por ejemplo, si queremos obtener el primer día del mes de una determinada fecha podemos usar el método TemporalAdjusters.firstDayOfMonth(). Del mismo modo existen "ajustadores" para otras operaciones similares, que puedes ver en el enlace anterior.

Así, para averiguar la fecha del primer día del mes que viene, podemos escribir:

System.out.println("El primer día del próximo mes es: " +
    LocalDate.now().with(
      TemporalAdjusters.firstDayOfNextMonth()
    ).getDayOfWeek() );

Lo he sangrado para que se lea con mayor claridad, pero lo que hace es utilizar el método with() de las clases de tiempo (del que hablamos al principio) que en este caso toma un ajustador para obtener el primer día del mes siguiente. Luego usamos el método getDayOfWeek() sobre la fecha resultante para saber qué día de la semana es.

O para saber la fecha del último día del mes actual:

System.out.println("El último día de este mes es: " +
    LocalDate.now().with(
      TemporalAdjusters.lastDayOfMonth() ));

Por supuesto, como lo que devuelven son objetos de tiempo, se pueden combinar con las funciones plusXXX() y minusXXX() para hacer más operaciones.

Es importante señalar que, se pueden crear nuestros propios ajustadores temporales con tan solo implementar una interfaz. Puedes ver un ejemplo en la propia documentación de Java.

Tiempo transcurrido entre fechas y horas

Otra tarea habitual que necesitaremos hacer es obtener la diferencia entre dos fechas u horas, o sea, el tiempo transcurrido entre dos instantes de tiempo.

Para ello existe una interfaz java.time.temporal.TemporalUnit, una enumeración ChronoUnit y un clase Period en ese mismo paquete que se encargan de facilitarnos la vida para esto. Con sus métodos: between() y until() nos proporcionan respectivamente el tiempo transcurrido entre dos instantes de tiempo y el tiempo que falta para llegar a una fecha u hora determinadas. Vamos a verlo.

Por ejemplo, imaginemos que queremos saber cuánto tiempo ha transcurrido entre la fecha de tu nacimiento y el día de hoy. Para averiguarlo sólo hay que hacer algo como esto:

LocalDate fNacimiento = LocalDate.of(1972, Month.MAY, 23);
System.out.println("Tu edad es de " +
  ChronoUnit.YEARS.between(fNacimiento, LocalDate.now())
  + " años."
);

La clase ChronoUnit dispone de una serie de constantes que nos permiten obtener las unidades que nos interesen (que a su vez son también objetos de la clase ChronoUnit) y que, con su método between() nos permiten obtener el intervalo que nos interese. En este caso un número que representa la cantidad de años entre la fecha de mi nacimiento y el día de hoy, o sea, mi edad.

Si quisiésemos, por ejemplo, saber cuánto tiempo falta para llegar a final de año, podemos sacar partido a la clase Period para lograrlo:

LocalDate hoy = LocalDate.now();
LocalDate finDeAnio = hoy.with(TemporalAdjusters.lastDayOfYear());
Period hastaFinDeAnio = hoy.until(finDeAnio);
int meses = hastaFinDeAnio.getMonths();
int dias = hastaFinDeAnio.getDays();
System.out.println("Faltan " + meses + " meses y " + dias + " días hasta final de año."
);

La clase Period también dispone del método estático between() para obtener el periodo entre dos elementos de tiempo, por lo que la línea 3 anterior se podría sustituir por esta:

Period hastaFinDeAnio = Period.between(hoy, finDeAnio);

y todo funcionaría igual.

Puedes conocer otros métodos de Period en la documentación oficial de Java. Verás que son sencillos de utilizar.

"Parseando" fechas

Una tarea habitual en el manejo de fechas es "parsearlas", es decir, interpretarlas a partir de una cadena, generalmente recibida de la entrada de un usuario o de algún sistema de almacenamiento externo.

Las clases de java.time, por fortuna, ofrecen el método parse() que se ocupa de esto de manera trivial. Tiene dos sobrecargas, una que recibe la cadena a interpretar y, una segunda que además añade un formateador especializado si lo necesitamos:

LocalDate hoy = LocalDate.parse("2020-07-06");
LocalDate seisNov = LocalDate.parse("6/11/2020", DateTimeFormatter.ofPattern("d/M/yyyy") );

En el primer caso se interpreta el formato por defecto, ISO 8601, el cual podría incluir la hora si usamos una clase que contemple horas también.

En el segundo caso usamos un formato propio mediante la clase DatTimeFormatter y su método ofPattern(), que es un método factoría para obtener un formateador a partir de una cadena basada en letras estándar para formato.

Formato personalizado de fechas

Del mismo modo que podemos usar la clase DateTimeFormatter para interpretar cadenas de texto y convertirlas a fechas, también le sacaremos partido para la operación contraria: convertir una clase temporal en una cadena de texto usando el formato que nos interese.

Para ello sólo tenemos que usar el método format() que todas ellas poseen y pasarle un formateador.

Este código muestra el aspecto de una fecha con su conversión a texto por defecto (ISO 8601), en ese mismo formato pero indicándolo explícitamente y en formato español (día del mes antes del mes y los elementos separados por barras) indicando ese formato manualmente:

System.out.println("Formato por defecto: " + fechaConHora);
System.out.println("Formato ISO 8601 (explícito): " + fechaConHora.format(DateTimeFormatter.ISO_DATE_TIME));
DateTimeFormatter esDateFormat = DateTimeFormatter.ofPattern("dd/MM/yyyy hh:mm:ss");
System.out.println("Formato español (manual): " + fechaConHora.format(esDateFormat));

Como vemos, existen diversos formateadores ya predefinidos en forma de constantes de DateTimeFormatter, como el que hemos usado (hay más que no se ven en la captura pero puedes consultar aquí):

Algunas constantes predefinidas para formato

En el caso del formato manual hemos empleado los símbolos de formato que indicábamos en el apartado anterior para el parsing.

Hay que tener cuidado, no obstante, con el uso que se hace de alguno de ellos. Por ejemplo, supongamos que queremos que el mes se muestre con su nombre completo y no con un número; para ello usaríamos el formato "MMMM" (las 4 M representan el nombre del mes). Por defecto ese nombre se mostraría en inglés, por lo que veríamos por pantalla, por ejemplo "July" y no "Julio", en español, que es lo que nos interesa. ¿Cómo lo solventamos? ¿Cómo conseguimos localizar ese valor?

Pues utilizando el método withLocale() de la clase DateTimeFormatter, que toma como argumento un objeto de la clase java.util.Locale.

Esta clase dispone de algunas constantes con valores predefinidos de idiomas, como el inglés, inglés americano, alemán... hasta francés canadiense, pero no español (se ve que es un idioma poco utilizado 🤦‍♂️). Por ello, para nuestro idioma tendremos que definirlo explícitamente. Pero mejor, porque así aprendemos. El siguiente código te muestra cómo conseguirlo:

DateTimeFormatter esDateFormatLargo =
    DateTimeFormatter
      .ofPattern("EEEE, dd 'de' MMMM 'de' yyyy 'a las' hh:mm:ss")
      .withLocale(new Locale("es", "ES"));
System.out.println("Formato español (largo, localizado): " + fechaConHora.format(esDateFormatLargo));

Fíjate en dos cosas:

  • En primer lugar creamos el patrón que nos interesa usando EEEE para el nombre largo del día de la semana (mira la tabla de formatos de nuevo) y "escapeamos" todos los fragmentos que no son formato, como el "de" y el "a las" usando una comilla simple.
  • Instanciamos un nuevo objeto Locale pasándole como parámetros el código ISO 639 de idioma y el código ISO 3166 de país. Así somos más explícitos (español de España, pero podría haber sido español de México o de otro país hispanohablante). Si le hubiésemos pasado tan solo el primer parámetro funcionaría con la versión más neutra del idioma (simplemente español), que en esta ocasión no tendría diferencia alguna.

En este caso veríamos por pantalla la fecha formateada de la siguiente manera: lunes, 06 de julio de 2020 a las 08:40:15, que es lo que buscábamos.

Finalmente, nos faltaría saber cómo formatear la fecha con el formato actual del usuario de la aplicación, en lugar de uno arbitrario elegido por nosotros. Para ello usaremos la misma técnica, pero antes tenemos que averiguar el idioma y país del usuario actual. (He sangrado el código un poco para ganar en claridad y no verlo todo en una sola línea en la tercera sentencia):

String idiomaLocal = System.getProperty("user.language");
String paisLocal = System.getProperty("user.country");
System.out.println("Formato actual del sistema (" + idiomaLocal + "-" + paisLocal + "): " +
  fechaConHora.format(
    DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
      .withLocale(
        new Locale(idiomaLocal, paisLocal)
      )
    )
  );

Usamos las propiedades del sistema para averiguar el código de idioma y el código de país, y luego procedemos de la misma manera que antes, con un nuevo Locale en función de estos datos.

La única cosa nueva que tenemos aquí es que, en lugar de definir el formato manualmente, hacemos uso del valor SHORT de la enumeración FormatStyle para expresar de manera genérica el formato corto de fecha y hora. En este caso no podríamos haber utilizado el formato largo (LONG) ni completo (FULL) porque necesitan la información de zona horaria, que nuestra fecha de ejemplo no tiene por ser un LocalDateTime y no un ZonedDateTime.

Ejemplo práctico

Ejemplo práctico en ejecución

¡Buff! Ha sido un recorrido largo por el proceloso mundo del manejo de datos temporales con Java, algo en lo que, en mi opinión se complica mucho, comparado con otros lenguajes como C#.

En este artículo hemos repasado la manera correcta de manejar datos de fechas y horas en Java, las clases más importante involucradas, cómo definirlos, cómo transformarlos tanto de manera numérica como con ajustadores, cómo extraer sus partes, cómo compararlas, cómo "parsearlas" y cómo formatearlas, con algunos trucos. En definitiva, cómo llevar a cabo todas las tareas comunes e importantes que necesitarás hacer con fechas y horas en Java.

Para facilitarte el estudio y la prueba de todo lo que he explicado, te dejo todo el código de ejemplo en este repl.it, que puedes usar para probar todos los ejemplos, ejecutándolos en el navegador y también clonar (con la opción "Fork") a tu propio repl.it si quieres crear variantes.

Y recuerda: si quieres aprender Java con garantías y no "de oído", con recetillas o todo prendido con alfileres, nuestro excelente y completo curso de Java te está esperando. ¿Te lo vas a perder?

Fundamentos: Cómo crear tu primera aplicación con .NET Core y C#

$
0
0

En este artículo vamos a repasar los pasos básicos para crear una sencilla aplicación con .NET Core para que puedas hacer una primera toma de contacto y comprobar lo sencillo que es empezar.

Para crear aplicaciones para .NET realmente tan solo necesitamos disponer de un compilador. Por ello, bastaría con descargarse desde Internet el .NET SDK (el Kit de desarrollo para .NET), que es gratuito y dispone de esos compiladores. Con ellos y un simple editor de texto, como el bloc de notas, ya podríamos crear nuestras aplicaciones con .NET.

Vamos a ver cómo se crea y se ejecuta una aplicación usando tan solo la línea de comandos, de modo que puedas hacerlo de manera simple y sin necesidad de herramientas adicionales (ni siquiera un editor). Podrás seguir estos pasos desde Windows, Linux o macOS, y funcionarán exactamente igual, aunque yo los voy a mostrar en Windows.

Al final del artículo se incluye un vídeo para verlo en acción en la práctica.

Antes de nada, asegúrate de tener instalado en tu sistema el SDK de .NET más reciente para tu plataforma (necesitarás permisos de administración para instalarlo):

 

Descargar .NET Core SDK para cada sistema

Si estás en Windows y tienes un Visual Studio 2019 instalado en tu sistema, ya dispones de este SDK y no necesitas instalarlo. Sólo asegúrate de tener todo actualizado a las versiones más recientes.

Al realizar la instalación del SDK de .NET se instalan también las herramientas de línea de comandos necesarias, lo que nos posibilitará llevar a cabo la creación y compilación de nuestras aplicaciones desde la línea de comandos mediante la utilidad dotnet que enseguida veremos.

Por tanto, una vez instalado podremos abrir una línea de comandos y llamar al comando dotnet --version que nos devolverá la versión actual del SDK que estamos utilizando, y sabremos que todo está bien instalado y funcionando:

La versión actual de .NET que estás usando

Por ejemplo, en mi caso estoy usando la versión 3.1.301 de .NET Core. Puedes usar este comando y todos los que veremos a continuación en PowerShell clásico, en PowerShell 7 para Windows, Linux o macOs, y por supuesto en cualquier terminal de Linux o macOs y el resultado será el mismo.

Crear un proyecto de .NET

El primer paso para crear una aplicación .NET es crear el proyecto. Para hacerlo, no es necesario ningún programa especial para crear el código de nuestra aplicación, siendo suficiente con el SDK de .NET y un editor de texto plano, por lo que lo haremos de este modo.

Para crear nuestro proyecto, bastará con realizar una llamada al comando dotnet con el parámetro new, indicando la plantilla que queremos emplear.

Pero, en primer lugar, veamos las plantillas que tenemos instaladas en nuestro sistema ejecutando dotnet new sin ningún parámetro más, lo que nos devolverá algo similar a lo que se ve a continuación:

Lista de plantillas disponibles

Si te fijas, podrás comprobar que tienes plantillas disponibles para crear multitud de tipos de proyectos, desde proyectos ASP.NET para desarrollo web, a proyectos de consola o aplicaciones WinForms entre otras muchas posibilidades.

Para continuar con nuestro ejemplo, vamos a generar un proyecto de tipo consola y veremos qué es lo que nos crea dentro de la carpeta del proyecto.

Según la plantilla que vayamos a utilizar, existen varias opciones y parámetros que se pueden definir al crear un proyecto, que dependen en cierto modo del proyecto en sí. Dichos parámetros pueden ser, desde cosas comunes como la carpeta del proyecto, hasta parámetros muy particulares como podría ser el tipo de autenticación a utilizar si se trata de un proyecto Web. No obstante, si no se indica nada, el sistema les dará los valores por defecto establecidos.

Si creamos un proyecto con valores por defecto, bastará llamar a dotnet new [NombrePlantilla] para crearlo, utilizando el nombre corto de la plantilla que nos interese, sacado de la lista anterior.

Así que, muévete en tu terminal a alguna carpeta vacía de tu disco duro y escribe:

dotnet new console

En la siguiente imagen se puede ver cómo se crea el proyecto y los archivos y carpetas que contiene:

Nota: En Linux o macOS el comando para listar el contenido de la carpeta sería ls y no dir.

Esta plantilla de proyecto de consola, crea 2 ficheros y una carpeta:

  • AppConsole.csproj: archivo con la definición del proyecto.
  • Program.cs: fichero de texto con código C# en su interior. Contiene el método de entrada en el programa, y por defecto lo único que hace es imprimir por pantalla el texto: "Hello World!", un clásico de toda la vida 😉
  • obj: carpeta con los objetos de compilación. Es una carpeta que utiliza .NET para llevar a cabo la compilación del programa.

No obstante, podríamos haber añadido parámetros que especificasen opciones a tener en cuenta a la hora de crear el proyecto. En el ejemplo anterior hemos creado la carpeta del proyecto y desde dentro hemos ejecutado el comando. El sistema ha tomado como uno de los valores por defecto el nombre del proyecto, que lo ha hecho coincidir con la carpeta en la que nos encontramos. Sin embargo, podríamos haber modificado este comportamiento mediante el parámetro -o u --output (son equivalentes), de forma que dotnet crearía la carpeta con el nombre indicado a continuación de éste, y usaría ese nombre tanto para la carpeta como para el proyecto.

Ejecutando nuestro proyecto

De forma similar a la creación del proyecto tenemos disponible la opción de compilar y/o ejecutar nuestros proyectos con ayuda de dotnet.

En el caso de nuestra aplicación de consola, tenemos dos opciones de ejecutarla.

  1. dotnet run desde la carpeta del proyecto o dotnet run --project <ruta_al_csproj>, que realiza la compilación y ejecución inmediata del proyecto.

    A continuación puedes ver la ejecución de ambos modos:

    El programa Hola Mundo ejecutado desde fuera de la carpeta y desde dentro, más sencillo

  2. dotnet build desde la carpeta del proyecto o dotnet build <ruta_al_csproj>: únicamente compila el proyecto. Posteriormente podríamos ejecutar el fichero compilado o llevarlo a otra computadora para ejecutarlo.

    En la siguiente imagen puedes ver cómo sería el resultado al compilar y luego ejecutar el .exe directamente desde la carpeta en la que se genera:

    El proyecto compilado a un ejecutable y ejecutado con el .exe

¡Ya has creado y ejecutado tu primera app de .NET Core!

Eso no es todo

La idea de este artículo era tan solo "romper el hielo" y ver cómo crear la aplicación más simple posible con .NET Core, desde la línea de comandos. Pero lógicamente no es lo único que se puede hacer, y a medida que aprendas más sobre la plataforma será necesario que utilices el comando dotnet para más cosas.

Alguna de las principales funciones incluidas de serie que se pueden llevar a cabo con dotnet son:

ComandoUso
newPermite crear un nuevo proyecto
restoreLleva a cabo la restauración de las dependencias
buildCompila el proyecto
publishCopia a una carpeta el resultado de la compilación del proyecto, lista para distribuir
runCompila y ejecuta el proyecto
testEjecuta las pruebas unitarias especificadas en el proyecto
packCrea un paquete NuGet para poder publicarlo
nugetPermite gestionar la publicación del paquete NuGet en un servidor
cleanLimpia las carpetas de compilación del proyecto
addPermite añadir paquetes NuGet y referencias al proyecto
removePermite eliminar paquetes NuGet y referencias al proyecto

 

Del mismo modo existen comandos que tienen más niveles de profundidad, como puede ser nuget, al que tendríamos que indicar si queremos realizar una inserción en el servidor o un borrado. Lo mismo ocurre con new, que además de crear proyectos nos permite controlar las plantillas: añadirlas y eliminarlas, etc...

La herramienta dotnet puede ampliarse dotándola de mayor funcionalidad mediante el modelo de extensibilidad del que dispone. Dicho de otra forma, es posible aumentar la cantidad de comandos disponibles en dotnet bien, a nivel global mediante la adición de comandos en el sistema, o bien a nivel de proyecto mediante paquetes NuGet. Por ejemplo, para llevar a cabo la gestión de Entity Framework Core o EFCore, se nos añade el parámetro ef, que nos permite hacer cosas como dotnet ef migrations list, que muestra la lista de migraciones de Entity Framework (pero esto es un tema específico que no viene a cuento ahora).

Vídeo práctico

A continuación te dejo un vídeo práctico en el que te explico cómo funciona el comando dotnet y vemos en acción todas las cosas que he comentado por escrito en el artículo:

[youtube:AR7UnHI_2Xg]

Si te interesa la plataforma .NET y el lenguaje C#, tenemos el curso que necesitas: Desarrollo con la plataforma .NET Core 3 y C# 8.

RRHH: Cómo gestionar a programadores en prácticas en tu empresa

$
0
0

Imagen ornamental

Hay empresas y departamentos de recursos humanos que por política no contratan a programadores en prácticas. El principal motivo suele ser que no saben gestionar a este tipo de perfiles (o no han sabido en el pasado), y no quieren volver a tener que vivir este tipo de experiencias, que en muchos casos derivan en situaciones poco agradables para las personas implicadas. Este artículo va dirigido a esas empresas.

Desde mi punto de vista es un error no darles la oportunidad a los programadores en prácticas siempre y cuando se sepa gestionar dentro de la empresa a este perfil de trabajador.

Si la empresa necesita personas que entren y empiecen a "producir" desde el primer día, evidentemente no deberían contratar a programadores en prácticas con este objetivo.

Ahora bien, si la empresa desde recursos humanos tiene un plan a medio plazo para desarrollar el talento y obtener un retorno a 2 años vista, tiene que aprender a gestionar este tipo de perfiles. Y, evidentemente, enseñar a alguien a trabajar no es fácil -ni agradable- en todos los casos y hay que estar preparados.

¿Qué es un programador con un contrato en prácticas?

Antes de entrar en el fondo del artículo, me gustaría aclarar que en España para poder acceder a un contrato en prácticas como programador hay que estar en posesión de un título universitario o de formación profesional de grado medio o superior, de títulos oficialmente reconocidos como equivalentes o de un certificado de profesionalidad específico.

La duración mínima del contrato será de 6 meses y máxima de 2 años, aunque dentro de estos límites y a través de Convenio Colectivo podrán establecerse distintas duraciones del contrato.

No existe un límite de edad. El único requisito fundamental es que el programador no haya finalizado su titulación hace más de 5 años si éste tiene más de 30 años, o 7 años si posee alguna discapacidad. Si es menor de 30 años, no se aplica ninguno de estos plazos.

La retribución del programador será la fijada en convenio colectivo, sin que, en su defecto, pueda ser inferior al 60% o al 75% durante el primer o el segundo año de vigencia del contrato, respectivamente. En ningún caso el salario será inferior al salario mínimo interprofesional.

No sé cómo se regula esta figura en otros países, pero, en mi opinión, cualquier persona con una titulación o formación acreditada y un contrato debería recibir siempre una remuneración digna a cambio de su trabajo. Trabajar gratis o mal pagado cuando se realiza un trabajo válido no es trabajar, es otra cosa: formación, un favor personal o, directamente, explotación laboral...

IMPORTANTE: existen otras figuras parecidas, como las prácticas no remuneradas, que forman parte del proceso formativo de los programadores, pero en concreto en este artículo me refiero a los programadores con un contrato en prácticas.

Es, en definitiva, un proceso de gestión de expectativas y de evaluación continua. Ni más, ni menos.

¿Qué esperar de un programador en prácticas?

La gestión de las expectativas es clave a la hora de trabajar con programadores en prácticas:

  • ¿Qué espera la empresa del programador?
  • ¿Qué espera el programador de la empresa?
  • ¿Son realistas las expectativas de ambas partes?
  • ¿Se acuerdan por escrito en un plan de desarrollo profesional?
  • ¿Se orientan o traducen dichas expectativas a objetivos o a tareas concretas medibles?
  • ¿Se hace un seguimiento periódico semanalmente de los progresos?
  • ....

Suena a mucho trabajo porque lo es y, además, sin retorno en el corto plazo.

El objetivo principal de los programadores en prácticas remuneradas es ofrecer al programador experiencia a cambio de la realización de una serie de tareas.

Al principio de este tipo de relaciones laborales es importante evaluar qué es capaz de hacer cada persona en ese momento, y tomarlo como punto de partida o "referencia base" para trabajar a partir de ahí.

El objetivo a corto plazo es ver que cada día que pasa estén más capacitadas que cuando llegaron. Con cada tarea que completan, hay que reevaluar sus habilidades actuales y ampliarlas esperando siempre un poco más, subiendo el listón.

Por supuesto, existe un estándar mínimo en función del trabajo que van a hacer, que es para lo que sirven las entrevistas. Se debe contratar a programadores en prácticas que tengan las capacidades básicas para empezar a trabajar y capacidad de aprender lo que no saben.

Aquí incluyo una pequeña lista de las cualidades que son de esperar de un programador en prácticas (y de cualquier persona con las que me gustaría trabajar, la verdad) al empezar:

  • Voluntad y la capacidad de aprender.
  • Conocimiento básico del tipo de trabajo que harán (por ejemplo, "Tendré que escribir código en C# que utilice Internet para enviar mensajes, lo que significa que necesito saber/aprender C# así como los protocolos de mensajería pertinentes").
  • Que tenga iniciativa propia, y proactividad.
  • Que en realidad quiera estar haciendo el trabajo: que se toma el trabajo en serio.

La selección de los candidatos

Para la empresa, la selección de este tipo de perfiles se debe tomar tan en serio como la de los demás puestos "permanentes". Como empresa no se puede llegar a la conclusión de que los programadores en prácticas no funcionan si no tomamos a los candidatos en serio desde el principio, y eso incluye la selección.

Conozco a muchas empresas que contratan en prácticas a los hijos de un conocido o de un cliente, por quedar bien y porque en realidad no tienen ningún tipo de expectativa fijada en el candidato en cuestión.

Meter a una persona en la empresa siempre tiene un impacto en la organización y en la cuenta de resultados. Mejor tomárselo en serio y dar y exigir cosas.

Si saben en lo que se están metiendo y están dispuestos a aprender y mejorar, entonces puedes esperar básicamente cualquier cosa de ellos con el tiempo y la práctica.

Hacerlo así no garantiza que la experiencia vaya a salir bien, pero si minimiza la posibilidad que salgan las cosas mal.

Plan formativo y objetivos

Por un lado, los objetivos de los programadores en prácticas deben centrarse más en la realización de tareas que en ratios o porcentajes. Y por otro, dentro de los objetivos también deberían poder realizar un plan formativo que le interese a la empresa en un plazo determinado.

En campusMVP tenemos cursos para programadores de calidad que son perfectos para trabajar y estudiar a la vez.

Si quieres contar tu experiencia como programador en prácticas remunerado o como empresa, no dudes en compartirla en la sección de comentarios.

 


El operador de "unión nulosa" o "nullish coalescing" de ECMAScript: parámetros opcionales y valores por defecto en funciones

$
0
0

Imagen ornamental de la portada

Una necesidad muy habitual a la hora de programar es la de obtener valores por defecto para los parámetros de las funciones.

Al contrario que en otros lenguajes como C# o Java, en JavaScript no hay manera de forzar la definición de una función y sus parámetros. Podemos definir una función con tantos parámetros como deseemos, pero eso no significa que luego los otros programadores nos los vayan a pasar siempre.

Por ejemplo, en C# defines una función tan simple como esta:

int Sumar(int a, int b)
{
    return a+b;
}

Que suma dos enteros y siempre tienes que pasarle dos enteros para que funcione. Es decir, intentar una llamada a Sumar(5) daría un error inmediato, sin posibilidad ni de compilar.

Esto parece una obviedad, pero en JavaScript eso no es así en absoluto. Si defino la misma función en este lenguaje:

function sumar(a, b) {
  return a+b;
}

Podría llamarla de la manera obvia: sumar(5,4), pero también podría hacer: sumar(5) o incluso sumar() sin pasarle parámetro alguno. Y funcionaría en todos los casos, sólo que si no le paso algún valor obtendría un NaN (un resultado que no es un número).

Y es que en JavaScript todos los parámetros de una función son opcionales.

En otros lenguajes podemos prever esta necesidad especificando el valor que queremos otorgarle a un parámetro opcional cuando no se pasa. Por ejemplo, en el caso de C# la función anterior podríamos definirla así:

int Sumar(int a = 0, int b = 0)
{
    return a+b;
}

Y ahora sí, podría llamarla como quisiera: pasándole dos, uno o ningún parámetro. En caso de que falte alguno, su valor sería el especificado por defecto, en este caso un 0.

Pero en JavaScript no tenemos nada similar a esto en la sintaxis. Aunque eso no significa que no se pueda hacer.

Simulando valores por defecto para parámetros opcionales en JavaScript "clásico"

En JavaScript podemos sacar partido de los valores "verdadosos" y "falsosos" (truly y falsy) generados por conversiones implícitas a booleanos (ver el enlace para detalles), para así especificar valores por defecto para parámetros de funciones en caso de que falten.

Como seguramente sabrás, el operador lógico OR (||) se evalúa con cortocircuito de expresiones lógicas de la siguiente manera:

  • Si el primer operando de la comparación es true devuelve ese mismo primer operando.
  • Si el primer operando es false, entonces se devuelve automáticamente el segundo operando.

Sacando partido a esto es muy fácil definir valores por defecto para los parámetros sin tener que escribir condicionales ni código largo que "embarre" la definición de nuestra función.

Por ejemplo, si tenemos una función test que toma dos parámetros, a y b, y queremos asegurarnos de que ambos tienen sendos valores por defecto, aunque no se hayan especificado, podemos definirla así:

function sumar(a, b) {
  a = a || 0;
  b = b || 0;
  return a+b;
}

Por el efecto que acabo de explicar, lo que se consigue con la primera línea del cuerpo de la función, es que si no se le pasa a la misma el parámetro a (es decir, se recibe un undefined) se le asignará automáticamente un 0 como valor por defecto. Y lo mismo con b. Al evaluarse el operador OR (||) el primer parámetro se convierte en un booleano. Si no está definido esto es equivalente a un "falsoso" (o sea, se convierte implícitamente en un false) y por lo tanto se asigna en la propia variable el segundo operando del OR (el valor por defecto).

Si se le pasa una cadena o un objeto de cualquier tipo se evaluará como true y por lo tanto se devolverá el propio objeto (se reasignará a sí mismo).

Pero esto tiene algunos fallos. Por ejemplo, si el parámetro que esperábamos es un booleano y queremos que el valor por defecto sea true, si usamos esta técnica, cuando se le pasase un false como valor para el parámetro, el efecto que obtendríamos es que se cambiaría su valor a true y, en la práctica, no lograríamos nunca pasarle un false como valor efectivo. Y si esperásemos una cadena de texto y nos valiesen también cadenas de texto vacías, al hacer algo como esto:

function test(p) {
  p = p || 'Hola';
  return p;
}
console.log(test(''));

veríamos por la consola la cadena 'Hola', y no una cadena vacía, como quizá podríamos haber pensado. El motivo es que las cadenas vacías al forzar su conversión a booleano (con el ||) se interpretan como "falsosas", o sea, con false, por lo que nunca podríamos recibir de este modo una cadena vacía. Lo mismo ocurre, por ejemplo, con el número 0 y otros valores "falsosos".

Existen, por supuesto, formas de lograr lo mismo en estas situaciones, pero añaden complejidad a algo que debería ser más sencillo.

Cómo hacerlo bien con ECMAScript

El resumen de lo anterior es que, con JavaScript "clásico" (ECMAScript 5) es posible definir parámetros opcionales con valores por defecto, pero como no tengamos cuidado podemos meter la pata bien a fondo.

Por suerte, en ECMAScript hay una manera mucho mejor de lograrlo: el operador de unión nulosa, más conocido por su extraño nombre en inglés operador nullish coalescing.

Este operador se representa por una doble interrogación ?? y sirve precisamente para lograr de manera sencilla y directa lo que acabamos de describir: ofrecer un valor por defecto cuando un elemento al que necesitamos acceder no existe, es nulo o está sin definir (undefined).

Este operador lleva existiendo en otros lenguajes desde hace mucho tiempo (en C#, por ejemplo, desde hace 15 años al menos), pero en JavaScript/ECMAScript es una propuesta que hace poco tiempo ha pasado a fase 4 y se ha adoptado por todos los navegadores modernos "evergreen".

Su uso es muy sencillo: se pone a continuación de un valor que creamos que puede ser nulo o no definido y, en caso de que lo sea, devolverá lo que pongamos a su derecha, es decir, esto: var resultado = valor ?? valorXDefecto.

Con él, nuestra función de suma con parámetros opcionales quedaría así:

function sumar(a, b) {
  a = a ?? 0;
  b = b ?? 0;
  return a+b;
}

En este caso siempre va a funcionar bien, no como ocurría con la técnica convencional, que debemos vigilar mucho más de cerca.

Lo importante a considerar aquí es que el valor a la izquierda del operador ?? se comprueba para ver si es null o undefined, o lo que es lo mismo: "nuloso" (nullish). Así que actúa como el operador || pero comprobando si es "nuloso", y no "falsoso", por lo que estas expresiones que con || son problemáticas:

var res = false ?? true; // --> false
res = '' ?? 'Hola'; // --> ''
res = 0 ?? 1; // --> 0

no lo son para el operador de nullish coalescing.

Paréntesis obligatorios

¿Qué ocurre si combinas este operador ?? con otros operadores de tipo lógico? Por ejemplo, ¿cuál sería el resultado de esto?:

var a = 0;
var b = false;
var res = a || b ?? true;

El resultado es que se produce un error: Unexpected token ??:

El error que se produce

El motivo es que, al combinar este tipo de operadores, hagas lo que hagas con la precedencia no puedes evitar que la interpretación que hace el programador de lo que está escribiendo entre en conflicto con lo que el diseñador del lenguaje haya decidido. Por ejemplo, en el fragmento anterior, eso se puede interpretar como:

  • (a || b) ?? true
  • a || (b ?? true)

y en ambos casos tiene sentido.

Así que los diseñadores de esta característica decidieron que no puede utilizarse con || o && salvo que especifiquemos claramente la precedencia mediante paréntesis. De este modo se fuerza a que el programa tome la decisión sobre cómo interpretarlo y además facilita su lectura inequívoca por parte de cualquiera.

Cortocircuito de expresiones

Un último detalle sobre el operador: al igual que sus "hermanos" lógicos, implementa cortocircuito de expresiones. Esto quiere decir que si el elemento a la izquierda no es "nullish" y por lo tanto no se va a devolver el valor por defecto, nunca se llega a evaluar el elemento de la derecha:

var res = "algo" ?? funcionValorPorDefecto();

En este ejemplo, como el operando de la izquierda no es null ni undefined (es una cadena) y por lo tanto no se va a devolver el valor por defecto de la derecha (tras el ??), la llamada a la función no se producirá porque no es necesaria. Así que si cuentas con que se debe llamar siempre a esta función (por ejemplo para inicializar algo), tendrías un problema.. Tenlo en cuenta.

Además esto tiene una aplicación interesante: poder generar una excepción en caso de que falte un parámetro indispensable y al que no se le pueda dar un valor predeterminado:

function transformaObjeto(obj) {
	obj = obj ?? function() {throw 'Debes pasar un objeto!!'}();
  //Resto del código
  console.log(obj);
}

En este caso, si no se le pasa un objeto a la función se produce una excepción que podemos capturar. En caso de pasar un objeto se ejecutaría el código de la función.

Esta función anónima, obviamente, podría ser una función cualquiera a la que se llamaría, y de hecho podría también devolver un valor, el cual se asociaría a la variable obj para asignarle un valor predeterminado siguiendo reglas tan complejas como necesitásemos, encapsuladas en esa función a la que llamamos tras el ??.

 

En resumen

El operador doble interrogación, conocido como de nullish coalescing o de "unión nulosa", es muy simple, pero al mismo tiempo un gran añadido al arsenal de herramientas del programador JavaScript. En especial, a la hora de lidiar con argumentos de funciones, ya que en JavaScript éstos siempre son opcionales, pero sin poder especificar un valor por defecto.

Aunque oficialmente forma parte de ECMAScript 2020, aparecido este mismo mes de junio, está soportado hace ya meses por la práctica totalidad de los navegadores modernos excepto Internet Explorer y en concreto lo soportan:

  • Chrome 80+ y todos los que usan Chromium por debajo (Opera, Microsoft Edge, Brave, Vivaldi...)
  • Firefox 72+
  • Safari 13.1+ para macOS
  • Safari 13.5+ para iOS
  • Chrome 84+ para Android
  • WebView de Android 81+

Así que puedes usarlo con bastante seguridad de que no tendrás problemas, salvo en entornos corporativos o donde no te puedas permitir el lujo de prescindir de usuarios de IE o de otros navegadores de móvil como el de Samsung, el de Xiaomi y similares (aunque probablemente lo incorporen pronto).

¡Espero que te resulte útil!

Java: Introducción a los tipos genéricos (vídeo)

$
0
0

Además del polimorfismo, una característica que permite a la plataforma Java tratar homogéneamente objetos heterogéneos, de los que habitualmente no se conoce su tipo concreto, el lenguaje Java cuenta con otro mecanismo con el mismo fin: los tipos genéricos.

Al definir una clase genérica, o bien una interfaz, en realidad estamos definiendo un meta-tipo, una especie de plantilla a partir de la cual se crearán posteriormente clases/interfaces que actuarán como tipos concretos

El objetivo principal de los tipos genéricos es evitar que tengamos que utilizar la clase Object como tipo para ciertos atributos, parámetros o valores de retorno, como se hacía tradicionalmente en las primeras versiones de Java, al ser Object el ascendiente común de todos los tipos por referencia y, por tanto, un mínimo común denominador. En el siguiente vídeo, nuestro tutor Francisco Charte te ayuda a conocer los aspectos esenciales sobre la definición de clases/interfaces con tipos genéricos, así como la posterior instanciación de tipos concretos. También comprobarás la utilidad de esta funcionalidad de Java:

[youtube:SQSPIb8zvdA]

Transcripción del vídeo

 

En este vídeo vamos a conocer la utilidad de los tipos genéricos de Java definiendo una clase de tipo genérico y viendo cómo podemos utilizarla con diferentes tipos concretos.

Los tipos genéricos de Java son similares, en cuanto a funcionalidad se refiere, a las plantillas de C++ y lenguajes similares. El objetivo es definir una clase, una interfaz o un método cuyos tipos de datos son parametrizables. Los tipos genéricos están estrechamente vinculados a las estructuras de datos clásicas como pueden ser las colas y las listas, etc.

En lugar de definir una clase "Pila" para trabajar con números enteros, otra para trabajar con números en punto flotante, una tercera para trabajar con cadenas y así sucesivamente, es mucho más sencillo definir una clase que sea capaz de tratar con datos de diferentes tipos. En Java esto inicialmente implicaba utilizar el tipo Object, pero esto conlleva una serie de problemas.

Al definir los atributos de una clase como Object es necesario realizar posteriormente conversiones a los tipos concretos que el usuario desea utilizar. Asimismo, es un peligro, puesto que sería potencialmente posible introducir datos de diferentes tipos dentro de esa estructura de datos.

Con los tipos genéricos estos inconvenientes se evitan, puesto que el tipo concreto de dato con el que va a trabajar es un parámetro de entrada más.

Vamos a utilizar, como es habitual, Netbeans y vamos a crear un nuevo proyecto que contará inicialmente con una clase "Plano". El objetivo de esta clase será representar planos cuyas coordenadas podrán ser de diferentes tipos. Podrán ser números enteros de diferentes tamaños o números en punto flotante.

Lo primero que vamos a hacer en esta clase es agregar detrás del nombre de la clase el parámetro T que es el que va a actuar como tipo de dato parametrizable. En lugar de T podríamos asignarle cualquier otro nombre (otro identificador), pero es habitual utilizar esta notación. Completamos la documentación asociada a la clase y, dentro de la definición de la clase, ya podemos utilizar este tipo parametrizable T. Por ejemplo, para definir los atributos con los que va a contar cada uno de los planos, vamos a tener un mínimo y un máximo para el eje X, y un mínimo y un máximo para el eje Y. Observa que el tipo de estos cuatro atributos, de estas variables, es T. No conocemos en este momento cuál va a ser el tipo concreto.

De igual forma, al definir el constructor de esta clase, definimos la lista de parámetros utilizando este mismo tipo y guardamos los datos recibidos en los correspondientes atributos.

Análogamente podemos implementar el habitual método toString() para mostrar el plano, las coordenadas del plano, por la consola y también implementamos los getters y setters, que todos ellos utilizarían el tipo T como parámetro.

Vamos a comprobar cómo podríamos crear objetos de esta clase plano especificando un tipo concreto para este parámetro T.

Nos vamos a nuestra clase principal donde tenemos el habitual método en el que vamos a introducir el código.

Vamos a comenzar declarando una variable Plano<Integer>.

Fíjate que al especificar el tipo ya entre los símbolos menor y mayor, especificamos cuál será el tipo concreto.

Este ha de ser necesariamente un tipo por referencia.

No podemos utilizar tipos primitivos de Java.

Una vez hemos declarado la variable podemos crear un objeto de esa clase y especificar los parámetros.

El objeto podemos mostrarlo por consola.

Si ejecutamos la aplicación aquí podemos comprobar que tenemos un plano con coordenadas enteras.

Análogamente podríamos definir un segundo plano cuyas coordenadas sean de un tipo numérico en punto flotante: Plano<Double> Aquí especificamos cuáles son sus coordenadas.

Suponemos que es un plano virtual con centro en (0,0) y que va de -1 a +1 en los dos ejes.

Mostramos también que este plano por consola, y aquí tenemos las coordenadas.

Partiendo de una única definición para la clase Plano de tipo genérico, estamos creando objetos de una clase Plano que trabaja con enteros y de una clase Plano que trabaja con números en punto flotante.

De hecho, este sistema es tan flexible que podríamos llegar a introducir cualquier tipo de dato como coordenada.

Por ejemplo, vamos a crear una variable de la clase Plano especificando como tipo concreto TiposGenericos.

Observa qué TiposGenericos es esta misma clase.

No es un tipo numérico que podamos utilizar para establecer las coordenadas de un plano pero esto, la clase que hemos definido, Plano, no lo sabe, con lo cual nos permite declarar la variable y a la hora de crear el objeto observa que en este punto no he especificado el tipo.

Podría introducirlo pero no es necesario.

La notación "diamond" que así se denomina en Java, infiere el tipo a partir del tipo de la variable.

Como decía, aquí creamos una variable de tipo Plano<TiposGenericos> y puesto que el constructor espera cuatro parámetros de este tipo TiposGenericos lo que hacemos es crear cuatro instancias de esta clase completa y las facilitamos como parámetro.

No hay ningún problema.

Si ejecutamos el programa veremos cómo funciona.

Lo que ocurre es que las coordenadas de este hipotético plano son objetos que no tienen un sentido para esta funcionalidad concreta.

Para evitar este problema, lo que podemos hacer es establecer una restricción en la definición de la clase Plano, del tipo genérico Plano, especificando que este tipo que hemos indicado aquí, T, ha de ser necesariamente un subtipo de la clase Number, que es la clase que actúa como superclase de todos los tipos numéricos por referencia de Java, incluyendo Integer, Long y Double.

Una vez que hemos introducido esta modificación, si volvemos al código del proyecto podemos comprobar cómo esta sentencia directamente ya genera un error.

No podemos ejecutar el programa.

Vemos que ese error lo impide de tal forma que la única manera de crear variables de tipo Plano sea especificando alguno de los tipos numéricos que ya conocemos.

En resumen, lo que nos permiten los tipos genéricos es facilitar una sola definición de una funcionalidad que posteriormente se aplicaría a diferentes tipos concretos, lo cual en definitiva nos ahorra mucho trabajo de codificación.

Cómo contratar programadores en tiempos de teletrabajo (y pandemia)

$
0
0

Persona usando un ordenador en una azotea, por Avi Richards, CC0

En la época poscovid en la que vivimos es importante contar con trabajadores que sepan trabajar en remoto, porque no todos saben, ni están capacitados para ello por mil motivos diferentes.

En este artículo hablaremos de la selección de candidatos que vayan a trabajar por cuenta ajena (contratados) para la empresa, no de outsourcing ni de selección de personal freelance. A diferencia de los desarrolladores freelance, los que trabajan a tiempo completo tienen una mayor participación en la empresa. Sus salarios están, de alguna manera, ligados al éxito del producto que están desarrollando y, este es un factor clave para contratar teletrabajadores.

Es cierto que llevamos ya más de 10 años hablando del tema del teletrabajo y que se considera una práctica muy común en la industria de la programación. Sin embargo, quizá no esté tan extendida como podría parecer, al menos en España, como hemos podido comprobar en este tiempo de reclusión forzosa que nos ha impuesto el coronavirus.

Teletrabajar no es simplemente conectarse desde casa o desde la casa de veraneo, es mucho más que todo eso y requiere unas cualidades y una formación previa para que sea realmente una forma rentable de trabajar.

Contratar a un desarrollador en plantilla para trabajar en remoto el 100% del tiempo es una tarea que requiere mucho tiempo y energía, más que una contratación convencional para trabajo presencial. Encontrar el perfil adecuado es un proceso que, si se ejecuta de manera ineficiente, puede costarte a ti y a tu organización mucho tiempo y dinero. Es necesario elaborar una definición del puesto de trabajo, seleccionar a muchos candidatos, hacer un montón de llamadas telefónicas y videoconferencias, aprender más sobre cada uno de ellos y, finalmente, ponerlos a prueba.

Como con cualquier otro trabajador, cuando finalmente contrates a un desarrollador para teletrabajar, no sabrás con certeza si esta persona es la elección idónea hasta después de que empiece a trabajar. Pero en el caso de los trabajadores remotos puede ser más complicado saberlo.

Si eres gerente de la empresa o trabajas en un departamento de recursos humanos, responsable de formar un equipo de desarrolladores y puedes identificarte con esta problemática y deseas hacerlo lo mejor posible, sigue leyendo, este artículo es para ti.

Si estás buscando contratar a desarrolladores para que hagan teletrabajo, probablemente tengas algunas preguntas del tipo:

  • ¿Dónde encuentro buenos desarrolladores?
  • ¿Cómo atraigo el talento adecuado a mi oferta de trabajo?
  • ¿Cómo puedo determinar si son una buena elección?
  • ¿Qué cualidades y habilidades deberían tener?

Todas estas cuestiones se abordarán en este artículo y, con suerte, al final, deberías sentir más confianza en tu habilidad de contratar el talento adecuado para tu empresa o departamento de desarrollo de software.

El proceso de contratación de un programador

Para la mayoría de las empresas, el proceso de contratación de desarrolladores en remoto seguiría los siguientes pasos:

1.- Publicar una oferta de trabajo precisa y atractiva

Aunque esto puede parecer una obviedad, ya que debería ser requisito indispensable para cualquier tipo de oferta de trabajo que se publique, resulta especialmente importante cuando se buscan programadores de forma remota. Para que el proceso resulte eficaz, se deben describir con el mayor detalle posible las responsabilidades y las expectativas asociadas al puesto.

Una forma de hacer más atractiva la oferta es incluir un pequeño vídeo en la misma. De hecho, según Carreer Builder las ofertas de trabajo que incluyen vídeo se ven un un 12% más y reciben un 34% más de solicitudes que los anuncios de empleo basados en texto e imágenes.

El vídeo puede estar formado por distintos cortes que hayan sido grabados por los miembros del equipo de TI (futuros compañeros del candidato) y por el responsable de recursos humanos. Los primeros pueden hablar sobre los tipos de proyectos que se llevan a cabo en la empresa y la persona de recursos humanos, se centrará en comentar algún punto relevante sobre la cultura de la organización. De esta forma, además, se involucra al equipo en el proceso de contratación y se potencia la imagen de marca de la empresa.

2.- Recibir y revisar las solicitudes

Identificar a los candidatos correctos de un gran grupo de solicitantes es la parte más difícil de la contratación, sin embargo, un proceso sólido de preselección puede marcar una gran diferencia.

Con criterios bien definidos que te permitan clasificar a los candidatos, podrás acelerar el proceso de selección. Lo primero que debes hacer es identificar dichos criterios. En función de la complejidad del puesto, a lo mejor te compensa hacer un cuadro de mandos para que te resulte más sencillo evaluar y comparar distintas candidaturas.

Si el número de candidaturas es muy elevado, ayúdate de killer questions para una primera criba. Por ejemplo, haz preguntas relacionadas con conocimientos en ciertos lenguajes de programación, o pregunta sobre años de experiencia. En función de sus respuestas serán automáticamente descartados porque:

  • Las habilidades no coinciden (se necesita un desarrollador de .NET con experiencia pero lo que dominan es Java: si bien pueden tener una buena base, no es lo que buscas).
  • Los años de experiencia no coinciden (necesitas a alguien con 4-5 años pero tienen unos meses).
  • Viven en una zona horaria totalmente incompatible con la tuya cuando expresamente se pide lo contrario.

Algunos desarrolladores enviarán su currículum con la esperanza de conseguir una entrevista, sin que les importe lo más mínimo lo que se está demandando. A menudo esto se hace a expensas de no prestar atención al puesto que se ofrece. Como resultado, su CV no se adapta al puesto, y puedes dar por sentado que no se preocupan lo suficiente por tu proyecto. Estos solicitantes no valen tu tiempo.

También existen herramientas digitales que permiten hacer todo tipo de evaluaciones desde cognitivas y técnicas, hasta de personalidad para comprobar si el candidato encaja con la cultura de la empresa.

La carta de presentación también es una buena herramienta de preselección, aunque aquí ya debes invertir más tiempo, pues esta parte del proceso no es automatizable como las otras dos.

Lo positivo, es que las cartas son muy reveladoras, tanto para lo bueno como para lo malo, por lo que también te pueden ayudar en tu selección inicial.

Un currículo sólo debe ser usado para medir si el candidato cumple con ciertas habilidades mínimas indispensables, con los años de experiencia y, si acaso, con requisitos educativos o de entrenamiento. Las cartas de presentación, si un candidato presenta una, pueden dar una idea de la personalidad del candidato. Una buena carta de presentación es genuina, muestra una buena capacidad de escritura y es agradable de leer.

Tras la selección inicial viene la parte más tediosa del proceso que consiste en leerse todos aquellos CV que no han sido automáticamente descartados.

Al mismo tiempo, no debemos olvidarnos de ponernos en contacto con todas aquellas personas que no pasarán a la siguiente fase. Este es uno de los puntos más importantes en todo proceso de selección y son pocas las empresas que lo hacen bien, demostrando una gran falta de profesionalidad por su parte.

Ponte en contacto con todos aquellos que no pasen a la siguiente fase.

3.- Seleccionar a los candidatos adecuados

Para poder llevar a cabo una buena selección es importante entender cómo es el candidato ideal tanto sobre el papel como en la práctica.

Contratar al desarrollador inadecuado para teletrabajar no sólo puede costarte tiempo y dinero, sino que también puede causar un daño significativo a la empresa y a su reputación.

Entonces, ¿cómo es un buen programador para trabajar en remoto? Además de identificar las cualidades tradicionales y la experiencia profesional, también deberías indagar en las habilidades sociales directamente relacionadas con el trabajo a distancia:

  • Disciplina: el trabajo a distancia implica automáticamente un alto nivel de autoorganización y autonomía por parte de los empleados. Hay que buscar desarrolladores de software que estén lo suficientemente motivados para mantenerse productivos sin que su responsable les esté controlando su trabajo todo el tiempo.
  • Comunicación: la clave de un gran desarrollador está en su habilidad para comunicar sus ideas, tanto verbalmente como por escrito con su equipo.
  • Iniciativa: dada la naturaleza del trabajo a distancia, los desarrolladores que muestran iniciativa cuando trabajan en un proyecto son los más deseables. Estos tienden a ser desarrolladores de nivel sénior que ya han vivido todo tipo de situaciones y han hecho todo tipo de cosas.
  • Equilibrio: el teletrabajo ofrece la ventaja singular de permitir a los desarrolladores conciliar sus vidas de manera que puedan ser más productivos. Aunque puede parecer agradable contratar al programador que solo se dedica exclusivamente a la programación, probablemente es mejor buscar a alguien que tenga hobbies e intereses fuera del trabajo. El síndrome de burnout puede impactar negativamente en la empresa.
  • Madurez: un buen programador es lo suficientemente maduro para proporcionar a su jefe una estimación de tiempo fiable en relación con una tarea o proyecto. Desarrollar software no es fácil, e incluso las tareas "simples" pueden acarrear errores inesperados y otros retrasos. Uno de los rasgos de un buen desarrollador es ser previsor y tener en cuenta las eventualidades más comunes.

Es fácil quedarse enfrascado en la evaluación de la habilidad técnica de un desarrollador, y aunque es importante asegurarse de que el candidato pueda resolver todo tipo de problemas con su código, las habilidades blandas determinarán el valor de un desarrollador a largo plazo.

Además, no hace falta decir que los desarrolladores con pocas habilidades blandas tienden a no durar mucho tiempo en un equipo.

En resumen, un buen desarrollador cumplirá la mayoría de sus requisitos de habilidad sobre el papel y mostrará esas habilidades durante las pruebas técnicas. Un buen desarrollador para teletrabajar tendrá fuertes habilidades de comunicación verbal y dejará que sus habilidades blandas brillen a través de su trabajo pasado y durante el proceso de entrevistas.

Hay que intentar identificar a los candidatos que no describan su historial de trabajo con frases como "Hice esto, hice aquello", sino que valoren a los candidatos que defiendan los esfuerzos del equipo.

Es importante entender qué es lo que hace a un gran desarrollador. Si no tienes certeza sobre las cualidades que deberías buscar, no te preocupes, puedes leer más sobre ello en este artículo.

Por último, destacar que un buen candidato para teletrabajar como programador tendrá aptitudes autodidactas pero también exigirá un plan de carrera que incluya formación online para seguir puliendo sus habilidades, pero de eso hablaremos un poco más adelante.

4.- Entrevistas en remoto

Una vez que hayas filtrado y seleccionado a los mejores candidatos que reúnan los requisitos necesarios sobre el papel para el puesto, es el momento de entrevistarlos.

Entrevistar a distancia es un poco diferente a entrevistar a los candidatos presencialmente porque, a priori, no tendrás la oportunidad de conocerlos en persona y te perderás probablemente algunas cuestiones de la presencia personal y de la comunicación no verbal. Aunque estos obstáculos se solucionan con una buena conexión a internet y una herramienta que permita hacer una videollamada (Microsoft Teams, Google Meet, Zoom, ...).

Antes de que comience la entrevista asegúrate de que todo funciona correctamente y, en cualquier caso ten siempre un plan B, por ejemplo un móvil por si es necesario terminar la entrevista o solventar algún problema técnico telefónicamente.

Otra opción, en lugar de hacer una entrevista directamente, es enviar por correo electrónico a los candidatos una batería de preguntas que deseamos que nos respondan grabando un vídeo. Podemos pedirles que graben el vídeo por su cuenta, o mejor aún, podemos usar un sistema web que permita a los candidato grabarse usando únicamente su webcam, sin necesidad de disponer de ningún programa.

Existen varios sistemas en el mercado que ofrecen ambas alternativas como por ejemplo Spark Hire. Elijas esta plataforma u otra, asegúrate de que cumple escrupulosamente con la GPDR. Hablaremos de la protección de datos más adelante.

Lo más relevante ahora es desarrollar un proceso de entrevista que te ayude a determinar si el candidato encaja bien o no en formato online.

5.- Probar y evaluar al candidato

5.1.- Presentación en videoconferencia de 3-5 minutos

Las presentaciones mediante videoconferencia se están convirtiendo en una práctica cada vez más extendida en el proceso de entrevistas. En un breve espacio de tiempo, se puede ver lo que se puede esperar del aspirante: sus habilidades de comunicación, su personalidad, su entusiasmo, así como el dominio de la tecnología por parte del aspirante.

5.2.- Entrevista técnica

Las entrevistas técnicas permiten profundizar y conocer las habilidades específicas y los conocimientos profesionales del candidato. Por lo general, las entrevistas consisten en un conjunto de preguntas que son específicas para el puesto y la antigüedad para la que se está contratando y verifican que los aspirantes son capaces de rendir en el rol ofertado.

Además de comprobar los conocimientos del candidato, una entrevista técnica es una buena oportunidad para conocer los proyectos que ha realizado anteriormente y de los que está orgulloso, así como la forma en que ha gestionado situaciones difíciles en el pasado, la forma en que manejaría un determinado tipo de proyecto o situación, etc.

Conocer sus expectativas de carrera y de crecimiento, lo que les impulsa y lo que buscan lograr en el nuevo puesto.

La clave aquí es hacer preguntas que permitan al desarrollador abrirse y hablar de las cosas que le apasionan.

5.3.- Ejercicio de programación

No querrás perder el tiempo contratando a la persona equivocada.

Es por ello que cada candidato a programador en remoto que estés entrevistando debe pasar por un ejercicio de programación. Esta es una buena forma de ver si el candidato puede producir código. Aprenderás cómo enfocan y resuelven los problemas.

Pero no puede ser de cualquier forma.

Dales una pequeña tarea (30-60 minutos), y tendrás una idea bastante clara sobre cómo programan, estructuran y documentan su código, así como sobre cómo lo prueban e implementan. Apóyate, por supuesto, en tu equipo técnico para diseñar y revisar las pruebas, y asegúrate de que hay algún miembro disponible durante la prueba por si surgen dudas.

5.4.- La importancia de la formación online

Por último, pero no menos importante, debemos fijarnos en aquellos candidatos que se muestren dispuestos a seguir formándose en remoto y que demuestren en su CV haberse formado online.

Es bastante obvio que si van a trabajar online, quieran y demuestren interés en formarse online. Los desarrolladores necesitan planes de desarrollo profesional que incluyan cursos de programación de forma constante a lo largo de los años.

Es recomendable ya desde las entrevistas fijar estas expectativas. Muchas veces el mejor programador para un puesto es el que esta más dispuesto a aprender y a desarrollar sus habilidades técnicas.

En nuestro catálogo de cursos online para programadores tenemos una amplia oferta que puede resultar muy útil en este sentido.

6.- Contratarlos y trabajar encantados para siempre

Bueno, esta fase, la de la fidelización de las nuevas incorporaciones puede ser muy complicada y requiere varios artículos por sí misma. Aquí te dejamos algunos:

Garantizar la protección de datos personales

A lo largo de todo el proceso de selección, la protección de datos del candidato debe estar garantizada. Presta especial atención si utilizas software ajeno y sobre todo si grabas las entrevistas y estas van a ser visionadas por distintos departamentos.

Conclusión

Si decidís contratar a desarrolladores para trabajar desde casa, debéis estar preparados para afrontar todos los desafíos que conlleva el proceso de contratación: cientos de solicitudes inadecuadas que inundan tu bandeja de entrada, correos electrónicos y mensajes no solicitados en LinkedIn, horas de entrevistas y pruebas, negociaciones agotadoras sobre salarios y condiciones laborales, procesos de contratación complicados, etc.

Con este artículo simplemente queremos ayudarte con el proceso, pero sobre esto no hay nada escrito y seguro que hay otras formas de afrontarlo, siempre con paciencia y con perseverancia.

7 claves para aprender bien CSS

$
0
0

El soporte estándar de CSS en los navegadores y sus posibilidades han evolucionado muchísimo desde que yo empecé con esto a principio de los dosmiles, así que me dio por pensar en la suerte que tienen las nuevas generaciones de tener tanto poder en sus manos.

Pero por otro lado, también caí en la cuenta de que, si hoy en día tuviese que aprender desde cero todo lo que he ido aprendiendo en los últimos 15/20 años, realmente sería abrumador.

CSS es mucho más potente y profundo de lo que muchos desarrolladores creen o van a querer reconocer. Y cualquiera que lo quiera abordar seriamente para maquetar webs, como he dicho, realmente puede sentirse abrumado y caer en la tentación de solo rascar la superficie.

Por eso, he recopilado unos cuantos puntos clave que me parecen importantes para intentar transmitírselos a cualquiera que se esté iniciando en serio en la maquetación web.

No se trata de decirte qué debes hacer o no porque sí. Son cosas que considero que, si alguien me las hubiese explicado bien en su momento, me habrían ahorrado mucho tiempo y penurias.

Ojalá te sean útiles.

1- Todo está conectado

Los siguientes puntos se interrelacionan entre sí. No los tomes como puntos aislados.

En CSS todo está conectado, una mariposa aletea en Pekín y a ti te aparece un scroll dentro de un div con el que no contabas.

Estos puntos que he recopilado me parecen importantes después de más de 15 años usando CSS a diario, tanto diseñando webs, como emails y aplicaciones web.

Son conceptos sobre los que cualquiera que esté empezando es fácil que pase de puntillas en el afán de aprender rápido.

2 - El modelo de caja (Box model)

Todos los elementos HTML son cajas que vamos colocando como buenamente podemos usando CSS.

A cada una de estas cajas se le pueden definir un montón de propiedades: altura, anchura, márgenes internos y externos, borde...

En la guerra de los Navegadores de los años 90 (y principios de los 2000) había dos modelos de caja luchando frente a frente y una misma propiedad te podía dar resultados distintos según el navegador.

En cambio, ahora puedes usar el que más te convenga según tus intereses en diferentes elementos de la misma página.

El tema es estudiarlo y conocerlo bien. Es muy importante porque pueden ser una fuente de errores absurdos que te traigan de cabeza.

En cambio, conocerlo bien puede ahorrarte escribir mucho código.

Es tan importante que en el curso de HTML y CSS nuestro tutor José Manuel Alarcón le dedica un módulo entero.

3 - Unidades de medida relativas y absolutas

Y para sacarle todo el provecho al modelo de caja, nada mejor que conocer bien las unidades de medida que puedes usar. Hay muchas: px, em, rem, ch, vw, vh...

Saber cuáles son, cómo funciona cada una y probarlas bien hará que descubras las grandes ventajas usar la idónea en cada momento.

4 - Conocer el flujo o la importancia de tener Flow

En el navegador los elementos HTML se van colocando siguiendo una cierta pauta según aparecen en el documento. Para abreviar, es como si existiese un imán arriba a la izquierda que los va atrayendo sin remedio y ellos se van colocando en función de su tamaño y el espacio disponible.

Nota: esta metáfora solo sirve para sistemas occidentales donde la dirección de lectura va de izquierda a derecha y de arriba abajo. Para otros sistemas y lenguas esto puede variar y además se le pueden asignar estilos específicos en función de la dirección de lectura.

Algunos elementos también pueden variar su colocación en función de si son elementos de línea o de bloque.

Investiga bien los valores de la propiedad display. Hasta aquí puedo leer.

Frente al navegador: be water, my friend

Para maquetar una web, necesitarás surfear bien el flujo.

Esto es, recolocar elementos incluso sacándolos del flujo, pero que de alguna forma se siguen relacionando con él.

Si entiendes bien estas relaciones y empiezas maquetando para móviles (lo que se conoce como mobile-first) podrás crear webs fluidas que se adapten a cualquier contenido y resolución de pantalla con mucho menos código.

Menos código supone menos errores y más flexibilidad presente y futura.

Aprende bien cómo usar float , flexbox y grid para que el navegador trabaje para ti y tu web se adapte a él como el agua se adapta a una jarra.

5 - Posicionamiento

Seguimos con el flujo. Se puede hacer caso omiso de este flujo completamente. Es posible sacar elementos fuera del flujo y colocarlos donde quieras en función de otros elementos.

Es como convertirse en superhéroe y poder volar hacia donde quieras.

Pero ojo, sin abusar. Todo poder conlleva una gran responsabilidad. Si empiezas a acumular elementos posicionados de forma fija o absoluta te puedes llevar desagradables sorpresas si no sabes cómo usar bien z-index

La clave: entender cómo funcionan los contextos.

6 - Cascada y Herencia

Imagínate que te cae un bote de pintura en la cabeza. La mayoría de la pintura se te queda en el pelo, pero poco a poco va bajando y te van salpicando gotas hacia los pies.

CSS funciona de forma parecida, las propiedades se van aplicando en cascada según el orden que aparecen en la hoja de estilos (igual que se desparrama la pintura), pero además va salpicando hacia abajo a sus elementos hijos y otros descendientes.

Esto es lo que se llama herencia, algunas propiedades se heredan de padres a hijos, y para poder modificarlas tienes que hacer reglas CSS más específicas.

Por suerte esto es menos aleatorio que las salpicaduras de pintura, pero hay que conocer bien sus reglas inflexibles.

7 - Selectores (y compañía)

¿Y cómo aplicamos las reglas específicas? Con los selectores.

Los selectores ayudan a especificar a quién le quieres aplicar una determinada propiedad.

Los más conocidos son las clases (.) y los identificadores (#) pero hay mucho más y todos muy útiles: de atributo, para seleccionar elementos hijos, para elementos adyacentes... Pero además están sus "primos": pseudoelementos, pseudoselectores, contadores...

Y lo mejor, los puedes combinar entre ellos para lograr lo que quieras como si fuesen piezas de Lego.

En CSS solo hay una cosa que no se puede hacer con los selectores (y probablemente nunca se podrá), y es volver atrás en el árbol del documento para seleccionar al elemento padre.

Para todo lo demás, seguro que existe una forma de hacerlo, pero requiere conocer bien los selectores.

Especificidad

¿Cómo sabemos si una regla es suficientemente específica? Vale, si consigues sobrescribirla y que se apliquen sus propiedades será más específica, pero no es simplemente eso lo que queremos.

Hay normas muy claras sobre cómo se cuenta la especificidad según el selector que usemos.

Lo interesante es usar siempre la mínima especificidad posible por si necesitamos volver a sobrescribirla.

Así tu hoja de estilos será lo más sencilla posible y evitarás recurrir a los nocivos !important.

Despedida y cierre

Por supuesto que maquetar con HTML y CSS es mucho más que todo lo nombrado aquí, pero he intentado ceñirme a lo que considero los aspectos más importantes y a los que no se les suele dar la importancia que merecen.

Si consideras que me he dejado algo atrás, estaré encantado de escuchar tus sugerencias en los comentarios.

Por otro lado, si quieres aprender a maquetar bien con HTML y CSS, no puedo dejar de recomendarte nuestros dos cursos: el curso de "HTML y CSS para desarrolladores" y el de Maquetación Responsive.

Todos los consejos de este post se explican y desarrollan en detalle (entre otros muchos más conceptos importantes) a lo largo de estos cursos. Escoge el primero de ellos si partes de cero, y el segundo si ya sabes HTML y CSS y quieres modernizarte. Te permitirán aprender poco a poco, con seguridad y con la posibilidad de consultar dudas directamente a auténticos expertos.

La base que obtendrás con estos cursos no solo te servirá para tus proyectos de hoy, sino que te permitirá incorporar con solvencia cualquier novedad futura en los estándares de HTML y CSS.

Qué es la Inyección de Dependencias y cómo funciona

$
0
0

La inyección de dependencias es un patrón de diseño que describe un conjunto de técnicas destinadas a disminuir el acoplamiento entre los componentes de una aplicación. Es uno de los principios SOLID más populares y utilizados en la creación de aplicaciones, frameworks y componentes por las ventajas que aporta a las mismas.

La inyección de dependencias suele también conocerse como inversión de dependencias o inversión de control. En inglés, los términos más frecuentemente utilizados son "dependency injection", abreviado como "DI", e "inversion of control" o simplemente "IoC".

Muy resumidamente, el Principio de Inyección de Dependencias propone evitar las dependencias rígidas entre componentes mediante las siguientes técnicas:

  • Utilizar abstracciones (interfaces) en lugar de referencias directas entre clases, lo que facilita que podamos reemplazar componentes con suma facilidad.
  • Hacer que una clase reciba referencias a los componentes que necesite para funcionar, en lugar de permitir que sea ella misma quien los instancie de forma directa o a través de factorías.

Seguro que entiendes mejor estos conceptos si vemos algo de código. Observa el siguiente ejemplo, escrito en C# (pero valdría para cualquier lenguaje) aún sin usar inyección de dependencias, donde se muestra una clase llamada InvoiceServices cuyo funcionamiento depende, como mínimo, de otras dos clases externas, InvoiceRepository e EmailNotifier:

public class InvoiceServices
{
    ...
    public void Remove(int invoiceId)
    {
        using (var invoiceRepository = new InvoiceRepository())
        {
            var removed = invoiceRepository.Remove(invoiceId);
            if (removed)
            {
                var notifier = new EmailNotifier();
                notifier.NotifyAdmin($"Invoice {invoiceId} removed");
            }
        }
    }
}

El código del método Remove(), aunque aparentemente correcto, presenta algunos problemas:

  1. Tiene bastantes líneas de código de "fontanería", dedicadas a instanciar y preparar las dependencias, en lugar de centrarse en su misión, que es eliminar una factura y notificar al administrador.
  2. Observa además que, muchas de esas líneas deberían repetirse en otros métodos de la clase que requirieran los servicios de estos componentes. Por ejemplo, otros métodos, como Add() o Update(), probablemente necesitarían acceder al repositorio de facturas y quizás también al componente de notificación.
  3. Estamos atando inexorablemente la implementación de InvoiceServices a InvoiceRepository e EmailNotifier. Cualquier modificación en estas últimas podría afectar a la primera, o incluso requerir cambios en ésta.
  4. Complicamos la reutilización de la clase, puesto que siempre va a ir unida a los componentes de los que depende.
  5. No quedan claras las dependencias de la clase. Para conocerlas deberíamos leer todo su código y ver qué clases externas utiliza. Aunque en ese ejemplo no es un problema porque es poco código, en clases más extensas sí sería bastante complicado determinarlas.
  6. Dificultamos la realización de pruebas unitarias, puesto que no hay forma de probar únicamente el correcto funcionamiento del método Remove() de InvoiceServices sin probar al mismo tiempo el funcionamiento de las clases de las que depende.

Utilizando el principio de Inyección de Dependencias, el código anterior podríamos transformarlo en el siguiente:

public class InvoiceServices: IInvoiceServices
{
    private readonly IInvoiceRepository _invoiceRepository;
    private readonly INotifier _notifier;

    public InvoiceServices (IInvoiceRepository invoiceRepository, INotifier notifier)
    {
        _invoiceRepository = invoiceRepository;
        _notifier = notifier;
    }
    ...
    public void Remove(int invoiceId)
    {
        var removed = _invoiceRepository.Remove(invoiceId);
        if (removed)
        {
            _notifier.NotifyAdmin($"Invoice {invoiceId} removed");
        }
    }
}

Observa que, ahora, las dependencias de la clase las recibimos en el constructor y las almacenamos localmente en miembros de instancia.

En la práctica podemos utilizar un sistema de inyección de dependencias. Un sistema de inyección de dependencias es el encargado de instanciar las clases que necesitemos y suministrarnos ("inyectar") las dependencias enviando los parámetros oportunos al constructor.

Existe otra forma de indicar las dependencias de una clase que, en lugar de utilizar el constructor para recibir las dependencias, utiliza propiedades decoradas con algún tipo de atributo que el inyector de dependencias es capaz de reconocer. Por ejemplo, en el siguiente fragmento se utiliza el atributo [Dependency] para indicar al contenedor que el valor de dichas propiedades debe ser inyectado. El resultado sería totalmente equivalente a usar inyección en el constructor:

public class InvoiceServices: IInvoiceServices
{
    [Dependency]
    private readonly IInvoiceRepository _invoiceRepository;
    [Dependency]
    private readonly INotifier _notifier;
    ...
    // Otros miembros de la clase
}

¡Ojo!: el nombre del atributo [Dependency] es solo a nivel ilustrativo, no existe en .NET Core. Ahora mismo estamos hablando de manera genérica sobre estas técnicas.

En cualquiera de las dos opciones, fíjate en que nos abstraemos de implementaciones concretas mediante el uso de interfaces. No nos importarán los tipos concretos que lleguen al constructor como dependencias, siempre que cumplan los contratos definidos por sus interfaces. Por ejemplo, para la interfaz INotifier podría llegarnos la clase EmailNotifier que usábamos en el ejemplo anterior, o bien una instancia de TwitterNotifier o MobilePushNotifier; nos da igual, lo importante es que dispongan del método NotifyAdmin(), que es lo que en realidad usamos de ellas.

De esta forma conseguiremos las siguientes ventajas:

  1. La implementación de InvoiceServices queda totalmente desacoplada, puesto que no depende de ningún componente específico para funcionar, sólo de contratos.
  2. Para conocer las dependencias de la clase basta con echar un vistazo a su constructor o a las propiedades decoradas con el atributo usado por el marco de trabajo para marcar los miembros inyectables, por lo que simplificamos su lectura y facilitamos su comprensión.
  3. Los métodos pueden centrarse ahora en lograr su cometido porque las dependencias ya están disponibles a nivel de instancia. Esto nos lleva a disponer de un código más conciso, limpio, fácil de escribir y de leer. Observa que puedes entender el método Remove() del segundo ejemplo de un rápido vistazo, mientras que en el primer ejemplo necesitabas una lectura algo más detenida.
  4. La clase será mucho más reutilizable porque no depende de otros componentes, sino de abstracciones.
  5. Podemos realizar fácilmente pruebas unitarias de esta clase de forma aislada, enviándole dependencias falsas o controladas (fakes, stubs, mocks...) desde los métodos de test.

Para que todo esto funcione necesitamos un componente, habitualmente denominado contenedor de inversión de control (IoC Container en inglés) o simplemente contenedor de inyección de dependencias, cuya única responsabilidad es crear clases con todas sus dependencias asociadas. A todos los efectos, actúa como una factoría a la que podemos solicitar objetos de tipos específicos que él, internamente, se encargará de instanciar y gestionar.

Para ello, el contenedor dispone de un registro de servicios y equivalencias entre abstracciones y tipos concretos que usa para resolver las dependencias cuando es necesario. Por ejemplo, para el escenario anterior, el contenedor de IoC podría disponer de la siguiente información:

AbstracciónClase concreta
IInvoiceServicesInvoiceServices
IInvoiceRepositoryInvoiceRepository
INotifierEmailNotifier

De esta forma, cuando una aplicación requiere una instancia de InvoiceServices, lo que haría, en lugar de crearla directamente, es solicitar al contenedor IoC un objeto IInvoiceServices. Éste sabría que la clase concreta a crear es InvoiceServices y analizaría los parámetros de su constructor o propiedades decoradas con un atributo apropiado según el inyector que usemos, detectando que a su vez depende de dos abstracciones: IInvoiceRepository e INotifier. Así, atendiendo a su registro interno, primero crearía objetos de tipo InvoiceRepository e EmailNotifier y luego crearía la instancia de InvoiceServices suministrándole como dependencias las instancias anteriores.

Por supuesto, si InvoiceRepository o EmailNotifier necesitaran a su vez satisfacer otras dependencias para poder ser instanciadas, se haría exactamente lo mismo, resolviendo todas las dependencias de forma recursiva hasta conseguir crear los objetos de los tipos solicitados.

 

Viewing all 776 articles
Browse latest View live