We shifted all new product backend build to C# (because at this time last year, we were still "AI assisted" and deemed F# too foreign for most devs to read well), but all internal tooling was built on F#.
By maybe May of this year? We had multiple F# backend codebases, fully AI generated. Only one person on the entire team had worked with F# by hand. Several things work in F#'s favor even though it has a lot less representation in training: 1) the people that write F# really love F# so the code out in the wild is going to be better quality (hunch), 2) F# is a bit more terse/dense than C# and less noise in context, 3) F# benefits from the same tooling stack, 4) F# type system is still a bit more expressive than C#.
I know C# deeply so I know what good looks like and I know how to get agents to write good C#. F# I'm less familiar with, but we had great success with it.
This in contrast to C# where, within one .csproj, there is no "order" and all classes and methods are able to mutually reference one another.
The F# way creates an annoying housekeeping step of maintaining file order, and would be a nightmare to do with manually edited .fsproj files (I have only ever written F# with an IDE), but IMO, it is worth it. On the reading side, it makes it easier to understand how a project you've never read before is structured, since you know the order of the F# source files also must be the bottom-to-top layering of the code. On the writing side, it sometimes causes one to rethink the architecture and simplify it to minimize the number of mutual declarations.
AI isnt quite as creative and clever coders as humans though, but thats true for any language you direct it at, but its a bit more obvious for higher level languages like F#.
The corporate history of C# is undeniably strong, and even till this day in modern C#, they still have a lot of legacy components in it, like AppDomain, APM and MarshalByRefObject.
It is also unfortunately that C# has largely lost in the AI race, that it failed to gain tractions from AI developers despite heavy investments from Microsoft. Early AI stuff like Semantic Kernel actually included C# as its primary languages besides from Python. Nowadays it is not mentioned so much and the default is just Python but perhaps it really isn't a fair comparison due to the gravity of Python in AI as a whole.
The fact that even within Microsoft, Rust wins over C# for agentic development, is a clear indication that C# has lost a meaningful place in software development as its former parent started to berate it as well.
Sure, maybe Bing and a large part of Azure still relies it, probably because of ASP.NET Core and Orleans, but outside of the two Microsoft greenhouses, it is a free for all battle royale where C# don't even have a chance of being picked at due to being too weak.
The last few thing C# is good at is just Unity I'm afraid, and that is also dying too as Unity is also not doing so well lately.
For web API backends and MCP servers, I firmly believe C# is the better choice.
For AI use cases (as in make calls to LLMs), there's not much difference, IMO from Python and C# and TypeScript. At the end of the day, those are all just REST API calls and SDKs are mostly wrappers around it.
For LoB apps where the AI/LLM is just one small leg of the actual flow, C# is still a winner, IMO, because of how good the overall stack is (e.g. EF Core). Orleans (actor model framework) is also super interesting and I'm exploring some memory/communication system based around it.
https://devblogs.microsoft.com/typescript/typescript-native-...
I was curious about their approach to durable execution, so I told them to come back to me when they had a .NET SDK.
The fact that they don’t is either due to the fact that they have a historic prejudice against Microsoft and by extension dotnet, or they can’t be bothered and see no point in it.
Either way the types of companies that are going to have a strong interest in resilient distributed applications, are corporations, and dotnet is still highly prevalent in those types of organizations.
So refusing to support dotnet as a SDK option in Restate is beyond short sighted. It’s leaving money in the table. I can’t take them seriously if that’s the case.
I think the fact that most coding agent images don’t include dotnet support by default. Just look at codex-universal. Swift gets included but not dotnet. Yes it’s not hard to install dotnet yourself and setup the environment, but there isn’t a real valid reason to exclude it since they’ve covered nearly every other popular language.
This. A lot of segregation is very much due to this.
> I think the fact that most coding agent images don’t include dotnet support by default
And it gets worse that that:
https://github.com/openai/codex-universal/issues/13
https://github.com/openai/codex/issues/1276
https://github.com/openai/codex-universal/pull/120
And you see that the community members are doing the work for them, yet they would still refuse using the stiffiest corpo lingo in the most passive aggressive way
"Sorry, we're not accepting PRs in this repo. If you'd like to see a change to the codex universal docker image, please file a feature request in the main codex issue tracker."
Okay, so what the fuck is this:
https://github.com/openai/codex-universal/pull/131
https://github.com/openai/codex-universal/pull/90
I know, but the fact that it is not included by default, then someone made a PR to hopefully get it in, but got refused, tell him to turn around to another issue, and then idling that issue for a long time now, it really pisses me off because there is no communication whatsoever, just like how I have this recent situation with LayerStack has fucked up their control panel, and stripped some features, not ever telling me, just avoided me. This is the part that pisses me off.
"come on, it is just a matter of adding the fucking dotnet SDK by yourself". You know, the problem is not that simple anymore, there is a little hidden agenda which turned into a stance problem, or, oh, well, I hate to say it, "technological politics".
Yeah, I know, "tech is just a tool", then where are all the criticisms when the punks are boasting how "Rust or Golang is so fast, Python and Node is dead" blah blah blah.
Anyway, if you are just trying to hide the vendetta against dotnet, then just say it. Say it out loud and stop fucking around. Make an assertion and stand on your fucking ground.
I might point out that passive-aggressive weasels hiding behind stiff corporate speak are visually undifferentiatiable from people doing the best they can in a project with multiple competing goals in a world where they could lose their jobs for a careless public comment. Not everybody is free.
> I have to go on a furious exchange in the tickets to them
...furious?
Did you have to?
Did you _have_ to?
> Being a Microsoft product is such a heavy baggage
Which I always found ironic because TypeScript (from the same guy that created C# even!), VS Code are both Microsoft. Microsoft also makes big contributions and investments in Postgres and Python as well.JavaScript is one of the most common compile targets. "Who's the better JavaScript" was the most contested field a decade ago. These are not just toy languages - Facebook has Flow and Google has Dart. Both companies are far more web-adjacent than Microsoft. Yet Typescript won.
edit: I think it was considered, but go was a better fit
I guess I... just got bored of it, and fed up of Microsoft. I came to hate Windows and having to work on Windows so much that it sort of tainted C# for me. I'm not saying I'd never use it again but, whilst it will run cross-platform nowadays, so will loads of other languages and frameworks, so it's never at the top of the list when it comes to choosing something for a new project.
As for TypeScript? Absolutely loathe it. I'll use it professionally when I have to, but I'd never use it for one of my own projects. It's just so ugly I don't even like looking at it. I get why people like it but, for me, it's not enough. If I'm staring at something all day then, I'm sorry, it can't be ugly.
But, strangely, I still use and enjoy VS Code from Microsoft, and have no plans to switch to anything else.
(I also still have an Office subscription - or whatever it is Microsoft are calling that suite nowadays, I don't care - because I still use both Excel and Word fairly frequently. I could probably get by without, but I actually quite like Office - even on the Mac.)
The thing is, now most things I do target some flavour of Linux - and although I could write C# and target Linux with absolutely no problem - there are a lot of other options that C# has to complete with. Granted those options would also, at least in theory, have been viable options on Windows but it definitely would have felt a bit like paddling against the flow to host Ruby on Rails or Python or whatever on Windows.
And if I want to target command line or desktop with something cross-platform I'm going to compile to a native binary that requires minimal dependencies, so Rust feels like a more natural fit than C#, especially with Microsoft's desktop UI story being such a dog's breakfast (and even more-so when you account for cross-platform).
Again, in a commercial environment, I could imagine using Electron for that sort of thing because that's what most companies do (but because everything I do nowadays is backend, web, or systems integration, I haven't written a desktop app in a commercial context in 13 years, and I haven't written a command line app in that context since just before the pandemic). However, for my own projects I want to hew as closely as possible to my own principles and ideals and, for them, it'll be a cold day in hell before I ship a desktop or command line app that needs some giant runtime like Electron as a dependency.
Again, the point is that C#, whilst in many ways a great language, doesn't come out as the frontrunner.
> As for TypeScript? Absolutely loathe it.
I just write my TS like I write my C#. They are similar enough, IMO. I ended up writing this as a precursor to our team's move from TS to C#: https://typescript-is-like-csharp.chrlschn.dev/The main things I don't like about TS are the volatile tooling, bad habits from other devs (not declaring types), compromised ORMs, require lots of package hunting, and module system.
Language is fine, tho.
It was pretty impressive work to scale PostgreSQL.
I got so worked up by it I built my own TyepScript compiler with my clankers in C# to prove that it can be done, my revival of the Common Language Runtime concept, project Copeland:
https://github.com/yuechen-li-dev/copeland
Currently, Copeland compiles a dialect of TypeScript to JavaScript, C#, WASM via Blazor WebAssembly, and NativeAOT, runs in csproj, and uses xUnit.NET for testing framework.
(The other reason is that Microsoft still refuses to do the obvious thing in .NET 11 and take the best part of Rust and let enums be `match`ed, despite the fact that both TypeScript and C# have weak enums historically, so I added payload enums to my TypeScript dialect because Microsoft won't.)
Pattern matching is in C# unless I misunderstood you.
https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html
Instead of .NET 11's more awkward (imo) `union` form.
https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
On the historical (And semi-current-ish) downside we had/have;
- lots of 'flash in the pan' stuff between 2008-2012ish (Silverlight comes to mind here)
- Anemic Entity Framework in pre-core, and the weird stuff EF Core had going on early in it's life.
- That constant worry as to whether you need to add .ConfigureAwait(false) to every async call in a library you want to open source.
- The thing where trying to do 'traits' in NetCore (Now that you can mostly mimic the behavior) has had some side-effects on performance, mostly resolved but still something you have to consider as far as impact. (To be clear tho, thank god, needed that...)
- WTF was that UWP workflow?
- Why can't we get a proper designer? I don't think it would be that hard especially given I can do nice things around workflow designers today. Speaking of workflows...
- The complete (what I probably guess was MS groups eating their own tail) thing where Workflow Foundation was almost truly productive, SSMS had a thing based on it that was surprisingly productive, and it has been murdered instead of getting the TLC it needed to really disrupt. This one is extra loud because it is so similar to the 'low code' platforms so many orgs have sank money into.
- Less impactful as far as people eating the lunch microsoft left on the table, but WCF.
The upshot is;
- If you're willing to do some looking there are great options out there. lazy example, linq2db is basically a DSL that is close enough to EFCore as far as 'how you compose' that you can teach it to a group of WITCH devs in 30 minutes or less.
- It really is easy to do all sorts of stuff. It's just productive.
And I am still not over the many breaking changes when they dumped full framework. Have to support a large code base, I think only now with AI will it be economical to redevelop it for .net core.
EF Core now is very, very good with only one minor gap with respect to migration merging (long migration history causes build times to explode).
Other than that I totally agree with everything you said.
> I think C# would be a great choice for both CLI apps and server less functions.
Really? I feel those are the two areas where it falls short. CLI apps and serverless both value cold starts and C# is not great at it because of the JIT overhead. The immaturity of NativeAOT ecosystem on C# was cited by Anders as one of the reasons the TS team chose to rewrite in Go.I may have to play with F# for that.
https://learn.microsoft.com/en-us/dotnet/core/deploying/nati...
I'm especially interested in exploring F# as a concise, non ambiguous way to communicate definitions between humans and agents, somewhere between writing code like the olden days of yesteryear and describing things in English prose.
Yes, the plan is essentially 100% AI code with a small team of programmers and domain experts from other technical backgrounds. But still depends on a few things on the business end before I can really kick it off. More of an exploration until then.
But the gist of it is that I wire it up through Aspire (don't need to, but makes it nice) to set the environment variable on my main app host.
I have a small set of utility functions to access the DI container with common patterns like creating a scope for scoped services: https://github.com/zeeq-ai/zeeq-app/blob/main/src/backend/Ze... (optional, but useful)
And a skill: https://github.com/zeeq-ai/zeeq-app/blob/main/.agents/skills...
I recommend you "train" the skill like I do in my the blog post. Basically ask the agent to get an instance of an object from DI like a repository class or your DbContext and have it perform some operations. Have it wrap a function and add logging. Have it replace a function. Then once it learns how to do this, have it write the skill bespoke to your project.
I still choose .NET as the go-to whenever I can. On Windows or Linux.
I think the baggage view was valid for a while - but now we have a new generation of developers who only know about .Net Core and may not associate the Framework years in quite the same way.
Opinionated in a good way, which means almost all C# apps have the same parameterisation with environment variables, command line, and config files. It’s all very systematic, consistent, but flexible and pluggable.
“Node has that too!” you’re about to say. Lol, no. It has bits and pieces the devs have to wire themselves, which means a fleet of vaguely related apps made by independent teams will have wildly different config styles. Good luck consistently making all of them pull API client certs from KMS or Key Vault, or whatever!
OpenTelemetry is now baked in to the core components and rapidly spreading through other libraries.
Excellent “pervasive” system APIs for spans, traces, metrics, and logs means that the sources are omnipresent and very rich, going back decades. Even very old third party libraries are often a breeze to instrument.
Debug snapshots from production crashes on App Service feels like cheating.
Built in container build outputs with no extra tooling.
Sane defaults for its package manager means far fewer nasty security surprises.
Far more first-party “coverage” of essential capabilities in the optional Microsoft packages so you don’t have to resort to NPM where your security-critical code is written by Sergei and support is “LOL Wut!?”
A GC that automatically responds to container limits instead of the host limits.
It uses thread pools for async tasks in most configurations so whole categories of production-time issues that occur with single threaded runtimes just doesn’t happen.
Etc, etc…
A lot of people also seem to have bought into the "Clean Code" dogma popular in the 00s, completely still living in a world where RAM is cheap and JITs are magical.
Add to this the absolutely miserable state of Microsoft's official .NET documentation, and it's a bizarre experience. Everything is "documented" - with things like "Foo(): Constructs a new instance of the Foo class", in case you were wondering what a constructor is...
Context: I'm coming from a background of C++ and Rust. Especially the latter leaves .NET's documentation in the dust, but that doesn't mean the CLR isn't a piece of extremely solid engineering. I'm continually impressed by the .NET JIT and GC, for example, and when you dive into the poorly documented details of the standard library (it's open source these days), you'll see that the quality is very high almost every time.
So yeah, I agree that .NET and C# has everything it needs to be really great in the right hands, but many hands aren't super right.
My recommendation is always to understand the technology in depth, then make decisions based on what makes engineering sense. Understand the tools first. For me, that's a hands-on process, but others learn better from books. A lot of programming books seem a bit ideological from a methodological point of view, often written by people who thought they found the magic bullet (see the "Clean Code" fiasco) with perhaps limited time in the trenches.
Wait until you meet devs who don’t understand that other OS are a thing!
As far as I know, C# and friends still require setting up a project file, and there isn't a tutorial that has one you can easily copy.
Maybe this is why I also can't get into Android development.
(It also doesn't help that my Linux distro doesn't ship the toolchain because it can't be bootstrapped from text files, as far as I understand it)
You could also `dotnet run script.cs` to run a single file without setting up project.
dotnet new console
dotnet run
dotnet new webapi
dotnet new console
dotnet new <template_or_sdk_here>
dotnet run
dotnet test
The tool chain has matured significantly and stabilized and it's quite easy to use. IMO, much easier than building backends with TS (the madness that is tsconfig.json) and getting configuration correct with TS tool chain (some easier than others).Thanks for the links. Your agent repl thing looks cool as hell.
Modern .NET is great though.
Have these decisions changed? Is everything now open source?
Maybe MS can work out a longish-term roadmap, where in a certain future version C# and TS gradually "morph" into being the same thing (e.g. Typescript 10 == C# 20 or so)
I suppose it would look a bit like F# (LMAO)
I recently tried to come back and the problem isn't the language. It's getting into it. Try to click "new project" and you're met with dozens of templates, and a lot of them sound identical to the untrained observer. Which web project should you start with? Which GUI project?
I pushed through it and made the wrong choice a few times for each of those. Apparently there are stable old one, and rapidly changing and underdeveloped new ones, and no way to tell them apart other than knowing the magic incantation.
Microsoft would do themselves a major favor if they cleaned that up and made things clearer, including definite guidance on why you should pick one template over another.
Instead, they treat them all as if they're perfect and newcomers have to just guess.
And no, web research doesn't help. There are tons of people who love each of them, and only a few people willing to write well-reasoned articles based on their experience, helping someone pick the best ones to start with.
You can get C# to match Rust or Go, but those would require going off the beaten path and you might as well use Rust or Go.
I think C# CLI for internal tooling that is only used by an existing C# codebase makes perfect sense. As a standalone tool that's being shipped for multiple platforms, it would be better to use Rust or Go since those will give smaller native binaries and default better performance profiles in speed and memory usage.
I think backend web APIs are where .NET shines.
Some folks out there still think .NET is Windows only. Some folks out there haven't looked at C# since the .NET Framework days. Some folks out there are still salty about some of Microsoft's decision around OSS (debugger, hot reload fiasco, etc.)
But day to day working with C# and .NET for backends? It's really fantastic. AI seems to like it a lot and a lot of tools for agents to go HAM. Roslyn analyzer, source generators, tons of really good docs from Microsoft, generally low variance and high consistency (e.g. Drizzle vs Prisma vs TypeORM vs Kysely vs ... in Node land).
My previous team switched from TS to C# and F#[0] and it was a blessing; a lot of the platform ops have been simplified. It's just one very solid tool chain and packages are generally stable. First party packages get consistent security updates.
C# feels like a really solid choice in the age of AI coding because of it's general stability, strong static analyzer, insistence on types, and really good tooling.
[0] https://engineering.usemotion.com/moving-off-of-typescript-e...