Backward Compatibility: Nghệ Thuật Nâng Cấp Hệ Thống Không Làm "Vỡ Trận" Mobile App
Chào anh em! Trong đời làm Backend, chắc hẳn anh em đã từng trải qua khoảnh khắc này: Bạn vừa deploy một bản nâng cấp API cực kỳ xịn sò. Code chạy mượt, Web Frontend load nhanh như chớp. Bạn xoa tay hài lòng.
Nửa tiếng sau, team Mobile xông vào chửi bới ầm ĩ: "Trời ơi, app trên iOS và Android crash hàng loạt rồi! Ông vừa đổi cái key user_id thành account_id đúng không???" Đó là hậu quả của một Breaking Change (Thay đổi phá vỡ).
Và liều thuốc giải duy nhất cho cơn ác mộng này mang tên: Backward Compatibility (Tính tương thích ngược). Hôm nay, bằng những kinh nghiệm "đổ máu", tôi sẽ chỉ cho anh em cách bước về phía trước mà không bỏ lại bất kỳ ai ở phía sau.
1. Bản chất của Backward Compatibility là gì?
Hiểu một cách dân dã nhất: Giống như việc máy PlayStation 5 vẫn có thể bỏ đĩa game của PlayStation 4 vào chơi ngon lành.
Trong thiết kế API/Backend, Backward Compatibility nghĩa là: Khi bạn tung ra phiên bản API mới (Version 2), nó vẫn phải chấp nhận những Request (dữ liệu gửi lên) theo cấu trúc cũ, và vẫn phải trả về Response (dữ liệu phản hồi) mà các Client phiên bản cũ (Version 1) có thể hiểu được.
2. Nỗi đau mang tên "Client không chịu Update"
Anh em làm Web thường hay bị "ảo tưởng" về tốc độ cập nhật. Bạn sửa code API, bạn báo team Web sửa JS, deploy cùng lúc là xong. Sự thật là Web luôn tải code mới nhất mỗi khi F5.
Nhưng thế giới của Mobile App và Third-party API (bên thứ 3 tích hợp) lại là một hiện thực phũ phàng:
- Để lên bản cập nhật mới, phải chờ Apple App Store / Google Play duyệt mất vài ngày.
- Người dùng cực kỳ lười bấm Update.
- Hệ quả: Tại một thời điểm, server của bạn đang phải hầu hạ cùng lúc App v1.0 (từ năm ngoái), App v1.5 (tháng trước) và App v2.0 (vừa ra mắt).
Nếu bạn thẳng tay xóa một cột trong Database, hoặc đổi kiểu dữ liệu từ Int sang String, App cũ gọi lên không parse được JSON -> App Crash (Văng ứng dụng) ngay lập tức.
3. 3 "Nguyên Tắc Vàng" Của Nghệ Thuật Tương Thích Ngược
Để sống sót qua những đợt nâng cấp lớn, hãy ghim chặt 3 nguyên tắc này vào đầu:
Nguyên tắc 1: Thêm vào chứ đừng Sửa/Xóa (Expand & Contract Pattern)
Giả sử hệ thống cũ trả về "full_name": "Nguyen Van Teo". Nay sếp yêu cầu tách ra làm "first_name" và "last_name".
- Cách Junior làm (Breaking Change): Xóa luôn
full_name, trả vềfirst_namevàlast_name. -> App cũ crash vì tìm không thấyfull_nameđể hiển thị. - Cách Senior làm: Thêm cột
first_namevàlast_namevào DB. API trả về CẢ 3 TRƯỜNG:
{
"full_name": "Nguyen Van Teo", // Giữ lại cho App cũ chạy
"first_name": "Teo", // Cho App mới
"last_name": "Nguyen Van" // Cho App mới
}
Cứ để đó, code có vẻ "thừa thãi" một chút, nhưng hệ thống bình yên vô sự.
Nguyên tắc 2: Lỏng lẻo ở đầu vào (Tolerant Reader) Khi viết API nhận dữ liệu (POST/PUT), đừng bao giờ quăng lỗi HTTP 500 nếu Client gửi lên một trường lạ hoắc mà bạn không định nghĩa. App cũ có thể gửi lên những trường dư thừa. Trách nhiệm của Backend là chỉ nhặt những thứ mình cần để validate và xử lý, cái nào lạ thì phớt lờ đi (Ignore unknown fields).
Nguyên tắc 3: API Versioning (Phân mảnh ngay từ Day 1)
Đừng đợi đến lúc API phình to mới nghĩ đến Version. Hãy luôn thiết kế API có chứa version: https://api.domain.com/v1/users.
Khi hệ thống thay đổi kiến trúc tận gốc (ví dụ đổi logic thanh toán hoàn toàn mới không thể tương thích ngược), đó là lúc bạn đẻ ra /v2/users. Bắt team Mobile code cái mới theo V2, trong khi V1 vẫn sống song song phục vụ người dùng cũ.
4. Vòng đời của một "Trường Dữ Liệu Chết" (Deprecation)
Bạn không thể cứ ôm mãi cái "full_name" cũ kỹ kia được, database sẽ thành một bãi rác. Vậy làm sao để "giết" nó an toàn?
- Mark as Deprecated: Đánh dấu trường đó là
@Deprecatedtrong code và API Documentation. - Theo dõi Log: Bật theo dõi xem số lượng Request từ App cũ truy cập vào cái API chứa
full_namecòn nhiều không (Ví dụ dùng ELK / Datadog). - Cảnh báo (Force Update): Khi nhận thấy lượng user dùng App cũ chỉ còn dưới 1%, gửi một thông báo (Push Notification) ép họ phải update App lên bản mới nhất thì mới cho dùng tiếp.
- Contract (Thu hồi): Rút ống thở! Tự tin xóa cái
full_namera khỏi source code và Database.
Lời kết
Viết code để cho App mới chạy được là chuyện quá dễ. Viết code mới mà App từ 2 năm trước vẫn sống nhăn răng mới gọi là đẳng cấp.
"Backward Compatible" không chỉ là một kỹ thuật viết code, nó là sự thấu cảm của Backend Developer dành cho người dùng cuối và các team Frontend/Mobile. Hãy nhớ: Chậm lại một nhịp ở Backend, là giữ lại hàng ngàn user ở Frontend!
All rights reserved