← 返回部落格列表 Shopify OA 2026 完整攻略|Backend / Full-Stack 真題、Cart 狀態機與 Webhook 冪等性
Shopify

Shopify OA 2026 完整攻略|Backend / Full-Stack 真題、Cart 狀態機與 Webhook 冪等性

2026-05-16

Shopify 自從 2022 大幅收緊 University Recruiting 後,2026 cycle 又把校招與 Early Career 通道重新開放——但 OA 也從過去的「一道 LC Medium」變成了一套業務導向極強的實戰題。本文基於一畝三分地、Reddit r/cscareerquestionsCAD 與 Glassdoor 上 2026-03 到 2026-05 的最新回饋,把 Shopify OA 的平台、題型、評分邏輯、Onsite 銜接講清楚。

Shopify OA 概覽

維度 細節
平台 CodeSubmit(Backend 主流)/ HackerRank(Full-Stack 偶爾)
時長 90-120 分鐘(Backend 90 / Full-Stack 120)
題量 1-2 道開放式程式題(不是 LC 風格)
難度 LC Medium 演算法 + 業務建模 + 測試覆蓋
通過線 全部 hidden test 過 + 程式碼 review 評分 ≥ 7/10
回饋週期 5-14 天
二面 Life Story 行為面 + Technical Pair Programming

Shopify 獨特之處:OA 不是純演算法題,而是模擬真實業務——你交付的程式碼會被 Senior Engineer 像 PR review 一樣打分:可讀性、錯誤處理、測試組織都計分。Lambda 一行流不會加分,反而扣分

真題一:Cart Inventory State Machine(購物車庫存狀態機)

題目描述

實作一個最小化 Cart 類別,支援以下方法:

庫存與單價以建構子注入:Cart(inventory: dict, prices: dict)

解題思路

這道題不是演算法題,是軟體工程題。Shopify 評分維度:

  1. 狀態機清晰:購物車有「待結算 / 結算中 / 已完成」三態,checkout 之前不要扣庫存
  2. 錯誤碼 vs 例外:題目要求回傳字串 → 不要 raise
  3. 原子性:checkout 失敗必須回滾——乾淨的寫法是先校驗再提交,不是「先扣再補」
  4. 可測性:庫存與價格透過 DI 注入,方便 mock

Python 解法

from collections import defaultdict

class Cart:
    def __init__(self, inventory: dict, prices: dict):
        self.inventory = dict(inventory)
        self.prices = dict(prices)
        self.items = defaultdict(int)
        self.discount = 0.0

    def _subtotal(self) -> float:
        return sum(self.prices[p] * q for p, q in self.items.items())

    def add_item(self, product_id, qty):
        if self.inventory.get(product_id, 0) < self.items[product_id] + qty:
            return "OUT_OF_STOCK"
        self.items[product_id] += qty
        return round(self._subtotal() * (1 - self.discount), 2)

    def remove_item(self, product_id, qty):
        if product_id not in self.items:
            return "NOT_FOUND"
        self.items[product_id] -= qty
        if self.items[product_id] <= 0:
            del self.items[product_id]
        return round(self._subtotal() * (1 - self.discount), 2)

    def apply_discount(self, code):
        if code == "SAVE10" and self._subtotal() >= 100:
            self.discount = 0.10
            return "OK"
        return "INVALID"

    def checkout(self):
        # 先全量校驗,再提交——避免部分扣減
        for p, q in self.items.items():
            if self.inventory.get(p, 0) < q:
                return "INVENTORY_CHANGED"
        for p, q in self.items.items():
            self.inventory[p] -= q
        total = round(self._subtotal() * (1 - self.discount), 2)
        self.items.clear()
        self.discount = 0.0
        return total

時間複雜度:每次操作 O(k),k = 購物車 SKU 數 評分關鍵checkout 的兩階段(驗證 + 提交)幾乎是必拿分點。先扣後補的解法即使 hidden test 全過,code review 分也會被扣 2 分以上

真題二:GraphQL Webhook Idempotency(訂單 webhook 去重)

題目描述

Shopify 給商家推送訂單 webhook(POST 請求),但同一事件可能因為重試重複發送 2-5 次。每個 webhook payload 包含:

{ "event_id": "evt_abc", "order_id": 1001, "amount": 250.0, "ts": 1715000000 }

實作 WebhookHandler

解題思路

經典「冪等性 + 滑動視窗」組合:

Python 解法

from collections import deque

WINDOW = 24 * 60 * 60  # 24 hours in seconds

class WebhookHandler:
    def __init__(self):
        self.seen = set()
        self.queue = deque()  # (ts, event_id)
        self.total = 0.0

    def _evict(self, now):
        while self.queue and self.queue[0][0] < now - WINDOW:
            _, eid = self.queue.popleft()
            self.seen.discard(eid)

    def handle(self, payload):
        eid = payload["event_id"]
        ts = payload["ts"]
        self._evict(ts)
        if eid in self.seen:
            return "DUPLICATE"
        self.seen.add(eid)
        self.queue.append((ts, eid))
        self.total += payload["amount"]
        return "PROCESSED"

    def revenue_total(self):
        return round(self.total, 2)

