Static Testing

Static testing is the evaluation of work products — requirements, user stories, designs, API contracts, code, configuration, test plans and test cases — by people and by tools, without running the software under test. That definition is accurate but undersells the practice. The value of static testing is not the absence of execution; it is what that makes possible: you can examine a requirement before any code exists, land on the defect itself rather than a symptom of it, and check qualities such as consistency and maintainability that no running test measures.

This page is the map of the practice: static versus dynamic testing, what reviews and static analysis each find, the main kinds of review, the tool categories and their blind spots, and where static checks sit in a CI pipeline. The review taxonomy and the requirements review each have their own detailed page in this topic.

What static testing is — beyond “not executing the code”

The ISTQB Foundation Level syllabus (CTFL v4.0) devotes its third chapter to static testing and divides it into two families: reviews, in which people examine a work product, and static analysis, in which tools evaluate it against formal rules. Both share the same objectives: to find defects directly in the work product, to evaluate its quality and build confidence in it, and to create a shared understanding between the people who wrote it and the people who will build, test and operate from it.

The distinction that matters most is defect versus failure. A defect is a flaw in a work product — a contradictory sentence, an unhandled null value, a missing error response in a contract. A failure is what a running system does wrong because of a defect. Dynamic testing observes failures, and then someone debugs backwards to the defect. Static testing lands on the defect directly, usually with its exact location. That is why a static finding is typically cheaper to fix: there is nothing to reproduce, nothing to diagnose, and often no code to rewrite yet.

Anything a person can read and understand can be reviewed, including documents that will never run; anything with a formal structure — code, a schema, a model, a configuration file — can also be analysed by a tool.

  • It is not proofreading. Spelling fixes are a by-product; the target is defects that would otherwise become wrong behaviour, rework or risk.
  • It is not a substitute for dynamic testing. It cannot show that the system is fast, that services integrate, or that a user can complete a task.
  • It is not only a developer activity. Testers review requirements and test documents, product owners and designers review each other’s work, and operations engineers review deployment configuration.

Static testing vs dynamic testing

The two are complementary, not alternatives. The differences below build on the comparison in the ISTQB syllabus; most teams need both at every test level.

Static testing lands on the defect itself; dynamic testing sees a failure first and debugs back to the defect. The two are complementary.

Static testing

  1. 1A defect sits in a work productRequirement, design, API contract, code, configuration, test document
  2. 2People review it, or tools analyse itReviews: anything readable. Static analysis: anything with a formal structure
  3. 3Defect found directlyUsually with its exact location; nothing to reproduce or diagnose
When it can start:
As soon as a work product exists, even before any code
Cost of a finding:
Usually lower: location known, less built on top

Dynamic testing

  1. 1Tests run the software
  2. 2A failure is observedWhat the running system does wrong because of a defect
  3. 3DebuggingReproduce, diagnose, trace backwards
  4. 4Defect located
When it can start:
Once something executable exists
Cost of a finding:
Higher: includes reproduction, diagnosis and rework of what was built on the defect
  • What it finds — static: defects, directly in the work product. Dynamic: failures, from which defects are traced by debugging.
  • When it can start — static: as soon as a work product exists, including before any code. Dynamic: once something executable exists.
  • What it can examine — static: executable and non-executable work products (requirements, designs, test plans, contracts). Dynamic: running software only.
  • Quality characteristics — static: those that do not depend on execution, such as maintainability, consistency and conformance to standards. Dynamic: those that do, such as performance efficiency, reliability under load and real integration behaviour.
  • Defects found more easily — static: ambiguous or contradictory requirements, unreachable or duplicated code, undeclared or uninitialised variables, mismatched interface parameters, deviations from coding standards, some security weaknesses, acceptance criteria that no test covers. Dynamic: wrong outputs for particular data, timing and concurrency failures, environment and configuration problems, usability problems.
  • Cost of a finding — static: usually lower, because the location is known and less has been built on top of the defect. Dynamic: higher, because it includes reproduction, diagnosis and rework of what was built on the defect.

