<?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>DEV Community: Jesus Almaral</title>
    <description>The latest articles on DEV Community by Jesus Almaral (@hackaprende).</description>
    <link>https://dev.to/hackaprende</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F442645%2Fd714594b-de50-4281-bf51-acd2c2427cdf.jpg</url>
      <title>DEV Community: Jesus Almaral</title>
      <link>https://dev.to/hackaprende</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hackaprende"/>
    <language>en</language>
    <item>
      <title>Creating an AI Agent that curates images for ML datasets (Google All Things Agentic Hackathon)</title>
      <dc:creator>Jesus Almaral</dc:creator>
      <pubDate>Fri, 14 Aug 2026 22:49:51 +0000</pubDate>
      <link>https://dev.to/hackaprende/creating-an-ai-agent-for-curating-images-for-ml-datasets-google-all-things-agentic-hackathon-55fh</link>
      <guid>https://dev.to/hackaprende/creating-an-ai-agent-for-curating-images-for-ml-datasets-google-all-things-agentic-hackathon-55fh</guid>
      <description>&lt;p&gt;The last months I have been working on an app that is like Pokemon GO but for real dogs: a person takes a photo of a dog, the app recognizes the breed and adds it to a dog collection, you can get achievements and participate on seasonal challenges, among other fun stuff. The app's name is Todogs and it's available on &lt;a href="https://play.google.com/store/apps/details?id=com.hackaprende.todogs&amp;amp;gl=US" rel="noopener noreferrer"&gt;Android&lt;/a&gt; and &lt;a href="https://apps.apple.com/mx/app/todogs/id6768757812" rel="noopener noreferrer"&gt;iOS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A big challenge was to make the app work in real time, with no delays, and without needing internet connection to perform the recognition, so for that I needed to train my own Machine Learning model, for what I used Tensorflow (And TFLite to run it), I trained a model with 117 breeds and worked like a charm!.&lt;/p&gt;

&lt;p&gt;Now, in order to train an image model like this you need a big enough dataset for the model accuracy to be good enough for a product, I found some images repositories that I used, needing around 150 images for each one of the 117 breeds. The thing is, even in these repositories there are some mistakes, some breeds with wrong tags, some images are too dark, or multiple dogs show in them, etc.&lt;/p&gt;

&lt;p&gt;Also, now that I want the model to include more breeds and improve itself, I have to search on the internet for more dog images (The more data the better).&lt;/p&gt;

&lt;p&gt;The problem is the manual process is exhausting, these are the steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I go to internet and search for a breed, let's say Chow chow.&lt;/li&gt;
&lt;li&gt;Pick a lot of images and save them, it's cool that there are some repos out there with folders of dogs, so that saves me some time, for other dogs I have to pick the images myself.&lt;/li&gt;
&lt;li&gt;Filter those images one by one, check that they are not studio photos (the real users world is messier), that dogs are not cropped, that no multiple dogs are in the photo, anything that could confuse the model.&lt;/li&gt;
&lt;li&gt;Move the photos to my dataset to re-train.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also, another thing I want to do is use the same photos generated on the app as new data to re-train the model, but there are lots of low-precision and not adequate photos, applying the process above is just unsustainable, I'd need a full time person for that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enter &lt;a href="https://allthingsagentichackathon.devpost.com/" rel="noopener noreferrer"&gt;Google All Things Agentic Hackathon&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I got a link to this Hackathon from a friend, the hackathon is about learning to create AI agents with the Google services and platforms (Cloud Run, Antigravity SDK, VertexAI, Gemini, etc) and create a project using those tools so solve a problem you have.  Right away came to my mind "Wait, I could create an agent for my problem!", so I started digging on it and started building a solution that is going to save me a lot of headaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I wrote this article as part of my submission to the All Things Agentic Hackathon.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The project I started was an AI agent to do the whole process described above in minutes instead of weeks, it works like this: I open the agent, write "Give me 100 Husky images" and it goes to a repository with photos of many dogs, then it picks Husky and goes image by image. Then it starts throwing images away. First the cheap checks in Python: anything too small, anything that won't even open, and near-duplicates caught with perceptual hashing (so the same photo at two sizes gets spotted, not just byte-identical files). Then the expensive one: Gemini looks at every surviving image and answers the questions I used to answer by hand. Is there actually a dog? Is it the breed I asked for? Is there only one? Is it sharp enough? And the weird one: does it look like a photo a phone would take, or like a studio shot? Studio photos are beautiful and useless to me, because my users are outside with bad lighting and a dog that won't sit still.&lt;/p&gt;

