- Một ví dụ về việc bổ sung tùy chọn –dry-run trong quá trình phát triển ứng dụng tạo báo cáo để có thể mô phỏng kết quả thực thi
- Tùy chọn này in ra những tác vụ sẽ được thực hiện mà không tạo ra thay đổi thực tế, giúp kiểm thử an toàn và phản hồi nhanh
- Nhờ đó, lập trình viên có thể kiểm tra ngay cấu hình, khả năng truy cập và trạng thái hệ thống, đồng thời dùng nó như một công cụ xác minh hằng ngày
- Nhược điểm được nêu là độ phức tạp tăng nhẹ do cần kiểm tra cờ dryRun trong mã
- Với các ứng dụng dạng lệnh, chức năng –dry-run rất hữu ích, và triển khai càng sớm trong dự án thì hiệu quả phát triển càng cao
Bối cảnh
- Ứng dụng tạo báo cáo đang được phát triển mới có cấu trúc: mỗi ngày làm việc sẽ tạo báo cáo, nén báo cáo rồi tải lên máy chủ sftp, kiểm tra phản hồi lỗi và gửi email thông báo
- Các tệp được tạo ở từng bước và các tệp phản hồi được chuyển đến các thư mục khác nhau theo từng giai đoạn
- Ở giai đoạn đầu phát triển, tác giả nhớ đến Subversion và tùy chọn –dry-run của nhiều lệnh Linux, nên đã thêm chức năng tương tự
- Tùy chọn này sẽ in ra điều gì sẽ xảy ra khi chạy mà không tạo thay đổi thực tế
- Khi chạy với –dry-run, hệ thống sẽ in theo từng bước các báo cáo sẽ được tạo và bị loại trừ, các tệp sẽ được nén và di chuyển, cũng như các tệp mục tiêu để tải lên và tải xuống qua sftp
Cách dùng và lợi ích
- Đây là một chức năng hữu ích đến mức được dùng hằng ngày trong quá trình phát triển và kiểm thử
- Khi dùng để kiểm tra trạng thái trước khi chạy, có thể xác nhận ngay cấu hình, khả năng truy cập và trạng thái hệ thống
- Vì không có thay đổi thực tế nên có thể chạy một cách an toàn
- Sau khi thay đổi ngày trong tệp trạng thái báo cáo, có thể xác nhận ngay liệu báo cáo đó có được tạo hay không
- Việc tạo báo cáo thực tế mất thời gian, nhưng dry-run cung cấp phản hồi nhanh
- Ngay cả khi kiểm thử toàn bộ hệ thống, nó cũng cung cấp một vòng lặp xác minh nhanh
Nhược điểm
- Do phải lặp lại việc kiểm tra cờ dryRun trong mã, nên có phát sinh một chút làm mã kém gọn
- Ở mỗi bước chính, cần kiểm tra cờ để chỉ in log thay vì thực thi thật
- Tuy nhiên việc kiểm tra này không đi quá sâu, và bên trong logic tạo báo cáo không cần xử lý riêng
- Chỉ cần kiểm tra ở tầng trên, nơi quyết định có thực thi hay không
Kết luận
- Các ứng dụng được chạy theo kiểu mệnh lệnh và tạo ra kết quả rất phù hợp với tùy chọn –dry-run
- Ngược lại, nó không phù hợp với các ứng dụng phản ứng chờ nhận thông điệp
- Việc thêm tính năng này ngay từ đầu dự án đã giúp cải thiện hiệu quả phát triển rất nhiều
- Đây không phải là chức năng cần thiết trong mọi tình huống, nhưng được đánh giá là một công cụ rất hữu ích khi dùng đúng chỗ
1 bình luận
Ý kiến trên Hacker News
Khi tương tác với hệ thống có trạng thái, ngay cả
--dry-runcũng có thể gặp race conditionCông cụ cho thấy “nó sẽ làm gì” ở trạng thái hiện tại, nhưng đến lúc chạy thật thì tình hình có thể đã thay đổi
Vì vậy tôi thích cách làm của chế độ “plan” của Terraform hơn. Chế độ này tạo ra một kế hoạch có thể thực thi, và nếu các giả định tại thời điểm lập kế hoạch thay đổi thì có thể dừng lại hoặc rollback
Ngoài ra cũng không cần chèn
if dry_run:khắp nơi trong code, mà có thể tách phần lập kế hoạch và thực thi để đơn giản hóa thành dạngexecute(plan())Vấn đề về thời điểm giữa DNS Planner và Enactor đã khiến một kế hoạch cũ ghi đè lên kế hoạch mới nhất
Nội dung liên quan cũng từng được bàn trong thread HN trước đó
Vì để làm chế độ plan thì cần một DSL hoặc cấu trúc dữ liệu chuyên biệt cho miền đó
(1) Chụp trạng thái file system rồi lưu kế hoạch → (2) kiểm tra xem trạng thái có thay đổi không rồi thực thi và ghi log → (3) so sánh với trạng thái trước đó để xác minh có mất dữ liệu hay không
Tôi đang tách ba bước này thành các script hoặc flag riêng để dùng
rmnhư thế nàoKhi một công cụ không có
--dry-run, đôi khi tôi tự tạo lấyVí dụ trước khi chạy một lệnh
sedphức tạp, tôi dùngdiffđể so sánh trước các thay đổiCó thể kiểm tra khác biệt theo kiểu
diff -u <(echo "hello") <(echo "hello" | sed "s/hello/hi/g")Tôi có tổng hợp thêm trong blog của mình
Tôi thích pattern
--dry-run, nhưng code ở đường dry phải hoạt động giống hệt đường chạy thậtNếu chỉ in ra “sẽ làm gì” rồi bỏ qua logic thực tế, thì khi chạy thật có thể bỏ sót bug
Ít nhất nên chạy giống hệt cho đến ngay trước khi ghi vào database hoặc gọi API
Dry-run là để cho thấy “điều gì sẽ xảy ra”, còn test thực sự là một phạm vi khác
Tôi thì ngược lại, thích dùng các flag như
--commithoặc--execute, còn mặc định sẽ là chế độ chỉ đọc (dry)Cách này giúp giảm khả năng vô tình gây ra thay đổi thật
--committhì thay đổi mới xảy raTrong khi đó đã có nhiều lần người ta quên
--dry-runrồi gây ra sự cốMặc định chỉ so sánh, và chỉ khi dùng
--executemới thực sự thay thế bằng hard linkNhững bước xác nhận kiểu này rất hiệu quả để giảm sai sót
--wet-run. Tùy ngữ cảnh mà một flag mang nghĩa ngược lại đôi khi trực quan hơnDELETE-ACCOUNTTừ trước đến nay tôi chưa từng vô tình xóa tài khoản lần nào
Để tránh làm bẩn code, nên tách persistence thành một chiến lược có thể inject được
Việc chèn
if dry_run:khắp nơi là không tốtThực ra sẽ an toàn hơn nếu việc chạy production được ghi rõ bằng
--wet-runNhư vậy chỉ cần quyết định có dry-run hay không tại đúng một điểm — theo kiểu “functional core, imperative shell”
rm --wet-run tempfile.tmpthì khá phiềnTôi thích mặc định là chạy thật hơn, và thay vào đó có
--undođể hoàn tác thao tác cuối cùng--wet-run, nhưng cũng từng dùng cách mặc định làdry-runvà chỉ khi ghi rõ--no-dry-runthì thay đổi mới xảy raNếu là service thì lý tưởng nhất là tự chọn safe mode theo môi trường chạy (dev/prod)
Bài viết nói rằng “ban đầu chỉ thêm
--dry-run, nhưng hóa ra lại rất hữu ích”,nhưng thực ra những flag kiểu này thường được AI coding agent (ví dụ Claude) tự động gợi ý
Có lẽ việc ngày nay nhiều công cụ CLI có pattern tương tự cũng một phần do hiện tượng hội tụ mã nguồn dựa trên agent
--dry-runcủa Subversion, nên đó vẫn là một lý do khá thuyết phụcTôi thường thêm flag
--reallyvào các tiện ích CLI, và để mặc định ở chế độ chỉ đọcMục đích là buộc phải có bước xác nhận có chủ đích để tránh sai sót
--i-meant-that.Đây là lệnh xóa máy từ xa, và mặc định sẽ chờ 10 giây để cho cơ hội hủy
May là flag này chưa từng bị dùng nhầm lần nào
Một trong những điểm hay của PowerShell là chỉ cần thêm một dòng
[CmdletBinding(SupportsShouldProcess)]là có thể tự động dùng tính năng dry-run
-WhatIf. Đây là một tính năng rất tiện-Confirmcũng được bật cùng lúc, và có thể tương tác với ngưỡng xác nhận của người dùng qua hàmShouldProcess. Thiết kế này thực sự rất hayTrong CLI nội bộ mà tôi quản lý, tôi đặt
if not dry_run:bên trong phần gọi REST APILàm vậy thì thay vì gọi thật, nó sẽ để lại log lệnh CURL để có thể kiểm tra request nào sẽ được gửi đi
Nhưng khi sự liên kết giữa các API trở nên phức tạp thì việc mô phỏng cũng trở nên khó khăn, và sẽ phức tạp hơn rất nhiều so với một câu
if not dry_run:đơn giảnTôi cũng bảo trì khá nhiều CLI cho pipeline tự động hóa, và áp dụng pattern này cho gần như mọi công cụ
Quan trọng là trước hết phải làm tốt công cụ local đã
Nếu flag
--dry-runđang bị rải khắp codebase, thì nên áp dụng state machine pattern để tách rõ từng bước