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 (JavaScript & TypeScript, SQL & Database, Python). 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.
01So sánh
Promise.all,Promise.allSettled,Promise.racevàPromise.any. Xử lý lỗi với async/await thế nào?JavaScript & TypeScriptGợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Cả bốn đều chạy nhiều Promise song song, khác nhau ở cách kết thúc:
all: chờ tất cả thành công; một cái lỗi là reject ngay.allSettled: chờ tất cả xong, trả về trạng thái từng cái; không bao giờ reject.race: lấy kết quả (thành công hay lỗi) của cái xong đầu tiên; hay dùng để đặt timeout.any: lấy cái thành công đầu tiên; chỉ reject khi tất cả đều lỗi.
try { const [user, orders] = await Promise.all([getUser(id), getOrders(id)]); } catch (err) { showError(err); }Hay sai:
awaitlần lượt trong vòng lặp các request không phụ thuộc nhau, khiến tổng thời gian cộng dồn. Ngược lại, gọiPromise.allvới hàng nghìn request cùng lúc có thể làm quá tải server; khi đó cần giới hạn số request đồng thời.02Trong TypeScript,
interfacevàtypekhác nhau thế nào? Phân biệtany,unknownvànever.JavaScript & TypeScriptGợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Cả hai đều mô tả hình dạng object và phần lớn trường hợp dùng thay nhau được.
interfacecó thể gộp khai báo (declaration merging) vàextends; hợp để mô tả object, API công khai.typemô tả được union, tuple, kiểu ánh xạ, kiểu điều kiện:type Status = "idle" | "loading".
Ba kiểu đặc biệt:
any: tắt kiểm tra kiểu, lỗi lọt qua compiler.unknown: nhận mọi giá trị nhưng bắt buộc thu hẹp kiểu trước khi dùng. Hợp cho dữ liệu từJSON.parse, API,catch (e).never: giá trị không bao giờ xảy ra; dùng để bắt thiếu case trongswitch.
function assertNever(x: never): never { throw new Error("Unhandled: " + x); }Hay sai: dùng
as SomeTypeđể ép dữ liệu từ API. Đó chỉ là lời hứa với compiler; muốn chắc chắn phải validate lúc runtime (ví dụ bằng Zod).03Debounce và throttle khác nhau thế nào? Mỗi cái dùng trong trường hợp nào?JavaScript & TypeScript
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Cả hai giới hạn số lần một hàm chạy khi sự kiện bắn liên tục.
- Debounce: chỉ chạy sau khi sự kiện ngừng một khoảng thời gian. Hợp với ô tìm kiếm gợi ý (gõ xong mới gọi API), validate form, resize xong mới tính lại layout.
- Throttle: chạy tối đa một lần mỗi khoảng thời gian. Hợp với scroll, kéo thả, theo dõi vị trí chuột.
function debounce(fn, ms) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), ms); }; }Hay sai: tạo hàm debounce mới mỗi lần render (trong React), khiến timer bị reset liên tục và debounce không có tác dụng. Với ô tìm kiếm, nhớ huỷ request cũ (
AbortController) để kết quả cũ không đè kết quả mới.04Một API chạy chậm, nghi do query. Bạn điều tra và tối ưu thế nào?SQL & Database
Gợi ý trả lời
Ôn thêm câu hỏi SQL & Database →Đo trước rồi mới sửa: tìm đúng query chậm, đọc execution plan, rồi chỉnh index hoặc cách viết query.
- Bật slow query log (MySQL) hoặc
pg_stat_statements(PostgreSQL) để biết query nào tốn thời gian nhất. - Chạy
EXPLAIN(PostgreSQL nên dùngEXPLAIN (ANALYZE, BUFFERS)) xem có full scan, ước lượng số dòng lệch nhiều so với thực tế, sort trên đĩa không. - Sửa theo nguyên nhân: thêm hoặc đổi composite index, bỏ hàm bọc cột, chỉ
SELECTcột cần, phân trang bằng keyset thay choOFFSETlớn. - Kiểm tra phía ứng dụng: lỗi N+1 query, thiếu connection pool, lock chờ lâu.
- Cập nhật thống kê (
ANALYZE) nếu planner ước lượng sai.
Người phỏng vấn muốn nghe: quy trình có số liệu trước/sau, không phải đoán rồi thêm index bừa.
- Bật slow query log (MySQL) hoặc
05Chuẩn hoá (normalization) là gì? Khi nào nên denormalize?SQL & Database
Gợi ý trả lời
Ôn thêm câu hỏi SQL & Database →Chuẩn hoá là tách dữ liệu để mỗi thông tin chỉ lưu ở một chỗ, tránh dư thừa và sai lệch khi cập nhật.
- 1NF: mỗi ô một giá trị nguyên tử, không lưu danh sách trong một cột.
- 2NF: cột không khoá phụ thuộc vào toàn bộ khoá chính, không chỉ một phần.
- 3NF: cột không khoá không phụ thuộc bắc cầu qua cột không khoá khác.
Denormalize khi đọc nhiều hơn ghi rất nhiều và JOIN trở thành điểm nghẽn: lưu sẵn
comment_counttrong bảng bài viết, chépcustomer_namevào đơn hàng để giữ đúng tên tại thời điểm đặt, hoặc dựng bảng báo cáo riêng.Hay sai: denormalize mà không có cơ chế đồng bộ (trigger, job, event) nên số liệu lệch dần. Hãy nói rõ cái giá phải trả là logic ghi phức tạp hơn.
06Context manager (
with) là gì? Làm sao tự viết một context manager?PythonGợi ý trả lời
Ôn thêm câu hỏi Python →Context manager đảm bảo việc "mở" và "dọn dẹp" luôn đi cùng nhau, kể cả khi có exception: đóng file, trả connection, nhả lock, rollback transaction.
Cách nhanh nhất là dùng
contextlib.contextmanager:from contextlib import contextmanager @contextmanager def db_transaction(conn): try: yield conn conn.commit() except Exception: conn.rollback() raise- Cách đầy đủ: viết class có
__enter__và__exit__(self, exc_type, exc, tb). Nếu__exit__trả vềTruethì exception bị nuốt. - Bản async:
async with,__aenter__/__aexit__hoặcasynccontextmanager.
Hay sai: quên
raiselại exception trong khốiexcept, khiến lỗi bị che mất và code phía sau tưởng mọi thứ đã thành công.- Cách đầy đủ: viết class có
07Dùng generics và type narrowing trong TypeScript thế nào? Cho ví dụ discriminated union.JavaScript & TypeScript · Senior
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Generics giúp viết hàm và kiểu dùng lại được mà vẫn giữ thông tin kiểu, thay vì rơi về
any.type Result<T> = | { ok: true; data: T } | { ok: false; error: string }; function show(r: Result<User>) { if (r.ok) return r.data.name; // TS biết có data return r.error; }- Field chung (
ok,type,status) giúp TypeScript thu hẹp kiểu trongif/switch. Đây là cách mô hình hoá trạng thái không thể sai: không có chuyện vừa códatavừa cóerror. - Ràng buộc generic:
<T extends { id: string }>. - Utility types hay dùng:
Partial,Pick,Omit,Record,ReturnType,Awaited. - Type guard tự viết:
function isUser(x: unknown): x is User.
Người phỏng vấn muốn nghe: bạn dùng kiểu để làm trạng thái sai không biểu diễn được, chứ không chỉ để chú thích.
- Field chung (
08Một trang web dùng càng lâu càng chậm và tốn RAM. Bạn tìm memory leak ở frontend thế nào?JavaScript & TypeScript · Senior
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Memory leak ở frontend thường do thứ gì đó vẫn giữ tham chiếu đến object đáng lẽ đã bỏ đi, nhất là trong SPA vì trang không bao giờ tải lại.
Nguyên nhân hay gặp:
addEventListenertrênwindow/documentmà không gỡ khi component bị huỷ.setInterval, subscription (WebSocket, store) không huỷ.- DOM đã bị gỡ khỏi trang nhưng vẫn được biến JS giữ lại (detached DOM).
- Cache hoặc mảng toàn cục chỉ tăng không giảm.
Cách điều tra với Chrome DevTools:
- Tab Memory: chụp heap snapshot, thực hiện thao tác vài lần, chụp lại, so sánh phần tăng thêm.
- Lọc "Detached" để tìm DOM bị giữ, xem chuỗi retainers để biết ai đang giữ.
- Tab Performance bật Memory để xem heap có giảm sau GC không.
Người phỏng vấn muốn nghe: mỗi tài nguyên đăng ký đều có chỗ huỷ tương ứng (cleanup của effect,
ngOnDestroy,onUnmounted,AbortController).09Core 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.
10Bạn thấy mình ở đâu sau 2–3 năm nữa?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á →Người phỏng vấn muốn biết mục tiêu của bạn có khớp với hướng phát triển ở công ty và bạn có định gắn bó không.
- Nêu hướng phát triển cụ thể, thực tế: đi sâu chuyên môn (senior, chuyên gia một mảng) hay quản lý (lead, PM).
- Kèm kế hoạch: kỹ năng cần học, trách nhiệm muốn nhận thêm.
- Gắn với cơ hội ở vị trí đang ứng tuyển.
Câu mẫu: "Trong 2–3 năm tới em muốn trở thành senior có thể tự thiết kế và chịu trách nhiệm một module lớn, đồng thời hướng dẫn được các bạn mới. Em đang học thêm về thiết kế hệ thống và thấy team bên mình có nhiều bài toán ở quy mô lớn, rất hợp để em phát triển theo hướng đó."
Hay sai: "Em muốn mở công ty riêng" hoặc "em chưa nghĩ tới". Cũng đừng nói muốn ngồi vào chỗ của người đang phỏng vấn.
Bạn đã phỏng vấn ở AI+DI?
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