← Tất cả bài viết 🔬 Hệ thống & AI Hậu trường · 10X Life OS

VoiceFlow: một đêm, một app — và con bug suýt bỏ cuộc

Cách một app biến tiếng Việt thành chữ được dựng, đóng gói cho cả Mac và Windows, vá lỗi và nâng cấp. Và những gì nó dạy về việc làm ra thứ chạy được.

Một ý muốn rất nhỏ: "làm một app chép lời tiếng Việt, để tặng bạn bè."

Nói tiếng Việt vào, chữ hiện ra. Chạy ngay trên máy, riêng tư tuyệt đối, không gửi giọng nói mình đi đâu cả. Nghe đơn giản.

Kết quả sau một chặng làm là VoiceFlow: app chạy thật trên Mac và Windows, có bản cài tự dựng, tự cập nhật. Trên đường đi có một con bug làm app đơ cứng — con bug suýt khiến tôi bỏ cuộc, cho tới khi tôi ngồi xuống đọc log.

Bài này không phải lý thuyết. Đây là hậu trường thật — kèm những cú vấp thật — kể lại thành bốn chặng để bạn cũng làm được, không phải mò lại từ đầu.

Người làm sản phẩm giỏi không viết nhiều code hơn. Họ chọn đứng trên vai người khổng lồ, rồi để lại một bộ khung lắp lại được cho lần sau.
01

Dựng lõi: chọn một việc, đứng trên vai người khổng lồ

Chặng đầu không phải là vẽ giao diện đẹp. Mà là trả lời một câu: app này làm thật tốt một việc gì?

Với VoiceFlow, câu trả lời gọn: nói tiếng Việt → ra chữ, chạy offline, riêng tư. Một mũi tên, một đích. Không ôm đồm.

Và tôi không viết lại từ số 0. Có sẵn một dự án mã nguồn mở tốt tên là Handy — một app chép lời. Thay vì xây lại cả cái nền, tôi fork nó về repo riêng, rồi cải tiến trên đó. Đứng trên vai người khổng lồ tiết kiệm hàng tháng trời.

Fork mã nguồn mở rồi Việt hoá thay vì viết lại từ đầu Từ dự án Handy có sẵn, fork về VoiceFlow, đổi nhận diện và Việt hoá, chạy lõi trên máy mình. Handymã nguồn mở Fork riêngrepo của mình VoiceFlowViệt hoá + nhận diện tiết kiệm hàng tháng Không xây lại cái nền — cải tiến trên nền đã tốt sẵn.
Fork nền tốt sẵn → đổi tên, đổi màu, mặc định tiếng Việt → chạy lõi trên máy mình trước.

Việt hoá không chỉ là dịch nút bấm. Tôi đặt tên VoiceFlow, khoác bộ màu navy/cam của 10X, mặc định ngôn ngữ tiếng Việt, và chọn đúng model nhận giọng Việt — model hiểu được cả khi mình nói xen vài từ tiếng Anh.

Nguyên tắc của chặng này: lo phần ruột trước. Đảm bảo cái lõi nói-ra-chữ chạy đúng trên máy mình — rồi mới lo đóng gói cho người khác. Vẽ giao diện cho một cái lõi chưa chạy là xây nhà trên cát.

02

Để máy chủ đóng gói ba nền — đừng build tay

App chạy trên máy tôi rồi. Nhưng bạn bè tôi dùng Mac, dùng Windows, có người dùng Linux. Ngồi build tay từng bản cho từng hệ là một cực hình — và lần nào cũng sót.

Nên tôi viết một "công thức" đóng gói tự động: gắn một nhãn phiên bản, máy chủ tự làm phần còn lại. Nó dựng bản Mac, bản Windows, bản Linux, tự ký để app cập nhật được, rồi tự đăng lên trang tải về. Tôi chỉ tăng số phiên bản và đẩy lên — khoảng 20–25 phút sau là có đủ bản cài.

  • Viết một lần — một quy trình build cho cả ba hệ, dùng lại mãi mãi.
  • Tạo khoá ký riêng — khoá kế thừa từ dự án gốc không dùng được, phải tự tạo khoá mới của mình.
  • Gắn nhãn rồi đẩy — tăng số (ví dụ v0.8.5), push, máy chủ tự chạy.
  • Thử trên máy sạch — tải bản cài về một máy chưa từng làm dev để chắc bạn bè mở là chạy.
