Blog of Plataformatec, the Brazilian consultancy behind much early Elixir work, covering Elixir, Erlang, Rails and web architecture.
HN commenters link its posts on Kubernetes and the Erlang VM, Elixir variable rebinding, and mocks, using them to explain how Elixir and k8s fit together.
I'm working for a software consultant in Singapore. We tried Elixir for a few projects, and now we're still trying. So, I can share a few experiences from my view.
- Elixir and the Phoenix framework is perfect when building concurrency systems. Our company had made a ticketing system, solving the tickets for "Cirque du Soleil - Hongkong 2018". During two months of the event, our system (one Elixir/Phoenix server - scale up to two - one load balancer) can handle more than 5000 tickets per second. We were so excited. Unfortunately, the client's company closed after that. So the project was closed too.
- The second project was a product of a finance startup. Not so much thing I can share in this project, but the main feature is chatting. We used the Phoenix Channel and Graphql Subscription for this feature.
- In the side project, one member of the team used Phoenix Liveview to build a Coup game (board game).
So, in general, when I work with Elixir, I see a few pros and cons:
Pros:
- Fast. Functional programming is really fun. I love pattern matching.
- Not so hard to write tests. It's easy to mock external APIs. (Check out this blog: http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/)
- Easy to deploy. After dockerize the application, I can run it anywhere. The ticketing system was run on AliCloud, and the second project was run on Kubernetes (AWS).
Cons:
- Lack of supported libraries for external services. Sometimes, our team has to write a…
Elixir creator here. This is a very good question and a very hard one to answer because there are so many facets to consider. I will provide some bullets instead of doing direct comparisons.
* Concurrency is not an after thought. The primary abstraction are actors (Erlang processes) that are isolated and communicate with message passing. Data structures are also immutable which helps greatly with concurrency and also leads to clearer code (it is really comforting to know that a value won't change under your feet).
* The Erlang VM: we are leveraging 30 years of knowledge in building and running fault-tolerant systems, thanks to the underlying process model. We could really go deep into this topic so I will just link to an article I wrote some time ago talking about Elixir and microservices: http://blog.plataformatec.com.br/2015/06/elixir-in-times-of-microservices/
* Performance may also be a big deal depending on what you are doing. Elixir/Erlang are not good at number crunching but if you are running anything behind TCP/UDP, it will give you better throughput and a consistent latency compared to Ruby, Python, Node.JS, etc.
* We don't have compile time checking but we do provide type specifications and a way to check them: http://elixir-lang.org/docs/master/elixir/Kernel.Typespec.html. The integration between Elixir and Dialyzer is suboptimal today but if type checking is a big deal for you, it will certainly help.
TL;DR: Elixir is for building and running…
Yes, this is similar to Plug, but we only have a "connection". There is no distinction b/w request/response. A series of Plugs form a transformation layer on the connection where plug middleware can transform the connection, send a response etc. "Yielding to the next plug" is just a matter of returning the connection. There is no explicit yield. You either halt the connection (don't invoke further plugs) or continue the stack by returning the conn unhalted. Where your "upstream" concept fits is a connection can have callbacks bound that are invoked just before the response is sent, this is where a cache plug would cache the response. Plug avoids the issue of separating request/response because for things like streaming, sending a response does not mean that the stack is done with the request. Rack has this issue with streaming and José put a great deal of thought into Plug around the lessoned learned from Rack style APIs. You can read about his thoughts here:
http://blog.plataformatec.com.br/2012/06/why-your-web-framework-should-not-adopt-rack-api/
> From what I understand, once a Plug chooses to reply, the response ends at that Plug.
In Phoenix, we halt the connection when you use most of our functions that send a response, because there is little you can do afterwards. The Plug response API itself never halts when a response is sent.
Someone else has already noted this: Don't use the native Javascript Number type for calculations involving currency. There's some great reading available online on this subject.[1][2]
There are various libraries around that can facilitate precise decimal arithmetic in Javascript. A well known one is BigNumber.js (https://mikemcl.github.io/bignumber.js/)
A great quote that I found on StackOverflow regarding this issue and wrote down:
"When you are dealing with things that are measured (speed, distance, weight etc.) rounding is OK as the accuracy of the original measurement is probably less than the accuracy of your calculation, and users accept that the final number is still an estimate even if its a good estimate.
When you are dealing with things that are counted (money, people, ICBMs) rounding errors are a disaster. There is always an arithmetically correct answer, and, often the accuracy of this answer is enshrined in regulation, treaties and tax law. Providing a slightly wrong number gets you a free ticket into the Kafkaesque world of auditors, tax collectors and government inspectors; you may never escape with your sanity intact." - James Anderson
[1] https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency
[2] http://blog.plataformatec.com.br/2014/09/floating-point-and-currency/
Kubernetes and Erlang are really solving different problems. Kubernetes makes sure my OS process is running - Erlang lets me compose my application from a set of actors in a single OS process that are fault tolerant and supervised. Somewhat similar problems but at totally different levels of abstraction. Erlang out-of-the-box isn't especially great at building distributed applications and benefits from leader services, message queues, RPC mesh frameworks etc nearly as much as any other application; its just that you can solve pretty small-scale simple problems without reaching for those in some cases. Jose Valim (creator of Elixir) wrote about this in a great blog article a few years ago: http://blog.plataformatec.com.br/2019/10/kubernetes-and-the-erlang-vm-orchestration-on-the-large-and-the-small/
This is not really a problem in zig. If you really need a destructor, write a .deinit() function in your struct, and call that. For your case with mocks (note mock as a noun, not a verb, which is a bit of an antipattern anyways, see: http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/). That aside:
Go doesn't have generics so you can't really do this, but in the case of zig, your function would be parametrized on the type, so you would issue it a struct to be shared by the tests that has the common initialization upfront, make_ functions that return the pre-initialized stuff, followed by running the function, followed by no-op deinits, followed by whatever forensics you'd like to run later.