cswork / libraryOpen practice workspace

Articles · Amazon

Amazon Ring 台灣軟體面試準備:C/C++、裝置情境與 LP 故事

Amazon Ring 台灣軟體面試準備指南:先核對台北 IoT 職缺要求,再練 C++ 所有權、斷線、generation、deadline 與過期 ACK。用命令狀態模型、失敗 trace 和 LP 證據表,分開本地單元測試、裝置驗證與真實個人責任。不推定固定輪次,不把 C++ 練習當成必考題,也不虛構生產成果。

Amazon Ring 台灣軟體面試準備,先分清你申請的是哪一種軟體職缺。Ring 的裝置、行動端、雲端與 AI 工作,不會因為同一個品牌就需要相同的技術答案。若 JD 明確要求嵌入式 C/C++、驅動與硬體除錯,準備應把程式所有權、裝置失敗情境與 Leadership Principles 故事連在一起。

可以先用 不可靠裝置的命令分派與 telemetry 對帳題練習。本文依目前的官方台北 IoT/嵌入式軟體職缺限定範圍,提供原創的 stale ACK 狀態模型與 LP 證據整理方法,不把個別面經寫成台灣 Ring 的固定流程。

現行職缺確認了什麼,沒有確認什麼

截至 2026 年 10 月 6 日,Amazon Jobs 的 Ring 台北軟體職缺 10467361描述嵌入式工程師參與 IoT 產品、跨職能協作、完整產品開發與技術責任。基本條件列出至少三年非實習軟體開發經驗、兩年設計或架構經驗,以及至少一種程式語言;沒有指定 C/C++、ARM 或 ISR。這筆有經驗職缺,不是所有 Ring SWE 或新鮮人的統一能力表。

因此,本文的 C++ 命令模型是裝置軟體方向的準備建議,不是該職缺公布的題目或必用語言。只有你的 JD 或邀請進一步要求 C/C++、driver、ISR 或 AI 工作,才把相應練習列為優先。閱讀 JD 時保存 job ID 與查閱日期,分辨實際職責、基本條件與加分條件;舊連結或搜尋摘要不能證明職缺仍開放。

本次查到的個別候選人分享涉及 Ring AI 職位與不同年度經驗,沒有形成兩份同週期、同市場、同職類的流程交叉證據。因此不提供「必考幾題 C++」「固定幾輪」「OA 一定用什麼平台」等結論。自己的邀請與招募窗口才是安排來源。

可以確認下一輪的工作範圍、可用語言與環境、是否包含專案/裝置除錯、是否需事先準備文件,以及工具規則。若資深職缺需要跨團隊領導,也應準備你如何設定工程標準、協助他人定位問題,而不只是單人解題速度。

C/C++ 不只考語法,還要考所有權

準備指標、物件生命週期、RAII、copy/move 與 container 操作時,讓每個知識點對應一個可觀測問題。哪個物件擁有 buffer?callback 執行時來源還存在嗎?裝置斷線後誰取消 pending work?鎖或資源在哪條失敗路徑釋放?

不要把 smart pointer 當成所有生命週期問題的答案。共享所有權可能延長物件生命,卻不會自動處理邏輯上的「這筆回覆已過期」。同樣地,optional 能表達目前沒有 pending command,但是否可以接受某個 ACK,仍需要狀態契約。

如果問題涉及 MCU 或 ISR,需要使用平台允許的同步與呼叫方式。以下 C++ 程式只是單執行緒、host 上可測試的命令狀態模型,不是可直接上機的 driver、完整 Ring 協定,也不是並行安全實作。

原創情境:重新連線後,舊 ACK 不應完成新命令

假設每次成功連線取得新的 generation;同一時間最多有一筆 pending command。命令帶有 ID、generation 與單調時鐘的絕對 deadline。斷線清除 pending;ACK 只有在 ID 與 generation 都符合、且現在時間嚴格小於 deadline 時才能成功。

