<?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: Ishant gupta</title>
    <description>The latest articles on DEV Community by Ishant gupta (@ishantgupta).</description>
    <link>https://dev.to/ishantgupta</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3903810%2F5764cbd3-17d3-4c9c-aa8e-120a5d8dcf40.webp</url>
      <title>DEV Community: Ishant gupta</title>
      <link>https://dev.to/ishantgupta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ishantgupta"/>
    <language>en</language>
    <item>
      <title>Git-regret 💀 — I Finally Finished the CLI That Reads Your Shame</title>
      <dc:creator>Ishant gupta</dc:creator>
      <pubDate>Tue, 02 Jun 2026 08:31:21 +0000</pubDate>
      <link>https://dev.to/ishantgupta/git-regret-i-finally-finished-the-cli-that-reads-your-shame-ngf</link>
      <guid>https://dev.to/ishantgupta/git-regret-i-finally-finished-the-cli-that-reads-your-shame-ngf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is an official submission for the GitHub Finish-Up-A-Thon Challenge.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Yourself: &lt;a href="https://ishantgupta30.github.io/git-regret/" rel="noopener noreferrer"&gt;https://ishantgupta30.github.io/git-regret/&lt;/a&gt;
&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%2F6q23xflr3yp0zpsjpm9f.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%2F6q23xflr3yp0zpsjpm9f.png" alt="page" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🕒 Introduction: The Ghost in the &lt;code&gt;.git&lt;/code&gt; Directory
&lt;/h2&gt;

&lt;p&gt;Every developer has a digital graveyard.&lt;/p&gt;

&lt;p&gt;It is a hidden directory, usually tucked away in a generic &lt;code&gt;/Developer/Projects&lt;/code&gt; folder, filled with half-baked ideas, abandoned repositories, and code that was written at 3:00 AM under the influence of intense caffeine and false confidence.&lt;/p&gt;

&lt;p&gt;For 18 months, my personal digital graveyard contained a repository called &lt;code&gt;git-audit-tool&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you opened that repository and ran &lt;code&gt;git log&lt;/code&gt;, you would find a brief, tragic story told in three commits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;initial commit - hackathon night&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wip add secret detection - crashes on big repos&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;giving up for tonight, too tired - will finish later&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last commit sat there untouched for a year and a half.&lt;/p&gt;

&lt;p&gt;The code inside was embarrassing.&lt;/p&gt;

&lt;p&gt;It was a single Python file that hardcoded execution limits, contained broken placeholders, and regularly threw unhandled exceptions if you pointed it at a repository with a mature history.&lt;/p&gt;

&lt;p&gt;But the core problem never went away:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nobody actually checks their git history until something goes terribly wrong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We push API keys.&lt;/p&gt;

&lt;p&gt;We squash terrible commit messages into production branches.&lt;/p&gt;

&lt;p&gt;We leave panic-driven &lt;code&gt;fix: layout again&lt;/code&gt; commits scattered across our history.&lt;/p&gt;

&lt;p&gt;When the GitHub Finish-Up-A-Thon was announced, I realized it was time to stop closing the tab.&lt;/p&gt;

&lt;p&gt;It was time to take this broken, abandoned script and turn it into a production-ready CLI.&lt;/p&gt;

&lt;p&gt;This is the story of how I completely rewrote that technical mess into &lt;strong&gt;git-regret&lt;/strong&gt;—a tool that scans repository history for mistakes, calculates project "regret metrics," and generates GitHub Copilot remediation plans automatically.&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%2F2z6kxx3u9vufz2gbq12p.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%2F2z6kxx3u9vufz2gbq12p.png" alt="demo" width="800" height="797"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛑 The Before: One Function, Zero Architecture, Infinite Shame
&lt;/h2&gt;

&lt;p&gt;Before discussing the rebuild, we need to look at the starting line.&lt;/p&gt;

&lt;p&gt;The original script was a masterclass in anti-patterns.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One file&lt;/li&gt;
&lt;li&gt;Zero tests&lt;/li&gt;
&lt;li&gt;Fragile parsing&lt;/li&gt;
&lt;li&gt;Hardcoded limits&lt;/li&gt;
&lt;li&gt;Empty TODOs everywhere
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="c1"&gt;# wip - trying to add secret detection
# TODO: this crashes on big repos lol
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_audit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--oneline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-20&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not a git repo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="n"&gt;commits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checking &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; commits...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bad commit: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sha&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; — &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;run_audit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔍 Anatomy of a Broken Script
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Magic Number Limitation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--oneline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-20&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool literally refused to inspect more than 20 commits.&lt;/p&gt;




&lt;h3&gt;
  
  
  Fragile String Parsing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parser assumed every log line would always have the same structure.&lt;/p&gt;

&lt;p&gt;Not exactly resilient engineering.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Phantom Feature
&lt;/h3&gt;

&lt;p&gt;The tool claimed to perform secret detection.&lt;/p&gt;

