A 9-Day Full-Stack Rebuild with Codex: From Vue 2 to Nuxt 4 and NestJS
I'm a frontend developer and also did UI work in earlier years. Over the years, because of work and personal projects, I've been continuously involved with backend, databases, and servers. But truly connecting the frontend, API, data migration, and deployment all by myself is still very draining. It's not that I can't do any single part, but that my brain has to constantly switch contexts.
For this rebuild of Hangzhan, I integrated Codex into the entire development process. It didn't just help me complete a few components; it participated in code sorting, feature implementation, data scripts, testing, and deployment documentation.
Looking at the Git records, the first project baseline commit was on August 9th, and the production environment configuration landed on August 17th. The core refactoring took exactly 9 days.
Hangzhan itself has been running for many years. I already had answers in my mind for how the product should work, what the pages should look like, and which data must be preserved. I didn't expect to input a single sentence and wait for a website to be generated. I mainly let Codex take over the tasks that required flipping back and forth through code, modifying files, and running checks.
First, let me talk about what I rebuilt
The old version of Hangzhan was rewritten in 2019, with a tech stack of Vue 2, Webpack 4, ThinkPHP 5, and MySQL.
That codebase accompanied me for over five years. Articles, web navigation, community, comments, favorites, personal center, and changelogs were all added bit by bit later. At the time, for SEO, I also made a rather compromised solution: Vue handled page interactions, ThinkPHP queried the database first, and then injected the title, keywords, and description into the packaged HTML template.
This solution worked at the time, but as time went on, continuing to maintain it became uncomfortable. Vue 2 and old dependencies became harder and harder to upgrade, the frontend and backend types were completely separate, and the SEO logic for public pages was scattered in different places.
The new version was replaced with the following structure:
Frontend: Nuxt 4 + Vue 3 + TypeScript
Backend: NestJS 11 + Fastify 5
Data: Prisma + MySQL 8 + Redis
Engineering: pnpm Workspace + Turborepo
Deployment: Nginx + PM2
Hangzhan is now more than just a blog. Besides reading and publishing articles, it also has tool discovery, community, user profiles, comments and favorites, image uploads, content moderation, reporting, backend management, search, subscriptions, and visit statistics.
While writing this article, I casually counted the repository: 151 main source files, about 36,000 lines; 20 page files on the frontend, 8 Controllers on the backend, 30 data models and 12 migrations in Prisma.
I'm listing these numbers not to show off the amount of code, but just to give a sense of the project's scale: what I was dealing with wasn't a demo in an empty directory, but a website I plan to continue maintaining long-term.
The "Discover" page of the live Hangzhan site, screenshot taken before this article was published.
Why I used Codex for this refactoring
I've used various AI programming tools before. The most common usage was: copy the code over, ask it where the problem is, and copy the reply back.
This is fine for small issues, but it gets very tiring when the project is large. For example, modifying the article publishing flow involves the frontend form, state management, API DTOs, database fields, and backend moderation. If you only paste one Vue file, it's hard for the AI to know what's happening elsewhere.
The most useful aspect of Codex for me is that it can directly read the entire project and can also find related files on its own. When I want to change a feature, I don't need to manually organize a dozen files and feed them to it first. It can follow a page to a composable, then to an API Controller, Service, and Prisma model, and after making changes, continue to run type checks and builds.
So the tasks I gave it were generally not "help me write a piece of code," but "handle this problem within the current project."
But I also wouldn't let it make big changes right from the start. At the beginning of the project, I first let it only read the code, without modifying files, to sort out what pages, interfaces, and data entities the old site had, and then discuss which things should be kept and which could be redesigned during this opportunity.
This step took some time at that point, but it saved a lot of rework later. I didn't dare let it directly start working before it understood the project, otherwise it was very likely that after changing one place, I'd have to patch holes in several other places.
I didn't throw all the requirements in at once
The easiest mistake to make in a refactoring is to first lay out a bunch of pages, then a bunch of interfaces, and finally do the integration. On the surface, progress seems fast, but when you actually open the pages, they're full of mock data.
This time, I basically worked through complete chains one by one.
The first chain I got through was public articles: Prisma reads articles from MySQL, NestJS provides the API, Nuxt requests data on the server side, and then outputs HTML containing the real title and body. Only after the article list, article detail, and 404 pages were all working did I continue with accounts, creation, and media uploads.
The subsequent Git commits also show this order: first establish the project baseline, then connect public articles to the database, then work on accounts and publishing APIs, the creator-side pages, production readiness, and finally fill in content moderation and environment configuration.
I usually describe tasks quite specifically. For example, for the article detail page round, I would write something like:
Refactor the article detail page; public content continues to use SSR.
The body is the main part of the page; author, table of contents, and related recommendations are all auxiliary information.
Do not modify the existing article API; control the body width on desktop, collapse the sidebar on mobile;
Continue using existing design variables.
After completion, check long titles, images, Markdown, and code blocks,
and confirm that the article title can be found in the HTML returned by SSR. Finally, run type checks and build.
This isn't the same as writing a very formal requirements document. I'm just trying to clearly state "what cannot be changed" and "what counts as done."
If I just said "make the article page look more premium," Codex would also write code, but the result would easily turn into a bunch of cards, gradients, and large rounded corners. It might look like it has everything, but it just wouldn't look like my website.
A design draft version of the article detail page. I would first determine the reading width, the hierarchy of the table of contents and author info, and then let Codex implement it in code.
For the frontend part, I still watched very closely
My main job is frontend, and I've done UI before, so it was impossible for a page to be done after just one generation.
This time, the homepage, login, article list, article detail, tool detail, creator dashboard, and personal homepage all went through multiple design versions. The design file names in the project carry v1, v2, v3, and some pages even went up to v7.
The homepage once tried a dark editor's-pick style, and was later further adjusted based on real content and the overall brand.
Codex was very fast at writing layouts, components, and responsive styles, but "can reproduce" doesn't equal "looks comfortable." For this part, I would still look at screenshots myself and tell it where the problems were.
I rarely said "optimize it" anymore, but instead directly pointed out:
- Too much whitespace above the fold, the article title is pushed down;
- The sidebar is visually too heavy, drawing attention away from the body;
- Too many card borders, the whole page looks like an admin backend;
- Buttons are crowded together on mobile, the primary action isn't obvious enough;
- Font sizes are fine, but the hierarchy between the title and excerpt isn't enough.
The more specific the feedback, the more accurate its next round of changes.
By the seventh version of the creator profile page, the information hierarchy and Hangzhan's current visual language slowly started to unify.
However, I also stepped on a very obvious pitfall: after letting Codex make many rounds of CSS changes, local styles easily piled up more and more. Later, after confirming a round of page changes, I would have it go back and check for duplicate rules, invalid selectors, and values that could be consolidated into design variables, rather than just focusing on making the screenshot look right.
For frontend developers, this should be very familiar. When pages iterate quickly, the first thing to lose control is often not the business logic, but those styles that were "just add one line, we'll tidy it up later."
What really saved time was data migration and those engineering scripts
If you only look at the pages, you might think Codex's greatest value is writing components. But what saved me the most time this time was actually processing historical data.
The old site had years of articles, images, tool data, and changelogs. They came from different stages, and the fields and content formats weren't completely consistent. Some bodies were HTML, some image URLs had already been changed, and some publish times, categories, and excerpts needed to be reorganized.
Doing this kind of thing manually is tedious and very easy to miss things.
I had Codex break the migration into several steps: first read and clean, outputting data to be reviewed and a report; after my confirmation, preview the import; finally, actually write to the database and verify the counts before and after import.
There are now quite a few such scripts in the project:
- Organize old articles, output Markdown and image lists;
- Import reviewed articles;
- Migrate historical changelogs;
- Check if tool links are still accessible;
- Review resource descriptions for mixed-in English;
- Clean up expired sessions, verification codes, and access logs.
I've always been quite cautious with data operations. Previews can be run directly, but actual imports, overwrites, or deletions must be confirmed separately. Codex can help me write scripts and can list verification items very completely, but the final step of pressing execute, I won't hand over to a vague command.
A database problem is not on the same level as a page margin mistake.
SSR can't just rely on "looking normal in the browser"
A main reason for switching to Nuxt this time was to make the SSR and SEO for public content cleaner.
On the old site, to let search engines get the title and description, we had to go through a layer between the ThinkPHP controller and the Vue page. On the new site, article, tool, and community details fetch data directly on the Nuxt server side. Page content, Meta, Canonical, Open Graph, and structured data are all generated from the same content.
Pages that don't need to be indexed, like login, personal center, creator dashboard, and backend, are explicitly marked with noindex. Because the login state for the backend is saved on the browser side, SSR was simply turned off to avoid the server outputting protected content that shouldn't appear.
This logic is hard to fully verify just by clicking around in a browser, so I had Codex add SSR smoke tests.
The tests start the production-built Web and API services, read real articles, tools, and community content from the database, and then request the corresponding pages. Besides checking status codes, they also confirm that the returned content is complete HTML and that the detail pages indeed contain the titles from the database.
The project ended up retaining a few fixed check commands:
pnpm typecheck
pnpm build
pnpm smoke:content
pnpm smoke:ssr
Every time Codex finished a stage, I would have it run the corresponding checks. It saying "done" didn't count as done; the commands truly passing and the pages meeting expectations marked the end of that round.
For the launch, I didn't let it freewheel
After the development environment was running, there was still Nginx, PM2, MySQL, Redis, SMTP, upload directories, backups, and HTTPS.
For this part, Codex helped me organize a very long deployment document and also checked the production environment variables and reverse proxy configuration. Which addresses the API, Nuxt SSR, and uploaded files go to, which processes PM2 starts, how to back up before database migrations, and what to do if a build fails were all written into the repository.
One rule among them is very simple: if any step of type checking, building, or migration fails, stop the release and do not continue to reload PM2.
Because the production environment is not a trial-and-error field. If code is broken, it can be rolled back, but if databases and user-uploaded files are handled incorrectly, the recovery cost is much higher.
So I would let Codex help me check, execute clear steps, and organize output, but when it came to database recovery, deleting files, or overwriting live data, the goals and paths had to be confirmed again.
My real feelings about Codex after using it
It really made my development speed much faster as a single person, especially for tasks spanning frontend, backend, and scripts. Before, to change a complete feature, I had to constantly switch between pages, interfaces, and data models; now I can first clearly state the goal, let it track down the related code, and put more of my energy into product judgment and final acceptance.
But it's also not magical to the point where you can ignore it.
If the requirements are stated too broadly, it will change many files at once, making review more troublesome; if design feedback is too abstract, it will apply common visual styles; in places not covered by tests, it can also very confidently believe there are no problems.
The way I'm more used to now is: first let it read the project, then give a task with clear boundaries; after it's done, look at the diff, run checks, look at the page; if there's a problem, describe the actual phenomenon, rather than just adding another "optimize it further."
Another very important point: my frontend experience didn't become less important because of using Codex; it became even more important.
I have to know why SSR is done this way to judge whether its implementation is correct; I have to know how components and styles should be organized to discover that although the page is visually restored, the code has started to become messy; I also have to understand interfaces, databases, and deployment to dare let it continue working.
So towards the end, I became more and more willing to let it do more, but the places requiring my judgment didn't decrease at all.
Final words
Nine days certainly can't produce the content and accumulation a website has built over five years. What Codex helped me complete was moving these already existing things onto a new technical foundation, and filling in the engineering issues I had wanted to tidy up but never had the time for.
The most comfortable part of this refactoring for me is that in the future, when adding a feature, I won't have to first bypass the pile of limitations left from five years ago. As for how much code was generated, that's not so important.
Hangzhan will continue to be improved. I will also continue to treat it as my long-term project, writing articles, collecting useful development tools, and occasionally tinkering with features that might not be useful but I really want to make.
If you are also a frontend developer and happen to have a personal project that's been dragging on for a long time, you can try treating Codex as a partner that can directly work in the repository. First give it a small, complete task, and see if it can get the page, interface, and verification all working together.
Don't start with "finish the entire project in one sentence." That usually looks fast, but in the end, you're the one cleaning up.
Hangzhan: https://www.vipbic.com
Top 1 of 3 from juejin.cn, machine-translated. The original thread is authoritative.
Page buttons jump and shake badly when clicked—unacceptable for a frontend dev.
Ah, I deliberately made that jelly effect [grin]
Too stiff, no transition.