<?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: Gerardo Andrés Ruiz Castillo</title>
    <description>The latest articles on DEV Community by Gerardo Andrés Ruiz Castillo (@geanruca).</description>
    <link>https://dev.to/geanruca</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%2F2362403%2F23c8e30e-3559-4269-9287-e8c095c40fae.webp</url>
      <title>DEV Community: Gerardo Andrés Ruiz Castillo</title>
      <link>https://dev.to/geanruca</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/geanruca"/>
    <language>en</language>
    <item>
      <title>Enhancing UI Reactivity: Dynamic Editor Panels with Alpine.js and PHP</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Mon, 18 May 2026 22:01:03 +0000</pubDate>
      <link>https://dev.to/geanruca/enhancing-ui-reactivity-dynamic-editor-panels-with-alpinejs-and-php-1hj4</link>
      <guid>https://dev.to/geanruca/enhancing-ui-reactivity-dynamic-editor-panels-with-alpinejs-and-php-1hj4</guid>
      <description>&lt;p&gt;Building highly interactive user interfaces often means juggling complex client-side state and ensuring seamless communication with the backend. For the Breniapp/brenia project, we recently tackled this by revamping the editor panel to offer a more fluid user experience, specifically focusing on dynamic layer expansion and real-time canvas preview updates. This overhaul leveraged Alpine.js for frontend reactivity, streamlining how users interact with generated content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Static Panels vs. Dynamic Needs
&lt;/h2&gt;

&lt;p&gt;Previously, parts of our editor panel might have relied on full page reloads or more cumbersome JavaScript to manage UI state. As features like iterative content generation and customizable layers became more central, the need for an instant, reactive UI grew. Users expect immediate visual feedback and the ability to expand and collapse detailed controls without disruption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alpine.js for Lightweight Reactivity
&lt;/h2&gt;

&lt;p&gt;The core of this enhancement lies in Alpine.js, a lightweight JavaScript framework that brings the power of reactive data binding directly to your HTML markup. We introduced &lt;code&gt;x-data&lt;/code&gt; to manage the state of the editor panel, specifically for &lt;code&gt;expandedLayer&lt;/code&gt; (controlling the visibility of layer details) and &lt;code&gt;canvasImageUrl&lt;/code&gt; (displaying the generated content preview). This declarative approach simplifies complex UI logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event-Driven Updates: Seamless Canvas Previews
&lt;/h2&gt;

&lt;p&gt;A critical aspect was updating the canvas preview in real-time as content is generated. Rather than polling or manual DOM manipulation, we implemented an event-driven mechanism. After a generation process completes, a &lt;code&gt;generation-completed&lt;/code&gt; event is dispatched on the client-side. Alpine.js then listens for this event and updates the &lt;code&gt;canvasImageUrl&lt;/code&gt; state, instantly refreshing the preview without user intervention.&lt;/p&gt;

&lt;p&gt;This approach cleanly separates the backend generation logic from the frontend rendering, making the system more robust and easier to maintain. The PHP backend is responsible for processing the generation request and ultimately providing the new image URL, which is then picked up by the Alpine.js component.&lt;/p&gt;

&lt;p&gt;Consider a simplified PHP endpoint that might handle the image generation request and provide the updated URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Services\ImageGenerator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Framework\Http\Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EditorApiController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;ImageGenerator&lt;/span&gt; &lt;span class="nv"&gt;$generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ImageGenerator&lt;/span&gt; &lt;span class="nv"&gt;$generator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;generateCanvasPreview&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Assume request body contains necessary generation parameters&lt;/span&gt;
        &lt;span class="nv"&gt;$params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'php://input'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Call a service to process and generate the image&lt;/span&gt;
        &lt;span class="nv"&gt;$newImageUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Return the new URL to the client&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'imageUrl'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$newImageUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Generation complete!'&lt;/span&gt;
        &lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This PHP code snippet illustrates a backend endpoint that would receive a request, trigger the image generation process (e.g., in a &lt;code&gt;ImageGenerator&lt;/code&gt; service), and then return a JSON response containing the &lt;code&gt;imageUrl&lt;/code&gt;. On the frontend, an Alpine.js component would make an AJAX call to this endpoint, and upon receiving the &lt;code&gt;imageUrl&lt;/code&gt;, dispatch a custom &lt;code&gt;generation-completed&lt;/code&gt; event (or directly update its state if the AJAX call is handled within the component).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Actionable Takeaway
&lt;/h2&gt;

