<?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: Parsa Mohammadi</title>
    <description>The latest articles on DEV Community by Parsa Mohammadi (@parsa_m).</description>
    <link>https://dev.to/parsa_m</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%2F4123367%2F2199da8a-7ce0-4062-b7d9-0a75f65bf20d.png</url>
      <title>DEV Community: Parsa Mohammadi</title>
      <link>https://dev.to/parsa_m</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/parsa_m"/>
    <language>en</language>
    <item>
      <title>A Blind Scan Found IBM ContextForge's Connection Leak From One File</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Wed, 21 Oct 2026 15:31:45 +0000</pubDate>
      <link>https://dev.to/parsa_m/a-blind-scan-found-ibm-contextforges-connection-leak-from-one-file-1cnh</link>
      <guid>https://dev.to/parsa_m/a-blind-scan-found-ibm-contextforges-connection-leak-from-one-file-1cnh</guid>
      <description>&lt;p&gt;On January 3, 2026, a 3,000-user load test against &lt;a href="https://github.com/IBM/mcp-context-forge" rel="noopener noreferrer"&gt;IBM ContextForge&lt;/a&gt; left PostgreSQL connections stuck in &lt;code&gt;idle in transaction&lt;/code&gt; until the pool ran dry. The cause was three lines of cleanup code. We rewound the repository to the commit before the fix, gave Tomosu that one file and nothing else, and ran a blind scan. It named the same weakness and proposed a fix that makes connection release unconditional.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;42 to 81&lt;/th&gt;
&lt;th&gt;120 s&lt;/th&gt;
&lt;th&gt;27 / 100&lt;/th&gt;
&lt;th&gt;Direct hit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;connections stuck &lt;code&gt;idle in transaction&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;held before a timeout released them&lt;/td&gt;
&lt;td&gt;PRI on the blind scan&lt;/td&gt;
&lt;td&gt;against a criterion sealed before scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The system
&lt;/h2&gt;

&lt;p&gt;ContextForge is an open-source gateway and registry for MCP, A2A, REST and gRPC. Agents, apps and copilots call it, and it routes, authenticates, scopes tokens and federates tools to downstream servers. Every request opens a database session against a PostgreSQL pool with bounded capacity, so it sits squarely in a high-concurrency path.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the load test found
&lt;/h2&gt;

&lt;p&gt;The team ran 3,000 concurrent users to surface behavior ordinary development never hits. Requests looked healthy, but connections stopped cycling. Between 42 and 81 sat in &lt;code&gt;idle in transaction&lt;/code&gt;: checked out, doing nothing, unavailable to anyone else. Each one stayed stuck until PostgreSQL's &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt; killed it at 120 seconds. Two minutes is a long time when thousands of requests are waiting on a finite pool.&lt;/p&gt;

