CodiotFree estimate
Costs, comparisons & buying guides

How our QA process catches bugs before you see them

Ankush Bajad··8 min read

Good QA is not a final click-through before release; it is a set of layers that catch different failures at different points, plus deliberate edge-case hunting for the things users actually hit: empty states, interruptions, lost networks, denied permissions, and the one device nobody tested. Most bugs that reach users were findable before release. They were missed because testing stopped at "it works" instead of asking "what breaks it." This is our actual process, not a list of platitudes.

Why most bugs users meet were findable

Most bugs that reach a user were not exotic or unpredictable; they were findable, and they slipped through because testing confirmed the happy path and stopped there. The person who builds a feature naturally tests that it does what they intended, which is exactly the blind spot: they do not test what they did not intend, and that is where the defects live.

This is the honest opening because it reframes what QA is for. The job is not to verify that the software does what it is supposed to do when used correctly; developers already check that. The job is to find what it does when used incorrectly, interrupted, starved of network, denied a permission, or run on a device the team never held. Those situations are not rare edge cases from the user's side; they are Tuesday. A phone call arrives mid-task, a form is submitted empty, a connection drops in a lift. Calling these "edge cases" is a developer's framing; to the user they are just how life uses the app. Good QA exists to meet the software in those conditions before the user does, which means thinking adversarially about a thing you want to work.

The layers: what gets tested where

QA is not one activity but several layers, each catching a different class of problem at a different time, and understanding which layer catches what is how you avoid both gaps and wasted effort. A defect caught at the unit layer is cheap; the same defect caught by a user is expensive and public. The table below is the shape of it.

LayerWhat it catchesWhen it runs
Unit and developer checksLogic errors in isolated piecesDuring development, before merge
Functional testingFeatures not doing what they shouldAs features complete
Cross-device and cross-browserEnvironment-specific breakageBefore release, on real devices
RegressionOld bugs returning, new changes breaking old featuresEvery release, ideally automated
Exploratory (the detective pass)The unscripted, unexpected, and interruptedContinuously, especially near release
User acceptance (UAT)Does it actually meet the real needBefore sign-off

The layer that separates a mature process from a checkbox one is the exploratory pass, the detective work. Scripted tests find what you thought to write down; exploratory testing finds what you did not, because a tester is actively trying to break the thing rather than confirm it. No single layer is sufficient. Skip regression and old bugs return; skip exploratory and the surprising failures ship; skip real-device testing and the environment gets you. The layers are not a menu to pick from, they are a stack that each catch what the others cannot.

Hunting edge cases: how we think about breaking things

Edge-case hunting is not random; it is systematic pressure on a small set of categories where software reliably fails, applied deliberately rather than hoped for. The mindset is to stop asking "does this work" and start asking "what would make this fall over," then go do those things on purpose.

The categories are consistent enough to name. Empty states: what does the screen do with no data, no results, a first-run user with nothing yet? Interruptions: a call, a notification, the app backgrounded and restored, the screen locked mid-action. Network loss: the connection drops during a save, comes back, is slow rather than absent, or flaps on and off. Permissions: the user denies location or storage or camera, or grants it then revokes it later. Device fragmentation: small screens, large screens, old OS versions, and manufacturer quirks, which on Android is a genuine testing surface rather than a footnote. Each category is a place to push, not a box to tick, and the value is in actually performing the awkward sequence, denying the permission, killing the network at the wrong moment, rather than assuming it is handled. This is the "detective" part of the work: you are looking for the ways a confident piece of software quietly assumes the world will behave.

Mobile vs web testing: what actually differs

Mobile and web testing share the fundamentals but diverge sharply on environment and interruption, and treating them as the same is how mobile-specific bugs escape. The core discipline, functional checks, regression, exploratory pressure, is common; what changes is the terrain that terrain-specific failures come from.

Web testing concentrates on browser and viewport variety, and on the fact that users can do unexpected things with the back button, refreshes, and multiple tabs. Mobile testing adds a harder environment: real device fragmentation, OS-version differences, permissions models, battery and performance constraints, and above all interruptions that the operating system itself imposes, calls, notifications, and the app being killed in the background to reclaim memory. A media feature that plays perfectly in a browser tab has a whole extra life on a phone, as our Android team wrote about in building a production Android media player, where the interruptions were most of the work. The practical rule is that mobile testing must happen on real devices across a real spread, because the failures are environmental and an emulator hides exactly the differences that cause them.

