Việc làm

24/09 · Đà Nẵng · Hà Nội · Tại văn phòng

Backend Engineer (Nodejs)

Everfit · Backend Developer

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

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, SQL & Database, AI / Machine Learning). 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.

  1. 01Trong NestJS, module, provider và dependency injection hoạt động ra sao? Guard, interceptor, pipe khác nhau thế nào?Node.js

    Gợi ý trả lời

    NestJS tổ chức app thành module. Mỗi module khai báo controller, provider (service, repository) và exports những gì module khác được dùng. IoC container của Nest tự tạo provider và inject qua constructor.

    @Injectable()
    export class OrdersService {
      constructor(private readonly repo: OrdersRepository) {}
    }
    

    Thứ tự một request đi qua: middleware, guard, interceptor (trước), pipe, handler, interceptor (sau); exception filter bắt lỗi ở bất kỳ bước nào.

    • Guard: quyết định request có được đi tiếp không (xác thực, phân quyền).
    • Pipe: biến đổi và validate input (ValidationPipe với DTO).
    • Interceptor: bọc quanh handler: log, đo thời gian, đổi định dạng response, cache.

    Hay sai: hai provider phụ thuộc vòng tròn rồi xử lý bằng forwardRef khắp nơi. Thường đó là dấu hiệu nên tách lại trách nhiệm giữa các module.

    Ôn thêm câu hỏi Node.js →
  2. 02Process Node trên production tăng RAM liên tục rồi bị kill. Bạn tìm memory leak thế nào?Node.js · Senior

    Gợi ý trả lời

    Trước hết xác nhận có leak thật: theo dõi heap qua thời gian (process.memoryUsage(), metrics của APM). Heap tăng rồi giảm sau GC là bình thường; chỉ tăng không giảm mới là leak.

    Nguyên nhân hay gặp:

    • Cache trong bộ nhớ (Map, object) không giới hạn kích thước hoặc thời hạn.
    • Event listener đăng ký mỗi request mà không gỡ (thường kèm cảnh báo MaxListenersExceededWarning).
    • Closure giữ object lớn; timer không clearInterval.
    • Mảng toàn cục lưu dữ liệu request để debug rồi quên xoá.

    Cách điều tra:

    1. Chạy với --inspect, mở Chrome DevTools, chụp heap snapshot trước và sau khi bắn tải, so sánh object tăng thêm và chuỗi retainers.
    2. Dùng --heapsnapshot-near-heap-limit để tự chụp snapshot khi sắp hết bộ nhớ.

    Người phỏng vấn muốn nghe: cache thì dùng LRU có giới hạn hoặc Redis, và biết rằng tăng --max-old-space-size chỉ làm lỗi đến chậm hơn.

    Ôn thêm câu hỏi Node.js →
  3. 03Bạ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

    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.

    Ôn thêm câu hỏi Node.js →
  4. 04Deadlock trong database xảy ra thế nào và xử lý ra sao?SQL & Database · Senior

    Gợi ý trả lời

    Deadlock là khi hai transaction giữ khoá mà bên kia cần và chờ nhau mãi. DB tự phát hiện, chọn một transaction làm nạn nhân và rollback nó.

    Ví dụ: T1 khoá dòng A rồi muốn B, cùng lúc T2 khoá B rồi muốn A.

    Cách giảm deadlock:

    • Luôn khoá tài nguyên theo cùng một thứ tự (vd. sắp xếp theo id trước khi update nhiều dòng).
    • Giữ transaction ngắn, không chờ I/O bên ngoài khi đang giữ khoá.
    • Có index phù hợp để câu UPDATE/DELETE không khoá quá nhiều dòng.
    • Ứng dụng bắt lỗi deadlock và retry có giới hạn.

    Khi điều tra: MySQL dùng SHOW ENGINE INNODB STATUS, PostgreSQL xem log (deadlock được ghi kèm câu lệnh) và pg_locks.

    Người phỏng vấn muốn nghe: bạn coi retry là bình thường nhưng vẫn tìm gốc rễ ở thứ tự khoá.

    Ôn thêm câu hỏi SQL & Database →
  5. 05Khi nào chọn NoSQL (MongoDB, Redis…) thay vì SQL? Trade-off là gì?SQL & Database · Senior

    Gợi ý trả lời

    Chọn theo mô hình truy cập dữ liệu, không theo xu hướng. SQL mạnh ở quan hệ, ràng buộc và transaction; NoSQL mạnh ở schema linh hoạt, mô hình dữ liệu đặc thù hoặc scale ngang dễ hơn.

    • Document (MongoDB): dữ liệu dạng cây, đọc nguyên khối theo một khoá, schema thay đổi thường xuyên.
    • Key-value (Redis): cache, session, rate limit, bộ đếm, hàng đợi nhỏ; dữ liệu chủ yếu nằm trong RAM.
    • Wide-column, time-series, search engine: ghi khối lượng lớn, log, tìm kiếm full-text.

    Trade-off: JOIN và transaction nhiều document hạn chế hoặc đắt hơn, ràng buộc dữ liệu chuyển sang tầng ứng dụng, và phải thiết kế theo query ngay từ đầu.

    Người phỏng vấn muốn nghe: nhiều hệ thống dùng kết hợp, vd. PostgreSQL làm nguồn sự thật, Redis làm cache. PostgreSQL với JSONB cũng đủ cho nhiều nhu cầu "schema linh hoạt".

    Ôn thêm câu hỏi SQL & Database →
  6. 06Chi phí và độ trễ của ứng dụng LLM quá cao. Bạn tối ưu thế nào?AI / Machine Learning · Senior

    Gợi ý trả lời

    Đo trước: chi phí và độ trễ đến từ số token vào/ra, kích thước mô hình, số lần gọi trong một yêu cầu.

    • Chọn mô hình: định tuyến, việc dễ cho mô hình nhỏ, việc khó mới gọi mô hình lớn.
    • Giảm token: prompt gọn, truy xuất ít đoạn nhưng chính xác hơn (rerank), giới hạn độ dài đầu ra.
    • Prompt caching cho phần prompt cố định lặp lại; cache câu trả lời cho câu hỏi trùng.
    • Streaming để người dùng thấy chữ sớm, cải thiện độ trễ cảm nhận.
    • Gộp việc không gấp thành batch.
    • Tự host: quantization, batching động, engine suy luận tối ưu.

    Người phỏng vấn muốn nghe: mọi tối ưu đều kiểm tra lại bằng bộ eval để chắc chất lượng không giảm.

    Ôn thêm câu hỏi AI / Machine Learning →
  7. 07Khi 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

    • Validate mọi input ở biên bằng schema (Zod, Joi, class-validator trong 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 audit hoặ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ó.

    Ôn thêm câu hỏi Node.js →
  8. 08Thiế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

    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 setTimeout hay mảng trong bộ nhớ làm "queue". Restart hoặc deploy là mất hết job.

    Ôn thêm câu hỏi Node.js →
  9. 09Node.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

    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.

    Ôn thêm câu hỏi Node.js →
  10. 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

    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.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở Everfit?

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