<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Juan C. Ruiz</title>
    <description>The latest articles on Forem by Juan C. Ruiz (@juancrg90).</description>
    <link>https://forem.com/juancrg90</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F124396%2Ff2df59ba-9a4f-426e-8a29-26b437816892.jpeg</url>
      <title>Forem: Juan C. Ruiz</title>
      <link>https://forem.com/juancrg90</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/juancrg90"/>
    <language>en</language>
    <item>
      <title>Consejos prácticos para proteger tu identidad digital 🔐</title>
      <dc:creator>Juan C. Ruiz</dc:creator>
      <pubDate>Tue, 23 Nov 2021 04:51:38 +0000</pubDate>
      <link>https://forem.com/juancrg90/consejos-practicos-para-proteger-tu-identidad-digital-3i6b</link>
      <guid>https://forem.com/juancrg90/consejos-practicos-para-proteger-tu-identidad-digital-3i6b</guid>
      <description>&lt;p&gt;Hace unos días un amigo me contó de una experiencia que tuvo donde el robo de un celular desencadenó mucho estrés y situaciones que uno muchas veces no contempla.&lt;/p&gt;

&lt;p&gt;Por lo general cuando escuchamos de un robo o pérdida de celular lo primero que pensamos es en "va a terminar en una casa de empeño o en el tianguis".&lt;/p&gt;

&lt;p&gt;Sin embargo, hoy en día nuestro smartphone es más que una simple herramienta para hacer una llamada o enviar un SMS, la mayor parte de nuestra vida se encuentra contenida en ese pequeño aparato, además de que es la puerta de entrada a nuestra identidad digital.&lt;/p&gt;

&lt;p&gt;Desde cuentas de redes sociales (Twitter, FB, IG, TkTk, etc) hasta ya temas más serios, como accesos bancarios, criptomonedas (para estar a la moda), servicios de compra en linea, entre otros, están contenidos a solo un slide de distancia.&lt;/p&gt;

&lt;p&gt;Incluso he llegado a escuchar a familiares y conocidos decir "no tengo nada que ocultar" o "no tengo nada que me roben".&lt;/p&gt;

&lt;p&gt;Pero viéndolo en perspectiva ¿te has cuestionado, qué pasaría si alguien al robar tu celular tuviera suficiente tiempo para acceder a tu correo electrónico y cambiar tu teléfono de acceso además de las claves de recuperación?&lt;/p&gt;

&lt;p&gt;Nota: Todo lo que voy a compartir a partir de aquí son solo consejos de alguien que ya tiene unos cuantos años vagando por este mundo digital, no soy ningún experto en seguridad, ni sé hackear FB. Sin embargo, me pareció importante apoyar un poco a crear conciencia en estos temas.&lt;/p&gt;

&lt;p&gt;La seguridad de nuestra identidad digital debe ser algo que tengamos bien presente a la hora de crear cuentas en los distintos servicios en los que nos hemos registrado o estemos por agregar a nuestra lista.&lt;/p&gt;

&lt;p&gt;Muchos hemos pasado por el ir agregando nuevos caracteres a una contraseña básica que obtuvimos hace unos ayeres y pensando que nuestra estrategia es infalible y "cómoda" porque nadie nunca sabrá como la creamos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.commitstrip.com/en/2015/08/19/password-memories/"&gt;&lt;br&gt;
  &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o-xQXpo0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1637642123/Consejos%2520pra%25CC%2581cticos%2520para%2520proteger%2520tu%2520identidad%2520digital/Untitled_u6sipz.png" alt="Password memories commitstrip" width="650" height="904"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pero hoy día, las computadoras pueden calcular una cantidad enorme de passwords por segundo, para expresarlo en números redondos, este artículo de &lt;a href="https://www.gizmodo.com.au/2020/09/a-computer-can-guess-more-than-100000000000-passwords-per-second/"&gt;Gizmodo AU&lt;/a&gt; escrito en 2020 menciona la cifra de 100,000,000,000 passwords por segundo.  Dentro del artículo se comenta que un password de 8 caracteres puede ser hackeado en aproximadamente 12 minutos usando una instancia en la nube. Ahora bien, entre más larga sea nuestra contraseña más tiempo le tomará a un atacante el tratar de calcularla.&lt;/p&gt;

&lt;p&gt;Por lo que nuestra mejor opción hoy día es hacer uso de algún servicio de manejo de contraseñas, el cual se encargue de generarlas y almacenarlas quedando así la tarea de aprender solo una contraseña maestra. Esta contraseña  debería ser lo suficientemente compleja como para evitar que nos la roben en un par de minutos.&lt;/p&gt;

&lt;p&gt;Aquí en los internets existen una gran cantidad de servicios, los cuales podemos usar para esta labor, entre los más populares destacan &lt;a href="https://1password.com/"&gt;1password.com&lt;/a&gt;, &lt;a href="https://www.lastpass.com/"&gt;lastpass.com&lt;/a&gt;, &lt;a href="https://www.enpass.io/"&gt;enpass.io&lt;/a&gt;, &lt;a href="http://www.safe-in-cloud.com/"&gt;safe-in-cloud.com&lt;/a&gt; entre otros.  El servicio que decidas utilizar dependerá de factores como el si almacena las contraseñas en la nube de la empresa proveedora o en un archivo encriptado en tu nube personal, así  como del precio y algunas funciones como llenado de formularios o integración biométrica con tu smartphone. Si aún no utilizas ninguno, no pierdes nada con echarles un ojo, muchas de estas herramientas incluso cuentan con funciones que te permiten detectar si alguno de tus passwords ha sido comprometido y anda vagando por ahí en la Dark web.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P1uqPUBm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1637642121/Consejos%2520pra%25CC%2581cticos%2520para%2520proteger%2520tu%2520identidad%2520digital/Untitled_1_buyhdp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P1uqPUBm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1637642121/Consejos%2520pra%25CC%2581cticos%2520para%2520proteger%2520tu%2520identidad%2520digital/Untitled_1_buyhdp.png" alt="Dark web monitoring" width="880" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G_I5YSuV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1637642121/Consejos%2520pra%25CC%2581cticos%2520para%2520proteger%2520tu%2520identidad%2520digital/Untitled_2_pjgmfs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G_I5YSuV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1637642121/Consejos%2520pra%25CC%2581cticos%2520para%2520proteger%2520tu%2520identidad%2520digital/Untitled_2_pjgmfs.png" alt="Search compromised passwords" width="518" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora bien esto es algo muy general, si un atacante lograra adueñarse del acceso a tu correo electrónico y de tu número de teléfono, podría simplemente solicitar la recuperación de contraseña a los servicios y comenzar a hacer cargos a tu nombre. Para buscar limitar o retrasar el que el atacante pueda ingresar al resto de los servicios relacionados con tu cuenta. podemos utilizar lo que se le conoce como la autenticación en 2 factores (2FA). Esta función hará que los servicios donde este activa soliciten una clave adicional al momento de iniciar sesión. Esta clave es generada de manera dinámica por algunas aplicaciones como  &lt;a href="https://support.google.com/accounts/answer/1066447?hl=en&amp;amp;co=GENIE.Platform%3DAndroid"&gt;Google Authenticator&lt;/a&gt; o &lt;a href="https://authy.com/"&gt;Authy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Algunos servicios te dan la opción de recibas el código via SMS, pero si estamos hablando del caso hipotético de que nos roben el celular, dudo que queramos que sea nuestra opción preferida.&lt;/p&gt;

