<?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: Vishal Somaraju</title>
    <description>The latest articles on DEV Community by Vishal Somaraju (@vishalsomaraju).</description>
    <link>https://dev.to/vishalsomaraju</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%2F3960032%2F39a73921-209b-4a78-ae46-95fbfc258363.png</url>
      <title>DEV Community: Vishal Somaraju</title>
      <link>https://dev.to/vishalsomaraju</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vishalsomaraju"/>
    <language>en</language>
    <item>
      <title>We built a security gate for Git pushes. Then we gave it a memory.</title>
      <dc:creator>Vishal Somaraju</dc:creator>
      <pubDate>Tue, 29 Sep 2026 19:14:02 +0000</pubDate>
      <link>https://dev.to/vishalsomaraju/we-built-a-security-gate-for-git-pushes-then-we-gave-it-a-memory-4jpm</link>
      <guid>https://dev.to/vishalsomaraju/we-built-a-security-gate-for-git-pushes-then-we-gave-it-a-memory-4jpm</guid>
      <description>&lt;p&gt;One of the most annoying things about security issues in a project is that you can fix the same type of problem more than once.&lt;/p&gt;

&lt;p&gt;You find a vulnerability.&lt;/p&gt;

&lt;p&gt;You fix it.&lt;/p&gt;

&lt;p&gt;A few days later, something similar happens again.&lt;/p&gt;

&lt;p&gt;And your security tool starts from zero.&lt;/p&gt;

&lt;p&gt;That was the idea behind &lt;strong&gt;SecurePush&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We wanted to build something that could sit in the normal Git workflow and check code before it gets pushed, but we also wanted it to remember what happened in previous reviews.&lt;/p&gt;

&lt;p&gt;That is where Hindsight came in.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is SecurePush?
&lt;/h2&gt;

&lt;p&gt;SecurePush is basically a security gate for &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of pushing code directly to the remote repository, SecurePush checks the changes first.&lt;/p&gt;

&lt;p&gt;The flow is pretty simple.&lt;/p&gt;

&lt;p&gt;You make your changes.&lt;/p&gt;

&lt;p&gt;You 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 push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SecurePush picks up the changed files and reviews them for security issues.&lt;/p&gt;

&lt;p&gt;If it finds something, it explains what is wrong and suggests a fix.&lt;/p&gt;

&lt;p&gt;You can accept the fix or reject it.&lt;/p&gt;

&lt;p&gt;If you accept it, SecurePush applies the change and runs the checks again.&lt;/p&gt;

&lt;p&gt;If everything passes, the push continues.&lt;/p&gt;

&lt;p&gt;If there is still a problem, the push is blocked.&lt;/p&gt;

&lt;p&gt;Simple enough.&lt;/p&gt;

&lt;p&gt;But the interesting part comes after that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if it remembered?
&lt;/h2&gt;

&lt;p&gt;Let's say SecurePush finds a hardcoded credential in your project.&lt;/p&gt;

&lt;p&gt;It tells you about it and suggests moving it to an environment variable.&lt;/p&gt;

&lt;p&gt;You accept the fix.&lt;/p&gt;

&lt;p&gt;The fix is applied.&lt;/p&gt;

&lt;p&gt;The tests pass.&lt;/p&gt;

&lt;p&gt;The push goes through.&lt;/p&gt;

&lt;p&gt;Normally, that's the end of it.&lt;/p&gt;

&lt;p&gt;But we don't want SecurePush to forget that entire interaction.&lt;/p&gt;

&lt;p&gt;We use Hindsight to remember it.&lt;/p&gt;

&lt;p&gt;Not the actual secret.&lt;/p&gt;

&lt;p&gt;The useful information around it.&lt;/p&gt;

&lt;p&gt;Something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A hardcoded credential was found in &lt;code&gt;scripts.js&lt;/code&gt;. The developer accepted the environment variable fix and the tests passed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that information is part of the repository's security memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then comes the second push
&lt;/h2&gt;

&lt;p&gt;Later, you make another change.&lt;/p&gt;

