6 ms·
I'm not really qualified to talk about either topic at length, but my impression is that the Microservice crowd is kind of a different group than the anti-OOP c
by sroerick 11mo ago
I'm not really qualified to talk about either topic at length, but my impression is that the Microservice crowd is kind of a different group than the anti-OOP crowd.
As a total beginner to the functional programming world, something I've never seen mentioned at length is that OOP actually makes a ton of sense for CRUD and database operations.
I get not wanting crazy multi tier class inheritance, that seems like a disaster.
In my case, I wanted to do CRUD endpoints which were programmatically generated based on database schema. Turns out - it's super hard without an ORM or at least some kind of object layer. I got halfway through it before I realized what I was making was actually an ORM.
Please feel free to let me know why this is all an awful idea, or why I'm doing it wrong, I genuinely am just winging it.
- TZubiri 11mo ago"OOP actually makes a ton of sense for CRUD and database operations." Not at all OOP is great at simulations, videogames, emergent behaviour in general. If you do crud with oop you will complain about overengineering.
- jay_kyburz 11mo agoEven in video games, I avoid inheritance, I always much prefer composition. Build a complex object from many small objects, then vary behavior with parameters rather than deriving a child class and overriding methods.
- TZubiri 11mo agoRight that's still OOP.
- jay_kyburz 11mo agoOh yeah, just saying inheritance is the part I don't like.
- 1718627440 11mo agoInheritance and composition is fundamentally the same. This is easy to see when you implement it in a language that has no specific support for either (like C). Inheritance is basically composition of the data and the vtable and then overriding the default vtable entry with the supertype implementation.
- sroerick 11mo agoI think that's fair, and I generally prefer a lighter stack for CRUD, but I still love Django and Rails. Maybe just having "objects" is not enough to qualify as OOP but for many use cases, the convenience offered by "Batteries Included" is worth the trade off in "overengineering". If I have to build an app, I'm going for rails. If I'm building a back end, I'm reaching for Go. If I need to integrate with Python libraries, Django is great. But ask me again when I get to the other side of some OCAML projects
- shagmin 11mo agoAlmost all languages have some sort of object representation, right? Classes with their own behavior, DTOs, records, structs, etc.,. What language are you working in? If you're coupled to a specific database provider anyway there's usually a system table you can query to get your list of tables, column names, etc., so you could almost just use one data source and only need to deal with its structure to provide all your endpoints (not really recommending this approach).
- sroerick 11mo agoThis is probably the correct solution for this use case, but obviously and objectively much harder than object.get(id=1). I was mainly doing this in Go, posted more in a side post.
- WorldMaker 11mo ago> As a total beginner to the functional programming world, something I've never seen mentioned at length is that OOP actually makes a ton of sense for CRUD and database operations. I've heard this a lot in my career. I can agree that most object-oriented languages have had to do a lot of work to make CRUD and database operations easy to do, because they are common needs. ORM libraries are common because mapping between objects and relations (SQL) is a common need. It doesn't necessarily mean that object-oriented programming is the best for CRUD because ORMs exist. You can find just as many complaints that ORMs obfuscate how database operations really work/think. The reason you need to map from the relational world to the object world is because they are different worlds. SQL is not an object-oriented language and doesn't follow object-oriented ideals. (At least, not out of the box as a standardized language; many practical database systems have object-oriented underpinnings and/or present object-oriented scripting language extensions to SQL.) > it's super hard without an ORM or at least some kind of object layer This seems like you might have got caught in something of a tautological loop situation that because you were working in a language with "object layers" it seemed easiest to work in one, and thus work with an ORM. It might also be confusing the concepts of "data structure" and "object". Which most object-oriented languages generally do, and have good reason to. A good OOP language wants every data structure to be an object. The functional programming world still makes heavy use of data structures. It's hard to program in any language without data structures. FP CRUD can be as simple as four functions `create`, 'read`, `update`, and `delete`, but still needs some mapping to data structures/data types. That may still sound object-oriented if you are used to thinking of all data structures as "objects". But beyond that, it should still sound relatively "easy" from an FP perspective: CRUD is just functions that take data structures and make database operations or make database operations and return data structures. A difference between FP and OOP's view of data structures is where "behaviors" live. An object is a data structure with "attached" behaviors which often modify a data structure in place. FP generally relies on functions that take one data structure and return the next data structure. If you aren't using much in the way of class inheritance, if your "objects" out of your ORM have few methods of their own, you may be closer to FP than you think. (The boundary is slippery.)
- sroerick 11mo ago> This seems like you might have got caught in something of a tautological loop situation that because you were working in a language with "object layers" it seemed easiest to work in one, and thus work with an ORM. I mean, I think this is likely the case. So, I tried this, for example in Go, which is not really a proper functional programming language as I understand it, but is definitely not object-oriented. So for my use case, I wanted to be able to take a database schema and programmatically create a set of CRUD endpoints in a TUI. Based on my pretty limited knowledge of Go, I found this to be pretty challenging. At first, I built it with Soda / Pop, the ORM from Buffalo framework. It worked fairly well. Then I got frustrated with using Soda outside Buffalo, and yoinked the ORM to try and remove a layer. Using vanilla Go, it seems like the accepted pattern is that you create separate functions for C R U and D, as you referred to. However, it seems like this is pretty challenging to do programmatically, particularly without sophisticated metaprogramming, and even if you had a language which had complex macros or something, that is objectively significantly harder than object.get() and object.save(). Finally, I put GORM back in, and it worked fine. And GORM is a nice library, even though I think having an ORM is not the "Go" way of doing things in the first place. But also, Gorm is basically using function magic to feel like OOP. And maybe the problem with this idea is that it's not "Proper Go" to make a thing like this, it would be better to just code it. There's an admin panel in the Pagoda go stack which relies on ent ORM to function as well. I can only assume the developer motivations but I assume they are along the same lines as my experience. I certainly don't think any of this requires insane class inheritance, and maybe that's all people are talking about with OOP. But I still think methods go a long way in this scenario. In the real world, in business logic, objects do things. They aren't just data structures. To summarize, CRUD seems pretty easy in any language, programmatically doing CRUD seems super hard in FP. Classes make that a lot easier. Maybe we shouldn't do that ever, and that's fine, but I'm a Django guy, I love my admin panels. Just my experience.
- beachy 11mo agoYou're not wrong. It's fashionable to dunk on OOP (because most examples - like employee being a subtype of person - are stupid) and ORM (because yes you need to hand write queries of any real complexity). But there's a reason large projects rely on them. When used properly they are powerful, useful, time-saving and complexity-reducing abstractions. Code hipsters always push new techniques and disparage the old ones, then eventually realise that there were good reasons for the status quo. Case in point the arrival of NoSQL and wild uptake of MongoDB and the like last decade. Today people have re-learned the value of the R part of RDBMS.
- senderista 11mo agoLarge projects benefited from OOP because large projects need abstraction and modularization. But OOP is not unique in providing those benefits, and it includes some constructs (e.g. inheritance, strictly-dynamic polymorphism) that have proven harmful over time.
- beachy 11mo agoInheritance == harmful is quite an extreme position.
- Jtsummers 11mo agoIt may be extreme, but it's very common. It's probably the single most common argument used against OOP. If you drop out inheritance, most of the complaints about OO fall away.
- HumanOstrich 11mo agoCan you share where you've seen inheritance get dropped and it resulted in fewer complaints about OOP?
- bonesss 11mo agoOOP without inheritance is exactly what VB6 had… now its cool again, I guess.
- kevstev 11mo agoMaybe I am not exactly what you mentioned, but I do feel OOP set us back about a decade or two and do think the general concept of microservices is a good idea. But maybe to your point, these beliefs are completely orthagonal to one another, and why they are mentioned as being related baffled me. To be honest the whole post baffled me and I am disappointed I can not downvote the submission. Anyway more to your topic- OOP in the early 2000s was put on this massive pedestal and trying to point out its flaws would often get you chastised or shunned, and labeled that you just didn't get OOP and such. But the object hierarchies often became their own source of inflexibility, and shoehorning something new into them could often be very difficult and often involve an hour or three of debate/meetings on how to best make teh change. Microservices are more about making very concrete borders between components with an actual network in between them... and really a contract that has to be negotiated across teams. I feel the best thing this did was force a real conversation around the API boundary and contract, monoliths turn to a big ball of mud once a change slips through that passes in an entire object when just a field is needed, and after a few of these now everything is fairly tightly coupled- modern practices with PRs could prevent a lot of this, but there is still a lot of rubber stamping going on and they don't catch everything. Objects themselves are fine ideas, and I think OOP is great when you focus on composition over inheritance, and bonus points if the objects map cleanly into a relational database schema- once you are starting getting inheritance hierarchies, they often do not. If I had to guess, your experience with OOP is mostly using ORMs where you define the data and it spits out a table for you and some accessor methods, and that works... until it doesn't. At a certain level of complexity the ORM falls apart, and what I have seen in nearly every place I have worked at- is that at some point some innocuous change gets included and now all of a sudden a query does not use an index properly, and it works fine in dev, but then you push it to prod and the DB lights on fire and its really difficult to understand what happened. The style of programming you are talking about would be derided by some old heads as "C with objects" and not "really" OOP. But I do think you are onto something by taking the best parts and avoiding the bad. "Micro" services aren't great when they are taken to their utmost tiny size, but the idea of a problem domain being well constrained into a deployable unit usually leads to better long term outcomes than a monolith, though its also very true that for under $10k you can get 32 cores of xeons and about 256 gigs of ram, and unless you are building something with intense compute requirements, that is going to get you a VERY long way in terms of concurrent users.
- vjerancrnjak 11mo agoOOP is not simplifying CRUD or DB ops because you want to batch. You don’t want lazy loading. You don’t want to load 1 thing. You don’t want to update 1 thing. You want to actually exploit RETURNING and not have the transaction fail on a single element in batch. If you care about performance you do not want ORM at all. You want to load the response buffer and not hydrate objects. If you ignore ORM you will realize CRUD is easy. You could even batch the actual HTTP requests instead of processing them 1 by 1. Try to do that with a bunch of objects. I would personally never use ORM or dependency injection (toposort+annotations). Both approaches in my opinion do not solve hard problems and in most cases you don’t even want to have the problems they solve.
- sroerick 11mo agoI agree with you, but I am not sold on optimizing for performance above all else. Business logic ran fine on ancient mainframes. It can run fine on Raspberry Pis. CRUD is super easy. It's also not super resource intensive. I know that's the path that led us all down into Java OOP / start menu is a react native component, but it is actually true. ORM adds a convenience layer. It also adds some decent protection against SQL injects OOTB and other dev comforts. Is that trade off worth it? Probably not. But sometimes it's the best tool for the job
- jason_oster 11mo agoOptimize for your users above all else. Yes, even above developer experience. If that means optimize for performance, you optimize for performance. The only thing that matters is what your users feel when using your product. Everything else is a waste of time. OOP, FP, language choice, it's all just fluff.
- sroerick 11mo agoI think this is a wonderful philosophy, but many times the actually more important thing is "optimize for the client's budget"
- marcosdumay 11mo ago> As a total beginner to the functional programming world, something I've never seen mentioned at length is that OOP actually makes a ton of sense for CRUD and database operations. That's because you are wrong. There's nothing in relational databases mapping that make objects a better target than even the normal data structures you see in functional languages. > In my case, I wanted to do CRUD endpoints which were programmatically generated based on database schema. What is a pure transformation. The problem is that CRUD applications are an incredibly bad explored area. There only mature projects out there are the OOP ORM ones. That's not because OOP is inherently better suited to the application, it's because there's simply not a lot of people willing to risk into working at that problem. (And the reason people are not willing can be because developers don't choose their tools through rational evaluation, or may be some irrational one IDK. Mine is certainly because I know if I built an amazing system, nobody would come.)
- sroerick 11mo ago> What is a pure transformation. I don't know! Can you explain it, or how I would use it for this application? > And the reason people are not willing can be because developers don't choose their tools through rational evaluation, or may be some irrational one IDK. I think "do the tools exist in the world" is a pretty rational evaluation. I'd love to see the FP equivalent!
- 1718627440 11mo ago> I don't know! A pure function is a function whose output only depends on the paramters and not has any internal state. Which in my opinion is a somewhat useless distinction, because you can make your state/instance/whatever the first parameter, (what C and Python do) and tada, every function that doesn't use globals (or static in C), is a pure function.
- sroerick 11mo agoIs that distinct from a pure transformation?