4 ms·
I've been meaning to write a longer essay on this for years, but I believe the reason for this observation is different cohorts. Imagine you are a C# programme
by munificent 2y ago
I've been meaning to write a longer essay on this for years, but I believe the reason for this observation is different cohorts.
Imagine you are a C# programmer just as C# 1.0 is released. C# is a fairly simple language at that time (and similar to other languages you already know), so you can get caught up on it fairly easily and quickly. A few years later, C# 2.0 comes out. It's got a handful of features, but not too much for you to absorb. Likewise C# 3.0, 4.0, etc. As long as you stay on the C# train, the rate of new features does not exceed the rate that you can learn them.
Years later, another person comes along and is new to C#, which is now at version 5.0. They are presented with a huge sprawling language and they have to learn nearly all of it at once to deal with codebases they are contributing to. It's a nightmare. They long for a language that's actually, you know simple.
So maybe they find some other newer language, Foo, which is at 1.0. It's small and they learn the whole thing. After a couple of years of happy productive use, they realize they would be a little more happy and productive if Foo had just one or two extra little features. They put in a request. The language team wants happy users so they are happy to oblige. The user is easily able to learn those new features. And maybe some other Foo users want other new things. 2.0 comes out, and they can keep up. They can stay on the train with 3.0, 4.0, etc.
They never explicitly asked for a complex language, but they have one and they're happy, because they've mastered the whole thing over a period of years. They've become part of the problem that bothered them so much years ago.
Fundamentally, the problem is that existing users experience a programming language as the delta between the latest version and the previous one. New users experience a programming language as the total sum of all of its features (perhaps minus features it has in common with other languages you already know). If you assume users can absorb information at a certain fixed rate, it means those two cohorts have very different needs and different experiences.
I don't think there's a silver bullet. The best you can hope for is that a language at 1.0 has as few bad ideas as possible. But no one seems to have perfect skill at that.
- spease 2y agoI can't speak for how C#; but in C++'s case, the issue is that there's a lot of programmers who don't keep up with the language that they're using. As a result, you get a few people pushing the language ahead, who are deeply involved in its future. And then the vast majority of people are still using C++03, and it's still taught the same way as it was ~20 years ago. I think the only way to address what you're alluding to is to continually deprecate small parts of the language, so that upgrading is manageable for active codebases. And you probably have to be really aggressive about pushing this forward, because there will always be excuses about why you should hold back just this one time and this one feature is an exception that needs to be held back just a little bit longer. But in the long run, if you don't force people to change a little bit continuously, it will become a big enough issue to split the ecosystem. See python2 to python3. Or you end up forced into supporting bad practices for all eternity, like C++. And having to take them into account for every. Single. New. Feature. Further raising the barrier to entry to participation in developing the language to people who are completely focused on its development and have unusual mastery of it, who can't identify with the people struggling with its complexity. If not at the technical level, then at the business level, where people definitely don't have the time to understand why it'd be safer for the go-to heap allocation method should return a scoped pointer instead of a raw pointer. Unfortunately, this probably is only viable for strongly-typed languages like C#; for loosely-typed languages like Python, the risk of iterative changes is that if someone moves a codebase several generations ahead at once, it'll introduce lots of subtle changes that'll only become obvious once certain codepaths are exercised...and given how bad testing coverage is for a lot of software, that probably risks breakages only occurring once it's deployed, that are nontrivial to discern via reviews or even static analysis.
- pjmlp 2y agoNot keeping up with the language is quite common in traditional enterprises, that is why you get shops still doing Python 2, Java 8, .NET Framework (C# 7.x), C89, C++98,.... People get paid to keep running what already exists, not to write new stuff. Usually new stuff only comes to be if there is a new product being added into the portfolio, and most of the time it comes via an aquisition or external contractors, not new development from scratch in a cooler version of the stuff they are using.
- pjc50 2y ago> continually deprecate small parts of the language Did you see https://news.ycombinator.com/item?id=41788026 https://news.ycombinator.com/item?id=41788026 ? "My concern, and IMO what should be the overwhelming concern of the maintainers, is not the code that is being written, or the code that will be written, but all the code that has been written, and will never be touched again. A break like this will force lots of python users to avoid upgrading to 3.17, jettison packages they may want to keep using, or deal with the hassle of patching unmaintained dependencies on their own. For those Python users for whom writing python is the core of their work that might be fine. For all the other users for whom python is an foreign, incidental, but indispensable part of their work (scientists, analysts, ...) the choice is untenable. While python can and should strive to be a more 'serious', 'professional' language, it _must_ have respect and empathy for the latter camp. Elevating something that should be a linter rule to a language change ain't that." Strongly phrased, but planned obsolescence in a language is really expensive. You're basically quietly rotting the work of your users, and they will hate you for it. I note that C# basically hasn't deprecated any of the language itself, the dotnet core transition was a big change to the runtime. And that was expensive enough, again due to dropping a lot of old libraries.
- sensanaty 2y agoBut they can simply continue using whatever version of Python works for them, right? I never got this argument, personally. Sure, having to rip out a bunch of code because of lang-level changes can suck, but you also only really need to do that if you're starting a new project anyway or for whatever reason want to keep being pinned to @latest version of the lang. If you're a researcher who uses Python 2 as a means to an end, then just stick to Py2. It's unreasonable to expect the entire python world to freeze in place so you don't have an annoying migration journey. If you need the latest Py3 features, them's just the brakes I'm afraid, eventually APIs need to change.
- johnfn 2y agoSomething about OP didn't strike me quite right, but your explanation here really nails it, I think. Especially because I can see that I'm in quite an old JS cohort - and quite happy with the language as a result - but if I were to start coding in JS yesterday I think I would gnash my teeth and tear out my hair.
- iainmerrick 2y agoThis is quite a compelling story, but thinking about it, I don’t fully agree. There’s more than one language that I initially disliked, and only learned to like after some of (what I saw as) the glaring flaws were fixed. After they added more features! For one, Objective-C. I didn’t like it at all until they added ARC, removing the need for manual addRef / release everywhere. After that, coming to the language pretty late, I came to like Obj-C quite a lot. JavaScript is another one. For a long time I thought it was an awful language and avoided using it, but a few things have brought me round and now I really like it: - modules - async/await - TypeScript, if you’ll allow that as a “JavaScript feature” I even quite like JS classes, although I could live without them. Simplicity is good, but power and expressiveness are also good.
- troad 2y agoThis is spot on, well written, and perfectly reflective of my recent experience doing some recreational language hopping. Incoherence through gradual accretion of complexity is the probably fate of most non-trivial systems, beyond just programming languages. Individual programs, certainly. Buildings too. (People?) Also, I am a big fan of your books, Bob! Thank you! :)
- terandle 2y agoBeen using .net since 2.0 and nah C# has jumped the shark. Primary constructors are a very poorly designed feature that for some reason was added in the last version. The new-ish yearly release cycle I think is mostly to blame, they feel like they need to add some headline features every year but the team also, maybe due to org-chart politics, seems to not really able to make deep runtime level changes that are needed to actually add anything useful so they just add syntax sugar every year bloating the language.
- kevingadd 2y agoThe emphasis on syntax sugar has a very useful side effect, which is that new language features can be used on old runtimes. To this day some of the newer C# features are usable when targeting the ancient .NET Framework 4.x, like 'ref returns'. This would not be possible if every new language feature was paired with runtime-level changes. (Many new language features do come with changes to the runtime and BCL). I support a bunch of people who use NET4x to this day and I'm able to write modern C# for that target thanks to the language and compiler being designed this way. A lot of stuff is also designed to be independent of library changes - IIRC for example if you use nullability, the compiler will emit the Nullable attribute's definition into your .dll as a hidden class, so that your library will work even on older versions of the runtime with older base class libraries. Doing this complicates the compiler (and adds a tiny, tiny amount of bloat to your dll) but means that more people can adopt a new feature without having to think about upgrading their SDK or runtime. My personal opinion is that if a change can be done adequately entirely at the compiler level without runtime/library changes, it should be done there. It allows the people working on the language, libraries and runtime to iterate independently and fix problems without having to coordinate across 3 teams.
- wvenable 2y ago> Primary constructors are a very poorly designed feature that for some reason was added in the last version. I upgraded to .NET 8 recently and I love primary constructors. I don't use them everywhere but they are great for dependency injection or for small classes.
- 2y ago
- mdhb 2y agoJust as a quick side note this is actually one of the things I’ve come to appreciate most about some of the work you and the others have done with Dart where it very clearly has gotten much more powerful and has had to deal with some major changes in requirements over the years as well but on the whole I feel with only a few exceptions the complexity doesn’t feel like it’s gotten away from me or the community at large at all. It’s very obvious to just look at it and see that a tremendous amount of work has gone into the language design itself and just figured now would be a good time to offer my appreciation for that.
- munificent 2y agoThank you! We try really hard. I'm always worried about pouring too much complexity in and alienating new users. At the same time, we also want to serve existing users who really do benefit from new features. It's a tricky balancing act.
- pjc50 2y agoSpeaking as a long time C# developer (and before that, C and C++), every time I try to touch javascript I get that kind of allergic reaction - not because of the language features itself, but the ecosystem. In theory npm and nuget are the same kind of complexity; in practice, all the complexity of C# building disappears into Visual Studio. A lot of people seem to think that the overall size and "complexity" of the language (and only the language) matters? Personally I don't think it matters how long the spec is if you and your team aren't using those features. The ecosystem matters more. "What should I use to write a GUI in C#?" is a complicated question with tradeoffs, but none of them have anything to do with the language per se. Nothing is going to compete with C++'s template system for complexity, though.
- nordsieck 2y ago> in practice, all the complexity of C# building disappears into Visual Studio IMO, that's even worse. It means that when you want to learn C#, you're also forced into learning a complicated tool that isn't really useful for much else. At least when I'm learning Rust or Typescript, I can keep using my existing editor. > A lot of people seem to think that the overall size and "complexity" of the language (and only the language) matters? Personally I don't think it matters how long the spec is if you and your team aren't using those features. That works until you have to use code that does use those features. > The ecosystem matters more. "What should I use to write a GUI in C#?" is a complicated question with tradeoffs, but none of them have anything to do with the language per se. That's fair. At least to an extent. The further you stray from the ecosystem's intended use cases, the more you have to depend on the quality of the language itself. Thankfully, for mature, mainstream languages like C#, there are a lot of things you can do before that point.
- neonsunset 2y ago> in practice, all the complexity of C# building disappears into Visual Studio > IMO, that's even worse. To be fair, the more accurate way to phrase it is "disappears into .NET tooling". Because this part is also exposed through standard CLI of .NET, and isn't Visual Studio specific. Managing packages through npm and dotnet is quite similar, with a significant difference that the average dependency graph of a .NET application or a package is 10 to 100 times smaller than the one of Nodejs, and the compatibility breaks happen much, much more rarely. > It means that when you want to learn C#, you're also forced into learning a complicated tool that isn't really useful for much else. This is untrue. On top of Visual Studio, your choices are Rider, VS Code and VSCodium, Neovim and Emacs, and anything else that integrates through VSC extension bridges, LSP and debugger adapter protocols. I also usually recommend to all newcomers to start with CLI to manage projects and packages, because it's more straightforward than navigating through all sorts of windows in an IDE, and because they also get know the basics .NET builds on top of. It's an experience that is very similar to using Cargo.