Get AI summaries of any video or article — Sign up free
Life after TypeScript thumbnail

Life after TypeScript

Theo - t3․gg·
6 min read

Based on Theo - t3․gg's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Motion’s TypeScript monorepo delivered strong full-stack type sharing, but scaling exposed persistent tooling and workflow pain: language server crashes and slow CI/typechecking.

Briefing

Motion ran a massive TypeScript monorepo for years—about 2.5 million lines of code at peak—and still decided to move off TypeScript. The core takeaway isn’t that TypeScript is “bad,” but that scaling a large TypeScript organization can become a developer-experience bottleneck: editor instability, slow CI, and compiler/tooling pain can start to outweigh the benefits of full-stack type sharing.

TypeScript’s original promise at Motion was real. It enabled rapid validation of ideas, let teams work across front end and back end with shared types, and supported code reuse across a React web app, React Native mobile, an Electron desktop app, and infrastructure-as-code via Palumi. Motion also leaned on the ecosystem to unify development workflows and share backend/API type definitions across multiple products.

Over time, the “share everything” dream didn’t fully materialize. Differences in React Native versus the web meant UI and component sharing often failed in practice. Motion found that developers frequently forgot mobile when changing shared libraries, causing repeated breakages. For some screens, performance demands forced a retreat from React Native: a calendar view reportedly hit around 190% CPU, with the underlying issue framed as costly back-and-forth between JavaScript and native layers when nested components repeatedly fetch data. The fix wasn’t abandoning the stack entirely, but changing strategy—prototype features quickly in React Native, then convert to native selectively once usage and performance justify it.

On the TypeScript side, Motion described the familiar pain points of a growing codebase: TypeScript language server crashes, CI pipelines that regularly took 20+ minutes, and a long wait for upstream improvements. They pointed to TypeScript Go as a major effort aimed at improving performance, but also highlighted stopgaps and ecosystem upgrades. Zod v3 triggered TypeScript compiler max recursion depth issues, leading them to wait for Zod 4. They also cited improvements from Zod 4 and targeted work on TRPC, including fixes that reportedly improved TypeScript-related performance by up to 80% for some applications.

Database tooling added more friction. Prisma was criticized for destructive behavior—deleting an entire table when a where clause evaluates to undefined—along with other bugs. Motion described Prisma as improving over time, but also supported alternatives like Drizzle, which they viewed as a longer-term option.

The most consequential part of the decision was organizational control. Motion’s argument, as reflected in the discussion, is that waiting on community timelines can slow a scaling startup. TypeScript’s ecosystem is vibrant, but the team couldn’t afford to treat core build and tooling reliability as someone else’s roadmap.

Instead of swapping languages for performance alone, Motion chose C# and .NET for a mix of productivity and guardrails: a mature ORM story via Entity Framework Core, strong tooling via Roslyn, and a large ecosystem. The transition was also framed as lower-risk because C# and TypeScript share familiar syntax patterns (async/await, nullable types, arrow functions). The broader debate in the discussion then turns to tradeoffs: moving off TypeScript can reduce editor/build pain, but it can also shift complexity into backend infrastructure and type synchronization elsewhere. The bottom line is less “leave TypeScript” and more “don’t bet your company’s velocity on tooling that can’t keep up with your scale.”

Cornell Notes

Motion’s TypeScript monorepo delivered real full-stack benefits—shared types across React, React Native, Electron, and Palumi—but the expected “share everything” payoff weakened as platform differences and developer workflow gaps caused breakages. Performance issues on mobile (notably a calendar screen hitting ~190% CPU) pushed Motion toward a hybrid approach: build new features in React Native for speed, then convert performance-critical parts to native. On the TypeScript tooling side, language server crashes, slow CI, and compiler limits (including Zod v3 max recursion depth) created persistent friction, with improvements tied to TypeScript Go, Zod 4, and TRPC-related compiler fixes. The decision to move off TypeScript centered on control: startups can’t afford to wait for community timelines when reliability and iteration speed are business-critical.

Why did Motion’s “share code everywhere” strategy fail to fully deliver, even with TypeScript?

The biggest gap wasn’t type safety—it was platform reality and workflow. React Native and the web differ enough that UI/component sharing often doesn’t work well; Motion also saw frequent breakages when developers changed shared libraries without accounting for mobile. The discussion emphasizes that React Native’s original goal wasn’t “write once, run everywhere,” but to let web-focused teams contribute native-adjacent features without degrading native platform quality. As a result, Motion ended up sharing more logic (like API hooks/state management) than UI components, and even then had to manage reuse carefully to avoid mobile regressions.

What mobile performance mechanism was blamed for the calendar screen’s CPU spike?

