<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es"><id>https://iyaki.ar/</id><title>mi Blog | iyaki</title><subtitle>Blog personal donde comparto mis experiencias y pensamientos</subtitle><updated>2025-12-01T13:13:10Z</updated><link href="https://iyaki.ar/blog" hreflang="es"/><link href="https://iyaki.ar/posts/feed.xml" rel="self"/><author><name>Ivan Yakimovicz</name><uri>https://iyaki.ar</uri></author><icon>https://iyaki.ar/blog/logo.png</icon><logo>https://iyaki.ar/blog/logo.png</logo><entry><id>https://iyaki.ar/posts/20251130_sobre_las_mentorias/</id><title><![CDATA[Sobre las mentorías]]></title><summary><![CDATA[La opinión que nadie pidió respecto a las mentorías pagas.]]></summary><content type="html"><![CDATA[<blockquote>La opinión que nadie pidió respecto a las mentorías pagas.</blockquote><h1>Sobre las mentorías</h1>
<p><time datetime="2025-12-13T13:07:10.903Z">01/12/2025</time></p>

<p>¿Por qué alguien pagaría por un mentor?</p>
<p>Es una pregunta seria.</p>
<p>Muchas veces vi anuncios ofreciendo este servicio. Muchas veces, amigos y compañeros me han sugerido que debería dar este servicio.</p>
<p>Pero siento que la idea de pagar por un mentor va en contra del concepto de un mentor en sí mismo.</p>
<p>¿De hecho, cómo llega alguien al punto de necesitar un <em>mentor as a service</em>?</p>
<p>Al menos como yo entiendo el concepto de mentor, se supone que sea un <em>compañero</em> con más experiencia que <em>acompaña</em> y guía a otro.</p>
<p>En condiciones “extremas” puede si quien va a ser mentoreado carece por completo de experiencia, es valido que la relación comience con una dinámica más parecida a la de un profesor y un alumno, pero esa dinámica debe cambiar a la de compañeros en cuanto se tenga un mínimo de autonomía para llevar a cabo trabajos simples.</p>
<p>El mentor debe ser un <i lang="ja">senpai</i>, no un <i lang="ja">sensei</i>.</p>
<p>En este esquema, el mentor se ve beneficiado de la posibilidad de delegar trabajo. De contar con un par extra de manos. De poder perpetuar su profesión y su existencia mediante el legado y la huella que deja en su protegido.</p>
<p>Para mí, una mentoría paga es solo una forma de agregar mística al concepto de un profesor particular.</p>
<p>El concepto de un profesor particular no tiene nada de malo. De hecho, me parece excelente para que alguien pueda aprender de manera acelerada.</p>
<p>Un curso acelerado de 3 meses para aprender Python puede hacer maravillas. Y deja mucho más claras las expectativas para todos. Un profesor. Por 3 meses.</p>
<p>Siento que un mentor no debería tener fecha de caducidad.</p>
<p>También puede ser solo TOC mío con el uso de las palabras y el lenguaje.</p>
<p>Dicho todo esto,</p>
<p>Si creen que les serviría acompañamiento temporal; de alguien con espíritu de hacker open source, que por vocación hubiera estudiado filosofía y letras, pero prefirió no pasar hambre y enfocó su carrera profesional en la arquitectura de software (con programación orientada a objetos) como <a href="https://posthog.com/blog/what-is-a-product-engineer" target="_blank" lang="en" hreflang="en">Product Engineer</a> hasta que la parte de “<span lang="en">product</span>” se le fue de las manos y terminó cumpliendo el rol de <i lang="en">Head of Product</i> donde pasó a tener gente a cargo y terminó interesándose más por el <i lang="en">People &amp; Teams Management</i> que por la programación, pero a pesar de eso no deja de programar; pueden contactarme a <a href="mailto:me@iyaki.ar" target="_blank">me@iyaki.ar</a>.</p>
<p>Larga y prospera vida 🖖.</p>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20251130_sobre_las_mentorias/"/><updated>2025-12-01T13:13:10Z</updated><published>2025-12-01T13:13:10Z</published></entry><entry><id>https://iyaki.ar/posts/20251129_hice_un_jueguito/</id><title><![CDATA[Hice un jueguito]]></title><summary><![CDATA[<a href="https://juego-papa.com/" target="_blank">Papa online</a> - Para jugar con familiares o amigos de facebook.]]></summary><content type="html"><![CDATA[<blockquote><a href="https://juego-papa.com/" target="_blank">Papa online</a> - Para jugar con familiares o amigos de facebook.</blockquote><h1>Hice un jueguito</h1>
<p><time datetime="2025-11-30T00:20:09.504Z">29/11/2025</time></p>

<p><a href="https://juego-papa.com/" target="_blank">Papa online</a> - Para jugar con familiares o amigos de facebook.</p>
<p>En realidad no sé si decir que HICE un juego porque prácticamente no toque nada del código fuente, no diseñe las reglas, ni siquiera fue mi idea hacer una versión online de este juego.</p>
<p>A principio del 2024, hicimos una reunión con mis compañeros de la escuela, a la mayoría los conozco desde los 6 años y compartimos prácticamente todos nuestros días hasta los 18 años, cuando terminamos el secundario. A pesar de eso, a algunos, hacía prácticamente 10 años que no los veía.</p>
<p>—Cosas de la vida—.</p>
<p>Supongo.</p>
<p>Dejando de lado la melancolía, la cuestión es que mientras hablaba con uno de mis amigos, no sé muy bien cómo, llegó a la conclusión (o quizás fuese algo que ya venía fraguando hace tiempo) de que era inadmisible que no existiera una versión online del juego de <a href="https://www.youtube.com/watch?v=eti9nKsukMs">la papa</a>. Y que además, por alguna razón, yo debería desarrollarlo.</p>
<p>Podría haber quedado como una charla cualquiera más que no lleva a nada, pero la verdad es que la idea me gustó.</p>
<p>Lo primero fue buscar cómo se jugaba. Si bien estaba seguro de que las reglas eran muy simples, era algo que hacíamos, principalmente, durante la primaria, así que necesitaba refrescar la memoria.</p>
<p>Así me enteré de que, al parecer, las reglas completas eran un poco diferente a lo que recordaba e involucraban una especie de puntajes durante la partida. La versión simplificada, donde solo se ganaba o se perdía que yo recordaba, me gustó más.</p>
<p>Lo siguiente era desarrollar el juego. Hay personas que aman la idea de desarrollar sus propios videojuegos. Les apasiona la parte relacionada con los gráficos, interacción, ambientación.<br>
Yo no soy una de esas personas.</p>
<p>Así que me pareció una excelente oportunidad para probar herramientas de desarrollo impulsadas por <abbr title="Inteligencia Artificial">IA</abbr> para lograrlo.</p>
<p>Quizás el problema fue que no supe usarlas adecuadamente, pero no conseguí el resultado que buscaba con ninguna de las herramientas que conocía en ese momento.</p>
<p>Copilot, Windsurf, Codex, ninguna me daba un resultado lo suficientemente bueno como para que no tuviera que involucrarme en los pormenores del uso de canvas y los algoritmos del juego.<br>
En algún punto este proyecto se convirtió en mi forma de probar las <i>IAs</i>, <i>LLMs</i>, agentes y herramientas del estilo.</p>
<p>Pasaban los meses y ninguna lo lograba.</p>
<p>Hace unas semanas leí sobre el lanzamiento del <abbr title="Integrated Development Environment">IDE</abbr> <a href="https://antigravity.google/">Antigravity</a> de Google. Y adivinen qué.</p>
<p><strong>Lo consiguió.</strong></p>
<p><img src="you-did-it.png" alt="Meme de Jurassic Park. You did it! You crazy son of a bitch, you did it"></p>
<p>Necesitó de unos cuantos prompts e iteraciones para llegar al estado actual, pero desde el primer resultado generó una aplicación que cumplía las expectativas mínimas para poder considerarse un prototipo funcional.</p>
<p>No es perfecto. La mitad de las veces que le pedí agregar nuevas features o resolver bugs, lo hizo a costa de romper otras cosas que ya funcionaban. Por otra parte, eso no es muy distinto del resultado de muchos programadores humanos (?).</p>
<p lang="en">Anyway.</p>
<p>El punto es que ahora existe una versión online de uno de los juegos de mi infancia con el que pasé horas libres, clases aburridas y me divertí con amigos:</p>
<p><a href="https://juego-papa.com/" target="_blank">Papa online</a> - Para jugar con familiares o amigos de facebook.</p>
<p>Literalmente ese es el nombre que le dio mi amigo esa noche, no sé por qué fue tan específico respecto a con quién se debe jugar.
<br>
Por desgracia, las reglas para restringir a quien se elige como adversario eran un poco difíciles de implementar, por lo que podrán jugar con quien sea que quieran ;).</p>
<p>Espero que se diviertan.</p>
<p>Hasta la próxima.</p>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20251129_hice_un_jueguito/"/><updated>2025-11-30T00:20:09Z</updated><published>2025-11-30T00:20:09Z</published></entry><entry><id>https://iyaki.ar/posts/20250304_devcontainers_de_cero_a_cien/</id><title><![CDATA[Devcontainers de cero a cien, en español]]></title><summary><![CDATA[Si alguna vez tuviste problemas gestionando múltiples versiones de un mismo lenguaje de programación o distribuyendo herramientas necesarias para los programadores de un proyecto, este post podría interesarte.]]></summary><content type="html"><![CDATA[<blockquote>Si alguna vez tuviste problemas gestionando múltiples versiones de un mismo lenguaje de programación o distribuyendo herramientas necesarias para los programadores de un proyecto, este post podría interesarte.</blockquote><h1>Devcontainers de cero a cien, en español</h1>
<p><time datetime="2025-03-04T01:13:04.984Z">03/03/2025</time></p>

<aside aria-label="Índice">
	<details>
		<summary>
			<h2 style="display: inline;">Índice</h2>
		</summary>
		<nav aria-label="Índice">
			<ul>
				<li><a href="#sobre-esta-guia">Sobre esta guía</a>
				<li><a href="#nuevo-proyecto">Comenzando un nuevo proyecto con devcontainers</a>
				<li><a href="#templates-imagenes">Eligiendo templates o imágenes según nuestras necesidades</a>
				<li><a href="#features">Agregando funcionalidades (Devcontainer Features)</a>
				<li><a href="#ciclo-de-vida">Ciclo de vida del container de desarrollo, comandos y scripts</a>
				<li><a href="#montajes-puertos-variables-entorno">Montajes, puertos, variables de entorno y otras hierbas</a>
				<li><a href="#intermedio">Intermedio</a>
				<li><a href="#debugging">Debugging y solución de errores</a>
				<li><a href="#customizations">Customizations</a>
				<li><a href="#dockerfile">Back to the basics, Dockerfile</a>
				<li><a href="#docker-compose">Múltiples containers con Docker compose</a>
				<li><a href="#dotfiles">Sintiendonos como en casa con dotfiles</a>
				<li><a href="#impresiones-finales">Impresiones finales</a>
			</ul>
		</nav>
	</details>
</aside>