Một tài sản làm một lần — một file quy trình build — phục vụ mọi phiên bản về sau. Đó mới là đòn bẩy thật.

Và đây là cú vấp đầu tiên, cú vấp mà ai làm lần đầu cũng gặp. Lần đóng gói đầu tiên, cả ba bản đều lỗi — mỗi bản một lý do khác nhau. Bản Mac cố nạp một "chứng chỉ rỗng". Bản Windows sai mật khẩu khoá. Bản Linux thiếu công cụ đóng gói. Không có bản nào giống bản nào.

Phản xạ lúc đó là nản, hoặc là đoán bừa rồi sửa lung tung. Tôi làm khác: mở nhật ký lỗi của từng bản ra đọc, tìm đúng nguyên nhân gốc của mỗi cái, vá từng cái một — rồi chạy lại. Lần hai: xanh hết cả bốn bản.

Lần đầu luôn trục trặc. Đó là chuyện bình thường, không phải dấu hiệu bạn dở. Đọc log, sửa đúng gốc, chạy lại.
03

Săn con bug "đơ" — đọc dấu vết, sửa tận gốc

Đây là chặng suýt làm tôi bỏ cuộc. Có một triệu chứng khó chịu: đang ghi âm, nếu cái micro Bluetooth rớt sóng giữa chừng, thanh ghi âm đứng hình. App kẹt ở trạng thái "đang bận" mãi mãi. Chỉ tắt đi mở lại mới hết.

Một con bug như thế rất dễ khiến người ta buông tay — vì nó "chập chờn", khó tái hiện, nghe như một thứ ma quái không đầu không cuối. Nhưng tôi tin một điều: một lỗi khó chịu gần như luôn có một nguyên nhân gốc rất cụ thể. Cứ đọc dấu vết và lần theo là ra.

Đọc logTìm chỗ kẹtSửa tận gốcSoi đối khángXác minh

Nhật ký chỉ thẳng đường: ngay khi micro rớt, log ghi "thiết bị không còn khả dụng", rồi ngay sau đó "bỏ qua lệnh: đang bận". Có manh mối là có hướng. Lần theo, tôi tìm ra đúng chỗ kẹt: luồng xử lý âm thanh đang chờ mãi một mẩu dữ liệu không bao giờ tới — vì cái micro đã chết rồi — nên nó không bao giờ nghe thấy lệnh "Dừng".

Micro rớt sóng khiến luồng chờ mãi và app kẹt busy Trước khi vá: micro chết thì luồng chờ mẫu không bao giờ tới, lệnh dừng không tới, app kẹt. Sau khi vá: luồng kiểm lệnh mỗi vòng và tự thoát. TRƯỚC — KẸT Micro chết Chờ mẫu mãi Lệnh Dừngkhông bao giờ tới Kẹt "busy" SAU — TỰ THOÁT Kiểm lệnh mỗi vòng Micro chết → báo "hết luồng" Thoát sạch + van an toàn
Gốc: luồng chặn chờ mẫu không tới nên bỏ lỡ lệnh Dừng. Vá: kiểm lệnh mỗi vòng, tự thoát khi micro chết, thêm van an toàn.

Bản vá không chỉ một chỗ, mà nhiều lớp: luồng luôn kiểm lệnh Dừng mỗi vòng thay vì chờ vô hạn; khi micro chết thì tự báo "hết luồng" để thoát ra; và thêm một van an toàn phòng khi đường dừng cũng bị treo.

Nhưng tôi không tin ngay bản vá của chính mình. Trước khi phát hành, tôi cho ba "chuyên gia ảo" soi đối kháng — ba góc nhìn độc lập, mỗi góc cố tình đi tìm một loại lỗi khác nhau: một góc soi xem còn kẹt lại chỗ nào không, một góc soi xem có bị mất tiếng không, một góc soi xem có làm hỏng thứ đang chạy tốt không. Kết luận cả ba: an toàn để phát hành.

Từ treo vĩnh viễn phải khởi động lại → giờ app tự nhận ra micro mất và phục hồi cho lần ghi sau. Con bug tưởng ma quái, hoá ra chỉ là một chỗ chờ sai.
04

