Bài này nói về monitoring frontend ở góc nhìn senior: bạn biết gì về production sau khi đã ship. Người phỏng vấn không hỏi bạn dán Sentry.init. Họ muốn sampling, PII, release, Vue bắt được gì, LCP/INP/CLS (không phải FID), và bạn có page một người hay không.
Đọc 19.1–19.2 trước (Sentry như một sản phẩm, rồi tầng lỗi Vue). 19.3 là RUM so với lab. 19.4–19.5 là log và pager. 19.6 là playbook spike flag. Vue 3 / Nuxt trước; ý telemetry không phụ thuộc framework. Khác Q&A observability (chuyện nói thành lời): ở đây bạn nêu rõ cơ chế.
Tóm tắt: Sentry là một sản phẩm: release bằng git SHA, source map hidden upload trên CI, PII được scrub trong beforeSend, sampling để inbox là regression, không phải firehose #frontend và không phải dán Sentry.init từ docs.
Vì sao quan trọng: “Dùng Sentry trên production thế nào?” Một issue 50k event chẳng ai quan tâm; stack hiện chunk-7a2.js:1:20483. Họ muốn sampling, PII, release, và ownership. Stack không đọc được là map/release lệch, không phải “Vue minify quá mạnh”. Khác Q&A observability: ở đây là cơ chế.
Cách hoạt động: Upload map hidden đúng SHA đó (build-tools 12.1). Gán
release,environment,dist. Tag feature flags. Bỏ 401 đã handle trongbeforeSend. Sample volume lặp; giữ uniqueness cho issue mới. Trace 0.1–- 0.2. Session Replay thấp trên session khỏe; che text/media.
- Route issue tới codeowner (checkout → checkout). Inbox là regression trên release này, issue mới, spike, không phải
ResizeObserverba năm từ extension. ĐừngcaptureExceptionmọiconsole.errorhoặc$fetch404 bị reject. - Gắn issue với PR: release → git SHA → digest GitOps → PR. Tăng
tracesSampleRatechỉ khi đang săn incident đã biết, rồi hạ lại.
Sentry.init({
app,
dsn: dsnFromRuntimeConfig, // không phải secret VITE_ trong JS
release: import.meta.env.VITE_COMMIT_SHA, // phải khớp digest GitOps
environment: "production",
tracesSampleRate: 0.15,
replaysOnErrorSampleRate: 1,
beforeSend(event) {
if (event.tags?.http_status === "401") return null
return scrubPii(event) // bỏ email, token, payload đơn hàng
},
})Điều gì xảy ra: 401 không bao giờ mở issue. Email không vào Sentry. Stack đọc được chỉ khi CI đã upload map hidden đúng SHA này. Replay lúc lỗi bật; replay session khỏe giữ thấp.
| Cơ chế | Mặc định prod | Vì sao |
|---|---|---|
| release + map hidden | SHA từ CI | Stack đọc được, gắn với PR |
| beforeSend | Bỏ 401; scrub PII | Inbox và privacy |
| tracesSampleRate | 0.1–0.2 | Chi phí so với lúc săn |
| Replay | Lúc lỗi; che text | PII trong video |
Góc nhìn senior
- Grouping gộp hai bug khi fingerprint quá thô. Thêm tên transaction, không thêm volume.
- Map trên CDN public là lộ source. Hidden + upload Sentry (12.1).
- Ownership: project không có owner thì inbox thành vòi xối. Route theo path / flag / team.
- Kiểm chứng: một throw biết trước trên staging có stack đã map đúng SHA mà Argo vừa sync.
Lỗi hay gặp
- Không release. Triệu chứng: stack folklore. Nguyên nhân:
Sentry.initthiếu SHA. Sửa: CI inject digest.- Map sai commit. Triệu chứng: số dòng trỏ vào comment. Nguyên nhân: upload sau lần rebuild. Sửa: cùng SHA với image.
- Capture mọi 404. Triệu chứng: 50k event, không bug checkout. Nguyên nhân:
$fetchthrow. Sửa:beforeSend/ ignore list.- Email trong extra. Triệu chứng: incident PII. Nguyên nhân:
JSON.stringify(user)trong breadcrumb. Sửa: scrub; log id.
Nhà tuyển dụng hay hỏi gì
Dùng Sentry trên production thế nào? Một issue 50k event chẳng ai quan tâm; stack prod hiện chunk-7a2.js. Họ muốn sampling, PII, release, và ownership.
Trả lời khi phỏng vấn
Mình coi Sentry là một sản phẩm. Release trùng digest GitOps, map hidden và upload trong CI, beforeSend bỏ 401 và gỡ email. Stack không đọc được là map/release lệch, không phải Vue minify quá mạnh. Mình route issue checkout tới owner checkout. Inbox là regression trên release này, không phải ResizeObserver ba năm từ một extension. Trace giữ quanh 0.15 đến khi mình săn, rồi hạ lại. Mình mở được PR từ SHA trên issue. Đó là cơ chế; chuyện telemetry nói thành lời nằm ở câu observability.
Nhớ nhanh
- release = SHA. Map hidden trong CI. beforeSend scrub PII và 401.
- Sample volume lặp. Giữ uniqueness của issue mới. Route tới owner.
- Stack không đọc được là map lệch, không phải folklore minify.
- Đừng capture mọi 404. Inbox là regression trên release này.
Câu hỏi nối tiếp thường gặp
- Gắn issue Sentry với PR thế nào? release SHA → digest GitOps → PR đã đưa SHA đó lên.
- Vì sao grouping gộp hai bug? Fingerprint quá thô. Thêm transaction / flag, không thêm volume.
- Vì sao map trên CDN public là failure mode? Bạn đã publish source. Chỉ hidden + Sentry.
- Khi nào tăng tracesSampleRate? Khi đang săn incident đã biết. Xong thì hạ lại.
Tóm tắt: Vue không có boundary bọc hết kiểu React. onErrorCaptured bắt render và lifecycle đồng bộ. Hầu hết bug production là async (click, $fetch, watch). Bạn cần ba tầng: app.config.errorHandler / Nuxt vue:error, unhandledrejection + window.error, và try/catch tại chỗ. Cô lập widget bằng <NuxtErrorBoundary> hoặc handler captured local.
Vì sao quan trọng: “Có error boundary không?” Nếu bạn dừng ở onErrorCaptured, họ hỏi nó miss gì: submitOrder(), click handler, và chart làm trắng shell. Khác Q&A React error-boundary-limits: ở đây là API Vue/Nuxt.
- Cách hoạt động:
onErrorCapturedở parent chạy với throw đồng bộ ở descendant lúc render/lifecycle.return falsedừng lan truyền. Luôn báo Sentry trước.return falsekhông báo là fail im lặng. app.config.errorHandler(hook Nuxtvue:error) là lưới cả process. Ghépunhandledrejectionvàwindow.errorcho promise và throw ngoài Vue. Lỗi async setup / Suspense thường vào đây, không phảionErrorCaptured.- Cô lập widget: boundary cho chart kèm fallback. Dialog Teleport có thể thoát cây bạn tưởng đã bọc. Đặt boundary ở host, không chỉ opener. Chrome checkout phải sống sót khi một tile throw.
app.config.errorHandler = (err, _instance, info) => {
Sentry.captureException(err, { extra: { info } })
}
window.addEventListener("unhandledrejection", (e) => {
Sentry.captureException(e.reason)
})
window.addEventListener("error", (e) => {
Sentry.captureException(e.error ?? e.message)
})
// widget: onErrorCaptured + return false sau Sentry; Nuxt: <NuxtErrorBoundary>Điều gì xảy ra: Throw lúc render trong chart có thể được bắt local. Click mà await $fetch reject thì không bao giờ đụng onErrorCaptured. unhandledrejection hoặc try/catch của bạn mới bắt. Shell còn nếu chart return false sau khi báo.
| Miss | Ghép với |
|---|---|
Click / submit / $fetch | try/catch + errorHandler |
| Promise không await | unhandledrejection |
| Script throw ngoài Vue | window.error |
| Child Teleport | Boundary trên host |
Góc nhìn senior
- Fallback widget hay chặn checkout là quyết định product, không phải mặc định framework.
- Boundary React cũng miss async: cùng chỗ tách, API khác. Đừng giả Vue có bọc hết.
- Test boundary: throw lúc render (bắt), throw lúc click (không). Giống mục React.
- Kiểm chứng: chart throw thì header còn; click pay 500 vẫn báo.
Lỗi hay gặp
return falsemà không Sentry. Triệu chứng: widget trắng im lặng. Nguyên nhân: “mình đã handle”. Sửa: báo rồi mới dừng.- Chỉ một boundary cấp app. Triệu chứng: checkout chết cùng chart. Nguyên nhân: không cô lập widget. Sửa:
<NuxtErrorBoundary>quanh tile.- Nhận Vue chạy như React. Triệu chứng: trượt follow-up. Nguyên nhân: folklore API. Sửa: nêu rõ ba tầng.
- Teleport ngoài cây captured. Triệu chứng: dialog throw giết shell. Sửa: boundary trên host đích.
Nhà tuyển dụng hay hỏi gì
Có error boundary không? Nếu bạn dừng ở onErrorCaptured họ sẽ hỏi nó miss gì: submitOrder, click handler, và chart trắng làm sập shell.
Trả lời khi phỏng vấn
Mình không nhận Vue có error boundary kiểu React. onErrorCaptured dành cho render đồng bộ; checkout chết trong click handler, nên mình cũng cài errorHandler, unhandledrejection, và try/catch quanh $fetch. Chart có boundary riêng và mình return false sau Sentry để shell còn. NuxtErrorBoundary cùng ý. Dialog Teleport phải có boundary ở host. Mục React là boundary miss gì; câu này là API Vue mình nối vào.
Nhớ nhanh
- onErrorCaptured là render đồng bộ. Hầu hết bug prod là async.
- Ba tầng: errorHandler, event window, try/catch tại chỗ.
- Cô lập widget. Báo trước khi return false.
- NuxtErrorBoundary cho tile. Teleport ở host.
Câu hỏi nối tiếp thường gặp
- Test boundary thế nào? Throw lúc render (bắt) và lúc click (không). Assert shell sống sót.
- Lỗi Suspense / async setup trông thế nào? Thường đụng errorHandler, không phải onErrorCaptured.
- Vì sao dialog Teleport thoát boundary? Chúng render dưới host, không phải opener.
- React vs Vue, cả hai miss gì? Event handler và async. API khác; lỗ hổng thì không.
Tóm tắt: Theo dõi LCP (Largest Contentful Paint ≤ 2.5 s), INP (Interaction to Next Paint ≤ 200 ms; đã thay FID), và CLS (Cumulative Layout Shift ≤ 0.1). RUM (Real User Monitoring) là nguồn sự thật. Lab (Lighthouse CI) là ngân sách và cổng regression. Chúng sẽ lệch; bạn cần cả hai.
Vì sao quan trọng: “Theo dõi Core Web Vitals thế nào?” Nếu mở bằng FID bạn đã lỗi thời. Họ muốn ngân sách theo template, p75 theo route kể cả soft navigation, và bạn làm gì khi INP tệ, không phải Lighthouse 100 hão. Khác performance 9.2.5 (topic vitals): ở đây là cách bạn báo chúng.
- Cách hoạt động:
web-vitalsonLCP/onINP/onCLSkèm tên route và release. Báo p75 cắt theo template (/vs/app), thiết bị, và flag. Trung bình homepage sẽ giấu checkout. - Lab: Lighthouse CI như ngân sách byte và TBT, không phải điểm 100. TBT mang tính lab; INP là metric field. Soft navigation trong SPA vẫn cần dimension route, không thì bạn chỉ thấy lần load đầu.
- INP tệ → long task > 50 ms, handler input, filter 50k hàng không virtualize. CLS → media chưa size, nhét banner, skeleton không giữ chỗ. LCP → ảnh hero, TTFB, font. Tag third-party load idle hoặc bị kill (Q&A third-party).
import { onINP, onLCP, onCLS } from "web-vitals"
function report(m: { name: string; value: number; id: string }) {
sendToRum({ ...m, route: route.name, release: SHA, flag: flags.checkout_v2 })
}
onINP(report) // field, không phải FID
onLCP(report)
onCLS(report)
// alert p75 so tuần trước trên /checkout, không phải trung bình cả siteĐiều gì xảy ra: Mỗi vital mang route, SHA, và flag để bạn cắt. INP là metric tương tác bạn alert. Trung bình cả site sẽ giấu checkout 400 ms.
| Lab (Lighthouse CI) | RUM (web-vitals) | |
|---|---|---|
| Việc | Ngân sách PR, TBT, byte | Sự thật cho LCP/INP/CLS |
| Fail PR? | Có, theo byte / TBT | Alert p75, không chặn merge |
| Sai khi | Máy lab ≠ user | Quá ít traffic để cắt |
| FID? | Bỏ qua | Bỏ qua: INP đã thay nó |
Góc nhìn senior
- Ngưỡng tốt (2026): INP ≤ 200 ms, LCP ≤ 2.5 s, CLS ≤ 0.1, cắt theo route chứ không lấy trung bình homepage.
- Chặn PR theo ngân sách byte hơn theo điểm Lighthouse. Điểm thì nhiễu.
- Debug INP bằng long task trên panel Performance và attribution INP, không phải 100 hão.
- Kiểm chứng: dashboard p75 theo route; alert tuần này so tuần trước; CI lab khớp byte.
Lỗi hay gặp
- Mở đầu bằng FID. Triệu chứng: bạn lỗi thời. Nguyên nhân: thói quen 2020. Cách xử lý: INP.
- Chỉ lấy trung bình homepage. Triệu chứng: checkout cháy. Nguyên nhân: không có dimension route. Cách xử lý: cắt theo route.
- Skeleton không giữ chỗ. Triệu chứng: bạn mua CLS để giấu loading. Cách xử lý: layout khớp.
- Chặn merge bằng Lighthouse 100. Triệu chứng: CI flaky. Nguyên nhân: nhiễu lab. Cách xử lý: ngân sách byte + TBT.
Nhà tuyển dụng hay hỏi gì
Theo dõi Core Web Vitals thế nào? Nếu mở đầu bằng FID thì bạn lỗi thời. Họ muốn LCP / INP / CLS, RUM so với lab, và ngân sách theo template.
Trả lời khi phỏng vấn
Mình theo dõi LCP, INP, và CLS, không phải FID. RUM là sự thật; Lighthouse là ngân sách CI. Mình cắt p75 theo route và alert regression tuần này so tuần trước. INP tệ thì mình nhìn long task và input handler, không phải điểm hão. Tag third-party load idle, không thì bị kill. Ngưỡng mình nêu: INP 200 ms, LCP 2.5 s, CLS 0.1. Doc performance là định nghĩa vitals; topic này là cách mình báo chúng kèm route và SHA.
Nhớ nhanh
- LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1. FID không còn.
- RUM là sự thật. Lab là ngân sách PR. Cắt p75 theo route.
- Báo bằng web-vitals kèm route, SHA, flag.
- INP → long task. CLS → giữ chỗ. LCP → hero / TTFB.
Câu hỏi nối tiếp thường gặp
- Debug INP thế nào? Long task > 50 ms, input handler, virtualize. Panel Performance, không phải điểm.
- TBT khác INP? TBT là lab. INP là field. Đừng lẫn chúng.
- Vì sao FID là mở đầu sai? Nó đã bị thay. Nói FID là tín hiệu lỗi thời.
- Khi nào chặn PR bằng Lighthouse, khi nào chỉ byte? Byte và TBT. Chặn theo điểm thì flaky.
Tóm tắt: Không console.log trên prod. Client gửi event có cấu trúc: level, name, x-request-id từ BFF, một code lỗi ổn định, flag, release. Không bao giờ access token hay payload đơn hàng. DEBUG tắt mặc định.
Vì sao quan trọng: “Bạn log gì trên client?” Họ muốn rule PII và correlation id để FE + API + trace khâu được với nhau, không phải log sót trong store Pinia. Một 500 mà thành ba ticket là fail correlation. Khác 19.1 (issue Sentry): ở đây là shape của event.
- Cách hoạt động: DEBUG là local hoặc
?debug=và tắt trên prod. INFO rất hiếm (vào checkout, id lần thanh toán, không phải số thẻ). WARN là retry đã hồi phục. ERROR được capture một lần kèm context, không trong vòng lặp. - Log id, không phải payload. Session id ≠ user id. Lấy
x-request-idtừ response BFF rồi gắn vào event tiếp theo và Sentry. SSR in hồ sơ user ra stdout của pod là incident PII. - Replay và log dùng chung một redaction. Đừng log object Axios thô, hãy log
code. Context sát secret (token, cookie) không bao giờ nằm trong breadcrumb.
logger.error("checkout.pay_failed", {
requestId: lastRequestId, // từ x-request-id của BFF
code: err.code, // QUOTE_EXPIRED, không phải object Axios
flag: flags.checkoutV2,
release: SHA,
// không bao giờ: token, email, payload đơn hàng, JSON.stringify(user)
})
// DEBUG tắt trên prod; INFO chỉ là nav + id lần thửĐiều gì xảy ra: Một event, một code, một request id. Grep req_9f trong log BFF khớp Sentry. Số thẻ không rời browser. DEBUG không spam prod.
| Level | Khi nào | Không bao giờ |
|---|---|---|
| DEBUG | Local / ?debug= | Bật mặc định trên prod |
| INFO | Vào checkout, id lần thử | Thẻ, địa chỉ, token |
| WARN | Retry đã hồi phục | Mọi 429 trong vòng lặp |
| ERROR | Một lần, kèm code + requestId | Axios thô / dump user |
Góc nhìn senior
- Sentry hay log trước? Sentry cho exception có stack. Log cho vệt breadcrumb không phải exception.
- Sample INFO ồn là kiểm soát chi phí. ERROR trên checkout không bị sample đến mức mất tín hiệu.
- Correlation thắng “fail với ai đó trên Chrome”.
- Kiểm chứng: một 500 có cùng id trên Sentry, BFF, và event client.
Lỗi hay gặp
console.logsót trong store Pinia. Triệu chứng: token trên console máy dùng chung. Cách xử lý: logger để DEBUG thành no-op trên prod.JSON.stringify(user)trong breadcrumb. Triệu chứng: PII trên Sentry. Cách xử lý: chỉ log id.- Không request id. Triệu chứng: ba ticket cho một 500. Cách xử lý: header BFF, propagate.
- Log mọi retry trong vòng lặp. Triệu chứng: spike chi phí, không tín hiệu. Cách xử lý: đếm + một ERROR.
Nhà tuyển dụng hay hỏi gì
Bạn log gì trên client? Họ muốn event có cấu trúc, rule PII, và correlation id từ BFF, không phải console.log sót trong store Pinia.
Trả lời khi phỏng vấn
Mình không ship console.log. Client gửi event có cấu trúc với x-request-id từ BFF, một code lỗi ổn định, flag, và release. Không bao giờ access token hay payload đơn hàng. DEBUG tắt. Một 500 mà thành ba ticket là bọn mình fail correlation. Sentry giữ stack; log giữ vệt. SSR không được in hồ sơ user ra stdout. Replay và log dùng chung redaction. Đó là shape event; 19.1 là sản phẩm Sentry quanh nó.
Nhớ nhanh
- Event có cấu trúc. Id, không phải payload. DEBUG tắt trên prod.
- Lan truyền x-request-id. Một 500, một grep.
- Không bao giờ log token, email, hoặc JSON.stringify(user).
- Sentry cho stack. Log cho vệt. Cùng redaction với Replay.
Câu hỏi nối tiếp thường gặp
- Sentry hay log trước? Stack trên Sentry. Vệt breadcrumb trên log. Cùng request id.
- Tránh rò PII trên Replay và log thế nào? Một list redaction. Che text. Chỉ id.
- Context sát secret để ở đâu? Không bao giờ trong breadcrumb. Log server, đã scrub.
- JSON.stringify(user) trong breadcrumb sai chỗ nào? PII. Log user id.
Tóm tắt: Chỉ gọi người khi chỗ đau user thấy, đang xảy ra, xử lý được trên path critical, kèm runbook. Một TypeError, CLS 0.12 trên blog lúc 3 giờ sáng, và vách error-rate checkout sau deploy không giống nhau. Chỉ cái vách (và chunk-404 / Nitro crash) mới nên đánh thức ai.
Vì sao quan trọng: “Bạn có gọi người vì cái này không?” Tín hiệu là bạn gọi theo triệu chứng (checkout gãy) chứ không theo nguyên nhân (đếm TypeError), với baseline ngày thường, không phải “> 50 event”. Pager FE ngoài giờ phải hiếm; nếu không, ngưỡng đang sai.
- Cách hoạt động: FE gọi pager khi: lỗi JS checkout/pay/login so với baseline trên release này, chunk 404 / RUM màn trắng trên
/sau deploy, error rate ×checkout_v2, SSR 5xx / vòng crash Nitro, bão refresh auth. - Dùng rate + baseline ngày thường, không phải đếm thô. Gắn release, route, và flag, không thì không cắt được. Pager tới owner dịch vụ kèm link runbook. Crawler
window is not definedlà ticket. - Bắt đầu im và chỉ thêm pager khi đã có runbook. Blip lúc deploy khác spike kéo dài: chờ một cửa sổ deploy, rồi gọi nếu vẫn còn. Khác 18.7 (bạn làm gì khi đã bị gọi): ở đây bạn quyết có gọi hay không.
PAGE nếu đủ:
- route trong {/login, /checkout, /pay}
- error_rate > 3× baseline ngày thường trên release này
- duration > 5 phút (không phải blip deploy)
- đã có runbook
TICKET: một TypeError, CLS 0.12 trên /blog lúc 03:00, crawler SSR window is not defined
payload: release, route, flag, link Sentry, p75 RUM
# gọi owner dịch vụ, không phải #frontendĐiều gì xảy ra: CLS blog lúc 3 giờ sáng không bao giờ reo. Vách checkout năm phút sau một digest, gấp ba baseline, có runbook, thì reo. Payload đã có SHA và flag để 19.6 gạt được flag.
| Gọi pager (on-call) | Ticket / Slack |
|---|---|
| Vách checkout/pay/login so với baseline | Một TypeError |
| Chunk 404 / màn trắng sau deploy | CLS trên blog lúc 3 giờ sáng |
| Nitro crash loop / bão refresh auth | Crawler window is not defined |
| Lỗi cắt lát theo flag × checkout_v2 | Volume INFO nhiễu |
Góc nhìn senior
- Ai on-call cho SPA tĩnh? Vẫn phải có người: HTML trên CDN, digest, flag. “Nó tĩnh” không phải lý do bỏ qua.
- SLO cho FE: availability login/pay, INP p75 trên những route đó. Không phải đếm lỗi cả site.
- Đếm event thô sẽ gọi bạn dậy khi traffic spike. Rate so với baseline thì không.
- Kiểm chứng: một buổi drill gọi pager đúng một lần, có runbook, rồi review nhiễu mỗi tháng.
Lỗi hay gặp
- Gọi
#frontendkhi không có owner. Triệu chứng: không ai tới. Sửa: service owner + runbook.> 50 events. Triệu chứng: Black Friday gọi bạn dậy. Nguyên nhân: không baseline. Sửa: rate × ngày thường.- Gọi pager vì CLS hero chiến dịch lúc 3 giờ sáng. Triệu chứng: mệt vì alert. Nguyên nhân: không lọc critical path. Sửa: ticket.
- Không payload. Triệu chứng: 19.6 không gạt được flag. Sửa: release + route + flag trên alert.
Nhà tuyển dụng hay hỏi gì
Bạn có gọi pager vì chuyện này không? Một TypeError, CLS 0.12 trên blog lúc 3 giờ sáng, và vách error-rate checkout sau deploy: cái nào đánh thức người?
Trả lời khi phỏng vấn
Mình gọi pager khi vách checkout, chunk-404 sau deploy, hoặc Nitro crash loop. Không phải một TypeError, cũng không phải CLS trên blog lúc 3 giờ sáng. Alert mang release, route, và flag, rồi gọi service owner kèm runbook. Mình thà bắt đầu im, rồi thêm pager khi đã có runbook. Rate so với baseline ngày thường, không phải ngưỡng lớn hơn 50 event. Khi đã gọi người, 18.7 là mốc giờ; 19.6 là gạt flag. Topic này hỏi có gọi hay không.
Nhớ nhanh
- Gọi pager theo triệu chứng trên critical path, có runbook và owner.
- Rate so với baseline ngày thường, không phải đếm event thô.
- Gắn release, route, flag. Không thì không cắt lát được.
- TypeError và CLS blog lúc 3 giờ sáng là ticket. Vách checkout thì gọi pager.
Câu hỏi nối tiếp thường gặp
- SLO cho FE? Availability login/pay và INP p75 trên những route đó. Không phải đếm cả site.
- Ai on-call cho SPA tĩnh? Vẫn phải có người chịu trách nhiệm cache HTML, digest, và flag.
- Vì sao đếm event thô là pager dở? Spike traffic trông như incident. Dùng rate.
- Blip lúc deploy khác spike kéo dài, phân biệt thế nào? Chờ một cửa sổ deploy; gọi nếu còn.
Tóm tắt: Bạn rollout flag lên 10% và Sentry nổ. Nếu spike bám theo flag trên critical path, đưa về 0% trước. Giữ evidence. Rồi RCA và rollout lại theo bước. Đừng debug ở 30% và đừng tăng 50% “cho tín hiệu”.
Vì sao quan trọng: Họ muốn gạt trước, telemetry cắt lát, và rollout lại, không phải hai mươi phút debug khi 10% checkout đang cháy. Khác 14.4 (loại flag) và Q&A rollout (chuyện cleanup): ở đây là mốc giờ incident.
- Cách hoạt động: Vẽ errors / INP / conversion cắt theo flag key. Treatment át control trên pay/login → mitigate về 0% hoặc bản trước rồi announce (17.7 / 18.7).
- Ổn định: để ý HTML cache vẫn hiện UI mới (16.2 / 14.1). Giữ link Sentry, SHA, payload flag, một Replay không PII. Đừng xóa evidence lúc đang mitigate.
- RCA, thêm test và alert (19.5), rollout lại 1 → 5 → 25. Ghim bucket SSR để hydrate khớp (14.4). Flag chỉ evaluate trong GTM thì không cắt lát được trên Sentry. Đó là bug thiết kế.
- CLS trên hero chiến dịch vẫn phải gạt flag nếu user thấy. Flag off mà user vẫn crash: HTML cache sót, flag thứ hai, hoặc digest bạn cũng vừa ship.
1. Cắt Sentry/RUM theo flag key + route + release
2. Nếu treatment >> control trên /pay|/login → flag = 0% (đừng debug ở 30%)
3. Announce: bọn mình đã gạt checkout_v2, theo dõi p75 15 phút
4. Giữ: link Sentry, SHA, payload flag, một Replay (PII đã che)
5. Purge HTML nếu CDN vẫn trả UI mới
6. RCA + test + alert; rollout lại 1% → 5% → 25%
7. Nếu flag off mà user vẫn crash: cache, flag thứ hai, hoặc digest
# không bao giờ: tăng 50% cho tín hiệu khi checkout đang cháyĐiều gì xảy ra: Bước 2 cầm máu trước khi bước 6 kịp học gì. Replay bạn giữ là cách chứng minh giá null, không phải đoán. Rollout lại theo bước để spike sau nhỏ hơn.
| Tín hiệu | Làm | Đừng |
|---|---|---|
| Treatment át control trên pay | Flag 0% ngay | Debug ở 30% |
| CLS trên hero chiến dịch | Vẫn gạt nếu user thấy | Chờ tới sáng |
| Flag off, vẫn crash | Cache / flag 2 / digest | Coi như gạt xong là xong |
| Flag chỉ GTM | Không cắt được trên Sentry | Tin dashboard vendor |
Góc nhìn senior
- Gạt trước không phải chống debug. Đó là cách mua một giờ để RCA.
- Test cả hai variant trên CI (14.4) để lần rollout lại không phải cầu may.
- Ghim bucket SSR, không thì chính lần gạt gây spike hydrate mismatch.
- Kiểm chứng: error rate của treatment về control trong 15 phút sau 0%.
Lỗi hay gặp
- Tăng 50% để lấy tín hiệu. Triệu chứng: thiệt hại gấp đôi. Nguyên nhân: tò mò. Sửa: 0%, rồi RCA.
- Xóa issue Sentry lúc đang mitigate. Triệu chứng: không RCA được. Sửa: giữ link và payload.
- Quên cache HTML. Triệu chứng: flag off, UI vẫn còn. Sửa: purge (14.1).
- Flag chỉ trong GTM. Triệu chứng: không cắt lát được. Sửa: evaluate ở BFF, tag Sentry.
Nhà tuyển dụng hay hỏi gì
Bạn rollout flag 10% và Sentry nổ. Kể từng bước đi. Họ muốn gạt trước, telemetry cắt lát, và rollout lại, không phải hai mươi phút debug ở 30%.
Trả lời khi phỏng vấn
Mình gạt flag trước nếu treatment át control trên path pay hoặc login. Mình không debug ở 30% và không tăng 50% để lấy tín hiệu. Sau mitigate mình giữ issue, SHA, và payload flag, rồi RCA và rollout lại 1 rồi 5 rồi 25. Hydration mismatch sau lần gạt flag là bug bucket: mình ghim giá trị SSR. HTML cache có thể giữ UI mới sau 0%; mình purge. CLS trên hero chiến dịch vẫn phải gạt nếu user thấy. 14.4 là các loại flag; câu rollout là cleanup; đây là mốc giờ.
Nhớ nhanh
- Nếu spike bám theo flag trên pay/login, 0% trước.
- Giữ evidence. Rồi RCA. Rollout lại 1 → 5 → 25.
- Purge HTML. Ghim SSR. Đừng tăng 50% để lấy tín hiệu.
- Flag chỉ GTM không cắt được. Evaluate ở BFF.
Câu hỏi nối tiếp thường gặp
- Test cả hai variant trên CI thế nào? 14.4: component test on và off chỗ chặn tiền.
- Nếu spike là CLS, không phải lỗi? Vẫn gạt nếu user thấy trên path chiến dịch.
- Vì sao ghim key bucket SSR? Không thì chính lần gạt hydrate sai và trông như spike mới.
- Flag off mà user vẫn crash: bạn quên gì? Cache HTML, flag thứ hai, hoặc digest.