[HN Gopher] What .NET 10 GC changes mean for developers
___________________________________________________________________
What .NET 10 GC changes mean for developers
See also: _Preparing for the .NET 10 GC_ -
https://news.ycombinator.com/item?id=45358527
Author : roxeem
Score : 255 points
Date : 2025-10-01 08:40 UTC (4 days ago)
(HTM) web link (roxeem.com)
(TXT) w3m dump (roxeem.com)
| mrcsharp wrote:
| On the topic of DATAS, there was a discussion here recently:
| https://news.ycombinator.com/item?id=45358527
| dang wrote:
| Thanks! Macroexpanded:
|
| _Preparing for the .NET 10 GC_ -
| https://news.ycombinator.com/item?id=45358527 - Sept 2025 (60
| comments)
| dtech wrote:
| Interesting, I mostly work in JVM, and am always impressed how
| much more advanced feature-wise the .NET runtime is.
|
| Won't this potentially cause stack overflows in programs that ran
| fine in older versions though?
| sbinder wrote:
| I don't think the runtime is "much more advanced", the JVM has
| had most of these optimizations for years.
| grumpyprole wrote:
| The JVM famously boxes everything though, probably because it
| was originally designed to run a dynamic language. An array
| list of floats is an array list of pointers. This created an
| entire cottage industry of alternative collections libraries
| with concrete array list implementations.
| ysleepy wrote:
| A float[] is packed and not a list of pointers in the jvm.
|
| An ArrayList<Float> is a list of pointers though.
| grumpyprole wrote:
| Arrays have a static fixed size though, making them far
| less useful in practice. Anything one builds with
| generics is boxed. Dotnet doesn't have this problem.
| pjmlp wrote:
| Currently you can get around this with Panama, even if
| the API is kind of verbose for the purpose.
|
| Eventually value classes might close the gap, finally
| available as EA.
| mrsmrtss wrote:
| Valhalla is over 10 years in the works already and there
| is still no clear date when or if at all it would be
| released. It's very difficult to change (or fix) such
| fundamental things so late in the game.
| vips7L wrote:
| They're famously working on changing that. I think we're
| all hopeful that we'll start seeing the changes from
| Valhalla roll in post-25.
| dtech wrote:
| Almost none of this is in the JVM. Escape analysis is
| extremely limited on the standard JVM, and it's one of
| GraalVM's "enterprise" features. You have to pay for it.
| doikor wrote:
| > one of GraalVM's "enterprise" features. You have to pay
| for it.
|
| Free for some (most?) use cases these days.
|
| Basically enterprise edition does not exist anymore as it
| became the "Oracle GraalVM" with a new license.
|
| https://www.graalvm.org/faq/
| kimixa wrote:
| One limitation of the stack is that it needs to be contiguous
| virtual addresses, so it was often limited when devices just
| didn't have the virtual address space to "waste" on a large
| stack for every thread in a process.
|
| But 64 bits of virtual address space is large enough that you
| can keep the stacks far enough apart that even for pretty
| extreme numbers of threads you'll run out of physical memory
| before they start clashing. So you can always just allocate
| more physical pages to the stack as needed, similar to the
| heap.
|
| I don't know if the .net runtime actually does this, though.
| throw-qqqqq wrote:
| > So you can always just allocate more physical pages to the
| stack as needed, similar to the heap.
|
| You set the (max) stack size once when you create the thread
| and you can't increase the (max) size after that.
|
| Processes see a virtual address space that is handled by the
| OS, so you would have to involve the OS if you needed to add
| to the stack size dynamically.
| samus wrote:
| I am surprised that they didn't already do a lot of
| optimizations informed by escape analysis, even though they
| have had value types from the beginning. Hotspot is currently
| hampered by only having primitive and reference types, which
| Project Valhalla is going to rectify.
| adzm wrote:
| > Won't this potentially cause stack overflows in programs that
| ran fine in older versions though?
|
| That's certainly a possibility, and one that's come up before
| even between .net framework things migrated to .net core.
| Though usually it's a sign that something is awry in the first
| place. Thankfully the default stack sizes can be overridden
| with config or environment variables.
| yread wrote:
| I think that DATAS also has more knobs to tune it than the old
| GC. I plan to set the Throughput Cost Percentage (TCP) via
| System.GC.DTargetTCP to some low value so that is has little
| impact on latency.
|
| https://learn.microsoft.com/en-us/dotnet/core/runtime-config...
| highwaylights wrote:
| Very mixed feelings about this as there's a strong case for the
| decisions made here but it also moves .NET further away from
| WASMGC, which makes using it in the client a complete non-starter
| for whole categories of web apps.
|
| It's a missed opportunity and I can't help but feel that if the
| .NET team had gotten more involved in the proposals early on then
| C# in the browser could have been much more viable.
| pjmlp wrote:
| Webassembly taking off on the browser is wishful thinking.
|
| There are a couple unicorns like Figma and that is it.
|
| Performance is much better option with WebGPU compute, and not
| everyone hates JavaScript.
|
| Whereas on the server it is basically a bunch of companies
| trying to replicate application servers, been there done that.
| breve wrote:
| > _Webassembly taking off on the browser is wishful
| thinking._
|
| It has taken off in the browser. If you've ever used Google
| Sheets you've used WebAssembly.
| pjmlp wrote:
| Another niche use case.
| breve wrote:
| Google Sheets is one of the most widely used applications
| on the planet. It's not niche.
|
| Amazon switched their Prime Video app from JavaScript to
| WebAssembly for double the performance. Is streaming
| video a niche use case?
| easton wrote:
| I think they meant most people aren't building a high
| performance spreadsheet, not most people aren't _using_ a
| high performance spreadsheet.
| breve wrote:
| > _most people aren't building a high performance
| spreadsheet_
|
| Lots of people are building Blazor applications:
|
| https://dotnet.microsoft.com/en-us/apps/aspnet/web-
| apps/blaz...
|
| > _not most people aren't using a high performance
| spreadsheet_
|
| A spreadsheet making use of WebAssembly couldn't be
| deployed to the browser if WebAssembly hadn't taken off
| in browsers.
|
| Practical realities contradict pjmlp's preconceptions.
| koakuma-chan wrote:
| Can you actually build something like Figma in Blazor?
| Does Blazor somehow facilitate that?
| pjmlp wrote:
| Don't mix mainstream adoption at the same level as
| regular JavaScrip and Typescript, with availability.
|
| Microsoft would wish Blazor would take off like React and
| Angular, in reality it is seldom used outside .NET shops
| intranets in a way similar to WebForms.
| breve wrote:
| > _Blazor is seldom used outside .NET shops intranets_
|
| So, in other words, widely used in lots and lots of
| deployments.
| pjmlp wrote:
| Do you have a number for us?
| lanyard-textile wrote:
| I think that was sarcasm :)
| whizzter wrote:
| I wouldn't be surprised if it did take off, classic Wasm
| semantics were horrible since you needed a lot of language
| support to even have simple cludges when referring to DOM
| objects via indices and extra lifeness checking.
|
| WASM-GC will remove a lot of those and make quite a few
| languages possible as almost first-class DOM manipulating
| languages (there's still be cludges as the objects are opaque
| but they'll be far less bad since they can at least avoid
| external ID mappings and dual-GC systems that'll behave
| leakily like old IE ref-counts did).
| pjmlp wrote:
| All great and dandy, except tooling still sucks.
|
| You still need to usually install plenty of moving pieces
| to produce a wasm file out of the "place language here",
| write boilerplate initialisation code, debugging is
| miserable, only for a few folks to avoid writing
| JavaScript.
| Rohansi wrote:
| I think you may be underestimating how many people
| _really_ dislike JavaScript.
| pjmlp wrote:
| As many that dislike PHP, C, C++, yet here we are.
| whizzter wrote:
| There will always be enthusiasts to take the initial
| steps, the question is if they have the taste to make it
| a coherent system that isn't horrible to use.
|
| Counted out over N languages, we should see something
| decent land before long.
| Kwpolska wrote:
| Those changes affect the .NET runtime, designed for real
| computers. This does not preclude the existence of a special
| runtime designed for Wasm with WasmGC support.
|
| The .NET team appears to be aware of WasmGC [0], and they have
| provided their remarks when WasmGC was being designed [1].
|
| [0] https://github.com/dotnet/runtime/issues/94420
|
| [1] https://github.com/WebAssembly/gc/issues/77
| Rohansi wrote:
| .NET was already incompatible with WASM GC from the start [1].
| The changes in .NET 10 are nothing in comparison to those.
| AFAIK WASM GC was designed with only JavaScript in mind so
| that's what everyone is stuck with.
|
| [1] https://github.com/dotnet/runtime/issues/94420
| whizzter wrote:
| There's 2 things,
|
| 1: JavaScript _interoperability_ , ie same heap but
| incompatible objects (nobody is doing static JS)
|
| 2: Java, Schemes and many other GC derived languages ,etc
| have more "pure" GC models, C# traded some of it for
| practicality and that would've required some complications to
| the regular JS GC's.
| torginus wrote:
| A lot of the features here, stuff like escape analysis for
| methods etc. does not directly involve the GC - it reduces the
| amount of objects that go to the GC heap so the GC has less
| work to do in the first place.
| mdasen wrote:
| How would this move .NET further away from WASMGC? This is a
| new GC for .NET, but doesn't add new things to the language
| that would make it harder to use WASMGC (nor easier).
|
| For example, .NET has internal pointers which WASMGC's MVP
| can't handle. This doesn't change that so it's still a barrier
| to using WASMGC. At the same time, it isn't adding new language
| requirements that WASMGC doesn't handle - the changes are to
| the default GC system in .NET.
|
| I agree it's disappointing that the .NET team wasn't able to
| get WASMGC's MVP to support what .NET needs. However, this
| change doesn't move .NET further away from WASMGC.
| cake-rusk wrote:
| Are you now allowed to benchmark the .Net runtime / GC?
|
| Edit: Looks like you are allowed to benchmark the runtime now. I
| was able to locate an ancient EULA which forbade this (see
| section 3.4):
| https://download.microsoft.com/documents/useterms/visual%20s...
|
| > You may not disclose the results of any benchmark test of the
| .NET Framework component of the Software to any third party
| without Microsoft's prior written approval.
| Krutonium wrote:
| ...Were you not before?
| cake-rusk wrote:
| IIRC the EULA forbids it. This is why you don't see .net v/s
| Java GC comparisons for example.
| rezonant wrote:
| I can't find mention of anything resembling this. The .NET
| runtime is under the MIT license.
| jfyi wrote:
| https://download.microsoft.com/documents/useterms/visual%
| 20s...
|
| It's because you aren't looking at 20 year old EULA's
|
| >3.4 Benchmark Testing. The Software may contain the
| Microsoft .NET Framework. You may not disclose the
| results of any benchmark test of the .NET Framework
| component of the Software to any third party without
| Microsoft's prior written approval.
|
| This person is not likely familiar with the history of
| the .net framework and .net core because they decided a
| long time ago they were never going to use it.
| cake-rusk wrote:
| Yeah, you got me there. I have moved on to Linux
| development since then. Haven't kept up with Microsoft
| developer tools.
| adzm wrote:
| .net core on Linux works great btw.
| SideburnsOfDoom wrote:
| In recent versions (i.e. since .NET 5 in 2020) ".NET
| core" is just called ".NET"
|
| The cross-platform version is mainstream, and this isn't
| new any more.
|
| .NET on Linux works fine for services. Our .NET services
| are deployed to Linux hosts, and it's completely
| unremarkable.
| homebrewer wrote:
| As long as it's your deployment target and nothing else.
| For development, both macOS and Linux continue to be
| second class citizens, and I don't see this changing as
| it goes against their interests. In most .NET shops
| around me, the development and deployment tooling is so
| closely tied to VS that you can't really _not_ use it.
|
| It's fine if you stick to JetBrains and pay for their IDE
| (or do non-commercial projects only), and either work in
| a shop which isn't closely tied to VS (basically non-
| existent in my area), or work by yourself.
| SideburnsOfDoom wrote:
| Well, in most .NET shops around me:
|
| > The development and deployment tooling is so closely
| tied to VS that you can't really not use it.
|
| Development tooling: It's 50-50. Some use Visual Studio,
| some use Rider. It's fine. The only drawback is that VS
| Live Share and the Jetbrains equivalent don't
| interoperate.
|
| deployment tooling: There is deployment tooling tied to
| the IDE? No-one uses that, it seems like a poor idea. I
| see automated build/test/deploy pipelines in GitHib
| Actions, and in Octopus Deploy. TeamCity still gets used,
| I guess.
|
| It's true though that the most common development OS is
| Windows by far (with Mac as second) and the most common
| deployment target by far is Linux.
|
| However the fact that there is close to no friction in
| this dev vs deploy changeover means that the cross-
| platform stuff just works. At least for server-side
| things such as HTTP request and queue message processing.
| I know that the GUI toolkit story is more complex and
| difficult, but I don't have to deal with it at all so I
| don't have details or recommendations.
| aksss wrote:
| > is there deployment tooling tied to the IDE?
|
| VS has the "Publish" functionality for direct deployment
| to targets. It works well for doing that and nothing
| else. As you said, CI/CD keeps deployment IDE agnostic
| and has far more capabilities (e.g. Azure DevOps, GitHub
| Actions).
| jfyi wrote:
| Yeah? Ncurses still a thing? I only ask because that's
| the only api name I remember from forever ago.
|
| I worked on a mud on linux right after high school for
| awhile. Spent most of the time on the school's bsdi
| server prior to that though.
|
| Then I went java, and as they got less permissive and
| .net got more permissive I switched at some point. I've
| really loved the direction C# has gone merging in
| functional programming idioms and have stuck with it for
| most personal projects but I am currently learning
| gdscript for some reason even though godot has C# as an
| option.
| homebrewer wrote:
| The only thing that has become "less permissive" is
| Oracle's proprietary OpenJDK build, which isn't really
| needed or recommended in 99.9% of cases (except for when
| the vendor of your proprietary application requires it to
| provide support).
|
| The rest of the ecosystem is "more permissive" than .NET
| since there are far more FOSS libraries for every task
| under the sun (which don't routinely go commercial
| without warnings), and fully open / really cross-platform
| development tooling, including proper IDEs.
| jfyi wrote:
| Funny, because one the libraries I was using at the time
| went hyper commercial (javafxports). Java burned me on
| two fronts at the very same time and lost me. Ymmv I
| guess. It's always a good time to try something new
| anyway... I also moved to kotlin on android and couldn't
| be happier with it, it's a clearly superior language.
| mrsmrtss wrote:
| The fact that you even need to be very careful when
| choosing a JDK is a lot bigger problem than some simple
| easily replaceable library is going commercial (not that
| this has not happend also in Java land). Also .NET is
| fully open and really cross-platform for a long time
| already and it includes more batteries than Java out of
| the box, you may not even need to include any third party
| dependencies (although there are also plenty to choose -
| 440k packages in Nuget). .NET has also proper IDEs or is
| Jetbrains Rider not a proper IDE for you?
| 369548684892826 wrote:
| As a dotnet developer all my code these days is run on
| Linux.
| zenmac wrote:
| Wow didn't know that. Can you provide some links?
| Traubenfuchs wrote:
| What are you talking about?
|
| https://benchmarksgame-
| team.pages.debian.net/benchmarksgame/...
| magicalhippo wrote:
| I seem to vaguely recall such a thing from way back in the
| early days, but the only copy[1] of the .Net Framework EULA
| I could readily find says it's OK as long as you publish
| all the details.
|
| [1]: https://docs.oracle.com/en/industries/food-
| beverage/micros-w...
| msk-lywenn wrote:
| why wouldn't you be?
| aiisthefiture wrote:
| Yes.
| deely3 wrote:
| Yes, you probably mixed it with SQL Server.
|
| > Publishing SQL Server benchmarks without prior written
| approval from Microsoft is generally prohibited by the standard
| licensing agreements.
| elmigranto wrote:
| Use managed language, it will handle memory stuff for you, you
| don't have to care.
|
| But also read these 400 articles to understand our GC. If you are
| lucky, we will let you change 3 settings.
| phito wrote:
| It works just fine out of the box. The articles/manuals are
| just if you want to really understand how it works and get the
| most out of it. What's the issue with that?
| samus wrote:
| You need these settings when you drive your application hard
| into circumstances where manual memory allocation arguably
| starts making sense again. Like humongous heaps, lots of big,
| unwieldy objects, or tight latency (or tail latency)
| requirements. But unless you're using things like Rust or
| Swift, the price of memory management is the need to
| investigate segmentation faults. I'd prefer to spend developer
| time on feature development and benchmarking instead.
| pjmlp wrote:
| Dr. Dobbs and The C/C++ Users Journal archives are full of
| articles and ads for special memory allocators, because the
| ones on the standard library for C or C++ also don't work in
| many cases, they are only good enough as general purpose
| allocation.
| bob1029 wrote:
| You can provide your own GC implementation if you really wanted
| to:
|
| https://learn.microsoft.com/en-us/dotnet/core/runtime-config...
|
| https://github.com/dotnet/runtime/blob/main/src/coreclr/gc/g...
| JaggerJo wrote:
| Interesting!
| CharlieDigital wrote:
| In my 20+ years using C#, there's only been one instance where
| I needed to explicitly control some behavior of the GC (it
| would prematurely collect the managed handle on a ZMQ client)
| and that only required one line of code to pin the handle.
|
| It pretty much never gets in your way for probably 98% of
| developers.
| 1a527dd5 wrote:
| DATAS has been great for us. Literally no effort, upgrade the app
| to net8 and flip it on. Huge reduction in memory.
|
| TieredCompilation on the other hand caused a bunch of esoteric
| errors.
| neonsunset wrote:
| FWIW Tiered Compilation has been enabled on by default since
| .NET Core 3.1. If the code tries to use refection to mutate
| static readonly fields and fails, it's the fault of that code.
| adzm wrote:
| A hobby audio and text analysis application I've written, with no
| specific concern for low level performance other than
| algorithmically, runs 4x as fast in .net10 vs .net8. Pretty much
| every optimization discussed here applies to that app. Great
| work, kudos to the dotnet team. C# is, imo, the best cross
| platform GC language. I really can't think of anything that comes
| close in terms of performance, features, ecosystem, developer
| experience.
| grumpyprole wrote:
| > C# is, imo, the best cross platform GC language. I really
| can't think of anything that comes close
|
| How about F#? Isn't F# mostly C# with better ergonomics?
| denismenace wrote:
| He means the runtime ".NET CLR". They have the same runtime.
| Xss3 wrote:
| Lmao, functional programming is far from ergonomic
| samus wrote:
| That really depends on your preferred coding style.
| grumpyprole wrote:
| F# is hardly modern functional programming. It's more like
| a better python with types. And that's much more ergonomic
| than C#.
| ZenoArrow wrote:
| Python and F# are not very similar. A better comparison
| is OCaml. F# and OCaml are similar. They're both ML-style
| functional languages.
| grumpyprole wrote:
| I'd much rather code F# than Python, it's more
| principled, at least at the small scale. But F# is in
| many ways closer to modern mainstream languages than a
| modern pure functional language. There's nothing scary
| about it. You can write F# mostly like Python if you
| want, i.e. pervasive mutation and side effects, if that's
| your thing.
| ZenoArrow wrote:
| If Python is the only language you have to compare other
| languages to, all other programming languages are going
| to look like "Python with X and Y differences". It makes
| no sense to compare Python to F# when OCaml exists and is
| a far closer relative. F# isn't quite "OCaml on .NET" but
| it's pretty close.
| grumpyprole wrote:
| It absolutely does make sense to compare it to the worlds
| most popular programming language, especially when
| dismissed as "functional programming". Who benefits from
| an OCaml comparison? You think F# should be marketed to
| OCaml users who might want to try dotnet? That's a pretty
| small market.
| raincole wrote:
| It's so weird to describe F# as "Python with Types."
| First of all, Python is Python with Types. And C# is much
| more similar to Python than F# is.
| grumpyprole wrote:
| It all depends on the lens one chooses to view them. None
| of them are really "functional programming" in the truly
| modern sense, even F#. As more and more mainstream
| languages get pattern matching and algebraic data types
| (such as Python), feature lambdas and immutable values,
| then these languages converge. However, you don't really
| get the promises of functional programming such as
| guaranteed correct composition and easier
| reasoning/analysis, for that one needs at least purity
| and perhaps even totality. That carries the burden of
| proof, which means things get harder and perhaps too hard
| for some (e.g. the parent poster).
| maleldil wrote:
| If purity is a requirement for "real" functional
| programming, then OCaml or Clojure aren't functional.
| Regarding totality, even Haskell has partial functions
| and exceptions.
| grumpyprole wrote:
| Both OCaml and Clojure are principled and well designed
| languages, but they are mostly evolutions of Lisp and ML
| from the 70s. That's not where functional programming is
| today. Both encourage a functional style, which is good.
| And maybe that's your definition of a "functional
| language". But I think that definition will get
| increasingly less useful over time.
| maleldil wrote:
| What is an example of a real functional language for you?
| azkalam wrote:
| Sure, Python has types as part of the syntax, but Python
| doesn't have types like Java, C#, etc. have types. They
| are not pervasive and the semantics are not locked down.
| CharlieDigital wrote:
| Exactly what I've observed in practice because most devs
| have no background in writing functional code and will
| complain when asked to do so.
|
| Passing or returning a function seems a foreign concept to
| many devs. They know how to use lambda expressions, but
| rarely write code that works this way.
|
| We adopted ErrorOr[0] and have a rule that core code must
| return ErrorOr<T>. Devs have struggled with this and
| continue to misunderstand how to use the result type.
|
| [0] https://github.com/amantinband/error-or
| physPop wrote:
| honestly this sounds like you've never really done it. FP
| is much better for ergonomics, developer productivity,
| correctness. All the important things when writing code.
| raincole wrote:
| Personally I love F#, but I feel the community is probably
| even smaller than OCaml...
| gixco wrote:
| I once got a temporary F# role _without any F# experience_
| simply by having 7 YoE with C# and the knowledge that F#
| exists.
|
| As much as I'd like to do more with it, the "just use F#"
| idea flaunted in this thread is a distant pipe dream for
| the vast majority of teams.
| actionfromafar wrote:
| It is but in practice it's very hard to find programmers for
| it.
| devjab wrote:
| Having worked with C# professionally for a decade, going
| through the changes with LINQ, async/await, Roslyn, and the
| rise of .NET Core, to .NET Core becoming .NET, I disagree. I
| certainly think that C# is a great tool and that it's the best
| it has ever been. It's also relies on very implicit behaviour,
| it is build upon OOP design principles and a bunch of
| "needless" abstraction. Things I personally have come to view
| as anti-patterns over the years. This isn't because I
| specifically dislike C#, you could find me saying something
| similar about Java.
|
| I suspect that the hidden indirection and runtime magic, may be
| part of why you love the language. In my experience, however,
| it leads to poor observability, opaque control flow, and
| difficult debugging sessions in every organisation and company
| I've ever worked for. It's fair to argue that this is because
| the people working with C# are bad at software engineering.
| Similar to how Uncle Bob will always be correct when he calls
| teams out for getting his principles wrong. To me that means
| the language itself has a poor design fit for software
| development in 2025. Which is probably why we see more and more
| Go adoption, due to its explicit philosophies. Though to be
| fair, Python seems to be "winning" as far as adoption goes in
| the cross platform GC language space. Having worked with
| Django-Ninja I can certainly see why. It's so productive, and
| with stuff like Pyrefly, UV and Ruff it's very easy to make it
| a YAGNI experience with decent type control.
|
| I am happy you enjoy C# though, and it's great to see that it
| is evolving. If they did more to enhance the developer
| experience so that people were less inclined to do bad
| engineering on a thursday afternoon after a long day of useless
| meetings. Then I would probably agree with you. I'm not sure
| any of the changes going toward .NET 10 are going in that
| direction though.
| OtomotO wrote:
| I am paid to work in Java and C# among Go, Rust, Kotlin,
| Scala and I wholeheartedly agree.
|
| I hate the implicitness of Spring Boot, Quarkus etc. as much
| as the one in C# projects.
|
| All these magic annotations that save you a few lines of code
| until they don't, because you get runtime errors due to
| incompatible annotations.
|
| And then it takes digging through pages of docs or even
| reporting bugs on repos instead of just fixing a few explicit
| lines.
|
| Explicitness and Verbosity are orthogonal concepts mostly!
| npodbielski wrote:
| What are those magic annotations you are talking about?
| Attributes? Not much of those are left in modern .net.
| debugnik wrote:
| Attributes and reflection are still used in C# for source
| generators, JSON serialization, ASP.NET routing,
| dependency injection... The amount of code that can fail
| at runtime because of reflection has probably increased
| in modern C#. (Not from C# source generators of course,
| but those only made interop even worse for F#-ers).
| mrsmrtss wrote:
| Don't we have automated tests for catching this kind of
| things or is everyone only YOLOing in nowadays?
| Serialization, routing, etc can fail at runtime
| regardless of using or not using attributes or
| reflection.
| osigurdson wrote:
| Ease of comprehension is more important than tests for
| preventing bugs. A highly testable DI nightmare will have
| more bugs than a simple system that people can understand
| just by looking at it.
| mrsmrtss wrote:
| I haven't experienced a DI 'nightmare' myself yet, but
| then again, we have integration tests to cover for that.
| CharlieDigital wrote:
| Try Nest.js and you'll know true DI "nightmares".
| thatnerdyguy wrote:
| If the argument is that most developers can't understand
| what a DI system does, I don't know if I buy that. Or is
| the argument it's hard to track down dependencies?
| Because if that's the case the idiomatic c# has the
| dependencies declared right in the ctor.
| LinXitoW wrote:
| But the "simple" system will be full of repetition and
| boilerplate, meaning the same bugs are scattered around
| the code base, and obscured by masses of boilerplate.
|
| Isn't a GC also a Magic? Or anything above assembly?
| While I also understand the reluctance to use too much
| magic, in my experience, it's not the magic, it's how
| well the magic is tested and developed.
|
| I used to work with Play framework, a web framework built
| around Akka, an async bundle of libraries. Because it
| wasn't too popular, only the most common issues were well
| documented. I thought I hated magic.
|
| Then, I started using Spring Boot, and I loved magic.
| Spring has so much documentation that you can also become
| the magician, if you need to.
| jakewins wrote:
| Aye, was involved in some really messed up outages from
| New Relics agent libraries generating bogus byte code at
| runtime, absolute nightmare for the teams trying to debug
| it because none of the code causing the crashing existed
| anywhere you could easily inspect it. Replaced opaque
| magic from new relic with simpler OTEL, no more outages
| CharlieDigital wrote:
| That's likely the old emit approach. Newer source gen
| will actually generate source that is included in the
| compilation.
| CharlieDigital wrote:
| I disagree on this.
|
| I am at a (YC, series C) startup that just recently made
| the switch from TS backend on Nest.js to C# .NET Web
| API[0]. It's been a progression from Express -> Nest.js ->
| C#.
|
| What we find is that having attributes in both Nest.js
| (decorators) and C# allows one part of the team to move
| faster and another smaller part of the team to isolate
| complexity.
|
| The indirection and abstraction are explicit decisions to
| reduce verbosity for 90% of the team for 90% of the use
| cases because otherwise, there's a lot of repetitive
| boilerplate.
|
| The use of attributes, reflection, and source generation
| make the code more "templatized" (true both in our Nest.js
| codebase as well as the new C# codebase) so that 90% of the
| devs simply need to "follow the pattern" and 10% of the
| devs can focus on more complex logic backing those
| attributes and decorators.
|
| Having the option to dip into source generation, for
| example, is really powerful in allowing the team to reduce
| boilerplate.
|
| [0] We are hiring, BTW! Seeking experienced C# engineers;
| very, very competitive comp and all greenfield work with
| modern C# in a mixed Linux and macOS environment.
| badhombres wrote:
| The trade offs are though that patterns and behind the
| scenes source code generation is another layer that the
| devs who have to follow need to deal with when debugging
| and understanding why something isn't working. They
| either spend more time understanding the bespoke things
| or are bottle necked relying on a team or person to help
| them get through those moments. It's a trade off and one
| that has bit me and others before
| CharlieDigital wrote:
| Would you rather a team move faster and be more
| productive or be a purist and disallow abstractions to
| avoid some potential runtime tracing challenges which can
| be mitigated with good use of OTEL and logging? I don't
| know about you, but I'm going to bias towards
| productivity and use integration tests + observability to
| safeguard code.
| hu3 wrote:
| How much faster are we talking? Because you'd have to
| account for the time lost debugging annotations.
| CharlieDigital wrote:
| What are you working on that you're debugging annotations
| everyday? I'd say you've made a big mistake if you're
| doing that/you didn't read the docs and don't understand
| how to use the attribute.
|
| (Of course you are also free to write C# without any of
| the built in frameworks and write purely explicit
| handling and routing)
|
| On the other hand, we write CRUD every day so anything
| that saves repetition with CRUD is a gain.
| OtomotO wrote:
| I don't debug them every day, but when I do, it takes
| days for a nasty bug to be worked out.
|
| Yes, they make CRUD stuff very easy and convenient.
| badhombres wrote:
| Disallow bespoke abstractions and use the industry
| standard ones instead. People who make abstractions
| inflate how productive they're making everyone else. Your
| user base is much smaller than popular libs, so your docs
| and abstractions are not as battle tested and easy to use
| as much as you think.
| CharlieDigital wrote:
| This is raw OpenFGA code: await
| client.Write( new ClientWriteRequest(
| [ // Alice is an admin of form 123
| new() {
| Object = "form:124", Relation =
| "editor", User = "user:avery",
| }, ] ) );
| var checkResponse = await client.Check( new
| ClientCheckRequest { Object =
| "form:124", Relation = "editor",
| User = "user:avery", } );
| var checkResponse2 = await client.Check( new
| ClientCheckRequest { Object =
| "form:125", Relation = "editor",
| User = "user:avery", } );
|
| This is an abstraction we wrote on top of it:
| await Permissions .WithClient(client)
| .ToMutate() .Add<User, Form>("alice",
| "editor", "226") .Add<User, Team>("alice",
| "member", "motion") .SaveChangesAsync();
| var allAllowed = await Permissions
| .WithClient(client) .ToValidate()
| .Can<User, Form>("alice", "edit", "226")
| .Has<User, Team>("alice", "member", "motion")
| .ValidateAllAsync();
|
| You would make the case that the former is better than
| the latter?
| badhombres wrote:
| In the first example, I have to learn and understand
| OpenFGA, in the second example I have to learn and
| understand OpenFGA and your abstractions.
| mrsmrtss wrote:
| Well the point of using abstractions is that you don't
| need to know the things that it is abstracting. I think
| the abstraction here is self explaining what it does and
| you can certainly understand and use it without needing
| to understand all the specifics behind it.
| CharlieDigital wrote:
| More importantly: it prevents "usr:alice_123" instead of
| "user:alice_123" by using the type constraint to generate
| the prefix for the identifier.
| speed_spread wrote:
| That's the deal with all metaprogramming.
| germandiago wrote:
| I am not talking about C# specifically but also and I
| agree.
|
| Implicit and magic looks nice at first but sometimes it
| can be annoying. I remember the first time I tried Ruby
| On Rails and I was looking for a piece of config.
|
| Yes, "convention over configuration". Namely, ungreppsble
| and magic.
|
| This kind of stuff must be used with a lot of care.
|
| I usually favor explicit and, for config, plain data
| (usually toml).
|
| This can be extended to hidden or non-obvious allocations
| and other stuff (when I work with C++).
|
| It is better to know what is going on when you need to
| and burying it in a couole of layers can make things
| unnecessarily difficult.
| belmont_sup wrote:
| It has been worth the abstraction in my organization with
| many teams. Thinking 1000+ engineers, at minimum. It
| helps to abstract as necessary for new teammates that
| want to simply add a new endpoint yet follow all the
| legal, security, and data enforcement rules.
|
| Better than no magic abstractions imo. In our large
| monorepo, LSP feedback can often be so slow that I can't
| even rely on it to be productive. I just intuit and
| pattern match, and these magical abstractions do help. If
| I get stuck, then I'll wade into the docs and code
| myself, and then ask the owning team if I need more help.
| bcrosby95 wrote:
| People were so afraid of macros they ended up with
| something even worse.
|
| At least with macros I don't need to consider the whole
| of the codebase and every library when determining what
| is happening. Instead I can just... Go to the macro.
| CharlieDigital wrote:
| C# source generators are...just macros?
| skydhash wrote:
| They are not. They are generators. Macros tends to be
| local and explicit as the other commenters have said.
| They are more like templates. Generators can be fairly
| involved and feels like a mini language, one that is not
| as observable as macros.
| CharlieDigital wrote:
| Isn't this just a string template? https://github.com/Cha
| rlieDigital/SKPromptGenerator/blob/mai...
|
| Maybe you're confusing `System.Reflection.Emit` and
| source generators? Source generators are just a source
| tree walker + string templates to write source files.
| com2kid wrote:
| > so that 90% of the devs simply need to "follow the
| pattern" and 10% of the devs can focus on more complex
| logic backing those attributes and decorators.
|
| Works well until the 10% that understand the behind the
| scenes leave and you are left with a bunch of developers
| copy and pasting magic patterns that they don't
| understand.
|
| I love express because things are very explicit. This is
| the JSON schema being added to this route. This route is
| taking in JSON parameters. This is the function that
| handles this POST request endpoint.
|
| I joined a team using Spring Boot and the staff engineer
| there couldn't tell me if each request was handled by its
| own thread or not, he couldn't tell me what variables
| were shared across requests vs what was uniquely
| instantiated per request. Absolute insanity, not
| understanding the very basics of one's own runtime.
|
| Meanwhile in Express, the threading model is stupid
| simple (there isn't one) and what is shared between
| requests is obvious (everything declared in an outer
| scope).
| pjmlp wrote:
| As polyglot developer, I also disagree.
|
| If I wanted explicitness for every little detail I would
| keep writing in Assembly like in the Z80, 80x86, 68000
| days.
|
| Unfortunately we never got Lisp or Smalltalk mainstream, so
| we got to metaprogramming with what is available, and it is
| quite powerful when taken advantage of.
|
| Some people avoid wizard jobs, others avoid jobs where
| magic is looked down upon.
|
| I would also add that in the age of LLM and AI generated
| applications, discussing programming languages explicitness
| is kind of irrelevant.
| skydhash wrote:
| Explicitness is different than verbosity. Often
| annotations and the like are abused to create a lot of
| accidental complexity just to not write a few keywords.
| In almost every lisp project you'll find that macros are
| not intended for reducing verbosity, they are there to
| define common patterns. You can have something like
| (define-route METHOD PATH BODY)
|
| You can then easily expect the generated code. But in
| Java and others, you'll have something like
| @GET(path=PATH)
|
| And there's a whole system hidden behind this, that you
| have to carefully understand as every annotation
| implementation is different.
| germandiago wrote:
| This is the trade-off with macros and annotation/code-
| generation systems.
|
| I tend to do obvious things whwn I use this kind of
| tools. In fact, I try to avoid macros.
|
| Even if configurability is not important, I favor
| sinplification over reuse. In case I need reuse, I go for
| higher order functions if I can. Macro is the last
| bullet.
|
| In some circumstances like Json or serialization maybe
| they can be slightly abused to mark fields and such. But
| whole code generation can take it so far and magic that
| it is not worth in many circumstances IMHO, thiugh every
| tool has its use cases, even macros and annotations.
| skydhash wrote:
| IMO, macros and such should be to improve coding UX. But
| using it for abstractions and the like is very much not
| worth it. So something like JSX (or the loop system in
| Common Lisp) is good. But using it for DI is often a code
| smell for me.
| pjmlp wrote:
| Only if those Lisp projects are done by newbies, Clojure
| is quite known for having a community that takes that
| approach to macros, versus everyone else on Lisp since
| its early days.
|
| Using macros for DSLs has been common for decades, and is
| how frameworks like CLOS were initially implemented.
| CharlieDigital wrote:
| You are missing the forest for the trees.
|
| C# has increasingly become more terse (e.g. switch
| expressions, collection initializers, object initializers,
| etc) and, IMO, is a good balance between OOP and
| functional[0].
|
| Functions are first class objects in C# and teams can write
| functional style C# if they want. But I suspect that this
| doesn't scale well in human terms as we've encountered a LOT
| of trouble trying to get TypeScript devs to adopt more
| functional programming techniques. We've found that many devs
| _like to use_ functional code (e.g. Linq, `.filter()`,
| `.map()`), but dislike _writing_ functional code because most
| devs are not wired this way and do not have any training in
| how to write functional code and understanding monads. Asking
| these devs to use a monad has been like asking kids to eat
| their carrots.
|
| Across much of our backend TS codebase, there are very, very
| few cases where developers accept a function as input or
| return a function as output (almost all of it written by 1
| dev out of a team of ~20). > ...it is build
| upon OOP design principles and a bunch of "needless"
| abstraction
|
| Having been working with Nest.js for a while, it's clear to
| me that most of these abstractions are not "needless" but
| actually "necessary" to manage complexity of apps beyond a
| certain scale and the reasons are less technical and more
| about scaling teams and communicating concepts.
|
| Anyone that looks at Nest.js will immediately see the
| similarities to Spring Boot or .NET Web APIs because it fills
| the same niche. Whether you call a `*Factory` a "factory" or
| something else, the core concept of what the thing does still
| exists whether you're writing C#, Java, Go, or JS: you need a
| thing that creates instances of things.
|
| You can say "I never use a factory in Go", but if you have a
| function that creates other things or other functions, that's
| a factory...you're just not using the nomenclature. Good for
| you? Or maybe you misunderstand why there is standard
| nomenclature of common patterns in the first place and are
| associating these patterns with OOP when in reality, they are
| almost universal and are rather _human language abstractions_
| for programming patterns.
|
| [0] https://medium.com/itnext/getting-functional-
| with-c-6c74bf27...
| devjab wrote:
| I notice that none of the examples in your blog entry on
| functional C# deals with error handling. I know that is not
| the point of your article, but that is actually one of my
| key issues with C# and its reliance on implicit, because
| like so many other parts of C# you'd probably hand it over
| to an exeception handler. I'd much rather prefer you to
| deal with it explicitly right where it happens, and I would
| prefer if you were actually forced to do it for examples
| like yours. This is because implicit error handling is
| hard. I have no doubt you do it well, but it is frankly
| rare to meet a C# developer who has as much of an
| understanding on the language that you clearly have.
|
| I think this is an excellent blog post by the way. My
| issues with C# (and this applies to a lot of other GC
| languages) is that most developers would learn a lot from
| your article. Because none of it is an intuitive part of
| the language philosophy.
|
| I don't think you should never use OOP or abstractions. I
| don't think there is a golden rule for when you should use
| either. I do think you need to understand why you are doing
| it though, and C# sort of makes people go to abstractions
| first, not last in my experience. I don't think these
| changes to the GC is going to help people who write C#
| without understanding C#, which is frankly most C#
| developers around here. Because Go is opinionated and
| explicit it's simply an issue I have to deal with less in
| Go teams. It's not an issue I have to deal with less in
| Python teams, but then, everyone who loves Python knows it
| sucks.
| CharlieDigital wrote:
| My team just recently made the switch from a TS backend
| to a C# backend for net new work. When we made this
| switch, we also introduced `ErrorOr`[0] which is a
| monadic result type.
|
| I would not have imagined this to be controversial nor
| difficult, but it turns out that developers really prefer
| and understand exceptions. That's because for a backend
| CRUD API, it's really easy to just throw and catch at a
| global HTTP pipeline exception filter and for 95% of
| cases, this is OK and good enough; you're not really
| going to be able to handle it nor is it worth it to to
| handle it.
|
| We'll stick with ErrorOr, but developers aren't using it
| as monad and simply unwrapping the value and the error
| because, as it turns out, most devs just have a
| preference/greater familiarity with imperative try-catch
| handling of errors and practically, in an HTTP backend,
| there's nothing wrong in most cases with just having a
| global exception filter do the heavy lifting unless the
| code path has a clear recovery path. >
| I don't think you should never use OOP or abstractions. I
| don't think there is a golden rule for when you should
| use either.
|
| I do think there _is_ a "silver rule": OOP when you need
| _structural scaffolding_ , functional when you have
| "small contracts" over big ones. An interface or abstract
| class is a "big contract" that means to understand how to
| use it, you often have to understand a larger surface
| area. A function signature is still a contract, but a
| micro-contract.
|
| Depending on what you're building, having structural
| scaffolding and "big contracts" makes more sense than
| having lots of micro-contracts (functions). Case in
| point: REST web APIs make a lot more sense with
| structural scaffolding. If you write it without
| structural scaffolding of OOP, it ends up with a lot of
| repetition and _even worse_ opaqueness with functions
| wrapping other functions.
|
| The silver rule for OOP vs FP for me: OOP for structural
| templating for otherwise repetitive code and "big
| contracts"; FP for "small contracts" and algorithmically
| complex code. I encourage devs on the team to write both
| styles depending on what they are building and the nature
| of complexity in their code. I think this is also why TS
| and C# are a sweet spot, IMO, because they straddle both
| OOP and have just enough FP when needed.
|
| [0] https://github.com/amantinband/error-or
| fabian2k wrote:
| You introduced a pattern that is simply different than
| the usual in C#. It's also not clearly better, it's
| different. In languages designed for result types like
| this the ergonomics of such a type are usually better.
|
| All the libraries you use and all methods from the
| standard library use exceptions. So you have to deal with
| exceptions in any case.
|
| There's also a million or so libraries that implement
| types like this. There is no standard, so no
| interoperability. And people have to learn the
| pecularities of the chosen library.
|
| I like result types like this, but I'd never try to
| introduce them in C# (unless at some point they get more
| language support).
| CharlieDigital wrote:
| These are developers that have never written C# before so
| there's no difference between whether it's language
| supported or not. It was in the core codebase on day 1
| when they onboarded so it may as well have been native.
|
| But what I takeaway from this is that Go's approach to
| error handling is "also not clearly better, it's
| different".
|
| Even if C# had core language support for result types,
| you would be surprised how many developers would struggle
| with it (that is my takeaway from this experience).
| fabian2k wrote:
| If you're not coming from a strongly typed functional
| language, it's still a pattern you're not used to. Which
| might be a bit of a roundabout way to say that I agree
| about your last part, developers without contact to that
| kind of language will struggle at first with a pattern
| like this.
|
| I know how to use this pattern, but the C# version still
| feels weird and cumbersome. Usually you combine this with
| pattern matching and other functional features and the
| whole thing makes it convenient in the end. That part is
| missing in C#. And I think it makes a different in
| understanding, as you would usually build ony our
| experience with pattern matching to understand how to
| handle this case of Result|Error.
| CharlieDigital wrote:
| > Usually you combine this with pattern matching and
| other functional features and the whole thing makes it
| convenient in the end. That part is missing in C#
|
| You mean like this? string foo =
| result.MatchFirst( value => value,
| firstError => firstError.Description);
|
| Or this? ErrorOr<string> foo = result
| .Then(val => val * 2) .Then(val => $"The
| result is {val}");
|
| Or this? ErrorOr<string> foo = await
| result .ThenDoAsync(val => Task.Delay(val))
| .ThenDo(val => Console.WriteLine($"Finsihed waiting {val}
| seconds.")) .ThenDoAsync(val =>
| Task.FromResult(val * 2)) .ThenDo(val =>
| $"The result is {val}");
|
| With pattern matching like this? var
| holidays = new DateTime[] {...}; var output = new
| Appointment( DayOfWeek.Friday,
| new DateTime(2021, 09, 10, 22, 15, 0), false
| ) switch { { SocialRate: true } => 5,
| { Day: DayOfWeek.Sunday } => 25, Appointment
| a when holidays.Contains(a.Time) => 25, {
| Day: DayOfWeek.Saturday } => 20, { Day:
| DayOfWeek.Friday, Time.Hour: > 12 } => 20, {
| Time.Hour: < 8 or >= 18 } => 15, _ => 10,
| };
|
| C# pattern matching is pretty damn good[0] (seems you are
| not aware?).
|
| [0] https://timdeschryver.dev/blog/pattern-matching-
| examples-in-...
| fabian2k wrote:
| None of your examples use native C# pattern matching. And
| without language support like e.g. discriminated unions
| you can't have exhaustive pattern matching in C#. So
| you'll have to silence the warnings about the missing
| default case or always add one, which is annoying.
| CharlieDigital wrote:
| I mean, it's not a stretch to see how you can use native
| pattern matching with ErrorOr result types.
| #!/usr/local/share/dotnet/dotnet run #:package
| ErrorOr@2.0.1 using ErrorOr; var
| computeRiskFactor = ErrorOr<decimal> () =>
| 0.5m; // Just an example var
| applyAdjustments = ErrorOr<decimal> (decimal
| baseRiskFactor) => baseRiskFactor + 0.1m; //
| Just an example var approvalDecision =
| computeRiskFactor() .Then(applyAdjustments)
| .Match( riskFactor => riskFactor switch {
| < 0.5m => "Approved", < 0.75m and >=
| 0.5m => "Approved with Conditions",
| >= 0.75m and < 0.9m => "Manual Review",
| _ => "Declined" }, errors
| => "Error computing risk factor" );
| Console.WriteLine($"Loan application:
| {approvalDecision}");
|
| (Fully contained program, BTW)
|
| Here's the OCaml version: let
| compute_risk_factor () = 0.5 let
| apply_adjustments base_risk_factor = base_risk_factor +.
| 0.1 let approval_decision = let
| risk_factor = compute_risk_factor () |> apply_adjustments
| in match risk_factor with | r when r
| < 0.5 -> "Approved" | r when r < 0.75 ->
| "Approved with Conditions" | r when r < 0.9 ->
| "Manual Review" | _ -> "Declined"
| let () = print_endline approval_decision
|
| Still not functional enough?...Or you just don't like C#?
| No point moving goal posts.
| ed_elliott_asc wrote:
| What sort of issues do you get debugging?
|
| My experience of .NET even from version 1 is that it has the
| best debugging experience of any modern language, from the
| visual studio debugger to sos.dll debugging crash dumps.
| Nullabillity wrote:
| > I really can't think of anything that comes close in terms of
| [...] developer experience.
|
| Of all the languages that I have to touch professionally, C#
| feels by far the most opaque and unusable.
|
| Documentation tends to be somewhere between nonexistant and
| useless, and MSDN's navigation feels like it was designed by a
| sadist. (My gold standard would be Rustdoc or Scala 2.13 era
| Scaladoc, but even Javadoc has been.. _fine_ for basically
| forever.) For third-party libraries it tends to be even more
| dire and inconsistent.
|
| The Roslyn language server crashes _all the time_ , and when it
| does work.. it doesn't do anything useful? Like cross-project
| "go-to-definition" takes me to either a list of members or a
| _decompiled_ listing of source code, even when I have the
| actual source code right there! (I know there 's this thing
| called "SourceLink" which is.. supposed to solve this? I think?
| But I've never seen it actually use it in practice.)
|
| Even finding where something comes from is ~impossible without
| the language server, because `using` statements don't mention..
| what they're even importing. (Assuming that you have them at
| all. Because this is also the company that thought project-
| scoped _imports_ were a good idea!)
|
| And then there's the dependency injection, where I guess
| someone thought it would be cute if every library just had an
| opaque extension method on the god object, that didn't tell you
| anything about what it actually did. So good luck finding where
| the actual implementation of anything is.
| Guillaume86 wrote:
| Try "go to implementation" in place of go to definition.
| Rohansi wrote:
| I almost exclusively work in C# and have never experienced
| the Roslyn crashes you mentioned. I am using either Rider or
| Visual Studio though.
|
| > Like cross-project "go-to-definition" takes me to either a
| list of members or a decompiled listing of source code, even
| when I have the actual source code right there!
|
| If these are projects you have in the same solution then it
| should never do this. I would only expect this to happen if
| either symbol files or source files are missing.
| CharlieDigital wrote:
| I use VS Code on macOS for all of my C# code over the last
| 5 years and also never experienced Roslyn crashes.
| munchler wrote:
| Except for F#, which also gets all the .NET10 cross-platform GC
| improvements for free and is a better programming language than
| C#.
| physPop wrote:
| +1 F# is criminally under-used
| ComputerGuru wrote:
| C# will be a force to reckon with if/when discriminated unions
| finally land as a language feature.
| CharlieDigital wrote:
| I think people who last looked at C# 10 years ago or haven't
| adapted to new language features seriously don't know how
| good C# is these days.
|
| Switch expressions with pattern matching are absolutely
| killer[0] for its terseness.
|
| Also, it is possible to use OneOf[1] and Dunet[2] to get
| access to DU
|
| [0] https://timdeschryver.dev/blog/pattern-matching-examples-
| in-...
|
| [1] https://github.com/mcintyre321/OneOf
|
| [2] https://github.com/domn1995/dunet
| ComputerGuru wrote:
| I write C# and rust fulltime. Native discriminated unions
| (and their integration throughout the ecosystem) are often
| the deciding factor when choosing rust over C#.
| CharlieDigital wrote:
| Very hard to imagine teams cross shopping C# and Rust and
| DU's being the deciding factor. The tool chains,
| workflows, and use cases are just so different, IMO. What
| heuristics were your team using to decide between the
| two?
| azkalam wrote:
| This surprises me.
|
| If you want the .NET ecosystem and GC conveniences, there
| is already F#. If you want no GC and RAII-style control,
| then you would already pick Rust.
| vips7L wrote:
| For me, it will be if they ever get checked errors of some
| sort. I don't want to use a language with unchecked
| exceptions flying about everywhere. This isn't saying I want
| checked exceptions either, but I think if they get proper
| unions and then have some sort of error union type it would
| go a long way.
| CharlieDigital wrote:
| You can get an error union now:
| https://github.com/amantinband/error-or
| vips7L wrote:
| The issue is the ecosystem and standard library. They
| still will be throwing unchecked exceptions everywhere
| buzzin__ wrote:
| Or you can use the "C# without the line noise", which goes
| under the name of F#.
| bonesss wrote:
| Yeah, but if you use F# that then you'll have all the
| features C# has been working on for years, only in complete
| and mature versions, and also an opinionated language
| encouraging similar styles between teams instead of wild
| dialects of kinda-sorta immutablity and kinda-sorta DU's, and
| everything in between, requiring constant vigilance and
| training... ;)
|
| I'm a fan of all three languages, but C# spent the first
| years relearning why Visual Basic was very productive and the
| last many years learning why OCaml was chosen to model F#
| after. It's landed in a place where I can make beautiful code
| the way I need to, but the mature libraries I've crafted to
| make it so simply aren't recreate-able by most .Net devs, and
| the level of micro-managing it takes to maintain across
| groups is a line-by-line slog against orthodoxy and seeming
| 'shortcuts', draining the productivity those high level
| guarantees should provide. And then there's the impact of EF
| combined with sloppy Linq which makes every junior/consultant
| LOC a potentially ticking time bomb without more line-by-
| line, function-by-function slog.
|
| Compiler guarantees mean a lot.
| bob1029 wrote:
| > I really can't think of anything that comes close in terms of
| performance, features, ecosystem, developer experience.
|
| This is also why I prefer to use Unity over all other engines
| by an incredible margin. The productivity gains of using a
| mature C# scripting ecosystem in a tight editor loop are
| shocking compared to the competition. Other engines do offer C#
| support, but if I had to make something profitable to save my
| own standard of living the choice is obvious.
|
| There's only two vendors that offer built-in SIMD accelerated
| linear math libraries capable of generating projection matrices
| out of the box. One is Microsoft and the other is Apple. The
| benefits of having stuff like this baked into your ecosystem
| are very hard to overstate. The amount of time you could waste
| looking for _and troubleshooting_ primitives like this can
| easily kill an otherwise perfect vision.
| 5ersi wrote:
| Java?
|
| Is supported on more platforms, has more developers, more jobs,
| more OSS projects, is more widely used (Tiobe 2024).
| Performance was historically better, but c# caught up.
| mrsmrtss wrote:
| Reified generics, value types, LINQ are just a few things
| that you would miss when going to Java. Also Java and .NET
| are both big, that's not a real argument here. Not that I
| would trust Tiobe index too much, but as of 2025 September C#
| is right behind Java at 5th place.
| throw45678943 wrote:
| My experience was that .NET programs were typically more
| tunable for greater perf than Java for many years now even if
| it didn't come free out of the box which generally is what
| matters with performance. The ability to optimise further
| what needs to be optimised means that generally you are
| faster for your business domain than the alternative - with
| Java code it generally is harder and/or less ergonomic to do
| this.
|
| For example just having value types and reified generics as a
| combination meant you could write generic code against value
| types which usually meant for hot algorithmic loops or
| certain data structures a big win w.r.t memory and CPU
| consumption. For example for a collection type critical to an
| app I wrote many years ago the use of value types would
| almost half the memory footprint compared to the best Java
| one I could find, and was somewhat faster with less cache
| misses. The Java alternative wasn't an amateur one either but
| they couldn't get the perf out of it even with significant
| effort.
|
| It also last time I checked doesn't have a value decimal type
| for financial math which IMO can be a significant performance
| loss for financial/money based systems. Anything with math,
| and lots of processing/data structures for example I would
| find .NET significantly faster after doing the optimisation
| work. If I had to choose the 2 targets these days I would
| find .NET in general an easier target w.r.t performance. Of
| course perf isn't everything depending on the domain.
| dgllghr wrote:
| I am considering dotnet Maui for a project. On the one hand, I am
| worried about committing to the Microsoft ecosystem where
| projects like Maui have been killed in the past and Microsoft has
| a lot of control. Also XML... On the other hand, I've been seeing
| so many impressive technical things about dotnet itself. Has
| anyone here used Maui and wants to comment on their experience?
| Dusseldorf wrote:
| Speaking as an experienced desktop .NET Dev, we've avoided it
| due to years of instability and no real confidence it'll get
| fully adopted. We've stuck with WPF, which is certainly a bit
| warty, but ultimately fine. If starting fresh at this point I'd
| give a real look at Avalonia, seems like they've got their head
| on their shoulders and are in it for the long haul.
| mrsmrtss wrote:
| Would also recommend Avalonia. It's truly cross-platform
| (supports also Linux) unlike MAUI.
| eterm wrote:
| If you're windows based, I'd unironically consider winforms,
| it's been re-added to dotnet in windows, and is one of the
| easiest and best ways to make simple GUI applications.
|
| Sadly it's not cross-platform, which is a benefit of MAUI.
| maxlb wrote:
| I've been a C# developer my entire career and spent a few years
| building apps with Xamarin/Uno. At my current company, we
| evaluated MAUI and Flutter for our mobile app rewrite (1M+
| monthly active users).
|
| We first built a proof of concept with 15 basic tasks to
| implement in both MAUI and Flutter. Things like authentication,
| navigation, API calls, localization, lists, map, etc. In MAUI,
| everything felt heavier than it should've been. Tooling issues,
| overkill patterns, outdated docs, and a lot of small
| frustrations that added up. In Flutter, we got the same
| features done much faster and everything just worked. The whole
| experience was just nicer. The documentation, the community,
| the developer experience... everything is better.
|
| I love C#, that's what we use for our backend, but for mobile
| developement Flutter was the clear winner. We launched the new
| app a year ago and couldn't be happier with our decision.
| ivm wrote:
| Aside from using an esoteric language and being a Google
| product with a risk of shutting down just because, Flutter's
| game-like UI rendering on a canvas was confirmed to be quite
| a questionable approach with the whole Liquid Glass
| transition. If anything, React Native is a more reliable
| choice: endless supply of React devs and native UI binding
| similar to MAUI.
|
| I'd say Uno Platform[0] is a better alternative to Flutter
| for those who do not care much about the native look: it
| replicates WinUI API on iOS, Mac, Android, and Linux, while
| also providing access to the whole mature .NET ecosystem -
| something Flutter can't match for being so new and niche.
|
| [0]: https://platform.uno/
| fourside wrote:
| > Flutter's game-like UI rendering on a canvas was
| confirmed to be quite a questionable approach with the
| whole Liquid Glass transition.
|
| Im not a flutter dev and Im very interested to hear how it
| doesn't play well liquid glass.
| ivm wrote:
| It simply can't use it because it does not use native
| UIs, but instead mimics them with its own rendering
| engine. This approach worked to some extent during the
| flat minimalist era, but now that Apple has added so many
| new animations and transitions, reproducing them all has
| become close to impossible.
|
| At best, Flutter can implement some shaders for the
| glass'y look of the controls, but something as basic as
| the Liquid Glass tab bar would require a huge effort to
| replicate it inside Flutter, while in MAUI and RN it's an
| automatic update.
| realusername wrote:
| Not a single user cares about "native ui", it's only a
| debate among developers. Take the top 20 apps people are
| using, all of them use their own design system which
| isn't native.
|
| Flutter will always have multiple advantages against
| React Native (and even Native toolkits themselves) in
| terms of upgradability, you can do 6 months of updates
| with only 30mins of work and make sure it 100% works
| everywhere.
|
| The quality of the testing toolkit is also something
| which is still unmatched elsewhere and makes a big
| difference on the app reliability.
| ivm wrote:
| Classic HN comment with unapologetic statements. If
| Flutter were that good, it wouldn't have flatlined so
| fast after the initial hype a few years ago. I tried it
| last year, only to see rendering glitches in the sample
| project.
| realusername wrote:
| Not everything is related to tech, in my company for
| example, they picked React Native because they have the
| ability to tap into the front-end job market (or they
| think they do), certainly not for it's intrisic
| qualities.
|
| Personally I've done a 50k+ line project in Flutter and I
| didn't hit any of these. There's been a few issues for
| sure but nowhere near what I experienced with React
| Native (and don't start me on native itself)
| zigzag312 wrote:
| Rendering glitches may be due to completely new,
| lightweight rendering engine made from scratch, that has
| replaced Skia. Shoudn't be a problem when it matures a
| bit.
| fyzix wrote:
| 28% of new iOS apps are made with flutter and it's the #1
| cross platform framework on stack overflow 2024 survey so
| I highly doubt it has flatlined.
|
| https://flutter.dev/multi-platform/ios
|
| https://survey.stackoverflow.co/2024/technology#1-other-
| fram...
| ivm wrote:
| All those stats look great on paper, but a few months ago
| I checked job postings for different mobile frameworks,
| and Flutter listings were 2-3 times fewer than RN. Go on
| Indeed and see for yourself.
|
| For a "28% of new iOS apps", the Flutter subreddit is a
| ghost town with regular "is it dying? should I pick RN?"
| posts. I just don't buy the numbers because I'm myself in
| a rather stagnant cross-platform ecosystem, so I know
| this vibe well.
|
| If I ever leave .NET, no way I'd move to something like
| Flutter. Even Kotlin Multiplatform is more promising
| concept-wise. LLMs are changing cross-platform
| development and Flutter's strong sides are not that
| important anymore, while its weak sides are critical.
| nekitamo wrote:
| I would personally prefer Avalonia (https://avaloniaui.net/)
| over MAUI.
| ivm wrote:
| I highly recommend using MvvmCross with native UIs instead of
| MAUI: you get your model and view model 100% cross-platform,
| and then build native UIs twice (with UIKit and Android SDK),
| binding them to the shared VM. It also works with AppKit and
| WinUI.
|
| In the past it was rather painful for a solo dev to do them
| twice, but now Claude Code one-shots them. I just do the iOS
| version and tell it to repeat it on Android - in many cases 80%
| is done instantly.
|
| Just in case, I have an app with half a million installs on
| both stores that has been running perfectly since 2018 using
| this ".NET with native UIs" approach.
| hahn-kev wrote:
| I would say it really depends on your target. If you want only
| mobile, then there's different option's (see other comments).
| But if you want only desktop then Avilonia is good. However if
| you want both (like my team) then we did end up going for MAUI.
| However we use MAUI Blazor as we also want to run on a server.
| We're finding iOS to be difficult to target but I don't think
| that has anything to do with MAUI.
| osigurdson wrote:
| I don't really understand why Microsoft didn't do a Tauri like
| thing for C# devs instead of this Maui stuff. It would be a
| tiny project in comparison and then isn't completely going
| against the grain like Maui is. If you want a write once / run
| in more places compromise, the browser already does that very
| well.
| noveltyaccount wrote:
| Maui Blazor Hybrid has a cool model where the HTML UI binds
| to native code (not WASM) for mobile and desktop. That is the
| closest you can get to Tauri-like. If you want to run that
| same app in a browser, then it'll use Blazor with WASM.
| bigstrat2003 wrote:
| Because web UI for a desktop app sucks compared to actual
| native UI. As a user, any time that I see an app uses
| Electron, Tauri or any of that ilk, I immediately look for an
| alternative because the user experience will be awful.
| osigurdson wrote:
| Worse than WPF?
| jabart wrote:
| MAUI Blazor Hybrid is great if you won't want to learn XAML.
| Apple killed Silverlight, Microsoft kept it running for ~20
| years. If you stayed close to what Xamarin was the migration to
| MAUI isn't bad from what I've seen.
| MaXtreeM wrote:
| I have used MAUI at my previous job to build 3 different apps,
| used only on mobile (Android and iOS). I don't know why many
| people dislike XAML, to me it felt natural to use it for UI, I
| researched flutter and liked MAUI/XAML more. Although the
| development loop felt smoother with flutter. What I didn't like
| was the constant bugs, with each new version that I was eager
| to update to fix current issues, something new appeared. After
| spending countless hours searching through the projects GitHub,
| I am under the impression that there aren't much resources
| dedicated to MAUI development from Microsoft, the project is
| carried forward by few employees and volunteers. If I would
| start another project I would seriously look into Avalonia. But
| I always was a backend guy so now at my current job I do server
| backend development in C# and couldn't be happier.
| CharlieDigital wrote:
| I do think server/backend is C#'s sweetspot because EF Core
| is soooo good.
|
| But it's curious that it's used widely with game engines
| (Unity, Godot), but has a pretty weak and fractured UI
| landscape.
| holbrad wrote:
| Certainly wouldn't recommend MAUI. Even using it as a simple
| shell with for Blazor Hybrid was noticeably harder than WPF.
|
| If Microsoft aren't using it themselves in any real capacity,
| then it's not good bet IMO.
| 999900000999 wrote:
| I wonder if this makes .net competitive for high frequency
| trading...
| bob1029 wrote:
| It's been competitive for a long time now.
|
| https://medium.com/@ocoanet/improving-net-disruptor-performa...
| klysm wrote:
| Why would you ever pick a language like this for HFT? It seems
| like a nonstarter for me but I guess Java is out there in use
| clanky wrote:
| There are plenty of domains where the competition is not one
| of pure latency (where FPGAs and custom hardware have even
| taken over from C++). In these domains managed languages can
| be sufficient to get to "fast enough" and the faster
| iteration speed and other comforts they provide can give an
| edge over native languages.
| CharlieDigital wrote:
| Benchmark Games[0] shows C# just behind C/C++ and Rust across a
| variety of benchmark types. C# has good facilities for dipping
| into unmanaged code and utilizing hardware intrinsics so you'd
| have to tap into that and bypass managed code in many cases to
| achieve higher performance.
|
| [0] https://benchmarksgame-
| team.pages.debian.net/benchmarksgame/...
| honkostani wrote:
| I always found that Jit and GC are a marriage destined to come
| together, but never found one another entirely. Jit marks the
| hotloop in code- and thus can tell the GC in detail what a
| generation really is and how long a generation lifetime really
| lasts.
|
| It can reveal secret cull conditions for long generational
| objects. If that side-branch is hit in the hot-loop, all longterm
| objects of that generation, are going to get culled, in a single
| stroke... so bundle them and keep them bundled. And now they
| started using it, to at least detect objects that do not escape
| lambdas. So its all stack, no more GC involved at all. Its almost
| at the static allocation thing we do for games. If the model
| proofs that every hotloop 5 objects are allocated and life until
| a external event occurs- static allocation and its done.
|
| Great start. But you could do so much more than that with this.
| If you write a custom JIT whose goal is not just to detect and
| bytecompile hotloops, but to build a complete multi-lifetime
| model of object generation.
| whizzter wrote:
| Any interpreter could theoretically do those "marking" things,
| also JIT's do far more than just "bytecompile" hot loops, _all_
| cooperative modern GC's are enabled by JIT semantics for things
| like read and/or write barriers (this helps a GC keep track of
| objects that keep getting "touched" whilst the GC can work in
| parallel).
|
| Outside of the mentioned, things like detecting finegrained
| lifetimes is very very hard and the mentioned escape analysis
| is an optimization that needs to be capped to avoid the halting
| problem. (1)
|
| A fairly deep covererage of GC behaviours can found in Bacon's
| "Unified Theory of Garbage Collection" where the author
| theoretically connect previous works on tracing collectors and
| reference-counting systems and show that the optimized
| variations often existing in a design-space between them. (2)
|
| 1: https://en.wikipedia.org/wiki/Halting_problem
|
| 2:
| https://web.eecs.umich.edu/~weimerw/2008-415/reading/bacon-g...
| andyjohnson0 wrote:
| Comprehensive and (I thought) interesting article in perf
| improvements in .net 10:
|
| Performance Improvements in .NET 10
|
| https://devblogs.microsoft.com/dotnet/performance-improvemen...
| OtherShrezzing wrote:
| This is a great article, as soon as you're beyond the
| introductory 5 paragraphs on the minutiae of the opening song
| of Disney's Frozen.
| torginus wrote:
| Do these updates mean that the JIT can finally optimize LINQ away
| into simple loops?
___________________________________________________________________
(page generated 2025-10-05 23:00 UTC)