<p>Los lenguajes de programación evolucionan a una velocidad increíble, esto puede generar que tengamos que trabajar con distintas versiones de un mismo lenguaje de programación para distintos proyectos pero en una misma computadora.</p>
<p>Este es un problema bien conocido y con algunas soluciones que las distintas comunidades han ido desarrollando con el tiempo, dándonos herramientas específicas como <a href="https://github.com/nvm-sh/nvm" target="_blank" hreflang="en">nvm</a> para node.js o algunas con un mayor alcance como <a href="https://asdf-vm.com/" target="_blank" hreflang="en">asdf-vm</a>.</p>
<p>Y aunque estas soluciones pueden ser excelentes a la hora de permitirnos cambiar rápidamente entre versiones de nuestros lenguajes de programación, también presentan algunos inconvenientes, ya que, al basar su funcionamiento en el compilado desde el código fuente de las distintas versiones, requieren que uno cuente con todas las dependencias necesarias para poder compilarlas.</p>
<p>Esto en muchos casos puede resolverse simplemente con la ayuda de Google, pero en otros casos puede resultar bastante complicado.</p>
<p>Especialmente si tu sistema operativo ya no provee las versiones específicas de las dependencias necesarias para compilar alguna versión en particular (por ser demasiado nuevas o demasiado viejas).</p>
<p>Otra complejidad de estas herramientas aparece al tener que trabajar en equipo, ya que requieren que cada uno, de manera individual, configure su computadora según las necesidades del proyecto.</p>
<p>¿Pero qué tal si existiese una herramienta que permitiera asegurar la replicabilidad de los entornos de desarrollo?</p>
<p>Y no solo de los lenguajes de programación, sino de todas las herramientas adicionales que se puedan requerir para mejorar la <i lang="en" title="experiencia del programador">Developer eXperience</i>, permitiendo incluso instalar extensiones específicas en tu <abbr title="Integrated Development Environment" lang="en">IDE</abbr> y teniendo todo esto versionado junto con el código fuente del proyecto.</p>
<p>Pues existe.</p>
<p>Hoy vengo a compartir una herramienta que uso cada vez más en mis proyectos de desarrollo software: <a href="https://containers.dev/" target="_blank" hreflang="en" lang="en">devcontainers</a>.</p>
<h2 id="sobre-esta-guia"><a href="#sobre-esta-guia">Sobre esta guía</a></h2>
<p>Devcontainers utiliza virtualización mediante containers para crear entornos de desarrollo personalizables, replicables y versionables.</p>
<p>En esta guía exploraremos sus casos de uso y configuraciones sin detenernos en conceptos propios de docker (el motor de containers que utiliza por defecto).</p>
<p>En los ejemplos utilizaremos <a href="https://code.visualstudio.com/" target="_blank" hreflang="en" lang="en">Visual Studio Code</a>, <a href="https://www.docker.com/" target="_blank" hreflang="en" lang="en">Docker</a> y <a href="https://docs.docker.com/compose/" target="_blank" hreflang="en" lang="en">Docker compose</a>.</p>
<p>Tener algunos conocimientos básicos de estas herramientas o de GNU Linux (<abbr title="Sistema Operativo">S.O.</abbr> utilizado dentro de los containers) puede ayudar a facilitar el entendimiento de algunas de las cosas que haremos a lo largo de esta guía.</p>
<p>Al momento de escribir esto me encuentro utilizando:</p>
<ul>
<li><a href="https://neon.kde.org/" target="_blank" hreflang="en" lang="en">KDE Neon</a> 6.3 (Distribución de GNU Linux derivada de Ubuntu).</li>
<li><a href="https://code.visualstudio.com/" target="_blank" hreflang="en" lang="en">Visual Studio Code</a> 1.97.2.</li>
<li><a href="https://www.docker.com/products/docker-desktop/" target="_blank" lang="en">Docker Desktop</a> 4.38.0.</li>
</ul>
<p>Todos los pasos de esta guía deberían poder seguirse también, sin problemas, tanto utilizando <a href="https://docs.docker.com/engine/" target="_blank" hreflang="en" lang="en">Docker Engine</a> como en los sistemas operativos <span lang="en">Mac</span> o <span lang="en">Windows</span> (utilizando <a href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux" target="_blank" hreflang="en" lang="en">Windows Subsystem for Linux</a>).</p>
<p>Para instalar devcontainers en <span lang="en">Windows</span> existe <a href="https://code.visualstudio.com/blogs/2020/07/01/containers-wsl" target="_blank" hreflang="en" lang="en">una guía</a>, escrita por el equipo de <span lang="en">Visual Studio Code</span>.</p>
<p>Devcontainers posee integraciones con una <a href="https://containers.dev/supporting" target="_blank" hreflang="en" lang="en">amplia lista de IDEs y herramientas de desarrollo</a>, no solo se integra con el <span lang="en">Visual Studio Code</span>.</p>
<p>Mención especial para <a href="https://github.com/michidk/vscli" target="_blank" hreflang="en" lang="en">vscli</a>, una herramienta que aunque no se encuentra listada oficialmente, se ve interesante.</p>
<p><strong>Para poder seguir los distintos pasos deberán contar con Docker instalado y ejecutándose.</strong></p>
<p>Por último, para aquellos con experiencia en Docker. Muchos de los “problemas” que solucionaremos a lo largo de esta guía mediante las características y opciones de devcontainers podrían resolverse (a veces, incluso de manera más simple) mediante el uso de Docker puro o eligiendo mejor las imágenes base, pero el objetivo aquí es explorar estas características y opciones.</p>
<h2 id="nuevo-proyecto"><a href="#nuevo-proyecto">Comenzando un nuevo proyecto con devcontainers</a></h2>
<p>Como ejemplo para poder explicar el funcionamiento de estos entornos de desarrollo crearemos una pequeña aplicación <abbr title="PHP: Hypertext Preprocessor" lang="en">PHP</abbr>.</p>
<p>Por lo que en un directorio vacío comenzaremos creando el archivo <code>public/index.php</code> y en el escribiremos:</p>
<pre><code class="language-php">Hola mundo
</code></pre>
<p><img-viewer><a href="01-simplest-php-application.png"><img src="01-simplest-php-application.png" alt="La aplicación PHP más simple del mundo, una única línea de código: Hola mundo" loading="lazy" decoding="async"></a></img-viewer></p>
<p>¡Excelente! Ya tenemos la aplicación más simple del mundo. Pero para poder ejecutarla necesitamos PHP. Y ahí es en donde entra en juego devcontainers.</p>
<p>Para utilizarlo comenzaremos por instalar la extensión <em lang="en">Dev Containers</em> del VSCode.</p>
<p>Pueden buscarla en la pestaña de extensiones.</p>
<p><img-viewer><a href="02-vscode-extensions-devcontainers.png"><img src="02-vscode-extensions-devcontainers.png" alt="Extensión Devcontainers en Visual Studio Code" loading="lazy" decoding="async"></a></img-viewer></p>
<p>O bien instalarla mediante el comando <code>ext install ms-vscode-remote.remote-containers</code> en el <em lang="en">Qucik Open</em> (Ctrl+p) del VSCode.</p>
<p>Ya con la extensión instalada y activa, en la paleta de comandos (Ctrl+Shift+p) ejecutaremos <strong lang="en">Dev Containers: Add Dev Container Configuration Files</strong>.</p>
<p><img-viewer><a href="03.png"><img src="03.png" alt="Captura de pantalla del Visual Studio Code al ejecutar el comando mencionado anteriormente" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Esto nos desplegará un pequeño <i lang="en">Wizard</i> en el que configuraremos algunas opciones básicas del entorno de desarrollo.</p>
<blockquote lang="en">
<p>Where would you like to create your container configuration?<br>
<strong>&gt; Add configuration to workspace</strong></p>
</blockquote>
<p><img-viewer><a href="04.png"><img src="04.png" alt="Captura de pantalla del Visual Studio Code mostrando las opciones disponibles" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Elegimos esta opción para poder versionar el entorno de desarrollo junto con el resto de la aplicación.</p>
<blockquote lang="en">
<p>Select a container configuration template or enter a custom template id<br>
<strong>&gt; PHP</strong> (devcontainers)</p>
</blockquote>
<p><img-viewer><a href="05.png"><img src="05.png" alt="Captura de pantalla del Visual Studio Code mostrando las opciones disponibles" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Elegimos la plantilla para proyectos PHP publicada por el equipo de devcontainers.</p>
<blockquote lang="en">
<p>PHP version (use -bookworm, -bullseye variants on local arm64/Apple Silicon):<br>
<strong>&gt; 8.2-bullseye</strong> (default)</p>
</blockquote>
<p><img-viewer><a href="06.png"><img src="06.png" alt="Captura de pantalla del Visual Studio Code mostrando las opciones disponibles" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Seleccionamos la versión de PHP que deseamos utilizar. En esta guía usaremos PHP 8.2.</p>
<p>Los sufijos <em>-bullseye</em> y <em>-bookworm</em> indican la <a href="https://www.debian.org/releases/index.es.html" target="_blank">versión de Debian</a> que el container utilizará como base para el entorno de desarrollo.</p>
<blockquote lang="en">
<p>Select additional features to install</p>
</blockquote>
<p>De memento no agregaremos ninguna funcionalidad adicional, por lo que solo damos click en <strong>OK</strong> par finalizar la configuración.</p>
<p><img-viewer><a href="07.png"><img src="07.png" alt="Captura de pantalla del Visual Studio Code mostrando las opciones disponibles" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Finalmente, se nos preguntará si deseamos configurar <a href="https://docs.github.com/en/code-security/dependabot" target="_blank" hreflang="en" lang="en">Github Dependabot</a> para que actualice automáticamente las dependencias de devcontainers. Para mantener esta guía lo más simple posible continuaremos sin esta opción, pero en proyectos reales puede ser muy recomendable utilizar esta actualización automática de dpenedencias.</p>
<p><img-viewer><a href="08.png"><img src="08.png" alt="Captura de pantalla del Visual Studio Code mostrando las opciones disponibles" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Luego de seguir estos pasos se creará el archivo <code>./devcontainer/devcontainer.json</code> y el VSCode nos preguntará si deseamos reabrir el proyecto dentro del container (cartel de abajo a la derecha)</p>
<p><img-viewer><a href="09-devcontainer-basic-config.png"><img src="09-devcontainer-basic-config.png" alt="Captura de pantalla del Visual Studio Code luego de terminar la configuración de devcontainers" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Damos click al botón <strong lang="en">Reopen in Container</strong>.</p>
<p>Si por la razón que fuese el cartel dejase de visualizarse, podemos abrir la paleta de comandos y ejecutar <strong lang="en">Dev Containers: Reopen in Container</strong></p>
<p><img-viewer><a href="10-reopen-in-container.png"><img src="10-reopen-in-container.png" alt="Captura de pantalla del comando Reopen in Container" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Al hacer esto se comenzará a crear el container con el entorno de desarrollo (este proceso puede demorar algunos minutos la primera vez).</p>
<p>Al terminar de crear el container y conectarse deberíamos tener en pantalla algo así:</p>
<p><img-viewer><a href="11-vscode-attached.png"><img src="11-vscode-attached.png" alt="Visual Studio Code conectado al contenedor de desarrollo" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Sabemos que nos encontramos conectados al container porque así lo indica en la parte inferior izquierda del VSCode (barra azul) y también el nombre del proyecto (barra superior central).</p>
<p>Ahora que ya nos encontramos dentro del entorno de desarrollo podemos, desde la terminal integrada del <span lang="en">Visual Studio Code</span>, ejecutar: <code>php -S 0.0.0.0:8080 -t public/</code> y con esto habremos puesto nuestra aplicación en ejecución. Si en una pestaña de nuestro navegador web introducimos la URL <a href="http://localhost:8080/" target="_blank">http://localhost:8080/</a> deberíamos ver:</p>
<p><img-viewer><a href="12-hello-world.png"><img src="12-hello-world.png" alt="Hola mundo, visualizado en el navegador al ingresar a la URL de nuestra aplicación" loading="lazy" decoding="async"></a></img-viewer></p>
<p><img-viewer><a href="13-php-builtin-webserver-running.png"><img src="13-php-builtin-webserver-running.png" alt="Terminal ejecutando el servidor web integrado de PHP" loading="lazy" decoding="async"></a></img-viewer></p>
<p>También deberíamos ser capaces de <i lang="spanglish">commitear</i> e incluso <i lang="spanglish">pushear</i> nuestra aplicación a <span lang="en">Github</span> desde dentro del container, ya que, este se encarga de replicar la configuración de git de nuestro usuario local.</p>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/e81fc42517186ce825c35294bb498b07173fd966" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="templates-imagenes"><a href="#templates-imagenes">Eligiendo templates o imágenes según nuestras necesidades</a></h2>
<p>El equipo de devcontainers mantiene una serie de templates e imágenes predefinidas para facilitar el despliegue de entornos de desarrollo.</p>
<p>En <a href="https://containers.dev/templates" target="_blank" hreflang="en">este link</a> pueden encontrar la lista completa de templates.</p>
<p>Y en <a href="https://github.com/devcontainers/images?tab=readme-ov-file" target="_blank" hreflang="en">este link</a> una lista de las imágenes mantenidas por el equipo de devcontainers.</p>
<p>En caso de que no encuentren ninguno que se ajuste a las necesidades exactas de su proyecto, siempre pueden optar por el template base de Debian y ajustarlo según necesiten.</p>
<p>Lo más básico y primero que nos enfocaremos en cambiar es la imagen a partir de la cual se genera el entorno de desarrollo.</p>
<p>Volviendo a nuestra aplicación de ejemplo, supongamos que la versión de PHP que queremos utilizar es la última disponible (a día de <abbr title="2025-03-04">hoy</abbr>, la versión 8.4).<br>
Durante la ejecución del <i lang="en">Wizard</i>, y utilizando el template de PHP las únicas versiones que se nos ofrecían eran la 8.0, 8.1 y 8.2.</p>
<p>Lo bueno es que al utilizar una tecnología bien establecida como lo son los containers contamos con muchísimas imágenes ya preparadas en distintos repositorios, que podemos utilizar sin problemas.</p>
<p>Primero verificamos la versión actual de PHP ejecutando, desde la terminal integrada del VSCode:</p>
<pre><code class="language-sh">php -v
</code></pre>
<p><img-viewer><a href="14-php-version-82.png"><img src="14-php-version-82.png" alt="Captura de pantalla de la terminal mostrando la versión de PHP actual, 8.2" loading="lazy" decoding="async"></a></img-viewer></p>
<p>En nuestro caso, utilizaremos la <a href="https://hub.docker.com/_/php" target="_blank" hreflang="en">imagen oficial de PHP</a>, proveniente de <a href="https://hub.docker.com/" target="_blank" hreflang="en" lang="en">Docker Hub</a>.</p>
<p>Desde Docker Hub, en la sección de tags buscamos el correspondiente a la versión 8.4.</p>
<p><img-viewer><a href="15-dockerhub-php.png"><img src="15-dockerhub-php.png" alt="Captura de pantalla de la imagen oficial de PHP en Docker Hub" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Ahora que ya tenemos el nombre de la imagen y tag que deseamos utilizar en nuestro <code>.devcontainer/devcontainer.json</code> modificamos el atributo <em lang="en">image</em>.</p>
<pre><code class="language-diff">{
        &quot;name&quot;: &quot;PHP&quot;,
        // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
-       &quot;image&quot;: &quot;mcr.microsoft.com/devcontainers/php:1-8.2-bullseye&quot;,
+       &quot;image&quot;: &quot;php:8.4-cli-bullseye&quot;,

        // Features to add to the dev container. More info: https://containers.dev/features.
        // &quot;features&quot;: {},

</code></pre>
<p>Luego de esto, desde la paleta de comandos, ejecutaremos <strong lang="en">Dev Containers: Rebuild Container</strong>.</p>
<p>Al hacer esto, el antiguo container se destruirá y se creará uno nuevo con los cambios que hemos realizado.</p>
<p>Una vez que el nuevo container ha sido creado, volvemos a revisar la versión de PHP.</p>
<p><img-viewer><a href="16-php-version-84.png"><img src="16-php-version-84.png" alt="Captura de pantalla de la terminal mostrando la versión actualizada de PHP, 8.4" loading="lazy" decoding="async"></a></img-viewer></p>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/c061b6510ea02539a714c5c47b4c3715281d35f3" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="features"><a href="#features">Agregando funcionalidades (Devcontainer Features)</a></h2>
<p>Ya tenemos la versión de PHP que queremos utilizar, pero si intentamos commitear nuestros cambios nos encontraremos un <em>pequeño</em> inconveniente.</p>
<p>La imagen oficial de PHP está diseñada para ser utilizada en entornos productivos, por lo que no cuenta con git instalado.</p>
<p>Por suerte contamos con un mecanismo muy simple para agregar herramientas adicionales a nuestros entornos de desarrollo, mediante las <i lang="en">Features</i> de devcontainers.</p>
<p>Para configurar la <i lang="en">features</i>, en la paleta de comandos escribimos <strong lang="en">Dev Container: Configure Container Features</strong>.</p>
<p>Esto nos desplegará una lista de <i lang="en">features</i> disponibles, similar a la que ya vimos al ejecutar inicialmente el <span lang="en">wizard</span>.</p>
<p><img-viewer><a href="17-devcontainer-features.png"><img src="17-devcontainer-features.png" alt="Lista de features disponibles desde el Visual Studio Code" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Aquí buscamos <strong lang="en">Git (from source)</strong> y marcamos su <span lang="en">checkbox</span>.</p>
<p><img-viewer><a href="18-devcontainers-features-git.png"><img src="18-devcontainers-features-git.png" alt="Captura de pantalla del feature de git en el Visual Studio Code" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Luego damos click a <strong>OK</strong> y en el siguiente paso elegimos la opción <strong lang="en">Keep Defaults</strong>.</p>
<p>Esto nos agregará una nueva sección en nuestro archivo de configuración.</p>
<pre><code class="language-diff">
        // &quot;customizations&quot;: {},

        // Use 'forwardPorts' to make a list of ports inside the container available locally.
-       &quot;forwardPorts&quot;: [8080]
+       &quot;forwardPorts&quot;: [8080],
+       &quot;features&quot;: {
+               &quot;ghcr.io/devcontainers/features/git:1&quot;: {}
+       }

        // Use 'postCreateCommand' to run commands after the container is created.
        // &quot;postCreateCommand&quot;: &quot;sudo chmod a+x \&quot;$(pwd)\&quot; &amp;&amp; sudo rm -rf /var/www/html &amp;&amp; sudo ln -s \&quot;$(pwd)\&quot; /var/www/html&quot;
</code></pre>
<p>Podemos revisar la lista completa de <span lang="en">Features</span> en <a href="https://containers.dev/features" target="_blank" hreflang="en">este link</a>.</p>
<p>Para reconstruir el container con el agregado de git damos click al botón <strong>Rebuild</strong> que nos debería haber aparecido abajo a la derecha o bien, en la paleta de comandos, ejecutamos <strong>Dev Containers: Rebuild Container</strong></p>
<p>Et voila! Una vez que el container se haya reconstruido ya podremos utilizar git.</p>
<p><img-viewer><a href="19-git-version.png"><img src="19-git-version.png" alt="Captura de pantalla de la terminal, ejecutando git exitosamente" loading="lazy" decoding="async"></a></img-viewer></p>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/5092a1acd9fe0b2ef26d48b24da73105f2c9020f" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="ciclo-de-vida"><a href="#ciclo-de-vida">Ciclo de vida del container de desarrollo, comandos y scripts</a></h2>
<p>Tener un entorno de desarrollo con la versión más nueva de nuestro lenguaje de programación y git está muy bien, pero todavía podemos llevarlo al siguiente nivel.</p>
<p>El único próximo paso lógico es agregar herramientas de <i lang="en">debugging</i>, porque, enfrentemos la realidad. Nuestra aplicación el día de mañana podría tener millones de usuarios activos. ¿A fin de cuentas, por qué alguien no querría una aplicación diseñada específicamente para saludar al mundo?</p>
<p>Pero junto con los usuarios llegan los pedidos de nuevas funcionalidades.</p>
<p>Saludar al vecino, saludos de cumpleaños, quizás una comunidad de yoga quiera un saludo al sol.</p>
<p>Y junto con las nuevas funcionalidades llegan los <i lang="en">bugs</i>.</p>
<p>Definitivamente, necesitamos agregar herramientas de <i lang="en">debugging</i> a nuestro entorno de desarrollo.</p>
<figure>
	<img src="20-premature-optimization-meme.jpg" alt="Meme de los Simpsons &quot;Skinner: Am I out of touch?&quot; sobre la optimización prematura">
	<figcaption>
		<a href="https://wiki.c2.com/?PrematureOptimization" target="_blank">Optimización prematura</a>, la causa de todos los males. - Donald Knuth.
	</figcaption>
</figure>
<p>Según la documentación de PHP en <span lang="en">Docker Hub</span>, instalar <a href="https://xdebug.org/" target="_blank" hreflang="en" lang="en">xdebug</a> es tan simple como ejecutar 2 comandos.</p>
<pre><code class="language-sh">pecl install xdebug
docker-php-ext-enable xdebug
</code></pre>
<p>Podríamos simplemente ejecutar esto en nuestra terminal y tendríamos la extensión instalada… Hasta que por alguna razón reconstruyamos nuestro container de desarrollo.</p>
<p>Podríamos dejar una nota en nuestro <em>README</em> indicando que luego de crearse el container deben ejecutarse dichos comandos… Si tan solo siguiera existiendo gente que leyera la documentación de los proyectos.</p>
<p>Por suerte para nosotros, devcontainers nos da una mejor solución.</p>
<p>Podemos configurarlo para que luego de crearse el container ejecute comandos que definamos.</p>
<p>Esto lo haremos mediante el atributo <em>postCreateCommand</em> de nuestro archivo de configuración.</p>
<pre><code class="language-diff">
        &quot;forwardPorts&quot;: [8080],
        &quot;features&quot;: {
                &quot;ghcr.io/devcontainers/features/git:1&quot;: {}
-       }
+       },

        // Use 'postCreateCommand' to run commands after the container is created.
-       // &quot;postCreateCommand&quot;: &quot;sudo chmod a+x \&quot;$(pwd)\&quot; &amp;&amp; sudo rm -rf /var/www/html &amp;&amp; sudo ln -s \&quot;$(pwd)\&quot; /var/www/html&quot;
+       &quot;postCreateCommand&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;

        // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
        // &quot;remoteUser&quot;: &quot;root&quot;
</code></pre>
<p>Una vez más reconstruimos nuestro container y revisamos que nuestros cambios hayan tenido éxito con <code>php -m | grep xdebug</code>.</p>
<p><img-viewer><a href="21-php-xdebug.png"><img src="21-php-xdebug.png" alt="Captura de pantalla de xdebug instalado correctamente" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Luego de ejecutar el comando la terminal devuelva <strong lang="en">xdebug</strong> es indicador de que la extensión se encuentra instalada y activa.</p>
<p>En la documentación de devcontainers pueden encontrar la <a href="https://containers.dev/implementors/json_reference/#lifecycle-scripts" target="_blank" hreflang="en">lista completa de los puntos del ciclo de vida del container en los que es posible ejecutar comandos personalizados</a>.</p>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/7c8a5cf28b80307a34f12b325469f140e1153d1b" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="montajes-puertos-variables-entorno"><a href="#montajes-puertos-variables-entorno">Montajes, puertos, variables de entorno y otras hierbas</a></h2>
<p>¡El desarrollo de nuestra aplicación va viento en popa! Pero algo no termina de sentirse correcto.</p>
<p>¿Cómo es posible que cada vez que abrimos nuestro container de desarrollo debamos iniciar manualmente nuestro servidor web?</p>
<p>¡Inaceptable!</p>
<p>Aprovecharemos el atributo <em>postStartCommand</em> para arrancar automáticamente nuestro web server.</p>
<pre><code class="language-diff">
    },

    // Use 'postCreateCommand' to run commands after the container is created.
-   &quot;postCreateCommand&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;
+   &quot;postCreateCommand&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;,
+
+   &quot;postStartCommand&quot;: &quot;php -S 0.0.0.0:8080 -t public/&quot;

    // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
    // &quot;remoteUser&quot;: &quot;root&quot;
</code></pre>
<p>Reconstruimos nuestro container y podremos acceder a nuestra aplicación en <a href="http://localhost:8080" target="_blank">http://localhost:8080</a> sin necesidad de ejecutar comandos adicionales de manera manual.</p>
<p>Mientras observamos con aires de victoria como nuestra aplicación saluda al mundo, un pensamiento nos ataca por la retaguardia sin previo aviso.</p>
<p>Un pensamiento que comenzó a formarse en lo más recóndito de nuestra mente, utilizando memorias distantes, ahora viene a por nosotros.</p>
<p>Buenas prácticas…</p>
<p>… <a href="https://12factor.net/" target="_blank" hreflang="en" lang="en">The 12 Factor App</a>, una de las convenciones de DevOps para sistemas cloud…</p>
<p>… <a href="https://12factor.net/dev-prod-parity" target="_blank" hreflang="en" lang="en">X Factor - Dev/prod parity</a>…</p>
<p>… El Built-in web server de PHP, tal como indica <a href="https://www.php.net/manual/en/features.commandline.webserver.php#features.commandline.webserver" target="_blank" hreflang="en">su documentación</a>, no debería ser utilizado en entornos productivos.</p>
<p><img-viewer><a href="22-php-builtin-webserver-docs-warning.png"><img src="22-php-builtin-webserver-docs-warning.png" alt="Captura de pantalla de la documetnación de PHP sobre su webserver" loading="lazy" decoding="async"></a></img-viewer></p>
<p>… Entonces, según el factor <i lang="en">Dev/Prod Parity</i> de los <i lang="en">12 Factor App</i>, tampoco deberíamos estar usándolo en nuestro entorno de desarrollo, ya que debemos buscar que nuestros ambientes de producción y desarrollo sean lo más similares posible…</p>
<p>😞</p>
<p>¡No importa! Algo como esto no va a detenernos.</p>
<p>Vamos a usar en nuestro entorno de desarrollo el mismo websever que utilizaremos en producción.</p>
<p>Existen varias opciones perfectamente válidas, pero hoy usaremos <a href="https://frankenphp.dev/" target="_blank" hreflang="en" lang="en">FrankenPHP</a>.</p>
<p>Para esto, eliminaremos el <em>postStartCommand</em> que acabamos de agregar y cambiaremos la imagen por la de FrankenPHP.</p>
<pre><code class="language-diff">
 {
    &quot;name&quot;: &quot;PHP&quot;,
    // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
-   &quot;image&quot;: &quot;php:8.4-cli-bullseye&quot;,
+   &quot;image&quot;: &quot;dunglas/frankenphp:1-php8.4&quot;,

    // Features to add to the dev container. More info: https://containers.dev/features.
    // &quot;features&quot;: {},
@@ -18,9 +18,7 @@
    },

    // Use 'postCreateCommand' to run commands after the container is created.
