Việc làm

Hôm qua · Hồ Chí Minh · Tại văn phòng

[Ho Chi Minh] Technical Architect

ONE Tech Stop Vietnam Company Ltd · Software/Technical Architect

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, System Design & Kiến trúc, ReactJS). 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. 01Thiế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 →
  2. 02Event loop của Node gồm những phase nào? process.nextTick, Promise, setImmediate và setTimeout chạy theo thứ tự nào?Node.js

    Gợi ý trả lời

    Event loop của Node (libuv) đi qua các phase chính: timers (setTimeout, setInterval), poll (chờ và xử lý I/O), check (setImmediate), close callbacks.

    Giữa các callback, Node chạy hết hàng đợi process.nextTick trước, rồi đến microtask của Promise.

    setTimeout(() => console.log("timeout"));
    setImmediate(() => console.log("immediate"));
    Promise.resolve().then(() => console.log("promise"));
    process.nextTick(() => console.log("nextTick"));
    // nextTick, promise, rồi timeout/immediate
    
    • Thứ tự giữa setTimeout(0) và setImmediate ở module chính là không chắc chắn; nhưng nếu cả hai được đặt trong một callback I/O thì setImmediate luôn chạy trước.

    Hay sai: gọi process.nextTick đệ quy, khiến event loop không bao giờ sang được phase I/O.

    Ôn thêm câu hỏi Node.js →
  3. 03Stream trong Node là gì? Backpressure là gì? Làm sao xử lý file vài GB mà không hết RAM?Node.js

    Gợi ý trả lời

    Stream xử lý dữ liệu theo từng chunk thay vì đọc hết vào bộ nhớ. Có bốn loại: Readable, Writable, Duplex, Transform.

    Backpressure: khi bên ghi chậm hơn bên đọc, dữ liệu dồn lại trong bộ đệm. write() trả về false để báo nên dừng đọc cho đến khi có sự kiện drain.

    Dùng pipeline để tự xử lý backpressure, lỗi và dọn dẹp:

    import { pipeline } from "node:stream/promises";
    import { createGzip } from "node:zlib";
    
    await pipeline(
      fs.createReadStream("big.csv"),
      createGzip(),
      fs.createWriteStream("big.csv.gz"),
    );
    

    Ứng dụng: upload/download file lớn, xuất CSV từ DB theo con trỏ, proxy dữ liệu.

    Hay sai: dùng fs.readFile cho file lớn, hoặc .pipe() nối nhiều bước mà không bắt lỗi ở từng stream, khiến file descriptor bị rò.

    Ôn thêm câu hỏi Node.js →
  4. 04Monolith và microservices: khi nào nên tách? Cái giá phải trả là gì?System Design & Kiến trúc

    Gợi ý trả lời

    Microservices giải quyết vấn đề tổ chức: nhiều team cần deploy độc lập và scale riêng từng phần. Nó không tự làm hệ thống nhanh hơn.

    Nên cân nhắc tách khi:

    • Nhiều team đụng nhau trong cùng codebase, deploy phải chờ nhau.
    • Một phần có tải hoặc yêu cầu công nghệ rất khác phần còn lại.
    • Ranh giới nghiệp vụ (bounded context) đã rõ ràng và ổn định.

    Cái giá: gọi mạng thay vì gọi hàm (độ trễ, lỗi một phần), nhất quán dữ liệu phân tán, cần tracing, service discovery, CI/CD và vận hành phức tạp hơn.

    Người phỏng vấn muốn nghe: "modular monolith" là bước khởi đầu hợp lý: chia module rõ ràng trong một ứng dụng, sau này tách theo đúng ranh giới đó. Tách sai ranh giới tạo ra "distributed monolith", tệ hơn cả monolith.

    Ôn thêm câu hỏi System Design & Kiến trúc →
  5. 05Thiết kế cache: các chiến lược cache-aside, write-through và cách xử lý cache invalidation.System Design & Kiến trúc

    Gợi ý trả lời

    Cache giảm độ trễ và tải cho DB, đổi lại phải chấp nhận dữ liệu có thể cũ trong một khoảng thời gian.

    • Cache-aside (phổ biến nhất): đọc cache, miss thì đọc DB rồi ghi vào cache kèm TTL. Khi cập nhật thì ghi DB rồi xoá key cache.
    • Write-through: ghi qua cache rồi cache ghi xuống DB, dữ liệu cache luôn mới nhưng ghi chậm hơn.
    • Write-behind: ghi cache trước, đẩy xuống DB sau; nhanh nhưng có thể mất dữ liệu.

    Các vấn đề cần xử lý:

    • Cache stampede: key nóng hết hạn, hàng nghìn request cùng đánh vào DB. Giải pháp: lock hoặc single-flight, làm mới sớm, thêm độ lệch ngẫu nhiên cho TTL.
    • Cache penetration: truy vấn key không tồn tại liên tục. Giải pháp: cache cả kết quả rỗng, dùng bloom filter.

    Hay sai: cập nhật cache thay vì xoá, dễ ghi đè giá trị cũ khi hai request chạy song song.

    Ôn thêm câu hỏi System Design & Kiến trúc →
  6. 06Custom hook là gì? Các quy tắc của hooks là gì và vì sao có chúng?ReactJS

    Gợi ý trả lời

    Custom hook là hàm bắt đầu bằng use, dùng các hook khác bên trong, để tái sử dụng logic có state giữa nhiều component (không chia sẻ state, mỗi component có bản riêng).

    function useDebounce(value, ms) {
      const [debounced, setDebounced] = useState(value);
      useEffect(() => {
        const id = setTimeout(() => setDebounced(value), ms);
        return () => clearTimeout(id);
      }, [value, ms]);
      return debounced;
    }
    

    Quy tắc: chỉ gọi hook ở cấp cao nhất của component hoặc custom hook; không gọi trong if, vòng lặp hay sau return sớm. Lý do: React nhận diện từng hook theo thứ tự gọi, đổi thứ tự là state bị ghép nhầm.

    Ngoại lệ: hook use() của React 19 được gọi trong điều kiện.

    Người phỏng vấn muốn nghe: bạn bật ESLint plugin react-hooks để bắt các lỗi này tự động.

    Ôn thêm câu hỏi ReactJS →
  7. 07Một endpoint làm tác vụ nặng CPU (xử lý ảnh, tạo báo cáo) làm cả server chậm theo. Bạn xử lý thế nào?Node.js

    Gợi ý trả lời

    Tác vụ CPU chạy trên main thread sẽ chặn event loop, nên mọi request khác phải chờ. Có vài hướng:

    • worker_threads: chạy tính toán ở thread khác trong cùng process. Nên dùng một pool worker (ví dụ thư viện Piscina) thay vì tạo worker mới cho mỗi request.
    • Hàng đợi công việc: API chỉ nhận yêu cầu, đẩy job vào queue (BullMQ trên Redis, RabbitMQ), trả về 202 kèm mã job; worker riêng xử lý, client hỏi lại trạng thái hoặc nhận thông báo.
    • Tách service: nếu việc nặng là trọng tâm, viết bằng ngôn ngữ phù hợp hơn hoặc dùng dịch vụ chuyên dụng.

    cluster hay PM2 chạy nhiều process giúp tận dụng nhiều core, nhưng không giải quyết việc một request nặng chặn process của nó.

    Người phỏng vấn muốn nghe: bạn đo được độ trễ event loop (perf_hooks.monitorEventLoopDelay) để phát hiện vấn đề này.

    Ôn thêm câu hỏi Node.js →
  8. 08Trong 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 →
  9. 09Process 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 →
  10. 10Điểm mạnh lớn nhất của bạn là gì?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Chọn một hai điểm mạnh liên quan tới vị trí, và chứng minh bằng ví dụ thật, không liệt kê tính từ.

    Cấu trúc: điểm mạnh, ví dụ ngắn, kết quả, liên hệ với công việc mới.

    Câu mẫu: "Điểm mạnh của em là chủ động tìm nguyên nhân gốc. Ở dự án trước, hệ thống thỉnh thoảng bị timeout mà không ai tái hiện được. Em tự đọc log, thêm theo dõi ở các điểm nghi ngờ và tìm ra một truy vấn bị khoá bảng vào giờ cao điểm. Sau khi sửa, lỗi đó không còn xuất hiện. Em nghĩ điều này hữu ích với vị trí bên mình vì hệ thống có lượng truy cập lớn."

    Hay sai: "Em chăm chỉ, ham học hỏi, chịu được áp lực" mà không có ví dụ. Ai cũng nói vậy nên không đọng lại gì.

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

Bạn đã phỏng vấn ở ONE Tech Stop Vietnam Company Ltd?

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