I started using pg functions, views, triggers, etc and found it extremely convenient. Now, my sql strings in go are mostly `SELECT * FROM func_name(<$params>)` and I'm happy with that.
I'm not saying ORMs are useless (active_record is an incredibly cool tool), but it's been surprisingly way easier to live without them that I'd thought.
People used to defend writing things in assembly language for similar reasons, as a way of getting more machine sympathy. I'm not buying it.
I have to assume those people did not first learn and use C, then went to assembly, but rather the opposite ;) So, the comparaison does not apply, here.
> you're still writing the same basic SQL statements (for the 80% of your database interactions that are essentially CRUD)
It's not the case, actually. My functions are way more expressive that the crud I did with orms. With PL/pgSQL, I group several queries in a same pg function, I always do more that a simple insert or update. I also fine tune my returned fields - which most of the time are composite from several tables / computations.
This is really outsourcing all data manipulation to the database, the comparison that applies is more with a presenter/serializer than with an orm model.
I realize this is a very general and vague question but it's something I've been pondering about.
Initially, I would use `\ef <func_name>` in psql and just write and try my function, but this turned out to be not as pleasant as writing code in an editor in a dedicated file. The annoying thing with pg functions is that, if you want to replace them, you have to provide exactly the same in and out parameters. This means that if you want to change the parameters, you have to drop the function and create a new one.
This has been annoying at first, but now I'm more comfortable with it. I write my functions as migration files [1] and just migrate/rollback to do my tests. I guess I started to give more attention to the input and output parameters from the start in the mean time.
The cases where I won't use functions is when I want to do something extremely simple, like `SELECT email FROM users WHERE id = 1`. It's just not worth writing a function for that. It doesn't happen often that this is all I need to process my request either.
[1] I use https://github.com/mattes/migrate
SQL is already much higher level than assembly. Piling on more abstraction just increases the likelihood that the code is doing extraneous work.
If you avoid an ORM, the typical medium sized CRUD app usually ends up with some poorly conceived bespoke abstractions that would be better used by some flavor of ORM. I'm with you that ORMs make it way too easy for junior devs to blow their leg off, but I still think they provide some very solid abstractions when used correctly.
https://github.com/jmoiron/sqlx
It has features like directly unpacking the results of a query into a structure. Very handy.
You get that for free in any language that allows you to pass parameters to a query. There is nothing Go specific.
> One of my biggest complaints about ORMs is that I often know the exact query I need to write, and it is a pain to compose it with the ORM.
I agree that ORMs are often their own DSL one has to learn, when SQL is already there. In that sense they are opaque. Yet ORM still deal with relationships that have to be manually wired with queries, since AFAIK SQL query are still 2 dimensional. In my opinion query builders are often a nice trade-off between a fully featured ORM and raw SQL : you get SQL abstraction thus query reuse at minimal cost without the complexity.
An other alternative is Jooq style code generation/scaffolding but each time a tool like that is added to a compilation pipeline it is yet another dependency to manage. Code generation is opaque in its own ways.
Finally there is the nuclear option in shape of HQL but it sounds like a half baked farce to go back to "SQL" to avoid SQL.
I myself chose to go with query builders, in fact i'm porting Doctrine DBAL to Go as we speak.
For every heavily used ORM features, there's an opportunity to fall into a trap, e.g N+1, because the surface area of an ORM is wide.
In contrast, wrapper functions are low cost, easy to write, and recognizable because you write them yourself.
Isn't saying that you should use functions that are "recognizable because you write them yourself" basically saying "you shouldn't use libraries?" This seems like the obvious NIH fallacy.
Not invented here also means not invented for the specific task at hand but for a much more general set of use cases. In my view it's a trade-off, not a fallacy.
Of course, you can write software in the same domain where ORMs excel in Go, but I am not sure it is the best tool for that job. A good ORM would go a long way to making that less true, but I'm not sure even that would put Go at the same level as tools that are geared towards that type of problem. Go, like all tools, is still targeted at a certain class of problems.
Might be interesting to hear reasons - by both you and the parent commenter.