<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Víctor Villalobos</title>
    <description>The latest articles on Forem by Víctor Villalobos (@viktorvillalobos).</description>
    <link>https://forem.com/viktorvillalobos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F156109%2Fb581c17e-64cb-47be-97ba-35b824e335d2.png</url>
      <title>Forem: Víctor Villalobos</title>
      <link>https://forem.com/viktorvillalobos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/viktorvillalobos"/>
    <language>en</language>
    <item>
      <title>Why we rebuilt our own messaging stack for AI apps</title>
      <dc:creator>Víctor Villalobos</dc:creator>
      <pubDate>Wed, 24 Dec 2025 02:30:27 +0000</pubDate>
      <link>https://forem.com/viktorvillalobos/why-we-rebuilt-our-own-messaging-stack-for-ai-apps-32g7</link>
      <guid>https://forem.com/viktorvillalobos/why-we-rebuilt-our-own-messaging-stack-for-ai-apps-32g7</guid>
      <description>&lt;h1&gt;
  
  
  Why we rebuilt our messaging stack for AI apps
&lt;/h1&gt;

&lt;p&gt;Earlier this year, while building &lt;strong&gt;Nely.ai&lt;/strong&gt;, other developers started asking to use our internal messaging API. What we had built to support our own product was solving problems they were also facing, especially around WhatsApp, pricing, and operational complexity.&lt;/p&gt;

&lt;p&gt;At first, exposing the API seemed like the obvious next step. But very quickly, it became clear that the problem was not access. The problem was the CPaaS model itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI apps changed how messaging is used
&lt;/h2&gt;

&lt;p&gt;Traditional messaging platforms were designed for human-driven communication. One user sends one message at a time. Pricing, APIs, and tooling evolved around that assumption.&lt;/p&gt;

&lt;p&gt;AI apps do not work this way.&lt;/p&gt;

&lt;p&gt;Conversations are generated, routed, retried, and managed automatically by agents and workflows. A single user interaction can trigger multiple background messages, retries, classifications, or follow ups. Messaging becomes part of an execution graph, not a single action.&lt;/p&gt;

&lt;p&gt;Most CPaaS stacks were not designed for this model, especially when pricing is tied to per message usage and fragmented providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing breaks first
&lt;/h2&gt;

&lt;p&gt;When AI agents are involved, per message pricing becomes unpredictable very quickly. Messages are no longer a proxy for value. Many of them are internal, transient, or part of automation logic.&lt;/p&gt;

&lt;p&gt;What actually matters is how many users your system is actively serving and the underlying carrier costs required to deliver those messages.&lt;/p&gt;

&lt;p&gt;This is why we moved away from raw message counting and rebuilt pricing around MAU combined with transparent carrier pass through. This model aligns much better with how AI apps scale and makes costs easier to understand and forecast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation needs fast access to channels
&lt;/h2&gt;

&lt;p&gt;When you automate communication with AI, you usually do not care about providers or setup steps. You just want access to the channels your users already use.&lt;/p&gt;

&lt;p&gt;In many cases, that means WhatsApp or SMS, and that requires a phone number. Getting that number should be fast, simple, and predictable. Waiting days, dealing with external vendors, or navigating fragmented dashboards breaks development flow and slows down experimentation.&lt;/p&gt;

&lt;p&gt;We made phone number provisioning a first class part of the platform. Developers can get an API key, provision a number, and start sending messages in minutes. Everything works together by default, so teams can focus on building AI workflows instead of wiring infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rebuilding the developer experience
&lt;/h2&gt;

&lt;p&gt;Modern developer workflows expect clean abstractions, strong typing, and predictable behavior. Messaging APIs, especially WhatsApp, are often fragmented, inconsistent, and hard to reason about.&lt;/p&gt;

&lt;p&gt;We rebuilt the developer experience from scratch with modern SDKs and simpler primitives. The goal was to abstract away provider specific quirks and expose a consistent interface that fits naturally into AI driven systems.&lt;/p&gt;

&lt;p&gt;Instead of adapting AI workflows to legacy CPaaS constraints, we designed the messaging layer around how AI apps are actually built today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this enables
&lt;/h2&gt;

&lt;p&gt;Rebuilding the stack enabled faster iteration, cleaner architectures, and messaging that no longer feels like a bottleneck. Teams can focus on agents, workflows, and product logic instead of pricing surprises and provider management.&lt;/p&gt;

