2/1/2026 at 2:13:14 PM
I started my career doing ~20 years of C# from pretty much the very beginning (JavaScript even before then!).Around 2020, I switched into the startup space and quickly picked up TypeScript since that's "what the kids use". It wasn't without struggles, but once I wrapped my head around TypeScript as "shapes for JavaScript", it clicked.
At the time, the startup was undertaking a new product built on Nest.js[0] which looked awfully similar to ASP.NET web APIs which had the benefit of being much more mature, complete, and with one of the best ORMs (EF Core). I suggested it to the team and was a bit shocked by the pushback.
It ultimately inspired me to do a bunch of writing [1][2] on just how similar these two languages are and how they've converged on syntax (no doubt owing to Anders at the helm). Of course, they ultimately still have quite a bit of a gap, but for teams that are working in TS and finding that they are outgrowing it, C# is a very natural choice.
I left that startup after a short stint, but boomeranged almost 3 years later. Company went from seed to series C in that time. End of last year, we started the migration from TypeScript on the backend to C# on the backend and haven't looked back. The toolchain is far simpler and stable, the "default" ORM is far more flexible and powerful, the more rigorous type chain seems more conducive for agent-driven coding. Team adapted relatively quickly within ~4 weeks.
[1] https://typescript-is-like-csharp.chrlschn.dev/
[2] https://itnext.io/getting-functional-with-c-6c74bf279616
by CharlieDigital
2/1/2026 at 5:14:01 PM
I can't say for sure because I don't know the full situation, but I've heard a similar story a few times and IMNSHO looking at it as "they made the wrong choice initially" is off because it assumes that a C# team could have delivered the initial version in the timeline required to get to the the next level of project.If they had a team that knows nest and can iterate fast with it that's the perfect choice. I've worked at multiple agencies over the years and was mostly in C# teams. Whenever a C# team got on greenfield/startup projects it ended up being a shit-show of self inflicted slowdowns/over-complications. I've seen 3 projects where in large part due to slow development of the MVP the project missed investment window or ran out of funds.
From my experience Node/Rails teams were much more capable of delivering shit that works. It would eventually have a bunch of problems that would be non issues by default on a different stack like ASP.NET, but the difficulty of getting to that point and realizing that just being in that situation is a win is what most engineers miss.
by rafaelmn
2/1/2026 at 8:31:50 PM
> ...because it assumes that a C# team could have delivered the initial version in the timeline required to get to the the next level of project.
I linked the Nest.js project because you can see how similar these two are[0] with Nest.js leaning into the OOP aspects of TS at the very core. Controllers and services are classes in Nest.js, for example. It uses a somehow more complicated DI system than ASP.NET. It is Spring Boot or ASP.NET, but without the maturity, performance, and ergonomics.The team had developers that had done C# before and later hires also included former C# developers.
TS itself was new for the team at the time and several mistakes were made along the way resulting in a "dual-ORM" situation (Prisma + Drizzle; both with their faults) that ends up sapping a lot of productivity (one of the drivers to move to C#).
[0] https://typescript-is-like-csharp.chrlschn.dev/pages/interme...
by CharlieDigital
2/1/2026 at 9:31:12 PM
Hmmm. I have a different take there: when you are young and wild, you achieve stuff because you think later and instantly produce code. When you turn older, you do it the other way leading to your example.In the early 2000s I have been in a startup and we delivered rapidly in C# as we did in PHP. We just coded the shit.
by oaiey
2/1/2026 at 10:00:56 PM
I think what you said is a healthy progression : write dumb code -> figure out it doesn't scale -> add a bunch of clever abstraction layers -> realize you fucked yourself when you're on call for 12 hours trying to fix a mess for a critical issue over the weekend x how many time it takes you to get it -> write dumb code and only abstract when necessary.Problem is devs these days start from step two because we're teaching that in all sources - they never learned why it's done by doing step one - it's all theoretical example and dogma. Or they are solving problems from Google/Microsoft/etc. scale and they are a 5 people startup. But everyone wants to apply "lessons learned" by big tech.
And all this advice is usually coming from very questionable sources - tech influencers and book authors. People who spend more time talking about code than reading/writing it, and earn money by selling you on an idea. I remember opening uncle bobs repo once when I was learning clojure - the most unreadable scattered codebase I've seen in the language. I would never want to work with that guy - yet Clean Code was a staple for years. DDD preachers, Event driven gurus.
C# is the community where I've noticed this the most.
by rafaelmn
2/1/2026 at 5:36:57 PM
Same experience here. C# might have superior tooling, performance, whatever but the OOP baggage is too heavy. In theory you can write something else than a giant over-complicated, over-abstracted pile of OOP nonsense in C#, but every team I've seen has.by ngruhn
2/1/2026 at 8:36:37 PM
You can write very functional C#. Our codebase is a mix with some aspects being functional (`ErrorOr`[0] being a big part of it as well as `OneOf`[1]) and OOP. Our core, common services all return `ErrorOr` to allow fluent call chaining at the top of the stack (controllers).Modern C# features like `switch` expressions[2] (not `switch-case`) and pattern matching mean that it is possible to write very terse, expressive code. Extension members and methods[3] go a long way as well by making the builder pattern easier to implement.
Overall, it's up to the team to make use of the tools provided by the C# team. You can write C# in a very OOP heavy way (as is possible with TS in the case of Nest.js); you can also write in in a very functional way given many of the functional features adopted from F# over the years. It's up to the team.
[0] https://github.com/amantinband/error-or
[1] https://github.com/mcintyre321/OneOf
[2] https://timdeschryver.dev/blog/pattern-matching-examples-in-...
[3] https://typescript-is-like-csharp.chrlschn.dev/pages/interme...
by CharlieDigital
2/1/2026 at 10:07:12 PM
That's a perfect example of making it overcomplicated, just in the FP direction.C# uses exceptions for error handling. It has it's own control flow primitives. C# developers know how to work with it, everything else uses it. Why would I want to pull in a randos GH DSL and types to pretend I'm writing F# when I can just use F# that has first class support for this ?
by rafaelmn
2/1/2026 at 10:20:17 PM
C# is already very functional when it comes to Linq. In real world use, this just feels like extending in the same directionality while still preserving familiarity. Whereas F# does feel like an entirely different language.Learning C# when you know TS is like learning Portuguese when you know Spanish.
by CharlieDigital
2/1/2026 at 7:40:10 PM
Just to keep it relevant and off topic. Typescript is moving to go because you just cannot get performance out of javascript because of its design.As he says in the video about perf "you cannot leave 10x on the table".
by hvb2
2/1/2026 at 10:31:19 PM
> In theory you can write something else than a giant over-complicated, over-abstracted pile of OOP nonsense in C#, but every team I've seen has.C# syntax is fine, but has a rotten[1] culture/conventions. I suppose it makes sense that Microsoft's "Java-killer" became enterprise-y, with the same over-engineered indirections.
1. IMO - I find it very unpleasant and never allowed myself to Internalize the IConventions out of spite. YMMV.
by overfeed
2/1/2026 at 6:57:09 PM
> the "default" ORM is far more flexible and powerfulI have never used any ORM that is as capable. Entity Framework Core with Linq is what keeps me on .NET.
by hirvi74
2/1/2026 at 10:01:11 PM
Because EF's default behavior implements Unit of Work, a LOT of the complex transactional spaghetti ended up disappearing when we switched.This aspect of EF is highly underrated for complex entity graph mutations.
EF makes the 90% use case easy and the 10% case possible with very little pain. The interceptors, global conventions, and other extension points are an enabler of complex behaviors that are still transparent to most of the team.
by CharlieDigital
2/2/2026 at 2:57:33 AM
> Because EF's default behavior implements Unit of Work, a LOT of the complex transactional spaghetti ended up disappearing when we switched.I have never felt more understood by a fellow HN user. I think I know the exact spaghetti you are talking about, and I agree with you 100%. I wish EF could create (SQL) views, but it's not really any issue considering I can just use raw SQL to accomplish the same thing.
> complex entity graph mutations.
I'm too dumb to know what those words mean together. But I going to assume it's something to do with complex entities. If so, I completely agree as well. I sometimes harness the powers of the Dark Magics where my domain entities and database entities are the same objects (I'm not one of those DDD people either). Thanks to EF, I've been able to create some complex objects that really have cut down on a lot of useless objects I used to litter applications with.
by hirvi74
2/1/2026 at 5:41:08 PM
I assume the startup wasn't also leveraging typescript heavily on the frontend, that tends to shift the weight in its favor. Having one set of tools to use across everything, being able to share logic and types without needing to go through lossy translation layers, and giving (especially small) teams better flexibility to move people around is a huge benefit.by wbobeirne
2/1/2026 at 8:26:27 PM
We are using TS on the FE with React.But the reality is that at some point, your FE and BE teams will diverge anyways and we use an OpenAPI spec as the contract (Nest.js, not Next.js).
So there was no benefit to using TS on both ends; only pain on the BE.
If a team is going to ship an OpenAPI spec and run it through a transformer, then it changes the selection criteria for a BE language:
- Easy for a TS team to adopt; similar core semantics like `async/await`, exception handling, etc.
- Flexible and pluggable OpenAPI spec generation for edge cases and advanced scenarios
- Excellent ORM to improve productivity around CRUD
- Good tooling
- Extensive docs, platform maturity, but modern language features
C# meets all of those in ways that no other language and platform does.
by CharlieDigital
2/2/2026 at 7:09:58 AM
I can't speak to your use case, but for past projects I've not only wanted to share types but actual logic. For instance, if you want instantaneous validation on the frontend, while applying the same logic on the backend with submitted data. In many instances that would be simple and maybe even serializable, but in my case I was working with architectural data that had extremely detailed logic for what was and was not valid.by wbobeirne
2/2/2026 at 12:01:26 PM
That's simple; check out https://Orval.dev or https://Kubb.dev. Both of which can generate validation code (among other things) from OpenAPI.Example with .NET: https://github.com/CharlieDigital/dn-kubb
by CharlieDigital
2/2/2026 at 10:44:49 AM
Using a TS validation library like Zod gives you both the shared validation code for both the frontend and backend, the TS types, as well as the json schema for openapi. It's a triple whammy too good to ignore. Especially as a small team it is huge leverage enabling you to go further faster.by Sammi