Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

probability-impact.md 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. # Probability and Impact Scale
  2. ## Principle
  3. Risk scoring uses a **probability × impact** matrix (1-9 scale) to prioritize testing efforts. Higher scores (6-9) demand immediate action; lower scores (1-3) require documentation only. This systematic approach ensures testing resources focus on the highest-value risks.
  4. ## Rationale
  5. **The Problem**: Without quantifiable risk assessment, teams over-test low-value scenarios while missing critical risks. Gut feeling leads to inconsistent prioritization and missed edge cases.
  6. **The Solution**: Standardize risk evaluation with a 3×3 matrix (probability: 1-3, impact: 1-3). Multiply to derive risk score (1-9). Automate classification (DOCUMENT, MONITOR, MITIGATE, BLOCK) based on thresholds. This approach surfaces hidden risks early and justifies testing decisions to stakeholders.
  7. **Why This Matters**:
  8. - Consistent risk language across product, engineering, and QA
  9. - Objective prioritization of test scenarios (not politics)
  10. - Automatic gate decisions (score=9 → FAIL until resolved)
  11. - Audit trail for compliance and retrospectives
  12. ## Pattern Examples
  13. ### Example 1: Probability-Impact Matrix Implementation (Automated Classification)
  14. **Context**: Implement a reusable risk scoring system with automatic threshold classification
  15. **Implementation**:
  16. ```typescript
  17. // src/testing/risk-matrix.ts
  18. /**
  19. * Probability levels:
  20. * 1 = Unlikely (standard implementation, low uncertainty)
  21. * 2 = Possible (edge cases or partial unknowns)
  22. * 3 = Likely (known issues, new integrations, high ambiguity)
  23. */
  24. export type Probability = 1 | 2 | 3;
  25. /**
  26. * Impact levels:
  27. * 1 = Minor (cosmetic issues or easy workarounds)
  28. * 2 = Degraded (partial feature loss or manual workaround)
  29. * 3 = Critical (blockers, data/security/regulatory exposure)
  30. */
  31. export type Impact = 1 | 2 | 3;
  32. /**
  33. * Risk score (probability × impact): 1-9
  34. */
  35. export type RiskScore = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
  36. /**
  37. * Action categories based on risk score thresholds
  38. */
  39. export type RiskAction = 'DOCUMENT' | 'MONITOR' | 'MITIGATE' | 'BLOCK';
  40. export type RiskAssessment = {
  41. probability: Probability;
  42. impact: Impact;
  43. score: RiskScore;
  44. action: RiskAction;
  45. reasoning: string;
  46. };
  47. /**
  48. * Calculate risk score: probability × impact
  49. */
  50. export function calculateRiskScore(probability: Probability, impact: Impact): RiskScore {
  51. return (probability * impact) as RiskScore;
  52. }
  53. /**
  54. * Classify risk action based on score thresholds:
  55. * - 1-3: DOCUMENT (awareness only)
  56. * - 4-5: MONITOR (watch closely, plan mitigations)
  57. * - 6-8: MITIGATE (CONCERNS at gate until mitigated)
  58. * - 9: BLOCK (automatic FAIL until resolved or waived)
  59. */
  60. export function classifyRiskAction(score: RiskScore): RiskAction {
  61. if (score >= 9) return 'BLOCK';
  62. if (score >= 6) return 'MITIGATE';
  63. if (score >= 4) return 'MONITOR';
  64. return 'DOCUMENT';
  65. }
  66. /**
  67. * Full risk assessment with automatic classification
  68. */
  69. export function assessRisk(params: { probability: Probability; impact: Impact; reasoning: string }): RiskAssessment {
  70. const { probability, impact, reasoning } = params;
  71. const score = calculateRiskScore(probability, impact);
  72. const action = classifyRiskAction(score);
  73. return { probability, impact, score, action, reasoning };
  74. }
  75. /**
  76. * Generate risk matrix visualization (3x3 grid)
  77. * Returns markdown table with color-coded scores
  78. */
  79. export function generateRiskMatrix(): string {
  80. const matrix: string[][] = [];
  81. const header = ['Impact \\ Probability', 'Unlikely (1)', 'Possible (2)', 'Likely (3)'];
  82. matrix.push(header);
  83. const impactLabels = ['Critical (3)', 'Degraded (2)', 'Minor (1)'];
  84. for (let impact = 3; impact >= 1; impact--) {
  85. const row = [impactLabels[3 - impact]];
  86. for (let probability = 1; probability <= 3; probability++) {
  87. const score = calculateRiskScore(probability as Probability, impact as Impact);
  88. const action = classifyRiskAction(score);
  89. const emoji = action === 'BLOCK' ? '🔴' : action === 'MITIGATE' ? '🟠' : action === 'MONITOR' ? '🟡' : '🟢';
  90. row.push(`${emoji} ${score}`);
  91. }
  92. matrix.push(row);
  93. }
  94. return matrix.map((row) => `| ${row.join(' | ')} |`).join('\n');
  95. }
  96. ```
  97. **Key Points**:
  98. - Type-safe probability/impact (1-3 enforced at compile time)
  99. - Automatic action classification (DOCUMENT, MONITOR, MITIGATE, BLOCK)
  100. - Visual matrix generation for documentation
  101. - Risk score formula: `probability * impact` (max = 9)
  102. - Threshold-based decision rules (6-8 = MITIGATE, 9 = BLOCK)
  103. ---
  104. ### Example 2: Risk Assessment Workflow (Test Planning Integration)
  105. **Context**: Apply risk matrix during test design to prioritize scenarios
  106. **Implementation**:
  107. ```typescript
  108. // tests/e2e/test-planning/risk-assessment.ts
  109. import { assessRisk, generateRiskMatrix, type RiskAssessment } from '../../../src/testing/risk-matrix';
  110. export type TestScenario = {
  111. id: string;
  112. title: string;
  113. feature: string;
  114. risk: RiskAssessment;
  115. testLevel: 'E2E' | 'API' | 'Unit';
  116. priority: 'P0' | 'P1' | 'P2' | 'P3';
  117. owner: string;
  118. };
  119. /**
  120. * Assess test scenarios and auto-assign priority based on risk score
  121. */
  122. export function assessTestScenarios(scenarios: Omit<TestScenario, 'risk' | 'priority'>[]): TestScenario[] {
  123. return scenarios.map((scenario) => {
  124. // Auto-assign priority based on risk score
  125. const priority = mapRiskToPriority(scenario.risk.score);
  126. return { ...scenario, priority };
  127. });
  128. }
  129. /**
  130. * Map risk score to test priority (P0-P3)
  131. * P0: Critical (score 9) - blocks release
  132. * P1: High (score 6-8) - must fix before release
  133. * P2: Medium (score 4-5) - fix if time permits
  134. * P3: Low (score 1-3) - document and defer
  135. */
  136. function mapRiskToPriority(score: number): 'P0' | 'P1' | 'P2' | 'P3' {
  137. if (score === 9) return 'P0';
  138. if (score >= 6) return 'P1';
  139. if (score >= 4) return 'P2';
  140. return 'P3';
  141. }
  142. /**
  143. * Example: Payment flow risk assessment
  144. */
  145. export const paymentScenarios: Array<Omit<TestScenario, 'priority'>> = [
  146. {
  147. id: 'PAY-001',
  148. title: 'Valid credit card payment completes successfully',
  149. feature: 'Checkout',
  150. risk: assessRisk({
  151. probability: 2, // Possible (standard Stripe integration)
  152. impact: 3, // Critical (revenue loss if broken)
  153. reasoning: 'Core revenue flow, but Stripe is well-tested',
  154. }),
  155. testLevel: 'E2E',
  156. owner: 'qa-team',
  157. },
  158. {
  159. id: 'PAY-002',
  160. title: 'Expired credit card shows user-friendly error',
  161. feature: 'Checkout',
  162. risk: assessRisk({
  163. probability: 3, // Likely (edge case handling often buggy)
  164. impact: 2, // Degraded (users see error, but can retry)
  165. reasoning: 'Error handling logic is custom and complex',
  166. }),
  167. testLevel: 'E2E',
  168. owner: 'qa-team',
  169. },
  170. {
  171. id: 'PAY-003',
  172. title: 'Payment confirmation email formatting is correct',
  173. feature: 'Email',
  174. risk: assessRisk({
  175. probability: 2, // Possible (template changes occasionally break)
  176. impact: 1, // Minor (cosmetic issue, email still sent)
  177. reasoning: 'Non-blocking, users get email regardless',
  178. }),
  179. testLevel: 'Unit',
  180. owner: 'dev-team',
  181. },
  182. {
  183. id: 'PAY-004',
  184. title: 'Payment fails gracefully when Stripe is down',
  185. feature: 'Checkout',
  186. risk: assessRisk({
  187. probability: 1, // Unlikely (Stripe has 99.99% uptime)
  188. impact: 3, // Critical (complete checkout failure)
  189. reasoning: 'Rare but catastrophic, requires retry mechanism',
  190. }),
  191. testLevel: 'API',
  192. owner: 'qa-team',
  193. },
  194. ];
  195. /**
  196. * Generate risk assessment report with priority distribution
  197. */
  198. export function generateRiskReport(scenarios: TestScenario[]): string {
  199. const priorityCounts = scenarios.reduce(
  200. (acc, s) => {
  201. acc[s.priority] = (acc[s.priority] || 0) + 1;
  202. return acc;
  203. },
  204. {} as Record<string, number>,
  205. );
  206. const actionCounts = scenarios.reduce(
  207. (acc, s) => {
  208. acc[s.risk.action] = (acc[s.risk.action] || 0) + 1;
  209. return acc;
  210. },
  211. {} as Record<string, number>,
  212. );
  213. return `
  214. # Risk Assessment Report
  215. ## Risk Matrix
  216. ${generateRiskMatrix()}
  217. ## Priority Distribution
  218. - **P0 (Blocker)**: ${priorityCounts.P0 || 0} scenarios
  219. - **P1 (High)**: ${priorityCounts.P1 || 0} scenarios
  220. - **P2 (Medium)**: ${priorityCounts.P2 || 0} scenarios
  221. - **P3 (Low)**: ${priorityCounts.P3 || 0} scenarios
  222. ## Action Required
  223. - **BLOCK**: ${actionCounts.BLOCK || 0} scenarios (auto-fail gate)
  224. - **MITIGATE**: ${actionCounts.MITIGATE || 0} scenarios (concerns at gate)
  225. - **MONITOR**: ${actionCounts.MONITOR || 0} scenarios (watch closely)
  226. - **DOCUMENT**: ${actionCounts.DOCUMENT || 0} scenarios (awareness only)
  227. ## Scenarios by Risk Score (Highest First)
  228. ${scenarios
  229. .sort((a, b) => b.risk.score - a.risk.score)
  230. .map((s) => `- **[${s.priority}]** ${s.id}: ${s.title} (Score: ${s.risk.score} - ${s.risk.action})`)
  231. .join('\n')}
  232. `.trim();
  233. }
  234. ```
  235. **Key Points**:
  236. - Risk score → Priority mapping (P0-P3 automated)
  237. - Report generation with priority/action distribution
  238. - Scenarios sorted by risk score (highest first)
  239. - Visual matrix included in reports
  240. - Reusable across projects (extract to shared library)
  241. ---
  242. ### Example 3: Dynamic Risk Re-Assessment (Continuous Evaluation)
  243. **Context**: Recalculate risk scores as project evolves (requirements change, mitigations implemented)
  244. **Implementation**:
  245. ```typescript
  246. // src/testing/risk-tracking.ts
  247. import { type RiskAssessment, assessRisk, type Probability, type Impact } from './risk-matrix';
  248. export type RiskHistory = {
  249. timestamp: Date;
  250. assessment: RiskAssessment;
  251. changedBy: string;
  252. reason: string;
  253. };
  254. export type TrackedRisk = {
  255. id: string;
  256. title: string;
  257. feature: string;
  258. currentRisk: RiskAssessment;
  259. history: RiskHistory[];
  260. mitigations: string[];
  261. status: 'OPEN' | 'MITIGATED' | 'WAIVED' | 'RESOLVED';
  262. };
  263. export class RiskTracker {
  264. private risks: Map<string, TrackedRisk> = new Map();
  265. /**
  266. * Add new risk to tracker
  267. */
  268. addRisk(params: {
  269. id: string;
  270. title: string;
  271. feature: string;
  272. probability: Probability;
  273. impact: Impact;
  274. reasoning: string;
  275. changedBy: string;
  276. }): TrackedRisk {
  277. const { id, title, feature, probability, impact, reasoning, changedBy } = params;
  278. const assessment = assessRisk({ probability, impact, reasoning });
  279. const risk: TrackedRisk = {
  280. id,
  281. title,
  282. feature,
  283. currentRisk: assessment,
  284. history: [
  285. {
  286. timestamp: new Date(),
  287. assessment,
  288. changedBy,
  289. reason: 'Initial assessment',
  290. },
  291. ],
  292. mitigations: [],
  293. status: 'OPEN',
  294. };
  295. this.risks.set(id, risk);
  296. return risk;
  297. }
  298. /**
  299. * Reassess risk (probability or impact changed)
  300. */
  301. reassessRisk(params: {
  302. id: string;
  303. probability?: Probability;
  304. impact?: Impact;
  305. reasoning: string;
  306. changedBy: string;
  307. }): TrackedRisk | null {
  308. const { id, probability, impact, reasoning, changedBy } = params;
  309. const risk = this.risks.get(id);
  310. if (!risk) return null;
  311. // Use existing values if not provided
  312. const newProbability = probability ?? risk.currentRisk.probability;
  313. const newImpact = impact ?? risk.currentRisk.impact;
  314. const newAssessment = assessRisk({
  315. probability: newProbability,
  316. impact: newImpact,
  317. reasoning,
  318. });
  319. risk.currentRisk = newAssessment;
  320. risk.history.push({
  321. timestamp: new Date(),
  322. assessment: newAssessment,
  323. changedBy,
  324. reason: reasoning,
  325. });
  326. this.risks.set(id, risk);
  327. return risk;
  328. }
  329. /**
  330. * Mark risk as mitigated (probability reduced)
  331. */
  332. mitigateRisk(params: { id: string; newProbability: Probability; mitigation: string; changedBy: string }): TrackedRisk | null {
  333. const { id, newProbability, mitigation, changedBy } = params;
  334. const risk = this.reassessRisk({
  335. id,
  336. probability: newProbability,
  337. reasoning: `Mitigation implemented: ${mitigation}`,
  338. changedBy,
  339. });
  340. if (risk) {
  341. risk.mitigations.push(mitigation);
  342. if (risk.currentRisk.action === 'DOCUMENT' || risk.currentRisk.action === 'MONITOR') {
  343. risk.status = 'MITIGATED';
  344. }
  345. }
  346. return risk;
  347. }
  348. /**
  349. * Get risks requiring action (MITIGATE or BLOCK)
  350. */
  351. getRisksRequiringAction(): TrackedRisk[] {
  352. return Array.from(this.risks.values()).filter(
  353. (r) => r.status === 'OPEN' && (r.currentRisk.action === 'MITIGATE' || r.currentRisk.action === 'BLOCK'),
  354. );
  355. }
  356. /**
  357. * Generate risk trend report (show changes over time)
  358. */
  359. generateTrendReport(riskId: string): string | null {
  360. const risk = this.risks.get(riskId);
  361. if (!risk) return null;
  362. return `
  363. # Risk Trend Report: ${risk.id}
  364. **Title**: ${risk.title}
  365. **Feature**: ${risk.feature}
  366. **Status**: ${risk.status}
  367. ## Current Assessment
  368. - **Probability**: ${risk.currentRisk.probability}
  369. - **Impact**: ${risk.currentRisk.impact}
  370. - **Score**: ${risk.currentRisk.score}
  371. - **Action**: ${risk.currentRisk.action}
  372. - **Reasoning**: ${risk.currentRisk.reasoning}
  373. ## Mitigations Applied
  374. ${risk.mitigations.length > 0 ? risk.mitigations.map((m) => `- ${m}`).join('\n') : '- None'}
  375. ## History (${risk.history.length} changes)
  376. ${risk.history
  377. .reverse()
  378. .map((h) => `- **${h.timestamp.toISOString()}** by ${h.changedBy}: Score ${h.assessment.score} (${h.assessment.action}) - ${h.reason}`)
  379. .join('\n')}
  380. `.trim();
  381. }
  382. }
  383. ```
  384. **Key Points**:
  385. - Historical tracking (audit trail for risk changes)
  386. - Mitigation impact tracking (probability reduction)
  387. - Status lifecycle (OPEN → MITIGATED → RESOLVED)
  388. - Trend reports (show risk evolution over time)
  389. - Re-assessment triggers (requirements change, new info)
  390. ---
  391. ### Example 4: Risk Matrix in Gate Decision (Integration with Trace Workflow)
  392. **Context**: Use probability-impact scores to drive gate decisions (PASS/CONCERNS/FAIL/WAIVED)
  393. **Implementation**:
  394. ```typescript
  395. // src/testing/gate-decision.ts
  396. import { type RiskScore, classifyRiskAction, type RiskAction } from './risk-matrix';
  397. import { type TrackedRisk } from './risk-tracking';
  398. export type GateDecision = 'PASS' | 'CONCERNS' | 'FAIL' | 'WAIVED';
  399. export type GateResult = {
  400. decision: GateDecision;
  401. blockers: TrackedRisk[]; // Score=9, action=BLOCK
  402. concerns: TrackedRisk[]; // Score 6-8, action=MITIGATE
  403. monitored: TrackedRisk[]; // Score 4-5, action=MONITOR
  404. documented: TrackedRisk[]; // Score 1-3, action=DOCUMENT
  405. summary: string;
  406. };
  407. /**
  408. * Evaluate gate based on risk assessments
  409. */
  410. export function evaluateGateFromRisks(risks: TrackedRisk[]): GateResult {
  411. const blockers = risks.filter((r) => r.currentRisk.action === 'BLOCK' && r.status === 'OPEN');
  412. const concerns = risks.filter((r) => r.currentRisk.action === 'MITIGATE' && r.status === 'OPEN');
  413. const monitored = risks.filter((r) => r.currentRisk.action === 'MONITOR');
  414. const documented = risks.filter((r) => r.currentRisk.action === 'DOCUMENT');
  415. let decision: GateDecision;
  416. if (blockers.length > 0) {
  417. decision = 'FAIL';
  418. } else if (concerns.length > 0) {
  419. decision = 'CONCERNS';
  420. } else {
  421. decision = 'PASS';
  422. }
  423. const summary = generateGateSummary({ decision, blockers, concerns, monitored, documented });
  424. return { decision, blockers, concerns, monitored, documented, summary };
  425. }
  426. /**
  427. * Generate gate decision summary
  428. */
  429. function generateGateSummary(result: Omit<GateResult, 'summary'>): string {
  430. const { decision, blockers, concerns, monitored, documented } = result;
  431. const lines: string[] = [`## Gate Decision: ${decision}`];
  432. if (decision === 'FAIL') {
  433. lines.push(`\n**Blockers** (${blockers.length}): Automatic FAIL until resolved or waived`);
  434. blockers.forEach((r) => {
  435. lines.push(`- **${r.id}**: ${r.title} (Score: ${r.currentRisk.score})`);
  436. lines.push(` - Probability: ${r.currentRisk.probability}, Impact: ${r.currentRisk.impact}`);
  437. lines.push(` - Reasoning: ${r.currentRisk.reasoning}`);
  438. });
  439. }
  440. if (concerns.length > 0) {
  441. lines.push(`\n**Concerns** (${concerns.length}): Address before release`);
  442. concerns.forEach((r) => {
  443. lines.push(`- **${r.id}**: ${r.title} (Score: ${r.currentRisk.score})`);
  444. lines.push(` - Mitigations: ${r.mitigations.join(', ') || 'None'}`);
  445. });
  446. }
  447. if (monitored.length > 0) {
  448. lines.push(`\n**Monitored** (${monitored.length}): Watch closely`);
  449. monitored.forEach((r) => lines.push(`- **${r.id}**: ${r.title} (Score: ${r.currentRisk.score})`));
  450. }
  451. if (documented.length > 0) {
  452. lines.push(`\n**Documented** (${documented.length}): Awareness only`);
  453. }
  454. lines.push(`\n---\n`);
  455. lines.push(`**Next Steps**:`);
  456. if (decision === 'FAIL') {
  457. lines.push(`- Resolve blockers or request formal waiver`);
  458. } else if (decision === 'CONCERNS') {
  459. lines.push(`- Implement mitigations for high-risk scenarios (score 6-8)`);
  460. lines.push(`- Re-run gate after mitigations`);
  461. } else {
  462. lines.push(`- Proceed with release`);
  463. }
  464. return lines.join('\n');
  465. }
  466. ```
  467. **Key Points**:
  468. - Gate decision driven by risk scores (not gut feeling)
  469. - Automatic FAIL for score=9 (blockers)
  470. - CONCERNS for score 6-8 (requires mitigation)
  471. - PASS only when no blockers/concerns
  472. - Actionable summary with next steps
  473. - Integration with trace workflow (Phase 2)
  474. ---
  475. ## Probability-Impact Threshold Summary
  476. | Score | Action | Gate Impact | Typical Use Case |
  477. | ----- | -------- | -------------------- | -------------------------------------- |
  478. | 1-3 | DOCUMENT | None | Cosmetic issues, low-priority bugs |
  479. | 4-5 | MONITOR | None (watch closely) | Edge cases, partial unknowns |
  480. | 6-8 | MITIGATE | CONCERNS at gate | High-impact scenarios needing coverage |
  481. | 9 | BLOCK | Automatic FAIL | Critical blockers, must resolve |
  482. ## Risk Assessment Checklist
  483. Before deploying risk matrix:
  484. - [ ] **Probability scale defined**: 1 (unlikely), 2 (possible), 3 (likely) with clear examples
  485. - [ ] **Impact scale defined**: 1 (minor), 2 (degraded), 3 (critical) with concrete criteria
  486. - [ ] **Threshold rules documented**: Score → Action mapping (1-3 = DOCUMENT, 4-5 = MONITOR, 6-8 = MITIGATE, 9 = BLOCK)
  487. - [ ] **Gate integration**: Risk scores drive gate decisions (PASS/CONCERNS/FAIL/WAIVED)
  488. - [ ] **Re-assessment process**: Risks re-evaluated as project evolves (requirements change, mitigations applied)
  489. - [ ] **Audit trail**: Historical tracking for risk changes (who, when, why)
  490. - [ ] **Mitigation tracking**: Link mitigations to probability reduction (quantify impact)
  491. - [ ] **Reporting**: Risk matrix visualization, trend reports, gate summaries
  492. ## Integration Points
  493. - **Used in workflows**: `*test-design` (initial risk assessment), `*trace` (gate decision Phase 2), `*nfr-assess` (security/performance risks)
  494. - **Related fragments**: `risk-governance.md` (risk scoring matrix, gate decision engine), `test-priorities-matrix.md` (P0-P3 mapping), `nfr-criteria.md` (impact assessment for NFRs)
  495. - **Tools**: TypeScript for type safety, markdown for reports, version control for audit trail
  496. _Source: Murat risk model summary, gate decision patterns from production systems, probability-impact matrix from risk governance practices_