&lt;p&gt;The implementation?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# TODO secret detection here
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  The README
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# git-audit-tool&lt;/span&gt;

TODO: write this.

&lt;span class="gu"&gt;## Status&lt;/span&gt;
Gave up. Crashes on any repo &amp;gt; 10 commits. Will fix later.
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] fix the crash
&lt;span class="p"&gt;-&lt;/span&gt; [ ] add secret detection
&lt;span class="p"&gt;-&lt;/span&gt; [ ] make it actually useful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time I looked at the repository, the technical debt felt bigger than the project itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Designing git-regret
&lt;/h2&gt;

&lt;p&gt;To build a utility people would actually use, I had to stop thinking like a script writer and start thinking like a systems engineer.&lt;/p&gt;

&lt;p&gt;The vision centered around three pillars:&lt;/p&gt;

&lt;h2&gt;
  
  
  1️⃣ Multi-Dimensional Analysis
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Repository Scan Engine]
       │
       ├── HIGH SEVERITY 🚨
       │      ├── Secret Leaks
       │      └── Fix Chains
       │
       └── MEDIUM SEVERITY ⚠️
              ├── WIP Commits
              ├── Regret Keywords
              └── Giant Commits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2️⃣ Decoupled Architecture
&lt;/h2&gt;

&lt;p&gt;Analysis and presentation should never depend on one another.&lt;/p&gt;




&lt;h2&gt;
  
  
  3️⃣ AI-Powered Remediation
&lt;/h2&gt;

&lt;p&gt;Finding mistakes is useful.&lt;/p&gt;

&lt;p&gt;Generating the exact fix plan is even better.&lt;/p&gt;

&lt;p&gt;This became the foundation of the &lt;code&gt;--copilot&lt;/code&gt; feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤖 Pairing with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;A surprising amount of CLI development is boilerplate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regex creation&lt;/li&gt;
&lt;li&gt;Test fixtures&lt;/li&gt;
&lt;li&gt;Argument parsing&lt;/li&gt;
&lt;li&gt;State tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub Copilot helped accelerate all of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Challenge #1: Secret Detection
&lt;/h2&gt;

&lt;p&gt;I needed a reliable set of patterns for identifying leaked credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;I am building a high-performance Python static analysis tool for git history. Generate compiled regex patterns for AWS keys, Stripe live keys, GitHub tokens, private SSH keys, database URLs, and common API secret assignments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="n"&gt;SECRET_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws_access_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AKIA[A-Z0-9]{16}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stripe_live_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk_live_[0-9a-zA-Z]{24}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github_pat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ghp_[0-9a-zA-Z]{36}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pem_private_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-----BEGIN[A-Z ]+PRIVATE KEY-----&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;database_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(mongodb|postgresql|postgres)://[^:]+:[^@]+@[^/]+&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This became the foundation of the scanning engine.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⛓️ Challenge #2: Detecting Fix Chains
&lt;/h2&gt;

&lt;p&gt;One common signal of rushed development is a series of consecutive patch commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix: layout bug
fix: layout bug try 2
fix: forgot import
bugfix: typo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These should usually be squashed into a single commit.&lt;/p&gt;

&lt;p&gt;I asked Copilot to generate a state machine capable of identifying runs of three or more consecutive fix commits.&lt;/p&gt;

&lt;p&gt;The resulting implementation correctly handled edge cases and trailing chains without introducing off-by-one errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Challenge #3: Automated Testing
&lt;/h2&gt;

&lt;p&gt;Testing Git tooling is difficult because you cannot safely mutate a real repository during unit tests.&lt;/p&gt;

&lt;p&gt;I used Copilot to generate a Pytest fixture that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates a temporary repository.&lt;/li&gt;
&lt;li&gt;Configures dummy Git identities.&lt;/li&gt;
&lt;li&gt;Generates synthetic commit histories.&lt;/li&gt;
&lt;li&gt;Runs assertions against isolated repositories.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result was a reproducible testing environment covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secret detection&lt;/li&gt;
&lt;li&gt;Empty histories&lt;/li&gt;
&lt;li&gt;Pagination&lt;/li&gt;
&lt;li&gt;Fix chains&lt;/li&gt;
&lt;li&gt;WIP detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By release day, the project had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;35 assertions&lt;/li&gt;
&lt;li&gt;10 test scenarios&lt;/li&gt;
&lt;li&gt;Full isolated execution&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.amazonaws.com%2Fuploads%2Farticles%2F8fsqmmdbtb7sgdagdo9g.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%2F8fsqmmdbtb7sgdagdo9g.png" alt="A full remediation plan" width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Architecture
&lt;/h2&gt;

&lt;p&gt;The rewrite introduced strict separation between components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────┐
│ cli.py                     │
│ Flag parsing               │
└─────────────┬──────────────┘
              │
              ▼
┌────────────────────────────┐
│ analyzer.py                │
│ Repository analysis        │
└─────────────┬──────────────┘
              │
              ▼