&lt;p&gt;SecurePush doesn't just look at the new code.&lt;/p&gt;

&lt;p&gt;Before reviewing it, it checks the repository's previous security memory.&lt;/p&gt;

&lt;p&gt;It might find that the repository has already had a similar credential issue before.&lt;/p&gt;

&lt;p&gt;That context is then given to the security review.&lt;/p&gt;

&lt;p&gt;So instead of every review being completely isolated, the agent has some idea of what happened before.&lt;/p&gt;

&lt;p&gt;And this is the part we wanted to demonstrate with Hindsight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First review:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It finds the problem.&lt;/p&gt;

&lt;p&gt;The developer makes a decision.&lt;/p&gt;

&lt;p&gt;The result gets remembered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Later review:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A similar situation comes up.&lt;/p&gt;

&lt;p&gt;The previous decision can be recalled.&lt;/p&gt;

&lt;p&gt;The current code is still checked normally, but now there is some history behind the review.&lt;/p&gt;

&lt;h2&gt;
  
  
  We also wanted the memory to be visible
&lt;/h2&gt;

&lt;p&gt;We didn't want Hindsight to just be something hidden in the backend that we mention during the presentation.&lt;/p&gt;

&lt;p&gt;So we added a Memory section to SecurePush.&lt;/p&gt;

&lt;p&gt;It shows what the repository has remembered from previous security reviews.&lt;/p&gt;

&lt;p&gt;You can see things like the type of issue, the file involved, what the developer decided, whether the fix worked, and other relevant information.&lt;/p&gt;

&lt;p&gt;The idea is simple.&lt;/p&gt;

&lt;p&gt;You should be able to see what the system remembers instead of just being told that it has memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  There was one important problem
&lt;/h2&gt;

&lt;p&gt;If SecurePush is supposed to find secrets, we obviously can't just store those secrets in its memory.&lt;/p&gt;

&lt;p&gt;So before something is retained, sensitive values are removed.&lt;/p&gt;

&lt;p&gt;We want to remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A hardcoded credential was found and the developer moved it to an environment variable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We don't want to remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;API_KEY = "actual-secret"&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was an important part of building the memory layer properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete flow
&lt;/h2&gt;

&lt;p&gt;So the final workflow looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code change&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;git push&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SecurePush scans the changes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Previous security memory is recalled&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security issue is found&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer accepts or rejects the fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix is applied and verified&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Push is allowed or blocked&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result is remembered&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And the next review can use that memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we actually found interesting
&lt;/h2&gt;

&lt;p&gt;For me, the interesting part wasn't just getting an LLM to find a security vulnerability.&lt;/p&gt;

&lt;p&gt;There are already plenty of tools that can do that.&lt;/p&gt;

&lt;p&gt;The interesting part was seeing what happens when the system doesn't forget every interaction.&lt;/p&gt;

&lt;p&gt;A normal security scan can tell you what is wrong with the code right now.&lt;/p&gt;

&lt;p&gt;With memory, the system can also have some context about what happened in the repository before.&lt;/p&gt;

&lt;p&gt;That opens up a lot of possibilities beyond SecurePush too.&lt;/p&gt;

&lt;p&gt;Repositories have a lot of history.&lt;/p&gt;

&lt;p&gt;Why a certain decision was made.&lt;/p&gt;

&lt;p&gt;Which fixes were tried.&lt;/p&gt;

&lt;p&gt;Which security issues kept coming back.&lt;/p&gt;

&lt;p&gt;What the team accepted or rejected.&lt;/p&gt;

&lt;p&gt;A lot of that knowledge normally stays scattered across commits, issues, chats, and people's heads.&lt;/p&gt;

&lt;p&gt;We wanted to see what happens when some of that knowledge can actually be remembered and used during the next interaction.&lt;/p&gt;

&lt;p&gt;That's what we built with SecurePush.&lt;/p&gt;

&lt;p&gt;**A security gate that doesn't just check your code.&lt;/p&gt;