&lt;p&gt;The stuck query was a &lt;code&gt;SELECT&lt;/code&gt; from the token-scoping middleware (&lt;a href="https://github.com/IBM/mcp-context-forge/issues/1885" rel="noopener noreferrer"&gt;issue #1885&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Three lines of cleanup
&lt;/h2&gt;

&lt;p&gt;The investigation led to &lt;code&gt;mcpgateway/middleware/token_scoping.py&lt;/code&gt;. It looks fine at a glance: a &lt;code&gt;finally&lt;/code&gt;, a commit, a close.&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="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;# can raise
&lt;/span&gt;    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;    &lt;span class="c1"&gt;# never reached if commit raises
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;finally&lt;/code&gt; guarantees the block starts, not that every statement in it finishes. &lt;code&gt;db.commit()&lt;/code&gt; can fail (a PgBouncer timeout, a connection reset). When it raises, Python leaves the block and &lt;code&gt;close()&lt;/code&gt; never runs. Under enough concurrency, three lines become pool exhaustion.&lt;/p&gt;

&lt;p&gt;The issue lists the same pattern in four files, about 24 instances in total, with &lt;code&gt;admin.py&lt;/code&gt; holding around 20 of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/IBM/mcp-context-forge/pull/1886" rel="noopener noreferrer"&gt;PR #1886&lt;/a&gt; nests the commit in its own &lt;code&gt;try&lt;/code&gt; so the close survives a failing commit:&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="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;# always runs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the same 3,000-user load:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before the fix&lt;/th&gt;
&lt;th&gt;After the fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stuck connections&lt;/td&gt;
&lt;td&gt;42 to 81&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection release&lt;/td&gt;
&lt;td&gt;Held up to 120 s&lt;/td&gt;
&lt;td&gt;Cycling in 1 to 2 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pool state&lt;/td&gt;
&lt;td&gt;Exhausted&lt;/td&gt;
&lt;td&gt;Healthy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rule underneath: if you acquire a finite production resource, its release has to survive every failure path.&lt;/p&gt;

&lt;h2&gt;
  
  
  The experiment: could the code have told us first?
&lt;/h2&gt;

&lt;p&gt;Everything the team learned came from production-scale conditions. The question was whether the weakness was visible in the code alone, before any load test.&lt;/p&gt;

&lt;p&gt;We checked out commit &lt;code&gt;cf5adcd5d&lt;/code&gt;, the code exactly as it stood before the fix, and gave Tomosu one file: &lt;code&gt;token_scoping.py&lt;/code&gt;, 755 lines. Tomosu did not get the issue, the PR, the load-test results, the release notes, or any hint of what to look for.&lt;/p&gt;

&lt;p&gt;Before scanning, we sealed the success criterion so the result couldn't be reinterpreted afterwards:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Identify unsafe database-session cleanup where an operation before &lt;code&gt;close()&lt;/code&gt; can fail and prevent reliable connection release.&lt;br&gt;
&lt;strong&gt;Direct hit:&lt;/strong&gt; names the unsafe session lifecycle on this path. &lt;strong&gt;Adjacent:&lt;/strong&gt; generic exception-handling or database advice. &lt;strong&gt;Miss:&lt;/strong&gt; anything else.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;One file, 1 minute 41 seconds, 5 findings, a Production Reliability Index of 27 out of 100. One of the five, rated high severity:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Unsafe database-session lifecycle.&lt;/strong&gt; An operation performed before the session is closed can fail, leaving the connection unreleased.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It names the cleanup path, says the preceding operation can fail, and ties that to connection release. All three checks in the criterion pass.&lt;/p&gt;

&lt;p&gt;Tomosu's suggested change went a different way from IBM's. This path only reads (the stuck query was a &lt;code&gt;SELECT&lt;/code&gt;), so it drops the commit entirely:&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="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three versions of the same block, one invariant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before:&lt;/strong&gt; release depends on the commit succeeding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IBM's fix:&lt;/strong&gt; commit kept, close protected by its own &lt;code&gt;finally&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tomosu, blind:&lt;/strong&gt; commit removed, so there is nothing for release to depend on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this does and doesn't show
&lt;/h2&gt;

&lt;p&gt;It shows the weakness was readable from the source, with no incident context, before anyone ran a load test.&lt;/p&gt;

&lt;p&gt;It doesn't show Tomosu would have found all ~24 instances: we scanned one file, and we chose that file knowing where the bug was. A full-repository blind scan at the same commit is the stronger test. Static analysis also doesn't replace load testing. It moves some of what load testing teaches earlier, to the pull request, where the fix costs three lines instead of an outage.&lt;/p&gt;

&lt;p&gt;If you use SQLAlchemy sessions by hand, search your code for &lt;code&gt;commit()&lt;/code&gt; followed by &lt;code&gt;close()&lt;/code&gt; in the same &lt;code&gt;finally&lt;/code&gt;. A context manager (&lt;code&gt;with SessionLocal() as db:&lt;/code&gt;) or a nested &lt;code&gt;try/finally&lt;/code&gt; closes the hole.&lt;/p&gt;




&lt;p&gt;Tomosu scores each pull request with a 0 to 100 Production Reliability Index before it merges. &lt;a href="https://tomosu.ai/start?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=contextforge-case-study" rel="noopener noreferrer"&gt;Run a free scan&lt;/a&gt; on one public repository, or read the &lt;a href="https://tomosu.ai/case-studies/contextforge-connection-leak.html?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=contextforge-case-study" rel="noopener noreferrer"&gt;full case study&lt;/a&gt; with the 22-slide forensics deck.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ContextForge is an open-source project. This analysis uses its public repository history; Tomosu AI is not affiliated with IBM. Originally published on &lt;a href="https://tomosu.ai/case-studies/contextforge-connection-leak.html" rel="noopener noreferrer"&gt;tomosu.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>postgres</category>
      <category>sqlalchemy</category>
      <category>sre</category>
    </item>
    <item>
      <title>What AI Reliability Actually Means (and How to Measure It)</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:50:09 +0000</pubDate>
      <link>https://dev.to/parsa_m/what-ai-reliability-actually-means-and-how-to-measure-it-kmg</link>
      <guid>https://dev.to/parsa_m/what-ai-reliability-actually-means-and-how-to-measure-it-kmg</guid>
      <description>&lt;p&gt;AI reliability is how consistently an AI system, or software an AI has changed, does the correct thing under real production conditions, measured over time. Most writing on the term covers only the first half: whether a model gives good answers. For engineering teams in 2026 the second half matters as much, because agents now write and merge a growing share of the code that runs in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short version:&lt;/strong&gt; reliability for AI has two halves.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime half:&lt;/strong&gt; whether a model, an LLM app or an agent behaves correctly and consistently while it runs. Judged with evals, tracing, guardrails and consistency across runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change half:&lt;/strong&gt; whether the code, config and infrastructure an agent produces behave correctly once merged and deployed. Judged with review, tests, merge gates and change risk scoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where they meet:&lt;/strong&gt; an agent that acts, because its output is a change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Uptime measures neither. You need a quality signal next to availability, and a change-level view of what agents ship.&lt;/p&gt;

&lt;p&gt;NIST's AI Risk Management Framework borrows the ISO/IEC definition of reliability: the ability of an item to perform as required, without failure, for a given time interval, under given conditions (&lt;a href="https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.100-1.pdf" rel="noopener noreferrer"&gt;NIST AI RMF&lt;/a&gt;). That definition fits both halves. What changes is which "item" you're judging.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two meanings of AI reliability
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Reliability of AI systems&lt;/th&gt;
&lt;th&gt;Reliability of AI-written changes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What is being judged&lt;/td&gt;
&lt;td&gt;A model, LLM app or agent at runtime&lt;/td&gt;
&lt;td&gt;Code, config or infra an agent produced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical failure&lt;/td&gt;
&lt;td&gt;Wrong, inconsistent or unsafe output&lt;/td&gt;
&lt;td&gt;A merged change that breaks production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who notices first&lt;/td&gt;
&lt;td&gt;Users, evals, support tickets&lt;/td&gt;
&lt;td&gt;On-call, error rates, rollbacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main tools&lt;/td&gt;
&lt;td&gt;Evals, tracing, guardrails&lt;/td&gt;
&lt;td&gt;Review, tests, merge gates, change risk scoring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; AI SYSTEM AT RUNTIME            AI-WRITTEN CHANGE
 model, LLM app, agent           code, config, infrastructure
 judged by evals,                judged by review, tests,
 tracing, guardrails             merge gates
            \                        /
             \                      /
          AN AGENT THAT ACTS: its output is a change
      (both halves apply when an agent edits, migrates or deploys)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two meet when an agent takes actions. An agent that edits files, runs migrations or calls production APIs is an AI system whose output is a change. For the broader picture on the second column, see &lt;a href="https://tomosu.ai/blogs/what-is-production-reliability-software-engineering.html?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=what-is-ai-reliability" rel="noopener noreferrer"&gt;What Is Production Reliability in Software Engineering?&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is uptime the same as reliability for an LLM?
&lt;/h2&gt;

&lt;p&gt;No. A traditional service is mostly reliable when it's up and fast. An LLM endpoint can return 200 OK in 400 ms with a wrong answer, and the availability dashboard stays green the whole time.&lt;/p&gt;

&lt;p&gt;Uptime is necessary, not sufficient. An LLM feature needs at least one quality signal next to the infrastructure ones. Without it, a prompt change that quietly lowers answer quality looks like a perfect week.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to measure AI reliability
&lt;/h2&gt;

&lt;p&gt;Google's SRE book defines an SLI as a carefully defined quantitative measure of some aspect of a service, and an SLO as a target for it (&lt;a href="https://sre.google/sre-book/service-level-objectives/" rel="noopener noreferrer"&gt;SRE book&lt;/a&gt;). The same approach works for AI, with a few extra indicators.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SLI&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;th&gt;How to compute it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Availability&lt;/td&gt;
&lt;td&gt;Provider or gateway outages&lt;/td&gt;
&lt;td&gt;Successful responses / total requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency (p95)&lt;/td&gt;
&lt;td&gt;Slow tool loops, long generations&lt;/td&gt;
&lt;td&gt;p95 of end-to-end request time, including retries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eval pass rate&lt;/td&gt;
&lt;td&gt;Quality regressions after a prompt or model change&lt;/td&gt;
&lt;td&gt;Pass rate on a fixed, versioned eval set, run on every change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency&lt;/td&gt;
&lt;td&gt;Answers that flip between runs&lt;/td&gt;
&lt;td&gt;Agreement rate across N runs of the same input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guarded-action rate (agents)&lt;/td&gt;
&lt;td&gt;Agents attempting risky actions&lt;/td&gt;
&lt;td&gt;Tool calls blocked by policy / total tool calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Change fail rate (AI-authored)&lt;/td&gt;
&lt;td&gt;Agent PRs that break production&lt;/td&gt;
&lt;td&gt;DORA's change fail rate, filtered to AI-authored changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DORA defines change fail rate as the ratio of deployments that need immediate intervention after they go out (&lt;a href="https://dora.dev/guides/dora-metrics-four-keys/" rel="noopener noreferrer"&gt;DORA&lt;/a&gt;). Splitting it by author type is the simplest way to see whether agent-written changes are more or less reliable than human ones in your codebase. A PR label or the bot's author name is usually enough. &lt;a href="https://tomosu.ai/blogs/how-to-measure-software-change-reliability.html?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=what-is-ai-reliability" rel="noopener noreferrer"&gt;How to Measure Software Change Reliability&lt;/a&gt; goes deeper on the change-level metrics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents turn reliability into a change problem
&lt;/h2&gt;

&lt;p&gt;In July 2025, SaaStr founder Jason Lemkin described Replit's agent deleting his production database during a code freeze, then telling him a rollback wouldn't work when it actually did (&lt;a href="https://www.theregister.com/2025/07/21/replit_saastr_vibe_coding_incident/" rel="noopener noreferrer"&gt;The Register&lt;/a&gt;). Model quality wasn't the issue. The failure was an agent with write access to production and nothing between its decision and the database.&lt;/p&gt;

&lt;p&gt;That's the part of AI reliability most teams under-invest in. Evals tell you whether the model is good on average. They don't tell you whether this specific change, to this specific service, is safe to ship. That question belongs to &lt;a href="https://tomosu.ai/blogs/change-risk-management-in-software-engineering.html?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=what-is-ai-reliability" rel="noopener noreferrer"&gt;change risk management&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to set reliability targets for AI features
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pick one quality SLI per feature, not five. Eval pass rate is usually the right first one.&lt;/li&gt;
&lt;li&gt;Version the eval set and run it in CI on every prompt, model or retrieval change.&lt;/li&gt;
&lt;li&gt;Set the SLO from a baseline week of data, not a guess.&lt;/li&gt;
&lt;li&gt;Tag AI-authored PRs so change fail rate can be split by author.&lt;/li&gt;
&lt;li&gt;Decide which failures block a merge or a deploy, and which only alert.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Treating a provider's status page as your reliability number.&lt;/li&gt;
&lt;li&gt;Running evals once at launch and never again.&lt;/li&gt;
&lt;li&gt;Measuring the model while ignoring the code the agent ships.&lt;/li&gt;
&lt;li&gt;Averaging consistency across all inputs, which hides the few prompts that flip every time.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Tomosu works on the change half. It scores each pull request with a 0 to 100 &lt;a href="https://tomosu.ai/platform?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=what-is-ai-reliability#indexes" rel="noopener noreferrer"&gt;Production Reliability Index&lt;/a&gt; before it merges, with an advisory Ready or Not Ready to Merge verdict at 70. A plain scan covers the code; the production-side sub-indices need observability and ticketing integrations connected. &lt;a href="https://tomosu.ai/start?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=what-is-ai-reliability" rel="noopener noreferrer"&gt;Run a free PRI scan&lt;/a&gt; on one repository.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://tomosu.ai/blogs/what-is-ai-reliability.html" rel="noopener noreferrer"&gt;tomosu.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>sre</category>
      <category>llm</category>
    </item>
    <item>
      <title>Connection Leak vs. Pool Exhaustion: How to Tell the Difference</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Mon, 12 Oct 2026 13:48:31 +0000</pubDate>
      <link>https://dev.to/parsa_m/connection-leak-vs-pool-exhaustion-how-to-tell-the-difference-1fmm</link>
      <guid>https://dev.to/parsa_m/connection-leak-vs-pool-exhaustion-how-to-tell-the-difference-1fmm</guid>
      <description>&lt;p&gt;Every connection is checked out, requests are queuing, and the pool is throwing timeouts. The next decision depends on one question: is something leaking connections, or is the pool being asked for more than it can give? Both produce the same error. They need opposite fixes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short version:&lt;/strong&gt; pool exhaustion is a state (every pooled connection is checked out, new callers wait). A connection leak is one cause of it (code checks out a connection and never returns it). Watch in-use connections against traffic. A leak ratchets up and stays up. Load-driven exhaustion falls when traffic falls.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leak:&lt;/strong&gt; in-use climbs with uptime, ignores quiet periods, resets on restart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load or slow work:&lt;/strong&gt; in-use tracks request rate and query time, and recovers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm at the database:&lt;/strong&gt; leaked connections usually sit &lt;code&gt;idle&lt;/code&gt; while the pool counts them as busy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix a leak in code&lt;/strong&gt; (release on every path), not with a bigger pool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mechanism is the same for HikariCP on the JVM, &lt;code&gt;pg.Pool&lt;/code&gt; in Node.js, SQLAlchemy's &lt;code&gt;QueuePool&lt;/code&gt; in Python, and Go's &lt;code&gt;database/sql&lt;/code&gt;. Where the tools differ, there's a per-stack table below. For a JVM-only walkthrough of HikariCP's exception, leak detection and thread dumps, see &lt;a href="https://tomosu.ai/blogs/hikaricp-connection-is-not-available-request-timed-out.html" rel="noopener noreferrer"&gt;HikariCP "Connection Is Not Available, Request Timed Out"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leak vs. exhaustion: the definitions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pool exhaustion&lt;/strong&gt; is the condition where every connection a pool may hold is checked out, so the next caller waits and eventually times out. It's a symptom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A connection leak&lt;/strong&gt; is a specific bug: a code path checks out a connection and never returns it, so usable capacity shrinks by one each time that path runs. A leak always ends in exhaustion if the process lives long enough. Exhaustion does not imply a leak.&lt;/p&gt;

&lt;p&gt;The other causes of exhaustion return every connection eventually, just not fast enough:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load:&lt;/strong&gt; request rate × normal hold time exceeds pool size at peak.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slow queries:&lt;/strong&gt; a missing index, a lock wait, or a bigger table stretches each hold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long holds in application code:&lt;/strong&gt; a transaction that spans an HTTP call, a queue publish, or slow processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nested acquisition:&lt;/strong&gt; a thread holding one connection waits for a second, which can deadlock the pool at modest concurrency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amplified demand:&lt;/strong&gt; client retries add requests exactly when each request is slowest.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Search &lt;a href="https://stackoverflow.com/search?q=connection+pool+exhausted+leak" rel="noopener noreferrer"&gt;Stack Overflow for pool exhaustion&lt;/a&gt; and most threads end with "increase the pool size" or "you have a leak", usually without evidence for either. The useful question is "leak, or one of the causes that recover on their own?" A leak needs a code change on a specific path. The others need faster work, shorter holds, less demand, or (last) more connections.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Connection leak&lt;/th&gt;
&lt;th&gt;Exhaustion without a leak&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Every connection eventually returned?&lt;/td&gt;
&lt;td&gt;No. Stays checked out until the process exits&lt;/td&gt;
&lt;td&gt;Yes, after the query, transaction, or remote call finishes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-use when traffic drops&lt;/td&gt;
&lt;td&gt;Stays at its new high&lt;/td&gt;
&lt;td&gt;Falls back to a low baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correlates with&lt;/td&gt;
&lt;td&gt;Uptime, and the rate of one code path (often an error path)&lt;/td&gt;
&lt;td&gt;Request rate, query latency, dependency latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect of a restart&lt;/td&gt;
&lt;td&gt;Fixes it completely, until the leak catches up&lt;/td&gt;
&lt;td&gt;Little or none if the load or slowness persists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect of a bigger pool&lt;/td&gt;
&lt;td&gt;Delays the outage&lt;/td&gt;
&lt;td&gt;Helps only if the pool is truly undersized&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What they look like over a day
&lt;/h2&gt;

&lt;p&gt;At the moment of the timeout both look identical: in-use equals max, idle is zero, callers are waiting. The difference only shows up as a time series. Put &lt;strong&gt;in-use connections&lt;/strong&gt; and &lt;strong&gt;request rate&lt;/strong&gt; on the same chart over at least one daily cycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONNECTION LEAK (pool max = 10)
in-use
 10 |              ┌─────────────┐
    |         ┌────┘   traffic   │      steps up, never steps down,
  5 |    ┌────┘      falls, but  │      even while traffic falls
    |┌───┘           in-use stays│  ┌───
  0 +────────────────────────────┴──┘───   ← restart resets it
     00:00   06:00   12:00   18:00   24:00

EXHAUSTION FROM LOAD (no leak, pool max = 10)
in-use
 10 |              ┌───┐   ← pool full at peak, callers wait
    |           ┌──┘   └──┐
  5 |        ┌──┘         └──┐
    |    ┌───┘               └───┐
  0 +────┘                       └──────   recovers as traffic falls
     00:00   06:00   12:00   18:00   24:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three details carry most of the diagnosis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Does in-use come down in the quiet hours?&lt;/strong&gt; If traffic drops 80% overnight and in-use doesn't move, those connections aren't doing work for anyone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What does a restart do?&lt;/strong&gt; Instant drop to baseline followed by a slow climb is the classic leak signature. If the pool is full again within minutes of a restart, look at load or slow work first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where are the steps?&lt;/strong&gt; Leaks step up when the leaking path runs. If steps line up with a batch job, a retry burst, or error spikes on one endpoint, you have a lead on which path it is.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Autoscaling and deploys hide leaks.&lt;/strong&gt; If instances get replaced often, a slow leak may never reach the pool limit. It shows up when deploys pause over a holiday, or when a failing dependency makes the leaking path run far more often. Chart in-use &lt;strong&gt;per instance against instance uptime&lt;/strong&gt;, not only the fleet total, or the leak hides inside the average.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Exhaustion in Java, Node.js, Python, and Go
&lt;/h2&gt;

&lt;p&gt;Every pool has a max size, a way to wait for a free connection, and some way to report how many are checked out. The defaults differ in ways that change what the incident looks like. Two of the four common stacks don't fail fast by default.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;Pool size (default)&lt;/th&gt;
&lt;th&gt;Wait limit (default)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JDBC · HikariCP&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;maximumPoolSize&lt;/code&gt; (10)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;connectionTimeout&lt;/code&gt; (30 s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js · &lt;code&gt;pg.Pool&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;max&lt;/code&gt; (10)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;connectionTimeoutMillis&lt;/code&gt; (&lt;strong&gt;0 = wait forever&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python · SQLAlchemy &lt;code&gt;QueuePool&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pool_size&lt;/code&gt; (5) + &lt;code&gt;max_overflow&lt;/code&gt; (10)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pool_timeout&lt;/code&gt; (30 s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go · &lt;code&gt;database/sql&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SetMaxOpenConns&lt;/code&gt; (&lt;strong&gt;0 = unlimited&lt;/strong&gt;)&lt;/td&gt;
&lt;td&gt;None, bounded only by the caller's context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;What exhaustion looks like&lt;/th&gt;
&lt;th&gt;Where to read in-use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JDBC · HikariCP&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SQLTransientConnectionException&lt;/code&gt;: "Connection is not available, request timed out"&lt;/td&gt;
&lt;td&gt;Pool stats in the message, &lt;code&gt;hikaricp.connections.active&lt;/code&gt;, &lt;code&gt;leakDetectionThreshold&lt;/code&gt; for borrower traces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js · &lt;code&gt;pg.Pool&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Requests &lt;strong&gt;hang&lt;/strong&gt;. With a timeout set: "timeout exceeded when trying to connect"&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pool.totalCount&lt;/code&gt;, &lt;code&gt;pool.idleCount&lt;/code&gt;, &lt;code&gt;pool.waitingCount&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python · SQLAlchemy&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;TimeoutError&lt;/code&gt;: "QueuePool limit of size 5 overflow 10 reached, connection timed out"&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;engine.pool.status()&lt;/code&gt;, &lt;code&gt;checkedout()&lt;/code&gt;, &lt;code&gt;checkout&lt;/code&gt;/&lt;code&gt;checkin&lt;/code&gt; pool events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go · &lt;code&gt;database/sql&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Goroutines block, then &lt;code&gt;context deadline exceeded&lt;/code&gt;. If unlimited, the database refuses new connections instead&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;db.Stats()&lt;/code&gt;: &lt;code&gt;InUse&lt;/code&gt;, &lt;code&gt;Idle&lt;/code&gt;, &lt;code&gt;WaitCount&lt;/code&gt;, &lt;code&gt;WaitDuration&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Defaults as documented by &lt;a href="https://github.com/brettwooldridge/HikariCP" rel="noopener noreferrer"&gt;HikariCP&lt;/a&gt;, &lt;a href="https://node-postgres.com/apis/pool" rel="noopener noreferrer"&gt;node-postgres&lt;/a&gt;, &lt;a href="https://docs.sqlalchemy.org/en/20/core/pooling.html" rel="noopener noreferrer"&gt;SQLAlchemy&lt;/a&gt;, and &lt;a href="https://pkg.go.dev/database/sql#DB.SetMaxOpenConns" rel="noopener noreferrer"&gt;Go&lt;/a&gt;. Frameworks often override them, so read the effective values from your running config.&lt;/p&gt;

&lt;h3&gt;
  
  
  node-postgres waits forever by default
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;connectionTimeoutMillis&lt;/code&gt; at 0, a caller that can't get a client waits with no limit. An exhausted pool doesn't throw. Requests just stop completing, which usually surfaces as a load balancer or gateway timeout far from the real cause. Set a timeout so the failure is visible, and export the counters the pool already has:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;connectionTimeoutMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// default 0 means wait forever&lt;/span&gt;
  &lt;span class="na"&gt;idleTimeoutMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gauge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db.pool.total&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gauge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db.pool.idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idleCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gauge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db.pool.waiting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitingCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gauge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db.pool.in_use&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalCount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idleCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;unref&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Go's database/sql is unlimited by default
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;SetMaxOpenConns&lt;/code&gt; at 0, a Go service never exhausts its own pool. A leak opens new connections until the &lt;em&gt;database&lt;/em&gt; refuses them (PostgreSQL's "sorry, too many clients already", MySQL's "Too many connections"). That error can hit every other service sharing the database, so one service's leak looks like a platform-wide outage. Set a limit and log the stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetMaxOpenConns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c"&gt;// default 0 = unlimited&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetMaxIdleConns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c"&gt;// default 2&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetConnMaxLifetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"db pool open=%d inuse=%d idle=%d waits=%d waited=%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenConnections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InUse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Idle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitDuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On any stack the four numbers that matter are &lt;strong&gt;max&lt;/strong&gt;, &lt;strong&gt;in use&lt;/strong&gt;, &lt;strong&gt;idle&lt;/strong&gt;, and &lt;strong&gt;waiting&lt;/strong&gt; (or cumulative wait count and time). If you add one metric after reading this, make it in-use as a time series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm a leak from the database side
&lt;/h2&gt;

&lt;p&gt;The pool knows which connections are checked out. The database knows what each of them is actually doing. Comparing the two is the fastest confirmation, and it works on any stack.&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;SELECT&lt;/span&gt; &lt;span class="n"&gt;application_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;sessions&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;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;state_change&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;longest_in_state&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;pg_stat_activity&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;datname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_database&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="n"&gt;backend_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'client backend'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Column meanings are in the PostgreSQL &lt;a href="https://www.postgresql.org/docs/current/monitoring-stats.html" rel="noopener noreferrer"&gt;cumulative statistics docs&lt;/a&gt;. Set &lt;code&gt;application_name&lt;/code&gt; in each service's connection string so you can filter to one service's pool, then line the result up against the pool's counters at the same moment.&lt;/p&gt;

&lt;p&gt;Example with a pool of 10 (illustrative numbers):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;View&lt;/th&gt;
&lt;th&gt;What it reports&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pool&lt;/td&gt;
&lt;td&gt;10 in use, 0 idle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;2 &lt;code&gt;active&lt;/code&gt;, 1 &lt;code&gt;idle in transaction&lt;/code&gt;, 7 &lt;code&gt;idle&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 2 active sessions are running queries right now. The 1 idle-in-transaction session has an open transaction and no query, so check its age. The &lt;strong&gt;7 idle sessions the pool counts as busy&lt;/strong&gt; were borrowed and never given back. Those are your leak candidates.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pool says&lt;/th&gt;
&lt;th&gt;Database says&lt;/th&gt;
&lt;th&gt;Most likely cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;All in use&lt;/td&gt;
&lt;td&gt;Most sessions &lt;code&gt;idle&lt;/code&gt;, &lt;code&gt;state_change&lt;/code&gt; minutes or hours old&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Leak&lt;/strong&gt; with autocommit: connections borrowed and abandoned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All in use&lt;/td&gt;
&lt;td&gt;Many &lt;code&gt;idle in transaction&lt;/code&gt;, old &lt;code&gt;xact_start&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Leak mid-transaction&lt;/strong&gt;, or a long hold around non-database work. Age tells them apart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All in use&lt;/td&gt;
&lt;td&gt;Most sessions &lt;code&gt;active&lt;/code&gt;, long &lt;code&gt;query_start&lt;/code&gt; age&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Slow queries&lt;/strong&gt; or a lock chain. Check &lt;code&gt;wait_event_type&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All in use&lt;/td&gt;
&lt;td&gt;Most sessions &lt;code&gt;active&lt;/code&gt;, queries short&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Load&lt;/strong&gt;: throughput × hold time exceeds the pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Below max, callers waiting&lt;/td&gt;
&lt;td&gt;Fewer sessions than expected, connection errors logged&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Creation failure&lt;/strong&gt;, e.g. the database's connection limit reached by other clients&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MySQL shows the same picture in &lt;code&gt;SHOW FULL PROCESSLIST&lt;/code&gt; (&lt;code&gt;Sleep&lt;/code&gt; with a large &lt;code&gt;Time&lt;/code&gt;) and &lt;code&gt;information_schema.innodb_trx&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A database timeout protects the database, not the pool.&lt;/strong&gt; PostgreSQL's &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt; ends sessions that sit in an open transaction too long and releases their locks. Good guardrail. It does not return the connection to your pool: the pool still counts it as checked out until application code releases it, and the next use of that connection fails. Damage control, not a leak fix.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why leaks show up during a different incident
&lt;/h2&gt;

&lt;p&gt;Most real leaks aren't on the happy path. The happy path runs in every test and every request, so a missing release there gets found within hours. Leaks survive on &lt;strong&gt;error and early-return paths&lt;/strong&gt;: a row fails to parse, a downstream call throws, validation fails, a request is cancelled. So &lt;strong&gt;a leak drains the pool at the rate its path runs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Time to drain = pool size ÷ leak rate (illustrative numbers):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Error path rate&lt;/th&gt;
&lt;th&gt;Pool of 20 drains in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Normal day&lt;/td&gt;
&lt;td&gt;~2 per hour&lt;/td&gt;
&lt;td&gt;~10 hours (daily deploys restart it first, so nobody notices)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependency failing&lt;/td&gt;
&lt;td&gt;~5 per second&lt;/td&gt;
&lt;td&gt;~4 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is why "the pool ran out during the payment provider outage" is often a misdiagnosis. The outage was real. The exhaustion was a second, pre-existing bug the outage exposed. The tell is what happens &lt;strong&gt;after the dependency recovers&lt;/strong&gt;: exhaustion from load or slowness clears within seconds, while a leak leaves the pool full until someone restarts the service.&lt;/p&gt;

&lt;p&gt;The same logic covers leaks that appear "right after a deploy". The deploy may not have added the leak. It may have added a new failure mode, stricter validation, or a new timeout that sends more traffic down an old error branch. When you're &lt;a href="https://tomosu.ai/blogs/which-commit-caused-the-production-incident.html" rel="noopener noreferrer"&gt;hunting for the commit behind an incident&lt;/a&gt;, include changes that increase the rate of existing error paths, not only changes to data access code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code patterns that leak connections
&lt;/h2&gt;

&lt;p&gt;Same across languages: acquisition without a guaranteed release, plus resources that pin a connection (result sets, cursors, streams, transactions) not closed on every path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BROKEN
acquire() ──► query + map rows ──► release() ──► return
                    │
                    └── throws ──► error propagates, release() skipped
                                   (connection stays checked out forever)

FIXED
acquire() ──► try: query + map ──► finally: release() ──► exit
                    │                     ▲
                    └── throws ───────────┘
              (success and error both pass through finally)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The leak is a missing edge in the control flow. Structured release (try/finally, try-with-resources, &lt;code&gt;with&lt;/code&gt;, &lt;code&gt;defer&lt;/code&gt;) adds it for every path at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node.js: &lt;code&gt;client.release()&lt;/code&gt; skipped when a query throws
&lt;/h3&gt;

&lt;p&gt;Leaks when a query throws:&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;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&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;client&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;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BEGIN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DEBIT_SQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;   &lt;span class="c1"&gt;// may throw&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CREDIT_SQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COMMIT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                                  &lt;span class="c1"&gt;// never reached on error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Released on every path:&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;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&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;client&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;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;broken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BEGIN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DEBIT_SQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CREDIT_SQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COMMIT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ROLLBACK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;broken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;broken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// always runs; truthy arg destroys it&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;For single statements, &lt;code&gt;pool.query()&lt;/code&gt; checks out and releases a client for you, so it can't leak. The risk lives in code that calls &lt;code&gt;pool.connect()&lt;/code&gt; directly, usually for transactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go: &lt;code&gt;rows&lt;/code&gt; or &lt;code&gt;tx&lt;/code&gt; left open on an early return
&lt;/h3&gt;

&lt;p&gt;Leaks on a &lt;code&gt;Scan&lt;/code&gt; error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listItemsSQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderID&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SKU&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Qty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;   &lt;span class="c"&gt;// rows still open: its connection stays in use&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Closed on every path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listItemsSQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderID&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c"&gt;// safe to call even after Next has returned false&lt;/span&gt;

&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BeginTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rollback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c"&gt;// no-op after a successful Commit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;rows.Next()&lt;/code&gt; returns false the rows close automatically, which is why the happy path in the broken example doesn't leak. Only the early return does, and only when a &lt;code&gt;Scan&lt;/code&gt; fails. Exactly the kind of path tests rarely hit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python: a Session not closed on an exception
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Broken: close() is skipped if anything between here and there raises
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SessionLocal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invoice_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;render_pdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# raises: session keeps its connection
&lt;/span&gt;    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Fixed: the context manager closes the Session on every path
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;SessionLocal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invoice_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;render_pdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c1"&gt;# connection returned when the block exits
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In CPython, reference counting sometimes returns that connection when the abandoned object is garbage collected, and SQLAlchemy may log a warning when it cleans up a connection that was never checked in. That makes Python leaks intermittent rather than absent. Don't rely on it. FastAPI dependencies with &lt;code&gt;yield&lt;/code&gt;, or &lt;code&gt;scoped_session.remove()&lt;/code&gt; at request end, exist to make release structural.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other patterns that pin a connection
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Streams and cursors returned to the caller:&lt;/strong&gt; a repository method that returns a lazy stream or server-side cursor keeps its connection until the caller closes it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactions left open&lt;/strong&gt; by an early return between &lt;code&gt;BEGIN&lt;/code&gt; and &lt;code&gt;COMMIT&lt;/code&gt;, which also hold locks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connections stored in fields or passed to another thread&lt;/strong&gt;, where ownership of the release is unclear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background jobs&lt;/strong&gt; that open a connection outside the request lifecycle, so request-scoped cleanup never runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Track the borrower when your pool won't
&lt;/h3&gt;

&lt;p&gt;HikariCP has &lt;code&gt;leakDetectionThreshold&lt;/code&gt;. node-postgres has no built-in equivalent, but you can add one in a few lines by recording where each client was checked out:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;held&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;connect&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;client&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;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;held&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checked out here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stack&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;release&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;release&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;release&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;held&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;held&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`db client held &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ms`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stack&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="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;unref&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A warning means "held a long time", not "leaked". A warning for a client that's later released is a long hold. A warning that repeats from the same stack and never clears is a leak. In SQLAlchemy, the pool's &lt;code&gt;checkout&lt;/code&gt; and &lt;code&gt;checkin&lt;/code&gt; events give you the same hook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step by step: leak or exhaustion?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the error and the pool counters.&lt;/strong&gt; Record max, in use, idle, and waiting at the moment of failure, plus any chained cause. If total is below max while callers wait, the pool is failing to &lt;em&gt;create&lt;/em&gt; connections, which is a separate problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chart in-use against request rate over a full day,&lt;/strong&gt; per instance, against instance uptime. A ratchet that ignores quiet periods and resets on restart points to a leak. A curve that follows traffic points to load or slow work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare the pool's view with the database's.&lt;/strong&gt; Many connections checked out in the pool but &lt;code&gt;idle&lt;/code&gt; or long &lt;code&gt;idle in transaction&lt;/code&gt; at the database confirms a leak. Mostly &lt;code&gt;active&lt;/code&gt; sessions point to load or slow queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on borrower tracking.&lt;/strong&gt; Use the pool's leak detection, or a wrapper that records checkout stack and age. Keep the warnings that never clear; they name the leaking path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find every acquisition path without structured release.&lt;/strong&gt; Search for manual checkouts, transactions, returned cursors, and streams. Check error and early-return branches, not just the happy path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix, then verify the shape.&lt;/strong&gt; After the fix ships, in-use should return to baseline in quiet periods and after the next dependency failure. If it still ratchets, there's a second path.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fixes, matched to the cause
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;th&gt;What not to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Connection leak&lt;/td&gt;
&lt;td&gt;Release structurally on every path (try/finally, try-with-resources, &lt;code&gt;with&lt;/code&gt;, &lt;code&gt;defer&lt;/code&gt;). Close returned cursors and streams. Prefer single-call APIs like &lt;code&gt;pool.query()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Raise the pool size or restart on a schedule. Both hide the leak until the error rate rises&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Leak mid-transaction&lt;/td&gt;
&lt;td&gt;Roll back on every non-commit path. Add &lt;code&gt;idle_in_transaction_session_timeout&lt;/code&gt; as a lock guardrail&lt;/td&gt;
&lt;td&gt;Rely on the database timeout as the fix; the pool still thinks the connection is out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow queries&lt;/td&gt;
&lt;td&gt;Fix the query plan or index; add a statement timeout so one query can't hold a connection indefinitely&lt;/td&gt;
&lt;td&gt;Raise the pool wait timeout. Users wait longer for the same failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long holds around remote calls&lt;/td&gt;
&lt;td&gt;Move I/O out of the transaction and add client timeouts to the remote call&lt;/td&gt;
&lt;td&gt;Add connections to match the slowest dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load or amplified demand&lt;/td&gt;
&lt;td&gt;Reduce demand (cache, batch, bound retries), shed load, then size the pool from throughput × hold time within the database's limit&lt;/td&gt;
&lt;td&gt;Size each instance's pool without multiplying by instance count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Undefined limits&lt;/td&gt;
&lt;td&gt;Set a pool max and a wait timeout explicitly (Go &lt;code&gt;SetMaxOpenConns&lt;/code&gt;, node-postgres &lt;code&gt;connectionTimeoutMillis&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Leave defaults that wait forever or open unlimited connections&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Treat any change to pool size, pool timeout, or data access code as a change to a shared production resource. Several services and background jobs usually share the database's connection budget, so a local "fix" can move the exhaustion somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pool exhaustion is a symptom. A leak is one cause; load, slow queries, long holds, and nested acquisition are others.&lt;/li&gt;
&lt;li&gt;Chart in-use against request rate, per instance. A leak ratchets and ignores quiet periods. Load-driven exhaustion recovers.&lt;/li&gt;
&lt;li&gt;Connections checked out in the pool but &lt;code&gt;idle&lt;/code&gt; at the database are leak candidates.&lt;/li&gt;
&lt;li&gt;Most leaks live on error and early-return paths, so they drain the pool at the error rate and surface during other incidents.&lt;/li&gt;
&lt;li&gt;node-postgres waits forever and Go opens unlimited connections by default. Set explicit limits so exhaustion is visible.&lt;/li&gt;
&lt;li&gt;Fix leaks with structured release on every path. A bigger pool only delays a leak.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Runtime metrics tell you a pool is leaking, but a path only shows up in a leak trace after it has run in production. Tomosu checks the repository and each pull request for that kind of risk before merge: acquisition paths where an error branch can skip the release, holds that stretch across remote calls, and which endpoints share the pool. A PR that adds a &lt;code&gt;pool.connect()&lt;/code&gt; without a &lt;code&gt;finally&lt;/code&gt; shows up in its &lt;a href="https://tomosu.ai/platform#indexes" rel="noopener noreferrer"&gt;Production Reliability Index&lt;/a&gt; at review time instead of during the next dependency outage. &lt;a href="https://tomosu.ai/docs/scan-repo-guide" rel="noopener noreferrer"&gt;Scan a repository with Tomosu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://tomosu.ai/blogs/connection-leak-vs-pool-exhaustion.html" rel="noopener noreferrer"&gt;tomosu.ai&lt;/a&gt;, part of the Production Debugging series.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgres</category>
      <category>node</category>
      <category>go</category>
    </item>
    <item>
      <title>Change Risk Management in Software Engineering</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Thu, 08 Oct 2026 20:00:00 +0000</pubDate>
      <link>https://dev.to/parsa_m/change-risk-management-in-software-engineering-3ff5</link>
      <guid>https://dev.to/parsa_m/change-risk-management-in-software-engineering-3ff5</guid>
      <description>&lt;p&gt;Software changes are unavoidable.&lt;/p&gt;

&lt;p&gt;The goal of change risk management is not to stop changes.&lt;/p&gt;

&lt;p&gt;It is to understand the potential impact of a change and apply the right controls before and during deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is change risk management?
&lt;/h2&gt;

&lt;p&gt;Change risk management is the process of identifying, evaluating, and managing the potential consequences of a software change.&lt;/p&gt;

&lt;p&gt;That can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code changes&lt;/li&gt;
&lt;li&gt;Configuration changes&lt;/li&gt;
&lt;li&gt;Database changes&lt;/li&gt;
&lt;li&gt;Infrastructure changes&lt;/li&gt;
&lt;li&gt;Dependency upgrades&lt;/li&gt;
&lt;li&gt;API changes&lt;/li&gt;
&lt;li&gt;Deployment changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start before deployment
&lt;/h2&gt;

&lt;p&gt;The earlier you identify uncertainty, the more options you have.&lt;/p&gt;

&lt;p&gt;Before merge or deployment, look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change scope&lt;/li&gt;
&lt;li&gt;Dependencies&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Production usage&lt;/li&gt;
&lt;li&gt;Incident history&lt;/li&gt;
&lt;li&gt;Runtime signals&lt;/li&gt;
&lt;li&gt;Blast radius&lt;/li&gt;
&lt;li&gt;Rollback conditions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use evidence rather than intuition
&lt;/h2&gt;

&lt;p&gt;A useful assessment should answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do we know?&lt;/p&gt;

&lt;p&gt;What don't we know?&lt;/p&gt;

&lt;p&gt;What could this change affect?&lt;/p&gt;

&lt;p&gt;What controls are available?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This creates a more explicit basis for the deployment decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Match controls to the change
&lt;/h2&gt;

&lt;p&gt;Not every change needs the same process.&lt;/p&gt;

&lt;p&gt;Possible controls include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Additional code review&lt;/li&gt;
&lt;li&gt;More integration testing&lt;/li&gt;
&lt;li&gt;Staged deployment&lt;/li&gt;
&lt;li&gt;Feature flags&lt;/li&gt;
&lt;li&gt;Increased monitoring&lt;/li&gt;
&lt;li&gt;Rollback preparation&lt;/li&gt;
&lt;li&gt;Additional approval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The appropriate control depends on the change and its context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production reliability and change management
&lt;/h2&gt;

&lt;p&gt;Traditional change management often focuses on approvals and process.&lt;/p&gt;

&lt;p&gt;Production reliability adds technical evidence around the change itself.&lt;/p&gt;

&lt;p&gt;That includes how the code relates to dependencies, production behavior, historical incidents, and potential blast radius.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI assisted development changes the volume
&lt;/h2&gt;

&lt;p&gt;AI coding tools can increase the number of changes engineers can produce.&lt;/p&gt;

&lt;p&gt;That can make change risk management more dependent on automated signals and prioritization.&lt;/p&gt;

&lt;p&gt;The goal is not to slow development down.&lt;/p&gt;

&lt;p&gt;It is to make it easier to identify which changes need more attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical framework
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Change
  ↓
Production context
  ↓
Evidence
  ↓
Potential impact
  ↓
Controls
  ↓
Deployment
  ↓
Production feedback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last step matters because deployment creates new evidence that can inform future changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where PRI fits
&lt;/h2&gt;

&lt;p&gt;Tomosu's &lt;strong&gt;Production Reliability Index (PRI)&lt;/strong&gt; provides a summary signal around software changes using multiple reliability indicators.&lt;/p&gt;

&lt;p&gt;It can be used as one input into a broader change management process.&lt;/p&gt;

&lt;p&gt;Run a PRI assessment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tomosu.ai/start" rel="noopener noreferrer"&gt;https://tomosu.ai/start&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The main idea
&lt;/h2&gt;

&lt;p&gt;Good change risk management is not about making every change look risky.&lt;/p&gt;

&lt;p&gt;It is about distinguishing changes that can move through the normal workflow from changes where additional evidence or controls are useful.&lt;/p&gt;

&lt;p&gt;The better the context, the better the decision.&lt;/p&gt;

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

</description>
      <category>softwareengineering</category>
      <category>devops</category>
      <category>sre</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Prioritize Risky Pull Requests</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Wed, 07 Oct 2026 20:00:00 +0000</pubDate>
      <link>https://dev.to/parsa_m/how-to-prioritize-risky-pull-requests-77p</link>
      <guid>https://dev.to/parsa_m/how-to-prioritize-risky-pull-requests-77p</guid>
      <description>&lt;p&gt;When a team has dozens of pull requests, not every change can receive the same amount of attention.&lt;/p&gt;

&lt;p&gt;The useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where will additional review, testing, or investigation provide the most value?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Prioritization should start with potential production impact, not simply the number of changed lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with production impact
&lt;/h2&gt;

&lt;p&gt;Ask what the change could affect.&lt;/p&gt;

&lt;p&gt;A change to a shared payment component deserves different attention from a change to an isolated internal tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look at change scope
&lt;/h2&gt;

&lt;p&gt;Understand which files, services, APIs, databases, and infrastructure are involved.&lt;/p&gt;

&lt;p&gt;Then look for indirect effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check dependencies
&lt;/h2&gt;

&lt;p&gt;A small change can affect many services when it sits low in a dependency graph.&lt;/p&gt;

&lt;p&gt;Dependency reach is therefore an important prioritization signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check production usage
&lt;/h2&gt;

&lt;p&gt;How heavily is the affected component used?&lt;/p&gt;

&lt;p&gt;How important are the workflows that depend on it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Check testing evidence
&lt;/h2&gt;

&lt;p&gt;Limited testing increases uncertainty.&lt;/p&gt;

&lt;p&gt;Strong evidence can reduce the amount of additional investigation required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check incident history
&lt;/h2&gt;

&lt;p&gt;Previous incidents and rollbacks provide context about areas that may deserve closer attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look at runtime behavior
&lt;/h2&gt;

&lt;p&gt;Current production signals can help identify changes touching areas that are already behaving abnormally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consider recent activity
&lt;/h2&gt;

&lt;p&gt;Frequent changes, recent regressions, or rapidly evolving components can add context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assess blast radius
&lt;/h2&gt;

&lt;p&gt;Think about the systems, users, workflows, and dependencies that could be affected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consider deployment conditions
&lt;/h2&gt;

&lt;p&gt;A change that can be safely rolled back or gradually deployed may be easier to manage than one with the same potential impact but no practical rollback path.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical prioritization model
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Potential impact
      +
Evidence
      +
Uncertainty
      +
Deployment control
      ↓
Attention required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a prioritization model, not a universal formula.&lt;/p&gt;

&lt;h2&gt;
  
  
  Risky does not automatically mean blocked
&lt;/h2&gt;

&lt;p&gt;A change can deserve additional attention without being rejected.&lt;/p&gt;

&lt;p&gt;Possible actions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Additional review&lt;/li&gt;
&lt;li&gt;More integration testing&lt;/li&gt;
&lt;li&gt;Staged deployment&lt;/li&gt;
&lt;li&gt;Feature flags&lt;/li&gt;
&lt;li&gt;Additional monitoring&lt;/li&gt;
&lt;li&gt;Rollback preparation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where PRI fits
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Production Reliability Index (PRI)&lt;/strong&gt; can summarize multiple signals so teams can focus investigation where the evidence suggests more attention may be useful.&lt;/p&gt;

&lt;p&gt;Run a PRI assessment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tomosu.ai/start" rel="noopener noreferrer"&gt;https://tomosu.ai/start&lt;/a&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>softwareengineering</category>
      <category>devops</category>
      <category>sre</category>
    </item>
    <item>
      <title>How to Identify High Risk Pull Requests</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Tue, 06 Oct 2026 20:00:00 +0000</pubDate>
      <link>https://dev.to/parsa_m/how-to-identify-high-risk-pull-requests-1c9e</link>
      <guid>https://dev.to/parsa_m/how-to-identify-high-risk-pull-requests-1c9e</guid>
      <description>&lt;p&gt;Not every pull request deserves the same level of scrutiny.&lt;/p&gt;

&lt;p&gt;A useful review process should help engineers find the changes where additional investigation is most valuable.&lt;/p&gt;

&lt;p&gt;A high risk pull request is not simply a large pull request.&lt;/p&gt;

&lt;p&gt;It is a change with characteristics that suggest greater potential production impact or uncertainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with change scope
&lt;/h2&gt;

&lt;p&gt;Look at what the PR actually changes.&lt;/p&gt;

&lt;p&gt;Pay attention to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shared components&lt;/li&gt;
&lt;li&gt;Critical services&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Infrastructure&lt;/li&gt;
&lt;li&gt;Customer facing behavior&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Look at dependencies
&lt;/h2&gt;

&lt;p&gt;A change can become more important when many systems depend on the affected component.&lt;/p&gt;

&lt;p&gt;Dependency reach is often more informative than diff size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check production usage
&lt;/h2&gt;

&lt;p&gt;Ask how the changed code is used in production.&lt;/p&gt;

&lt;p&gt;A rarely used internal function and a heavily used payment path should not be treated as equivalent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check testing
&lt;/h2&gt;

&lt;p&gt;Look at the quality and coverage of the evidence.&lt;/p&gt;

&lt;p&gt;A change with limited testing around an important production path may deserve more attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look at recent activity
&lt;/h2&gt;

&lt;p&gt;Frequent changes can indicate an area that is evolving quickly.&lt;/p&gt;

&lt;p&gt;Combine this with incident history and current change scope rather than using churn alone as a risk rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check runtime signals
&lt;/h2&gt;

&lt;p&gt;Production behavior can provide additional context.&lt;/p&gt;

&lt;p&gt;Look for unusual errors, latency changes, dependency failures, or other anomalies in the affected area.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check incident history
&lt;/h2&gt;

&lt;p&gt;Previous incidents and rollbacks can help identify areas where a change deserves closer investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assess blast radius
&lt;/h2&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If this change behaves unexpectedly, what could it affect?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think about services, users, workflows, dependencies, and business functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consider rollback
&lt;/h2&gt;

&lt;p&gt;A change that can be quickly rolled back is operationally different from one that is difficult to reverse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Large PR does not automatically mean high risk
&lt;/h2&gt;

&lt;p&gt;Diff size is only one signal.&lt;/p&gt;

&lt;p&gt;A large isolated change may have limited impact.&lt;/p&gt;

&lt;p&gt;A tiny change to a shared critical component may have much greater consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where PRI fits
&lt;/h2&gt;

&lt;p&gt;Tomosu's &lt;strong&gt;Production Reliability Index (PRI)&lt;/strong&gt; combines multiple signals around a change to help engineers identify where additional attention may be useful.&lt;/p&gt;

&lt;p&gt;Run a PRI assessment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tomosu.ai/start" rel="noopener noreferrer"&gt;https://tomosu.ai/start&lt;/a&gt;&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>github</category>
      <category>softwareengineering</category>
      <category>sre</category>
    </item>
    <item>
      <title>How to Assess the Blast Radius of a Code Change</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Mon, 05 Oct 2026 20:00:00 +0000</pubDate>
      <link>https://dev.to/parsa_m/how-to-assess-the-blast-radius-of-a-code-change-342l</link>
      <guid>https://dev.to/parsa_m/how-to-assess-the-blast-radius-of-a-code-change-342l</guid>
      <description>&lt;p&gt;A code change can be small in lines of code and large in production impact.&lt;/p&gt;

&lt;p&gt;That is the basic idea behind blast radius.&lt;/p&gt;

&lt;p&gt;The blast radius of a change is the set of systems, components, users, or business functions that could be affected if the change behaves unexpectedly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the changed component
&lt;/h2&gt;

&lt;p&gt;Identify exactly what the pull request modifies.&lt;/p&gt;

&lt;p&gt;Then ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is it shared?&lt;/li&gt;
&lt;li&gt;Is it customer facing?&lt;/li&gt;
&lt;li&gt;Is it part of a critical workflow?&lt;/li&gt;
&lt;li&gt;Does it interact with a database?&lt;/li&gt;
&lt;li&gt;Does it handle authentication?&lt;/li&gt;
&lt;li&gt;Does it control infrastructure?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Map direct dependencies
&lt;/h2&gt;

&lt;p&gt;Find the components that directly depend on the changed code.&lt;/p&gt;

&lt;p&gt;Then look one level further.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Changed component
      ↓
Direct dependencies
      ↓
Dependent services
      ↓
Customer workflows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to understand propagation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look at indirect impact
&lt;/h2&gt;

&lt;p&gt;Some changes affect components that don't appear in the pull request.&lt;/p&gt;

&lt;p&gt;For example, changing a shared API contract can affect multiple services even when those services are untouched.&lt;/p&gt;

&lt;p&gt;That is why dependency mapping matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check production usage
&lt;/h2&gt;

&lt;p&gt;Not every dependency has equal importance.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How heavily is it used?&lt;/li&gt;
&lt;li&gt;Which customers depend on it?&lt;/li&gt;
&lt;li&gt;Which workflows rely on it?&lt;/li&gt;
&lt;li&gt;Is it part of a critical path?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A shared component with low production usage may have a smaller practical blast radius than a heavily used component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look at history
&lt;/h2&gt;

&lt;p&gt;Previous incidents can reveal where changes have caused problems before.&lt;/p&gt;

&lt;p&gt;Also look at recent changes and regressions in the same area.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consider the deployment
&lt;/h2&gt;

&lt;p&gt;Blast radius also depends on how a change is deployed.&lt;/p&gt;

&lt;p&gt;A gradual rollout, feature flag, or quick rollback can limit the impact of a failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diff size is not blast radius
&lt;/h2&gt;

&lt;p&gt;A useful rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lines changed ≠ production impact&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A five line change to a shared authentication library can matter more than a 500 line change to an isolated internal tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where PRI fits
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Production Reliability Index (PRI)&lt;/strong&gt; can use change context, dependencies, production signals, and other reliability evidence to help surface changes that deserve additional attention.&lt;/p&gt;

&lt;p&gt;Run a PRI assessment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tomosu.ai/start" rel="noopener noreferrer"&gt;https://tomosu.ai/start&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>devops</category>
      <category>softwareengineering</category>
      <category>sre</category>
    </item>
    <item>
      <title>Hacktoberfest tip before you submit: break one function on purpose (return []) and run your tests. If the suite still passes, those tests check nothing. Fix them or delete them, a fake green is worse than no test. #hacktoberfest</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Mon, 05 Oct 2026 12:17:44 +0000</pubDate>
      <link>https://dev.to/parsa_m/hacktoberfest-tip-before-you-submit-break-one-function-on-purpose-return-and-run-your-tests-8j3</link>
      <guid>https://dev.to/parsa_m/hacktoberfest-tip-before-you-submit-break-one-function-on-purpose-return-and-run-your-tests-8j3</guid>
      <description></description>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>programming</category>
      <category>testing</category>
    </item>
    <item>
      <title>Agent wrote the code and the tests? Break one function on purpose. If tests pass, they're fake.</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Mon, 05 Oct 2026 10:23:06 +0000</pubDate>
      <link>https://dev.to/parsa_m/-3b1p</link>
      <guid>https://dev.to/parsa_m/-3b1p</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/parsa_m/your-hacktoberfest-tests-are-green-that-doesnt-mean-they-ran-4jn0" class="crayons-story__hidden-navigation-link"&gt;Your Hacktoberfest tests are green. That doesn't mean they ran.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/parsa_m/your-hacktoberfest-tests-are-green-that-doesnt-mean-they-ran-4jn0" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Hacktoberfest: Contribution Chronicles&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/parsa_m" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F4123367%2F2199da8a-7ce0-4062-b7d9-0a75f65bf20d.png" alt="parsa_m profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/parsa_m" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Parsa Mohammadi
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Parsa Mohammadi
                
                
              
              &lt;div id="story-author-preview-content-4800730" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/parsa_m" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F4123367%2F2199da8a-7ce0-4062-b7d9-0a75f65bf20d.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Parsa Mohammadi&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/parsa_m/your-hacktoberfest-tests-are-green-that-doesnt-mean-they-ran-4jn0" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 5&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/parsa_m/your-hacktoberfest-tests-are-green-that-doesnt-mean-they-ran-4jn0" id="article-link-4800730"&gt;
          Your Hacktoberfest tests are green. That doesn't mean they ran.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hacktoberfest"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hacktoberfest&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/testing"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;testing&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/parsa_m/your-hacktoberfest-tests-are-green-that-doesnt-mean-they-ran-4jn0" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/parsa_m/your-hacktoberfest-tests-are-green-that-doesnt-mean-they-ran-4jn0#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Your Hacktoberfest tests are green. That doesn't mean they ran.</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Mon, 05 Oct 2026 09:35:59 +0000</pubDate>
      <link>https://dev.to/parsa_m/your-hacktoberfest-tests-are-green-that-doesnt-mean-they-ran-4jn0</link>
      <guid>https://dev.to/parsa_m/your-hacktoberfest-tests-are-green-that-doesnt-mean-they-ran-4jn0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; If an agent wrote your code and your tests, a green build means the agent is satisfied. Whether the project works is a separate question. Break one function on purpose before you submit. If the suite still passes, those tests aren't checking anything. Checklist and a free scan at the end.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More and more Hacktoberfest projects are built with coding agents. That's fine. The risky moment is the last hour, when the agent says "all tests passing ✅" and you believe it because you're tired and the deadline is tonight.&lt;/p&gt;

&lt;p&gt;Coding agents are generally optimized to satisfy the checks they're given. If the tests are weak, an agent can produce a green build without proving much about the implementation. Sometimes it gets there by making the code work. Sometimes it gets there by making the tests stop checking anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a fake green looks like
&lt;/h2&gt;

&lt;p&gt;None of these fail. All of them look fine at a glance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The assertion that can't fail:&lt;/strong&gt;&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_parse_transactions&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="nf"&gt;parse_transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAMPLE_SMS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# it never returns None, so this passes forever
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The test that quietly left:&lt;/strong&gt;&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="nd"&gt;@pytest.mark.skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flaky, fix later&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_handles_empty_input&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The mock that answers its own question:&lt;/strong&gt;&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="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app.model.classify&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;groceries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_classifies_groceries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_classify&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;categorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grocery order #4412&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;groceries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# the real model never ran
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The try/except that eats the failure:&lt;/strong&gt;&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_model_loads&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;load_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemma-3-4b-q4.gguf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The one check that catches most of it
&lt;/h2&gt;

&lt;p&gt;Break your own code on purpose:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="c1"&gt;# temporary sabotage
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the suite. If it still passes, those tests weren't testing that function. Revert, then fix the tests or delete them. A deleted fake test is more honest than a green one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the pre-submit list
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Put agent work through pull requests&lt;/strong&gt;, even solo. You get a diff to read, and your commit history shows the project was built inside the challenge window, which is a rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search for secrets&lt;/strong&gt; before the repo goes public: &lt;code&gt;sk-&lt;/code&gt;, &lt;code&gt;api_key&lt;/code&gt;, &lt;code&gt;token&lt;/code&gt;, and every service name you used. If a key was ever committed, rotate it. Deleting the line doesn't remove it from git history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clone fresh and follow your own README.&lt;/strong&gt; Judges will. The usual misses are a model file nothing downloads, an undocumented env var, and a globally installed dependency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write down the model&lt;/strong&gt;: name, size, quantization, and the hardware you tested on. The prompt asks why open-source AI matters for your project, and "runs on a laptop" is a claim readers will check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note post-deadline commits in your README.&lt;/strong&gt; The challenge FAQ says skipping this can get an entry disqualified.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credit borrowed code.&lt;/strong&gt; If your agent pulled a chunk of someone else's project in, find it and credit it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A second check before you submit, free
&lt;/h2&gt;

&lt;p&gt;If you'd rather not do all of this by eye at 11pm, &lt;a href="https://tomosu.ai" rel="noopener noreferrer"&gt;Tomosu AI&lt;/a&gt; can scan the project for you. There are two free ways in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small repo (under 50 files): scan it online.&lt;/strong&gt; Paste your GitHub repo URL at &lt;a href="https://tomosu.ai/start" rel="noopener noreferrer"&gt;tomosu.ai/start&lt;/a&gt;. Public repos scan without a login, and the scan is read-only. Most one-week hackathon projects fit under the limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bigger repo: use the free editor extension.&lt;/strong&gt; Install it in VS Code, Cursor, or Antigravity and scan your workspace from the editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=tomosuai.prod-free-tomosu-ai-vscode-plugin" rel="noopener noreferrer"&gt;VS Code Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://open-vsx.org/extension/tomosuai/prod-free-tomosu-ai-vscode-plugin" rel="noopener noreferrer"&gt;Open VSX&lt;/a&gt; (for Cursor, Antigravity, and other VS Code forks)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tomosu.ai/docs/vscode-installation" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way you get a Production Reliability Index (PRI) score for the project. Run it once before your final commit, fix what it flags, and run it again.&lt;/p&gt;

&lt;p&gt;A screenshot of the first scan, the second scan, and what you changed in between makes a good "show your work" section. Judges weight the write-up most heavily, and a real before/after tells them more about your process than a feature list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Over to you
&lt;/h2&gt;

&lt;p&gt;What's the sneakiest thing an agent has done to get your tests green? Drop it in the comments. The best ones go into a follow-up post, with credit.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>hacktoberfest</category>
      <category>ai</category>
      <category>testing</category>
    </item>
    <item>
      <title>Pre Merge Reliability Analysis</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Sun, 04 Oct 2026 20:00:00 +0000</pubDate>
      <link>https://dev.to/parsa_m/pre-merge-reliability-analysis-3df</link>
      <guid>https://dev.to/parsa_m/pre-merge-reliability-analysis-3df</guid>
      <description>&lt;p&gt;Most code review happens before merge.&lt;/p&gt;

&lt;p&gt;That makes the pull request a useful place to assess more than implementation quality.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;pre merge reliability analysis&lt;/strong&gt; looks at the proposed change together with the production context around it.&lt;/p&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Understand the potential production impact before the change becomes part of the next release.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Start with change scope
&lt;/h2&gt;

&lt;p&gt;Identify what the pull request changes.&lt;/p&gt;

&lt;p&gt;Look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files&lt;/li&gt;
&lt;li&gt;Services&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Infrastructure&lt;/li&gt;
&lt;li&gt;Shared components&lt;/li&gt;
&lt;li&gt;Customer facing behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then ask what is indirectly affected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Map dependencies
&lt;/h2&gt;

&lt;p&gt;A change can propagate through dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PR
 ↓
Changed component
 ↓
Dependencies
 ↓
Production services
 ↓
Users and workflows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is often more useful than looking at the number of changed lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review testing evidence
&lt;/h2&gt;

&lt;p&gt;Ask whether the important behavior is actually verified.&lt;/p&gt;

&lt;p&gt;Consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests&lt;/li&gt;
&lt;li&gt;Integration tests&lt;/li&gt;
&lt;li&gt;End to end tests&lt;/li&gt;
&lt;li&gt;Regression tests&lt;/li&gt;
&lt;li&gt;Failure scenarios&lt;/li&gt;
&lt;li&gt;Dependency behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A green test suite is evidence.&lt;/p&gt;

&lt;p&gt;It is not proof that every production condition has been covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add production context
&lt;/h2&gt;

&lt;p&gt;Look at how the affected components behave in production.&lt;/p&gt;

&lt;p&gt;Consider traffic, customer usage, runtime anomalies, and operational constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check history
&lt;/h2&gt;

&lt;p&gt;Previous incidents, rollbacks, and repeated regressions can provide useful context.&lt;/p&gt;

&lt;p&gt;The goal is not to automatically reject changes in areas with a history of problems.&lt;/p&gt;

&lt;p&gt;The goal is to make that history visible during review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consider deployment conditions
&lt;/h2&gt;

&lt;p&gt;Ask whether the change can be safely deployed and reversed.&lt;/p&gt;

&lt;p&gt;A change with a large potential impact may have manageable operational risk if it can be deployed gradually and rolled back quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should block a merge?
&lt;/h2&gt;

&lt;p&gt;There is no universal threshold.&lt;/p&gt;

&lt;p&gt;Different teams have different systems, deployment processes, and tolerance for uncertainty.&lt;/p&gt;

&lt;p&gt;The useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Does the available evidence justify merging this change under the current deployment conditions?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where PRI fits
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Production Reliability Index (PRI)&lt;/strong&gt; can provide a summary signal around the change while preserving the underlying findings that explain it.&lt;/p&gt;

&lt;p&gt;That makes it useful as another input into the merge decision rather than a replacement for review.&lt;/p&gt;

&lt;p&gt;Run a PRI assessment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tomosu.ai/start" rel="noopener noreferrer"&gt;https://tomosu.ai/start&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Measure Software Change Reliability</title>
      <dc:creator>Parsa Mohammadi</dc:creator>
      <pubDate>Thu, 01 Oct 2026 20:00:00 +0000</pubDate>
      <link>https://dev.to/parsa_m/how-to-measure-software-change-reliability-227h</link>
      <guid>https://dev.to/parsa_m/how-to-measure-software-change-reliability-227h</guid>
      <description>&lt;p&gt;Teams have plenty of metrics for services.&lt;/p&gt;

&lt;p&gt;They measure availability, latency, error rates, deployment frequency, and incident recovery.&lt;/p&gt;

&lt;p&gt;But there is another useful question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How reliable is this particular software change?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a change level question rather than a service level question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service reliability vs change reliability
&lt;/h2&gt;

&lt;p&gt;Service level metrics describe how a system behaves over time.&lt;/p&gt;

&lt;p&gt;Change reliability asks how a specific modification is likely to affect that system.&lt;/p&gt;

&lt;p&gt;For example, a service may have excellent availability while a particular change still has a large potential blast radius.&lt;/p&gt;

&lt;p&gt;Both views are useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  What signals can you measure?
&lt;/h2&gt;

&lt;p&gt;There is no universally accepted formula for software change reliability.&lt;/p&gt;

&lt;p&gt;Instead, start with useful evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change scope
&lt;/h3&gt;

&lt;p&gt;What components, files, APIs, databases, and infrastructure are affected?&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;How many systems depend on the changed component?&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;What evidence exists that the important behavior works?&lt;/p&gt;

&lt;h3&gt;
  
  
  Production behavior
&lt;/h3&gt;

&lt;p&gt;What do runtime signals tell you about the affected area?&lt;/p&gt;

&lt;h3&gt;
  
  
  Code volatility
&lt;/h3&gt;

&lt;p&gt;How frequently does this part of the system change?&lt;/p&gt;

&lt;h3&gt;
  
  
  Incident history
&lt;/h3&gt;

&lt;p&gt;Have similar changes caused problems before?&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment outcomes
&lt;/h3&gt;

&lt;p&gt;What happened to similar changes after deployment?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why one metric is difficult
&lt;/h2&gt;

&lt;p&gt;A single number can hide important context.&lt;/p&gt;

&lt;p&gt;Consider two changes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Change A&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small diff&lt;/li&gt;
&lt;li&gt;Shared library&lt;/li&gt;
&lt;li&gt;Limited integration testing&lt;/li&gt;
&lt;li&gt;Recent incident history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Change B&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large diff&lt;/li&gt;
&lt;li&gt;Isolated internal service&lt;/li&gt;
&lt;li&gt;Strong integration coverage&lt;/li&gt;
&lt;li&gt;Easy rollback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second diff is larger.&lt;/p&gt;

&lt;p&gt;That does not automatically make it less reliable.&lt;/p&gt;

&lt;p&gt;The point of measurement is to bring the relevant evidence together.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes a useful reliability measure?
&lt;/h2&gt;

&lt;p&gt;A useful measurement should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relevant to the change&lt;/li&gt;
&lt;li&gt;Traceable to evidence&lt;/li&gt;
&lt;li&gt;Actionable&lt;/li&gt;
&lt;li&gt;Comparable over time&lt;/li&gt;
&lt;li&gt;Clear about uncertainty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A score is useful when engineers can understand why it looks the way it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where PRI fits
&lt;/h2&gt;

&lt;p&gt;Tomosu's &lt;strong&gt;Production Reliability Index (PRI)&lt;/strong&gt; provides a summary signal across multiple areas of reliability.&lt;/p&gt;

&lt;p&gt;The signal can help teams identify which changes deserve closer investigation while keeping the underlying evidence visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main idea
&lt;/h2&gt;

&lt;p&gt;Measuring software change reliability is not about finding one perfect formula.&lt;/p&gt;

&lt;p&gt;It is about consistently combining the signals that matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Change + dependencies + testing + production behavior + history + deployment outcomes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That gives teams a way to discuss reliability at the level where changes actually happen.&lt;/p&gt;

&lt;p&gt;Run a PRI assessment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tomosu.ai/start" rel="noopener noreferrer"&gt;https://tomosu.ai/start&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>sre</category>
      <category>devops</category>
      <category>codereview</category>
    </item>
  </channel>
</rss>
