<?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: Hrishikesh Dalal</title>
    <description>The latest articles on DEV Community by Hrishikesh Dalal (@hrishikesh_dalal_ced8f95e).</description>
    <link>https://dev.to/hrishikesh_dalal_ced8f95e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3564172%2F53b92bbb-b5a6-4a39-bbe0-b13f02839490.png</url>
      <title>DEV Community: Hrishikesh Dalal</title>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hrishikesh_dalal_ced8f95e"/>
    <language>en</language>
    <item>
      <title>Drupal x Google Summer of Code - Week 3: Reuse &amp; Previews</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Tue, 07 Jul 2026 07:17:42 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/drupal-x-google-summer-of-code-week-3-reuse-previews-1470</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/drupal-x-google-summer-of-code-week-3-reuse-previews-1470</guid>
      <description>&lt;p&gt;&lt;strong&gt;WELLLLCOMMEE to Week 3 of this series!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvzrtddmyo94iok6led4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvzrtddmyo94iok6led4w.png" alt="Drupal x Google Summer of Code - Week 3: Reuse &amp;amp; Previews" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the last week made quite a few structural changes that will help us. Have created the YAML extractor and parser which can extract the YAML component form any response of the LLM using Symfony to validate it. Made a Utility for this thereby making the code reusable. &lt;/p&gt;

&lt;p&gt;Official Issue Tracker: &lt;a href="https://www.drupal.org/project/ai_recipe_generator/issues/3594062" rel="noopener noreferrer"&gt;Week 3 Tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me now give me glimpse for this. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=7sH6pv2Vyns" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=7sH6pv2Vyns&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftwko3jwu176a313bzl5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftwko3jwu176a313bzl5c.png" alt="The Output Preview &amp;amp; Validator" width="799" height="398"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Output Preview &amp;amp; Validator&lt;/em&gt;&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Time to Dive Deep into this&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  YAML Validator &amp;amp; Extractor
&lt;/h2&gt;

&lt;p&gt;Created a Utility folder under src and created a file named Yaml Processor. The concept of this file was simple, when you get an output from LLM, from whichever Service or Function you pull this from this will extract the YAML and give it to you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here it uses this : use Drupal\Component\Serialization\Yaml&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This under the hood uses Symfony which helps to validate and extraction.  Symfony is the underlying PHP framework that powers modern Drupal ( this is just one of the usecase)&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;Drupal\ai_recipe_generator\Utility&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;Drupal\Component\Serialization\Yaml&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Utility class to parse and extract YAML from AI responses.
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;YamlProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="cd"&gt;/**
   * Cleans a response down to YAML text when possible.
   *
   * @param string $response
   *   The raw model response.
   *
   * @return string
   *   A normalized YAML string or the response with code fences removed.
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;normalize&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;$response&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;$parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;extractAndParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;);&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;is_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parsed&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="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Yaml&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parsed&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;preg_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/```

(?:yaml|yml)?\s*|

```/i'&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;$response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cleaned&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cd"&gt;/**
   * Attempts to extract and validate YAML from a given string.
   *
   * @param string $response
   *   The raw string response.
   *
   * @return array|null
   *   The parsed YAML as an array, or null if no valid YAML could be extracted.
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;extractAndParse&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;$response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// First try parsing the entire response as YAML.&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Yaml&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;);&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;is_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parsed&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;$parsed&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;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&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;// Not valid YAML, continue with extraction.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Extract YAML from markdown code blocks.&lt;/span&gt;
    &lt;span class="nv"&gt;$pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/```

(?:yaml|yml)?\s*(.*?)

```/is'&lt;/span&gt;&lt;span class="p"&gt;;&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="nv"&gt;$pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$yaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Yaml&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$yaml&lt;/span&gt;&lt;span class="p"&gt;);&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;is_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parsed&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;$parsed&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;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;NULL&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;NULL&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;h2&gt;
  
  
  Reusable YAML Previewer
&lt;/h2&gt;

&lt;p&gt;If you have seen my previous blog, the older YAML previewers they were not upto the mark. So improved that with having better parsers and lines. What I used for this was a template, using a html.twig format which helped to render properly anywhere, anyhow!&lt;/p&gt;

&lt;p&gt;This is slightly difficult to think and then implement (tedious slightly) but since highly reusable, highly useful!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{#
/**
 * @file
 * Template for the fluid YAML previewer component with line numbers.
 */
#}
&amp;lt;div class="ai-yaml-preview-container" {{ attributes }}&amp;gt;
  &amp;lt;div class="ai-yaml-preview-header"&amp;gt;
    &amp;lt;span class="ai-yaml-preview-title"&amp;gt;{{ title|default('YAML Output Preview') }}&amp;lt;/span&amp;gt;

    &amp;lt;button class="ai-yaml-copy-btn" onclick="
      const container = this.closest('.ai-yaml-preview-container');
      const code = container.querySelector('code').innerText;
      navigator.clipboard.writeText(code);
      const originalText = this.innerText;
      this.innerText = 'Copied!';
      setTimeout(() =&amp;gt; this.innerText = originalText, 2000);
    "&amp;gt;Copy YAML&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class="ai-yaml-preview-body"&amp;gt;
    {# This empty div will be filled with numbers by the JS below #}
    &amp;lt;div class="ai-yaml-line-numbers" aria-hidden="true"&amp;gt;&amp;lt;/div&amp;gt;

    &amp;lt;pre class="ai-yaml-preview-content"&amp;gt;&amp;lt;code class="language-yaml"&amp;gt;{{ yaml_string }}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

{# Inline script to generate line numbers based on the code length #}
&amp;lt;script&amp;gt;
  (function() {
    // Find all preview containers on the page
    const containers = document.querySelectorAll('.ai-yaml-preview-container');

    containers.forEach(container =&amp;gt; {
      const codeBlock = container.querySelector('code');
      const linesContainer = container.querySelector('.ai-yaml-line-numbers');

      // Only process if it hasn't been filled yet
      if (codeBlock &amp;amp;&amp;amp; linesContainer &amp;amp;&amp;amp; linesContainer.innerHTML === '') {
        // Split by newline to get total lines
        const lines = codeBlock.innerText.split('\n');

        // Handle the case where the AI leaves a trailing empty line at the end
        const lineCount = lines[lines.length - 1] === '' ? lines.length - 1 : lines.length;

        let numbersHTML = '';
        for (let i = 1; i &amp;lt;= Math.max(1, lineCount); i++) {
          numbersHTML += i + '&amp;lt;br&amp;gt;';
        }
        linesContainer.innerHTML = numbersHTML;
      }
    });
  })();
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Code Reduction
&lt;/h2&gt;

&lt;p&gt;Let's understand the problem, we had 3 files for calling the LLM models &amp;amp; providers. All of them had separate functions. So if I ever wanted to change anything, I would have to change in all the three files : (&lt;/p&gt;

&lt;p&gt;So we changed the calling part. for 2 (ArenaService &amp;amp; RecipeGenerationService) of them as they were the same replicas the third one at BenchmarkDashboardService was a different one. Clubbing them was fairly simple, just had to change the function calls. &lt;/p&gt;

&lt;h2&gt;
  
  
  Started fixing the Pipeline
&lt;/h2&gt;

&lt;p&gt;Did the cspell checking and then the phpcs checking which was initially failing but then started fixing the same. For fixing the cspell introduced a file called: .cspell.json. This needed to have all the words in the document which I wanted to exclude form the checking.&lt;/p&gt;

&lt;p&gt;Will write a separate blog on testing!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"words"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ai_recipe_generator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"chartjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"crosshairs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"dalal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"fontawesome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"hrishikesh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"llm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"llms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"multichat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"onclick"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Starshot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"tempstore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"unconfigured"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
        &lt;/span&gt;&lt;span class="s2"&gt;"xaxis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"xmark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"yaml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"yaxis"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ignorePaths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"vendor/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"*.min.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"*.min.css"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Thank you &amp;amp; catch you up next time!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Follow me at &lt;a href="https://www.linkedin.com/in/hrishikeshdalal/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/hrishikeshdalal/&lt;/a&gt;&lt;br&gt;
Portfolio: &lt;a href="https://www.hrishikeshdalal.dev/" rel="noopener noreferrer"&gt;https://www.hrishikeshdalal.dev/&lt;/a&gt;&lt;br&gt;
Alt Portfolio: &lt;a href="https://hrishikesh-dalal.vercel.app/" rel="noopener noreferrer"&gt;https://hrishikesh-dalal.vercel.app/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

</description>
    </item>
    <item>
      <title>Google Summer of Code (GSoC) x Drupal: Week 2 - Mapping the Dashboards</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Tue, 07 Jul 2026 07:09:11 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/google-summer-of-code-gsoc-x-drupal-week-2-mapping-the-dashboards-3gm1</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/google-summer-of-code-gsoc-x-drupal-week-2-mapping-the-dashboards-3gm1</guid>
      <description>&lt;p&gt;&lt;strong&gt;WELCOMMMEEEE to another episode of Weekly Recap of my project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2qqxl6lvq6y3h48xi3hd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2qqxl6lvq6y3h48xi3hd.png" alt="Google Summer of Code (GSoC) x Drupal: Week 2 - Mapping the Dashboards" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This week we have built two things: LLM Benchmarking Dashboard &amp;amp; LLM Arena to show you which ones are the best. Both of these will be super useful while building our final product. &lt;/p&gt;

&lt;p&gt;The Offical Issue Tracker: &lt;a href="https://www.drupal.org/project/ai_recipe_generator/issues/3593030" rel="noopener noreferrer"&gt;Week 2 Issues Tackled&lt;/a&gt;&lt;br&gt;
Let me give you a glipmse of what our output will look like. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=FWhz2f81urQ" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=FWhz2f81urQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8vr14yqiu8oeacjc7g59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8vr14yqiu8oeacjc7g59.png" alt="Dashboard" width="800" height="373"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Dashboard (Part 1)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcwcze542104arp0lbd5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcwcze542104arp0lbd5s.png" alt="Dashboard " width="800" height="227"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Dashboard (Part 2)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;The Benchmarking Dashboard&lt;/h2&gt;

&lt;p&gt;In order to capture the true essence of which model performs the best in the long run, had made a LLM benchmarking dashboard. Someone just by looking at charts can capture which model is most suitable for their usecase. &lt;/p&gt;

&lt;blockquote&gt;Metrics that provided the overall picture&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;1). The Top Banner&lt;/strong&gt;: It had the total runs so far, along with the average response time and the average accuracy for the recipe generated. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2). The Visuals&lt;/strong&gt;: Any dashboard is incomplete without a good visual. The first graph which shows the latency for the previous runs mapped across models is useful for finding the trend for model hits. The second graph is for using a bar graph for visualising the model avg. latency to compare which model is the faster as compared to others. Used the Chart module for the same. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3). Key Metric Table&lt;/strong&gt;: This is the detailed table for per prompt summary. It covers: &lt;em&gt;the provider, the model, the speed, whether the YAML is valid or not, whether the Schema proper or not as per Drupal Symfony, &amp;amp; finally tells us about the accuracy along with timestamps&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What is useful in this is that you can filter it along with sort the columns in ascending or descending order. Is if further &lt;strong&gt;colour coded&lt;/strong&gt; so that you can quickly view the page at a glance. &lt;/p&gt;