&lt;p&gt;It remembers what happened before.**&lt;/p&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>git</category>
      <category>security</category>
    </item>
    <item>
      <title>How CascadeFlow Cut Our Review Cost Without Hurting Quality</title>
      <dc:creator>Vishal Somaraju</dc:creator>
      <pubDate>Sun, 28 Jun 2026 17:38:59 +0000</pubDate>
      <link>https://dev.to/vishalsomaraju/how-cascadeflow-cut-our-review-cost-without-hurting-quality-3h3e</link>
      <guid>https://dev.to/vishalsomaraju/how-cascadeflow-cut-our-review-cost-without-hurting-quality-3h3e</guid>
      <description>&lt;p&gt;*&lt;em&gt;Most AI dev tools have the same bad habit: *&lt;/em&gt;&lt;br&gt;
send everything to the biggest model available and hope the bill feels reasonable later.&lt;br&gt;
That works for demos. It doesn't work when you're actually building something.&lt;br&gt;
A five-line hello world should not cost the same as reviewing auth middleware touching JWTs, SQL, async flows, and environment variables. But a lot of tools treat every snippet like a production incident. Same model, same cost, every time — regardless of what the code actually is.&lt;br&gt;
That was the problem we kept running into while building Refyn. We were working under real time pressure, and the fastest way to burn both budget and reliability was dumb routing. We'd already watched Gemini fail completely — four supposedly valid API keys from three different Google accounts, all returning "API key not valid." To this day we don't know why. Then a Groq model got decommissioned mid-testing, mid-session, with a 400 error and no warning. After that, "just send everything to one model" stopped sounding simple. It started sounding fragile.&lt;br&gt;
CascadeFlow became our answer: score the code first, then decide how much model you actually need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Refyn Is :&lt;/strong&gt;&lt;br&gt;
Refyn is a browser-based VS Code-style code review workspace — React, Vite, Monaco Editor, Tailwind, Framer Motion on the frontend, Node.js and Express on the back.&lt;br&gt;
You paste code, hit Analyze, get structured issues back with severity tags, a quality score, and a Smart Fix button. Standard stuff. The part that isn't standard is what happens before the model call.&lt;br&gt;
Refyn has two layers most review tools skip entirely.&lt;br&gt;
The first is Hindsight memory. It remembers your recurring patterns across sessions and injects that history into future prompts. By your third review, it's leading with your known weak spots instead of treating you like a stranger.&lt;br&gt;
The second is CascadeFlow-style routing. Before any model runs, Refyn scores the code on a 0–100 complexity scale. Simple code takes the cheap fast path. Security-sensitive or structurally dense code escalates automatically.&lt;br&gt;
Both of these came out of necessity, not ambition. Gemini was removed entirely after it became unusable. Groq became primary. OpenRouter became the backup layer with four free model fallbacks. Piston replaced Judge0 for execution because Docker overhead was too heavy locally and too expensive in production. The architecture we shipped was shaped by what kept breaking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Routing Problem :&lt;/strong&gt;&lt;br&gt;
The math is simple: most code is not equally hard to review, but most LLM integrations price it as if it is.&lt;br&gt;
If you always call the expensive path, costs drift upward even when quality doesn't improve. A tiny utility function doesn't need the same review budget as an authentication service. But without routing logic, it gets the same one anyway.&lt;br&gt;
Refyn's cost model is explicit in cascadeService.js. Groq runs at roughly $0.00002 per 1K tokens. The premium baseline for savings comparison is $0.000075 per 1K tokens. On a 100-token review:&lt;/p&gt;

&lt;p&gt;Premium baseline: 100 / 1000 * 0.000075 = $0.0000075&lt;br&gt;
Groq route: 100 / 1000 * 0.00002 = $0.000002&lt;br&gt;
Savings: ~73%&lt;/p&gt;

