I think the period where I grew the most as a dev was an interlude when I knew I was changing jobs, including moving countries.
From having built trading systems in c++, I ended up building, over about a year or so:
- A django (python / js) website. It integrated with both Android (java) and iOS (objective-C) apps that could read QR codes and ask the server stuff. It also made me interact with the app stores for the first time. It was also the first time I did anything on the web, including figuring out how pages are laid out. Bought a MacBook for the first time too.
- A consumer iOS app written in Swift. New language again. Went native, ran into a lot of constraint errors that eventually taught me a lot.
- A trading system in c++ that didn't use STL.
I remember at the beginning of each new language/framework thinking "OMG how do I do this". And yet it's not that bad once you've done it once or twice. I reckon it's a lot easier than learning a new natural language (German, Mandarin).
The thing it really gives you is a low opportunity cost. If you have some task, and the evidence points towards some specific framework/language being a good choice, hopefully you won't resist it due to internal anxiety about starting over again.
The constraints are such a pain at first for iOS! Then you get used to it and become a guru :D Happy you've had opportunity to try out a vast ecosystem.
It was an interlude, so not the normal leetcode grind.
I knew various people who needed an MVP made for their startup, and with a little bit of familiarity people will let you do things you haven't done before.
I know people I'd bring in to help no matter what the framework is because i trust them. And some people would do the same with me if they needed help.
Absolutely incorrect. Big firms hire “jack of all trades” devs as well to fill the gaps of the “experts”. I’ve worked with masters degree holders at very large firms that can’t even explain how DNS works. Deep domain knowledge puts you in a niche from my experience.
You remove your desire from making the absolute amount of money, then devote yourself to get compensation via experience. Do this for some years. You’ll end up in a better place with no degree than those with one.
Different languages also have their own culture and ecosystem, which are also valuable to learn in order to get a different perspective.
Some things that are taken as best practices are bad practices in other langauges and vice versa.
For example, lots of Java devs are against early and multiple returns to the point of absurdity, while in functional languages this is idiomatic and no problem at all.
Using different languages lets you see how different approaches work out in practice, so you can get a better view about which approaches work and which approaches are just cargo-cult nonsense.
I think this is my first post on HN, but have been a long time lurker.
That aside. Your point around ecosystems is interesting to me. As a wannabee coder I'm always trying new things. Most of my code was in PowerShell although I did do a bit of C++ and Delphi in school.
I have been dabbling with dotnet core (C#) and Python the last couple of years.
I tried to get into Java a bit, but to be frank the learning curve to go from start to consuming a REST service was just way too hard. I couldn't understand the difference between when I should be using JavaEE, JavaSE, spring boot, spring framework etc. I gave up after two days.
In contrast Python is just so easy. And if I want something more robust I'll rather go to dotnet.
Go is on my list as it seems to offer a lot of what I want, and although there are some apparently weird things in it I think i can be productive with it for some basic business logic within a couple of days.
I am using Java occasionally, there isn't any special learning curve if you know basic syntax.
Same as in Python you don't start with Jdango and virtual environments, you shouldn't start using Java with Spring Boot/Spring.
First example is using "reactive-streams", you don't have to use that syntax if you aren't familiar (same as in Python there are new features, which beginners may learn later), other examples strait forward.
If library you need doesn't exist in standard library, download jar, add it your `classpath` and use.
Skip any tutorials which are using Spring (unless you want to learn Spring). If you want to write more advanced Java code, learn about [Maven](https://maven.apache.org/)
Same thing about JavaEE/JavaSE, if you google about it, you will find that you may use any, there are different libraries included in installation, it doesn't mean you can't add them later.
You sound like you'd enjoy TypeScript with NodeJS. Low learning curve. Highly productive. Massively versatile ecosystem. You can be functional or OO. And of course, the type system, which will feel similar to C#. It's very fast and obviously JS being the language of the web is a natural benefit too.
For starting with TypeScript today, I’d almost certainly recommend trying Deno before Node. It’s much closer to standard web APIs, the tooling story is “you don’t need any until you know you do”. And it doesn’t have a zillion footguns like CJS/ESM interop, or different stream APIs, or complex package.json configs.
This is the way. Being able to make a website, PWA, cross-platform desktop app with Tauri, mobile app with RN / Quasar / Svelte Native / Cordova, or a standalone cross-platform executable with Deno is invaluable. Edge functions / cloudflare workers / Deno deploy, or even embedded js runtimes are all first class. As a typescript dev I feel like I can do almost anything, and UI is cake with Svelte and the other great web ui frameworks around JS. Not to mention the incredible ecosystem and the top-tier DX with PNPM and Vite. I feel spoiled when the possibilities are limitless and the tooling / ecosystem has it all!
Yes this is an excellent example of different cultures and ecosystems.
In Python a library like requests is a thin abstraction over http calls it is very easy to understand what is going on and get started.
In Java, there is a tendency to wrap everything so it fits with a framework and is super generic and configurable. For example look at the Feign library.
Each approach has its pro's and cons, but knowing both of them good as it allows you to pick the right solution for different use cases.
About getting into java: i'd recommend using Spring Boot. I provides a low abstraction http client (RestTemplate) that allows for straightforward calls to REST services.
The problem being, cargocults tend to hold the power. So yes, learn new concepts, practice them and more, but don't be surprised if in practice you can barely utilize them thanks to cargocults insisting they are wrong.
We see this in C#. Has a ridiculous amount of features outside default OOP and continues to get more baseline, even has great interop with several other languages. But culture changes very, very slowly. Large scale adoption tends to happen only when some major framework insists on using a certain paradigm.
.net core is not Windows only - works on the big three (linux mac windows).
That said, I get where you are coming from - .net framework is pretty much just windows, and there a large number of libraries/apps that are Windows first, other platforms second.
If you do end up needing to do Windows dev, though, I'd recommend it, same as if I were doing Apple Dev I'd recommend Objective C/Swift, or recommend you brush up on bash/zsh/fish, C and maybe Xorg/Systemd for linux.
You can use other languages on all these platforms but the 'native' toolkit tends to enjoy better interop and have better support.
The other big C# ecosystem is Unity Engine with its own frameworks and plugins. It lags a bit behind Windows, and might be one reason why some C# developers seem to be behind the times when it comes to new language features.
Edit: The other reason Unity developers are careful around new language features is avoiding memory allocations. When you write C# like it was Java it's more straightforward to see where allocations happen but it's all too easy to accidentally write allocations in your main loop if you use everything the language has to offer.
Problem is the mono C# compiler is several years behind Roslyn in terms of features. It's pretty much a non-starter if you don't have nullable types in 2022.
Take about anything you like of minimum level C# 6. My personal gripe being poor adoption of tuples/anonymous types and records.
C# has been trending towards introducing more things from the functional paradigm in particular, but just getting away from typical OOP patterns and replacing them with something more on the spot or functional isn't amazingly well received. The main thing which seems very well adopted is LINQ, and that's primarily due to Entity Framework and Microsoft pushing LINQ-adoption hard. Most places still program in C# as if it were Java with Lombok built-in.
And that's where I'm getting at mostly. New paradigms and languages are cool, but historically it's been difficult for them to get mass adoption unless a big name is pushing for it hard. Convergence has been the name of the game for a while now, rather than divergence.
"For example, lots of Java devs are against early and multiple returns to the point of absurdity, while in functional languages this is idiomatic and no problem at all."
Which functional language are you talking about? Of the two functional languages I know, Elm can only have a single return statement. Scala encourages a single return.
In a functional language technically you don’t have multiple returns, because the function is a single expression so in a way you’re right. On the other hand the actual result of the expression is determined on a leaf of the expression so you could consider it a return point
To make an example the following expression can be considered to have two returns:
max x y =
if x > y
then x
else y
The Java equivalent is
int max(int x,int y) {
if (x>y)
return x;
else
return y;
}
Some people abhor the idea of having multiple returns in a method, and say that you should write it like
int max(int x, int y) {
int result;
if (x>y)
result = x;
else
result = y;
return result;
}
(Disclaimer: Contrived and buggy example as I am on mobile)
Yes this is exactly what I meant.
After years of coding in Haskell and Clojure, and then going back to Java I have absolutely no problems with
int max(int x,int y) {
if (x>y)
return x;
else
return y;
}
I just looks completely fine to me, but colleagues would complain about it in reviews and I just don't understant the problem at all.
The variant with the extra result variable looks just wierd to me, and I have seen much uglier code written by people desperately avoiding early/multiple returns.
What do these languages do when you accidentally miss a branch in you decision tree? Is there any lexical or static analysis error, or does it cause a runtime error or implicit null return when you hit the actual missed condition? I think these differences in potential outcomes are what guide many of these cultural rules of thumb in different programming styles.
In imperative programming, a bunch of nested conditionals can easily have incomplete coverage of possible program states, and it can be easy to overlook problems if you have a mixture of side-effect branches and early returns. I think some people struggle with this more than others, and it can flummox them almost like goto-laden spaghetti code.
Of course, there are other areas where similar errors can occur in different languages, i.e. in exception-handling or pattern-matching constructs. There are many different coding styles which can make these control-flow structures easier or harder to debug. But, I think there can also be a lot of "cargo culting" where zombie rules of thumb continue beyond when they were really particularly helpful.
And this is likely one of those zombie rules. It's a trivial case and anyone with a semblance of programming knowledge understands what happens. You don't even need the else statement in GP's case. Meanwhile, any non-trivial case is so context-dependent you can't blindly apply rules and claim to have the best result or even just a better result from what people would come up with naturally.
The fact everything is context-dependent is the basis of all these discussions to begin with. We severely lack evidence and most rules are hearsay taken as gospel.
Cargo-culting from C where you have explicit malloc() and free(). Best practice was to have only 1 return to ensure free() was always called on everything just before the return, so you wouldn't have a memory leak if one was missing from an early return.
Rust convinced me that having a section at the top of a function that early returns in error/exceptional situations works very well. I find myself missing this in the one functional language I've tried, Elixir, although I find Elixir worth it for other reasons.
I initially fell in love with the Rust convention of simply bubbling up errors throughout a function but I've gradually realized that leads to a poor API. [1]
I'm currently learning c++ for a personal project. I want to use a library with an API that's sufficiently templated it'll be easier to write a c++ shim than directly generating bindings. So far I'm at the stage where I'm constantly thinking Rust does things better. Hopefully later on I'll realize why the different tradeoffs c++ makes make sense. (One example would be how generic code is typechecked).
[1]: If you want to be able to simply propagate up errors with the `?` operator you need to implement automatic conversations from your dependencies' errors to your error type. But that means your API will have a single error type for every dependency error type - say MyError::Io(io::Error) - which is the wrong level of specificity. I now think you should either expose only a string message your user can just show their end user or an error that's much more specific so they can write code that intelligently responds to it. Something like MyError::ReadConfigFailed(io::Error)
Regarding citation #1: I'm a Rust learner myself, so this is something that's rather salient for me. What do you think about the "boxing errors[1]" approach? This strikes me as one possible way you could bubble up dependency errors without being forced to deal with type conversions.
> I now think you should either expose only a string message your user can just show their end user or an error that's much more specific so they can write code that intelligently responds to it.
a boxed trait object is the main way to implement an error that merely conveys a string message.
I see, thanks for the response! I guess you're looking for something more deterministic so you can more effectively pattern match against the error's type?
I think both are very good options. In a lot of cases it's not useful to deterministically match against the error type. The classic example of this is an application that's just going to pop up an error box, but this applies whenever your consumer can't fix the error even if you tell them specifics. In that case it's a significant waste of time to go beyond the boxed trait style (but anyhow or eyre add nice frills on top of a boxed trait).
Edit: Realized it might not have been clear that "boxed error" and "all you get are a string message" refer to the same thing. While you can try and convert a trait object to a specific type at runtime it's a crude and ugly approach that suggests that trait object should have been something else to start with. The main thing you do with a Box<dyn Error> is ask it for a Display message.
I find the java example different to my experience. java doesn't have all expressions return values, like if, making the attempt to have single returns from functions much harder to do tidily than in other languages like for example ruby, rust, scala.
You can program just about any darned way in Java which can lead to some pretty schizophrenic code bases and organizations. I recall a fairly recent code review where the author had a static method in a class which took in another type, did some reasoning on the state of the type and returned a result. I asked why that method was not in the type itself since all the state required to answer the question lived in the instance of that object. The author said, oh we tend not to use “smart” objects.
not just get familiar with somebody's interpretations that they implemented as a language.
Languages that I used besides my job language barely gave me anything, in most cases they made me appreciate more for sane environment (package managers, IDEs, debuggers, strong standard library)
edit. just to be clear:
I don't see value in learning C#+Java+PHP, but C#+Erlang? yea
I do see value in learning Rust after C
but still - concepts are more important, you don't have to learn langs in order to be familiar with concepts
Learn software engineering - it's broad as hell and you can easily do that using one language
Concepts can often be simulated in many languages, but only clumsily, and the lack of ergonomic affordances in syntax, control flow and type systems can lead one to believe that a different paradigm doesn't work very well - it can be hard to see the wood for the trees.
It's hard to write a declarative querying library in C. SQL is not an ideal language, but as a way of expressing intent rather than a physical query plan, it's a long way away from C.
It's hard to appreciate the power of interactive debugger REPLs like binding.pry or JS's debugger without experiencing them. A C++ debugger, even a Java debugger, is a pale imitation, and it's not easy to simulate the experience without building a dynamic language inside the static language.
If all you've ever known is statically typed languages, you can suffer from a myopic parochialism with respect to dynamic typing. IMO the most popular statically typed languages only work well because they have runtime polymorphism holes in their type systems. More powerful statically typed programming languages tend to grow ever more esoteric typing constructs to enable static types to follow dynamic control and data flow - there's an inherent tension there and if language complexity isn't carefully managed, it becomes harder to express intent and error messages get more vague and cryptic.
Algebraic data types and pattern matching tends to be under-appreciated if one is schooled in object-oriented languages. Object orientation has a big hammer for switching control flow based on data values, dynamic dispatch, and architectures tend to leverage dynamic dispatch where they would be much better off with switch statements. If the set of variants is known up front, you're probably much better off with sum types and pattern matching than with interfaces, abstract classes and virtual methods.
It's interesting you bring up REPLs. I never in 20 years working have ever worked with one. I switched jobs and a few people are coco in love with them.
This probably more than anything else comes down the programmer style. I had to use Jupiter notebook to help my wife learn programming Python and it was horrific. I could never imagine that real work was done in this way, but I know many good programmers that swear by it. I suppose that when I need something like a REPL, I tend to use my debugger instead.
Well, there you go. Jupyter isn't very much like binding.pry, because it lacks the debugger-ness.
REPLs without the debugger bit aren't nearly as good. That's why I mentioned debugger for JS and binding.pry (implicitly, for Ruby).
binding.pry pops you into a terminal REPL with the local binding in scope, just like the eval function of your debugger; except you can start writing loops, define new variables, and more rarely, new functions, right there.
When writing code with a test-first model, you set up the data fixture then pop a binding.pry in the implementation, right where you expect to start implementing it, and run the test. You can then implement the logic in the middle of the test execution - in the middle of your debugger session - and because the REPL is so expressive, you can do a lot. You then copy & paste out those lines and save them in the implementation logic, and shuffle the binding.pry on to the next spot.
Or in a hobby project, you can fly by the seat of your pants, and write code live on the server. There's a remote version of Pry if multiple workers sharing a terminal start fighting each other to own keystrokes.
I agree with your sentiment about REPL's, but there are two use cases where they're very useful.
1. When you need to explore behavior.
For example, C# has slicing syntax now. MyString[..15]. What happens when the string is only 8 characters long? Lets hit the REPL and find out.
2. Poking at systems using the same code as the rest of the codebase. For example, RoR. Being able to load up the RoR framework and poke at the ActiveRecord objects can be hugely helpful for tracking down an issue.
I have a few reasons to work in (not just learn) close languages. The languages might use roughly similar concepts and syntax and solve similar problems (think, C++, C#, PHP, Java, Go), yet their community, practices, tooling, frameworks are wildly different.
If as a C++ developer in 2005 you never experienced how smooth debugging and profiling could be in Java, how fast things could compile, how easy it was to drop a jar and deploy to your server farm, you were missing out.
Similarly, if as a Go developer you don’t know how quickly you can install WordPress, write a 200 line PHP plugin, and get an entire business off the ground, you are missing out.
Most transformational for me was to be a Common Lisp developer, and find the language that allowed the ideas and concepts in my brain to be turned into prototype at the speed of thought. It’s a challenging language to use in the real world, but it really brought the point home that a language is just a vessel for ideas.
Yeah for example I can’t think of a better way to teach yourself to understand and use structured concurrency than a language like Cilk which naturally drives you in that direction. I actually started a PhD on how language design shapes the way you think about concepts (but finished it on implementation instead.)
>but still - concepts are more important, you don't have to learn langs in order to be familiar with concepts
You kinda do. Concepts are often exclusive to languages. And often concepts can't be internalized without practice of that lang.
Your own examples in your edit literally contradict what you say as you chose langs with extremely divergent concepts. C# and Erlang, Rust and C. You missed haskell and lisp.
>Your own examples in your edit literally contradict what you say as you chose langs with extremely divergent concepts. C# and Erlang, Rust and C. You missed haskell and lisp.
How so?
You can learn and understand concepts implemented in Rust without learning Rust
Concepts aren't just "learned" by reading about it. True learning requires internalization. Internalization requires practice. Practice requires the concepts to be applied. Application requires real world tools. Usage of tools requires one to learn about the tools.
If the only tool available is rust, then one must learn rust to truly learn the concepts related to it.
100% agree. And the Rust compiler is a great way to internalize those lessons since it shows what you got wrong, where you missed an ownership assignment, and often telling you how to fix the problem in plain English. It's as close as I've found to an actual teacher while writing code.
When the compiler stops yelling at you all the time, you'll get your feedback that you've actually learned those lessons.
When I was in undergrad our professor had us learn Java, but also Lisp and Prolog, and that was very valuable to my growth as an aspiring (and now thankfully employed) programmer :)
I agree, but certain concepts exist strongly at the language level. Like functional programming, Haskell for example: the power of static typechecking along with good type inference, or functional/immutable patterns, or programs as data (IO monad).
Or Prolog and logic-based declarative programming.
Or any GC'd language.
Or a Lisp and its metaprogramming.
I daresay these ideas would be very difficult to see the practical use of, or "impractical" beauty of, without touching the languages.
+1. I once wrote an article exactly describing this[1]... but I think that learning concepts involves learning different languages because the concepts are not implemented the same way in every language and seeing different implementations helps understanding the nuances and differences a lot.
Charlie Watts wasn't just into jazz, he was into all music. George Harrison understood that jazz could deepen him as a musician, but found Indian music.
If one wants to write a language that is truly original, that might actually influence the future, not just smearing around familiar weightings for features found useful "in the trades", then one wants to have some grasp of languages representative of every language in use. Then, only proceed with a compelling vision of what's missing.
For the rest of us, being a language junkie can be a problem. Without being judgmental, it is possible for a person to be too promiscuous. Same with languages. I wish I could just stick to Haskell and get work done.
I agree that it is way more valuable to know / being able to invent concepts, algorithms, architectures. That is what interests me most when I design software products for my clients and for my own company.
Languages for me are just tools, like screwdrivers. I use / used many and do not get hung up. And no I do not learn languages just for the fuck of it. Only if I see some positive ROI.
In my opinion, having it baked it into a language you use allows a sort of internalization of the concept by practice/use. You don't just get the idea, but get a feel for it.
I’ve worked deeply with about 9 programming languages.
Not hobby level, but client work level.
They did not really help me to become a better software engineer.
What did?
Being able to understand the business of the customer and create software that helped them do that easier.
That’s why today I tell junior programmers that almost everything we do these days could be accomplished with Bash, text files to hold the data and static html files.
Don’t focus on the language, focus on the business case.
Sure some languages are more efficient than others but all those do is help you build a solution faster. And languages are similar for the most part - so that helps you learn new languages faster.
"Being able to understand the business of the customer and create software that helped them do that easier."
I don't consider that software 'engineering' necessarily.
Yes to be a more useful employee or more successful freelancer etc understanding the business case is absolutely an essential skill. But the 'engineering' begins once the problem has been defined; how do you actually implement the vision.
This is what I see as the main point to get out of this. The larger your toolbox, the better you are able to solve problems. Learning new languages adds tools to that toolbox.
Of course understanding the requirements is essential, but the post effectively said the most important aspect of SE is to help the client business.
Imagine it's civil engineering, and we're talking about building a bridge.
To what extend does a bridge engineer need to understand regional trading patterns, and what bridge location and size would give maximum economic benefit?
To me that's a separate discipline to building actual bridges, and no amount of practice with different bridge designs or methodologies is going to be relevant to that, and vice versa.
I don't think you're making the point you think you're making.
You should absolutely understand what the purpose of the bridge is, what sort of things will be transported across it (trains? pipelines? weights? hazardous materials?), what sort of volume it will handle, what are the goals you're looking to solve with the bridge, what are acceptable and unacceptable tradeoffs? where are OK connection points, and where won't work (and how does it tie into the broader regional/municipal transit story)? I could go on...
you give most software engineers a bridge building problem and they'll build the bay bridge when all they needed was a simple suspension bridge for foot traffic.
> you give most software engineers a bridge building problem and they'll build the bay bridge when all they needed was a simple suspension bridge for foot traffic.
Care to give me an example in the physical world a case where overnight that foot bridge overnight suddenly becomes a highway off-ramp? Oh and suddenly we’ve realized that our highway motorists all need a place to park within walking distance of the hotel right nearby that we built last night? Also, there’s going to be spies looking for weaknesses in your bridge, robbers and invading armies looking to loot and pillage on a daily basis.
Of course engineers need to understand all the requirements for the bridge.
But coming up with those requirements, ie designing the economic/business case, is a separate discipline. As someone said, some of that may also be engineering; economic engineering, traffic engineering, social, political etc, but it is not bridge engineering.
Definition of software engineering I got from DuckDuckGo: The application of scientific and mathematical principles to practical ends such as the design, manufacture, and operation of efficient and economical structures, machines, processes, and systems.
> "Being able to understand the business of the customer and create software that helped them do that easier."
I don't consider that software 'engineering' necessarily.
Economical and efficient processes and systems to help someone figure out how to do their business would seem to fall under that definition, no?. I’m curious if you did attend engineering school because part of my curriculum in being part of an accredited engineering curriculum was classes that explored what it’s like being an engineer. You have to prioritize public safety first, integrity of being an engineer second, making sure the business succeeds and providing your advice third.
I disagree with OP in that learning languages does help you be a better engineer. It broadens your exposures to different ideas and cost effective ways to write code to solve those problems so that you can make better recommendations. That’s equally as important (perhaps even more in the beginning of a career) to being a well rounded engineer as is understanding business needs and matters enough that dismissing it as “I could do the same thing in Bash” kind of misses the forest through the trees because technology choices to impact cost to build, test, maintenance, ability to hire talent, cost to train people, etc etc.
Well, first of all, in your example bridge building is not the only civil engineering discipline involved—traffic engineers absolutely would be involved in those other questions.
More to the point though, software is not like a bridge. With a bridge, the high-level requirements are easily defined in a way that any layman can understand (weight limits, lane count, etc). Failure of a bridge is also viscerally obvious. Obviously there may be extreme engineering challenges in actually building a given bridge, but at the end of the day no one involved ever runs a serious risk of losing their handle on the big picture.
Software, by contrast, is arbitrary logic full of leaky abstractions and unknown touchpoints. Even if we narrow down to the most vanilla business CRUD app, precise requirements can be frustratingly elusive. No matter how well thought out a project, there are always discoveries at build time of any non-trivial project which significantly impact the requirements.
For this reason, I consider it a core responsibility for senior engineers to help define requirements and not just be a passive recipient of them. Martin Fowler captures my viewpoint more eloquently: https://www.youtube.com/watch?v=4E3xfR6IBII
You can write basically the same code with different syntax, or you can learn the paradigms, styles, and idioms associated with each language. That latter is the valuable part because it teaches you a new way to conceptualize and approach problems.
That's because you've been learning languages that are similar. The algol family of languages with OOP.
You need to learn languages outside of this family to see the benefit. But the benefit won't be in your ability to do "business". It will be exclusively improvements in programming unrelated to "business programming"
>That’s why today I tell junior programmers that almost everything we do these days could be accomplished with Bash, text files to hold the data and static html files.
Why? Junior programmers are aware of this already. And get this: junior programmers are even aware that everything can be accomplished with assembly language. They are also aware why things aren't done in assembly, typically.
> That's because you've been learning languages that are similar. The algol family of languages with OOP.
What makes you so confident of that?
I took a look at eric4smith's website. His current language of choice is Elixir, which is not in fact a member of "the Algol family of languages with OOP".
That's one language. He said he learned 9. I'm betting out of the nine of those most of them are from the algol family and involve OOP. Yeah it's an assumption. My confidence is based on the fact that most languages are from the algol family and are OOP.
GP mentioned 9 languages. I'm betting most of those 9 languages are inline with what I said. Yeah it's an assumption, but it's a very likely one.
Second of all, no stones were thrown. I made an educated guess. Don't assume I'm "throwing stones", this is not an attack. Just a statement of my thoughts.
I would say the "throwing stones" comment was waay more accusatory and presumptuous then mine, and would indeed require verification before pointing a finger. But to each his own. Yes... I certainly admit I'm making an assumption about the 9 programming languages he learned.
Learning different programming languages definitely helped expose me to new ideas, but I think it's a relatively shallow way to grow. There's only a few that I think are really worth learning for the sake of personal growth:
- both C and C++
- Some form of lisp. I could see julia taking this spot.
- Haskell
- SQL
There are obviously more that are worth learning for industrial use. Learning the above will give a solid understanding of how every other language works though, even if there's different semantics for a particular language.
When I was really into learning new languages, I think it was because I didn't have a good sense of how to do more challenging things.
At some point, I settled down and got more into projects that really require more domain knowledge. Writing an external merge sort, interpreter & compiler, a gameboy emulator, a disk persisted b+tree, ray tracing & physically based rendering, etc.
Those types of problems are what really pushed my boundaries and made me think more about how to solve problems. In the case of ray tracing & pbr, it forced me to learn some calculus, linear algebra, and statistics, which has been an incredibly rewarding experience.
It can be fun to imagine what solving a problem looks like in a language, because it may have some qualities that play to the strengths of a solution. At the end of the day, the languages are just tools people have come up with. Learning the quirks of C++ becomes less fun when you start to realize that it is the way it is because of how history unfolded rather than because of some interesting concept.
I think it is impossible to make a list of programming languages that are worth learning.
Just as a comparison, would you also make such a list for normal languages? English, Arabic and Chinese?
Choosing a language to learn depends on, among many more aspects:
* What style do you prefer. Return codes vs exceptions, small lambdas vs elaborate functions...
* What paradigms you find useful (procedural, functional)
* how do you think, rather mathematical, more in a state/execution way or more like cooking (recipes),...
* what applications do you like? Frontend to a store, embedded/industrial, mission critical,...
* simply taste. I hate ruby and love python. For a lot of people it is the other way around.
* do you love optimizing? For what memory, speed?
* Do you prioritize or advocate readability, maintainability, re-use?
In the end you have to choose your battles, you can't master all paradigms and styles in one lifetime.
But I think the point should be to get at least a taste of either end of every one of those continuums, so that you get a broad overview of what kinds of problems are well suited to different sets of tradeoffs. For another silly example, see the difference between rust’s collect and using generators in Python. Same end result with very different semantics.
Without this, there’s a risk that the “learning” is just tunneling in on your own pre-existing prejudices (insert hammer-nail metaphor here).
I did a linguistics degree as an undergrad, and one of the requirements was coursework/proficiency in two different foreign languages. Unless you planned on actually using both of them, you were strongly encouraged to choose very different languages, rather than (say) Spanish and Italian.
Having learnt a Romance language, I took a year of intensive Japanese (the tones in Chinese were a bridge too far). It was a nightmarish amount of memorization and I juste eked out a passing grade, but the exposure to totally different writing, grammatical, and honorific systems was also really fascinating.
I understand what you mean and I agree for the most part.
Still, there are multiple degrees of freedom for choosing languages, any list is just a proposal.
Right, but the presence of all those degrees means it’s valuable to at least dabble in many different languages, vs asserting that a list of just a few basically has it covered.
> I think it is impossible to make a list of programming languages that are worth learning
"Worth learning" is a subjective phrase, so it'll naturally be different for everyone. My list above was to give a list of languages that would cover as much of the spectrum as possible.
> would you also make such a list for normal languages? English, Arabic and Chinese?
No, for starters because I'm terrible with spoken languages and rather ignorant. But to make it a better comparison, I would say "learn one romance language" (or maybe just latin), or "learn one language written right-to-left".
If you were taking a painting class, it would be reasonable for the teacher to select some techniques to teach over others, or to showcase some select master painters to try and cover as much ground as possible in the course.
The idea being that you have a finite amount of time. Do you want to spend it getting a wealth of very similar experiences, or a broad appreciation.
Using your python vs ruby example: I think the more you learn, the smaller the differences between python and ruby are. There may be a profound difference on the experience of using either based on the users preferences and choices the languages made. But from a conceptual point of view, they're very close to each other.
I think SQL is both the simplest to learn and most worthwhile in this aspect.
Why? Short answer - Because it doesn't have for loops.
It forces you to think about data in another way. It is like origami for programming. You can't brute force it, you have to fold your way to the result.
What would you say the value is in learning some sort of lisp? I see the value in all the others, but lisp dialects tend to be pretty vanilla aside from their syntax. I can't think of anything you'd learn from lisp that you wouldn't from Haskell.
I think the big value is in learning to work with the AST itself, internalizing structure and representation. And how else are you supposed to enjoy SICP?
Leaning new languages is great fun, but I do think it has diminishing returns when you have covered the major paradigms. The key to growth is to discover new "aspects" where you can broaden your experience, e.g. windows vs unix, desktop GUI versus web versus systems programming, low level vs high level, game programming vs line-of-business development, stand-alone apps vs large enterprise systems, green field versus legacy.
Knowing 27 languages but only having worked on say small green-field web apps is still a very limited experience.
I value learning new languages, especially as I have yet to find a very good one.
Some are pure and beautiful like Scheme, but if you asked me what single language I would take to a lonely island with me where I was stranded, it would still have to be C.
To those here who would like to have some guidance diving into other languages I can recommend these two books:
Other than the fact that C can be run on basically any device on earth, and the super rich history behind it, does C as a language have any benefits? I personally don’t find C more ergonomic, expressive or beautiful compared to any other language.
C gets out of your way and lets you do your job without buying into a
lot leaky abstractions and without going to annual C conferences. C
doesn't argue with you about whether something is a number or an array
of bits. C is blazingly fast compared to most alternatives. C enables
you to detect and handle heap overflow errors without panicking or
crashing (just in case your memory is finite).
I found it useful to learn just because there's so much existing code that's been written in it.
Means I can at least dig through some old codebases and understand what's going on.
Lots of higher level languages also have C bindings, so at least a couple of times in my career I've ended up writing short C modules for speed critical bits of code.
The pain of manual memory management also gave me much more of an appreciation of its pitfalls and alternatives.
C used to have a close to bare metal semantics, and on some environments¹ still have it. So if you need low level semantics, it normally beats assembly on those environments.
The Rust people are working hard to support everything that C does, so in the future C and asm will probably not have a monopoly there. But this is still not our reality.
1 - Of course, not a PC software stack, and not on any ARM available today.
C is not meant to be a beautiful language. C is meant to write programs that run as fast as possible on any CPU. The beauty of C comes from being able to tame the hardware and OS complexity to get the most speed, not from taming the complexity of the software architecture or abstract design.
If I had to pick one language to code in for the rest of my life, it would be C.
The first book is my all-time favorite programming book. I was disappointed by the second one, though. It didn't hold the same charm and wonder as the original.
TypeError: Cannot read properties of undefined (reading 'reduce')
at getTweets (/myapp/build/index.js:898:22)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at loader5 (/myapp/build/index.js:991:48)
at Object.callRouteLoader (/myapp/node_modules/@remix-run/server runtime/dist/data.js:77:14)
It's also really, really fun to use a language that's completely different than what you're used to.
I've learned Elixir, Scala, F#, and Common Lisp on the side in the past year or so. I like Elixir and CL to the point where they're typically my go-to for personal projects. I liked Scala and F# a lot but I don't reach for them so often; it'd be nice to work with them.
The worst part about learning (and liking!) new languages is that at work, we're pretty firmly in Go. IMO, while Go is nice from a minimalism and resource utilization standpoint, it's simply not fun to write after using much more expressive languages.
> IMO, while Go is nice from a minimalism and resource utilization standpoint, it's simply not fun to write after using much more expressive languages.
It's definitely a blessing and a curse. What I think I'd say about Go is that it's a simple "day 1" language and a complicated "day 2" language. Speaking strictly from an expressivity and language semantics standpoint, as its runtime and standard toolset are simply great, it's just filled with so many head scratchers when you dig into what's going on and why.
Like the go modules import compatibility rules forcing you to create new vX versions of modules. It's not that it's unsound, it's just unlike everything else out there, and so you can't take your knowledge from other packages managers and apply them to Go. Maybe that expands the mind a bit, but in my experience it just confuses teams and forces them to sometimes go back and revert a major version bump because it's easier to just do that.
> What I think I'd say about Go is that it's a simple "day 1" language and a complicated "day 2" language.
This resonates. I very much dislike Go's error handling model. Its overly simple and leads to exorbitant
if err != nil
checks all over the place. It clutters the actual business code and remains a clunky, leaky abstraction.
I think Go is a language designed for engineers who don't care to learn about more elegant solutions that come with more intricate semantics (Option, Either, Try etc).
It's get the job done inspiration is sloppy and poorly conceived.
The big thing too, with the `if err != nil` pattern is that it's _good_ Go practice to do that, but it doesn't feel good to constantly be spamming a 3 line error check on every single statement.
I really don't like hopping into a codebase and seeing a 27-line function (uncommented, of course) with 21 of the lines being `if err != nil` checks
Elixir was the most pleasant experience I’ve had learning a new language. I really enjoy the ergonomics of pattern matching and the pipe operator. Really wish I had an opportunity to use it professionally.
I agree, so many of the idioms (tuple returns into pattern matching, comfortable lambdas, DI through behaviours) all just felt like a breath of fresh air.
It feels like the language was designed to be as good as any other language you've learned.
I have used several languages professionally and written personal projects in about a dozen languages. But I'm not sure if it has helped me grow as a software engineer.
I'm mostly saying this because every company I worked for implemented similar antipatterns. It's like the human brain subconsciously leads people to make similar bad decisions regardless of the programming language.
On the other hand, learning different languages makes you more gritty. If you can troubleshoot code in half a dozen languages, chances are you can figure out the 7th.
I don't know why the comments are so negative. I agree that the fundamentals are important and that you can then learn almost any language, but OP might have a different learning process and getting the hands dirty might be helping him and honestly trying out a new language along with making your fundamentals clear does not hurt anyone.
Frankly the article reads like it’s an Instagram post that you’d find with #blessed on it or some other self-important nonsense. Which is fine if the target audience is juniors—look at all the cool stuff this guy is doing because he’s continuing his education. But then he gets the content of the message wrong, so to other seniors it’s kind of open season.
Agreed. Personally, the way I overcame my lack of a complete college education was through learning multiple languages. Researching the way different languages solved the same problems - and why - helped me understand the fundamentals, as I didn't have an algorithms or data structures course to lean on.
A single programming language, isn't always a "single language" across different code bases. Especially for languages like C and C++, there are many different approaches that different code bases use -- memory management strategies, all kinds of internal APIs, different control flow, etc.
Other languages might feel more like a "single language" because they are more domain-specific, and there are established basic libraries that are widely used, so moving from one code base to another is more fluid.
Wouldn’t it be more useful to your audience (juniors I assume) to say that “learning patterns in software design helped you grow as an engineer?” I mean you call out things like discovering DDD and I’m sure you also came across Data-oriented design as well, Patterns of enterprise architecture, etc. In my experience knowing those patterns provides far more value in one’s toolkit than “knowing” Kotlin, Ruby, Scala, Golang, Rust & C++.
Different languages provide different features and support these patterns in different ways. I think they’re complementary.
Not to mention that learning different languages can be quite valuable for one’s career, as it is another skill one can employ for practical purposes and for distinguishing oneself among peers.
> Different languages provide different features and support these patterns in different ways. I think they’re complementary.
I mean, I didn’t learn that much from learning FP that didn’t already know from reading Uncle Bob’s clean code and style guides. In fact I think for awhile it just made me a pretentious clown who wouldn’t stop calling things monads.
Furthermore, haven’t most languages today pretty much converged around the “imperative++” feature set:
Language-level async/await, object-oriented, garbage-collected, w/ a package manager for other people’s code. Whatever nuance between how Kotlin does abstract classes vs Typescript I can certainly learn on the job, for instance.
I don’t disagree that there is value in learning two or three languages but beyond that the marginal utility drops off quite sharply in my experience.
> Not to mention that learning different languages can be quite valuable for one’s career, as it is another skill one can employ for practical purposes and for distinguishing oneself among peers.
I mean if it comes right down to it, a decision between two otherwise identical candidates, hire the person w/ language experience. But we probably agree that theory knowledge precedes language familiarly in importance, always. Which is largely my argument for why the title thesis is probably inaccurate.
> Furthermore, haven’t most languages today pretty much converged around the “imperative++” feature set:
If anything, functional programming has absolutely won.
Support for first-class and higher order functions, anonymous functions and so in are absolute must-haves for any modern language.
These features have become so bread and butter that people will not even think about them as functional programming but these were the main features that functional programming languages pioneered.
Now, PURE functional programming like in Haskell is not mainstream. It is a testament to the success of functional programming that these days we mostly think about these extreme examples when we talk about functional programming.
If anything, it is object oriented features that are becoming optional. At least the class based approach is starting to decline for good with many new languages explicitly not implementing them.
Also, I strongly disagree that all programming languages are starting to converge. It seems you just picked languages that are very similar which I agree, will not expand your mind much.
Learning languages like Common Lisp, APL, Forth, Prolog, Haskell and so on though will will greatly expand your understanding of programming.
> It seems you just picked languages that are very similar
No no, I picked languages that have jobs waiting in industry. 3 Billion devices run on a language that literally requires everything to be an object ;) I haven’t seen too many elm/elixir job postings, but rest assured when they start cropping up with the frequency of say JavaScript or C#, I’ll be dusting off my notes from previous side projects. I’ve also done Prolog, I’ve also done Haskell, I’ve also done Scala. Far more important than being able to write Haskellish code for my
typescript backend is the ability to write idiomatic Typescript code with good patterns. And the whole discussion here is not whether you should check out FP (you should) but whether learning extra languages is more important than learning patterns in software design (ie canonical Martin Fowler, GoF, Uncle Bob). There are absolutely useful models and perspectives to be found in FP, but I haven’t found them to be stuff that I use every day. If every imperative language is shipping with first-class functions (which I personally don’t think is as dazzling a notion as it sounds—even C has shipped with function pointers since the days of old) and all the jobs are in imperative languages, it seems pretty practical to focus on learning patterns before learning FP. Respectfully.
I’m pretty sure Clean Code by Uncle Bob has a section on why you should use const to achieve immutable variables. In any case, good style guides inevitably are preaching this as well, as are good senior engineers.
All in one’s first language (human), without needing to understand lambda calculus, or monads to run a hello world.
> I’m pretty sure Clean Code by Uncle Bob has a section on why you should use const to achieve immutable variables.
Doesn't go far enough.
Firstly, it's just advice. I might like it, but it doesn't mean I can get my teammates to do it.
Secondly, if I want to do it, I have to get it right, any mistakes I make are on me.
Thirdly, const is not enough. You'll probably end up with immutable pointers to mutable data, rather than any kind of referential transparency.
A language which helps you get immutability right is a world of difference. It's like the difference between a memory-safe language vs just using C (and a C textbook which recommends that you write memory-safe code)
> A language which helps you get immutability right is a world of difference. It's like the difference between a memory-safe language vs just using C (and a C textbook which recommends that you write memory-safe code)
I mean I understood how to get immutability right before I ever picked up an FP language, but I can see how this is a flexible point though. The virtues of immutability really don’t take a long time to extol, and they’re a concept you’re introduced to immediately in MyFirstFPLang™.
Of course; but reading it from clean code 10 years ago I thought “yeah sure”.
Having no way to mutate my variable in List or Elm or scala … and seeing first hand that a lot whole class of bug disappeared, is different.
But we need both ! Absolutely both. And I will probably never code again in scala or lisp. That was fun. I learned a few things ( scala taught me how functional codebase can be unreadable as well )
> Because of some of my programming teachers, I had a feeling that JavaScript is not a “real” programming language. In addition, it was JavaScript in the frontend environment, so you know, moving divs and buttons around, not algorithms.
Well, it certainly does not live up to the standards of languages, which those people would call "real programming languages". So while they are doing a true scotchman thing there, there is also some truth at the core of it. If we remember JS how it was a decade or more ago, and what JS today still keeps downwards-compatibility with, it is easy to see, how the very foundations are flawed, to say the least. Self-respecting computer scientists, professors or not, will most likely not accept those shaky foundations as something worthy.
Anyway, it is not the pure count of programming languages, but rather the count of programming language families one got to know, that makes the difference. If all one ever touches are languages out of the algol family, then it will have far far less benefit, compared to trying out languages from various families. For example one will learn much more, trying out SQL, a lisp, an APL, Prolog and a Smalltalk dialect, a language with stytic typing and a langauge with dynamic typing, than from trying out Java, C++ and C# and the like.
This is because the various different families do things vastly differently and require you to switch your brain into different modes, while languages of the same family will mostly only make you learn a new syntax and new built-in function names.
Feels like the moment to mention 'Strange Code' - https://nostarch.com/strange-code by Ronald T. Kneusel (no relation or affiliation, I just like the book).
You're never going to build your next major project in one of them (unless you happen to be a COBOL developer or happen to maintain OpenFirmware boot code written in Forth) but seeing different ways of doing things is both interesting and instructive.
A lot of people seem to overdo it, but I'd recommend
- Some sort of assembly
- C
- Java/Kotlin/C#/similar
- Python
- Javascript
- Some Lisp flavor
- Possibly Erlang
- Possibly Rust
This covers a lot of layers of the stack, along with some unique paradigms. I'd use it somewhat seriously for a while (months to year), not just as a toy to learn a language in a week. For a toy, JS and Java are close enough that you might not appreciate that once you get past the syntax, they're radically different.
I think it's also very important to see (and easy to miss) that a language is just a part of bigger programming systems. Every problem domains with enough complexity will eventually grow into some sort of a domain specific programming system to provide flexibility and simplicity. Composition, abstraction and generalization are all important tools to tackle this problem and programming languages exist exactly for this reason.
Because general purpose language only solves a tip of iceberg, you will find out that your system begins to resemble more and more of a language as your system evolves. (Although it's usually best to avoid this situation IMHO, but if more than thousands of users depend on it then it's inevitable!) At this moment, some knowledge on language design and implementation could be helpful to avoid pitfalls and make the system more consistent and easy to the users even if it doesn't take a form of a textual language with its own unique syntax.
If I hadn't tried new languages, I would be stuck with assembler language forever. ;)
To this date I feel that using Python and SQL are amongst the best decisions in my developer life. Domain specific, without any helper libraries nor macros. Eye opener for using abstractions instead of doing everything the hard way using any low level languages.
What grew me the most as a programmer was doing the Advent of Code challenge every year using a different language each year (and doing 1 year with 3 languages each day). Learned so many new algorithms and languages I never would’ve otherwise encountered. Side note, Advent of Code 2022 starts December 1!
I agree that learning ML style languages has benefits, when I discovered the concept of "Option" the ideas behind pattern matching and functional programming made a lot more sense, especially when it comes to writing systems software.
But for some reason your comment reminds me of James Mickens quote from "USENIX: The Night Watch" article[1] "You can’t just place a LISP book on top of an x86 chip and hope that the hardware learns about lambda calculus by osmosis"
"null-safe type" is a term which takes the perspective of backpedaling out of a mistake, which keeps highlighting the mistake. The mistake is unnecessary in the first place; types do not naturally have a null in them that has to be exorcised.
For instance, when we think of a type like "the natural numbers", that's just a set of 1, 2, 3, ... there is no "null reference to Integer" in there.
Thus I think terms like "null-safe type" is just something we should leave to Java programmers, and not use as a way to talk about types.
I’ve learned a handful of languages over the years, it was fairly straightforward to reason about all the c-likes (java, php, js, c#, etc), and others (python, tcl). They all have quirks and unique pros/cons, but it was mostly a matter of applying familiar ideas to a new environment/syntax.
But by a large margin nothing melted my brain more than learning a pure functional language (xquery, in my case). To have to think about programming in a completely different way was extremely beneficial. I highly recommend it, given the chance.
I love moving domains every so often because changing up tooling and languages is such a great learning experience. Here's a related piece I wrote talking about why I think learners should also be learning multiple programming languages: https://blog.boot.dev/education/learn-multiple-programming-l...
My job involves using multiple languages, our small team own packages that include java, kotlin, ruby, python, typescript. Everyone is expected to write in any language as needed.
We also conduct interview in any language a candidate would like to use.
I think if you try different enough programming languages it helps a lot... but what I found even more interesting is doing the same coding kata in those different languages.
Elixir vs Javascript vs Python vs Ruby... so much difference.
Here my thought: I would love a language that is simple as Lisp, compose via pipe all the way down, have elegant type like StandardML, that run on powerful concurrency engine like Erlang/OTP.
Without even reading the article, one hundred times yes. Exposure to different paradigms and concepts is more important to absorb than syntax in this endeavour, however.
the startup company i work in uses 6 different programming languages, which makes me feel what you argue here. to add, markup languages are fun and contrubiting also, particularly devops developing with yaml and composing Docker components makes you think in a different organizational mindset than other programming domains
For my personal case, I found out that writing "real programs" in anything else than assembly with a conservative usage of a macro processor is kind of toxic: it is a complex matter based on my experience and what I could understand from "software history".
A high level script language would be such "real program". Idem for a command shell.
risc-v, if successfull, will help a lot to this then remove a lot of what I consider toxic.
Are you actually saying all programs should be written in a macro assembly language? Tied to only one hardware platform, with high level concepts completely obfuscated by the need to express them in assembly? So when MS wants to make an ARM version of Windows, rewrite! When Apple switched from 68k to Power to x86/x64 to ARM, rewrite! Port the Linux kernel to a new platform, rewrite! Port the unix utilities to a new platform, rewrite! Any application writer wanting to port their program from one hardware system to another, rewrite!
That would be an awful world to live in, though more people would be employed. Less would actually get done, though.
> Yes, and porting from 1 assembly to another is easy.
So I disagree with this because it discounts the actual differences between hardware systems (including RISC-V variations, 32-bit memory addresses versus 64-bit, different sets of instructions depending on the underlying variant). Then again, I've worked with systems that gave you complex number registers which are not terribly common on any other system. Could be translated away (and sometimes was, but usually to a high level language first) but if you stuck to assembly you exploded the instruction count to do so since now you needed complex math routines (among other things) that were baked into the older system.
But let's say it is easy, then you are either wasting a massive amount of time doing something by hand a computer can do for you (only a fool does this for long), or you have a computer do it for you. At which point your ISA I Assembly->ISA II Assembly translator becomes, wait for it, a compiler. And then you have a really shitty language (some old system's ISA) instead of an actually useful high level language.
EDIT: And suppose, somehow, you kill all non-RISC-V ISAs (good luck). How do you handle other hardware like GPUs? Do you really expect a common ISA to form there any time soon?
As I said, I do agree to "pay this price", because I am literally fed up to pay the other price of dependency on very few grotesquely and absurdely massive compilers and the never ending planned obsolescence of computer language syntax.
And high level script interpreters (python-like/swift-like/lua-like/javascript-like/etc-like) written themselve in assembly would be around.
In the interim, I would still code plain and simple C (probably "stuck" at c89 with benign bits of c99/c11), but using the most idiotic small and simple compilers out there, and never ever use gcc or clang to compile it.
And with risc-v, even ultra-small risc-v SOCs offer at least a 64bits core, so I would stick to 64bits.
Why do you think so? I assume that writing the same piece of code in assembly would take more than twice as long (conservative estimate) if you do the same in high level languages without much benefit. Can you elaborate your opinion a bit more as I am curious. I have been wanting to get into learning assembly but just cant seem to do it as high level languages are so easy to code in and want to know if anyone really uses assembly nowadays.
You are perfectly right. Basically, it is "moving the lines".
Taking more time to code, even duplicate some code paths for major ISAs is saner than to depend on the planned obsolescence of grotesquely and absurdely massive compilers and many computer language syntaxes. See that as short term thinking vs long term thinking.
You would find some kind of middle ground when combining those with high level scripting languages.
Think about a world where many code paths are available in risc-v assembly, with risc-v written python-like interpreters.
From having built trading systems in c++, I ended up building, over about a year or so:
- A django (python / js) website. It integrated with both Android (java) and iOS (objective-C) apps that could read QR codes and ask the server stuff. It also made me interact with the app stores for the first time. It was also the first time I did anything on the web, including figuring out how pages are laid out. Bought a MacBook for the first time too.
- A consumer iOS app written in Swift. New language again. Went native, ran into a lot of constraint errors that eventually taught me a lot.
- A trading system in c++ that didn't use STL.
I remember at the beginning of each new language/framework thinking "OMG how do I do this". And yet it's not that bad once you've done it once or twice. I reckon it's a lot easier than learning a new natural language (German, Mandarin).
The thing it really gives you is a low opportunity cost. If you have some task, and the evidence points towards some specific framework/language being a good choice, hopefully you won't resist it due to internal anxiety about starting over again.