How Codex Helped Me Migrate My Site from Hexo to Astro | Codex 稳稳接住了我,吗?


Note: The dialogues in the dialog boxes are not AI-generated.


注:对话框中内容为非AI生成内容。

This paragraph describes how I used Codex to migrate my blog from Hexo to Astro. I’ve been using Hexo to power this static blog for many years, and on a whim, after multiple conversations with AI, I decided to migrate to Astro. During this process, I spent about three nights completing the migration from 0 to 1, maximizing the vibe coding approach. This article summarizes the migration experience based on our collaboration over the past few days as an AI, along with some personal feelings as a non-AI.


本文讲述了我如何使用 Codex 将博客从 Hexo 迁移到 Astro 的过程。我使用 Hexo 来搭建这个静态博客已有多年,一时兴起,在与AI多轮对话后,我决定向 Astro 迁移。在此期间,我花了大约三晚的时间,从0到1,最大限度以 vibe coding 的形式,完成了迁移工作。本文是我让 Codex 根据过去我们这几天的合作,AI总结的迁移经验,以及我作为非AI的一点个人感受。

I’ve been wanting to move my blog from Hexo to Astro for a long time. Hexo served me well, but the longer the site lived, the more I felt the friction: plugin glue, brittle templates, and a build process that was harder to reason about as the content library grew. Astro promised a cleaner mental model and a simpler future. I finally made the jump, and I did it with the help of Codex.

我一直想把博客从 Hexo 迁移到 Astro。Hexo 曾经很好用,但网站越久越让我感觉摩擦变多:插件粘合、模板脆弱、内容库变大后构建过程难以理解。Astro 承诺更清晰的心智模型和更简单的未来。我终于迈出这一步,而且是在 Codex 的帮助下完成的。

a cleaner mental model and a simpler future - LOL.


更清晰的心智模型和更简单的未来——笑。

This post is a quick walkthrough of how the migration actually went—what Codex did well, what I had to watch out for, and a few concrete details that made the process feel less like a risky rewrite and more like a steady refactor.

这篇文章简要记录迁移的真实过程——Codex 做得好的地方、我需要警惕的坑,以及一些具体细节,让整个过程更像稳定的重构,而不是冒险式的重写。

Step 1: Start with a plan (and keep it real)

The first thing I did was write a migration plan. Not a corporate one—just a doc that listed what the Hexo site actually had: routes, templates, pagination, tags, categories, archives, destination pages, and all the small behaviors you only notice after years of usage. Codex helped turn that into a structured checklist so we didn’t lose track of what “done” really means.

第一步是写迁移计划。不是那种公司式文档,而是一份真实清单,列出 Hexo 站点真正拥有的东西:路由、模板、分页、标签、分类、归档、目的地页,以及那些用了多年才意识到的小行为。Codex 帮我把它整理成结构化清单,避免在过程中忘记“完成”的真正含义。

“All the small behaviors you only notice after years of usage” summarized by AI are quite funny, listing many of my nitpicks—folder structure, CSS styles, etc.


AI总结的“那些用了多年才意识到的小行为”还是很有意思的,例举了我很多的吹毛求疵——文件夹结构,CSS样式,等等。

The plan kept us honest. It also revealed a few non‑obvious dependencies, like how archives were organized by year and month in Hexo, or how category labels were bilingual. That kept the new site from drifting away from the old one while we rebuilt the core routes.

计划让我们保持诚实,也暴露了一些不显眼的依赖,比如 Hexo 归档按年/月组织,分类标签是双语的。正是这些细节,让新站在重建核心路由时不至于偏离旧站体验。

Step 2: Port features, not just pages

The migration wasn’t just about copying content. It was about carrying over the behaviors:

迁移不只是搬运内容,而是迁移行为本身:

  • A post layout that matched the original theme structure
  • 与原主题结构一致的文章布局
  • A sidebar with subscriptions, tags, and destinations
  • 带订阅、标签和目的地的侧栏
  • Lightbox handling for gallery posts
  • 画廊文章的灯箱浏览
  • Disqus comments on post pages
  • 文章页的 Disqus 评论
  • RSS/Atom feeds that still worked across readers
  • 能被各类阅读器识别的 RSS/Atom