&lt;p&gt;The shift to a more reactive editor panel using Alpine.js significantly enhanced the user experience within Breniapp/brenia. By leveraging &lt;code&gt;x-data&lt;/code&gt; for local component state and event listeners for inter-component communication, we achieved dynamic UI behavior with minimal JavaScript overhead. If you're looking to add interactive elements and real-time feedback to your web applications without the complexity of a full-blown framework, consider Alpine.js. Pair it with a robust PHP backend for data processing, and you can build highly responsive interfaces that delight users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actionable Takeaway:&lt;/strong&gt; When faced with static UI elements that need to become dynamic, explore lightweight JavaScript frameworks like Alpine.js for managing frontend state and implementing event-driven updates. Design your backend APIs to provide the necessary data payloads for these frontend interactions.&lt;/p&gt;

</description>
      <category>php</category>
      <category>alpinejs</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Manejo de Errores y Prevención de Publicaciones Duplicadas en devlog-ist/landing</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Wed, 11 Mar 2026 07:04:04 +0000</pubDate>
      <link>https://dev.to/geanruca/manejo-de-errores-y-prevencion-de-publicaciones-duplicadas-en-devlog-istlanding-3l3d</link>
      <guid>https://dev.to/geanruca/manejo-de-errores-y-prevencion-de-publicaciones-duplicadas-en-devlog-istlanding-3l3d</guid>
      <description>&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;En el proyecto devlog-ist/landing, que permite a los usuarios publicar contenido en diversas plataformas, se ha trabajado en mejorar la robustez del sistema y prevenir problemas comunes relacionados con la publicación de contenido.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Desafío
&lt;/h2&gt;

&lt;p&gt;El sistema presentaba dos problemas principales:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Errores 500 al alcanzar el límite diario de publicaciones:&lt;/strong&gt; Cuando un usuario intentaba publicar más contenido del permitido en un día, el sistema respondía con un error 500 genérico, lo cual no era informativo ni amigable.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Publicaciones duplicadas:&lt;/strong&gt; En ciertas ocasiones, el sistema programaba publicaciones y, al mismo tiempo, el usuario intentaba publicar el mismo contenido de forma inmediata, resultando en una doble publicación.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  La Solución
&lt;/h2&gt;

&lt;p&gt;Para abordar estos problemas, se implementaron las siguientes soluciones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Manejo de &lt;code&gt;DailyPostLimitReachedException&lt;/code&gt;:&lt;/strong&gt; Se capturó la excepción &lt;code&gt;DailyPostLimitReachedException&lt;/code&gt; en todas las acciones de publicación (LinkedIn, Dev.to, individual y masiva). En lugar de mostrar un error 500, se muestra una notificación traducida al idioma del usuario, indicando que ha alcanzado su límite diario de publicaciones.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Lógica de publicación&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DailyPostLimitReachedException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Mostrar notificación traducida&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'daily_publish_limit_reached'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cancelación de publicaciones programadas pendientes:&lt;/strong&gt; Al publicar contenido de forma inmediata, se cancelan los registros de &lt;code&gt;ScheduledPost&lt;/code&gt; pendientes para evitar la doble publicación desde la cola de programación.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Cancelar publicaciones programadas pendientes&lt;/span&gt;
&lt;span class="nc"&gt;ScheduledPost&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'post_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$postId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Decisiones Clave
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Traducciones:&lt;/strong&gt; Se agregaron traducciones para el mensaje &lt;code&gt;daily_publish_limit_reached&lt;/code&gt; en los cuatro idiomas soportados (inglés, español, francés y alemán).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Robustez:&lt;/strong&gt; Manejar la excepción específica mejora la experiencia del usuario al proporcionar información clara sobre el problema.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Prevención:&lt;/strong&gt; Cancelar las publicaciones programadas evita la duplicación de contenido, manteniendo la integridad del sistema.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resultados
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Se eliminaron los errores 500 relacionados con el límite diario de publicaciones, mejorando la experiencia del usuario.&lt;/li&gt;
&lt;li&gt;  Se previnieron las publicaciones duplicadas, asegurando que el contenido se publique solo una vez.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lecciones Aprendidas
&lt;/h2&gt;

&lt;p&gt;Es crucial manejar las excepciones específicas en lugar de depender de errores genéricos. Además, la prevención de problemas a través de la cancelación de tareas redundantes puede mejorar significativamente la fiabilidad del sistema.&lt;/p&gt;

