<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Astro on recca0120 技術筆記</title><link>https://recca0120.github.io/tags/astro/</link><description>Recent content in Astro on recca0120 技術筆記</description><generator>Hugo -- gohugo.io</generator><language>zh-hant-tw</language><lastBuildDate>Fri, 03 Apr 2026 02:57:00 +0800</lastBuildDate><atom:link href="https://recca0120.github.io/tags/astro/index.xml" rel="self" type="application/rss+xml"/><item><title>我用 Vibe Coding 做了一個全球開發者排行榜</title><link>https://recca0120.github.io/2026/04/03/codeatlas-vibe-coding-developer-ranking/</link><pubDate>Fri, 03 Apr 2026 02:57:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/03/codeatlas-vibe-coding-developer-ranking/</guid><description>&lt;img src="https://recca0120.github.io/" alt="Featured image of post 我用 Vibe Coding 做了一個全球開發者排行榜" /&gt;&lt;p&gt;GitHub 上有超過一億個開發者帳號，但你有沒有想過——你在自己國家排第幾名？&lt;/p&gt;
&lt;p&gt;這個念頭讓我花了幾天時間，用 vibe coding 的方式做出了 &lt;a class="link" href="https://recca0120.github.io/codeatlas/" target="_blank" rel="noopener"
 &gt;CodeAtlas&lt;/a&gt;，一個涵蓋 130 多個國家的全球開發者排行榜，還附帶一顆可以轉的 3D 地球。&lt;/p&gt;
&lt;h2 id="什麼是-vibe-coding"&gt;&lt;a href="#%e4%bb%80%e9%ba%bc%e6%98%af-vibe-coding" class="header-anchor"&gt;&lt;/a&gt;什麼是 Vibe Coding
&lt;/h2&gt;&lt;p&gt;Vibe coding 是 Andrej Karpathy 在 2025 年初提出的概念：把需求用自然語言丟給 AI，讓 AI 產出程式碼，開發者負責方向和驗收。整個過程更像在「導演」一個專案，而不是逐行寫 code。&lt;/p&gt;
&lt;p&gt;CodeAtlas 就是這樣生出來的。我定義好資料來源、排名邏輯、UI 互動，剩下的交給 AI 去實作。最後出來的技術組合超出我原本預期——Astro 6 搭 Svelte 5 Islands、Three.js 3D 地球、GitHub GraphQL API 自動收集——這些如果要從零手刻，光是學習成本就很可觀。&lt;/p&gt;
&lt;h2 id="技術架構"&gt;&lt;a href="#%e6%8a%80%e8%a1%93%e6%9e%b6%e6%a7%8b" class="header-anchor"&gt;&lt;/a&gt;技術架構
&lt;/h2&gt;&lt;p&gt;整個專案分成兩個部分：資料收集 pipeline 和前端展示。&lt;/p&gt;
&lt;h3 id="資料收集"&gt;&lt;a href="#%e8%b3%87%e6%96%99%e6%94%b6%e9%9b%86" class="header-anchor"&gt;&lt;/a&gt;資料收集
&lt;/h3&gt;&lt;p&gt;資料來源是 GitHub GraphQL API。每個國家在設定檔裡有對應的地點關鍵字，例如 Taiwan 對應 &lt;code&gt;[&amp;quot;Taiwan&amp;quot;, &amp;quot;Taipei&amp;quot;, &amp;quot;Kaohsiung&amp;quot;, &amp;quot;Taichung&amp;quot;]&lt;/code&gt;。收集腳本會用這些關鍵字搜尋 GitHub 使用者，依照 followers 數量由高到低分頁抓取。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 搜尋查詢：用 location 加上 followers 排序
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// query: &amp;#34;location:Taiwan sort:followers-desc&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;octokit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;searchQuery&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`location:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; sort:followers-desc`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;first&lt;/span&gt;: &lt;span class="kt"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;after&lt;/span&gt;: &lt;span class="kt"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;每位開發者會收集以下資料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;公開 / 私人貢獻數&lt;/li&gt;
&lt;li&gt;Follower 數量&lt;/li&gt;
&lt;li&gt;前 5 名使用的程式語言&lt;/li&gt;
&lt;li&gt;前 5 個星星最多的 repo&lt;/li&gt;
&lt;li&gt;個人資訊（公司、bio、Twitter、blog）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;收集過程有幾個眉角。GitHub API 有 rate limit，所以用了 &lt;code&gt;@octokit/plugin-throttling&lt;/code&gt; 控制請求頻率，每頁之間加 500-1000ms 延遲。遇到 secondary rate limit 會自動重試最多 3 次。整個收集跑完 130 多個國家大概要兩小時。&lt;/p&gt;
&lt;h3 id="模糊地點過濾"&gt;&lt;a href="#%e6%a8%a1%e7%b3%8a%e5%9c%b0%e9%bb%9e%e9%81%8e%e6%bf%be" class="header-anchor"&gt;&lt;/a&gt;模糊地點過濾
&lt;/h3&gt;&lt;p&gt;GitHub 使用者的 location 是自由填寫的欄位，這會產生誤判。最經典的例子是 Georgia——它既是美國的州，也是一個國家。&lt;code&gt;location-filter.ts&lt;/code&gt; 用排除規則處理這類情況，避免把美國喬治亞州的開發者算進喬治亞共和國。&lt;/p&gt;
&lt;h3 id="自動化排程"&gt;&lt;a href="#%e8%87%aa%e5%8b%95%e5%8c%96%e6%8e%92%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;自動化排程
&lt;/h3&gt;&lt;p&gt;資料收集透過 GitHub Actions 每天凌晨跑一次。設計了 checkpoint 機制，每次只處理一部分國家，中斷了可以續跑。跑完自動 commit 到 repo，GitHub Pages 就會更新。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# .github/workflows/collect-data.yml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;17 3 * * *&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 每天 UTC 3:17&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 也可以手動觸發&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="排名機制"&gt;&lt;a href="#%e6%8e%92%e5%90%8d%e6%a9%9f%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;排名機制
&lt;/h2&gt;&lt;p&gt;排名邏輯刻意做得簡單。三個維度，各自獨立排序：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;維度&lt;/th&gt;
 &lt;th&gt;說明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Public Contributions&lt;/td&gt;
 &lt;td&gt;只計算公開貢獻&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Total Contributions&lt;/td&gt;
 &lt;td&gt;公開 + 私人貢獻&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Followers&lt;/td&gt;
 &lt;td&gt;GitHub 追蹤者數量&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;rankUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;: &lt;span class="kt"&gt;GitHubUser&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt;: &lt;span class="kt"&gt;RankingDimension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;getRankValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;getRankValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;沒有加權、沒有複合評分。使用者可以在頁面上切換維度，自己判斷哪個指標對他有意義。做過 ranking system 的人都知道，一旦開始加權就會有無止盡的爭議，不如把選擇權交給使用者。&lt;/p&gt;
