Where it started
Incident Net, the IT incident management and ticketing system I built for Banque Nationale d'Algérie, ran on a React.js frontend with a C# .NET / Entity Framework backend. It was my deepest exposure to the .NET world — strongly typed from top to bottom, Entity Framework doing a lot of the heavy lifting around migrations and relational mapping, and a request pipeline built around dependency injection that .NET makes almost impossible to get wrong.
What carried over cleanly
The mental model, mostly intact: layered architecture, dependency injection, typed models mapped to a relational schema. NestJS's decorator-based structure will look immediately familiar to anyone coming from .NET's [ApiController] and attribute-based routing — modules, controllers, providers map almost one-to-one onto .NET's own separation of concerns. Entity Framework's migration workflow translated directly to Prisma's, once I stopped looking for the exact same commands and started looking for the same underlying idea: schema as code, migrations as history.
What actually snapped
TypeScript's structural typing took the longest to stop fighting. Coming from C#'s nominal type system, where two types with identical shapes are still different types unless you say otherwise, TypeScript's 'if it quacks like a duck, it's a duck' model felt loose at first and then, once it clicked, felt like less ceremony for the same safety in most day-to-day code. The other adjustment was async ergonomics — trading .NET's Task-based async/await for JavaScript's event-loop model meant relearning what actually blocks and what doesn't, especially around database calls that look synchronous in C# and are very much not in a Node runtime.
The takeaway
A year later, I write both without translating in my head, and the thing I'd tell anyone making the same jump: the stack changes, but the discipline — typed contracts, clear layering, migrations you can read like a changelog — is the part worth protecting, and it survives the move.