1. This is based on my experience, so YMMV and there are many cases where more than one way can work well!
2. I appreciate pragmatism. Try to use an appropriate tool for the job.
3. I identify as a polyglot, and have used Go and node.js extensively for 8+ years (and other languages for many more years than this).
4. Of the three languages listed, Rust is my weakest. That said, I always enjoy reading about it and learning about the design philosophy and choices.
---
Rust: If/when I had the luxury of enough time to actually learn rust :) ..and one or more of the following were true:
- Cases requiring eeking out max performance and guarantees about program safety / correctness.
- Real-time or soft (near) real-time is a serious requirement.
- targeting embedded applications or other highly resource-constrained runtime environments.
Node.js: Great for frontend work and single page apps. Also a goto when I know there's already a decent library written that will do what is required.
Go: Shines for a few classes of problems:
- Command-line apps and systems work - it can be a very quick way to a working solution, and also reasonable to create reliable and robust applications compared to many other languages.
- Situations where ease of deployment is important.
Distributing a single binary can eliminate drastic amounts of complexity.
- Problems where it's useful to trivially spin up many concurrent processes.
Then, for most other things there's Python, bash, Java / Groovy / JVM, and C.
“Why would you choose a motorbike over a hanglider or a catamaran?”
Different technologies might all be able to get you to your destination, but some are going to be more suited than others. This depends heavily or a wide variety of different factors. Large team or small team? High throughput or low throughout? Simple or complex?
The variables are too diverse to offer a meaningful answer. Use Rust when performance and safety are paramount. Use Go when you need to write a thin network-aware app or API. Use Node when you have an excess of JavaScript talent…
I came to say mostly the same... Rust is great for absolute control, and definitely in low-level scenarios (drivers, libraries, etc). Go is great for network services and alike.
Where I find node shines is when it comes down to time to market, or orchestrating disparate platforms together. Node works great as a glue layer. Not that Go doesn't, but the amount of developer time to do it is quite a bit lower in Node imho, with a more agile approach. I'm a pretty big fan of web-facing applications talking to a service written with Node that will in turn talk to other systems behind it. It's probably the easiest place to transport that weirdly bound WS* service to something easier to consume in a different language. If/when performance is an issue, you have a working prototype for a Go rewrite.
Most of the same network scaling issues apply to all of the above. And as an aside, all service authors should have to write a client in a static and dynamic language.
I would like to add that a Ruby or Node developer might very well want to rewrite some performance-critical service in Rust, which the strict language enables them to do without shooting themselves in the foot too much.
One thing we're seeing is that this isn't an either/or choice: for example, Bouyant's latest product, Conduit, is both Rust and Go. Projects like Neon let you extend Node with Rust, and there's been a lot of interest in using Rust through wasm in Node as well as the browser.
Rust puts you very close to the metal, which can be great for writing high performance code, while forcing you to be very explicit about how you use memory.
This is great if memory managment and high performance are a big concern for you, if they are not you might want to choose a language like Go or Node which allow you to gloss over such details until they really become an issue.
Do not fix problems you do not have by throwing Rust at them, but feel free make the climb of learning it to expand you horizon. :)
I'll throw out one more, which is that Rust does a great job at running on a diverse set of platforms(this is distinct from architectures).
If you wanted to run one codebase across Desktop(Win32, OSX, Linux), Mobile(Android, iOS) and Web(Asm.js/WASM) you'd usually have to resort to a scripting(perf-impact) or C/C++(compiler differences, poor dependency management).
With Rust I can do all that one from one Cargo.toml and a codebase that just works for all my platforms(modulo any forking inside libs that use "target(win32)", etc).
For someone who's done that most of their career it was amazing to see how solid and straightforward that is with Rust.
In my experience, this is true not just of memory management, but also of all sorts of safety guarantees. I currently write a (de)compression library in Rust, and the type system is very helpful for all sorts of things beyond memory management.
> Do not fix problems you do not have by throwing Rust at them
Thank you. I write mostly Node & wanted to try Go for a long time because of performance & then I heard that Rust is even faster. So I thought I would learn that.
But you are right, I don't have any problem with Node. Its working great for me. :)
Rust excels at giving you fine-grain control over how your code runs. This was important to me recently, when I found I couldn't prevent Go from using threads:
I recently wrote the same UNIX PAM module in Go, Python, and Rust. I started with Go because I enjoy writing Go and the Go compiler can generate shared libraries. Go didn't work out because Go's use of threads resulted difficult-to-debug conflicts with the use of fork(2) in sshd - after realizing that the issue was related to threading, I found that you can write PAM modules using the excellent pam-python [1] package. The code I wrote in Python worked great, but I really wanted to generate a module as a stand-alone shared object binary, so I re-wrote the code using Rust, which worked great.
What did the modules do? Would you be willing to open source it? (Curious to see how the implementation between the 3 differs, even if the go one is non-functional)
I currently write an application that uses both Rust and Node. Rust for everything that needs strong typing and performance, Node to take advantage of the existing ecosystem. In my mind, this pretty much summarizes the respective advantages of both technologies.
My knowledge of Go limits itself to "Hello, world", so I can't speak for or against that technology.
I have a simple test to know what to chosee. I try to pick the HARDEST and MOST COMPLEX task(s) I need to do and see which one give me the easier/better/faster/etc path torwards it.
Easy things are easy, but sometimes hard things are almost impossible with the wrong tool.
They are catering to different domains! Node is intended for doing backend programming for supporting web applications. And there are many better alternatives IMO.
Rust is intended for systems programming manly (not limited to that, of course). It also would be good for certain high-performance backends.
As for Go, I'll let Rob Pike (Go creator) explain the reason for Go:
The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.
Every single time I read this Rob Pike quote I have an overwhelming desire to find Rob, take him out for beers and ask him, off the record, what he thinks of the Google hiring processes.
Because what he's saying here is, in my opinion at least, anathema to company line about who they hire.
> The key point here is our programmers are Googlers, they’re not researchers.
I know things have changed, but in the early '00s good luck getting hired to Google without a PhD, masters minimum.
4.0 dual major in CS and psychology from Georgia Tech, graduated in 4 years with both of those degrees. Yeah, no thanks. [Not me, a friend of mine who wanted to work for them]
They always insisted, at every hiring event I attended through around 2008, that they only hired the "best of the best" and things like that. If that's true, then this:
> They’re not capable of understanding a brilliant language
Isn't true.
So Rob has it right and Google is hiring really mediocre people. Or Rob has it wrong but only saw the mediocre hires.
Well they can't possibly have the same hiring bar they used to, due to the amount of hires they need. When you're a smaller company, or a rapidly growing one, you can't really afford a bad egg. At google's current size, they can. To be clear, I think their hiring standard has dropped. But that doesn't mean they don't still have very talented people. Or that their average competence level isn't high compared to other companies
I don't know if it means code monkeys, although I agree you could interpret Pike that way. When you look at root cause analysis you're going to see everybody's bad days, and your bad days can be pretty mediocre.
I've lost count of the number of engineers I've pissed off by implying that they or their coworkers might in fact be human beings and thus prone to errors.
White knuckling (as the term is applied - as a pejorative - in Alcoholics Anonymous) is rampant in software. If you make a mistake it's because you're bad and you should TRY HARDER LOSER. Not because our architecture or tools are error prone/obtuse.
You could say the pressure cooker interviewer process is looking for people who don't cave under pressure, but I don't think that just because you don't crumble doesn't mean you aren't incapacitated. I suspect it ends up selecting for people who think working under intense pressure is normal and there's no point in trying to relieve that pressure, ie working smarter.
JavaScript in the context of Node is a (just-in-time) compiled language. And compilation in and of itself is not a magical conferrer of speed or resource efficiency, and there is a wide range of performance among compiled languages. A shitty compiler will produce shitty machine code. And while V8 is an amazing project, there's the mild inconvenience of JS/Node not even approaching languages like C, C++ or Rust in speed or memory efficiency.
I mean, if you want to go right ahead and admit that you have so few users/your users use your product so little that build time is a significant metric for you vs runtime performance costs, I won't stop you
How is dynamic typing easier? It makes it way easier to introduce bugs- Recently I was working on a timer (for speedsolving Rubik's cubes) and I had accidentally had tried to format an already formatted time for user display, so all that was shown was `undefined` for times over a minute. It was hard to uncover, because the calls were in seperate parts of the codebase, and I wasn't able to make the field/formatter only allow numbers because of the nature of Javascript.
Have these "studies" taken into account maintainability? There is a whole class of bugs that won't happen with a statically typed language. There also a whole class of automatic refactors that you can do with statically typed languages.
How can you be "productive" writing code that isn't "correct"? That's the worse type of developer - the one who does "negative work".
Negative work is when someone does work so badly that it takes more time to correct their errors than it would have for them not to do the work at all.
For a given task, random programmers are quicker at achieving the goal with dynamic programming languages. That's why scripting languages are popular. They are also easier to learn.
Sure, but it doesn’t scale well. Static-typing makes code easier to come back to a year later or collaborate on because it’s more self-documenting.
You don’t have to dig through the codebase to figure out that a function should have only accepted a number, because the interpreter/compiler would have screamed at you the moment you try something else.
Scripting languages may be easier to learn, but they aren’t as scalable and efficient. In requiring less scaffolding and structure, they’re not as well suited at creating correct code in large codebases. They’re still great for prototyping, though.
Not only correct code, but refactorable code. With statically typed languages, you can use tools like Resharper and do all types of refactorings that are guaranteed safe - unless you're using reflection
I've softened a little bit about scripting languages. They are great for small functions like AWS lambdas and "scripting" like the UserData portion when you're setting up a VM in AWS.
Interesting. It looks like a variant or an object whose type list is only known at runtime.
What I have in mind is a language able to convert functions calls from dynamic to static (and the opposite). If the compiler can infer the type, the call is static else dynamic (with multidispatch for function overload).
With a modern IDE though, once you discover the error of your ways and do start using OOP. You can click on a few methods and do an "extract class" and it will automatically create your class, modify all of your references, find all of the referenced variables that your functions use and give you nice helpful auto refactorings and you know that your code won't break.
But as far as using C++ and not using OOP, as thevold saying goes, you can write FORTRAN in any language.
I think OOP (precisely inheritence) makes programming more difficult even if OOP works for 80% of the cases. The last 20% are impossible to handle with that pattern and make a class hierarchy unstable.
I think Smalltalk is the most productive language because of its VM (builtin IDE, DB...) and not because of OOP.
I use Node and Golang for small to medium-size programs which are mostly network-bound. They're very good for getting something up quickly, and performance is not a problem unless you have to de/serialize tons of data.
When I need something to go faster, or have bulletproof reliability, I use Rust. The type system requires more work up front, but then it guarantees you won't chase unexpected `nil`/`undefined`. It has fast and incredibly convenient de/serialization with Serde. Fearless multicore computation with Rayon. It's good at number crunching, and gives enough low-level control to manipulate large data structures with little memory overhead.
Rust is often a good choice in place of "large project in C/C++".
The safety features of Rust helps you maintain correctness as the project grows.
At 500 lines Rust may be somewhat inconvenient and C seems fine (at least for someone new to Rust).
As the size grows most C/C++ projects tend to go out of control (unless they are really strict and serious) with a continuous repetitive stream of "oops null pointer over here" ... "oops forgot to free over here". As the size grows the inconvenience of Rust fades and it starts to provide more value.
The older I get and the grumpier I get, the more I want my code to be statically typed and verified at compile time because I don't like to be awaken in the middle of the night for a stupid null/undefined issue. Also, as much as I like to have a clean test coverage, most tests go away when you have a good type system.
In the end, I find Rust to strike the right balance between expressiveness and correctness. Unlike C/C++ where you can shoot yourself in the foot, with Rust you're forced to write thread and memory-leak safe code. And in the end, Rust code often look much cleaner AND safer than the C++ equivalent.. what's not to like.
go -> fast, easy to deploy services talking to other services.
node.js -> full-stack developers rapidly developing a product.
rust -> safer language than C/C++ but with the same performance.
62 comments
[ 4.2 ms ] story [ 146 ms ] thread1. This is based on my experience, so YMMV and there are many cases where more than one way can work well!
2. I appreciate pragmatism. Try to use an appropriate tool for the job.
3. I identify as a polyglot, and have used Go and node.js extensively for 8+ years (and other languages for many more years than this).
4. Of the three languages listed, Rust is my weakest. That said, I always enjoy reading about it and learning about the design philosophy and choices.
---
Rust: If/when I had the luxury of enough time to actually learn rust :) ..and one or more of the following were true:
- Cases requiring eeking out max performance and guarantees about program safety / correctness.
- Real-time or soft (near) real-time is a serious requirement.
- targeting embedded applications or other highly resource-constrained runtime environments.
Node.js: Great for frontend work and single page apps. Also a goto when I know there's already a decent library written that will do what is required.
Go: Shines for a few classes of problems:
- Command-line apps and systems work - it can be a very quick way to a working solution, and also reasonable to create reliable and robust applications compared to many other languages.
- Situations where ease of deployment is important. Distributing a single binary can eliminate drastic amounts of complexity.
- Problems where it's useful to trivially spin up many concurrent processes.
Then, for most other things there's Python, bash, Java / Groovy / JVM, and C.
---
grin
No matter what, have fun!
Different technologies might all be able to get you to your destination, but some are going to be more suited than others. This depends heavily or a wide variety of different factors. Large team or small team? High throughput or low throughout? Simple or complex?
The variables are too diverse to offer a meaningful answer. Use Rust when performance and safety are paramount. Use Go when you need to write a thin network-aware app or API. Use Node when you have an excess of JavaScript talent…
Where I find node shines is when it comes down to time to market, or orchestrating disparate platforms together. Node works great as a glue layer. Not that Go doesn't, but the amount of developer time to do it is quite a bit lower in Node imho, with a more agile approach. I'm a pretty big fan of web-facing applications talking to a service written with Node that will in turn talk to other systems behind it. It's probably the easiest place to transport that weirdly bound WS* service to something easier to consume in a different language. If/when performance is an issue, you have a working prototype for a Go rewrite.
Most of the same network scaling issues apply to all of the above. And as an aside, all service authors should have to write a client in a static and dynamic language.
https://conduit.io/
https://www.neon-bindings.com/
This is great if memory managment and high performance are a big concern for you, if they are not you might want to choose a language like Go or Node which allow you to gloss over such details until they really become an issue.
Do not fix problems you do not have by throwing Rust at them, but feel free make the climb of learning it to expand you horizon. :)
If you wanted to run one codebase across Desktop(Win32, OSX, Linux), Mobile(Android, iOS) and Web(Asm.js/WASM) you'd usually have to resort to a scripting(perf-impact) or C/C++(compiler differences, poor dependency management).
With Rust I can do all that one from one Cargo.toml and a codebase that just works for all my platforms(modulo any forking inside libs that use "target(win32)", etc).
For someone who's done that most of their career it was amazing to see how solid and straightforward that is with Rust.
I would not write that specific code in Node.
Thank you. I write mostly Node & wanted to try Go for a long time because of performance & then I heard that Rust is even faster. So I thought I would learn that.
But you are right, I don't have any problem with Node. Its working great for me. :)
I recently wrote the same UNIX PAM module in Go, Python, and Rust. I started with Go because I enjoy writing Go and the Go compiler can generate shared libraries. Go didn't work out because Go's use of threads resulted difficult-to-debug conflicts with the use of fork(2) in sshd - after realizing that the issue was related to threading, I found that you can write PAM modules using the excellent pam-python [1] package. The code I wrote in Python worked great, but I really wanted to generate a module as a stand-alone shared object binary, so I re-wrote the code using Rust, which worked great.
Footnote:
1: http://pam-python.sourceforge.net/
My knowledge of Go limits itself to "Hello, world", so I can't speak for or against that technology.
Easy things are easy, but sometimes hard things are almost impossible with the wrong tool.
Oh Jesus, here we go again.
They are catering to different domains! Node is intended for doing backend programming for supporting web applications. And there are many better alternatives IMO.
Rust is intended for systems programming manly (not limited to that, of course). It also would be good for certain high-performance backends.
As for Go, I'll let Rob Pike (Go creator) explain the reason for Go:
The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.
Because what he's saying here is, in my opinion at least, anathema to company line about who they hire.
I know things have changed, but in the early '00s good luck getting hired to Google without a PhD, masters minimum.
4.0 dual major in CS and psychology from Georgia Tech, graduated in 4 years with both of those degrees. Yeah, no thanks. [Not me, a friend of mine who wanted to work for them]
They always insisted, at every hiring event I attended through around 2008, that they only hired the "best of the best" and things like that. If that's true, then this:
> They’re not capable of understanding a brilliant language
Isn't true.
So Rob has it right and Google is hiring really mediocre people. Or Rob has it wrong but only saw the mediocre hires.
EDIT: Cleaned up the grammar a bit.
I've lost count of the number of engineers I've pissed off by implying that they or their coworkers might in fact be human beings and thus prone to errors.
White knuckling (as the term is applied - as a pejorative - in Alcoholics Anonymous) is rampant in software. If you make a mistake it's because you're bad and you should TRY HARDER LOSER. Not because our architecture or tools are error prone/obtuse.
You could say the pressure cooker interviewer process is looking for people who don't cave under pressure, but I don't think that just because you don't crumble doesn't mean you aren't incapacitated. I suspect it ends up selecting for people who think working under intense pressure is normal and there's no point in trying to relieve that pressure, ie working smarter.
They’re not [in]capable of understanding a brilliant language, but we want to use them to build good software.
i.e. they're smart people, but we don't want them to focus those smarts on the language itself, but on writing software with the language...
That’s bullshit too, and they know it, but it’s the best they have and works well enough when they have a limitless pipeline of people willing to try.
I’d like to see google recycle their current employees through their interview pipeline and see what happened.
All big corporations start hiring mediocre people sooner or later. Nothing wrong with that. It's just the way it is.
Still too stupid for anything more complex than go.
IMO about smalltalk, it's not just because of dynamic typing but also because of the builtin IDE, VM and DB.
Negative work is when someone does work so badly that it takes more time to correct their errors than it would have for them not to do the work at all.
For a given task, random programmers are quicker at achieving the goal with dynamic programming languages. That's why scripting languages are popular. They are also easier to learn.
You don’t have to dig through the codebase to figure out that a function should have only accepted a number, because the interpreter/compiler would have screamed at you the moment you try something else.
Scripting languages may be easier to learn, but they aren’t as scalable and efficient. In requiring less scaffolding and structure, they’re not as well suited at creating correct code in large codebases. They’re still great for prototyping, though.
I've softened a little bit about scripting languages. They are great for small functions like AWS lambdas and "scripting" like the UserData portion when you're setting up a VM in AWS.
What I have in mind is a language able to convert functions calls from dynamic to static (and the opposite). If the compiler can infer the type, the call is static else dynamic (with multidispatch for function overload).
My experience is Javascript and even PHP scale better than C++. I do a lot of type checking and I don't use OOP features.
But as far as using C++ and not using OOP, as thevold saying goes, you can write FORTRAN in any language.
I think Smalltalk is the most productive language because of its VM (builtin IDE, DB...) and not because of OOP.
When I need something to go faster, or have bulletproof reliability, I use Rust. The type system requires more work up front, but then it guarantees you won't chase unexpected `nil`/`undefined`. It has fast and incredibly convenient de/serialization with Serde. Fearless multicore computation with Rayon. It's good at number crunching, and gives enough low-level control to manipulate large data structures with little memory overhead.
Rust is often a good choice in place of "large project in C/C++".
The safety features of Rust helps you maintain correctness as the project grows.
At 500 lines Rust may be somewhat inconvenient and C seems fine (at least for someone new to Rust).
As the size grows most C/C++ projects tend to go out of control (unless they are really strict and serious) with a continuous repetitive stream of "oops null pointer over here" ... "oops forgot to free over here". As the size grows the inconvenience of Rust fades and it starts to provide more value.
In the end, I find Rust to strike the right balance between expressiveness and correctness. Unlike C/C++ where you can shoot yourself in the foot, with Rust you're forced to write thread and memory-leak safe code. And in the end, Rust code often look much cleaner AND safer than the C++ equivalent.. what's not to like.