Building and Open-Sourcing Wix's A/B Testing System

Bài tóm tắt Wix 2015 2. Tăng trưởng Độ khó: Trung bình

Wix · 2015 · 2. Tăng trưởng · Độ khó: Trung bình — bài gốc

Bối cảnh

  • Wix làm continuous delivery: code đẩy lên là chạy production. Theo Wix, vì code tồi cũng lên production ngay, một hệ thống experiment (A/B test và feature toggle) là điều kiện cần của CD: đánh dấu code là thử nghiệm, bật/tắt cho một nhóm người dùng.
  • Sau hai thế hệ hệ thống experiment, Wix viết thế hệ thứ ba từ đầu: PETRI (Product Experiments and Toggles Report Infrastructure), làm TDD hoàn toàn, định mở mã nguồn từ đầu.

Thay đổi chính

  • Một khái niệm cho cả hai: mọi A/B test (có trạng thái, ghi nhận ai đã tham gia) và feature toggle (không trạng thái) đều là một Experiment với danh sách eligibility filter (ngôn ngữ, địa lý, user id, ngày đăng ký). Đủ điều kiện thì nhận A hoặc B theo tỉ lệ; không đủ thì nhận giá trị dự phòng (thường là A).
  • Server quản lý, client tự quyết: PETRI server lưu experiment; thư viện Laboratory nhúng trong mỗi ứng dụng lấy danh sách experiment đang chạy và tự chạy thuật toán tung đồng xu và lọc theo ngữ cảnh request (ví dụ đọc header Accept-Language).
  • Mở mã nguồn: tách module lõi (dùng chung giữa repo công khai và nội bộ) khỏi module tích hợp với hệ thống riêng của Wix (vòng đời request, RPC); bản công khai có module cho servlet-api. Test hợp đồng chạy cùng bộ test trên fake server (RAM) và server thật (H2) nên tách module "gần như không đau".
  • Sự cố forward compatibility: experiment được serialize gửi xuống client, và client bỏ qua trường lạ, nhưng không tính tới loại filter mới. Khi server tạo experiment đầu tiên có loại filter mới, mọi service chưa deploy bản mới đều lỗi deserialize và kẹt ở danh sách experiment cuối cùng đọc được, không nhận cập nhật nào nữa. Giảm nhẹ: dừng experiment đó (server chỉ gửi experiment đang chạy). Sửa hẳn: loại filter lạ được deserialize thành UnrecognizedFilter; experiment chứa nó bị coi là không hợp lệ (không ai đủ điều kiện), khi bị gọi thì báo lỗi cho chủ service và trả giá trị dự phòng.

Vì sao

  • Không thể bắt mọi service deploy trước khi thêm loại filter mới, vì với CD luôn có hai phiên bản cùng chạy; với thư viện dùng ở mọi service, lỗi tương thích lan rộng hơn nhiều so với lỗi của một service, và chủ thư viện không tự rollback giùm ai được.
  • Chọn "không hợp lệ, trả dự phòng" thay vì "bỏ qua filter lạ": bỏ qua filter có thể vô tình đưa người dùng vào thí nghiệm mà họ không đủ điều kiện.

Bài học cho SWE

  • A/B test và feature toggle dùng chung một cơ chế giúp CD an toàn hơn: tính năng mới luôn đi sau một cổng bật/tắt được.
  • Forward compatibility không chỉ là bỏ qua trường lạ: kiểu/biến thể mới (enum, loại filter) cũng phải có đường xử lý ở client cũ, và đường đó phải thất bại về phía an toàn.
  • Thư viện nhúng ở mọi service là nơi một lỗi tương thích nhỏ thành sự cố diện rộng.

Liên quan