<?xml version="1.0" encoding="utf-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0"><channel><title>Giovanni Collazo</title><atom:link href="https://gcollazo.com/feed.xml" rel="self" type="application/rss+xml"/><link>https://gcollazo.com</link><description>RSS Feed for Giovanni Collazo</description><lastBuildDate>Fri, 09 Jan 2026 20:33:32 +0000</lastBuildDate><generator>gcollazo.com</generator><language>en-us</language><itunes:explicit>no</itunes:explicit><copyright>Some rights reserved</copyright><itunes:image href="http://elweb.co/apple-touch-icon.png"/><itunes:keywords>blimp,programming,python,django,startups</itunes:keywords><itunes:summary>Stuff about programming, startups and anything else.</itunes:summary><itunes:subtitle>elweb</itunes:subtitle><itunes:category text="Business"/><itunes:author>Giovanni Collazo</itunes:author><item><title>Python N00b</title><link>https://gcollazo.com/python-n00b</link><description>&lt;p&gt;
  Hace mas de dos años he estado interesado en aprender
  &lt;a href="https://en.wikipedia.org/wiki/Python_%28programming_language%29"
    &gt;Python&lt;/a
  &gt;. Este lenguaje se caracteriza por ser fácil de escribir y sobre todo fácil
  de leer. Tiene la capacidad de mezclar poder con &lt;i&gt;friendliness&lt;/i&gt;.
&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;
  Hace poco menos de un mes decidí que ya era tiempo de añadir esta herramienta
  a mi arsenal así que me compré unos cuantos libros y desde entonces he estado
  sumergido en el mundo de Python. He aprendido sobre
  &lt;a href="https://www.python.org/~guido/"&gt;Guido van Rossum&lt;/a&gt;,
  &lt;a href="http://www.jython.org/"&gt;Jython&lt;/a&gt;,
  &lt;a href="http://pyjs.org/"&gt;Pyjamas&lt;/a&gt;,
  &lt;a href="http://ironpython.net/"&gt;IronPython&lt;/a&gt;,
  &lt;a href="http://codespeak.net/pypy/dist/pypy/doc/"&gt;PyPy&lt;/a&gt;,
  &lt;a href="https://code.google.com/p/unladen-swallow/"&gt;unladen-swallow&lt;/a&gt;,
  &lt;a href="https://www.python.org/dev/peps/pep-3000/"&gt;Python 3000&lt;/a&gt;,
  &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;Beautiful Soup&lt;/a&gt;,
  &lt;a href="http://www.tornadoweb.org/"&gt;Tornado&lt;/a&gt; y muchas otras personas,
  tecnologías y herramientas que nunca hubiera conocido en otras circunstancias.
&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;
  De esta experiencia aprendí mucho. Primero que saber programar no tiene nada
  que ver con lenguajes de programación. Saber programar es una destreza
  completamente separada del lenguaje que se conozca y que la noción de que se
  es mejor programador con un lenguaje que con otro es una falacia. La realidad
  es que se es mejor programador con mas práctica y el ejercicio de aprender un
  nuevo lenguaje es un gran ejercicio. Como resultado cuando se aprende un nuevo
  lenguaje se es mejor programador.
&lt;/p&gt;
&lt;p&gt;
  En mi caso personal Python me enseño a tratar de ser mas económico en la
  cantidad de código que uso, a ser mas conciso. Ahora cuando escribo JavaScript
  o PHP en el día a día uso las cosas nuevas que he aprendido al estar expuesto
  a Python.
&lt;/p&gt;
&lt;p&gt;
  Finalmente quiero decir que Python me parece una herramienta súper poderosa.
  Es un lenguaje muy utilizado y con una comunidad vibrante de desarrollares
  produciendo todo tipo de packages. En las pocas semanas que llevo aprendiendo
  y practicando me he dado cuenta que hay un package para casi cada cosa que se
  me ocurre. Además he descubierto un proyecto que se llama
  &lt;a href="http://pyobjc.sourceforge.net/"&gt;PyObjC&lt;/a&gt; que es un puente entre
  Python y &lt;a href="https://en.wikipedia.org/wiki/Objective-C"&gt;Objective-C&lt;/a&gt; /
  &lt;a href="https://en.wikipedia.org/wiki/Cocoa_%28API%29"&gt;Cocoa&lt;/a&gt;, lo que
  permite escribir aplicaciones nativas para Mac OS X usando solo Python. Un
  ejemplo de una aplicación escrita de esta manera es
  &lt;a href="http://www.checkoutapp.com"&gt;Checkout&lt;/a&gt;, un sistema de POS. Pienso
  que mi primer proyecto será una aplicación para mac escrita en Python.
&lt;/p&gt;
&lt;p&gt;
  Mas adelante hablaré un poco sobre los libros que compré cuales son buenos y
  cuales malos.
&lt;/p&gt;
&lt;p&gt;
  Quiero saber tu experiencia aprendiendo nuevos lenguajes de programación, si
  sabes Python y que te gusta o no de este lenguaje. Deja un comentario abajo.
&lt;/p&gt;</description><pubDate>Tue, 15 Dec 2009 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/python-n00b</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>mongoDB.app</title><link>https://gcollazo.com/mongodb-app</link><description>&lt;p&gt;
  &lt;img src="/public/media/mongodbapp.png" /&gt;
&lt;/p&gt;
&lt;p&gt;
  A few days ago I released my first ever “real” swift app. The app is called
  MongoDB.app and it’s just a thin wrapper around the official MongoDB binaries
  that you can download from their site. The app will add a status menu icon
  which will allow you to start and stop a local MongoDB server visually. All
  data saved by the server will be stored to &lt;em&gt;~/Documents/MongoData&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;&lt;!--more--&gt;&lt;/p&gt;
&lt;p&gt;
  The idea was to create the simplest possible experience for MongoDB beginners.
  My inspiration was the great
  &lt;a href="http://postgresapp.com/"&gt;Postgres.app&lt;/a&gt; which does an amazing job
  at running PostgreSQL for your mac development environment.
&lt;/p&gt;
&lt;p&gt;
  Currently the app is using the latest stable release of mongo which at this
  moment is &lt;a href="https://www.mongodb.org/downloads"&gt;v2.6.7&lt;/a&gt;. I plan to
  keep the app up to date so expect frequent updates which for now I will post
  on the &lt;a href="https://github.com/gcollazo/mongodbapp/"&gt;GitHub repo&lt;/a&gt;
  &lt;del datetime="2016-01-10T23:03:35+00:00"
    &gt;until I figure out how to do automated updates using the
    &lt;a href="http://sparkle-project.org/"&gt;Sparkle framework&lt;/a&gt;. I might use
    some help doing this
  &lt;/del&gt;
  . The app will check for updates at startup and allow one-click download and
  update using Sparkle framework.
&lt;/p&gt;
&lt;p&gt;
  Inside the actual application package you will be able to find all the
  standard binaries that ship with mongodb including:
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;bsondump&lt;/li&gt;
  &lt;li&gt;mongo&lt;/li&gt;
  &lt;li&gt;mongod&lt;/li&gt;
  &lt;li&gt;mongodump&lt;/li&gt;
  &lt;li&gt;mongoexport&lt;/li&gt;
  &lt;li&gt;mongofiles&lt;/li&gt;
  &lt;li&gt;mongoimport&lt;/li&gt;
  &lt;li&gt;mongooplog&lt;/li&gt;
  &lt;li&gt;mongoperf&lt;/li&gt;
  &lt;li&gt;mongorestore&lt;/li&gt;
  &lt;li&gt;mongos&lt;/li&gt;
  &lt;li&gt;mongosniff&lt;/li&gt;
  &lt;li&gt;mongostat&lt;/li&gt;
  &lt;li&gt;mongotop&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  If you want to have access to all of these from your terminal you should add
  the application’s vendor folder to your path by adding the following to
  ~/&lt;em&gt;.bash_profile&lt;/em&gt;.
&lt;/p&gt;
&lt;pre&gt;&lt;code class="bash"&gt;# Add MongoDB.app to path
PATH="/Applications/MongoDB.app/Contents/Resources/Vendor/mongodb:$PATH"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
  &lt;strong
    &gt;&lt;a href="https://github.com/gcollazo/mongodbapp/releases"
      &gt;Download&lt;/a
    &gt;&lt;/strong
  &gt;&lt;br /&gt;
  &lt;img
    decoding="async"
    src="https://img.shields.io/github/release/gcollazo/mongodbapp.svg"
  /&gt;
&lt;/p&gt;
&lt;p&gt;
  To get the latest release go to the
  &lt;a href="https://github.com/gcollazo/mongodbapp/releases"
    &gt;repo&amp;#8217;s releases page&lt;/a
  &gt;. Please let me know if you find this useful.
&lt;/p&gt;</description><pubDate>Fri, 16 Jan 2015 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/mongodb-app</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>¿Quieres aprender a programar? Empieza aquí</title><link>https://gcollazo.com/quieres-aprender-a-programar-empieza-aqui</link><description>&lt;p&gt;
  &lt;span
    &gt;Con cierta frecuencia amigos, familiares y personas en Twitter o Facebook
    me preguntan sobre cómo aprender a programar. Algunos me dicen “quiero
    aprender hacer apps” refiriéndose a las aplicaciones en teléfonos
    móviles&amp;nbsp;y otros me dicen que quieren hacer un website para su
    negocio.&lt;/span
  &gt;
&lt;/p&gt;

&lt;p&gt;
  Mi respuesta casi automática&amp;nbsp;es la que creo que muchos otros
  programadores tienen, enviar unos cuantos enlaces&amp;nbsp;a uno de los muchos
  &lt;a href="https://www.freecodecamp.org/"&gt;sites de cursos online&lt;/a&gt; o
  &lt;a href="https://eloquentjavascript.net/"&gt;algún libro&lt;/a&gt;. Lo que nunca he
  hecho es sentarme y pensar cuidadosamente como fue que yo aprendí y tratar de
  extraer lecciones de mi experiencia.
&lt;/p&gt;

&lt;p&gt;
  Esta es la intención de este post. Compartir&amp;nbsp;las lecciones de mi
  experiencia en los últimos 15 años tratando de aprender a programar. Este es
  mi mejor intento de producir una contestación correcta para la pregunta: ¿Cómo
  aprendo a programar?
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;&lt;b&gt; 1. Necesitas un primer proyecto&lt;br /&gt;&lt;/b&gt;&lt;/span
  &gt;Decide que quieres construir antes de empezar a aprender. Mi recomendación es
  que trates de clonar un app o website que conoces bien y usas todo el tiempo.
  Una mala idea es querer hacer un video juego 3D, Microsoft Excel o Facebook.
  Escoge algo simple, no te compliques la vida.
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;Aprender a programar es suficientemente complicado como para tratar de ser
    ambiciosa / ambicioso en este punto. Si no se te ocurre nada trata con una
    calculadora de propinas o haciendo un website o portafolio online.&lt;/span
  &gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;Yo aprendí mucho tratando de clonar
    &lt;a href="https://en.wikipedia.org/wiki/Minesweeper_(video_game)"
      &gt;Minesweeper&lt;/a
    &gt;, el juego que venía con Windows 95 o 98, no recuerdo bien.&lt;/span
  &gt;
  Años después hice un website que usaba una base de datos para generar páginas
  dinámicamente. Esta experiencia me ayudó a aprender a usar bases de datos en
  el contexto de aplicaciones web.
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;&lt;b&gt; 2. Aprende lo menos posible&lt;br /&gt;&lt;/b&gt;&lt;/span
  &gt;Enfócate en aprender sólo lo necesario para ejecutar tu primer proyecto. Hay
  una cantidad prácticamente infinita de información sobre programación. Si
  tratas de entender todo antes de ejecutar algo concreto, nunca terminarás.
  Este fue mi problema principal tratando de aprender y la razón por la cual me
  tomó años en vez de meses. La estrategia es aprender de programación general
  mientras te enfocas en los problemas específicos que plantea cada proyecto que
  haces. Esta es la única estrategia efectiva que conozco.
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;&lt;b&gt; 3. Define una fecha de entrega&lt;br /&gt;&lt;/b&gt;&lt;/span
  &gt;Necesitas tener una fecha concreta para terminar tu primer proyecto y tienes
  que esforzarte para lograr cumplir con esa fecha. Piensa que es un trabajo y
  que te están pagando para hacerlo. Si puedes dejar de tratar y aprender cuando
  se ponga difícil, nunca vas a terminar. Esto también me pasó muchas veces y
  como lo resolví fue prometiendo a un amigo que le mostraría la aplicación
  terminada en 3 semanas. Un poco de presión social ayuda.
&lt;/p&gt;

&lt;p&gt;
  &lt;span&gt;&lt;b&gt; 4. Busca&amp;nbsp;&lt;/b&gt;&lt;/span
  &gt;&lt;b&gt;ayuda&lt;br /&gt;&lt;/b&gt;Identifica personas que te puedan ayudar. Todos los
  programadores que conozco les encanta ayudar a personas que están empezando.
  Me atrevo a decir que es casi parte de la cultura. No tengas miedo, haz
  preguntas. Si no conoces a nadie puedes contactar a
  &lt;a href="https://twitter.com/jpadilla_"&gt;@jpadilla_&lt;/a&gt;,
  &lt;a href="https://twitter.com/skfroi"&gt;@skfroi&lt;/a&gt; o a mi
  &lt;a href="https://twitter.com/gcollazo"&gt;@gcollazo&lt;/a&gt;&amp;nbsp;/
  &lt;a href="mailto:hello@gcollazo.com"&gt;hello@gcollazo.com&lt;/a&gt;&amp;nbsp;y te
  ayudaremos con gusto.
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;&lt;b&gt; 5. No te rindas&lt;br /&gt;&lt;/b&gt;&lt;/span
  &gt;Aprender a programar es increíblemente difícil, no importa lo que nadie diga.
  La buena noticia es que ahora hay muchos recursos y comunidades diseñadas
  especialmente para ayudarte. Cuando sientas que no quieres seguir porque es
  muy difícil o estás cansada/o, toma un descanso y cuando tengas más energía
  sigue tratando. Busca ayuda y no te rindas, es difícil pero posible.
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;&lt;b&gt; 6. Proyecto #1: Done&lt;br /&gt;&lt;/b&gt;&lt;/span
  &gt;Una vez completes tu primero proyecto todo se vuelve mucho más fácil. Cuando
  terminé mi primero proyecto, me di cuenta que todas las aplicaciones son una
  combinación diferente de los mismos principios que ya había aprendido. Para
  hacer el segundo proyecto sólo tienes que aprender un poquito más y usar lo
  que ya sabes.
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;&lt;b&gt; 7. Nunca dejes de aprender&lt;br /&gt;&lt;/b&gt;&lt;/span
  &gt;Ya hiciste uno o dos programas así que es hora de empezar de nuevo y repetir
  el proceso para poder aprender un poco más. Es lo que todos hacemos. Si nunca
  dejas de estar en este proceso serás una/uno de las/los mejores en esto.
&lt;/p&gt;

&lt;p&gt;
  &lt;span
    &gt;&lt;b&gt; ¿Qué tecnologías debo aprender?&lt;br /&gt;&lt;/b&gt;&lt;/span
  &gt;Aunque para mi lo más importante de este post son las 7 lecciones que están
  arriba no podía faltar una recomendación específica en cuanto a que
  tecnologías aprender. La lista de abajo está en el orden que creo se deberían
  estudiar pero esto puede variar para cada cual.
&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;b&gt;&lt;/b&gt;&lt;span&gt;&lt;b&gt;HTML&lt;/b&gt; – Sirve para hacer páginas web estáticas.&lt;/span&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;b&gt;&lt;/b
    &gt;&lt;span
      &gt;&lt;b&gt;CSS&lt;/b&gt; – Sirve para hacer que tus páginas web se vean bien.&lt;/span
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;b&gt;&lt;/b
    &gt;&lt;span
      &gt;&lt;b&gt;JavaScript &lt;/b&gt;para el browser&amp;nbsp;– Sirve para añadir interactividad
      a tus paginas web. Si dominas JS, podrás aprovecharte de todas las
      capacidades que tienen los browsers, que son muchísimas.&lt;/span
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;span
      &gt;&lt;b&gt;Node.js &lt;/b&gt;– es una plataforma y no un lenguaje. Node.js te permitirá
      crear servers para generar páginas web dinámicamente. El lenguaje que se
      utiliza para programar con Node.js es JavaScript. La ventaja es que en
      este punto ya deberías saber suficiente JavaScript.&lt;/span
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;b&gt;&lt;/b
    &gt;&lt;span
      &gt;&lt;b&gt;PostgreSQL&lt;/b&gt; – es un programa de base de datos. Le permitirá a tu
      servidor guardar y buscar datos de forma fácil.&lt;/span
    &gt;&lt;b&gt;&lt;/b&gt;
  &lt;/li&gt;
&lt;/ol&gt;</description><pubDate>Sat, 21 Mar 2015 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/quieres-aprender-a-programar-empieza-aqui</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>The Security Footgun in etcd</title><link>https://gcollazo.com/the-security-footgun-in-etcd</link><description>&lt;p&gt;
  From an application security perspective databases are the most valuable parts
  of our systems. They store the data that gives value to our apps and
  companies. This data which has been entrusted to us by our users should be
  kept safe and away of the hands of criminals.
&lt;/p&gt;

&lt;p&gt;
  Every developer I talk to is very aware of this. Their MySQL, PostgreSQL and
  MongoDB databases are treated with caution and security is definitely a
  thought. It doesn’t always works but at least everyone is aware and tries
  their best.
&lt;/p&gt;

&lt;p&gt;
  But what happens with databases that don’t feel like “regular databases”? I’m
  talking about Memcached, Redis and of course etcd. This kind of databases are
  often used for a single use case and treated without much care.
&lt;/p&gt;

&lt;p&gt;
  The other day I was reading the CoreOS documentation and came across etcd. I
  have read about etcd before but in the context of CoreOS is really
  interesting. Etcd is an integral part of their clustering system. They use it
  to do orchestration and a lot of other stuff.
&lt;/p&gt;

&lt;p&gt;
  Etcd is really cool, It uses the raft consensus algorithm to keep all the
  nodes in sync, it has a very simple to use CLI tool and best of all it has an
  HTTP API. I really like etcd and it sure feels like we are going to use it in
  the near future for some of our orchestration needs.
&lt;/p&gt;

&lt;p&gt;
  Reading about etcd made me remember the news about
  &lt;a
    href="https://thenextweb.com/insider/2017/01/08/mongodb-ransomware-exists-people-bad-security/"
    &gt;MongoDB security issues&lt;/a
  &gt;
  that turned out to be people not enabling authentication which is not enabled
  by default. It really beats me why this is the happy path but hey, I don’t
  build databases I just use them.
&lt;/p&gt;

&lt;p&gt;
  Here’s an excerpt from the etcd’s documentation talking about their
  authentication mechanism.
&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;
    “etcd before 2.1 was a completely open system; anyone with access to the API
    could change keys. In order to preserve backward compatibility and
    upgradability, this feature is off by default.”
    &lt;a href="https://coreos.com/etcd/docs/latest/v2/authentication.html"
      &gt;Read more&lt;/a
    &gt;
  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  Yes. The same thing, etcd has an authentication mechanism which is disabled by
  default and it also has a very nice RESTful API as it’s main interface, what
  could go wrong right. People are smart and they will keep their etcd services
  from leaking to the open internet. Wrong!
&lt;/p&gt;

&lt;p&gt;
  I did a
  &lt;a href="https://www.shodan.io/search?query=etcd"&gt;simple search on shodan&lt;/a&gt;
  and came up with &lt;strong&gt;2,284 etcd servers on the open internet&lt;/strong&gt;. So
  I clicked a few and on the third try I saw what I was hoping not to see.
  CREDENTIALS, a lot of CREDENTIALS. Credentials for things like cms_admin,
  mysql_root, postgres, etc.
&lt;/p&gt;

&lt;p&gt;
  In order to try to get a sense of the issue I downloaded the full shodan
  report and wrote a very simple script that basically called the etcd API and
  requested all keys. That’s basically equivalent to doing a database dump but
  over their very nice REST API.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;GET http://&amp;lt;ip address&amp;gt;:2379/v2/keys/?recursive=true&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  This will return all the keys stored on the servers in JSON format. So my
  script basically went down the list and created a file for each IP
  (127-0-0-1.json) with the contents of etcd. I stopped the script at about 750
  MB of data and 1,485 of the original IP list.
&lt;/p&gt;

&lt;p&gt;
  Then I performed a few basic searches to get a sense of what was there and
  found a bunch of credentials. Passwords for databases of all kinds, AWS secret
  keys, and API keys and secrets for a bunch of services. Also came across a few
  certificates, you name it. There’s a lot of stuff there.
&lt;/p&gt;

&lt;figure&gt;
  &lt;table&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;password&lt;/td&gt;
        &lt;td&gt;8781&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;aws_secret_access_key&lt;/td&gt;
        &lt;td&gt;650&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;secret_key&lt;/td&gt;
        &lt;td&gt;23&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;private_key&lt;/td&gt;
        &lt;td&gt;8&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/figure&gt;

&lt;p&gt;
  I did not test any of the credentials but if I had to guess I would guess that
  at least a few of them should work and this is the scary part. Anyone with
  just a few minutes to spare could end up with a list of hundreds of database
  credentials which can be used to steal data, or perform a ransomware attacks.
&lt;/p&gt;

&lt;p&gt;
  In order to prevent this from happening just enable authentication and if is
  not strictly required please get your etcd servers off the open internet. Use
  a security group or set a firewall rule to avoid random people from reading
  and writing to your etcd server.
&lt;/p&gt;

&lt;p&gt;
  Finally, I did not test this but since all of these etcd servers are
  completely open I’m almost certain that an attacker could write to them using
  the same API. An attacker might use it to change the data in etcd and mess
  with configuration and even maybe authentication or it could be used to store
  exfiltrated data from other attacks.
&lt;/p&gt;

&lt;p&gt;
  I really hope that the etcd team would reconsider their position and make a
  breaking change soon to enable authentication by default. As we learned from
  the MongoDB experience this is a huge footgun that can be easily removed an
  otherwise awesome software.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
  Update: Twitter user
  &lt;a href="https://twitter.com/BradErzwh"&gt;@BradErzwh&lt;/a&gt; reminded me to mention
  that etcd is “used as Kubernetes’ backing store for all cluster data”. In most
  cases that data includes secrets encoded as base64 strings.
  &lt;a href="https://kubernetes.io/docs/concepts/overview/components/#etcd"
    &gt;More info&lt;/a
  &gt;
&lt;/p&gt;</description><pubDate>Fri, 16 Mar 2018 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/the-security-footgun-in-etcd</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Sobre como ser mejor developer</title><link>https://gcollazo.com/sobre-como-ser-mejor-developer</link><description>&lt;p&gt;
  En abril pasado mi amigo Froilan Irizarry publicó un
  &lt;a
    href="https://medium.com/@skfroi/quieres-ser-considerado-un-desarrollador-senior-se-un-mentor-primero-b92d0e330a57"
    &gt;blog post&lt;/a
  &gt;
  hablando de sobre como convertirse en un “senior developer” y hace unos días
  en Facebook me encontré con
  &lt;a
    href="https://www.facebook.com/groups/programadorespuertorico/permalink/915314515296277/"
    &gt;un post&lt;/a
  &gt;
  preguntando “¿Cuál ha sido su estrategia para sobresalir en el campo laboral?”
