<?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: Ghazanfar Uruj</title>
    <description>The latest articles on DEV Community by Ghazanfar Uruj (@ghazanfaruruj).</description>
    <link>https://dev.to/ghazanfaruruj</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%2F3975750%2F2a0df8b6-96d6-40df-a593-64468e35337f.png</url>
      <title>DEV Community: Ghazanfar Uruj</title>
      <link>https://dev.to/ghazanfaruruj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ghazanfaruruj"/>
    <language>en</language>
    <item>
      <title>Find the regex that can freeze your Python service - before it ships</title>
      <dc:creator>Ghazanfar Uruj</dc:creator>
      <pubDate>Wed, 10 Jun 2026 10:56:41 +0000</pubDate>
      <link>https://dev.to/ghazanfaruruj/find-the-regex-that-can-freeze-your-python-service-before-it-ships-4c5d</link>
      <guid>https://dev.to/ghazanfaruruj/find-the-regex-that-can-freeze-your-python-service-before-it-ships-4c5d</guid>
      <description>&lt;p&gt;A regex like &lt;code&gt;^(\w+)+$&lt;/code&gt; looks harmless. But feed it a long string that &lt;em&gt;almost&lt;/em&gt; matches and it can backtrack exponentially — a couple dozen characters can take seconds, and a slightly longer one can peg a CPU core and hang the thread indefinitely. That's a &lt;strong&gt;ReDoS&lt;/strong&gt; (Regular-expression Denial of Service), and a single user-facing input field is all it takes.&lt;/p&gt;

&lt;p&gt;I wanted those patterns caught automatically, in CI, so I built &lt;strong&gt;redos&lt;/strong&gt; — a small, dependency-free CLI.&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%2Fjsa5zi3z4b60jghzf0g6.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%2Fjsa5zi3z4b60jghzf0g6.gif" alt="redos flagging a vulnerable regex" width="760" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;redos never imports or runs your code. It parses each &lt;code&gt;.py&lt;/code&gt; file with the standard-library &lt;code&gt;ast&lt;/code&gt; module, collects every literal pattern passed to &lt;code&gt;re&lt;/code&gt; / &lt;code&gt;regex&lt;/code&gt; (&lt;code&gt;re.compile&lt;/code&gt;, &lt;code&gt;re.match&lt;/code&gt;, &lt;code&gt;re.search&lt;/code&gt;, …), then parses each one with Python's own regular-expression parser and inspects the &lt;em&gt;parsed structure&lt;/em&gt; for the two classic causes of catastrophic backtracking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nested quantifiers&lt;/strong&gt; — a repeated group that itself repeats, like &lt;code&gt;(a+)+&lt;/code&gt;, &lt;code&gt;(a*)*&lt;/code&gt;, or &lt;code&gt;([a-z]+)*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ambiguous alternation under a repeat&lt;/strong&gt; — branches that can match the same text, like &lt;code&gt;(a|a)+&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because it analyses the pattern &lt;em&gt;after&lt;/em&gt; Python's own optimiser runs, it doesn't flag patterns the engine already makes safe — &lt;code&gt;(abc|abd)+&lt;/code&gt; has its common prefix factored out, &lt;code&gt;(\w|\d)+&lt;/code&gt; collapses to a single character class. That keeps false positives low, which is the thing that actually kills adoption of a linter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it
&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;redos
redos &lt;span class="nb"&gt;.&lt;/span&gt;                  &lt;span class="c"&gt;# scan the current project&lt;/span&gt;
redos &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json    &lt;span class="c"&gt;# machine-readable output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It exits non-zero when it finds a risk, so &lt;code&gt;redos .&lt;/code&gt; drops straight into CI or a pre-commit hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/gazzycodes/redos&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v0.1.0&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redos&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why a static tool?
&lt;/h2&gt;

&lt;p&gt;An AI assistant can spot a dangerous regex if you ask it about that specific line — but redos is deterministic, runs headless in CI on every commit in milliseconds, reasons over your &lt;em&gt;entire&lt;/em&gt; project at once, and never sends your code anywhere. It's the cheap, reliable gate, the same reason linters and type checkers keep thriving.&lt;/p&gt;

&lt;p&gt;It's open source (MIT) and early — feedback and contributions welcome: &lt;a href="https://github.com/gazzycodes/redos" rel="noopener noreferrer"&gt;https://github.com/gazzycodes/redos&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Stop shipping a .env.example that's already out of date</title>
      <dc:creator>Ghazanfar Uruj</dc:creator>
      <pubDate>Wed, 10 Jun 2026 08:47:15 +0000</pubDate>
      <link>https://dev.to/ghazanfaruruj/stop-shipping-a-envexample-thats-already-out-of-date-4l2c</link>
      <guid>https://dev.to/ghazanfaruruj/stop-shipping-a-envexample-thats-already-out-of-date-4l2c</guid>
      <description>&lt;p&gt;You add a new feature, it reads a new environment variable, you ship it. Three weeks later a teammate clones the repo, copies &lt;code&gt;.env.example&lt;/code&gt;, runs the app — and it crashes on a &lt;code&gt;KeyError&lt;/code&gt; for a variable nobody documented. Or the reverse: &lt;code&gt;.env.example&lt;/code&gt; is full of variables that no code reads anymore, and new devs waste time setting them.&lt;/p&gt;

