یک مغایرت بحرانی قیمت در Staging، سی دقیقه پیش از انتشار کشف شده است. نسخه به Production نرسیده، کاربری آسیب ندیده و تیم میگوید «خوششانس بودیم». آیا این فقط یک باگِ بستهشده است؟ نه. اگر فاصلهٔ میان کنترلهای مورد انتظار و چیزی که واقعاً جلوی انتشار را گرفت ثبت نشود، تیم نمیداند دفعهٔ بعد هم همان مانع عمل میکند یا نه.
این راهنما یک Near-Miss Learning Loop برای نرمافزار میسازد: Trigger → Event Packet → Timeline → Potential Impact → Barrier Map → Causal Hypotheses → Actions → Verification → Learning Decision. هدف یافتن مقصر، اثبات یک «علت ریشهای» واحد یا ادعای تضمین عدم تکرار نیست؛ هدف تبدیل یک خطرِ بهموقعمهارشده به شواهد و کنترل قابلآزمون است.
پاسخ کوتاه: Near Miss نرمافزاری را چگونه بررسی کنیم؟
ابتدا ثابت کنید چه رخ داده و چه رخ نداده است. سپس Timeline را از شواهد همزمان بسازید، پیامد بالقوه را با برچسب Counterfactual و دامنهٔ عدمقطعیت بیان کنید، کنترلهای مورد انتظار و کنترلِ واقعاً مؤثر را روی Barrier Map بگذارید و چند فرضیهٔ علّیِ ابطالپذیر بسازید. اقدام باید به Gap مشخص وصل باشد، مالک نقش، موعد، معیار پذیرش و روش Verification داشته باشد. گزارش پس از Review منتشر میشود؛ پروندهٔ یادگیری فقط وقتی بسته میشود که نتیجهٔ اقدامات سنجیده و Decision ثبت شده باشد.
- Fact: رخداد مشاهدهشده با زمان، منبع و شناسهٔ قابلبازیابی.
- Inference: تفسیر موقت از چند Fact که ممکن است تغییر کند.
- Counterfactual: برآورد مشروط از اینکه اگر مانع عمل نمیکرد چه میشد؛ نه خسارت واقعی.
- Barrier: کنترل پیشگیرانه، آشکارساز، مهارکننده یا بازیابیکننده با شواهد اجرای واقعی.
- Learning closure: تصمیم مستند پس از Verification؛ نه صرفاً پایان جلسه یا بستن تیکت.
Near Miss در نرمافزار چیست؟
Near Miss یا «نزدیکبهحادثه» رویدادی است که ظرفیت ایجاد پیامد نامطلوب داشت، اما پیش از رسیدن آن پیامد به محدودهٔ تعریفشده مهار شد. در نرمافزار، این محدوده میتواند Production، کاربر، دادهٔ واقعی، SLO یا تعهد قراردادی باشد. یافتن هر نقص پیش از انتشار Near Miss نیست؛ شدت بالقوه، فاصلهٔ کم تا Exposure و شکست یا دورزدن کنترلهای مورد انتظار باید از آستانهٔ مصوب عبور کند.
| وضعیت | آنچه رخ داده | مسیر اصلی |
|---|---|---|
| Defect عادی | نقص در محل و زمان مورد انتظار تست پیدا شده است | چرخهٔ مدیریت نقص |
| Near Miss | پیامد نرسیده، اما کنترلهای مورد انتظار Gap داشتهاند یا Catch دیر/اتفاقی بوده است | Learning Review همین مقاله |
| Incident | اثر واقعی در Production، کاربر، داده یا خدمت رخ داده است | Incident response و سپس Postmortem |
| Security Escape | یافتهٔ امنیتی از کنترل مورد انتظار عبور کرده است | تحلیل رخنهٔ امنیتی |
| Retrospective | مرور دورهای روش کار تیم، نه یک Event خاص | Sprint Retrospective |
چرا «نزدیک بود فاجعه شود» یک داده نیست؟
«فاجعه»، «میلیونها کاربر» و «ضرر هنگفت» بدون مدل Exposure و شواهد، زبان هیجانیاند. بگویید Build کدام بود، چه رفتار نامطلوبی دیده شد، مسیر انتشار در چه مرحلهای متوقف شد، آیا Production exposure صفر بودنش اثبات شده و دامنهٔ بالقوه با چه فرضهایی محاسبه شده است. این دقت ارزش رویداد را کم نمیکند؛ از تصمیمهای گران مبتنی بر ترس جلوگیری میکند.
Postmortem چه نسبتی با Near Miss دارد؟
Postmortem یک محصول یادگیریِ مکتوب و بازبینیشده برای Event مهم است. کتاب رسمی Google SRE دربارهٔ فرهنگ Postmortem بر معیار ازپیشتعریفشده، ثبت Incident، عوامل مشارکتکننده، اقدام پیشگیرانه و اشتراک آموختهها تأکید میکند. این مقاله همان انضباط را آگاهانه به Near Missهای نرمافزاری تعمیم میدهد؛ این تعمیم، تعریف رسمی گوگل از Near Miss نیست.
مرز با RCA، تریاژ و Retrospective را حفظ کنید
تریاژ دربارهٔ Disposition نقص تصمیم میگیرد؛ جلسهٔ تریاژ باگ مسیر Fix، Defer، Duplicate یا Reject را مالک است. تحلیل علت ریشهای مجموعهای از تکنیکهای آزمون فرضیهٔ علّی است. Retrospective الگوی دورهای بهبود تیم است. Near-Miss Review یک Event خاص را از Trigger تا اثربخشی اقدام دنبال میکند و میتواند RCA را بهعنوان یک زیرکار فراخوانی کند.
خروجی واقعی Review چیست؟
خروجی، یک صورتجلسهٔ طولانی نیست. حداقل باید Event Packet نسخهدار، Timeline مبتنی بر شواهد، Impact واقعی و بالقوهٔ جدا، Barrier Map، فرضیهها و وضعیتشان، Action Register، Approval، Distribution policy و برنامهٔ Verification داشته باشید. هر ادعا باید به Evidence locator یا برچسب «نامعلوم» متصل باشد.
آستانهٔ آغاز Review را پیشاپیش تعریف کنید
اگر تصمیم به Review به شهرت فرد یا فشار همان روز وابسته باشد، رویدادهای مشابه رفتار متفاوت میگیرند. آستانه را پیش از رویداد تصویب و هر فصل بازبینی کنید. ذینفع نیز باید بتواند Review درخواست کند، اما پذیرش یا رد درخواست با دلیل ثبت شود.
| Trigger نمونه | Evidence لازم | سطح Review |
|---|---|---|
| امکان آسیب داده یا پول واقعی، بدون Exposure | مسیر داده/تراکنش و نقطهٔ مهار | کامل |
| دورزدن دو Barrier مستقل | اجرای کنترل، Waiver و Catch | کامل |
| توقف Release یا Rollback پیش از Exposure | Release log و Approval | کامل یا سبک بر اساس Severity |
| تکرار الگوی مشابه در ۳۰ روز | Cluster ID و Eventهای مرتبط | مرور خوشهای |
| نقص در همان لایهٔ مورد انتظار یافت شده | Test result و Defect | مدیریت نقص؛ بدون Review مستقل |
Severity بالقوه را با Probability یکی نگیرید
پیامد بالقوه میتواند شدید باشد، اما احتمال عبور یا دامنهٔ Exposure نامعلوم بماند. یک ماتریس ساده Severity × Proximity × Barrier degradation برای اولویت Review مفید است، ولی عدد حاصل «ریسک واقعی» یا خسارت مالی نیست. مدل، نسخه، مالک و محدودیتهایش را کنار امتیاز نگه دارید.
Review Charter را پیش از دعوت جلسه بنویسید
Charter باید Event ID، سؤال تصمیم، بازهٔ زمانی، سیستمها، موارد خارج از دامنه، سطح محرمانگی، تسهیلگر، Approver، مهلت Draft و معیار Closure را مشخص کند. سؤال خوب این نیست که «چه کسی اشتباه کرد؟»؛ سؤال خوب این است: «کدام شرایط و Barrierها اجازه دادند CH-۸۴۲ تا Release gate پیش برود و کدام تغییر قابلسنجش این مسیر را کوتاه میکند؟»
review_id: NMR-2026-042 event_id: NM-IRR-042 decision_question: reduce_late_detection_of_discount_combinations window: 2026-08-10T08:00:00+03:30/2026-08-10T16:00:00+03:30 in_scope: [pricing-service, checkout-web, release-pipeline] out_of_scope: [performance, production-impact-claim] classification: internal-redacted facilitator_role: reliability-facilitator approver_role: engineering-manager draft_due: 2026-08-12 closure_rule: verified_actions_and_learning_decision
اختیارها را از مشارکت جدا کنید
| نقش | مسئولیت | اختیار ندارد |
|---|---|---|
| Incident/Event owner | Event Packet و دسترسی به Evidence | تأیید یکنفرهٔ روایت |
| Facilitator | زمان، قواعد، تفکیک Fact از inference | تحمیل علت یا Action |
| Evidence custodian | حفظ، Redaction و locator | تغییر منبع خام |
| Subject-matter expert | فرضیه و نقد فنی | پنهانکردن Alternative |
| Action owner role | Delivery و Evidence پذیرش | Self-verify در کنترل پرریسک |
| Approver | تصویب انتشار گزارش/Closure | بازنویسی Fact بدون Evidence |
امنیت روانی و پاسخگویی متضاد نیستند
Blameless یعنی رفتار افراد را در زمینهٔ اطلاعات، ابزار، فشار و محدودیت همان لحظه بررسی کنید و متن را روی «چه شد» نگه دارید، نه برچسب شخصیت. این اصل مصونیت از سیاست منابع انسانی، تخلف عمدی، آزار، تقلب یا الزام قانونی نیست. چنین موضوعی باید با حداقل افشای لازم به مسیر مستقل HR، حقوقی یا امنیتی ارجاع شود؛ Learning Review جای بازجویی نیست.
Evidence Hold کوتاه و متناسب ایجاد کنید
پیش از چرخش لاگ یا حذف Artifact، بازهٔ محدود Evidence را حفظ کنید: Deployment record، commit/PR، CI result، feature flag، TestRail run، alert و decision receipt. Secret، Token، دادهٔ پرداخت و PII را در سند مشترک کپی نکنید؛ locator کنترلشده، هش، سطح دسترسی و Retention کافی است. Snapshot بدون زمان و نسخه ارزش کمی دارد.
Event Packet؛ قرارداد ورودی Review
| فیلد | نمونه | خطای رایج |
|---|---|---|
| Event/Change/Build | NM-IRR-042 / CH-842 / b271 | عبارت مبهم «نسخهٔ جدید» |
| Observed behavior | discount_total=-۱۲۰۰۰۰ در سناریوی C-۱۷ | «قیمت خراب بود» |
| Environment/config | staging-tehran / pricing-rules:v43 | حذف Fingerprint |
| Detection | manual-smoke-۷۷ در ۱۵:۱۲ | نسبتدادن به «QA» بدون Run |
| Containment | release gate HOLD در ۱۵:۱۸ | یکیگرفتن Catch و Containment |
| Actual exposure | production deploy=۰؛ transaction=۰ | فرض صفر بدون log |
| Unknowns | رفتار نسخه روی cache قدیمی | پرکردن شکاف با حدس |
Timeline را از حافظه نسازید
هر ردیف Timeline باید زمان با Zone، Actor از نوع نقش یا سیستم، Event، Source locator و Confidence داشته باشد. زمان گفتگو میتواند زمان «گزارش» باشد، نه زمان «وقوع». ساعت CI، Git، Jira و پیامرسان ممکن است Drift داشته باشند؛ ساعت مرجع و Offset را ثبت کنید و ترتیب نامطمئن را قطعی ننویسید.
| زمان تهران | رویداد | منبع | Confidence |
|---|---|---|---|
| 14:06 | Build b271 ساخته شد | ci/build/271 | بالا |
| 14:21 | integration-pricing در وضعیت skipped ثبت شد | ci/job/991 | بالا |
| 14:37 | Waiver W-۱۹ بدون امضای دوم وارد Gate شد | release/w19 | بالا |
| 15:12 | سناریوی C-۱۷ اختلاف قیمت نشان داد | tr/run/77 | بالا |
| 15:18 | Release در وضعیت HOLD قرار گرفت | gate/decision/512 | بالا |
| 15:44 | Production deployment صفر تأیید شد | deploy/audit/204 | بالا |
Fact، inference و Unknown را در متن نشانهگذاری کنید
| عبارت | نوع درست | چرا |
|---|---|---|
| Job در CI برابر skipped بود | Fact | Artifact همزمان دارد |
| فشار زمان باعث Waiver شد | Hypothesis | نیازمند شواهد تصمیم و Alternative است |
| اگر منتشر میشد همهٔ کاربران زیان میدیدند | Counterfactual ضعیف | Exposure و eligibility معلوم نیست |
| هیچ نسخهای به Production نرفت | Fact مشروط | با deployment audit و بازهٔ مشخص |
| اثر cache قدیمی | Unknown | Run شاهد وجود ندارد |
Actual Impact را پیش از Potential Impact بنویسید
در مثال ما Actual Impact این است: انتشار ۴۶ دقیقه متوقف شد، سه نقش درگیر شدند، Production deploy و تراکنش واقعی مرتبط صفر بود. Potential Impact باید جدا بیاید: «اگر b271 با rules:v43 منتشر میشد، سفارشهای واجد ترکیب C-۱۷ ممکن بود مبلغ منفی ببینند». اندازهٔ جمعیت، مدت Exposure و واکنش Gate هنوز پارامترند؛ پس رقم مالی قطعی نداریم.
Counterfactual را قابلممیزی کنید
potential_exposure = eligible_orders_per_hour
× assumed_exposure_hours
× affected_path_share
label: counterfactual_estimate
range: [low, central, high]
assumptions:
- b271 would pass the remaining gate
- rules:v43 would be active for eligible traffic
excluded:
- user retry and support intervention
- emergency rollback latency
decision_use: prioritize_review_only
این مدل برای اولویتگذاری است، نه ثبت خسارت. اگر دادهٔ ورودی قابل اتکا نیست، «نامعلوم» بهتر از عدد دقیقِ ساختگی است. سناریوی worst case را با expected case ترکیب نکنید.
Catch Point با Expected Detection Point فرق دارد
کشف در Smoke دستی یک موفقیت واقعی است، اما اگر کنترل طراحیشده برای این ریسک Decision Table در Requirement review یا integration check بوده، Catch دیرهنگام Gap را پاک نمیکند. فاصلهٔ «اولین نقطهای که میتوانستیم با هزینهٔ معقول کشف کنیم» تا «نقطهٔ کشف واقعی» را ثبت کنید؛ آن را به عملکرد فردی تبدیل نکنید.
Barrier Map را حول Claim بسازید
| Barrier | هدف | Expected | Actual | Evidence |
|---|---|---|---|---|
| Requirement example table | پیشگیری از ابهام ترکیب تخفیف | C-01..C-24 | C-۱۷ غایب | req/842:v8 |
| Unit property | مبلغ نهایی هرگز منفی نباشد | اجرا روی PR | Property وجود نداشت | repo/pr/842 |
| Integration matrix | ترکیب Rule و category | required | skipped | ci/job/991 |
| Waiver control | پذیرش آگاهانهٔ Gap | دو امضا + expiry | یک امضا | release/w19 |
| Manual smoke | Sample مسیر حیاتی | C-01,C-07,C-17 | C-۱۷ failure؛ Catch | tr/run/77 |
| Release gate | مهار نسخه | Block on critical | HOLD | gate/512 |
کنترل مستقل را از کنترل همبسته تشخیص دهید
سه Test که از یک Example، Oracle و Dataset مشترک تغذیه میشوند سه Barrier مستقل نیستند. Failure mode مشترک را ثبت کنید: Specification source، Generator، محیط، Approver یا Credential. استقلال نسبی یعنی شکست یک منبع، همهٔ کنترلها را همزمان کور نکند. مقالهٔ تستر بهعنوان شبکهٔ ایمنی طراحی Protection Control و Handoff را عمیقتر پوشش میدهد.
Lucky Catch را پاداش دهید، اما کنترل اعلام نکنید
اگر تستر خارج از Script یک ترکیب را امتحان کرده و نقص را یافته، مشاهده و Escalation ارزشمند است. بااینحال «دقت تستر» کنترل تکرارپذیر نیست. ابتدا Catch را incidental علامت بزنید؛ سپس اگر ارزش دارد آن را به Property، Example، Monitor یا Gate با مالک و Verification تبدیل کنید.
پنج چرا را صورتجلسهٔ حقیقت نکنید
تعداد «چرا» جادویی نیست و آخرین پاسخ الزاماً Root cause نیست. پاسخهایی مثل «نیازمندی ناقص بود» خود به فرضیه نیاز دارند: چه Requirement claimی غایب بود، چه Reviewی انتظار میرفت، آیا نبود آن Claim برای ایجاد رفتار کافی بود و چه شاهد خلافی داریم؟ روشهای کاملتر، Evidence و Alternative را در راهنمای RCA دنبال کنید.
فرضیهٔ علّی باید ابطالپذیر باشد
| ID | فرضیه | Supporting | Disconfirming test | وضعیت |
|---|---|---|---|---|
| H1 | نبود C-۱۷ در Rule example باعث نبود Oracle مشترک شد | req:v8 و Testها C-۱۷ ندارند | بررسی منبع دیگری که C-۱۷ را الزام کرده باشد | supported-limited |
| H2 | Config متفاوت Staging رفتار را ساخته است | اختلاف محیط محتمل بود | Replay با همان Artifact و config تولید | rejected-by-replay |
| H3 | Cache قدیمی عامل اصلی است | گزارش شفاهی | Run سرد/گرم با trace | unsupported |
| H4 | Waiver تکامضایی اجازهٔ پیشروی با Gap را داد | W-۱۹ و Gate log | بازسازی Gate با policy مصوب | supported |
عامل مشارکتکننده را با علت کافی یکی نگیرید
Deadline، خستگی، تغییر Specification، وابستگی ناپایدار و نبود Test data میتوانند شرایط مشارکتکننده باشند. برای هر کدام Mechanism بنویسید: «چگونه این شرط احتمال یا شدت را تغییر داد؟» اگر حذف عامل بهتنهایی جلوی مسیر را نمیگیرد، آن را علت کافی ننامید. مدل چندعاملی معمولاً از داستان خطی صادقانهتر است.
«خطای انسانی» نقطهٔ شروع است، نه پایان
اگر فرد Waiver را با یک امضا ثبت کرد، بپرسید UI چگونه آن را پذیرفت، Policy چه ابهامی داشت، فشار Queue چه بود، Feedback کجا غایب بود و کنترل دوم چرا مستقل نبود. همزمان Action را به «بیشتر دقت کنید» تقلیل ندهید. تغییر رفتار فقط وقتی معتبر است که شرایط اجرا، Feedback و سنجش دارد.
جلسهٔ Review را پس از تثبیت شواهد برگزار کنید
Draft اولیه و Evidence index را پیشخوانی کنید. جلسه برای خواندن لاگ از صفر نیست؛ برای حل اختلاف، ثبت Unknown، نقد فرضیه و انتخاب Action است. افراد درگیر فرصت اصلاح Fact داشته باشند، اما حذف روایت نامطلوب نیازمند Evidence باشد. زمان جلسه و تعداد شرکتکننده را با پیچیدگی متناسب کنید.
دستور جلسهٔ ۷۵ دقیقهای
- ۵ دقیقه: Charter، قواعد و Decision question.
- ۱۰ دقیقه: Actual impact، containment و Unknownها.
- ۱۵ دقیقه: Timeline؛ فقط اختلافهای Evidence.
- ۱۵ دقیقه: Barrier Map و Catch point.
- ۱۵ دقیقه: فرضیهها، شاهد موافق و آزمون خلاف.
- ۱۰ دقیقه: Action options، do-nothing و trade-off.
- ۵ دقیقه: Decision، مالکها، Review due و موارد باز.
Facilitation را قابل مشاهده کنید
Facilitator باید جملههای سرزنشآمیز را به سؤال سیستمی بازنویسی کند، صدای نقشهای کمقدرت را فعالانه بگیرد و disagreement را حذف نکند. عبارتهای مفید: «منبع این گزاره چیست؟»، «در همان لحظه چه اطلاعاتی در دسترس بود؟»، «چه شاهدی نظر ما را عوض میکند؟» و «این اقدام کدام Failure mode را میشکند؟»
Action را از Gap استخراج کنید، نه از هیجان جلسه
| نوع Action | نمونه برای NM-IRR-۰۴۲ | Verification |
|---|---|---|
| Prevent | Property مبلغ نهایی ≥ ۰ برای Generator ترکیبها | Seedهای مرزی + mutation شاهد |
| Detect | Integration matrix برای Rule × category | ۲۴ Case و failure injection |
| Contain | Gate مانع Waiver تکامضایی شود | Policy test در CI |
| Mitigate | Feature flag با kill switch نقشمحور | Game day بدون Production data |
| Investigate | اثر cache در Build مشابه | Replay و trace مقایسهای |
| Learn | جستوجوی همان Rule gap در دو سرویس | Cluster review و ثبت نتیجه |
Action Contract کامل بنویسید
action_id: AI-042-03 risk_link: BARRIER-WAIVER-02 type: contain change: reject waiver unless two distinct roles approve before expiry owner_role: release-platform-owner due_at: 2026-08-20T17:00:00+03:30 priority: P1 acceptance: - single_signature_fixture == blocked - expired_waiver_fixture == blocked - valid_two_role_fixture == allowed verification_owner_role: qa-governance-reviewer evidence_due: 2026-08-21 rollback: feature_flag gate_policy_v2=false status: accepted_not_verified
«بهبود تستها» Action نیست. فعل، Object، Boundary، معیار پذیرش و Evidence لازم است. مالک را با Role ثبت کنید تا جابهجایی فرد Action را یتیم نکند؛ سامانهٔ کار میتواند فرد فعلی را resolve کند.
اقدام بزرگتر همیشه بهتر نیست
بازنویسی کامل سرویس ممکن است سالها طول بکشد و Failure mode را هم هدف نگیرد. Optionها را بر کاهش Risk، زمان تا اثر، Blast radius تغییر، هزینهٔ نگهداشت و قابلیت بازگشت مقایسه کنید. یک کنترل کوچکِ قابلسنجش همراه با Investigation میتواند از پروژهٔ مبهم مؤثرتر باشد.
اقدامات پیشگیرانه و مهارکننده را متوازن کنید
فقط افزودن Alert، کشف را بهتر میکند اما وقوع را کم نمیکند؛ فقط افزودن Test ممکن است در برابر Unknown unknown کافی نباشد. برای Risk مهم، ترکیبی متناسب از Prevent، Detect و Contain بسازید و استقلال نسبی آنها را بررسی کنید. همهٔ Actionها الزامی نیستند؛ دلیل انتخاب و رد Optionها را نگه دارید.
Verification را هنگام پذیرش Action طراحی کنید
Done در Jira اثربخشی نیست. مشخص کنید چه Fixture یا Runی Failure mode را بازآفرینی میکند، چه Control runی False positive را میسنجد، چه کسی مستقل بررسی میکند و Evidence تا چه زمانی معتبر است. اگر Test جدید همیشه سبز است ولی Fault injection آن را قرمز نمیکند، کنترل هنوز اثبات نشده است.
Recurrence را با شناسهٔ الگو بسنجید
«همان Incident تکرار نشد» میتواند ناشی از ترافیک کم یا تغییر محصول باشد. برای Failure mode یک Pattern ID و تعریف Match بسازید: همان Rule family، همان Barrier gap یا همان Waiver path. Detection coverage و exposure opportunity را کنار recurrence نگه دارید؛ صفر بدون Opportunity شاهد اثربخشی نیست.
Closure دو مرحله دارد
| مرحله | شرط | تصمیمهای مجاز |
|---|---|---|
| Review publication | Fact review، Redaction، فرضیه/Unknown، Action acceptance و Approval | PUBLISH / HOLD / REVISE |
| Learning closure | Verification، residual risk، overdue disposition و recurrence window | ADOPT / ADAPT / REPLACE / ACCEPT-RISK / REOPEN |
جلسه میتواند تمام شود و گزارش منتشر شود، درحالیکه Learning هنوز باز است. این تفکیک مانع میشود تیم برای رسیدن به آمار Closure، Actionهای بیشاهد را بسته اعلام کند.
Decision Receipt را فراموش نکنید
decision_id: LD-042 as_of: 2026-09-15T12:00:00+03:30 review_revision: 3 verified: [AI-042-01, AI-042-03] adapt: [AI-042-02] residual_risk: cache_path_not_replayed risk_acceptor_role: product-engineering-director next_review: 2026-10-15 decision: ADAPT evidence_manifest: em://near-miss/042/v3
گزارش را نسخهدار و قابل اصلاح منتشر کنید
Header باید status، revision، last reviewed، owners، sensitivity و correction policy داشته باشد. اگر Evidence جدید H1 را رد کرد، نسخهٔ قبلی را بیصدا ویرایش نکنید؛ Correction با دلیل و زمان اضافه کنید و مصرفکنندگان تصمیم را خبر دهید. لینک منبع کنترلشده از Screenshot جداافتاده بهتر است.
اشتراکگذاری گسترده با افشای گسترده یکی نیست
Audience را بر اساس نیاز یادگیری تعریف کنید. نسخهٔ عمومی داخلی میتواند نام اشخاص، پیام خصوصی، Customer identifier، Secret، جزئیات exploit و دادهٔ حساس تجاری را Redact کند و درعینحال Timeline، Barrier gap و Action را حفظ کند. اصل کمینهسازی داده و Retention سازمان را رعایت کنید.
Postmortem خوب باید بازبینی و پیگیری شود
فصل عملی Google SRE دربارهٔ Postmortem توضیح میدهد که Action مبهم، بدون اولویت یا بدون Tracking بهآسانی فراموش میشود و بستن Actionها باید بهاندازهٔ نوشتن گزارش ارزش داشته باشد. در این چارچوب، Review کیفیت سند و Follow-up کیفیت کنترل دو Gate جدا هستند.
متریکها را برای یادگیری طراحی کنید، نه رتبهبندی افراد
| Metric | Denominator/تعریف | Countermetric |
|---|---|---|
| Time to draft | Trigger تا Draft آمادهٔ Review | نرخ Correction پس از انتشار |
| Action verification rate | Verified / actionهای سررسیدشده | نرخ Verification بیاثر در fault injection |
| Late detection distance | مرحلهٔ Expected تا Actual catch | هزینه و Flakiness کنترل افزوده |
| Pattern recurrence | Event match / exposure opportunity | Detection coverage drift |
| Reporting propensity | Near miss ثبتشده در واحد فرصت | ناشناسبودن/امنیت روانی Survey |
| Overdue risk | Action عقبافتاده وزندهیشده با Risk | Feature delay و operational load |
افزایش تعداد Near Miss لزوماً بد نیست
پس از ایجاد کانال امن، گزارشها ممکن است زیاد شوند؛ این میتواند Visibility بهتر باشد، نه Reliability بدتر. تعداد خام را KPI کاهش ندهید، وگرنه تیم رویدادها را پنهان یا طبقهبندی را بازی میکند. Severity mix، opportunity، reporting propensity و عبور واقعی از Barrier را با هم بخوانید.
Dashboard بدون Decision contract خطرناک است
هر نمودار باید owner، refresh، data delay، denominator، exclusion و اقدام ناشی از Threshold داشته باشد. مثلاً اگر overdue P1 بیش از دو شد، چه کسی ظرفیت Release را بازتخصیص میدهد؟ برای بستن حلقهٔ پیام تا تصمیم میتوانید از پروتکل ارتباط ریسک کیفیت استفاده کنید.
ضدالگوهای رایج Postmortem
| ضدالگو | پیامد | اصلاح |
|---|---|---|
| Hero story | وابستگی به فرد و پنهانشدن Gap | Catch را قدردانی؛ Barrier را تکرارپذیر کنید |
| یک Root cause قطعی | حذف Alternative و عوامل همبسته | Hypothesis register و disconfirming test |
| Impact خیالی دقیق | اولویتگذاری هیجانی | Actual/Counterfactual جدا با range |
| Action «آموزش بیشتر» | پایداری کم و Verification مبهم | شرایط، Feedback و کنترل سیستمی |
| Close با پایان جلسه | Action یتیم | دو Closure و Decision receipt |
| انتشار log خام | نشت PII/Secret | Evidence locator و Redaction |
| رتبهبندی تیم با تعداد Event | کمگزارشی و Gaming | Metric + countermetric + opportunity |
نمونهٔ عملی: باگ قیمتگذاری پیش از Release
در NM-IRR-۰۴۲، Build b271 برای ترکیب تخفیف عضویت و گروه کالای جدید مبلغ منفی تولید کرد. Smoke دستی آن را یافت و Gate انتشار را متوقف کرد. Audit نشان داد ۰ Deployment و ۰ تراکنش Production رخ داده؛ پس خسارت کاربر ادعا نشد. Review دو Gap را supported دانست: نبود Contract سناریوی C-۱۷ و Waiver تکامضایی. فرضیهٔ Config با Replay رد شد و اثر Cache باز ماند.
تیم چهار Action پیشنهاد کرد، اما سه مورد را پذیرفت: Property قیمت نامنفی، Matrix بیستوچهارحالته و Gate دونقشی. «جلسهٔ آموزشی عمومی» به دلیل Mechanism و معیار ضعیف رد شد. برای پوشش آزمونها، ادعای «۱۰۰٪» مطرح نشد؛ قرارداد مخرج و استثنا باید طبق راهنمای Coverage Claim جدا ثبت شود.
آزمایش تکرارپذیر: ممیزی بستهٔ Near Miss
Fixture زیر عمداً گزارش را «آمادهٔ بستن» اعلام میکند، درحالیکه Actionها فقط accepted هستند، Evidence بالقوه برچسب ندارد، یک Barrier منبع ندارد و H3 آزمون خلاف ندارد. Validator باید بهجای اعتماد به summary، Gapها را بازگرداند.
{
"review_id": "NMR-2026-042",
"actual_exposure": {"production_deploys": 0, "transactions": 0, "evidence": "deploy/audit/204"},
"potential_impact": {"value": 920000000, "label": "", "assumptions": []},
"timeline": [{"at": "2026-08-10T15:12:00+03:30", "event": "C-17 failed", "source": "tr/run/77"}],
"barriers": [
{"id": "B1", "expected": "required", "actual": "skipped", "evidence": "ci/job/991"},
{"id": "B2", "expected": "two signatures", "actual": "one signature", "evidence": ""}
],
"hypotheses": [
{"id": "H1", "support": ["req/842:v8"], "disconfirming_test": "search alternative contract"},
{"id": "H3", "support": ["interview/note/3"], "disconfirming_test": ""}
],
"actions": [
{"id": "A1", "risk_link": "B1", "owner_role": "qa-platform-owner", "due": "2026-08-20", "acceptance": ["24 cases pass"], "verification_evidence": "", "status": "accepted"},
{"id": "A2", "risk_link": "B2", "owner_role": "release-owner", "due": "2026-08-20", "acceptance": ["single signer blocked"], "verification_evidence": "", "status": "accepted"}
],
"declared": "READY_FOR_LEARNING_CLOSURE"
}
const fs = require('node:fs');
const r = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
const findings = [];
const add = (rule, path, msg) => findings.push({ rule, path, msg });
if (!r.review_id) add('IDENTITY', 'review_id', 'missing review identity');
if (!r.actual_exposure?.evidence) add('ACTUAL_EVIDENCE', 'actual_exposure', 'actual impact lacks evidence');
if (r.potential_impact && r.potential_impact.label !== 'counterfactual_estimate')
add('COUNTERFACTUAL_LABEL', 'potential_impact.label', 'potential impact must be labeled');
if (r.potential_impact && !(r.potential_impact.assumptions || []).length)
add('COUNTERFACTUAL_ASSUMPTIONS', 'potential_impact.assumptions', 'assumptions are empty');
for (const [i, x] of (r.timeline || []).entries()) {
if (!x.at || !x.source) add('TIMELINE_EVIDENCE', `timeline[${i}]`, 'time/source required');
}
for (const [i, b] of (r.barriers || []).entries()) {
if (!b.expected || !b.actual || !b.evidence)
add('BARRIER_CONTRACT', `barriers[${i}]`, 'expected/actual/evidence required');
}
for (const [i, h] of (r.hypotheses || []).entries()) {
if (!(h.support || []).length || !h.disconfirming_test)
add('HYPOTHESIS_TEST', `hypotheses[${i}]`, 'support and disconfirming test required');
}
for (const [i, a] of (r.actions || []).entries()) {
for (const key of ['risk_link', 'owner_role', 'due', 'verification_evidence'])
if (!a[key]) add('ACTION_CONTRACT', `actions[${i}].${key}`, `${key} required`);
if (!(a.acceptance || []).length) add('ACTION_ACCEPTANCE', `actions[${i}]`, 'acceptance required');
}
const verified = (r.actions || []).length > 0 && r.actions.every(a => a.status === 'verified');
const computed = findings.length === 0 && verified
? 'READY_FOR_LEARNING_CLOSURE' : 'HOLD';
if (r.declared !== computed) add('DECISION_MISMATCH', 'declared', `${r.declared} != ${computed}`);
console.log(JSON.stringify({ computed, count: findings.length, findings }, null, 2));
process.exitCode = findings.length ? 2 : 0;
اجرا:
node audit-near-miss.js near-miss.json # computed: HOLD # findings: COUNTERFACTUAL_LABEL, COUNTERFACTUAL_ASSUMPTIONS, # BARRIER_CONTRACT, HYPOTHESIS_TEST, ACTION_CONTRACT × 2, # DECISION_MISMATCH
پس از افزودن label و فرضها، locator کنترل، آزمون Replay برای H3 و Evidence مستقل هر دو Action، status آنها verified میشود. اجرای دوباره باید count=۰ و READY_FOR_LEARNING_CLOSURE بدهد. این ابزار کیفیت داده را بررسی میکند؛ درستی رابطهٔ علّی را خودکار اثبات نمیکند.
چکلیست پیش از انتشار گزارش
- Trigger، Scope، Decision question و Approver روشن است.
- Actual impact از Potential impact جدا و Counterfactual برچسبدار است.
- Timeline برای هر ردیف time zone، source و confidence دارد.
- Expected و Actual هر Barrier با Evidence مقایسه شده است.
- Catch دیرهنگام یا اتفاقی بهعنوان کنترل پایدار جا زده نشده است.
- فرضیهها شاهد موافق، آزمون خلاف و وضعیت دارند.
- Actionها به Gap وصل و دارای owner role، due، acceptance و verification هستند.
- PII، Secret و دادهٔ حساس Redact و دسترسی کنترل شده است.
- Review publication و Learning closure دو Decision جدا دارند.
- Correction policy، recurrence window و next review ثبت شده است.
نسخهٔ سبک برای تیم کوچک
برای Near Miss متوسط، یک صفحه کافی است: Event/actual impact، شش ردیف Timeline، سه Barrier، دو فرضیه، حداکثر سه Action و تاریخ Verification. نقشها میتوانند روی یک نفر جمع شوند، اما Self-approval برای Risk بالا را مشخص و یک Reviewer دوم تعیین کنید. سبکبودن به معنی حذف Evidence و Closure نیست.
نسخهٔ سازمانی و چندتیمی
برای سامانهٔ چندتیمی، taxonomy مشترک Event/Barrier/Pattern، مخزن قابل جستوجو، دسترسی لایهای، Action sync با Issue tracker و Review board دورهای لازم میشود. Deadline محلی را به ظرفیت واقعی وصل کنید و Action مشترک را به چند تیکت متناقض تکثیر نکنید؛ یک source of truth و مصرفکنندگان مشخص داشته باشید.
AI در Postmortem چه کار کند و چه کار نکند؟
AI میتواند Timeline candidate را از منابع مجاز مرتب، PII candidate را علامت، Eventهای مشابه را بازیابی و Action مبهم را هشدار دهد. نباید log گمشده بسازد، نیت فرد را حدس بزند، علت را قطعی اعلام کند، Severity یا Risk acceptance را خودمختار تصویب کند یا دادهٔ محرمانه را به سرویس بدون قرارداد بفرستد. خروجی مدل باید source، confidence و reviewer انسانی داشته باشد.
برنامهٔ اجرایی ۳۰روزه
| بازه | کار | خروجی قابل مشاهده |
|---|---|---|
| روز ۱–۳ | تعریف Trigger، taxonomy و sensitivity | Policy v1 و دو مثال مرزی |
| روز ۴–۷ | ساخت Template و Evidence index | Fixture کامل/ناقص |
| روز ۸–۱۲ | انتخاب Facilitator و اجرای Dry run | یافتههای Facilitation |
| روز ۱۳–۱۸ | Pilot روی یک Near Miss واقعیِ Redactشده | Draft، Review و Action register |
| روز ۱۹–۲۴ | اتصال Action به Tracker و Reminder | مالک، موعد و escalation |
| روز ۲۵–۳۰ | Verification و Retro روی خود فرایند | ADOPT/ADAPT/STOP و Policy v2 |
جمعبندی
ارزش Near Miss در ترسِ «چه میشد اگر» نیست؛ در مشاهدهٔ کمهزینهترِ مسیر شکست است. یک Postmortem قابل اتکا Fact را از حدس جدا میکند، Actual را با Counterfactual مخلوط نمیکند، Lucky catch را Barrier پایدار نمینامد و از چند فرضیه به Action قابلVerification میرسد. اگر گزارش منتشر شد اما Action سنجیده نشد، یادگیری هنوز بسته نشده است.
سؤالات متداول دربارهٔ Near Miss و Postmortem
آیا هر باگ بحرانی پیش از انتشار Near Miss است؟
خیر. اگر نقص در لایه و زمان مورد انتظار پیدا شده، چرخهٔ عادی Defect کافی است. Near Miss زمانی معنا دارد که Potential severity، نزدیکی به Exposure، تضعیف Barrier یا Catch دیر/اتفاقی از Trigger مصوب عبور کند.
Postmortem بدون سرزنش یعنی هیچکس پاسخگو نیست؟
خیر. مالکیت Action، Approval و موعد کاملاً روشن است؛ اما رفتار در Context بررسی میشود و گزارش روی سیستم و شواهد تمرکز دارد. تخلف عمدی یا موضوع HR/حقوقی به مسیر مستقل میرود.
آیا جلسهٔ پنج چرا برای بستن Review کافی است؟
خیر. ۵ Whys میتواند فرضیه بسازد، اما Closure به Timeline، Evidence، Alternative، Barrier gap، Action contract و Verification نیاز دارد. تعداد چراها عمق یا صحت علّی را تضمین نمیکند.
اگر هیچ کاربری آسیب ندیده، Impact را چه بنویسیم؟
Actual impact را دقیق بنویسید: مثلاً صفر Deployment و تراکنش Production، ۴۶ دقیقه تأخیر و هزینهٔ عملیاتی محدود. پیامد بالقوه را جدا، مشروط، Rangeدار و با برچسب counterfactual ثبت کنید.
چه زمانی پروندهٔ یادگیری بسته میشود؟
وقتی Actionهای لازم Verification شده، Action ناموفق Adapt یا Replace شده، Risk باقیمانده صاحب اختیار دارد و Decision receipt با Evidence manifest ثبت شده است. انتشار گزارش فقط Closure مرحلهٔ اول است.

