<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged software-quality</title>
<link>https://coderlegion.com/tag/software-quality</link>
<description>Powered by Question2Answer</description>
<item>
<title>Why One AI Review Pass Is Not Enough</title>
<link>https://coderlegion.com/24955/why-one-ai-review-pass-is-not-enough</link>
<description>&lt;p&gt;Most of us here are already shipping AI-assisted code, and the review side of that has not caught up. This is a write-up of what changes when you stop treating AI review as a single yes-or-no gate and start treating it as a pipeline.&lt;/p&gt;
&lt;h3 id=&quot;the-volume-problem-nobody-planned-for&quot;&gt;The Volume Problem Nobody Planned For&lt;/h3&gt;
&lt;p&gt;GitHub's Octoverse 2025 numbers put roughly 41% of new code as AI-assisted, and that share keeps climbing. The awkward part is what comes with it: AI-assisted pull requests carry about 1.7 times more issues per PR than human-written ones, logic errors show up roughly 75% more often, and security vulnerabilities appear 1.5 to 2 times more frequently.&lt;/p&gt;
&lt;p&gt;Meanwhile review capacity is flat. Senior engineers already spend 20 to 30 percent of their week reading other people's code. Accelerating the writing phase does not create reviewers, it just moves the bottleneck downstream and makes it worse. PRs pile up, reviewers skim, and defects reach production.&lt;/p&gt;
&lt;h3 id=&quot;why-a-second-and-third-pass-pay-for-themselves&quot;&gt;Why A Second And Third Pass Pay For Themselves&lt;/h3&gt;
&lt;p&gt;A single review pass is like one read of a dense paper. You catch the surface errors and the obvious structural problems, and you miss the things that only appear on closer inspection.&lt;/p&gt;
&lt;p&gt;The measured curve is consistent across teams running both approaches. The first pass catches roughly 60% of the issues the system can detect at all. The second pass adds another 25%, largely because some findings only resolve once cross-file context is available. Three passes reach about 95%, which makes four the practical ceiling for most codebases.&lt;/p&gt;
&lt;p&gt;A workable pipeline has four stages. Planning estimates the scope of the change and groups related files, so a database migration gets different scrutiny than a front-end component. The initial pass runs broad and fast on obvious bugs, style and basic security patterns. The deep pass takes those findings, adds cross-file context, and traces data flow across function boundaries, checks error handling consistency along a call chain, validates transaction scoping and looks for race conditions. It costs 3 to 5 times more compute than the first pass and it catches the bugs that cause incidents.&lt;/p&gt;
&lt;p&gt;The fourth stage is the one people leave out: fix verification. After developers address the earlier findings, re-review only the modified areas to confirm the fixes are correct and did not introduce anything new. That is what prevents the classic case of a fix creating a second bug.&lt;/p&gt;
&lt;h3 id=&quot;a-model-reviewing-itself-retraces-its-own-reasoning&quot;&gt;A Model Reviewing Itself Retraces Its Own Reasoning&lt;/h3&gt;
&lt;p&gt;This is the part with the biggest payoff per unit of effort. When a model reviews code it wrote, it tends to walk back through its original reasoning and confirm its original conclusions. Prompting it to be critical does not fix that, because the problem is structural rather than motivational.&lt;/p&gt;
&lt;p&gt;Models from the same family share training distributions, architectures and optimization objectives, and those shared foundations produce correlated blind spots. Where one model misses an integer overflow, another flags it immediately because its training made it more sensitive to numeric boundaries. Teams running cross-model review report 40 to 60 percent better defect detection than same-model review, with the largest gains on logic errors and edge case handling.&lt;/p&gt;
&lt;p&gt;Running the review in a separate session matters as much as changing the model. The reviewer should see the code and the requirements, not the reasoning that produced the code. That structural separation is what creates real independence.&lt;/p&gt;
&lt;p&gt;There is a fuller writeup of the pipeline stages and the cross-model architecture in &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.autolearningagents.com/ai-code-review/&quot;&gt;our guide to multi-pass AI code review&lt;/a&gt; if you want the details.&lt;/p&gt;
&lt;h3 id=&quot;where-it-still-falls-short&quot;&gt;Where It Still Falls Short&lt;/h3&gt;
&lt;p&gt;Worth being honest about the ceiling. AI review is strong on exhaustive path tracing, so off-by-one errors, null dereferences, resource leaks and concurrency races get caught reliably. It is strong on vulnerability patterns and on consistency enforcement, which humans apply unevenly under deadline pressure.&lt;/p&gt;
&lt;p&gt;It is weak on whether the code should exist. It will confirm a function implements its logic correctly and say nothing about whether that logic fits the business problem. Code that is technically correct and architecturally wrong passes without comment. It is also weak on algorithmic complexity, where a cleanly written quadratic solution often gets approved, and on novel vulnerability classes that are thin in training data.&lt;/p&gt;
&lt;h3 id=&quot;the-takeaway&quot;&gt;The Takeaway&lt;/h3&gt;
&lt;p&gt;If you already run AI review, the cheapest upgrade available is not a better model. It is a second pass, a different model family doing the reviewing, and a fix verification stage at the end.&lt;/p&gt;
&lt;p&gt;Reserve the expensive multi-pass configuration for the code that deserves it: authentication, payments, data access and infrastructure config. Let UI and docs changes ride on a single pass. Cost then scales with risk instead of with repository size.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24955/why-one-ai-review-pass-is-not-enough</guid>
<pubDate>Thu, 20 Aug 2026 03:54:55 +0000</pubDate>
</item>
<item>
<title>QA is not clicking buttons: it is the quality gatekeeper for the whole SDLC</title>
<link>https://coderlegion.com/15338/qa-is-not-clicking-buttons-it-is-the-quality-gatekeeper-for-the-whole-sdlc</link>
<description>&lt;p&gt;QA gets stereotyped into a clicker-of-buttons role: open the app, walk the flow, file a Jira ticket when something looks wrong. That caricature is what junior QA looks like at companies that have not yet figured out what QA is actually for. The role at senior is something else entirely — the single person who owns quality across the full software development lifecycle, from the requirements meeting through post-deployment monitoring. The button-clicker is a subset of the role, not the role.&lt;/p&gt;
&lt;p&gt;I run this posture in claude-code-mcp-qa-automation as the concrete operator surface: 16 Claude Code skills, a Python implementation that pulls Jira, aggregates sprint trends into a 7-table SQLite store, emits deterministic HTML reports, and posts Slack digests. The tool exists because senior QA work is pipeline work, and pipeline work wants reproducible artifacts, not manual checklists.&lt;/p&gt;
&lt;p&gt;Six SDLC stages, one gate per stage. Each gate has the button-clicker version (the failure mode) and the systems version (the senior posture).&lt;/p&gt;
&lt;p&gt;Stage 1 — Requirements review&lt;br&gt;
Button-clicker version. QA shows up after requirements are finalized, reads the spec, writes test cases against it.&lt;/p&gt;
&lt;p&gt;Systems version. QA is in the requirements meeting and asks problem-first questions before the spec is written. Who is the user? What outcome are they after? Does a non-software workaround already solve 70% of this? What is the observable failure condition? This is Eugene Yan's &quot;start with the problem, not the technology&quot; applied at the gate where technology gets chosen.&lt;/p&gt;
&lt;p&gt;The reason QA has to be here is that half the bugs that ship are not implementation bugs — they are requirements bugs. A feature that was built correctly against a vague spec is still a failure to the user. Catching vagueness at Stage 1 is exponentially cheaper than catching it in Stage 6 when the user is asking why the thing does not do what they expected.&lt;/p&gt;
&lt;p&gt;Stage 2 — Design review&lt;br&gt;
Button-clicker version. QA waits until the code is ready to be tested, then asks &quot;how do I test this?&quot;&lt;/p&gt;
&lt;p&gt;Systems version. QA reads the architecture doc or attends the design review. Asks which services are touched, which data flows cross a trust boundary, which new external dependencies are introduced. Names the error budget. Names the rollback plan.&lt;/p&gt;
&lt;p&gt;The question the senior QA is trying to answer at Stage 2 is: &quot;What is the smallest change downstream that breaks this design?&quot; That question is what separates a testable design from one that will collapse the first time production traffic hits a pattern the designers did not consider.&lt;/p&gt;
&lt;p&gt;Design-review QA does not require a separate title. It requires the literacy to read an architecture doc and the authority to flag a risk before implementation starts.&lt;/p&gt;
&lt;p&gt;Stage 3 — Implementation&lt;br&gt;
Button-clicker version. QA is a ticket consumer. Developer writes code, opens a PR, QA picks up the ticket when it hits the test-ready column.&lt;/p&gt;
&lt;p&gt;Systems version. QA has peer-review authority on PRs touching user-facing flows or critical paths. Not final sign-off on code — sign-off on the test coverage, the observability hooks, the feature flag plan, the rollback path. A PR without a flag wrapping a risky change does not pass Stage 3. A PR that adds an endpoint without a structured-logging statement on the failure path does not pass Stage 3.&lt;/p&gt;
&lt;p&gt;This is the stage where &quot;works on my machine&quot; gets caught. The senior QA is reading the PR not for correctness of the logic — that is the developer's job — but for the surface area the logic creates. What can break? How would we know? How would we undo it?&lt;/p&gt;
&lt;p&gt;Stage 4 — Pre-merge&lt;br&gt;
Button-clicker version. CI runs the test suite. If it passes, merge. QA's role is &quot;make sure there is a test.&quot;&lt;/p&gt;
&lt;p&gt;Systems version. QA owns the test pyramid. Unit tests for logic. Integration tests for service boundaries. Contract tests for APIs that other services consume. End-to-end tests only for the three or four flows that literally cannot regress. The shape of the pyramid is the senior QA's artifact, not a byproduct.&lt;/p&gt;
&lt;p&gt;Pre-merge QA also owns the flake-free contract. A flaky test in the suite is a tax on every future PR — developers start ignoring the red, which is how a real failure slips through undetected. Flake investigation is senior QA work; &quot;re-run the failing job&quot; is not.&lt;/p&gt;
&lt;p&gt;Stage 5 — Pre-deploy&lt;br&gt;
Button-clicker version. QA smoke-tests the staging environment by clicking the happy path.&lt;/p&gt;
&lt;p&gt;Systems version. QA owns the staging canary. Before a release ships, the senior QA has run the same load profile staging sees in the first 60 minutes of prod. Has checked that feature flags default to off, or default to on for a small cohort. Has verified the migration rolls back cleanly on a cloned schema.&lt;/p&gt;
&lt;p&gt;This is also the stage where environment teardown and recreation matter. A staging environment that drifted out of date from prod weeks ago is not a pre-deploy gate; it is a rubber stamp. The senior QA rebuilds staging from scratch on a defined cadence, or asks for the pipeline authority to do it themselves. Which connects to the next stage.&lt;/p&gt;
&lt;p&gt;Stage 6 — Post-deploy&lt;br&gt;
Button-clicker version. QA files tickets when users complain.&lt;/p&gt;
&lt;p&gt;Systems version. QA reads CloudWatch (or the equivalent) first, before triaging user reports. Correlation IDs on every request. Structured-logging queries that surface error-rate deltas. X-Ray traces or OpenTelemetry spans that localize the blame span. Alarms written by QA, tuned by QA, routed to the right team by QA.&lt;/p&gt;
&lt;p&gt;Triage theater is the 40-minute meeting where QA says &quot;the user reports the upload is broken,&quot; devs say &quot;we do not see anything in the logs,&quot; and nobody has opened CloudWatch. A senior QA who opened CloudWatch first routes the bug directly to the developer who owns the failing span — with the correlation ID, the timestamp, and the stack trace already attached. The &quot;is this a real issue?&quot; step and the &quot;who owns it?&quot; step are already done.&lt;/p&gt;
&lt;p&gt;Why this shape matches the Claude-Code operator pattern&lt;br&gt;
The six-stage gate shape is structurally identical to the agentic-engineering discipline I run on the code side: spec-first work at Stage 1, eval loops at Stage 2, review authority at Stage 3, test pyramid at Stage 4, canary + rollback at Stage 5, traced observability at Stage 6. Same operator pattern, different surface.&lt;/p&gt;
&lt;p&gt;That is not an accident. The QA-automation repo exists because the same &quot;spec-first, sub-agent orchestration, eval on output&quot; pattern that ships reviewable Claude-Code artifacts is what makes sprint-reporting and alarm-triaging reproducible instead of manual. The role-specific parts of QA (the Jira literacy, the CloudWatch reading, the flag hygiene) plug into a pipeline shape that does not care whether the artifact is a sprint digest, a staging deploy, or a Claude-Code skill file.&lt;/p&gt;
&lt;p&gt;The cultural objection&lt;br&gt;
The objection senior QAs hit when they try to operate this way is always some version of &quot;QA should not have that authority.&quot; Not prod write, not design-meeting presence, not rollback triggering, not peer-review sign-off on PRs.&lt;/p&gt;
&lt;p&gt;The answer is not that QA should have prod write. The answer is that the role requires the authority to do the job it is measured against. If QA is accountable for quality across the SDLC but only has the authority to file tickets, QA is being set up to fail publicly while having no lever to succeed privately. The six gates above are what the authority looks like concretely.&lt;/p&gt;
&lt;p&gt;What this means for a QA career&lt;br&gt;
The stereotype of QA as a dead-end &quot;tester&quot; role is true for the button-clicker version and false for the senior version. The ceiling on button-clicker QA is flat. The ceiling on SDLC-wide quality gatekeeper work is the same as any other senior engineering role that owns a cross-cutting concern.&lt;/p&gt;
&lt;p&gt;Two capabilities decide which version a QA becomes: CS fundamentals literacy (how the stack works, so you can diagnose instead of triage) and pipeline freedom (the authority to create environments, trigger deploys, and write alarms without filing a ticket to somebody else). Both are skills. Neither is inherent to the role. Both compound.&lt;/p&gt;
&lt;p&gt;The next two posts in this series go deeper on each: why CS fundamentals are a QA superpower, and why pipeline freedom is what separates a senior QA from a blocked one.&lt;/p&gt;
&lt;p&gt;Aman Bhandari. Operator of an AI-engineering research lab running Claude Opus as the coaching partner, plus a QA-automation surface shipping against a real sprint workload. Public artifacts: claude-code-agent-skills-framework and claude-code-mcp-qa-automation. github.com/aman-bhandari.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15338/qa-is-not-clicking-buttons-it-is-the-quality-gatekeeper-for-the-whole-sdlc</guid>
<pubDate>Mon, 20 Apr 2026 21:06:37 +0000</pubDate>
</item>
<item>
<title>Surface Tension of Software: why systems hold together</title>
<link>https://coderlegion.com/8261/surface-tension-of-software-why-systems-hold-together</link>
<description>&lt;p&gt;If you press your finger against water, it pushes back. That invisible resistance, surface tension, keeps the liquid whole even when disturbed.&lt;/p&gt;
&lt;p&gt;Good software has something like it. Some systems hold together when you change them; others leak at the slightest touch. The difference lies in integrity — the way a system manages its side effects without losing its shape.&lt;/p&gt;
&lt;p&gt;I've seen codebases that felt strangely calm, where every possible state meant something real and nothing arbitrary could slip in. Others allowed nonsense to exist, and from there, entropy spread quietly like cracks beneath paint.&lt;/p&gt;
&lt;p&gt;Type systems, invariants, and boundaries exist to make meaning explicit. They define where things start and stop — what’s allowed, and what isn’t. Without that structure, logic turns soft; assumptions spread, and the system eventually folds under its own ambiguity.&lt;/p&gt;
&lt;p&gt;Systems stay whole when their structure insists on coherence: clear boundaries, honest interfaces, consistent language.&lt;br&gt;
Each adds its own gravity, and together they make a world that holds. Stability isn’t declared; it emerges from the sum of small, consistent forces.&lt;/p&gt;
&lt;p&gt;Constraint-driven design makes that gravity visible. In software, these laws have names: purity, immutability, idempotence, transparency, composability. They’re the physics that keep a system in orbit.&lt;/p&gt;
&lt;p&gt;Pure functions return the same output for the same input, with no hidden effects. Immutable data can't be changed after creation, only transformed. Idempotent operations produce the same result no matter how many times you apply them. These aren't academic exercises — they're physics that prevent the impossible.&lt;/p&gt;
&lt;p&gt;But let one careless change skip a step and the world tears.&lt;/p&gt;
&lt;p&gt;Consider a UI that fetches user data. Without tension, it leaks:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;struct UserProfile {
    loading: bool,
    error: Option&amp;lt;String&amp;gt;,
    data: Option&amp;lt;User&amp;gt;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What does it mean when loading is false, error is Some, and data is also Some? The type allows nonsense. You write defensive checks everywhere. Every render must guess which combination is real.&lt;/p&gt;
&lt;p&gt;Now give it shape:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;enum UserProfile {
    Loading,
    Failed(String),
    Loaded(User),
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The impossible states vanish. You can't be loading and failed. Pattern matching forces you to handle every valid case, and only those. The type system becomes the membrane — it holds the shape.&lt;/p&gt;
&lt;p&gt;In well-shaped systems, nonsense simply cannot exist because the universe of the program doesn't contain it. You don't defend against the impossible. You design a world where the impossible has no syntax.&lt;/p&gt;
&lt;p&gt;When those laws are clear, surface tension appears on its own — you feel it when refactors stop rippling outward, when a change bends without breaking, when boundaries push back just enough to preserve meaning.&lt;/p&gt;
&lt;p&gt;Good patterns and abstractions behave like membranes. They don't restrain motion; they guide it. They contain side effects the way water's surface holds its ripples — movement without spillage, energy without collapse. Parsing, typing, composition: these are the laws of motion that let a system stay whole when disturbed.&lt;/p&gt;
&lt;p&gt;There's an old engineering instinct that says, &quot;We'll handle it later if it happens.&quot; But in coherent systems, it simply can't happen. You can only move through valid states, and that constraint is what makes motion possible at all.&lt;/p&gt;
&lt;p&gt;But tension has its limits. Too much and water hardens into ice — flawless, unmoving, lifeless. Software can freeze the same way, becoming so rigid it forgets to flow. Balance lives somewhere between order and change, between holding on and letting go.&lt;/p&gt;
&lt;p&gt;The best systems live there, in that delicate balance where structure meets freedom. And perhaps that's where the worlds of hackers and painters quietly meet, each shaping their medium until form and motion become one.&lt;/p&gt;
&lt;p&gt;That precise balance is the true art of code.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Further reading:&lt;/em&gt;&lt;br&gt;
Rich Hickey’s &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.infoq.com/presentations/Simple-Made-Easy/&quot;&gt;&lt;em&gt;Simple Made Easy&lt;/em&gt;&lt;/a&gt; — a classic talk on why&lt;br&gt;
simplicity comes from separation, not convenience.&lt;/p&gt;
</description>
<category>Full-stack Dev</category>
<guid isPermaLink="true">https://coderlegion.com/8261/surface-tension-of-software-why-systems-hold-together</guid>
<pubDate>Sun, 07 Dec 2025 14:07:00 +0000</pubDate>
</item>
</channel>
</rss>