Skip to content

Tech Blog Reopen Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Reopen the Tech blog and publish exactly two one-time Notion imports, with Daily Questions Challenge pinned first and old Tech posts unpublished.

Architecture: Keep the existing VitePress blog structure. Extend the current Markdown loader to preserve pinned and sort pinned posts first, then update navigation and content files without adding a new content system.

Tech Stack: VitePress 1.6.4, Vue 3 theme components, Markdown frontmatter parsed by gray-matter, Node/npm build scripts.


File Structure

  • Modify: docs/.vitepress/loadData.js
    • Responsibility: read Tech post frontmatter, expose pinned, sort pinned posts before date ordering.
  • Modify: docs/.vitepress/config.js
    • Responsibility: re-enable the Tech navigation item only.
  • Create: docs/tech/posts/2026-05-26-daily-questions-challenge-2026.md
    • Responsibility: VitePress Markdown import of the Notion Daily Questions Challenge main article.
  • Create: docs/tech/posts/2026-05-26-time-complexity.md
    • Responsibility: VitePress Markdown import of the Notion Time Complexity article.
  • Move:
    • docs/tech/posts/2022-07-05-heroku.md to docs/tech/archived-posts/2022-07-05-heroku.md
    • docs/tech/posts/2022-cheerio.md to docs/tech/archived-posts/2022-cheerio.md
    • docs/tech/posts/2022-vercel.md to docs/tech/archived-posts/2022-vercel.md

Task 1: Preserve Pinned Metadata And Sort Posts

Files:

  • Modify: docs/.vitepress/loadData.js:84-99

  • [ ] Step 1: Inspect the current loader

Run:

bash
nl -ba docs/.vitepress/loadData.js | sed -n '78,105p'

Expected: the post object includes title, href, date, avatar, excerpt, and tags, and the final sort is b.date.time - a.date.time.

  • [ ] Step 2: Update the loader to preserve pinned and sort pinned posts first

Change loadArticlesFromDirectory() to this exact shape:

js
function loadArticlesFromDirectory(currentDir, asFeed = false) {
  return fs
    .readdirSync(currentDir)
    .map((file) => {
      const src = fs.readFileSync(path.join(currentDir, file), 'utf-8')
      const { data, excerpt, tags } = matter(src, { excerpt: true })
      const post = {
        title: data.title,
        href: `posts/${file.replace(/\.md$/, '.html')}`,
        date: formatDate(data.date),
        avatar: data.avatar,
        pinned: data.pinned === true,
        excerpt,
        tags
      }
      if (asFeed) {
        // only attach these when building the RSS feed to avoid bloating the
        // client bundle size
        post.data = data
      }
      return post
    })
    .sort((a, b) => {
      if (a.pinned !== b.pinned) {
        return a.pinned ? -1 : 1
      }

      return b.date.time - a.date.time
    })
}
  • [ ] Step 3: Verify syntax with build

Run:

bash
npm run docs:build

Expected: build succeeds. Existing old Tech posts may still be present at this point; that is acceptable in this task.

  • [ ] Step 4: Commit loader change

Run:

bash
git add docs/.vitepress/loadData.js
git commit -m "feat(blog): support pinned tech posts"

Expected: one commit containing only docs/.vitepress/loadData.js.

Task 2: Reopen Tech Navigation

Files:

  • Modify: docs/.vitepress/config.js:55-59

  • [ ] Step 1: Inspect the current nav block

Run:

bash
nl -ba docs/.vitepress/config.js | sed -n '53,61p'

Expected: Resume is enabled and Tech, Travel, and Running are commented out.

  • [ ] Step 2: Enable only Tech

Change the nav block to:

js
    nav: [
      { text: 'Resume', link: '/resume'  },
      { text: 'Tech', link: '/tech/'  },
      // { text: 'Travel', link: '/travel/' },
      // { text: 'Running', link: '/running/' }
    ]
  • [ ] Step 3: Build after nav change

Run:

bash
npm run docs:build

Expected: build succeeds.

  • [ ] Step 4: Commit nav change

Run:

bash
git add docs/.vitepress/config.js
git commit -m "feat(blog): reopen tech navigation"

Expected: one commit containing only docs/.vitepress/config.js.

Task 3: Import Two Notion Articles As Markdown

Files:

  • Create: docs/tech/posts/2026-05-26-daily-questions-challenge-2026.md

  • Create: docs/tech/posts/2026-05-26-time-complexity.md

  • [ ] Step 1: Create the Daily Questions Challenge article

