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.
- Responsibility: read Tech post frontmatter, expose
- 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.mdtodocs/tech/archived-posts/2022-07-05-heroku.mddocs/tech/posts/2022-cheerio.mdtodocs/tech/archived-posts/2022-cheerio.mddocs/tech/posts/2022-vercel.mdtodocs/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
pinnedand 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:buildExpected: 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:buildExpected: 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.mdCreate:
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 -13. 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 False6. 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!)
在後端開發的日常中,我們追求的目標通常是:
- 盡量利用 Hash Table (Python 的 Dictionary / Set) 把 O(n^2) 的比對降級為 O(n)。
- 對於需要頻繁搜尋的資料,確保資料庫有建索引 (Index),讓查詢維持在 O(log n)。
- 看到巢狀的
for迴圈要有警覺心;看到遞迴函數則要思考有沒有可能引發 O(2^n) 的運算災難。
參考:
- [ ] **Step 3: Build after importing articles**
Run:
```bash
npm run docs:buildExpected: 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.mdMove:
docs/tech/posts/2022-cheerio.mdMove:
docs/tech/posts/2022-vercel.mdCreate directory:
docs/tech/archived-posts/[ ] Step 1: Create archive directory
Run:
bash
mkdir -p docs/tech/archived-postsExpected: 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.mdExpected: 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' | sortExpected:
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:buildExpected: 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:buildExpected: build succeeds.
- [ ] Step 2: Confirm Tech route exists
Run:
bash
test -f docs/.vitepress/dist/tech/index.htmlExpected: 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' | sortExpected:
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.htmlExpected: 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 --shortExpected: 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 asfalse.