&lt;p&gt;I wanted that drift caught automatically, so I built &lt;strong&gt;envcheck&lt;/strong&gt; — a small, dependency-free CLI.&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%2Fv8pjps6gzf2arzsu4b1y.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%2Fv8pjps6gzf2arzsu4b1y.gif" alt="envcheck flagging an undocumented and an unused variable" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;envcheck never imports or runs your code. It parses each &lt;code&gt;.py&lt;/code&gt; file with the standard-library &lt;code&gt;ast&lt;/code&gt; module, collects every literal-key environment-variable access (&lt;code&gt;os.getenv("X")&lt;/code&gt;, &lt;code&gt;os.environ["X"]&lt;/code&gt;, &lt;code&gt;os.environ.get("X")&lt;/code&gt;, &lt;code&gt;os.environ.setdefault("X", ...)&lt;/code&gt;), then diffs that against your example env file. Dynamic keys are ignored on purpose, so it never produces noisy false positives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it
&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;envcheck-sync
envcheck &lt;span class="nb"&gt;.&lt;/span&gt;                      &lt;span class="c"&gt;# auto-detects .env.example, .env.sample, ...&lt;/span&gt;
envcheck &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json        &lt;span class="c"&gt;# machine-readable for tooling&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It exits non-zero when it finds &lt;strong&gt;undocumented&lt;/strong&gt; variables, so dropping &lt;code&gt;envcheck .&lt;/code&gt; into CI or a pre-commit hook keeps your &lt;code&gt;.env.example&lt;/code&gt; honest. It also reports &lt;strong&gt;possibly-unused&lt;/strong&gt; variables as a warning (without failing) so you can prune them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a static tool in the age of AI?
&lt;/h2&gt;

&lt;p&gt;An AI assistant can spot a missing env var if you ask it, but envcheck is deterministic, runs headless in CI on every commit in milliseconds, reasons over your &lt;em&gt;entire&lt;/em&gt; project at once, and never sends your code anywhere. It's the cheap, reliable gate — the same reason linters and type checkers keep thriving.&lt;/p&gt;

&lt;p&gt;It's open source (MIT) and early — feedback and contributions welcome: &lt;a href="https://github.com/gazzycodes/envcheck" rel="noopener noreferrer"&gt;https://github.com/gazzycodes/envcheck&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>python</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>Catching circular imports in Python before they crash you</title>
      <dc:creator>Ghazanfar Uruj</dc:creator>
      <pubDate>Tue, 09 Jun 2026 10:31:04 +0000</pubDate>
      <link>https://dev.to/ghazanfaruruj/catching-circular-imports-in-python-before-they-crash-you-4ifk</link>
      <guid>https://dev.to/ghazanfaruruj/catching-circular-imports-in-python-before-they-crash-you-4ifk</guid>
      <description>&lt;p&gt;Circular imports are one of those Python problems that stay invisible until a specific import order triggers an &lt;code&gt;ImportError&lt;/code&gt; or a half-initialized module in production. They also quietly tighten the coupling in your codebase.&lt;/p&gt;

&lt;p&gt;I wanted something that catches them &lt;em&gt;before&lt;/em&gt; runtime, so I wrote &lt;strong&gt;knot&lt;/strong&gt; — a small, dependency-free CLI.&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%2F399mk4f4jjqee509yplm.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%2F399mk4f4jjqee509yplm.gif" alt="knot detecting a circular import" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;knot never imports or runs your code. It parses each file with the standard-library &lt;code&gt;ast&lt;/code&gt; module, maps every file to its fully-qualified module name, resolves both absolute and relative imports to internal modules, then finds cycles by computing strongly connected components with an iterative Tarjan's algorithm (safe on very large graphs). For each cycle it prints a concrete path like &lt;code&gt;a -&amp;gt; b -&amp;gt; a&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it
&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;knot-imports
knot mypackage
knot mypackage &lt;span class="nt"&gt;--format&lt;/span&gt; mermaid   &lt;span class="c"&gt;# draws the import graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns a non-zero exit code when cycles exist, so adding &lt;code&gt;knot .&lt;/code&gt; to CI or a pre-commit hook keeps cycles from ever reaching &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a static tool in the age of AI coding?
&lt;/h2&gt;

&lt;p&gt;An AI assistant can spot a cycle if you ask it, but knot is deterministic, runs headless in CI on every commit in milliseconds, reasons over your &lt;em&gt;entire&lt;/em&gt; import graph at once, and never sends your code anywhere. It's the cheap, reliable gate — the same reason linters and type checkers keep thriving.&lt;/p&gt;

&lt;p&gt;It's open source (MIT), and early — feedback and contributions welcome: &lt;a href="https://github.com/gazzycodes/knot" rel="noopener noreferrer"&gt;https://github.com/gazzycodes/knot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