Codex made it easier to translate those across frameworks, especially when the files weren’t straightforward copies. For example, Astro components replaced EJS partials, and a single layout file replaced the old template layering. We kept the look and feel, but simplified the system underneath.

Codex 让跨框架迁移更顺畅,尤其是在文件不是简单拷贝时。比如,用 Astro 组件替代 EJS partial,用一个布局文件替代旧的模板层级。外观与体验保持一致,但底层系统更简洁。

Step 3: The slug surprise

One of the first real “gotchas” was slug normalization. I changed the slug rules to clean up punctuation in post titles. That seemed harmless—until images started breaking. In the old structure, assets lived under folders named after the old slug. In the new one, the slug changed, so the asset path changed too. Hello, broken images.

第一个真正的“坑”是 slug 规范化。我改了 slug 规则来清理标题里的标点,听起来很安全——直到图片开始坏掉。旧结构里,资源目录按旧 slug 命名;新结构里 slug 变了,资源路径也跟着变了,于是图片 404。

The fix wasn’t glamorous but it was solid: I made the asset folders match the new slug rules. That meant a one‑time rename sweep under public/assets and then letting the Figure component use the same slug logic as the routes. The result: cleaner URLs and working images, without a permanent layer of per‑post overrides.

解决方案不花哨但靠谱:让资源目录和新的 slug 规则保持一致。我们做了一次性重命名,把 public/assets 下的旧目录统一成新规则,然后让 Figure 组件和路由用同一套 slug 逻辑。结果是 URL 更干净,图片也正常显示,而且不需要长期维护每篇文章的覆盖字段。

The debugging playbook here was unexpectedly valuable: reproduce on the homepage (where Astro.params is empty), log the computed base path in the Figure component, and compare it with the post page (where Astro.params is present). That made it obvious that the index renderer needed a different base‑path source, and it kept me from chasing the wrong codepath.

这段调试流程意外地有用:先在首页复现问题(Astro.params 为空),在 Figure 组件里输出计算出来的 base path,再和文章页对比(Astro.params 有值)。这样一眼就能看出首页需要不同的 base‑path 来源,也避免我盲目追错代码路径。

This was the worst part of the whole experience. The AI had no foresight about resource path issues caused by slug changes. During the troubleshooting, it didn’t realize that the actual asset paths were tied to the slugs. While trying to fix things, AI kept patching in the same place, with adding unnecessary variables and functions, leading to increasing code complexity until this problem became completely unfixable by AI.


这是整个流程体验下来最差的一部分,AI对于slug变化引起的资源路径问题完全没有预见,在修改过程中也没有意识到真正的asset路径是和slug绑定的,很多时候在挑事的过程中不断的在原地打补丁,导致代码的复杂性越来越高,直到最后这个问题已经完全无法被AI修复。

Step 4: Restore the small affordances

I was surprised how much I missed the little things. The “Newer / Older” links at the bottom of posts. The subtle comment link in the footer. The exact spacing on the article header. Migration isn’t just about big features—it’s about muscle memory.

我惊讶于自己多么在意这些小细节:文章底部的“Newer / Older”链接,页脚里低调的评论入口,标题区域的细微间距。迁移不只是大功能,更是肌肉记忆。

We added the post nav back in, ported the CSS from the Hexo theme, and even adjusted the Disqus section background to match the original layout. These are small touches, but they’re the difference between “a new site” and “my site.”

我们把文章导航加回去,移植了 Hexo 主题的 CSS,甚至把 Disqus 区域的背景调整成原来的样子。这些小改动让它从“新站”变成“我的站”。

I wasn’t that “surprised” by myself. What surprised me more was that AI didn’t record these “little things” from the beginning. During the entire parity handling process, AI hardly played a role in discovering parity. Of course, one reason is that I didn’t provide the generated webpage source code back to AI, but since AI could access the entire project source code, I wasn’t expecting No assistance from AI on these detailed tasks.


我到没有多么的“惊讶”我自己。我更多的惊讶于AI完全没有在一开始就记录下这些”小细节“。在整个处理parity的过程中,AI几乎没有起到发现parity的作用。当然这其中的原因更多的是因为我并没有把生成的网页的源代码重新反馈给AI,但是在AI可以access整个项目的源代码的情况下,这些细节工作并没有得到AI的辅助。