&lt;p&gt;Things I used to build this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Chart.js / Drupal Chart Module: To render the responsive, interactive graphs.&lt;/li&gt;
  &lt;li&gt;Custom Database Schema (.install): To reliably save and query the historical benchmarking data.&lt;/li&gt;
  &lt;li&gt;Drupal AJAX API: To ensure the table filters and dynamic sorting update seamlessly.&lt;/li&gt;
  &lt;li&gt;Font Awesome Library: To have cool icons in the dashboard&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;h2&gt;The LLM Arena &lt;/h2&gt;

&lt;p&gt;This was particularly fun to build as this was for comparing the different responses. You get to type the custom prompt and then fire it across all the different models and providers. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can have all the different models &amp;amp; providers, there is NO LIMIT!!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Further these is also an option to store the responses in the database so that they reflected in the schema. There is also a delete button for the same. If you want to remove some model-provider.  Further you will see a very familiar screen that, while firing the prompts, it will look like new module install screen, this is because of the Batch API Screen. &lt;/p&gt;

&lt;p&gt;Sources I used:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;AJAX API: Model - Provider dropdown&lt;/li&gt;
  &lt;li&gt;Batch API: For firing all the prompts together&lt;/li&gt;
  &lt;li&gt;Font Awesome Icons: For the icons there&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;Laying out the Structure&lt;/h2&gt;

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

&lt;p&gt;Just like a good Drupal Practice (have to mention), have used AI to generate the image above. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Client (User Browser) Layer
&lt;/h3&gt;

&lt;p&gt;Handles the user interface and visual presentation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User (Arena/Dashboard Interface):&lt;/strong&gt; Renders the comparison forms, interactive data tables, and metrics dashboards.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frontend Components:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript:&lt;/strong&gt; Uses &lt;code&gt;Chart.js&lt;/code&gt; to render interactive line and bar charts for model latency trends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Icons:&lt;/strong&gt; Integrates &lt;strong&gt;Font Awesome&lt;/strong&gt; for immediate visual cues (success/failure statuses).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSS:&lt;/strong&gt; Uses &lt;code&gt;css/arena.css&lt;/code&gt; and &lt;code&gt;css/dashboard.css&lt;/code&gt; via &lt;code&gt;.libraries.yml&lt;/code&gt; for custom, polished layouts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client-Server Communication:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AJAX API:&lt;/strong&gt; Controls non-blocking form updates, dynamic filtering, and column sorting without page reloads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Batch API (Client-Side):&lt;/strong&gt; Drives the real-time progress bar (similar to the module installation screen) during execution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Drupal Application Logic Layer
&lt;/h3&gt;

&lt;p&gt;The PHP backend that processes requests and orchestrates module logic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Form API (&lt;code&gt;ArenaForm.php&lt;/code&gt;, &lt;code&gt;BenchmarkDashboardForm.php&lt;/code&gt;):&lt;/strong&gt; Handles form submissions and handles backend execution triggers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services Layer (&lt;code&gt;ArenaService.php&lt;/code&gt;, &lt;code&gt;BenchmarkDashboardService.php&lt;/code&gt;):&lt;/strong&gt; Houses decoupled, reusable core logic registered via &lt;code&gt;ai_recipe_generator.services.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Integration Engine:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drupal Batch API:&lt;/strong&gt; Splits concurrent LLM requests into isolated chunks to prevent gateway timeouts and server lockups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drupal AJAX API:&lt;/strong&gt; Manages seamless, lightweight data passing between backend services and frontend components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Data &amp;amp; External Integrations Layer
&lt;/h3&gt;

&lt;p&gt;Manages persistent storage and outbound network requests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local Database (&lt;code&gt;.install&lt;/code&gt;):&lt;/strong&gt; Uses a custom schema defined in the install file to save historical run stats, latency, timestamps, and schema validation flags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External LLM APIs:&lt;/strong&gt; Handles outbound calls to external models (OpenAI, Anthropic, Gemini). Isolated by the Batch API so a timeout on one provider doesn't crash the session.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks a lot for reading!!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Follow me at &lt;a href="https://www.linkedin.com/in/hrishikeshdalal/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/hrishikeshdalal/&lt;/a&gt;&lt;br&gt;
Portfolio: &lt;a href="https://www.hrishikeshdalal.dev/" rel="noopener noreferrer"&gt;https://www.hrishikeshdalal.dev/&lt;/a&gt;&lt;br&gt;
Alt Portfolio: &lt;a href="https://hrishikesh-dalal.vercel.app/" rel="noopener noreferrer"&gt;https://hrishikesh-dalal.vercel.app/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

</description>
    </item>
    <item>
      <title>Drupal x GSoC: Week 1 - Building the Base</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Tue, 07 Jul 2026 07:02:24 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/drupal-x-gsoc-week-1-building-the-base-42j9</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/drupal-x-gsoc-week-1-building-the-base-42j9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fslqp63w9cio97mfh71kh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fslqp63w9cio97mfh71kh.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WELLLLLLCOMMEE To Week 1 of GSoC x Drupal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over the first week my focus was on establishing a proper base for the project. This was particularly fun as you can see your project come to life. These are some other chapters of the journal that you should definitely read!&lt;/p&gt;

&lt;p&gt;I created a project: &lt;a href="https://blog.hrishikeshdalal.dev/drupal/6a1bbb83a9efe6ea5bb97c16" rel="noopener noreferrer"&gt;Creating a Module Project on Drupal (NO CODE) (With BONUS Section)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I learnt about &lt;a href="https://blog.hrishikeshdalal.dev/drupal/6a200d7d5f3958f4a28782d7" rel="noopener noreferrer"&gt;PIRLS - Drupal's Core .yml files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before you proceed any further, you should have a drupal project set up. If not, you can check it out here: &lt;a href="https://blog.hrishikeshdalal.dev/drupal/6a21075f8d7f810de39a3084" rel="noopener noreferrer"&gt;Installing Drupal&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have added a bit more changes too, you can check it our here: &lt;a href="https://www.drupal.org/project/ai_recipe_generator/issues/3592993" rel="noopener noreferrer"&gt;Week 1 Official Tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=2IcKTPBoyqc" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=2IcKTPBoyqc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftw9hzzj8mhquyx32nq3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftw9hzzj8mhquyx32nq3z.png" alt="Week 1 Issues Tackled" width="800" height="584"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Week 1 Issues Tackled&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's zoom out and see what we have built. UI might not be that great yet but that was not the focus of the first week.&lt;/p&gt;

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

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

&lt;p&gt;You should clone the basic repo of the module and paste it in: &lt;strong&gt;web/modules/custom/ai_recipe_generator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You might need to create this folder &amp;amp; add all the files. We use custom as we haven't made a deployment of the project yet. So once I did this I made. Now I have added ai_recipe_generator.info.yml, ai_recipe_generator_services.yml, ai_recipe_generator.routing.yml. I have made a blog on these files and what do these mean, you can check that out too. &lt;/p&gt;

&lt;p&gt;I then created 2 things in src folder - Form &amp;amp; Services. &lt;/p&gt;

&lt;p&gt;So we have two pages as you saw above. Let's understand the routing or the file urls&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvekze81htm1fkxxh7h0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvekze81htm1fkxxh7h0w.png" alt="Our File Structure" width="358" height="357"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Our File Structure&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ai_recipe_generator.test_form&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/admin/config/development/ai-recipe-test'&lt;/span&gt;
  &lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;_form&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\Drupal\ai_recipe_generator\Form\RecipeGeneratorTestForm'&lt;/span&gt;
    &lt;span class="na"&gt;_title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Recipe&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Generator&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(Test&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;UI)'&lt;/span&gt;
  &lt;span class="na"&gt;requirements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;_permission&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;administer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;site&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;configuration'&lt;/span&gt;

&lt;span class="na"&gt;ai_recipe_generator.settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/admin/config/ai-recipe/settings'&lt;/span&gt;
  &lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;_form&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\Drupal\ai_recipe_generator\Form\SettingsForm'&lt;/span&gt;
    &lt;span class="na"&gt;_title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Recipe&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Generator&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Settings'&lt;/span&gt;
  &lt;span class="na"&gt;requirements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;_permission&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;administer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;site&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;configuration'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry has the following&lt;/p&gt;

&lt;p&gt;1 The URL Path&lt;/p&gt;

&lt;p&gt;2 The Form File which it should render&lt;/p&gt;

&lt;p&gt;3 The title of the Page&lt;/p&gt;

&lt;p&gt;4 Permission Requirements&lt;/p&gt;




&lt;p&gt;We have dependecies as well. This module as of now depends on the AI Module, Gemini Provider Module as well as the Key Module for managing all the keys. You have to install and enable all these modules for the AI Recipe Generator to function. This is actually cool as we are using the Drupal Ecosystem to do our tasks. &lt;/p&gt;

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

&lt;h3&gt;
  
  
  Phase 1: The Setup (Settings Form)
&lt;/h3&gt;

&lt;p&gt;Before a user can generate a recipe, an administrator sets the rules.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Setting the Ground Rules:&lt;/strong&gt; An admin visits the "AI Recipe Generator Settings" form and enters the &lt;strong&gt;System Instructions&lt;/strong&gt; (e.g., "You are a senior Drupal developer...").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saving to the Database:&lt;/strong&gt; When the admin clicks "Save configuration," Drupal uses its configuration service (&lt;code&gt;@config.factory&lt;/code&gt;) to safely store these instructions in the database for later use.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Phase 2: The User Request (Test UI Form)
&lt;/h3&gt;

&lt;p&gt;This is where the actual generation happens.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User Input:&lt;/strong&gt; A user opens the "AI Recipe Generator (Test UI)" form. They select their preferred &lt;strong&gt;AI Provider&lt;/strong&gt; (like Gemini) and type exactly what they want to build into the &lt;strong&gt;Recipe Prompt&lt;/strong&gt; box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triggering the AJAX Call:&lt;/strong&gt; The user clicks the &lt;strong&gt;Generate Recipe&lt;/strong&gt; button. Instead of reloading the whole webpage, the browser sends a quiet, background message (an &lt;strong&gt;AJAX Request&lt;/strong&gt;) to the Drupal server containing the user's prompt and their chosen AI provider.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Phase 3: The Backend Brain (Drupal Services)
&lt;/h3&gt;