</description>
      <category>php</category>
    </item>
    <item>
      <title>Optimizing Deployments for Low-Resource VPS Environments</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Sat, 07 Mar 2026 07:31:05 +0000</pubDate>
      <link>https://dev.to/geanruca/optimizing-deployments-for-low-resource-vps-environments-1eki</link>
      <guid>https://dev.to/geanruca/optimizing-deployments-for-low-resource-vps-environments-1eki</guid>
      <description>&lt;p&gt;Deploying applications to low-resource Virtual Private Servers (VPS) can be challenging. This post explores strategies for optimizing deployments to minimize resource consumption and improve reliability, specifically for the devlog-ist/landing project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Low-resource VPS environments often suffer from limited RAM and CPU. Standard deployment procedures can easily exhaust these resources, leading to slow deployments, failed builds, and application instability. The goal is to reduce the memory footprint and optimize build processes to fit within the constraints of the VPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies for Optimization
&lt;/h2&gt;

&lt;p&gt;Several techniques can be employed to optimize deployments for low-resource environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Reusing &lt;code&gt;node_modules&lt;/code&gt;:&lt;/strong&gt; Similar to the &lt;code&gt;vendor/&lt;/code&gt; directory in PHP projects, the &lt;code&gt;node_modules/&lt;/code&gt; directory can be reused from the previous release. This avoids re-downloading and re-installing dependencies on every deploy, saving significant time and bandwidth. The principle here is that dependencies change less frequently than the application code itself.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Limiting Node RAM Usage:&lt;/strong&gt; Node.js can consume substantial RAM during builds. By setting the &lt;code&gt;NODE_OPTIONS=--max-old-space-size=512&lt;/code&gt; environment variable, we can limit the maximum memory Node.js can use. This prevents the build process from exhausting all available RAM and triggering out-of-memory errors.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Utilizing &lt;code&gt;npm ci --prefer-offline&lt;/code&gt;:&lt;/strong&gt; The &lt;code&gt;npm ci&lt;/code&gt; command performs a clean install from the &lt;code&gt;package-lock.json&lt;/code&gt; file, ensuring consistent dependency versions. The &lt;code&gt;--prefer-offline&lt;/code&gt; flag instructs npm to use the local cache whenever possible, further reducing network activity and improving speed.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Splitting Deployments into Phases:&lt;/strong&gt; Separating the deployment process into pre-activate and post-activate phases allows for more granular control and error handling. Pre-activate tasks can include copying necessary files and running database migrations, while post-activate tasks can involve clearing caches and restarting services.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Timing Deployment Phases:&lt;/strong&gt; Adding timing information to each deployment phase (e.g., using &lt;code&gt;time&lt;/code&gt; command) helps identify bottlenecks and areas for further optimization. This allows for data-driven decisions when making changes to the deployment process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;The following example demonstrates how to limit Node RAM usage in a deployment script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NODE_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;--max-old-space-size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512 npm ci &lt;span class="nt"&gt;--prefer-offline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command sets the &lt;code&gt;NODE_OPTIONS&lt;/code&gt; environment variable to limit the maximum old space size to 512MB before running &lt;code&gt;npm ci&lt;/code&gt;. This prevents the Node.js process from consuming excessive memory during installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Optimizing deployments for low-resource VPS environments requires careful consideration of resource consumption and build processes. By reusing &lt;code&gt;node_modules&lt;/code&gt;, limiting Node RAM usage, utilizing &lt;code&gt;npm ci --prefer-offline&lt;/code&gt;, splitting deployments into phases, and timing deployment steps, we can significantly improve the reliability and speed of deployments, even on constrained hardware.&lt;/p&gt;

</description>
      <category>node</category>
      <category>php</category>
      <category>javascript</category>
      <category>vps</category>
    </item>
    <item>
      <title>Tenant-Specific Scheduling in devlog-ist/landing</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Sat, 07 Mar 2026 06:00:05 +0000</pubDate>
      <link>https://dev.to/geanruca/tenant-specific-scheduling-in-devlog-istlanding-2b8k</link>
      <guid>https://dev.to/geanruca/tenant-specific-scheduling-in-devlog-istlanding-2b8k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;devlog-ist/landing&lt;/code&gt; project, which likely involves a landing page generator with publishing capabilities, a key requirement is the ability to tailor content scheduling to individual tenants. This post explores how configurable publishing schedules were introduced to provide a more natural and less bot-like content delivery experience, enhancing the platform's flexibility and user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Hardcoded Scheduling
&lt;/h2&gt;

