Bài này hơi lạc flow. Đúng ra sau phần nhập môn mình nên đi vào SDLC, Agile rồi Test Process, sau đó mới đến Error, Defect và Failure.

Nhưng nếu chưa hiểu ba cái này thì rất khó giải thích tại sao testing lại quan trọng. Vậy nên cứ chen nó lên trước đã, SDLC để bài sau.

Mối quan hệ giữa Error, Defect và Failure

Ở công ty, mọi người thường gom hết vào một câu:

Đoạn này lỗi rồi.

Nói chuyện hằng ngày như thế không vấn đề gì, người trong team vẫn hiểu nhau. Nhưng khi học hoặc phân tích một incident, chữ “lỗi” có thể đang chỉ hành động làm sai của một người, thứ sai nằm trong code, hoặc hành vi sai mà người dùng vừa nhìn thấy. Gom cả ba vào một thì rất khó lần xem chuyện bắt đầu từ đâu.

Flow nôm na sẽ là:

Root causeHuman errorDefect trong workproductFailure khi chạyImpactTừ nguyên nhân đến thiệt hại
// Mermaid diagram

Một nguyên nhân nào đó khiến con người làm sai. Cái sai đó để lại một defect trong requirement, code, test case hoặc configuration. Khi gặp đúng điều kiện, defect bị kích hoạt và phần mềm failure. Failure đi vào production thì bắt đầu có impact: mất tiền, mất dữ liệu, mất khách, hoặc nặng hơn là ảnh hưởng an toàn.

Đây là flow để dễ hình dung, không phải công thức một-một. Một failure to có thể là kết quả của cả đống defect và vấn đề tổ chức chồng lên nhau.

Typical question: How are error, defect and failure related?

A human error may introduce a defect into a work product. When the defect is triggered under particular conditions, it may cause an observable failure and create an impact.

Root cause

Root cause là nguyên nhân gốc. Nếu xử lý được nó thì khả năng cùng một kiểu vấn đề lặp lại sẽ giảm đi.

Giả sử dev quên validate số tiền âm. Viết trong bug report rằng “dev quên validate” mới chỉ mô tả error. Hỏi thêm một lúc có thể lòi ra requirement không nói miền giá trị, team không có checklist validation, codebase không có component dùng chung và buổi review chỉ chạy qua happy path.

Sửa thêm một câu if sẽ xử lý defect hiện tại. Còn nếu nguyên nhân gốc vẫn nguyên thì feature sau có thể lại dính đúng kiểu đó.

Không phải bug nào cũng cần họp năm lần để Root Cause Analysis. Nhưng bug nghiêm trọng hoặc bug cứ đóng rồi lại mọc thì chỉ fix phần ngọn là chưa đủ.

Error

Error là sai sót do con người tạo ra: hiểu sai, quên, nhập nhầm, chọn nhầm hoặc quyết định sai. Nó chưa phải một vật thể nằm trong source code. Nó là hành động đã tạo ra kết quả không đúng.

Nói triết học một tí thì error không có hình thù. Ta không mở repository lên rồi chỉ vào một cục “error” được. Thứ nhìn thấy trong repository là defect do error để lại.

Ví dụ ở requirement

Mày paste template vào requirement nhưng quên sửa rule hả em?

Vâng, em đọc lại mới thấy vẫn còn rule của dự án cũ.

Cái quên sửa là error. Requirement ghi sai sau đó là defect.

Ví dụ ở source code

Mày chưa handle trường hợp API trả về null à?

Bỏ mẹ, em nghĩ lúc nào nó cũng có data.

Cái nghĩ sai về dữ liệu đầu vào là error. Nhánh code thiếu xử lý null là defect. Chạy đúng case đó mà app lăn ra crash thì ta nhìn thấy failure.

Ví dụ ở test case

Hình như bộ test này thiếu boundary dưới?

Toang, em chia partition xong quên lấy giá trị biên.

Áp dụng test technique thiếu là error. Bộ test bị thiếu case cần thiết là defect trong testware. Nó không tự sinh thêm defect vào sản phẩm, nhưng nó làm cho hàng rào bắt defect yếu đi.

Nguyên nhân khiến con người sai thì nhiều: thiếu kiến thức, requirement mơ hồ, mệt, deadline dí, công cụ khó dùng, đang làm thì cô giáo gọi báo con ngã đập mặt vào cạnh bàn. Vì thế cách giảm error không thể chỉ là dặn nhau “lần sau cẩn thận”.

Requirement cần review, code cần unit test và code review, test case cần peer review. Ăn đủ ngủ đủ cũng có tác dụng, đùa nhưng thật.

Typical question: What is an error?

An error is a human action that produces an incorrect result.

Defect

Defect là thứ sai đã nằm lại trong một work product. Nó có thể nằm trong requirement, design, source code, test case, script deploy hay configuration.

Lấy lại ba ví dụ bên trên:

ErrorDefect để lại
BA quên sửa rule từ templateRequirement chứa business rule của dự án khác
Dev cho rằng API luôn trả dataCode không xử lý null
Tester quên lấy boundary dướiBộ test thiếu một trường hợp biên quan trọng

Đây là chỗ bài cũ của mình phân biệt bug là lỗi trong code còn defect là lỗi trong tài liệu. Cách chia đó không có quy ước chung và không nên dùng như định nghĩa.

Trong phần lớn team, bug, defect và đôi khi fault được dùng qua lại cho nhau. Công ty nào có glossary riêng thì theo glossary của công ty đó. Không có thì cứ nói rõ: defect trong requirement, defect trong code, defect trong test case. Thế là hết tranh cãi.