What can be statically tested, and what it typically finds

For example, a user story says “the discount applies to orders over 500 SAR”. A reviewer asks whether an order of exactly 500.00 SAR qualifies — a static finding, in one sentence, before any code. If nobody asks, the developer chooses “greater than” or “greater than or equal”, and a boundary-value test weeks later may reveal that the choice differs from what finance expected. The same defect, found in two places, at very different cost. The list below shows what each kind of work product typically hides.

  • Requirements and user stories — ambiguity, contradictions, omissions (error paths, permissions, limits), untestable statements, duplicated or conflicting business rules.
  • Architecture and design — poor modularisation, single points of failure, inefficient data structures or database design, missing error handling between components, unclear ownership of data.
  • API contracts and schemas — inconsistent naming, missing error responses, types that contradict their examples, breaking changes between versions, fields without constraints.
  • Source code — undefined or unused variables, unreachable or duplicated code, excessive complexity, unsafe calls, query strings built from user input, violations of coding standards.
  • Configuration and infrastructure-as-code — secrets committed in plain text, insecure defaults, wrong values for an environment, invalid YAML, a cron schedule that never fires.
  • Test documents — a test plan with no exit criteria, test cases with vague expected results, acceptance criteria without any test, duplicated or obsolete cases.

Try it: AI Requirement Ambiguity Detector Try it: AI API Contract Risk Analyzer

Early defect detection and defect prevention

Defects tend to be introduced early and discovered late. A misunderstanding in a requirement becomes a design decision, then code, then tests written to the same misunderstanding — and every layer built on it has to be reworked when the problem is finally noticed. Static testing shortens that feedback loop: the requirement is questioned the day it is written, the design before it is coded, the change before it is merged. The ISTQB syllabus covers early and frequent stakeholder feedback together with the review process and notes that it can prevent misunderstandings about requirements.

Early detection and defect prevention are related but different. Detection finds this defect sooner; prevention stops the next one from being made. Reviews contribute to prevention in two ways. People learn: a developer whose changes keep drawing “input not validated” comments starts validating input. And findings feed back into the process as checklist items, templates, coding rules and lint rules. A defect type that appears in several reviews is a candidate for an automated check, so that no human has to look for it again.

  • Classify review findings by type — ambiguity, missing error handling, naming, security, test gap. The counts show where defects come from.
  • Fix the template, not only the document: a user-story template with an “error cases” heading removes a whole class of omissions.
  • Bring testers into story refinement. Asking “how would I test this?” is itself a static test.

Reviews: the human side of static testing

A review is a structured examination of a work product by people other than, or in addition to, its author. Reviews range from a colleague reading a pull request to a formal inspection with trained roles, entry criteria and collected metrics. CTFL v4.0 describes four review types along that formality scale: the informal review, the walkthrough, the technical review and the inspection. IEEE 1028-2008, the IEEE standard for software reviews and audits (now listed by IEEE as inactive-reserved), additionally defines management reviews and audits, which assess progress and conformance rather than the technical content of a work product. ISO/IEC 20246 describes a generic framework for work product reviews.

Reviews draw on one generic process — planning, review initiation, individual review, communication and analysis, fixing and reporting — and the more formal the review, the more of it is performed; an informal review follows no defined process. The principal roles are manager, author, moderator (facilitator), scribe (recorder), reviewer and review leader. The page on software review types compares the four types on purpose, formality, leader, roles, documentation and metrics, and walks through the process, the roles and the success factors in depth.

  • Informal review — a quick second look with no defined process.
  • Walkthrough — the author leads participants through the work product to explain it and reach a common understanding.
  • Technical review — qualified peers, led by a moderator, reach decisions on a technical question.
  • Inspection — the most formal type, aimed at finding as many defects as possible, with metrics.

Requirements, design, code and test-document reviews

