Why I program in Erlang (2012)
evanmiller.org
evanmiller.org
The third project was a complete rewrite of a really badly created dating website written in PHP that would crash at least once a day and would be unbereablly slow, built by 5 people for a full year. We rewrote everything in 3 months, with two developers, while adding features that were impossible to do otherwise. One lighting bulb moment is when we realize we could use ejabberd and the xmpp protocol for way more than messages and chats, but also to handle our news feed. Once we launched the rewrite, the website became extremely fast and stable. Since then, the BEAM has always been my go-to platform, even if now I use elixir.
There is no platform/language that makes me feel more productive and confident about the code I write. The fact I can introspect the code (the repl is way more than a repl), the crazy stability of the runtime, and the simplicity of the language makes it a no brainer.
LiveView is a game changer. We built our entire e-commerce stack on it, it runs flawlessly.
My only issue with the Elixir/Erlang ecosystem is its lack of business oriented libraries and solutions: - not many payment gateway integrations - no CMS / E-commerce platforms - quite no ready to use open source apps
Yes it's not as fast in pure performance as many other programming languages, but in practice, it's fast as hell due to concurrency and performance doesn't degrade with load.
And so many exciting things are being developed, from LiveView based native apps to ML and IOTs.
Fwiw I was using firestore for real time before since it was easier. Now I like phoenix channels (I’m still struggling with the language truth be told, but it makes sense after reviewing at the code like eight times).
My app (social app, still wip) is currently using flutter for the mobile apps, a nodeJS backend for the rest APIs, and a phoenix server for the chats. I’d be interested to have a discussion if that’s okay.
The ejabberd system was great at the time but lacked flexibility. But I think we saved at least 3 months of dev thanks to it.
I see you are using 3 languages, that's a lot of things to coordinate and keep in the brain. May I ask why you use node for the rest API and not elixir ? That would simplify things a lot.
Sure you can email me at jm at producture dot com
I am using 3 languages because I originally started this project using nodeJS+pg backend + flutter mobile apps. I've been experimenting with a lot of stuff as well, at one point switching to Go because I had some CPU heavy stuff, but I find myself going back to nodeJS because I'm most productive in it and I feel that its performance (I/O wise, since I'm doing DB or API calls) to dev time ratio is pretty good especially considering the number of well made dependencies and SDKs available.
I am still a noob at elixir (only a few days of total experience), so maybe some day I might migrate the REST API over. However, Phoenix channels are amazing and for that piece of the overall system I think I am happy to have stumbled upon it. I found that using phoenix channel API is a lot easier to work with both from server perspective and flutter client side. I need to figure out the proper way to auth (right now just auth'ing on socket connect against my nodeJS API, then continue).
My backend is super modular, feature wise, so I can swap out any piece for something better easily.
I been working on it since May :D
This matches my experience with one of the few opportunities I had to use Elixir in "production". I took over as a replacement "technical co-founder" (in actuality: a part-time contractor) for a startup, and inherited a Node codebase that would take an hour or so to batch process a bunch of student data every night. The Elixir replacement I wrote did it in minutes (even before actually making any effort to parallelize it), and it thus turned into an hourly job and eventually near-realtime. I probably could've made similar gains while sticking with Node, but it would've been a lot harder, and the rewrite ended up creating a lot more opportunities for feature development.
Unfortunately that startup failed to raise funds, and it got put on permanent hiatus once the founder's runway ran out. I also nearly became an exception to the "nobody is ever fired for picking IBM" rule when I migrated the hosting from Heroku to Bluemix (the latter deciding to eat our Postgres container for lunch, prompting a mad scramble to migrate yet again to AWS). Fun times, fond memories.
CB would get frustrating when wanting to extend certain functionality however, which phoenix isn't quite as bad about.
It’s one of those things that we want to release when we get time as think the primitives will be useful for people.
We have payment gateway solutions (stripe, mollie, buckaroo). See: https://github.com/zotonic/zotonic_mod_payment
It has a full blown MQTT communication bus. Browsers connect as mqtt clients to the cms via a web socket.
As a huge Erlang fan, I’m not certain I’d say “general purpose computing” is a strength of it though.
From the following:
https://www.erlang.org/faq/introduction.html
1 What is Erlang
1.1 In a nutshell, what is Erlang?
Erlang is a general-purpose programming language and runtime environment.
Erlang has built-in support for concurrency, distribution and fault tolerance.- Web -> Phoenix ( https://www.phoenixframework.org/ )
- Parallel data processing (like scrapers, ETLs, etc...) -> GenStage / Flow / Broadway ( https://hexdocs.pm/gen_stage/GenStage.html )
- IOT -> Nerves ( https://www.nerves-project.org/ )
Soon:
Mobile Apps -> Elixir Desktop ( https://github.com/elixir-desktop/desktop )
ML -> NX ( https://github.com/elixir-nx )
- Ecto (data mapping) is fantastic, really glad they separated the SQL part - Oban is fantastic (job processing) - Liveview is really good. (frontend framework that uses websockets to build realtime apps that doesn't require writing JS (uses JS under the hood though)).
- embedded systems (network switches, control systems)
- middleware (databases, message brokers)
- generic web services
Why I would use it:
- it has a lot of operational support tools to remotely monitor and debug a running production application built into the runtime.
- it's not necessarily the fastest language, but it's pretty fast for many things. The thing is, if you program in idiomatic erlang from the start, your application will naturally scale well without having to do anything, and will even handle overload gracefully.
- Erlang and Elixir are both well designed and expressive languages with few flaws and intelligently made tradeoffs. It's an enjoyable experience.
Are Elixir and Erlang very similar? Do they belong to the same family?
Clojure : Java :: LFE : Erlang
Knowing Erlang + Scheme and CL, LFE like exactly what I expected, with a strong Erlang vibe. Knowing Java + Scheme and CL, Clojure felt nothing like Java except that it could access Java classes and objects.
I love the opinionated, constrained nature of Erlang and the BEAM, vs the kitchen sink approach of Java and the JVM.
That... depends. If you work with only highly disciplined people who want to do the right thing always, then yeah, I agree. But if you work with people who have deadlines or need to get stuff done _now_, you could see functions that take unnecessary arguments, or excessively large records, and those sorts of things are hard to work with.
I think this person is an excellent engineer who loves to code and is really good at it, but hasn't worked with Erlang in a professional capacity. Which...
> I have spent a large chunk of my free time programming in Erlang
ding ding ding
That is to say: I agree with you, and I think there's some correlation between "software at the mercy of changing business requirements" and "software written in popular class-oriented languages".
Like, if you go from one type of sort to needing a bunch of different sorts, you might need to make the sorting pluggable, but that shouldn't change the observable behavior of the existing sort type, assuming it gets to stay. That would be a refactoring, plus whatever sorts you needed to plugin.
> improve the quality without changing the behavior
IMO, quality cannot be judged without reference to requirements, except in extreme cases. Within a broad range of "code that doesn't make your eyes bleed", comparing one approach to another is difficult to do without reference to how it specifically fulfils the requirements. When requirements change, they may reveal that your existing code doesn't fulfil them well, or doesn't easily generalise, can't be reused, etc.
So when you need to change the code, it may first be necessary to do some refactoring. But refactoring without a prompt, without new knowledge entering via changed requirements, is just fiddling with aesthetics.
Commonly known as 'making your code more maintainable' so that the following programmers who work on that code do not find your address, look you up and murder you for your crimes against humanity.
I heartily endorse tidy code, good naming, etc. I would put that, though, in a separate category from refactoring.
> Otherwise why even keep the source code at all? If you take the position that there's no point in maintainability without specific maintenance goals, why not keep only the compiled binaries?
"Access to source code" is a maintenance goal, it just usually goes without saying and is rarely an issue. It also doesn't involve making any changes to the code, so I'd say it's also in a separate category to refactoring.
> I heartily endorse tidy code, good naming, etc. I would put that, though, in a separate category from refactoring.
I don't see the distinction. In either case you're trying to change the code into some equivalent code that's easier to comprehend; separation of concerns may involve more subjective judgement than applying consistent formatting to your source code (though I've seen plenty of subjective arguments about the right way to format code), but they're both the same kind of work.
> "Access to source code" is a maintenance goal, it just usually goes without saying and is rarely an issue.
I've never seen it considered a "goal" for any usual definition of goal. Rather keeping access to the source code is something you do as a means to an end; you want to make sure you can easily achieve future maintenance goals, even if you don't yet know what those goals are. And I'd take the view that refactoring to make your code more easily comprehensible is valuable in the same way and for the same reasons.
Renaming is absolutely refactoring since refactoring is a process, not just individual acts but often a series of refactorings to achieve some goal. For instance, you may be trying to make two (or more) pieces of code look the same so you can replace them with a single implementation. A stupid kind of thing I saw and addressed recently (pretend these are more useful, simple examples for demo):
func some_fun(f *os.File) string {
var contents []byte
contents = make([]byte, 100)
count, _ := f.Read(contents)
return string(contents[0:count])
}
func same_fun_different_name(conn net.Conn) string {
buffer := make([]byte, 100)
n, _ := conn.Read(buffer)
return string(buffer[0:n])
}
These ought to be one function. They do the same thing but have different names, different names for internal variables, and different type signatures. But the types are actually unimportant, they both conform to the `io.Reader` interface. But the code doesn't quite look the same, so if it were longer we might have a hard time believing the two functions are actually doing the same thing. So step 1 of the refactor is rename and reorganize the function internals so they match. Let's decide the second function's form is preferable (we flipped a coin): func some_fun(f *os.File) string {
buffer := make([]byte, 100)
n, _ := f.Read(buffer)
return string(buffer[0:n])
}
Repeat as much as necessary and then you end up with one piece of code in the end that covers both cases (and many more): func better_named_fun(reader io.Reader) string {
buffer := make([]byte, 100)
n, _ := reader.Read(buffer)
return string(buffer[0:n])
}
All thanks to refactoring by renaming.or simply reducing technical debt
Most likely this requirement was there from the beginning (serious project) or introduced later (like a prototype that had to go to production). But refactoring can occur multiple times at any time
This can be a compelling reason to refactor, but my experience is that refactoring is often called for without/before any requirements change. Typical cases:
- Existing requirements, and interactions between them, are better understood over time, presenting opportunities to serve them better
- Current design/APIs are brittle and adapt poorly to maintenance tasks
- Resistance to “premature” abstraction/DRY produces large scale redundancies, and consequently increases volume of bugs and time spent versus value of work
- Of course the opposite is true, with improper abstractions becoming unnecessarily entrenched and unwieldy
- Ecosystem/stdlib improvements make certain areas of custom code obsolete, or more of a liability than they’re worth
Is that wrong? I'm hesitant to say that it is. We're optimizing different things. Their approach is better in some situations - startups need to ship features fast, and if you don't have to touch the code again, it doesn't need to look good, it just needs to work. My method is optimized for sustaining engineering by adding up-front engineering time, which startup engineers may not have.
I think the most appropriate thing is to realize what situation you're in, and make value judgements there. If speed is required, optimize for speed. If sustainment is required, optimize for sustainment.
OO requires the programmer to make assumptions that create a maintenance burden. Elixir/Erlang organizes its code with modules, which leaves it less burdened by the constraints of OO. Consider the module system like a tree. You import functions that are wrapped in a module and just call them. Concurrency is baked in so cleanly that you only write single threaded code inside of a process.
Reducing the argument down to not having deadlines and not being used for "real" purposes is a bit reductionist and misses the point.
I've been writing a lot of plain JS in my free time lately and it's been awesome. Just like the article, I've written all kinds of things in it and it's been a breeze. I don't think this is because JS is great - it's just because I have a lot of practice and I don't have to worry about real business requirements, deadlines, other people, or other people's code. If these side projects of mine became team projects where each member has varying skills, interests, and experiences, I'm sure it'd become a mess - especially if I quit!
Similarly I use Nerves for a hardware control application. I have an operation that's doing a multi-step bit of communication where the actual comms is handled via an IC on an SPI bus. You build your layers of driver, abstraction and then eventually application code over the top, and I end up with a really clear top level function that basically looks like:
configure_radio()
send_request(flags, data)
wait_for_done_flag()
read_response()
All of this works perfectly, doing SPI transactions, waiting for responses and all operates perfectly on the same VM that's also monitoring some registers, as well as running a live web interface for introspection, handling graphql requests etc.And then if there's an error case that I haven't handled the code that communicates with the IC will all restart and reconfigure the IC, so that transaction will fail, but the next one will likely work fine.
[1] Processes here using the BEAM terminology - a lightweight userspace scheduled thread of execution. Similar to green-threads, fibres or goroutines.
I have learned that in order to get people to produce good Elixir code, the important thing is to get them to think about the data structures first and then the functions: how your information is represented on the way in and out, what your state looks like etc.
Even something as simple as parsing a request, get them to start from "ok I have a binary as my input and then it's going to produce a map/tuple/whatever". Once they think like that (and have some understanding of which datastructures work well where) then the functions you need fall out quite easily, they tend to have roughly the right set of arguments, and modules tend to separate quite cleanly
After gaining some recent experience in Go, I'd still choose Elixir for any web project. Phoenix/Ecto/Absinthe/LiveView are all extremely productive libraries for web projects. Tooling/infrastructure work may be another discussion.
The elixir community has been focused on a few key areas.
- The nerves project * An open-source platform and infrastructure you need to build, deploy, and securely manage your fleet of IoT devices at speed and scale.
- https://www.nerves-project.org/
- Phoenix Framework and Ash-hq etc which has a strong emphasis on web development/and generating APIS
- https://www.phoenixframework.org/
- Data Science/ Machine learning / Data Infrastructure
Its not until you get to their documentation you can figure out what it is: "Nerves defines a new way to build embedded systems using Elixir. It is specifically designed for embedded systems, not desktop or server systems." Why is this not front and center?
Also to find a single code example, you need to look it up online, because its only in their github repo? https://github.com/nerves-project/nerves_examples
Why do so many websites nowadays just NOT tell you what they are attempting to sell you? Its so weird and frustrating.
also, the page says one thing.
Nerves is the open-source platform and infrastructure you need to build,
deploy, and securely manage your fleet of IoT devices at speed and scale.
There's two CTA on the site.- 1) GetStarted - 2) What is Nerves?
Both then proceed to tell you exactly what nerves is and funnel you to code examples, guides, and case studies.
Elixir is essentially feature complete now. So what you might be feeling is the slower pace of language development due to this.
https://elixir-lang.org/blog/2019/06/24/elixir-v1-9-0-releas...
Maybe Elixir isn't as ubiquitous in other areas, but where I live, I'll be able to retire as an Elixir engineer, which is awesome since it is hands-down the best language I've ever used.
For comparison, I've coded in Erlang, Java, C#, C, C++, Scala, Clojure, JavaScript, Python, Ruby, Perl, F#, Node, Go, Lisp, Elm, Scheme, PHP, Dart, Swift, Objective C, Groovy, Pascal, D, Cobol, Assembly, and CoffeeScript. I think that's all of them anyway.
If you're interested in Elixir, learn it. I'll hire you if you're good and I have any openings.
This might be the best line ever I read on an HN article in recent years.
Joe could explain the basic ideas and where they came from so concise and humble [1], you can probably just binge watch this whole list, one other gem being "The forgotten ideas in computer science" [2]...
[0] https://youtu.be/bo5WL5IQAd0 [1] https://youtu.be/i9Kf12NMPWE [2] https://youtu.be/-I_jE0l7sYQ?list=PLvL2NEhYV4ZsIjT55t-kxylCU...
A single erlang node runs on a dedicated OS process. It consists of one or more Erlang processes, which are essentially Carl Hewitts actors, which are very lightweight, don't share any memory, and hundreds and thousands of them can be multiplexed onto the handful of cores of a typical server. The idea being that if one erlang "process" dies, the whole system doesn't come to a halt. There's also no stray pointers, because all communication is done through deep copying messages, so there's no pointers referring to the process of another memories process.
Where it gets murky for me is how it interacts with databases. There's mnesia which... I don't understand. Is it embedded? I know it can run on top of stuff like LevelDB and RocksDb, and those engines don't like you opening the same database multiple times on the same OS process. But how do you stop that happening?
Also how do you test the whole thing? You could test individual processes I guess, but testing the interaction between them all seems fairly impossible.
Doesn't this bog down from all the context switching and IO latency from message passing? Or does erlang have some special way of doing it
All that said, BEAM isn't chosen because of its high performance, it's chosen because it enables a simple programming model for distributed systems while being fast enough. It's gotten faster over time too; of course, so has everything else.
Mnesia is a application that in my experience runs on some of your Erlang nodes; it provides APIs and also a supervision tree that owns the data. Mnesia is a layer on top of key/value stores; there's some out of the box stores it can use, or there's bindings for LevelDB and Roc0ksDB I guess. When you do a Mnesia read/write/etc, that becomes (more or less) a message sent to an mnesia process, so I'd imagine the LevelDB and RocksDB APIs are only called by a specific mnesia process and there should be no attempt to use the same database by multiple Erlang processes, so you're good from a concurrency point of view.
You can use Mnesia from remote Erlang nodes, it's just sending messages, so easy peasy. However, in my use, we would typically put state management services up on the same nodes storing the data in Mnesia, so we'd send an application message to a process running on the node, which would turn that into mnesia reads/writes as appropriate, and send back a response if appropriate.
If you wanted to use an external database like some sort of SQL, you'd need to find a client and then you could interface with that like in any other language. You'd almost certainly want to put some sort of intermediate connection pool together though, cause most databases aren't thrilled with tons of connections (although there was an article today about going to 1M, so maybe). Our needs fit well with key/value though, so I never looked into that.
> Also how do you test the whole thing? You could test individual processes I guess, but testing the interaction between them all seems fairly impossible.
Everybody has a test environment; some people even have a test environment that isn't production. :)
Testing distributed systems isn't simple. You can test the individual processes, but the interaction between all of them can give rise to emergent behavior which is difficult to test. However an important thing to consider is the importance of testing is related to the cost of failures; if you lower the cost of failures enough, comprehensive testing is much less important. Isolation of processes means that in many cases the rest of the system goes on when one piece fails; of course, if that once piece is in the critical path, that's not true. Hot loading means you can update code without losing established state, which can significantly reduce the update cycle time compared to a system where you need to drain users and restart servers or even longer cycles like in mobile development or even lengthier cycles of traditional boxed software. If you were writing Erlang for boxed software, you'd need to spend a lot more time on testing than in a messaging server.
With the Beam and things like Ets you can often avoid reaching for things like Redis though.
1. No concurrency
2. Concurrency on the same machine
3. Concurrency between machines
Of course there can be more distinctions, but those are very helpful for almost every case that I had to deal with.
Erlang's BEAM really nailes #3. The BEAM is just so cool, I'm surprised there are no similar products out there (if you know any, please tell me!).
Unfortunately for #2 I really don't like either Erlang nor Elixir. To nail 2, I found pure functional programming languages to be the best by far. They allow extreme precise control over concurrency on the same machine (be it single-threaded or multi-threaded), better than any other paradigm I've used (including actors for example). My preferred language here is Scala, but others are doing really well too.
I wish I could run Scala on the BEAM, since Akka (actors on the JVM) are just not nearly as good as the BEAM.
The combination of those 3 allows you to do things like "run those two processes at the same time, where one process does something every minute and the other when a request comes in. Then merge the two together, only execute one action per 30 seconds even if there are more events from the two combined processes. But if an action fails, retry it 3 times while waiting a few seconds between each try. If the 3rd try fails then, depending on the error, stall the process that runs every minute for 10 minutes and only accept events from the request-based process..."
And so on. I found that in languages that don't support those explicit (pure functional) concepts, it is almost impossibly hard to get the logic right when multiple levels of concurrency and error-handling come in.
If you have prior JVM knowledge or want to build something productive then I suggest to start with Scala and ZIO (https://zio.dev/) using the same concepts as described for Haskell.
Then afterwards, have a look into STM (https://zio.dev/reference/stm/ for Scala or https://hackage.haskell.org/package/stm for Haskell). Those essentially allow you to do things that database often do for you (fine grained locking) - just more powerful and composible and in-memory without database IO.
With those things you are well equipped to deal with problems of type #2.
I do believe that the creator of Elixir (parent comment's poster) already knows the concepts. I might be wrong, though.
Why I Program in Erlang - https://news.ycombinator.com/item?id=4715823 - Oct 2012 (93 comments)
I am interested in the parallelism and multithreading, asynchrony and things progressing independently (concurrency)
I wrote a multithreaded Java Actor implementation. It can send messages between threads at 19-100 million messages a second depending on the variation. There are variations that generate messages in parallel and in different threads, in advance or as the program goes. If messages are created in advance it can add 1 billion integers a second. This is due to the fact that each thread can add up a different range of the integers. For comparison a single thread on my computer can add 1-1000000000 in 2 seconds. This means it doubles the performance of adding.
https://GitHub.com/samsquire/multiversion-concurrency-contro...
I also wrote a multithreaded interpreter that uses this actor implementation to support a "receive" and "send" instruction. It can also send jump instructions to other threads with a "sendcode" and "receivecode" instruction for those threads to change their execution.
is this cpu cache-friendly ?
I suspect the answer is probably moot.
But I also think that the answer would depend on what the bytes are being used for, how large the chunks are, etc. CPU cache utilisation is influenced by many factors, and can't be deduced from examining the data structure alone.
Note: I am more familiar with C++, so C++ digression here.
C++ has std::deque that is a similar non-contiguous chunked container, comparing it to std::vector(the contiguous container) it is really close for things vector is good at, and better than it at things vector is bad at like random insert and removal.
https://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vecto...
If your I/O list is a bunch of binaries you got from here and there and you don't need to inspect them, just pass them along to another destination, then maybe yes. When BEAM writes an I/O list to an FD, it's going to use scatter/gather I/O and at no time does BEAM need to form a contiguous chunk of memory with all the data; what the OS does with that is up to the OS though.
The idea is rather simple but powerful.
Let's say you want to send `"Hello " + name`, where `name` is a string, over the network. Traditionally, in C, you would allocate a buffer that is large enough and copy the "Hello " literal into it and then copy the `name` string before calling `write(fd, buffer, length)`.
If you wanted to avoid allocating the buffer and doing the copy, you would make use of `writev(fd, iovec, count)`, and this is exactly what the `iolist()` structure allows for. The erlang runtime system (erts) makes use of efficient `writev` calls instead of having to allocate temporary buffers just to send data over the network (something Erlang is notoriously good at) when you make use of `iolists`.
[1] https://github.com/gleam-lang/gleam [2] https://podcast.thinkingelixir.com/
This sounds like it may break cache coherence? Template rendering in Erlang may be faster than Ruby & Python but I suspect using non-contiguous blocks of memory isn't the reason.
OK this isn't a discussion about the usefulness of the programming language anymore, just the author's idea of fun. Not having libraries sounds like a horrible thing productivity wise.
- could not find a platform/runtime where to host it
- basic open source libraries are not available
It’s nice and all to be attracted to cool technology but we also have to be aware of everything else that goes with it: developer experience, oss libraries, community support, ease of use and so on
- Can be deployed to Heroku or Fly.io without much effort
- Well designed tools like mix
- There is a thriving community
- "basic open source libraries" highly depends on the context, but yes there are not as many as for more popular languages (yet)
There were many aspects considered, but if I remember correctly, the barrier to entry is way too high and new developers need to learn too many things at the same time. With this regard, the lack of a mature business-oriented ecosystem of libraries does not help. My overall impression interacting with the community was that there were either veteran programmers having worked with functional languages for 10+ years and working in teams of veterans (and therefore assuming you already knew plenty of things), or folks using it in toy projects.
Despite trying really hard, we didn't get the confidence that we would be productive and successful in this ecosystem as a company so we went back home to daddy Microsoft. In my view, with the introduction of Phoenix LiveView and such they are trying to market this tech as the easiest and most scalable ecosystem for the web, but their community fails to realize the extent to which people coming from an OOP background (especially Java, C# or even Javascript - which describes most back-end developers nowadays) are at complete lost in this world and have everything to (re)learn.
Another aspect is that even if this platform is very scalable from a technical perspective, functional languages in general are not that great for modeling complex business domains with evolving requirements: since pure functions are the key primitive, you end up needing to update or check plenty of functions whenever you introduce or modify a feature. And when you take into account the fact that Elixir is not strongly typed, you end up having to write plenty of tests to refactor with peace of mind (while a statically language would have provided you 99% of these checks for free).
I would only use it for the infrastructure layer of a system, not to manage actual business rules and business entities. But then, this means that you need to build a separate backend and an API. This is the reason we called it quit and decided to use a stack allowing us to do everything, and boost our productivity by removing the need to build an API. We were really seduced by the messaging capabilities of Elixir, but it just didn't work for us. Not just at a technical level but at a business level.
My takeaway is that if you have a team being already proficient in another backend language, whatever you may gain by building your product with Elixir may very well be lost by building your BUSINESS and your TEAM with Elixir. Even if you are building out a new team, there is a broader market of OOP developers available, and generally speaking, people have a much easier time learning programming through OOP concepts than through the mathematical abstractions backing functional languages like Elixir. Learning OOP with the abstract car and concrete cars tutorials is a breeze. From that point, learning a codebase properly modeled in an objected-oriented manner using DDD principles is a breeze. Learning typescript or even go was a breeze thanks to my C# background. However, learning elixir was quite a time investment, and I am quite senior having built a whole ERP system on my own and built and led multiple teams.
Elixir has true merits but it is definitely not a cure all solution, and too often people overstate how easy to use it is. It may be easy if you are already proficient with Elixir and functional programming but definitely isn't if you are new to this world. I'd treat it as an optimization and think carefully about whether or not one really want to use this stake. Truly outstanding from a technical perspective, but paradoxically not necessarily the best platform to build a business on (which took us a hard time to accept, but at some point we called it quit).
Believe what you see with your eyes, do not buy what people tell you and the hype around it. If you feel relief after modeling your problem space with it go for it, otherwise if you feel it is not working for you whether at a mental level or at an implementation level, follow you guts and move on. Do not waste as much time as we did. Not trying to take anything from the Elixir community but this had to be said to counter balance rosy comments that honestly sometimes seem to have no basis in reality. I doubt most of the people singing most of these praises on Elixir have actually tried to go all in with it and build a business on it.
Erlang is just an engineering marvel, but in the real world, engineering is always a compromise.
I doubt most of the people singing most of these praises on Elixir have actually tried to go all in with it and build a business on it.
Lots have and some quite successfully
* divvy - https://getdivvy.com/blog/why-divvy-uses-elixir/
* discord - https://discord.com/blog/how-discord-scaled-elixir-to-5-000-...
* frame.io - https://medium.com/frame-io-engineering/elixir-open-source-f...
Both Divvy and Frame.io were acquired btw -
* https://techcrunch.com/2021/08/19/adobe-buying-frame-io-in-1...
* https://techcrunch.com/2021/05/06/why-did-bill-com-pay-2-5b-...