&lt;p&gt;Previously, the post scheduling mechanism relied on hardcoded values within the &lt;code&gt;PostStaggeringService&lt;/code&gt;. This approach lacked the flexibility needed to accommodate the diverse needs of different tenants. All tenants were subject to the same rigid schedule, leading to potentially unnatural publishing patterns and a one-size-fits-all experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Configurable Tenant Settings
&lt;/h2&gt;

&lt;p&gt;The solution involved introducing configurable publishing schedule settings for each tenant. Specifically, tenants can now adjust the following parameters via the AutomationSettings page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Publish Window Hours (Start/End):&lt;/strong&gt; Define the time frame during which posts should be published.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Maximum Posts Per Hour:&lt;/strong&gt; Control the density of posts published within the defined window.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Jitter Minutes:&lt;/strong&gt; Introduce randomness to the exact publishing time, making the schedule less predictable and more organic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Details
&lt;/h2&gt;

&lt;p&gt;The hardcoded values in &lt;code&gt;PostStaggeringService&lt;/code&gt; were replaced with these tenant-specific settings. This allows each tenant to customize their publishing schedule according to their preferences and audience behavior. For example, consider a hypothetical scenario where a tenant wants to publish content primarily during business hours, with a limited number of posts per hour and a slight variation in timing to avoid appearing robotic. The configuration might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$tenantSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'publish_start_hour'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'publish_end_hour'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'max_posts_per_hour'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'jitter_minutes'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// The PostStaggeringService would then use these settings to determine the&lt;/span&gt;
&lt;span class="c1"&gt;// appropriate time to publish each post.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Benefits of Configurable Scheduling
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Improved User Experience:&lt;/strong&gt; Tenants can tailor content delivery to their specific audience, leading to higher engagement.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;More Natural Publishing Patterns:&lt;/strong&gt; The introduction of jitter and customizable windows prevents the appearance of automated bot-like behavior.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Increased Flexibility:&lt;/strong&gt; The platform becomes more adaptable to the diverse needs of different tenants.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By introducing configurable publishing schedules, the &lt;code&gt;devlog-ist/landing&lt;/code&gt; project has significantly enhanced its content delivery capabilities. This change provides tenants with the flexibility they need to create a more engaging and natural user experience, moving away from rigid, hardcoded schedules towards a more dynamic and personalized approach.&lt;/p&gt;

</description>
      <category>php</category>
    </item>
    <item>
      <title>Enhancing Security Audits: Avoiding False Positives in File Path Detection</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Fri, 06 Mar 2026 12:33:04 +0000</pubDate>
      <link>https://dev.to/geanruca/enhancing-security-audits-avoiding-false-positives-in-file-path-detection-4o63</link>
      <guid>https://dev.to/geanruca/enhancing-security-audits-avoiding-false-positives-in-file-path-detection-4o63</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;devlog-ist/landing&lt;/code&gt; project, we're continually working to improve our security posture. A recent focus has been on refining our security auditing tools to reduce false positives, particularly around the detection of potentially sensitive file paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Our automated security audits sometimes flagged placeholder file paths as potential exposures of sensitive information. For example, paths like &lt;code&gt;/path/to/certificate&lt;/code&gt; or &lt;code&gt;/path/to/private/key&lt;/code&gt; were incorrectly identified as containing actual private keys or certificates. This was due to the LLM misinterpreting these paths, which were intended only as examples, as real file locations containing sensitive data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;To address this, we've reinforced the rule that paths matching the &lt;code&gt;/path/to/&lt;/code&gt; pattern are always examples. This helps the LLM to correctly interpret these paths and avoid flagging them as potential security risks. Here's an example of how we might handle this in code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SecurityAudit&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isSensitivePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Check if the path matches the /path/to/ pattern and exclude it from sensitive checks&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/^\/path\/to\//'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// It's an example path, not a real one&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Perform other checks for sensitive files (e.g., checking for known certificate extensions)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strpos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'.pem'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;strpos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'.key'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Potentially sensitive file&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet demonstrates how we can use a regular expression to identify placeholder paths and exclude them from further security checks. By explicitly excluding paths that match the &lt;code&gt;/path/to/&lt;/code&gt; pattern, we prevent false positives and ensure that our security audits focus on real potential vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Decisions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Regular Expression Matching&lt;/strong&gt;: Using &lt;code&gt;preg_match&lt;/code&gt; to identify example paths based on the &lt;code&gt;/path/to/&lt;/code&gt; pattern.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Exclusion Logic&lt;/strong&gt;: Implementing logic to exclude identified example paths from sensitive file checks.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;By implementing this change, we've significantly reduced the number of false positives in our security audits. This allows our security team to focus on real potential vulnerabilities, improving our overall security posture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;This experience highlights the importance of context in security auditing. Automated tools must be able to distinguish between example paths and real file locations to avoid generating unnecessary alerts. By carefully crafting our audit rules and incorporating contextual awareness, we can improve the accuracy and effectiveness of our security audits.&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>llm</category>
    </item>
    <item>
      <title>Tenant Landing Page Optimization: Caching for Performance</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Fri, 06 Mar 2026 10:55:03 +0000</pubDate>
      <link>https://dev.to/geanruca/tenant-landing-page-optimization-caching-for-performance-18nf</link>
      <guid>https://dev.to/geanruca/tenant-landing-page-optimization-caching-for-performance-18nf</guid>
      <description>&lt;p&gt;The &lt;code&gt;devlog-ist/landing&lt;/code&gt; project delivers landing pages for tenants. A recent optimization focused on reducing database load by implementing a caching strategy for tenant-specific landing page data. This change significantly improves performance by minimizing redundant database queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Previously, each request to a tenant's landing page triggered multiple database queries to fetch stats, badges, recommendations, and post languages. In particular, loading all posts to generate statistics resulted in a substantial performance bottleneck. This "load ALL posts for stats" query ran on every landing page hit, even if the data hadn't changed. This approach was clearly unsustainable as the number of tenants and posts grew.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Caching Tenant Landing Page Data