Generation 在模型中從 6 開始,方便下方 trace 使用 7 與 8;這是練習初始值,不是產品格式。ID 在同一 generation 不應重新使用;模型把 ID 的唯一性留給呼叫端,必須明說這個前提,否則相同 ID 的晚到 ACK 仍可能誤配。

#include <cstdint>
#include <limits>
#include <optional>

struct Pending {
    std::uint64_t id, generation;
    std::int64_t deadline;
};

class CommandState {
    std::uint64_t generation_ = 6;
    bool connected_ = false;
    std::optional<Pending> pending_;
public:
    bool connect() {
        pending_.reset();
        connected_ = false;
        if (generation_ == std::numeric_limits<std::uint64_t>::max())
            return false;
        ++generation_;
        connected_ = true;
        return true;
    }
    void disconnect() { connected_ = false; pending_.reset(); }
    void expire(std::int64_t now) {
        if (pending_ && now >= pending_->deadline) pending_.reset();
    }
    bool send(std::uint64_t id, std::int64_t deadline,
              std::int64_t now) {
        expire(now);
        if (!connected_ || pending_ || deadline <= now) return false;
        pending_ = Pending{id, generation_, deadline};
        return true;
    }
    bool ack(std::uint64_t id, std::uint64_t gen,
             std::int64_t now) {
        expire(now);
        if (!connected_ || !pending_ || pending_->id != id ||
            pending_->generation != gen) return false;
        pending_.reset();
        return true;
    }
};

每個方法的狀態成本都是常數,沒有 queue 或無界 retry。呼叫端提供不倒退的單調時間;deadline 如何計算、持久化、跨重啟辨識與命令重送,不在此模型內。connect() 也不是網路連線實作,而是已確認連線成功後的狀態轉換。

用狀態 trace 驗證,不只看一個成功 ACK

動作狀態與預期
connect,send A:ID 101,deadline 100,now 0generation 7,pending A
disconnect不連線,pending 清空
connect,send B:ID 102,deadline 200,now 20generation 8,pending B
ACK A:ID 101,generation 7,now 30拒絕,pending B 不變
ACK B:ID 102,generation 8,now 199成功,pending 清空
再收到相同 ACK拒絕,沒有 pending 可完成

另開一個測試,只將 B 的第一次 ACK 改成 now 200。這次必須拒絕並清除 expired pending。不要在上表成功完成 B 之後才用 now 200 測超時;那只能測「沒有 pending」,沒有隔離 deadline 規則。

還應測:未連線 send、同時第二筆 send、deadline 等於 now、錯 ID 但正確 generation、正確 ID 但錯 generation,以及 expire 後的新 send。對於錯誤 ACK,應檢查後續正確 ACK 仍能完成,避免拒絕路徑不小心刪掉新命令。

如果面試官允許同時多筆 pending,就需要按 ID 索引、deadline 清除與容量上限,空間成本隨 pending 數增加。如果進一步要求進程重啟後不誤配,generation 的來源與保存方式也需重新設計;只在記憶體裡加一個數字並不夠。

裝置追問:正確性、耗電與可觀測性會互相牽動

retry 能提高暫時性失敗後的成功機會,卻可能增加無線活動、電量消耗與重複副作用。先確認命令是否可安全重試、如何辨識已執行但 ACK 遺失的情況,以及達到上限後對使用者呈現什麼結果。

若裝置長時間離線,不能只回答「等它上線」。需要決定命令是否已過期、恢復連線後會補送哪些內容、最新狀態與歷史命令如何區分。設定變更與一次性動作可能需要不同政策,不能共用一個無限重放清單。

logging 也有取捨。為了定位 stale ACK,可以記錄去識別化的 command ID、generation、狀態變化、deadline 分類與失敗原因;不需要把影像內容、使用者私密資料或憑證放進日誌。保留多少、誰能讀取、如何到期,也屬於設計的一部分。

測試應分層:host unit test 檢查狀態機;模擬 transport 測 disconnect、重送與亂序;裝置整合測試再觀察實際連線、耗電與復原。前兩者成功,不代表已達成實機延遲、radio 或電池目標。

LP 故事:讓工程判斷有證據,不讓故事取代證據