&lt;p&gt;Complementando lo que hablábamos de manejadores de contraseñas, algunos de estos servicios incluyen ya la funcionalidad para que también manejar el &lt;a href="https://support.1password.com/one-time-passwords/"&gt;token dinámico del 2FA&lt;/a&gt;. Por el momento no tengo una postura respecto a manejar las contraseñas y los tokens de 2FA en una sola herramienta o dividir las responsabilidades. Si tu utilizas la misma herramienta para ambas labores me gustaría leer tu opinion.&lt;/p&gt;

&lt;p&gt;Si realmente quisiéramos llevar este tema del segundo paso de autenticación a otro nivel, existen herramientas como las &lt;strong&gt;YubiKey&lt;/strong&gt; creadas por  &lt;a href="https://www.yubico.com/"&gt;yubico.com&lt;/a&gt;, las cuales nos permiten hacer login hasta que la llave este conectada a nuestra computadora o smartphone y lo hayamos aprobado.&lt;/p&gt;

&lt;p&gt;El principal contra de estas llaves es que no todos los servicios lo soportan, sin embargo es una buena opción para proteger los servicios más importantes, como nuestro correo electrónico (en el caso de Gmail).&lt;/p&gt;

&lt;p&gt;Como un último consejo, identifica donde puedes dar de baja dispositivos de tus cuentas principales, por ejemplo Google tiene esta ruta donde puedes ver que dispositivos están conectados a tu cuenta y terminar la sesión desde ahí. &lt;a href="https://myaccount.google.com/device-activity"&gt;https://myaccount.google.com/device-activity&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recapitulando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nunca dejes tu teléfono sin contraseña para entrar a las aplicaciones.&lt;/li&gt;
&lt;li&gt;Utiliza un manejador de contraseñas en lugar de usar "la misma con unos pequeños cambios".&lt;/li&gt;
&lt;li&gt;Siempre activa la autenticación de dos pasos.&lt;/li&gt;
&lt;li&gt;No uses SMS como tu opción para recuperar cuentas&lt;/li&gt;
&lt;li&gt;Si puedes y tienes la oportunidad invierte en una llave física para el 2FA&lt;/li&gt;
&lt;li&gt;Identifica donde desconectar dispositivos de tus cuentas principales en caso de una emergencia.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Espero este artículo te sea de utilidad, repito no soy un gurú de seguridad, sin embargo creo esta es información valiosa que puede servirle a más de una persona, si conoces a alguien que le pudiera servir por favor ayúdame a difundirlo.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Potenciando tu carrera profesional a través del Open Source</title>
      <dc:creator>Juan C. Ruiz</dc:creator>
      <pubDate>Wed, 27 Oct 2021 02:43:16 +0000</pubDate>
      <link>https://forem.com/juancrg90/potenciando-tu-carrera-profesional-a-traves-del-open-source-3m6o</link>
      <guid>https://forem.com/juancrg90/potenciando-tu-carrera-profesional-a-traves-del-open-source-3m6o</guid>
      <description>&lt;p&gt;Durante las últimas semanas tuve la oportunidad de dar una charla titulada "Potenciando tu carrera profesional a través del Open Source" en diferentes eventos y comunidades, te comparto la grabación de la presentación con el equipo de &lt;a href="https://codigofacilito.com"&gt;Código Facilito&lt;/a&gt; en su 10 Aniversario.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/C5KdEAQd4m8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;En esta charla traté de plasmar algunos de los aprendizajes obtenidos a lo largo de casi 6 años contribuyendo a proyectos de código abierto, si no tuviste la oportunidad de verla o bien prefieres el formato en texto, aquí te comparto el contenido de esta.&lt;/p&gt;