&lt;/h2&gt;

&lt;p&gt;To address this, a caching mechanism was introduced. Now, tenant-specific landing page data is cached with a Time-To-Live (TTL) of one hour. This means that the first request for a tenant's landing page will still incur the database queries, but subsequent requests within the hour will be served from the cache. The cache is automatically invalidated whenever a post is saved or deleted, ensuring data consistency.&lt;/p&gt;

&lt;p&gt;Here's a simplified example of how the caching might be implemented in PHP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\Cache\Adapter\FilesystemAdapter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FilesystemAdapter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'tenant_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$tenantId&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'_landing_page_data'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$landingPageData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$cache&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Fetch data from the database&lt;/span&gt;
    &lt;span class="nv"&gt;$stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTenantStatsFromDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$badges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTenantBadgesFromDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$recommendations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTenantRecommendationsFromDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$postLanguages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTenantPostLanguagesFromDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'stats'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$stats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'badges'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$badges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'recommendations'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$recommendations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'postLanguages'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$postLanguages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Use $landingPageData to render the landing page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet demonstrates how the Symfony cache component can be used to store and retrieve tenant landing page data. The &lt;code&gt;get&lt;/code&gt; method either returns the cached data or executes the provided closure to fetch fresh data from the database if the cache is empty or expired.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Impact
&lt;/h2&gt;

&lt;p&gt;This caching strategy significantly reduces the number of database queries per request, particularly on cache warm. More importantly, it eliminates the expensive "load ALL posts for stats" query that previously ran on every landing page hit. This leads to improved landing page load times and reduced database load, resulting in a better user experience and improved system scalability.&lt;/p&gt;

</description>
      <category>php</category>
      <category>caching</category>
      <category>performance</category>
    </item>
    <item>
      <title>Geolocation Optimization: Caching and Local Database Lookup</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Fri, 06 Mar 2026 10:43:05 +0000</pubDate>
      <link>https://dev.to/geanruca/geolocation-optimization-caching-and-local-database-lookup-3c5g</link>
      <guid>https://dev.to/geanruca/geolocation-optimization-caching-and-local-database-lookup-3c5g</guid>
      <description>&lt;p&gt;This post discusses optimizing geolocation lookups in the &lt;code&gt;devlog-ist/landing&lt;/code&gt; project, which enhances user experience by personalizing content based on location.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Initially, the application relied on an external HTTP service (&lt;code&gt;ip-api.com&lt;/code&gt;) to determine a visitor's country based on their IP address. This approach introduced significant latency, adding approximately 1 second to each page load. Furthermore, relying on an external service meant potential rate limits and service disruptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;To address these issues, we implemented two key optimizations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Local Database Lookup:&lt;/strong&gt; Replaced the external HTTP calls with lookups against a local MaxMind GeoLite2-City database. This eliminates network latency and reliance on an external service.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Caching:&lt;/strong&gt; Introduced a caching layer to minimize repeat lookups. Resolved country codes are cached for 7 days, while unresolvable IPs are cached for 1 hour.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementation Details
&lt;/h2&gt;

