Senior Software Engineer / Infrastructure

Omnissa interview command center

Một trang để nắm bức tranh tuyển dụng, câu hỏi đã gặp, phong cách Nate và Phil, checklist chuẩn bị, bài live coding và câu trả lời tiếng Anh ngắn gọn.

Tóm tắt nhanh

Họ đang tìm người lập trình thật, có ownership, giao tiếp được bằng tiếng Anh.

Quy trình đã đổi vì nhiều ứng viên không hiểu code mình nộp, dùng AI hoặc không giải thích rõ dự án. Vòng 1 hiện rút còn 40 phút, tập trung live coding trực tiếp. Candidate yếu tiếng Anh có thể bị kết thúc sớm.

Điểm thắng là trình bày kinh nghiệm sâu trên codebase lớn, duy trì lâu dài, có ví dụ thực tế về CI/CD, AWS, Docker, monitoring, migration và scripting.

Timeline tuyển dụng và tín hiệu cần rút ra

Tháng 12

Điều chỉnh tiêu chí

Cần ứng viên từng duy trì codebase lớn, lập trình/scripting tốt, kể được vấn đề kỹ thuật cụ thể và đóng góp cá nhân.

Vòng 1

Nate hỏi sâu ownership

Kiến trúc dự án, migration monolith sang microservices, CI/CD GitHub/Jenkins, EC2, deploy image, monitoring, S3, Docker multi-stage.

Vòng 2

Phil kiểm tra code thật

FizzBang, unit tests, SQL aggregation, walkthrough CPU logger/analyzer, memory, sampling, file buffering.

Mới nhất

Bỏ pre-interview exercise

Chuyển sang live coding trong phỏng vấn. AI/tool ngoài màn hình là red flag tức thì.

Interviewer profiles

Nate Artz

Hiring manager, vòng 1

  • Process-oriented, lịch sự nhưng thẳng.
  • Hỏi “your specific contribution” liên tục.
  • Thích walk-through từng bước cấu hình thực tế.
  • Ưu tiên CI/CD, AWS, Docker, monitoring, microservices.
  • OSINT public: LinkedIn cho thấy Nathan Artz là Staff Engineer tại Omnissa/former VMware, Denver; background thiên về machine learning, metrics và phân tích.

Cách thắng: kể câu chuyện cụ thể, có trade-off và kết quả đo được.

Phil Krasko

Technical interviewer, vòng 2

  • Vào IDE nhanh, xem code ngay.
  • Hỏi “what if” đến gốc rễ.
  • Anti-AI mạnh trong phỏng vấn.
  • Ưu tiên memory, CPU behavior, file I/O, SQL, unit tests.
  • OSINT public: username pkrasko khớp LinkedIn/GitHub; LinkedIn ghi Director of Engineering tại VMware, San Francisco, mô tả seasoned developer/manager.

Cách thắng: code sạch, test ngay, giải thích từng dòng và impact khi thay đổi.

Nghĩa Do

Client contact

  • Quản lý budget, headcount và lịch onsite.
  • Viết email formal, nhiều action items.
  • Có tín hiệu team quan tâm UI vì nhắc Vinay, lead UI engineer.

Nếu gặp onsite: chuyên nghiệp, ngắn gọn, có cấu trúc.

Maigret clean run: dùng --no-recursion --no-extracting -n 10; chỉ giữ tín hiệu public có khả năng liên quan cao, loại các username bị extract nhầm như zznate, nx, Nate Erickson.

Câu hỏi lõi

Chuẩn bị câu trả lời bằng tiếng Anh đơn giản, không cần hoa mỹ.

Project và ownership
  • Tell me about yourself and your experience.
  • Were you working on the team or were you the main developer?
  • Which parts were you doing? What code did you write?
  • What technical problems did you solve, and what were the trade-offs?
Infrastructure
  • Go through each step to configure GitHub Actions for multiple teams.
  • How did you set up Jenkins for multiple environments?
  • What does the EC2 architecture look like?
  • How do you deploy images?
  • How do you monitor and troubleshoot slow or down servers?
AWS, Docker, S3
  • Have you set up multi-stage builds?
  • How do you configure an S3 bucket?
  • Are you familiar with deleting data on S3?
  • Which AWS services did you use and why?