Step 5: Get the feed right

Feeds are deceptively tricky. I wanted an Atom feed that used full HTML content, not just summaries, and limited itself to the most recent posts. Codex helped me update the feed generator so it renders full HTML, uses a 20‑post limit, and exposes a clean self‑link so feed readers don’t complain.

Feed 看似简单却暗含细节。我想要的是:Atom feed 使用完整 HTML 内容,而不是摘要,并限制为最近的文章。Codex 帮我更新了生成逻辑,输出完整 HTML、限定 20 篇、并设置正确的 self link,让阅读器不再报错。

This was the kind of detail that’s easy to skip—but once the site is migrated, it’s painful to come back and fix later. Doing it right now saved time and avoided subtle breakage for long‑time subscribers.

这是那种很容易跳过的细节,但一旦迁移完成再回头修就会很痛。现在一次做好,既省时间,也避免老订阅读者被小问题折磨。

Step 6: Ship with guardrails

Finally, I wanted a deployment path that felt safe. The old Hexo deploy script assumed a role and synced public/ to S3. We copied the same approach for Astro and added a dry‑run mode that prints every file that would be uploaded. That gave me confidence to run the real deploy without holding my breath.

最后我需要一个安全的部署路径。旧的 Hexo 脚本是 assume role 后把 public/ 同步到 S3。我们沿用这个思路,给 Astro 加了 dry‑run 模式,逐行打印将要上传的文件,让我在真正部署前心里有底。

At the end of this, Astro is the primary build. Hexo remains in the repo as a backup, but the daily workflow is now Astro‑first.

迁移完成后,Astro 成为主构建。Hexo 仍保留在仓库里当备份,但日常流程已经彻底转到 Astro。

What Codex was best at

  • Large-scale edits: moving logic across multiple files without missing a dependency
  • 大规模改动:跨多个文件迁移逻辑而不遗漏依赖
  • Consistency: maintaining naming conventions and style across the whole codebase
  • 一致性:全仓范围保持命名和风格一致
  • Fast iteration: I could ask for a change, see it, test it, and refine quickly
  • 快速迭代:提出修改、立即看到、快速测试并持续调整

In the entire process, I was most satisfied with:

  • AI quickly setting up the framework, which is of course the most basic task for AI at present.
  • AI automatically generating auxiliary scripts, and using these scripts to batch process some repetitive tasks.
  • During the debugging process, AI quickly generated some temporary Python scripts to perform relatively more complex debugging tasks. There was obviously human intervention behind the AI implementation, which saved both Tokens and Compute, and also solved the problem faster.

我在整个流程中,最满意的有以下这些点:

  • AI快速搭建框架,当然目前来说这对于AI是最基本也是最擅长的事情了。
  • AI自动生成辅助性脚本,并且用这个脚本文件来批量处理一些重复性的工作。
  • AI在debug的过程中,能够快速生成一些临时的Python脚本,去做相对更复杂的debug的工作。这点明显是背后人工对于AI流程的主动干预,以此节省Token和Compute,也能够更快解决问题。

What still needed human judgment

  • Which behaviors were worth preserving vs. simplifying
  • 哪些行为值得保留,哪些可以简化
  • How far to push slug normalization without breaking legacy assets
  • slug 规范化要推进到什么程度才不会破坏旧资源
  • Where to rely on route params vs. render-time context (index pages don’t behave like post routes)
  • 在哪里依赖路由参数,哪里依赖渲染上下文(首页并不像文章路由)
  • When to stop chasing perfect parity and just ship
  • 什么时候停止追求完美对齐,直接上线

I think, it needs more than human judgement. Our goals were very clear. But many times the project still couldn’t move forward.

  • Throughout the entire debugging process, most of the time the quality of the generated code was not high. Every time a problem occurred, AI was more likely to add rather than subtract. The result was that unnecessary code kept accumulating. It was hard for AI to determine which code was redundant and which was necessary, eventually leading to decreasing debugging efficiency with iterations after iterations.
  • AI made fewer proactive judgments or decisions during the process, more like a “poke and prod” attitude, lacking initiative.
  • AI often produced sloppy and perfunctory results when dealing with detailed issues, forcing me to add more iterations to achieve the desired effect.

