Quality assurance best practices help a software team decide what quality means, where a change could fail, and what evidence is needed before release. A larger test suite alone cannot answer those questions. Teams also need clear requirements, useful test data, investigation time, and a way to learn from customer problems.
This guide covers ten practices for software development and testing teams, including the support teams that report product defects. For reviewing agent conversations and coaching service delivery, use our separate guide to customer support quality assurance.
The ISTQB Foundation Level framework covers testing across the development lifecycle, risk management, defect management, collaboration, and the benefits and risks of automation. The practical examples below are illustrative recommendations, rather than reported results from particular companies.
1. Shift testing and review earlier
Shift-left testing means bringing relevant quality activities earlier in development. Start by reviewing the proposed behavior before implementation: what should happen, who can perform the action, and how will a customer recover if it fails?
For an export feature, discuss permissions, empty results, large datasets, interrupted downloads, and whether the exported fields match the customer’s access. Those questions can expose an ambiguous requirement before anyone writes a regression test.
- Review acceptance criteria with a developer, tester, and product owner.
- Identify dependencies, migration requirements, and failure states while planning.
- Record unresolved questions and assign someone to answer them.
- Choose early checks that address an actual risk, such as validating a permission rule.
Early review complements later integration, accessibility, and production monitoring. It does not make those activities unnecessary. Measure whether requirement gaps are still appearing after implementation, rather than counting review meetings.
2. Prioritize testing by risk
When time is limited, prioritize the failures that would matter most. Consider customer impact and the likelihood of a problem, using evidence such as change complexity, previous incidents, new dependencies, and affected account types.
A permission change deserves different scrutiny from a wording change. A familiar screen can still be high risk if it initiates payments, deletes records, or changes access. Keep severity distinct from popularity: a rare data exposure can matter more than a common cosmetic issue.
| Illustrative change | Risk to investigate | Relevant evidence |
|---|---|---|
| Team permissions | A member sees another team’s records | Allow and deny cases across roles and accounts |
| Import workflow | Retries create duplicates or partial records | Interrupted, repeated, and partially invalid imports |
| Help text | Instructions mislead the customer | Content review and a check of the affected task |
Document which risks were tested and which remain. A risk assessment is a decision aid, not evidence that every failure is covered.
3. Use the test pyramid as a design guide
The practical test pyramid describes automated tests at different levels of granularity. The useful decision is where a behavior can be checked reliably and economically. No universal percentage split suits every application.
Use focused tests for business rules that can be exercised directly. Add integration checks where components must agree, such as a database transaction or an external service contract. Keep end-to-end checks for important journeys whose failures would escape the narrower checks.
For example, an invitation workflow may need direct checks for expiration rules, an integration check that the invitation is stored correctly, and a browser check that a recipient can accept it. Avoid repeating the same detailed rule assertions at every level without a reason.
Playwright’s guidance recommends checking user-visible behavior and keeping tests isolated. For browser tests, favor meaningful outcomes over incidental CSS classes or implementation details. Review automation maintenance and unreliable failures alongside execution speed.
4. Make continuous testing failures actionable
Continuous integration is useful when a failed check tells the team what needs attention. A pipeline that fails unpredictably or produces no usable evidence encourages people to ignore it.
Separate quick feedback on a change from slower checks that require a larger environment. Decide which failures block a merge or release, who investigates them, and how urgent results reach that person. Choose the checks and execution frequency according to the change and system risk.
- Include the tested revision and environment in results.
- Retain useful failure messages, logs, and screenshots when appropriate.
- Track flaky checks separately from confirmed product failures.
- If a check is temporarily quarantined, give it an owner and a review date.
- Verify that a repaired check fails for the regression it is meant to detect.
Retries can help diagnose intermittent behavior, but a passing retry does not explain the first failure. Investigate whether the cause is test isolation, an unstable environment, or a real timing problem.