The review type describes how formal a review is; the work product decides what reviewers look for. The same team might run a walkthrough of a design, an informal review of most code changes and an inspection of the payment rules.

  • Requirements reviews — check ambiguity, completeness, consistency, correctness, testability, feasibility, traceability and non-functional needs, ideally with product, development and testing together. This is where static testing usually pays off most, and it has its own page with a checklist and a worked example.
  • Design reviews — check that the design meets the requirements, including the non-functional ones: how failures propagate between services, where data lives and who owns it, what happens under peak load, how the system will be tested and observed. Ask “what happens when this dependency is slow or unavailable?” of every arrow in the diagram.
  • Code reviews — check correctness, tests, readability, security and consistency with the codebase. Google’s published code review guide sets a pragmatic standard: approve a change once it clearly improves the overall health of the code, even if it is not perfect, and mark optional remarks (for example with “Nit:”) so the author knows what blocks the merge. Let tools enforce formatting and style so that human attention goes to logic, design and tests.
  • Test plan reviews — check scope and what is explicitly out of scope, the risks the plan addresses, the approach per test level, entry and exit criteria, environments and data, responsibilities and schedule. A plan without exit criteria cannot tell anyone when testing is finished.
  • Test case reviews — check that each case traces to a requirement or acceptance criterion, states its preconditions and data, has an observable expected result, and that the set covers negative and boundary cases without duplicates. The anatomy of a good test case is covered in the guide on writing test cases.

Try it: AI Acceptance Criteria Reviewer Try it: AI Test Case Optimizer Try it: AI Duplicate Test Case Detector Try it: AI Test Coverage Analyzer

Static analysis: categories of tools and what they check

Static analysis is the tool-driven half of static testing: a program reads code, a model or a structured document and reports violations of rules. It needs a formal structure — a programming language, a schema, a specification format — and it scales in a way people cannot: a linter checks every line of every change, every time, in seconds.

The names below are well-known examples in each category, not a ranking. The right tool is the one that supports your languages, runs in your pipeline and produces few enough false alarms that the team keeps reading its output.

  • Linters and style checkers — flag suspicious constructs and convention violations: unused variables, unreachable code, shadowed names, a missing await, inconsistent formatting. Examples: ESLint (JavaScript, TypeScript), Pylint and Ruff (Python), Checkstyle (Java), RuboCop (Ruby), ShellCheck (shell scripts), SQLFluff (SQL).
  • Type checkers and compilers — check that values are used consistently with their declared types, catching whole classes of null and type errors before execution. Examples: the TypeScript compiler, mypy for Python, and the compilers of statically typed languages such as Java, C#, Go and Rust.
  • Static application security testing (SAST) — trace data from untrusted sources to dangerous sinks and match known weakness patterns: SQL injection, cross-site scripting, path traversal, hard-coded secrets, weak cryptography. Examples: Semgrep, CodeQL, SonarQube, SpotBugs with security plugins. OWASP publishes guidance on what source-code analysis tools can and cannot do, and NIST’s SAMATE programme maintains test suites for evaluating them.
  • Schema and specification validation — check that structured artefacts are valid against their specification: JSON against a JSON Schema, an OpenAPI document against the OpenAPI specification, XML for well-formedness, YAML and cron syntax. OpenAPI linters such as Spectral add consistency and style rules on top of structural validity.
  • Complexity and maintainability metrics — cyclomatic complexity, function length, duplication and coupling, which point reviewers at code that is hard to test and to change.
  • Dependency, secret and infrastructure scanners — check declared dependencies against vulnerability databases, search commits for credentials, and inspect infrastructure-as-code and container files for insecure settings. Examples: npm audit and pip-audit, OWASP Dependency-Check, gitleaks, hadolint, Checkov.
# Illustrative findings from three kinds of static analysis
src/checkout/discount.ts:41:18  error  "rate" is possibly "undefined".              TS18048 (type checker)
src/checkout/discount.ts:57:3   error  Unreachable code.                              no-unreachable (linter)
src/orders/search.ts:22:9       error  SQL query built by concatenating request
                                       parameter "q"; use a parameterised query.   sql-injection (SAST)
openapi.yaml  paths./orders/{id}.get.responses
                                warn   Operation defines no 4xx response.           (contract linter)