Amazon 官方 Leadership Principles提供文化與行為方向。Customer Obsession、Ownership、Dive Deep 與堅持高標準,可以用來檢視故事中是否有使用者影響、主動責任、調查與長期修正;不需要把每句話硬貼一個 LP 名稱。

以下是原創整理方法,不是候選人的真實成果。假設你確實遇過斷線後狀態錯亂,故事要交代使用者看到什麼、你負責哪一段、如何排除替代假說、為什麼選擇修正,以及驗證到哪個環境。沒有經歷過,便用它練思考,不放進自己的履歷。

故事層次可被深挖的證據
情境與影響事件時間線、影響範圍;不要暴露敏感客戶內容
個人責任你分析、改動、協調的部分,與別人的工作分開
判斷與調查相同 ID、舊 generation、timeout 等假說如何被驗證或推翻
選擇與取捨快速恢復措施與永久修正為何不同
結果與限制回歸結果、實機觀測、尚未覆蓋的故障

如果數據只來自測試環境,就描述測試範圍,不把它說成生產故障率下降。如果長期結果尚未觀測,可以說已建立回歸與監控,並指出下一個驗證窗口。這比用無法回答分母的百分比裝飾故事更可靠。

準備三個追問:「你最初錯在哪個假設?如果其他團隊不同意修正成本怎麼辦?怎麼防止同類錯誤再出現?」資深職缺尤其需要說明如何讓驗證方法可被團隊重用,而不只是自己成功找出一次 bug。

C++ 所有權追問:callback 活著,不代表命令有效

假設 transport callback 捕捉了控制物件的 shared_ptr,因此回覆到達時物件還在。這只能處理物件存活的一部分問題,不能證明該回覆屬於目前連線。callback 仍必須檢查 command ID 與 generation,否則一個合法存活的物件仍可能被過期資料修改。

如果 callback 捕捉的是引用或 raw pointer,則需要另外確認被引用物件在 callback 執行前不會釋放。把它改成 shared ownership 也可能造成 cycle、延後 shutdown 或讓 callback 保存大量資源。應先描述誰持有什麼,再選 smart pointer,而不是看到非同步就全部換成 shared_ptr。

停止流程同樣要有契約。是停止接受新命令後等待目前工作完成,還是立即取消?callback 是否仍可能到達?取消與 ACK 競爭時,誰決定結果?上面的單執行緒模型沒有這些並行保證,若要加入 thread,就要新增同步與狀態轉移的測試。

RAII 適合讓資源釋放跟物件生命週期對齊,但某個硬體操作的完成與取消可能是協定事件,不等於 destructor 會替你等待。面試時應把記憶體釋放、作業取消與裝置狀態清理分開說明。三者名字相近,卻不能共用一個「自動清理」承諾。

如果有 queue,要選擇 ownership transfer:producer enqueue 之後還能否修改 payload?consumer 處理後誰回收?若使用 pool,錯誤與 timeout 路徑是否都交還槽?這些追問把 C++ 語法連到裝置資源限制,不需要猜測 Ring 內部的實作。

一份可深挖的裝置故障練習紀錄

以下是原創示例,不可當作自己的真實工作成果。假設測試在重新連線後顯示命令已完成,但裝置沒有執行新動作。第一個觀測是 client 狀態與裝置回報不一致;「ACK 錯配」此時只是待驗證假說。

先保存狀態時間線:送出 A、斷線、建立新 generation、送出 B、收到 ACK 的 ID 與 generation。若 ACK 屬於舊連線,就能提出可重現輸入。再用錯 ID/正 generation 與正 ID/錯 generation 的組合,確認修正檢查的是兩個欄位,而不是僅因這次 ID 不同而通過。

替代假說也要檢查。例如裝置已執行 B 但顯示未更新,與控制端把 A 的 ACK 當作 B 的完成,是不同故障。前者要對照 telemetry 與結果呈現,後者要檢查命令對應與狀態更新。兩者都可能看起來像「畫面不對」,卻需要不同修正。

