Rails has a ton of magic, implicit behaviours, monkey patching, ERB, and so on.
Go is a touch below Java verbose, explicit, no magic, every function call can be very easily traced without having to do meta programming and code generation in your head to understand what's going on.
In my 8 years of writing Go, I would liken it the most to C, where you had to spell everything out, except without the manual memory management and the macro preprocessor.
Doing magic with Go via reflection or other implicit behaviours is generally annoying to deal with. One example is some libraries using struct tags, most of the time they work as expected, but sometimes you get some weird failure and these kinds of implicit behaviours are the culprit.
Overall, I don't rate these "all in one" frameworks highly in Go. The standard library is excellent for most applications, you only need to add some code to remove some boilerplate. For most apps that I have worked on in Go that involved web components, we maybe had to import e.g a websockets library or a more elegant routing library, but that's pretty much it.
Generics help a little bit, but the fact remains that error checking is pervasive and verbose, and you can't chain calls (e.g. slice.Map(func(a int) { return a * 2 }).Filter(func (a int) { return a % 2 == 0}).Reduce(0, func(a int, i int) { return a + i }) is not something that can be supported by golang when errors are involved, not to mention the extreme verbosity since it does not have a short way to pass functions).
An ORM that didn't let you escape to SQL when you wanted to do more complex things would be a total failure, of course -- but to suggest that they're not more convenient for the 80/20 or even 90/10 use case is just really hard to understand. Must be NIH syndrome.
I strongly disagree that not wanting to add yet another abstraction layer onto a proven technology would be NIH.
Something like sqlc[0] is leagues better than any ORM in terms of simplicity, complexity and maintainability.
https://sqlc.dev/ makes your SQL the focus, not your Go-specific query code.
Client.find_by_sql("
SELECT * FROM clients
INNER JOIN orders ON clients.id = orders.client_id
ORDER BY clients.created_at desc
")
Which returns Client objects. Insert your own vastly more complex query above.Why increase the complexity of something that's already a high level abstraction? Abstractions are about simplification. An orm and sqlc are not it.
SQL is already a very high level API that compiles to low level algorithms? Why put another very high level API on top of that?
To top it off SQL is a leaky abstraction by nature. Two high level SQL queries with equivalent results can both compile into algorithms with COMPLETELY different performance profiles. You have to manipulate the SQL query to generate the correct performance profile. This means understanding things below the SQL abstraction.
You put a ORM or any new abstraction on top of that guess what? That abstraction must compile into hacked SQL. You have to be able to manipulate the ORM such that it generates the correct SQL such that the correct SQL generates the algorithm with the correct performance profile. The leak from the sql abstraction must propogate into the ORM layer which in itself must be a leaky abstraction. It's like dealing with a leaky pipe embedded within another leaky pipe.
Optimizing SQL is already a domain knowledge thing. Now you have to use new tricks to optimize the abstraction on top of it. Two Leaky abstractions on top of each other and both very high level is a bit of a head scratcher.
Why do people even make these abstractions that only make life harder? I think it's an illusion. It's to satisfy an OCD thing but people don't realize the OCD is an illusion. People want to deal with a single language, not have strings of another language living in the code. For example, SQL strings are seemingly kind of ugly in something like GO code.
Databases are the classic bottleneck of web development in terms of speed. Optimization is a very important part of writing SQL queries as a result. Having orms and other high level abstractions on top of this area is much much more harmful then it otherwise would be if databases did not exist in this bottle-necked area of web development.
In actuality it's also questionable whether or not a leaky abstraction in the first place was the right design decision. Is SQL the right abstraction for database queries? Or should we design another high level API that has a more 1 to 1 correspondence with optimization as in a High level API that's not leaky, like What Rust is to systems programming.
I'm hoping to add something like Phoenix to Copper. It should help with the "heavily API only side" problem. I've already added integrations for Tailwind and added some utilities on top of the templating (going to add more) to fix the lack of good templating.
I’m considering writing a Typescript clone of Ent with codegen powered by Typescript types. I like codegen over dynamic magic because the runtime behavior is often easier to understand. In Rails, I need to traverse a lot of space in Pry’s debugger mode to figure out WTF is happening. I would much rather have codegen.
My take is that Remix + Next.js + SvelteKit are going to continue to innovate fast in the Frontend and "Backend for Frontend" space. Rails and Laravel don't hold a candle to the experience you get in that ecosystem.
But the JS ecosystem is massive and, as a result, fragmented. As you mentioned, there's no consensus on ORMs, mailers, queues, etc.
I don't see those frameworks trying to push too far in that direction, they'll remain "UI focused". This is nice for their focus, but not great for someone who wants to launch a web app and doesn't want to figure out all the surrounding ecosystem tooling.
This is where Laravel, Rails (and soon Bud) (and I assume Copper) will shine. They provide more tools and interfaces out of the box for building full-featured backends. These frameworks definitely need to keep an eye on the best ideas coming out of the JS framework ecosystem though!
There is no Rails equivalent in JS, theres lots of competitors that feel years away like SailsJS, the new Deno Fresh one etc, Adonisjs... Is NextJS/SvelteKit/RemixRun considered also? I don't even know if they have a standardised background job processor in JS land.
Java's solutions are dreadful imo for if you want to compare to Rails. Quarkus/SpringBoot/Micronaut are nowhere near productivity levels for a fullstack app. They lean heavily on the API only side of things. (I do like Java oddly enough)
PHP is the main competitor to Rails oddly enough, Laravel seems brilliant.
Go is just starting up in this space it looks like, Bud is another attempt at Rails in another language https://github.com/livebud/bud. However the Go ecosystem is heavily API only side of things instead of SSR. Go's templating libs suck imo.
Elixir of course has Phoneix which is apparently great, purely functional langs unfortunately dont fit my head and feel to abstract for myself (don't hate me)
Its no wonder we have the backend / frontend developer split nowadays.