Khi ứng dụng còn nhỏ, việc sửa database thường rất đơn giản: thêm một cột, đổi kiểu dữ liệu, xóa một bảng không dùng nữa. Nhưng khi web/app đã có người dùng thật, dữ liệu thật và nhiều phiên bản ứng dụng cùng truy cập, database migration không còn là chuyện “chạy một lệnh là xong”. Một thay đổi thiếu cẩn thận có thể làm API lỗi, app cũ bị văng, hoặc dữ liệu bị mất khó khôi phục. Database migration là quá trình thay đổi cấu trúc dữ liệu theo cách có kiểm soát: thêm bảng, thêm cột, đổi index, tách dữ liệu, di chuyển dữ liệu cũ sang dạng mới. Mục tiêu không chỉ là “database đúng thiết kế mới”, mà còn là hệ thống vẫn chạy ổn trong lúc thay đổi. Một nguyên tắc quan trọng: ưu tiên thay đổi tương thích ngược. Ví dụ, thay vì đổi ngay cột full_name thành first_name và last_name rồi xóa cột cũ, ta có thể làm theo nhiều bước: - Bước 1: thêm cột first_name và last_name, vẫn giữ full_name. - Bước 2: cập nhật code mới để ghi đồng thời cả dữ liệu cũ và mới, hoặc ghi sang cấu trúc mới nhưng vẫn đọc được cấu trúc cũ. - Bước 3: chạy job chuyển dữ liệu cũ sang cột mới. - Bước 4: sau khi chắc chắn không còn code nào phụ thuộc full_name, mới xóa cột cũ trong một đợt migration riêng. Cách làm này có vẻ dài hơn, nhưng giảm rủi ro rất nhiều, đặc biệt khi backend, frontend, mobile app và worker không được deploy cùng lúc. Một ví dụ thực tế khác là thêm cột bắt buộc, chẳng hạn orders.delivery_address_id. Nếu thêm ngay với ràng buộc NOT NULL, migration có thể thất bại vì dữ liệu cũ chưa có giá trị. Cách an toàn hơn là thêm cột nullable trước, cập nhật code để bắt đầu ghi dữ liệu mới, backfill dữ liệu cũ, kiểm tra không còn dòng nào null, cuối cùng mới thêm constraint NOT NULL. Với bảng lớn, cần đặc biệt cẩn thận với các thao tác nặng như tạo index, đổi kiểu cột, hoặc cập nhật hàng loạt. Những thao tác này có thể khóa bảng hoặc làm database chậm đáng kể tùy hệ quản trị và cách cấu hình. Nếu có thể, hãy chia nhỏ việc cập nhật dữ liệu theo lô, chạy ngoài giờ cao điểm, theo dõi tải hệ thống và chuẩn bị phương án dừng lại nếu có dấu hiệu bất thường. Một vài lời khuyên có thể áp dụng ngay: - Luôn lưu migration vào version control, không sửa database thủ công mà không ghi lại. - Tách migration thay đổi schema và migration chuyển dữ liệu lớn nếu có thể. - Kiểm thử migration trên dữ liệu gần giống môi trường thật, không chỉ trên database rỗng. - Có kế hoạch rollback, nhưng đừng giả định rollback luôn dễ, nhất là khi đã xóa hoặc biến đổi dữ liệu. - Không xóa cột, bảng, API field ngay khi code mới vừa deploy; hãy chờ chắc chắn không còn thành phần nào sử dụng. - Ghi log hoặc thống kê tiến trình với các job backfill lớn để biết đang chạy đến đâu. Điểm mấu chốt là xem database migration như một phần của quy trình phát hành sản phẩm, không phải công việc phụ của lập trình viên backend. Một migration tốt nên nhỏ, rõ mục đích, có thể theo dõi, hạn chế khóa hệ thống và tương thích với nhiều phiên bản code trong giai đoạn chuyển tiếp. Làm chậm hơn một chút ở bước chuẩn bị thường rẻ hơn rất nhiều so với việc phải khôi phục dữ liệu hoặc xử lý sự cố trên production.