&lt;p&gt;Primero que nada el contenido de este blogpost no es sobre qué es el Open Source o como contribuir a este, está más bien orientado en por qué comenzar a contribuir.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jESBm7HT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jESBm7HT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled.png" alt="Open Source Meme" width="726" height="726"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nota: Todo lo expresado en esta charla viene desde mi experiencia personal así que puede que haya cosas que a otros no les resulte relevantes o bien no sea el camino que siguieron, sin embargo si estas tomándote el tiempo de leerlo espero sea de tu agrado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4k_6Dz_R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4k_6Dz_R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_1.png" alt="Personal Experience Meme" width="676" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Contribuyendo desde tu comezón
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lyh_Ei8C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lyh_Ei8C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_2.png" alt="amazon-product-api" width="880" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creo la mejor manera de explicarte esto es empezar por mi primer intento por contribuir a un proyecto de código abierto. Hace unos años estaba trabajando en una devshop donde necesitábamos implementar un meta-buscador de productos y una de nuestras fuentes de información era Amazon, cuando estábamos haciendo la implementación nos encontramos con una biblioteca de JavaScript de nombre &lt;a href="https://github.com/t3chnoboy/amazon-product-api/"&gt;t3chnoboy/amazon-product-api&lt;/a&gt; la cual contenía algunas de las operaciones que necesitábamos en nuestro proyecto; sin embargo, ya que avanzamos con la implementación, nos encontramos con que no todas las operaciones que íbamos a utilizar estaban contenidas en esta dependencia, por lo que decidí ponerme a leer cómo estaba hecha la biblioteca e intenté agregar las operaciones que nos faltaban.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ELPnYuml--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ELPnYuml--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_3.png" alt="my-first-pull-request" width="880" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ya que terminé de realizar el primer cambio, decidí probar suerte y abrí un pull request titulado &lt;a href="https://github.com/t3chnoboy/amazon-product-api/pull/2"&gt;Added support for ItemPage&lt;/a&gt; donde después de un poco de feedback por parte de &lt;a href="https://github.com/t3chnoboy"&gt;t3chnoboy&lt;/a&gt;  terminó siendo integrado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qV7dClsi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qV7dClsi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_4.png" alt="collaboration-invitation" width="880" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Después de enviar otro aporte al repositorio t3chnoboy me ofreció hacerme &lt;a href="https://github.com/t3chnoboy/amazon-product-api/pull/3#issuecomment-82641462"&gt;colaborador del proyecto&lt;/a&gt; para que pudiera hacer aportes sin necesidad de depender de mi Fork, no sé si fue porque ya no le interesaba tanto la dependencia o bien porque vio mi interés por seguir haciéndole mejoras.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ABxGBet8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ABxGBet8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_5.png" alt="Grogu meme" width="880" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;De este primer intento aprendí que no se necesita ser un Super Senior para comenzar a contribuir, solo es de tener una necesidad y un poco de ganas de intentarlo. &lt;strong&gt;La comunidad está abierta a recibir gente nueva y a ayudarte a mejorar, así que no te descartes sin intentarlo.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  No todo lo que envíes será aceptado
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--raYfzqmu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--raYfzqmu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301090/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_6.png" alt="cropuploader package" width="880" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Por esas fechas estaba explorando un paquete llamado cropuploader para un framework de JavaScript llamado Meteor JS, envié un &lt;a href="https://github.com/jamgold/cropuploader/pull/3"&gt;pull request&lt;/a&gt; donde agregaba algunos archivos de configuración faltantes para que el ejemplo funcionará y apliqué una indentación automática para satisfacer a JSHint. Sin embargo, al autor le parecieron innecesarios estos cambios de estilo y prefirió aplicar él mismo los cambios de configuración que consideró necesarios, cerrando mi pull request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xQj4a5wQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xQj4a5wQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_7.png" alt="cropuploader-pr" width="880" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;De esta experiencia aprendí que no todo lo que envíes a un repositorio público necesariamente será aceptado, pero esta no es razón para desmotivarte. &lt;strong&gt;Hay muchos proyectos ahí afuera esperando gente entusiasta en busca de sumarse a un proyecto.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zNyIr3Tg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zNyIr3Tg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_8.png" alt="no-meme" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Nunca sabes quién terminará viendo tu trabajo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XsHLJXHV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XsHLJXHV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_9.png" alt="react-stl-viewer" width="880" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En otra ocasión necesitábamos hacer una prueba de concepto para un proyecto que teníamos que estimar relacionado con visualizar &lt;a href="https://es.3dsystems.com/quickparts/learning-center/what-is-stl-file"&gt;archivos de impresión 3D STL&lt;/a&gt;. Parte de los requerimientos del proyecto era que este fuera desarrollado con &lt;a href="https://reactjs.org"&gt;React&lt;/a&gt;, así que en nuestra búsqueda encontramos una biblioteca de nombre &lt;a href="https://github.com/chiedolabs/react-stl-viewer"&gt;react-stl-viewer&lt;/a&gt; la cual hacia la implementación para visualizar los archivos STL utilizando &lt;a href="https://threejs.org"&gt;Three.js&lt;/a&gt;. Después de echar un ojo al código terminamos haciendo un refactor de esta biblioteca para remover código duplicado que encontramos y hacerla más mantenible, ya que existía la posibilidad de si el cliente decidía seguir adelante con el proyecto era una dependencia que íbamos a terminar utilizando en producción, y no hacer estas mejoras se podía volver un dolor de cabeza potencial; y pues hecho dicho refactor decidimos enviar su respectivo &lt;a href="https://github.com/chiedolabs/react-stl-viewer/pull/11"&gt;pull request&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_UgFDvJp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_UgFDvJp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_10.png" alt="Course invitation" width="880" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo interesante de este aporte fue que años después me contactaron para invitarme a grabar un curso relacionado con esta dependencia, aunque lamentablemente por falta de tiempo no pude  seguir adelante con ello. Sin embargo de esta experiencia me quedó el aprendizaje de que &lt;strong&gt;uno nunca sabe quién verá tu trabajo y tal vez termine abriéndote puertas a cosas que no esperabas.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creando impacto con tus aportes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QXCiQcaw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QXCiQcaw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_11.png" alt="Solidus" width="880" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Más delante en mi carrera, estuve trabajando en un proyecto de eCommerce donde utilizábamos &lt;a href="https://solidus.io"&gt;Solidus&lt;/a&gt; como framework de desarrollo. Conforme iba haciendo mejoras al proyecto, aportes para el framework iban saliendo a la luz.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wUvumO9q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wUvumO9q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_12.png" alt="solidus pull requests" width="880" height="868"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;De los aportes que pude ir enviando al framework, hubo uno que me dejó un aprendizaje muy valioso, fue el cambio titulado &lt;a href="https://github.com/solidusio/solidus/pull/3221"&gt;[API] Complete Shipments Big json with small json fields #3221&lt;/a&gt;. Este cambio consistía en fusionar los campos que devolvía el endpoint de Shipments Big JSON con los campos del endpoint de Shipments Small JSON, ya que de primera vista daba la impresión de que small JSON era solo un subset de Big JSON pero en la práctica contenían diferente información, lo que hacía que no fuera posible intercambiarlos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SOpDgKKO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SOpDgKKO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_13.png" alt="Solidus API PR" width="880" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Después de hablarlo con uno de los miembros del Core Team en el Slack de la comunidad, apliqué el cambio, el cual literal fue agregar solo 1 línea de código a un archivo y remover 10 líneas que ya no eran necesarias. Este Pull request terminó etiquetado como un cambio importante para el release de la siguiente versión del framework. &lt;strong&gt;De esta experiencia aprendí que cambios grandes no implican tocar miles de archivos sino estar consciente del impacto que tu contribución puede causar al ecosistema.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enseñando desde el Open Source
&lt;/h2&gt;

&lt;p&gt;Para este momento de mi carrera ya había trabajado como maestro en una universidad local y había participado como mentor en un bootcamp. Sin embargo, el papel del Open Source era el de utilizar frameworks o librerías para hacer proyectos más allá de conocer cómo estaban hechas las dependencias que se utilizaban. En 2019 tuve la oportunidad de ser aceptado como mentor en el programa de &lt;a href="https://rubyme.org/"&gt;RubyMe&lt;/a&gt; organizado por Ruby Together, este programa consistía en apoyar a un mentee a iniciar a hacer aportes al Open Source utilizando Ruby. Mis aportes a proyectos de Open Source fueron mi carta de presentación para formar parte de este proyecto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zhJgTmP9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zhJgTmP9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1635301091/Potenciando%2520tu%2520carrera%2520profesional%2520a%2520traves%2520del%2520open%2520source/Untitled_14.png" alt="RubyMe" width="880" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Durante 3 meses estuve trabajando con Alicia, una chica de Atlanta que decidió hacer su &lt;a href="https://rubytogether.org/news/2019-06-30-career-switching-into-code"&gt;cambio de carrera de ciencias políticas a desarrollo de software&lt;/a&gt;. Estuvimos haciendo aportes a diferentes proyectos, enviando desde mejoras a la documentación así como funcionalidades nuevas. Considero que lo más interesante durante este programa de mentorías fue hacer la transferencia de conocimiento de cómo abordar un proyecto cuando no estás familiarizado con él.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concluyendo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Trata de que tus aportes sean relacionados con algo que estés trabajando, de esta manera no lo verás como algo forzado o un trabajo extra.&lt;/li&gt;
&lt;li&gt;No hay aportes pequeños, siempre que sea algo para mejorar el proyecto es un candidato a ser integrado. Roma no se hizo en un día, posiblemente quieres que tus aportes sean al repositorio más importante de la comunidad a la que perteneces, sin embargo, hasta hacer una corrección en la documentación es algo que ayuda a crear un mejor ecosistema y sentará las bases de otros aportes que en un futuro estarás haciendo.&lt;/li&gt;
&lt;li&gt;Considera el Open Source como parte de tu portafolio profesional, un pull request o merge request público dice más que un curriculum lleno de estrellitas sin pruebas.&lt;/li&gt;
&lt;li&gt;Usa el Open Source como una herramienta para enseñar a otros o aprender algo nuevo. A menudo escucho comentarios como “no tengo experiencia” o "no sé por dónde empezar". El Open Source es un excelente lugar para empezar a crear experiencia comprobable, incluso antes de empezar a buscar trabajo activamente. Las guías de contribución, los issues y los pull request existentes son un excelente lugar para que empieces a relacionarte con el proyecto y tengas un mejor entendimiento de a dónde va este encaminado.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y bueno, quiero agradecerte por leer hasta aquí, espero este pequeño ensayo te sea de utilidad en tu camino como desarrollador, si quieres charlar puedes encontrarme en redes sociales en &lt;a href="https://twitter.com/Juancrg90"&gt;@JuanCrg90&lt;/a&gt; y en la comunidad de &lt;a href="https://twitter.com/CalzadaCode"&gt;Calzada Code&lt;/a&gt;, mis DMs están abiertos.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>opensource</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Quiero ser un ingeniero de software remoto</title>
      <dc:creator>Juan C. Ruiz</dc:creator>
      <pubDate>Sat, 09 Oct 2021 05:19:11 +0000</pubDate>
      <link>https://forem.com/juancrg90/quiero-ser-un-ingeniero-de-software-remoto-3p17</link>
      <guid>https://forem.com/juancrg90/quiero-ser-un-ingeniero-de-software-remoto-3p17</guid>
      <description>&lt;p&gt;Este Artículo esta basado en un hilo de twitter que publiqué hace unas semanas: &lt;a href="https://twitter.com/JuanCrg90/status/1436734271541202945"&gt;Quiero ser un Ingeniero de software remoto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hace unos días recibí un DM de un joven de primer año de universidad en computer science con el texto: "Señor, quiero ser ingeniero de software remoto" seguido de un par de mensajes y la pregunta en concreto: "Entonces señor, ¿cómo puedo conseguir un trabajo remoto ...?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zCDAv2-P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1633755284/Quiero%2520ser%2520un%2520ingeniero%2520de%2520software%2520remoto/image1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zCDAv2-P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1633755284/Quiero%2520ser%2520un%2520ingeniero%2520de%2520software%2520remoto/image1.png" alt="Twitter Conversation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nota: los textos citados en el párrafo anterior, pasaron por google translate, si me ves en algún evento digital o físico post pandemia no me digas Señor soy chavoruco joven forever así que un "wey 🇲🇽" o "Juan" basta.&lt;/p&gt;

