10 câu hỏi có thể gặp khi phỏng vấn
Chọn theo kỹ năng và cấp của tin (Node.js, JavaScript & TypeScript, SQL & Database). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.
01Bạn mở rộng (scale) một API Node.js thế nào? Graceful shutdown là gì và vì sao cần?Node.js · Senior
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →Node một process chỉ dùng tốt một core, nên scale theo chiều ngang:
- Chạy nhiều process (cluster, PM2) hoặc nhiều container sau load balancer, Kubernetes tự tăng giảm số pod.
- App phải stateless: session, cache, rate limit, lock để ở Redis hoặc DB, không để trong bộ nhớ process.
- WebSocket nhiều instance cần adapter qua Redis pub/sub để gửi tin giữa các instance.
- Đặt giới hạn connection pool DB theo tổng số instance.
Graceful shutdown: khi nhận
SIGTERM(deploy, scale down), ngừng nhận request mới, chờ request đang chạy xong, đóng DB và queue, rồi thoát:process.on("SIGTERM", () => { server.close(async () => { await db.close(); process.exit(0); }); });Hay sai: không xử lý
SIGTERM, mỗi lần deploy lại cắt ngang request và job đang làm dở. Nên đặt thêm timeout buộc thoát nếu chờ quá lâu.02Khi xây dựng API bằng Node.js, bạn chú ý những vấn đề bảo mật nào?Node.js · Senior
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →- Validate mọi input ở biên bằng schema (Zod, Joi,
class-validatortrong NestJS); chỉ nhận field cho phép để tránh mass assignment. - Injection: dùng query có tham số hoặc ORM, không nối chuỗi SQL. Với MongoDB, chặn object toán tử như
{ "$gt": "" }lọt vào điều kiện truy vấn. - Xác thực và phân quyền: kiểm tra quyền trên từng tài nguyên (user này có sở hữu đơn hàng này không), không chỉ kiểm tra đã đăng nhập.
- Rate limiting cho login, OTP, API tốn tài nguyên.
- Header bảo mật (Helmet), CORS chỉ mở cho origin cần thiết.
- Secret để trong biến môi trường hoặc secret manager, không commit; không log token, mật khẩu.
- Dependency: chạy
npm audithoặc công cụ quét trong CI, khoá phiên bản bằng lockfile.
Người phỏng vấn muốn nghe: lỗi phân quyền theo đối tượng (IDOR) rất phổ biến, và bạn biết cách phòng nó.
- Validate mọi input ở biên bằng schema (Zod, Joi,
03Thiết kế xử lý job nền (gửi email, đồng bộ dữ liệu) trong hệ thống Node.js thế nào để không mất job và không xử lý trùng?Node.js · Senior
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →Dùng message queue thay vì xử lý ngay trong request: BullMQ (Redis), RabbitMQ hoặc dịch vụ queue của cloud. API đẩy job rồi trả về ngay; worker riêng xử lý.
Những điểm cần thiết kế:
- Retry có backoff (tăng dần thời gian chờ) và giới hạn số lần thử; job vẫn lỗi thì đưa vào dead-letter queue để xem lại.
- Idempotency: hầu hết queue đảm bảo "ít nhất một lần", nên job có thể chạy hai lần. Dùng khoá idempotency hoặc kiểm tra trạng thái trước khi làm (ví dụ email đã gửi chưa).
- Không mất job khi ghi DB: ghi dữ liệu và "sự kiện cần gửi" trong cùng transaction (outbox pattern), rồi một tiến trình khác đẩy sang queue.
- Giới hạn concurrency của worker, timeout cho từng job.
Hay sai: dùng
setTimeouthay mảng trong bộ nhớ làm "queue". Restart hoặc deploy là mất hết job.04Core Web Vitals gồm những chỉ số nào? Bạn cải thiện hiệu năng tải trang ra sao?JavaScript & TypeScript · Senior
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Ba chỉ số chính đo trải nghiệm thật của người dùng:
- LCP (Largest Contentful Paint): thời gian hiện phần tử lớn nhất. Cải thiện: tối ưu ảnh (định dạng hiện đại, đúng kích thước, ưu tiên tải ảnh hero), giảm thời gian phản hồi server, cache/CDN, render phía server.
- INP (Interaction to Next Paint, thay FID từ 2024): độ trễ khi tương tác. Cải thiện: chia nhỏ tác vụ JS dài, giảm JS chạy trên main thread, tránh render lại quá nhiều.
- CLS (Cumulative Layout Shift): độ xô lệch bố cục. Cải thiện: khai báo kích thước cho ảnh, video, quảng cáo; tránh chèn nội dung đẩy trang xuống; xử lý font.
Ngoài ra: code splitting và lazy load theo route, bỏ thư viện nặng không cần, nén và cache tài nguyên tĩnh.
Người phỏng vấn muốn nghe: bạn đo bằng dữ liệu người dùng thật (field data) lẫn Lighthouse, và biết điểm Lighthouse không phải là mục tiêu cuối.
05Frontend cần chú ý gì về bảo mật: XSS, CSRF, CORS? Nên lưu access token ở đâu?JavaScript & TypeScript · Senior
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →- XSS: kẻ tấn công chèn script chạy trên trang của bạn. Phòng: framework tự escape khi render; tránh
innerHTML,v-html,dangerouslySetInnerHTMLvới dữ liệu người dùng (nếu bắt buộc thì sanitize); cấu hình Content-Security-Policy. - CSRF: trang lạ khiến trình duyệt gửi request kèm cookie của người dùng. Phòng: cookie
SameSite=LaxhoặcStrict, CSRF token, kiểm traOrigin. - CORS: là cơ chế trình duyệt nới lỏng same-origin policy, do server cấu hình. Nó không phải lớp bảo vệ API khỏi người gọi ngoài trình duyệt.
Lưu token:
localStoragetiện nhưng script XSS nào cũng đọc được.- Cookie
HttpOnly+Secure+SameSitethì JS không đọc được, nhưng phải tính đến CSRF.
Người phỏng vấn muốn nghe: không có chỗ lưu nào an toàn tuyệt đối; chặn XSS mới là việc quan trọng nhất, vì có XSS thì mọi cách lưu đều bị lợi dụng.
- XSS: kẻ tấn công chèn script chạy trên trang của bạn. Phòng: framework tự escape khi render; tránh
06Làm sao migrate schema (thêm cột, đổi kiểu, thêm index) trên bảng lớn mà không downtime?SQL & Database · Senior
Gợi ý trả lời
Ôn thêm câu hỏi SQL & Database →Chia thay đổi thành nhiều bước tương thích ngược để phiên bản code cũ và mới chạy song song được (expand rồi contract).
- Expand: thêm cột mới cho phép
NULLhoặc có default; tránh câu lệnh viết lại cả bảng. - Code mới ghi vào cả cột cũ và mới.
- Backfill dữ liệu cũ theo từng lô nhỏ, có nghỉ giữa các lô để không gây lag replica.
- Chuyển code sang đọc cột mới, kiểm tra kỹ.
- Contract: bỏ cột cũ ở một lần deploy sau.
Index trên bảng lớn: PostgreSQL dùng
CREATE INDEX CONCURRENTLY; MySQL dùng online DDL hoặc công cụ như gh-ost, pt-online-schema-change.Người phỏng vấn muốn nghe: bạn biết DDL có thể giữ lock khiến mọi query xếp hàng, nên đặt
lock_timeoutvà luôn có kế hoạch rollback.- Expand: thêm cột mới cho phép
07Node.js chạy đơn luồng thì làm sao xử lý được nhiều request cùng lúc? Node hợp với loại ứng dụng nào?Node.js · Junior
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →Code JavaScript của bạn chạy trên một thread, nhưng I/O (mạng, file, DB) là non-blocking: Node giao việc cho hệ điều hành hoặc thread pool của libuv, rồi tiếp tục xử lý request khác. Khi I/O xong, callback được đưa lại vào event loop.
- Hợp với: API, BFF, real-time (WebSocket, chat), proxy, ứng dụng nhiều I/O và ít tính toán.
- Không hợp nếu chỉ dùng thread chính cho tác vụ nặng CPU: mã hoá video, xử lý ảnh lớn, tính toán dài.
// Chặn cả server: mọi request khác phải chờ app.get("/hash", (req, res) => { res.send(crypto.pbkdf2Sync(pw, salt, 1e6, 64, "sha512")); });Người phỏng vấn muốn nghe: "đơn luồng" chỉ đúng với phần code JS; Node vẫn dùng thread pool cho file system, DNS, một số hàm crypto và zlib.
08CommonJS và ES Modules khác nhau thế nào? Dự án mới nên dùng loại nào?Node.js · Junior
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →CommonJS dùng
require/module.exports, nạp đồng bộ lúc chạy. ES Modules dùngimport/export, là chuẩn của ngôn ngữ, được phân tích tĩnh nên hỗ trợ tree-shaking và top-levelawait.- Bật ESM bằng
"type": "module"trongpackage.jsonhoặc đuôi.mjs. - Trong ESM không có sẵn
__dirname; dùngimport.meta.dirname(Node mới) hoặc tính từimport.meta.url. - ESM import được CommonJS. Chiều ngược lại trước đây phải dùng
import()động; các bản Node gần đây đã chorequire()module ESM đồng bộ (trừ module có top-levelawait).
Dự án mới nên dùng ESM, vì cả hệ sinh thái đang chuyển dần sang đó.
Hay sai: khi viết ESM thuần, quên ghi đuôi file trong đường dẫn import tương đối (
./utils.js), dẫn đến lỗi không tìm thấy module.- Bật ESM bằng
09Middleware trong Express là gì? Viết error-handling middleware thế nào?Node.js · Junior
Gợi ý trả lời
Ôn thêm câu hỏi Node.js →Middleware là hàm
(req, res, next)chạy theo thứ tự đăng ký. Mỗi middleware có thể sửareq/res, trả response luôn, hoặc gọinext()để chuyển tiếp. Ví dụ: parse JSON, log, xác thực, giới hạn tần suất.Error-handling middleware có bốn tham số và đặt sau các route:
app.use((err, req, res, next) => { logger.error(err); const status = err.status ?? 500; res.status(status).json({ message: status < 500 ? err.message : "Internal error" }); });- Gọi
next(err)để nhảy thẳng tới error handler. - Express 5 tự chuyển Promise bị reject trong handler
asyncsang error handler; Express 4 thì phải tựtry/catchrồi gọinext(err).
Hay sai: quên gọi
next()và cũng không trả response, khiến request treo đến khi timeout. Hoặc gửi response hai lần, gây lỗi "headers already sent".- Gọi
10Vì sao bạn muốn rời công ty hiện tại?Câu hỏi hành vi & văn hoá
Gợi ý trả lời
Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →Trả lời thật nhưng hướng về phía trước: nói điều bạn đang tìm kiếm, không kể xấu nơi cũ.
Lý do chấp nhận được: muốn thử thách mới, muốn làm sản phẩm, dự án kết thúc, công ty tái cấu trúc, thu nhập chưa tương xứng (nói khéo).
Câu mẫu: "Em đã làm 2 năm ở dự án outsourcing, học được nhiều về quy trình và làm việc với khách nước ngoài. Giờ em muốn gắn bó lâu dài với một sản phẩm để thấy được tác động của những gì mình làm, và vị trí bên mình đúng hướng đó."
Hay sai: chê sếp, chê đồng nghiệp, kể chuyện drama. Người phỏng vấn sẽ nghĩ bạn cũng sẽ nói về công ty họ như vậy. Nếu nhảy việc nhiều, chuẩn bị lời giải thích cho từng lần.
Bạn đã phỏng vấn ở IONAH?
Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.
Chia sẻ kinh nghiệm phỏng vấn