<?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: SSA </title>
    <description>The latest articles on DEV Community by SSA  (@sa_a_334b500fa1f).</description>
    <link>https://dev.to/sa_a_334b500fa1f</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%2F4101107%2Fc070bc36-f965-43bd-a229-66b0521a3d55.png</url>
      <title>DEV Community: SSA </title>
      <link>https://dev.to/sa_a_334b500fa1f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sa_a_334b500fa1f"/>
    <language>en</language>
    <item>
      <title>Building DevLog: What Happens When You Tell an AI to Think Like a Security Engineer First</title>
      <dc:creator>SSA </dc:creator>
      <pubDate>Sun, 06 Sep 2026 11:07:32 +0000</pubDate>
      <link>https://dev.to/sa_a_334b500fa1f/building-devlog-what-happens-when-you-tell-an-ai-to-think-like-a-security-engineer-first-5b1b</link>
      <guid>https://dev.to/sa_a_334b500fa1f/building-devlog-what-happens-when-you-tell-an-ai-to-think-like-a-security-engineer-first-5b1b</guid>
      <description>&lt;p&gt;Submission for the Cloud Run AI Challenge — #AccelerateAIwithCloudRun&lt;/p&gt;

&lt;p&gt;When I started this challenge, the brief was to build a "Personal Gemini Journal" — an authenticated app where you chat with Gemini and it saves your reflections. Simple enough on paper. The actual point of the challenge, though, wasn't the journal. It was what happens before you write a single line of code: configuring Google AI Studio to behave like a security engineer, not just a code generator.&lt;/p&gt;

&lt;p&gt;Here's what I built, why I diverged from the base spec, and what I learned along the way — including the parts that had nothing to do with code.&lt;/p&gt;

&lt;p&gt;Phase 1: Teaching AI Studio to threat-model before it builds&lt;/p&gt;

&lt;p&gt;The starter directive isn't "write secure code." It's a structured constitution that forces a specific sequence: before any feature gets implemented, AI Studio has to produce a Threat Summary Table mapping risks to countermeasures across five zones — input surfaces, planning/reasoning, tool execution, memory/state, and inter-system communication.&lt;/p&gt;

&lt;p&gt;Concretely, this meant every time I asked for a new feature, AI Studio would first lay out a threat/zone/countermeasure breakdown before writing any code. Here's the actual breakdown it produced for DevLog's architecture:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Input Surfaces&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Risks: prompt injection/jailbreak attempts in bug descriptions, malformed JSON payloads, client tampering with log identifiers or user IDs&lt;br&gt;
Controls: treat all user input as untrusted plain data, never executable instructions; defensive payload ingestion with schema validation; zero-trust path checks so records only write to /users/{userId}/... matching request.auth.uid&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Planning &amp;amp; Reasoning&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Risks: hallucinated or non-conforming extraction fields breaking database ingestion; model drift between conversational chat and structured extraction&lt;br&gt;
Controls: strict two-call separation between chat and extraction endpoints; structured output enforcement via Gemini JSON mode (responseSchema); automatic model fallback ladder with schema validation before writing&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tool Execution&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Risks: SSRF or arbitrary remote execution if tool calling is improperly scoped; unchecked client-side API key disclosure&lt;br&gt;
Controls: server-side API proxying only, zero exposure of GEMINI_API_KEY to the client bundle; no arbitrary command/code evaluation tools exposed to client endpoints&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memory &amp;amp; State&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Risks: cross-user data leakage; insecure default Firestore rules (allow read, write: if true;); orphaned writes or undefined-property crashes&lt;br&gt;
Controls: owner-bound Firestore rules (request.auth.uid == userId); undefined-stripping before every write; Pattern Radar strictly bounded to the authenticated user's own logs, zero cross-user aggregate queries&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inter-System Communication&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Risks: Gemini API quota exhaustion or transient failures (429, 503); token/credential leakage in transit&lt;br&gt;
Controls: resilient fallback ladder catching recoverable status codes before surfacing UI alerts; HTTPS/WSS only; Secret Manager for all credentials&lt;/p&gt;

&lt;p&gt;That breakdown isn't decoration — it's a contract. If AI Studio proposes code that violates it, that's a signal to push back, not ship it.&lt;/p&gt;

&lt;p&gt;Why I didn't build a journal&lt;/p&gt;

&lt;p&gt;The base spec is a mood/reflection journal. I build things and break things for a living (well — for a degree, currently), so I asked: what if the thing being journaled wasn't a feeling, but a bug?&lt;/p&gt;