&lt;p&gt;Para no hacerte el cuento mas largo te comparto algunos de los consejos que se me vinieron a la mente al leer su comentario y que fue parte de lo que le terminé compartiendo. &lt;strong&gt;Nota: Los siguientes párrafos vienen desde mi experiencia personal y puede que a otros les haya funcionado diferente, no hay recetas mágicas en la vida:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Comienza a practicar y crear un portafolio, no te esperes hasta ver los temas en tu universidad, hoy día hay recursos como&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com"&gt;Udemy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.coursera.org"&gt;Coursera&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.freecodecamp.org"&gt;freeCodeBootcamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ockham.education/course/ruby-desde-cero"&gt;Ockham Education&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codigofacilito.com"&gt;Código facilito&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En los que puedes ir aprendiendo de los temas que a ti te interesan.&lt;/p&gt;

&lt;p&gt;Trabaja en tu comunicación en Inglés, si estas leyendo este artículo, es muy probable tu Idioma nativo sea el español así que enfócate en pulir tus habilidades para comunicarte en Inglés. La gramática, ortografía y una buena pronunciación son tus aliados. De lo anterior puedo decirte, no te detengas con el argumento de "mi ingles no es el mejor" yo a la fecha cometo muchos errores y sigo estudiando, pero el simple hecho de poder trasmitir una idea en más de un idioma te abre muchísimas puertas. Hoy día hay Herramientas como &lt;a href="https://invite.duolingo.com/BDHTZTB5CWWKSV3USHSUPMDE2U"&gt;Duolingo&lt;/a&gt;, &lt;a href="https://share.elsanow.io/H3FvpbE84jb"&gt;Elsa Speak&lt;/a&gt; donde puedes practicar en tus ratos libres y a tu ritmo. Ya si buscas atención personalizada con un maestro, &lt;a href="https://www.italki.com/i/ref/EfbfCc?hl=en"&gt;italki&lt;/a&gt; es una buena opción.&lt;/p&gt;

&lt;p&gt;Previamente hablaba de un portafolio, pero es bueno comentar que las bases son muy útiles, buenas bases hacen que tu capacidad de adaptarte a nuevas herramientas y tener un mejor entendimiento de los problemas sea más solida. Por lo que conocer conceptos como Notación Big O o algoritmos "de libro" te ayudará a la hora de aplicar a algunas empresas. Plataformas como: &lt;a href="https://codesignal.com/"&gt;CodeSignal&lt;/a&gt; o &lt;a href="https://www.hackerrank.com/"&gt;HackerRank&lt;/a&gt; son Lugares buenos para practicar. Hay quien también recomienda el libro de "&lt;a href="https://amzn.to/3oCdGo1"&gt;Cracking the Coding Interview&lt;/a&gt;" para eso, en lo personal nunca lo he leído así que no puedo decir que es 100% necesario.&lt;/p&gt;

&lt;p&gt;Volviendo al tema de la comunicación, aprende a trabajar de forma asíncrona. Cuando trabajas remoto para una empresa de otro país es muy común trabajar con personas que están en husos horarios distintos por lo que el concepto de "Para YA" no existe como tal. de lo anterior, eso no significa que no haya trabajo urgente, simplemente es que escribir un mensaje y esperar respuesta inmediata no es el estándar.&lt;/p&gt;

&lt;p&gt;En cuanto a libros que a mi me motivaron al inicio de mi carrera en software hay 2 que quisiera recomendarte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://amzn.to/3uE4EYo"&gt;Passionate Programmer&lt;/a&gt;  en el cual, a través de una serie de pasajes cortos, Chad Fowler te comparte su perspectiva de ser un desarrollador de software después de haber tenido una carrera en música.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://amzn.to/3mh45QA"&gt;Remote: Office Not Required&lt;/a&gt; el cual fue escrito por los creadores de @basecamp y hoy día es un buen referente de lo "ideal" en un ambiente remoto. &lt;strong&gt;OJO, NO es una biblia, no es un manual que debes seguir al pie de la letra, son consejos de lo que a ellos les ha funcionado.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Algo muy importante que te ayudará en tu camino es acercarte a comunidades de entusiastas y expertos que existen en tu zona o bien en internet, este tema ha cambiado mucho desde que empezó el distanciamiento social. Comunidades como &lt;a href="http://link.juancrg90.me/twt-calzadacode"&gt;Calzada Code&lt;/a&gt;, &lt;a href="https://twitter.com/futurelabmx"&gt;Future Lab&lt;/a&gt;, &lt;a href="https://twitter.com/hackademymx"&gt;Hackademy&lt;/a&gt;, &lt;a href="https://twitter.com/visual_partner"&gt;Visual Partner-Ship&lt;/a&gt;, &lt;a href="https://twitter.com/en_rails"&gt;enrails&lt;/a&gt; son solo algunas de las comunidades que hoy día están en linea y que realmente disfrutan compartir y ayudar a otros en su carrera.&lt;/p&gt;