Audit & nâng cấp — từ 6 lên 9 điểm, có kiểm chứng

App đã chạy, đã hết đơ. Nhưng "chạy được" và "đáng dùng" là hai chuyện. Chặng cuối là ngồi lại đánh giá toàn diện — rồi nâng cấp những thứ người dùng cảm nhận được, không đập đi làm lại, cũng không ôm hết một lần.

Tôi audit ba góc song song, mỗi góc một báo cáo độc lập: một góc soi kiến trúc và cấu trúc bên trong; một góc soi tốc độ trên chip Apple; một góc soi giao diện và trải nghiệm. Ba góc nhìn khác nhau soi ra những thứ mà một góc nhìn duy nhất sẽ bỏ sót.

  • Hết đơ — con bug micro rớt sóng ở chặng trên đã vá gọn.
  • Nhẹ & nhanh hơn — tối ưu đúng cho chip Apple, bỏ những việc thừa.
  • Chữ dễ đọc — chỉnh màu chữ đạt chuẩn tương phản, dùng được cả bằng bàn phím.
  • Hết chữ tiếng Anh sót — Việt hoá cho tự nhiên, không lạc tông.

Có một điều tôi cố ý làm, và tôi nghĩ nó quan trọng hơn cả danh sách trên: ghi rõ những gì để lại đợt sau. Thay vì hứa hẹn "làm hết", tôi liệt kê trung thực phần chưa làm — những thứ rủi ro cao hơn, cần thời gian hơn — để dành cho phiên bản kế. Đó là cách giữ cho mỗi lần nâng cấp đều an toàn và có kiểm chứng.

Và thứ tự vẫn là bất di bất dịch: xây bản thật → chạy thử trên máy → soi đối kháng → rồi mới phát hành. Không tin bản vá cho tới khi có bằng chứng nó chạy.

05

6 bài học mang đi

Bốn chặng trên chưng cất lại thành sáu bài học. Chúng không chỉ đúng cho một app chép lời — chúng đúng cho gần như mọi thứ ta bắt tay làm:

  • Một việc, làm thật tốt. Một app — một đích. Đừng ôm đồm.
  • Đứng trên vai người khổng lồ. Fork nền tốt sẵn, cải tiến trên đó — nhanh gấp nhiều lần làm từ số 0.
  • Để máy chủ đóng gói. Một quy trình tự build Mac/Windows/Linux — đừng ngồi build tay từng bản.
  • Lần đầu luôn trục trặc. Đọc log, sửa đúng gốc, chạy lại — đừng đoán, đừng nản.
  • Chạy thử + soi đối kháng rồi mới phát hành. Đừng tin bản vá cho tới khi có bằng chứng.
  • Đánh số phiên bản mỗi lần. Mỗi lần đẩy là tăng số và ghi rõ "sửa gì".
Làm một lần cho đàng hoàng. Để lại một tài sản. Lần sau lắp lại, không xây lại.
06

Checklist — in ra và làm theo

Lần tới bạn bắt tay làm một thứ gì đó chạy được, mở checklist này ra và tick từng dòng:

① Dựng lõi

Chốt một việc app làm thật tốt (một mũi tên, một đích)
Tìm nền mã nguồn mở tốt sẵn để đứng lên, thay vì viết từ 0
Chạy phần "ruột" trên máy mình trước khi lo đóng gói

② Đóng gói

Một quy trình build tự động cho mọi nền · tự ký để cập nhật
Lần đầu lỗi → đọc log từng bản, vá đúng gốc, chạy lại
Thử cài trên máy SẠCH (không phải máy dev)

③ Săn lỗi

Đọc log tìm dấu vết trước khi đụng code
Truy đến đúng chỗ kẹt · sửa tận gốc nhiều lớp
Cho nhiều góc độc lập soi đối kháng trước khi tin bản vá

④ Nâng cấp

Audit nhiều góc song song · sửa cái cảm nhận được trước
Xây → chạy thử → soi → rồi mới phát hành
Ghi rõ "để lại đợt sau" · tăng số phiên bản mỗi lần

Muốn xem sản phẩm thật?

Chính là VoiceFlow — app chép lời tiếng Việt, chạy offline, riêng tư tuyệt đối, miễn phí.

Khám phá tài nguyên 10X →