Live coding focus

FizzBang

In mỗi số theo format n: EVEN/ODD, thêm BANG nếu chia hết cho 7.

def fizz_bang(start, end):
    return [
        f"{n}: {'EVEN' if n % 2 == 0 else 'ODD'}" + (" BANG" if n % 7 == 0 else "")
        for n in range(start, end + 1)
    ]

Test cần có: odd, even, 7, 14, boundary.

SQL aggregation

Đếm device theo state, platform, model. Sort state A-Z, count giảm dần trong mỗi state.

SELECT
  u.state,
  d.platform,
  d.model,
  COUNT(*) AS total_device_count
FROM device d
JOIN user u ON u.user_id = d.user_id
GROUP BY u.state, d.platform, d.model
ORDER BY u.state ASC, total_device_count DESC;

CPU logger/analyzer: câu hỏi xoáy

  • INTERVAL - 1:psutil.cpu_percent(interval=1) đã block 1 giây, sleep thêm 59 giây để chu kỳ gần 60 giây.
  • f.flush(): đẩy buffer ra OS để giảm rủi ro mất log nếu process/server chết.
  • Bỏ sleep: log dày hơn, tốn disk/I/O hơn; nếu bỏ cả interval blocking có thể thành busy-loop ăn CPU.
  • Bắt peak giữa hai lần log: dùng sampling tần suất cao trong thread/process riêng và lưu max trong window.
  • CSV vài triệu dòng: dùng csv.reader/DictReader streaming, không dùng readlines() hoặc pandas nếu không cần.

Mẫu câu trả lời tiếng Anh

Khi bắt đầu live coding

“Before I start coding, let me clarify the requirements. You want me to iterate from startNumber to endNumber inclusively, print whether each number is even or odd, and append BANG if it is divisible by seven, correct?”

Khi nói về ownership

“My specific contribution was designing and implementing the deployment workflow. I wrote the GitHub Actions YAML, configured environment secrets, added branch protection, and documented the rollback steps.”

Khi gặp bug

“I think there is a logic issue here. Let me trace the input and expected output for this edge case before changing the code.”

Khi nói về AI

“I use AI as a pair programmer for exploring libraries and reviewing ideas, but I always verify the generated code and I do not rely on it during interviews or production changes without understanding it.”

Python live coding kit

Chọn Python: cách làm bài phải rõ, chạy được, có test, giải thích được từng dòng.

Quy trình 7 bước khi nhận đề

  1. Nhắc lại đề bằng tiếng Anh để xác nhận input/output.
  2. Hỏi edge cases: empty input, invalid range, duplicates, large file, negative number.
  3. Viết 2 đến 4 ví dụ nhỏ trước khi code.
  4. Tách hàm pure logic khỏi phần print/CLI để dễ test.
  5. Viết test bằng unittest hoặc ít nhất assert nhanh.
  6. Chạy thử happy path rồi edge cases.
  7. Giải thích complexity: time, memory, I/O impact.

Câu mở miệng nên dùng

“I'll first clarify the expected output, then implement the core function, then add a few unit tests for normal cases and edge cases.”

“I’ll keep the logic in a function instead of printing directly, because that makes it easier to test.”

“For a large input file, I’ll stream rows one by one instead of loading everything into memory.”

Những thói quen ghi điểm

  • Nói ra suy nghĩ khi chọn data structure.
  • Dùng tên biến rõ: threshold, window, line_number.
  • Handle lỗi tối thiểu: file not found, invalid value.
  • Không over-engineer, không framework nặng.
  • Luôn tự thêm test cho boundary và “what if”.

Syntax Python cần nhớ dưới áp lực

Function, type hints, loop

def solve(start: int, end: int) -> list[str]:
    result = []
    for n in range(start, end + 1):
        label = "EVEN" if n % 2 == 0 else "ODD"
        if n % 7 == 0:
            label += " BANG"
        result.append(f"{n}: {label}")
    return result

unittest nhanh

import unittest

class TestSolve(unittest.TestCase):
    def test_even_bang(self):
        self.assertEqual(solve(14, 14), ["14: EVEN BANG"])

