EthicsComputerCourses
Join training +91 90532 10052

Verification vs Validation

Software Testing · Lesson 3 of 30 Beginner 7 min read Updated

A team can build software exactly as the document says and still deliver something users do not want. Testing protects you from both problems: building the product wrongly and building the wrong product. These two kinds of checking are called verification and validation.

In this lesson you will learn the difference, see static and dynamic testing, meet the different kinds of reviews and run a tiny static analysis tool in your browser.

Two questions, two activities

The classic way to remember the difference is with two short questions:

VerificationValidation
QuestionAre we building the product right?Are we building the right product?
Checks againstSpecifications, designs, standardsReal user needs and business goals
Typical methodsReviews, walkthroughs, inspections, static analysis, unit testsSystem tests with realistic use, user acceptance testing, beta testing
WhoDevelopers, testers, reviewersTesters, users, product owners, customers
WhenFrom the very first document onwardsMostly once working software exists

Verification asks, "does this work product match what was specified?" Validation asks, "does this product actually solve the user's problem in their real situation?" You need both. A product that passes verification but fails validation is a well-built answer to the wrong question.

A product that is verified but not valid

The requirement for a pension app in Lucknow said: "An OTP expires 30 seconds after it is sent." The developer coded it carefully, and the tests prove the code matches the requirement. That is successful verification.

PythonRun it yourself
def otp_is_valid(sent_at_seconds, entered_at_seconds, limit=30):
    return entered_at_seconds - sent_at_seconds <= limit

# Verification: does the code match the written requirement?
print("Entered after 10 s:", otp_is_valid(0, 10))
print("Entered after 30 s:", otp_is_valid(0, 30))
print("Entered after 31 s:", otp_is_valid(0, 31))
Output
Entered after 10 s: True
Entered after 30 s: True
Entered after 31 s: False

Then the team ran a small trial with real pensioners. Many of them needed time to read the SMS, switch apps and type six digits on a slow phone. The trial data told a different story:

PythonRun it yourself
def otp_is_valid(sent_at_seconds, entered_at_seconds, limit=30):
    return entered_at_seconds - sent_at_seconds <= limit

# Validation: seconds each trial user took to enter the OTP
trial_times = [22, 41, 35, 58, 19, 64, 47, 28, 90, 38]

succeeded = [t for t in trial_times if otp_is_valid(0, t)]
rate = len(succeeded) / len(trial_times) * 100
print(f"Users who could log in: {len(succeeded)} of {len(trial_times)} ({rate:.0f}%)")
print("Slowest successful user:", max(succeeded), "seconds")
Output
Users who could log in: 3 of 10 (30%)
Slowest successful user: 28 seconds

The code is correct, yet only 3 of 10 trial users could log in. Validation found that the requirement was wrong for these users. The fix is to change the requirement (for example to a few minutes with a resend button), not to fix the code.

Static and dynamic testing

Dynamic testing runs the software and checks its behaviour, as in the examples above. Static testing examines work products without running them: requirements, designs, code, test cases and even user manuals. Static testing can find defects that running the code never would, such as a contradiction between two requirements, a missing error message in the design, or code that is hard to maintain.

Kinds of reviews

Reviews are the human side of static testing. They range from light to formal:

  • Informal review. A colleague looks over your work and gives comments. No fixed process.
  • Walkthrough. The author leads the group through the document or code, explaining it and collecting questions.
  • Technical review. Qualified peers check the work for technical correctness and agree on improvements.
  • Inspection. The most formal type: trained roles (moderator, author, reviewers, scribe), checklists, defined entry and exit criteria, and measured results.

Reviewing requirements is one of the cheapest ways to find defects. Vague words are a warning sign, because nobody can write a test for "fast" or "user-friendly". This short script flags them:

PythonRun it yourself
VAGUE = ["fast", "user-friendly", "easy", "etc", "approximately", "as needed", "quickly"]

requirements = [
    "The search page must load quickly on mobile.",
    "A student can upload a PDF of up to 5 MB.",
    "The receipt shows the date, items, GST etc.",
    "The app must be user-friendly for shopkeepers.",
]