5. Use behavior-driven development to clarify expectations
Behavior-driven development, or BDD, starts with collaboration around concrete examples. Cucumber’s documentation describes discovery, formulation, and automation as related practices. Writing feature files alone does not establish shared understanding.
Ask business and technical participants to describe ordinary behavior, exceptions, and disputed rules. The following is an illustrative example for an account invitation:
Given an invitation has expired When the recipient tries to accept it Then access is not granted And the recipient is shown how to request a new invitation
Discuss what “expired” means, whether the requester must sign in, and who is allowed to issue another invitation. Resolve those questions before treating the example as an agreed specification.
Automate examples that need ongoing verification. Keep the wording focused on the business behavior; avoid making every click or implementation step part of the shared specification.
6. Reserve time for exploratory testing
Exploratory testing combines learning about the product, designing checks, and investigating the results as the session progresses. It can examine assumptions that a scripted suite does not currently cover.
Give each session a focused charter. For example: “Investigate whether a new customer can recover from interrupted account setup without losing progress or creating a second account.” Explore refreshes, back navigation, expired links, unfamiliar terminology, and different permission states where relevant.
Record the environment, actions tried, observations, defects, and unanswered questions. A session that finds no defect can still provide useful information about what was examined. Avoid reporting it as proof that the whole feature is safe.
Use findings to improve requirements, documentation, or automated coverage when a repeatable risk warrants it. Not every exploratory observation needs a permanent automated test.
7. Turn defects into prevention work
A resolved defect answers what was repaired. Prevention work asks why the problem was introduced, why existing checks missed it, and what would make a recurrence less likely.
Review significant incidents and repeated defect categories with the people who understand the workflow. Examine contributing factors such as ambiguous requirements, an undocumented dependency, unrealistic test data, or a missing rollback path. Avoid forcing every incident into a single convenient “root cause.”
Choose a specific action with an owner and a completion condition. “Improve QA” is too vague; “add a denied-access case to the permission review and verify it before the next permission change” is reviewable.
Use a consistent quality assurance procedure to record findings and actions. Follow up on whether the action changed the relevant risk, rather than assuming an action marked complete prevented all future defects.
8. Manage test data deliberately
Useful test data represents the conditions a feature must handle. A dataset containing only new accounts and perfectly completed records can hide failures affecting older accounts, large histories, missing fields, or restricted roles.
Create reusable datasets for meaningful scenarios. Label their purpose, expected state, ownership, and cleanup rules. Make setup reproducible so another person can investigate the same failure without guessing which records were changed.
- Include boundary cases and valid variations, not only malformed data.
- Keep test accounts and credentials separate from customer accounts.
- Prefer synthetic data when it can represent the scenario adequately.
- Review any proposed use of production data through your established privacy and security process.
- Check whether a data refresh would remove evidence needed for an investigation.
Masking some fields does not automatically make a dataset anonymous or establish compliance. Review remaining identifiers, access, and retention with the responsible team.
9. Test performance against explicit expectations
A performance result needs a workload, environment, and acceptance criteria. “The page is fast” leaves too much undefined. Specify the operation, concurrency, dataset size, duration, and the measures that matter to the customer.
For a reporting feature, evaluate response times and errors under a representative workload, plus resource behavior where relevant. Compare results against a justified baseline. Note differences between the test environment and production so reviewers can assess the limits of the result.
Grafana k6 thresholds let teams define pass/fail criteria for test metrics. Failed thresholds produce a nonzero exit code, which can support automated checks. The example response-time targets in tool documentation are examples, rather than standards for your application.
Run heavier load tests in an authorized environment with safeguards for shared dependencies. A functional test passing under light traffic does not demonstrate acceptable behavior under sustained load.
10. Give cross-functional quality work clear owners
Shared responsibility works when each decision still has an owner. Product owners clarify intended behavior; developers implement and investigate; testers challenge assumptions and design evidence; support supplies customer context; release owners decide whether remaining risk is acceptable.
Bring support into relevant defect reviews. A report should include expected and actual behavior, reproduction steps, environment, impact, and available evidence. Separate an observed fact from a suspected cause.
A screen recording can show a visible sequence that is difficult to describe. Include written reproduction steps too, and remove sensitive information before sharing. A recording supports an investigation; it does not establish the underlying cause by itself.
Keep decision records accessible across teams. Our continuous improvement examples offer ways to turn recurring problems into small, reviewable changes.
A practical release evidence checklist
Use this checklist for a specific change and adapt it to your product’s risk. It is an illustrative starting point, not a certification or universal release standard.
| Question | Evidence to record |
|---|---|
| What behavior is changing? | Agreed acceptance examples and affected users |
| What could fail? | Prioritized risks, dependencies, and relevant past defects |
| What was checked? | Results tied to the revision, environment, and test data |
| What remains uncertain? | Untested scenarios, known defects, and environment limitations |
| Who owns the decision? | Release decision, accepted risks, monitoring, and recovery plan |
Start with the practice that addresses a repeated problem in your current workflow. Review whether it produced useful evidence and better decisions before expanding it across every team.
Frequently asked questions
What is the difference between QA and testing?
Quality assurance includes the processes used to establish confidence in quality. Testing evaluates software and provides evidence about its behavior. Tests are part of the work; requirements review, defect prevention, and release decisions also matter.
Should every test be automated?
No. Automate repeatable checks when the ongoing value justifies setup and maintenance. Human investigation, usability review, and unfamiliar scenarios may need a different approach. Keep an explicit reason for each automated check.
Which quality metric should a team prioritize?
Choose a measure related to the risk you are addressing, such as recurring permission defects or failed imports. Pair it with evidence about customer impact and investigation effort. Test counts and code coverage alone do not establish product quality.