&lt;/p&gt;
&lt;p&gt;
  Ambas publicaciones me hicieron pensar en cual es mi respuesta a esta pregunta
  de sobre como mejorar y sobresalir en la industria de desarrollo de software.
&lt;/p&gt;
&lt;p&gt;
  A continuación los factores que considero más importantes para lograr ser un
  mejor developer y con suerte sobresalir.
&lt;/p&gt;
&lt;p&gt;
  &lt;strong&gt;Enfocarse en el usuario y su experiencia&lt;br /&gt;&lt;/strong&gt;Todos mis
  proyectos comienzan con dibujos detallados de lo que será la solución final y
  de ahí trabajo para determinar cuales son los requisitos técnicos para lograr
  la solución. El diseño define los requisitos técnicos. Al cliente no le
  importa si usas C#, Haskell, PHP, VBScript o “whatever” para solucionar el
  problema. Lo que importa es la solución y en eso hay que enfocarse.
&lt;/p&gt;
&lt;p&gt;
  &lt;strong
    &gt;Los “soft skills” son tanto o más importantes que las capacidades
    técnicas&lt;/strong
  &gt;&lt;br /&gt;Entender el problema que quiere resolver el cliente, poderlo explicar a
  otros, poder plantear posibles soluciones, escuchar otros puntos de vista con
  respeto y empatía, ponerse en los zapatos de otros y ser tolerante.
&lt;/p&gt;
&lt;p&gt;
  &lt;strong&gt;Saber de lo que estás hablando&lt;br /&gt;&lt;/strong&gt;Ningún argumento le gana
  a una &lt;strong&gt;experiencia del mundo real&lt;/strong&gt; en un escenario similar.
  Hacer muchos proyecto es la mejor forma de tener éxito. Todos los proyectos
  tendrán problemas y serán difíciles de completar pero si tienes muchos por lo
  menos algunos serán buenos y poco a poco con el tiempo aprenderás a reconocer
  y utilizar los patrones que te llevan a lograr proyectos exitosos.
&lt;/p&gt;
&lt;p&gt;
  &lt;strong&gt;Compartir lo que has aprendido&lt;/strong&gt;&lt;br /&gt;Para mi la mejor forma de
  aprender y aclarar ideas es tratando de compartirlas. Trato de compartir lo
  que aprendo con todo el que le interese. Para esto escribo blog posts, hago
  presentaciones en “meetups” y conferencias y recientemente rants en Twitter.
&lt;/p&gt;
&lt;p&gt;
  &lt;strong&gt;“Under promise and over deliver”&lt;/strong&gt;&lt;br /&gt;Ser conservador en
  cuanto a estimados de tiempo y “features”. Luego si el tiempo permite,
  sorprender al cliente con una experiencia mejor a la esperada. Este funciona
  todas las veces.
&lt;/p&gt;</description><pubDate>Sat, 21 Mar 2015 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/sobre-como-ser-mejor-developer</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Un bug en el app CESCO Digital expone la información de todos los conductores registrados en Puerto Rico</title><link>https://gcollazo.com/bug-app-cesco-digital</link><description>&lt;h4&gt;Antes de empezar&lt;/h4&gt;

&lt;p&gt;
  Reporté este
  &lt;a href="https://en.wikipedia.org/wiki/Software_bug"&gt;bug&lt;/a&gt; dos veces con la
  intención de que se resolviera el problema y se protegiera la información de
  los ciudadanos. La primera vez que reporté el problema recibí confirmación de
  que sería atendido, pero ocho meses más tarde el problema no había sido
  resuelto.
&lt;/p&gt;

&lt;p&gt;
  Recientemente, reporté el bug por segunda vez e hice énfasis en el riesgo
  potencial de este tipo de vulnerabilidad y en mi intención de hacer público el
  problema siguiendo el modelo de “&lt;a
    href="https://en.wikipedia.org/wiki/Responsible_disclosure"
    &gt;responsible disclosure&lt;/a
  &gt;”. Esta vez había gente diferente a cargo del otro lado y se atendió la
  situación con prontitud hasta corregir el problema.
&lt;/p&gt;

&lt;p&gt;
  A cambio de este reporte no se pidió recompensa, ni contratos para asesoría,
  ni contratos para programar, ni ninguna otra forma de compensación directa o
  indirecta. Tengo trabajo y estoy bastante ocupado con eso pero siento que es
  mi responsabilidad reportar este tipo de problema cuando lo encuentro para que
  se pueda resolver y evitar situaciones que lamentar.
&lt;/p&gt;

&lt;p&gt;
  En el pasado he reportado problemas como este a compañías pequeñas y grandes
  dentro y fuera de Puerto Rico. Esta es mi primera vez haciendo “responsible
  disclosure” con una entidad del Gobierno de Puerto Rico, lo que me ha tenido
  algo nervioso durante los últimos días.
&lt;/p&gt;

&lt;p&gt;
  Como todos sabemos, en nuestras islas cualquier cosa que hace o no hace el
  gobierno es una controversia y estoy seguro que algunos me criticarán por mi
  acercamiento y otros dirán lo bien que lo hice según les convenga para
  adelantar su punto de vista.
&lt;/p&gt;

&lt;p&gt;
  Me siento satisfecho y convencido de que hice lo correcto y logré conseguir el
  resultado deseado.
&lt;/p&gt;

&lt;span id="more-4060"&gt;&lt;/span&gt;

&lt;h4&gt;El bug&lt;/h4&gt;

&lt;p&gt;
  Según &lt;a href="https://en.wikipedia.org/wiki/OWASP"&gt;OWASP&lt;/a&gt;, el nombre
  técnico de este bug es un “&lt;a
    href="https://www.owasp.org/index.php/Top_10_2007-Insecure_Direct_Object_Reference"
    &gt;insecure direct object reference&lt;/a
  &gt;“. Este tipo de bug permite a un usuario autorizado, cambiar el valor de un
  parámetro para tener acceso a un objeto para el cual no tiene autorización.
&lt;/p&gt;

&lt;p&gt;Trataré de explicarlo con un ejemplo simple.&lt;/p&gt;

&lt;p&gt;
  Cuando usted usa Google Drive y está viendo un documento, el browser muestra
  arriba la dirección web de ese documento. Para propósitos de esta explicación
  digamos que la dirección es
  &lt;em&gt;https://drive.google.com/documentos/101&lt;/em&gt;. La última parte de esa
  dirección (101) muestra el nombre o el valor que identifica a ese documento
  dentro de Google Drive.
&lt;/p&gt;

&lt;p&gt;
  Si usted modifica la dirección y reemplaza el &lt;strong&gt;101&lt;/strong&gt; por
  &lt;strong&gt;102&lt;/strong&gt; casi seguro verá un error en la pantalla. El error será
  algo como “no se encontró el documento” o “no tiene permiso para ver este
  documento”.
&lt;/p&gt;

&lt;p&gt;
  Lo que está pasando en este ejemplo es que Google Drive usa la última parte de
  la dirección para identificar cada documento que almacena para sus usuarios.
  El documento &lt;strong&gt;101&lt;/strong&gt; te pertenece, así que lo puedes ver, pero el
  documento &lt;strong&gt;102&lt;/strong&gt; probablemente sea de otro usuario así que no
  tienes permiso para verlo.
&lt;/p&gt;

&lt;p&gt;
  El
  &lt;a href="https://en.wikipedia.org/wiki/Application_programming_interface"
    &gt;API&lt;/a
  &gt;
  que usa el app de
  &lt;a href="https://itunes.apple.com/us/app/cesco-digital/id1389951990"
    &gt;CESCO Digital&lt;/a
  &gt;
  para acceder la información de los ciudadanos tenía un bug que permitía ver la
  información de cualquier ciudadano con solo cambiar la parte final de la
  dirección, tal y como expliqué en el ejemplo anterior. Si su información
  personal era visible en la dirección que termina en &lt;strong&gt;101&lt;/strong&gt; y
  usted cambia esa dirección para que termine en &lt;strong&gt;102&lt;/strong&gt;, usted
  vería la información de otro ciudadano.
&lt;/p&gt;

&lt;p&gt;
  Un atacante al darse cuenta de esta vulnerabilidad podría escribir un programa
  que de forma automática modifique la dirección empezando por el número 1,
  luego 2, 3, 4, 5 y así consecutivamente hasta llegar a ver y almacenar los
  datos de todos los usuarios.
&lt;/p&gt;

&lt;p&gt;
  Este ataque se podría completar en horas pero si se tratara de atacante un
  poco mas sofisticado, podría estirar el proceso a lo largo de multiples días o
  incluso semanas para asegurarse de no levantar alarmas o sospechas de los
  administradores del sistema.
&lt;/p&gt;

&lt;p&gt;Entre lo datos de los ciudadanos que quedaron expuestos están:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Nombre legal del ciudadano&lt;/li&gt;

  &lt;li&gt;Fecha de nacimiento&lt;/li&gt;

  &lt;li&gt;Dirección física&lt;/li&gt;

  &lt;li&gt;Últimos 4 dígitos del número de seguro social&lt;/li&gt;

  &lt;li&gt;Número de licencia de conducir&lt;/li&gt;

  &lt;li&gt;Fecha de expiración de licencia de conducir&lt;/li&gt;

  &lt;li&gt;
    Autos registrados con marca, modelo, color, tablilla y
    &lt;a href="https://en.wikipedia.org/wiki/Vehicle_identification_number"
      &gt;VIN&lt;/a
    &gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Un poco más técnico&lt;/h4&gt;

&lt;p&gt;
  Para detectar el problema lo único que había que hacer era usar un
  &lt;a href="https://en.wikipedia.org/wiki/Proxy_server"&gt;proxy&lt;/a&gt; e inspeccionar
  los
  &lt;a
    href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_message"
    &gt;requests&lt;/a
  &gt;
  que hace la aplicación al API. En mi caso uso
  &lt;a href="https://itunes.apple.com/us/app/charles-proxy/id1134218562"
    &gt;Charles Proxy&lt;/a
  &gt;
  en iOS y &lt;a href="https://mitmproxy.org"&gt;mitmproxy&lt;/a&gt; en macOS.
&lt;/p&gt;

&lt;figure&gt;
  &lt;video
    width="100%"
    controls=""
    poster="/public/media/ScreenShot_gIKYFmgH@2x.png"
    src="/public/media/Vulnerabilidad-en-app-CESCO-Digital-oHAULBHGjyg.mp4"
  &gt;&lt;/video&gt;
  &lt;figcaption&gt;
    En este vídeo se demuestra el bug antes de ser corregido
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;
  Lo primero que llama la atención es que desde el primer request al API se
  puede ver un valor en el
  &lt;a href="https://en.wikipedia.org/wiki/List_of_HTTP_header_fields"&gt;header&lt;/a&gt;
  de “Authorization”. Esto es inesperado porque no he entrado ningún dato y el
  app ya está haciendo llamadas autenticadas al API. Aunque es poco común, no es
  necesariamente un problema.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;GET /config HTTP/1.1
Host: cescodigital.dtop.gov.pr
Authorization: Basic dGVzdDoxMjM0NTppb3NDZXNjb0RpZ2l0YWw=
User-Agent: dtop_mobile/1 CFNetwork/976 Darwin/18.2.0
Connection: keep-alive
Accept: application/json
Accept-Language: en-us
Accept-Encoding: gzip, deflate, br&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Al inspeccionar el valor que se está enviando me doy cuenta que esta usando
  “&lt;a href="https://en.wikipedia.org/wiki/Basic_access_authentication"
    &gt;basic access authentication&lt;/a
  &gt;” y se que en esos casos se envía el valor codificado en
  &lt;a href="https://en.wikipedia.org/wiki/Base64"&gt;base 64&lt;/a&gt;. Copié el valor y
  lo descodifiqué para darme cuenta que definitivamente era un valor “&lt;a
    href="https://en.wikipedia.org/wiki/Hard_coding"
    &gt;hard coded&lt;/a
  &gt;” dentro del app. El valor descodificado era “test:12345:iosCescoDigital”.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ echo "dGVzdDoxMjM0NTppb3NDZXNjb0RpZ2l0YWw="|base64 -D
test:12345:iosCescoDigital&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Al darme cuenta de esto pensé que quizás una vez se completara el cuestionario
  que hace el app para autorizarme se crearía algún tipo de
  &lt;a href="https://en.wikipedia.org/wiki/Session_(computer_science)"&gt;sesión&lt;/a&gt;
  que me identificaría en los otros requests del app.
&lt;/p&gt;

&lt;p&gt;
  Entré mi número de licencia de conducir, últimos 4 dígitos del seguro social y
  mi fecha de nacimiento. El API validó mis datos y respondió con alguna
  información personal y un “uid” que me identifica dentro del sistema.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "uid": "1234567",
  "name": "DEL PUEBLO, JUAN",
  "entityType": "ssid"
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Inmediatamente el app cambia de pantalla y muestra mis datos personales.
  Cuando miro el request que hace el app puedo ver que la dirección que usa
  contiene el mismo “uid” del request anterior y cuando me fijo en los headers
  me doy cuenta que no hay ninguna información de sesión o algo parecido para
  identificarme. Lo único que puedo ver es el mismo authorization header “hard
  coded” del principio.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;GET /entity/1234567 HTTP/1.1