我认为远不只是human judgement。很多时候项目无法继续推进下去,即使目标十分清楚。比如:

  • 整个debug的流程,大部分的情况下AI生成的代码的质量不高,这里我不满意的是每次出现问题,AI更擅长于“做加法”而非“做减法”,这样的结果是导致不必要的代码越来越多,在删除和清理的过程中,又无法确定哪些代码是冗余的,哪些是必要的,进而导致debug的效率随着迭代越来越低。
  • AI相对更少的在过程中做出主动的判断和决策,更像是“戳一戳,动一动”的态度,缺乏主动性。
  • AI在处理细节问题上,很容易出现糊弄和敷衍的结果,不得不迫使我增加更多的迭代才能达到预期的效果。

This migration took real time, but it didn’t feel like a rewrite. It felt like gradually untying a knot—one decision, one file, one subtle bug at a time. Codex didn’t do the thinking for me, but it made the execution smoother and more transparent.

这次迁移确实花了时间,但并不感觉像重写,更像慢慢解开一个结:一个决定、一处文件、一个细小 bug。Codex 没有替我做决策,但它让执行过程更顺滑、更透明。

If you’re thinking about a similar move, my advice is simple: write the plan, stick to it, and keep the small details that make the site feel like home.

如果你也在考虑类似迁移,我的建议很简单:写计划、坚持执行,并保留那些让网站有“归属感”的小细节。

Anyway, after saying the good and bad things, I need to admit: Codex ‘get’ me. It’s not me writing code, it’s me writing code with AI. In the past year, there have been more and more “AI shocks” in life and work - only gratitude 🙇. I hope that when I grow older and accidentally fall, AI can still ‘get’ me. 🙏


说了这么多好的坏的,但无论如何还是需要承认:Codex稳稳地接住了我。不是我在写代码,而是我在和AI一起写代码。过去的一年间,生活工作中出现了越来越多的“AI震撼”——唯有感恩 🙇。希望未来在我生理年纪变大,不慎跌倒的时候,AI还能稳稳地接住我。🙏

West Lake Standard Endurance Trail | 西湖标毅线 故乡似他乡


2025, my third zodiac cycle year, has been a tough one. Even though New Year’s Day is behind, the Year of the Snake hasn’t ended yet on the Chinese calendar. I have to keep going.

圣诞节前回到杭州,专程挤入了个跑山(trail running)活动,打卡久闻大名的西湖跑山线路——标毅线。因为近两年开始跑山,偶然间惊喜的发现原来家乡杭州还有这样的线路,甚至吸引外地朋友专程前来打卡。虽然从小在杭州长大,这条线上好多地方却还从未听说,总觉愧疚。于是做了些功课,这条线全长大概24公里,累计爬升1500米,网上有各种完线、破五、破四、甚至破三的帖子晒出来。我回去翻了翻最近几次西雅图本地的西虎三(West Tiger)数据:12.7迈,4600ft爬升,三小时内搞定。我拍拍自己的腿:破四有戏。

Route Sketch (unknown source)

这条线网上夸赞甚多,说是已有20余年历史,是当初浙大毅行的人在西湖边的群山间兜兜转转,最终跑出来的精华中的精华的一条线,连接了西湖边最著名的一串山峰,北起古荡老和山,途径灵峰山、北高峰、美人峰、龙门山、天竺山、五云山、林海亭、贵人阁、玉皇山,直至西湖东边的吴山。我仔细端详这条路线,感觉中间些许段落应该也曾走过。不过小时候爬山,和爸妈或是同学一起,都是背上好吃好喝的,一路谈天说地的,还真没体验过西湖边群山里跑步的感觉。在西雅图打包好跑步装备,更是激动了好几晚。

意外发现小红书上很适合“找搭子”,输入日期和地点,随机的问了几个可能当天有同样计划的人后,便误打误撞加入了一个当地“如乐”跑团组织的活动。随着时间步步逼近,最后竟集结了近30余人。跑团很是专业,分了好几个组别,定了不同的配速,一看便知不是打卡散步的休闲局。一大票的跑友都把目标设在了四小时以内,网上原本看帖子以为标毅破四就是值得发帖炫耀之事,没想到这里厉害的人还真不少。我又掂量掂量了自己,也厚着脸皮加入四小时组别。