-   &quot;postCreateCommand&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;,
-
-   &quot;postStartCommand&quot;: &quot;php -S 0.0.0.0:8080 -t public/&quot;
+   &quot;postCreateCommand&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;

    // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
    // &quot;remoteUser&quot;: &quot;root&quot;

</code></pre>
<p>La imagen de FrankenPHP, por defecto, tiene su <a href="https://docs.docker.com/reference/dockerfile/#workdir" target="_blank" hreflang="en" lang="en"><i>WORKDIR</i></a> configurado en <code>/app/public/</code> asi que el proyecto intentará montarse allí. Pero nosotros ya contamos con un directorio <code>public/</code> por lo que deberemos configurar tanto el <i lang="en">working dir</i> como el punto de montaje.</p>
<pre><code class="language-diff">
    // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
    &quot;image&quot;: &quot;dunglas/frankenphp:1-php8.4&quot;,

+   &quot;workspaceMount&quot;: &quot;source=${localWorkspaceFolder},target=/app,type=bind,consistency=cached&quot;,
+   &quot;workspaceFolder&quot;: &quot;/app&quot;,
+
    // Features to add to the dev container. More info: https://containers.dev/features.
    // &quot;features&quot;: {},
</code></pre>
<p>Dentro de <em>wokspaceMount</em> estamos utilizando una variable propia de devcontainers (<code>${localWorkspaceFolder}</code>), para más información sobre las variables disponibles y como utilizarlas pueden visitar <a href="https://containers.dev/implementors/json_reference/#variables-in-devcontainerjson" target="_blank" hreflang="en">este link</a>.</p>
<p>Además, siguiendo la <a href="https://frankenphp.dev/docs/config/#environment-variables" target="_blank" hreflang="en">documentación de FrankenPHP</a> agregaremos algunas variables de entorno para configurar nuestro webserver.</p>
<pre><code class="language-diff">

    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    &quot;forwardPorts&quot;: [8080],
+
+   &quot;containerEnv&quot;: {
+       &quot;SERVER_NAME&quot;: &quot;http://localhost:8080&quot;,
+       &quot;CADDY_GLOBAL_OPTIONS&quot;: &quot;auto_https off&quot; // Disable automatic HTTPS
+   },
+
    &quot;features&quot;: {
        &quot;ghcr.io/devcontainers/features/git:1&quot;: {}
    },
</code></pre>
<p>El atributo <em>forwardPorts</em> no lo modificaremos ya que estamos configurando nuestro nuevo webserver para escuchar en el puerto 8080 por lo que queremos seguir exponiendo este mismo puerto.</p>
<p>Por último configuraremos <em>overrideCommand</em> para evitar que devcontainers sobreescriba el proceso default de la imagen, ejecutar el webserver, ya que eso es exactamente lo que queremos que haga.</p>
<pre><code class="language-diff">
    // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
    &quot;image&quot;: &quot;dunglas/frankenphp:1-php8.4&quot;,

+   &quot;overrideCommand&quot;: false,
+
    &quot;workspaceMount&quot;: &quot;source=${localWorkspaceFolder},target=/app,type=bind,consistency=cached&quot;,
    &quot;workspaceFolder&quot;: &quot;/app&quot;,

</code></pre>
<p>El resultado final debería ser que nuestro <code>.devcontainer/devcontainer.json</code> se vea así:</p>
<pre><code class="language-json">{
    &quot;name&quot;: &quot;Helloer&quot;,
    &quot;image&quot;: &quot;dunglas/frankenphp:1-php8.4&quot;,

    &quot;overrideCommand&quot;: false,

    &quot;workspaceMount&quot;: &quot;source=${localWorkspaceFolder},target=/app,type=bind,consistency=cached&quot;,
    &quot;workspaceFolder&quot;: &quot;/app&quot;,

    &quot;forwardPorts&quot;: [8080],

    &quot;containerEnv&quot;: {
        &quot;SERVER_NAME&quot;: &quot;http://localhost:8080&quot;,
        &quot;CADDY_GLOBAL_OPTIONS&quot;: &quot;auto_https off&quot; // Disable automatic HTTPS
    },

    &quot;features&quot;: {
        &quot;ghcr.io/devcontainers/features/git:1&quot;: {}
    },

    &quot;postCreateCommand&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;
}

</code></pre>
<p>Reconstruimos nuestro container y una vez más tenemos nuestra aplicación funcionando en nuestro entorno de desarrollo, esta vez utilizando el mismo webserver que utilizaríamos en producción.</p>
<blockquote>
<p>Al haber cambiado el <em>workspaceFolder</em> en la configuración es posible que luego de recrear el container el <span llang="en">Visual Studio Code</span> falle al intentar conectarse. Si esto ocurre solo debemos dar click a <strong lang="en">Open workspace</strong> y buscar el directorio que hemos configurado para ser el nuevo <em>workspaceFolder</em> (<code>/app/</code>).</p>
</blockquote>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/https://github.com/iyaki/devcontainers-guide-app-example/tree/cc57ef417d3f753114bada6d676c8cd4a99a658a" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="intermedio"><a href="#intermedio">Intermedio</a></h2>
<p>Por fin, nuestro entorno de desarrollo es perfecto y estamos listos para seguir agregando valor a la vida de millones mediante nuestra aplicación.</p>
<p>Nuestra siguiente funcionalidad será recibir un número como parámetro y saludar indicando si el número es par o impar.</p>
<p><a href="https://sahithyan.dev/post/the-is-odd-npm-package-meme" target="_blank" hreflang="en">Pero como somos desarrolladores que se respetan a sí mismos y siguen las mejores prácticas de comunidades importantes como la de Javascript sabemos que para realizar semejante proeza necesitaremos utilizar alguna biblioteca de terceros</a>.</p>
<p>En PHP se utiliza el gestor de dependencias <a href="https://getcomposer.org/" target="_blank" hreflang="en" lang="en">composer</a> para gestionar bibliotecas de terceros en nuestra aplicación.</p>
<p>Procedemos a instalar la biblioteca que necesitamos ejecutando en la terminal integrada, <code>composer require kylelamse/is-even</code>.</p>
<p><img-viewer><a href="23-php-composer-require-iseven.png"><img src="23-php-composer-require-iseven.png" alt="Captura de pantalla de la terminal ejecutando el comando anterior y fallando" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Otra herramienta que nos falta.</p>
<p>🫠</p>
<blockquote>
<p>Si trabajan como DevOps (si, si, ya se que ne realidad no es un rol), SysAdmin, Platfom engineer, etc lo que van a leer no es ninguna novedad; pero si son programadores es importante que sepan esto.<br>
Este tipo de trabajos, aunque gratificantes cuando las cosas finalmente funcionan, pueden ser sumamente tediosos e iterativos.<br>
Aprecien que alguien se preocupe por ustedes e intente facilitarles las cosas. Si ven un DevOps cansado ofrezcanle un cafe y recuereden, en <a href="https://es.wikipedia.org/wiki/D%C3%ADa_del_Administrador_de_Sistemas_Inform%C3%A1ticos" target="_blank">sú día</a> comprar unas facturas para compartir o algo por el estilo.</p>
</blockquote>
<p>Llegados a este punto creo que ya todos tenemos bastante claro como proceder.</p>
<p>Buscamos si existe una <i lang="en">feature</i> de composer.</p>
<p><img-viewer><a href="24-devcontainers-features-list-composer.png"><img src="24-devcontainers-features-list-composer.png" alt="Lista de features de devcontainers, buscando composer" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Sí, existe.</p>
<p>La agregamos a nuestro archivo de configuración y reconstruimos el container.</p>
<p>O tal vez nó…</p>
<p>Quizás para cuando estén leyendo esta guía este problema ya se haya arreglado, pero <abbr title="2025-03-04">hoy</abbr>, al agregar el feature de composer ocurre lo siguiente:</p>
<p><img-viewer><a href="25-devcontainer-error.png"><img src="25-devcontainer-error.png" alt="Captura de pantalla del Visual Studio Code fallando al intentar compilar el container" loading="lazy" decoding="async"></a></img-viewer></p>
<p>El container falla.</p>
<h2 id="debugging"><a href="#debugging">Debugging y solución de errores</a></h2>
<p>Cuando se encuentren con un error que impide que el container compile correctamente, hay algunas cosas que pueden probar.</p>
<p>Primero pueden dar click a <strong lang="en">Retry</strong>. Si el error proviene de alguna causa externa y temporal (por ejemplo, si se cortó el internet mientras el container se creaba) con esto debería solucionarse.</p>
<p>Si <i lang="en">Retry</i> no funciona, pueden dar click en <strong lang="en">Edit devcontainer.json Locally</strong> y comenzar a leer, de abajo hacia arriba, el log que se abrirá del lado derecho de la pantalla, mientras rezan para que la causa del error sea comprensible.</p>
<p><img-viewer><a href="26-devcontainer-error-log.png"><img src="26-devcontainer-error-log.png" alt="Log de errores en la compilación del container" loading="lazy" decoding="async"></a></img-viewer></p>
<blockquote>
<p>Si conocen mejores maneras de <span lang="spanglish">debuggear</span> estas configuraciones, por favor déjenlo abajo, en los comentarios. Yo ya perdí bastantes horas de mi vida con errores de lo más simple, pero todavía están a tiempo de ayudar a personas que se estén iniciando en este mundo.</p>
</blockquote>
<p>El único consejo real que puedo darles es que <span lang="spanglish">commiteen</span> tan seguido como puedan. Cada vez que tengan una versión de la configuración que compila (aunque todavía no funcione al 100% como ustedes quieren) <span lang="spanglish">commiteenla</span>.</p>
<p>En este caso el problema está claramente en el <i lang="en">feature</i> de composer.</p>
<p>Si revisamos <a href="https://github.com/devcontainers-extra/features/tree/main/src/composer" target="_blank" hreflang="en">la documentación del <i lang="en">feature</i></a> vemos que el único parámetro que acepta es para configurar la versión que se instalará, por lo que no parece que haya mucho que podamos hacer desde allí.</p>
<p>En nuestro caso será más fácil buscar una manera de instalar composer por algún otro medio, pero si se encuentran con algún problema persistente que los bloquea pueden recurrir a los <span lang="en">Issues</span> de <span lang="en">Github</span> del repositorio que corresponda.</p>
<p>Composer cuneta con una <a href="https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md" target="_blank" hreflang="en">breve guía</a> para realizar instalaciones automáticas. Siguiendo sus instrucciones (y adaptándolas un poco) haremos lo siguiente.</p>
<p>Creamos un nuevo archivo <code>.devcontainer/composer.sh</code>.</p>
<pre><code class="language-sh">#!/bin/sh

EXPECTED_CHECKSUM=&quot;$(php -r 'copy(&quot;https://composer.github.io/installer.sig&quot;, &quot;php://stdout&quot;);')&quot;
php -r &quot;copy('https://getcomposer.org/installer', 'composer-setup.php');&quot;
ACTUAL_CHECKSUM=&quot;$(php -r &quot;echo hash_file('sha384', 'composer-setup.php');&quot;)&quot;

if [ &quot;$EXPECTED_CHECKSUM&quot; != &quot;$ACTUAL_CHECKSUM&quot; ]
then
    &gt;&amp;2 echo 'ERROR: Invalid installer checksum'
    rm composer-setup.php
    exit 1
fi

php composer-setup.php --quiet --install-dir=/usr/local/bin --filename=composer
RESULT=$?
rm composer-setup.php
exit $RESULT
</code></pre>
<p>Y desde la terminal integrada le damos permisos de ejecución.</p>
<pre><code class="language-sh">chmod +x .devcontainer/composer.sh
</code></pre>
<p>Luego configuramos nuestro <code>.devcontainer/devcontainer.json</code> para ejecutar el script.</p>
<pre><code class="language-diff">
        &quot;ghcr.io/devcontainers/features/git:1&quot;: {}
    },

-   &quot;postCreateCommand&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;
+   &quot;postCreateCommand&quot;: {
+       &quot;install xdebug&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;,
+       &quot;install composer&quot;: &quot;.devcontainer/composer.sh&quot;
+   }

    // Configure tool-specific properties.
    // &quot;customizations&quot;: {},
</code></pre>
<p>¡Ahora sí!</p>
<p>Instalamos la dependencia <em>is-even</em></p>
<pre><code class="language-sh">composer require kylelamse/is-even
</code></pre>
<p>Esto debería crear los archivos <code>compose.json</code>, <code>composer.lock</code> y el directorio <code>vendor/</code>, donde se encuentra el código fuente de la dependencia que hemos instalado</p>
<p>Crearemos manualmente un archivo <code>.gitignore</code> para evitar commitear la carpeta <code>vendor/</code>.</p>
<pre><code class="language-sh">printf &quot;/vendor\n&quot; &gt; .gitignore
</code></pre>
<p>Y modificamos nuestro archivo <code>public/index.php</code> para agregar nuestra nueva funcionalidad:</p>
<pre><code class="language-diff">
-Hola mundo
+&lt;?php
+
+declare(strict_types=1);
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$number = $_GET['number'] ?? null;
+$number = is_numeric($number) ? (int) $number : null;
+
+$helloed = match (true) {
+    ! is_int($number) =&gt; 'mundo',
+    is_even($number) =&gt; 'par',
+    default =&gt; 'impar',
+};
+
+echo &quot;Hola, $helloed&quot;;
</code></pre>
<p>¡Perfecto! Nuestra aplicación ya puede diferenciar números pares e impares de manera correcta y cortes.</p>
<p><img src="27-hola-world-again.png" alt="Captura de pantalla de la aplicación mostrando el mensaje &quot;Hola mundo&quot;">
Aplicación saludando sin parámetro <em>numero</em>.</p>
<p><img src="28-hello-odd.png" alt="Captura de pantalla de la aplicación mostrando el mensaje &quot;Hola impar&quot;">
Aplicación saludando con parámetro <em>numero</em> en 1 (número impar)</p>
<p><img src="29-hello-even.png" alt="Captura de pantalla de la aplicación mostrando el mensaje &quot;Hola par&quot;">
Aplicación saludando con parámetro <em>numero</em> en 2 (número par)</p>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/0b732647467180125739184120d0967df1e885c6" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="customizations"><a href="#customizations">Customizations</a></h2>
<p>Algunas de las herramientas y servicios que poseen integración con devcontainers permiten configuraciones específicas para ayudar a configurar ciertos aspectos de nuestro entorno de desarrollo.</p>
<p>La integración con <span lang="en">Visual Studio Code</span> nos permite definir preferencias que aplicarán al proyecto dentro de nuestro entorno de desarrollo y extensiones que se instalarán de manera automática.</p>
<p>Siendo que estamos trabajando en un proyecto PHP puede ser deseable agregar lo siguiente a nuestro archivo de configuración para tener un mejor soporte del lenguaje.</p>
<pre><code class="language-diff">
    &quot;postCreateCommand&quot;: {
        &quot;install xdebug&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;,
        &quot;install composer&quot;: &quot;.devcontainer/composer.sh&quot;
-   }
+   },

+   &quot;customizations&quot;: {
+       &quot;vscode&quot;: {
+           &quot;settings&quot;: {
+               &quot;files.eol&quot;: &quot;\n&quot;,
+               &quot;files.trimFinalNewlines&quot;: true,
+               &quot;intelephense.completion.fullyQualifyGlobalConstantsAndFunctions&quot;: true,
+               &quot;intelephense.completion.triggerParameterHints&quot;: true,
+               &quot;intelephense.environment.shortOpenTag&quot;: false,
+               &quot;php.suggest.basic&quot;: false
+           },
+           &quot;extensions&quot;: [
+               &quot;bmewburn.vscode-intelephense-client&quot;,
+               &quot;xdebug.php-debug&quot;
+           ]
+       }
+   }
 }
</code></pre>
<p>Estas preferencias y extensiones afectarán a todos los que utilicen el entorno de desarrollo de devcontainer. Son especialmente útiles para definir <em>sensible defaults</em> que ayuden a todos los desarrolladores.</p>
<p>Además de esta opción propia de devcontainer, el <span lang="en">Visual Studio Code</span> nos brinda una manera de configurar, solo para nosotros, extensiones que queremos que se instalen siempre de manera automática en todos nuestros containers de desarrollo.</p>
<p>Para configurar esto nos dirigimos a las preferencias de VScode (Ctrl+Shift+p &gt; <strong>Preferences: Open Settings (UI)</strong>).<br>
Y una vez allí buscamos <em>dev.containers.defaultExtensions</em>. Con el botón <strong lang="en">Add Item</strong> podremos agregar las extensiones que deseemos.</p>
<p><img-viewer><a href="30-vscode-devcontainer-default-extensions.png"><img src="30-vscode-devcontainer-default-extensions.png" alt="Captura de pantalla de la configuración del Visual Studio Code" loading="lazy" decoding="async"></a></img-viewer></p>
<p>Los nombres de las extensiones que debemos agregar corresponden a los IDs de las extensiones. Para saber el ID de una extensión la buscamos en la pestaña de extensiones del VSCode (Ctrl+Shift+x), damos click al icono de engranaje que hay al lado de los botones de instalar y elegimos la opción <strong>Copy Extension ID</strong>. Esto copiará el ID de la extensión al portapapeles.</p>
<p><img-viewer><a href="31-vscode-extensions-copilot.png"><img src="31-vscode-extensions-copilot.png" alt="Captura de pantalla de una extensión del Visual Studio Code con la opción de copiar su ID desplegada" loading="lazy" decoding="async"></a></img-viewer></p>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/ba8cf78bc09860263976c789ed6f634630a16e22" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="dockerfile"><a href="#dockerfile">Back to the basics, Dockerfile</a></h2>
<p>Con todo lo que hicimos hasta ahora debería ser suficiente para poder trabajar sin problemas en una amplia variedad de proyectos.</p>
<p>Pero, en caso de despliegues mas complejos o si necesitásemos contar con muchas herramientas instaladas que no estén disponibles como <i lang="en">features</i>, escalar la creación de estos entornos de desarrollo utilizando exclusivamente características de devcontainers puede ser inviable.</p>
<p>A partir de este punto trabajaremos en gran medida con características propias de Docker. El objetivo de esta guía no es dar una introducción a Docker ni Docker Compose por lo que no profundizaremos en aspectos que no sean propios de devcontainers.</p>
<p>Entonces, ¿Qué hacemos si devcontainers nos queda “corto”?</p>
<p>Recurrir a características nativas de Docker.</p>
<p>Devcotnainers brinda la posibilidad de trabajar directamente con archivos Dockerfile y compose.yaml.</p>
<p>Comencemos moviendo las configuraciones que actualmente tenemos en <code>.devcontainer/devcontainer.json</code> a un Dockerfile.</p>
<p>Para esto creamos el archivo <code>.devcontainer/Dockerfile</code> y escribimos:</p>
<pre><code class="language-dockerfile">FROM dunglas/frankenphp:1-php8.4

