Và đó là quyết định đúng nhất của cả dự án.
Buông — không phải bỏ cuộc.
Tôi đo được con bug đó chạy. Log không nói dối. Vậy mà tôi vẫn xóa sạch.
Vì cố thêm lúc đó là phục vụ cái tôi — không phải người dùng. Nhưng để hiểu vì sao một người lại xóa thứ mình vừa làm cho chạy được, phải quay lại từ đầu.
Sáu tháng trước, ý tưởng rất rõ: một app nhỏ trên macOS — chụp màn hình, quay GIF, sửa ảnh ngay tại chỗ. Nghe đơn giản. Thực tế là một bãi mìn. Build xong → crash. Sửa xong → mất quyền. Có đêm tôi gõ đúng một câu cho AI: "Kiểm tra lại 3 lần trước khi ship." Rồi đóng máy. Gần như bỏ.
Bài này không phải về app của tôi. Mà về cách bạn làm việc với AI — và biết khi nào dừng — để không bao giờ bế tắc (hay bỏ cuộc) như cũ. App chỉ là bằng chứng: giờ nó rút mỗi lần chụp-sửa-gửi từ 4 thao tác xuống 2, không một lần crash.
Đừng sửa theo cảm giác. Sửa theo cảm giác → sửa nhầm chỗ.
Cú vấp đầu: "ấn phím lưu thì app thoát." Triệu chứng gọn vậy. Nếu đoán, tôi đi sửa nút Lưu. Đo kỹ thì sự thật lộ ra: thư viện phím tắt gọi một API hệ thống ngoài luồng chính trên macOS 26 → crash tầng C, không try/except nào bắt được.
Lời giải không phải vá. Mà là thay máu — dựng phím tắt bằng một event tap chạy đúng luồng chính. Không TIS. Không crash.
Cú vấp hai: "chỉ viết được mũi tên, không gõ được chữ." Lại một triệu chứng đánh lừa. Gốc rễ: chọn sai loại cửa sổ — loại "Tool" trên macOS không thể nhận bàn phím.
Build lần thứ chín. Lại crash. Tôi sửa một dòng — người dùng phải vào cài đặt cấp quyền lại, lần thứ năm trong một tối. Đây là chỗ tôi suýt buông.
Mỗi lần build lại, chữ ký đổi. macOS coi đó là app khác → âm thầm xoá quyền cũ. Cứ sửa một dòng là phải cấp quyền lại. Mỗi. Lần.
Đo ra gốc rễ rồi, lời giải mới đúng tầm: ký bằng một chứng chỉ cố định. Bằng chứng nằm ở "designated requirement" của bản build:
Cụ thể với bạn: ngày build 15 lần × 2 phút cấp lại 5 quyền = 30 phút bốc hơi mỗi ngày. Sau khi ký cố định: 0. Làm 1 lần cho đàng hoàng → để lại một tài sản → dùng mãi.
Đó là khoảnh khắc con app ngừng chống lại tôi.
Một người dùng báo: bấm phím chụp khi app đang toàn màn hình thì không chạy.
Tôi lao vào. Thử cách một: đặt thuộc tính cho cửa sổ hiện trên mọi không gian. Không đủ. Cách hai: nâng cửa sổ lên mức cao nhất — như công cụ chụp của chính macOS. Hiện được, nhưng ESC chết. Cách ba: cho chuột và phím đi vòng qua event tap, không kích hoạt app.
Và tôi đo được nó chạy — overlay nhận chuột, vẽ ra đúng vùng chọn, không nhảy không gian:
Nhưng cái editor mở ra sau đó vẫn cần kích hoạt app để gõ được. Và tôi không tự dựng được cảnh "toàn màn hình thật" để test cho trọn — máy người dùng tiếng Việt, mọi cách script đều trượt.
Người dùng nhắn lại, gọn: "Bỏ qua đi. Tôi thu nhỏ cửa sổ lại là xong."
Tôi gỡ sạch toàn bộ phần đó. Trả app về trạng thái ổn định.
Đây là chỗ Đời chạm Đạo. Suốt sáu tháng tôi dạy huynh đệ 10X đúng một câu: cái tôi muốn hoàn hảo, phụng sự chỉ cần đủ dùng.
Con bug này không phải bài toán code. Nó là bài kiểm tra: tôi đang sửa cho người dùng — hay đang sửa cho cái tôi của mình? Người dùng đã có lối đi: thu nhỏ cửa sổ. Cố thêm, là tôi leo một ngọn núi không ai cần tôi leo. Buông nó xuống không phải một lần thua. Mà là một lần tu điềm tĩnh — biết tách cái mình muốn ra khỏi cái người khác cần.
Bản chốt không dựa vào "tôi nghĩ là chạy". Tôi cho nhiều con AI soi song song, rồi bắt mỗi phát hiện tự chứng minh:
Kẹt-busy khi lỗi chụp · re-entry qua hộp thoại · cửa sổ GIF bị thu hồi · đa-màn-hình chọn nhầm vùng · và bảy điểm nữa. Tất cả: đo, vá, đo lại.
Đây là tài sản đắt nhất. Code rồi sẽ cũ. Cách làm việc với AI thì dùng lại cho mọi việc sau. Tôi gọi nó là khung này — năm nguyên tắc để làm việc với AI mà không mất quyền kiểm soát:
Lấy Khung CẦM LÁI, chạy đúng một vòng với AI: báo triệu chứng → bắt nó đo → đóng vòng. Rồi nhắn tôi nghe nó đi tới đâu.