&lt;p&gt;This work became &lt;strong&gt;Zavu&lt;/strong&gt;, a unified messaging API designed for AI apps and modern communication SaaS.&lt;/p&gt;

&lt;p&gt;You can learn more at &lt;a href="https://zavu.dev" rel="noopener noreferrer"&gt;https://zavu.dev&lt;/a&gt; and explore the documentation at &lt;a href="https://docs.zavu.dev" rel="noopener noreferrer"&gt;https://docs.zavu.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are building AI driven products and have struggled with CPaaS complexity, pricing, or developer experience, we would love to hear what has broken for you.&lt;/p&gt;

</description>
      <category>cpaas</category>
      <category>developers</category>
      <category>messaging</category>
      <category>api</category>
    </item>
    <item>
      <title>Testing Django Rest Framework, la forma correcta</title>
      <dc:creator>Víctor Villalobos</dc:creator>
      <pubDate>Wed, 07 Aug 2019 00:16:51 +0000</pubDate>
      <link>https://forem.com/viktorvillalobos/testeando-drf-la-forma-correcta-40pf</link>
      <guid>https://forem.com/viktorvillalobos/testeando-drf-la-forma-correcta-40pf</guid>
      <description>&lt;p&gt;Post originalmente publicado en:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pythonist.dev/articles/testing-django-drf-manera-correcta/" rel="noopener noreferrer"&gt;https://pythonist.dev/articles/testing-django-drf-manera-correcta/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como desarrolladores django es común tener que testear DRF, pero no siempre lo hacemos de manera correcta, terminamos por crear tests unitarios lentos, no aislados, que nos dificulta encontrar errores y mantener un coverage alto, en este post tratare de explicar de manera fácil&lt;/p&gt;

&lt;p&gt;como testear utilizando pytest como reemplazo a la librería por defecto&lt;br&gt;
de django. Durante todo el proceso, usare las siguiente premisas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Usare la librería pytest para los test unitarios.&lt;/li&gt;
&lt;li&gt;Los tests unitarios no tendrán acceso a la BD. &lt;/li&gt;
&lt;li&gt;Utilizaremos pytest-mock para "Mockear" los métodos que no nos interese testear,
como las cosas propias del framework que vienen ya testeadas por los desarrolladores
del framework.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  ¿Porque los test unitarios no deberían tener acceso a la BD?
&lt;/h3&gt;

&lt;p&gt;Muchos autores han discutido, acerca de esto a través de los años, en este momento, dare&lt;br&gt;
mi opinión personal, basada es mi experiencia como desarrollador web.&lt;/p&gt;

&lt;p&gt;Mientras tu proyecto crece, la creación de fixtures, para la gran cantidad de modelos, se volverá increíblemente molesta, la gran cantidad de migraciones migraciones incrementará el problema, por supuesto, hacer squash de las migraciones cada cierto tiempo puede aminorar esta carga,&lt;br&gt;
pero aun así muchas veces es complicado, todo esto en conjunto tiene un ineludible final, el tiempo de ejecución de los tests crecerá vertiginosamente, por lo que sera un problema para el CI/CD de tu proyecto, y una perdida de tiempo para el equipo en general.&lt;/p&gt;

&lt;p&gt;Por otro lado los test sin acceso a BD nos permite testear de manera aislada.&lt;/p&gt;
&lt;h2&gt;
  
  
  ¿Porque deberían tus tests unitarios ser aislados?
&lt;/h2&gt;

&lt;p&gt;Aislar tus test unitarios es una de las muestras de que tu código esta desacoplado por lo&lt;br&gt;
que estas siguiendo el principio de Unix, has que cada pieza de tu código hace una sola cosa y la hace bien, lo que &lt;br&gt;
por supuesto ayuda a tener un código mas mantenible. Acá te dejo algunas de las ventajas de aislar tus test&lt;br&gt;
unitarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sera fácil encontrar nuestros errores.&lt;/li&gt;
&lt;li&gt;No dependeremos de la BD, por lo que nuestros tests serán muy rápidos.&lt;/li&gt;
&lt;li&gt;Facilitaremos el despliegue de la aplicación.- &lt;/li&gt;
&lt;li&gt;Comprenderemos como funciona el framework mas a profundidad.&lt;/li&gt;
&lt;li&gt;Al empezar a testear de esta manera, nos obligaremos a crear código más "testeable", por lo que sera mas legible, organizado y limpio.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  ¿ Que es Mocking ?
&lt;/h2&gt;

