
Ditching Go for Node.js - carlchenet
https://github.com/maxpert/raspchat/releases/tag/v1.0.0-alpha
======
iends
Some random thoughts as somebody who codes a lot of Node.js at work and a lot
Go in my freetime (and sometimes at work):

Did you try using pprof and the other tooling go provides to better understand
your performance limitations? Tooling is a lot better in go ecosystem for
understanding CPU and memory consumption, so if/when you run into into issues
with Node you're going to be in a world of pain (this is basically a large
portion of my job in a large node.js code base in the $day_job). You'll
basically have to resort to using lldb and heapdumps in the Node world. I'm
surprised the number of concurrent clients you go with Go was so small. I know
lots of people using Go and Gorilla websockets that exceed 1.5k clients with
similar memory constraints. To be perfectly honest, it sounds like you're
doing something wrong.

As of Go 1.4, the default stack size per goroutine was 2KB and not 4KB.

If you add in TypeScript, you'll have a better type system than Go provides in
the Node ecosystem. That's a huge point for using Node.js, especially if there
are multiple contributors and the code base lasts for many years.

~~~
chmln
I've found typescript an invaluable tool.

Having worked on large backend codebases in both TS and JS, the difference is
stark. The TS API in my case had something like 90% less errors, and those
were all subtle bugs in business logic.

On the other hand, the large JS API over time had all sorts of unexpected type
errors and undefined behavior. I'm aware that this is anecdotal evidence, but
TS is pretty much a no-brainer now on backend.

~~~
grsblk
> The TS API in my case had something like 90% less errors

That sounds like a red flag to me. I hardly have any type related bugs in my
pure JS server code. Must be a poor developer in the team. Too easy to blame
JS for that.

~~~
KirinDave
If Typescript can deliver 90% less errors with the same team, saying "Must be
a bad developer" is a useless comment.

If TypeScript let's that developer make good code, then the variable is
JavaScript.