Host: cescodigital.dtop.gov.pr
Authorization: Basic dGVzdDoxMjM0NTppb3NDZXNjb0RpZ2l0YWw=
User-Agent: dtop_mobile/1 CFNetwork/976 Darwin/18.2.0
Connection: keep-alive
Accept: application/json
Accept-Language: en-us
Accept-Encoding: gzip, deflate, br
{
  "principal": {
    "uid": "1234567",
    "name": "DEL PUEBLO, JUAN",
    "email": null,
    "entityType": "ssid"
  },
  "license": {
    "category": "3",
    "expirationDate": 946699200000,
    "dateOfBirth": 946699200000,
    "address": {
      "line1": "123 MAIN STREET",
      "line2": "",
      "city": "SAN JUAN",
      "state": "PR",
      "zipCode": "00901"
    },
    "unpaidFines": 0,
    "alert": {
      "type": "Warning",
      "text": "RenewLicense"
    },
    "id": XXXXXX
  },
  "vehicles": [
    {
      "vinNumber": "XXXXXXXXXXXXXXXXX",
      "registrationNumber": "XXXXXXXX",
      "year": XXXX,
      "make": "XXXXXX",
      "model": "XXXXXX",
      "color": "XXXXX",
      "plate": "XXXXXX",
      "tag": {
        "number": "XXXXXXXX",
        "expirationDate": XXXXXXXXXXX,
        "id": XXXXXXXX
      },
      "unpaidFines": X,
      "alert": null,
      "canRenewTag": false,
      "id": XXXXXXXX
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  En este punto ya estaba casi seguro del problema por lo que hice “log out” y
  le dije a mi esposa que hiciera “login” para confirmar el problema. Siguió el
  mismo proceso que yo y tan pronto entró pude ver que sus datos personales
  venían de una dirección diferente pero estaba usando la misma información de
  autenticación que yo. Esto confirmó el bug.
&lt;/p&gt;

&lt;h4&gt;Primer intento&lt;/h4&gt;

&lt;p&gt;
  Tan pronto confirmé el bug traté de reportarlo y logré conseguir una
  confirmación de que fue recibido y que se atendería a la mayor brevedad
  posible. Dejé el tema ahí y lo di por resuelto. Me olvidé de este asunto.
&lt;/p&gt;

&lt;p&gt;
  Hace más o menos una semana tuve que entrar al app para buscar la licencia
  nueva de mi auto. Me tocaba renovar el marbete. Cuando entro al app veo que
  todo se ve igual y recordé el bug, así que me dio curiosidad mirar a ver si
  resolvieron el problema que reporté.
&lt;/p&gt;

&lt;p&gt;
  Activé Charles Proxy para inspeccionar la comunicación del app con el API.
  Para mi sorpresa habían pasado &lt;strong&gt;ocho meses&lt;/strong&gt; y no se había
  resuelto el problema. La información de todos los ciudadanos seguía expuesta y
  disponible para el que la quisiera obtener.
&lt;/p&gt;

&lt;h4&gt;Segundo intento&lt;/h4&gt;

&lt;p&gt;
  Esta vez decidí tomar un acercamiento un poco más fuerte y proveer los
  incentivos necesarios para que las personas a cargo se vieran motivadas a
  resolver el problema con la prisa que requería.
&lt;/p&gt;

&lt;p&gt;
  Cuando reporté el problema esta vez expliqué nuevamente los riesgos y mi
  intención de hacer “responsible disclosure”. En otras palabras, que era
  importante ponernos de acuerdo en un “timeline” en el que las personas a cargo
  resolverían el problema y que en un tiempo específico yo publicaría este blog
  post que estas leyendo, explicando el problema en detalle.
&lt;/p&gt;

&lt;p&gt;
  Esto significa que si no lograran resolver el problema en un tiempo
  previamente acordado, la única opción que tendrían sería apagar el servicio
  por completo para evitar que atacantes que lean sobre la vulnerabilidad la
  exploten.
&lt;/p&gt;

&lt;p&gt;
  En ese momento no sabía cómo se tomaría la idea y si se volvería una situación
  incómoda pero para mi sorpresa se me escuchó y se le prestó la atención que
  pienso era necesaria. Durante el proceso hubo comunicación y cuando se me
  pidió, saqué tiempo y compartí todo lo que sabía sobre el problema con los
  involucrados del otro lado.
&lt;/p&gt;

&lt;p&gt;
  Para mantenerme honesto, tan pronto reporté el bug a las personas encargadas,
  también alerté de forma confidencial a miembros de la prensa bajo la condición
  de que no se publicara nada hasta por lo menos una semana después del reporte
  original.
&lt;/p&gt;

&lt;p&gt;
  Una semana me pareció un tiempo justo y suficiente para hacer los ajustes
  necesarios o apagar el servicio. Recordemos que el problema fue reportado ocho
  meses antes.
&lt;/p&gt;

&lt;p&gt;
  Tres días más tarde un equipo de desarrolladores resolvió el problema y al día
  siguiente y por petición de ese equipo, pude confirmar que el problema estaba
  resuelto.
&lt;/p&gt;

&lt;h4&gt;La solución&lt;/h4&gt;

&lt;p&gt;
  Los desarrolladores implementaron una solución bastante ingeniosa que permite
  proteger los datos de los ciudadanos de este ataque sin la necesidad de
  actualizar el app.
&lt;/p&gt;

&lt;p&gt;
  Ahora, luego del cambio, cuando un usuario completa el proceso de login, el
  app recibe una respuesta idéntica a la anterior pero en el campo de “uid” el
  valor es ahora un
  &lt;a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function"&gt;hash&lt;/a&gt;
  &lt;a href="https://en.wikipedia.org/wiki/SHA-2"&gt;SHA256&lt;/a&gt; en vez de un número.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "uid": "afa27b44d43b02a9fea41d13cedc2e4016cfc...",
  "name": "Juan del Pueblo",
  "entityType": "ssid"
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  En vez de tener direcciones como &lt;em&gt;/entity/1234567&lt;/em&gt; ahora tienen
  direcciones como esta &lt;em&gt;/entity/afa27b44d43b02a9fea41d13cedc2e4…&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
  No he visto el código pero de mi conversación con los developers y las pruebas
  que pude hacer me atrevo a adivinar lo siguiente:
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    El hash se calcula usando algún valor secreto, la fecha y/o data random.
  &lt;/li&gt;

  &lt;li&gt;
    El hash expira luego de un tiempo, forzando al usuario a entrar nuevamente
    su información de login para continuar usando el app.
  &lt;/li&gt;

  &lt;li&gt;
    El tiempo de validez es aproximadamente 30 minutos así que no debe tener
    impacto en la experiencia de los usuarios al usar el app.
  &lt;/li&gt;

  &lt;li&gt;
    Posiblemente están persistiendo el hash en la base de datos para poder
    expirarlo.
  &lt;/li&gt;

  &lt;li&gt;
    Es posible que también estén persistiendo la relación entre el hash generado
    y el ID interno del usuario.
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
  En resumen, es suficientemente difícil adivinar un hash que sea válido para
  que valga la pena intentarlo.
&lt;/p&gt;

&lt;p&gt;
  Esta solución está cumpliendo el rol de una sesión que confirma que el usuario
  completó el proceso de login (autenticación) y que tiene permiso para ver
  ciertos datos (autorización) en todos los requests al API.
&lt;/p&gt;

&lt;h4&gt;Timeline&lt;/h4&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td width="150"&gt;2018-06-26&lt;/td&gt;
      &lt;td&gt;Se reportó el problema por primera vez.&lt;br /&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2018-06-26&lt;/td&gt;
      &lt;td&gt;
        Recibí confirmación de que el reporte fue recibido y que se
        canalizaría.&lt;br /&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2019-02-04&lt;/td&gt;
      &lt;td&gt;Se reportó el problema por segunda vez.&lt;br /&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2019-02-04&lt;/td&gt;
      &lt;td&gt;
        Recibí confirmación de que el reporte fue recibido y que se trabajaría
        de inmediato.&lt;br /&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2019-02-04&lt;/td&gt;
      &lt;td&gt;
        Se compartió el bug confidencialmente con miembros de la prensa.&lt;br /&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2019-02-07&lt;/td&gt;
      &lt;td&gt;
        Se le explicó el problema al equipo de desarrollo, confirmaron el bug y
        dijeron que lo resolverían ese mismo día.&lt;br /&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2019-02-08&lt;/td&gt;
      &lt;td&gt;Confirmé que se resolvió el problema.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2019-02-12&lt;/td&gt;
      &lt;td&gt;Se publicó este blog post.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;br /&gt;

&lt;h4&gt;Hace falta un proceso&lt;/h4&gt;

&lt;p&gt;
  Todo software tiene bugs. Esto siempre ha sido cierto y siempre será cierto.
  Algunos de estos bugs tienen implicaciones de seguridad y deben ser atendidos
  de una forma diferente. Nuestro gobierno es guardian de muchos datos
  sensitivos y deberían prepararse mejor para atender situaciones en las que
  estos datos estén en riesgo.
&lt;/p&gt;

&lt;p&gt;
  Hay muchas formas de hacer esto, pero hay que empezar por algún lado. Mi
  recomendación es publicar un proceso para reportar bugs de seguridad en el que
  los que reportan se sientan a salvo. Este documento debe contener nombres e
  información de contacto de las personas que atenderán los casos que se
  reporten. Además debe especificar términos de tiempo y los pasos que se
  seguirán para manejar la situación.
&lt;/p&gt;

&lt;p&gt;
  Luego de tener esto, las personas encargadas deben respetar ese proceso y
  colaborar con las personas que hagan los reportes. Tal y como pasó en mi
  segundo intento.
&lt;/p&gt;

&lt;p&gt;
  Como punto de partida pueden copiar
  &lt;a href="https://www.gasolinamovil.com/security/"
    &gt;el proceso que usamos en Gasolina Móvil&lt;/a
  &gt;
  que aunque no es perfecto, funciona cuando el volumen de reportes es bajo. Si
  lo usan recuerden cambiar los nombres e información de contacto. &#128521;
&lt;/p&gt;

&lt;p&gt;
  Una vez tengan algo simple funcionando, se podría pensar en implementar un
  programa de “&lt;a href="https://en.wikipedia.org/wiki/Bug_bounty_program"
    &gt;bug bounties&lt;/a
  &gt;” aunque no se pague dinero por los reportes. La recompensa podría ser algo
  tan simple como tener una página web que contenga una lista de las personas
  que más bugs han reportado. Como los “leaderboards” que tienen algunos juegos
  de video.
&lt;/p&gt;

&lt;p&gt;
  Estoy seguro que hacer algo en esta dirección motivaría a personas con la
  capacidad de buscar, documentar y reportar responsablemente este tipo de
  problemas, logrando mejorar la seguridad y funcionamiento de los sistemas del
  gobierno.
&lt;/p&gt;</description><pubDate>Tue, 12 Feb 2019 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/bug-app-cesco-digital</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Español de Puerto Rico</title><link>https://gcollazo.com/espanol-de-puerto-rico</link><description>&lt;p&gt;
  Hey, si tu o alguien que conoces maneja un website escrito en Español y
  producido en Puerto Rico, estaría super cool que lo marcan con el idioma y
  región correcta.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;html lang="es-PR"&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Esto ayudaría mucho a
  &lt;a
    rel="noreferrer noopener"
    href="https://t.co/wY5WxvdIIf?amp=1"
    target="_blank"
    &gt;http://tesoro.pr&lt;/a
  &gt;
  en varios inventos futuros.
&lt;/p&gt;

&lt;p&gt;
  Por mi parte todavía estoy pensando cómo manejar el tema en este site. Lo
  mismo escribo en Español que en Inglés.
&lt;/p&gt;

&lt;p&gt;Links con más información técnica:&lt;/p&gt;

&lt;p&gt;
  &lt;a
    href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang"
    &gt;https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang&lt;/a
  &gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a
    href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html#Example"
    &gt;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html#Example&lt;/a
  &gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://www.ietf.org/rfc/bcp/bcp47.txt"
    &gt;https://www.ietf.org/rfc/bcp/bcp47.txt&lt;/a
  &gt;
&lt;/p&gt;</description><pubDate>Wed, 02 Dec 2020 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/espanol-de-puerto-rico</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>User-defined type guards in TypeScript</title><link>https://gcollazo.com/user-defined-type-guards-in-typescript</link><description>&lt;p&gt;
  Decided to write this blog post because I couldn’t find any documentation or
  online examples like the one I’m sharing here. Every example I’ve seen the
  &lt;a
    href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates"
    &gt;user-defined type guard&lt;/a
  &gt;
  is used to replace the whole type definition and not specific properties.
&lt;/p&gt;

&lt;p&gt;Here’s the code without type guard.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;interface Config {
  foo: string;
  bar: string;
}

class Thing {
  name: string;
  config?: Config;

  constructor(name: string, config?: Config) {
    this.name = name;
    this.config = config;
  }

  // &#128064; returning boolean, will change on next example
  hasConfig(): boolean {
    if (this.config) {
      return true;
    }
    return false;
  }
}

let firstThing = new Thing("Apple", { foo:"one", bar: "two" });

if (firstThing.hasConfig()) {
  // #1 - Here firstThing.config is `Config | undefined`
  console.log(firstThing.config.foo);
}

if (firstThing.config) {
  // #2 - Here firstThing.config is `Config`
  console.log(firstThing.config.foo);
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  On #1 &lt;code&gt;firstThing.config&lt;/code&gt;. is &lt;code&gt;Config | undefined&lt;/code&gt; even
  though we know it can’t be &lt;code&gt;undefined&lt;/code&gt; because our
  &lt;code&gt;hasConfig&lt;/code&gt; method made sure the value is truthy.
&lt;/p&gt;

&lt;p&gt;On #2 &lt;code&gt;firstThing.config&lt;/code&gt; is &lt;code&gt;Config&lt;/code&gt; as expected.&lt;/p&gt;

&lt;p&gt;
  So in order to help TypeScript detect the correct type we can use a
  &lt;em&gt;type predicate&lt;/em&gt; as the return type.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;interface Config {
  foo: string;
  bar: string;
}

class Thing {
  name: string;
  config?: Config;

  constructor(name: string, config?: Config) {
    this.name = name;
    this.config = config;
  }

  // &#128064; notice the return type
  hasConfig(): this is { config: Config } {
    if (this.config) {
      return true;
    }
    return false;
  }
}

let firstThing = new Thing("Apple", { foo:"one", bar: "two" });

if (firstThing.hasConfig()) {
  // Now firstThing.config is `Config`
  console.log(firstThing.config.foo);
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Now &lt;code&gt;firstThing.config&lt;/code&gt; is &lt;code&gt;Config&lt;/code&gt; as expected. Have in
  mind that even though the type guard says
  &lt;code&gt;this is { config: Config }&lt;/code&gt;, TypeScript will merge this type with
  the original type definition of &lt;code&gt;Thing&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Thanks to the nice folks at the TypeScript Discord who helped me solve this
  issue. Hope this helps more people.
&lt;/p&gt;</description><pubDate>Tue, 09 Mar 2021 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/user-defined-type-guards-in-typescript</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>¿Dónde está el programa?</title><link>https://gcollazo.com/donde-esta-el-programa</link><description>&lt;p&gt;
  ¿Dónde está el programa? Esa fue la pregunta que hice a un programador de
  nuestro equipo. ¿Dónde está el código que describe como funciona nuestra
  aplicación? No pudo contestar y no esperaba que pudiera. En el proyecto que
  estábamos trabajando, la respuesta era bastante complicada. El programa estaba
  en todas partes y en ninguna a la misma vez.
&lt;/p&gt;

&lt;p&gt;
  Cuando comenzamos en este proyecto, lo primero que hicimos fue un prototipo.
  Bueno, el prototipo lo hice yo y sí, era “founder code”. Se escribió en poco
  tiempo y funcionó para validar la idea y para poder movernos adelante a
  conseguir clientes. Nunca me detuve a pensar en arquitectura o si podría
  escalar.
&lt;/p&gt;

&lt;p&gt;
  El único objetivo era que funcionara y que convenciera a clientes del
  potencial del producto. Misión cumplida.
&lt;/p&gt;

&lt;p&gt;
  Cuando le hice la pregunta al programador, al que llamaré Carlos, ya el
  proyecto era viejo en años de software y del “founder code” original quedaba
  muy poco, pero la falta de una arquitectura clara y definida todavía estaba
  presente. Luego de lograr los primeros clientes, en vez de repensar todo,
  optimizamos para velocidad y acumulamos mucha deuda técnica. Poco a poco los
  atajos que tomamos para movernos rápido nos estaban pasando factura.
&lt;/p&gt;

&lt;p&gt;
  Cundo le hice la pregunta a Carlos, ya llevaba unos meses estudiando sobre
  arquitectura de software. Antes de meterme en este tema cuando me hablaban de
  arquitectura pensaba en nuestra infraestructura en AWS. Que servicios estamos
  usando, si usamos Lambda, si estamos haciendo load balancing o estamos usando
  un “queue” y este tipo de cosas, nunca pensaba en la estructura de nuestro
  codebase.
&lt;/p&gt;

&lt;p&gt;
  Antes de esto tenía algunas nociones de arquitectura de software. Cuando
  conocí Ruby on Rail aprendí sobre Model, View, Controller y cuando empecé a
  hacer front end “en serio” usando Backbone.js hablábamos de client side MVC y
  MVVM. Desde entonces todos los proyectos que trabajé usaban algo parecido a
  MVC pero nunca me detuve a pensar por qué y si había algo mejor. Parecía
  funcionar y si todo el mundo lo está usando, creo que estamos ok.
&lt;/p&gt;

&lt;p&gt;
  Luego trabajé mucho con Django y uno de los “best practices” de la comunidad
  era la idea “fat models”. Lo que esto quiere decir es que lo que se puede
  implementar como un método de un modelo, lo debemos implementar como un método
  del modelo. En esos días, estaba muy pendiente a que no tuviéramos lógica
  regada por la aplicación y cuando había dudas sobre donde poner algo decía
  casi automáticamente, “fat models”. Tuve mucho éxito haciendo esto pero
  parecería que no aprendí nada.
&lt;/p&gt;

&lt;p&gt;
  Hoy llevo mas de un año estudiando sobre arquitectura de software. He
  aprendido mucho sobre como pensar acerca de la estructura de un proyecto de
  software y sobre que cosas funcionan en los codebases que trabajo y con los
  equipos que trabajo.
&lt;/p&gt;

&lt;p&gt;
  De todo lo que he estudiado lo más que me ha hecho sentido han sido
  acercamientos que se pueden considerar principalmente “object-oriented” pero
  vale la pena mencionar que lo que aprendí en mis años en el culto de
  “functional programming”, sigue siendo útil y lo uso todos los días. Una de
  las grandes ventajas de JavaScript, el lenguaje que uso principalmente, es que
  es multi paradigma y nos deja mezclar lo mejor de OOP y FP sin problema.
&lt;/p&gt;

&lt;p&gt;
  Cuando aprendí sobre domain-drive design o DDD, me hizo click inmediatamente.
  Todos los problemas que describían los había vivido y las soluciones que
  planteaban, no solo eran mejor para el codebase si no que también ayudaban a
  facilitar la comunicación entre el equipo e incluso los clientes. No voy ni a
  intentar definir DDD, pero es un tema que creo todo el mundo que le interese
  escribir software “mantenible” y “testiable” debe estudiar.
&lt;/p&gt;

&lt;p&gt;
  DDD, resultó ser una especie de puerta a un mundo de patrones de diseño y
  arquitecturas que ponen en el centro el “domain”.
&lt;/p&gt;

&lt;p&gt;
  El “domain” es la capa que contiene el programa. En el domain hay objetos que
  están diseñados para representar todo lo que existe en la aplicación, en el
  domain están todas las acciones que esos objetos pueden hacer y en el domain
  es imposible tener data en un estado inválido. En el domain no hay código
  específico de ningún framework o base de datos o cualquier otra dependencia de
  la infraestructura. Es lógica pura.
&lt;/p&gt;

&lt;p&gt;
  Esta era la respuesta que estaba buscando. De ahí me moví a aprender más sobre
  arquitecturas que usan esta idea de el domain en el centro de todo. Aprendí
  sobre Clean Architecture, Onion Architecture y Hexagonal Architecture o Ports
  and Adapters Architecture que es como prefiero referirme a este diseño. Todas
  estas arquitecturas aportan ideas valiosas y todas ponen el domain en el
  centro.
&lt;/p&gt;

&lt;p&gt;
  Para nuestro equipo llegamos a un diseño inspirado principalmente por Ports
  and Adapters Architecture. La experiencia hasta el momento ha sido positiva.
  Hacer cambios es fácil, es obvio donde están las reglas de negocio, nos obliga
  a ser mucho más explícitos y sentimos que escribimos menos bugs. Además hacer
  pruebas es fácil, rápido y casi divertido.
&lt;/p&gt;

&lt;p&gt;
  Recientemente, trabajando en un proyecto nuevo, después de semanas de haber
  comenzado, decidimos cambiar el web server library de Express.js a Fastify. El
  cambio tomó poco más de un día de trabajo y fue fácil lograr que todo
  funcionara luego del cambio. En el código del domain no cambiamos ni una sola
  linea.
&lt;/p&gt;

&lt;p&gt;
  Estoy seguro que ahora estarás pensando: ¿Pero cada cuánto tiempo se cambia de
  web server library? La repuesta es casi nunca, pero si puedo decir que el
  diseño que nos ayuda a hacer este tipo de cambios fácilmente, también nos
  ayuda de otras formas.
&lt;/p&gt;

&lt;p&gt;
  Por ejemplo, tenemos varias implementaciones de base de datos. Una para la
  base de datos de producción y otra que usamos en el ambiente de pruebas, que
  almacena los datos en memoria. También tenemos implementaciones de queues en
  SQS, Redis y una basada en archivos JSON en el file system. Durante
  development usamos la versión basada en archivos, en staging la versión Redis
  y en producción la de SQS y el código del domain siempre es el mismo. Los
  límites de cada capa y las interfaces entre ellas está claramente definidas y
  esto tiene muchos beneficios.
&lt;/p&gt;

&lt;p&gt;
  Esta experiencia me ha dado muchas herramientas nuevas y ha revivido mi
  entusiasmo por la disciplina de crear software.
&lt;/p&gt;

&lt;p&gt;
  Hoy todavía es difícil contestar la pregunta ¿dónde está el programa? pero
  todos en el equipo sabemos donde debe estar y cada día que pasa nos acercamos
  más a la meta. En algunos meses espero poder responder: “En el centro, el
  programa está en el centro”.
&lt;/p&gt;</description><pubDate>Mon, 15 Mar 2021 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/donde-esta-el-programa</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Bolsas llenas de funciones</title><link>https://gcollazo.com/bolsas-llenas-de-funciones</link><description>&lt;p&gt;
  En este post menciono functional programming (FP) y object-oriented programing
  (OOP) en el contexto de JavaScript (JS). Hablo de mis experiencias con los dos
  paradigmas en un proyecto bastante grande. El título del post
  &lt;strong&gt;no &lt;/strong&gt;es &lt;em&gt;FP vs OOP&lt;/em&gt; y no tengo en interés en ese tipo de
  discusión.
&lt;/p&gt;

&lt;p&gt;
  Las ideas de FP llevan varios años tomando fuerza en el mundo de JS. Mi
  interés en el tema llegó algunos años antes del lanzamiento de React. En eso
  días ya habían librerías y developers muy activos evangelizando sobre FP en JS
  y sus beneficios.
&lt;/p&gt;

&lt;p&gt;
  Aunque había algo de interés antes de que React estuviera en el panorama no es
  hasta el lanzamiento de React y su enorme popularidad que la gran mayoría de
  devs se interesaron en el tema.
&lt;/p&gt;

&lt;p&gt;
  Los vendedores (developer advocates) de React presentaron buenos argumentos
  sobre las ventajas de FP, compartieron ejemplos convincentes y escribieron
  librerías super populares justo en la raíz de lo que luego se convirtió en la
  librería más popular e influyente en el mundo de JS desde jQuery.
&lt;/p&gt;

&lt;p&gt;
  En este momento no se conocían &lt;em&gt;codebases&lt;/em&gt; grades y
  &lt;em&gt;open source&lt;/em&gt; que sirvieran de ejemplo para los millones de developers
  que ahora decidieron escribir a lo FP. Los ejemplos disponibles en muy pocos
  casos pasaban del ya trillado &lt;em&gt;to-do list&lt;/em&gt; o el casi cómico
  &lt;em&gt;counter&lt;/em&gt; que consiste en dos botones que al presionarlos aumentan o
  disminuyen un número. Quedaba como asignación para el o la developer el
  definir e implementar una arquitectura escalable en un paradigma poco
  familiar.
&lt;/p&gt;

&lt;p&gt;
  Más o menos para esta misma época ya había escrito algunos proyectos usando un
  estilo, estrictamente funcional o lo más funcional posible dentro de las
  limitaciones que impone JS. Admito que eran proyectos pequeños con un solo
  developer, pero eran prometedores.
&lt;/p&gt;

&lt;p&gt;
  Organizar todo como &lt;em&gt;pure functions&lt;/em&gt;, data inmutable y evitar side
  effects se sentía limpio. Estos proyectos eran básicamente treinta o cuarenta
  funciones trabajando en conjunto. Las mayoría de las pruebas eran fáciles de
  escribir y solo tenía una o dos funciones que creaban side effects así que
  todo era hermoso.
&lt;/p&gt;

&lt;p&gt;
  Como muchos me deje seducir por la aparente simpleza de este paradigma. Mi
  error fue pensar que esto que funcionaba para proyectos pequeños podría
  funcionar en proyectos mucho más grandes. Estas experiencias me dieron
  confianza en FP JS y me hice de la idea de que se podría usar en proyectos más
  grandes con los mismos resultados.&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
  No funcionó. Cuando traté de llevar lo que había aprendido de FP JS a un
  proyecto mucho más grande, todo lo que se sentía limpio poco a poco se volvió
  complicado y difícil de entender. Tenía muy poca experiencia y no conocía
  suficientes técnicas para manejar esta escala. Teníamos funciones que llaman
  funciones, que llaman a otras funciones y cada una llama más funciones.
&lt;/p&gt;

&lt;p&gt;
  Mientras escribíamos el código no veíamos problemas pero cuando había que leer
  ese código meses más tarde, daba mucho trabajo entenderlo. Los code reviews
  tomaban mucho tiempo o simplemente se volvían muy superficiales. Hubo muchas
  señales de que estábamos haciendo algo mal pero las ignoramos.
&lt;/p&gt;

&lt;p&gt;
  Como buenos programadores de FP, mantuvimos la data separada del
  comportamiento. Nuestro app recibía objetos simples (POJOs), los transformaba
  y producía nuevos objetos simples. Suena fácil, pero cuando tienes decenas de
  funciones, manipulando estos objetos, saber en que estado está la data en todo
  momento puede ser complicado. Muchas veces nos veíamos obligados a usar el
  debugger para poder inspeccionar el estado de un objeto en un lugar
  específico. En otros momentos teníamos que correr y ejecutar la aplicación
  para entender las estructuras de data. El código no era fácil de
  entender.&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
  En este proyecto siempre tuvimos una idea de “modelos” pero, no eran realmente
  modelos. Mas bien eran una capa muy delgada por encima de una pequeña
  abstracción de la base de datos. Nuestros modelos no mantenían estado,
  simplemente nos daban acceso a la base de datos y servían para guardar algunas
  funciones que estaban muy ligadas a la entidad específica.
&lt;/p&gt;

&lt;p&gt;
  Uno de los síntomas más claros de que teníamos un problema es que en el
  proyecto teníamos una carpeta que se llamaba &lt;em&gt;utils&lt;/em&gt; con cientos de
  files y cada file con dos o tres funciones. Muchas de estas funciones
  contenían lógica de negocio y hacían cosas realmente importantes y centrales
  para la aplicación. Estos archivos terminaron en esta carpeta porque eran
  funciones que queríamos reutilizar y no teníamos una idea clara de donde
  ponerlas. Después de todo queríamos tratar de mantener la lógica y la data
  separada y estas funciones eran lógica.
&lt;/p&gt;

&lt;p&gt;
  Más de una vez durante un review, encontramos que alguien había escrito una
  función que ya existía. En este proyecto es difícil encontrar código existente
  para reutilizar. Es fácil imaginar como terminamos con funciones duplicadas
  que hacen las cosas un poco diferente la una de la otra. Demás está decir que
  esto produce bugs.
&lt;/p&gt;

&lt;p&gt;
  En otros casos las funciones estaban muy ligadas a un pedazo de data o a un
  evento de la aplicación, así que esas funciones se escribían en el mismo
  archivo donde se usaban, complicando más aún el encontrar funciones para
  reutilizar y mantener la lógica centralizada.
&lt;/p&gt;

&lt;p&gt;
  La combinación de estas cosas y la falta de experiencia sobre como diseñar un
  proyecto FP de gran escala nos llevó a tener una arquitectura que solo se
  puede describir como &lt;em&gt;una montaña de bolsas llenas de funciones&lt;/em&gt;.
&lt;/p&gt;

&lt;p&gt;
  Estas bolsas de funciones se organizan de muchas formas, alguna veces de
  acuerdo al tipo de acciones que hacen, otras de acuerdo a la data que
  modifican y otras como &lt;em&gt;utils&lt;/em&gt; cuando no sabemos que hacer con ellas.
  Sin duda, haber tenido un mejor esquema de organización pudo haber ayudado
  mucho a reducir la complejidad pero estábamos aprendiendo mientras
  construíamos nuestro programa y no teníamos muchas referencias.
&lt;/p&gt;

&lt;p&gt;
  Nuestro problema con este proyecto no es un problema producido por FP, pero si
  un problema al que es fácil llegar si se tiene poca experiencia con este
  paradigma y si no existen suficientes recursos para aprender y tomar ideas
  sobre como diseñar la arquitectura de tu aplicación. Creo que este es el caso
  de la mayoría de los y las devs que conozco. Les interesan las ideas de FP, se
  ven fáciles de entender y aplicar pero no tenemos idea de como usarlas
  efectivamente en un proyecto de gran escala.
&lt;/p&gt;

&lt;p&gt;
  Pensar que solo con usar funciones puras, tener data inmutable y evitar los
  side effects es suficiente para crear software de cualquier escala es una
  trampa.
&lt;/p&gt;

&lt;p&gt;
  Aprendemos a usar map, reduce y filter y nos creemos genios del FP. Vemos un
  proyecto con dos o tres pantallas escritas con React y solo funciones y
  pensamos que eso puede escalar a un proyecto de cualquier tamaño. Nos decimos
  que si todo es una función pura y fácil de entender, será imposible que se
  complique. Es tan simple que esa es la única abstracción que hace falta.
&lt;/p&gt;

&lt;p&gt;
  Cualquier proyecto de suficiente tamaño se puede salir de control si no se
  tiene una arquitectura clara que nos ayude a organizar el código y a comunicar
  de forma efectiva lo que hace nuestro programa.
&lt;/p&gt;

&lt;p&gt;
  Hoy, sigo usando muchas técnicas que aprendí en mis días de FP pero también
  uso ideas de OOP y me alegra reportar que nadie ha muerto en el proceso. He
  encontrado un &lt;em&gt;punto medio&lt;/em&gt; que usa ideas de ambos paradigmas y que me
  permite crear aplicaciones con una arquitectura clara, fácil de entender, que
  me ayuda a moverme rápido y hacer cambios con tranquilidad.
&lt;/p&gt;

&lt;p&gt;
  En un post futuro hablaré mas en detalle sobre algunas de estas técnicas e
  ideas de arquitectura.
&lt;/p&gt;</description><pubDate>Sun, 11 Apr 2021 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/bolsas-llenas-de-funciones</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Debe ser imposible representar estados ilegales</title><link>https://gcollazo.com/debe-ser-imposible-representar-estados-ilegales</link><description>&lt;p&gt;
  Este es el tercer post en esta serie sobre arquitectura de software. En los
  posts anteriores he hablado de mi experiencia aprendiendo sobre este tema y
  sobre algunos de los problemas que he enfrentado en situaciones donde no hay
  una arquitectura clara.
&lt;/p&gt;

&lt;p&gt;
  En este post espero empezar a introducir algunas ideas y técnicas concretas
  que he usado para comenzar a resolver mis problemas.
&lt;/p&gt;

&lt;h2&gt;Estados ilegales&lt;/h2&gt;

&lt;p&gt;
  Cuando digo &lt;em&gt;“que sea imposible representar estados ilegales”&lt;/em&gt;, lo que
  quiero decir es que nuestra meta debería ser que nuestro programa nunca esté
  en un estado inválido. O sea que sea imposible hacer algo que llevará a
  nuestro programa a estar en un estado inesperado o no definido.
&lt;/p&gt;

&lt;p&gt;
  Eso significa que si nuestro programa es &lt;em&gt;un carrito de compras&lt;/em&gt; y
  tenemos una regla de negocio que permite un máximo de tres artículos en
  carrito, debería ser imposible añadir un cuarto artículo al carrito.
&lt;/p&gt;

&lt;p&gt;
  En el pasado he atentido este tipo de reglas de negocio, poniendo validaciones
  en el perímetro de mi programa. Usualmentemeste trabajo en aplicaciones web,
  así que esto signfica, validación en un controller o algo directamente
  relacionado al &lt;em&gt;endpoint&lt;/em&gt; que recibe el &lt;em&gt;request&lt;/em&gt; del usuario
  para añadir un artículo al carrito. No hay nada malo con esto, solo que si la
  petición de añadir un cuarto artículo al carrito llega de otra parte, nada
  evitará que suceda, poniendo nuestro programa en un estado inválido o ilegal.
  Este podría ser un efecto deseado en algunos casos pero en este ejemplo
  estamos supiniendo que no queremos que esto pase nunca.
&lt;/p&gt;

&lt;p&gt;
  Este ejemplo me ayuda a ilustrar lo que queremos alcanzar, pero este post no
  cubrirá todo lo necesario para lograrlo. Me voy a enfocar en la pieza mas
  pequeña de un sistema que no permite representar estados ilegales.
&lt;/p&gt;

&lt;h2&gt;Value Objects&lt;/h2&gt;

&lt;p&gt;
  No voy a ponerme a definir términos así que puedes mirar
  &lt;a href="https://en.wikipedia.org/wiki/Value_object"&gt;aquí&lt;/a&gt; y
  &lt;a href="https://www.martinfowler.com/bliki/ValueObject.html"&gt;aquí&lt;/a&gt; para
  buenas definiciones. De lo que si voy a hablar es sobre como uso VOs dentro de
  mis programas para aplicar reglas de negocio.
&lt;/p&gt;

&lt;p&gt;
  Lo primero es decidir cuando usar un VO. Mi respuesta es que todo valor dentro
  de tu programa por defecto, debería ser un VO. La ventaja de este acercamiento
  es que te da la oportunidad de establecer reglas para cada valor
  individualmente. El ejemplo obvio de un VO es una cantidad de dinero
  (&lt;code&gt;Amount&lt;/code&gt;).
&lt;/p&gt;

&lt;p&gt;
  Si tenemos un &lt;em&gt;endpoint&lt;/em&gt; en un web app que recibe una cantidad de
  dinero probablemente tendremos un &lt;em&gt;payload&lt;/em&gt; JSON como este.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
 "amount": 100
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Este objeto muestra el &lt;code&gt;amount&lt;/code&gt; con un valor de
  &lt;code&gt;100&lt;/code&gt; centavos o $1.00. Cómo nuestro programa puede manejar varios
  tipos de moneda, rápidamente nos damos cuenta que necesitamos saber la moneda
  de esa cantidad así que añadimos ese campo.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
 "amount": 100,
 "currency": "USD"
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Ok, ahora si tenemos todo lo que necesitamos. Lo próximo que hacemos es
  validar el &lt;em&gt;input&lt;/em&gt; en nuestra aplicación así que podemos escribir el
  siguiente código.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;app.post(async (req, res) =&amp;gt; {
    let {amount, currency} = req.body;

    if (typeof amount !== "number") {
        return res.send(422);
    }

    if (amount &amp;lt; 0 || amount &amp;gt; 1000000) {
        return res.send(422);
    }

    if (typeof currency !== "string") {
        return res.send(422);
    }

    if (!["USD", "EUR"].includes(currency)) {
        return res.send(422);
    }

    try {
        let response = await controller(amount, currency);
        return res.json(response);
    } catch (error) {
        // do some error handling
        return res.send(400);
    }

});&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;async function controller(amount, currency) {
    // do stuff with amount and currency
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Obviamente esto se puede hacer de forma mucho mas declarativa usando algo como
  &lt;a href="https://json-schema.org"&gt;JSON Schema&lt;/a&gt; o cualquier otra librería de
  validación de objetos.
&lt;/p&gt;

&lt;p&gt;
  Pero hacer ese tipo de validación no nos ayuda a recordar que ya hicimos esa
  validación. Las funciones que reciban esto valores no tienen forma de saber
  que ya fueron validados y por lo menos en mi caso esto crea un poco de duda.
  ¿Que tal si no valido el &lt;em&gt;input&lt;/em&gt; en esta funcion y alguien pasa valores
  sin verificar? Esto puede producir &lt;em&gt;bugs&lt;/em&gt; e incluso problemas de
  seguridad. Este problema se puede resolver con VOs.
&lt;/p&gt;

&lt;p&gt;
  De este punto en adelante voy a usar
  &lt;a href="https://www.typescriptlang.org"&gt;TypeScript&lt;/a&gt; para los ejemplos. El
  sistema de &lt;em&gt;types&lt;/em&gt; de TS nos va a ayudar a garantizar el contenido de
  nuestro VOs. Primero vamos a definir este VO Amount.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Amount {
    readonly value: number;
    readonly currency: string;

    constructor(value: number, currency: string) {
        this.value = value;
        this.currency = currency;
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Este es el primer paso. Si te das cuenta definí una clase con ambos valores.
  Esto es una decisión de diseño importante. Como sabemos estos dos valores
  están completamente conectados. No se puede cambiar uno, sin que el otro
  cambie. $1 USD y 1€ EUR no son lo mismo. Así que estos dos valores en realidad
  son uno solo. Esto es lo primero que añade este VO a nuestro programa. Nos
  está dejando representar dos datos como un solo objeto conectándolos para
  siempre y ayudándonos a recordar que no podemos cambiar uno sin pensar como se
  afecta el otro. Sigamos.
&lt;/p&gt;

&lt;p&gt;
  Ahora el problema que veo es que si creo una instancia de esta clase no hay
  nada impidiendo que ponga valores que no puedo aceptar en mi programa. Por
  ejemplo si estamos haciendo un carrito de compras, ningún producto puede tener
  como precio un número negativo pero este VO nos permite pasar un número
  negativo. Como esta, hay otras reglas de negocio, que podemos implementar en
  este objeto para asegurarnos de que siempre que lo usemos, estas reglas sean
  aplicadas y si alguien o algo trata de romper estas reglas nuestro programa no
  lo permita.
&lt;/p&gt;

&lt;p&gt;
  A mi me gusta poner estas reglas en el &lt;code&gt;constructor&lt;/code&gt; y si alguna de
  estas reglas no se cumple, lo que hago es &lt;code&gt;throw&lt;/code&gt; una excepción
  para impedir la creación de la instancia. Esto hace imposible que exista una
  instancia que no cumpla con las reglas que establecimos.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Amount {
    readonly value: number;
    readonly currency: string;

    constructor(value: number, currency: string) {
        // Check value
        if (typeof value !== "number") {
            throw new Error("Amount value must be a number");
        }

        if (value &amp;lt; 1 || value &amp;gt; 1000000) {
            throw new Error("Amount value must be between 1 and 1,000,000");
        }

        // Check currency
        if (typeof currency !== "string") {
            throw new Error("Amount currency must be a string");
        }

        if (currency.length !== 3) {
            throw new Error("Amount currency must be a valid ISO-4217 alpha code")
        }

        if (!["USD", "EUR"].includes(currency)) {
            throw new Error("Amount currency can only be USD or EUR");
        }

        this.value = value;
        this.currency = currency;
    }

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

&lt;p&gt;
  Ok, ahora este VO es bastante más estricto, verifica los tipos de los
  argumentos que recibe y también se asegura que este objeto sigue nuestras
  reglas de negocio. Como hace &lt;code&gt;throw&lt;/code&gt; de una excepción si algo está
  mal, no es posible crear una instancia en un estado ilegal.
&lt;/p&gt;

&lt;p&gt;
  Por otro lado, si tenemos una instancia de este objeto, sabemos que tiene que
  ser válida porque logró pasar por todas las pruebas (guards) que establecimos.
  Así que una vez tenemos una instancia de este objeto podemos estar seguros que
  es válida y podemos confiar en lo que contiene. No tenemos que volver a
  verificar nada porque sabemos todo.
&lt;/p&gt;

&lt;p&gt;
  Ahora donde quiera que necesitemos este valor en vez de pasar un
  &lt;code&gt;number&lt;/code&gt; y un &lt;code&gt;string&lt;/code&gt; podemos pasar un
  &lt;code&gt;Amount&lt;/code&gt; y estar confiados.
&lt;/p&gt;

&lt;p&gt;
  Se que esto puede parece increíblemente obvio para algunos, especialmente los
  que están acostumbrados a trabajar en lenguajes &lt;em&gt;strongly typed&lt;/em&gt; pero
  les puedo asegurar que para los que llevamos toda la vida en lenguajes
  &lt;em&gt;dynamically typed&lt;/em&gt; esto es una revelación.
&lt;/p&gt;

&lt;p&gt;
  Que más podemos hacer con un VO. Pues habrás notado declare los fields de
  &lt;code&gt;Amount&lt;/code&gt; como &lt;code&gt;readonly&lt;/code&gt;. Una de las caracteristicas más
  importantes de un VO es que sea &lt;em&gt;inmutable&lt;/em&gt; y que una vez creado, no se
  pueda cambiar. Esto nos da muchas garantías sobre el estado interno de nuestro
  objeto.
&lt;/p&gt;

&lt;p&gt;
  Una acción que seguro vamos a necesitar hacer con nuestro
  &lt;code&gt;Amount&lt;/code&gt; es sumarle otro &lt;code&gt;Amount&lt;/code&gt; pero como dije antes
  no podemos modificar un VO una vez creado. Así que lo que podemos hacer es
  crear un método que recibe el &lt;code&gt;Amount&lt;/code&gt; que queremos sumar y crea un
  VO nuevo que contiene el resultado.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;add(amount: Amount): Amount {
    if (amount.currency !== this.currency) {
        throw new Error("Amounts of different currency cannot be added");
    }

    return new Amount(amount.value + this.value, this.currency);
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Nuevamente aquí podemos ver que estamos validando que ambos VOs son del mismo
  &lt;code&gt;currency&lt;/code&gt; antes de sumarlos. Una vez sabemos que son del mismo
  &lt;code&gt;currency&lt;/code&gt; creamos una nueva instancia de &lt;code&gt;Amount&lt;/code&gt; con
  el resultado de la suma.
&lt;/p&gt;

&lt;p&gt;
  Otra cosa importante es poder comparar dos VOs por su valor así que vamos a
  escribir un método para esto.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;equals(amount: Amount): boolean {
    return amount.value === this.value &amp;amp;&amp;amp; amount.currency === this.currency;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Fácil. Ahora podemos comparar dos VOs y saber si contienen el mismo valor. Es
  importante notar que estamos comparando tanto el &lt;code&gt;value&lt;/code&gt; como el
  &lt;code&gt;currency&lt;/code&gt; porque sabemos bien que un Euro y un Dólar no son lo
  mismo.
&lt;/p&gt;

&lt;h2&gt;VOs en mi web app&lt;/h2&gt;

&lt;p&gt;
  Creo que esos son suficientes ejemplos simples. Ahora un último ejemplo de
  como se puede usar este VO en el contexto de nuestra aplicación web.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;app.post(async (req, res) =&amp;gt; {
    let {amount, currency} = req.body;

    try {
        let amount = new Amount(amount, currency);
        let response = await controller(amount);
        return res.json(response);
    } catch(error) {
        // do some error handling
        return res.send(400);
    }
});&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;async function controller(amount: Amount) {
    // do stuff with Amount
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Ahí está. Ahora &lt;code&gt;Amount&lt;/code&gt; nos ayuda a validar el input que estamos
  recibiendo y luego de que tenemos una instancia sabemos que es un valor que no
  necesitamos validar nuevamente. El resto de nuestro programa, siempre y cuando
  reciba una instancia de &lt;code&gt;Amount&lt;/code&gt;, puede trabajar con el valor de
  forma segura y sin preocupación.
&lt;/p&gt;

&lt;p&gt;
  Además este VO nos ayuda a organizar el código y mantener las reglas de
  negocio cerca del los datos que las necesitan. La clase
  &lt;code&gt;Amount&lt;/code&gt; tiene todo el código necesario para crear una instancia
  válida y las acciones (método) que podemos tomar con ese objeto.
&lt;/p&gt;

&lt;h2&gt;Result&lt;/h2&gt;

&lt;p&gt;
  Como notaste, cada vez que queremos crear un VO, necesitamos hacer
  &lt;code&gt;new&lt;/code&gt; y pasar los argumentos que hagan falta para crear la
  instancia. Como nuestro &lt;code&gt;constructor&lt;/code&gt; puede
  &lt;code&gt;throw&lt;/code&gt; siempre tenemos que asegurarnos de hacer
  &lt;code&gt;try/catch&lt;/code&gt; para manejar cualquier error al momento de la
  instanciación.
&lt;/p&gt;

&lt;p&gt;
  Aunque esto no es el fin del mundo, bien rápido se puede hacer algo tedioso.
  Dicho eso, hay formas de mejorar la experiencia al usar VOs.
&lt;/p&gt;

&lt;p&gt;
  Todos mis VOs tienen un método estático llamado &lt;code&gt;create&lt;/code&gt;. Este
  método me permite instanciar VOs sin temor a una excepción pero en vez de
  devolver una instancia de &lt;code&gt;Amount&lt;/code&gt;, devulve una instancia de un
  objeto llamado &lt;code&gt;Result&lt;/code&gt; que sirve para envolver el
  &lt;code&gt;Amount&lt;/code&gt; que se creo o un &lt;code&gt;Error&lt;/code&gt; si hubo problemas al
  crear el VO.
&lt;/p&gt;

&lt;p&gt;
  Esta idea de usar un objecto para envolver el resultado es algo muy común en
  otros lengajes como
  &lt;a href="https://doc.rust-lang.org/std/result/enum.Result.html"&gt;Rust&lt;/a&gt; y nos
  permite hacer cosas interesantes. Los detalles sobre &lt;code&gt;Result&lt;/code&gt; los
  voy a dejar para un post futuro donde también hablare sobre como uso
  &lt;code&gt;Maybe&lt;/code&gt; en mis programas.
&lt;/p&gt;

&lt;h2&gt;Resumen&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    Los VOs nos ayudan a implementar reglas de negocio para un valor dentro de
    nuestro programa.
  &lt;/li&gt;
  &lt;li&gt;Los VOs deben ser inmutables.&lt;/li&gt;
  &lt;li&gt;Debe ser imposible instanciar un VO en un estado ilegal.&lt;/li&gt;
  &lt;li&gt;
    Una vez tenemos una instancia de VO podemos estar seguros que contiene un
    estado válido dentro de nuestro programa.
  &lt;/li&gt;
&lt;/ul&gt;</description><pubDate>Sun, 16 May 2021 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/debe-ser-imposible-representar-estados-ilegales</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Result y Maybe</title><link>https://gcollazo.com/result-y-maybe</link><description>&lt;p&gt;
  En el post anterior mencioné brevemente que mis
  &lt;a
    href="https://gcollazo.com/debe-ser-imposible-representar-estados-ilegales/"
    &gt;value objects&lt;/a
  &gt;
  implementan un método estático &lt;code&gt;create&lt;/code&gt; que utilizo para crear
  instancias sin temor a excepciones. Este método devuelve un objeto llamado
  &lt;code&gt;Result&lt;/code&gt; que sirve para envolver el resultado de llamar al método
  &lt;code&gt;create&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  En este post vamos a ver cómo funciona &lt;code&gt;Result&lt;/code&gt; y al final vamos a
  hablar un poco de &lt;code&gt;Maybe&lt;/code&gt; que es muy similar.
&lt;/p&gt;

&lt;h2&gt;Create&lt;/h2&gt;

&lt;p&gt;Vamos directo al código, así implemento &lt;code&gt;create&lt;/code&gt; en mis VOs.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// amount.ts
static create(value: number, currency: string): Result&amp;lt;Amount, Error&amp;gt; {
    try {
        return ok(new Amount(value, currency));
    } catch(error) {
        return err(error);
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Esté método estático se puede llamar escribiendo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Amount.create(value, currency)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  La única diferencia es que en vez de devolver una instancia de
  &lt;code&gt;Amount&lt;/code&gt; como haría:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;new Amount(value, currency)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Este método devuelve un &lt;code&gt;Result&lt;/code&gt; que podría ser un
  &lt;code&gt;Ok&lt;/code&gt; o un &lt;code&gt;Err&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Veamos el contenido de &lt;code&gt;create&lt;/code&gt; en detalle. En el
  &lt;code&gt;try&lt;/code&gt; intento crear una instancia de &lt;code&gt;Amount&lt;/code&gt;. Si no hay
  problema utilizo la función &lt;code&gt;ok&lt;/code&gt; para crear una instancia de
  &lt;code&gt;Ok&lt;/code&gt; que envuelva el &lt;code&gt;Amount&lt;/code&gt; que acabo de crear.
&lt;/p&gt;

&lt;p&gt;
  En el &lt;code&gt;catch&lt;/code&gt; atrapo el error si es que el
  &lt;code&gt;constructor&lt;/code&gt; de &lt;code&gt;Amount&lt;/code&gt; lanza una excepción. Sabemos
  que las excepciones que lanza el &lt;code&gt;constructor&lt;/code&gt; son de tipo
  &lt;code&gt;Error&lt;/code&gt;. Si este es el caso uso la función &lt;code&gt;err&lt;/code&gt; para
  crear una instancia de &lt;code&gt;Err&lt;/code&gt; que envuelva nuestro error.
&lt;/p&gt;

&lt;p&gt;
  Tanto &lt;code&gt;Ok&lt;/code&gt; como &lt;code&gt;Err&lt;/code&gt; son &lt;code&gt;Result&lt;/code&gt;. Por lo
  que el tipo que devuelve este método estático siempre es &lt;code&gt;Result&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Ahora vamos a mirar &lt;code&gt;Result&lt;/code&gt; en detalle para entender mejor. No te
  preocupes si en este punto no entiendes todo, he omitido algunos detalles que
  iré revelando según vamos avanzando.
&lt;/p&gt;

&lt;h2&gt;Result&lt;/h2&gt;

&lt;p&gt;
  Un &lt;code&gt;Result&lt;/code&gt; es un objeto que puede contener solo uno de dos tipos.
  &lt;code&gt;Ok&lt;/code&gt; o &lt;code&gt;Err&lt;/code&gt;. El primero lo usamos para envolver
  resultados positivos o sin problemas y el segundo lo usamos para envolver
  errores.
&lt;/p&gt;

&lt;p&gt;
  La idea aquí es que en vez de lanzar una excepción cuando instanciamos un VO,
  devolvemos un valor que podemos inspeccionar e incluso pasar a otras partes
  del código sin preocupación de excepciones.
&lt;/p&gt;

&lt;p&gt;Vamos a ver como se implementa &lt;code&gt;Result&lt;/code&gt; en TypeScript.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// result.ts
export type Result&amp;lt;T, E&amp;gt; = Ok&amp;lt;T, E&amp;gt; | Err&amp;lt;T, E&amp;gt;;

export class Ok&amp;lt;T, E&amp;gt; {
  constructor(readonly value: T) {
    this.value = value;
  }

  isOk(): this is Ok&amp;lt;T, E&amp;gt; {
    return true;
  }

  isErr(): this is Err&amp;lt;T, E&amp;gt; {
    return !this.isOk();
  }

  unwrap(): T {
    return this.value;
  }
}

export class Err&amp;lt;T, E&amp;gt; {
  constructor(readonly error: E) {
        this.error =  error;
    }

  isOk(): this is Ok&amp;lt;T, E&amp;gt; {
    return false;
  }

  isErr(): this is Err&amp;lt;T, E&amp;gt; {
    return !this.isOk();
  }

  unwrap(): T {
    throw new Error("Called `unwrap` on an Err");
  }
}

// Utility functions
export function ok&amp;lt;T, E&amp;gt;(value: T): Ok&amp;lt;T, E&amp;gt; {
  return new Ok(value);
}

export function err&amp;lt;T, E&amp;gt;(err: E): Err&amp;lt;T, E&amp;gt; {
  return new Err(err);
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Vamos a romper este archivo en pedazos para entender todo lo que está pasando.
  Empecemos con &lt;code&gt;Ok&lt;/code&gt;.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Ok&amp;lt;T, E&amp;gt; {
  constructor(readonly value: T) {
    this.value = value;
  }

  isOk(): this is Ok&amp;lt;T, E&amp;gt; {
    return true;
  }

  isErr(): this is Err&amp;lt;T, E&amp;gt; {
    return !this.isOk();
  }

  unwrap(): T {
    return this.value;
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Como ves el &lt;code&gt;constructor&lt;/code&gt; recibe un valor y lo guarda en el campo
  &lt;code&gt;value&lt;/code&gt; de la clase. Además tiene algunos métodos simples como
  &lt;code&gt;isOk&lt;/code&gt; que siempre responde &lt;code&gt;true&lt;/code&gt; porque todas las
  intancias de la clase &lt;code&gt;Ok&lt;/code&gt; están ok. También tiene lo opuesto
  &lt;code&gt;isErr&lt;/code&gt; que obviamente hace lo contrario que &lt;code&gt;isOk&lt;/code&gt;.
  Estos métodos los usamos para verificar que contiene un &lt;code&gt;Result&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Finalmente tenemos el método &lt;code&gt;unwrap&lt;/code&gt; que se encarga de sacar el
  valor de dentro de nuestro &lt;code&gt;Ok&lt;/code&gt;. Este método lo usamos para sacar
  el valor que nos interesa de dentro de un &lt;code&gt;Result&lt;/code&gt; que sabemos está
  &lt;code&gt;Ok&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;Ahora veamos la clase &lt;code&gt;Err&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Err&amp;lt;T, E&amp;gt; {
  constructor(readonly error: E) {
        this.error = error;
    }

  isOk(): this is Ok&amp;lt;T, E&amp;gt; {
    return false;
  }

  isErr(): this is Err&amp;lt;T, E&amp;gt; {
    return !this.isOk();
  }

  unwrap(): T {
    throw new Error("Called `unwrap` on an Err");
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  &lt;code&gt;Err&lt;/code&gt; es muy similar a &lt;code&gt;Ok&lt;/code&gt;. En este caso el
  &lt;code&gt;constructor&lt;/code&gt; recibe un error como único valor. Tenemos los mismos
  métodos que en &lt;code&gt;Ok&lt;/code&gt; porque queremos que estas dos clases tengan la
  misma interface.
&lt;/p&gt;

&lt;p&gt;
  Algo interesante es que el método &lt;code&gt;unwrap&lt;/code&gt; de &lt;code&gt;Err&lt;/code&gt; hace
  &lt;code&gt;throw&lt;/code&gt; de una excepción si se llama. Esto es porque cuando tenemos
  un error no tenemos un valor que podamos sacar así que no hay otra cosa que
  podamos hacer.
&lt;/p&gt;

&lt;p&gt;
  Para evitar caer en esta situación siempre tenemos que usar los métodos
  &lt;code&gt;isOk&lt;/code&gt; o &lt;code&gt;isErr&lt;/code&gt; para verificar que no vamos a tener
  problema cuándo llamemos &lt;code&gt;unwrap&lt;/code&gt;.
&lt;/p&gt;

&lt;h3&gt;Generics&lt;/h3&gt;

&lt;p&gt;
  Lamentablemente este objeto &lt;code&gt;Result&lt;/code&gt; tienen que hacer uso de un
  &lt;em&gt;feature&lt;/em&gt; avanzado de TS llamado
  &lt;a href="https://www.typescriptlang.org/docs/handbook/2/generics.html"
    &gt;generics&lt;/a
  &gt;
  pero te puedo jurar que vale la pena entender que está pasando.
&lt;/p&gt;

&lt;p&gt;
  Tanto &lt;code&gt;Ok&lt;/code&gt; como &lt;code&gt;Err&lt;/code&gt; tienen unos
  &lt;code&gt;&amp;lt;T, E&amp;gt;&lt;/code&gt; al lado del nombre de la clase. Esto lo que hace es
  que le dice a TS que cuando instanciemos estas clases queremos poder decidir
  que &lt;code&gt;type&lt;/code&gt; van a tener los valores que vamos a envolver más
  adelante. En resumen es una forma de reutilizar estas clases sin tener que
  saber los &lt;em&gt;types&lt;/em&gt; de antemano.
&lt;/p&gt;

&lt;p&gt;Aquí un ejemplo.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;new Ok&amp;lt;number, Error&amp;gt;(1);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Acabo de crear una instacia de &lt;code&gt;Ok&lt;/code&gt; que puede aceptar un
  &lt;code&gt;number&lt;/code&gt; o un &lt;code&gt;Error&lt;/code&gt; aunque la clase &lt;code&gt;Ok&lt;/code&gt; no
  usa el tipo &lt;code&gt;Error&lt;/code&gt;. Esto va hacer sentido más tarde.
&lt;/p&gt;

&lt;p&gt;Otro ejemplo.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;new Err&amp;lt;number, Error&amp;gt;(new Error('Something wrong'));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Aquí cree un &lt;code&gt;Err&lt;/code&gt; y nuevamente pasé &lt;code&gt;number&lt;/code&gt; y
  &lt;code&gt;Error&lt;/code&gt; como &lt;em&gt;type variables&lt;/em&gt; a la clase para que TS sepa
  los types que puede aceptar.
&lt;/p&gt;

&lt;h3&gt;Result type alias&lt;/h3&gt;

&lt;p&gt;
  Si te fijas el objeto &lt;code&gt;Result&lt;/code&gt; no es una clase, en realidad es un
  &lt;em&gt;type alias&lt;/em&gt;. Aquí está la definición.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;type Result&amp;lt;T, E&amp;gt; = Ok&amp;lt;T, E&amp;gt; | Err&amp;lt;T, E&amp;gt;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Voy a tratar de traducir esto. &lt;code&gt;Result&lt;/code&gt; de &lt;code&gt;T&lt;/code&gt; y
  &lt;code&gt;E&lt;/code&gt; es igual a &lt;code&gt;Ok&lt;/code&gt; de &lt;code&gt;T&lt;/code&gt; y &lt;code&gt;E&lt;/code&gt; o
  &lt;code&gt;Err&lt;/code&gt; de &lt;code&gt;T&lt;/code&gt; y &lt;code&gt;E&lt;/code&gt;. Por esto es que ambos
  &lt;code&gt;Ok&lt;/code&gt; y &lt;code&gt;Err&lt;/code&gt; aceptan dos &lt;em&gt;type variables&lt;/em&gt; aunque
  cada uno usa solo uno. &lt;code&gt;Result&lt;/code&gt; puede ser cualquiera de estos dos
  tipos. O sea, un &lt;code&gt;Ok&lt;/code&gt; que envuelve un valor de tipo
  &lt;code&gt;T&lt;/code&gt; o un &lt;code&gt;Err&lt;/code&gt; que envuelve un error de tipo
  &lt;code&gt;E&lt;/code&gt;.
&lt;/p&gt;

&lt;h3&gt;Utility functions&lt;/h3&gt;

&lt;p&gt;
  Las últimas piezas de este rompecabezas son las funciones &lt;code&gt;ok&lt;/code&gt; y
  &lt;code&gt;err&lt;/code&gt; que están al final del archivo. Nota que son en minúscula a
  diferencia de las clases &lt;code&gt;Ok&lt;/code&gt; y &lt;code&gt;Err&lt;/code&gt; que usan pascal
  case.
&lt;/p&gt;

&lt;p&gt;
  Estas funciones son con lo que interactuamos la mayoría de las veces. En el
  ejemplo del método &lt;code&gt;create&lt;/code&gt; al principio de este post se puede ver
  como las usamos.
&lt;/p&gt;

&lt;h2&gt;Usando Result&lt;/h2&gt;

&lt;p&gt;Regresemos al método &lt;code&gt;create&lt;/code&gt; del principio.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;static create(value: number, currency: string): Result&amp;lt;Amount, Error&amp;gt; {
    try {
        return ok(new Amount(value, currency));
    } catch(error) {
        return err(error);
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Cómo se puede ver aquí este método devulve un objeto &lt;code&gt;Result&lt;/code&gt; de
  &lt;code&gt;Amount&lt;/code&gt; y &lt;code&gt;Error&lt;/code&gt;. En otras palabras este método puede
  devolver una instancia de &lt;code&gt;Ok&lt;/code&gt; que contendrá un valor de tipo
  &lt;code&gt;Amount&lt;/code&gt; o una instancia de &lt;code&gt;Err&lt;/code&gt; que contendrá un error
  de tipo &lt;code&gt;Error&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Vuelve a leer esa dos oraciones anteriores. Creo que es importante llamar la
  atención sobre toda la información que estamos “codificando” dentro del
  &lt;em&gt;type system&lt;/em&gt;. Estamos poniendo los &lt;em&gt;types&lt;/em&gt; a ayudarnos a
  programar y detectar problemas en nuestro programa.
&lt;/p&gt;

&lt;p&gt;
  Sobre las funciones &lt;code&gt;ok&lt;/code&gt; y &lt;code&gt;err&lt;/code&gt; no creo que tenga que
  decir mucho. Simplemente se encargan de crear una instancia de un objeto
  &lt;code&gt;Ok&lt;/code&gt; o &lt;code&gt;Err&lt;/code&gt;. Las usamos cuando estamos implementando
  algún método que queramos devuelva un &lt;code&gt;Result&lt;/code&gt;. Siempre usamos
  &lt;code&gt;ok&lt;/code&gt; cuando la operación tiene éxito y usamos
  &lt;code&gt;err&lt;/code&gt; cuando algo falla.
&lt;/p&gt;

&lt;p&gt;
  Veamos cómo se ve cuando usamos el método &lt;code&gt;create&lt;/code&gt; ahora que
  entendemos mejor como se comporta &lt;code&gt;Result&lt;/code&gt;.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;let priceOrErr = Amount.create(1, "USD");

if (priceOrErr.isErr()) {
    console.error(priceOrError.error);
    return res.send(422);
}

let price = priceOrErr.unwrap();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  En la primera linea &lt;code&gt;priceOrErr&lt;/code&gt; es una instancia de
  &lt;code&gt;Result&amp;lt;Amount, Error&amp;gt;&lt;/code&gt;, así que puede ser un
  &lt;code&gt;Ok&lt;/code&gt; o un &lt;code&gt;Err&lt;/code&gt; y no tenemos forma de saber. En la
  próxima linea hacemos el check con &lt;code&gt;isErr&lt;/code&gt;, si es cierto sabemos
  que el contenido es un error. Pero si logramos pasar ese &lt;em&gt;check&lt;/em&gt;, en
  las lineas subsiguientes sabemos que &lt;code&gt;priceOrErr&lt;/code&gt; tiene que ser un
  &lt;code&gt;Ok&lt;/code&gt; y podemos hacer &lt;code&gt;unwrap&lt;/code&gt; sin tenemor a ninguna
  exepción.
&lt;/p&gt;

&lt;p&gt;
  Lo bueno de esto es que no solo nosotros sabemos que en este punto
  &lt;code&gt;priceOrErr&lt;/code&gt; es un &lt;code&gt;Ok&lt;/code&gt;, también TS lo sabe y nos ayuda
  con los completions. Además como le dimos mucha información a TS, el
  &lt;em&gt;type system&lt;/em&gt; sabe que es un &lt;code&gt;Ok&lt;/code&gt; de &lt;code&gt;Amount&lt;/code&gt;.
  Por lo que cuando hacemos &lt;code&gt;unrap&lt;/code&gt; TS sabe que lo que devuelve es un
  &lt;code&gt;Amount&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  De la misma forma dentro del &lt;code&gt;if&lt;/code&gt; TS sabe que
  &lt;code&gt;priceOrErr&lt;/code&gt; es una instancia de &lt;code&gt;Err&lt;/code&gt; y que contiene un
  valor de tipo &lt;code&gt;Error&lt;/code&gt;. Gracias TypeScript.
&lt;/p&gt;

&lt;h2&gt;Result en todas partes&lt;/h2&gt;

&lt;p&gt;
  Espero que ya veas el valor que puede añadir usar &lt;code&gt;Result&lt;/code&gt; en tu
  programa.
&lt;/p&gt;

&lt;p&gt;
  El uso de &lt;code&gt;Result&lt;/code&gt; no tiene que limitarse a la creación de VOs.
  Puedes usar un &lt;code&gt;Result&lt;/code&gt; en cualquier parte de tu programa donde
  realizas una operación que puede fallar. O sea en muchas partes.
&lt;/p&gt;

&lt;p&gt;
  Por ejemplo cuando hacemos queries a la base de datos, en vez de devolver la
  data directamente, envolvemos el resultado en un &lt;code&gt;Result&lt;/code&gt;. De esta
  forma podemos verificar si está &lt;code&gt;Ok&lt;/code&gt; antes de continuar.
&lt;/p&gt;

&lt;p&gt;
  &lt;code&gt;Result&lt;/code&gt; nos permite usar el &lt;em&gt;type system&lt;/em&gt; para “codificar”
  información importante sobre el programa.
&lt;/p&gt;

&lt;h2&gt;Combine&lt;/h2&gt;

&lt;p&gt;
  Además de las funciones &lt;code&gt;ok&lt;/code&gt; y &lt;code&gt;err&lt;/code&gt; uso una función
  adicional que me ayuda a verificar varios &lt;code&gt;Result&lt;/code&gt; en un solo paso.
  A esta funcion le llamo &lt;code&gt;combine&lt;/code&gt; porque combina todos los
  &lt;code&gt;Result&lt;/code&gt; en un solo &lt;code&gt;Result&lt;/code&gt; que me dice si todos los
  &lt;code&gt;Result&lt;/code&gt; están &lt;code&gt;Ok&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  &lt;code&gt;combine&lt;/code&gt; recibe un &lt;code&gt;Array&lt;/code&gt; de &lt;code&gt;Result&lt;/code&gt;,
  itera por ellos y si alguno es un &lt;code&gt;Err&lt;/code&gt; devuelve un
  &lt;code&gt;Err&lt;/code&gt;, si todos son &lt;code&gt;Ok&lt;/code&gt;, devuelve un &lt;code&gt;Ok&lt;/code&gt;.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function combine(
  results: Result&amp;lt;unknown, Error&amp;gt;[]
): Result&amp;lt;unknown, Error&amp;gt; {
  for (let result of results) {
    if (result.isErr()) {
      return result;
    }
  }
  return ok(results);
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Así usamos &lt;code&gt;combine&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;let priceOrErr = Amount.create(100, "USD");
let discountOrErr = Amount.create(1, "USD");
let taxOrErr = Amount.create(1, "USD");

let validationOrErr = combine([
    priceOrErr,
    discountOrErr,
    taxOrErr
]);

if (validationOrErr.isErr()) {
    console.error(validationOrErr.error);
    return res.send(422);
}

let price = priceOrErr.unwrap();
let discount = discountOrErr.unwrap();
let tax = taxOrErr.unwrap();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Una vez pasamos el &lt;code&gt;if&lt;/code&gt; sabemos que todos los valores son
  &lt;code&gt;Ok&lt;/code&gt; y podemos &lt;code&gt;unwrap&lt;/code&gt; sin problema.
&lt;/p&gt;

&lt;h2&gt;Extendiendo Result&lt;/h2&gt;

&lt;p&gt;
  No hay ninguna regla que diga que &lt;code&gt;Result&lt;/code&gt; no puede tener más
  métodos. Incluso si miramos la documentación de
  &lt;a href="https://doc.rust-lang.org/std/result/enum.Result.html"&gt;Rust&lt;/a&gt; para
  su tipo &lt;code&gt;Result&lt;/code&gt; no damos cuenta que implementa algunos método
  interesantes como &lt;code&gt;unwrap_or_else&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Ese método lo podemos implementar en nuestro &lt;code&gt;Result&lt;/code&gt; para poder
  sacar nuestro valor o un valor default si resulta que nuestro
  &lt;code&gt;Result&lt;/code&gt; es un &lt;code&gt;Err&lt;/code&gt;.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;let defaultAmount = new Amount(0, "USD");

let priceOrErr = Amount.create(req.body.price, "USD");
let price = priceOrErr.unwrapOr(defaultAmount);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  De esta forma si el &lt;code&gt;Result&lt;/code&gt; de &lt;code&gt;priceOrErr&lt;/code&gt; es
  &lt;code&gt;Err&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt; será el valor de
  &lt;code&gt;defaultAmount&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Hay muchas cosas interesantes que se pueden hacer con &lt;code&gt;Result&lt;/code&gt;, por
  ejemplo podemos añadir un método &lt;code&gt;map&lt;/code&gt; que aplique una funcion al
  valor que contenien el &lt;code&gt;Result&lt;/code&gt; y devuelva un nuevo
  &lt;code&gt;Result&lt;/code&gt; con el valor modificado. Este &lt;code&gt;map&lt;/code&gt; debe ser lo
  suficientemente inteligente como para no devolver un error si resulta que el
  &lt;code&gt;Result&lt;/code&gt; lo que contiene es un &lt;code&gt;Err&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Con este método de &lt;code&gt;map&lt;/code&gt; se pueden hacer muchas cosas interesantes,
  especialmente hacer cadenas de método que todos devueven un
  &lt;code&gt;Result&lt;/code&gt; y todos esperan como &lt;em&gt;input&lt;/em&gt; un
  &lt;code&gt;Result&lt;/code&gt;. Pero de esto podemos hablar otro día.
&lt;/p&gt;

&lt;h2&gt;Maybe&lt;/h2&gt;

&lt;p&gt;
  Finalmente hablaré brevemente del objeto &lt;code&gt;Maybe&lt;/code&gt;. Este objeto es
  muy similar a &lt;code&gt;Result&lt;/code&gt;. Usamos &lt;code&gt;Maybe&lt;/code&gt; como valor para
  operaciones que pueden devolver o resultado o no.
&lt;/p&gt;

&lt;p&gt;
  Por ejemplo imagina que tienes una funcion &lt;code&gt;notJuan&lt;/code&gt; que recibe una
  &lt;code&gt;Array&lt;/code&gt; de nombres de personas y devuelva un &lt;code&gt;Array&lt;/code&gt; de
  nombres que no son Juan. Esta función podría devolver un &lt;code&gt;Array&lt;/code&gt; de
  resultado pero también podría devovler un &lt;code&gt;Array&lt;/code&gt; vació, o sea
  ningún resultado.
&lt;/p&gt;

&lt;p&gt;
  No tengo nada en contra de los Juanes, simplemente este fue el mejor ejemplo
  que se me ocurrió.
&lt;/p&gt;

&lt;p&gt;
  Esta función la podemos implementar usando &lt;code&gt;Maybe&lt;/code&gt; como el
  &lt;em&gt;return type&lt;/em&gt;.
&lt;/p&gt;

&lt;p&gt;
  Un &lt;code&gt;Maybe&lt;/code&gt; puede devolver un &lt;code&gt;Some&lt;/code&gt; o un
  &lt;code&gt;None&lt;/code&gt;. Devuelve &lt;code&gt;Some&lt;/code&gt; cuando tenemos un valor que
  devolver y &lt;code&gt;None&lt;/code&gt; cuando no tenemos un valor que devoler. Similar a
  &lt;code&gt;Result&lt;/code&gt; tenemos funciones &lt;code&gt;some&lt;/code&gt; y
  &lt;code&gt;none&lt;/code&gt; para crear &lt;code&gt;Maybe&lt;/code&gt; fácilmente.
&lt;/p&gt;

&lt;p&gt;Así implementaríamos la función &lt;code&gt;notJuan&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function notJuan(names: string[]): Maybe&amp;lt;string&amp;gt; {
    let validNames = names.filter((n) =&amp;gt;  n !== "juan");

    if (validNames.length === 0) {
        return none();
    }

    return some(validNames);
}

let friends = ["María", "Luis", "Juan"];

let filteredFriends = notJuan(friends);

if (filteredFriends.isNone()) {
    return res.send(400);
}

let friendsExceptJuan = filteredFriends.unwrap();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  En la última linea sabemos que &lt;code&gt;friendsExceptJuan&lt;/code&gt; contiene algunos
  nombres y que ninguno es “Juan”.
&lt;/p&gt;

&lt;h2&gt;Result y Maybe&lt;/h2&gt;

&lt;p&gt;
  En mis programas mas recientes he empezado a utilizar tanto
  &lt;code&gt;Result&lt;/code&gt; como &lt;code&gt;Maybe&lt;/code&gt;. Siento que me ayudan a expresar
  mejor la intención de los métodos en mi programa.
&lt;/p&gt;

&lt;p&gt;
  Utilizo &lt;code&gt;Result&lt;/code&gt; como &lt;em&gt;return value&lt;/em&gt; para cualquier
  operación que puede devolver un error y utilizo &lt;code&gt;Maybe&lt;/code&gt; para
  cualquier operación que puede devolver datos o no.
&lt;/p&gt;

&lt;p&gt;
  En algunos casos me he visto usando ambos en cojunto por ejemplo puedes
  imaginar un método &lt;code&gt;findById&lt;/code&gt; que busca en la base de datos un
  &lt;em&gt;record&lt;/em&gt; usando el &lt;em&gt;id&lt;/em&gt;.
&lt;/p&gt;

&lt;p&gt;
  Esta al ser una operación de la base de datos puede fallar por muchas razones,
  podemos tener un error en la conexión o quizás la petición tarda mucho y
  falla. Para capturar esa información uso un &lt;code&gt;Result&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  Si todo sale bien lo próximo que puede pasar es que no exista un objeto con
  ese &lt;em&gt;id&lt;/em&gt; en la base de datos y para comunicar esto uso un
  &lt;code&gt;Maybe&lt;/code&gt;. O sea que cuando hago una operación como
  &lt;code&gt;findById&lt;/code&gt; eso devuelve algo como esto.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Result&amp;lt;Maybe&amp;lt;User&amp;gt;, Error&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Admito que puede ser complicado pero pienso que de esta forma estoy
  codificando lo más posible dentro del &lt;em&gt;type system&lt;/em&gt;. No quiero decir
  que esta es &lt;em&gt;“the one true way”&lt;/em&gt; pero por ahora nos funciona.
&lt;/p&gt;

&lt;p&gt;
  Seguiremos experimentando con esto y seguirmos evolucionando pero por ahora
  estamos bastante satisfechos.
&lt;/p&gt;

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

&lt;p&gt;
  Es increíble que hemos llegado hasta aquí sin usar la palabra
  &lt;a href="https://en.wikipedia.org/wiki/Monad_(functional_programming)"
    &gt;monad&lt;/a
  &gt;. Aquí te dejo algunos enlaces que me han ayudado mucho.
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;a
      href="https://khalilstemmler.com/articles/enterprise-typescript-nodejs/handling-errors-result-class/"
      &gt;Flexible Error Handling w/ the Result Class&lt;/a
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a
      href="https://dev.to/_gdelgado/type-safe-error-handling-in-typescript-1p4n?signin=true"
      &gt;Type-Safe Error Handling In TypeScript&lt;/a
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a href="https://gist.github.com/s-panferov/5269524dcf23dad9a1ef"
      &gt;Result type in TypeScript&lt;/a
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a
      href="https://codewithstyle.info/advanced-functional-programming-in-typescript-maybe-monad/"
      &gt;Advanced functional programming in TypeScript: Maybe monad&lt;/a
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a href="https://github.com/patrickmichalina/typescript-monads"
      &gt;TypeScript monads: Better TypeScript Control Flow&lt;/a
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a href="https://github.com/hqoss/monads"
      &gt;Monads: Type safe Option, Result, and Either types&lt;/a
    &gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=F9bznonKc64"
      &gt;Get value out of your monad&lt;/a
    &gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Próximo&lt;/h2&gt;

&lt;p&gt;
  En un próximo post lo que me interesa discutir es el rol de los VOs en la
  validación de request en un web app y cómo podemos complementar usando otros
  tipos de validación con librerías como JSON Schema.
&lt;/p&gt;</description><pubDate>Sat, 22 May 2021 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/result-y-maybe</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Logging is important</title><link>https://gcollazo.com/logging-is-important</link><description>&lt;p&gt;
  Getting application
  &lt;a href="https://en.wikipedia.org/wiki/Log_file"&gt;logging&lt;/a&gt; right is crucial
  for every software project. When done correctly it makes the maintainers work
  a lot simpler. There’s no worst feeling than getting an error report and
  searching for logs to try to understand what happened and not finding a single
  entry. This is especially bad with difficult to reproduce production errors.
&lt;/p&gt;

&lt;p&gt;
  Like everything else, having good logs requires a little bit of thought and
  consistency. But is not that hard.
&lt;/p&gt;

&lt;p&gt;
  I’m not an expert on this subject. This post contains my notes on what has
  worked for me in the past in the context of backend web applications.
&lt;/p&gt;

&lt;h2&gt;Use a real logger&lt;/h2&gt;

&lt;p&gt;
  Every language has a very simple way to send strings to &lt;code&gt;stdout&lt;/code&gt;.
  That’s not a “real” logger.
&lt;/p&gt;

&lt;p&gt;
  In Node you have &lt;code&gt;console.log()&lt;/code&gt; and in Python
  &lt;code&gt;print()&lt;/code&gt;. These work great for simple scripts and print debugging.
  On a real production application, you want to use a “real” logger.
&lt;/p&gt;

&lt;p&gt;
  For Node, I prefer using &lt;a href="https://getpino.io/#/"&gt;pino&lt;/a&gt;. It’s fast,
  has great support, and is very easy to use.
&lt;/p&gt;

&lt;h2&gt;What to log&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;strong&gt;Request/Response&lt;/strong&gt; – You must log every request and response.
    For requests, I like to include all headers, except the
    &lt;code&gt;Authorization&lt;/code&gt; header. For responses, I always include the HTTP
    status code and the response time. Both request and response include a
    unique request ID to correlate them.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;App Errors&lt;/strong&gt; – every &lt;code&gt;catch&lt;/code&gt; in your code must
    include an error log describing what went wrong. Also whenever your app gets
    into a state that will result in an HTTP status code greater than 299 you
    should have some sort of application-level error log, describing why your
    request ended in an error.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;Security events&lt;/strong&gt; – Always log when a login fails, or when a
    user tries to access a page or an object is not supposed to. Always log
    password changes, email changes or any other security credentials change.
    Make sure you don’t log the actual password or sensitive information.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;Business events&lt;/strong&gt; – This is optional but it’s a good idea. If
    your app is a shopping cart, create a log entry every time you close a sale.
    The idea here is that you might want to create alerts based on these log
    events. If you usually see 100 sales per hour and suddenly see zero, it
    might be a good idea to take a look and understand what it’s going on.
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;A good log line&lt;/h2&gt;

&lt;p&gt;
  Having an unsearchable pile of logs is not useful. All my logs are in JSON
  format. This is very useful if you use a service that allows you to query JSON
  logs. I use AWS CloudWatch logs which is not perfect, but it does a decent job
  of helping me find what I’m looking for if I created the logs correctly.
&lt;/p&gt;

&lt;p&gt;Every log line must have the following.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;strong&gt;Log level&lt;/strong&gt; – Usually a number to indicate what kind of log
    it is. Info, error, and debug are the most common.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;Message&lt;/strong&gt; – This is the most important part of the log entry.
    Try to make this explicit and easy to understand for your future self or
    your fellow team members. Try to make messages unique so they are easy to
    search on your codebase. Having multiple error logs with the same message
    makes things harder than they have to be.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;Group&lt;/strong&gt; – All of my logs have a &lt;code&gt;group&lt;/code&gt; field that
    use to indicate what part of the application is producing the log entry.
    Most of the time this group is used to indicate which specific file is
    creating the log entry. This facilitates searching.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;Context&lt;/strong&gt; – Application error logs usually require some
    context that will help you determine what is the problem without logging all
    of your application state. It is very important to include the minimum
    amount of data possible on every log entry but enough to determine what is
    going on. For example if your input validation failed it might be a good
    idea to log what exactly went wrong. For example if you only allow names of
    a certain length, it might be a good idea to log the length of the submitted
    name that produced the error.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;Timestamp&lt;/strong&gt; – Almost every logger adds a timestamp. Just make
    sure that the format used works for you and make sure you include the time
    zone or that your are very certain of what zone it’s being used.
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Security concerns&lt;/h2&gt;

&lt;p&gt;
  If your system deals with
  &lt;a href="https://en.wikipedia.org/wiki/Personal_data"&gt;PII&lt;/a&gt; or
  &lt;a
    href="https://en.wikipedia.org/wiki/Payment_Card_Industry_Data_Security_Standard"
    &gt;CHD&lt;/a
  &gt;, make sure you are not logging sensitive information. Pino and every decent
  logger have a feature to
  &lt;a href="https://getpino.io/#/docs/redaction?id=redaction"&gt;redact&lt;/a&gt; logs.
&lt;/p&gt;

&lt;p&gt;
  Also, make sure that you periodically check your logs for sensitive data. This
  is something not obvious and I’ve seen it go wrong multiple times.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t log sensitive or secret information&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;
  I know logs are boring but they are an essential part of every app. A lot of
  thought is put into many aspects of software development but I rarely find
  people writing or discussing this subject. You should put some thought on your
  logging practices. You future self will thank you.
&lt;/p&gt;

&lt;p&gt;In summary:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    Having decent logs will greatly improve the maintenance of your software.
  &lt;/li&gt;
  &lt;li&gt;Error logs are the most important kind of log.&lt;/li&gt;
  &lt;li&gt;Always include context on error logs.&lt;/li&gt;
  &lt;li&gt;Log every request and response and include status codes.&lt;/li&gt;
  &lt;li&gt;Make sure you don’t log sensitive information.&lt;/li&gt;
&lt;/ul&gt;</description><pubDate>Tue, 01 Jun 2021 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/logging-is-important</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>El Lenguaje de Programación Perfecto</title><link>https://gcollazo.com/el-lenguaje-de-programacion-perfecto</link><description>&lt;p&gt;
  En mi trabajo escribo principalmente
  &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; sobre
  &lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; y aunque no es mi lenguaje
  favorito, no me molesta, tiene muchas cosas buenas. Para el tipo de proyecto
  que trabajo, el tener un “&lt;a
    href="https://en.m.wikipedia.org/wiki/Type_system"
    &gt;type system&lt;/a
  &gt;” es altamente valioso.
&lt;/p&gt;

&lt;p&gt;
  También escribo bastante &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt; (de mis
  lenguajes favoritos) para “scripts” locales y otras cosas que no
  necesariamente terminan en un ambiente de producción. Algún día escribiré un
  poco más sobre porque terminamos corriendo en Node.js y
  &lt;a href="https://en.m.wikipedia.org/wiki/MongoDB"&gt;MongoDB&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
  No creo que exista el lenguaje de programación perfecto, perdón por el
  “clickbait”, pero para mi el lenguaje ideal es una mezcla entre
  &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt;,
  &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt;,
  &lt;a href="https://go.dev/"&gt;Go&lt;/a&gt; y
  &lt;a href="https://www.rust-lang.org/"&gt;Rust&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
  En este post enumero los &lt;em&gt;ingredientes&lt;/em&gt; que me gustaría tomar prestados
  para un lenguaje nuevo imaginario. Seguramente este lenguaje jamás existirá y
  posiblemente eso sea lo mejor. Si hay algo que NO soy, es diseñador de
  lenguajes de programación.
&lt;/p&gt;

&lt;h2&gt;Python&lt;/h2&gt;

&lt;p&gt;
  De Python me encanta que es fácil de leer y escribir y que tiene un “&lt;a
    href="https://docs.python.org/3/library/index.html"
    &gt;standard library&lt;/a
  &gt;” bastante completo. Cuando quiero resolver un problema que se puede resolver
  corriendo un pequeño “script” local, Python siempre me da los mejores
  resultados. Además hay muchas librerías para manejar datos y visualizaciones
  que son fáciles y útiles.
&lt;/p&gt;

&lt;p&gt;
  En cuanto “web apps” que es lo mas que hago, creo que
  &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; es de lo mejor que he
  usado. Todo lo importante está incluido y lo que falta es fácil de añadir. Me
  gustaría que fuera un poco más flexible en cuanto a como organizar los
  proyectos para hacer mas fácil implementar una
  &lt;a href="https://en.m.wikipedia.org/wiki/Hexagonal_architecture_(software)"
    &gt;arquitectura hexagonal&lt;/a
  &gt;
  pero al final del día no tengo quejas mayores.
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fácil de leer y escribir.&lt;/li&gt;
  &lt;li&gt;El “standard library” es bastante completo.&lt;/li&gt;
  &lt;li&gt;Hay muchas librerías para análisis y visualización de datos.&lt;/li&gt;
  &lt;li&gt;Me encanta &lt;a href="https://jupyter.org/"&gt;Jupyter Notebook&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Django es mi web framework favorito.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;TypeScript y JavaScript&lt;/h2&gt;

&lt;p&gt;
  De TypeScript me gusta mucho el “&lt;a
    href="https://www.typescriptlang.org/docs/"
    &gt;type system&lt;/a
  &gt;” y principalmente la idea de que puedes añadir los tipos poco a poco en un
  “codebase” viejo. Se que
  &lt;a href="https://docs.python.org/3/library/typing.html#module-typing"
    &gt;Python tiene su propio “type system”&lt;/a
  &gt;
  pero de lo que he visto el de TypeScript es mas poderoso y no es mucho más
  difícil de aprender.
&lt;/p&gt;

&lt;p&gt;
  Otro feature de TypeScript y JavaScript que me gusta mucho es la sintaxis para
  los “&lt;a
    href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions"
    &gt;arrow functions&lt;/a
  &gt;” &lt;code&gt;(args) =&amp;gt; body&lt;/code&gt;. Además de facilitar el crear funciones
  anónimas creo que hace que el código sea más fácil de leer, especialmente
  cuando se usa
  &lt;a
    href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"
    &gt;map&lt;/a
  &gt;
  y
  &lt;a
    href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter"
    &gt;filter&lt;/a
  &gt;.
&lt;/p&gt;

&lt;p&gt;
  Python tiene los
  &lt;a
    href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions"
    &gt;list y dictionary comprehensions&lt;/a
  &gt;
  que me gustan bastante, pero creo que la sintaxis de los “arrow functions” es
  un poco mejor. Tambien en Python hay
  &lt;a
    href="https://docs.python.org/3/tutorial/controlflow.html?#lambda-expressions"
    &gt;lambdas&lt;/a
  &gt;
  para definir funciones anónimas pero nunca recuerdo como se escriben. Sin duda
  los “arrow functions” son mejor.
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;El “type system” es buenísimo y opcional.&lt;/li&gt;
  &lt;li&gt;
    Me parece que el “type system” es mas poderoso que lo que ofrece Python en
    versiones mas recientes.
  &lt;/li&gt;
  &lt;li&gt;Es fácil de aprender a sacarle provecho a los “types”.&lt;/li&gt;
  &lt;li&gt;
    Los “arrow functions” en JavaScript me gustan más que los list y dictionary
    comprehensions de Python.
  &lt;/li&gt;
  &lt;li&gt;Hacer funciones anónimas es mucho más natural que en Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Go&lt;/h2&gt;

&lt;p&gt;
  De Go lo más que me gusta es el modelo de concurrencia. Los goroutines hacen
  que sea fácil escribir código concurrente sin errores, algo que es un poco más
  complicado en Node y ni hablar de Python.
&lt;/p&gt;

&lt;p&gt;
  Otra cosa que me parece buenísima es la posibilidad de generar binarios
  “statically linked” para Windows, Linux y Mac. Un solo archivo binario con
  todas sus dependencias hace posible crear contenedores de Docker pequeños
  usando &lt;code&gt;FROM scratch&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
  El tema de “performance” aunque no es una de las cosas que más me emocionan de
  un lenguaje, no se debe ignorar. Go al ser un lenguaje compilado produce
  programas que ejecutan mucho más rápido que programas escritos para Node o
  Python. Sin duda esto es algo positivo especialmente cuando estás haciendo
  “hosting” en la nube.
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    El modelo de concurrencia facilita escribir código concurrente con menos
    errores.
  &lt;/li&gt;
  &lt;li&gt;
    Compilar a un solo binario “statically linked” facilita “deploys” y el
    manejo de dependencias.
  &lt;/li&gt;
  &lt;li&gt;
    Poder hacer un contenedor &lt;code&gt;FROM scratch&lt;/code&gt; permite tener imágenes
    pequeñas y con la menor superficie de ataque posible.
  &lt;/li&gt;
  &lt;li&gt;Mejor “performance” cuando se compara con Python o Node.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Rust&lt;/h2&gt;

&lt;p&gt;
  De Rust me gustan muchas cosas pero el enfoque del lenguaje en producir
  programas que sean fácil de analizar estáticamente, ha empujado ese lenguaje a
  tomar decisiones conservadoras sobre como manejar los datos.
&lt;/p&gt;

&lt;p&gt;
  El que todo sea
  &lt;a href="https://en.m.wikipedia.org/wiki/Immutable_object"&gt;inmutable&lt;/a&gt; por
  “default” sin duda es bueno para facilitar el análisis estático del programa y
  ayuda al programador a no tener sorpresas.
&lt;/p&gt;

&lt;p&gt;
  Los tipos &lt;a href="https://doc.rust-lang.org/std/option/"&gt;Option&lt;/a&gt; y
  &lt;a
    href="https://doc.rust-lang.org/std/result/enum.Result.html"
    target="_blank"
    rel="noreferrer noopener"
    &gt;Result&lt;/a
  &gt;
  son otra cosa que me encanta de Rust. Estos dos tipos se encargan de manejar
  errores y funciones que pueden no devolver el valor que esperas. Esto te
  obliga a ser extremadamente explicito manejando errores y los casos donde el
  programa puede fallar. Nuevamente esto ayuda al programador a no tener
  sorpresas.
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Toda la data es inmutable por “default”.&lt;/li&gt;
  &lt;li&gt;
    Al igual que Go los errores son valores que hay que atender de forma
    explícita.
  &lt;/li&gt;
  &lt;li&gt;
    Los tipos &lt;code&gt;Option&lt;/code&gt; y &lt;code&gt;Result&lt;/code&gt; son enormemente útiles y
    me gustan mas que el &lt;code&gt;if err != nil {}&lt;/code&gt; que se ve en todas partes
    en Go.
  &lt;/li&gt;
  &lt;li&gt;
    Los “features” de “&lt;a
      href="https://doc.rust-lang.org/rust-by-example/flow_control/match.html"
      &gt;pattern matching&lt;/a
    &gt;” en combinación con &lt;code&gt;Option&lt;/code&gt; y &lt;code&gt;Result&lt;/code&gt; son una
    maravilla.
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Conclusión&lt;/h2&gt;

&lt;p&gt;
  Ahí está, mi lenguaje de programación ideal es una mezcla de algunas ideas y
  “features” de estos cuatro lenguajes. ¿Te gustaría un lenguaje así?
&lt;/p&gt;</description><pubDate>Mon, 13 Dec 2021 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/el-lenguaje-de-programacion-perfecto</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Después de Gasolina Móvil</title><link>https://gcollazo.com/despues-de-gasolina-movil</link><description>&lt;p&gt;
  Hace mas de siete años y sin avisar me llamó Arnaldo para contarme de un
  invento que tenía. La idea era simple, vamos a hacer un app para que la gente
  pueda pagar la gasolina.
&lt;/p&gt;

&lt;p&gt;
  Inmediatamente me pareció una buena idea. Estaba seguro que los consumidores
  les encantaría poder pagar en la bomba. Ahí, en esa misma llamada acordamos
  hacer el app y ver qué pasaba.
&lt;/p&gt;

&lt;h2&gt;Desde cero&lt;/h2&gt;

&lt;p&gt;
  Dos o tres semanas más tarde nos encontramos en una estación de gasolina en
  Caguas. El dueño de la estación nos permitió probar el app ahí en un ambiente
  real. En este punto Arnaldo tenía casi todo el software de su lado terminado y
  yo tenía un app que parecía que debería funcionar. Lanzamos la primera
  transacción pero falló porque yo cometí un error configurando la bomba de
  gasolina. En el segundo intento la transacción llegó a la estación pero la
  bomba no encendió. Luego de tres o cuatro intentos más, la bomba encendió y
  logramos servir gasolina. ¡Funciona!
&lt;/p&gt;

&lt;p&gt;
  Desde ese día hemos estado pensando y trabajando sobre esta idea. Han pasado
  muchas cosas desde entonces, en su mayoría buenas y otras menos buenas pero
  hemos logrado seguir adelante. Hoy nuestros apps funcionan en la mayoría de
  las estaciones de servicio en Puerto Rico y el negocio sigue creciendo.
&lt;/p&gt;

&lt;p&gt;
  Tenemos clientes en República Dominicana, Colombia, Islas Vírgenes Americanas,
  Bahamas y Estados Unidos. Ahora el enfoque es Centro América así que muy
  pronto habrá noticias sobre este esfuerzo.
&lt;/p&gt;

&lt;h2&gt;Siempre aprendiendo&lt;/h2&gt;

&lt;p&gt;
  Trabajando en este proyecto he tenido muchas oportunidades de aprender.
  Aprendí a hacer servicio al cliente a gran escala, a contratar personal
  técnico y personal no técnico, sobre ciberseguridad y sobre cómo manejar un
  equipo remoto de forma efectiva mucho antes que eso fuera la norma.
&lt;/p&gt;

&lt;p&gt;
  En los últimos años me he enfocado en tres areas: arquitectura de software,
  ciberseguridad y procesos de desarrollo de software.
&lt;/p&gt;

&lt;p&gt;
  Mucho de mi aprendizaje ha sido producto del éxito súbito e inesperado de
  nuestro negocio. Como suele pasar el “demo” se volvió el producto y había que
  moverse rápido. La velocidad produce deuda técnica que si no se atiende con
  prontitud se puede volver un problema.
&lt;/p&gt;

&lt;p&gt;
  Hace un tiempo nos dimos cuenta que nos estábamos moviendo un poco más lento
  de lo usual. La deuda técnica estaba empezando a ser un tema importante. Desde
  entonces hemos estado poco a poco “refactorizando” las areas donde hay más
  deuda y empezando a usar nuevas arquitecturas para evitar problemas en el
  futuro. Todos en el equipo hemos aprendido mucho, el software está en mucho
  mejor forma que cuando empezamos y tenemos una ruta clara para seguir
  mejorando.
&lt;/p&gt;

&lt;p&gt;
  Sobre el tema de ciberseguridad es de lo menos que sabía y a lo mas que le he
  dedicado tiempo. Desde antes de entrar en este negocio me interesaba la
  ciberseguridad pero nunca fue mi enfoque. En este negocio la ciberseguridad es
  posiblemente el tema más importante. Por suerte lo teníamos claro desde el
  principio y le hemos dedicado el tiempo y los recursos necesarios. Ha costado
  mucho dinero y ha tomado mucho tiempo pero lo hemos hecho bastante bien.
&lt;/p&gt;

&lt;p&gt;
  Pasar por el proceso de auditoría de
  &lt;a
    href="https://en.m.wikipedia.org/wiki/Payment_Card_Industry_Data_Security_Standard"
    &gt;PCI&lt;/a
  &gt;
  por primera vez fue casi traumático pero ya en este punto me parece lo mas
  normal del mundo. Además, desde el principio y sin saber, muchas de nuestras
  políticas y medidas de seguridad superaban por mucho los requisitos de PCI. Lo
  que más trabajo nos dio fue aprender a documentar cómo esperan los auditores,
  para poder demostrar lo que en gran medida ya estábamos haciendo
  correctamente.
&lt;/p&gt;

&lt;p&gt;
  Estos años han sido una escuela y me siento en un gran momento profesional.
  Tengo mucho conocimiento y muchas experiencias del mundo real que son
  increíblemente valiosas. Le he sacado el jugo a esta experiencia y estoy muy
  complacido.
&lt;/p&gt;

&lt;h2&gt;De cero otra vez&lt;/h2&gt;

&lt;p&gt;
  &lt;strong
    &gt;Hace mas o menos un año tomé la decisión de dejar mi posición en Alias
    Payments (Gasolina Móvil) y moverme a hacer algo nuevo.&lt;/strong
  &gt;
&lt;/p&gt;

&lt;p&gt;
  En Alias Payments tomamos una simple idea y la llevamos a ser un gran negocio.
  En este punto ya siento que he logrado todo lo que me propuse y que es momento
  de llevar lo aprendido a otros lugares.
&lt;/p&gt;

&lt;p&gt;Empezar de cero otra vez.&lt;/p&gt;

&lt;p&gt;
  En el momento que escribo esto, todavía no tengo claro que voy a hacer pero
  hay tres areas generales que me interesan y estoy explorando:
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    Empezar una compañía de software para negocios (B2B) con un modelo “software
    as a service” en algo relacionado a ciberseguridad.
  &lt;/li&gt;
  &lt;li&gt;
    Armar un equipo de ingenieros especializados en “secure coding practices”
    para hacer consultoría a empresas y startups. El enfoque sería hacer
    auditorias de seguridad y ayudar a establecer políticas de “secure coding”.
  &lt;/li&gt;
  &lt;li&gt;
    Explorar la idea de hacer consultoría a nivel ejecutivo a tiempo parcial con
    varias compañías a la vez, comúnmente conocido como “fractional CTO / CISO”.
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
  Para ser honesto no tengo una idea favorita y no sé si terminaré haciendo
  alguna cosa totalmente diferente. Desde hoy empezaré a hablar con mis
  contactos y a explorar diferentes ideas para poder ir acercándome a una
  decisión.
&lt;/p&gt;

&lt;p&gt;
  Estoy claro que lo que sea que escoja hacer será un proyecto de tres o cuatro
  años mínimo así que no quiero apresurarme. Dicho eso, antes de que acabe el
  año espero saber y compartir a qué me voy a dedicar.
&lt;/p&gt;

&lt;h2&gt;Beluga es un “hobby”&lt;/h2&gt;

&lt;p&gt;
  Llevo unos meses trabajando en un app para iOS que se llama Beluga. Este app
  es “un Twitter” que funciona sin necesidad de centralizar todos los datos en
  los servidores de una sola compañía. Es un Twitter descentralizado y antes de
  que me pregunten, no usa blockchain.
&lt;/p&gt;

&lt;p&gt;
  Mi meta es recrear “el Twitter del principio”, donde había mucha menos gente y
  las interacciones eran mas saludables. Había un momento cuando la gente en
  Twitter no estaba buscando de qué quejarse o de cómo montarsela a alguien que
  dijo una tontería. Tampoco había anuncios en cada tres tweets y no había un
  feed algorítmico tratando de robar toda tu atención para vender anuncios.
&lt;/p&gt;

&lt;p&gt;
  Otro de los objetivos de este proyecto es diseñar un protocolo que otros apps
  puedan adoptar y no quedarme en construir un app que solo funciona si una
  empresa tiene el control total.
&lt;/p&gt;

&lt;p&gt;
  Beluga no es mi próximo negocio o por lo menos dudo que lo sea. Por diseño,
  este proyecto hace bastante difícil que una sola entidad pueda “adueñarse” de
  los datos de los usuarios dificultando la mayoría de los modelos de negocio
  tradicionales para este tipo de apps.
&lt;/p&gt;

&lt;p&gt;
  Beluga es un “hobby” y dudo que se vuelva un negocio. Si se vuelve un negocio
  sería un grata sorpresa pero lo dudo. Pronto compartiré mas información sobre
  Beluga y anunciaré cuando lo publique.
&lt;/p&gt;

&lt;h2&gt;Agradecimiento&lt;/h2&gt;

&lt;p&gt;
  No quiero terminar este post sin darle las gracias a todas las personas que
  han trabajado y que trabajan en Alias Payments y Artisoft Labs por lo mucho
  que me han enseñado, lo profesionales que son y sus buenos deseos. También
  quiero darle gracias a José Padilla quien fue uno de nuestros socios
  originales cuando arrancamos el negocio. Gracias a todos.
&lt;/p&gt;

&lt;p&gt;
  Finalmente, quiero agradecer a mi amigo desde por lo menos 1999 y socio en
  este negocio Arnaldo Rivera. Sin su iniciativa, conocimiento y su ética de
  trabajo sería imposible haber llegado donde hemos llegado. Durante todos estos
  años que hemos trabajado juntos hemos tenido sorprendentemente pocas
  diferencias y cuando han surgido siempre se han resuelto rápidamente y con
  mucho respeto. Hemos trabajado mucho y la hemos pasado muy bien. Ha sido
  genuinamente una gran experiencia en gran medida por el liderato de Arnaldo.
  Gracias.
&lt;/p&gt;

&lt;h2&gt;Próximo&lt;/h2&gt;

&lt;p&gt;
  Tan pronto sepa lo que voy a hacer estoy seguro que lo comunicaré por aquí.
  Por ahora voy a explorar las alternativas y escucharé todas las propuestas que
  reciba.
&lt;/p&gt;

&lt;p&gt;
  Estoy increíblemente entusiasmado por empezar algo nuevo y sentir nuevamente
  la adrenalina de esos primeros días.
&lt;/p&gt;

&lt;p&gt;
  Déjame tus comentarios escribiendo a
  &lt;a href="mailto:hello@gcollazo.com"&gt;hello@gcollazo.com&lt;/a&gt;
&lt;/p&gt;</description><pubDate>Mon, 18 Jul 2022 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/despues-de-gasolina-movil</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Favorite Tiny Desk Concerts</title><link>https://gcollazo.com/favorite-tiny-desk-concerts</link><description>&lt;p&gt;Ordered alphabetically:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=XfzpYcwiUrA"&gt;Adele&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=O70s9aV_9_Q"&gt;Café Tacvba&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=j82L3pLjb_0"&gt;Coldplay&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=SW6L_lTrIFg"&gt;C. Tangana&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=F4neLJQC1_E"&gt;Dua Lipa&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=wTqCthvtL8k"&gt;Hermanos Gutiérrez&lt;/a&gt;
  &lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=rZjRU5ElGrk"&gt;iLe&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=wSl5_RDCfrQ"&gt;Jessie Reyez&lt;/a&gt;
  &lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=ze4xcmBFvaE"&gt;Jon Batiste&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=aWjihS2LHLs"
      &gt;Juanes &amp;amp; Mon Laferte&lt;/a
    &gt;
  &lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=DFiLdByWIDY"&gt;Lizzo&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=QrR_gm6RqCo"&gt;Mac Miller&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=GYJ03MIPoIk"
      &gt;Megan Thee Stallion&lt;/a
    &gt;
  &lt;/li&gt;

  &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=Dy4pEFFbFsA"&gt;Mon Laferte&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=JGL-eQAAxGs"&gt;Monsieur Periné&lt;/a&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=JODaYjDyjyQ"&gt;Natalia Lafourcade&lt;/a&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=KGczofguB0c"&gt;Olivia Rodrigo&lt;/a&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=ysdjtyV_DuY"&gt;The Lumineers&lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;</description><pubDate>Thu, 18 May 2023 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/favorite-tiny-desk-concerts</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Running a Bug Bounty Program Without Spending a Fortune</title><link>https://gcollazo.com/running-a-bug-bounty-program-without-spending-a-fortune</link><description>&lt;p&gt;
  Bug bounty programs have emerged as a crucial cybersecurity measure, allowing
  organizations to harness the skills of the security community in identifying
  and resolving vulnerabilities before malicious actors can exploit them. While
  many opt for popular bug bounty platforms, these can come with hefty price
  tags. In this blog post, we’ll explore the steps to establish a successful and
  cost-effective bug bounty initiative without relying on expensive vendors.
&lt;/p&gt;

&lt;h4&gt;Clearly Define Objectives and Scope&lt;/h4&gt;

&lt;p&gt;
  Before starting your bug bounty program, set clear objectives and scope.
  Determine which assets and systems are in-scope for testing and be transparent
  about your expectations from participants. Outline the types of
  vulnerabilities you’re interested in and specify any restrictions or rules
  during testing.
&lt;/p&gt;

&lt;h4&gt;Prepare Legal and Ethical Guidelines&lt;/h4&gt;

&lt;p&gt;
  Craft comprehensive legal agreements and ethical guidelines that participants
  must follow throughout the bug bounty program. These documents should outline
  the rules, the reward structure, and the responsibilities of both your
  organization and the researchers. Consult with legal professionals to ensure
  compliance with local regulations and to safeguard all parties’ interests.
&lt;/p&gt;

&lt;h4&gt;Build a Secure Platform for Submission&lt;/h4&gt;

&lt;p&gt;
  Create a secure platform where researchers can submit their findings and
  communicate with your security team. There are many alternatives here but
  keeping it simple for researchers while deterring spammers is the holly grail.
  I have tried a bunch of solutions and to be honest the best so far is a well
  crafted Google form.
&lt;/p&gt;

&lt;h4&gt;Respond to Reports&lt;/h4&gt;

&lt;p&gt;
  Having an internal security team to manage and assess submissions is vital for
  the success of your program. They should be skilled in cybersecurity, capable
  of verifying reported vulnerabilities, and assigning appropriate rewards.
  Utilizing your existing team can be more cost-effective than relying on
  external vendors.
&lt;/p&gt;

&lt;h4&gt;Set Reasonable Rewards&lt;/h4&gt;

&lt;p&gt;
  Offering competitive rewards is essential for attracting talented researchers
  to your program. Conduct market research to gauge average payouts for various
  types of vulnerabilities and set rewards accordingly. Remember that
  recognition and swag, in addition to monetary rewards, can also be appealing
  incentives for researchers.
&lt;/p&gt;

&lt;h4&gt;Foster a Positive Community Culture&lt;/h4&gt;

&lt;p&gt;
  Promote a positive and respectful culture within your bug bounty community.
  Promptly acknowledge submissions, maintain open lines of communication, and
  express gratitude for researchers’ efforts. Treat participants as valuable
  allies in securing your systems, not adversaries.
&lt;/p&gt;

&lt;h4&gt;Utilize Cost-Effective Promotion Strategies&lt;/h4&gt;

&lt;p&gt;
  Without the need for popular vendors’ expensive platforms, focus on
  cost-effective promotion strategies. Utilize your company’s website, blog, and
  social media to spread the word about your bug bounty program. Engage with the
  cybersecurity community through forums and mailing lists to raise awareness
  effectively.
&lt;/p&gt;

&lt;h4&gt;Continuous Improvement&lt;/h4&gt;

&lt;p&gt;
  Regularly review and refine your bug bounty program based on feedback from
  researchers and your internal security team. Continuous improvement is
  essential to keep the program effective and relevant in the ever-changing
  cybersecurity landscape, and it doesn’t require additional costs associated
  with external platforms.
&lt;/p&gt;

&lt;h4&gt;You Can Do It&lt;/h4&gt;

&lt;p&gt;
  Running a cost-effective bug bounty program without relying on expensive
  vendors demands thoughtful planning, commitment, and investment in your
  internal resources. By clearly defining objectives, setting up a secure
  submission platform, and fostering a positive community culture, your
  organization can leverage the security community’s expertise to identify and
  resolve vulnerabilities without breaking the bank. Remember, the success of
  your bug bounty program lies in the trust you build with researchers and the
  dedication to making your digital assets more secure in a financially
  responsible manner.
&lt;/p&gt;

&lt;p&gt;
  If you’re looking for a user-friendly and cost-effective solution to create
  and run your bug bounty program, consider using
  &lt;a href="https://blimp.io"&gt;Blimp&lt;/a&gt;. We offer a budget-friendly alternative
  to popular bug bounty platforms, enabling you to efficiently manage
  submissions, engage with the security community, and continuously improve the
  security of your systems while keeping costs in check.
&lt;/p&gt;</description><pubDate>Wed, 19 Jul 2023 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/running-a-bug-bounty-program-without-spending-a-fortune</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Optimal SQLite settings for Django</title><link>https://gcollazo.com/optimal-sqlite-settings-for-django</link><description>&lt;p&gt;
  Requires:
  &lt;strong
    &gt;&lt;a
      href="https://docs.djangoproject.com/en/5.1/ref/databases/#setting-pragma-options"
      &gt;Django &amp;gt;= 5.1&lt;/a
    &gt;&lt;/strong
  &gt;
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
  There’s plenty of information out there on how to scale Django to handle
  numerous requests per second, but most of it doesn’t really apply to the kind
  of software I work on.
&lt;/p&gt;

&lt;p&gt;
  For the past few years, I’ve been developing custom software for smaller
  organizations. All my deployments have fewer than 100 users and operate on a
  single AWS instance. Initially, I chose PostgreSQL because I know it well and
  Django’s documentation suggests that SQLite isn’t suitable for production.
&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;
    SQLite provides an excellent &lt;em&gt;development&lt;/em&gt; alternative for
    applications that are predominantly read-only or require a smaller
    installation footprint.
  &lt;/p&gt;
  &lt;cite
    &gt;&lt;a href="https://docs.djangoproject.com/en/5.0/ref/databases/#sqlite-notes"
      &gt;Django Documentation&lt;/a
    &gt;&lt;/cite
  &gt;
&lt;/blockquote&gt;

&lt;p&gt;
  However, my previous experience with SQLite has taught me that it’s much more
  capable than what the Django docs suggest, offering better performance and
  simpler administration. So, I decided to migrate. To achieve the best possible
  performance, I had to configure Django and SQLite correctly.
&lt;/p&gt;

&lt;p&gt;
  After extensive research, testing, and trial and error, here are my
  recommended settings for Django web apps using SQLite as the primary database.
  I anticipate these settings will evolve over time, and I will update them
  accordingly. My goal is to have these settings ready to copy and paste into
  every new Django project, ensuring they are the most current and optimal for
  most web apps.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
        "OPTIONS": {
            "init_command": (
                "PRAGMA foreign_keys=ON;"
                "PRAGMA journal_mode = WAL;"
                "PRAGMA synchronous = NORMAL;"
                "PRAGMA busy_timeout = 5000;"
                "PRAGMA temp_store = MEMORY;"
                "PRAGMA mmap_size = 134217728;"
                "PRAGMA journal_size_limit = 67108864;"
                "PRAGMA cache_size = 2000;"
            ),
            "transaction_mode": "IMMEDIATE",
        },
    },
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  &lt;em
    &gt;If you’re looking for help with custom web apps or need cybersecurity
    consulting, feel free to &lt;a href="https://blimp.io"&gt;reach out&lt;/a&gt;. I’m
    available for consulting work.&lt;/em
  &gt;
&lt;/p&gt;

&lt;p&gt;
  The rest of this post contains information about each setting, my suggestion
  for a simple backup strategy and a lot of reference links.
&lt;/p&gt;

&lt;hr /&gt;

&lt;h3&gt;PRAGMA foreign_keys = ON&lt;/h3&gt;

&lt;p&gt;
  By default, SQLite does not enforce foreign key constraints. To enable foreign
  key enforcement, you need to activate it manually.
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sqlite.org/foreignkeys.html"&gt;Read more&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;PRAGMA journal_mode = WAL&lt;/h3&gt;

&lt;p&gt;
  The WAL journaling mode uses a write-ahead log instead of a rollback journal
  to implement transactions. The WAL journaling mode is persistent; after being
  set it stays in effect across multiple database connections and after closing
  and reopening the database.
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sqlite.org/wal.html"&gt;Read more&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;PRAGMA synchronous = NORMAL&lt;/h3&gt;

&lt;p&gt;
  When synchronous is NORMAL, the SQLite database engine will still sync at the
  most critical moments, but less often than in FULL mode.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://www.sqlite.org/pragma.html#pragma_synchronous"&gt;Read more&lt;/a&gt;
&lt;/p&gt;

&lt;h3&gt;PRAGMA busy_timeout = 5000&lt;/h3&gt;

&lt;p&gt;
  The&amp;nbsp;busy_timeout&amp;nbsp;setting in SQLite configures the database to wait
  for a specified amount of time when a database file is locked, rather than
  returning an error immediately. This is particularly useful in scenarios like
  web apps where multiple requests are trying to access the database
  simultaneously.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://www.sqlite.org/pragma.html#pragma_busy_timeout"&gt;Read more&lt;/a&gt;
&lt;/p&gt;

&lt;h3&gt;PRAGMA temp_store = MEMORY&lt;/h3&gt;

&lt;p&gt;
  When temp_store is MEMORY temporary tables and indices are kept as if they
  were in pure in-memory databases.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://www.sqlite.org/pragma.html#pragma_temp_store"&gt;Read more&lt;/a&gt;
&lt;/p&gt;

&lt;h3&gt;PRAGMA mmap_size = 134217728&lt;/h3&gt;

&lt;p&gt;
  Stolen from
  &lt;a
    href="https://github.com/rails/rails/blob/ed76d0fbc74545f0275506b4ebf2dfd375fdda18/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L81-L88"
    &gt;Rails SQLite settings&lt;/a
  &gt;.
&lt;/p&gt;

&lt;p&gt;
  This is the maximum number of bytes of the database file that will be accessed
  using memory-mapped I/O
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://www.sqlite.org/pragma.html#pragma_mmap_size"&gt;Read more&lt;/a
  &gt;&lt;br /&gt;&lt;a
    href="https://oldmoe.blog/2024/02/03/turn-on-mmap-support-for-your-sqlite-connections/"
    &gt;Turn on mmap support for your SQLite connections&lt;/a
  &gt;
&lt;/p&gt;

&lt;h3&gt;PRAGMA journal_size_limit = 67108864&lt;/h3&gt;

&lt;p&gt;
  Stolen from
  &lt;a
    href="https://github.com/rails/rails/blob/ed76d0fbc74545f0275506b4ebf2dfd375fdda18/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L81-L88"
    &gt;Rails SQLite settings&lt;/a
  &gt;.
&lt;/p&gt;

&lt;p&gt;
  … in&amp;nbsp;&lt;a href="https://www.sqlite.org/wal.html"&gt;WAL mode&lt;/a&gt;, the
  write-ahead log file is not truncated following a&amp;nbsp;&lt;a
    href="https://www.sqlite.org/wal.html#ckpt"
    &gt;checkpoint&lt;/a
  &gt;. Instead, SQLite reuses the existing file for subsequent WAL entries since
  overwriting is faster than appending.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://www.sqlite.org/pragma.html#pragma_journal_size_limit"
    &gt;Read more&lt;/a
  &gt;
&lt;/p&gt;

&lt;h3&gt;PRAGMA cache_size = 2000&lt;/h3&gt;

&lt;p&gt;
  Stolen from
  &lt;a
    href="https://github.com/rails/rails/blob/ed76d0fbc74545f0275506b4ebf2dfd375fdda18/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L81-L88"
    &gt;Rails SQLite settings&lt;/a
  &gt;.
&lt;/p&gt;

&lt;p&gt;
  … maximum number of database disk pages that SQLite will hold in memory at
  once per open database file.
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://www.sqlite.org/pragma.html#pragma_cache_size"&gt;Read more&lt;/a&gt;
&lt;/p&gt;

&lt;h3&gt;“transaction_mode”: “IMMEDIATE”&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;
    By default, SQLite starts transactions
    in&amp;nbsp;&lt;code&gt;DEFERRED&lt;/code&gt;&amp;nbsp;mode: they are considered read only. They
    are upgraded to a write transaction that requires a database lock in-flight,
    when query containing a write/update/delete statement is issued.
  &lt;/p&gt;

  &lt;p&gt;
    The problem is that by upgrading a transaction after it has started, SQLite
    will immediately return a&amp;nbsp;&lt;code&gt;SQLITE_BUSY&lt;/code&gt;&amp;nbsp;error without
    respecting the&amp;nbsp;&lt;code&gt;busy_timeout&lt;/code&gt;&amp;nbsp;previously mentioned, if
    the database is already locked by another connection.
  &lt;/p&gt;

  &lt;p&gt;
    This is why you should start your transactions with&amp;nbsp;&lt;code
      &gt;BEGIN IMMEDIATE&lt;/code
    &gt;&amp;nbsp;instead of only&amp;nbsp;&lt;code&gt;BEGIN&lt;/code&gt;. If the database is locked
    when the transaction starts, SQLite will
    respect&amp;nbsp;&lt;code&gt;busy_timeout&lt;/code&gt;.;
  &lt;/p&gt;
  &lt;cite&gt;&lt;a href="https://kerkour.com/sqlite-for-servers"&gt;Source&lt;/a&gt;&lt;/cite&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  &lt;br /&gt;&lt;a href="https://www.sqlite.org/lang_transaction.html"&gt;Read more&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;Backups&lt;/h2&gt;

&lt;p&gt;
  This is the simplest solution for backup. It’s a little bash script that can
  be scheduled to run every hour using a cron job. This script will create a
  backup in a safe way even if your database is receiving a high volume of
  writes, as it captures a snapshot from a single transaction. Once the backup
  file is created it’s uploaded to an S3 bucket.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash
#
# Run using cron to backup db every hour
#
set -euf -o pipefail

sqlite3 /path/to/db "VACUUM INTO '/path/to/backup'"
tar --zstd -cf "/path/to/backup.tar.zst" "/path/to/backup"

# 1-day, rolling hourly backup
aws s3 cp /path/to/backup.tar.zst s3://mybucket/backup-`date +%H`.tar.zst

# 1-month, rolling daily backup
aws s3 cp /path/to/backup.tar.zst s3://mybucket/backup-`date +%d`.tar.zst

# 1-month, rolling hourly backup
aws s3 cp /path/to/backup.tar.zst s3://mybucket/backup-`date +%d%H`.tar.zst&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://litestream.io/alternatives/cron/"&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  If you need something more robust than that you should look into the great
  &lt;a href="https://litestream.io"&gt;Litestream&lt;/a&gt;, which will continuously stream
  changes to an object store like S3 or any of it’s clones.
&lt;/p&gt;

&lt;h2&gt;Other references and related links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;a href="https://kerkour.com/sqlite-for-servers"
      &gt;Optimizing SQLite for servers&lt;/a
    &gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=XcAYkriuQ1o"
      &gt;Building Production Applications Using Go &amp;amp; SQLite&lt;/a
    &gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://tailscale.com/blog/database-for-2022"
      &gt;A database for 2022&lt;/a
    &gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;a href="https://www.youtube.com/watch?v=B-_P0d1el2k"
      &gt;The change Pieter Levels did to speed up SQLite&lt;/a
    &gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;a
      href="https://fractaledmind.github.io/2023/09/07/enhancing-rails-sqlite-fine-tuning/"
      &gt;Enhancing your Rails app with SQLite: Fine-tuning your database&lt;/a
    &gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;a
      href="https://blog.pecar.me/sqlite-django-config"
      data-type="link"
      data-id="https://blog.pecar.me/sqlite-django-config"
      &gt;This blog post&lt;/a
    &gt;
    has info on how to implement this on Django&amp;lt;5.1
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
  Do you have any suggestions on how to improve this configuration? Send me an
  &lt;a href="mailto:hello@gcollazo.com"&gt;email&lt;/a&gt;.
&lt;/p&gt;</description><pubDate>Wed, 12 Jun 2024 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/optimal-sqlite-settings-for-django</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Software hecho a la medida</title><link>https://gcollazo.com/software-hecho-a-la-medida</link><description>&lt;p&gt;
  Todos los negocios usan software que es &lt;em&gt;casi-casi&lt;/em&gt; perfecto, pero
  siempre falta algo. Siempre hay una parte de algún proceso que depende de una
  hoja de Excel que se envía por email o se comparte en Google Drive y que nadie
  sabe exactamente quien modificó o quien tiene la última versión. Como esto hay
  muchos &lt;em&gt;paper cuts&lt;/em&gt; que hacen que los negocios pierdan el tiempo
  dándole la vuelta a soluciones de software que no están optimizadas para su
  negocio y su forma de trabajar.
&lt;/p&gt;

&lt;p&gt;
  Estos negocios deben considerar la opción de adquirir
  &lt;em&gt;custom software&lt;/em&gt;. Gracias a los &lt;em&gt;economics&lt;/em&gt; del
  &lt;strong&gt;cloud&lt;/strong&gt; y a que la mayoría de los negocios en Puerto Rico
  tienen una escala relativamente pequeña, es posible entregar software hecho a
  la medida a un costo accesible para muchos negocios que en el pasado no tenían
  esta posibilidad.
&lt;/p&gt;

&lt;p&gt;
  A diferencia del software enlatado, el software hecho a la medida
  &lt;strong&gt;se puede adquirir sin necesidad de modelos SAAS&lt;/strong&gt; que obligan a
  los negocios a pagar todos los meses para poder tener acceso. Gracias a una
  serie de avances tecnológicos (cloud, Docker, Go, SQLite, etc.) y técnicas de
  desarrollo recientes, es posible desplegar este tipo de software de forma que
  requiera un mínimo de mantenimiento, permitiendo que el negocio pague una sola
  vez por una &lt;strong&gt;licencia permanente&lt;/strong&gt;. Muchos de los negocios con
  los que hablo quieren dejar de pagar por rentar el software que necesita su
  negocio para operar.
&lt;/p&gt;

&lt;p&gt;
  Poder diseñar y dirigir la dirección del software que usa el negocio abre la
  puerta a muchas optimizaciones y eficiencias que no son posibles con el
  software enlatado que usa la mayoría. Es por esto que los negocios grandes
  siempre crean sus propias soluciones y lo consideran una ventaja competitiva
  estratégica.
&lt;/p&gt;

&lt;p&gt;
  Estoy convencido que para que los negocios locales tengan una posibilidad real
  de competir en el mercado es necesario que tengan herramientas de software
  similares a las que usan sus competidores más grandes. Me da esperanza que
  finalmente tenemos un camino para hacerlo posible.
&lt;/p&gt;

&lt;p&gt;
  Si tienes un negocio y te interesa explorar la posibilidad de adquirir
  software hecho a la medida me puedes contactar
  &lt;a href="mailto:hello@gcollazo.com"&gt;hello@gcollazo.com&lt;/a&gt; directamente o
  visitar &lt;a href="https://blimp.io/"&gt;blimp.io&lt;/a&gt;
  para que conozcas algunos de los clientes para los que he trabajado y los
  servicios que ofrezco.
&lt;/p&gt;</description><pubDate>Mon, 15 Jul 2024 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/software-hecho-a-la-medida</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Essential BBOT Commands for Recon</title><link>https://gcollazo.com/essential-bbot-commands-for-recon</link><description>&lt;p&gt;
  &lt;a
    href="https://github.com/blacklanternsecurity/bbot"
    rel="noreferrer noopener"
    target="_blank"
    &gt;BBOT&lt;/a
  &gt;
  (BEE·bot) is a powerful recursive internet scanner designed for
  reconnaissance, bug bounties, and attack surface management. Think of it as
  your all-in-one tool information gathering and security assessment.
&lt;/p&gt;

&lt;p&gt;
  I prefer running BBOT through Docker for consistent behavior across
  environments:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# BBOT: Automated reconnaissance framework
bbot() {
  docker run --rm -it \
    -v "$HOME/.bbot:/root/.bbot" \
    -v "$HOME/.config/bbot:/root/.config/bbot" \
    blacklanternsecurity/bbot:stable "$@"
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Add this function to your shell configuration file, and you’re ready to go.
&lt;/p&gt;

&lt;h2&gt;Essential Commands&lt;/h2&gt;
&lt;p&gt;Here are some BBOT commands I regularly use in my security assessments:&lt;/p&gt;
&lt;h3&gt;Full Subdomain Enumeration&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# Comprehensive subdomain discovery
bbot -t example.com -p subdomain-enum&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Perfect for initial reconnaissance of a target domain. This command leverages
  multiple sources to build a complete picture of the target’s subdomain
  landscape.
&lt;/p&gt;

&lt;h3&gt;Passive Subdomain Reconnaissance&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# Non-intrusive subdomain discovery
bbot -t example.com -p subdomain-enum -rf passive&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Ideal for situations requiring stealth or when active scanning isn’t
  appropriate. This method relies solely on external data sources without
  directly interacting with the target.
&lt;/p&gt;

&lt;h3&gt;Enhanced Domain Visualization&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# Combine subdomain enumeration with port scanning and web screenshots
bbot -t example.com -p subdomain-enum -m portscan gowitness&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  This command creates a comprehensive visual map of your target’s attack
  surface, combining port scanning with web interface documentation.
&lt;/p&gt;

&lt;h3&gt;Basic Web Assessment&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# Non-intrusive web technology enumeration
bbot -t example.com -p subdomain-enum web-basic&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Gathers essential information about web technologies while maintaining a light
  touch. Includes technology fingerprinting and robots.txt analysis.
&lt;/p&gt;

&lt;h3&gt;Targeted Web Crawling&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# Controlled depth web crawling with automated analysis
bbot -t www.example.com \
    -p spider \
    -c web.spider_distance=2 web.spider_depth=2&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  Efficiently maps web application structure while automatically identifying
  sensitive information like emails and potential secrets.
&lt;/p&gt;

&lt;h3&gt;Comprehensive Scan&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;# Full-spectrum reconnaissance
bbot -t example.com -p kitchen-sink&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
  When you need the full picture, this command combines subdomain enumeration,
  email discovery, cloud bucket identification, port scanning, web analysis, and
  vulnerability scanning with nuclei.
&lt;/p&gt;

&lt;p&gt;
  This post is just scratching the surface. For more detailed information, check
  out the
  &lt;a href="https://github.com/blacklanternsecurity/bbot"
    &gt;official BBOT repository&lt;/a
  &gt;
  and
  &lt;a href="https://www.blacklanternsecurity.com/bbot/"&gt;documentation&lt;/a&gt;.
&lt;/p&gt;</description><pubDate>Wed, 15 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/essential-bbot-commands-for-recon</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Demo Days: A Simpler Approach to Engineering Leadership</title><link>https://gcollazo.com/demo-days</link><description>&lt;p&gt;Weekly demo days started as an experiment: a regular time when anyone can demo anything to the rest of the team. No
    slides allowed, no status reports, just a live demo.&lt;/p&gt;

&lt;p&gt;The format was straightforward: 3 to 4 engineers would each get up to 15 minutes to demonstrate
    their
    work in progress or whatever they like. Attendees can ask questions and the facilitator is responsible for
    maintaining a respectful and professional atmosphere.&lt;/p&gt;

&lt;p&gt;While most demos focused on current work projects, the format was open to anything that might interest the team - a
    cool new technology, a competitor's product, a side project, or a game
    someone built over the weekend.&lt;/p&gt;

&lt;p&gt;This freedom to share whatever they found exciting kept the sessions fun, engaging and low pressure. Despite how
    good it
    worked and felt, demo days gradually faded from our routine as we defaulted back to standard "agile" ceremonies.&lt;/p&gt;
&lt;p&gt;Reading Ken Kocienda's "&lt;a href="http://creativeselection.io"&gt;Creative Selection&lt;/a&gt;" about Apple's product
    development process
    reignited my interest in demo days. His book showed me how this approach could scale beyond small teams. Kocienda
    describes their process eloquently:&lt;/p&gt;
&lt;blockquote&gt; "We always started small, with some inspiration. We made demos. We mixed in feedback. We listened to
    guidance from
    smart colleagues. We blended in variations. We honed our vision. We followed the initial demo with another and then
    another. We improved our demos in incremental steps. We evolved our work by slowly converging on better versions of
    the vision. Round after round of creative selection moved us step by step from the spark of an idea to a finished
    product."
&lt;/blockquote&gt;
&lt;p&gt;Here was one of the world's most successful technology companies using regular demos to drive their product design
    and as a cornerstone of their
    engineering culture.&lt;/p&gt;
&lt;p&gt;Then I recently learned about Oxide Computer Company's &lt;a href="https://oxide.computer/blog/engineering-culture"&gt;experience
    with demo days&lt;/a&gt;
    - a team I deeply respect for their engineering excellence. Oxide's journey with demos particularly resonates with
    me. As they describe it:&lt;/p&gt;
&lt;blockquote&gt; "...our weekly Demo Friday, an hour-long unstructured session to demo our work for one another. Demo Friday
    is such
    an essential part of Oxide’s culture that it feels like we have always done it, but in fact it took us nearly two
    years into the company’s life to get there: over the spring and summer of 2021, our colleague Sean Klein had
    instituted regular demos for the area that he works on (the Oxide control plane), and others around the
    company — seeing the energy that came from it — asked if they, too, could start regular demos for their domain. But
    instead of doing it group by group, we instituted it company-wide starting in the fall of 2021: an unstructured hour
    once a week in which anyone can demo anything."
&lt;/blockquote&gt;
&lt;p&gt;Currently, I'm transitioning to doing more work as factional CTO for startup companies, and I've become
    increasingly frustrated
    with the
    endless stream of meetings that seem to define modern engineering management. I miss demo days. Demos feel easy and
    fun, and they
    produce
    high quality feedback and questions that help with implementation and help engineers focus on the
    important stuff.
&lt;/p&gt;

&lt;p&gt;The benefits I observed went beyond just better code. Engineers started thinking differently about their work when
    they knew they'd be showing it to their peers. They naturally prioritized getting to working software faster and
    thought more carefully about their implementation choices.&lt;/p&gt;

&lt;p&gt;However, I'm also aware of the potential challenges. Teams might over-optimize for demos at the expense of deeper
    technical work. Some engineers might feel undue pressure to show progress every week. Without proper facilitation,
    demo sessions could devolve into critique sessions that harm team morale.&lt;/p&gt;

&lt;p&gt;As I prepare to implement this approach again with my consulting clients, I'm particularly interested in hearing from
    other technical leaders who have experience with demo days. How have you scaled this practice in larger
    organizations? What challenges did you encounter with distributed teams? How do you balance the demo cadence with
    other engineering processes?&lt;/p&gt;

&lt;p&gt;If you're using demo days in your organization, I'd love to hear about your experience. What's worked? What hasn't?
    How have you adapted the practice for your specific context?&lt;/p&gt;

&lt;p&gt;&lt;a href="mailto:hello@gcollazo.com"&gt;hello@gcollazo.com&lt;/a&gt;&lt;/p&gt;</description><pubDate>Wed, 19 Feb 2025 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/demo-days</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Value Objects en Go</title><link>https://gcollazo.com/go-value-objects</link><description>&lt;p&gt;
    Llevo años experimentando con Value Objects (VO) en varios lenguajes, desde
    JavaScript y &lt;a href="/debe-ser-imposible-representar-estados-ilegales/"&gt;TypeScript&lt;/a&gt; hasta Python y, más
    recientemente, Go.&lt;/p&gt;

&lt;p&gt;
    Llevo dos años sumergido en Go por lo que he tenido la necesidad de implementar Value Objects en este lenguaje.
    Abajo
    comparto el acercamiento que he estado usando recientemente con bastante éxito.
&lt;/p&gt;

&lt;p&gt;Esta implementación cumple con todos mis requisitos:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Es imposible instanciar un VO inválido&lt;/li&gt;
    &lt;li&gt;Métodos &lt;code&gt;New*&lt;/code&gt; devuelven un &lt;code&gt;error&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;Métodos &lt;code&gt;MustNew*&lt;/code&gt; hacen panic si hay un error&lt;/li&gt;
    &lt;li&gt;Inmutabilidad&lt;/li&gt;
    &lt;li&gt;Igualdad basada en valor&lt;/li&gt;
    &lt;li&gt;Encapsulación del comportamiento&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para la mayoría de los casos uso el método &lt;code&gt;New*&lt;/code&gt; que devuelve un &lt;code&gt;error&lt;/code&gt; y me permite
    decidir como manejar los casos en que la instanciación falla. El método &lt;code&gt;MustNew*&lt;/code&gt; lo uso principalmente
    en pruebas, dónde puedo estar seguro que estoy creando un VO válido manualmente y de esta forma evito tener que
    manejar
    un error que nunca sucederá.&lt;/p&gt;

&lt;p&gt;En todo caso es imposible crear un VO usando uno de estos métodos sin que cumpla con todos los requisitos de mi
    dominio. Y donde veo que se crea un VO sin usar uno de estos dos métodos puedo rechazar ese código sin pensarlo dos
    veces. Aunque esto require un poco de disciplina, es una regla fácil de seguir y hacer cumplir.&lt;/p&gt;

&lt;p&gt;Este es un ejemplo sintético de como implemento VO en Go. Es intencionalmente simple para que sea fácil de entender y
    aplicar en otros codebases. Aunque los patrones y el diseño del VO está bien y correcto esto no es una
    implementación válida de &lt;code&gt;Amount&lt;/code&gt; así que no hagas copy y paste y lo uses en producción, te va a doler.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;package amount

import (
    "errors"
    "fmt"
)

type Currency string

const (
    USD Currency = "USD"
    EUR Currency = "EUR"
)

func (c Currency) IsValid() bool {
    validCurrencies := map[Currency]bool{
       USD: true,
       EUR: true,
    }
    return validCurrencies[c]
}

type Amount struct {
    value    int
    currency Currency
}

func NewAmount(value int, currency Currency) (Amount, error) {
    if value &lt; 0 {
       return Amount{}, errors.New("amount must be positive")
    }

    if value &gt; 1_000_000 {
       return Amount{}, errors.New("amount must be less than or equal to 1,000,000")
    }

    if !currency.IsValid() {
       return Amount{}, errors.New("invalid currency")
    }

    return Amount{
       value:    value,
       currency: currency,
    }, nil
}

func MustNewAmount(value int, currency Currency) Amount {
    a, err := NewAmount(value, currency)
    if err != nil {
       panic(err)
    }
    return a
}

func (a Amount) Value() int {
    return a.value
}

func (a Amount) Currency() Currency {
    return a.currency
}

func (a Amount) String() string {
    switch a.currency {
    case USD:
       return fmt.Sprintf("$%d", a.value)
    case EUR:
       return fmt.Sprintf("%d€", a.value)
    default:
       return fmt.Sprintf("%d %s", a.value, a.currency)
    }
}

func (a Amount) Equal(other Amount) bool {
    return a.value == other.value &amp;&amp; a.currency == other.currency
}

func (a Amount) ConvertTo(newCurrency Currency) (Amount, error) {
    if a.currency == newCurrency {
       return a, nil
    }

    // Assume 1 USD = 0.85 EUR
    conversionRate := 0.85
    if a.currency == EUR {
       conversionRate = 1 / conversionRate
    }

    newValue := int(float64(a.value) * conversionRate)
    return NewAmount(newValue, newCurrency)
}

func (a Amount) Add(other Amount) (Amount, error) {
    if a.currency != other.currency {
       return Amount{}, errors.New("cannot add amounts with different currencies")
    }
    return NewAmount(a.value+other.value, a.currency)
}

func (a Amount) Subtract(other Amount) (Amount, error) {
    if a.currency != other.currency {
       return Amount{}, errors.New("cannot subtract amounts with different currencies")
    }
    return NewAmount(a.value-other.value, a.currency)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Siento que si un veterano de Go mira este código no le encantaría. Pero para mí que llevo solo dos años en Go se
    siente natural y creo que encaja bien con los otros APIs que he visto en el "standard library". Si tienes una mejor
    forma de implementar VOs en Go me gustaría verla.&lt;/p&gt;</description><pubDate>Tue, 25 Mar 2025 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/go-value-objects</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>AQUI.PR: Un nuevo newsletter que está cambiando la forma de consumir noticias en Puerto Rico</title><link>https://gcollazo.com/aquipr</link><description>&lt;p&gt;Recientemente lanzamos &lt;a
        href="https://aqui.pr?utm_source=gcollazo.com&amp;utm_medium=web&amp;utm_campaign=aquipr"&gt;aqui.pr&lt;/a&gt;,
    un proyecto que me tiene super entusiasmado y
    del que quería compartir un poco
    más.&lt;/p&gt;
&lt;p&gt;Todos lo hemos vivido, entras a un site de noticias para leer algo que te interesa y te bombardean con anuncios,
    videos que se reproducen solos y popups que se meten en el medio. La experiencia es tan frustrante que muchos
    terminamos
    abandonando antes de leer lo que queríamos o nos vemos obligados a usar "ad blockers" o el "reader mode" que muchos
    navegadores tienen disponible para lidiar con este problema.&lt;/p&gt;
&lt;p&gt;Así nació aqui.pr, un newsletter diario que llega a tu correo a las 6:30 de la mañana con las 10 noticias más
    importantes y comentadas de Puerto Rico. Sin fastasmeo, sin clickbait, sin publicidad invasiva.&lt;/p&gt;

&lt;h3&gt;¿Cómo funciona?&lt;/h3&gt;

&lt;p&gt;Lo que hace diferente a aqui.pr es su modelo híbrido que combina lo mejor del periodismo tradicional con las
    herramientas de inteligencia artificial:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Nuestros editores seleccionan diariamente las 10 noticias más relevantes.&lt;/li&gt;
    &lt;li&gt;Para cada noticia, recopilamos información de hasta tres fuentes periodísticas distintas.&lt;/li&gt;
    &lt;li&gt;Utilizamos IA para resumir ese contenido extenso en párrafos concisos y claros.&lt;/li&gt;
    &lt;li&gt;Los editores revisan, pulen y verifican cada resumen antes de publicarlo.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Ese proceso nos permite ofrecerte en cinco minutos toda la información que necesitas para estar al día con lo que
    ocurre en la isla.&lt;/p&gt;

&lt;h3&gt;Respetando al lector&lt;/h3&gt;

&lt;p&gt;No es ningún secreto que el modelo de negocio de los medios digitales está roto. La dependencia de clicks y
    publicidad intrusiva ha creado una experiencia horrible para los lectores.&lt;/p&gt;

&lt;p&gt;En aqui.pr apostamos por un enfoque diferente:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Sin clickbait: No necesitamos titulares sensacionalistas porque no dependemos de que hagas click.&lt;/li&gt;
    &lt;li&gt;Respeto por tu tiempo: Resumimos lo importante para que estés informado en minutos.&lt;/li&gt;
    &lt;li&gt;Publicidad no intrusiva: Sí, tenemos anuncios (tres por newsletter), pero están diseñados para no interrumpir tu
        lectura.
    &lt;/li&gt;
    &lt;li&gt;Patrocinios voluntarios: Por solo $3 mensuales puedes apoyar este proyecto y ayudarnos a seguir creciendo.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Los resultados hasta ahora&lt;/h3&gt;

&lt;p&gt;Aunque hoy es el lanzamiento oficial, llevamos varias semanas haciendo pruebas y puliendo el producto. Estas
    primeras
    semanas han sido increíbles. El feedback que hemos recibido confirma lo que sospechábamos: la gente
    quiere estar informada, pero no quiere lidiar con lo difícil y fragmentado que se ha vuelto consumir noticias.&lt;/p&gt;

&lt;p&gt;Muchos suscriptores nos comentan que ahora se sienten más preparados para sus conversaciones diarias y más conectados
    con lo que pasa en Puerto Rico—especialmente aquellos boricuas que viven fuera de la isla.&lt;/p&gt;

&lt;p&gt;Suscríbete
    gratis visitando &lt;a
            href="https://aqui.pr?utm_source=gcollazo.com&amp;utm_medium=web&amp;utm_campaign=aquipr"&gt;aqui.pr&lt;/a&gt;.&lt;/p&gt;</description><pubDate>Mon, 07 Apr 2025 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/aquipr</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Taking on New Development Work</title><link>https://gcollazo.com/taking-on-new-development-work</link><description>&lt;p&gt;It's been a while since I've shared any professional updates here.&lt;/p&gt;

&lt;p&gt;As some of you might know, I stepped down as CTO of Gasolina Móvil (now Alias Payments) in 2022 after more than
    seven years building what became one of Puerto Rico's largest mobile payment platforms. What started as a simple
    idea (let people pay for gas with an app) turned into a complex fintech operation spanning multiple countries, with
    custom hardware, payment processing, and some of the most stringent security requirements I've ever worked with.&lt;/p&gt;
&lt;p&gt;Those years were an incredible learning experience. I went from knowing very little about cybersecurity to becoming
    obsessed with it. Going through our first PCI compliance audit was almost traumatic, now it's just routine. I
    learned to scale teams, handle customer service at enterprise level, and most importantly, how to build secure
    systems when the stakes are real, when you're processing actual payments and handling real customer data.&lt;/p&gt;
&lt;p&gt;Now I'm also in a transition period. After spending so many years focused on one
    product, I'm exploring what comes next. I'm taking my time to figure it out, whatever I choose will likely be a
    long-term commitment, so I don't want to rush.&lt;/p&gt;
&lt;p&gt;In the meantime, I'm doing software development work and some consulting.&lt;/p&gt;
&lt;blockquote&gt;Over the past two years, I've worked on custom software development for a large construction company,
    product design
    and development for a nonprofit, and SDLC consulting for a data analytics company. I've also helped a fintech
    startup with PCI DSS compliance, an insurance startup with SOC 2, and a SaaS company with ISO 27001
    implementation.
&lt;/blockquote&gt;
&lt;p&gt;I currently have time available to take on new clients, so now is a great time to reach out if you need help
    building software. &lt;strong&gt;My primary focus is hands-on software development, backend and frontend work in Python,
        Go, and TypeScript.&lt;/strong&gt; But I have some experience in a bunch of other technologies. I want to write code
    and ship products alongside other teams, or help
    entrepreneurs
    create and launch their product idea.&lt;/p&gt;
&lt;p&gt;I can also consult on DevSecOps, cloud architecture, and secure development lifecycle practices when needed. I've
    been through the trenches of building payment systems, implementing security compliance frameworks, and scaling
    teams under pressure.&lt;/p&gt;
&lt;p&gt;We can talk about your architectural decisions, security posture, development processes, or dive deep into specific
    technical challenges. Whether you're a startup trying to build secure-by-design systems or an established company
    dealing with technical debt and compliance requirements, I enjoy sharing what I've learned from building systems
    that handle real money and real security threats.&lt;/p&gt;
&lt;p&gt;The format is flexible, we can do a single consultation call to start, or work together over several sessions if it
    makes sense. This will probably work best for technical leaders, CTOs, or teams dealing with security and scaling
    challenges.&lt;/p&gt;
&lt;p&gt;If this sounds interesting, drop me an email at &lt;a href="mailto:hello@gcollazo.com"&gt;hello@gcollazo.com&lt;/a&gt;, and I'll
    get back to you if I can help.&lt;/p&gt;
&lt;p&gt;¡Gracias!&lt;/p&gt;</description><pubDate>Wed, 11 Jun 2025 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/taking-on-new-development-work</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item><item><title>Tomando Nuevos Proyectos de Software</title><link>https://gcollazo.com/tomando-nuevos-proyectos-de-software</link><description>&lt;p&gt;Ha pasado tiempo desde que he compartido actualizaciones profesionales por aquí.&lt;/p&gt;
&lt;p&gt;Como algunos ya saben, dejé mi posición como CTO de Gasolina Móvil (ahora Alias Payments) en el 2022 después de más
    de siete años construyendo lo que se convirtió en una de las plataformas de pagos móviles más grandes de Puerto
    Rico. Lo que empezó como una idea simple, que la gente pueda pagar la gasolina con un app, se convirtió en una
    operación fintech compleja que abarca múltiples países, con hardware custom, procesamiento de pagos, y
    algunos de los requisitos de seguridad más estrictos que he trabajado.&lt;/p&gt;
&lt;p&gt;Esos años fueron una experiencia de aprendizaje increíble. Pasé de saber muy poco sobre ciberseguridad a
    obsesionarme con el tema. Pasar por nuestra primera auditoría de cumplimiento PCI fue casi traumático, ahora se
    siente rutinario. Aprendí a escalar equipos, manejar servicio al cliente, y lo
    más importante, cómo construir sistemas seguros cuando los riesgos son reales, cuando estás procesando pagos de
    verdad y manejando datos reales de clientes.&lt;/p&gt;
&lt;p&gt;Ahora estoy en un período de transición. Después de tantos
    años enfocado en un solo producto, estoy explorando qué viene después. Me estoy tomando mi tiempo para decidir, lo
    que sea que escoja probablemente será un compromiso a largo plazo, así que no me quiero apurar.&lt;/p&gt;
&lt;p&gt;Mientras tanto, estoy haciendo trabajo de desarrollo de software y algo de consultoría.&lt;/p&gt;
&lt;blockquote&gt;Durante los últimos dos años, he trabajado en desarrollo de software "custom" para una compañía grande
    de
    construcción, diseño de productos y desarrollo para una nonprofit, y consultoría de SDLC para
    una compañía de análisis de datos. También he ayudado a un startup fintech con cumplimiento PCI DSS, un startup de
    seguros con SOC 2, y una compañía SaaS con implementación de ISO 27001.
&lt;/blockquote&gt;
&lt;p&gt;Actualmente, tengo tiempo disponible para tomar nuevos clientes, así que ahora es un buen momento para contactarme si
    necesitas ayuda construyendo software. &lt;strong&gt;Mi enfoque principal es desarrollo de software, trabajo de
        backend y
        frontend en Python, Go y TypeScript.&lt;/strong&gt; Pero tengo experiencia en un montón de otras tecnologías. Quiero
    escribir código y lanzar productos junto a
    otros equipos, o
    ayudar emprendedores a crear y lanzar su idea de producto.&lt;/p&gt;
&lt;p&gt;También puedo hacer consultoría en DevSecOps, arquitectura de "cloud", y prácticas de "secure software development
    lifecycle" cuando sea necesario. He pasado por las trincheras construyendo sistemas de pagos, implementando marcos
    de
    cumplimiento de seguridad, y escalando equipos bajo presión.&lt;/p&gt;
&lt;p&gt;Podemos hablar sobre tus decisiones arquitectónicas, postura de seguridad, procesos de desarrollo, o profundizar en
    desafíos técnicos específicos. Ya sea que tengas un startup tratando de construir sistemas seguros desde el diseño o
    una compañía establecida lidiando con deuda técnica y requisitos de cumplimiento, disfruto compartir lo que he
    aprendido construyendo sistemas que manejan dinero real y amenazas de seguridad reales.&lt;/p&gt;
&lt;p&gt;El formato es flexible, podemos hacer una llamada de consultoría para empezar, o trabajar juntos durante
    varias sesiones si tiene sentido. Esto probablemente funcionará mejor para líderes técnicos, CTOs, o equipos
    lidiando con desafíos de seguridad y escalabilidad.&lt;/p&gt;
&lt;p&gt;Si esto te suena interesante, envíame un email a &lt;a href="mailto:hello@gcollazo.com"&gt;hello@gcollazo.com&lt;/a&gt;, y te
    respondo si puedo ayudar.&lt;/p&gt;
&lt;p&gt;¡Gracias!&lt;/p&gt;</description><pubDate>Wed, 11 Jun 2025 00:00:00 -0000</pubDate><guid isPermaLink="true">https://gcollazo.com/tomando-nuevos-proyectos-de-software</guid><author>hello@gcollazo.com (Giovanni Collazo)</author></item></channel></rss>