我们把 Railway 的前端从 Next.js 迁移掉了。构建时间从 10 分钟以上缩短到不到两分钟。
We moved Railway's frontend off Next.js. Builds went from 10+ mins to under two

原始链接: https://blog.railway.com/p/moving-railways-frontend-off-nextjs

## 铁路前端迁移至 Vite + TanStack 铁路最近将其整个生产前端——仪表盘、画布和网站——从 Next.js 迁移至 Vite + TanStack Router,仅通过两个拉取请求,零停机时间完成了迁移。虽然 Next.js 最初帮助铁路扩展至数百万用户,但日益缓慢的构建时间(超过 10 分钟,其中大量时间用于优化页面)已成为其快速开发周期的重大瓶颈。 他们的应用程序主要在客户端运行,他们发现 Next.js 的服务器优先方法是不必要的,需要针对布局和路由进行变通。TanStack Start 提供了一种以客户端为先、明确且更快的开发体验,具有类型安全的路由、一流的布局和即时 HMR。 迁移过程包括在一个 PR 中删除 Next.js 依赖项,然后在另一个 PR 中替换框架,同时使用 Nitro 处理服务器函数并整合配置。权衡包括用更简单的替代方案替换内置功能,例如图像优化和 SEO 工具。结果是构建速度显著加快(不足 2 分钟),开发者体验得到改善,并通过 Fastly 优化资产交付,从而加快迭代速度并提高前端工作流程效率。

黑客新闻 新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 我们已将 Railway 的前端从 Next.js 迁移。构建时间从 10 多分钟缩短到不到两分钟 (railway.com) 16 分,bundie 发表于 2 小时前 | 隐藏 | 过去 | 收藏 | 讨论 帮助 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系方式 搜索:
相关文章

原文

Railway's entire production frontend no longer runs on Next.js. The dashboard, the canvas, railway.com, all of it now runs on Vite + TanStack Router, and we shipped the migration in two PRs with zero downtime.

Next.js got railway.com from zero to a production app serving millions of users monthly. It's an excellent framework, but it stopped being the right one for our product.

Frontend builds had crept past 10 minutes. Six of those minutes were Next.js alone, half of it stuck on "finalizing page optimization." For a team that ships multiple times a day, that kind of build time isn't a minor annoyance. It's like a very expensive tax on every single iteration.

Railway’s app is overwhelmingly client-side. The dashboard is a rich, stateful interface. The canvas is real-time. Websockets are everywhere. The server-first primitives in Next.js weren't something we used, and we'd ended up building our own abstractions on top of the Pages Router just to support layouts and routing concerns that the framework didn't handle the way we needed.

We were still on the Pages Router, which made shared layouts hacky. Every layout pattern was a bolted-on workaround rather than a first-class framework primitive. The App Router would have solved some of these problems, but it leans heavily into server-first patterns, and our product is intentionally client-driven. Adopting it would have meant rebuilding around a paradigm we don't need.

We wanted a stack that matches how we actually build: explicit, client-first, and fast to iterate on. It also helps that we genuinely enjoy working with it.

For the Product team, we wanted a few niceties that help us avoid thinking about how we needed to implement our front-end and found the following to really convince us.

  • Type-safe routing out of the box. Route params and search params are inferred, autocomplete works across the entire route tree, and the routes themselves are generated from the file system.
  • First-class layouts. Pathless layout routes replaced all of our previous hacks with something composable and predictable.
  • A dev loop fast enough that you stop thinking about it. Instant HMR, near-zero startup time. The feedback cycle between changing code and seeing the result effectively disappears.
  • SSR where it actually matters. Marketing pages, the changelog, careers. Pure client-side everywhere else, because we're not going to force server rendering on screens that don't benefit from it. (Ed: This Blog, ironically isn’t on Tanstack yet.)
  • An explicit model. Meaning, we found TanStack to lean less on framework magic, more control over how things actually work under the hood.

Several of us tried TanStack Start over the holidays and the reaction was unanimous. We like building with it, and for a product like Railway's dashboard, that matters as much as any benchmark.

Once we made the choice, I got to work. Pre-squash before merge, I must have made 100s of commits.
Migrating a production frontend that serves millions of users across 200+ routes is the kind of thing that usually takes months of parallel running and incremental cutover. We were on a deadline, so I did it in two pull requests.

PR 1 replaced everything Next.js-specific: next/image, next/head, next/router. Each was swapped for either a native browser API or a framework-agnostic alternative. This PR changed nothing about the framework itself. It just removed every dependency on it, so that PR 2 could be a clean swap.

PR 2 swapped the framework. 200+ routes migrated. We systematically extracted everything non-routing-related from page files into individual React components first, then generated all routes from the original page tree.

We then added Nitro as the server layer and replaced next.config.js with Nitro config, consolidating redirects (500+), security headers, and caching rules into one place. We also replaced Node.js APIs that Next.js had provided polyfills for (Buffer, url.parse, and others) with browser-native alternatives, which left us with cleaner code as a side effect.

Merged on an early Sunday morning. The team dogfooded immediately with a live war room in Discord, and a stream of fixes landed same day. No downtime.

Sure we gained a faster, more explicit stack, but not without trade-offs.

  • Built-in image optimization. We replaced next/image with <img> tags and Fastly image optimization at the edge.
  • Parts of the ecosystem. We replaced tools like next-seo and next-sitemap with small in-house equivalents. Straightforward to build, no extra dependencies.
  • Maturity. TanStack Start is new, and rougher edges can be expected. We're comfortable with that because the direction is right, the maintainers are responsive, and we sponsor both Vite and TanStack because we believe in where they're going.

We run our production frontend the same way our users run theirs: preview deploys per PR, health checks, zero-downtime rollouts. When we swapped the entire build system and framework, we didn't touch infrastructure. We changed code, pushed it, and Railway handled the rest.

Fastly now serves most of our traffic directly from the edge. Marketing pages are cached, dynamic pages use ISR where needed, and our frontend servers are mostly idle as a result. Vite's asset model makes this work particularly well. Each module gets its own content-hashed chunk, so shipping a change to billing only invalidates that chunk. Returning users download kilobytes, not megabytes.

This is how we think frontends should be deployed: the build is fast, the assets are immutable and cache-friendly, and the infrastructure underneath handles rollouts, previews, and routing without you having to think about it. Your frontend framework should be optimized for iteration speed, and your infrastructure should make shipping those iterations invisible. That's the experience we're building for ourselves and for everyone on Railway.

The speed of iteration on a frontend matters more now than it ever has.

Builds that took 10+ minutes now finish in under two. The dev server starts instantly. Route changes are type-checked at the boundary. Layouts compose without workarounds.

The gap between writing code and getting it in front of users is the bottleneck, and everything we've done here, the framework swap, the edge caching, the asset model, is about closing that gap. Vite + TanStack sets us up for a world where shipping frontend changes is near-instant, and that's the world we're building toward.

联系我们 contact @ memedata.com