What "done" means before release

"Done" is not "the tester finished clicking"; it is a set of exit criteria agreed before testing starts, so that release is a decision against a standard rather than a feeling. Without stated criteria, "done" quietly becomes "we ran out of time," which is how known-shaky software ships.

Honest exit criteria are specific to the project's risk, but the shape is consistent: the planned test coverage has actually been executed, not just intended; known defects are either fixed or explicitly accepted with a reason, not silently carried; regression has confirmed that new work did not break old work; and the real-device and edge-case passes have been done, not deferred. The important word is explicit. There is nothing wrong with releasing with a minor known issue if someone accountable decided to, in the open, with a plan to fix it. There is a lot wrong with releasing while pretending the issue is not there. "Done" is a shared, stated bar, and the value of QA is partly in forcing that bar to be named.

What we do not do

The honest counterpart to describing a process is naming its limits, so here is what we do not claim: we do not pretend everything is automated, we do not promise zero bugs, and we do not treat testing as a rubber stamp at the end. Those claims sound impressive and are usually a sign of a process that is thinner than it looks.

Full automation of everything is neither realistic nor desirable; automation is powerful for regression and repetitive checks and useless for the exploratory judgement that finds the surprising failures, so we automate where it pays and test manually where a person trying to break things is the only thing that works. Zero bugs is not a target a truthful team offers, because software of any complexity ships with unknowns; the target is that the bugs which escape are rare, minor, and quickly closed, and that each one teaches the process something. And testing is not a final gate bolted on after development, because a gate at the end can only reject, not improve; the value comes from testing throughout. If you are evaluating a software partner, the QA claims are worth pressing on, which is part of why we wrote about red flags in software proposals and how to evaluate a software development company. A team that tells you the truth about what its testing does and does not cover is telling you something important about the software you will get. See how this fits the rest of delivery on our services page, or talk to us about the standard your project needs.

FAQ

Do you use manual or automated testing?
Both, chosen per project rather than as a doctrine, and honestly weighted toward thorough manual testing where the value is highest. Automation earns its place on stable, repetitive checks like regression suites, where a machine repeating the same steps forever is exactly right. Manual testing earns its place on exploratory work, new features, visual and interaction detail, and the edge cases a script was never written to try, which is where most user-facing bugs actually live. Anyone claiming full automation coverage for everything is either testing something very simple or overselling; the useful answer is to automate what is worth automating and test the rest with a person who is trying to break it.
How is QA staffed on a project?
It depends on the project's size, risk, and release cadence, so the honest answer is that staffing is scoped rather than fixed. A small, low-risk build needs less dedicated QA than a system where a defect has real consequences, and the point in the schedule matters too, since testing effort concentrates around releases. What stays constant is that QA is a distinct responsibility, not something developers do to their own code in spare moments, because the person who wrote a feature is the worst-placed to find its blind spots. We size the QA involvement to the actual risk and pace of the work.
Do you test on real devices?
Yes, on real devices, because emulators and simulators miss a whole class of problems that only appear on physical hardware. Manufacturer differences in codecs, permissions behaviour, screen sizes, performance, and OS customisation are real on Android especially, and an emulator presents an idealised device that does not represent what your users hold. Emulators are useful for fast, early checks and breadth; real devices are where you confirm the things that actually break in the field. A test plan that lives entirely on emulators is a plan with a predictable gap.
What happens when a bug ships anyway?
Bugs still ship sometimes, and the honest measure of a QA process is what happens next, not a pretence that none ever escape. The response is to reproduce it, understand how it got past the layers that should have caught it, fix it, and then close the gap so that class of bug is caught next time, often by adding a regression check. A process that only fixes the symptom will meet the same bug again in a new costume. Treating each escape as feedback on the process, rather than as an isolated defect, is what makes the process better over time instead of just busier.
Start

Got an idea? Let's build it.

Tell us what you're making. We'll reply within two business days with an honest take on scope, timeline, and cost.

Get a free estimate