時間複雜度:handle 均攤 O(1)(每個 event_id 最多被 push/pop 一次) 陷阱_evict 一定要用當前 payload 的 ts,而不是 time.time()——hidden test 會發送亂序時間戳來檢查這一點。

真題三:Product Tag Search(前綴 + 模糊比對)

題目描述

實作一個產品檢索服務,store 端會註冊大量商品標籤(如 ["organic", "vegan", "gluten-free"]),並支援:

解題思路

Python 解法

from collections import Counter, defaultdict
import heapq

class TagSearch:
    def __init__(self):
        self.children = defaultdict(dict)
        self.tag_to_products = defaultdict(set)
        self.tag_count = Counter()

    def _insert(self, tag, pid):
        node = self.children
        for ch in tag:
            if ch not in node:
                node[ch] = {}
            node = node[ch]
        node.setdefault("$products", set()).add(pid)

    def _collect(self, node, out):
        if "$products" in node:
            out.update(node["$products"])
        for k, v in node.items():
            if k != "$products":
                self._collect(v, out)

    def index(self, pid, tags):
        for t in tags:
            self.tag_count[t] += 1
            self.tag_to_products[t].add(pid)
            self._insert(t, pid)

    def search(self, query):
        node = self.children
        for ch in query:
            if ch not in node:
                return []
            node = node[ch]
        result = set()
        self._collect(node, result)
        return sorted(result)

    def top_tags(self, k):
        return [t for t, _ in heapq.nsmallest(
            k, self.tag_count.items(), key=lambda x: (-x[1], x[0]))]

時間複雜度:index O(L),search O(L + R),L = 標籤長度,R = 命中商品數

備考策略

優先級 重點 推薦題號 / 資源
⭐⭐⭐ 設計 / 狀態機 LC 146、LC 460、LC 1396、LC 355
⭐⭐⭐ Trie / 前綴樹 LC 208、LC 211、LC 642
⭐⭐ 滑動視窗 / 雙端佇列 LC 239、LC 1438、LC 1004
⭐⭐ OOP / 測試組織 自練:寫測試覆蓋率 ≥ 80% 的 mini-cart
GraphQL 基礎 Shopify dev docs - GraphQL Admin API

時間分配:90 分鐘 = 讀題 5 + 類別設計 10 + 寫第一版 35 + 寫測試 20 + debug 20。寫測試不是可選項——Shopify 的評分明確包含「test coverage」。


FAQ

Q1:Shopify OA 平台到底是 CodeSubmit 還是 HackerRank?

2026 Backend 職缺主要走 CodeSubmit——這是一個讓你下載 repo、本地寫、git push 提交的平台,更像真實專案。Full-Stack 與 Front-End 偶爾走 HackerRank,但比例不到 20%。CodeSubmit 沒有 hidden test 進度條,提交後 5-14 天才出結果。

Q2:Shopify OA 沒有 hidden test 進度條,怎麼知道自己寫對了?

自己的單元測試。CodeSubmit 提交後的 README 通常會要求 npm testpytest 能跑通。評分官會先看你的測試覆蓋率——這是 Shopify 工程文化的體現。建議至少覆蓋:normal path、edge case(空輸入)、error path(無效 ID)、並發或時序(如適用)。

Q3:Shopify 後端用 Ruby on Rails,OA 必須用 Ruby 嗎?

不需要。CodeSubmit 倉庫通常給出 Ruby / Python / JS / Go / Java 多個 starter,選你最熟的就行。Senior Engineer review 的是程式碼組織而不是語言慣用法。不過如果你的履歷上寫「精通 Ruby」,又選其他語言,建議 Onsite 時準備解釋一下。

Q4:Shopify OA 通過後多久 onsite?

通常 2-3 週進入 Recruiter Call → Life Story 行為面(45 min)→ Technical Pair Programming(60 min)→ Hiring Manager。Life Story 是 Shopify 的招牌環節,從你的整個職業生涯順著講一遍,每段經歷追問「為什麼離開 / 加入 / 轉向」——準備至少 3 個職業轉折點的清晰敘事。

Q5:Shopify 2026 Early Career 在加拿大和美國職缺有什麼區別?

加拿大(Ottawa / Toronto / Remote-CA)名額較多,遠端 OK。美國職缺近兩年招聘極少,遠端美國常被拒絕——除非已有 work authorization。薪資:CAD New Grad 約 100-115K + RSU,US 等級折算後實際略高。

Q6:Shopify OA 第一次沒過,多久能重投?

12 個月窗口期。建議在第二次投遞前主動準備一份「工程改進筆記」:列出第一次 OA 你認為沒做好的點(測試、錯誤處理、命名)。Shopify recruiter 偶爾會讓你解釋這段反思——他們更看重「成長性」。


聯絡方式

正在準備 Shopify、Square、Stripe 這類業務導向 OA 的 candidate,最缺的不是演算法題,而是怎麼組織程式碼 + 寫出 PR 級別的測試。我們整理了 2025-2026 cycle 的 Shopify CodeSubmit 真題包(含完整可執行的多語言 starter + 評分標尺),歡迎聯絡交流。

立即加入微信 Coding0201獲取 Shopify OA 真題與程式碼組織模板