&lt;p&gt;Once Drupal receives the AJAX request, it hands the work over to the custom services defined in your &lt;code&gt;.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gathering the Pieces:&lt;/strong&gt; The main service (&lt;code&gt;RecipeGenerationService&lt;/code&gt;) goes to work collecting three crucial pieces of information:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Rules:&lt;/strong&gt; It pulls the saved "System Instructions" using &lt;code&gt;@config.factory&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Context:&lt;/strong&gt; It asks the &lt;code&gt;ContextExtractor&lt;/code&gt; service to pull any relevant background information about your specific Drupal site.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Request:&lt;/strong&gt; It takes the user's specific "Recipe Prompt" from the AJAX call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Talking to the AI:&lt;/strong&gt; The &lt;code&gt;RecipeGenerationService&lt;/code&gt; combines the rules, the context, and the user's request into one massive prompt. It then uses the &lt;code&gt;@ai.provider&lt;/code&gt; service to send this combined package over the internet to the external AI (e.g., Gemini).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Phase 4: The Result (AJAX Response)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Receiving the Recipe:&lt;/strong&gt; The external AI processes the prompt, generates the Starshot Recipe YAML file, and sends it back to your Drupal server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updating the Screen:&lt;/strong&gt; Drupal packages this generated recipe into an &lt;strong&gt;AJAX Response&lt;/strong&gt; and sends it back to the user's browser. The browser reads this response and instantly displays the new recipe on the screen, completing the process without ever forcing the user to refresh the page.&lt;/li&gt;
&lt;/ol&gt;

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

</description>
    </item>
    <item>
      <title>Adding your first piece of code to Drupal Project!</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Tue, 09 Jun 2026 02:33:54 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/adding-your-first-piece-of-code-to-drupal-project-4925</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/adding-your-first-piece-of-code-to-drupal-project-4925</guid>
      <description>&lt;p&gt;Setting up the codebase on Drupal is &lt;strong&gt;EASY!&lt;/strong&gt; Only you need a little bit of patience. Let's dive deep into it.&lt;/p&gt;

&lt;p&gt;So previously we had set up a project. Now we are going to initialise the git repo and add a few changes. First if you have created your project, you can move on into the Version Control section. This will contain instructions of what we should do further. &lt;/p&gt;

&lt;p&gt;This was my link: &lt;a href="https://www.drupal.org/project/ai_recipe_generator/git-instructions" rel="noopener noreferrer"&gt;Version Control&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Ai0n_9vG_Ig"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfwhwyadarhlv2g8g63q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfwhwyadarhlv2g8g63q.png" alt="Version Control " width="543" height="61"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Version Control&lt;/em&gt;&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="nb"&gt;mkdir &lt;/span&gt;ai_recipe_generator

&lt;span class="nb"&gt;cd &lt;/span&gt;ai_recipe_generator

git init

git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; 1.0.x

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"AI Recipe Generator"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; README.txt

git add README.txt

git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit."&lt;/span&gt;

git remote add origin git@git.drupal.org:project/ai_recipe_generator.git

git push origin 1.0.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They are the explicit instructions of what we are going to do.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Copying without understanding is useless. Let's go through each step one by one. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Why we do it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;mkdir ai_recipe_generator&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Creates a new directory named &lt;code&gt;ai_recipe_generator&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Allocates a dedicated local space for your project files.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;cd ai_recipe_generator&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moves your terminal path into the new folder.&lt;/td&gt;
&lt;td&gt;Ensures all future commands execute inside the project context.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;git init&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Initializes a hidden local &lt;code&gt;.git&lt;/code&gt; repository.&lt;/td&gt;
&lt;td&gt;Turns the plain folder into a version-controlled project.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;git checkout -b 1.0.x&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Creates and switches to a new branch named &lt;code&gt;1.0.x&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Bypasses standard &lt;code&gt;main&lt;/code&gt;/&lt;code&gt;master&lt;/code&gt; to match Drupal's release branch rules.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;echo "AI..." &amp;gt; README.txt&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generates a &lt;code&gt;README.txt&lt;/code&gt; file with project text.&lt;/td&gt;
&lt;td&gt;Creates an initial asset to track, as Git cannot track empty folders.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;git add README.txt&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stages the file to the Git index.&lt;/td&gt;
&lt;td&gt;Explicitly marks the new file to be included in the next snapshot.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;git commit -m "Text"&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Saves the staged file with a historic log message.&lt;/td&gt;
&lt;td&gt;Establishes the official baseline/first snapshot of your codebase.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;git remote add origin &amp;lt;url&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Links your local Git setup to the Drupal.org repository.&lt;/td&gt;
&lt;td&gt;Establishes the secure target destination path on Drupal's servers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;git push origin 1.0.x&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uploads the local branch and commit to the server.&lt;/td&gt;
&lt;td&gt;Publishes your code online so your mentors can access and review it.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Post this we had committed 3 more files of out own, which are equally crucial for the module development. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 ai_recipe_generator.module.info&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 composer.json&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 .gitignore&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Resources&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Module Creation &amp;amp; .info.yml Documentation
&lt;a href="https://www.drupal.org/docs/develop/creating-modules" rel="noopener noreferrer"&gt;Creating Custom Modules (Main Hub):&lt;/a&gt; This is the central landing page for all module development tutorials in modern Drupal, covering everything from routing to creating custom blocks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://www.drupal.org/docs/develop/creating-modules/let-drupal-know-about-your-module-with-an-infoyml-file" rel="noopener noreferrer"&gt;Defining the .info.yml File:&lt;/a&gt; This page provides the exact specifications, required properties, and core version requirement syntax for your module's metadata.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Composer Documentation for Modules
&lt;a href="https://www.drupal.org/docs/develop/using-composer/add-a-composerjson-file" rel="noopener noreferrer"&gt;Adding a composer.json File to a Module:&lt;/a&gt; This explains exactly why and how to add a composer.json file to your custom or contributed module, including the composer init steps specifically tailored for Drupal package types.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AI Recipe Generator&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Creates recipes using AI.&lt;/span&gt;
&lt;span class="na"&gt;package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AI&lt;/span&gt;

&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;module&lt;/span&gt;
&lt;span class="na"&gt;core_version_requirement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^10.5 || ^11.2&lt;/span&gt;

&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ai:ai&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"drupal/ai_recipe_generator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creates recipes using AI."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"drupal-module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GPL-2.0-or-later"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"authors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hrishikesh Dalal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"204620-hrishikesh-dalal@users.noreply.drupalcode.org"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;Somewhere where I struggled and made mistakes&lt;/h3&gt;

&lt;p&gt;If you have seen the video, you would know I had made a mistake initializing the git repo 😅. I made a commit to the main brand before setting up the 1.0.x which is crucial for creating forks and deployments.  Well it sucked but it was a great source of learning. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding the Concept of Remote in Git&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Think of Git &lt;strong&gt;Remotes&lt;/strong&gt; as a &lt;strong&gt;Cloud Backup or a Shared Office.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you are writing a book on your own private desk at home (&lt;strong&gt;Local Repository&lt;/strong&gt;). You can write, delete, and organize pages however you want.&lt;/p&gt;

&lt;p&gt;However, your publisher and co-authors are in a different building. To show them your work, you have a &lt;strong&gt;Shared Drafting Table&lt;/strong&gt; (&lt;strong&gt;Remote Repository&lt;/strong&gt;) in their office.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A "Remote":&lt;/strong&gt; It's just a bookmark or a "saved contact" in your computer's address book that tells Git exactly where that Shared Table is located on the internet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Origin:&lt;/strong&gt; This is just the default nickname for that Shared Table. (But you could call it anything, backup, publish etc)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Managing Remotes: The Commands&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes you need to change where your code is going (e.g., commit to a different fork or repository).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Check your current remotes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before changing anything, see where your project is currently "talking" to.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The &lt;code&gt;-v&lt;/code&gt; stands for "verbose." It shows you the URLs for fetching (downloading) and pushing (uploading).&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Change a remote's URL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the server location changes, but you want to keep the same nickname (&lt;code&gt;origin&lt;/code&gt;), use this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote set-url origin https://git.drupalcode.org/issue/ai_recipe_generator-3592994.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; Use this if you accidentally typed the wrong URL during setup or if the project moved to a new server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. Add a second remote&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can have more than one! You might have one for a private backup and one for the public Drupal project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add backup https://git.drupalcode.org/issue/ai_recipe_generator-000001.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Rename a remote&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you don't like the name &lt;code&gt;origin&lt;/code&gt; and want to call it something else: (here drupal_server)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;remote&lt;/span&gt; &lt;span class="nx"&gt;rename&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;drupal_server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Remove a remote&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to cut ties with a specific server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;remote&lt;/span&gt; &lt;span class="nx"&gt;remove&lt;/span&gt; &lt;span class="nx"&gt;backup&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro-Tip:&lt;/strong&gt; If you ever get an error saying "Remote origin already exists," it means your project is already linked to a server. Use &lt;code&gt;set-url&lt;/code&gt; to update it instead of &lt;code&gt;add&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;Eventually....&lt;/h1&gt;

&lt;p&gt;So after taking a short detour, we finally pushed our initial code to gitlab! 🎉🎉&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Follow me: &lt;a href="https://www.linkedin.com/in/hrishikeshdalal/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/hrishikeshdalal/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My portfolio: &lt;a href="https://hrishikeshdalal.tech/" rel="noopener noreferrer"&gt;https://hrishikeshdalal.tech/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>drupal</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating a Module Project on Drupal (NO CODE) (With BONUS Section)</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Mon, 08 Jun 2026 07:51:00 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/creating-a-module-project-on-drupal-no-code-with-bonus-section-19ld</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/creating-a-module-project-on-drupal-no-code-with-bonus-section-19ld</guid>
      <description>&lt;p&gt;Hey there!&lt;/p&gt;

&lt;p&gt;Welcome to my GSoC journey where in I am going to take you through an adventure of events. This blog is for the Level 1. Or rather I should saying learning about how to "make the dish". We are going to create our very own Drupal Module Project!! &lt;/p&gt;

&lt;p&gt;I will also append a Youtube link for the same if it does suit you better. Lets get into it!&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/z7AmnRiPOso"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;Step 1: Understanding what all types of Projects you can create&lt;/h1&gt;