Try it: OpenAPI Validator Try it: JSON Schema Validator Try it: SQL Query Validator Try it: YAML Formatter

What static analysis cannot find

A clean static analysis report means the artefact passed the rules someone wrote. It says nothing about whether the software does what its users need.

Every tool also makes two kinds of error. False positives — warnings about code that is fine — erode trust until people stop reading the report; false negatives — defects the tool misses — create false confidence. A classic result of computability theory is that no analyser can decide every non-trivial behavioural property of arbitrary programs, so every tool trades one error against the other. Treat static analysis as a fast filter in front of reviews and dynamic tests, not as a verdict.

  • Whether the behaviour is right. An analyser can confirm that a function returns a number; it cannot know the number should include VAT.
  • Missing or misunderstood requirements. If the story never mentioned refunds, no tool will report that refunds are absent.
  • Runtime and environment behaviour: response times, memory under real load, timeouts, integration with real services, configuration differences between staging and production.
  • Most concurrency and timing defects, and defects that depend on particular data values reached only at runtime.
  • Usability, and accessibility as experienced with a keyboard or a screen reader. Linters catch some markup problems, such as an image without alternative text, but not whether a page can actually be used.

Static testing in the CI/CD pipeline

Static checks are the cheapest stage of a pipeline, so they run first and on every change.

  • In the editor and in pre-commit hooks — formatters, linters and type checks give feedback before a commit exists.
  • On every pull request — lint, type check, SAST, schema and contract validation and secret scanning, with results shown inline on the diff so the human reviewer sees them.
  • As a quality gate — new violations above an agreed severity block the merge; existing violations in legacy code are recorded as a baseline and paid down, instead of blocking everyone on the first day.
  • Specification checks — validate OpenAPI, JSON Schema and configuration files in the same pipeline as the code that depends on them, so that a broken contract fails the build that introduced it.
# Illustrative stage order (commands are examples, not requirements)
stages:
  static-checks:
    - format-check        # formatter in check mode
    - lint                # e.g. eslint . --max-warnings=0
    - typecheck           # e.g. tsc --noEmit
    - validate-contracts  # OpenAPI and JSON Schema files
    - sast                # security rules on changed code
    - secret-scan
  unit-tests:          { needs: static-checks }
  integration-tests:   { needs: unit-tests }

A practical starting plan

  • Add a formatter and a linter with a small agreed rule set; fail the build only on new violations.
  • Validate every contract, schema and configuration file in the pipeline.
  • Make testers part of story refinement and review every story against a requirements checklist before it enters a sprint.
  • Agree a lightweight code review policy: who reviews, what blocks a merge, how long a change may wait for review.
  • Reserve formal inspections for the few work products where a missed defect is expensive: payment and pricing rules, security-critical code, regulatory requirements, data migrations.

Try it: AI Requirement Gap Analyzer Try it: AI Acceptance Criteria Reviewer

Frequently asked questions

Is static testing just testing without running the code?

That describes the mechanism, not the purpose. Static testing examines work products — requirements, designs, contracts, code, test documents — to find defects directly, evaluate quality and build a shared understanding. Because nothing has to run, it can start before any code exists and can examine documents that will never be executed.

What is the difference between static analysis and a code review?

Static analysis is performed by tools against formal rules; a code review is performed by people using judgement. Tools are fast, consistent and tireless on the patterns they know; people find design problems, wrong business logic, missing tests and unclear intent. Use tools first so that reviewers do not spend attention on formatting and simple mistakes.

Can static testing replace dynamic testing?

No. Static testing finds defects in work products and checks qualities such as maintainability; dynamic testing observes the behaviour of running software, including performance, integration and real data. They find different defects, and a mature process uses both at every test level.

Is an inspection the same as a review?

An inspection is one type of review — the most formal one. CTFL v4.0 describes four types: informal review, walkthrough, technical review and inspection. The inspection follows the complete review process, uses defined roles, aims to find as many defects as possible and collects metrics to improve the process.

Is SAST enough for application security?

