Việc làm

06/10 · Hồ Chí Minh · Hà Nội · Hybrid

Principal Frontend Engineer (ReactJS/ VueJS)

ANDPAD VietNam Co., Ltd · Frontend 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 (JavaScript & TypeScript, ReactJS, System Design & Kiến trúc). 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 TypeScript, interface và type khác nhau thế nào? Phân biệt any, unknown và never.JavaScript & TypeScript

    Gợi ý trả lời

    Cả hai đều mô tả hình dạng object và phần lớn trường hợp dùng thay nhau được.

    • interface có thể gộp khai báo (declaration merging) và extends; hợp để mô tả object, API công khai.
    • type mô 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 trong switch.
    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).

    Ôn thêm câu hỏi JavaScript & TypeScript →
  2. 02Debounce 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

    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.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  3. 03Dùng generics và type narrowing trong TypeScript thế nào? Cho ví dụ discriminated union.JavaScript & TypeScript · Senior

    Gợi ý trả lời

    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 trong if/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ó data vừ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.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  4. 04Stale closure trong React là gì? Vì sao setInterval trong useEffect cứ in ra giá trị cũ?ReactJS

    Gợi ý trả lời

    Mỗi lần render tạo ra hàm mới, "chụp" lại props và state của lần render đó. Nếu effect chỉ chạy một lần, callback bên trong mãi thấy giá trị lúc mount.

    useEffect(() => {
      const id = setInterval(() => {
        setCount(count + 1); // count luôn là 0
      }, 1000);
      return () => clearInterval(id);
    }, []);
    

    Cách sửa:

    • Dùng functional update: setCount(c => c + 1), không cần đọc count.
    • Khai báo đúng dependency (effect chạy lại khi giá trị đổi, nhớ cleanup).
    • Với giá trị chỉ cần đọc mới nhất mà không muốn chạy lại effect: lưu vào useRef.

    Hay sai: tắt cảnh báo exhaustive-deps của ESLint cho xong. Cảnh báo đó thường chính là dấu hiệu stale closure.

    Ôn thêm câu hỏi ReactJS →
  5. 05React Server Components là gì? Trong Next.js App Router, khi nào dùng Server Component và khi nào cần "use client"?ReactJS · Senior

    Gợi ý trả lời

    Server Component chạy trên server (lúc build hoặc lúc request), có thể await dữ liệu trực tiếp từ DB hoặc API, và không gửi JavaScript của nó xuống trình duyệt. Trong App Router, component mặc định là Server Component.

    • Dùng Server Component cho: lấy dữ liệu, render nội dung tĩnh, dùng secret hoặc thư viện nặng chỉ cần ở server.
    • Cần "use client" khi dùng state, effect, sự kiện (onClick), API trình duyệt (window, localStorage).
    • "use client" đánh dấu ranh giới: mọi module được import từ file đó đều vào bundle client.
    • Server Component có thể truyền Server Component khác vào Client Component qua children.
    • Props truyền từ server sang client phải serialize được (không truyền hàm thường; Server Action thì được).

    Hay sai: đặt "use client" ở layout gốc, biến cả app thành client và mất lợi ích. Hãy đẩy phần tương tác xuống các component lá.

    Ôn thêm câu hỏi ReactJS →
  6. 06Thiết kế rate limiter cho API. Bạn chọn thuật toán nào và đặt ở đâu?System Design & Kiến trúc

    Gợi ý trả lời

    Rate limiter bảo vệ hệ thống khỏi lạm dụng và quá tải, thường đặt ở API gateway hoặc middleware, đếm theo user, API key hoặc IP.

    Thuật toán:

    • Fixed window: đơn giản nhưng bị dồn request ở ranh giới hai cửa sổ.
    • Sliding window (log hoặc counter): mượt hơn, tốn bộ nhớ hơn.
    • Token bucket: cho phép burst ngắn trong giới hạn tốc độ trung bình, được dùng phổ biến.

    Khi chạy nhiều instance, bộ đếm phải dùng chung, thường là Redis. Thao tác kiểm tra và tăng phải atomic (INCR kèm EXPIRE, hoặc Lua script).

    Trả về HTTP 429 kèm header Retry-After.

    Người phỏng vấn muốn nghe: bạn quyết định được khi Redis lỗi thì cho qua (fail-open) hay chặn (fail-closed) và giải thích lý do, đồng thời biết giới hạn riêng cho endpoint đắt như đăng nhập, gửi OTP.

    Ôn thêm câu hỏi System Design & Kiến trúc →
  7. 07Mộ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

    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:

    • addEventListener trên window/document mà 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:

    1. 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.
    2. Lọc "Detached" để tìm DOM bị giữ, xem chuỗi retainers để biết ai đang giữ.
    3. 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).

    Ôn thêm câu hỏi JavaScript & TypeScript →
  8. 08Core 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

    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.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  9. 09Tell me about a technical decision you made and why.Phỏng vấn tiếng Anh

    Gợi ý trả lời

    "In my last project, we needed to send notifications to users after each order. Instead of calling the notification service directly, I proposed using a message queue. The main reason was reliability: if the notification service was down, messages would wait in the queue instead of being lost. It also made the order API faster. The trade-off was more infrastructure to maintain, but the team agreed it was worth it."

    Mẹo: nói theo thứ tự vấn đề, các lựa chọn, lý do chọn, đánh đổi. Các cụm hữu ích: "The main reason was…", "The trade-off was…", "We considered… but…". Người Việt hay quên nói phần đánh đổi, trong khi đó là điểm người phỏng vấn chờ nghe.

    Ôn thêm câu hỏi Phỏng vấn tiếng Anh →
  10. 10Điểm yếu của bạn là gì?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Chọn điểm yếu thật, không chí mạng với vị trí, và cho thấy bạn đang cải thiện.

    Cấu trúc: điểm yếu, ảnh hưởng đã gặp, việc bạn đang làm để khắc phục, tiến bộ đến nay.

    Câu mẫu: "Trước đây em hay ngại hỏi khi bị kẹt, tự loay hoay quá lâu, có lần làm trễ task. Giờ em tự đặt giới hạn: kẹt quá một hai giờ mà không có hướng thì em hỏi đồng đội, kèm những gì đã thử. Từ khi làm vậy em xong việc đúng hạn hơn và học được nhanh hơn từ người khác."

    Ví dụ khác có thể dùng: chưa quen nói trước đám đông, tiếng Anh nói chưa trôi chảy, hay ôm việc.

    Hay sai: "Điểm yếu của em là quá cầu toàn" (nghe như né câu hỏi), hoặc nêu điểm yếu là kỹ năng cốt lõi của vị trí.

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

Bạn đã phỏng vấn ở ANDPAD VietNam Co., 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