&lt;p&gt;That's why the stats bar matters. Cost control becomes visible instead of theoretical. You're not trusting a dashboard you check monthly — you see it on every single review.&lt;br&gt;
The reliability half of the problem is messier. During our build, one Groq model name worked fine, then returned a 400 saying it had been decommissioned. OpenRouter's first free model list started returning 404s because the free tier had quietly rotated. If your routing assumes providers stay stable, your app breaks the moment they act like providers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How cascadeService.js Works :&lt;/strong&gt;&lt;br&gt;
The scorer doesn't try to be academically perfect. It tries to be fast, explainable, and useful enough to make better runtime decisions.&lt;br&gt;
jsexport const scoreComplexity = (code, language) =&amp;gt; {&lt;br&gt;
  let score = 0;&lt;br&gt;
  const lines = code.split("\n").length;&lt;/p&gt;

&lt;p&gt;if (lines &amp;gt; 200) score += 30;&lt;br&gt;
  else if (lines &amp;gt; 100) score += 20;&lt;br&gt;
  else if (lines &amp;gt; 50) score += 10;&lt;br&gt;
  else score += 5;&lt;/p&gt;

&lt;p&gt;const securityPatterns = [&lt;br&gt;
    /eval\s*(/,&lt;br&gt;
    /exec\s*(/,&lt;br&gt;
    /subprocess/,&lt;br&gt;
    /os.system/,&lt;br&gt;
    /child_process/,&lt;br&gt;
    /dangerouslySetInnerHTML/,&lt;br&gt;
    /innerHTML\s*=/,&lt;br&gt;
    /SQL|mysql|postgres|sqlite/i,&lt;br&gt;
    /password|secret|token|apikey|api_key/i,&lt;br&gt;
    /.env/,&lt;br&gt;
    /crypto./,&lt;br&gt;
    /jwt/i,&lt;br&gt;
    /auth/i,&lt;br&gt;
  ];&lt;br&gt;
  const securityHits = securityPatterns.filter((p) =&amp;gt; p.test(code)).length;&lt;br&gt;
  score += securityHits * 8;&lt;br&gt;
It starts with length — a 220-line file is a different review problem than a 12-line snippet. Then it adds heavier weight for risky patterns: eval, SQL, auth, JWT, crypto, secrets, environment handling. Short code with security signals still escalates. That's intentional.&lt;br&gt;
After that it adds signals for async density, Promise usage, classes, try/catch depth, recursion hints, and import count. Final score capped at 100.&lt;br&gt;
The routing tiers are intentionally blunt:&lt;br&gt;
jsif (complexityScore &amp;gt;= 60) {&lt;br&gt;
  model = availableModels.includes("mixtral") ? "mixtral" : "groq";&lt;br&gt;
} else if (complexityScore &amp;gt;= 30) {&lt;br&gt;
  model = availableModels.includes("groq") ? "groq" : "openrouter";&lt;br&gt;
} else {&lt;br&gt;
  model = availableModels.includes("groq") ? "groq"&lt;br&gt;
    : availableModels.includes("mixtral") ? "mixtral"&lt;br&gt;
    : "openrouter";&lt;br&gt;
}&lt;br&gt;
Low complexity goes to Groq — fast, cheap, good enough. Medium complexity also prefers Groq for the speed/cost balance. High complexity or security-sensitive code escalates to Mixtral.&lt;br&gt;
Then aiRouter.js handles the actual runtime fallback:&lt;br&gt;
jsconst FALLBACK_ORDER = ["groq", "mixtral", "openrouter", "ollama"];&lt;/p&gt;

&lt;p&gt;for (const model of FALLBACK_ORDER) {&lt;br&gt;
  if (model === routing.model) continue;&lt;br&gt;
  try {&lt;br&gt;
    const result = await MODEL_FN&lt;a href="https://dev.tocode,%20language,%20memoryContext"&gt;model&lt;/a&gt;;&lt;br&gt;
    if (result.success) {&lt;br&gt;
      return enrichResult(result, model, startTime, code, memoryData, {&lt;br&gt;
        ...routing,&lt;br&gt;
        model,&lt;br&gt;
        reason: &lt;code&gt;${routing.model} unavailable — fell back to ${model}&lt;/code&gt;,&lt;br&gt;
      });&lt;br&gt;
    }&lt;br&gt;
  } catch (e) {&lt;br&gt;
    console.error(&lt;code&gt;[Cascade] ${model} threw:&lt;/code&gt;, e.message);&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
That fallback chain exists because the real world is messy. We needed it after Gemini became unusable, after Groq changed model availability mid-session, and after one genuinely embarrassing moment where the backend returned "All models failed to analyze" because our .env file was sitting in the wrong folder. We spent a solid chunk of debugging time blaming providers before realizing config was the actual bug. Classic.&lt;br&gt;
When OpenRouter takes over, it cycles through a list of free models:&lt;br&gt;
jsconst FREE_MODELS = [&lt;br&gt;
  "openrouter/free",&lt;br&gt;
  "meta-llama/llama-3.3-70b-instruct:free",&lt;br&gt;
  "qwen/qwen3-coder:free",&lt;br&gt;
  "google/gemma-4-31b-it:free"&lt;br&gt;
];&lt;br&gt;
That list exists because our first version had models that had already rotated out of the free tier. Hardcoded model names age fast. Lesson learned the annoying way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Demo Moment :&lt;/strong&gt;&lt;br&gt;
The best part of Refyn is that the routing is visible.&lt;br&gt;
Paste a small utility function and the stats bar shows:&lt;br&gt;
Model: Groq | Complexity: 12/100 | Cost: $0.000002 | Saved: 72% vs Mixtral | Latency: 340ms&lt;br&gt;
That one line explains the product better than anything else we built.&lt;br&gt;
Then paste something security-sensitive — auth middleware, token parsing, a SQL query builder, anything touching .env or jwt. The complexity score jumps. The selected model changes without you touching a dropdown. The UI makes the decision legible: this review was escalated because the code looked riskier.&lt;br&gt;
That transparency was the thing we kept coming back to. We'd already lived through the opposite — a system that looked smart in screenshots but hid all the runtime decisions. Once model, cost, latency, and savings were visible on every review, the routing story stopped being abstract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Hindsight Adds :&lt;/strong&gt;&lt;br&gt;
CascadeFlow decides which model to use. Hindsight decides what that model should remember.&lt;br&gt;
In aiRouter.js, Refyn loads memory before analysis, builds a context string from past patterns, passes that into the selected model, and saves new patterns after a successful review. Memory is part of the runtime path, not a side feature.&lt;br&gt;
We earned that the hard way. Our first memoryService.js was calling a /memories endpoint that didn't exist — every request silently died in 404s. The fix was switching to the official @vectorize-io/hindsight-client SDK and using recall() and retain() properly. Then we hit the second bug: the SDK returns { results: [...] }, not a plain array, so our parsing was quietly wrong the whole time.&lt;br&gt;
Once that was fixed, the memory panel started populating correctly. Pattern count climbed to 14 across sessions. Then we noticed it reset to zero on page refresh — React state only, not reloaded from Hindsight. Fixed with a useEffect that calls the backend on mount. Small bug, big trust issue. If memory disappears on refresh, it feels fake even when it isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What We'd Build Next :&lt;/strong&gt;&lt;br&gt;
GitHub PR integration — Refyn works well as a paste-and-review workspace. The natural next step is routing decisions happening file by file inside pull requests instead of one snippet at a time.&lt;br&gt;
Team memory banks — Individual memory is already useful. Shared memory across a team means recurring security issues, preferred patterns, and project-specific conventions survive across contributors, not just sessions.&lt;br&gt;
Budget enforcement — Refyn already calculates cost and savings. Hard limits, escalation warnings, and cost caps per session are the next layer that makes routing operational rather than just visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Closing :&lt;/strong&gt; &lt;br&gt;
Building Refyn taught me that AI cost control is mostly a routing problem disguised as a model problem.&lt;br&gt;
We didn't win by finding one perfect provider. Gemini failed. Groq changed underneath us. OpenRouter free models rotated. Our own .env setup broke everything once. What held up was a system that could score the work, choose the cheapest reasonable path, and recover when something went wrong.&lt;br&gt;
That's the part I trust now. Not the model names. The routing.&lt;/p&gt;

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