&lt;p&gt;When you head over to this site for project creation - &lt;a href="https://www.drupal.org/docs/develop/managing-a-drupalorg-theme-module-or-distribution-project/creating-a-new-project/how-to-create-a-new-project" rel="noopener noreferrer"&gt;Drupal Project Create&lt;/a&gt;. You will have 4 options. &lt;/p&gt;

&lt;p&gt;Drupal offers a few different project types:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Module project:&lt;/strong&gt; Contains PHP code that extends Drupal or adds new features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Theme project:&lt;/strong&gt; Changes the look and feel of a Drupal site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. General project:&lt;/strong&gt; Contains code, but isn't a module, theme, or specific type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Community project&lt;/strong&gt;: Non-code projects used to manage groups or camps.&lt;/p&gt;

&lt;p&gt;Since our aim is to build an AI Recipe Generator that adds brand-new functionality to the site, selecting the Module project was the clear choice.&lt;/p&gt;

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

&lt;h1&gt;Step 2: Have Git on your computer&lt;/h1&gt;

&lt;p&gt;The absolute bare minimum as all the code changes everything should be done by this. But if you reading this and are a techie, you must already be having git : )&lt;/p&gt;

&lt;h1&gt;Step 3: Creating a project&lt;/h1&gt;

&lt;p&gt;Now when you go over this link: &lt;a href="https://www.drupal.org/project/add" rel="noopener noreferrer"&gt;Project Creation&lt;/a&gt;, you will have a ton of options. Let us go through each one of them one by one. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drupal core:&lt;/strong&gt; The foundational software that runs the entire Drupal platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution project:&lt;/strong&gt; An all-in-one starter kit that bundles Drupal with pre-selected features to quickly launch a specific type of website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community project:&lt;/strong&gt; An organizational space for managing Drupal groups, events, and people rather than website code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;General project:&lt;/strong&gt; A catch-all category for miscellaneous code, tools, and libraries that don't fit into the standard module or theme categories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Module project:&lt;/strong&gt; A functional plugin that adds new features or changes how your website works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme project:&lt;/strong&gt; The design package that controls the colors, layout, and overall visual appearance of your website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme Engine project:&lt;/strong&gt; The behind-the-scenes system that translates your theme's visual design into something the Drupal platform can understand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation project:&lt;/strong&gt; An outdated archive for language translations, which are now managed on a separate, dedicated website.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;Step 4: The Nitty Gritties&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fos0m02myzor9cehi2710.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fos0m02myzor9cehi2710.png" alt="All Changes" width="799" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;All the things you can do in a snap&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now when you go to the next page, there is this huge form that you need to fill. Let me take you across each filed one by one.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Basic Identity
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Name: AI Recipe Generator&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; The project's human-readable title.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; It clearly tells users exactly what the tool does right away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Short name: &lt;code&gt;ai_recipe_generator&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; The machine-readable system name used for URLs and codebases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; It matches the readable name using standard Drupal convention (lowercase and underscores) for consistency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Project type: Full project&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; Defines if it's a temporary "Sandbox" or an official "Full project."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; To get end-to-end control and official release packaging for this GSoC module.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Status and Classification
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Maintenance status: Actively maintained&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; Shows the community how much attention the project gets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; I will be actively monitoring, updating, and managing this code for the next 12 weeks and beyond.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Development status: Under active development&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; Indicates the codebase's current maturity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; The module is currently being built from the ground up and evolving daily.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Module categories &amp;amp; Ecosystem: Artificial Intelligence (AI)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; Tags used to organize and filter Drupal projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; Since the module relies on generative LLMs, this ensures the right audience finds it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dependencies: AI module&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; Other modules required for this to work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; We are leveraging the existing Drupal AI core module to securely handle API configurations and process prompts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Issue Tracking and Releases
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Components: Code, Documentation, Miscellaneous, User interface&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; Categories for issue queue tickets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; Granular tags keep the project organized and make bug triage much easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Enable issue tracker: Checked&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; Allows the community to report bugs and submit patches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; Open source thrives on collaboration, and this is essential for community contributions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Version options: Tagged releases, Development branch releases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; How Drupal packages the code for download via Composer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why I chose it:&lt;/strong&gt; It gives users the choice between a stable "Tagged" release or the bleeding-edge "Development" version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The Description that I put is below.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;The AI Recipe Generator is a module designed to accelerate site building by utilizing Artificial Intelligence to generate Drupal Recipes dynamically.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;By integrating with the Drupal AI ecosystem, this tool allows developers and site builders to input natural language prompts and automatically generate the necessary configuration, module dependencies, and structure required for a functional Drupal Recipe.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"module-project--features"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Features&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Integrates directly with the Drupal AI module to process prompts.&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Dynamically generates &lt;span class="nt"&gt;&amp;lt;code&amp;gt;&lt;/span&gt;recipe.yml&lt;span class="nt"&gt;&amp;lt;/code&amp;gt;&lt;/span&gt; files and associated configurations.&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Streamlines the creation of standard site features (e.g., blog setups, event content types) without manual YAML writing.&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"module-project--post-installation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Post-Installation&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Once installed, you will configure the generator through the AI module's interface. You must have an active AI provider configured via the core AI module to process prompts and begin generating recipes.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"module-project--additional-requirements"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Additional Requirements&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This module requires the following dependencies:&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;AI (Artificial Intelligence) module ecosystem&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"module-project--recommended-libraries"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Recommended modules/libraries&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;There are currently no additional recommended modules beyond the required AI ecosystem dependencies.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"module-project--similar-projects"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Similar projects&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;While standard Drupal site-building tools exist, this project uniquely leverages generative LLMs and the new Drupal Recipe API to architect configurations via natural language.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"module-project--support"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Supporting this Module&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This project is currently being developed as part of &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Google Summer of Code 2026&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"module-project--community-documentation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Community Documentation&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Weekly progress, architectural breakdowns, and documentation for this GSoC 2026 project are being documented under the &lt;span class="nt"&gt;&amp;lt;em&amp;gt;&lt;/span&gt;Engineering in the Wild&lt;span class="nt"&gt;&amp;lt;/em&amp;gt;&lt;/span&gt; series.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Further you can also add your logo here. It was also confusing for me to find out where the logo is. &lt;/p&gt;

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

&lt;p&gt;You are almost done!!! You can review and see the details what they are. And then finally you can save and your project is successfully created! You can view my module here:&lt;a href="https://www.drupal.org/project/ai_recipe_generator" rel="noopener noreferrer"&gt;AI Recipe Generator&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;Bonus: How to Add Maintainers&lt;/h1&gt;

&lt;p&gt;Most probably, you will be working with someone else. And it will be necessary to add someone there. Head over to the Maintainers tab and select whom you want to add in the search box. You can select the necessary changes to be made and then press enter!&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Follow Along for my next journey&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.hrishikeshdalal.tech/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhrishikeshdalal.tech%2Fog-image.png" height="800" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.hrishikeshdalal.tech/" rel="noopener noreferrer" class="c-link"&gt;
            Hrishikesh Dalal | Portfolio | Engineer, Innovator, Hackathon Winner
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Official website of Hrishikesh Dalal – Computer Engineering student, Hackathon winner, and tech enthusiast.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hrishikeshdalal.tech%2Ffavicon.ico" width="256" height="256"&gt;
          hrishikeshdalal.tech
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>ai</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>drupal</category>
    </item>
    <item>
      <title>The AI Ghostwriter Experiment</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Sun, 17 May 2026 11:48:08 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/the-ai-ghostwriter-experiment-1o2a</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/the-ai-ghostwriter-experiment-1o2a</guid>
      <description>&lt;p&gt;Over the past few months, I ran an experiment on this blog.&lt;/p&gt;

&lt;p&gt;Balancing engineering projects, open-source contributions, and university means deep-focus writing is a luxury I rarely have. I wanted to test a hypothesis: &lt;em&gt;Can a developer leverage LLMs to fully automate content creation without losing technical depth?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I launched my &lt;em&gt;"Engineering in the Wild"&lt;/em&gt; &amp;amp; "System Design" series to find out. Here is what I learned from letting AI ghostwrite my blog, and why I’m officially pivoting my strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Workflow
&lt;/h3&gt;

&lt;p&gt;It wasn’t just mindless prompting. I built a specific pipeline to ground the AI in reality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input:&lt;/strong&gt; Raw notes on architectural decisions (like sync APIs vs. Pub/Sub) or details from my ongoing projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation:&lt;/strong&gt; Prompting the LLM to structure an engaging, DEV.to-optimized article.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; Flawless markdown, clean code snippets, and accessible analogies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On paper, it worked perfectly. I shipped consistently, and the articles read like polished, textbook overviews that explained complex concepts exceptionally well.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Flaw: Sanitized Engineering
&lt;/h3&gt;

&lt;p&gt;While the explanations were solid, the experiment revealed a fatal flaw in fully automated content: &lt;strong&gt;it completely sanitizes the reality of writing code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reviewing the generated articles made a few things glaringly obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loss of Signal:&lt;/strong&gt; AI outputs the statistical average of human knowledge. It produces clean, pristine ideals. But real engineering is chaotic and messy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Erasure of Friction:&lt;/strong&gt; The real value of a dev blog isn't just the final architecture—it’s the friction it took to get there. AI erased the edge-case geofencing bugs in my attendance platform, the legacy codebase constraints at my enterprise internship, and the brutal code reviews from merging open-source AI modules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Commodity Trap:&lt;/strong&gt; A flawless explanation of a protocol is useful, but if anyone can generate it in ten seconds, it loses its unique edge. It didn’t show how &lt;em&gt;I&lt;/em&gt; think; it showed how the model thinks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Pivot: Authentic Grit
&lt;/h3&gt;

&lt;p&gt;The foundational overviews the AI helped me build serve their purpose, and AI remains a phenomenal tool. But going forward, the workflow for &lt;em&gt;"Engineering in the Wild"&lt;/em&gt; &amp;amp; "System Design" is evolving.&lt;/p&gt;

&lt;p&gt;The best documentation isn’t just a flawless essay; it’s a collection of genuine experiences from the trenches of development. It’s time to bring the grit back to this blog.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Even this meta-analysis was generated by AI :) but the pivot is 100% human.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>ai</category>
    </item>
    <item>
      <title>EP 15: Pub/Sub - Stop Chaining Your Services</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Wed, 18 Feb 2026 11:20:06 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/ep-15-pubsub-stop-chaining-your-services-53ng</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/ep-15-pubsub-stop-chaining-your-services-53ng</guid>
      <description>&lt;p&gt;You just launched your dream startup, &lt;strong&gt;"DevEats"&lt;/strong&gt;—a food delivery app for developers who need serious fuel while debugging. Your signature item? The &lt;strong&gt;"Triple Stack Smash Burger with Truffle Fries."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the beginning, life was simple. You had one backend service (a monolith). A user ordered a burger, your code saved it to the database, and life was good.&lt;/p&gt;

