I’d love to use C# without having to deal with distributing the runtime, so I’d like to hear more!
In the later versions of DotNet there are a couple of common ways to distribute (I'd suggest either DotNet 6 [LTS] or preferably DotNet 7 [current]).
You'd usually use the dotnet publish command, which ideally produces one of three things, all of which are self-contained and can be deployed to a clean server without any framework. Ordered by worst-first (in relation to your requirement):
1. The halfway house from dotnet publish is a folder with your app/site/api alongside all the dlls needed from the standard library and/or nuget. This is a standalone folder, though messy.
2. With an extra couple of options on the dotnet publish command you get it all as a single binary which is exactly what you say: a big executable.
3. There is another option available on the dotnet publish command which will use magic (probably tree-shaking but I can't remember) to produce a smaller single binary by removing unused code.
As an aside, it's also worth noting a couple of extra points:
* The dotnet publish command can cross-compile ready-to-deploy outputs for any supported platform (eg Mac or Linux, using x86, AMD64, or ARM64) just by specifying the combination of platform and CPU as command line options.
* Within C# you can mark your assets as embedded resources (like an embed FS in Go) and they will also be included in your output.
The final result varies in size depending upon what your code does (and hence included libraries), and some code (eg reflection) may interfere with the tree-shaking (or whatever) of option 3 - but it warns you whilst it generates the output, and you can either ignore it or use option 2.
Generally speaking the option 3 builds are between 1.5 and 2 times the size of a Go one, but you're looking about 20MB to 30MB for useful stuff. Not tiny, but still quite small these days. Option 2 builds probably double.
In use (and this is subjective) they consume a bit more memory than Go, but seem more consistent/stable in that usage.
Also note that within that 20MB-30MB build, for an api or a website you get a built-in web server that can sit behind nginx etc as usual, but is also good enough to expose directly.
I can't quite tell from the .NET SDK repository -- any idea if this stuff works on Linux (i.e. building on Linux, perhaps for Windows)? I see mention of MSBuild, so I'm guessing maybe not.
I love C#, but I abandoned it a while ago because I wanted to only rely on open source tools (just to ensure my code is usable in the future). Then, of course, they open-sourced a bunch of stuff (including the compiler). If I at least had the option to develop C# on Linux (with support for cross-compiling to Windows), that would be great (and honestly something I would have never expected 10 years ago).
Here's a link to the commands I use to generate my cross-platform builds [1]. They are easy enough to stick in a shell script or batch file so you get all the builds with one command. These produce single executables, trimmed for size.
[1] https://github.com/kcartlidge/Newt#generating-stand-alone-bu...
For CLI tools whilst a single binary can be convenient, native-image lets you get those for JVM programs too these days. But it's not always the case that it's enough. In practice you will often hit the need for:
a. Cross platform / cross builds.
b. A way to easily update them for your users (that isn't "everyone mount this NFS/SMB drive")
c. Ability to ship other files e.g. third party libraries written in other languages, config files, data files, readmes ...
d. Possibly, avoiding virus scanners and Gatekeeper if you have users on Windows/macOS.
Conveyor [1] does support distributing CLI tools (in any language) that can then be updated via apt-get, the Windows package manager or Sparkle on macOS. If your language/runtime supports cross-building then it can do it all from your developer laptop, you don't need each OS to build for. The resulting artifacts are single files (deb, msix/exe, zip) and it supports both self-signing and regular signing if you want that.
It provides a few other neat features on Windows:
• One click install that immediately adds new tools to every single terminal session without needing restarts.
• If you want, silent background updates Chrome-style. If you don't, manually triggered updates.
• For JVM apps specifically it automatically configures the Windows terminal to support ANSI escapes, Unicode and other modern features so you can use all the same stuff as on UNIX without needing to futz around with win32 or wrappers.
Unfortunately the little default GUI that lets you trigger updates and add CLI tools to your path on macOS isn't officially launched yet, because it only works for JVM apps and not other types of program. But if anyone wants to try it just let me know, it's easy to activate.
If you don't need any such features then yes, a single binary can be a bit more convenient than a zip. But the number of situations where it breaks down is pretty high and it's not so hard to handle multiple files.
Your comment is super interesting, don't let me sound like I'm trying to shoot it down. I'm being deliberately terse to avoid creating receptors for language war antigens to bind to.
My feelings on this changed over time. About 10-15 years ago I thought single executable output was a critical feature for a language, because everywhere I went I saw people saying how important it was for them, how much simpler it made deployment. I figured, OK, people know what they want so that's what they should get.
Then Docker came along. Docker images aren't single files, they're the polar opposite. They aren't even things you directly manipulate using the filesystem at all. Yet people loved it and it took over the world. Clearly what all those people demanding single-file executables were actually wanting in 95% of cases was simpler deployment, and they were phrasing it as single executable because that was concrete and understandable whereas simpler deployment is a very vague concept so who knows what you'd get if you asked for it.
For people who are selecting Go or Rust or Graal native images primarily because of single-file output, I'd actually really appreciate a chance to ask a few questions or interview them quickly to learn more about the deployment context. Conveyor is all about deployment and it's good to understand more about how people are doing things and what could be better.
Java is slow(er) to to startup, but once it's going, it's pretty good.
Stop -> deploy code -> start is simpler and less likely to go wrong.
You may not have seen this if you haven't used Scala...
The most recent example was a Scala monolith. It had to use JVM 1.8 because <reasons> prevented migration to 11 (tried quite hard, but never succeeded). GC tuning doesn't really apply when considering start up delay, but yes it had been tuned over the years. Memory was not limited. The application, mainly due to Scala, had tens of thousands of classes. They all seemed to get JIT'ed on start up, which was the primary reason for the slow start up. People involved (who had come from heavy Scala shops like Twitter) seemed to think it was normal.