若要先恢復服務,可以取消並重建狀態,但須評估重送動作的副作用。恢復措施不是永久修正的證據;永久修正仍需要拒絕 stale ACK 的規則與回歸。對使用者的狀態也應是已取消、已過期或未知結果,而不是一律成功。

LP 故事中的 Dive Deep 可以由這種證據鏈支持:不只看表面症狀,而是把一個假說變成可重現的狀態序列。Ownership 可以由你如何協調 transport、device 與 UI 邊界支持。這些是事後整理經驗的角度,不能先選 LP 再虛構事件。

有經驗職缺:讓修正方法能被團隊沿用

當 JD 涉及技術領導,準備說明你如何讓問題的發現與預防不只依賴自己。可用的真實證據包括共同的命令狀態契約、code review 檢查點、可重播的失敗 trace、版本間回歸,以及新人能使用的除錯步驟。

這些成果也要限定範圍。你若只是提出文件草案,不要說成團隊已採用;若新增測試只在 CI 的模擬環境跑,不要說硬體故障已被全面預防。描述決策、採用狀態與觀測結果,能讓領導故事有真實的進度。

對 AI 工具與自動化,也應以相同標準解釋。若用工具生成測試,需要檢查 expected output、coverage 範圍與是否重複原本程式的錯誤假設。若工具產生 driver 或同步程式,尤其不能以「能編譯」代替平台規則與實機驗證。

面試可以準備一個你拒絕工具建議的真實例子:它缺了哪個前提、你用什麼反例發現,以及最後如何修正。沒有這段經驗時,不必編造;可以用原創練習示範自己的檢查方法,並明確標示這不是工作成果。

最好的故事不是每個決策都完美,而是讓對方看到你何時承認未知、如何用證據縮小問題、如何保護使用者,以及怎麼讓他人理解與驗證修正。這正好把 C++、裝置情境與 LP 接成同一條可追問的線。

最後一輪自我檢查:不要用通過編譯代替工程完整性

在練習結束前,對照狀態契約逐項檢查:每條拒絕路徑是否保留有效 pending?到期是否釋放狀態?斷線是否停止接受新命令?錯誤與成功回覆是否能被區分?這比只測一個正常 send/ACK 更能揭露設計漏洞。

如果你改了 ID 或 generation 型別,也要重新考慮界線與重用。練習程式對 generation 上限採拒絕連線的行為,沒有實作長期服務的輪替方案;產品可能需要更大的 epoch 識別或持久化設計。這個限制應在答案中出現,而不是期待面試官不問。

同樣地,deadline 使用絕對單調時間,是本模型的前提。若改成 wall clock,時鐘調整會影響比較;若跨進程保存,單調時間基準又可能不同。先確認真正需求,再選時間表示,不把每個時間戳都當作可直接相減的同一單位。

最後整理三份不同證據:程式與 unit test 說明狀態邏輯;device test 說明實際裝置行為;LP 故事說明你的責任、取捨與協作。它們互相支持,卻不能互相取代。沒有實機測試就保留實機未知,沒有真實經驗就把故事留作情境練習。

五題把 C++、裝置與故障推理連起來

下列是相關能力題,包含跨公司記錄;不是 Ring 台灣已確認的 assessment bank。依自己的 JD 選題,不用把所有題的技術面向都當成下一輪必考。

完整題目準備輸出
Compare C Generic Techniques and C++ Smart-Pointer Ownership所有權與生命週期的清楚邊界
Implement a Stoppable Producer–Consumer System in C++停止、等待與資源清理的失敗路徑
Design Command Dispatch and Telemetry Reconciliation for Unreliable Devices命令、重送、裝置回報與過期狀態
Design Camera Health Monitoring健康訊號、告警與誤判的驗證
Design an IoT Logging Platform with Late-Arriving Metrics晚到資料與查詢窗口的取捨

從 不可靠裝置命令分派題開始,先畫狀態,再寫測試,最後用自己的真實經驗回答 LP 深挖。讓「我懂 C++」與「我能對裝置失敗負責」之間有可檢查的證據。

資料來源與延伸閱讀