COPY --from=composer /usr/bin/composer /usr/bin/composer

RUN pecl install xdebug \
    &amp;&amp; docker-php-ext-enable xdebug \
    &amp;&amp; apt-get update \
    &amp;&amp; apt-get install -y --no-install-recommends \
        git \
        unzip \
    &amp;&amp; apt-get clean \
    &amp;&amp; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
</code></pre>
<p>y modificamos nuestro <code>.devcontainer/devcontainer.json</code></p>
<pre><code class="language-diff">
 {
    &quot;name&quot;: &quot;Helloer&quot;,
-   &quot;image&quot;: &quot;dunglas/frankenphp:1-php8.4&quot;,
+   &quot;build&quot;: {
+       &quot;dockerfile&quot;: &quot;Dockerfile&quot;
+   },

    &quot;overrideCommand&quot;: false,

@@ -14,15 +16,6 @@
        &quot;CADDY_GLOBAL_OPTIONS&quot;: &quot;auto_https off&quot; // Disable automatic HTTPS
    },

-   &quot;features&quot;: {
-       &quot;ghcr.io/devcontainers/features/git:1&quot;: {}
-   },
-
-   &quot;postCreateCommand&quot;: {
-       &quot;install xdebug&quot;: &quot;pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug&quot;,
-       &quot;install composer&quot;: &quot;.devcontainer/composer.sh&quot;
-   },
-
    &quot;customizations&quot;: {
        &quot;vscode&quot;: {
            &quot;settings&quot;: {
</code></pre>
<p>Reconstruimos el container y todo debería seguir funcionando como hasta ahora.</p>
<p>Este ejemplo es bastante simple, pero es posible configurar <i lang="en">context</i>, <i lang="en">arguments</i> y <i lang="en">target</i> y prácticamente todos los parámetros que podríamos utilizar al ejecutar <code>docker build</code>. La lista completa de opciones disponibles se encuentra en <a href="https://containers.dev/implementors/json_reference/#image-specific" target="_blank" hreflang="en">este link</a>.</p>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/d1ece3c1b5bd943c6630ec794b0f7590cbcec1ca" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="docker-compose"><a href="#docker-compose">Múltiples containers con Docker compose</a></h2>
<p>¿Por qué necesitaríamos varios containers en desarrollo?</p>
<p>Simple, los containers están diseñados para ejecutar únicamente un servicio. Y si bien existen maneras de ejecutar múltiples servicios en un mismo container; si queremos agregar una base de datos a nuestro entorno de desarrollo, suele ser más simple agregar un nuevo container para esta base de datos y orquestar su creación con docker compose.</p>
<p>Así que eso haremos.</p>
<p>Agregaremos una base de datos a nuestro proyecto y la utilizaremos para configurar saludos personalizados.</p>
<p>Comenzaremos migrando las configuraciones presentes en nuestro <code>.devcontiner/devcontainer.json</code> a un archivo de docker compose.</p>
<p>Primero crearemos el archivo <code>.devcontainer/compose.yaml</code>.</p>
<pre><code class="language-yaml">services:

  app:
    build:
      context: ..
      dockerfile: .devcontainer/Dockerfile
    working_dir: /app
    volumes:
      - ..:/app
      - caddy_data:/data
      - caddy_config:/config
    environment:
      SERVER_NAME: &quot;${SERVER_NAME:-http://localhost:8080}&quot;
      CADDY_GLOBAL_OPTIONS: &quot;auto_https off&quot;
    tty: true

volumes:
  caddy_data:
  caddy_config:
</code></pre>
<p>Y modificamos <code>.devconytainer/devcontainer.json</code>.</p>
<pre><code class="language-diff">
 {
    &quot;name&quot;: &quot;Helloer&quot;,
-   &quot;build&quot;: {
-       &quot;dockerfile&quot;: &quot;Dockerfile&quot;
-   },
+
+   &quot;dockerComposeFile&quot;: &quot;compose.yaml&quot;,
+   &quot;service&quot;: &quot;app&quot;,
+   &quot;workspaceFolder&quot;: &quot;/app&quot;,

    &quot;overrideCommand&quot;: false,

-   &quot;workspaceMount&quot;: &quot;source=${localWorkspaceFolder},target=/app,type=bind,consistency=cached&quot;,
-   &quot;workspaceFolder&quot;: &quot;/app&quot;,
-
    &quot;forwardPorts&quot;: [8080],

-   &quot;containerEnv&quot;: {
-       &quot;SERVER_NAME&quot;: &quot;http://localhost:8080&quot;,
-       &quot;CADDY_GLOBAL_OPTIONS&quot;: &quot;auto_https off&quot; // Disable automatic HTTPS
-   },
-
    &quot;customizations&quot;: {
        &quot;vscode&quot;: {
            &quot;settings&quot;: {

</code></pre>
<p>Una vez más reconstruimos el container y todo debería seguir funcionando como lo hacía hasta ahora.</p>
<p>Agregamos el servicio que contendrá la base de datos en nuestro archivo `.devcontainer/compose.yaml’</p>
<pre><code class="language-diff">
+x-db-default-params: &amp;db-default-params
+  MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-SUPER_SECRET_ROOT_PASSWORD}
+  MARIADB_DATABASE: ${DB_DATABASE:-helloer}
+  MARIADB_USER: ${DB_USER:-app_user}
+  MARIADB_PASSWORD: ${DB_PASSWORD:-SUPER_SECRET_PASSWORD}
+
 services:

   app:
@@ -10,10 +16,20 @@ services:
       - caddy_data:/data
       - caddy_config:/config
     environment:
+      &lt;&lt;: *db-default-params
       SERVER_NAME: &quot;${SERVER_NAME:-http://localhost:8080}&quot;
       CADDY_GLOBAL_OPTIONS: &quot;auto_https off&quot;
     tty: true

+  db:
+    image: mariadb:11.4-noble
+    restart: unless-stopped
+    volumes:
+      - mariadb:/var/lib/mysql
+    environment:
+      &lt;&lt;: *db-default-params
+
 volumes:
   caddy_data:
   caddy_config:
+  mariadb:
</code></pre>
<p>Configuramos <code>.devcontainer/devcontainer.json</code> par aunque inicie el servicio.</p>
<pre><code class="language-diff">
@@ -4,6 +4,7 @@
    &quot;dockerComposeFile&quot;: &quot;compose.yaml&quot;,
    &quot;service&quot;: &quot;app&quot;,
    &quot;workspaceFolder&quot;: &quot;/app&quot;,
+   &quot;runServices&quot;: [&quot;db&quot;],

    &quot;forwardPorts&quot;: [8080],
</code></pre>
<p>E instalamos la extensión de mysql para PHP y el cliente de mariadb en nuestro container desde <code>.devcontainer/Dockerfile</code>.</p>
<pre><code class="language-diff">
@@ -4,9 +4,11 @@ COPY --from=composer /usr/bin/composer /usr/bin/composer

 RUN pecl install xdebug \
     &amp;&amp; docker-php-ext-enable xdebug \
+    &amp;&amp; docker-php-ext-install mysqli \
     &amp;&amp; apt-get update \
     &amp;&amp; apt-get install -y --no-install-recommends \
         git \
+        mariadb-client \
         unzip \
     &amp;&amp; apt-get clean \
     &amp;&amp; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
</code></pre>
<p>Reconstruimos los containers y ya estamos listos para agregar saludos personalizados.</p>
<p>Empezamos creando la tabla de saludos y agregando algunos registros desde la terminal integrada.</p>
<pre><code class="language-sh">mariadb -h db -u &quot;$MARIADB_USER&quot; -p&quot;$MARIADB_PASSWORD&quot; &quot;$MARIADB_DATABASE&quot; -e '
CREATE TABLE greetings (
  name varchar(255),
  greeting varchar(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE utf8_unicode_ci;'
mariadb -h db -u &quot;$MARIADB_USER&quot; -p&quot;$MARIADB_PASSWORD&quot; &quot;$MARIADB_DATABASE&quot; -e '
INSERT INTO greetings
  (name, greeting)
VALUES
  (&quot;Marta&quot;, &quot;Doña&quot;),
  (&quot;Graciela&quot;, &quot;Señora&quot;),
  (&quot;Laura&quot;, &quot;Señorita&quot;),
  (&quot;Aureliano&quot;, &quot;Coronel&quot;),
  (&quot;Gervasio&quot;, &quot;Don&quot;),
  (&quot;Ezequiel&quot;, &quot;Rey&quot;),
  (&quot;Matias&quot;, &quot;Bro&quot;)
;'
</code></pre>
<p>Agregamos la nueva funcionalidad al código de nuestra aplicación.</p>
<pre><code class="language-diff">
@@ -7,7 +7,32 @@ require __DIR__ . '/../vendor/autoload.php';
 $number = $_GET['number'] ?? null;
 $number = is_numeric($number) ? (int) $number : null;

+$name = (string) ($_GET['name'] ?? null);
+
+$custom = null;
+if ($name) {
+    $db = new mysqli(
+        'db',
+        getenv('MARIADB_USER'),
+        getenv('MARIADB_PASSWORD'),
+        getenv('MARIADB_DATABASE')
+    );
+
+    $query = &lt;&lt;&lt;SQL
+        SELECT greeting FROM greetings
+        WHERE name = ?
+        LIMIT 1
+    SQL;
+
+    $result = $db-&gt;execute_query($query, [$name])-&gt;fetch_all(MYSQLI_ASSOC);
+
+    if ($result) {
+        $custom = $result[0]['greeting'];
+    }
+}
+
 $helloed = match (true) {
+    $custom !== null =&gt; $custom,
     ! is_int($number) =&gt; 'mundo',
     is_even($number) =&gt; 'par',
     default =&gt; 'impar',
</code></pre>
<p>Y ya tenemos saludos personalizados y configurables mediante la base de datos.</p>
<p><img-viewer><a href="32-hello-bro.png"><img src="32-hello-bro.png" alt="Captura de pantalla de nuestra aplicación respondiendo &quot;Hola Bro&quot; cuando se le envía el nombre Matías como parámetro" loading="lazy" decoding="async"></a></img-viewer></p>
<blockquote>
<p>Si revisan el repositorio de <em>templates</em> de devcontainers encontrarán uno diseñado para aplicaciones <a href="https://github.com/devcontainers/templates/tree/main/src/php-mariadb" target="_blank">PHP con MariaDB</a>. Es bastante similar a la solución a la que hemos llegado pero puede ser interesante verlo y buscar que hemos hecho distinto y por qué.</p>
</blockquote>
<p style="text-align: center; margin-block: 3em;"><a href="https://github.com/iyaki/devcontainers-guide-app-example/tree/b933b971fcad6b457dfcef6512da31925d6172b1" target="_blank">Link al repositorio del proyecto de ejemplo con los cambios hasta este punto de la guía</a>.</p>
<h2 id="dotfiles"><a href="#dotfiles">Sintiendonos como en casa con dotfiles</a></h2>
<p>Aunque no es una característica propiamente de devcontainers, sino del <span lang="en">Visual Studio Code</span>, hay una cosa más puede ayudarnos a que nuestro entorno de desarrollo nos resulte más cómodo: el uso de <a href="https://wiki.archlinux.org/title/Dotfiles_(Espa%C3%B1ol)" target="_blank" hreflang="en" lang="en">dotfiles</a>.</p>
<p>En linux es bastante común versionar ciertas configuraciones de las computadoras para poder tenerlas sincronizadas entre distintos equipos. A estos repositorios donde se versionan estas configuraciones se los suele llamar <i lang="en">dotfiles</i>.</p>
<p>Suelen utilizarse para modificar la apariencia de la terminal, agregas aliases, funciones, etc (casi siempre nos ayudará a ser más eficientes trabajando con la terminal).</p>
<p>Yo, por ejemplo, tengo mis dotfiles en <span lang="en">Github</span> (<a href="https://github.com/iyaki/dotfiles" target="_blank">link al repositorio</a>).</p>
<p>El VSCode puede configurarse para <a href="https://code.visualstudio.com/docs/devcontainers/containers#_personalizing-with-dotfile-repositories" target="_blank" hreflang="en">clonar estos repositorios y utilizar sus configuraciones</a>.</p>
<p>Para esto utilizaremos las siguientes configuraciones:</p>
<ul>
<li><strong>dotfiles.repository</strong>: Aquí debemos escribir la <abbr title="Uniform Resource Identifier">URI</abbr> del repositorio que deseamos clonar, en mi caso utilizo <code>https://github.com/iyaki/dotfiles</code>.</li>
<li><strong>dotfiles.targetPath</strong>: El directorio donde se clonará el repositorio. Por defecto <code>~/dotfiles</code>.</li>
<li><strong>dotfiles.installCommand</strong>: Comando a ejecutar luego de clonar el repositorio. Aquí generalmente se ejecuta un script que mueve o “linkea” los archivos de nuestro repositorio a las ubicaciones donde los archivos de configuración deben encontrarse para surtir efecto. Por defecto intentará ejecutar el primero de estos archivos que encuentre dentro del repositorio (en orden) <code>install.sh</code>, <code>install</code>, <code>bootstrap.sh</code>, <code>bootstrap</code>, <code>setup.sh</code>, <code>setup</code>.</li>
</ul>
<h2 id="impresiones-finales"><a href="#impresiones-finales">Impresiones finales</a></h2>
<p>Hemos experimentado con múltiples formas de configurar devcontainers, iniciando por las más simples hasta llegar a las más complejas.</p>
<p>A medida que la complejidad del deploy crece, el usar devcontainers y no simplemente las tecnologías en las que se basa (Docker compose) va perdiendo sentido, aunque la integración que brinda con ciertas herramientas, como el <span lang="en">Visual Studio Code</span>, puede seguir siendo interesante.</p>
<p>Por desgracia, la complejidad no puede ser eliminada, pero herramientas como esta nos ayudan a poder tenerla más ordenada e incluso “escondida” de quienes dependen de ella para poder realizar su trabajo.</p>
<p>Esto ha sido todo. Espero que les sea de utilidad y como siempre, por dudas, preguntas, consejos, recomendaciones o lo que sea que quieran compartir, tienen disponible la caja de comentarios o pueden escribirme a <a href="mailto:me@iyaki.ar" target="_blank">me@iyaki.ar</a>.</p>
<p>Hasta pronto!</p>

<script type="module" defer>
import hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js';
import diff from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/languages/diff.min.js';
import php from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/languages/php.min.js';
import json from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/languages/json.min.js';
import yaml from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/languages/yaml.min.js';
import dockerfile from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/languages/dockerfile.min.js';
import bash from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/languages/bash.min.js';
hljs.registerLanguage('diff', diff);
hljs.registerLanguage('php', php);
hljs.registerLanguage('json', json);
hljs.registerLanguage('yaml', yaml);
hljs.registerLanguage('dockerfile', dockerfile);
hljs.registerLanguage('bash', bash);
hljs.highlightAll()
</script>
<script type="module" defer>
import ImgViewer from '/components/ImgViewerElement.js'
customElements.define('img-viewer', ImgViewer);
</script>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20250304_devcontainers_de_cero_a_cien/"/><updated>2025-03-04T01:13:04Z</updated><published>2025-03-04T01:13:04Z</published></entry><entry><id>https://iyaki.ar/posts/20240503_LLMs_como_hypermedia_clients/</id><title><![CDATA[La IA conversacional como hypermedia client]]></title><summary><![CDATA[Algunas pruebas utilizando IA para consumir APIs.]]></summary><content type="html"><![CDATA[<blockquote>Algunas pruebas utilizando IA para consumir APIs.</blockquote><h1>La IA conversacional como <span lang="en">hypermedia client</span></h1>
<p><time datetime="2024-05-03">03/05/2024</time></p>
<h2>Motivación</h2>
<p>El proyecto <a href="https://htmx.org/" target="_blank" hreflang="en">htmx</a> siempre me llamó mucho la atención, me parece una propuesta (aunque aún no he tenido la oportunidad de probar esta biblioteca en escenarios reales) de lo más interesante. Pero aún más interesantes me parecen los <a href="https://htmx.org/essays/" target="_blank" hreflang="en">ensayos</a> que su equipo escribe (suelen ser bastante cortos, que la palabra ensayo no los desaliente de ir a leerlos).</p>
<p>De entre todos sus ensayos, por alguna razón el de <a href="https://htmx.org/essays/hypermedia-clients/" target="_blank" lang="en" hreflang="en">Hypermedia Clients</a> quedó grabado en mi cerebro y se volvió una referencia muy importante para mí a la hora de pensar en como diseñamos aplicaciones web y como estas interactúan, tanto con humanos como con otras aplicaciones.</p>
<p>No es el primero de sus ensayos que trata el tema de las <span lang="en">Hypermedia APIs</span> (forma <em lang="en">cool</em> de decir <a href="https://en.wikipedia.org/wiki/HATEOAS" target="_blank" hreflang="en">HAETOAS</a> sin asustar a la gente), ya que es una temática recurrente e importante para ellos, por lo que también tienen escritos al respecto en el <a href="https://htmx.org/essays/hypermedia-driven-applications/" target="_blank" hreflang="en"><time datetime="2022">2022</time></a> y <a href="https://htmx.org/essays/hypermedia-apis-vs-data-apis/" target="_blank" hreflang="en"><time datetime="2021">2021</time></a>. Pero <span lang="en">Hypermedia Clients</span>, de <time datetime="2023-01-28">fines de enero del 2023</time>, fue el primero en visibilizar y hacer notoria la relevancia que realmente tiene el cliente y no centrarse solo en los aspectos del server.</p>
<p>Casi a la par de la publicación de dicho ensayo, el mundo se veía revolucionado por uno de los, tal vez mayores, pero sin duda, más impactantes, avances tecnológicos con ChatGPT alcanzando los cien millones de usuarios luego de solo 2 meses online tras demostrar sus increíbles capacidades.</p>
<p>Desde entonces comencé a rumiar la idea de que una inteligencia artificial podría funcionar muy bien como nexo entre aplicaciones web que han sido diseñadas para ser utilizadas por humanos (y que utilizan fuertemente los conceptos de <span lang="en">hypermedia</span>) y aplicativos o bibliotecas que buscan consumir estas aplicaciones.</p>
<p>En ese momento <span lang="en">ChatGPT</span> no tenía la capacidad de acceder a internet, pero, desde hace un tiempo, esto ya es posible, así que decidí ponerme a experimentar un poco para averiguar que tan útil podía llegar a ser.</p>
<p>A continuación les comparto una muy informal prueba sobre este potencial uso de las inteligencias artificiales conversacionales. Este experimento lo realicé utilizando el GPT <a href="https://www.webpilot.ai" target="_blank" lang="en" hreflang="en">WebPilot</a> de <a href="https://chat.openai.com" target="_blank" lang="en" hreflang="en">ChatGPT</a>.</p>
<h2>El experimento</h2>
<p><a href="https://chatgpt.com/share/50488acd-2741-4121-9da1-22319468abbe" target="_blank">Link a la conversación con <span lang="en">ChatGPT</span>utilizando <span lang="en">WebPilot</span></a>.</p>
<p>El experimento se centró en probar la capacidad de la <abbr title="Inteligencia Artificial">IA</abbr> para navegar por una página web e interactuar con la misma, extrayendo información o incluso realizando acciones.</p>
<p>Para eso, primero, utilicé esta misma página, <a href="https://iyaki.ar" target="_blank">iyaki.ar</a>. Por alguna razón no supo identificar como ir desde la página principal al blog. Pero una vez “dentro” del blog no tuvo problemas para identificar los distintos posts y, posteriormente, poder recopilar información de los mismos; pudiendo incluso extraer el archivo <i>.gitconfig</i> de mi post <a href="https://iyaki.ar/"posts/20240224_gitconfig/" target="_blank">.gitconfig</a> y realizandole algunas modificaciones, como eliminar la mayoría de los comentarios o unificar una sección que se encontraba duplicada (la de <code>[diff]</code>).</p>
<p>A continuación les dejo un <i>diff</i> entre el .gitconfig original y el modificado por la IA:</p>
<pre><code class="language-diff">--- a/.gitconfig
+++ b/.gitconfig
@@ -1,62 +1,67 @@
 [user]
-    email = ****** # El email que utilizo para github
-    name = iyaki # Usuario coincidiendo con el de github
-    signingKey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFsDjKjz0+2nt9YmqETTLNM9PtxfKP/2ihhcj/q27Mtu # Clave pública correspondiente a la clave privada que utiizo para firmar los commits
+    email = tu_email_aqui # Cambia esto por tu email real
+    name = tu_nombre_de_usuario # Cambia esto por tu nombre de usuario de GitHub
+    signingKey = tu_clave_publica # Cambia esto por tu clave pública de SSH

 [core]
-    pager = delta # https://github.com/dandavison/delta Excelente herramienta para mejorar la experiencia al realizar diffs
-    editor = vim # https://xkcd.com/378/
-    autocrlf = input # \r quien te conoce?
-    excludesfile = ~/.gitignore-global # patrones para excluir de manera global
-    untrackedcache = true # https://github.blog/2022-06-29-improve-git-monorepo-performance-with-a-file-system-monitor/
-    fsmonitor = true # https://github.blog/2022-06-29-improve-git-monorepo-performance-with-a-file-system-monitor/
+    pager = delta # Utiliza Delta para mejorar la salida de los diffs
+    editor = vim # Establece Vim como el editor por defecto
+    autocrlf = input
+    excludesfile = ~/.gitignore-global # Utiliza un archivo global para ignorar patrones
+    untrackedcache = true
+    fsmonitor = true

 [color]
-    ui = auto # Colorcitos lindos
+    ui = auto

 [help]
-    autocorrect = prompt # No hay que dejar que la dislexia nos gane
+    autocorrect = prompt

 [diff]
-    colorMoved = default # Diferencia (en los diff) lineas movidas de lineas eliminadas y nuevas
+    colorMoved = default
+    tool = difftastic

 [interactive]
-    diffFilter = delta --color-only # https://github.com/dandavison/delta
+    diffFilter = delta --color-only
+
 [add.interactive]
-    useBuiltin = false # Utilizar delta tambien al ejecutar `git add --interactive` https://git-scm.com/book/en/v2/Git-Tools-Interactive-Staging
+    useBuiltin = false

-[delta] # Mis configuraciones para delta
+[delta]
     navigate = true
     light = false
     line-numbers = true

 [gpg]
-    format = ssh # Formato de la clave utilizada para firmar commits
+    format = ssh
+
 [commit]
-    gpgsign = true # Firmado automático de los commits
+    gpgsign = true
     verbose = true
+
 [tag]
-    gpgsign = true # Firmado automático de los tags
+    gpgsign = true
+
 [gpg &quot;ssh&quot;]
-    allowedSignersFile = ~/.config/git/authorized_signers # Claves publicas consideradas &quot;seguras&quot; para los commits firmados
+    allowedSignersFile = ~/.config/git/authorized_signers

 [init]
-    defaultBranch = main # Branch default
+    defaultBranch = main

 [merge]
-    conflictstyle = diff3 # Mejor resolución de conflictos durante los merge
+    conflictstyle = diff3

-[diff]
-    tool = difftastic # Herramienta alternativa para hacer diffs &quot;estructurales&quot;. Cuando la conocí me pareció una idea excelente pero la verdad es que despues nunca la usé
 [difftool]
     prompt = false
+    difftool = true
+
 [difftool &quot;difftastic&quot;]
     cmd = difft &quot;$LOCAL&quot; &quot;$REMOTE&quot;

 [pager]
-    difftool = true # Use a pager for large output, just like other git commands
+    difftool = true

-[alias] # Esto necesita explicación?
+[alias]
     a = add
     b = branch
     c = commit -m
@@ -68,7 +73,7 @@
     dft = difftool
     l = ! git log --show-signature
     lo = ! git log --color --pretty=format:'%Cred%H%Creset - %C(blue)(%G? %GT)%Creset%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset'
-    lol = ! git log --color --graph --pretty=format:'%Cred%h%Creset - %C(blue)(%G? %GT)%Creset%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit --
+    lol = ! git log --color --graph --pretty=format:'%Cred%h%Creset - %C(blue)(%G? %GT)%Creset%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit
     p = push
     psu = push --set-upstream
     pr = pull --rebase --autostash

</code></pre>
<p>El comportamiento a la hora de extraer y resumir contenido de cualquier de las páginas en todos los casos fue correcto.</p>
<p>Después de un rato de jugar con el contenido de esta web, le pedí que, utilizando el servicio de <a href="https://simple-newsletter.com" target="_blank" lang="en">Simple Newsletter</a>, me suscribiera a un cierto feed <abbr title="Really Simple Sindication" lang="en">RSS</abbr>, indicándole además un <span lang="en">e-mail</span> para realizar la suscripción.</p>
<p>Ante esta petición, la <abbr title="Inteligencia Artificial">IA</abbr> se negó indicando que no le era posible interactuar de esa manera con páginas web y brindando una serie de pasos genéricos para suscribirse a servicios.</p>
<p>Cómo el servicio se consume únicamente realizando una petición <abbr title="HyperText Transfer Protocol" lang="en">HTTP</abbr> <span lang="en">GET</span>, cosa que la <abbr title="Inteligencia Artificial">IA</abbr> estuvo haciendo continuamente para acceder al contenido de las distintas páginas de mi sitio web, insistí en que sí podía realizar dicha acción y argumentando que tenía la capacidad de realizar ese tipo de acciones.</p>
<p>Aun así decidió no realizar la petición, pero esta vez sí me dio indicaciones muy precisas de como armar el <abbr title="Uniform Resource Identifier" lang="en">URI</abbr> para poder suscribirme.</p>
<p>Yo, por supuesto, no iba a darme por vencido en lograr que la <abbr title="Inteligencia Artificial">IA</abbr> realizara la suscripción, por lo que procedí a consultarle por el contenido correspondiente al <abbr title="Uniform Resource Identifier" lang="en">URI</abbr> que acababa de proveerme. Esta vez funcionó y ejecutó la suscripción.</p>
<figure>
	<img src="https://iyaki.ar/posts/20240503_LLMs_como_hypermedia_clients/simple-newsletter-database-screenshot.png" alt="Captura de pantalla de queries SQL">
	<figcaption>Consulta a la base de datos de Simple Newsletter para verificar que se hubiese ejecutado la suscripción</figcaption>
</figure>
<h2>Conclusión</h2>
<p>El uso de agentes autónomos impulsados por <abbr title="Large Language Model" lang="en">LLM</abbr> me parece prometedor como una forma de simplificar las aplicaciones y evitar la necesidad de diferentes presentaciones para comunicarse con humanos y otras aplicaciones.</p>
<p>Es evidente que es algo que a día de hoy aún no sería 100% funcional, ya que incluso en unas pruebas tan básicas como las que realice yo tuvo problemas para identificar links cuando se utilizaban patrones un poco menos convencionales en el diseño de la interfaz web (la página principal tiene los links a las demás secciones “desparramadas” y la <abbr title="Inteligencia Artificial">IA</abbr> no los identificó adecuadamente; en cambio, cuando los links se encontraban en los títulos de los posts, lo cual es un patrón bastante común, funcionó de maravilla), pero esto se debería poder resolver con el entrenamiento adecuado de sus modelos de lenguaje.</p>
<p>El otro problema que debería resolverse para que el uso de la <abbr title="Inteligencia Artificial">IA</abbr> como un <span lang="en">Hypermedia Client</span> sea viable es el de la eficiencia. Si bien los tiempos de respuesta fueron aceptables para un uso distendido, es cierto que todas las interacciones requirieron mucho más tiempo del que consumiría cualquier aplicación para realizar una petición <abbr title="HyperText Transfer Protocol" lang="en">HTTP</abbr> y procesar el contenido de su respuesta.</p>
<p>Esto es todo por hoy, espero que les haya resultado tan interesante como a mí. Como siempre, si quieren conversar al respecto pueden dejar un comentario o contactarme vía <span lang="en">e-mail</span> a <a href="mailto:me@iyaki.ar" target="_blank">me@iyaki.ar</a>.</p>
<p lang="en">Bye!</p>

<script type="module" defer>
	import hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js';
	import diff from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/languages/diff.min.js';
	hljs.registerLanguage('diff', diff);
	hljs.highlightAll()
</script>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20240503_LLMs_como_hypermedia_clients/"/><updated>2024-05-03T02:35:39Z</updated><published>2024-05-03T02:35:39Z</published></entry><entry><id>https://iyaki.ar/posts/20240501_llegaron_las_newsletters/</id><title><![CDATA[Llegaron las newsletters]]></title><summary><![CDATA[Introducing Simple Newsletter! Atom/RSS to Newsletter Service]]></summary><content type="html"><![CDATA[<blockquote>Introducing Simple Newsletter! Atom/RSS to Newsletter Service</blockquote><h1>Llegaron las <span lang="en">Newsletters</span></h1>
<p><time datetime="2024-05-02">02/05/2024</time></p>
<p>Durante el último mes estuve trabajando en desarrollar una pequeña aplicación web que he bautizado como: <a href="https://simple-newsletter.com/" target="_blank" lang="en">Simple Newsletter</a>.</p>
<p><span lang="en">Simple Newsletter</span> es un servicio fuertemente inspirado en <a href="https://kill-the-newsletter.com/" target="_blank" lang="en">Kill the Newsletter</a> (del que ya les hablé en <a href="https://iyaki.ar/"posts/20230514_reuniendo_conocimiento/">este artículo</a>), pero que hace exactamente lo contrario. Permite subscribirse con una dirección <span lang="en">e-mail</span> a un <span lang="en">Feed</span> <span lang="en">Atom</span> o <abbr title="Really Simple Sindication" lang="en">RSS</abbr> y recibir las actualizaciones de dicho <span lang="en">feed</span> en tu casilla de correo mediante una <em>newsletter</em>.</p>
<p>En lo personal, considero que la practicidad de un buen gestor de <span lang="en">Feeds</span> supera ampliamente en comodidad de uso a una <span lang="en">Newsletter</span>, pero viendo como este medio de comunicación ha crecido en los últimos tiempos, me pareció que un servicio así podría llegar a ser útil.</p>
<p>Por ahora, la única manera de utilizar el servicio de <span lang="en">Simple Newsletter</span> es mediante su <abbr title="Application Programming Interface">API</abbr> web, pero mi intención es poder seguir mejorando el servicio y, entre otras cosas, agregar una página de inicio que permita a cualquiera subscribirse al <span lang="en">feed</span> que desee sin tener que estar escribiendo parámetros en la <i lang="en">query string</i> manualmente.</p>
<p>Si bien es un servicio pensado para ser utilizado por usuarios particulares y que cualquiera pueda recibir actualizaciones de los blogs y páginas que le interesan vía correo electrónico, <span lang="en">Simple Newsletter</span> también puede ser aprovechado por cualquiera que tenga un blog o página web con un <span lang="en">feed</span> <span lang="en">Atom</span> o <abbr title="Really Simple Sindication" lang="en">RSS</abbr> para mantener a sus lectores informados y en contacto agregando un simple formulario (tal y como está funcionando ahora mismo en este blog).</p>
<p>Cómo siempre, espero su <span lang="en">feedback</span> al respecto, se trata de un proyecto en muy temprana edad y cualquier sugerencia es más que bienvenida.</p>
<p>Hasta la próxima.</p>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20240501_llegaron_las_newsletters/"/><updated>2024-05-02T00:23:31Z</updated><published>2024-05-02T00:23:31Z</published></entry><entry><id>https://iyaki.ar/posts/20240328_no_solo_de_software_vive_el_hombre/</id><title><![CDATA[No solo de software vive el hombre]]></title><summary><![CDATA[En artículos anteriores hablamos sobre software que ayuda a la productividad. Hoy es el turno de esas leyes y principios que, si decidimos tener en cuenta también pueden ayudarnos enormemente.]]></summary><content type="html"><![CDATA[<blockquote>En artículos anteriores hablamos sobre software que ayuda a la productividad. Hoy es el turno de esas leyes y principios que, si decidimos tener en cuenta también pueden ayudarnos enormemente.</blockquote><h1>No solo de software vive el hombre</h1>
<p><time datetime="2025-02-13T19:26:09.846Z">13/02/2025</time></p>
<aside aria-label="Índice">
	<details>
		<summary>
			<h2 style="display: inline;">Índice</h2>
		</summary>
		<nav aria-label="Índice">
			<ul>
				<li><a href="#pareto">Principio de Pareto o Regla del 80/20</a>
				<li><a href="#carga-cognitiva">Carga cognitiva</a>
				<li><a href="#context-switching">Context Switching</a>
				<li><a href="#ockham">Principio de parsimonia o Navaja de Ockham</a>
				<li><a href="#conway">Ley de Conway</a>
				<li><a href="#parkinson">Ley de Parkinson</a>
				<li><a href="#brooks">Ley de Brooks</a>
				<li><a href="#hofstadter">Ley de Hofstadter</a>
				<li><a href="#ventanas-rotas">Teoría de las ventanas rotas</a>
				<li><a href="#hanlon">Principio de Hanlon</a>
			</ul>
		</nav>
	</details>
</aside>
<p>Usar las herramientas adecuadas para el trabajo adecuado, saber utilizarlas y darles el mantenimiento necesario para que sigan funcionando son formas importantes de aumentar nuestra productividad. Pero existe algo con un impacto mucho más profundo y de mayor magnitud: Tomar las decisiones que más nos acercarán a nuestros resultados deseados. Porque uno puede ser extremadamente rápido para construir un auto, pero si lo que se necesitaba era un barco, de poco servirá todo el esfuerzo que se invierta.</p>
<p>En este aspecto hay dos puntos elementales que nos ayudarán a alcanzar el mayor impacto posible mediante nuestro trabajo.</p>
<p>El primero es conocer cuál es el resultado deseado, en este sentido, por lo general, cuanto más contexto e información tengamos sobre el problema que buscamos solucionar, mejor preparados estaremos para brindar una solución.</p>
<p>El segundo aspecto relevante es poder predecir, de la manera más acertada posible, las consecuencias de nuestras acciones. Para esto podemos optar por opciones poco ortodoxas como el tarot, la bola de cristal o la lectura de la borra del café; o centrarnos en aprovechar lo que otros han estudiado al respecto en el pasado y sus conclusiones. Conclusiones que en muchos casos se han recopilado en leyes, teorías y principios ampliamente conocidos en el ámbito de la gestión y la tecnología.</p>
<p>En este artículo nos centraremos en estas últimas.</p>
<p>Por eso hoy, dejo un breve resumen de las que considero más útiles a la hora de avanzar sobre un proyecto como colaborador individual o como parte de su <span lang="en">management</span>.</p>
<h2 id="pareto"><a href="#pareto">Principio de Pareto o Regla del 80/20</a></h2>
<p>Durante el siglo XIX el economista italiano Vilfredo Pareto observó que <strong>aproximadamente el 80% de los efectos proviene del 20% de las causas</strong>.</p>
<p>En el contexto de proyectos de software, este principio cobra una relevancia significativa. Al aplicarlo, nos damos cuenta de que el 80% del valor agregado proviene del 20% de las características o funcionalidades implementadas.</p>
<p>Y entonces, ¿por qué esto es tan relevante para nosotros?</p>
<p>Porque nos ayuda a priorizar.</p>
<p>Si sabemos qué pequeña parte de nuestro trabajo es responsable de la mayoría de los resultados, podemos concentrarnos en eso primero.</p>
<p>Ya sea que estemos trabajando en solitario o en equipo, el Principio de Pareto nos da una hoja de ruta inteligente para maximizar nuestra eficiencia y lograr mejores resultados.</p>
<h2 id="carga-cognitiva"><a href="#carga-cognitiva">Carga cognitiva</a></h2>
<p>La carga cognitiva no es un principio como tal, pero sí un concepto que, si tenemos en cuenta, puede ayudarnos a nosotros y a nuestros compañeros a obtener mejores resultados.</p>
<p>La carga cognitiva <strong>se refiere a la cantidad de esfuerzo mental requerido para realizar una tarea o procesar información</strong>.</p>
<p>Imaginemos que estamos trabajando en un proyecto de software y debemos recordar todos los detalles sobre cómo funciona cada parte del código que se está escribiendo. Bueno, esa es la carga cognitiva en acción.<br>
Cuanto más complicado sea el proyecto, más trabajo mental habrá que hacer para mantener todo en orden.</p>
<p>A la hora de programar existen múltiples maneras de disminuir la carga cognitiva que una pieza de código fuente requiere. A mí, en particular, me agradan bastante las sugerencias de <a href="https://franiglesias.github.io/calisthenics-1/" target="_blank"><i lang="en">Object Calisthenics</i></a>.</p>
<h2 id="context-switching" lang="en"><a href="#context-switching">Context Switching</a></h2>
<p>Otro concepto que, a pesar de no tratarse de una ley o principio, puede impactar significativamente en nuestra productividad.</p>
<p><span lang="en">Context switching</span> <strong>se refiere al proceso de cambiar nuestra atención y enfoque de una tarea a otra</strong>. Este cambio constante de contexto puede ser perjudicial para nuestra productividad, ya que requerimos tiempo y energía para ajustarnos a la nueva tarea.</p>
<p>Y peor aún, aunque la interrupción fuese breve, volver a <a href="https://www.kenneth-truyers.net/2015/10/05/programming-in-the-zone/" target="_blank" hreflang="en"><i lang="en">the zone</i></a> o <a href="https://github.blog/2024-01-22-how-to-get-in-the-flow-while-coding-and-why-its-important/" target="_blank" hreflang="en"><i lang="en">the flow</i></a> puede tomarnos <a href="https://contextkeeper.io/blog/the-real-cost-of-an-interruption-and-context-switching/" target="_blank" hreflang="en">hasta 15 minutos</a>.</p>
<h2 id="ockham"><a href="#ockham">Principio de parsimonia o Navaja de Ockham</a></h2>
<p>El Principio de Parsimonia, también conocido como la Navaja de Ockham, es un principio filosófico que sugiere que, <strong>entre varias explicaciones posibles para un fenómeno, la más simple suele ser la correcta</strong>. Este principio, propuesto por el filósofo y teólogo Guillermo de Ockham en el siglo XIV, aboga por no multiplicar las entidades innecesariamente.</p>
<p>Entonces, en el mundo del desarrollo de software, esto significa que cuando nos encontramos con un problema a resolver (un bug, una nueva funcionalidad a implementar, un desafío a nivel de arquitectura, etc.), deberíamos intentar encontrar la solución más sencilla posible. En lugar de enredarnos con soluciones complejas que pueden generar más problemas, deberíamos seguir el camino más simple y directo.</p>
<p>Dentro del mundo de la programación, esto se encuentra también muy relacionado con los principios <a href="https://enterprisecraftsmanship.com/posts/yagni-revisited/" target="_blank" hreflang="en"><abbr title="You Aren't Gonna Need It" lang="en">YAGNI</abbr></a> y <a href="https://enterprisecraftsmanship.com/posts/kiss-revisited/" target="_blank" hreflang="en"><abbr title="Keep it Simple, Stupid" lang="en">KISS</abbr></a>.</p>
<h2 id="conway"><a href="#conway">Ley de Conway</a></h2>
<p>La Ley de Conway viene a recordarnos lo importante que es la comunicación en el desarrollo de software, formulada por el programador Melvin Conway en 1968, establece que <strong>las organizaciones que diseñan sistemas están inevitablemente limitadas a producir diseños que sean réplicas de la estructura de comunicación de sus propias organizaciones</strong>.</p>
<p>He visto muchas veces artículos y explicaciones que se centran en cómo un equipo con poca organización y falta de comunicación producirá software de baja calidad y que, probablemente, no resuelva necesidades reales. Pero a mí me resulta mucho más interesante tener en cuenta esta ley a la hora de diseñar sistemas y definir su arquitectura.</p>
<p>El impacto de la ley de Conway en la arquitectura de software es muy significativo y es algo que he podido comprobar de primera mano. Hace algunos años y en medio del hype por los microservicios, en la empresa donde trabajaba, nos propusimos a, para sorpresa de nadie, migrar a una arquitectura de microservicios. Los beneficios de esta arquitectura son bien conocidos, y entre ellos destacan:</p>
<ol>
<li>Mejor tolerancia a fallos, al facilitar desacoplar los servicios, permitiendo que una pieza de software fallé sin afectar a las demás.</li>
<li>Agnosticismo tecnológico, cada microservicio puede desarrollarse con las tecnologías que considere más apropiadas para lograr su cometido.</li>
<li>Mayor independencia a la hora de evolucionar y desplegar cada servicio al poder hacerlo de manera individual.</li>
<li>Reusabilidad entre áreas de negocio, si partes de la lógica de negocio se repiten entre distintos procesos y áreas, es posible reutilizar los servicios ya desarrollados.</li>
<li>Permite la experimentación y la evolución rápida, al poder ser desarrollados sin comprometer al resto del sistema.</li>
</ol>
<p>Pero nunca llegamos a beneficiarnos de ellos y es que:</p>
<ol>
<li>Nuestras reglas de negocio, aunque bien definidas, eran bastante complejas y la forma en que el equipo las concibe hace que estén fuertemente acopladas entre sí; por lo que, incluso desarrollando diferentes aplicativos, el nivel de dependencia entre ellos impedía una mayor resiliencia ante fallos de un componente.</li>
<li>Todo nuestro equipo tenía experiencia en los mismos lenguajes de programación, por lo que las posibles tecnologías a utilizar eran bastante limitadas.</li>
<li>Al haber dependencias tan fuertes entre las distintas reglas de negocio, cualquier cambio terminaba afectando múltiples componentes.</li>
<li>Nuestras reglas de negocio, aunque complejas, se mantenían dentro de un alcance bastante acotado, por lo que la reutilización de servicios era muy poco probable.</li>
<li>Al tener un equipo conformado, principalmente, por desarrolladores <span lang="en">Junior</span>, la experimentación resultaba casi nula.</li>
</ol>
<p>Y, para empeorar, las cosas, los integrantes del equipo siempre había trabajado de manera muy cercana entre ellos, por lo que no existía un <i lang="en">ownership</i> claro sobre procesos o módulos que permitieran la independencia necesaria para desacoplar las distintas piezas de software. Resultando en que los intentos de separar servicios y comunicarlos solo generara confusión y frustración.</p>
<p>Este es un claro ejemplo de cómo un equipo que tenía la capacidad técnica para desarrollar microservicios falló en hacerlo por razones meramente organizativas y comunicacionales.</p>
<p>Para poder implementar exitosamente una arquitectura de microservicios, primero deberíamos haber comenzado por cambiar la forma en que trabajábamos. Armar múltiples equipos (idealmente uno por microservicio) con un alcance y objetivos claros, capacitar a los desarrolladores en nuevas tecnologías, probablemente contratar más colaboradores, etc.</p>
<p>Por suerte, esta migración fue lanzada como una prueba piloto, y notamos todos estas fricciones antes de que se volvieran un problema.</p>
<p>Así, de común acuerdo con el resto del equipo, decidimos volver al esquema monolítico con el que trabajábamos hasta entonces y centrar nuestros esfuerzos en mejorar la estructura y modularidad de nuestra aplicación, en lugar de intentar separar todo de manera artificial y forzada en distintos servicios independientes.</p>
<h2 id="parkinson"><a href="#parkinson">Ley de Parkinson</a></h2>
<p>En 1957 Cyril Northcote Parkinson enunció: <strong>el trabajo se expande hasta llenar el tiempo disponible para que se termine</strong>.</p>
<p>Al realizar la gestión de un proyecto, ya sea como colaboradores individuales, estimando tareas que deberán realizarse o realizando planificaciones de alto nivel, como <span lang="en">Project Manager</span>, siempre es buena idea tener en cuenta esta ley.</p>
<p>Según nos advierte, aumentar “por las dudas” el tiempo que se planifica para dedicar a una cierta tarea puede no ser la mejor idea, ya que terminaremos consumiendo ese tiempo en detalles superfluos y probablemente innecesarios (este es un buen punto en el que aplicar el Principio de Pareto para decidir cuando detenerse) en lugar de aprovechar ese tiempo en adelantar otras tareas que puedan aportar un mayor valor real.</p>
<h2 id="brooks"><a href="#brooks">Ley de Brooks</a></h2>
<p>Fred Brooks, en su libro <em lang="en">The Mythical Man-Month</em> publicado en 1975, afirma: <strong>añadir más personal a un proyecto de software en retraso, lo retrasará más</strong>.</p>
<p>Esta es especialmente relevante para los roles de <span lang="en">Management</span> y Liderazgo en proyectos de desarrollo de software, ya que antaño era una práctica muy normal el intentar resolver los problemas de un proyecto mediante la adición de más y más colaboradores.</p>
<p>El problema es que, lejos de de generar resultados más velozmente, se produce el efecto contrario; donde la entrega de valor se retrasa aún más, debido a que la comunicación se complejiza de manera exponencial a medida que se agregan personas y se requiere tiempo adicional para capacitar a estas nuevas incorporaciones y transferirles todo el contexto necesario.</p>
<h2 id="hofstadter"><a href="#hofstadter">Ley de Hofstadter</a></h2>
<p>Esta ley describe la dificultad de estimar el tiempo que llevará completar tareas de gran complejidad acuñada por Douglas Hofstadter en su libro <em lang="en">Gödel, Escher, Bach: an Eternal Golden Braid</em> afirma: <strong>Siempre nos lleva más tiempo de lo esperado, incluso teniendo en cuenta la ley de Hofstadter</strong>.</p>
<p>Sobre esta creo que no hay mucho que agregar, pero conocerla es una buena forma de recordar que la estimación de tareas siempre cuenta con su cuota de incertidumbre.</p>
<h2 id="ventanas-rotas"><a href="#ventanas-rotas">Teoría de las ventanas rotas</a></h2>
<p>Esta teoría proviene de la criminología. Introducida en un artículo de 1982 por los científicos sociales James Q. Wilson y George L. Kelling y popularizada en la década de 1990, sostiene que <strong>los signos visibles de la delincuencia, el comportamiento antisocial y los disturbios civiles crean un entorno urbano que fomenta la delincuencia y el desorden, incluidos los delitos graves</strong>; sugiriendo que los métodos policiales que se centran en atacar los delitos menores, como el vandalismo, la vagancia, el consumo de alcohol en público, el cruce incorrecto de peatones y la evasión de tarifas, ayudan a crear una atmósfera de orden y legalidad.</p>
<p>Esta idea es portada en 1999 al ámbito de la programación por Andy Hunt y Dave Thomas en su libro <em lang="en">The Pragmatic Programmer</em>, donde aplican el concepto de las ventanas rotas a la calidad de software.</p>
<p>En su libro, sugieren nunca dejas las <i>ventanas rotas</i> sin arreglar y repararlas en cuanto se las identifica. Evitando así esparcir malas prácticas al resto del código fuente.</p>
<p>Considero esto particularmente importante cuando un equipo cuenta con una gran cantidad de desarrolladores <span lang="en">Junior</span>, ya que es mediante el mismo código fuente sobre el que están trabajando que, día a día, siguen aprendiendo cómo programar. Sin embargo al ser una práctica que depende en gran medida de la cultura del equipo (y del área o la empresa donde esté trabajando ese equipo) puede ser un desafío notable implementar prácticas que fomenten la reparación constante de estas <i>ventanas rotas</i>.</p>
<h2 id="hanlon"><a href="#hanlon">Principio de Hanlon</a></h2>
<p>El Principio de Hanlon (o navaja de Hanlon) no trata de manera directa sobre productividad pero es una herramienta que puede ayudarnos a lograr mejores resultados cuando se trabaja en conjunto.</p>
<p>Este principio establece: <strong>Nunca atribuyas a la maldad lo que se explica adecuadamente por la estupidez</strong>.</p>
<p>Me gusta porque es simple y pragmático, y nos invita a darle a las personas el beneficio de la duda. Recordándonos que muchas veces un conflicto que estemos teniendo con otras personas puede ser producto, simplemente, de un malentendido.</p>
<h2>Redondeando</h2>
<p>Esas fueron la leyes, principios y conceptos que, a mi parecer, resultan mas útiles a la hora de participar en un proyecto software.</p>
<p>Espero que les puedan ser de utilidad también a ustedes.</p>
<p>Si quieren charlar sobre este o cualquier otro tema siempre pueden contactarme via <span lang="en">e-mail</span> o dejar un comentario (Me olvidé de avisar eso! Ahora hay una sección de comentarios al final de los <span lang="en">posts</span> 😁).</p>
<p lang="ua">До побаченія!</p>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20240328_no_solo_de_software_vive_el_hombre/"/><updated>2024-03-28T16:57:07Z</updated><published>2024-03-28T16:57:07Z</published></entry><entry><id>https://iyaki.ar/posts/20240224_gitconfig/</id><title><![CDATA[.gitconfig]]></title><summary><![CDATA[Una de las herramientas más útiles para acompañar una sesión de programación es git, pero también puede ser el mayor enemigo de un programador si no se la usa adecuadamente.<br>En este artículo comparto como configuro git para simplificar su uso lo mas posible.]]></summary><content type="html"><![CDATA[<blockquote>Una de las herramientas más útiles para acompañar una sesión de programación es git, pero también puede ser el mayor enemigo de un programador si no se la usa adecuadamente.<br>En este artículo comparto como configuro git para simplificar su uso lo mas posible.</blockquote><h1>.gitconfig</h1>
<p><time datetime="2024-02-24">24/02/2024</time></p>
<p>Continuando con la temática de herramientas y productividad, y envalentonado por el post de <a href="https://jvns.ca/about/#about-me" target="_blank">Julia Evans</a>: <a href="https://jvns.ca/blog/2024/02/16/popular-git-config-options/" target="_blank">Popular git config options</a>, hoy quiero compartir con ustedes mi configuración de git.</p>
<pre><code class="language-toml"># ~/.gitconfig

[user]
    email = ****** # El email que utilizo para github
    name = iyaki # Usuario coincidiendo con el de github
    signingKey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFsDjKjz0+2nt9YmqETTLNM9PtxfKP/2ihhcj/q27Mtu # Clave pública correspondiente a la clave privada que utiizo para firmar los commits

[core]
    pager = delta # https://github.com/dandavison/delta Excelente herramienta para mejorar la experiencia al realizar diffs
    editor = vim # https://xkcd.com/378/
    autocrlf = input # \r quien te conoce?
    excludesfile = ~/.gitignore-global # patrones para excluir de manera global
    untrackedcache = true # https://github.blog/2022-06-29-improve-git-monorepo-performance-with-a-file-system-monitor/
    fsmonitor = true # https://github.blog/2022-06-29-improve-git-monorepo-performance-with-a-file-system-monitor/

[color]
    ui = auto # Colorcitos lindos

[help]
    autocorrect = prompt # No hay que dejar que la dislexia nos gane

[diff]
    colorMoved = default # Diferencia (en los diff) lineas movidas de lineas eliminadas y nuevas

[interactive]
    diffFilter = delta --color-only # https://github.com/dandavison/delta
[add.interactive]
    useBuiltin = false # Utilizar delta tambien al ejecutar `git add --interactive` https://git-scm.com/book/en/v2/Git-Tools-Interactive-Staging

[delta] # Mis configuraciones para delta
    navigate = true
    light = false
    line-numbers = true

[gpg]
    format = ssh # Formato de la clave utilizada para firmar commits
[commit]
    gpgsign = true # Firmado automático de los commits
    verbose = true
[tag]
    gpgsign = true # Firmado automático de los tags
[gpg &quot;ssh&quot;]
    allowedSignersFile = ~/.config/git/authorized_signers # Claves publicas consideradas &quot;seguras&quot; para los commits firmados

[init]
    defaultBranch = main # Branch default

[merge]
    conflictstyle = diff3 # Mejor resolución de conflictos durante los merge

[diff]
    tool = difftastic # Herramienta alternativa para hacer diffs &quot;estructurales&quot;. Cuando la conocí me pareció una idea excelente pero la verdad es que despues nunca la usé
[difftool]
    prompt = false
[difftool &quot;difftastic&quot;]
    cmd = difft &quot;$LOCAL&quot; &quot;$REMOTE&quot;

[pager]
    difftool = true # Use a pager for large output, just like other git commands

[alias] # Esto necesita explicación?
    a = add
    b = branch
    c = commit -m
    ca = commit --amend --no-edit
    cl = clean -fd .
    co = checkout
    d = diff
    dd = difftool
    dft = difftool
    l = ! git log --show-signature
    lo = ! git log --color --pretty=format:'%Cred%H%Creset - %C(blue)(%G? %GT)%Creset%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset'
    lol = ! git log --color --graph --pretty=format:'%Cred%h%Creset - %C(blue)(%G? %GT)%Creset%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit --
    p = push
    psu = push --set-upstream
    pr = pull --rebase --autostash
    r = reset
    s = status
    td = tag --delete
    tdr = push --delete
</code></pre>
<p>Adicionalmente, esta es mi configuración de exclusiones globales (<code>excludesfile</code>)</p>
<pre><code class="language-.gitignore"># ~/.gitignore-global

.*.sw? #Vim swap file
.directory
.vscode
</code></pre>
<p>Esta era mi configuración de git desde hace ya unos años y hasta hace unos pocos días, cuando, en base al artículo que les compartí al principio del post decidí hacer los siguientes cambios y adiciones:</p>
<pre><code class="language-.gitignore">[merge]
    conflictstyle = zdiff3 # Mejor x2 resolución de conflictos durante los merge

[rerere]
    enabled = true
[push]
    default = current
[rebase]
    autostash = true
[transfer]
    fsckobjects = true
[fetch]
    fsckobjects = true
[receive]
    fsckObjects = true
[status]
    submoduleSummary = true
[branch]
    sort = -committerdate
[log]
    date = iso
</code></pre>
<p>Estas nuevas configuraciones se ven prometedoras, pero aún no he tenido suficiente tiempo para probarlas a fondo.</p>
<p>Esto es todo por hoy, espero que puedan aprovechar alguna de estas configuraciones para mejorar sus propios flujos de trabajo.</p>
<p lang="ru">Пока́!</p>

<script type="module" defer>
	import hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js'
	hljs.highlightAll()
</script>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20240224_gitconfig/"/><updated>2024-02-24T03:37:25Z</updated><published>2024-02-24T03:37:25Z</published></entry><entry><id>https://iyaki.ar/posts/20240123_breaking_changes/</id><title><![CDATA[Breaking Changes]]></title><summary><![CDATA[Algunas lecciones acerca de URIs que me llevo del proceso de migración a eleventy.]]></summary><content type="html"><![CDATA[<blockquote>Algunas lecciones acerca de URIs que me llevo del proceso de migración a eleventy.</blockquote><h1 lang="en">Breaking Changes</h1>
<p><time datetime="2024-01-23">23/01/2024</time></p>
<p>Si utilizan <abbr title="Really Simple Sindication" lang="en">RSS</abbr> para seguir los artículos que escribo en mi blog personal, es posible que a fines del 2023 se hayan encontrado con <a href="https://iyaki.ar/posts/20231217_cambios_en_feeds_rss/" target="_blank">este post</a> en el que comento algunos cambios importantes sobre mis feeds <abbr title="Really Simple Sindication" lang="en">RSS</abbr>, que acompañan la migración de mi sitio web a <a href="https://www.11ty.dev/" target="_blank" lang="en" hreflang="en">eleventy</a>.</p>
<p>Hoy vengo a contar un poco sobre como estoy gestionando estos cambios en las <span lang="en">URIs</span> de las distintas páginas de mi <span lang="en">website</span>, siempre con el objetivo de mantener la mayor retrocompatibilidad posible, evitando así <i lang="en">breaking changes</i>.</p>
<p>Al migrar a <span lang="en">eleventy</span> decidí hacer caso a los consejos de <a href="https://www.w3.org/Provider/Style/URI" target="_blank" lang="en">Cool URIs don’t change</a> y eliminar la extensión <code>.html</code> de las <span lang="en">URIs</span> de mis páginas (el cual es ademas el comportamiento predeterminado de <span lang="en">eleventy</span>). Inmediatamente, comencé a buscar una manera de mantener las antiguas <span lang="en">URIs</span> funcionando.</p>
<p>Como soy una persona de bien, que respeta el protocolo <abbr title="HyperText Transfer Protocol" lang="en">HTTP</abbr>, mi primera intención fue configurar redirecciones (<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301" target="_blank" lang="en" hreflang="en">301 - Moved Permanently</a>) para que todas las direcciones antiguas redirigiesen de forma automática a las nuevas y el cambio en las <span lang="en">URIs</span> fuese explícito y visible para los visitantes de mi web. Esta era la opción más adecuada, pero, al parecer, <a href="https://pages.github.com/" target="_blank" lang="en" hreflang="en">Github Pages</a> (<i lang="en">hosting</i> que utilizo para mi <span lang="en">website</span>) no tiene soporte para configurar redirecciones más allá del clásico <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404" target="_blank" lang="en" hreflang="en">404 - Not Found</a>.</p>
<p>Fue entonces cuando revisando los directorios y archivos de mi sitio descubrí cuál sería la primera solución que implementaría: No hacer nada. ¿Nada? ¡Sí, eso mismo!</p>
<p>El proceso de deploy de este sitio es extremadamente simple, se basa en copiar archivos de un directorio a otro. Y como todos los archivos que <span lang="en">eleventy</span> había generado, tenían rutas distintas a los originales, la primera solución resultó ser seguir sirviendo los antiguos archivos en sus <span lang="en">URIs</span> originales, teniendo así duplicados de muchas de mis páginas.</p>
<p>Esto servía para salir del paso, pero no me terminaba que convencer como solución, ya que, más pronto que tarde, estos duplicados comenzarían a diferir en contenido, generando inconsistencias. Entonces, me embarqué en la búsqueda de una solución más elegante y sostenible, la cual aún no encuentro.</p>
<p>Pero esto no significa que no haya mejorado la solución original.</p>
<p>Actualmente, he cambiado las copias de las páginas por enlaces simbólicos (<i lang="en">symlinks</i>, para los amigos). Los enlaces simbólicos me permiten tener una única versión de cada página, pero mantener accesibles las antiguas <span lang="en">URIs</span>. Esto solo resuelve el problema de la consistencia, ya que el cambio en las <span lang="en">URIs</span> sigue sin hacerse visible para los visitantes. Otro problema que todavía mantiene esta solución es el de la canonicidad de las páginas, al haber múltiples <span lang="en">URIs</span> para un mismo contenido, los motores de búsqueda como <span lang="en">Google</span> no saben cuál deberían sugerir en los resultados porque sus <i lang="en">bots</i> no tienen manera de saber que una de las direcciones corresponde a un enlace simbólico.</p>
<p>Este problema con los motores de búsqueda, de momento, lo estoy resolviendo, incluyendo, en todas las páginas, tags <abbr title="Hyper Text Markup Language">HTML</abbr> del estilo: <code>&lt;link rel=&quot;canonical&quot; href=&quot;https://iyaki.ar/blog/&quot;&gt;</code> lo que, <a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls?hl=es" target="_blank">según <span lang="en">Google</span></a> debería resolver el problema.</p>
<p>Aún no termino de decidir si eventualmente debería dar de baja las direcciones antiguas de manera definitiva (Opción tentadora para simplificar el mantenimiento, sobre todo teniendo en cuenta que esta web es bastante joven y las posibilidades de que haya enlaces de terceros a sus páginas aún son muy bajos) es por esto que, de manera cautelar, he agregado en la <a href="https://iyaki.ar/404.html" target="_blank">página de 404</a> un <a href="https://blog.archive.org/2013/10/24/web-archive-404-handler-for-webmasters/" target="_blank" hreflang="en">script <abbr title="Javascript" lang="en">JS</abbr></a> del <a href="https://archive.org/" target="_blank" lang="en" hreflang="en">Internet Archive</a> que permite recomendar automáticamente páginas web guardadas con anterioridad en su base de datos en caso de no existir la dirección solicitada.</p>
<p>Además, aprovechando esta posibilidad que brinda <a href="https://web.archive.org/" target="_blank" lang="en" hreflang="en">WayBack Machine</a> del <span lang="en">Internet Archive</span>, para asegurarme de que mis posts y páginas queden almacenados, en el <span lang="en">Github Action</span> que se encarga de realizar las publicaciones agregué <del title="para mas detalles, leer las actualizaciones al final del post" cite="https://github.com/iyaki/iyaki.github.io/commit/ea6f3a745a3ecf6c740417c7be461789eaa3a00f" date="2024-05-29">una sección</del> para respaldar automáticamente el contenido de mi sitio web mediante la <abbr title="Application Programming Interface" lang="en">API</abbr> <abbr title="Representational State Transfer" lang="en">REST</abbr> que exponen.</p>
<p>Esto es todo por ahora, si tengo novedades sobre esta migración, sus ventajas y desafíos los verán en mis posts.</p>
<p lang="fr">Au revoir!</p>
<h2>Actualizaciones</h2>
<p>Durante mayo del 2024, después de varios meses de haber implementado WayBack Machine y comprobar su correcto funcionamiento he decidido, eliminar los enlaces simbólicos.</p>
<p>Esto se debe, principalmente, a que la generación de estos enlaces simbólicos complejizaba el <a href="https://github.com/iyaki/iyaki.github.io/commit/ea6f3a745a3ecf6c740417c7be461789eaa3a00f" target="_blank">nuevo proceso de deploy</a> de este sitio web.</p>
<p>También como consecuencia de los cambios del proceso de deploy fue necesario cambiar el proceso de archivado de las páginas en <span lang="en">WayBack Machine</span> del <span lang="en">Internet Archive</span> que ahora se realiza en un <a href="https://github.com/iyaki/iyaki.github.io/blob/main/.github/workflows/preserve.yml" target="_blank"><span lang="en">Github Action</span> independiente</a> y utiliza una pequeña <a href="https://github.com/iyaki/web-archiver" target="_blank" hreflang="en">aplicación que desarrollé para archivas páginas a partir de un sitemap</a>.</p>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20240123_breaking_changes/"/><updated>2024-01-23T22:41:44Z</updated><published>2024-01-23T22:41:44Z</published></entry><entry><id>https://iyaki.ar/posts/20240121_conoce_tus_herramientas/</id><title><![CDATA[Conoce tus herramientas]]></title><summary><![CDATA[Si quiero terminar mas rápido ese side project que me va a volver millonario (?) puedo aprender mecanografía y escribir código más rápido o puedo buscar la forma de escribir menos código...]]></summary><content type="html"><![CDATA[<blockquote>Si quiero terminar mas rápido ese side project que me va a volver millonario (?) puedo aprender mecanografía y escribir código más rápido o puedo buscar la forma de escribir menos código...</blockquote><h1>Conoce tus herramientas</h1>
<p><time datetime="2024-01-21">21/01/2024</time></p>
<p>Este post se centrará sobre una de las formas que considero más importantes para lograr ser productivo, especialmente, como <a href="https://payfit.com/es/contenido-practico/colaborador-individual/" target="_blank">colaborador individual</a>: Conocer nuestras herramientas de trabajo en profundidad.</p>
<p>Los seres humanos contamos con un único recurso universal e inmutable. Nacemos con él y morimos cuando finalmente se nos acaba. A diferencia de otras aptitudes que también podríamos considerar recursos como la fuerza o la inteligencia, las cuales pueden entrenarse para mejorar en el transcurso de nuestras vidas, este recurso en particular no es renovable ni podemos aumentarlo: nuestro tiempo.<br>
Nuestro tiempo es limitado y valioso, por lo que es normal querer asegurarnos de aprovecharlo al máximo. Pero aunque no exista manera de contar con más de 24 horas en un día, sí tenemos el poder de utilizar ese tiempo de manera más eficiente, generando un mayor valor agregado o impacto con el tiempo que dedicamos a ciertos objetivos.</p>
<p>A diferencia de otras áreas o rubros donde una mayor destreza física (por ejemplo, fuerza o velocidad) pueden derivar en una mayor productividad, en <abbr title="Information Technology" lang="en">IT</abbr> este no suele ser el caso. Es verdad que, por ejemplo, practicando mecanografía, un programador puede aumentar la cantidad de líneas de código que escribe por minuto, pero estrategias de este estilo suelen ser mucho menos efectivas que buscar formas de escribir menos código, en lugar de escribir más rápido.<br>
En mi opinión, debemos aspirar más a ser artesanos experimentados que trabajan con precisión y meticulosidad sin dar una sola cincelada de más; en lugar de buscar convertirnos en fábricas de producción masiva.</p>
<p>Es aquí donde entran en juego nuestras herramientas, y el conocerlas para saber cuál se adecúa mejor a cada una de nuestras necesidades y como utilizarlas para aprovecharlas al máximo.</p>
<h2>¿Cuales son nuestras herramientas?</h2>
<p>Todo el <span lang="en">software</span> que utilizamos es parte de nuestra caja de herramientas. Desde el sistema operativo que ejecuta nuestra máquina, hasta el editor de texto o <abbr title="Integrated Ddevelopment Environment" lang="en">IDE</abbr> en el que escribimos nuestro código e incluso los propios compiladores o interpretes de los distintos lenguajes de programación que utilizamos.</p>
<p>También los <i lang="en">frameworks</i> y bibliotecas que utilizamos son parte de nuestra caja de herramientas, y aunque el resto de este post no se centra en ellos, vale la pena mencionar su importancia ya que, conocerlos y entender las posibilidades y facilidades que nos brindan así como sus limitaciones es otra excelente manera de aumentar nuestra productividad a la hora de programar.</p>
<h2>Aspectos esenciales</h2>
<p>A mi entender, hay algunos aspectos transversales a casi todas las herramientas informáticas que resultan claves para aumentar nuestra eficiencia. Estos son:</p>
<ul>
<li>Configuraciones</li>
<li>Atajos de teclado</li>
<li>Respaldos y Sincronización</li>
</ul>
<h3>Configuraciones</h3>
<p>La mayoría del <span lang="en">software</span> dispone de distintas opciones y configuraciones que, dependiendo de nuestras tareas diarias, experiencia y preferencias personales, pueden simplificar nuestro trabajo.</p>
<p>Siempre es recomendable al utilizar una nueva herramienta, dedicar un tiempo a leer su documentación y explorar sus distintas opciones y configuraciones. Esto nos permitirá tener, por lo menos, una idea general sobre las posibilidades que ofrece para poder, a medida que la utilicemos, realizar los ajustes necesarios para poder trabajar de la manera que nos resulte más cómoda, práctica y productiva.</p>
<h3>Atajos de teclado</h3>
<p>Es muy común que los sistemas operativos y programas que utilizamos cuenten con múltiples atajos de teclado para realizar distintas acciones. La cantidad de teclas que podemos combinar para conseguir distintos resultados es realmente inmensa y no tiene ningún sentido intentar memorizar absolutamente todos los atajos de teclado de los que disponemos, pero sí, puede ahorrarnos bastante tiempo el aprender los atajos correspondientes a las acciones que realizamos con más frecuencia.</p>
<h3>Respaldos y sincronización</h3>
<p>La mayoría del <span lang="en">software</span> incluye alguna opción para crear <i lang="en">backups</i>, puntos de restauración o por lo menos realizar exportación de sus configuraciones. Es importante conocer estas opciones y utilizarlas.</p>
<p>Inevitablemente, en algún momento, vamos a romper nuestro ordenador. Pueden ser hoy, mañana o dentro de tres años. Puede ser intentando ajustar configuraciones para que la computadora funcione de manera más fluida, puede ser intentando <i>crackear</i> un juego o puede ser que nuestro gato camine arriba del teclado. Ni el momento ni la razón importan. Lo importante es que va a pasar. Y por eso debemos estar preparados para restaurar nuestro equipo a un estado en el que sea utilizable con la mayor rapidez, posible.</p>
<p>Mejor aún si además de crear estos respaldos tenemos alguna herramienta que nos permita almacenarlos en la nube o incluso sincronizar nuestras configuraciones entre dispositivos.</p>
<h2>Menciones de honor</h2>
<p>A continuación voy a dejar un repaso rápido sobre los aspectos que acabamos de discutir sobre algunas de las herramientas que más utilizo (y, por lo tanto, en las que valor me aporta el aumentar la eficiencia)</p>
<h3>Sistema Operativo</h3>
<p>No creo que nadie se sorprenda de ver el sistema operativo primero en la lista. ¿Es una obviedad que es importante saber usar una computadora? Sí. ¿Me va a volver más productivo por ser un <i lang="en">Power User</i> y conocer cada posible configuración de mi sistema operativo? No.</p>
<p>En el apartado de configuraciones lo que considero más relevante es conocer:</p>
<ol>
<li>La estructura de directorios (carpetas, si son usuarios de <span lang="en">Windows</span>).</li>
<li>Cómo visualizar archivos y directorios ocultos.</li>
<li>Sistema de permisos.</li>
<li><code>PATH</code> (Si van a programar esto es mucho muy importante).</li>
</ol>
<p>Sobre esta base se pueden seguir incorporando otros elementos que ayuden a organizarse mejor como, por ejemplo, el uso de múltiples escritorios o <i lang="en">workspaces</i>.</p>
<p>¿Atajos de teclado? Muchísimos. Solo voy a dejar las listas de atajos de algunos de los sistemas operativos más comunes. Queda a criterio de cada uno decidir cuales les resultaran más útiles.</p>
<ul>
<li><a href="https://support.microsoft.com/es-es/windows/m%C3%A9todos-abreviados-de-teclado-de-windows-dcc61a57-8ff0-cffe-9796-cb9706c75eec" target="_blank">Atajos de teclado <span lang="en">Windows</span></a></li>
<li><a href="https://help.ubuntu.com/stable/ubuntu-help/shell-keyboard-shortcuts.html.es" target="_blank">Atajos de teclado <span lang="za">Ubuntu</span></a></li>
<li><a href="https://docs.kde.org/stable5/es/plasma-desktop/plasma-desktop/shortcuts.html" target="_blank">Atajos de teclado KDE</a> (Ya sé que no es un sistema operativo como tal, ¿pero a quién le importa?)</li>
<li><a href="https://support.apple.com/es-es/HT201236" target="_blank">Atajos de teclado Mac</a></li>
</ul>
<p>En cuanto a los respaldos, los sistemas operativos suelen incluir alguna solución nativa para realizar respaldos.</p>
<p>Adicionalmente, a mí me gusta utilizar <a href="https://git-scm.com/" lang="en" hreflang="en" target="_blank">git</a> para versionar las configuraciones y documentos más importantes. Es algo bastante simple de realizar en <span lang="en">GNU Linux</span>, pero desconozco que tan efectivo sería esto en <span lang="en">Windows</span>.</p>
<p>Y para mantener backups en la nube yo utilizo el servicio de <a href="https://mega.io/?aff=3oYhIQCzTQc" target="_blank">Mega</a>.</p>
<h3>Terminal (Consola)</h3>
<p>En mi día a día yo suelo utilizar <a href="https://neon.kde.org/" lang="en" hreflang="en" target="_blank">KDE Neon</a>, una distribución de <span lang="en">GNU Linux</span> y, probablemente por costumbre más que por otra razón, mi terminal preferida es <span lang="en">Bash</span>, pero los puntos que voy a mencionar aplican a cualquier otra shell (<span lang="en">Z shell</span>, <span lang="en">Fish shell</span>, etc) o (al menos la mayoría) incluso a consolas de <span lang="en">Windows</span> como <span lang="en">Powershell</span> o el inmortal “cmd” .</p>
<p>Las terminales son herramientas de los más versátiles y sus opciones de configuración son prácticamente infinitas, pero entre todo lo que se puede configurar, personalmente, destaco:</p>
<dl>
<dt>Alias</dt>
<dd>s posible asignar un alias a una comando largo y complicado (incluso con <i lang="en">flags</i> o argumentos predefinidos) para simplificar su uso.</dd>
<dt>Funciones</dt>
<dd>Si un alias se queda corto para simplificar la ejecución de una tarea, siempre podemos definir funciones personalizadas (pequeños programas) que realicen una serie de comandos específicos.</dd>
<dt>Autocompletado</dt>
<dd>Muchas aplicaciones de CLI cuentan automáticamente con autocompletado de argumentos, pero para las que no, es posible definir un script o configuración que permita el autocompletado de forma personalizada.</dd>
</dl>
<pre><code class="language-bash"># git checkout remote
# For usage, execute `gcr` without any argument
function gcr() {
    local REMOTE='origin'

    if [ -z &quot;${1}&quot; ]
    then
        echo &quot;gcr creates a new branch based on an ${REMOTE} branch.
If the branch already exists it is overwritten.

Usage:
    gcr &lt;${REMOTE} base branch&gt; &lt;name-for-new-branch&gt;
Example:
        gcr development new-feature
    will result in the creation of the branch development_new-feature
    tracking ${REMOTE}/development
&quot;
        return
    fi

    git fetch -t -P &quot;${REMOTE}&quot; &amp;&amp;

    local BRANCH_NAME='' &amp;&amp;

    if [ -z &quot;$2&quot; ]
    then
        BRANCH_NAME=&quot;${1}&quot;
    else
        BRANCH_NAME=&quot;${1}_${2}&quot;
    fi &amp;&amp;

    git checkout -t &quot;${REMOTE}/${1}&quot; -B &quot;${BRANCH_NAME}&quot;
}
# gcr bash completion
function __remote_branch_completion() {
    local REMOTE_BRANCHES=&quot;$(__git_remote_heads)&quot;
    COMPREPLY=($(compgen -W &quot;${REMOTE_BRANCHES//'origin/'/}&quot; &quot;${COMP_WORDS[1]}&quot;))
}
complete -F __remote_branch_completion gcr
</code></pre>
<blockquote>
<p>Ejemplo de función bash que simplifica el comando <code>git checkout</code> para crear y cambiar a una nueva rama basada en una rama remota específica y la configuración de su autocompletado.</p>
</blockquote>
<p>Las terminales también cuentan con <a href="https://itsfoss.com/linux-terminal-shortcuts/" hreflang="en" target="_blank">atajos de teclado</a> que pueden resultarnos de los más útiles.</p>
<h3 lang="en">Visual Studio Code</h3>
<p>Otra herramienta con una cantidad enorme de configuraciones y atajos, además de la posibilidad de sincronizar estas entre dispositivos.</p>
<p>En cuanto a sus configuraciones sugiero revisarlas minuciosamente, la manera de hacer esto que a mí me resulta más práctica es:</p>
<ol>
<li>Presionamos la combinación de teclas <code>Ctrl+Shift+p</code> para abrir la barra de comandos.</li>
<li>Escribimos <code>Preferences: Open User Settings (JSON)</code> y pulsamos Enter.
Esto nos abrirá un archivo <abbr title="JavaScript Object Notation">JSON</abbr> donde podemos definir nuestras configuraciones personalizadas.</li>
<li>Comenzaremos agregando: <code>&quot;workbench.settings.useSplitJSON&quot;: true</code>.
Esto nos permitirá ver, a la vez, las configuraciones que nosotros hayamos definido y todas las configuraciones disponibles (incluidas las correspondientes a extensiones) con sus valores por defecto.</li>
</ol>
<p>De este modo, podemos explorar todas las opciones y ajustarlas según nuestras necesidades.</p>
<p>De manera similar con <code>Preferences: Open Keyboard Shortcuts</code> podemos revisar todos los atajos de los que el Visual Studio Code dispone, asi como configurar nuestros propios atajos personalizados.</p>
<h3 lang="en">Git</h3>
<p>Lean la documentación de <span lang="en">git</span>. Sus configuraciones son muchas y de los mas variadas, permite la creación de aliases y personalizar muchos de sus comportamientos con herramientas de terceros como <a href="https://github.com/dandavison/delta" hreflang="en" target="_blank">delta</a></p>
<h2>Próximamente</h2>
<p>Hasta aquí llega el post de hoy. Próximamente espero poder compartir algunas de las configuraciones que mas aprovecho en mi día a día.</p>
<p>¡Hasta pronto!</p>

<script type="module" defer>
	import hljs from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js';
	import bash from 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/languages/bash.min.js';
	hljs.registerLanguage('bash', bash);
	hljs.highlightAll()
</script>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20240121_conoce_tus_herramientas/"/><updated>2024-01-21T20:35:55Z</updated><published>2024-01-21T20:35:55Z</published></entry><entry><id>https://iyaki.ar/posts/20231210_sobre_el_governance_crew/</id><title><![CDATA[Sobre el Governace Crew en esquemas unFIX]]></title><summary><![CDATA[El modelo unFIX me pareció muy interesante desde la primera vez que leí sobre él pero intentar implementarlo no me fue tan sencillo como esperaba en un primer momento.]]></summary><content type="html"><![CDATA[<blockquote>El modelo unFIX me pareció muy interesante desde la primera vez que leí sobre él pero intentar implementarlo no me fue tan sencillo como esperaba en un primer momento.</blockquote><h1>Sobre el <span lang="en">Governace Crew</span> en esquemas <span lang="en">unFIX</span></h1>
<p><time datetime="2023-12-10">10/12/2023</time></p>
<p>Este artículo fue escrito a lo largo de varios meses y mis ideas respecto a
<i lang="en">unFIX</i> han ido mutando durante todo este tiempo. Este post busca reflejar la
transformación que fueron sufriendo esas ideas. Si en algún punto encuentran
inconsistencias o notan que algo se vuelve confuso, por favor háganmelo saber.
Cualquier aporte es más que bienvenido y voy a estar encantado de poder dialogar
sobre este tema. Como siempre pueden contactarme a: <a href="mailto:me@iyaki.ar" target="_blank">me@iyaki.ar</a>.</p>
<h2>Introducción</h2>
<p><a href="https://unfix.com/" lang="en" hreflang="en" target="_blank">UnFIX</a> es un proyecto relativamente nuevo y como tal evoluciona constantemente.
Lo descubrí hace varios meses y desde entonces lo he seguido desde cerca. Sin
embargo, hasta hace poco, y sin que me percatara, estaba ignorando uno de sus
conceptos (en mi opinión) más poderosos.</p>
<p>El objetivo de este artículo no es brindar una introducción al modelo, sino
ahondar en algunos de sus aspectos, por lo que si no están familiarizados con
<i lang="en">unFIX</i> les recomiendo estos dos excelentes artículos introductorios de Javier
Garzas:</p>
<ul>
<li><a href="https://www.javiergarzas.com/2023/02/unfix-innovador-modelo-de-escalado-o-un-refrito-gourmet-parte-i.html" target="_blank">Innovador modelo de escalado o un refrito <span en="en">gourmet</span> parte I</a>.</li>
<li><a href="https://www.javiergarzas.com/2023/02/unfix-2a-parte-innovador-modelo-de-escalado-o-un-refrito-gourmet-de-antiguos-patrones.html" target="_blank">Innovador modelo de escalado o un refrito <span en="en">gourmet</span> parte II</a>.</li>
</ul>
<p>O bien, que vayan al <a href="https://unfix.com/blog/the-unfix-model" hreflang="en" target="_blank">primer post del blog de <i lang="en">unFIX</i></a>
o a su <a href="https://unfix.com/what-is-unfix" hreflang="en" target="_blank">explicación “oficial”</a> y luego sigan
recorriendo las distintas secciones del website.</p>
<h2>Mi motivación</h2>
<p>Actualmente, trabajo como director del área de Producto y Tecnología de una
empresa enfocada en proveer un <abbr title="Software as a Service" lang="en">SaaS</abbr> dirigido a la industria de la salud. Es por
esto que siempre me encuentro buscando nuevas formas de mejorar los procesos y
la organización para ser más eficientes. Y en ese sentido, desde el comienzo,
<i lang="en">unFIX</i> me pareció una forma innovadora de organizar el equipo de personas que
trabaja conmigo.</p>
<p>Las responsabilidades de mi rol incluyen todo lo relativo al diseño, desarrollo
y mantenimiento de nuestro <abbr title="Software as a Service" lang="en">SaaS</abbr>, así como los temas relacionados con <abbr title="Recursos Humanos">RR.HH.</abbr> del
plantel del área de Producto, evaluaciones de desempeño, <i lang="en">coaching</i> o
acompañamiento en el desarrollo profesional.</p>
<p>Cuanto más leía respecto al modelo <i lang="en">unFIX</i> más convencido me encontraba de que
podía aportarnos valor y ayudarnos a seguir creciendo.</p>
<p>Aunque algunos de sus conceptos podían aplicarse de manera aislada dentro del
área de Producto, estaba convencido de que el mayor valor que tenía para aportar
se encontraba en la implementación del modelo en toda la compañía. Pero al
intentar convertir toda la empresa al modelo <i lang="en">unFIX</i> siempre había un aspecto que
no podía terminar de acomodar: El <i lang="en">management</i>.</p>
<p>Este es un organigrama aproximado del esquema de trabajo que manejamos
actualmente (que probablemente sea vea similar al 90% de los organigramas de
cualquier empresa pequeña o mediana).</p>
<figure>
	<img-viewer>
		<a href="https://iyaki.ar/posts/20231210_sobre_el_governance_crew/organigrama.webp">
			<img src="https://iyaki.ar/posts/20231210_sobre_el_governance_crew/organigrama.webp" alt="Organigrama previo a aplicar el modelo unFIX">
		</a>
	</img-viewer>
	<figcaption>Estructura básica del organigrama previo a aplicar el modelo <i lang="en">unFIX</i></figcaption>
</figure>
<p>Lo más natural (al menos para mí), en una primera aproximación, fue pensar una
migración al modelo <i lang="en">unFIX</i> de la siguiente manera:</p>
<figure>
	<img-viewer>
		<a href="https://iyaki.ar/posts/20231210_sobre_el_governance_crew/base_unfix.webp">
			<img src="https://iyaki.ar/posts/20231210_sobre_el_governance_crew/base_unfix.webp" alt="Primer intento de aplicar el modelo unFIX">
		</a>
	</img-viewer>
	<figcaption>Estructura básica luego de un primer acercamiento al modelo <i lang="en">unFIX</i></figcaption>
</figure>
<p>En esta migración, tanto los puestos <i lang="en">C-Level</i>, como los de dirección, pasan a
conformar el <a href="https://unfix.com/governance-crew" lang="en" hreflang="en" target="_blank">Governance Crew</a>.</p>
<p>Y hasta acá todo parecía funcionar. Sobre el papel, las piezas encajan. Todo
puede ser modelado según las reglas de <i lang="en">unFIX</i>. Pero aún sentía que había algunos
puntos que definir antes de poder implementar este nuevo esquema. Principalmente
en lo que respecta al <i lang="en">management</i>. Suena tentador (por lo fácil que sería)
mantener la separación de responsabilidades dentro del personal que forma el
<i lang="en">Governance Crew</i> igual que hasta ahora. Pero también se siente poco natural al
observar el nuevo “organigrama” y ver que no existen separaciones fuertes de
cara al resto de los Crews respecto a las responsabilidades de cada uno de los
integrantes del <i lang="en">Governance Crew</i>.</p>
<p><i lang="en">UnFIX</i> propone algunos lineamientos en cuanto a como debería funcionar el
<i lang="en">management</i>, un resumen de estos lineamientos sería:</p>
<ol>
<li>El trabajo de “gestionar gente” solo puede ser realizado por aquellos con el
rol de <a href="https://unfix.com/chief" lang="en" hreflang="en" target="_blank">Chief</a>.</li>
<li>Solo los <i lang="en">Chief</i> pueden ser <a href="https://in.indeed.com/career-advice/finding-a-job/what-does-people-manager-do" lang="en" hreflang="en" target="_blank">People managers</a>
de otras personas.</li>
<li>Los <i lang="en">Chief</i> únicamente pueden reportar ante otros <i lang="en">Chief</i>.</li>
<li>Los <i lang="en">Chief</i> son, en última instancia, responsables por todo lo que ocurra en
la <a href="https://unfix.com/base-types" lang="en" hreflang="en" target="_blank">Base</a>.</li>
<li>El rol de <i lang="en">Chief</i> solo existe dentro del <i lang="en">Governance Crew</i>.</li>
<li>El <i lang="en">Governance Crew</i> suele estar compuesto por múltiples <i lang="en">Chief</i>.</li>
<li>El <i lang="en">Governance Crew</i> es el encargado de asegurar la motivación del resto de
las personas.</li>
<li>El <i lang="en">Governance Crew</i> es el responsable del negocio frente a los
<i lang="en">stakeholders</i> externos.</li>
<li>El <i lang="en">Governance Crew</i> define el propósito de la <i lang="en">Base</i>.</li>
<li>Existe un único <i lang="en">Governance Crew</i> por <i lang="en">Base</i>.</li>
</ol>
<p>Son buenos lineamientos pero muy generales. Por lo que, me dispuse a agregar
algunas reglas más para “ordenar” un poco el equipo en nuestro caso particular.</p>
<p>Así comencé a pensar distintas maneras de separar las responsabilidades de los
integrantes del <i lang="en">Governance Crew</i>. Y llegué a múltiples opciones, algunas de
ellas fueron:</p>
<ul>
<li>Gobernanza por Perfil: Definir un <i lang="en">Chief</i> en el <i lang="en">Governance Crew</i> por cada área o
función específica dentro de la organización, como por ejemplo, un <i lang="en">Chief</i> de
<i lang="en">Marketing</i>, un <i lang="en">Chief</i> de Recursos Humanos, etc.</li>
<li>Gobernanza por <a href="https://unfix.com/value-streams" lang="en" hreflang="en" target="_blank">Value Stream</a>: Separar las responsabilidades del Governance
Crew según los diferentes flujos de valor de la organización, lo que
implicaría tener un <i lang="en">Chief</i> encargado de cada uno de ellos, como por ejemplo,
un <i lang="en">Chief</i> del proceso de producción, un <i lang="en">Chief</i> del proceso de ventas, etc.</li>
<li>Gobernanza por <a href="https://unfix.com/investment-horizons" target="_blank">Investment Horizon</a>:
Dividir las responsabilidades del <i lang="en">Governance Crew</i> según los diferentes
<i lang="en">Investment Horizon</i> en los que se enfoca la organización.</li>
<li>Gobernanza por entropía: Tirar dados para decidir como separar las
responsabilidades de los miembros del <i lang="en">Governance Crew</i>. Aunque es la opción
menos seria de todas las que pude pensar, finalmente me pareció igual de
arbitraria que cualquiera de las anteriores.</li>
</ul>
<p>Además, al reflexionar en como sería un día normal de trabajo con cualquiera de
estas nuevas estructuras, no puedo evitar sentir una sensación de inconformidad
al profundizar en algunos detalles.</p>
<p>Cuanto más intentaba encontrar una respuesta que me satisficiera, más me
alejaba de las opciones de gobernanza que había pensado, y es que en múltiples
artículos del <a href="https://unfix.com/blog" hreflang="en" target="_blank">blog de <i lang="en">unFIX</i></a>
(<a href="https://unfix.com/blog/manage-the-system" lang="en" hreflang="en" target="_blank">Manage the System, Lead the People</a>,
<a href="https://unfix.com/blog/agile-and-aligned" lang="en" hreflang="en" target="_blank">How Do We Keep the Business Agile and Aligned?</a>,
<a href="https://unfix.com/blog/middle-managers-should-stop-coordinating" lang="en" hreflang="en" target="_blank">Middle Managers Should Stop Coordinating</a>
) y algunos <a href="https://youtu.be/6IRQWT-kxXs" hreflang="en" target="_blank">recursos externos</a> quedaba bastante
claro que el enfoque que estaba dando al trabajo que debía realizar el
<i lang="en">Governance Crew</i> era incorrecto y gran parte de las responsabilidades de
dirección que hoy cubro no tenían lugar en el <i lang="en">Governance Crew</i>.</p>
<h2>El problema</h2>
<p>Mi trabajo ya no existía.</p>
<p>O mejor dicho, para seguir las directivas del modelo <i lang="en">unFIX</i> mi puesto de
<a href="https://en.wikipedia.org/wiki/Middle_management" lang="en" hreflang="en"  target="_blank">Middle Management</a> no debía
existir.</p>
<p>En una primera instancia darme cuenta de esto fue un <i lang="en">shock</i> importante. También
fue el punto en que dejé este artículo sin terminar durante bastante tiempo y
pensé en abandonar mi proyecto de implantación del modelo <i lang="en">unFIX</i> en mi trabajo.<br>
Pero a media que pasaron los días y las semanas, al seguir dándole vueltas al
asunto, y buscando más información sobre el tema, finalmente, todas las
piezas encajaron.</p>
<h2>La revelación</h2>
<p>Aunque mi puesto no pudiese existir como lo había hecho hasta ahora, eso no
tenía por qué cambiar mi rol o responsabilidades como individuo. Y es que <i lang="en">unFIX</i>
contempla la posibilidad de participar en
<a href="https://unfix.com/blog/the-multiteaming-way" hreflang="en" target="_blank">múltiples equipos a la vez</a>, así
como distintos <a href="https://unfix.com/participation-levels" lang="en" hreflang="en" target="_blank">Participation Levels</a>,
<a href="https://unfix.com/blog/no-more-fixed-jobs" lang="en" hreflang="en" target="_blank">Time commitments</a> y la existencia de
<a href="https://unfix.com/facilitation-crew" lang="en" hreflang="en" target="_blank">Facilitation crews</a>. Mis responsabilidades
y todo lo que disfruto de mi trabajo seguían ahí, pero ya no englobadas en
un único puesto.</p>
<p>Esto permite tomar decisiones sobre la base y su funcionamiento, como parte de
los miembros del <i lang="en">Governance Crew</i>, pero también seguir participando en el
<i lang="en">coaching</i> de otros colaboradores como parte de un <i lang="en">Facilitation Crew</i> o colaborar
en distintos equipos y proyectos de manera más flexible.</p>
<p>La posibilidad de participar en múltiples equipos es, en mi opinión, una de las
características más interesantes y liberadoras del modelo <i lang="en">unFIX</i>. Y una de
las que más lo diferencia de la mayoría de los <i lang="en">frameworks Agile</i> que prohíben
esta práctica y buscan equipos lo más estables posible. Es cierto que esta
estabilidad tiene sus ventajas, pero dependiendo de factores como el tamaño de
la organización o la velocidad a la que cambian los objetivos puede no ser la
opción más beneficiosa.</p>
<h2>¿Conclusión?</h2>
<p>Actualmente, estamos trabajando en adaptar nuestra organización interna a un
modelo más fluido y fuertemente influenciado por las propuestas de <i lang="en">unFIX</i>, pero
es una labor que apenas estamos comenzando, por lo que hoy no podría brindar una
conclusión sobre las ventajas, desventajas o desafíos que esto nos presente.
Espero tener noticias al respecto pronto para poder compartirlas.</p>
<script type="module" defer>
import ImgViewer from '/components/ImgViewerElement.js'
customElements.define('img-viewer', ImgViewer);
</script>
]]></content><link rel="alternate" href="https://iyaki.ar/posts/20231210_sobre_el_governance_crew/"/><updated>2023-12-10T20:21:38Z</updated><published>2023-12-10T20:21:38Z</published></entry></feed>