Typical question: What is a defect?

A defect is an imperfection in a work product that may cause the software to fail when it is triggered.

Failure

Failure là lúc component hoặc system không thực hiện được chức năng như yêu cầu trong khi đang chạy.

Giả sử code tính giá trị trung bình bằng tổng tiền chia cho số sản phẩm nhưng không xử lý quantity = 0:

Defect có thể nằm im rất lâu. Nếu không ai tạo ra đơn hàng rỗng thì nhánh code kia chưa chạy và failure chưa xuất hiện. Đây cũng là lý do không thể nói “production chạy ba tháng rồi, chắc không còn bug”. Có thể chỉ là chưa gặp đúng điều kiện.

Ngược lại, một failure không phải lúc nào cũng quy gọn về một dòng code. Network chết, cấu hình sai, ổ cứng hỏng, dữ liệu bẩn hoặc mấy service hiểu khác nhau cũng làm cả hệ thống ngã được.

Severity và Priority

Nói đến defect thì sớm muộn cũng phải đánh Severity và Priority.

Hai cái thường đi cùng nhau, nhưng không phải lúc nào cũng bằng nhau.

Một bug làm app mobile tràn bộ nhớ rồi crash sau khi chạy liên tục 20 tiếng có Severity cao vì crash hẳn. Nhưng nếu flow đó rất hiếm, có workaround và team đang phải sửa lỗi thanh toán trước thì Priority của nó có thể thấp hơn.

Ngược lại, banner chiến dịch trên trang chủ bị trắng nền nhưng vẫn bấm được. Về chức năng thì Severity thấp. Tuy nhiên chiến dịch chạy vào sáng mai, logo đối tác đang phơi giữa trang chủ, vậy Priority có thể bị đẩy lên cao nhất.

Các mức Critical, High, Medium, Low hay P0, P1, P2 tuỳ dự án sẽ định nghĩa khác nhau. Đừng học một bảng trên mạng rồi mang sang mọi công ty. Có nơi P0 nghĩa là sửa ngay trong đêm, có nơi P0 lại là priority mặc định của smoke test.

Typical question: What is the difference between severity and priority?

Severity describes the impact of a defect. Priority describes how urgently it should be fixed. A severe defect may have a lower priority, and a minor defect may be urgent because of business timing.

Một số failure kinh điển

Đọc mấy case này không phải để kết luận kiểu “giá mà có tester”. Các hệ thống lớn đương nhiên có kiểm tra. Vấn đề là nhiều lớp kỹ thuật và tổ chức cùng thủng, sau đó một defect mới có đường đi thẳng ra thiệt hại thật.

Therac-25

Therac-25 là máy xạ trị được dùng trong thập niên 1980. Sáu tai nạn được biết đến trong giai đoạn 1985–1987 khiến bệnh nhân nhận liều bức xạ quá mức, gây thương tích nặng và tử vong.

Điều tra của Nancy Leveson và Clark Turner không quy câu chuyện về một coder gõ sai. Nó có software design, race condition, thiếu cơ chế khoá an toàn độc lập, thông báo lỗi khó hiểu, điều tra incident kém và niềm tin quá lớn rằng phần mềm đã an toàn.

Với hệ thống có thể ảnh hưởng tính mạng, test thêm vài happy path là không đủ. Nó còn cần fail-safe design, hardware interlock, hazard analysis và quy trình phản ứng khi sự cố xuất hiện.

Patriot tại Dhahran

Ngày 25/02/1991, một hệ thống Patriot tại Dhahran không theo dõi và đánh chặn được tên lửa Scud. Tên lửa đánh trúng doanh trại, làm 28 quân nhân Mỹ thiệt mạng.

Báo cáo của U.S. GAO chỉ ra sai số trong phép tính thời gian tăng dần khi hệ thống chạy lâu. Một bài test ngắn có thể thấy mọi thứ vẫn ngon, nhưng để hệ thống chạy hàng chục giờ thì sai số đã đủ lớn để việc tracking mục tiêu bị lệch.

Case này là ví dụ rất rõ cho chuyện test environment và thời lượng test phải giống cách hệ thống được vận hành ngoài đời. Test năm phút rồi kết luận một hệ thống chạy liên tục ổn định thì hơi liều.

Knight Capital

Ngày 01/08/2012, Knight Capital deploy phần mềm không đầy đủ lên các server. Code cũ bị kích hoạt và hệ thống bắt đầu gửi lệnh bất thường ra thị trường.

Theo SEC, trong 45 phút hệ thống gửi hơn bốn triệu lệnh, giao dịch hơn 397 triệu cổ phiếu và làm công ty thiệt hại trên 460 triệu USD.

Ở đây cũng không phải chỉ có một bug. Deployment control có vấn đề, testing không chặn được, giới hạn rủi ro không đủ và email cảnh báo đã xuất hiện nhưng không được xử lý kịp. Một defect đi qua hết các lớp bảo vệ thì 45 phút là đủ để công ty gần bay màu.

Kết

Chốt lại:

Root cause → Error → Defect → Failure → Impact

Con người làm sai, cái sai để lại defect, defect gặp đúng điều kiện thì tạo failure, còn failure gây thiệt hại ra sao là impact. Flow thực tế có thể phức tạp hơn, nhưng nắm được khung này thì về sau đọc bug report, làm Root Cause Analysis hay phân biệt Severity với Priority sẽ đỡ lẫn.

Bài tiếp theo quay lại flow ban đầu: Software Development Life Cycle. Chúng ta sẽ xem requirement, design, code, test và release xuất hiện ở đâu, rồi testing chen vào từ đoạn nào.

Nguồn tham khảo