1 điểm bởi GN⁺ 2026-02-02 | 1 bình luận | Chia sẻ qua WhatsApp
  • 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

 
GN⁺ 2026-02-02
Ý kiến trên Hacker News
  • Khi tương tác với hệ thống có trạng thái, ngay cả --dry-run cũng có thể gặp race condition
    Cô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ạng execute(plan())

    • Sự cố AWS DNS outage trước đây cũng là do một race condition tương tự
      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 đó
    • Làm như vậy rốt cuộc cũng tương đương với việc triển khai compiler (đặc tả → kế hoạch)virtual machine (kế hoạch → thực thi)
    • Cách này lý tưởng cho các công cụ hạ tầng như Terraform hay Ansible, nhưng với các công cụ report đơn giản thì có thể gây ra độ phức tạp quá mứ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 đó
    • Tôi cũng đang làm một script sửa các file nhạy cảm và đang áp dụng cách này
      (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
    • Vì thế tôi cũng tò mò không biết có thể áp dụng kiểu chế độ plan này cho lệnh rm như thế nào
  • Khi một công cụ không có --dry-run, đôi khi tôi tự tạo lấy
    Ví dụ trước khi chạy một lệnh sed phức tạp, tôi dùng diff để so sánh trước các thay đổi
    Có 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ật
    Nế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

    • Nhưng có người cho rằng như vậy là đang nhầm lẫn giữa integration test và dry-run
      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ư --commit hoặ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

    • Tôi đã dùng cách này suốt 8 năm qua, và nó an toàn vì chỉ khi ghi rõ --commit thì thay đổi mới xảy ra
      Trong khi đó đã có nhiều lần người ta quên --dry-run rồi gây ra sự cố
    • Công cụ deduplicate thư mục của tôi cũng theo pattern này
      Mặc định chỉ so sánh, và chỉ khi dùng --execute mới thực sự thay thế bằng hard link
    • Một số công cụ tôi từng viết trước đây còn yêu cầu nhập một cụm từ nhất định trước khi chạy thật
      Những bước xác nhận kiểu này rất hiệu quả để giảm sai sót
    • Cá nhân tôi cũng thích các flag kiểu --wet-run. Tùy ngữ cảnh mà một flag mang nghĩa ngược lại đôi khi trực quan hơn
    • Script tôi viết gần đây mặc định là chỉ đọc, và nếu muốn xóa thật thì phải tự tay nhập DELETE-ACCOUNT
      Từ 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ốt
    Thực ra sẽ an toàn hơn nếu việc chạy production được ghi rõ bằng --wet-run

    • Sẽ tốt hơn nếu mô hình hóa hành vi của ứng dụng một cách tường minh rồi xử lý tập trung ở một nơi
      Như 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”
    • Nhưng việc phải gõ mỗi lần kiểu rm --wet-run tempfile.tmp thì khá phiền
      Tô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
    • Tôi không thích tên --wet-run, nhưng cũng từng dùng cách mặc định là dry-run và chỉ khi ghi rõ --no-dry-run thì thay đổi mới xảy ra
      Nếu là service thì lý tưởng nhất là tự chọn safe mode theo môi trường chạy (dev/prod)
    • Trong các trường hợp như vậy, dùng design pattern có thể giúp giữ cấu trúc gọn gàng
  • 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

    • Tuy vậy, tác giả cũng nói rõ là họ lấy cảm hứng từ --dry-run của Subversion, nên đó vẫn là một lý do khá thuyết phục
  • Tôi thường thêm flag --really vào các tiện ích CLI, và để mặc định ở chế độ chỉ đọc
    Mục đích là buộc phải có bước xác nhận có chủ đích để tránh sai sót

    • Trước đây tôi còn có một lệnh dùng flag --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

    • Ngoài ra -Confirm cũ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àm ShouldProcess. Thiết kế này thực sự rất hay
  • Trong 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 API
    Là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ản

    • Nếu có thể, nên cấu trúc sao cho hành động thực tế chỉ diễn ra ở đúng một nơi để tránh làm bẩn code
      Tô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ụ
    • Nhưng cũng có ý kiến cho rằng dùng REST quá nhiều trong công cụ console là không hiệu quả
      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