&lt;p&gt;Recientemente el equipo de Hackademy creo este recurso para que podamos conectar con más comunidades &lt;a href="https://comunidades.lat/"&gt;https://comunidades.lat&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DuWy3b8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1633755284/Quiero%2520ser%2520un%2520ingeniero%2520de%2520software%2520remoto/Atlas_2021-10-04_at_11.33.17_a.m..jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DuWy3b8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/juancrg90/image/upload/v1633755284/Quiero%2520ser%2520un%2520ingeniero%2520de%2520software%2520remoto/Atlas_2021-10-04_at_11.33.17_a.m..jpg" alt="Atlas de comunidades"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora sí, tal vez en este punto es de ¿y bueno cómo consigo el trabajo remoto? Una vez que hayas pulido algunas de tus habilidades (OJO de nuevo, no estoy diciendo TODA la lista de arriba), comienza tocando puertas buscando alguna oportunidad. Plataformas como &lt;a href="https://remotive.io"&gt;remotiveio&lt;/a&gt;, &lt;a href="https://www.fiverr.com"&gt;fiverr&lt;/a&gt;, &lt;a href="https://www.upwork.com"&gt;Upwork&lt;/a&gt; son buenos lugares para inicia buscar y darte una idea de lo que hay en el mercado actual. Ya que hayas ganado más experiencia es posible buscar en empresas grandes como &lt;a href="https://about.gitlab.com/jobs/"&gt;GitLab&lt;/a&gt;, &lt;a href="https://www.shopify.com/careers"&gt;Shopify&lt;/a&gt;, &lt;a href="https://x-team.com/remote-programming-jobs/"&gt;X-Team&lt;/a&gt;,  entre otras.&lt;/p&gt;

&lt;p&gt;Espero te sea de utilidad este pequeño blogpost. Repito, todo lo que puse ha sido desde mi experiencia personal, puede que haya cosas sean distintas en tu forma de ver el mundo y es totalmente válido. Mis redes sociales están abiertas si tienes dudas o simplemente quieres charlar.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>spanish</category>
      <category>opensource</category>
    </item>
    <item>
      <title>¿Por qué aprender Ruby en 2021?</title>
      <dc:creator>Juan C. Ruiz</dc:creator>
      <pubDate>Sun, 27 Jun 2021 16:37:05 +0000</pubDate>
      <link>https://forem.com/juancrg90/por-que-aprender-ruby-en-2021-51n7</link>
      <guid>https://forem.com/juancrg90/por-que-aprender-ruby-en-2021-51n7</guid>
      <description>&lt;p&gt;&lt;a href="https://www.ruby-lang.org/"&gt;Ruby&lt;/a&gt; es un lenguaje de programación open source de propósito general el cual tiene más de dos décadas de vida. Su creador, &lt;a href="https://matz.rubyist.net/"&gt;Yukihiro Matsumoto&lt;/a&gt;, lo define como un lenguaje optimizado para la felicidad de los programadores. Cuenta con una sintaxis amigable la cual permite escribir piezas de código fáciles de leer y escribir. &lt;/p&gt;

&lt;p&gt;Sin embargo, tal vez te preguntes si vale la pena aprender este lenguaje de programación en pleno 2021. He llegado a escuchar comentarios como: "Ruby ya es viejo ¿no?" o "¿Aún hay quién lo use?". Para responder a estos dos comentarios basta con revisar un par datos los cuales pueden ayudarnos a crear nuestro propio criterio.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/382"&gt;Ruby fue lanzado oficialmente al público en 1995&lt;/a&gt;, mientras que la primer versión pública de &lt;a href="https://python-history.blogspot.com/2009/01/brief-timeline-of-python.html"&gt;Python fue liberada en 1991 siendo esta la 0.9.0&lt;/a&gt;. A su vez, JavaScript que hoy día es uno de los lenguajes más populares, fue &lt;a href="https://web.archive.org/web/20070916144913/http://wp.netscape.com/newsref/pr/newsrelease67.html"&gt;anunciado por Netscape y Sun Systems en diciembre de 1995&lt;/a&gt;. Actualmente Ruby se encuentra en su versión 3.0, siendo totalmente compatible con la versión 2.7 y cuenta con &lt;a href="https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/"&gt;características&lt;/a&gt; como concurrencia a través de Ractor y Fiber Scheduler así como Análisis estático utilizando RBS y TypeProf.&lt;/p&gt;

&lt;p&gt;Ruby ha destacado en el campo de las aplicaciones de internet siendo &lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; su framework más popular. Este es utilizado por empresas de talla internacional como &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt;, &lt;a href="https://gitlab.com/"&gt;GitLab&lt;/a&gt;, &lt;a href="https://www.shopify.com/"&gt;Shopify&lt;/a&gt;, &lt;a href="https://cookpad.com/"&gt;Cookpad&lt;/a&gt;, &lt;a href="https://www.domestika.org/"&gt;Domestika&lt;/a&gt; entre otras, así como por startups las cuales aprovechan las bondades del lenguaje y del framework para poder crear iteraciones de sus productos rápidamente.&lt;/p&gt;

&lt;p&gt;Además de Ruby on Rails, existen bastantes proyectos creados en Ruby que son ampliamente utilizados por la comunidad de desarrolladores de software, aún cuando no desarrollen en este lenguaje. Por listar algunos tenemos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt;, el manejador de paquetes de MacOS&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cocoapods.org/"&gt;Cocoapods&lt;/a&gt;, el manejador de dependencias para Swift y Objective-C.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fastlane.tools/"&gt;Fastlane&lt;/a&gt;, la herramienta para automatizar tareas de liberación de aplicaciones Android y iOS&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.vagrantup.com/"&gt;Vagrant&lt;/a&gt;, la herramienta para creación y distribución de entornos de desarrollo.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.metasploit.com/"&gt;Metasploit&lt;/a&gt;, el framework de pruebas de penetración más utilizado a nivel mundial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Al ser un lenguaje maduro, Ruby cuenta con una documentación robusta de sus diferentes versiones siendo &lt;a href="https://docs.ruby-lang.org/"&gt;https://docs.ruby-lang.org/&lt;/a&gt; la página oficial de esta y &lt;a href="https://ruby-doc.org/"&gt;https://ruby-doc.org/&lt;/a&gt; la referencia generada desde &lt;a href="https://ruby.github.io/rdoc/"&gt;RDoc&lt;/a&gt;. Además de la documentación, Ruby cuenta con una comunidad activa en diferentes plataformas como &lt;a href="https://stackoverflow.com/questions/tagged/ruby"&gt;Stack Overflow&lt;/a&gt;, &lt;a href="https://www.ruby-lang.org/en/community/mailing-lists/"&gt;La lista de correo oficial&lt;/a&gt;, Boletines semanales como &lt;a href="https://rubyweekly.com/"&gt;Ruby Weekly&lt;/a&gt;, &lt;a href="https://twitter.com/en_rails"&gt;enrails&lt;/a&gt; entre otros.&lt;/p&gt;

&lt;p&gt;En Resumen, Ruby es un lenguaje que a sus más de 25 años sigue dando de que hablar, cuenta con una amplia variedad de proyectos donde la comunidad contribuye activamente y cada día nuevas aplicaciones son lanzadas a internet utilizando Ruby on Rails. &lt;/p&gt;