老和云起
吴山大观

第二天一早到达起始点“老和云起”的时候,已经好多人等着了。待快出发,竟真乌泱泱又不知从哪变出了更多的人。跑团也是真的厉害,还有专门的合影横幅和赞助环节。

恰逢冬至,虽然昨天异常干热,还有层雾霾笼罩,好在当晚气候骤变——降温,下雨。我开始还有些许忧虑,直到开跑了才发觉:这才是最完美的标毅线打开方式——起跑没多久雨便停了,全程的温度也处于停下来觉得冻,跑起来有风吹的凉爽的感觉。最重要的是,这清晨淅淅沥沥的雨吓退了大部分游客。听说这条线热闹的时候可是会堵的走都走不动的。今天倒好,全程完全没有遇到排队或者堵塞的地方,人少的时候,甚至可以连续跑十多二十分钟不见人,空气还被洗涤一新,简直是完美。

因为了解了这条线的名气,想着又是在国内“主场”,我完全懈怠了查找详细线路图存在手机上这回事。看网上说法,这条线热闹得一路上总有人能和你走或者跑在一起,就连路边的大爷都能给你指路。可我真正跑了起来才傻眼了,没有个导航,我这“国际友人”就是两眼一抹黑,要是不跟着别人一起跑,指定迷路(虽然也是真的迷路了)。

刚开始的时候还有大部队,路线也基本踩着石阶直截了当往上爬,爬了一会儿我便隐约觉察到了一丝竞争感,无论男女,大家都一股脑儿向上冲,速度远比我想象的快。看了眼手表,心率早就起来了。跑得快的已经超到前面不见踪迹,跑得慢的也因为我奋力往前追赶而被甩在身后。最终,周围就只剩下一俩人还在差不多速度往前跑。很快的,路上就出现了一个接一个的分叉,我没法辨别方向,只得硬着头皮跟着别人后面跑,勉强跟上的时候就偶尔插句话。随口聊了几句的时候猛然被问到:你是外地的吧。我一下语塞,不知如何作答,只得借着喘气声嗯嗯啊啊回应了。对方又重新戴上耳机,继续闷头往前跑。我也就收了声,专心做一好一个外地人。突然想起前些时候和朋友聊天,说回老家以后,发觉反而是和当地的小时候的玩伴有着一种莫名的隔阂,倒是和别处长大来这里打拼的外地人更聊得来,大概是因为嗅到了本地人身上没有的那种类似的漂泊味儿吧。

背影
背影
Me (Credit to Ruler Running Club)

说回跑步,这里的vibe还真挺不一样,大家都可认真,全然不是我想象中的那种:上坡边走边唠,平陆下坡有力气才跑上一段的节奏。一路上我跟了一个小哥一段时间,刚翻过山顶,眼见前面要开始个大下坡,小哥说需要缓缓,有些抽筋,让我自己先下,不过一路全是下坡台阶,小心点径直走就好。我便超过去继续往前。此时雨已停,但因已在群山深处,鲜有游客,一路上只我一人往下踩楼梯。果然不好走!虽说是台阶,每个阶梯都不到一脚掌宽,台阶又深,宽窄高低不一,却又连绵不绝,往下方远处望,只觉一阵眩晕,不得不全神贯注,侧着身子往下蹦,嘴里默念着节奏,免得乱了步子。

继续往前的分叉,就遇到了一位真正的“扫地僧”。当看到站在岔路中间迷茫四顾的我,一旁拎着扫帚的大爷轻巧的指点起了方向:沿着路到第几个雨水桶以后往哪儿转。一看就知道对这条线路了如指掌。之后一路上又遇到了几个小哥,一同跑了一段,穿出大山,跑入茶园,那段路的景色绝佳。之后游人逐渐多了些,我一路就在石板路,土路之间来回穿梭。这还是第一次在国内跑上没有“铺装”的“野路”,其中几段让我恍惚间以为自己就在西虎山上。

