Articles · Microsoft
Microsoft 台灣軟體工程師面試:解題、設計與測試怎麼準備
Microsoft 台灣軟體工程師面試準備指南:依官方技術指南,把澄清、解題、設計與測試連成可追問的答案。用原創健康區段程式檢查未知值、同長規則、輸入型別與不變量,再推演多服務、時間間隔和晚到資料。區分通用官方方向與自己的招募邀請,不推定台灣固定輪次、平台或題數,並整理能說清楚個人貢獻的專案證據。
準備 Microsoft 台灣軟體工程師面試,不能只問「要刷哪些題」。更有用的問題是:我能否把模糊要求變成清楚契約,寫出可執行的程式,主動找出反例,再說明需求改變時設計會怎麼調整?測試不是寫完後的附錄,而是答案的一部分。
本文以 Microsoft 官方技術面試指南為準備依據,不宣稱台灣各團隊有相同輪次、題數或平台。
先確認職缺,再使用官方技術指南
Microsoft 官方技術面試指南把解決問題、設計、撰寫程式與測試列為工程生命周期的評估面向。指南要求先澄清不確定處、提出計畫,使用熟悉的語言寫實際程式,並在宣布完成前測試。這是官方的通用準備方向,不是某一筆台灣職缺的完整面試行程。
先保存職缺名稱、job ID、工作地點、職等與團隊責任。台灣的校園計畫、實習、一般 SWE、雲端基礎設施與 AI 相關職缺,不應因為同屬 Microsoft 就共用一份流程推定。看到別人的面經時,也先確認年度、地區與職位是否相同。
本次資料不足以建立由兩份獨立、同週期台灣 SWE 面經交叉驗證的固定流程,因此本文集中在可準備的工程能力。若招募人員已提供面試安排,以你的邀請為準。沒有確認的部分,應直接詢問,而不是把其他地區的 OA、工具政策或面試時間當作自己的規則。
可以一次問清楚:「這次職缺的技術討論包含哪些面向?可用語言與編輯環境是什麼?是否有設計或專案深挖?文件、搜尋與 AI 工具的使用規則為何?」答案會影響練習方式,但不需要知道真題才開始準備。
解題第一步:把自然語言變成可測試的契約
以下是原創練習,不是 Microsoft 已確認的面試題:給定依觀測順序排列的健康狀態,找出最長的連續健康區段,回傳起始索引與長度。同長時選較早開始的區段;沒有健康值則回傳 None。輸入元素只允許 True、False 或 None,其中未知狀態 None 會中斷區段。
面試時先確認「連續」是相鄰索引,還是時間間隔也必須符合條件。本題先採相鄰索引,不排序、不跨過未知值,也不把數字 1 當成健康。這些不是語言細節,而是會改變答案的產品規則。
用兩個例子建立共同理解:[True, True, None, True, True] 應回傳 (0, 2),因為兩段同長而第一段更早;[False, None] 則沒有答案。回傳 (0, 0) 容易讓呼叫端誤以為零號位置是有效結果,因此選擇明確的缺席值。
先提出正確但較慢的基線:從每個索引開始向右掃描,找到該位置的健康段,再比較候選。最壞情況需要平方級時間。接著指出重複工作:同一段的長度被反覆計算。一次掃描只需要保存目前段與最佳段。
一個看似合理、其實不符合契約的版本
常見草稿會用 if value 判斷健康,並在 current_length >= best_length 時更新答案。這個版本有兩個獨立問題:truthiness 接受不在契約內的值;大於等於則讓較晚出現的同長區段覆蓋較早答案。
不要只說「可能有邊界錯誤」。提出最小反例:[True, False, True] 中,最後一個健康值不能把第一個同長答案換掉。再測 [1],要求拒絕而不是回傳健康長度。兩個測試分別定位比較規則與輸入驗證,避免修好其中一項就誤以為全部完成。
下面的修正版先完成整份輸入的型別檢查,再計算區段。這多走一遍資料,但仍是線性時間,且錯誤輸入不會被部分處理。這裡選擇 Python 示範,面試時應使用邀請允許、且你熟悉的語言。
def longest_healthy(samples):
for value in samples:
if value is not None and type(value) is not bool:
raise ValueError("expected bool or None")
best_start = None
best_length = 0
current_start = 0
current_length = 0
for i, value in enumerate(samples):
if value is True:
if current_length == 0:
current_start = i
current_length += 1
if current_length > best_length:
best_start = current_start
best_length = current_length
else:
current_length = 0
if best_start is None:
return None
return best_start, best_length
函式假設輸入是可重複迭代的有限序列,不是只能讀一次的串流。若面試官改成 iterator,這個兩遍版本便不適用:可以改為每次讀取時檢查,並說明錯誤可能在處理途中出現。不要把資料介面悄悄換掉後仍沿用原本的驗證承諾。
不變量可以這樣說:「處理完索引 i 後,current_length 是結尾位於 i 的健康段長度;最佳段是目前看過最長、且同長中最早開始的一段。」只在嚴格變長時更新最佳段,讓 tie rule 直接反映在程式裡。
測試不是列名詞,而是證明哪個決策
這份測試表是上述契約的檢查工具,不是公司評分表。每列都應能回答「刪掉哪一行或改錯哪個比較,這個測試會失敗?」
| 輸入 | 預期 | 要暴露的錯誤 |
|---|---|---|
[] | None | 把預設索引當作有效答案 |
[None, False] | None | 未知值被忽略而非中斷 |
[True] | (0, 1) | 單元素與初始更新 |
[True, True, None, True, True] | (0, 2) | 同長時選錯較晚段 |
[False, True, True, True] | (1, 3) | 忘記處理陣列尾端 |
[True, None, True, True] | (2, 2) | 未知值沒有歸零 |
[1] | ValueError | truthiness 取代型別契約 |
手算同長例子時,索引 0、1 讓最佳段成為 (0, 2);索引 2 把目前長度歸零;索引 3 開始新段;索引 4 的長度也是 2,所以不更新。這段 trace 同時說明未知值與同長規則,不需要逐行朗讀全部程式。
再用一個簡單、獨立的平方級實作作為小輸入 oracle,枚舉短序列比對結果。這能驗證更多組合,但不代表已證明所有輸入或生產可靠性。效能測試則要另看最大輸入量、配置與實際成本;不能用小案例通過推定大資料也符合要求。
時間複雜度是 O(n),額外狀態是 O(1),不包括呼叫端已持有的輸入。這個回答比「最佳解是線性」更完整,因為它說清楚了遍歷次數、資料介面與記憶體計算邊界。
設計追問:索引連續不等於時間連續
如果面試官把輸入改成 (service_id, timestamp, status),並要求相鄰健康觀測間隔不得超過指定秒數,原本演算法的「相鄰」就不夠了。先重新確認同一服務內的排序、重複 timestamp、延遲抵達資料與缺測規則,不要直接加一個全域排序就宣稱解決。
下面是原創的設計分支。假設目標是顯示各服務最近一段已確認的健康資料,不是對歷史進行最終結算;時間閾值與保存窗口由題目指定。
| 改變的要求 | 要做的決定 | 驗證方式 |
|---|---|---|
| 多個服務混在一起 | 依服務隔離目前狀態,避免交叉接段 | 交錯輸入兩個服務,結果各自獨立 |
| 缺測超過閾值 | 下一筆即使健康也開新段 | 剛好等於與略大於閾值各測一次 |
| 觀測晚到 | 拒絕、緩衝重排或修正歷史,選一個明確政策 | 晚到健康值不能無條件延長已顯示區段 |
| 同一觀測重送 | 用可辨識的觀測 ID 去重 | 同 ID 重送與同 timestamp 不同 ID 分開測 |
| 資料保存有上限 | 保存窗口與清除策略必須符合查詢需求 | 到期後不能回傳已刪除的歷史區段 |
多服務版本的狀態可能從常數增加為 O(s),其中 s 是保留中的服務數。若要修正晚到資料,還可能需要窗口內觀測集合。此時就應更新複雜度說明,而不是保留原本的常數空間標籤。
若要求跨租戶提供查詢,先定義租戶與服務的識別範圍,再談 cache、資料庫與擴展。單元測試驗證區段規則;整合測試驗證寫入、查詢與隔離;故障測試驗證延遲、重送與恢復。這三類測試處理的是不同失敗,不是把同一個測試改三個名字。
模擬面試:分辨自己卡在契約、演算法還是測試
把上述問題交給練習夥伴時,先讓對方只給簡短要求,不讀完整答案。你需要主動問未知值、同長規則與沒有答案的行為。夥伴不應用提示替你補每個前提;記錄哪些關鍵問題是自己提出,哪些是在提醒後才發現。
第一次嘗試結束後,不用把所有失敗歸類成「速度太慢」。如果你在範例前就寫程式,最後因同長規則重寫,瓶頸是契約;如果規則清楚但需要重複掃描,瓶頸是狀態設計;如果線性實作正確但忘記陣列尾端,瓶頸是驗證與邊界。
下一次練習只改一個條件,例如把同長時的選擇改為最晚開始。應改的是更新規則與測試,並重新說明不變量。若不看答案就能定位到這個分支,表示你理解了 tie rule;若整份程式重寫卻仍不確定原因,則要回到 trace。
再把答案改成回傳所有最長區段。此時不能繼續宣稱常數輸出空間:即使掃描仍是線性,所有候選的列表可能隨輸入增加。先問是否允許多個結果,以及輸出順序,再決定保存方式。這種追問能檢查複雜度是否來自實際資料,而非背下來的標籤。
如果夥伴提出串流版本,你也可以先保留「只回傳一個最佳段」的要求,逐筆更新狀態。但要說清楚目前答案只是已讀資料的最佳值,還不是未來資料的最終答案。未知資料還沒到齊與元素值是 None,則是兩種不同的未知,不應混為一談。
把一個 bug 修復說成完整的工程答案
假設測試發現同長時選了較晚段,好的修復解釋不只是「把大於等於換成大於」。先重述原本契約,指出何時候選不應替換,展示最小反例,再說明修改如何維持已正確的較長段行為。最後加入回歸,讓這個決策不會在未來的 cleanup 中被改回去。
也要檢查測試本身是否正確。如果參考實作與快速版本都從同一段錯誤邏輯改寫,兩者一致不代表真實答案正確。保留手算例子,並讓 oracle 採用不同、直接的枚舉方式,才能減少共同錯誤。
可以建立兩個簡單性質:在末端加上非健康值,不應改變原本最佳段;把一個健康值改成未知,不應增加最佳長度。性質測試補足例子,但不能取代型別拒絕、同長選擇等明確規則。若性質過於寬鬆,錯誤版本仍可能全部通過。
設計答案也使用相同修復思路。若跨租戶測試發現相同 service ID 共用了 cache,就先找識別範圍,而不是只提高 cache 容量。修正後應測兩個租戶的交錯操作,確認查詢與清除都保持隔離,再記錄受影響的既有資料如何處理。
若面試環境不能執行測試,仍可以展示可檢查的手算 trace,明確說「這是手動推演,尚未執行」。若可以編譯與執行,則確認最後修改後的版本確實重跑過;先前的綠燈不能證明現在的程式。這也符合把測試納入答案,而非只放到結尾的準備方向。
專案深挖:準備能被追問的技術證據
選一個你真的參與的專案,用短摘要交代使用者、你的責任與主要限制。接著準備一個曾被測試或觀測推翻的假設:你原先怎麼想、什麼證據不一致、如何縮小問題,以及為何選擇最後的修正。
例如,若你做過健康監控,不能把本文的假設案例說成自己的成果。你可以拿真實專案的去識別化測試、schema 或故障時間線,解釋當時是否處理晚到事件、哪些規則由你決定、哪些由別人負責。沒有生產數據時,直接說是課堂或本地驗證。
數字要能回答分母與觀測窗口。說「錯誤少了很多」不如說明比較了哪些案例、兩個版本如何測試,以及哪些環境還沒驗證。若結果是讓故障可重現、縮短定位步驟,而不是改善延遲,就描述真正的成果。
也準備取捨追問:「如果保存量翻倍會先壞哪裡?如果不能拒絕晚到資料會改什麼?如果只剩一天會保留哪些測試?」這些回答能把熟悉專案變成技術判斷的證據,而不只是履歷背誦。
面試結束前,把已驗證與待確認的項目收束
最後摘要不需要重新講完整解法,可以指出契約、主要狀態、複雜度與已測過的風險。例如:「目前版本處理有限序列,未知值中斷;同長保留最早段;線性時間、常數額外狀態;已測空輸入、尾段與同長,串流和時間間隔版需要另改介面。」這讓對方知道答案的確切範圍。
若尚有 bug,不必把它藏在「大致完成」裡。說明哪個案例仍不符合預期、最早不一致的位置與下一個定位步驟。面試答案的誠實範圍也是工程判斷:知道未知在哪裡,比給一個不成立的完成承諾更有用。
練習紀錄也分開執行與推演。保存最後執行版本、實際測試結果,以及尚未執行的手算例子。下次重寫時拿原始契約重新判斷,不把先前測試通過當成新版本的證明。
五題練習:把測試與追問放進答案
練習時以各題本身的契約為準,不要把本文的健康區段規則套進另一題。
| 完整題目 | 建議留下的證據 |
|---|---|
| Keep Stable Unique Column Names Across Schema Changes | 識別、順序與變更後的回歸測試 |
| Parse a Signed Integer in an Arbitrary Base | 輸入拒絕、符號與數值界線 |
| Validate a Hierarchy and Compute Propagation Time | 結構驗證與計算的責任分界 |
| Testing a New Feature: Planning Test Cases and Handling Flaky Tests | 功能風險、測試層級與 flaky 診斷 |
| Write good tests and define integration tests | 為每個測試命名它要驗證的邊界 |
一次練習結束後,檢查自己是否留下了可執行答案、最小反例、不變量與需求變更的修正方向。如果只能重現解答,下一步是離開提示重新實作;如果程式正確卻說不清規則,下一步是錄下短解釋再比對測試。
從 Microsoft 題目入口選擇符合職缺責任的下一題。準備的目標不是猜中題目,而是讓解題、設計與測試都能接受追問,並把尚未確認的面試安排留給招募窗口確認。