Verification vs Validation
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:
| Verification | Validation | |
|---|---|---|
| Question | Are we building the product right? | Are we building the right product? |
| Checks against | Specifications, designs, standards | Real user needs and business goals |
| Typical methods | Reviews, walkthroughs, inspections, static analysis, unit tests | System tests with realistic use, user acceptance testing, beta testing |
| Who | Developers, testers, reviewers | Testers, users, product owners, customers |
| When | From the very first document onwards | Mostly 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.
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))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:
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")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:
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}")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.
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'")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:
Requirements ...................... Acceptance testing
System design ................. System testing
Architecture design ....... Integration testing
Module design ......... Unit testing
CodingEach 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
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}")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?
Validation checks the product against real user needs, not only against the written specification.
2. A team reviews a design document without running any code. What is this?
Examining a work product without executing it is static testing.
3. Which review type is the most formal, with defined roles and checklists?
Inspections use trained roles, checklists, entry and exit criteria and recorded metrics.
4. All tests pass, but users cannot finish a task in real conditions. What failed?
The product matches its specification but not the users' real needs, which is a validation failure.
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