&lt;p&gt;But you grew fast. Now, YOU are the owner of a scaling platform, and your code is turning into a tangled mess of spaghetti (pun intended).&lt;/p&gt;

&lt;p&gt;Here is why you need the &lt;strong&gt;Pub/Sub&lt;/strong&gt; pattern, explained through the lens of your own burger empire.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What is the Need? (The "Smash Burger Panic")
&lt;/h2&gt;

&lt;p&gt;Let’s look at your current codebase. When a user clicks "Order Burger", your backend does this strictly in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Payment Service:&lt;/strong&gt; Charge the credit card $25.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order Service:&lt;/strong&gt; Save the order to the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notification Service:&lt;/strong&gt; Send a receipt email.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kitchen Service:&lt;/strong&gt; Print the ticket for the chef.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics Service:&lt;/strong&gt; Update your admin dashboard sales graph.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
One Friday night, your &lt;strong&gt;Email Provider goes down&lt;/strong&gt;.&lt;br&gt;
Because your code is sequential (A -&amp;gt; B -&amp;gt; C -&amp;gt; D), the entire process crashes at Step 3.&lt;/p&gt;

&lt;p&gt;The payment went through (Step 1), but the kitchen printer never got the ticket (Step 4).&lt;/p&gt;

&lt;p&gt;Now you have a hangry developer who paid for a Triple Stack Burger that isn't being made. They are angry, and your support inbox is flooding.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The Need:&lt;/strong&gt;&lt;br&gt;
You need a way for your system to say, &lt;em&gt;"Hey, a burger was just ordered!"&lt;/em&gt; without caring who is listening or if the email service is currently online. You need to &lt;strong&gt;decouple&lt;/strong&gt; the sender from the receivers.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Solution: Pub/Sub Architecture
&lt;/h2&gt;