&lt;p&gt;The local database lookup is performed using the &lt;code&gt;maxminddb&lt;/code&gt; package in Go. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"net"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/oschwald/geoip2/maxminddb"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;maxminddb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GeoLite2-City.mmdb"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseIP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Country&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;IsoCode&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"iso_code"`&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="s"&gt;`json:"country"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Country code: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsoCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet demonstrates how to open the MaxMind database, perform a lookup for a given IP address, and extract the country code.  A similar function integrates with a caching layer (e.g., Redis) to store the results.  When an IP address needs to be geolocated, the cache is checked first; if the result is not in the cache, the local database lookup is performed, and the result is then stored in the cache with the appropriate expiration time.&lt;/p&gt;

&lt;p&gt;To simplify the process of keeping the GeoLite2-City database up-to-date, an artisan command was added to download the latest version.&lt;/p&gt;

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

&lt;p&gt;By switching to a local database and implementing caching, we significantly reduced the latency associated with geolocation lookups. This resulted in faster page load times and an improved user experience.  The application is also more resilient to external service outages and rate limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Optimizing geolocation lookups can have a significant impact on application performance and reliability.  By leveraging local databases and caching, developers can minimize latency and improve the user experience. Regularly updating the local database is crucial to ensure accuracy.&lt;/p&gt;

</description>
      <category>go</category>
      <category>database</category>
      <category>caching</category>
      <category>optimization</category>
    </item>
    <item>
      <title>Optimizing Tenant Schema Access in Go Applications</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Fri, 06 Mar 2026 06:01:04 +0000</pubDate>
      <link>https://dev.to/geanruca/optimizing-tenant-schema-access-in-go-applications-37lp</link>
      <guid>https://dev.to/geanruca/optimizing-tenant-schema-access-in-go-applications-37lp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In multi-tenant applications, efficient management of database schemas is crucial for performance. This post explores how caching and schema tracking can significantly reduce database load in Go applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Accessing tenant-specific data often involves repetitive database queries to check for schema existence and set the appropriate search path. These redundant operations can lead to performance bottlenecks, especially when dealing with a large number of tenants. Specifically, the &lt;code&gt;devlog-ist/landing&lt;/code&gt; project experienced a high volume of &lt;code&gt;pg_class&lt;/code&gt; queries and redundant &lt;code&gt;SET search_path&lt;/code&gt; statements, impacting overall application responsiveness.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;To address these performance issues, the following optimizations were implemented:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Caching Tenant Table Existence:&lt;/strong&gt; The result of the &lt;code&gt;tenantsTableExists()&lt;/code&gt; check is now cached using a persistent cache with a Time-To-Live (TTL) of one hour. This eliminates a significant number of &lt;code&gt;pg_class&lt;/code&gt; queries.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Tracking Applied Schema:&lt;/strong&gt; The application now tracks the currently applied schema. Before executing a &lt;code&gt;SET search_path&lt;/code&gt; statement, it checks if the desired tenant schema is already set. If so, the redundant statement is skipped.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Skipping Redundant Schema Reset:&lt;/strong&gt; Similarly, the &lt;code&gt;resetTenantSchema()&lt;/code&gt; function is skipped when the schema is already null, avoiding unnecessary database interactions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These optimizations are implemented in Go, leveraging caching mechanisms and state management to minimize database load. Here's an example illustrating the concept of caching the schema existence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;schemaCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cacheTTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;checkSchemaExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemaName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Check if the schema exists in the cache&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;schemaCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Schema existence retrieved from cache for:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Simulate a database call to check schema existence&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Checking schema existence in database for:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// In real implementation, this would be a database query&lt;/span&gt;
    &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;simulateDatabaseCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Store the result in the cache with a TTL&lt;/span&gt;
     &lt;span class="n"&gt;schemaCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;

    &lt;span class="c"&gt;// Invalidate cache after TTL&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheTTL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemaCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cache invalidated for:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;simulateDatabaseCheck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemaName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Replace with actual database query&lt;/span&gt;
    &lt;span class="c"&gt;// For example:&lt;/span&gt;
    &lt;span class="c"&gt;// err := db.QueryRow("SELECT 1 FROM pg_namespace WHERE nspname = $1", schemaName).Scan(nil)&lt;/span&gt;
    &lt;span class="c"&gt;// return err == nil&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c"&gt;// Simulating schema exists&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;schemaName&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"tenant_123"&lt;/span&gt;
     &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;checkSchemaExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Schema exists:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Subsequent check will use the cache&lt;/span&gt;
     &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkSchemaExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Schema exists:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Give some time for goroutine to execute&lt;/span&gt;