&lt;p&gt;That became DevLog — a debugging journal for developers. You talk through a bug with Gemini in free-form chat — actual rubber-ducking, not a guided form — but instead of saving a paragraph of text as the record, the app runs a second, separate extraction call using Gemini's structured JSON output mode to pull out:&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="err"&gt;json&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PostgreSQL connection pool exhaustion during concurrent batch ingestion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rootCause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Worker threads opened unpooled client instances per webhook payload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Migrated to a lazy-initialized singleton connection manager, max 20 connections"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&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="s2"&gt;"postgresql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"connection-pooling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"difficulty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;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%2Fo35fzuejx5racnktoyou.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%2Fo35fzuejx5racnktoyou.png" alt=" " width="800" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The two-call split matters more than it sounds. One call stays conversational — it's allowed to be messy, exploratory, human. The other call has exactly one job: emit valid, schema-constrained JSON. Mixing those into one call is how you end up regex-parsing "sure, here's your JSON: json..." out of a chat reply, which is fragile and exactly the kind of thing the Secure Coding directive is meant to prevent.&lt;/p&gt;

&lt;p&gt;Pattern Radar&lt;/p&gt;

&lt;p&gt;Once you have structured tags instead of raw text, a second feature falls out almost for free: aggregate your own tags over time and you get a Pattern Radar — a live view of your most frequent failure modes. After a few weeks of logging, it can tell you "you've hit race-condition bugs 4 times this month" without a single extra Gemini call, because it's just counting data you already extracted correctly.&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%2Fde04z9bybqvz3e4m95ip.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%2Fde04z9bybqvz3e4m95ip.png" alt=" " width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the part I'd point to as the actual "authenticity" differentiator — not because it's flashy, but because it's a real product idea that only exists because of the structured-extraction discipline the security directives pushed me toward in the first place.&lt;/p&gt;

&lt;p&gt;The UI pass&lt;/p&gt;

&lt;p&gt;Once the app worked, the default AI-generated look was... exactly what you'd expect: dark background, one bright accent everywhere, ALL-CAPS badges, identical bordered cards. I went back to AI Studio with a specific token system instead of "make it look nicer" — named hex values for a greyish-black glass-panel palette, one restrained accent color, sentence-case labels instead of caps. Being specific about the actual design tokens, instead of vibes, is what got a result that didn't look like every other AI Studio demo.&lt;/p&gt;

&lt;p&gt;Before: &lt;br&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%2Fj01hnvz94thj0i3d15dk.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%2Fj01hnvz94thj0i3d15dk.png" alt=" " width="800" height="360"&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%2Fesqn5n57wi1svb0qdv8p.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%2Fesqn5n57wi1svb0qdv8p.png" alt=" " width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After:&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%2Fo4gc1svbz871je8hlnmq.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%2Fo4gc1svbz871je8hlnmq.png" alt=" " width="799" height="357"&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%2Fayjd2zfxp0860z75fyjh.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%2Fayjd2zfxp0860z75fyjh.png" alt=" " width="800" height="480"&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%2Fd1es7qszeumyk0f0sktr.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%2Fd1es7qszeumyk0f0sktr.png" alt=" " width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The challenge accepts a written walkthrough in place of a live URL — and honestly, deciding when a cost/complexity tradeoff isn't worth it for a given deadline is its own kind of engineering judgment. I closed the billing account cleanly afterward and kept building entirely inside AI Studio's own preview environment.&lt;/p&gt;

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

&lt;p&gt;If I keep working on this past the challenge deadline, the next thing I'd add is a weekly digest — a scheduled job that summarizes a week's worth of logs into "what this pattern suggests you should study next," reusing the same schema-validated extraction pattern rather than opening a new unguarded free-text path.&lt;/p&gt;

&lt;p&gt;For now: DevLog does what it set out to do. It's not a mood journal wearing a security badge — it's a genuinely different object (a structured debugging record) built on the same secure skeleton the challenge asked for: Firebase Auth, per-user Firestore isolation, Secret Manager for the Gemini key, and a constitution that made the AI show its threat model before it showed me any code.&lt;/p&gt;

&lt;p&gt;Tags: ai, googlecloud, gemini, webdev, accelerateaiwithcloudrun&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>About the Builder</title>
      <dc:creator>SSA </dc:creator>
      <pubDate>Sun, 30 Aug 2026 07:34:47 +0000</pubDate>
      <link>https://dev.to/sa_a_334b500fa1f/about-the-builder-27f9</link>
      <guid>https://dev.to/sa_a_334b500fa1f/about-the-builder-27f9</guid>
      <description>&lt;p&gt;Hi, I'm a developer and tech enthusiast passionate about building at the intersection of Generative AI, cloud infrastructure, and modern software engineering. My focus is on turning experimental AI concepts into practical, production-ready applications that solve real-world problems.&lt;/p&gt;

&lt;p&gt;Through hands-on challenges like Meet the Builders, I actively explore tools across the Google Cloud ecosystem—working with Gemini models, serverless deployments via Cloud Run, containerization with Docker, and interactive frontends using Python and Streamlit. I believe the best way to master emerging tech is by building, breaking, and iterating directly in the cloud.&lt;/p&gt;

&lt;p&gt;When I’m not experimenting with new APIs or deploying prototypes, I’m constantly learning, keeping up with advancements in LLM agent workflows, and sharing my development journey with the wider developer community.&lt;/p&gt;

&lt;p&gt;Feel free to swap in your name or specific tech stack if you'd like it even more personalized!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
