<?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>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>
    <item>
      <title>Improving SEO and Indexing with Canonical Tags and Robots Configuration</title>
      <dc:creator>Gerardo Andrés Ruiz Castillo</dc:creator>
      <pubDate>Thu, 05 Mar 2026 09:00:03 +0000</pubDate>
      <link>https://dev.to/geanruca/improving-seo-and-indexing-with-canonical-tags-and-robots-configuration-4g7p</link>
      <guid>https://dev.to/geanruca/improving-seo-and-indexing-with-canonical-tags-and-robots-configuration-4g7p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Maintaining a website's search engine optimization (SEO) and ensuring proper indexing by search engines like Google is crucial for visibility. This involves carefully managing canonical tags, robots configurations, and handling various URL parameters. Recent updates to the landing page project focused on addressing duplicate canonical tags and Google Search Console indexing issues to improve overall site health.&lt;/p&gt;

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

&lt;p&gt;The website experienced several SEO-related problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Duplicate Canonical Tags:&lt;/strong&gt; Multiple canonical tags on a single page confuse search engines, hindering proper indexing.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Indexing Issues:&lt;/strong&gt; Google Search Console reported issues with indexing, potentially caused by URL parameters or incorrect robots configurations.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Unintended Indexing:&lt;/strong&gt; Certain pages, like the newsletter unsubscribe page, were being indexed despite not being intended for public search.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;To address these issues, several changes were implemented:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Removal of Duplicate Canonical Tags:&lt;/strong&gt; The &lt;code&gt;seo-meta&lt;/code&gt; tag was removed from the portfolio layout to eliminate duplicate canonical tags, ensuring a single, authoritative tag per page.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Per-Page SEO Meta Tags:&lt;/strong&gt; Individual &lt;code&gt;seo-meta&lt;/code&gt; tags were added to all stats templates, providing specific and accurate meta information for each page.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Canonical URL Standardization:&lt;/strong&gt; Query parameters were stripped from canonical URLs to avoid fragmentation and ensure consistent indexing.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Noindex for Unsubscribe Page:&lt;/strong&gt; The newsletter unsubscribe page was set to &lt;code&gt;noindex&lt;/code&gt; to prevent it from appearing in search results.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Robots Configuration Update:&lt;/strong&gt; The robots configuration was updated to disallow &lt;code&gt;.md&lt;/code&gt; routes, preventing the indexing of markdown files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, the canonical URL standardization was achieved by implementing a function similar to 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="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;stripQueryParams&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;$url&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;$parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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;$parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$canonicalUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stripQueryParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$currentUrl&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 removes any query parameters from the current URL, ensuring that the canonical URL is clean and consistent.&lt;/p&gt;

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

&lt;p&gt;These changes resulted in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Elimination of duplicate canonical tag errors.&lt;/li&gt;
&lt;li&gt;  Improved indexing status in Google Search Console.&lt;/li&gt;
&lt;li&gt;  Prevention of unintended page indexing.&lt;/li&gt;
&lt;li&gt;  A more streamlined and SEO-friendly website structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Audit your website for duplicate canonical tags using SEO tools.&lt;/li&gt;
&lt;li&gt; Review your Google Search Console for indexing issues.&lt;/li&gt;
&lt;li&gt; Implement URL standardization to remove unnecessary parameters from canonical URLs.&lt;/li&gt;
&lt;li&gt; Use &lt;code&gt;noindex&lt;/code&gt; tags for pages that should not be indexed by search engines.&lt;/li&gt;
&lt;li&gt; Regularly update your robots configuration to control search engine access.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Insight
&lt;/h2&gt;

&lt;p&gt;Proactive management of canonical tags, URL parameters, and robots configurations is essential for maintaining a healthy SEO presence and ensuring accurate indexing by search engines. Regularly auditing and updating these elements can significantly improve your website's visibility and search performance.&lt;/p&gt;

</description>
      <category>html</category>
      <category>seo</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