&lt;p&gt;Es un "Doble de Prueba" nos permite Fakear objectos, funciones para que se retornen un valor predeterminados por nosotros, existen varios tipos de Mocking y normalmente los englobamos todos con el termino Mock, si quieres detallar mas los tipos, te recomiendo este &lt;a href="https://www.genbeta.com/desarrollo/desmitificando-los-dobles-de-test-mocks-stubs-and-friends" rel="noopener noreferrer"&gt;articulo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En este post usare la librería pytest-mock y django-mock-queries, para Fakear QuerySets de django, y omitir el acceso la BD, con lo que logramos test, que no dependan de la bd y que se ejecutan en micro segundos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sabiendo esto, ¿que testeamos?
&lt;/h2&gt;

&lt;p&gt;Por lo regular al desarrollar una API Rest usando DRF se deben testear,&lt;br&gt;
estos componentes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Serializer.&lt;/li&gt;
&lt;li&gt;View.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A esto debemos añadirle cosas comunes del framework, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helpers, UseCase, Services (Como llamen en tu equipo a la capa
de la lógica del negocio).&lt;/li&gt;
&lt;li&gt;Métodos del manager del modelo.&lt;/li&gt;
&lt;li&gt;Métodos del modelo. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estos últimos debemos evitar usarlos, ya que el &lt;strong&gt;modelo&lt;/strong&gt; debería ser usado solo como&lt;br&gt;
capa de acceso a datos, no una capa de lógica del negocio, como muchos (y me incluyo) en nuestros &lt;br&gt;
inicios, comentemos el error de usar.&lt;/p&gt;
&lt;h3&gt;
  
  
  El modelo.
&lt;/h3&gt;

&lt;p&gt;Para este caso de estudio, crearemos una pequeña API REST, para gestión de mi garage personal,&lt;br&gt;
en el cual solo tendremos un pequeño modelo llamado &lt;strong&gt;Car&lt;/strong&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Managers
&lt;/h3&gt;

&lt;p&gt;Los &lt;a href="https://docs.djangoproject.com/en/2.2/topics/db/managers/" rel="noopener noreferrer"&gt;managers de Django&lt;/a&gt; nos permiten separar la lógica de negocio de los modelos y como bono extra nos ayudan a mantener el &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;principio DRY(Don't repeat yourself)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Continuando con el ejemplo imaginemos que queremos obtener todos los autos cargados al sistema en una fecha especifica (Si, si tengo un&lt;br&gt;
inmenso garage imaginario), para esto debemos usar el ORM de Django y ejecutar esta consulta:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Car.objects.filter(created=my_date).order_by('year')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Sabiendo esto y para no repetir código a lo largo de nuestra aplicación con el objetivo de facilitar el testing, creamos un manager, y agregamos un nuevo metodo &lt;strong&gt;get_cars_by_created&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Test:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Serializer
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Test:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Vista / ViewSet
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Test: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Ahora podemos ver el coverage de nuestra aplicación:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmai74wuj4h8g55saysh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmai74wuj4h8g55saysh.png" alt="Coverage" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y eso es todo, con esto tendremos un coverage 100% en nuestra app, testeamos de manera separada, todo nuestro código e hicimos del mundo un lugar mejor, si quieren ver el repositorio completo &lt;a href="https://github.com/viktorvillalobos/testing-drf-pytest" rel="noopener noreferrer"&gt;aquí esta&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>spanish</category>
      <category>drf</category>
      <category>test</category>
    </item>
    <item>
      <title>¿Que es el PEP 8 y porque debería implementarlo?</title>
      <dc:creator>Víctor Villalobos</dc:creator>
      <pubDate>Sun, 14 Apr 2019 05:28:23 +0000</pubDate>
      <link>https://forem.com/viktorvillalobos/que-es-el-pep-8-y-porque-deberia-implementarlo-54bh</link>
      <guid>https://forem.com/viktorvillalobos/que-es-el-pep-8-y-porque-deberia-implementarlo-54bh</guid>
      <description>&lt;p&gt;Originalmente publicado en &lt;a href="https://pythonist.dev/articles/porque-usar-pep8-python/" rel="noopener noreferrer"&gt;pythonist.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En el mundo del desarrollo es muy común conseguir distintas formas de hacer las cosas, puedes organizar el código como quieras, incluso escribirlo de forma abstracta, esto en muchos casos logra que nuestra profesión sea considerada muchas veces un arte y no una ingeniería, debido a la ausencia de una base solida de como y porque se deben hacer las cosas de una manera en especifico, por lo que es común que conseguimos software mal redactado y mal organizado que al final nadie quiere leer. &lt;/p&gt;