No. SAST finds code-level weakness patterns, but it does not see runtime configuration, authentication and authorisation flaws that depend on business rules, or vulnerable deployments. Combine it with design review, dependency scanning, dynamic security testing and, for important systems, penetration testing.

References

Practice exercise

Static test a money-transfer change before it is merged

Your task

You are the tester on the mobile-banking team of a retail bank. Story TRF-118 lets customers send money to saved beneficiaries, and the first pull request is ready: the API contract excerpt and the server code are in the material below. Before anything is merged or run, the team reviews four work products together: the story, the contract, the code and the first test case. Your part is the individual review: record every defect you find and, for each one, decide whether a static analysis tool would most likely report it (name the category: linter, type checker, SAST, or contract and schema validation) or whether only a human reviewer would find it.

User story TRF-118: “As a retail customer, I want to transfer money to a saved beneficiary so that I can pay people without visiting a branch.” Acceptance criteria — AC1: A transfer up to the daily limit is completed immediately. AC2: The daily limit is 10,000 SAR per customer. AC3: The transfer screen is user-friendly. AC4: A transfer over the limit is rejected. AC5: Each account has its own daily limit of 10,000 SAR.

Test case TC-TRF-07, the only one written so far — title: “Transfer within limit”; steps: 1. Log in. 2. Transfer money to a beneficiary; expected result: “The transfer works correctly.” Allow about 45 minutes. Pen and paper are enough; the OpenAPI Validator and the JSON Schema Validator can confirm some of your tool findings.

Material
# openapi.yaml (excerpt)
openapi: 3.0.3
info: { title: Transfers API, version: 1.4.0 }
paths:
  /accounts/{accountId}/transfers:
    post:
      operationId: createTransfer
      summary: Create a transfer to a saved beneficiary
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                beneficiaryId: { type: string }
                amount: { type: number, example: "250.00" }
      responses:
        '201': { description: Transfer created }

// transfers.ts (excerpt, line numbers on the left)
12  export async function createTransfer(req: TransferRequest): Promise<Transfer> {
13    const limit = await getDailyLimit(req.user.id); // number | undefined
14    const sentToday: number = await db.scalar("SELECT SUM(amount) FROM transfers " +
15      "WHERE customer_id = '" + req.query.customer + "'");
16    if (sentToday + req.body.amount < limit) {
17      return saveTransfer(req.body);
18    }
19    throw new LimitExceeded();
20    audit.log('transfer rejected', req.user.id);
21  }
Steps
  1. Review the story: mark every ambiguity, contradiction, omission and untestable statement, and write each one as a question for the product owner.
  2. Review the contract against the story: path parameters, required fields, constraints, examples and error responses. Optionally paste the YAML part into the OpenAPI Validator and compare its report with your list.
  3. Review the code line by line against the story and the contract. For each finding, note the line and whether a linter, a type checker, a SAST tool or only a human reviewer would catch it.
  4. Review the test case: its link to an acceptance criterion, its preconditions and test data, whether its expected result can be observed, and the negative and boundary cases the set is missing. Optionally, derive the boundary amounts with the Boundary Value Analysis tool for a customer who has already sent 6,000 SAR today.
  5. Choose three code findings and describe the failure each one would cause for a customer or for the bank if it reached production.
  6. Finish with three short answers: two risks that no static check of this package can rule out, each with the dynamic test that would; one prevention measure and the pipeline stage where the automatable checks belong; and the review type you would choose for this change, with your reason.
Show the model answer
Model answer

Every item below is a defect: a flaw in a work product, found without running anything. None of them is a failure yet — a failure is what a customer would experience if the defect reached a running system (step 5). The model answer lists 17 findings; your wording can differ, and a few other valid findings are noted at the end.

Story 1 — ambiguity (human review). The limit rule is unclear: does “daily” mean the calendar day, and in which time zone, or the last 24 hours? Does the limit cap each transfer or the total of the day’s transfers? Do rejected or cancelled transfers count?