┌────────────────────────────┐
│ ui.py                      │
│ Rich rendering + prompts   │
└────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This architecture makes the analysis engine reusable in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;CI systems&lt;/li&gt;
&lt;li&gt;Future integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without modification.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 The Result: git-regret
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;git-regret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git-regret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╭──────────────────────────────╮
│ git-regret 💀                │
│ Unbreak your past commits.   │
╰──────────────────────────────╯

Scanned 142 commits

Found:
🚨 1 HIGH
⚠️ 2 MEDIUM

[HIGH] Secret Leak Detected

Commit: a3f910d

Message:
hotfix: override api connection auth

Detail:
Leaked Stripe key detected

Suggested Fix:
Rotate credential and purge history.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠️ The Killer Feature: &lt;code&gt;--copilot&lt;/code&gt;
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;The tool generates a GitHub Copilot prompt tailored to the repository findings.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an elite principal engineer auditing my repository.

Detected:
- secret_leak
- trailing_fix_chain

Affected commits:
- a3f910d
- 84b2c11

Please:

1. Provide an interactive rebase plan.
2. Show git filter-repo commands.
3. Draft replacement commit messages.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of searching Stack Overflow for hours, developers get an immediate remediation workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Before vs After
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Old Script&lt;/th&gt;
&lt;th&gt;git-regret&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Codebase&lt;/td&gt;
&lt;td&gt;87 Lines&lt;/td&gt;
&lt;td&gt;520+ Lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;Single File&lt;/td&gt;
&lt;td&gt;Modular&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;35 Assertions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analysis Rules&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;td&gt;print()&lt;/td&gt;
&lt;td&gt;Rich&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Handling&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Robust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Integration&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Copilot Prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔮 Roadmap
&lt;/h2&gt;

&lt;p&gt;Planned features include:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;git-regret install-hook&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Automatically install pre-commit protections.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;git-regret --ci&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Generate GitHub Actions workflows automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;.gitregret.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Custom organizational rules and policies.&lt;/p&gt;




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

&lt;p&gt;Finishing an abandoned project taught me something important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An old idea is not necessarily a bad idea. Sometimes it is simply waiting for a better implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The original project failed because it lacked structure.&lt;/p&gt;

&lt;p&gt;By introducing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decoupled architecture&lt;/li&gt;
&lt;li&gt;Automated testing&lt;/li&gt;
&lt;li&gt;GitHub Copilot assistance&lt;/li&gt;
&lt;li&gt;Clear design boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I transformed a forgotten hackathon script into a production-ready developer tool.&lt;/p&gt;

&lt;p&gt;If you have an abandoned repository sitting in your projects folder, this challenge is your sign to revisit it.&lt;/p&gt;

&lt;p&gt;You might discover that the hardest part was simply finishing.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Audit Your Repository Today
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;git-regret

&lt;span class="c"&gt;# Generate a report and Copilot remediation plan&lt;/span&gt;
git-regret &lt;span class="nt"&gt;--copilot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔗 Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Documentation &amp;amp; Website:&lt;/strong&gt; &lt;a href="https://ishantgupta30.github.io/git-regret/" rel="noopener noreferrer"&gt;https://ishantgupta30.github.io/git-regret/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/ishantgupta30/git-regret" rel="noopener noreferrer"&gt;https://github.com/ishantgupta30/git-regret&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚡ Get Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;git-regret
git-regret &lt;span class="nt"&gt;--copilot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If git-regret helps you uncover a secret, clean up a fix chain, or finally understand what happened in your repository six months ago, consider giving the project a ⭐.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>I Built a Space App That Gives You Real-Time Planetary Data — Powered by Gemma 4, No Backend</title>
      <dc:creator>Ishant gupta</dc:creator>
      <pubDate>Mon, 11 May 2026 18:36:34 +0000</pubDate>
      <link>https://dev.to/ishantgupta/i-built-a-space-app-that-gives-you-real-time-planetary-data-powered-by-gemma-4-no-backend-3823</link>
      <guid>https://dev.to/ishantgupta/i-built-a-space-app-that-gives-you-real-time-planetary-data-powered-by-gemma-4-no-backend-3823</guid>
      <description>&lt;h2&gt;
  
  
  I Built a Retro Space Mission Control Powered by Gemma 4 — and It Talks Like a NASA Commander 🚀
&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%2Fsoknamjxqkt9ht7ll0v6.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%2Fsoknamjxqkt9ht7ll0v6.png" alt="best" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What happens when you give an open-source AI model the keys to the solar system?&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I've been obsessed with space since I was a kid. But every space app I found was either too boring (just static facts) or too complex (needs a PhD to use).&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Cosmos Explorer&lt;/strong&gt; — a retro, terminal-aesthetic space mission control center that runs entirely in the browser, powered by &lt;strong&gt;Gemma 4&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://cosmos-explorer-ishant.netlify.app/" rel="noopener noreferrer"&gt;Live Demo → cosmos-explorer-ishant.netlify.app&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
🔗 &lt;strong&gt;&lt;a href="https://github.com/ishantgupta30/cosmos-explorer" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No downloads. No installs. Just open it and feel like you're at NASA.&lt;/p&gt;
&lt;h2&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%2Fp3h7gepx9awq8zr620dl.gif" alt="Astronaut floating in space" width="220" height="124"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  The Idea: What if space exploration felt alive?
&lt;/h2&gt;