&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;These optimizations resulted in a significant reduction in database queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Approximately 13,000 &lt;code&gt;pg_class&lt;/code&gt; queries per day were eliminated.&lt;/li&gt;
&lt;li&gt;  Around 10,000 redundant &lt;code&gt;SET search_path&lt;/code&gt; statements per day were skipped.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This leads to improved application performance and reduced load on the database server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Caching and state management are powerful tools for optimizing database-intensive applications. By identifying and eliminating redundant database operations, developers can achieve significant performance gains. Regular monitoring and profiling of database queries can help identify areas for optimization.&lt;/p&gt;

</description>
      <category>go</category>
      <category>caching</category>
      <category>optimization</category>
      <category>database</category>
    </item>
    <item>
      <title>Refactoring for Readability: Moving Sections in devlog-ist/landing</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Thu, 05 Mar 2026 12:29:06 +0000</pubDate>
      <link>https://dev.to/geanruca/refactoring-for-readability-moving-sections-in-devlog-istlanding-37ha</link>
      <guid>https://dev.to/geanruca/refactoring-for-readability-moving-sections-in-devlog-istlanding-37ha</guid>
      <description>&lt;p&gt;When working on a project like devlog-ist/landing, which likely involves presenting information in a clear and user-friendly way, the layout of different sections becomes crucial.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Importance of Layout
&lt;/h2&gt;

&lt;p&gt;Effective layout enhances readability and user experience. Content should be organized logically and presented in a way that's easy to digest. Sometimes, initial design decisions need revisiting as the project evolves and more information is available about user behavior or content priorities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Sections for Better Space Utilization
&lt;/h2&gt;

&lt;p&gt;The recent commit focused on improving the stats page by relocating key sections. The 'Technologies', 'Experience', and 'Education' sections were moved from a narrow left sidebar to the main column. This change was implemented across the simple and default themes to utilize the available horizontal space more efficiently. A minor layout tweak was also applied to the nan theme.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------------+-----------------------+
| Technologies        | Contribution Activity |
| Experience          |                       |
| Education           |                       |
+---------------------+-----------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------+
| Contribution Activity |
+-----------------------+
| Technologies          |
| Experience            |
| Education             |
+-----------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Benefits of the Change
&lt;/h2&gt;

&lt;p&gt;Moving these sections to the main column offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Improved Readability:&lt;/strong&gt; Wider columns generally allow for more comfortable reading.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Better Space Utilization:&lt;/strong&gt; The main column provides more horizontal space, allowing the content to breathe.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Enhanced User Experience:&lt;/strong&gt; A more balanced layout can lead to a more engaging and intuitive user experience.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;When designing or refactoring layouts, consider how different sections interact with each other and how they utilize the available space. Don't hesitate to revisit initial design decisions as the project evolves and new information becomes available.&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Refining the Retro Theme: Enhancing the Profile Bio</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Thu, 05 Mar 2026 09:52:03 +0000</pubDate>
      <link>https://dev.to/geanruca/refining-the-retro-theme-enhancing-the-profile-bio-539</link>
      <guid>https://dev.to/geanruca/refining-the-retro-theme-enhancing-the-profile-bio-539</guid>
      <description>&lt;p&gt;The &lt;code&gt;devlog-ist/landing&lt;/code&gt; project focuses on creating an engaging landing page experience. A recent update refines the retro theme by adjusting the profile bio's display. This post will explain the adjustments made to enhance the bio section within the retro theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Initial Constraint
&lt;/h2&gt;

&lt;p&gt;Previously, the profile bio section in the retro theme had a &lt;code&gt;max-w-lg&lt;/code&gt; CSS class applied. This limited the maximum width of the bio content, potentially causing text to wrap prematurely on larger screens and not fully utilizing available space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing the Constraint
&lt;/h2&gt;

&lt;p&gt;The update involved removing the &lt;code&gt;max-w-lg&lt;/code&gt; constraint. This allows the profile bio to expand and fill the available horizontal space more effectively, leading to a cleaner and more readable presentation, especially on wider displays.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impact
&lt;/h2&gt;