------
jonathanoliver
It's interesting he's referencing the Disruptor pattern. I wrote the Go port
of the Disruptor implementation that he links to
([https://github.com/smartystreets/go-
disruptor](https://github.com/smartystreets/go-disruptor)) a while back and it
performed beautifully. That said, channels weren't slow either. Our finding
showed that we could easily push 10-30 million messages per second through a
channel, so I'm struggling to understand what he defines as slow. That said,
with a few tweaks to the Go memory model and I think I could take the
Disruptor project to completion. Without those tweaks, I have to do memory
fences in assembly.

~~~
im_down_w_otp
On a really slow, in-order-execution processor w/ tiny caches and fairly awful
front-side bus that's also all responsible for managing the network connection
over USB?

The thing about Intel non-Atom level hardware is that Intel has spent a lot of
money over the better part of two decades packing processors full of features
that make all kinds of theoretically inefficient things run pretty fast.

This is not true of the Broadcom SoCs on a Raspberry Pi.

~~~
jonathanoliver
Good point. Go channels weren't nearly as efficient on non-x86 hardware. When
I compiled my Go Disruptor port and ran it on my Nexus 5 mobile phone, I was
getting about 9 million messages/sec.

~~~
im_down_w_otp
And your Nexus 5 is much, much faster than a Raspberry Pi :-)

------
MrBuddyCasino
Ryan Dahl, the creator of Node.js:

"That said, I think Node is not the best system to build a massive server web.
I would use Go for that. And honestly, that’s the reason why I left Node. It
was the realization that: oh, actually, this is not the best server-side
system ever."

Full interview: [https://www.mappingthejourney.com/single-
post/2017/08/31/epi...](https://www.mappingthejourney.com/single-
post/2017/08/31/episode-8-interview-with-ryan-dahl-creator-of-nodejs/)

~~~
erikpukinskis
I am a huge JS fan and I agree, for purely server side plays it’s probably not
the best choice.

I actually think Node has not yet realized its best feature. It’s still in a
research phase while it learns its best trick: components that bridge the
client and server.

Meteor was an attempt. And server side boot in the MVC frameworks is another
attempt. But both are wrong. Both try to create anonymous code that doesn’t
know whether it’s on the client or server. But the client and server are
different. The winning solution will acknowledge that, and just help the
component do both. Once we have components that span client and server we can
then write applications that don’t deal directly with HTTP. But not before.

That’ll be a huge thing.

~~~
styfle
Good point.

What do you think about shared components using React SSR?

[https://github.com/styfle/react-server-example-
tsx](https://github.com/styfle/react-server-example-tsx)

------
Thaxll
OP is complaining about Goroutine stack size at 4kb per connection, his test
shows that Node8 is taking up to 150MB of memory with 5k users, 4kb*5k = 20MB
for Go memory, I don't understand how Nodejs can take less memory than Go, and
without real numbers / test I'm pretty sure he's doing something wrong
somewhere.

From my experience on some large prod deployment, Nodejs app takes much more
memory and CPU vs Go app for doing similar work.

~~~
bryanlarsen
It's 2 per connection, so that's 40MB of RAM. The OP also said that was minor
compared to the channel overhead -- it seems the number of channels was
exponential to the number of users in a room.

~~~
todd8
A fully connected graph of n users contains n*(n-1) links if users don't
connect to themselves (I would describe this as polynomial growth in the
number of channels, a lot better than exponential). A chat broker that acts as
a switchboard between users could, I suppose, reduce this to a linear
relationship between users and channels.

~~~
amelius
You have to divide that by 2. It's "half the matrix".

~~~
todd8
Thanks, my mistake. You're right, there are n(n-1)/2 arcs in the fully
connected graph (K_n).

------
Vendan
One thing to note is that they were using boltdb, which is an in process K/V
store designed for high read loads, and doing a lot of writes to it. boltdb
also tends to use a lot of memory, as it's using a mmap'd file. The switch
also moved them to sqlite, which I would say is a much better fit for what
they are doing, but means a lot of this is an apples and oranges comparison.

~~~
Vendan
And now to get ranty:

Boltdb was being handled badly:
[https://github.com/maxpert/raspchat/blob/79315d861968c126670...](https://github.com/maxpert/raspchat/blob/79315d861968c1266701d07cb11d1db2fb95f47c/src/sibte.so/rascore/chat_log_store.go#L49)
You should be using bolt's helper funcs, so you don't forget to close the
transaction

[https://github.com/maxpert/raspchat/blob/79315d861968c126670...](https://github.com/maxpert/raspchat/blob/79315d861968c1266701d07cb11d1db2fb95f47c/src/sibte.so/rascore/chat_log_store.go#L74)
At least this is actually deferred, but still.

[https://github.com/maxpert/raspchat/blob/79315d861968c126670...](https://github.com/maxpert/raspchat/blob/79315d861968c1266701d07cb11d1db2fb95f47c/src/sibte.so/rascore/messages.go)
These messages are horrifying, would be much better switching back to json and
implementing a strategy like [http://eagain.net/articles/go-dynamic-
json/](http://eagain.net/articles/go-dynamic-json/)

It copies in an unlicensed "snowflake generation" thing?
[https://github.com/maxpert/raspchat/blob/79315d861968c126670...](https://github.com/maxpert/raspchat/blob/79315d861968c1266701d07cb11d1db2fb95f47c/src/sibte.so/rascore/snowflake.go)
and then essentially relicenses it? That's illegal...?

They do have a better json pattern for some stuff:
[https://github.com/maxpert/raspchat/blob/79315d861968c126670...](https://github.com/maxpert/raspchat/blob/79315d861968c1266701d07cb11d1db2fb95f47c/src/sibte.so/rascore/transport_json_decoder.go)
but I'd still steer away from reflection based, there's so much better ways of
doing this stuff, like so: [http://eagain.net/articles/go-json-
kind/](http://eagain.net/articles/go-json-kind/)

~~~
dullgiulio
[https://github.com/maxpert/raspchat/blob/79315d861968c126670...](https://github.com/maxpert/raspchat/blob/79315d861968c1266701d07cb11d1db2fb95f47c/src/sibte.so/rascore/snowflake.go#L83)
This is a spinner?! No wonders the code is slow.

The general use of inheritance and very non-idiomatic code makes me think this
is another person who ditched Go before understanding any of it. It seems a
very popular sport.

~~~
jimsmart
As the time-old adage goes "a bad workman blames his tools".

------
jimsmart
If instead of ditching Go he had posted some kind of help request to the
'gonuts' group / mailing list, I'm 100% certain that several people would've
helped him with code reviews and feedback. I've seen this happen in the gonuts
group countless times, including contributions/assistance from the core Go
team that hang out there :)

As noted by other commenters, below, the code seems to have some issues. And,
if it still didn't perform well after addressing those, somebody in gonuts
would've helped teach how to profile it, and then expert-eyes could have
looked over the profiler output and provided further feedback.

------
krylon
So, I am not a big fan of Javascript. I do not despise it or anything, I just
never got to like it. I guess.

I did really love POE, though, so when I heard of Node.JS, I thought I will
probably like this very much.

I am not sure what happened. I think it was the tutorials being always out of
date. Node.js seem so be such a fast-moving target. I do not mind
asynchronous, callback-driven code. But when a tutorial that was written three
months ago fails to run because some library had a breaking API change in
between, that tends to drive me away.

Think of Go what you want, but its policy towards backward compatibility is a
big plus.

~~~
iends
You're comparing core libraries of Go with non-core libraries of Node.js.
Since Go libraries are not versioned without a tool like dep, you're putting
trust that master doesn't have breaking changes vs hoping the maintainer
follows SemVer correctly. In fact, even the core Go team has problem with this
and has to revert changes to things under golang.org/x/ when they break
backwards compatibility.

~~~
acover
Node's fs library changed within the last 2 years. I copy pasted code and it
failed.

~~~
mmcnl
To be fair, you shouldn't be copy pasting code anyway. But I do understand
your feelings, any tutorial from before 2016 probably isn't relevant anymore.

~~~
acover
I remember the error now. I was using an older version of node (installed via
apt) but the current version of the documentation.

Stupid mistake. Please ignore.

------
riskable
I feel his pain about the lack of decent WebSockets support in Rust. There's a
few Websockets implementations but all of them are meant to run on a separate
port from the web server. As in, they want you to run your web server on port
443 and the websocket on... Something else. Which makes zero sense (browsers
will deny access to the second port because of security features related to
SSL certificates).

Also, unless you go low level (lower than frameworks like Tokio) you can't
easily access file descriptors to watch them (e.g. epoll) for data waiting to
be read. It makes it difficult to use WebSockets for their intended purpose:
Real-time stuff.

Rust needs a web framework that has built-in support for Websockets (running
on the same port as the main web server) and also provides low-level access to
things like epoll. Something like a very thin abstraction on top of mio (that
still gives you direct access to the mio TcpListener directly).

In my attempts to get Tokio reading a raw file descriptor I just couldn't get
it working. I opened a bug and was told that raw fd support wasn't really
supported (not well-tested because it only works on Unix and cross-cross-
platform stuff is a higher priority). Very frustrating.

I wish the Tokio devs didn't make the underlying mio TcpListener private in
their structs.

~~~
indream
I've found two frameworks supported websocket:

[https://github.com/tomaka/rouille](https://github.com/tomaka/rouille)

[https://github.com/actix/actix-web](https://github.com/actix/actix-web)

Ref: [https://github.com/flosse/rust-web-framework-
comparison](https://github.com/flosse/rust-web-framework-comparison)

------
inglor
I've dabbled a lot with Go. I've found it _very_ effective to a wide variety
of problems I don't really have most of the time.

If I wanted to implement RAFT I would probably pick Go. If I want a simple
REST/GraphSQL server then Node.js is so much easier. `async/await` is nicer
for me than goroutines and I find my code easier to reason about.

Full disclosure: I'm a Node.js core team member and a Go fan. Part of my
reasoning might be how much nicer Node.js got these last couple of years.

~~~
dullgiulio
Channels are basically the primitive with which you can implement futures,
thread-safe queues, etc.

Can you elaborate on why you think async/await is easier (for you) to reason
about than a goroutine and a channel?

~~~
inglor
Yes,

An async function is explicitly async (in its definition).

The syntax is obvious which is why JavaScript chose to go that route (rather
than adopt channels at a language level for example).

Plus, 95% of the time I care about the singular return value of a function - I
just want something that's a function but async - and not a green thread
that's using a channel to send information back. Both conceptually and in the
code it's a lot simpler.

~~~
anonymoushn
Most of the time you write `let x = await foo()` in js should become `x, err
:= foo() if err != nil then return nil, err end` in go, not anything to do
with channels

~~~
always_good
What about `const [a, b] = await Promise.all([thing2(), thing2()])`.

Or

    
    
        const results = await Promise.map(xs, x => process(x), { concurrency: 2 })
    

Or having 2 concurrent loops processing some data?

All things that require more boilerplate in Go while trivial in Node.

------
clandry94
I've been working on a similar project lately which also uses the
gorilla/websocket library. I just tested connecting 1500 connections in
parallel like was done in this link for Raspchat, and my application only uses
75 MB along with all other overhead within it. I'm not sure how this would
cause a Raspberry Pi with 512MB memory to thrash and come to a crawl unless
Raspchat has a ton of other overhead outside of connection management.

------
tschellenbach
I'm working on the exact opposite migration at the moment :) (Most of our
stack is Go, but we use the excellent Faye library written in Node) The Node
code is really well done.
[https://faye.jcoglan.com/](https://faye.jcoglan.com/) Nothing wrong with the
Node codebase. In our case we just had to add a lot of business logic. I could
have done that in Node (we did for a long time), but I decided that with the
latest set of changes we'd bring this component in line with the rest of our
infrastructure.

It's hard to know without the code, but the author seems to be doing a few
things wrong:

1\. You only need a few channels, not N. Maybe 4-5 is enough. 2\. In terms of
goroutines you only need as many as are actively communicating with your
server. So creating a new connection creates a goroutine, sending a message to
a channel creates a goroutine etc. 3\. You need something like Redis if you
want to support multiple nodes

For inspiration check out this awesome project: [https://github.com/faye/faye-
redis-node](https://github.com/faye/faye-redis-node)

This will perform well in Node and even better in Go.

------
_ph_
Besides that, as many here have pointed out, this sounds like a problem
somewhere hiding in the Go code ruining the performance, it is certainly true,
that an event-handler based approach is increadible efficient to manage a high
number of simple requests with limited resources. If every request can be
handled in a single event, it only has advantages. It does not require many
resources and you don't have to deal with any synchronisation issues.

In many typical web applications you have less if no interaction between the
connections, but rather complex logic running at each request. There the
event-based approach, which must not block, is getting more complex to manage
and you want to use all cpus in the system. There a goroutine based approach
should shine much stronger, as the goroutines may block and you don't have to
spread your program logic across callbacks.

------
zokier
As someone who has neither Go, nodejs, or RPi experience the results seem
surprising. Many have already commented that the author must have been doing
something wrong; the code is there for everyone to see, so could some wiser
gopher take a look and tell whats actually going on here?

~~~
Vendan
I did, and wrote some else in here, but a lot of it boils down to poor choices
in the Go code. They're using an embedded K/V store designed for high read
loads and are writing to it often, there's really complex concurrent lockfree
datastructures, and very poorly designed deserialization systems. On the flip
side, they switched to Node and to a db that can deal with mixed read/write,
nixed all the complex datastructures, and node can deal with unstructured
JSON.

------
nevi-me
This is a very interesting direction to take. I've built a lot of my personal
stuff on JS, and TBH, the one thing I really wish I had right now was a
statically typed codebase.

I spend a lot of time thinking about why I'm creating a certain data model,
whether I might need to change something in future, etc. About 60% of my
productive time is spent thinking about how and why, so I hardly refactor.
However, when the need arises, I wish I had something like Kotlin.

For the past few months I've been writing new JS code in TS, adding types here
and there, I haven't tried out Kotlin on JS, but I'm hoping to go there.

I'm learning Go, but for other reasons. I find JS to be performant, my oldest
active codebase has been around since the v0.8 days.

~~~
KirinDave
You have two excellent options:

TypeScript and PureScript.

Even better, they play fairly well together. You can work in Purescript but
still provide well typed integration points for contributors that can't.

And uh, no one here is talking about how fantastically slow Go channels are.
But I just saw the code last night, and it's not hard for 20 year old
techniques to beat out a "futex for literally every message send" technique.

~~~
nevi-me
I'm already using TypeScript, but you sometimes have to bend over backwards to
get it to work nicely with a huge JS codebase (which I have).

Talking about channels, I haven't gotten there with my Go learning. Few things
beat websockets + a nice wrapper (with simplicity). For example, I'm doing
realtime transit, and as part of it I'm sending out hundreds of vehicle
positions per 5-7 seconds.

On the back-end I have a pubsub through a gRPC stream, and I stream positions
to socket.io topics. Works beautifully, I can't imagine having to roll it out
manually over websockets. In another thread here, someone mentions how non-
trivial working with websockets is in Rust. I think that until we have a
socket.io (server) version for other languages, Node.js will always beat most
other languages.

~~~
KirinDave
I'm having trouble understanding what you're saying here, but I think we
agree?

Please do consider checking out PureScript. It's a lot of the good parts of
Haskell and some of it's libraries look like sorcery they're so good.

------
halayli
I don't use Go but I find the reasoning fueled by a confirmation bias to pick
JS.

goroutines are now 2k. If you use 2 goroutines per connection that's 4k. If
you have 10k connections that's roughly 20mb only which is very reasonable.

------
tankenmate
Why two (or three) go routines per connection? Why not one net socket (for
reads) and two channels (one for writes, other for pub/sub) and select()
between them? It seems like the OP is trying too hard to avoid event loops.

~~~
lenkite
Please upvote parent. This is the first thing that struck my mind too. golang
offers `select` for non-blocking calls. Deeply skeptical that node.js is the
best solution for a chat service. (unless the backend is serving rendered
UI's)

~~~
Thetawaves
You generally need one thread to read for incoming messages, typical:

    
    
      for {
          select {
          case <-finish:
              done <- true
              return
          default:
              // read from the socket
          }
      }
    

You need another thread to listen for messages on a channel and send them out
the socket typical:

    
    
      for {
          select {
          case <-finish:
              done <- true
              return
          case msg <- msg_out:
              // write msg to socket
    
          }
      }

~~~
tankenmate
Instead of?

    
    
      for {
        select {
        case <-finish:
          done <- true
          return
        case msg <- msg_out:
          // write handling
        case <-pubsub:
          // handle oob controls
        default:
          // read handling
        }
      }

~~~
Thetawaves
You wont be able to write messages while blocked on the read in this scenario.

------
majewsky
> Since go does not have generics or unions my only option right now is to
> decode message in a base message struct with just the @ JSON field and then
> based on that try to decode message in a full payload struct.

If he is in control of his protocol, why did he not shape it to suit his
parser library? Instead of this:

    
    
      message1 = { "@": "foo", "foo1": 1, "foo2": 2 }
      message2 = { "@": "bar", "bar1": 1, "bar2": 2 }
    

Do this:

    
    
      message1 = { "foo": { "foo1": 1, "foo2": 2 } }
      message2 = { "bar": { "bar1": 1, "bar2": 2 } }
    

Then you can read both types of messages into a single Go type,

    
    
      type Mesage struct {
        Foo *FooMessage
        Bar *BarMessage
      }
    

After parsing, that element which is not nil tells you which type of message
was sent.

~~~
KirinDave
I support ports of a Golang library and protocol that did this and I am very
tired of having to suffer Go's anemic type system in every other language I
work with.

Please stop infecting us with Go-specific type tags (that btw make the
protocol versioning story much more complicated) and either demand Go support
generics like every other modern statically typed language, or accept your
language is ill-equipped for parsing and check if things = nil a lot more.

Don't advocate for pushing your tooling's problems out on your peers.

~~~
Rapzid
This isn't Golang specific; most statically typed languages are going to fall
back on a similar pattern(TypeScript, C#, Javascript, etc). Take a look at
AWS's API schemas and you will see. Personally, I loath API schemas designed
with the assumption of a dynamically typed language.

~~~
KirinDave
What steams my clams is folks using go type names in f Label fields, or making
surprisingly behavior with fusion types that and up actually seeing use in the
wild.

------
dudul
I know very little about both Node and Go (currently learning the latter but
haven't done anything really interesting so far :) - but, really it's hard to
believe that Elixir/Phoenix would be disregarded so quickly if the crux of the
problem is to have good pub/sub support.

------
_Codemonkeyism
No clue about Go, but how can it take more memory then Javascript on Node?
This is hard to believe.

~~~
mattnewton
I think it’s the event loop model reuses more per connection. He mentions
nothing is stopping you from implementing an event loop model for the pub-sub
in go, just there wasn’t any library support and he didn’t want to spend time
building it out when it is the default model in Node.

~~~
iends
There was an event loop implemented in Go on HN a few weeks/months ago that
people might find interesting:
[https://github.com/tidwall/evio](https://github.com/tidwall/evio)

~~~
nevi-me
On a side note, tidwall (Josh) is one of the primary reasons why I want to
give Go a go. I love
[https://github.com/tidwall/tile38](https://github.com/tidwall/tile38).

------
igotsideas
I was curious about the memory usage for Elixir. It’s the second time I’ve
read (without details).

~~~
kenhwang
I wonder if something like [http://nerves-project.org/](http://nerves-
project.org/) would've been suitable for his use case; not sure how seriously
he was targeting a RasPi.

~~~
dnautics
I thought exactly the same thing.

------
drinchev
I'm happy about this article, not because of what it says, but because of the
popularity it got. NodeJS is not as bad as people think and I'm really excited
about when it will regain status in the "pros" community.

~~~
camus2
More like Go has reached peak Go in terms of hype and people are going back to
the stack they know best. Go is absolutely not a silver bullet, its community
hates "creative solutions" and at the end of the day it's not that enjoyable
to write for a lot of people. Go ecosystem isn't that big either compared to
JS.

------
divs1210
Hi!

I'm implementing channels/coroutines in clojure[0] and js[1].

These are alpha quality right now, but I already have channels with
backpressure and async go blocks. I wrote the js implementation to show that
these could be ported to any language.

The main thought behind this is: CSP - S = Communicating Processes = Green
Threads

[0] [https://github.com/divs1210/functional-core-
async](https://github.com/divs1210/functional-core-async) [1]
[https://github.com/divs1210/coroutines.js](https://github.com/divs1210/coroutines.js)

------
partycoder
Open source software often makes use of the wisdom of the crowds to move
forward, and when those crowds are in average less prepared, the results are
comparably bad.

See the top answer for this question in StackOverflow:
[https://stackoverflow.com/questions/5062614/how-to-decide-
wh...](https://stackoverflow.com/questions/5062614/how-to-decide-when-to-use-
node-js) . The top answer with 1360 upvotes is wrong and had little to do with
the question. This is a recurrent thing in the node world.

Go ask this same thing on an Erlang, Haskell, Rust, etc. forum and I am sure
the right answer will come up quickly.

------
verroq
Why not use mutexes and callbacks instead of channels for pubsub?

~~~
iends
This is not considered idiomatic Go and would be a premature optimization
unless you ran a profiler and determined that channels are actually your
bottleneck. It sounds like this has not been done.

~~~
calcifer
Not only that, but channels are internally implemented with mutexes anyway, so
a naive mutexes+callbacks implementation wouldn't be any faster.

~~~
iends
Channels have a performance overhead over handling the locking yourself. Maybe
look at stuff from Tyler Treat to learn more (maybe his most recent talk?
[https://www.youtube.com/watch?v=DJ4d_PZ6Gns](https://www.youtube.com/watch?v=DJ4d_PZ6Gns))
but there are some other discussions (mostly with earlier Go versions) but
maybe something changed?

------
RomanPushkin
We had pretty good number of sim.connections on 512MB instance. Unfortunately
there is no enough details on methodology you used to compare, so I can't
compare the number of clients it can support. I appreciate if you can do it
yourself.

[https://github.com/ro31337/hacktunnel](https://github.com/ro31337/hacktunnel)

------
_jsdw
Regarding Rust not having a mature websockets lib, this library seems to be a
decent websockets implementation at a glance and passes all of the autobahn
tests:

[https://github.com/housleyjk/ws-rs/](https://github.com/housleyjk/ws-rs/)

In any case, that is where I'd begin :)

------
qaq
That's the funny thing modern Node is surprisingly usable and if using TS has
very advanced tooling.

~~~
iends
It's incredibly easy to throw stuff together in Node.js, it's much harder to
maintain over time. TS helps with this but you still run into really hard to
diagnose memory leaks (mostly in 3rd party packages written in C++, but
sometime in Node core itself). The tooling for these type of issues is really
poor in my experience.

~~~
fermuch
My main concern with node is how fast everything moves. Try to maintain a
project that started with es5, then migrated to es6 (but some legacy code is
still es5), then added some es2017 and now is adding slowly typescript to the
monster, and all of this in just 3 years!

Things become a mess in no time, tooling changes constantly, and maintaining
legacy code while developing new parts with current best practices helps you
build a Frankenstein in no time.

(and don't even mention about coffee, or it'll make me start ranting about the
move from coffee to es6 and now TS!)

~~~
romanovcode
The secret is to rely on as little npm packages as possible and if you do,
rely on very very popular ones.

~~~
tonyedgecombe
Which applies equally well to Ruby, Python, Go etc.

------
Arzh
I think you are right, go is not the best language for your project, single
threaded just moving blobs of data around node is a good solution. I'm not
sure why anyone would have a problem understanding that. (And I'm a guy who
hates node but loves go)

------
nickthemagicman
TJ Hollowaychuck the author of express.js had the opposite opinion.

[https://medium.com/@tjholowaychuk/farewell-node-
js-4ba9e7f3e...](https://medium.com/@tjholowaychuk/farewell-node-
js-4ba9e7f3e52b)

~~~
Vendan
As did Ryan Dahl, the creator of node.js

[https://www.mappingthejourney.com/single-
post/2017/08/31/epi...](https://www.mappingthejourney.com/single-
post/2017/08/31/episode-8-interview-with-ryan-dahl-creator-of-nodejs/)

~~~
nickthemagicman
Ha! Thats insane the creator of Node switched to Go.

~~~
the_common_man
Node is very different now from what he started.

------
DiThi
Would Matrix be a good fit for this use case? There are a bunch of different
server implementations and the (web based) protocol is relatively simple and
very well documented.

~~~
philsnow
Last I looked at the reference implementation ("synapse"), the authors hadn't
started to optimize its memory usage yet, so it was pretty thrashy on the
512MB ram droplet I ran it on.

~~~
drdaeman
It's still the case, and it requires quite a lot of CPU time too.

There is no way Synapse is a good fit for something like RPi. Maybe when
Dendrite is ready, it will make this possible.

------
andmarios
Using go's websocket library would also be viable.

For the receiving end, go can use http handlers for websockets. So when a
message from any websocket is received the handler will be spawn and process
it (just as any http request). Preferably dispatch it to a big central
channel.

Keeping separate channels for each websocket seems overkill to me. Go has
maps. Create a map with all the websocket connections and maybe smaller maps
for each chat room, then set n workers to listen to the central channel and
dispatch messages directly to each room member.

------
amelius
This is surprising, as this is exactly the type of application where you'd
think Go would perform well.

~~~
dboreham
Perhaps this article is the CS version of "The Onion"?

------
noncoml
Erlang

~~~
rmetzler
or Elixir

------
jorblumesea
Please look into typescript. Makes js development sane.

------
coolaliasbro
Computers.

------
dictum
The reverse @tjholowaychuk ;)

(If you're a 10x engineer, check his new tool for 1e10x engineers:
[https://news.ycombinator.com/item?id=15731936](https://news.ycombinator.com/item?id=15731936))

~~~
dz0ny
He went meta recently. Made node_modules cleaner in golang
[https://github.com/tj/node-prune](https://github.com/tj/node-prune) :D