&lt;p&gt;Static facts are boring. I wanted something that &lt;em&gt;reacted&lt;/em&gt; — that felt intelligent, cinematic, and immersive.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Gemma 4&lt;/strong&gt; came in.&lt;/p&gt;

&lt;p&gt;Instead of hardcoding descriptions, I let Gemma 4 generate real-time mission briefings, reveal hidden planetary secrets, narrate the night sky like a planetarium, and compare worlds with the insight of an actual scientist.&lt;/p&gt;

&lt;p&gt;Gemma 4 isn't just a feature in this app. &lt;strong&gt;It's the soul of it.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What is Cosmos Explorer?
&lt;/h2&gt;

&lt;p&gt;It's a 7-in-one space dashboard built as a single HTML file:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🌍 Solar System&lt;/td&gt;
&lt;td&gt;Animated real-physics orbits, click any planet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🤖 Mission Briefings&lt;/td&gt;
&lt;td&gt;Gemma 4 generates NASA-style planet briefings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💡 Discovery Mode&lt;/td&gt;
&lt;td&gt;Gemma 4 reveals obscure scientific secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🛰️ Satellites&lt;/td&gt;
&lt;td&gt;Live mission data for Hubble, JWST, Voyager 1 &amp;amp; more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📡 ISS Tracker&lt;/td&gt;
&lt;td&gt;Real orbital physics, updates every second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔭 Sky CAM&lt;/td&gt;
&lt;td&gt;Point your camera at the sky — Gemma 4 narrates it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚖️ Planet Compare&lt;/td&gt;
&lt;td&gt;Gemma 4 analyzes any two worlds side by side&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let me walk you through each one.&lt;/p&gt;


&lt;h2&gt;
  
  
  Feature 1: The Solar System — Click a Planet, Get a Mission Briefing