Create docs/tech/posts/2026-05-26-daily-questions-challenge-2026.md:

markdown
---
layout: doc
title: "[Daily Questions Challenge] 2026 軟體後端面試題目準備與回顧"
description: 2026 軟體後端面試題目準備與每日問題整理
date: 2026-05-26
avatar: /avatar.jpeg
pinned: true
tags:
  - Interview
  - Backend
  - Daily Questions Challenge
head:
  - - meta
    - property: og:title
      content: "[Daily Questions Challenge] 2026 軟體後端面試題目準備與回顧"
  - - meta
    - property: og:description
      content: 2026 軟體後端面試題目準備與每日問題整理
  - - meta
    - name: twitter:title
      content: "[Daily Questions Challenge] 2026 軟體後端面試題目準備與回顧"
  - - meta
    - name: twitter:description
      content: 2026 軟體後端面試題目準備與每日問題整理
---

<script setup>
  import ArticleTitle from '@theme/components/ArticleTitle.vue'
  import ScrollToTopBtn from '@theme/components/ScrollToTopBtn.vue'
</script>

<ArticleTitle />

<ScrollToTopBtn />

## 前言

在 2026 年 4 月底離開前職後,我從 5 月開始陸續進行了幾場面試。經過幾輪面試,我開始意識到自己對面試題目的準備不夠充分。

回顧面試中遇到的問題,有些題目本身能理解,但回答不夠完整;有些則是我對問題涉及的知識本來就不夠熟悉。

總結來說,以下三個方向是我在準備中沒能夠做好的:

- 針對面試公司產品本身的功課做得不夠(比如說:如果是一個線上課程平台,應該如何設計架構?)
- 對產品功能所使用的技術沒有足夠了解
- 技術基礎不足,無法完整回答白板題

有鑑於此,我決定在自己的 Blog 中,進行一個 Daily Questions Challenge 日更挑戰:每天整理一個過去遇到的面試問題,並將它整理成一篇文章。

而這篇起頭的主文章將會作為置頂文章,除了整理題目連結,也會依照題目類型在這裡分類。

## 題目

### 演算法

- [解釋時間複雜度 (Time Complexity)](./2026-05-26-time-complexity.md)
  • [ ] Step 2: Create the Time Complexity article

Create docs/tech/posts/2026-05-26-time-complexity.md:

markdown
---
layout: doc
title: 解釋時間複雜度 (Time Complexity)
description: 以後端與 Python 開發情境整理 Big O 時間複雜度
date: 2026-05-26
avatar: /avatar.jpeg
tags:
  - Algorithm
  - Time Complexity
  - Interview
head:
  - - meta
    - property: og:title
      content: 解釋時間複雜度 (Time Complexity)
  - - meta
    - property: og:description
      content: 以後端與 Python 開發情境整理 Big O 時間複雜度
  - - meta
    - name: twitter:title
      content: 解釋時間複雜度 (Time Complexity)
  - - meta
    - name: twitter:description
      content: 以後端與 Python 開發情境整理 Big O 時間複雜度
---

<script setup>
  import ArticleTitle from '@theme/components/ArticleTitle.vue'
  import ScrollToTopBtn from '@theme/components/ScrollToTopBtn.vue'
</script>

<ArticleTitle />

<ScrollToTopBtn />

演算法的時間複雜度(time complexity)是一個函式,用於定性描述該演算法的執行時間,這是一個代表演算法輸入值的字串的長度的函式。

在工程上,我們通常用 **Big O 符號 (O)** 來表示。

### 如何看懂並計算一個 Function 的複雜度?

你可以把計算複雜度想像成「**計算程式碼執行的基本步驟數量**」。以下是從「最快到最慢」排序的常見複雜度級別,並搭配 Python 開發情境與範例:

### 1. O(1) - 常數時間 (Constant Time)

不管資料量是一筆還是一百萬筆,執行的時間都一樣,這是最理想的狀態。

- **情境:** 從 Redis 透過 Key 讀取一筆快取,或是從 Python 的 Dictionary (字典) 透過 Key 取值。
- **特徵:** 沒有依賴資料長度的迴圈。

