Hacker News new | ask | show | jobs
by bmitc 247 days ago
The Phoenix churn is definitely real. It's so much so that I've never gotten into it. It's also extremely macro heavy, and so it's its own DSL or collection of DSLs. A concrete example of the churn is that the LiveView book has been "about to release" for five years now.

Although, what parts of Elixir itself are rough or missing creature comforts? I generally feel it's stable and fine, but I admittedly haven't written Elixir code in a couple of years, sadly.

2 comments

LiveView was still before v1.0, hence the churn, but Phoenix itself did not introduce breaking changes since v1.0, released more than a decade ago. Our skeleton for new applications change, as best practices around web apps are still evolving, but it is completely up to you to migrate. As a reference point, most other web frameworks have gone through several major versions in the same time, changing how new apps are built but also breaking old ones.

The idea that Phoenix is also mostly macros does not hold in practice. Last time this came up, I believe less than 5% of Phoenix' public API turned out to be macros. You get this impression because the initial skeleton it generates has the endpoint and the router, which are macro heavy, but once you start writing the actual application logic, your context, your controllers, and templates are all regular functions.

> The idea that Phoenix is also mostly macros does not hold in practice

no, but the Framework does push you into using them. A good example is the `use MyAppWeb` pattern. That's a macro that nests other macros. the good news is that you can pretty much excise that and everything works fine, and LLMs have no problem even! (i think they slightly prefer it)

a few cognitive pain points with phoenix macros:

plug: (love it dearly) but a bit confusing that it creates a conn variable out of whole cloth. a minor complaint. worth it, otherwise.

phoenix.router: is a plug but isnt quite a plug.

anyways that's it! the rest is ~fabulous. i think to find a framework where you have two minor complaints is a blessing. remember how activerecord automagically pluralized tables for you?

What do you mean, "creates a Conn variable out of whole cloth"?

Conn is just a pipeline of functions, the initial Conn struct is created at request time and passed through to each function in the pipeline.

“It’s all regular functions” should be on our collective bumper sticker.
Thanks for the clarifications!

> I believe less than 5% of Phoenix' public API turned out to be macros.

The idea may still be right, but I'm curious if that addresses the majority of the public API that users are greeted with. I have unfortunately not written Elixir in a few years (cries), and I've never fully grokked Phoenix, so perhaps I'm still wrong.

> It's also extremely macro heavy, and so it's its own DSL or collection of DSLs.

I mean this describes every full stack web framework right? Like sure if the underlying language doesn't have macros or macro-like tools that limits how perverted the syntax can get but the line between "DSL" and "API" gets really blurry in all of these massive frameworks.

That's true for languages that have macros. I just don't like macros, as they get over-abused in every language that has them. I'd much rather deal with just boilerplate and tedious syntax but still straightforward and completely in the language over macros, for the most part. Some macros are indeed useful, like in Rust with `println`, but they still get thrown everywhere.
Frameworks in langauges that don't use macros have this problem too that's what I was getting at with the DSL vs API thing. I don't want to litigate the worthiness of macros for a given purpose here. But if you don't use them for this you have to use something for this the problem doesn't go away.

Wherever rails or phoenix has macro-defined syntax to handle a specific task, laravel or whatever will have a collection of related functions that need to be used in very specific ways to accomplish the same thing. Whether this collection is a "class" with an "api" or whether it is a "language" defined around this "domain" you will have the abstraction and the complexity.

Having a preference for one approach of managing this abstraction & complexity seems fine but "a collection of DSLs" is pretty much what a web framework is so that can't be the problem here.

It really depends on how good your inspecting tools are. Using runtime methods and functions instead of macros mean the code is all right there, and what you're debugging is what you see in your editor (setting aside silly things like reflection shenanigans).

With macros, even language servers may need customization if they introduce new syntax. The code that runs doesn't exist until it runs, so you can't see it ahead of time.

This doesn't sound like too big a problem if you're familiar with the tooling already, but trying to figure out where some random method comes from in a rails code base when you're new to Ruby is somewhere between a nightmare and impossible without debugging and using the repl to tell you where the source is.

React has a JSX macro, and I love using it, so there's definitely room for them. There is a world of difference in developer experience when macros are used versus when not, however, and it is wrong to say that it is all the same.

The counterpart in Laravel or Spring Boot or whatever would be annotations. As I understand it, that's how they're doing things on the .NET side too.

It's kind of the standard way to paper over the protocol grit of HTTP and make people able to quickly pump out fresh plumbing between outbound socket and database.

> Frameworks in langauges that don't use macros have this problem too that's what I was getting at with the DSL vs API thing.

You mean in the sense that the language's built-in syntax and available abstractions get abused so much that it approximates a DSL?