该来的果然还是来了,似乎错过了一个转弯,在山里走错了路。我不得不拿出PNW的拿手好戏,在杭州的山林里bushwhack——手里拿着strava,在湿软的山坡上向着可能正确的方向硬闯过去。最终成功跑入一个茶园,沿着茶园边上的小径重新回到了标毅线主路上。给我的留念当然是小腿和袜子上扎得满满的苍耳。除此之外,路上还经过了一两个专门为这条线跑步的人设的饮用水补给点,无人看管,就留着一箱水,一个收款二维码,一个指示牌。杭州果然遥遥领先。

回到了主路的我学乖了——这路果然不好认,还是需要“当地人”带路。好在我这一折腾,本来我已经超过去的一开始一起跑的小哥,已经又跑到了我的前面,这次又重新被我追上了,顺道一起还碰到了早上出发的另一个女生。我默默决定,从现在开始就好好跟着。这很明智,接下来的岔路越来越多,下山后我们竟然径直跑入了杭州儿童公园——跑过广场,跑过喷泉,跑过花园,跑过雕塑……然后竟然径直从儿童乐园的大门的闸机跑了出来。自小时候春秋游来过这以后,万万没想到二三十年后的我的再访竟然是从后门跑进来。

紧接着的路线更是特别,马路边跑一段,过几个红绿灯,又开始上山,路边竟是别人家的土坟。因为冬至,一个接一个的坟头边还站了不少人在烧纸钱。怪不得一同跑的女生说打死她也不来这里跑夜路。再往后的路线终于看到了终点的曙光,一大段的青石板的公路,再上上下下一些山坡,经过杭州博物馆,再跑过革命烈士纪念碑(这也是小时候我春秋游的地方),终于在一个下坡转弯结束看到了“吴山大观”四个大字。看看手表,四小时过十二分。

这时间对于我这一路迷路找路问路的“外地人”来说,倒也是基本满意了。虽然没破四,但是想想如果能努力再来一次,破四应该问题不大。不过跑完也是基本体力耗尽,一路都没来得及在几处制高点喘口气,观个景。中午下山,我厚着脸皮加入了“如乐”跑团的那些破四甚至破三的一众人吃了个新疆菜,顺便给我接下来的新疆之行做个简单铺垫。近二十个人挤在一个包厢里吃饭的感觉,上一次的话可能要十多年前了。

跑完回头看“两步路官方小助手”的线路图和标注,一路上密密麻麻的标记点,果然不做准备就想来跑还是天真了,笑。顺道贴两份别人的帖子,分享一下更多的视角,[1][2]

Hong Kong Photo Drop | 香港掠影——点心与滑蛋饭


第一次踏上香港,却也浮光掠影。当天匆匆而来,次日匆匆而去。随手翻过的一日游攻略,皆是大同小异,索性不再细看。借口未来还有机会,这次不妨走马观花,权当是假期最后一天画上的句点,逛吃逛吃为搬砖重新做好心理建设。

说到香港,童年记忆里并没有太多鲜明的印象,那些港片港剧港曲港星,在我的成长轨迹里似乎从未占据过主角。1997年香港回归,也只停留在电视机里一遍一遍循环播放中:降旗升旗交接仪式,查尔斯王子和长者的握手,另外便是“东方之珠,我的爱人”这首父母辈歌厅听得到的旋律了。但是,儿时的我却也深深得益于香港,以及整个繁体中文文化圈的,是来自文化的转运。我依稀记得小时候那台“足以改变命运”的PSP,大体是叔叔从香港带回来的。当时候,无论是电影,电视,动画,还是游戏,都得借由繁体中文翻译的跳板,为我打开了面向世界的窗口。我也自然而然地养成了阅读无碍的习惯,繁体中文对于我而言像是通往世界的桥梁。想必当时候的香港亦如是。

Hong Kong Photo Drop | 香港掠影——点心与滑蛋饭
Read More ...

Havasupai: 3 Days 40 Miles - Part II


Day 2: The Confluence Out And Back

Our second day began early. Though not quite an alpine start. The sun had already risen when we departed. We left most of our gear behind at the campsite and lightened our load for this 16-mile round trip to the Confluence, where Havasu Creek meets the Colorado River. With some online pictures spoiling ahead, I was expecting to see the green belt merging into the muddy yellow Colorado River.

Havasupai: 3 Days 40 Miles - Part II
Read More ...