&lt;p&gt;The part I'm most proud of is that the agent refuses to guess. If I ask for a breed that isn't in the corpus, it stops and tells me instead of handing me the closest thing it found. If a request is genuinely ambiguous, it asks me which one I meant instead of picking. That sounds like a small thing, but a wrong breed match poisons a training set silently — nothing downstream ever catches it, the model just gets a little worse and you find out months later. At the end it saves the good photos on my Google Drive.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F84j7bhpcmzi4by3ykk1y.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F84j7bhpcmzi4by3ykk1y.png" alt=" " width="800" height="488"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fonlmii44hjalydtixfqe.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fonlmii44hjalydtixfqe.png" alt=" " width="800" height="291"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7a93fzvrvuls67mzq6z.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7a93fzvrvuls67mzq6z.jpg" alt=" " width="800" height="533"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsvvnfwz9f1mfqxjjejhm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsvvnfwz9f1mfqxjjejhm.png" alt=" " width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this project I achieved several things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learned Agents from zero to a production product deployed on Cloud Run.&lt;/li&gt;
&lt;li&gt;Created a tool that's going to be very useful and will save tons of time for a project that I was already working on.&lt;/li&gt;
&lt;li&gt;Got to participate in the hackathon with possibility of earning a prize.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stack used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Google ADK
&lt;/li&gt;
&lt;li&gt;  Google GenAI SDK
&lt;/li&gt;
&lt;li&gt;  Gemini 3.5 Flash
&lt;/li&gt;
&lt;li&gt;  Vertex AI
&lt;/li&gt;
&lt;li&gt;  Cloud Run
&lt;/li&gt;
&lt;li&gt;  Cloud Storage
&lt;/li&gt;
&lt;li&gt;  Secret Manager
&lt;/li&gt;
&lt;li&gt;  Google Drive API
&lt;/li&gt;
&lt;li&gt;  Python 3.12
&lt;/li&gt;
&lt;li&gt;  Pillow
&lt;/li&gt;
&lt;li&gt;  ImageHash
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had to learn in record time, the hackathon had a deadline.&lt;/li&gt;
&lt;li&gt;Each inspected image is one Gemini call, so it's the part that scales with the dataset. I keep it down by shrinking images before sending them, using low media resolution, and only inspecting the images a request actually needs.&lt;/li&gt;
&lt;li&gt;Because I was totally new, I had to learn a big part of the stack from zero, this delayed me and sometimes made me go back because I did something wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plug the agent to my own Firebase Firestore for the photos generated by the users, so my model can learn from it's own mistakes.&lt;/li&gt;
&lt;li&gt;Crazy idea: Scrapping the web so I don't need to get the images together at all or depend on repositories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So it seems a Hackathon I didn't know some weeks ago helped me to solve one of my biggest challenges. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>allthingsagentichackathon</category>
    </item>
    <item>
      <title>Android: executePendingBindings() en RecyclerViews</title>
      <dc:creator>Jesus Almaral</dc:creator>
      <pubDate>Thu, 14 Jan 2021 14:36:22 +0000</pubDate>
      <link>https://dev.to/hackaprende/android-executependingbindings-en-recyclerviews-11ie</link>
      <guid>https://dev.to/hackaprende/android-executependingbindings-en-recyclerviews-11ie</guid>
      <description>&lt;p&gt;Como nada es perfecto, data binding tiene una desventaja con &lt;br&gt;
respecto a findViewById() en Android.&lt;/p&gt;

&lt;p&gt;Android “pinta” los views en la pantalla cada 16ms en promedio, esta actualización de la pantalla puede suceder en menos tiempo, pero si se hacen procesos pesados en el hilo principal (Main Thread) podría causar que la actualización sea de más de 16ms y empezamos a ver lags en la pantalla. Cuando usamos binding para asignar valores a un view, por ejemplo al asignar un texto a un TextView con binding.myTextView.text = “Some text”, el binding no se ejecuta al instante, sino que espera a que pase ese tiempo de 16ms para medir cuanto el área del nuevo texto y luego pintarlo. Es como cuando juegas con tu perro a lanzar la pelota y va por ella pero luego no la suelta hasta que pasas un rato tratando de quitársela (16ms en promedio).&lt;br&gt;
Esto por lo general no nos afecta, por ejemplo cuando abrimos una nueva Activity y pintamos todo con binding, 16ms no son nada. Lanzaste la pelota a tu perro una vez y se la quitas, no pasa nada.&lt;/p&gt;