&lt;p&gt;Removing this constraint ensures that the profile bio adapts fluidly to different screen sizes, improving the overall user experience by optimizing readability and visual appeal. This small change contributes significantly to the polished look and feel of the retro theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Consider testing the landing page on various screen sizes to ensure the profile bio scales appropriately and remains visually appealing across different devices.&lt;/p&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>Enhancing devlog-ist/landing: Sidebar Technologies and Expert Labels</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Thu, 05 Mar 2026 09:39:05 +0000</pubDate>
      <link>https://dev.to/geanruca/enhancing-devlog-istlanding-sidebar-technologies-and-expert-labels-2amo</link>
      <guid>https://dev.to/geanruca/enhancing-devlog-istlanding-sidebar-technologies-and-expert-labels-2amo</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Enhancements have been made to the &lt;code&gt;devlog-ist/landing&lt;/code&gt; project, a platform designed for developers. These updates focus on improving the presentation and accessibility of technologies and expertise showcased on the landing pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technologies Sidebar
&lt;/h2&gt;

&lt;p&gt;The 'Technologies' section has been relocated to the left sidebar in both the 'default' and 'simple' themes. This adjustment aims to provide a more consistent and accessible layout, ensuring that key technologies are prominently displayed and easily navigable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expert Labeling
&lt;/h2&gt;

&lt;p&gt;A significant update involves the introduction of an "Expert in:" label across all eight themes. This label is strategically placed before the expert tech tags, providing clear indication of a user's proficiency in a particular technology. The colored tags previously used in 'nan' themes have been removed to maintain visual consistency across all themes.&lt;/p&gt;

&lt;p&gt;Example of how the label could be implemented in Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;tech&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"Go"&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expert in: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tech&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Translation Key Addition
&lt;/h2&gt;

&lt;p&gt;To ensure comprehensive support, the &lt;code&gt;expert_in&lt;/code&gt; translation key has been added across all four supported languages. This addition facilitates proper localization of the "Expert in:" label, catering to a global audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impact and Benefits
&lt;/h2&gt;

&lt;p&gt;These changes collectively contribute to a more refined and user-friendly experience within the &lt;code&gt;devlog-ist/landing&lt;/code&gt; platform. By relocating the Technologies section and clearly labeling expert proficiencies, users can more effectively highlight their skills and expertise.&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Enhancing Tenant Management in devlog-ist/landing with Plan Visibility</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Thu, 05 Mar 2026 09:10:05 +0000</pubDate>
      <link>https://dev.to/geanruca/enhancing-tenant-management-in-devlog-istlanding-with-plan-visibility-5fp8</link>
      <guid>https://dev.to/geanruca/enhancing-tenant-management-in-devlog-istlanding-with-plan-visibility-5fp8</guid>
      <description>&lt;p&gt;This post details the addition of a 'plan' column to the admin tenants table in the devlog-ist/landing project, improving tenant management and visibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Need for Plan Visibility
&lt;/h2&gt;

&lt;p&gt;The original tenant management interface lacked a clear, immediate indicator of each tenant's current plan. This required administrators to navigate to separate sections or run custom queries to determine the plan associated with each tenant, creating unnecessary overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation: Adding the 'Plan' Column
&lt;/h2&gt;

&lt;p&gt;The solution involved modifying the admin tenants table to include a 'plan' column. This column displays each tenant's current plan using a colored badge, mirroring the style already in use within the users table. This ensures visual consistency and ease of recognition.&lt;/p&gt;

&lt;p&gt;To implement this, the table rendering logic was updated to fetch the plan information for each tenant. A mapping was created between plan types and corresponding badge colors. This mapping is used to dynamically generate the appropriate badge style for each tenant's plan.&lt;/p&gt;

&lt;p&gt;Example of badge generation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPlanBadge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$plan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$colorMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'basic'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'gray'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'premium'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'green'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'enterprise'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'purple'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$colorMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$plan&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'blue'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;span class='badge text-bg-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$plan&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function takes a plan name as input and returns an HTML span element representing a colored badge. The color is determined by a predefined &lt;code&gt;$colorMap&lt;/code&gt;. If the plan is not found in the map, a default color ('blue') is used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits and Impact
&lt;/h2&gt;

&lt;p&gt;Adding the 'plan' column provides several key benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Improved Visibility:&lt;/strong&gt; Administrators can now see each tenant's plan at a glance, without needing to navigate to other sections.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Enhanced Efficiency:&lt;/strong&gt; Streamlines tenant management workflows, reducing the time required to identify and manage tenants based on their plans.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Consistent UI:&lt;/strong&gt; Maintains a consistent user interface by using the same badge style as the users table.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By adding a 'plan' column with colored badges to the admin tenants table, the devlog-ist/landing project has significantly improved the usability and efficiency of its tenant management interface. This simple addition provides immediate value by increasing visibility and reducing administrative overhead.&lt;/p&gt;

</description>
      <category>php</category>
      <category>html</category>
      <category>css</category>
    </item>
  </channel>
</rss>