&lt;p&gt;La diferencia mas tangible de python con otros lenguajes es que la comunidad tiene como emblema la legibilidad del código, el ZEN de python establece que &lt;strong&gt;"Debe haber una y preferiblemente solo una forma obvia de hacer las cosas"&lt;/strong&gt;, por lo que siguiendo este principio, se definio una guiá de estilo única descrita íntegramente en el Python Enhancement Proposal numero 8, abreviado como PEP 8. En esta se define al pie de la letra, como debería estar escrito nuestro código python.&lt;/p&gt;

&lt;p&gt;Muchos sabemos que al común denominador de los desarrolladores de software no suele gustarle la obligación impuesta de una forma de hacer su trabajo, con el pasar del tiempo apreciamos que toda la comunidad use una guiá de estilo única, ya que al final del camino nos facilita nuestro trabajo, mejorando mucho nuestra productividad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Puntos mas importantes del PEP8
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Basicos:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Siempre preferir espacios en ves de tabs.&lt;/li&gt;
&lt;li&gt;Usar 4 espacios en la indentación.&lt;/li&gt;
&lt;li&gt;Las lineas deben tener menos de 80 caracteres.&lt;/li&gt;
&lt;li&gt;Las lineas que pasen de esta longitud deben ser divididas en dos lineas, y la linea resultante de la división debe estar indentada.&lt;/li&gt;
&lt;li&gt;En una fila, las funciones y las clases deben estar separadas por dos lineas en blanco.&lt;/li&gt;
&lt;li&gt;No colocar espacios alrededor de indices de lista, llamadas de funciones o argumentos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Nombres&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Las funciones deben estar declaradas en minúscula y las palabras separadas por guiones bajos &lt;code&gt;def funcion_cool()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Los metodos privados de una clase deben comenzar con doble guion bajo &lt;code&gt;def __private_method()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Los métodos protegidos de una clase deben comenzar con guion bajo &lt;code&gt;def _protected_method()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Las clases y excepciones deben ser capitalizadas por palabra &lt;code&gt;class SuperClass&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Constantes del module deben estar en mayusculas separadas por guiones bajos &lt;code&gt;NUMERO_MAXIMO = 10&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Los métodos de instancia de una clase deben usar el parametro &lt;code&gt;self&lt;/code&gt; como primer parámetro.&lt;/li&gt;
&lt;li&gt;Los métodos de clase deben usar cls como primer parámetro, para referirse a la misma clase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Expresiones&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Usar negación en linea (&lt;code&gt;if a is not b&lt;/code&gt;) en vez de negar una expresion positiva &lt;code&gt;(if not a is b)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No validar valores vacíos usando len &lt;code&gt;if (len(lista) == 0)&lt;/code&gt;, usar &lt;code&gt;if not lista&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Siempre coloca los &lt;code&gt;imports&lt;/code&gt; al inicio del archivo.&lt;/li&gt;
&lt;li&gt;Siempre importa funciones y clases usando &lt;code&gt;from my_module import MyClass&lt;/code&gt; en ves de importar el modulo completo &lt;code&gt;import my_module&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Si aun debes usar imports relativos, usa la sintaxis &lt;code&gt;from . import my_module&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Las importaciones siempre deben estar en el orden:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Módulos de la librería standar.&lt;/li&gt;
&lt;li&gt;Módulos externos.&lt;/li&gt;
&lt;li&gt;Módulos del proyecto.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Y cada sección debe estar en orden alfabético.&lt;/p&gt;

&lt;p&gt;Siguiendo estos sencillos puntos, tu equipo de desarrollo y toda la comunidad python estará muy alegre de tenerte. &lt;/p&gt;

</description>
      <category>python</category>
      <category>spanish</category>
    </item>
  </channel>
</rss>