&lt;p&gt;Pero ahora imagina un recyclerView con decenas de elementos, cuando haces scroll rápidamente y viene el siguiente elemento, puede ser que todavía no hayan pasado los 16ms recomendados, pero podría suceder que cuando aparece el siguiente elemento el binding no haya alcanzado a medir y pintar el anterior, entonces podrían verse lags extraños. Es como si quisieras romper un record de lanzar pelotas y ya viene la siguiente pero tu perro todavía no suelta la anterior.&lt;br&gt;
Esto es poco común, y de hecho si no implementas lo que estoy a punto de mencionarte muchas veces no tendrás problemas, pero es mejor prevenir.&lt;/p&gt;

&lt;p&gt;La solución para prevenir esto es que después de pintar los views de tu ViewHolder, mandes llamar binding.executePendingBindings(), así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inner class EqViewHolder(private val binding: ItemBinding): RecyclerView.ViewHolder(binding.root) {
    fun bind(earthquake: Earthquake) {
        binding.myTextView.text = someString
        binding.myImageView.src = someImage
        // Más cosas que hagas con tus views
        ... 
        // JUSTO AQUÍ VA 👇
        binding.executePendingBindings()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De esta manera, vas a forzar al binding a que no se espere los 16ms, sino que pinte todo inmediatamente. Sencillo, es solo agregar una linea de código, aunque Android no explica mucho para qué sirve en su documentación oficial y por eso a veces es difícil de recordar.&lt;/p&gt;

&lt;p&gt;Espero que los de Android manejen esto automáticamente en el futuro. Mientras tanto te invito a dejar tus comentarios.&lt;br&gt;
Como siempre todos los comentarios y dudas son más que bienvenidos. Te invito a visitar mi blog (&lt;a href="https://www.hackaprende.com"&gt;https://www.hackaprende.com&lt;/a&gt;) o &lt;a href="https://www.youtube.com/c/hackaprende"&gt;mi canal de Youtube&lt;/a&gt; para ver más artículos y cursos sobre diseño, programación y emprendimiento tecnológico.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>databinding</category>
    </item>
    <item>
      <title>Usando Corrutinas con Firebase en Android MVVM</title>
      <dc:creator>Jesus Almaral</dc:creator>
      <pubDate>Thu, 27 Aug 2020 21:21:06 +0000</pubDate>
      <link>https://dev.to/hackaprende/usando-corrutinas-con-firebase-en-android-mvvm-3efp</link>
      <guid>https://dev.to/hackaprende/usando-corrutinas-con-firebase-en-android-mvvm-3efp</guid>
      <description>&lt;p&gt;Estoy desarrollando un proyecto alterno que utiliza la arquitectura MVVM y Firebase/Firestore, para todo el manejo de los hilos y tareas en segundo plano estoy usando corrutinas (coroutines).&lt;/p&gt;

&lt;p&gt;Me acabo de topar con una situación que no me había sucedido, al usar Firebase, este trae sus propios listeners para saber cuando termina un proceso, el cual regresa un DocumentSnapshot que es el que necesitas para extraer los datos. Algo así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyRepository(private val firestoreDb: FirestoreDb) {

    suspend fun downloadSomething(listener: (DocumentSnapshot) -&amp;gt; Unit){
        firestoreDb.collection(collectionName).get()
            .addOnCompleteListener {
                task -&amp;gt;
            if (task.isSuccessful) {
               // Do something ...
               listener(myDocumentSnapshot)
            } else {
               // Show error
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto es llamado desde el ViewModel el cual se vería así.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;viewModelScope.launch {
    repository.downloadSomething {
        documentSnapshot -&amp;gt;
        // Do Something
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pero uno de los puntos principales de las corrutinas es que no necesitemos integrar interfaces y/o listeners para devolver los datos, sino que los podamos devolver en la misma linea para continuar con nuestro código, es decir, en vez de esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun downloadSomething() {
    repository.downloadSomething {
        result -&amp;gt;
        processResult(result)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con las corrutinas podemos hacer esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;suspend fun downloadSomething() {
    val result = repository.downloadSomething()
    processResult(result)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pero tal y como funciona normalmente Firebase no se puede porque como dije antes, ya trae ese listener .addOnCompleteListener integrado, entonces ¿Cómo lo manejamos para poder usar el resultado con corrutinas? La respuesta que encontré fue usar .await()&lt;/p&gt;

&lt;p&gt;await() nos ayuda a esperar a que un proceso se complete sin bloquear otros hilos (Como el Main Thread), haciendo que evitemos tener que llamar un listener una vez que se complete, ya que al terminar se ejecutará la siguiente linea de código del hilo. Pero await() no viene con Firebase sino con las Kotlin Coroutines, por lo que tenemos que agregarlo como dependencia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora que ya podemos usar await() el código de Firebase quedaría así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyRepository(private val firestoreDb: FirestoreDb) {
    suspend fun downloadSomething(): DocumentSnapshot {
        return withContext(Dispatchers.IO) {
            firestoreDb.collection(collectionName).get().await()
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto podemos regresar el DocumentSnapshot y continuar con nuestro trabajo de forma secuencial en el ViewModel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;viewModelScope.launch {
    val documentSnapshot = repository.downloadSomething()
    // Continuar secuencialmente
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto trae ventajas para hacer testing, para manejar excepciones, para cumplir con el MVVM y para manejar los LiveData.&lt;/p&gt;

&lt;p&gt;Como siempre todos los comentarios y dudas son más que bienvenidos. Te invito a visitar mi blog hackaprende.com para ver más artículos y cursos sobre diseño, programación y emprendimiento tecnológico.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>La mejor manera de manejar la versión tu código</title>
      <dc:creator>Jesus Almaral</dc:creator>
      <pubDate>Tue, 28 Jul 2020 23:22:43 +0000</pubDate>
      <link>https://dev.to/hackaprende/la-mejor-manera-de-manejar-la-version-tu-codigo-pb</link>
      <guid>https://dev.to/hackaprende/la-mejor-manera-de-manejar-la-version-tu-codigo-pb</guid>
      <description>&lt;p&gt;Al desarrollar cualquier cosa que involucre código debes llevar un registro de las versiones que vas lanzando continuamente, esto sirve para dos cosas:&lt;/p&gt;

&lt;p&gt;1- Que tus usuarios sepan qué versión tienen y si tienen la más actualizada.&lt;/p&gt;

&lt;p&gt;2- Que los desarrolladores puedan encontrar errores más fácilmente al saber en qué versión se presentan.&lt;/p&gt;

&lt;p&gt;Hay varias formas de llevar registro de la versión, la que yo uso y recomiendo es MAJOR.MINOR.PATCH, se ve así: 1.6.4, a continuación te explico en qué consiste cada parte:&lt;/p&gt;

&lt;p&gt;MAJOR: Mejoras y cambios mayores, esta versión puede empezar en 0 si tu proyecto está en Beta (por ejemplo 0.12.1) y solo se actualiza si sufrió un gran cambio, por ejemplo si cambiaste totalmente la interfaz, si ahora hace algo diferente a lo que hacía antes o incluso si hiciste un proyecto nuevo para reemplazar al anterior. Cambiar MAJOR es muy poco frecuente, si observas muchos productos con años en el mercado apenas van en versiones 2.0 o 3.0. También vale la pena actualizar MAJOR cuando se hacen cambios en la API que ya no serán compatibles con versiones anteriores.&lt;/p&gt;

&lt;p&gt;MINOR: Mejoras y cambios menores, lo más común es cuando agregas una nueva característica a tu aplicación, también podría ser cuando mejoras considerablemente una pantalla, lo normal es que este número cambie con cierta frecuencia (Una o dos veces por mes), más al principio del proyecto que vas a toda velocidad, ya después se estabiliza un poco.&lt;/p&gt;

&lt;p&gt;PATCH: Corrección de bugs y arreglos menores, por ejemplo cuando arreglas un bug que hace que tu app truene, cuando cambias cosas pequeñas de UI, cuando mejoras cosas no visuales, como algo que solo mejora un poco el rendimiento al descargar datos. Este cambiará cada vez que subas una actualización.&lt;/p&gt;

&lt;p&gt;Mi principal razón de preferir 3 números en la versión (2.0.0) en vez de solo dos números (2.0) es porque cuando tenemos dos números no hay forma de diferenciar entre bugs y características nuevas, 4 ya me parecen demasiados.&lt;/p&gt;

&lt;p&gt;Como te estarás preguntando, esto de las versiones tiene algo de feeling, podría ser que lo que para algunos es un PATCH para otros sea un MINOR. Para termina te dejo algunos consejos personales sobre esto del versionamiento:&lt;/p&gt;

&lt;p&gt;Cuando MAJOR se actualiza, MINOR y PATCH se reinician, cuando MINOR se actualiza, PATCH se reinicia, ejemplos: 1.3.10 -&amp;gt; 2.0.0, 1.80.17 -&amp;gt; 1.81.0&lt;/p&gt;

&lt;p&gt;No hay un límite de número, puedes tener 1.9999.1726, aunque lo normal es que el PATCH no suba mucho para cuando ya necesites el siguiente MINOR, pero que el número no te quite el sueño.&lt;/p&gt;

&lt;p&gt;No te estreses mucho decidiendo si la siguiente versión es PATCH o MINOR, lo más importante es tener versiones diferentes que se puedan distinguir más que llevar un conteo exacto.&lt;/p&gt;

&lt;p&gt;¿Qué te parece esta forma de versionar? ¿Estás o no de acuerdo?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
