Chuyển đến nội dung chính

Bài đăng

Step-up authentication with biometrics for sensitive actions

Session age is not enough for destructive actions. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Re-auth for payments, exports, and settings changes. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Re-auth before export, payment, and account deletion. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and calls the bo...
Các bài đăng gần đây

Background work limits on iOS and Android in 2026

Mobile OSes aggressively limit background execution. Design for it. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence WorkManager, BGTask, and realistic sync intervals. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Batch work and accept delay; do not promise realtime in background. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validate...

SSO for enterprise mobile: OIDC and SAML realities

Enterprise identity is a hard requirement, not a nice-to-have. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Browser auth sessions, MDM certs, and logout. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Use system browsers for SSO where required. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and calls the boundar...

Field-service apps: offline-first B2B patterns

Field techs work where networks do not. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Sync windows, media capture, and battery. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Queue media uploads; compress aggressively. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and calls the boundary. The boundary returns ty...

Escaping setState spaghetti with clear ownership

setState is fine for ephemeral UI. App state needs a real owner. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Local UI state vs app state and where Bloc/Riverpod fit. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Name the state owner before adding a package. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and ca...

CarPlay and Android Auto with mobile companion apps

Car UIs have strict distraction guidelines. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence What belongs on the car screen and what does not. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Glanceable actions only; no text entry while driving. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and calls the boundary. T...

Certificate pinning and network security on mobile

Pinning reduces MITM risk but causes outages if rotated wrong. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence When pinning helps and when it breaks you. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Prefer backup pins and short-lived leaves. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and calls the boundary. ...

ADRs for mobile architecture decisions

ADRs explain why the code looks like this six months later. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence One page per irreversible choice, with context. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Keep them next to the code, not in a wiki graveyard. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and calls the...

AR and visionOS companions for mobile products

AR is powerful and easy to overbuild. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Where AR belongs and how to keep scope honest. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Ship one killer AR job, not a feature museum. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and calls the boundary. The boundary retur...

AI chạy trên thiết bị trong Flutter: cái gì vừa với một chiếc điện thoại

Lời chào mời thật sự hấp dẫn: không API key, không tính tiền theo token, không vòng gọi mạng, và dữ liệu người dùng không bao giờ rời khỏi máy. Với một ứng dụng ghi chú, một bàn phím, hay bất cứ thứ gì đụng tới hồ sơ sức khoẻ hoặc tài chính, riêng điểm cuối cùng đã đủ quyết định kiến trúc. Rồi bạn nhìn vào những con số. Một mô hình ngôn ngữ nhỏ đã lượng tử hoá là gói tải về một tới ba gigabyte, cần giữ phần lớn chỗ đó trong RAM khi chạy, và sinh token với tốc độ khiến một API năm 2019 phải ngượng. Cả hai bức tranh đều đúng. Vấn đề là bức nào áp dụng cho tính năng của bạn. Cái gì thật sự chạy tốt trên điện thoại Những tác vụ mà một model nhỏ cục bộ thật sự làm tốt hẹp hơn các bản demo gợi ý, và chúng tụ quanh một tính chất: đầu vào ngắn, đầu ra ngắn, không cần kiến thức về thế giới. Tác vụ Khả thi trên thiết bị Vì sao Phân loại văn bản, nhận diện ý định Rất tốt Model tí hon, thường còn chẳng phải LLM Embedding cho tìm kiếm ...

Quan sát ứng dụng LLM: truy vết một hệ thống không tất định

“Một khách hàng nói trợ lý bảo họ rằng chúng ta cho đổi trả trong 90 ngày. Chúng ta không có chính sách đó.” Trong một dịch vụ bình thường, bạn tìm yêu cầu đó, chạy lại, và đọc đường đi của mã. Trong ứng dụng LLM, chạy lại cho bạn một câu trả lời khác, phần truy hồi có thể đã đổi, và đường đi của mã giống hệt với cả nghìn yêu cầu đã hành xử đúng đắn. Trace ở đây không phải công cụ hỗ trợ gỡ lỗi; nó là bằng chứng duy nhất rằng sự việc từng xảy ra. Ghi lại toàn bộ lần chạy, không chỉ điểm cuối Một tin nhắn của người dùng có thể sinh ra cả chục lời gọi mô hình, truy hồi và gọi công cụ. Chỉ ghi phản hồi cuối cùng thì cho bạn biết cái gì sai mà không cho biết sai ở đâu. Hãy mô hình hoá lần chạy thành một trace với các span lồng nhau: trace: conversation_turn user_id, conversation_id, turn_index ├── span: retrieve query, k, latency, chunk_ids, scores ├── span: llm_call model, temperature, tokens_in/out, stop_reason │ └── span: tool.search_orde...

Mô hình thị giác trong sản phẩm thật: những phần bản demo bỏ qua