The calendar view was described as suffering from nested rendering plus repeated data fetching across the JavaScript-to-native boundary. When a top-level component fetches data, then each nested event component fetches additional data, every hop requires communication between JS and native rendering layers. With React Native, that back-and-forth can become brutal—especially if the app isn’t using the newest JSI bridge improvements. The practical takeaway: hoist data fetching upward so it happens once in the JS layer, and move only the most performance-critical UI to native components when needed.

How did Zod and TRPC relate to TypeScript compiler performance problems?

Motion reported that Zod v3 caused TypeScript compiler max recursion depth issues in their monorepo, forcing them to wait for Zod 4. They also pointed to TRPC-related work: a deep dive by David (Archype) and subsequent fixes in TypeScript performance reportedly improved performance by up to ~80% for some applications. The key distinction made in the discussion is that these were largely developer-experience and tooling performance issues (editor/build/typechecking), not runtime performance of the deployed app.

What was the critique of Prisma, and why did Drizzle enter the picture?

Prisma was criticized for a particularly dangerous behavior: it can delete an entire table if a where statement evaluates to undefined. Motion also cited other bugs encountered on the ORM side. While Prisma was acknowledged as improving (including performance gains after moving from Rust to pure TypeScript), Motion still sponsored Drizzle for a better alternative, viewing it as an aspirational path even if it wasn’t at 1.0 yet.

What was the decision logic behind moving from TypeScript to C#/.NET?

The move wasn’t framed as abandoning TypeScript because it lacked value; it was about control and productivity at scale. C#/.NET offered mature enterprise tooling—especially Entity Framework Core with features like global query filters for soft deletes—and a strong, performant compiler platform via Roslyn. Familiar syntax (async/await, nullable types, arrow functions) was expected to reduce transition friction. The ecosystem argument was also pragmatic: it’s not about being the largest, but being “good enough” and stable for the team’s needs.

How did the discussion separate different kinds of “scale” and performance?

The discussion distinguishes QPS on a single box, horizontal scaling, and codebase scaling (how well tooling handles more developers and more code). TypeScript-related pain was mostly codebase scaling: editor crashes, slow CI, and typechecking/compiler limits. Backend runtime performance was treated as mostly separate, with the only explicit runtime/performance example being the React Native calendar CPU issue. The argument then warns that switching ecosystems can trade one set of problems for another—e.g., more responsibility for infrastructure/gateway concerns when leaving the web/serverless-style scaling model.

Review Questions

  1. What specific developer-experience failures (editor/CI/typechecking) pushed Motion toward reconsidering TypeScript, and how were they tied to particular tools like Zod and TRPC?
  2. Why did Motion’s approach to React Native evolve into a “React Native first, native later” strategy? Identify the performance mechanism described.
  3. What criteria made C#/.NET a better fit than other statically typed options for Motion’s constraints, and how did Entity Framework Core factor into that choice?

Key Points

  1. 1

    Motion’s TypeScript monorepo delivered strong full-stack type sharing, but scaling exposed persistent tooling and workflow pain: language server crashes and slow CI/typechecking.

  2. 2

    React Native UI sharing with the web often underperformed because the platforms differ; Motion saw breakages when shared libraries weren’t tested with mobile in mind.

  3. 3

    A mobile performance bottleneck was linked to nested rendering and repeated data fetching across the JS-to-native boundary, motivating hoisted data fetching and selective native conversion.

  4. 4

    Zod v3 contributed to TypeScript compiler max recursion depth issues, and Motion waited for Zod 4 while also relying on TypeScript Go and TRPC/compiler fixes for performance improvements.

  5. 5

    Prisma faced serious reliability concerns (including table deletion when a where clause is undefined), leading Motion to sponsor Drizzle as a longer-term alternative.

  6. 6

    The decision to move off TypeScript centered on organizational control: startups can’t afford to depend on community timelines for core build and editor stability.

  7. 7

    C#/.NET was chosen for guardrails and productivity, with Entity Framework Core and Roslyn-based tooling cited as major advantages for enterprise-style CRUD workloads.

Highlights

Motion’s “share code everywhere” promise weakened in practice: React Native differences and developer workflow gaps meant shared libraries often broke mobile when changes weren’t mobile-aware.
The calendar screen’s ~190% CPU spike was attributed to costly JS-to-native hops caused by nested components repeatedly fetching data.
Zod v3 triggered TypeScript compiler max recursion depth issues in Motion’s monorepo, pushing them to wait for Zod 4 and TypeScript Go improvements.
Prisma’s behavior of deleting an entire table when a where clause evaluates to undefined became a decisive reliability concern.
The move to C#/.NET emphasized control and guardrails—especially Entity Framework Core and Roslyn—more than raw runtime speed alone.

Topics

  • TypeScript Migration
  • React Native Performance
  • Zod Compiler Limits
  • Prisma vs Drizzle
  • C#/.NET Guardrails

Mentioned