&lt;h2 id="前端展示"&gt;&lt;a href="#%e5%89%8d%e7%ab%af%e5%b1%95%e7%a4%ba" class="header-anchor"&gt;&lt;/a&gt;前端展示
&lt;/h2&gt;&lt;h3 id="3d-互動地球"&gt;&lt;a href="#3d-%e4%ba%92%e5%8b%95%e5%9c%b0%e7%90%83" class="header-anchor"&gt;&lt;/a&gt;3D 互動地球
&lt;/h3&gt;&lt;p&gt;首頁最搶眼的是一顆可以旋轉、縮放的 3D 地球，用 &lt;a class="link" href="https://globe.gl/" target="_blank" rel="noopener"
 &gt;Globe.gl&lt;/a&gt; 搭配 Three.js 做的。已收錄的國家會在地球上高亮顯示，點擊就能直接跳到該國的排行榜。&lt;/p&gt;
&lt;p&gt;這顆地球在手機上也能跑，不過做了 responsive 處理——小螢幕上會調整 canvas 尺寸和互動行為，避免吃太多效能。&lt;/p&gt;
&lt;h3 id="astro-islands-架構"&gt;&lt;a href="#astro-islands-%e6%9e%b6%e6%a7%8b" class="header-anchor"&gt;&lt;/a&gt;Astro Islands 架構
&lt;/h3&gt;&lt;p&gt;前端框架選了 Astro 6，搭配 Svelte 5 做互動元件。Astro 的 Islands 架構很適合這種「大部分靜態 + 少量互動」的場景。頁面 HTML 是 build time 產好的靜態檔，只有需要互動的元件（地球、篩選器、搜尋框）才會在 client 端 hydrate。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;src/components/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── AppRouter.svelte # Client-side SPA 路由
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── HomePage.svelte # 首頁 + 3D 地球
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── CountryPage.svelte # 國家排行榜
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── ProfilePage.svelte # 個人開發者頁面
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── RankingFilter.svelte # 維度切換 + 搜尋 + 語言篩選
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── CountrySearch.svelte # 國家搜尋
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="篩選與搜尋"&gt;&lt;a href="#%e7%af%a9%e9%81%b8%e8%88%87%e6%90%9c%e5%b0%8b" class="header-anchor"&gt;&lt;/a&gt;篩選與搜尋
&lt;/h3&gt;&lt;p&gt;國家排行榜頁面可以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用名字搜尋開發者&lt;/li&gt;
&lt;li&gt;按程式語言篩選（列出最常見的 12 種）&lt;/li&gt;
&lt;li&gt;按城市 / 地區篩選&lt;/li&gt;
&lt;li&gt;切換三種排名維度&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所有篩選條件會同步到 URL query string，分享連結時對方看到的是一樣的篩選結果。&lt;/p&gt;
&lt;h3 id="個人-profile-頁"&gt;&lt;a href="#%e5%80%8b%e4%ba%ba-profile-%e9%a0%81" class="header-anchor"&gt;&lt;/a&gt;個人 Profile 頁
&lt;/h3&gt;&lt;p&gt;點擊任何開發者可以進入 profile 頁面，顯示排名、貢獻統計、使用的程式語言（帶顏色標籤）、星星最多的 repo，還有 GitHub / Twitter / 個人網站的連結。&lt;/p&gt;
&lt;h2 id="多語系與暗色模式"&gt;&lt;a href="#%e5%a4%9a%e8%aa%9e%e7%b3%bb%e8%88%87%e6%9a%97%e8%89%b2%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;多語系與暗色模式
&lt;/h2&gt;&lt;p&gt;網站支援英文和繁體中文兩種語言，翻譯檔用 TypeScript 管理，有型別檢查。語言偏好存在 localStorage，下次進來會自動跳轉。&lt;/p&gt;
&lt;p&gt;暗色模式跟著系統偏好走，也可以手動切換。狀態同樣存在 localStorage。&lt;/p&gt;
&lt;h2 id="og-圖片自動產生"&gt;&lt;a href="#og-%e5%9c%96%e7%89%87%e8%87%aa%e5%8b%95%e7%94%a2%e7%94%9f" class="header-anchor"&gt;&lt;/a&gt;OG 圖片自動產生
&lt;/h2&gt;&lt;p&gt;分享到社群時需要 Open Graph 圖片。用 &lt;a class="link" href="https://github.com/vercel/satori" target="_blank" rel="noopener"
 &gt;Satori&lt;/a&gt; 在 build time 把 HTML 模板轉成 SVG 再轉 PNG，每個國家頁面和個人 profile 都會自動產生對應的 OG 圖。&lt;/p&gt;