Story 2 — contradiction (human review). AC2 sets the limit per customer and AC5 per account, so a customer with two accounts could send 10,000 or 20,000 SAR a day depending on which criterion the developer follows. The conflict has already spread: the contract is organised per account (/accounts/{accountId}/transfers), while the code adds up transfers per customer.

Story 3 — untestable statement (human review). “User-friendly” in AC3 has no observable criterion. Ask what it means — for example, the screen shows the amount still available today and shows each error next to the field that caused it — or move it to a usability requirement that can be evaluated.

Story 4 — omissions (human review). There are no error cases: what does the customer see when the limit is exceeded, and what happens with an insufficient balance, an amount of zero or below, more than two decimal places, or a beneficiary that has been deleted?

Contract 1 — structural error (tool: contract and schema validation). The path uses {accountId}, but no path parameter accountId is defined; the OpenAPI specification requires a path parameter for every template expression in the path. The OpenAPI Validator reports it as an error (its other messages on this excerpt are only hints, about info.description and servers).

Contract 2 — missing error responses (a tool only with a team rule, otherwise human review). Only 201 is documented: there is no 4xx response for an amount over the limit, an invalid amount or an unknown beneficiary, so the app cannot know what an error looks like. Structural validation accepts an operation with a single success response; a contract linter rule that requires a 4xx response, like the one in the guide’s illustrative output, would report it.

Contract 3 — missing constraints (human review, or a custom lint rule). The request body is not marked required: true, neither field is listed in required, and amount has no minimum, so an empty body or an amount of 0 or -50 is valid according to the contract. To see it, write the request schema as JSON and validate { "amount": -50 } against it in the JSON Schema Validator: the tool reports the document as valid.

Contract 4 — a type that contradicts its example (tool: a contract linter that checks examples against their schemas). amount is a number, but its example "250.00" is a string, so documentation and mocks built from the example show the wrong type. The OpenAPI Validator checks structure, not examples; to confirm, validate "250.00" against { "type": "number" } in the JSON Schema Validator.

Code 1 — lines 13 and 16 (tool: type checker). getDailyLimit can return undefined, and line 16 compares with it without a check. With strict type checking, the TypeScript compiler reports TS18048: 'limit' is possibly 'undefined' on line 16.

Code 2 — lines 14 and 15 (tool: SAST). The SQL query is built by concatenating a request parameter, which allows SQL injection. The fix is a parameterised query.

Code 3 — line 15 (human review). The customer comes from req.query.customer, a value the sender of the request can change and that the contract does not even define, instead of the authenticated req.user.id. SAST reports the injection on this line, but no tool knows which customer is the right one.

Code 4 — lines 14 and 15 (human review). The query has no date condition, so it adds up every transfer the customer has ever made, not today’s. Only someone who knows that the requirement says “daily” can see it.

Code 5 — line 16 (human review). < rejects a transfer that brings the day’s total to exactly the limit, although AC1 allows transfers “up to the daily limit” and AC4 rejects only transfers over it. Read literally, <= is right; confirm it with the product owner together with the Story 1 questions and record the answer in the story. A boundary value test would expose this later as a failure, at a higher cost.

Code 6 — line 20 (tool: linter, for example the ESLint rule no-unreachable; the TypeScript compiler also reports it when allowUnreachableCode is false). The audit call comes after throw, so it never runs and rejected transfers are never written to the audit log.

Test case 1 — vague expected result (human review). “Works correctly” cannot be observed. State what can be checked: the transfer is created with the entered amount, the balance falls by that amount, and the remaining limit for today is updated.

Test case 2 — no preconditions, test data or traceability (human review). Which customer and account, which configured limit, how much was already sent today, and which amount? The case does not say that it covers AC1, and two testers would run it differently.

Test case 3 — gaps in the set (human review). Only a happy path exists. Missing: a total that reaches exactly 10,000.00 SAR, one step above it (10,000.01 SAR), the over-limit message, a customer with no configured limit, a zero or negative amount, and, once the questions are answered, a second account and transfers around midnight. With the tool: for a customer who has already sent 6,000.00 SAR today, enter amount: number, min=0.01, max=4000 and choose 2-value BVA; 0 and 4000.01 should be rejected, 0.01 and 4000 accepted (the tool also adds one mid-range value). These expected results rest on answers the product owner has not given yet — that the limit itself is allowed and that the smallest amount is 0.01 SAR — so record them as assumptions in the test case.

