- Rate limiting là gì và có những thuật toán nào?
Rate limiting giới hạn số request một client được gửi trong khoảng thời gian → chống lạm dụng/DoS, đảm bảo công bằng, kiểm soát chi phí. Vượt hạn trả 429 Too Many Requests (thường kèm header Retry-After). Thuật toán phổ biến: - Fixed window: đếm…
- Idempotency key là gì và giải quyết vấn đề gì khi retry?
Vấn đề: POST không idempotent — nếu client gửi tạo đơn/thanh toán rồi mạng timeout, nó không biết server đã nhận chưa. Retry khi chưa biết kết quả → tạo trùng (đơn hàng lặp, trừ tiền hai lần). Idempotency key là một chuỗi duy nhất…
- Phân trang offset và cursor khác nhau thế nào? Khi nào chọn cursor?
- Offset (LIMIT 20 OFFSET 40 / ?page=3): đơn giản, nhảy trang bất kỳ, hiện tổng số trang. Hai điểm yếu: (1) chậm ở trang sâu — DB vẫn quét rồi bỏ 40 hàng đầu; (2) lệch kết quả — nếu có bản ghi được…
- Safe và idempotent trong HTTP nghĩa là gì? Method nào thuộc loại nào?
Safe = method chỉ đọc, không làm thay đổi trạng thái phía server (GET, HEAD, OPTIONS). Idempotent = gọi 1 lần hay N lần cho cùng kết quả trạng thái (GET, HEAD, PUT, DELETE, OPTIONS). Mọi method safe đều idempotent, ngược lại thì không. Method…
- PUT khác PATCH thế nào? JSON Merge Patch và JSON Patch khác gì nhau?
PUT thay thế toàn bộ resource bằng body gửi lên: field nào không có trong body được coi như bị xoá/đưa về mặc định. PATCH chỉ mô tả phần thay đổi. Hai định dạng PATCH phổ biến: - JSON Merge Patch (application/merge-patch+json): body trông giống…
- Tạo resource xong trả status nào: 200, 201, 202 hay 204? Khi nào cần header Location?
Chọn theo việc đã làm xong chưa và có body trả về không: - 201 Created — đã tạo xong resource mới. Kèm header Location trỏ tới URL của resource vừa tạo, body thường là chính resource đó. - 200 OK — xử lý xong…
- Dữ liệu client gửi lên bị sai thì trả 400, 422 hay 409? Phân biệt ba mã này.
Ba mã ứng với ba tầng lỗi khác nhau: - 400 Bad Request — request sai cú pháp, server không parse nổi: JSON hỏng, thiếu field bắt buộc, kiểu dữ liệu sai (age: "abc"), query param không hợp lệ. - 422 Unprocessable Content — cú…
- Thiết kế error contract cho API thế nào? RFC 9457 (problem+json) gồm những field nào?
Nguyên tắc: một định dạng lỗi duy nhất cho toàn API, máy đọc được và người debug được. RFC 9457 chuẩn hoá đúng việc đó với Content-Type: application/problem+json. Các field chuẩn: - type — URI định danh loại lỗi (client so khớp field này, không…
- User bấm nút Thanh toán hai lần do mạng chậm. Em thiết kế luồng chống tạo trùng giao dịch thế nào?
Dùng idempotency key do client sinh, server ghi nhớ kết quả và phát lại thay vì xử lý lại. Luồng: 1. Client sinh key duy nhất cho mỗi ý định thanh toán (UUID v4, sinh lúc mở màn hình checkout, giữ nguyên khi retry) và…
- API versioning nên đặt ở URL hay header? Khi nào buộc phải lên version mới và deprecate version cũ ra sao?
Hai cách phổ biến: - URL path (/v1/orders) — Google API design guide và phần lớn API công khai chọn cách này. Dễ nhìn, dễ route, dễ cache, dễ chia traffic ở gateway. Nhược điểm: cùng một resource có nhiều URI. - Header (Accept: application/vnd.api+json;…
- Rate limiting dùng thuật toán nào? Server phải trả header gì để client biết đường chờ?
Thuật toán, từ đơn giản đến chính xác: - Fixed window — đếm request theo từng phút. Đơn giản nhất nhưng có lỗi biên: 100 request cuối phút 1 và 100 request đầu phút 2 → 200 request trong 2 giây vẫn hợp lệ. -…
- Endpoint list cần lọc, sắp xếp và chọn field. Em thiết kế query param thế nào cho gọn và an toàn?
Giữ một quy ước duy nhất cho mọi collection endpoint, đừng mỗi endpoint một kiểu. - Filter: field đơn giản thì đặt thẳng làm query param (status=paid). Cần biểu thức phức tạp thì dùng một param filter với cú pháp cố định (filter=status=paid AND total100000)…
- Endpoint export báo cáo chạy mất 3 phút nên hay bị timeout. Em thiết kế lại thế nào?
Chuyển từ request đồng bộ sang async job: nhận yêu cầu, trả ngay, xử lý nền, client theo dõi kết quả. Sau đó client theo dõi bằng một trong hai cách: Polling — GET /jobs/7c2 trả { "status": "running" "succeeded" "failed", "progress": 0.4 }. Khi…
- Cần endpoint cập nhật hàng loạt 500 bản ghi. Nếu 3 bản ghi lỗi thì trả status code gì?
Trước hết phải chốt ngữ nghĩa với bên gọi, vì đây là câu hỏi nghiệp vụ chứ không phải câu hỏi HTTP: Cách 1 — all-or-nothing (atomic). Cả batch chạy trong một transaction, một bản ghi lỗi thì rollback toàn bộ, trả 422 kèm danh…
- Cursor pagination: cursor nên chứa gì, và xử lý thế nào khi dữ liệu bị chèn/xoá lúc client đang duyệt?
- Hai người cùng sửa một bản ghi, người sau ghi đè mất thay đổi của người trước. Dùng ETag và If-Match xử lý thế nào?
- Em thiết kế hệ thống gửi webhook cho đối tác thế nào? Xử lý retry, ký chữ ký và chống replay ra sao?
- Client gọi service khác bị timeout thì retry thế nào cho đúng? Đặt timeout bao nhiêu?
- Cần đổi schema response mà không được làm hỏng client cũ (mobile app không ép update được). Em làm thế nào và kiểm soát breaking change ra sao?
- Vì sao ghi DB xong rồi publish message lại làm dữ liệu lệch nhau? (dual write problem)
Vì DB và message broker là hai hệ thống riêng biệt, không nằm chung một transaction. Bạn ghi vào cả hai trong cùng một hàm, nhưng không có gì đảm bảo cả hai cùng thành công. Hai chiều hỏng: - Ghi DB xong, publish lỗi…
- Consumer idempotent nghĩa là gì? Chọn khoá chống trùng (dedup key) thế nào?
Idempotent nghĩa là xử lý cùng một message nhiều lần cho kết quả giống hệt xử lý một lần. Cần thiết vì broker giao at-least-once: retry, rebalance, ack rớt đều sinh bản lặp. Cách làm phổ biến — lưu dấu vết message đã xử lý…
- Retry khi consumer xử lý lỗi nên làm thế nào? Vì sao cần exponential backoff và jitter?
Retry ngay lập tức và không giới hạn là cách nhanh nhất để biến một sự cố nhỏ thành sự cố lớn: service phía sau đang quá tải lại nhận thêm lưu lượng retry. Exponential backoff — giãn khoảng chờ theo cấp số nhân (1s,…
- Consumer xử lý chậm hơn producer trong thời gian dài thì xử lý thế nào?
Đầu tiên phải phân biệt hai tình huống, vì cách xử lý khác hẳn nhau. Chậm tạm thời (đợt cao điểm) — queue chính là bộ đệm, đó là công dụng của nó. Chỉ cần đủ dung lượng lưu trữ và giám sát để biết…
- Kafka, RabbitMQ và Redis Stream — với một bài toán cụ thể bạn chọn cái nào và vì sao?
Chọn theo cách dữ liệu được tiêu thụ, không theo độ phổ biến. Kafka — log phân tán, message được giữ lại theo retention và consumer đọc bằng offset. Chọn khi: - Cần replay: thêm service mới muốn đọc lại lịch sử, hoặc phải chạy…
- Triển khai transactional outbox ở production thế nào? Polling publisher hay CDC?
- Trong saga, bước sau lỗi thì bù trừ (compensation) thế nào? Nếu chính bước bù trừ cũng lỗi thì sao?
- Trong hệ thống có thanh toán, bạn lưu số tiền vào DB bằng kiểu dữ liệu gì? Nhiều loại tiền tệ thì xử lý ra sao?
Không bao giờ dùng float/double cho tiền. Số thực nhị phân không biểu diễn chính xác 0.1, cộng dồn nhiều dòng sẽ lệch xu và lệch đối soát. Hai cách đúng: - Số nguyên theo đơn vị nhỏ nhất: lưu amountminor BIGINT. VND không có…
- Bạn sẽ thiết kế phân quyền theo vai trò (RBAC) cho hệ thống có admin, nhân viên, khách hàng thế nào?
Mô hình tối thiểu là ba bảng: users, roles, permissions, cộng hai bảng nối userroles và rolepermissions. User nhận role, role gom permission dạng động từ + tài nguyên (order.read, order.refund, user.ban). Quy tắc khi cài đặt: - Kiểm tra ở server, mọi request. Ẩn…
- Gọi API cross-origin bằng `fetch` với `credentials: 'include'` thì server phải trả về những header gì?
Khi request mang cookie cross-origin, trình duyệt siết quy tắc chặt hơn bình thường. Server phải trả đủ hai header: - Access-Control-Allow-Origin: https://app.example.com — phải là origin cụ thể, dùng sẽ bị chặn. - Access-Control-Allow-Credentials: true. Nếu là preflight, response OPTIONS cũng phải lặp lại…
- Giữa hai service nên dùng REST, gRPC hay message queue? Căn cứ chọn là gì?
Câu hỏi đầu tiên không phải chọn giao thức mà là: người gọi có cần kết quả ngay mới đi tiếp được không? Đồng bộ (REST / gRPC) — cần kết quả để xử lý tiếp: kiểm tra tồn kho trước khi tạo đơn, xác…
- API gateway nên đảm nhận việc gì và tuyệt đối không nên nhét việc gì vào?
Gateway là cửa ngõ duy nhất cho client vào hệ thống, xử lý các cross-cutting concern — thứ mọi service đều cần và không nên viết lại 10 lần. Nên làm: - Định tuyến request tới service phù hợp; ẩn cấu trúc nội bộ khỏi…
- BFF (Backend for Frontend) là gì? Có gateway rồi thì thêm BFF để làm gì?
BFF là một backend riêng cho từng loại client (web, iOS/Android, màn hình nội bộ), do chính đội frontend đó sở hữu, đứng giữa client và các service nghiệp vụ. Vấn đề nó giải quyết: một API dùng chung cho mọi client thì luôn có…
- Cần đổi response của một API mà 4 service khác đang gọi — làm thế nào để không làm hỏng bên nào?
Nguyên tắc: thay đổi tương thích ngược thì không cần version mới; breaking change thì phải chạy song song hai phiên bản một thời gian. Thay đổi an toàn (thêm, không bỏ): thêm trường mới, thêm giá trị enum có xử lý mặc định, thêm…
- Dự án dùng ORM (Prisma/TypeORM/Hibernate) thì còn dính SQL injection được không? Chỗ nào là chỗ hở?
Có. ORM chỉ an toàn ở phần nó tự sinh câu lệnh; mọi chỗ bạn tự ghép chuỗi đều quay lại rủi ro cũ. Ba chỗ hở hay gặp: 1. Raw query nối chuỗi. Escape hatch của ORM ($queryRawUnsafe, createQuery với chuỗi, query() của driver)…
- Tính năng convert ảnh gọi ImageMagick qua shell với tên file do user đặt. Rủi ro gì và viết lại thế nào?
Rủi ro là command injection: khi chuỗi được đưa qua shell, các ký tự ;, , &&, $(...), backtick được shell diễn giải thành lệnh mới, chạy dưới quyền của process ứng dụng. Cách viết lại — bỏ shell đi: execFile/spawn (với shell: false, là…
- Frontend đã validate form bằng Zod rồi, backend còn cần validate lại không? Validate ở server nên làm thế nào cho đúng?
Cần, và đây là bắt buộc tuyệt đối. Validate ở client là trải nghiệm người dùng, không phải kiểm soát bảo mật — client nằm hoàn toàn trong tay người dùng, ai cũng gọi thẳng API bằng curl hay Postman được, bỏ qua toàn bộ…
- Endpoint `GET /users/:id` trả nguyên record từ ORM. Vấn đề ở đâu, kể cả khi UI không hiển thị các trường thừa?
Đây là excessive data exposure: API trả tất cả những gì có rồi để client tự chọn hiển thị. Nhưng client không phải nơi giấu dữ liệu — mở tab Network là thấy toàn bộ response. Trả thẳng entity ORM thường kéo theo passwordhash, resettoken,…