if __name__ == "__main__":
    unittest.main()

CSV streaming

import csv

with open("log.csv", newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for line_number, row in enumerate(reader, start=2):
        cpu = float(row["cpu_usage"])
        if cpu >= 90:
            print(line_number, row)

argparse CLI

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("path")
parser.add_argument("--threshold", type=float, default=90.0)
args = parser.parse_args()

try/except đúng mức

try:
    value = float(raw_value)
except ValueError:
    print(f"Invalid number: {raw_value}")
    continue

collections hay dùng

from collections import Counter, defaultdict, deque

counts = Counter(items)
groups = defaultdict(list)
window = deque(maxlen=60)

Thư viện Python nên thuộc cho dạng bài Omnissa

csvĐọc log/CSV lớn theo từng dòng, memory ổn định. Dùng DictReader nếu có header.
unittestViết test nhanh, có sẵn trong standard library, hợp với yêu cầu “write unit tests”.
argparseBiến script thành CLI chuyên nghiệp: path, threshold, interval, output.
datetime/timeTimestamp, sleep, đo thời gian chạy bằng time.perf_counter().
pathlibPath sạch hơn string: Path(path).exists(), read_text().
collectionsCounter, defaultdict, deque cho đếm, group, sliding window.
heapqTop K, priority queue, chọn top N dòng/log mà không sort toàn bộ dữ liệu.
jsonParse log JSON line, handle dữ liệu bẩn bằng JSONDecodeError.
reValidate pattern đơn giản như IP/log line, nhưng tránh regex nếu split rõ hơn.
psutilDùng cho CPU/memory monitoring nếu môi trường đã cài. Biết giải thích cpu_percent(interval=1).

Bài tập nên luyện thêm theo đúng gu Nate/Phil

1. FizzBang nâng cấp

Input start, end, rules. Rule là divisor và word. Output theo thứ tự rule, có unit tests cho boundary, start > end, divisor invalid.

2. CPU peak sampler

Viết hàm nhận list CPU samples theo giây, gom theo cửa sổ 60 giây và trả về max/avg. Giải thích vì sao bắt được peak tốt hơn log mỗi 60 giây.

3. CSV threshold analyzer

Đọc file CSV lớn, in dòng đầu tiên vượt threshold, line number, timestamp, value. Không load cả file.

4. Tail log errors

Mô phỏng tail -f, in dòng mới chứa ERROR. Giải thích vì sao cần sleep ngắn để tránh busy-loop.

5. Sliding window rate limiter

Class RateLimiter(limit, window_seconds), method is_allowed(user_id). Dùng deque, test nhiều user.

6. Top K error IPs

Đọc JSON lines, đếm IP có status >= 500, trả top K. Dùng Counter.most_common(k).

7. Dependency boot order

Cho map service phụ thuộc service khác, trả thứ tự khởi động. Dùng DFS/topological sort, phát hiện cycle.

8. SQL device report

Viết query JOIN device/user, GROUP BY state/platform/model, ORDER BY state ASC và count DESC. Biết giải thích index trên FK và cột group/sort.

Kinh nghiệm làm bài live code với Phil

Nên làm

  • Code chậm hơn một chút nhưng nói rõ từng quyết định.
  • Viết function trả dữ liệu, rồi print ở ngoài nếu cần.
  • Khi bí, nói: “Let me test this with a smaller example.”
  • Sau khi chạy được, tự hỏi: “What if the input is very large?”
  • Nếu sửa bug, giải thích nguyên nhân trước khi gõ.

Không nên

  • Im lặng quá lâu khi đang suy nghĩ.
  • Nhảy ngay vào code khi chưa xác nhận output.
  • Dùng pandas cho bài CSV chỉ cần scan dòng.
  • Viết quá nhiều abstraction khi đề đơn giản.
  • Nói “I used AI to generate this” hoặc nhìn sang màn hình khác.

Checklist 90 giây trước khi submit bài

  1. Chạy test/happy path.
  2. Chạy edge case nhỏ.
  3. Đọc lại format output có đúng dấu cách, dấu hai chấm không.
  4. Nói complexity: “This is O(n) time and O(1) extra memory except the output list.”
  5. Nêu cải tiến nếu có thêm thời gian: logging, argparse, error handling, streaming, tests rộng hơn.

Worked examples

Bài tập mẫu tương tự Omnissa, có lời giải và chú giải để luyện nói khi code.

1. FizzBang chuẩn Omnissa

Đề: Nhận start, end. In từng số theo format n: EVEN/ODD, thêm BANG nếu chia hết cho 7. Viết unit tests.

def fizz_bang(start: int, end: int) -> list[str]:
    if start > end:
        return []

    lines = []
    for number in range(start, end + 1):
        parity = "EVEN" if number % 2 == 0 else "ODD"
        suffix = " BANG" if number % 7 == 0 else ""
        lines.append(f"{number}: {parity}{suffix}")
    return lines
import unittest

class TestFizzBang(unittest.TestCase):
    def test_even_bang(self):
        self.assertEqual(fizz_bang(14, 14), ["14: EVEN BANG"])

    def test_empty_when_start_after_end(self):
        self.assertEqual(fizz_bang(5, 3), [])

Chú giải

  • range(start, end + 1): vì đề nói inclusive.
  • Tách paritysuffix giúp dễ đọc hơn if/else lồng nhau.
  • Trả về list thay vì print trực tiếp để dễ unit test.
  • Complexity: O(n) time, O(n) memory cho output.

Nói khi làm: “I’ll return a list first because it makes the function testable. If the interviewer wants printing, I can print the returned lines afterward.”

2. CSV threshold analyzer

Đề: Đọc file CSV rất lớn có cột timestampcpu_usage. Trả dòng đầu tiên có CPU vượt threshold, kèm line number.

import csv
from pathlib import Path


def find_first_cpu_peak(path: str, threshold: float) -> dict | None:
    with Path(path).open(newline="", encoding="utf-8") as file:
        reader = csv.DictReader(file)
        for line_number, row in enumerate(reader, start=2):
            try:
                cpu_usage = float(row["cpu_usage"])
            except (KeyError, ValueError):
                continue

            if cpu_usage >= threshold:
                return {
                    "line_number": line_number,
                    "timestamp": row.get("timestamp", ""),
                    "cpu_usage": cpu_usage,
                }
    return None
class TestCsvAnalyzer(unittest.TestCase):
    def test_find_first_peak(self):
        path = "sample.csv"
        with open(path, "w", encoding="utf-8") as f:
            f.write("timestamp,cpu_usage\n10:00,30\n10:01,95\n")
        self.assertEqual(
            find_first_cpu_peak(path, 90),
            {"line_number": 3, "timestamp": "10:01", "cpu_usage": 95.0},
        )

Chú giải

  • csv.DictReader stream từng dòng, không load toàn bộ file.
  • enumerate(..., start=2): dòng 1 là header, data đầu tiên là dòng 2.
  • KeyError xử lý thiếu cột, ValueError xử lý số lỗi.
  • Complexity: O(n) time, O(1) memory.

Nói khi làm: “For a multi-million-row file, I will not use readlines or pandas. This scans one row at a time, so memory stays constant.”

3. CPU peak sampler theo cửa sổ 60 giây

Đề: Cho list sample CPU mỗi giây. Gom theo window 60 giây, trả max và average của từng window.

def summarize_cpu_windows(samples: list[float], window_size: int = 60) -> list[dict]:
    summaries = []

    for start in range(0, len(samples), window_size):
        window = samples[start:start + window_size]
        if not window:
            continue
        summaries.append({
            "start_index": start,
            "end_index": start + len(window) - 1,
            "max_cpu": max(window),
            "avg_cpu": sum(window) / len(window),
        })

    return summaries
class TestCpuWindows(unittest.TestCase):
    def test_partial_window(self):
        result = summarize_cpu_windows([10, 20, 99, 30], window_size=3)
        self.assertEqual(result[0]["max_cpu"], 99)
        self.assertEqual(result[1]["avg_cpu"], 30)

Chú giải

  • Bài này trả lời câu “CPU peak xảy ra lúc logger sleep thì sao?”.
  • Sampling mỗi giây rồi lấy max trong window bắt peak tốt hơn log đúng một điểm mỗi 60 giây.
  • Slice tạo list nhỏ theo window. Với streaming thật, có thể dùng biến count/sum/max để O(1) memory.
  • Complexity: O(n) time.

Nói khi làm: “The key idea is to sample more frequently than the reporting interval, then report the maximum observed value in that interval.”

4. Tail log errors

Đề: Theo dõi file log như tail -f, in dòng mới chứa ERROR.

import os
import time


def follow_errors(path: str, sleep_seconds: float = 0.1):
    with open(path, "r", encoding="utf-8") as file:
        file.seek(0, os.SEEK_END)

        while True:
            line = file.readline()
            if not line:
                time.sleep(sleep_seconds)
                continue
            if "ERROR" in line:
                yield line.rstrip("\n")

Chú giải

  • seek(0, os.SEEK_END): chỉ quan tâm dòng mới sau khi script bắt đầu.
  • sleep(0.1): tránh busy-loop chiếm CPU khi chưa có log mới.
  • Dùng yield để function testable và tái sử dụng, thay vì print cứng.
  • Trong phỏng vấn có thể nói đây là generator.

Nói khi làm: “Without the sleep, the loop would continuously poll the file and waste CPU while waiting for new lines.”

5. Sliding window rate limiter

Đề: Cho mỗi user tối đa N request trong W giây. Viết is_allowed(user_id, now).

from collections import defaultdict, deque


class RateLimiter:
    def __init__(self, limit: int, window_seconds: float):
        self.limit = limit
        self.window_seconds = window_seconds
        self.requests = defaultdict(deque)

    def is_allowed(self, user_id: str, now: float) -> bool:
        queue = self.requests[user_id]

        while queue and now - queue[0] >= self.window_seconds:
            queue.popleft()

        if len(queue) >= self.limit:
            return False

        queue.append(now)
        return True
class TestRateLimiter(unittest.TestCase):
    def test_blocks_after_limit(self):
        limiter = RateLimiter(limit=2, window_seconds=10)
        self.assertTrue(limiter.is_allowed("u1", 0))
        self.assertTrue(limiter.is_allowed("u1", 1))
        self.assertFalse(limiter.is_allowed("u1", 2))
        self.assertTrue(limiter.is_allowed("u1", 11))

Chú giải

  • defaultdict(deque): mỗi user có queue timestamp riêng.
  • popleft() O(1), tốt hơn list pop(0) là O(n).
  • Truyền now vào hàm giúp test không phụ thuộc thời gian thật.
  • Complexity mỗi call phụ thuộc số request cũ bị dọn, trung bình rất nhẹ.

Nói khi làm: “I inject the current time as a parameter because it makes the logic deterministic and easy to unit test.”

6. Top K error IPs từ JSON logs

Đề: Đọc JSON lines, đếm IP có status >= 500, trả top K.

import json
from collections import Counter


def top_error_ips(lines: list[str], k: int) -> list[tuple[str, int]]:
    counts = Counter()

    for line in lines:
        try:
            record = json.loads(line)
        except json.JSONDecodeError:
            continue

        ip = record.get("ip")
        status = int(record.get("status", 0))
        if ip and status >= 500:
            counts[ip] += 1

    return counts.most_common(k)

Chú giải

  • Counter hợp cho bài đếm tần suất.
  • Bỏ qua JSON lỗi để script bền với log bẩn.
  • most_common(k) rõ nghĩa và ngắn.
  • Nếu input là file lớn, đổi lines thành iterator đọc từng dòng.

Nói khi làm: “I’m making the parser tolerant of bad log lines because production logs are often messy.”

7. Dependency boot order

Đề: Cho dependency map, trả thứ tự khởi động service. Nếu có cycle thì báo lỗi.

def boot_order(dependencies: dict[str, list[str]]) -> list[str]:
    order = []
    visiting = set()
    visited = set()

    def visit(service: str):
        if service in visited:
            return
        if service in visiting:
            raise ValueError(f"Cycle detected at {service}")

        visiting.add(service)
        for dependency in dependencies.get(service, []):
            visit(dependency)
        visiting.remove(service)

        visited.add(service)
        order.append(service)

    for service in dependencies:
        visit(service)

    return order
class TestBootOrder(unittest.TestCase):
    def test_dependencies_first(self):
        deps = {"api": ["db", "cache"], "db": [], "cache": []}
        self.assertEqual(boot_order(deps), ["db", "cache", "api"])

    def test_cycle(self):
        with self.assertRaises(ValueError):
            boot_order({"a": ["b"], "b": ["a"]})

Chú giải

  • Đây là topological sort bằng DFS.
  • visiting phát hiện cycle trong recursion stack.
  • visited tránh xử lý lại service đã xong.
  • Phù hợp câu hỏi infrastructure: service boot order, dependency graph, deployment order.

Nói khi làm: “I use two sets: visiting for the current recursion path to detect cycles, and visited for completed nodes.”

8. SQL device report

Đề: Hai bảng deviceuser. Tổng số device theo state, platform, model. Sort state alphabetically, count giảm dần trong mỗi state.

SELECT
  u.state,
  d.platform,
  d.model,
  COUNT(*) AS total_device_count
FROM device AS d
JOIN user AS u
  ON u.user_id = d.user_id
GROUP BY
  u.state,
  d.platform,
  d.model
ORDER BY
  u.state ASC,
  total_device_count DESC;

Chú giải

  • JOIN nối device với user để lấy state.
  • GROUP BY tất cả cột không nằm trong aggregate.
  • COUNT(*) đếm số device trong từng nhóm.
  • Index nên có: device.user_id, user.user_id; tùy DB có thể thêm index hỗ trợ group/sort.

Nói khi làm: “The grouping columns define the report granularity: state plus platform plus model. Then I sort by state ascending and count descending inside each state.”

VS Code thao tác nhanh

Mẹo dùng VS Code khi live coding Python: gọn, rõ, không giống đang phụ thuộc tool ngoài.

Setup trước buổi phỏng vấn

  • Mở folder trống, ví dụ omnissa-live-code/.
  • Tạo sẵn solution.pytest_solution.py.
  • Chọn đúng Python interpreter trước giờ phỏng vấn.
  • Tắt notification, tab AI, Copilot/AI suggestions nếu có.
  • Font editor đủ lớn khi share screen.
  • Terminal đang ở đúng folder và chạy được python --version.

Layout nên dùng

  • Chia editor 2 cột: trái là solution, phải là test.
  • Terminal đặt dưới cùng để interviewer thấy command và output.
  • Ưu tiên chạy bằng terminal thay vì nút Run, vì minh bạch hơn.
  • Không mở notebook, không mở project cá nhân nhiều file.

“I’ll keep the implementation and tests side by side so it is easier to verify each step.”

TerminalCtrl + ` hoặc Cmd + ` trên Mac.
Command PaletteCtrl + Shift + P, Mac: Cmd + Shift + P.
Find fileCtrl + P, Mac: Cmd + P.
Split editorCtrl + \, Mac: Cmd + \.
Comment lineCtrl + /, Mac: Cmd + /.
Duplicate lineShift + Alt + Down, Mac: Shift + Option + Down.
Move lineAlt + Up/Down, Mac: Option + Up/Down.
Multi-cursorAlt + click hoặc Ctrl + D, Mac: Option + click hoặc Cmd + D.

Command nên chạy trong terminal

python --version
python solution.py
python -m unittest -v test_solution.py
python -m pip install psutil

Nếu máy dùng python3, thay python bằng python3. Không mất thời gian tạo venv nếu đề không yêu cầu.

Cách xử lý lỗi nhìn chuyên nghiệp

Đọc lỗi thành tiếng, xác định nguyên nhân, sửa nhỏ, chạy lại.

“This looks like an off-by-one issue. The requirement says inclusive, so I should use range(start, end + 1).”

“I’ll add a temporary print to inspect the value, then remove it once the bug is clear.”

Câu nói mở đầu khi chọn Python

“I’ll use Python for this exercise. I’ll first clarify the expected input and output, then write the core function, add a few unit tests, and run them from the terminal.”

Checklist trước giờ phỏng vấn

Red flags cần tránh