&lt;h2 id="vibe-coding-的體驗"&gt;&lt;a href="#vibe-coding-%e7%9a%84%e9%ab%94%e9%a9%97" class="header-anchor"&gt;&lt;/a&gt;Vibe Coding 的體驗
&lt;/h2&gt;&lt;p&gt;回頭看這個專案，vibe coding 最大的好處是降低「嘗試的成本」。3D 地球聽起來很酷但我對 Three.js 不熟——沒關係，先讓 AI 生一版，跑起來再調。GraphQL 查詢要處理 cursor pagination 和 rate limit——讓 AI 寫初版，我來 review 和補邊界條件。&lt;/p&gt;
&lt;p&gt;實際感受到的幾個特點：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;速度確實快。&lt;/strong&gt; 從有想法到第一版能跑，大概兩天。如果要手寫 Three.js 地球加上 GitHub API 整合，光研究文件就不止這個時間。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;品質需要自己把關。&lt;/strong&gt; AI 產出的 code 跑得動不代表寫得好。code review 還是得自己做，尤其是 error handling 和 edge case。GitHub API 的 secondary rate limit、模糊地點過濾這些，都是 review 時補上的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;架構決策要自己做。&lt;/strong&gt; AI 不會主動幫你想「這個功能要不要做成 static、要不要用 Islands 架構」。技術選型和架構決策還是開發者的工作。&lt;/p&gt;
&lt;h2 id="技術棧總覽"&gt;&lt;a href="#%e6%8a%80%e8%a1%93%e6%a3%a7%e7%b8%bd%e8%a6%bd" class="header-anchor"&gt;&lt;/a&gt;技術棧總覽
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;類別&lt;/th&gt;
 &lt;th&gt;技術&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;框架&lt;/td&gt;
 &lt;td&gt;Astro 6 (SSG) + Svelte 5&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3D&lt;/td&gt;
 &lt;td&gt;Globe.gl + Three.js&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;樣式&lt;/td&gt;
 &lt;td&gt;Tailwind CSS v4&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;資料&lt;/td&gt;
 &lt;td&gt;GitHub GraphQL API + Octokit&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;驗證&lt;/td&gt;
 &lt;td&gt;Zod v4&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;測試&lt;/td&gt;
 &lt;td&gt;Vitest + Testing Library + Playwright&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;圖片&lt;/td&gt;
 &lt;td&gt;Satori + Resvg-js&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CI/CD&lt;/td&gt;
 &lt;td&gt;GitHub Actions + GitHub Pages&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;語言&lt;/td&gt;
 &lt;td&gt;TypeScript 5.9, Node.js 22+&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="參考資源"&gt;&lt;a href="#%e5%8f%83%e8%80%83%e8%b3%87%e6%ba%90" class="header-anchor"&gt;&lt;/a&gt;參考資源
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://recca0120.github.io/codeatlas/" target="_blank" rel="noopener"
 &gt;CodeAtlas 線上版&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.astro.build/" target="_blank" rel="noopener"
 &gt;Astro 官方文件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://globe.gl/" target="_blank" rel="noopener"
 &gt;Globe.gl — WebGL Globe Data Visualization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/vercel/satori" target="_blank" rel="noopener"
 &gt;Satori — Enlightened library to convert HTML and CSS to SVG&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.github.com/en/graphql" target="_blank" rel="noopener"
 &gt;GitHub GraphQL API 文件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://svelte.dev/docs" target="_blank" rel="noopener"
 &gt;Svelte 5 官方文件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>