```python
def get_first_item(items):
    # 只執行一次,不管 items 串列有多大,複雜度就是 O(1)
    return items[0]

2. O(log n) - 對數時間 (Logarithmic Time)

資料量大幅增加時,執行時間只會「微微」增加。通常是每次操作都能將剩餘的資料量「砍半」。

  • 情境: 在資料庫的 B-Tree 索引中尋找資料,或是經典的「二分搜尋法 (Binary Search)」。
  • 特徵: 迴圈或遞迴每次都將搜尋範圍縮小一半。
python
def binary_search(sorted_list, target):
    left, right = 0, len(sorted_list) - 1
    while left <= right:
        mid = (left + right) // 2
        if sorted_list[mid] == target:
            return mid
        elif sorted_list[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

3. O(n) - 線性時間 (Linear Time)

資料量有 n 筆,程式就需要執行 n 次。執行時間隨著資料量等比例增加。

  • 情境: 遍歷整個 List 來轉換 API 回傳的資料格式,或是在未排序的資料中尋找特定值。
  • 特徵: 有一層迴圈跑遍所有的資料。
python
def process_all_users(users):
    for user in users:
        # 如果有 100 個 user,這裡就執行 100 次 -> O(n)
        format_data(user)

4. O(n log n) - 線性對數時間 (Linearithmic Time)

稍微比 O(n) 慢一點,通常是將資料切半處理後,再全部合併掃描。這是大多數「高效能排序演算法」的速度極限。

  • 情境: 呼叫 Python 內建的 sort()sorted() 函數(底層是 Timsort),或是使用合併排序 (Merge Sort)。
  • 特徵: 在拆分的過程中 O(log n) 進行線性操作 O(n)。
python
def sort_my_data(data):
    # Python 內建的排序演算法高度優化,時間複雜度為 O(n log n)
    return sorted(data)

5. O(n^2) - 平方時間 (Quadratic Time)

當資料量變大時,執行時間會呈「平方」暴增。這是效能殺手,通常是我們在處理大量資料時需要極力避免的。

  • 情境: 雙層迴圈比對資料。例如找出兩個大型列表中的交集,但沒有先使用 Set 或 Dictionary 建立索引。
  • 特徵: 有兩層巢狀迴圈(外層跑 n 次,內層也跑 n 次)。
python
def find_duplicates(array1, array2):
    for item1 in array1:
        for item2 in array2:
            # 如果兩個串列各有 100 筆,這裡要執行 100 * 100 = 10,000 次 -> O(n^2)
            if item1 == item2:
                return True
    return False

6. O(2^n) - 指數時間 (Exponential Time)

資料量每增加 1,執行時間就「翻倍」。在實務上極度危險,只要資料量稍微大一點(例如 n=50),程式就會跑到當機。

  • 情境: 沒有經過優化(如快取機制 Cache、動態規劃 DP)的遞迴演算法,例如最原始的費氏數列求值、破解密碼的暴力法。
  • 特徵: 一個函式裡呼叫了多次自己(呈樹狀分支展開)。
python
def fibonacci(n):
    if n <= 1:
        return n
    # 每次呼叫都會再產生兩個子呼叫,呈指數爆炸 -> O(2^n)
    return fibonacci(n-1) + fibonacci(n-2)

7. O(n!) - 階乘時間 (Factorial Time)

最可怕的複雜度,比指數時間還慘。資料量只要增加一點點,計算量就會突破天際。

  • 情境: 求出所有的「排列組合」,例如旅行推銷員問題 (TSP) 的純暴力解法。
  • 特徵: 找出所有可能性的全排列。
python
import itertools

def get_all_permutations(items):
    # 如果有 5 個元素,就會產生 5*4*3*2*1 = 120 種組合 -> O(n!)
    # 如果有 10 個元素,會產生 3,628,800 種組合
    return list(itertools.permutations(items))

總結:實務上的心法

如果以執行效能「從最快到最慢」(也就是資料量增加時,執行時間增長幅度由小到大)依序為:

O(1) < O(log n) < O(n) < O(n log n) < O(n^2) < O(2^n) < O(n!)

在後端開發的日常中,我們追求的目標通常是:

  1. 盡量利用 Hash Table (Python 的 Dictionary / Set) 把 O(n^2) 的比對降級為 O(n)。
  2. 對於需要頻繁搜尋的資料,確保資料庫有建索引 (Index),讓查詢維持在 O(log n)。
  3. 看到巢狀的 for 迴圈要有警覺心;看到遞迴函數則要思考有沒有可能引發 O(2^n) 的運算災難。

參考:


- [ ] **Step 3: Build after importing articles**

Run:

```bash
npm run docs:build