Tally: five findings are typical tool findings (Contract 1 and 4, Code 1, 2 and 6), two need a rule the team would have to add (Contract 2 and 3), and ten need a person who knows the requirement. That is why static analysis is a fast filter in front of reviews, not a verdict.

Failures (step 5), any three of these: a customer with no configured limit has every transfer rejected, because a < comparison with undefined is always false in JavaScript (Code 1); once the total of all the customer’s past transfers reaches 10,000 SAR, every later transfer is rejected (Code 4); a customer who has sent 6,000 SAR today cannot send 4,000 SAR more, although AC1 allows it (Code 5); by changing the customer parameter, a customer has the limit checked against someone else’s transfers and gets around their own limit (Code 3); someone could read or change data in the database through the injection (Code 2); rejected transfers leave no trace in the audit log (Code 6).

Dynamic testing (step 6), any two of these: two transfers sent at the same moment may both pass the check before either is saved — a reviewer can raise the question, but only a test with parallel requests shows whether the database prevents it; whether saveTransfer really debits the account and reaches the beneficiary, which needs integration and system tests; response time under load for “completed immediately”, which needs a performance test; behaviour around midnight with the production time zone setting, once “daily” is defined, which needs a system test with transfers just before and just after midnight; and whether customers actually find the screen easy to use, which needs usability testing.

Prevention and pipeline (step 6). Fix the template, not only this story: add a “Limits and error cases” heading to the user-story template (the value, whether a total exactly at the limit is allowed, the period and time zone, per customer or per account, the message for each error). Turn the tool findings into checks that run on every pull request, before the unit tests: lint with no-unreachable, strict type checking, SAST, and contract validation with rules for path parameters, 4xx responses and examples. New violations above the agreed severity block the merge as a quality gate.

Review type (step 6). A transfer limit is a payment rule where a missed defect is expensive, so a formal inspection — or at least a technical review — fits better than an informal look at the pull request. Your list from this individual review is the input to the next activity of the review process, communication and analysis.

Other valid findings: “completed immediately” in AC1 is unclear when the beneficiary’s account is at another bank; SUM returns NULL when the customer has no transfers yet, so sentToday is not always the number its type declares, and a type checker will not see it if db.scalar returns an untyped value; nothing in the code excerpt checks that beneficiaryId belongs to the customer’s saved beneficiaries (ask whether saveTransfer does); and the contract excerpt declares no security requirement for an operation that moves money (ask whether the full file defines one).

Your progress on this topic

Part of the QA Roadmap

Tools mentioned in this guide

Detect ambiguous, vague or contradictory statements in requirements and get clarifying questions from Mutqan AI.

Requirement Analysis AI

Find missing requirements — error handling, permissions, edge conditions, non-functional needs — with Mutqan AI.

Requirement Analysis AI

Review acceptance criteria for testability, completeness and ambiguity, with rewritten suggestions from Mutqan AI.

Requirement Analysis AI

Validate OpenAPI 3.x and Swagger 2.0 documents for structural errors and best-practice issues.

Web

Analyze an OpenAPI contract for design risks: inconsistent naming, missing error responses, weak auth, breaking-change hazards, pagination and validation gaps.

Requirement Analysis AI

Validate a JSON document against a JSON Schema (draft-07 / 2019-09 / 2020-12 core keywords).

JSON & Data

Check SQL statements for syntax problems, unbalanced parentheses, unterminated strings and risky patterns.

Database

Improve existing test cases: clearer steps, precise expected results, missing preconditions and better structure, with Mutqan AI.

Test Optimization AI

Compare requirements or a user story against existing test cases to find untested areas, with Mutqan AI.

Test Optimization AI

How Mutqan Soft can help

More guides

More guides