&lt;p&gt;Si estás interesado en aprender los fundamentos de este lenguaje, mi curso de &lt;a href="https://ockham.education/course/ruby-desde-cero"&gt;Ruby desde Cero&lt;/a&gt; es para ti. En el curso aprenderás desde como instalar el lenguaje en tu sistema hasta cómo aplicar la orientación a objetos para crear tus primeros programas en este maravilloso lenguaje.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ruby-lang.org/"&gt;https://www.ruby-lang.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://matz.rubyist.net/"&gt;https://matz.rubyist.net/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/382"&gt;http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/382&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://python-history.blogspot.com/2009/01/brief-timeline-of-python.html"&gt;https://python-history.blogspot.com/2009/01/brief-timeline-of-python.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.archive.org/web/20070916144913/http://wp.netscape.com/newsref/pr/newsrelease67.html"&gt;https://web.archive.org/web/20070916144913/http://wp.netscape.com/newsref/pr/newsrelease67.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/"&gt;https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rubyonrails.org/"&gt;https://rubyonrails.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/"&gt;https://github.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.shopify.com/"&gt;https://www.shopify.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cookpad.com/mx"&gt;https://cookpad.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.domestika.org/"&gt;https://www.domestika.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://brew.sh/"&gt;https://brew.sh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cocoapods.org/"&gt;https://cocoapods.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastlane.tools/"&gt;https://fastlane.tools/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.vagrantup.com/"&gt;https://www.vagrantup.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.metasploit.com/"&gt;https://www.metasploit.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.ruby-lang.org/"&gt;https://docs.ruby-lang.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ruby-doc.org/"&gt;https://ruby-doc.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ruby.github.io/rdoc/"&gt;https://ruby.github.io/rdoc/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/tagged/ruby"&gt;https://stackoverflow.com/questions/tagged/ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rubyweekly.com/"&gt;https://rubyweekly.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/en_rails"&gt;https://twitter.com/en_rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ockham.education/course/ruby-desde-cero"&gt;https://ockham.education/course/ruby-desde-cero&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>beginners</category>
      <category>career</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Optimizando costos en GitHub Actions</title>
      <dc:creator>Juan C. Ruiz</dc:creator>
      <pubDate>Sun, 31 May 2020 03:19:53 +0000</pubDate>
      <link>https://forem.com/juancrg90/optimizando-costos-en-github-actions-24dm</link>
      <guid>https://forem.com/juancrg90/optimizando-costos-en-github-actions-24dm</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/juancrg90/optimizing-costs-in-github-actions-cf9"&gt;English version&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recientemente en &lt;a href="https://www.easybroker.com/" rel="noopener noreferrer"&gt;EasyBroker&lt;/a&gt; migramos nuestro sistema de Integración continua a GitHub Actions.&lt;/p&gt;

&lt;p&gt;Inicialmente configuramos un Workflow con  12 contenedores considerando que era una buena idea para identificar rápidamente donde fallaron las pruebas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_e9sjqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_e9sjqq.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En promedio cada contenedor tardaba 6 minutos en completar su tarea, mientras que 2 contenedores en específico (modelos y un grupo de controladores) tardaban en promedio 12 minutos.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_1_rbxmvb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_1_rbxmvb.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%201.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_2_oornut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_2_oornut.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%202.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Después de una semana trabajando con esta nueva implementación recibimos un mensaje de GitHub alertándonos que estábamos por llegar al límite de uso de la capa gratuita que GitHub nos provee 💸💸💸, por lo que decidimos investigar.&lt;/p&gt;

&lt;p&gt;En primer lugar notamos que de los 6 minutos que tardaban los 10 contenedores más rápidos, en promedio 4 minutos eran tareas de configuración.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_3_ryutfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_3_ryutfn.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%203.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Posteriormente notamos que a pesar de tener el cache "configurado", nuestro contenedor estaba descargando las gemas una y otra vez.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_4_mkohr1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_4_mkohr1.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%204.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y finalmente solo se invertían entre 20 y 50 segundos en ejecutar las pruebas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_5_dhlisu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_5_dhlisu.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%205.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_6_u1saes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_6_u1saes.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%206.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Ahorrándonos unos minutos
&lt;/h2&gt;

&lt;p&gt;Lo primero que decidimos hacer fue pasar de 12 contenedores a 2 contenedores. Considerando que las pruebas de los controladores de agentes son las que toman más tiempo, decidimos que lo modelos podían convivir con las pruebas de los 10 contenedores que tardaban 6 minutos en promedio, terminando con una configuración como la siguiente.&lt;/p&gt;

