Several times I saw someone on LinkedIn post something along the lines of:
Why won’t they hire me, a senior engineer with 15 years of experience, for a position that requires language X, but I’ve written in languages Y and Z — don’t they know a true software engineer can learn languages quickly on the job?
I have attempted to learn and write several languages over the years (in no particular order: Python, Rust, JavaScript, PHP, Objective-C, Kotlin, Common Lisp, Swift, C++, Erlang, Go, Ruby, C, Lua) and I can confidently say: “It depends”. For me more difficult by far have been not the languages themselves, but related aspects: usage domains, ecosystem and best practices.
True, you can learn the syntax and write some code that runs in weeks, but it will take you months or even years to become proficient in what to write (usage domain) and how (ecosystem and best practices).
Usage domains
Usage domain or knowledge domain is the class of problems you’re solving with a language. Mobile development, machine learning, game development are all broad usage domains. These are, in my opinion, the hardest to switch and are the main source of difficulty when learning a new language.
This one is a little tongue-in-cheek because you don’t necessarily have to change industries when changing your working language (especially if it’s JavaScript). But think about it: some languages are more aligned to certain domains than others, consequently there are more jobs in those domains that would allow you to work in that language.
Most of those jobs, besides language requirements, have knowledge domain requirements. If you wanted to write C at work, depending on the industry, you would be expected to know, for example, embedded development and all that entails, including electrical engineering and small controller architectures — not to mention the knowledge specific to writing C in such environments. If you wanted to write Swift full-time, the job expectations would almost certainly include intimate knowledge of macOS or iOS, their UI guidelines and generally knowledge specific to Apple platforms.
From my mobile development experience, when iOS development switched to Swift or Kotlin replaced Java on Android, the learning curve for mobile developers was not particularly steep. We kept using the same libraries on the same platform and wrote logically equivalent code. There is a saying I like: “You can write Java in any language”, and that’s exactly what we did for the first several months. We didn’t know yet what the best practices were for the new language, however we were very familiar with the domain and could get away with writing Objective-C in Swift and Java in Kotlin. Today, if we wrote code like that, we’d be laughed out of the room.
The usage domain is something to think about when considering a language you’d like to work in. Of course, most languages are general-purpose and you can, with some effort, write almost anything in any of them, so you can choose any one you like for your personal projects. My point is particularly about professional use. Maybe I’d enjoy writing OCaml (I hear it has a great type system), but where, most notably, is it used in a professional setting? Writing compilers and high-frequency trading. Both of those impose hard requirements on domain knowledge, and knowing only the language is emphatically not enough.
Ecosystem
The language’s ecosystem is the set of related tooling and especially libraries. Few people write most of the supporting code they need themselves (serious game developers are a notable exception), and you’ll probably use libraries to perform the tasks you’ve set out to do. Libraries for some tasks are huge and in some jobs, they will be part of the job description (for a popular example, React development on the web).
A language does not achieve that much by itself, and (again talking about a professional setting) you will be expected to know most popular libraries that are associated with a particular language by name, and to intimately know one or more of them. An expert, some say, is someone who has made all the mistakes to be made in a certain domain. It is the same with libraries. To be an expert, you have to have made the mistakes, and that means to have spent time writing real code using those libraries.
Even if you learn the language well, you won’t write production-grade code until you’ve learned to use the libraries. Effectively, your contribution will be far from expert level. It takes a long time to really learn and become familiar with some of them. Some libraries even have other libraries written on top of them that you’ll also be expected to know and use expertly.
Tooling can be similarly difficult for certain languages, for example CMake, the build system primarily used for C++, is as flexible as it is arcane. JavaScript, despite the fact it’s not compiled, also uses build systems, and an expert JS developer will have used several. Julia Evans in her post “Importing a frontend Javascript library without a build system” says:
I needed to figure out how to import a Javascript library in my code without using a build system, and it took FOREVER to figure out how to import it because the library’s setup instructions assume that you’re using a build system.
If tools are unfamiliar, without access to experts you can spend days banging your head on a wall for what may be a typical issue solved in a known way. This is both discouraging and unproductive.
Best practices
The language’s best practices are the set of techniques that a professional developer would use to write what is called “idiomatic code” that is natural, efficient and gets the job done in a way that works well with the design of a particular programming language. Only by mastering best practices you’ll start writing code that other developers will consider elegant.
In linguistics there exists the notion of linguistic relativity, also known as the Sapir–Whorf hypothesis, which says that (simply put) language defines thought. The modern understanding is that language does not define, but rather influences thought in subtle ways. This idea is succinctly expressed by the saying we all know: if all you have is a hammer, everything seems like a nail.
In exactly the same way, every programming language has certain approaches to problems, and the more you program in it, the more your learn to solve problems in a certain way. For example, functional languages favor function composition, so it becomes natural for you to solve problems through function composition. If you don’t regularly use a language with another paradigm, switching to it may be mentally difficult because its best practices suggest solving problems in way that now feels unnatural.
Coming back to the phrase I used above, “you can write Java in any language” — now it’s clear what it means: to write code in another language as if it were Java, that is, using Java’s best practices and not those native to another language. (Sorry for using Java as a scapegoat, but we’re all familiar with the characteristic verbose OOP style of its early versions.)
The next time you see that LinkedIn post, remember that what seems like a simple language switch is more similar to moving to a new country. You might quickly learn to order coffee in the local language, but true integration requires understanding the culture and eventually, learning to think like a local.
If I were trying to change my main working language, my best bet would probably be switching the role within the knowledge domain I know well, or trying to venture out into one adjacent. Meanwhile, I would work on a project in the new language to help me figure out what libraries people use most and what tools are available. The struggle to set them up would give me the initial practical knowledge to build upon.