Expected: build succeeds and generates these files:

text
docs/.vitepress/dist/tech/posts/2026-05-26-daily-questions-challenge-2026.html
docs/.vitepress/dist/tech/posts/2026-05-26-time-complexity.html
  • [ ] Step 4: Commit article imports

Run:

bash
git add docs/tech/posts/2026-05-26-daily-questions-challenge-2026.md docs/tech/posts/2026-05-26-time-complexity.md
git commit -m "feat(blog): import daily questions tech posts"

Expected: one commit containing only the two new Markdown files.

Task 4: Archive Old Tech Posts

Files:

  • Move: docs/tech/posts/2022-07-05-heroku.md

  • Move: docs/tech/posts/2022-cheerio.md

  • Move: docs/tech/posts/2022-vercel.md

  • Create directory: docs/tech/archived-posts/

  • [ ] Step 1: Create archive directory

Run:

bash
mkdir -p docs/tech/archived-posts

Expected: directory exists.

  • [ ] Step 2: Move old Markdown posts

Run:

bash
git mv docs/tech/posts/2022-07-05-heroku.md docs/tech/archived-posts/2022-07-05-heroku.md
git mv docs/tech/posts/2022-cheerio.md docs/tech/archived-posts/2022-cheerio.md
git mv docs/tech/posts/2022-vercel.md docs/tech/archived-posts/2022-vercel.md

Expected: docs/tech/posts/ contains only the two 2026 Markdown files.

  • [ ] Step 3: Verify source post directory

Run:

bash
find docs/tech/posts -maxdepth 1 -type f -name '*.md' | sort

Expected:

text
docs/tech/posts/2026-05-26-daily-questions-challenge-2026.md
docs/tech/posts/2026-05-26-time-complexity.md
  • [ ] Step 4: Build after archiving old posts

Run:

bash
npm run docs:build

Expected: build succeeds.

  • [ ] Step 5: Commit archived posts

Run:

bash
git add docs/tech/posts docs/tech/archived-posts
git commit -m "chore(blog): archive old tech posts"

Expected: one commit showing three renames from docs/tech/posts/ to docs/tech/archived-posts/.

Task 5: Verify Final Tech Blog Output

Files:

  • Verify generated output under docs/.vitepress/dist/

  • [ ] Step 1: Run final production build

Run:

bash
npm run docs:build

Expected: build succeeds.

  • [ ] Step 2: Confirm Tech route exists

Run:

bash
test -f docs/.vitepress/dist/tech/index.html

Expected: command exits with status 0.

  • [ ] Step 3: Confirm only the two current Tech posts are generated

Run:

bash
find docs/.vitepress/dist/tech/posts -maxdepth 1 -type f -name '*.html' | sort

Expected:

text
docs/.vitepress/dist/tech/posts/2026-05-26-daily-questions-challenge-2026.html
docs/.vitepress/dist/tech/posts/2026-05-26-time-complexity.html
  • [ ] Step 4: Confirm Daily Questions links to Time Complexity

Run:

bash
rg -n "2026-05-26-time-complexity.html|解釋時間複雜度" docs/.vitepress/dist/tech/posts/2026-05-26-daily-questions-challenge-2026.html

Expected: output includes the Time Complexity title or generated HTML link.

  • [ ] Step 5: Confirm old Tech post routes are not generated

Run:

bash
find docs/.vitepress/dist/tech/posts -maxdepth 1 -type f \( -name '2022-*.html' -o -name '*cheerio*.html' -o -name '*vercel*.html' \)

Expected: no output.

  • [ ] Step 6: Inspect git status

Run:

bash
git status --short

Expected: no uncommitted source changes from the implementation tasks. Generated docs/.vitepress/dist/ may appear only if it is tracked or changed by the build in this repository; if it appears, inspect it before deciding whether to include it.

Self-Review Notes

  • Spec coverage: navigation reopening is Task 2; two Markdown imports are Task 3; old article unpublishing is Task 4; pinned sorting is Task 1; final verification is Task 5.
  • The plan intentionally does not add Notion synchronization, a new blog framework, Travel/Running nav changes, visual redesign, or image cleanup.
  • The Daily Questions article uses pinned: true; the loader treats missing or non-boolean pinned values as false.