&lt;p&gt;Antes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;test_folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;helpers&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;mailers&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;presenters&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/*.rb&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/admin&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/agent&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/webhooks&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/api&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;test_folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;models helpers test/jobs test/lib test/mailers test/presenters test/services test/controllers/*.rb test/controllers/admin test/controllers/webhooks testcontrollers/api&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/agent&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;   

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo siguiente fue actualizar el apartado de cache para hacerlo funcionar, nuestra configuración utilizaba la versión 1 de actions/cache e incluía la opción de restore-keys la cual según la documentación es &lt;a href="https://github.com/actions/cache#inputs" rel="noopener noreferrer"&gt;opcional&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gem cache&lt;/span&gt;
        &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v1&lt;/span&gt;
        &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-gems-&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por lo que simplemente actualicé a la versión 2 de la acción y removí la opción de restore-keys al considerar que no es algo que requerimos aún.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gem cache&lt;/span&gt;
        &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v2&lt;/span&gt;
        &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-gem-use-ruby-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Además de eso, revisando el paso donde se instalan las dependencias, encontré que lo teníamos fusionado con el paso para crear la base de datos y nos mostraba un deprecation warning al momento de ejecutar bundle install  con la bandera para indicar la ruta de instalación de las gemas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bundle Install and Create DB&lt;/span&gt;
        &lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
          &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
          &lt;span class="na"&gt;DB_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.services.mysql.ports[3306] }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;sudo /etc/init.d/mysql start&lt;/span&gt;
          &lt;span class="s"&gt;cp config/database.ci.yml config/database.yml&lt;/span&gt;
          &lt;span class="s"&gt;gem install bundler --version 2.0.2 --no-ri --no-rdoc&lt;/span&gt;
          &lt;span class="s"&gt;bundle install --jobs 4 --retry 3 --path vendor/bundle&lt;/span&gt;
          &lt;span class="s"&gt;bin/rails db:setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;DEPRECATED] The &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; flag is deprecated because it relies on being remembered across bundler invocations, which bundler will no longer &lt;span class="k"&gt;do in &lt;/span&gt;future versions. Instead please use &lt;span class="sb"&gt;`&lt;/span&gt;bundle config &lt;span class="nb"&gt;set &lt;/span&gt;path &lt;span class="s1"&gt;'vendor/bundle'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;, and stop using this flag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por lo que opté por separarlo en 2 pasos independientes y actualizar la forma en la que bundler detecta la ubicación de las gemas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bundle install&lt;/span&gt;
        &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;gem install bundler --version 2.0.2 --no-ri --no-rdoc&lt;/span&gt;
          &lt;span class="s"&gt;bundle config path vendor/bundle&lt;/span&gt;
          &lt;span class="s"&gt;bundle install --jobs 4 --retry 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create DB&lt;/span&gt;
        &lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
          &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
          &lt;span class="na"&gt;DB_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.services.mysql.ports[3306] }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;sudo /etc/init.d/mysql start&lt;/span&gt;
          &lt;span class="s"&gt;cp config/database.ci.yml config/database.yml&lt;/span&gt;
          &lt;span class="s"&gt;bin/rails db:setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con estos pequeños cambios logramos pasar de  4 minutos en la instalación de las gemas a tan solo 4 segundos. &lt;/p&gt;

&lt;p&gt;Antes&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_7_tawvjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_7_tawvjy.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%207.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_8_dyhwlj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_8_dyhwlj.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%208.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como paso final para esta primer etapa de optimización, revisé los pasos dónde realizábamos la instalación de algunas dependencias por apt-get y encontré que algunos eran innecesarios. Por ejemplo, cuando verificábamos la conexión a MySQL, estábamos tratando de instalar el cliente que ya está disponible y esto tomaba 20 segundos en promedio (entre verificar la instalación y verificar que la conexión fue exitosa) con solo remover la llamada de apt-get que no era necesario logramos bajar a un promedio de 6 segundos.&lt;/p&gt;

&lt;p&gt;Antes&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892645%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_9_odbx1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892645%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_9_odbx1j.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%209.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_10_s1uik1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_10_s1uik1.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%2010.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Notas finales
&lt;/h2&gt;

&lt;p&gt;Esta fue la primer etapa de nuestra actualización para tener un sistema de integración continua saludable y eficiente, logramos pasar de un promedio de 1 hora 30 minutos por ejecución.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_11_tt7ewa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_11_tt7ewa.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%2011.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A tan solo 20 minutos en promedio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_12_kifcvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_12_kifcvl.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%2012.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como experiencia nos llevamos que, en servicios donde la factura es por tiempo de uso, el ahorrar segundos es crucial y es importante poner atención a los detalles por pequeños que sean. Como en el caso de la instalación de dependencias que ya existen en el contenedor. Puede que 13 segundos ahorrados suene a nada, pero si lo multiplicamos por el número de builds que tiene tu empresa al día, puede que logres ahorrar un par de horas en tu factura.&lt;/p&gt;

&lt;p&gt;Lo siguiente que haremos será tratar de mover la instalación de dependencias que requieren ser instaladas manualmente a un contenedor Docker (aún necesito investigar si es posible esto) y utilizar alguna herramienta como &lt;a href="https://evilmartians.com/chronicles/testprof-a-good-doctor-for-slow-ruby-tests" rel="noopener noreferrer"&gt;TestProf&lt;/a&gt; para identificar los tests más lentos y ver la manera de optimizarlos.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>github</category>
      <category>testing</category>
    </item>
    <item>
      <title>Optimizing costs in GitHub Actions</title>
      <dc:creator>Juan C. Ruiz</dc:creator>
      <pubDate>Sun, 31 May 2020 03:00:11 +0000</pubDate>
      <link>https://forem.com/juancrg90/optimizing-costs-in-github-actions-cf9</link>
      <guid>https://forem.com/juancrg90/optimizing-costs-in-github-actions-cf9</guid>
      <description>&lt;p&gt;Recently in &lt;a href="https://www.easybroker.com/" rel="noopener noreferrer"&gt;EasyBroker&lt;/a&gt; we decided to migrate our continuous integration system to GitHub Actions. &lt;/p&gt;

&lt;p&gt;Our first setup was a workflow with 12 containers considering a good idea to quickly identify errors in our tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_e9sjqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_e9sjqq.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The average time per container was about 6 minutes, while only 2 containers in specific (models and a group of controllers) took about 12 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_1_rbxmvb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_1_rbxmvb.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%201.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_2_oornut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_2_oornut.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%202.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a week working with this new implementation, we received a message from GitHub warning us about we were near to consume all the minutes of the free tier that they provides  💸💸💸, so we decided to investigate.&lt;/p&gt;

&lt;p&gt;First of all, we noticed the 10 containers that took 6 minutes to complete the task, 4 minutes were for setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_3_ryutfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_3_ryutfn.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%203.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Later we found despite we had the cache "configured", our container was downloading the gems over and over again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_4_mkohr1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_4_mkohr1.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%204.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, the tests were taken only between 20 and 50 seconds of the process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_5_dhlisu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_5_dhlisu.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%205.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_6_u1saes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892643%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_6_u1saes.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%206.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving some minutes
&lt;/h2&gt;

&lt;p&gt;The first change that we did was going from 12 containers to 2. Considering the agent controller tests takes most of the time, we decided to merge the models container with the other 10 containers that were taken between 20 and 50 seconds to complete the tests, ending with a configuration like the following.&lt;/p&gt;

&lt;p&gt;Before&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;test_folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;helpers&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;mailers&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;presenters&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/*.rb&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/admin&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/agent&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/webhooks&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/api&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;test_folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;models helpers test/jobs test/lib test/mailers test/presenters test/services test/controllers/*.rb test/controllers/admin test/controllers/webhooks testcontrollers/api&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;controllers/agent&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;   

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next change was updating the cache setup to make it work, our configuration was using the version 1 of &lt;code&gt;actions/cache&lt;/code&gt; and we were including the &lt;code&gt;restore-keys&lt;/code&gt; option, the documentation mentions that this input is &lt;a href="https://github.com/actions/cache#inputs" rel="noopener noreferrer"&gt;optional&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gem cache&lt;/span&gt;
        &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v1&lt;/span&gt;
        &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-gems-&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I updated to version 2 of the action and I removed the &lt;code&gt;restore-keys&lt;/code&gt; input considering is not necessary at this moment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gem cache&lt;/span&gt;
        &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v2&lt;/span&gt;
        &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-gem-use-ruby-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to that, reviewing the step where the dependencies are installed, I found that we had it merged with the step to create the database and it showed us a deprecation warning when executing bundle install with the flag to indicate the installation path of the gems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bundle Install and Create DB&lt;/span&gt;
        &lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
          &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
          &lt;span class="na"&gt;DB_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.services.mysql.ports[3306] }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;sudo /etc/init.d/mysql start&lt;/span&gt;
          &lt;span class="s"&gt;cp config/database.ci.yml config/database.yml&lt;/span&gt;
          &lt;span class="s"&gt;gem install bundler --version 2.0.2 --no-ri --no-rdoc&lt;/span&gt;
          &lt;span class="s"&gt;bundle install --jobs 4 --retry 3 --path vendor/bundle&lt;/span&gt;
          &lt;span class="s"&gt;bin/rails db:setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;DEPRECATED] The &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; flag is deprecated because it relies on being remembered across bundler invocations, which bundler will no longer &lt;span class="k"&gt;do in &lt;/span&gt;future versions. Instead please use &lt;span class="sb"&gt;`&lt;/span&gt;bundle config &lt;span class="nb"&gt;set &lt;/span&gt;path &lt;span class="s1"&gt;'vendor/bundle'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;, and stop using this flag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I opted to separate it into 2 separate steps and update the way the bundler detects the location of the gems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bundle install&lt;/span&gt;
        &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;gem install bundler --version 2.0.2 --no-ri --no-rdoc&lt;/span&gt;
          &lt;span class="s"&gt;bundle config path vendor/bundle&lt;/span&gt;
          &lt;span class="s"&gt;bundle install --jobs 4 --retry 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create DB&lt;/span&gt;
        &lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
          &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
          &lt;span class="na"&gt;DB_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.services.mysql.ports[3306] }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;sudo /etc/init.d/mysql start&lt;/span&gt;
          &lt;span class="s"&gt;cp config/database.ci.yml config/database.yml&lt;/span&gt;
          &lt;span class="s"&gt;bin/rails db:setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these little tweaks we were able to pass from 4 minutes installing the gems in just 4 seconds. &lt;/p&gt;

&lt;p&gt;Before&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_7_tawvjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_7_tawvjy.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%207.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_8_dyhwlj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_8_dyhwlj.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%208.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a final step for this first stage of optimization, I took a look at the steps where we did the installation of some dependencies using &lt;code&gt;apt-get&lt;/code&gt; and I found that some calls were unnecessary. For example, when we verified the MySQL connection, we were trying to install the client that is already installed in the container and this took about 20 seconds (between verify the installation and perform the connection test). Removing this unnecessary &lt;code&gt;apt-get&lt;/code&gt; call we were able to down the time to 6 seconds.&lt;/p&gt;

&lt;p&gt;Before&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892645%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_9_odbx1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892645%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_9_odbx1j.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%209.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_10_s1uik1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_10_s1uik1.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%2010.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;This was the first stage of our update to have a healthy and efficient CI system, after apply these changes we were able to reduce from about 1 hour and 30 minutes per execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_11_tt7ewa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_11_tt7ewa.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%2011.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To only about 20 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_12_kifcvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1590892644%2FOptimizing%2520costs%2520in%2520GitHub%2520Actions%25207b464c031286482f9b1db7b6d9b72442%2FUntitled_12_kifcvl.png" alt="Optimizing%20costs%20in%20GitHub%20Actions%207b464c031286482f9b1db7b6d9b72442/Untitled%2012.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The experience of this is, in services where the billing depends on the execution time, saving seconds is crucial, and it is important to pay attention to the little details. Like the &lt;code&gt;apt-get&lt;/code&gt; dependency installation scenario where the dependencies are already installed in the container. Maybe 13 seconds sounds as nothing relevant, but if you consider the number of builds that you have in your company every day probably you can save 1 or two hours per day and this will be reflected in your billing.&lt;/p&gt;

&lt;p&gt;The next step is to try to move the dependencies that require to be installed manually into a Docker container (I still need to investigate if this is possible) and use a tool like &lt;a href="https://evilmartians.com/chronicles/testprof-a-good-doctor-for-slow-ruby-tests" rel="noopener noreferrer"&gt;TestProf&lt;/a&gt; to identify the slowest tests and try to optimize them.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>github</category>
      <category>testing</category>
    </item>
    <item>
      <title>Maintaining an open source gem in the Hacktoberfest</title>
      <dc:creator>Juan C. Ruiz</dc:creator>
      <pubDate>Fri, 25 Oct 2019 02:24:07 +0000</pubDate>
      <link>https://forem.com/juancrg90/maintaining-an-open-source-gem-in-the-hacktoberfest-5hh</link>
      <guid>https://forem.com/juancrg90/maintaining-an-open-source-gem-in-the-hacktoberfest-5hh</guid>
      <description>&lt;p&gt;Some months ago I created a little gem to quickly access to different GitHub sections from the command line. The name of my gem is &lt;a href="https://github.com/JuanCrg90/vpr" rel="noopener noreferrer"&gt;vpr&lt;/a&gt; (view pull request). &lt;/p&gt;

&lt;p&gt;The idea of this gem is simple, you call the &lt;code&gt;vpr&lt;/code&gt; command + some instruction + a commit SHA and you can access to a Github section of your interest. For example, &lt;code&gt;vpr visit 123abc&lt;/code&gt; sends you to the commit page of the selected commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1562970485%2Fvpr%2Fvpr_visit.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjuancrg90%2Fimage%2Fupload%2Fv1562970485%2Fvpr%2Fvpr_visit.gif" alt="vpr visit command"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this Hacktoberfests I decided to open some issues to add the support for bitbucket pages. It was incredible the speed that the users started to contribute and recommend things for the project, like &lt;a class="mentioned-user" href="https://dev.to/andrewmcodes"&gt;@andrewmcodes&lt;/a&gt; who added a GitHub action to run &lt;a href="https://github.com/testdouble/standardrb" rel="noopener noreferrer"&gt;standardRB&lt;/a&gt; in the pull requests.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/JuanCrg90/vpr/issues/12" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Add linter/formatter GitHub action
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#12&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/andrewmcodes" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars1.githubusercontent.com%2Fu%2F18423853%3Fv%3D4" alt="andrewmcodes avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/andrewmcodes" rel="noopener noreferrer"&gt;andrewmcodes&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/JuanCrg90/vpr/issues/12" rel="noopener noreferrer"&gt;&lt;time&gt;Oct 04, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;I noticed you aren’t using a linter/formatter like rubocop or standardRB. Would you be interested in adding one?&lt;/p&gt;
&lt;p&gt;If so I can hook up a GitHub action for you. Check out &lt;a href="https://github.com/hopsoft/stimulus_reflex" rel="noopener noreferrer"&gt;this repo&lt;/a&gt; for an example of actions I have created.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/JuanCrg90/vpr/issues/12" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Even if this is a simple side project, I'm happy to have had the opportunity to be a maintainer this year. I hope the next year have the opportunity to host a contribution fest in my city.&lt;/p&gt;

&lt;p&gt;All these contributions will be released to &lt;a href="https://rubygems.org" rel="noopener noreferrer"&gt;RubyGems&lt;/a&gt; in November, thanks to all the participants for your support. If you are interested in know more about vpr, please visit the Github repository.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/JuanCrg90" rel="noopener noreferrer"&gt;
        JuanCrg90
      &lt;/a&gt; / &lt;a href="https://github.com/JuanCrg90/vpr" rel="noopener noreferrer"&gt;
        vpr
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      VPR is a CLI that helps you to quickly manage your project in GitHub/GitLab/Bitbucket
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;vpr&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/JuanCrg90/vpr/workflows/StandardRB/badge.svg"&gt;&lt;img src="https://github.com/JuanCrg90/vpr/workflows/StandardRB/badge.svg" alt="StandardRB"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/JuanCrg90/vpr/workflows/CI/badge.svg"&gt;&lt;img src="https://github.com/JuanCrg90/vpr/workflows/CI/badge.svg" alt="CI"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;VPR is a CLI that helps you to quickly manage your project in GitHub/GitLab/Bitbucket
You can visit with ease different project sections, like the issues page, a specific commit
create pull requests (merge requests for GitLab),
and more, also is configurable for other platforms PRs are open.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/18fd63700f8d8bd2575d3298f7485c8713e3de2087cbb493d40bb59477d76cd5/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6a75616e63726739302f696d6167652f75706c6f61642f76313536323937303234322f7670722f7670725f70756c6c2e676966"&gt;&lt;img src="https://camo.githubusercontent.com/18fd63700f8d8bd2575d3298f7485c8713e3de2087cbb493d40bb59477d76cd5/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f6a75616e63726739302f696d6167652f75706c6f61642f76313536323937303234322f7670722f7670725f70756c6c2e676966" alt="vpr pull"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;  $ gem install vpr&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;All the commands use the URL set as &lt;code&gt;origin&lt;/code&gt; in your git remote to perform the web request,
you can use the flag &lt;code&gt;--remote=REMOTE&lt;/code&gt; to specify which service you want to visit if you have your project stored in more than one platform.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;code&gt;vpr branch&lt;/code&gt;&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Open in your browser your current branch.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;code&gt;vpr branches&lt;/code&gt;&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Open the page that shows the uploaded branch list.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;code&gt;vpr help&lt;/code&gt;&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Show the likst of available &lt;code&gt;vpr&lt;/code&gt; commands, you can pass an specific command to get more details.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;code&gt;vpr home&lt;/code&gt;&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Open in your browser the project page.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;code&gt;vpr issues&lt;/code&gt;&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Open the in…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/JuanCrg90/vpr" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>ruby</category>
      <category>community</category>
      <category>hacktoberfest</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