for number, text in enumerate(requirements, start=1):
    found = [word for word in VAGUE if word in text.lower()]
    verdict = "VAGUE: " + ", ".join(found) if found else "testable"
    print(f"R{number}: {verdict}")
Output
R1: VAGUE: quickly
R2: testable
R3: VAGUE: etc
R4: VAGUE: user-friendly

R2 is testable because it states an exact limit. The others need rewriting, for example "the search page shows results within 2 seconds on a 4G connection".

Static analysis tools

Tools can review code automatically. Linters such as ruff (Python) and ESLint (JavaScript) read source code and report likely bugs without running it. Python's built-in ast module lets you see how such tools work: it turns code into a tree you can inspect.

PythonRun it yourself
import ast

source = '''def apply_coupon(total, code):
    try:
        discount = COUPONS[code]
    except:
        discount = 0
    print("debug", discount)
    if total == None:
        return 0
    return total - discount
'''

tree = ast.parse(source)
for node in ast.walk(tree):
    if isinstance(node, ast.ExceptHandler) and node.type is None:
        print(f"Line {node.lineno}: bare 'except:' hides real errors")
    if isinstance(node, ast.Call) and getattr(node.func, "id", "") == "print":
        print(f"Line {node.lineno}: leftover print() call")
    if isinstance(node, ast.Compare):
        for op, right in zip(node.ops, node.comparators):
            if isinstance(op, ast.Eq) and isinstance(right, ast.Constant) and right.value is None:
                print(f"Line {node.lineno}: use 'is None' instead of '== None'")
Output
Line 4: bare 'except:' hides real errors
Line 6: leftover print() call
Line 7: use 'is None' instead of '== None'

The code was never executed, yet three problems were found. That is static testing.

Verification, validation and the V-model

The V-model shows how each development phase on the left is checked by a test level on the right:

Text
Requirements ...................... Acceptance testing
    System design ................. System testing
        Architecture design ....... Integration testing
            Module design ......... Unit testing
                        Coding

Each pair is planned at the same time. Reviews of every document on the left are verification. The acceptance tests at the top right, run with real users, are the strongest form of validation. You will study each level in Test Levels.

Common mistakes

  • Treating the two words as synonyms. In interviews and in test plans, verification means "matches the specification" and validation means "meets the real need".
  • Skipping reviews because "testing will catch it". A wrong requirement passes every test written from it.
  • Validating too late. Showing users a prototype early is far cheaper than discovering a wrong requirement after release.

Key points

  • Verification asks "are we building the product right?"; validation asks "are we building the right product?".
  • Code can pass verification and still fail validation when the requirement itself is wrong.
  • Static testing examines work products without running them; dynamic testing runs the software.
  • Reviews range from informal reviews to walkthroughs, technical reviews and formal inspections.
  • Static analysis tools such as linters find likely bugs in code without executing it.
  • In the V-model each development phase has a matching test level planned at the same time.

Exercise

Add three more words to the VAGUE list (for example "simple", "minimal" and "some") and add two requirements of your own for a school fee payment app: one vague and one testable. Run the review script and check that it labels them correctly.

Show solution
Python
VAGUE = ["fast", "user-friendly", "easy", "etc", "approximately", "as needed",
         "quickly", "simple", "minimal", "some"]

requirements = [
    "Parents can pay fees with some popular payment methods.",
    "A receipt PDF is emailed within 60 seconds of a successful payment.",
]

for number, text in enumerate(requirements, start=1):
    found = [word for word in VAGUE if word in text.lower()]
    verdict = "VAGUE: " + ", ".join(found) if found else "testable"
    print(f"R{number}: {verdict}")
Output
R1: VAGUE: some
R2: testable

The first requirement does not say which payment methods, so nobody can test it. The second gives a measurable limit (60 seconds) and a clear result (a receipt PDF by email).

Quick Quiz

1. Which question does validation answer?

2. A team reviews a design document without running any code. What is this?

3. Which review type is the most formal, with defined roles and checklists?

4. All tests pass, but users cannot finish a task in real conditions. What failed?

Learn Software Testing with a mentor

Free lessons get you started. Our Software Testing training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call