Bản demo chạy ngon ngay lần đầu: chụp một hoá đơn, nhận về JSON có cấu trúc với tên cửa hàng, ngày và tổng tiền. Rồi bạn đưa nó ra sản phẩm, và người dùng gửi cho bạn một hoá đơn chụp nghiêng trong ánh sáng tệ với ngón tay che mất tổng tiền, một hợp đồng quét 40 trang, một ảnh chụp màn hình bảng tính, và một tấm ảnh mờ chụp lại một màn hình khác đang hiển thị hoá đơn. Mô hình xử lý được nhiều hơn bạn tưởng, và những phần nó không xử lý được mới là thứ quyết định tính năng này có dùng được hay không. Độ phân giải là cái núm chi phí Ảnh trở thành token, và số lượng tỉ lệ với diện tích điểm ảnh. Công thức chính xác khác nhau tuỳ nhà cung cấp, nhưng hình dạng thì phổ quát: một ảnh lớn có thể tốn hơn cả một trang văn bản, và dù sao đa số nhà cung cấp cũng thu nhỏ ảnh vượt quá một kích thước tối đa trước khi xử lý. Hai hệ quả theo sau. Gửi một tấm ảnh điện thoại ở độ phân giải đầy đủ thường là lãng phí. Ảnh 12 megapixel sẽ bị nhà cung cấp thu nhỏ, nên bạn đã trả tiền để tải lên những đ...

Định tuyến mô hình và thác bậc: chỉ trả tiền cho mức thông minh bạn cần

Hoá đơn về, và 90% trong đó là những yêu cầu kiểu “tóm tắt cái này trong một câu” hay “tin nhắn này có phải spam không?” — được gửi tới đúng cái model tiên tiến bạn dùng cho suy luận nhiều bước, chỉ vì đó là model bạn nối dây đầu tiên. Cách sửa nghe hiển nhiên: dùng model nhỏ ở chỗ model nhỏ là đủ . Cái khó nằm trọn trong chữ đủ , và trong việc biết yêu cầu nào là như vậy trước khi bạn có câu trả lời. Ba kiến trúc, không phải một Người ta nói “định tuyến” cho ba thứ khác nhau, và chúng có hồ sơ rủi ro khác nhau. Cách tiếp cận Quyết định thế nào Chi phí Rủi ro Định tuyến tĩnh Nhánh code quyết định model Ít phức tạp nhất Gán sai không lộ ra cho tới khi có phàn nàn chất lượng Định tuyến bằng phân loại Một model nhỏ hoặc heuristic chọn theo từng yêu cầu Thêm một lời gọi Phân loại sai đẩy việc khó cho model yếu Thác bậc Thử nhỏ trước, leo thang khi hỏng Đôi khi hai lời gọi đầy đủ Tiêu chí leo thang là toàn bộ bài toán Hãy bắt đầu bằng định tuyến tĩnh. Nó g...

Prompt injection: cái gì thật sự phòng thủ được

Tiền đề khó chịu, nói thẳng ra: một mô hình ngôn ngữ chỉ có một kênh đầu vào. System prompt của bạn, tin nhắn người dùng, một tài liệu truy hồi được và kết quả từ công cụ đều tới dưới dạng văn bản, và sự phân biệt của mô hình giữa “chỉ dẫn tôi tuân theo” và “dữ liệu tôi xử lý” là một xu hướng được học, không phải một ranh giới được cưỡng chế. Vì thế mọi phòng thủ dựa trên việc nhờ mô hình một cách kiên quyết hơn đều chỉ là biện pháp giảm nhẹ mang tính xác suất. Nó hạ tỉ lệ; nó không bịt được lỗ hổng. Những phòng thủ trụ được là những cái giả định rằng mô hình rồi sẽ bị lái đi, và giới hạn việc lái đó đạt được gì. Hình dạng của cuộc tấn công Injection trực tiếp là khi người dùng gõ “bỏ qua các chỉ dẫn trước đó”. Đó là ca dễ, và phần lớn chỉ gây phiền — người dùng đang tấn công chính phiên của họ. Injection gián tiếp mới là vấn đề thật sự. Chỉ dẫn tới bên trong dữ liệu mà hệ thống của bạn truy hồi thay mặt người dùng: Một ticket hỗ trợ mà phần thân chứa văn bản nhắm tới agent phâ...

Chọn mô hình embedding: những câu hỏi thật sự quan trọng

Quy trình thường thấy là: mở bảng xếp hạng, sắp theo điểm trung bình, lấy mô hình đứng đầu vừa túi tiền, rồi đi tiếp. Nó cho ra lựa chọn bảo vệ được khoảng một nửa số lần, và ở nửa còn lại, nó hỏng một cách đắt đỏ — vì đổi mô hình embedding nghĩa là nhúng lại toàn bộ và dựng lại mọi chỉ mục. Đây là những câu hỏi dự báo kết quả tốt hơn thứ hạng. Nó có chạy tốt trên văn bản của bạn không? Điểm trung bình trên benchmark là một hỗn hợp có trọng số của nhiều tác vụ, mà phần lớn không phải tác vụ của bạn. Một mô hình dẫn đầu tổng thể có thể tụt hậu tệ hại ở đúng cái bạn cần — điều khoản pháp lý, mã hàng, ticket hỗ trợ khách hàng tiếng Việt, hay mã nguồn. Bài đánh giá thật sự quan trọng chỉ mất một buổi chiều: Thu 50-100 truy vấn thật từ log của bạn (hoặc tự viết, nếu chưa có lưu lượng). Với mỗi truy vấn, đánh dấu những tài liệu trong kho đáng lẽ phải được truy hồi. Việc gán nhãn này chính là phần công việc thật. Nhúng kho tài liệu bằng từng mô hình ứng viên, chạy các truy vấn, đo r...