&lt;p&gt;In a Publish/Subscribe (Pub/Sub) model, we split your architecture into three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Publisher (You/The Order Service):&lt;/strong&gt; You simply shout a message ("Order #505: Triple Smash Burger!") into a channel. You don't know who is listening. You don't care.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Topic (The Channel):&lt;/strong&gt; A dedicated lane for specific types of messages (e.g., &lt;code&gt;events.burger_ordered&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Subscribers (The Listeners):&lt;/strong&gt; Independent services that have signed up to listen to that topic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The New Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User orders the burger.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You (Order Service)&lt;/strong&gt; publish a message to the &lt;code&gt;events.burger_ordered&lt;/code&gt; topic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Done.&lt;/strong&gt; You return "Success" to the user immediately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Meanwhile, in the background:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Kitchen Service&lt;/strong&gt; hears the message and instantly prints the ticket on the grill station.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Notification Service&lt;/strong&gt; hears the message and &lt;em&gt;tries&lt;/em&gt; to send an email. If it fails (because the provider is down), it retries later. &lt;strong&gt;Crucially, it does not stop the kitchen from cooking.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Rewards Service&lt;/strong&gt; hears the message and adds 50 points to the user's account.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. When to Use It?
&lt;/h2&gt;

&lt;p&gt;You shouldn't use Pub/Sub for everything. Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1-to-Many Relationships:&lt;/strong&gt; One event (Burger Ordered) triggers multiple, unrelated actions (Email, Kitchen Display, Inventory Check).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling is Vital:&lt;/strong&gt; You want to add a new "SMS Service" next week without rewriting your existing Order Service code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Processing:&lt;/strong&gt; You don't want the user staring at a loading spinner while your server generates a PDF invoice.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. The Pros and Cons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ The Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; You can add 50 new subscribers (e.g., a "Surge Pricing Bot") without touching the original Publisher code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability:&lt;/strong&gt; If one subscriber crashes (e.g., the Email service), the others (Kitchen, Analytics) keep working perfectly. The burger still gets made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; The Publisher fires the event and moves on. The user gets a "Your Order is Placed!" screen instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ The Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; You are introducing a new component (a Broker like Redis, Kafka, or Google Pub/Sub) to manage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging is a Nightmare:&lt;/strong&gt; You can't just follow a linear stack trace anymore. You have to trace messages flying across a network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency Issues:&lt;/strong&gt; What if the message is delivered twice? (Does the customer get charged twice? Does the chef make two burgers?). You have to write code to handle these edge cases.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Pub/Sub vs. Message Queue (The Confusion)
&lt;/h2&gt;

&lt;p&gt;This is where most devs get tripped up. Both use "Brokers," but the intent is different.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scenario A: Pub/Sub (The "Megaphone")&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; You launch a &lt;strong&gt;New limited-edition Ghost Pepper Burger.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Logic:&lt;/strong&gt; &lt;em&gt;1 Message -&amp;gt; Many Receivers.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Publisher:&lt;/strong&gt; The Menu Manager Service.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subscribers:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Notification Service:&lt;/strong&gt; Pushes "New Burger Alert!" to all users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory Service:&lt;/strong&gt; Reserves spicy peppers in the warehouse.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Marketing Service:&lt;/strong&gt; Posts automatically to Twitter/X.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Everyone needs to know about this event to do their own specific job.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scenario B: Message Queue (The "Grill Line")&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; It's lunch rush. You have 500 burger orders coming in.&lt;br&gt;
&lt;strong&gt;Logic:&lt;/strong&gt; &lt;em&gt;1 Message -&amp;gt; 1 Receiver (Load Balancing).&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Producer:&lt;/strong&gt; The Order Service pushes 500 tickets to a &lt;strong&gt;Queue&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consumers (Chefs):&lt;/strong&gt; You have 5 Chefs (Chef A, B, C, D, E).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Flow:&lt;/strong&gt; Chef A grabs Ticket #1. Chef B grabs Ticket #2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why?&lt;/strong&gt; If Chef A is grilling the burger for Ticket #1, you &lt;strong&gt;do not&lt;/strong&gt; want Chef B to grill the same burger. That’s a waste of meat. You want to &lt;em&gt;distribute&lt;/em&gt; the work, not broadcast it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Cheat Sheet&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Pub/Sub&lt;/th&gt;
&lt;th&gt;Message Queue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Philosophy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Everyone needs to know this happened."&lt;/td&gt;
&lt;td&gt;"Someone please do this job."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Distribution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Broadcast (One-to-All)&lt;/td&gt;
&lt;td&gt;Point-to-Point (One-to-One)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Burger Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"New Menu Item Launched!" (Tell everyone)&lt;/td&gt;
&lt;td&gt;"Grill this patty" (One chef does it)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Final Words&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As the owner of &lt;strong&gt;DevEats&lt;/strong&gt;, switching to Pub/Sub saved your Friday night rush. When your Email Service crashed, nobody noticed. The orders kept flowing, the smash burgers kept sizzling, and the emails were just queued up and sent an hour later.&lt;/p&gt;

&lt;p&gt;That is the power of decoupling. 🍔 code responsibly!&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>pubsub</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Jungle Protocol: Turning my Professional Journey into an Adaptive Quest</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Sun, 01 Feb 2026 13:58:07 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/the-jungle-protocol-turning-my-professional-journey-into-an-adaptive-quest-3p4m</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/the-jungle-protocol-turning-my-professional-journey-into-an-adaptive-quest-3p4m</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;I’m Hrishikesh Dalal, a Computer Engineering student at Sardar Patel Institute of Technology (SPIT), Mumbai. I don't just write code; I build experiences. Currently, I’m engineering a Jumanji-themed gamified portfolio that turns the standard resume into an adventure.&lt;/p&gt;

&lt;p&gt;My focus lies in Full Stack Development (Next.js, React) and creating developer tools—I recently published envalyze and env-fault to the NPM registry to help devs manage environments better. When I'm not pushing commits to Open Source projects like Drupal or contributing to MDN Web Docs, I'm exploring the intersection of AI and law with projects like VerdictAI.&lt;/p&gt;

&lt;p&gt;I write about system design, open source, and the reality of building software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://hrishikesh-dalal-portfolio-571902073238.europe-west1.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;p&gt;Link: &lt;a href="https://hrishikesh-dalal-portfolio-571902073238.europe-west1.run.app" rel="noopener noreferrer"&gt;https://hrishikesh-dalal-portfolio-571902073238.europe-west1.run.app&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  My Tech Stack
&lt;/h3&gt;

&lt;p&gt;To build an experience this immersive, I needed a stack that balanced performance with high-end visuals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: Next.js &amp;amp; Tailwind CSS&lt;/li&gt;
&lt;li&gt;Animations: GSAP (for that cinematic feel)&lt;/li&gt;
&lt;li&gt;3D Elements: Three.js (for the interactive globe and hero patterns)&lt;/li&gt;
&lt;li&gt;AI:Gemini API (powering the NPC-style chatbots)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Process: From Chaos to Clarity
&lt;/h3&gt;

&lt;p&gt;Building a portfolio is a trap. You want to show off everything, but if you're not careful, you end up with a bloated mess.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The Affinity Mapping Phase
&lt;/h4&gt;

&lt;p&gt;Initially, I was drowning in ideas from my UI/UX course. I had a laundry list: a full 3D world, a terminal, hidden easter eggs, business-centric project stats, and a Jumanji-themed game. I used &lt;strong&gt;Affinity Mapping&lt;/strong&gt; to dump every "cool" idea onto a board and then ruthlessly segregated them.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. The "Reality Check"
&lt;/h4&gt;

&lt;p&gt;I had to ask the most important question: &lt;strong&gt;Why is someone here?&lt;/strong&gt;&lt;br&gt;
Hiring managers are busy. They might be tech-savvy, or they might not be. They might want to play a game, or they might just want to see my resume and leave in 30 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Decision:&lt;/strong&gt; A "Clean &amp;amp; Clear" landing page with a defined Call to Action (CTA) is non-negotiable. The "wow" factor should enhance the info, not hide it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design Breakdown: Section by Section
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Hero (The First 5 Seconds)
&lt;/h4&gt;

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

&lt;p&gt;I treated the Hero section like a movie opening. I implemented a preloader with a cinematic transition that drops you into an interactive dot pattern. It’s meant to grab attention immediately.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Impact (Big Numbers &amp;amp; Open Source)
&lt;/h4&gt;

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

&lt;p&gt;Instead of just saying "I build tools," I used big, bold typography to showcase my &lt;strong&gt;NPM stats&lt;/strong&gt;. I wanted the real-world impact of &lt;code&gt;envalyze&lt;/code&gt; and &lt;code&gt;env-fault&lt;/code&gt; to be unavoidable. I also dedicated space to my Open Source journey—showing my Hacktoberfest badges and my role as a project admin.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Experience Timeline
&lt;/h4&gt;

&lt;p&gt;I used a scroll-triggered timeline with GSAP micro-interactions. When you hover over an entry, there’s a slight movement—just enough to feel alive, but not enough to be distracting.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Terminal (For the Geeks)
&lt;/h4&gt;

&lt;p&gt;I built a fully functional terminal for my projects. You can change themes, run an &lt;code&gt;echo&lt;/code&gt;, or see animations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; Try typing &lt;code&gt;sudo&lt;/code&gt;. You’ve been warned.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Accessibility Switch:&lt;/strong&gt; If you aren't a terminal person, there's a toggle to switch back to a standard grid. No one gets left behind.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Jungle" Experience
&lt;/h3&gt;

&lt;p&gt;This was the hardest part to get right. My first version was just a collection of games, and the feedback from friends was brutal: &lt;em&gt;"It’s disconnected. I don't see YOU in this."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I went back to the drawing board and turned it into a narrative. I added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A Prologue &amp;amp; Epilogue:&lt;/strong&gt; To give the "quest" a purpose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Level 2 (The Jumper):&lt;/strong&gt; While you play, the character narrates my tech stack and experience journey.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret Cheat Codes:&lt;/strong&gt; Because what's a game without them?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bringing it to life with AI
&lt;/h3&gt;

&lt;p&gt;My main chatbot you have different answers queries, sample questions that you can ask to make it better. &lt;/p&gt;

&lt;p&gt;While the game chatbot is like NPC character answering you.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Architecture
&lt;/h3&gt;

&lt;p&gt;The diagram below shows how the game chatbot is integrated to handle context-aware queries, ensuring it knows where you are in the game and who I am as a developer.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The User Interaction Layer&lt;br&gt;
User Sends Message: The process begins with the user interacting with the interface. &lt;br&gt;
Frontend App (NEXT JS): The core UI is built using React. It features a React Markdown Renderer, which ensures that the AI's responses—often containing code snippets or formatted text—are displayed cleanly and professionally to the user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security and Traffic Management&lt;br&gt;
Backend API Gateway &amp;amp; Security Layer: Instead of calling the Gemini API directly from the frontend (which would expose your API keys), the app calls a secure backend gateway. This layer handles authentication and protects your credentials.&lt;br&gt;
Load Balancer: To prevent any single server from becoming overwhelmed, a load balancer sits in the middle. It analyzes incoming requests and routes them efficiently across multiple active instances.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Processing Core&lt;br&gt;
Chat Model Instances: The system uses multiple parallel instances (Instance 1, 2, and 3) to process requests.&lt;br&gt;
Least Used Routing: The load balancer is shown routing a request specifically to "Instance 1" because it is currently the "Least Used," ensuring optimal performance and low latency.&lt;br&gt;
Google's Gemini API: These instances act as the bridge to Google’s infrastructure. They send the processed user prompt to the Gemini API and receive the raw AI generation in return.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Response Loop&lt;br&gt;
Forwards Response: Once a chat instance receives the data from Google, it forwards that response back through the secure backend.&lt;br&gt;
Final Display: The message is sent back to the NEXT JS app, where it is rendered as Markdown, completing the loop and appearing on the user's screen.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Easter Eggs
&lt;/h3&gt;

&lt;p&gt;I have a game at the start where the user can press the "?" button and play that simple.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Stay Hungry, Stay Foolish."&lt;/strong&gt; My portfolio ends with this quote because it sums up my approach to engineering. Whether it's deep-diving into the Drupal source code or building a terminal from scratch, I’m always looking for the next thing that feels slightly impossible to build.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;p&gt;If you’re a developer, you know the feeling: you spend 10% of your time on the features people see and 90% of your time on the "over-engineered" systems that make you feel like a wizard. Here are the pillars of this portfolio that I obsessed over.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Terminal (Because real devs use a CLI)
&lt;/h3&gt;

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

&lt;p&gt;I didn't want a static "Projects" page; I wanted something that felt like home. I built a fully functional, web-based &lt;strong&gt;Terminal&lt;/strong&gt; where you can actually interact with my work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Experience:&lt;/strong&gt; It’s a working CLI where you can navigate projects, change themes, or trigger animations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Sudo" Trap:&lt;/strong&gt; I’ve hidden easter eggs for people who like to poke around—try typing &lt;code&gt;sudo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Hybrid UI:&lt;/strong&gt; I know recruiters aren't always looking to type commands, so I built a "safety net" toggle that instantly switches the terminal into a standard project grid.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Projects with a "Business First" Logic
&lt;/h3&gt;

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

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

&lt;p&gt;Most portfolios just show a screenshot and a GitHub link. I decided to treat my projects like real products.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Beyond the Code:&lt;/strong&gt; When you click into a project, I don't just list the tech stack. I break down the &lt;strong&gt;Business Perspective&lt;/strong&gt;, the &lt;strong&gt;Impact&lt;/strong&gt;, and the &lt;strong&gt;Stats&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Why" Factor:&lt;/strong&gt; I included the reasoning behind my architectural choices so that a non-technical stakeholder can understand the value I created, not just the code I wrote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Dives:&lt;/strong&gt; Each project page is designed to show the journey from a pain point to a scalable solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. The Jumanji Game Engine
&lt;/h3&gt;

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

&lt;p&gt;I turned my professional experience into a narrative quest. This wasn't just about putting a game on a website; it was about making the resume &lt;em&gt;playable&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Narrative:&lt;/strong&gt; I added a full prologue and epilogue to make sure it felt like a real quest, not just a random mini-game.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Career Jumper:&lt;/strong&gt; In Level 2, the character literally narrates my tech stack and my journey through different roles while you play.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recruiter Mode:&lt;/strong&gt; I even made the game adaptive—a recruiter can upload a job description, and the game will fine-tune the information it highlights to match what they need.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. The Chatbot Architecture
&lt;/h3&gt;

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

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

&lt;p&gt;I’m most proud of what’s happening "under the hood" here. I built a production-ready pipeline for the Gemini-powered Chatbot.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Load Balancer:&lt;/strong&gt; My system uses three different chat model instances. I implemented a load balancer that routes traffic to the "least used" instance to keep things snappy, no matter the traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security &amp;amp; Scalability:&lt;/strong&gt; I kept the logic on the backend. The React frontend talks to a secure API Gateway, which handles the Gemini API calls to keep my keys safe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The NPC Persona:&lt;/strong&gt; I used the &lt;strong&gt;Gemini API&lt;/strong&gt; to give the bot a specific "In-game NPC" persona—it knows it’s in the Jungle, but it also knows my GitHub stats.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
    <item>
      <title>System Design: EP 14 - The Coffee Shop Guide to Understanding Message Brokers</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Wed, 21 Jan 2026 11:17:47 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/system-design-ep-14-the-coffee-shop-guide-to-understanding-message-brokers-393b</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/system-design-ep-14-the-coffee-shop-guide-to-understanding-message-brokers-393b</guid>
      <description>&lt;p&gt;If you are a developer moving from a monolith to microservices, you’ve probably hit &lt;strong&gt;The Problem&lt;/strong&gt;. Service A calls Service B, but Service B is slow, or down, or just busy. Suddenly, Service A hangs, the user sees a spinning wheel of death, and you get paged at 3 AM.&lt;/p&gt;

&lt;p&gt;The solution? Stop making your services talk directly. Introduce a &lt;strong&gt;Message Broker&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If that sounds abstract, let's talk about coffee.&lt;/p&gt;




&lt;h3&gt;
  
  
  The "Bad" Coffee Shop (Synchronous)
&lt;/h3&gt;

&lt;p&gt;Imagine a coffee shop where the person taking your order is also the &lt;em&gt;only&lt;/em&gt; person allowed to make your coffee.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt; (The User) walk in and order a latte.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Cashier&lt;/strong&gt; (Service A) takes your money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Cashier&lt;/strong&gt; then walks over to the espresso machine, steams the milk, pulls the shot, pours the art, and hands it to you.&lt;/li&gt;
&lt;li&gt;Only &lt;em&gt;then&lt;/em&gt; do they return to the register to take the next person's order.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt; A massive line out the door. If the espresso machine breaks, the cashier is stuck waiting, and nobody can even &lt;em&gt;place&lt;/em&gt; an order. This is &lt;strong&gt;Synchronous Communication&lt;/strong&gt; (like HTTP REST calls). It creates tight coupling and bottlenecks.&lt;/p&gt;




&lt;h3&gt;
  
  
  The "Good" Coffee Shop (Asynchronous with a Broker)
&lt;/h3&gt;

&lt;p&gt;Now, let's look at how a Starbucks or a high-volume cafe actually works.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt; (The User) order a latte.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Cashier&lt;/strong&gt; (Service A / Producer) takes your money and writes your order on a cup (or a ticket).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Cashier&lt;/strong&gt; places the cup on the &lt;strong&gt;Counter/Ticket Rail&lt;/strong&gt; (The Message Broker).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Cashier&lt;/strong&gt; immediately turns back to you and says, "Next please!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Barista&lt;/strong&gt; (Service B / Consumer) sees the cup on the rail, picks it up, and makes the coffee.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt; The cashier never stops working. They can take 50 orders in the time it takes the barista to make 5 drinks. The "Ticket Rail" acts as a buffer.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Technical Translation
&lt;/h3&gt;

&lt;p&gt;That "Ticket Rail" is your Message Broker (like RabbitMQ, Kafka, or AWS SQS). Here is the mapping for your next system design interview:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The Producer (The Cashier)
&lt;/h4&gt;

&lt;p&gt;This is the service that sends the data. In the coffee shop, the cashier "produces" the order. They don't care &lt;em&gt;who&lt;/em&gt; makes the coffee or &lt;em&gt;when&lt;/em&gt; it gets made; they just need to know the order has been safely placed on the rail.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dev Term:&lt;/strong&gt; &lt;code&gt;Publish&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. The Message Broker (The Ticket Rail)
&lt;/h4&gt;

&lt;p&gt;This is the middleware that sits between your services. It holds the messages (orders) until a consumer is ready to take them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Queue:&lt;/strong&gt; The line of cups waiting on the machine. If the baristas get overwhelmed, the queue grows, but the cups aren't lost. They are safely stored until the backlog clears.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. The Consumer (The Barista)
&lt;/h4&gt;

&lt;p&gt;This is the service that processes the data. The barista "consumes" the order from the rail.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dev Term:&lt;/strong&gt; &lt;code&gt;Subscribe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling:&lt;/strong&gt; If the line of cups gets too long, what do you do? You don't clone the cashier; you add a second barista. Message brokers allow you to scale your processing power (Consumers) independently of your intake power (Producers).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Why use a Message Broker?
&lt;/h3&gt;

&lt;p&gt;Using the coffee shop model, the benefits become obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling:&lt;/strong&gt; The cashier doesn't need to know which barista is working today. If the barista quits (Service B crashes), the cashier can keep taking orders. The cups just pile up on the rail until a new barista is hired (Service B restarts). No data is lost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throttling (Load Leveling):&lt;/strong&gt; If 100 people rush the store at once, the cashier takes all the orders quickly. The baristas continue working at their normal, safe speed. They don't explode from stress; they just work through the backlog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Processing:&lt;/strong&gt; Heavy tasks (like roasting beans or baking pastries) don't block the customer from paying and leaving.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Direct HTTP calls are like a cashier making every drink themselves. It works for a lemonade stand, but it fails at scale.&lt;/p&gt;

&lt;p&gt;A Message Broker is the ticket rail that lets your services work at their own speeds, independently and reliably.&lt;/p&gt;

&lt;p&gt;So, the next time you're architecting a system, ask yourself: &lt;strong&gt;Are we building a lemonade stand, or are we building a coffee empire?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>System Design - EP 13: Content Delivery Networks (CDNs)</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Fri, 16 Jan 2026 16:11:48 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/system-design-ep-13-content-delivery-networks-cdns-14ke</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/system-design-ep-13-content-delivery-networks-cdns-14ke</guid>
      <description>&lt;p&gt;We’ve all been there. You click a link, and the page hangs. You stare at the loading spinner, your patience draining with every second. In 2026, a slow website isn't just annoying; it’s a dealbreaker.&lt;/p&gt;

&lt;p&gt;As developers, we know &lt;strong&gt;Speed = Revenue&lt;/strong&gt;. But how do you make a website fast for a user in Tokyo when your server is sitting in a basement in New York?&lt;/p&gt;

&lt;p&gt;Enter the &lt;strong&gt;CDN (Content Delivery Network)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you've ever nodded along while someone said "just put it behind Cloudflare" but were secretly fuzzy on the details, this article is for you. Let's break down CDNs using a scenario we can all understand: &lt;strong&gt;The Global Pizza Empire.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Scenario: "Uncle Tony's Pizza"
&lt;/h2&gt;

&lt;p&gt;Imagine you run the world's best pizza shop, &lt;strong&gt;Uncle Tony's&lt;/strong&gt;, located in &lt;strong&gt;New York City&lt;/strong&gt;. Your pizza is legendary. People fly in from all over the world just to get a slice.&lt;/p&gt;

&lt;p&gt;In technical terms, your New York shop is the &lt;strong&gt;Origin Server&lt;/strong&gt;. It is the single source of truth where the pizza (content) is created.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: The Latency Delivery
&lt;/h3&gt;

&lt;p&gt;One day, you decide to offer global delivery.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customer A&lt;/strong&gt; lives in Brooklyn (5 miles away). They get their pizza hot and fresh in &lt;strong&gt;20 minutes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer B&lt;/strong&gt; lives in London (3,500 miles away). You have to bake the pizza in NY, put it on a supersonic jet, and fly it to London. It arrives 6 hours later, cold and soggy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This delay is &lt;strong&gt;Latency&lt;/strong&gt;. In the web world, even if your server (New York Kitchen) is super fast, the physical distance to the user (London Customer) creates unavoidable lag.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: The Franchise Model (The CDN)
&lt;/h2&gt;

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

&lt;p&gt;You realize you can't defy physics. You can't make the jet faster. So, you change your strategy. You open small, reheating stations in major cities around the world: London, Tokyo, Mumbai, and Sydney.&lt;/p&gt;

&lt;p&gt;These stations are your &lt;strong&gt;Edge Servers&lt;/strong&gt;. They don't have the full kitchen setup of the NYC headquarters, but they are capable of holding inventory.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the "Caching" Works
&lt;/h3&gt;

&lt;p&gt;You can't bake every single unique pizza at these small stations. Instead, you look at your data. You realize 80% of people just order &lt;strong&gt;Pepperoni&lt;/strong&gt; or &lt;strong&gt;Cheese&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The First Request (Cache Miss):&lt;/strong&gt;
A guy in London orders a Pepperoni pizza. The London station is empty. They call the NY HQ, get a Pepperoni pizza flown over, and deliver it. It’s slow, but now the London station keeps a stash of 50 frozen Pepperoni pizzas in their freezer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Second Request (Cache Hit):&lt;/strong&gt;
Ten minutes later, a girl in London orders a Pepperoni pizza. The London station checks its freezer. &lt;strong&gt;Bingo!&lt;/strong&gt; They grab one, heat it up, and deliver it in 10 minutes. They didn't even call New York.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Theory: Key Concepts in CDNs
&lt;/h2&gt;

&lt;p&gt;Now that we understand the pizza model, let's look at the actual technical terms you'll need for your system design interviews or cloud configs.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Edge Servers
&lt;/h3&gt;

&lt;p&gt;These are geographically distributed servers where CDN providers (like Cloudflare, Akamai, or Fastly) cache your content. Users are routed to the nearest edge server for faster delivery.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pizza Translation:&lt;/em&gt; The local reheating stations in London or Tokyo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Origin Server
&lt;/h3&gt;

&lt;p&gt;This is your main web server (e.g., an AWS EC2 instance or S3 bucket). The CDN fetches content from here &lt;em&gt;only&lt;/em&gt; if it’s not already cached at the edge server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pizza Translation:&lt;/em&gt; The main Uncle Tony's Kitchen in New York.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Caching
&lt;/h3&gt;

&lt;p&gt;CDNs store copies of your static content (e.g., images, videos, CSS, HTML) in their edge servers. Cached content is served directly to users, reducing the need for frequent requests to the origin server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pizza Translation:&lt;/em&gt; Storing frozen Pepperoni pizzas in the local freezer so you don't have to bake a fresh one every time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. TTL (Time to Live)
&lt;/h3&gt;

&lt;p&gt;The duration for which a file is cached on a CDN edge server.&lt;br&gt;
&lt;strong&gt;Example:&lt;/strong&gt; An image might have a TTL of 24 hours. This means it stays cached for 24 hours; after that, the Edge Server assumes it might be stale, deletes it, and fetches a fresh copy from the Origin.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pizza Translation:&lt;/em&gt; The expiration date on the frozen pizza. You throw it out after 24 hours to ensure quality.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. GeoDNS
&lt;/h3&gt;

&lt;p&gt;CDNs use &lt;strong&gt;GeoDNS&lt;/strong&gt; to route users to the nearest edge server based on their geographic location. When a user types in your URL, the DNS resolver looks at their IP address and says, "You're in Germany? Okay, talk to the Frankfurt server, not the New York one."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pizza Translation:&lt;/em&gt; The call center routing your order to the nearest branch automatically.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why this Architecture Wins
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Speed (Performance)
&lt;/h3&gt;

&lt;p&gt;Your users in Tokyo aren't downloading images from New York anymore; they are downloading them from a server in Shinjuku. The data travels fewer miles, reducing the &lt;strong&gt;Time to First Byte (TTFB)&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Scalability (Traffic Spikes)
&lt;/h3&gt;

&lt;p&gt;Imagine it's Super Bowl Sunday. Everyone calls New York at once. The phone lines jam. The kitchen catches fire.&lt;br&gt;
With the Franchise model (CDN), the New York kitchen only handles the weird custom orders. The millions of standard Pepperoni orders are handled by the thousands of local branches. Your main kitchen survives.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Security (DDoS Protection)
&lt;/h3&gt;

&lt;p&gt;Imagine a competitor hires a million bots to prank call your New York Pizza shop so no real customers can get through.&lt;br&gt;
With a CDN, these prank calls hit the thousands of local branches first. The branches are smart—they realize it's a prank and hang up before the call ever reaches the New York HQ.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>System Design EP: 12 - Why Your Database Hates Your Images: A Guide to BLOBs</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Wed, 14 Jan 2026 14:34:27 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/system-design-ep-12-why-your-database-hates-your-images-a-guide-to-blobs-2k00</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/system-design-ep-12-why-your-database-hates-your-images-a-guide-to-blobs-2k00</guid>
      <description>&lt;p&gt;Ummm, BLOBS. Everyone has heard that term somewhere sometime across their development journey, but what are those? &lt;/p&gt;

&lt;p&gt;In system design, a BLOB (Binary Large Object) is any unstructured data that doesn't fit neatly into a database row. Unlike integers or strings, the database engine doesn't "understand" this data,it just sees a massive chunk of bytes.&lt;/p&gt;

&lt;p&gt;Examples: User avatars (JPEG), video files (MP4), PDFs, Application Logs, Backup files.&lt;/p&gt;

&lt;p&gt;Size Range: From a few KBs (thumbnails) to TBs (genome sequencing data).&lt;/p&gt;

&lt;p&gt;So basically storing mp4 file directly as a file is difficult, how to store it say in MySQL but it becomes easier when we store in Blobs using 0 &amp;amp; 1s.&lt;/p&gt;

&lt;p&gt;When looking for the "definitive" resource on Blobs (Binary Large Objects) in System Design, most engineers end up piecing together chapters from different sources.&lt;/p&gt;

&lt;p&gt;Since a single perfect article is hard to find, I have synthesized the industry-standard approach below—essentially the article you are looking for—followed by links to the best external deep-dives.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Great Debate: Database vs. Object Storage&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The most common interview question and real-world decision is: &lt;em&gt;"Should I store user uploads in my SQL database or on disk?"&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Option A: Storing BLOBs in the Database&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;You store the image directly in a column (e.g., &lt;code&gt;VARBINARY&lt;/code&gt; in MySQL or &lt;code&gt;BYTEA&lt;/code&gt; in PostgreSQL).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; ACID compliance (the image and the user profile update commit together), easy backups (one dump file).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Suicide:&lt;/strong&gt; Databases are optimized for small, random reads/writes. Reading a 10MB image consumes the same I/O as reading thousands of user rows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; Block storage (SSD for DBs) is 3-5x more expensive than Object Storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; You cannot easily cache database responses at the edge (CDN) compared to static URLs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Option B: Storing BLOBs in Object Storage (The Standard)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;You store the actual file in a service like Amazon S3, Google Cloud Storage, or Azure Blob, and store only the &lt;strong&gt;reference URL&lt;/strong&gt; in your database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Infinite scalability, cheaper storage tiers, built-in redundancy, easy integration with CDNs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Loose consistency (you might delete the database row but forget to delete the S3 file, creating "orphan" data).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Verdict:&lt;/strong&gt; &lt;strong&gt;99% of the time, use Object Storage (Option B).&lt;/strong&gt; Only use Option A if your files are tiny (&amp;lt;20KB), strictly transactional, and security is paramount (e.g., encryption keys or sensitive legal docs that must never leak via a public URL).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Architecture Pattern&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The standard design pattern for handling blobs involves decoupling metadata from storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client Request:&lt;/strong&gt; User uploads a profile picture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway:&lt;/strong&gt; Authenticates the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presigned URL:&lt;/strong&gt; Instead of uploading to your server (which blocks your server's threads), your backend generates a "Presigned URL" from S3. This authorizes the client to upload &lt;em&gt;directly&lt;/em&gt; to the bucket for a limited time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Upload:&lt;/strong&gt; Client uploads the binary data directly to S3/Blob Storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirmation:&lt;/strong&gt; S3 returns a success code. The client notifies your backend: "Upload Complete."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata Save:&lt;/strong&gt; Your backend saves the file path (e.g., &lt;code&gt;s3://my-bucket/users/123/avatar.png&lt;/code&gt;) into the SQL database &lt;code&gt;users&lt;/code&gt; table.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>systemdesign</category>
      <category>blobs</category>
      <category>aws</category>
    </item>
    <item>
      <title>EP 11: The Legend of ShopStream: The In-Memory Revolution</title>
      <dc:creator>Hrishikesh Dalal</dc:creator>
      <pubDate>Tue, 13 Jan 2026 10:15:59 +0000</pubDate>
      <link>https://dev.to/hrishikesh_dalal_ced8f95e/ep-11-the-legend-of-shopstream-the-in-memory-revolution-9ic</link>
      <guid>https://dev.to/hrishikesh_dalal_ced8f95e/ep-11-the-legend-of-shopstream-the-in-memory-revolution-9ic</guid>
      <description>&lt;p&gt;Akash had fixed his database latency problems by using Redis as a cache. ShopStream was fast. But Akash was about to learn a hard lesson: &lt;strong&gt;Redis is not just a "dumb" cache.&lt;/strong&gt; It is a Ferrari engine that he was using to drive to the grocery store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chapter 1: The Day the Lights Went Out (Persistence)
&lt;/h3&gt;

&lt;p&gt;One stormy night, the power failed at the data center. The Redis server rebooted.&lt;/p&gt;

&lt;p&gt;When the lights came back on, Akash checked the dashboard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User Sessions:&lt;/strong&gt; Gone. Everyone was logged out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopping Carts:&lt;/strong&gt; Empty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revenue Impact:&lt;/strong&gt; Massive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Akash panicked. &lt;em&gt;"I thought Redis stores data in RAM! If RAM loses power, data is gone!"&lt;/em&gt;&lt;br&gt;
He realized he needed &lt;strong&gt;Persistence&lt;/strong&gt;. He had two options:&lt;/p&gt;

&lt;h4&gt;
  
  
  Option A: The Photographer (RDB - Redis Database Backup)
&lt;/h4&gt;

&lt;p&gt;Akash configured Redis to take a "Snapshot" every hour.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; Every hour, Redis forks a background process to save all data to a file (&lt;code&gt;dump.rdb&lt;/code&gt;) on the hard disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Trade-off:&lt;/strong&gt; It’s compact and fast to restore. &lt;em&gt;But&lt;/em&gt;, if the server crashes at 4:59 PM, he loses 59 minutes of data since the last snapshot at 4:00 PM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Option B: The Stenographer (AOF - Append Only File)
&lt;/h4&gt;

&lt;p&gt;This wasn't enough for "Shopping Carts." So Akash turned on AOF.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; Every time a command runs (e.g., &lt;code&gt;SET cart:123 "Apple"&lt;/code&gt;), Redis logs that command to a file on the disk immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Trade-off:&lt;/strong&gt; No data loss! But the file grows huge over time because it records &lt;em&gt;every&lt;/em&gt; change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Akash used a hybrid approach. He used &lt;strong&gt;AOF&lt;/strong&gt; for critical data (carts/sessions) and &lt;strong&gt;RDB&lt;/strong&gt; for less critical cache data to balance speed and safety.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chapter 2: The Data Structure Buffet (Beyond Strings)
&lt;/h3&gt;

&lt;p&gt;ShopStream was growing. Akash was building a "Live Leaderboard" for the most active shoppers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Newbie" Way:&lt;/strong&gt;&lt;br&gt;
Akash was storing the leaderboard as a JSON string in Redis:&lt;br&gt;
&lt;code&gt;"[{user: 'Alice', score: 10}, {user: 'Bob', score: 5}]"&lt;/code&gt;&lt;br&gt;
Every time Alice bought something, Akash had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GET the whole JSON string.&lt;/li&gt;
&lt;li&gt;Decode it in his backend code.&lt;/li&gt;
&lt;li&gt;Update Alice’s score.&lt;/li&gt;
&lt;li&gt;Sort the array again.&lt;/li&gt;
&lt;li&gt;SET the whole JSON string back to Redis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was slow and caused "Race Conditions" (two users updating at once).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Pro" Way (Redis Data Structures):&lt;/strong&gt;&lt;br&gt;
Akash realized Redis isn't just a Key-Value store; it's a &lt;strong&gt;Data Structure Server&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sorted Sets (The Leaderboard Fix):&lt;/strong&gt;
He used the &lt;code&gt;ZSET&lt;/code&gt; data type.&lt;/li&gt;
&lt;li&gt;Command: &lt;code&gt;ZADD leaderboard 10 "Alice"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Command: &lt;code&gt;ZINCRBY leaderboard 5 "Alice"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Magic:&lt;/strong&gt; Redis automatically kept the list sorted in RAM. To get the top 3 users, Akash just ran &lt;code&gt;ZREVRANGE 0 2&lt;/code&gt;. No application code needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hashes (The User Profile):&lt;/strong&gt;&lt;br&gt;
Instead of storing a user as a big blob of text, he used &lt;code&gt;HASH&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Command: &lt;code&gt;HSET user:101 name "Akash" email "a@test.com"&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Benefit: He could update &lt;em&gt;just&lt;/em&gt; the email without reading/writing the whole user object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lists (The Job Queue):&lt;/strong&gt;&lt;br&gt;
When a user bought an item, the system needed to send an email. Instead of making the user wait, Akash pushed the "Send Email" task into a Redis &lt;code&gt;LIST&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Command: &lt;code&gt;LPUSH email_queue {user_id: 101}&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A background worker simply monitored the list (&lt;code&gt;BRPOP&lt;/code&gt;) and processed emails instantly.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Chapter 3: The Traffic Jam (Single Threaded Nature)
&lt;/h3&gt;

&lt;p&gt;One day, the app froze. CPU usage on the Redis server was at 100%.&lt;/p&gt;

&lt;p&gt;Akash looked at the logs. A junior developer had run a command: &lt;code&gt;KEYS *&lt;/code&gt; (Give me ALL keys).&lt;br&gt;
Because ShopStream had 10 million keys, Redis had to iterate through every single one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Critical Lesson:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Redis is Single-Threaded.&lt;/strong&gt; It processes one command at a time. It is incredibly fast (handling 100,000+ ops/sec), but if &lt;em&gt;one&lt;/em&gt; command takes 1 second (like &lt;code&gt;KEYS *&lt;/code&gt;), all other 99,999 requests are blocked waiting in line.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Akash banned the &lt;code&gt;KEYS&lt;/code&gt; command and used &lt;code&gt;SCAN&lt;/code&gt; (which reads keys in small batches) to prevent blocking the main thread.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chapter 4: Too Big to Fail (Sentinel vs. Cluster)
&lt;/h3&gt;

&lt;p&gt;The data grew to 500GB. But the server only had 64GB of RAM. The server crashed with an OOM (Out of Memory) error.&lt;/p&gt;

&lt;p&gt;Akash faced the ultimate architectural choice: &lt;strong&gt;Scale Up&lt;/strong&gt; or &lt;strong&gt;Scale Out&lt;/strong&gt;?&lt;/p&gt;

&lt;h4&gt;
  
  
  Scenario A: High Availability (Sentinel)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; "If my main Redis server dies, the app dies."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Akash set up &lt;strong&gt;Redis Sentinel&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; 1 Master Node (Write), 2 Slave Nodes (Read).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; Sentinel watches the Master. If the Master dies, Sentinel automatically votes for one of the Slaves to become the new Master.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit:&lt;/strong&gt; The total data size is still limited to the RAM of &lt;em&gt;one&lt;/em&gt; node (64GB).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Scenario B: Infinite Scale (Redis Cluster)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; "I have 500GB of data. I need more RAM."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Akash migrated to &lt;strong&gt;Redis Cluster&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; He bought 10 servers (nodes). Redis automatically split the data across them using &lt;strong&gt;Sharding&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Keys A-M go to Node 1.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keys N-Z go to Node 2.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Magic:&lt;/strong&gt; The client app doesn't need to know which node has the data. It asks the cluster, and the cluster routes the request to the right node.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The Moral of the Story
&lt;/h3&gt;

&lt;p&gt;Akash learned that Redis is the "Swiss Army Knife" of backend engineering.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Persistence:&lt;/strong&gt; Use &lt;strong&gt;AOF&lt;/strong&gt; if you can't lose data; &lt;strong&gt;RDB&lt;/strong&gt; for backups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logic:&lt;/strong&gt; Don't pull data to your code to sort it. Push your data into &lt;strong&gt;Sorted Sets&lt;/strong&gt; or &lt;strong&gt;Lists&lt;/strong&gt; and let Redis do the work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Never block the single thread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling:&lt;/strong&gt; Use &lt;strong&gt;Sentinel&lt;/strong&gt; for reliability, use &lt;strong&gt;Cluster&lt;/strong&gt; for massive data size.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Summary Cheat Sheet
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;String&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic Key-Value&lt;/td&gt;
&lt;td&gt;Caching HTML pages, Sessions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Linked List&lt;/td&gt;
&lt;td&gt;Message Queues, Timelines (Twitter feed).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Set&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unordered Unique&lt;/td&gt;
&lt;td&gt;Storing Followers, IP Whitelists (fast lookups).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sorted Set (ZSet)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sorted by Score&lt;/td&gt;
&lt;td&gt;Leaderboards, Priority Queues.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Field-Value Map&lt;/td&gt;
&lt;td&gt;Storing Objects (User Profiles, Product Details).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pub/Sub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Radio Station&lt;/td&gt;
&lt;td&gt;Real-time Chat, Notification Systems.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TTL (Time to Live)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Expiry&lt;/td&gt;
&lt;td&gt;Auto-deleting OTPs or Cache data.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>architecture</category>
      <category>database</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