&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%2Fbtvs9oxj0gmwmmawi6f7.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%2Fbtvs9oxj0gmwmmawi6f7.png" alt="Solar system view with Saturn selected and Gemma mission briefing" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The moment you click a planet, Gemma 4 kicks in. No loading spinner for 5 seconds. Just instant, cinematic text that typewriters across the screen like a real mission computer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma's output for Saturn:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TARGET ACQUIRED: SATURN. Ring system spans 282,000 km yet only 10-100 
meters thick. Density lower than water — it would float. Enceladus moon 
sprays water geysers at 1,400 km/h. Titan has liquid methane lakes. 
145 moons confirmed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't hardcoded. Gemma 4 writes this fresh every time. I just gave it a persona:&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="nf"&gt;callGemma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are a NASA mission commander. Cite specific instruments and missions 
   by exact name. Begin with TARGET ACQUIRED:. Be cinematic. 2-3 sentences maximum.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`Generate a mission briefing for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;planetName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That system prompt is doing a LOT of work. The model picks up on tone, format, and scientific precision instantly. That's the power of a 27B instruction-tuned model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature 2: Discovery Mode — Secrets Gemma Reveals
&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%2Flkytzsy68wqmbpmqabjz.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%2Flkytzsy68wqmbpmqabjz.png" alt="Discovery mode showing Gemma revealing a hidden fact about Saturn's rings" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every planet has a &lt;strong&gt;Discovery Mode&lt;/strong&gt; button. Press it, and Gemma 4 digs up something genuinely obscure — not the usual "Saturn has rings" stuff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real output for Saturn:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;DISCOVERY: Saturn's rings are geologically young — only 10 to 100 million years old, formed when dinosaurs walked on Earth. Cassini measured ring mass and dust accumulation rates, shocking scientists who expected billion-year-old rings. The rings may disappear in 100 million years as Saturn's gravity pulls them inward.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I prompted it as a planetary scientist with access to mission data:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are a planetary scientist revealing a genuinely obscure fact. 
 Choose something highly specific. Cite missions, instruments, or datasets. 
 2-3 sentences. Begin with "DISCOVERY: "&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I love here: Gemma 4 doesn't just recite Wikipedia. It &lt;em&gt;connects&lt;/em&gt; facts. The dinosaur comparison? That's Gemma making an analogy on its own.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature 3: Sky CAM — Your Camera Becomes a Planetarium
&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%2Fsf0uxwjd7r7wgx560b9r.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%2Fsf0uxwjd7r7wgx560b9r.png" alt="Sky CAM view showing Orion constellation overlay with Gemma AI analysis panel" width="799" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the feature I'm most proud of.&lt;/p&gt;

&lt;p&gt;Point your device camera at the sky (or just use the direction/tilt sliders), and Sky CAM overlays constellation lines in real time. But the real magic? Hit &lt;strong&gt;"GEMMA AI ANALYSIS"&lt;/strong&gt; and watch this happen:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Turn your gaze southward, where Orion strides across the winter sky — Betelgeuse blazing with the light of a star 700 times larger than our own Sun, a red supergiant in its final cosmic act. In 2019, Betelgeuse dimmed so dramatically that astronomers worldwide predicted imminent supernova — a reminder that even celestial constants can surprise us. The ancient Egyptians saw Orion as Osiris, god of death and resurrection, his belt stars aligned with the three pyramids of Giza."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I gave Gemma 4 a very specific persona here:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are a live planetarium narrator with deep astronomy expertise. 
 Be vivid, poetic, scientifically precise, and cinematic.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt also feeds in the current compass direction, tilt angle, and which constellations are visible. Gemma uses all of that context to write something &lt;em&gt;specific&lt;/em&gt; to what you're actually looking at.&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;128K context window&lt;/strong&gt; of Gemma 4 shines — I can pass rich contextual data and get nuanced, location-aware responses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature 4: Planet Comparison — Gemma as Data Analyst
&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%2F4bok8padlj5z65xr1e1p.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%2F4bok8padlj5z65xr1e1p.png" alt="Planet comparison tool showing Mercury vs Mars with Gemma analysis" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pick any two planets, hit ANALYZE, and Gemma 4 breaks down the comparison like a scientist explaining it to a curious friend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mercury vs Mars:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;SIZE CONTRAST: Mars is 1.4× wider than Mercury. DISTANCE: Mercury orbits at 57.9M KM from the Sun, while Mars orbits at 227.9M KM — a vast difference in solar energy received. TEMPERATURE: Mercury surface reads -180→430°C vs Mars's -125→20°C — Mercury and Mars represent extreme thermal environments.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The structured breakdown format comes entirely from the prompt. I didn't template the output — Gemma 4 just... organizes it that way when you ask it to.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature 5: The Space Q&amp;amp;A Chat
&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%2Fnlgedl8bc2x5tgelgyi6.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%2Fnlgedl8bc2x5tgelgyi6.png" alt="Space Q&amp;amp;A chat showing Gemma answering a question about the ISS" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the bottom of the Solar System view, there's a chat bar. Ask anything about space.&lt;/p&gt;

&lt;p&gt;The system prompt is simple but effective:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are an expert space scientist. Answer questions about planets, moons, 
 satellites, black holes, and galaxies enthusiastically. Keep answers 2-4 
 sentences with specific numbers and surprising facts.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "specific numbers" instruction is key — it stops Gemma from giving vague answers and forces it to ground responses in real data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Gemma 4? The Model Selection Decision
&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%2Fgdvoovussjewdz1i5ib3.gif" 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%2Fgdvoovussjewdz1i5ib3.gif" alt="Thinking hard" width="480" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was a deliberate choice, not a default.&lt;/p&gt;

&lt;p&gt;I needed a model that could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Switch personas instantly&lt;/strong&gt; — NASA commander, planetarium narrator, planetary scientist, data analyst. All in the same app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Produce short, precise outputs&lt;/strong&gt; — The UI doesn't have space for essays. 2-3 sentences max.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Work reliably via API&lt;/strong&gt; — The whole app runs client-side, so I needed a stable, fast endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle creative + factual tasks&lt;/strong&gt; — Mission briefings need drama. Discovery Mode needs accuracy. Both at once is hard.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I went with &lt;strong&gt;&lt;code&gt;gemma-4-27b-it&lt;/code&gt;&lt;/strong&gt; (the 27B dense model via Google AI Studio's Gemini API).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why 27B and not 2B or 4B?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The smaller models are amazing for edge/mobile deployment — but they struggled with the tonal range this app demands. A 4B model would give me a decent mission briefing &lt;em&gt;or&lt;/em&gt; a poetic sky narration, but not both reliably. The 27B model handles persona-switching cleanly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not the 26B MoE?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The MoE is built for high-throughput reasoning. My use case is lots of &lt;em&gt;short&lt;/em&gt; creative + factual bursts, not long-chain reasoning. The dense 27B was the better fit here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The killer feature: instruction following&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every Gemma 4 call in this app has a very specific system prompt. The model follows them precisely — it doesn't ramble, doesn't break character, doesn't exceed the sentence limit I set. That level of instruction-following made the entire UI feel polished.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture (The Short Version)
&lt;/h2&gt;

&lt;p&gt;The entire app is &lt;strong&gt;one HTML file&lt;/strong&gt;. No frameworks, no build step, no server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cosmos Explorer
├── Canvas-based solar system (vanilla JS + requestAnimationFrame)
├── Real orbital mechanics (Kepler's equations)
├── ISS tracker (simulated TLE physics)
├── NASA images via JPL/APOD APIs
├── Space news via curated data
└── Gemma 4 via Gemini API (gemma-4-27b-it)
    ├── Mission briefings (on planet click)
    ├── Discovery Mode (on button press)
    ├── Sky CAM narration (on analyze)
    ├── Planet comparison (on analyze)
    └── Space Q&amp;amp;A (persistent chat)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Gemma integration is surprisingly clean — one &lt;code&gt;callGemma()&lt;/code&gt; function that every AI feature uses:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callGemma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;systemText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://generativelanguage.googleapis.com/v1beta/models/gemma-4-27b-it:generateContent?key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;system_instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;systemText&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userText&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxOutputTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;maxTokens&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Every Gemma feature in the entire app flows through that one function. Clean, simple, powerful.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment That Made Me Go "Whoa"
&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%2Frzs0vzzfds0vgl2kk8bl.gif" 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%2Frzs0vzzfds0vgl2kk8bl.gif" alt="Mind blown reaction" width="350" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During testing, I clicked on Earth and asked Gemma to generate a mission briefing.&lt;/p&gt;

&lt;p&gt;It said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"TARGET ACQUIRED: EARTH. The only confirmed biosphere in the known universe. Magnetic field generated by liquid iron outer core deflects solar wind at 400 km/s. 71% surface liquid water. 8.1 billion crew members aboard. Protect at all costs."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;"8.1 billion crew members aboard. Protect at all costs."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I didn't prompt that. The model extrapolated from the NASA commander persona and added something genuinely moving. That's the difference between a language model and a &lt;em&gt;creative&lt;/em&gt; language model.&lt;/p&gt;

&lt;p&gt;That line stayed in the app.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exoplanet Explorer&lt;/strong&gt; — Gemma 4 generating habitability reports for Kepler catalog planets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mission Planner&lt;/strong&gt; — Ask Gemma to plan a hypothetical mission to any planet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Night Sky Events&lt;/strong&gt; — Gemma narrating upcoming eclipses, conjunctions, meteor showers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline mode&lt;/strong&gt; — Gemma 4 2B/4B running locally via WebGPU (no API key needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The offline idea excites me most. The 4B model is small enough to run in the browser — imagine a space education tool that works with zero internet, perfect for schools and rural areas.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;🚀 Live Demo:&lt;/strong&gt; &lt;a href="https://cosmos-explorer-ishant.netlify.app/" rel="noopener noreferrer"&gt;cosmos-explorer-ishant.netlify.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open it, click Saturn, hit Discovery Mode, then go to Sky CAM and press Gemma AI Analysis. That sequence will show you everything Gemma 4 can do in about 60 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;People talk about AI like it's a search engine replacement. But Gemma 4 did something different here — it became a &lt;em&gt;narrator&lt;/em&gt;. It gave this app a voice, a personality, a sense of drama.&lt;/p&gt;

&lt;p&gt;Space is the most dramatic story humanity has ever told. I just needed an AI good enough to tell it.&lt;/p&gt;

&lt;p&gt;Gemma 4 was that AI. 🌌&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with vanilla JS, Canvas API, and Gemma 4 via Google AI Studio.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;No frameworks were harmed in the making of this app.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Thanks for reading!!&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;#gemma4&lt;/code&gt; &lt;code&gt;#googleai&lt;/code&gt; &lt;code&gt;#javascript&lt;/code&gt; &lt;code&gt;#space&lt;/code&gt; &lt;code&gt;#webdev&lt;/code&gt; &lt;code&gt;#ai&lt;/code&gt; &lt;code&gt;#opensource&lt;/code&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
    </item>
    <item>
      <title>I Tried to Run Google's Most Enterprise Database on My MacBook. Here's What Actually Happened.</title>
      <dc:creator>Ishant gupta</dc:creator>
      <pubDate>Wed, 29 Apr 2026 13:57:01 +0000</pubDate>
      <link>https://dev.to/ishantgupta/i-tried-to-run-googles-most-enterprise-database-on-my-macbook-heres-what-actually-happened-36gf</link>
      <guid>https://dev.to/ishantgupta/i-tried-to-run-googles-most-enterprise-database-on-my-macbook-heres-what-actually-happened-36gf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-cloud-next-2026-04-22"&gt;Google Cloud NEXT Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At Google Cloud NEXT '26, buried inside announcement #68 of 260 total announcements, was a single sentence that stopped me cold:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Run it anywhere — across multiple clouds, on-premises, or on your laptop."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"It" is &lt;strong&gt;Google Spanner&lt;/strong&gt; — the database that powers Gmail, Google Ads, Google Pay, and YouTube. The system Walmart, Goldman Sachs, and Shopify depend on for their most critical workloads.&lt;/p&gt;

&lt;p&gt;They said I could run it on my laptop. So I tried. Here's the honest account of what happened — including the part where Google locked me out before I even started.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spoiler: Spanner Omni itself requires whitelisting. But what I could run locally surprised me more than I expected.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Spanner Omni Actually Is
&lt;/h2&gt;

&lt;p&gt;Spanner Omni is Google's answer to a question nobody thought they'd ask: what if you could take the database that runs Google's entire ad business and make it deployable anywhere — on AWS, on-premises, in an air-gapped data center, or on a developer's MacBook?&lt;/p&gt;

&lt;p&gt;The hard engineering problem wasn't the database itself. It was &lt;strong&gt;TrueTime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Original Spanner's legendary consistency guarantees depend on atomic clocks and GPS hardware physically embedded inside Google's data centers. That hardware is what lets Spanner know, with mathematical certainty, the order in which transactions happened across data centers on opposite sides of the planet.&lt;/p&gt;

&lt;p&gt;You cannot ship GPS satellites in a Docker image.&lt;/p&gt;

&lt;p&gt;So Google built a software-defined TrueTime alternative — a reimplementation that provides error-bounded time synchronization without specialized hardware, relying on the insight that Spanner can tolerate weaker uncertainty bounds because it overlaps time-uncertainty waits with other database work.&lt;/p&gt;

&lt;p&gt;That is not a minor detail. That's a fundamental reimagining of one of distributed systems' most famous components, shipped quietly alongside 259 other announcements.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Part Google Didn't Advertise: It's Not Public Yet
&lt;/h2&gt;

&lt;p&gt;The blog post says "available today in preview." What it doesn't prominently say: the Docker image requires Google to whitelist your account before you can pull it.&lt;/p&gt;

&lt;p&gt;I found this out the hard way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull us-docker.pkg.dev/cloud-spanner-omni-preview/release/spanner-omni:2026.r1-beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even after configuring gcloud auth, the pull failed with the same authorization error: permission denied on the registry. The preview is real, but access is controlled. If you want in, you need to contact Google directly.&lt;/p&gt;

&lt;p&gt;This is worth knowing before you block off an evening.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Can Do Right Now: The Spanner Emulator
&lt;/h2&gt;

&lt;p&gt;While Spanner Omni stays behind Google's velvet rope, there's a fully functional local Spanner environment that IS publicly available right now: the &lt;strong&gt;Cloud Spanner Emulator&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull gcr.io/cloud-spanner-emulator/emulator

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9010:9010 &lt;span class="nt"&gt;-p&lt;/span&gt; 9020:9020 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; spanner-emulator &lt;span class="se"&gt;\&lt;/span&gt;
  gcr.io/cloud-spanner-emulator/emulator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It pulled cleanly and started in under 2 seconds:&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%2Fd2qkrp3elpfkg00onriv.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%2Fd2qkrp3elpfkg00onriv.png" alt="Docker ps showing spanner-emulator running" width="800" height="39"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure gcloud to point to your local emulator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;auth/disable_credentials &lt;span class="nb"&gt;true
&lt;/span&gt;gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project test-project
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;api_endpoint_overrides/spanner http://localhost:9020/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run a real query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud spanner databases execute-sql test-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"SELECT 'Spanner is running on my MacBook' AS message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F1kq9fb3xgxsigkyjm464.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%2F1kq9fb3xgxsigkyjm464.png" alt="Terminal showing Spanner is running on my MacBook" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That line printing in my terminal felt genuinely surreal. This is the same database engine that handles Google's global ad auction, running entirely offline on a MacBook Air.&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmark: How Fast Is It Locally?
&lt;/h2&gt;

&lt;p&gt;I didn't just want to run a hello-world query. I wanted real numbers.&lt;/p&gt;

&lt;p&gt;I inserted 1,000 rows using a shell loop:&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="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 1000&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;gcloud spanner rows insert &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-instance &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-db &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--table&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Users &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"UserId=&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;,Name=User&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;,Email=user&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;@test.com"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F0tafsi9802ggmvwco5db.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%2F0tafsi9802ggmvwco5db.png" alt="Insert loop running with commit timestamps" width="799" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then timed a COUNT query across all 1,000 rows:&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;time &lt;/span&gt;gcloud spanner databases execute-sql test-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"SELECT COUNT(*) FROM Users"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a LIMIT 100 fetch:&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;time &lt;/span&gt;gcloud spanner databases execute-sql test-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"SELECT * FROM Users LIMIT 100"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Frtt0s7wnz50mb0i1ekzp.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%2Frtt0s7wnz50mb0i1ekzp.png" alt="LIMIT 100 query returning 100 rows in 0.37 seconds" width="800" height="1613"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Result: 100 rows fetched in 0.37 seconds on a MacBook Air.&lt;/strong&gt; COUNT across 1,000 rows returned near-instantly. For a database engine designed for planet-scale distributed workloads, that's more than responsive enough for real local development.&lt;/p&gt;


&lt;h2&gt;
  
  
  ACID Transactions Working Locally
&lt;/h2&gt;

&lt;p&gt;This is Spanner's actual superpower — strong consistency across distributed nodes. I wanted to verify it works locally too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud spanner databases execute-sql test-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"UPDATE Users SET Name = 'Ishant Gupta' WHERE UserId = 1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-partitioned-dml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Immediately queried it back:&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%2Fvmz2gxvsi82x8pguyizw.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%2Fvmz2gxvsi82x8pguyizw.png" alt="UPDATE statement modifying 1 row" width="798" height="134"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud spanner databases execute-sql test-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"SELECT * FROM Users WHERE UserId = 1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fiowkvbqnxv6ao8o7razw.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%2Fiowkvbqnxv6ao8o7razw.png" alt="SELECT showing Ishant Gupta updated" width="800" height="130"&gt;&lt;/a&gt;&lt;br&gt;
The change was there instantly. Then I ran aggregations across all 1,000 rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud spanner databases execute-sql test-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;test-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"SELECT COUNT(*) as TotalUsers, 
        MAX(UserId) as MaxId, MIN(UserId) as MinId 
        FROM Users"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fo04xmzmi8alh3vx1v240.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%2Fo04xmzmi8alh3vx1v240.png" alt="TotalUsers 1000 MaxId 1000 MinId 1" width="798" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;COUNT, MAX, MIN — all correct, all returned in under a second. This isn't a mock. It's real Spanner consistency semantics running entirely offline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vector Search: The AI Angle
&lt;/h2&gt;

&lt;p&gt;One of the biggest NEXT '26 announcements was Spanner's support for vector search — making it viable as an AI retrieval database with no ETL pipeline needed.&lt;/p&gt;

&lt;p&gt;I created the schema locally to see if the emulator would accept it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;Documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;DocId&lt;/span&gt; &lt;span class="n"&gt;INT64&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;Embedding&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FLOAT32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DocId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fedkcdcw6bwrmzn54kl56.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%2Fedkcdcw6bwrmzn54kl56.png" alt="Vector search schema with Documents table created" width="799" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It accepted the schema instantly. Note: actual vector similarity search queries require cloud Spanner — but designing and validating your AI data model locally before touching production is exactly the workflow Google is targeting with these announcements.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Story: Solving the Parity Problem
&lt;/h2&gt;

&lt;p&gt;Here's what I think is genuinely underreported about both the emulator and Spanner Omni:&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%2Frj4apmspjtl9o849dayr.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%2Frj4apmspjtl9o849dayr.png" alt="Spanner dev workflow: local emulator to CI/CD to production with same SQL" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before this, building on Spanner meant one of three painful options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mocking the database (fast but inaccurate)&lt;/li&gt;
&lt;li&gt;Substituting SQLite (wrong SQL dialect, wrong behavior)&lt;/li&gt;
&lt;li&gt;Pushing every change to a cloud dev environment and waiting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a local Spanner running, the queries that work in your terminal work in production. The schema you design locally is the schema you deploy. The ACID semantics you rely on locally are the semantics you get in prod.&lt;/p&gt;

&lt;p&gt;For AI applications specifically, this matters even more. Spanner now supports vector search, Spanner Graph for relationship queries, and full-text search — all in one database. You can prototype an entire AI retrieval stack locally, for free, before spending a rupee on cloud credits.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Honest Take
&lt;/h2&gt;

&lt;p&gt;Spanner Omni as announced is not what you can run today unless Google whitelists you. That gap between the blog post and actual access is worth calling out — not as a criticism, but as something every developer should know before they try.&lt;/p&gt;

&lt;p&gt;But the emulator is real, it works, and the numbers speak for themselves: 1,000 rows, 0.37 second fetches, real ACID transactions, and vector-ready schema — all running on a MacBook Air that was also playing Spotify.&lt;/p&gt;

&lt;p&gt;The 2012 Spanner paper felt like reading about infrastructure from another planet. Running &lt;code&gt;SELECT * FROM Users&lt;/code&gt; locally in 2026 means that planet is now your dev environment.&lt;/p&gt;

&lt;p&gt;If you're building anything data-intensive — especially with AI retrieval — run these two Docker commands this weekend and see how it fits your stack. I'm waiting on Spanner Omni access; when it lands, I'll write the follow-up this article deserves.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://cloud.google.com/blog/products/databases/introducing-spanner-omni" rel="noopener noreferrer"&gt;Introducing Spanner Omni&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://docs.cloud.google.com/spanner-omni/overview" rel="noopener noreferrer"&gt;Spanner Omni Docs&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://cloud.google.com/spanner/docs/emulator" rel="noopener noreferrer"&gt;Cloud Spanner Emulator&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://cloud.google.com/blog/topics/google-cloud-next/google-cloud-next-2026-wrap-up" rel="noopener noreferrer"&gt;Google Cloud NEXT '26 Announcements&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>cloudnextchallenge</category>
      <category>googlecloud</category>
    </item>
  </channel>
</rss>
