Readit News logoReadit News
bedobi · 2 years ago
As a professional dev who has made a career out of working in oop languages and codebases, it took me far too long to realize that when it comes to oop, the emperor has no clothes.

To this day, oop advocates can't even agree on what oop even is or means.

Apparently oop as envisioned by Alan Kay was supposed to work like cells in the body that pass messages between each other and take actions independently.

Why? Who knows! It was never really explained why literally one of the most complex systems imaginable, one that we still really have very little idea how it even works, should be the model for what could and should probably be a lot simpler.

Today's modern oop languages are probably very far from what Kay envisioned (whatever that was), but it's remains unclear why classes and objects are "better" than the alternatives.

And before anyone goes and comments aksully code organization blabla like yes but code organization can be great or shit in oop or fp or procedural codebases, it has nothing to do with the "paradigm".

Let alone that the entrenched, canonical, idiomatic coding styles of most modern oop languages encourage state, mutability, nulls, exceptions and god knows how many trivially preventable entire classes of errors. Granted, most have now started to come around and are adopting more fp features and ideas every year, but still.

Don't get me wrong, writing programs like cells in the body that pass messages between each other and take actions independently is an interesting idea which deserves pursuing, if nothing else but to satisfy our curiosity and seeing to what if anything it's applicable and suited. (and even if the answer turns out to be "nothing", we've still learned something!)

But going from there to making strong claims about it being a more or less universally superior paradigm for computing and writing code, with little to zero evidence, that's a huge, huge stretch.

To the degree Erlang and Actors work, I think that's kind of a happy coincidence, and not due to any rigorous work on Alan Kay's part.

javajosh · 2 years ago
>Apparently oop as envisioned by Alan Kay was supposed to work like cells in the body that pass messages between each other and take actions independently.

IIRC he was interested in an abstraction that worked "all the way up" - he wanted the abstraction to essentially be a tiny computer. It's a cool idea with a lot of power. But it really is too powerful for applications, and leads to the same complexity problems you get in large distributed systems. And to not have 'function' as a primitive is inexcusable in any language.

Java's lack of a function primitive is a key weakness. It means you must have at least one "utility class" per project - a public class with static methods that are themselves simple functions. (The relatively recent introduction of lambdas, or anonymous inner classes, does not really address the problem. That's just ugly syntax sugar).

jayd16 · 2 years ago
Is the fact that static methods in Java need to be namespaced to a class really anything to even worry about? Like, whats even a single issue with it?
igouy · 2 years ago
> And to not have 'function' as a primitive…

"A block is essentially an anonymous function."

    [ :x | 1 + x ] value: 2
    ⇒ 3
https://cuis-smalltalk.github.io/TheCuisBook/Block-syntax.ht...

Akronymus · 2 years ago
> Why? Who knows! It was never really explained why literally one of the most complex systems imaginable, one that we still really have very little idea how it even works, should be the model for what could and should probably be a lot simpler.

From my understanding, it is that every object has a very limited set of functionalities it manages. And that a goal is for each object to know as little as possible of the outside world. And along with that, to not bind to specific objects/classes but to bind to specific messages (Including multiple dispatch)

Fire-Dragon-DoL · 2 years ago
That was poorly achieved with oop and better achieved with fp though. Oop couples functionality and data (not typescript, if you don't want)
taeric · 2 years ago
If you think that OOP advocates can't decide on what OOP is, don't look too hard at the FP side of the world. :D

Same goes for evidence based ideas. Odds are stupid high that you cannot find a single large scale codebase that has succeeded using any "pure" technique. Heck, I'd take small scale codebase for a fun thing to look at, at this point. Make performance a requirement, and really get ready for tears.

waterhouse · 2 years ago
Jonathan Rees had said: "Here is an a la carte menu of features or properties that are related to these terms; I have heard OO defined to be many different subsets of this list." The aspects he names: "encapsulation", "protection", "ad hoc polymorphism", "parametric polymorphism", "everything is an object", "all you can do is send a message", "specification inheritance = subtyping", "implementation inheritance/reuse", "sum-of-product-of-function pattern". http://www.paulgraham.com/reesoo.html
igouy · 2 years ago
> Apparently oop as envisioned by Alan Kay was supposed to work like cells in the body that pass messages between each other and take actions independently.

> Why? Who knows! It was never really explained…

Perhaps "Design Principles Behind Smalltalk" ?

https://www.cs.virginia.edu/~evans/cs655/readings/smalltalk....

c_crank · 2 years ago
>Let alone that the entrenched, canonical, idiomatic coding styles of most modern oop languages encourage state, mutability, nulls, exceptions and god knows how many trivially preventable entire classes of errors. Granted, most have now started to come around and are adopting more fp features and ideas every year, but still.

State isn't a feature solely of OOP. It's very easy to see the FP craze as a similarly dogmatic insistence on pure everything as removing useful features instead of preventing errors.

If FP truly was the indisputable way of the future, Scala would be the CRUDspeak of choice by now.

Fire-Dragon-DoL · 2 years ago
Elixir has opt-in state that works really well! Everything immutable until you need state.
MisterTea · 2 years ago
> Apparently oop as envisioned by Alan Kay was supposed to work like cells in the body that pass messages between each other and take actions independently.

When I think of programs I think of modules of code that act like machines on a conveyor belt or assembly line operating on and pushing data along from station to station. This is why Erlang, Go and Plan 9's thread(2) library are how I want to program.

> To the degree Erlang and Actors work, I think that's kind of a happy coincidence, and not due to any rigorous work on Alan Kay's part.

https://www.infoq.com/interviews/johnson-armstrong-oop/ (3. Is Erlang object oriented?) three points: message passing, isolation between objects, and polymorphism.

The industry seems to have ran away with #3 while ignoring #2 and forgetting completely about #1.

CyberDildonics · 2 years ago
What you are describing is what a directed acyclic graph achieves. It works well when you need to do a series of transformations. It does not work well when you need branching, loops and state at a high level. The mistake people make is that they see it work very well for certain parts of their software, then they give in to silver bullet syndrome and try to make it work for everything.
AtlasBarfed · 2 years ago
Most oop was designed in the context of desktop gui apps, which are much more complex that web apps imo.

Is-a vs has-a is a lot more clear in context of widgets and windows. It still isn't perfect, most popular gii used inheritance whereas composition would probably be better.

Inheritance ended up as a core component of oop principles when ultimately it is a flawed approach compared to more flexible composition methods.

Liskov substitution was a massive success, classes were an improvement over structs, private vs public was generally a good idea even if the astro architects went nuts with it.

robocat · 2 years ago
> oop languages encourage state, mutability, nulls, exceptions and god knows how many trivially preventable entire classes of errors. Granted, most have now started to come around and are adopting more fp features and ideas every year

Do functional languages actually discourage "state, mutability, nulls, exceptions"

I know why we might argue against these features, but implying that functional programming doesn't have those features seems a little weird (or equivalent e.g. call/cc can cause execution flow less predictable than exceptions)!

jacobr1 · 2 years ago
My limited experience with FP languages suggests they encourage very explicit state management. You remove a lot of the footguns from having side-effects all over the place at the cost of the (sometimes, depending on the tooling and your familiarity with it) it being more complex to introduce state or external side-effects when you want them.
jononomo · 2 years ago
Once I got the hang of Elixir/Erlang it was just such a weight off my shoulders, honestly. Programming without entire classes of bugs is so much nicer.
goatlover · 2 years ago
And yet despite all these criticisms and complaints, C++, Java, Python, Javascript, PHP and even Ruby to some extent, have been massively popular and used to write all sorts of software we rely on.
namaria · 2 years ago
What people are complaining about when they voice these sorts of concerns goes back to the concept of essential vs accidental complexity proposed by Fred Brooks. The issue is not that adding accidental, inessential complexity to software is somehow a sin. The problem is that someone will have to wrangle this extra complexity. Enterprises can just throw bodies at the problem, creating drudgery. The warning is against producing pointless, boring work.
patmorgan23 · 2 years ago
All of those languages also have functional/procedural language features. You don't have to go crazy with Classes/Objects to program in them.
12345hn6789 · 2 years ago
(nobody is talking about whether X pattern can be used successfully)
MetaWhirledPeas · 2 years ago
I do some programming, but not OOP. Over the years I've read about it and asked colleagues about it, and the why question finally settled on it helps you organize your code. I can get behind that. But I also want to understand why OOP instead of conventional programming; I'm still waiting for that part to click. Until that question is answered it won't be capturing my full attention.
hfkwer · 2 years ago
What is "conventional programming", pray tell?
tesseract · 2 years ago
I've never really seen this explicitly stated anywhere, so maybe this is some super idiosyncratic idea on my part, but it's always seemed to me that one of the key pieces of OO is the use of polymorphism as a sort of syntactic/conceptual sugar around function indirection, that many people seem to have an easier time understanding and reasoning about.
fassssst · 2 years ago
I like the mapping to Claude Shannon’s ideas in information theory.

https://cs.stanford.edu/people/eroberts/courses/soco/project...

friendzis · 2 years ago
> And before anyone goes and comments aksully code organization blabla

I'll bite. Having worked on "enterprise" application the OOP (or more like the dreaded enterprise patterns) have sort of "clicked" to me.

An aside. Git-flow is more than decade old at this point and people still have heated debates from time to time on whether that approach is any good. In essence git-flow merely introduces organization to patchsets. If you are building a SaaS like system, most probably every environment you have runs "current" code and each stage (prod, test, dev) is merely a shifted pointer that will eventually catch up. Why would you structure your teamwork around identifiable patchsets, why would you keep that information around when you do trunk-based development? There is seemingly no need to do that and you would rightfully diss on git-flow as overly complex.

However, if you build hardware appliances or installable offline application with multiple versions (think e.g. MS Office in 2000-2010 - multiple versions and tiers of those out in the wild under support), suddenly there are situations where you do in fact need to checkout and work on an exact patchset that is installed at customer site. If your field requires any form of certification, you will quickly learn that having patchsets provably not touching frozen (already certified or submitted for certification) code is highly valuable. Ability to cherry-pick a hotfix can save organization man-months.

Back to code organization. So you have this "enterprise" application that has various software modules/components, some of which are built by external contractors, some are off-the-shelf components, some have been left and forgotten five years ago and for one reason or another you want to work on one of these components. Whatever team you assemble, majority will have zero understanding of assumptions baked into code. Documentation, however meticulously maintained, will still leave holes in understanding. How do you make changes to a decade old component that is used in various weird ways all throughout the project with any amount of confidence that you are not breaking stuff at a distance? Apparently, OOP enterprise patterns that introduce decoupling points for implementation details and pass around objects with assumptions abstracted away are pretty solid barriers. They are cumbersome, difficult to use at early stages of development when all complexity of the task can fit into the heads of programmers implementing features. Similarly to git-flow, the advantages of these structures only become apparent when you start needing them and that will quite probably be when the principal engineers greenfielding the project have long left the company altogether.

A very specific example: factory pattern. Why would you have a layer of indirection to simply instantiate an object? All refcounting shenanigans aside, you just cannot change interface of object/component without affecting call sites. Call sites that may be outside of your control, code frozen or simply too dreadful to touch without strong justification. Factory pattern introduces a decoupling point between call sites and implementation. Having a factory you can implement interface shims and leave call sites untouched while having the freedom to rework component and its interface. You just cannot achieve that without OOP.

> Let alone that the entrenched, canonical, idiomatic coding styles of most modern oop languages encourage state, mutability, nulls, exceptions and god knows how many trivially preventable entire classes of errors.

I will agree that OOP implementations in mainstream languages leave much to be desired. That is part of the price we pay for backwards compatibility.

StackOverlord · 2 years ago
You can introduce indirection layers without OOP since a function's signature is its interface. I do it all the time.
willio58 · 2 years ago
I remember learning and using OOP in computer science classes and then going to apply it to a game I was working on. I object-ified everything! It felt like it all just made sense.

Then I ran the game. And it crawled at 15fps and I wondered why.

Of course I was new to programming so I can’t just blame OOP but it was my first lesson in how OOP isn’t a magic wand by any means. And later I got into more functional programming and today I find myself writing function-based components in react for a living and loving it. It’s funny how things go like that.

gumby · 2 years ago
This ten year old essay is still strong. It makes some points that are worth repeating

> I have a hypothesis: this pattern is so common for the simple reason that Java doesn’t have first-class functions.

She’s picking on Java deservedly because Gosling made what in retrospect was a mistake to go so all-in on classes, and because it became such an important pedagogical and deployment language (and nobody will shoot at you if you’re unsuccessful).

Say what you will about C++ but it took a different path, not only changing its name from “C with Classes” but making classes a tool for manipulating the type system (and not the only one) and supporting the true benefit of a class system, generics.

Edit: I changed an incorrect "He" pronoun to "She". Thanks to quickthrower2 for pointing out this embarassing blunder.

quickthrower2 · 2 years ago
Nitpick: She not he
gumby · 2 years ago
Thanks! Luckily there was still time to edit, so I patched it.

Dead Comment

narag · 2 years ago
The many "you're wrong if you believe that" made me remember the anecdote of Alan Kay attending some conference about OOP and saying that it was wrong, that he invented OOP and it was not like that.

IIRC, Kay's vision is that OOP is about messages.

I have my own pet theory, of course it must be wrong too, but if I may, only as food for thought: The core usefulness of OOP is usability. A language is a matter of connecting thoughts through a mental model. Subject, verb, predicate. Object, method, parameters. That's mostly it. The rest are implementation details and lots of bikeshedding.

padobson · 2 years ago
The core usefulness of OOP is usability.

This is the reason I find it useful. To me, OOP is as much about your organization as it is about the best way to load, transform, present, edit, and store data. I think the culture of some companies lends itself to various kinds of programming, but it's the cultureless companies where OOP is most useful. The places where nobody is trying to change the world, where people work to pay their mortgages, where an executive may only work for two years and a programmer may only work for six months.

It's in an environment like that where a self-documenting, self-configuring code base with custom classes and exceptions that guide the next developer is essential.

Every developer should have two users in mind. The person using the software, and the next developer who maintains the software after you're gone. OOP is a great way to empower the second user when the only thing that will reliably outlive the developer is the code base.

Hermitian909 · 2 years ago
I think that OOP is the best way to accomplish the goals you list only if you stick to languages with mainstream appeal - but think it's sad that's the state of the world. Objects (as they are used in e.g. Java) default to stateful and complect data with the methods on that data. I find that most of the time what I want is closer to OCaml's Modules[0] which give me many of the tools of code organization without the complexity with state. (note that OCaml allows objects, so you get a real sense for how often you want an object over a module, 95% of the time I wanted a module).

Maybe one day modules will hit the mainstream.

[0] https://ocaml.org/docs/modules

dial9-1 · 2 years ago
you can accomplish that with just modules and functions
Consultant32452 · 2 years ago
I want to second the idea that the primary benefit of OOP is logical organization.

For smaller projects I don't care one way or another about OOP practices, but once you start getting into hundreds of thousands of lines of code IMO it becomes an absolute necessity.

thumbuddy · 2 years ago
Everything is wrong or illfitting. I mean we are making tiny blocks of silicon do extreme amounts of human work, while being entertained with music and videos. OOP is like five abstractions deeper than what it intend to model. It's a tool, I wish people stopped trying to make the extreme case of engineering that is software development some sort of axioms around physical law. Our expectations would be way better especially when our bosses require we implement a nosql db that stores rss feeds and use machine learning to sort them in order of "coolness".

There's very few actual rules despite opinionations derived from arbitrary paradigms and what should be done to work cohesively as a team. I wish there were a smidge less ivory towers and a smidge more common language.

Deleted Comment

digging · 2 years ago
> The core usefulness of OOP is usability. A language is a matter of connecting thoughts through a mental model.

Incidentally, this is exactly how I came to "reinvent" OOP as a newb programmer. I was working in a codebase where I didn't know what tools I had at my disposal, but we had a few modules where I could just type `module.` and see a list of all the methods, right there in front of me (in VSCode's intellisense). I asked, "Why don't we do this for our helper functions?" and we ended up with a handful of major objects to import with easily discoverable methods.

now of course I could have crawled the codebase to get the same information, but for someone new to programming and/or someone brand new to the codebase, that isn't necessarily a good use of a time. it can be a lot of slow unraveling of "what goes where", whereas organizing things in "OOP" (I still don't know if I'm actually using this term right because it was just part of how I learned, without being named) teaches that information more quickly through use and experimentation. basically what I was looking for was "namespacing" I guess, but I would also say it helped organize our code in a more useful way.

arethuza · 2 years ago
What about things like CLOS where you have multi-methods and dispatching is done based on the type of all arguments?
whartung · 2 years ago
Runtime, multi-method dispatch is probably the singular thing that stands out as "missing" from other systems. I don't miss it a lot, but there have certainly been times when I have.

I also enjoyed the ability to not be locked into the rigor of a class structure when it comes to methods. Since CLOS is function based, it's trivial to add functions to a class that you don't even have the source code to.

There have been many times working with a 3rd party or system library where I've had that "if only I had this little method" moment that would make my life easier. I'd rather have that capability and fight, say, namespace issues for the "Well what if everyone added their own 'upshiftFirstLetter' method to the String class" problems on an ad hoc basis.

Part of this, of course, stems from the locked down nature of the scope of classes. Not having access to internal structures and state. I'd rather take those risks of leveraging internal state knowledge not supported by the original designer vs the alternatives of sometimes having to throw out the baby with the bathwater.

narag · 2 years ago
I haven't used it, so not sure if it fits the pattern, but the question for me is: is it useful, time-saving, more usable? does it make the task more clear for the programmer that uses it?
mistrial9 · 2 years ago
combine a type system with dispatch logic, using abstractions .. it is very clear for some engineering applications and/or tinkertoys. Many people can get the basic ideas with thirty minutes of introduction.
jghn · 2 years ago
> IIRC, Kay's vision is that OOP is about messages.

Yes. The actor model is closer to what Kay proposed than the C++/Java style of OOP.

goatlover · 2 years ago
But Smalltalk also has inheritance and metaclasses, so it wasn't just objects passing messages. It was also designed as a visual live programming environment which was basically the entire computer system. Kay had his vision, but Smalltalk was implemented as more than just message passing.

That and Simula proceeded Smalltalk, which C++ was inspired by, even if Kay coined OOP.

Deleted Comment

cheschire · 2 years ago
> The app is the object, and the various URL handlers are its state.

There were a lot of assertions in this article that rubbed me wrong, but this one was particularly egregious. Handlers are behavior. They handle something.

This feels like the kind of article I would've written early in my career when I was getting really clever with stuff and starting to be able to question the way I understood the world of programming.

robertlagrant · 2 years ago
I think it's saying that the state of the app object itself is the configuration of the linkage between paths, verbs and handler functions, and that's why it's worth making an app an object.
musingsole · 2 years ago
The Handlers implement behavior, sure. But from the perspective of the `app` object, the handler function objects are state. You can tell, because `app` is an instance of the Flask class and the Handlers are added to the instance by way of a function call.

So, Handlers are functions that "handle something", but they are also part of the app object's state, not its behavior.

kiviuq · 2 years ago
By that definition an app could never be stateless ?
BeefWellington · 2 years ago
That's a very oversimplified view of handlers.

An alternate view might be:

Handlers are functions that are part of the app object's state, which alters the app's behavior.

In the majority (possibly overwhelming majority) of the cases this is true. The purpose of Handlers and similar Publisher/Subscriber patterns is effectively to change application behavior when they are configured.

naasking · 2 years ago
> There were a lot of assertions in this article that rubbed me wrong, but this one was particularly egregious. Handlers are behavior. They handle something.

    class App {
      public IHandlerFoo Foo; // routed as /app/Foo
      public IHandlerBar Bar; // routed as /app/Bar
      ...
    }
    ...
    App.Foo = new HandlerFooInstance(...);
    ...
Foo, Bar, etc. can evolve under an app's state changes, so the handlers to invoke are states of the app. They implement behaviour too, yes, but the original wording isn't wrong either.

weego · 2 years ago
The article even displays clearly that the URL handlers are not state. They're the contracts that help negotiate state.
kgeist · 2 years ago
I actually like "dumb" service classes with 1 method. Usually that method may require tens of dependencies to function (db connection, authorization logic, etc.), which in turn have their own dependencies recursively, and your choices are: 1) use global variables/functions for dependencies, 2) pass all dependencies as arguments to your function, 3) inject dependencies in the constructor of a service class/functor. Global state is a no-no in multithreaded code and is poorly manageable (side effects and all), while forcing a client of your function to pass all required dependencies manually as arguments is cumbersome and basically leaks implementation details (hard to refactor). So for me, service classes are the best option. It's kind of equivalent to a closure (object fields are basically captured variables), just written using the familiar OOP syntax (OOP syntax != OOP semantics). In the context of web, I tend to prefer stateless architectures (because they are easily scalable and less error-prone), and coupling behavior with state often goes in the way, in my experience. So my favorite architecture is a network of service classes whose dependencies are injected in their constructors and which are basically "smart" functions (not "objects"), they have no state, and they operate on domain objects which have no logic other than basic self-validation during mutation (to uphold invariants). I know it's a heated topic (rich model vs. anemic) but I've seen projects naturally, through evolution, end up being more anemic than rich. Pure OOP, where state and behavior are entangled, feels very cumbersome to me, it's harder to refactor when requirements change every day, and having too much state is pretty error-prone. YMMV.
jbreckmckye · 2 years ago
It sounds to me like you're using classes to implement curried functions. In this case with the IO / DI context as the curried parameter.

Working in functional langs I just do DI with higher order functions.

slively · 2 years ago
Exactly! I find myself writing out the same code with classes and functions to show this to folks. I still prefer using classes as it’s easier for me to see at a glance that constructor = DI/curry vs a function returning an object of functions or something. So a class is just a way to communicate a pattern.
moron4hire · 2 years ago
"A closure is just a method where the instance members are implicit. A method is just a closure where the closed over state is explicit."

I agree with you about service classes. I think one of the things that service classes do well that regular functional code does not is indicate to the consuming developer which parameters are meant to be provided by the environment and are largely static, and which are meant to vary per invocation.

Lisp does this instead with dynamic scope. But dynamically scoped dependencies felt too much like global variables to me. The service class instance has its scope bound to it and it won't change. But dynamically scoped functions always felt like the ground could get ripped out from under me at any time.

galbar · 2 years ago
I do this and advocate for this in the teams I work in. In my experience, it works well.

Service classes are "configurable functions", as I tell my team. With the advantage that you don't have to monkey-patch an import in another module to do a unitary test of the function, but rather you can inject mocks!

One thing I do insist on is that a service class either does something (calculate some value, retrieve some information, etc.) xor it coordinates service classes (parse value, then call the validator and, finally, persist value). This aims to prevent deep call chains where every function in between does a little bit and calls someone else, which can be hard to reason about.

user9925 · 2 years ago
This is exactly how I write code. I wonder if there is a name for this pattern? Something like "functional dependency injection"?
AugustoCAS · 2 years ago
I recently escaped an organization that had a couple of apps built using this approach everywhere but for the opossite reason stated in the article. The team wanted to write code in a functional way in Java because _FP is better than OO_.

Most (if not all) classes had a single method and implemented BiFunction. The app was wired with Spring... if you had the bad luck of using Spring you can get the idea of how ugly this was. All method invocations were `apply`s and navigating the code was as easy as finding a needle in a haystack in the middle of the night. A good amount of the wonderful features of a tool like IntelliJ couldn't be used.

My rule of thumb is to follow the grain of the language / framework / library as to produce as little surprise to other devs.

a_wild_dandan · 2 years ago
Your org avoided OOP...by using the quintessential OOP language? That is, ah, certainly a perfect way to fail at functional programming.
c_crank · 2 years ago
I now want to work at a shop that uses fully imperative Haskell.
BeefWellington · 2 years ago
What's amazing about this is there are other very good functional JVM languages they could be doing this with...
bob1029 · 2 years ago
> Stop writing stupid classes.

This is really it. It's not a game of all-OO, zero-OO or strictly following a pattern you saw on YouTube that one time. It's all about nuance and minimizing the # of moving parts until each is really serving you some value.

I am in the process of ripping out a bunch of class bloat in a product iteration. Our typical pile of model class POCO spam for "one thing":

  Entity.cs --The "official" view
  EntityController.cs
  EntityView.cs --Another common view that became popular over time (???)
  EntityTableRow.cs --gotta have that 1:1 sql model, amirite?
  EntityViewForTypeA.cs --what was wrong with the common one :(
  EntityViewForTypeB.cs --And again...
  ...
Ultimately, every time we wanted a "view" of the data, we wound up creating another type to enshrine this and then tried to figure out how many holes we could more-or-less hammer it through. Every one of these has some cranky mapping layer too (NxM if you want to get crazy).

The alternative pattern I am working with is to directly query my database in the exact place the data is required and to use anonymous / scalar return types like so:

  //Some function that produces a HTML partial view for a webapp

  //View w/ join that would otherwise require a new POCO type to communicate.
  var myEntityView = await sql.QueryFirstAsync(@$"
    SELECT e.Id AS Id, ep.Name as Name
    FROM Entities e, EntityProperties ep
    WHERE e.Id = ep.EntityId
    AND e.Id = @Id", ...); 

  //Direct usage of anonymous result type.
  return $@"
    <h3>Entity:&nbsp;
      <a href='Entities/{myEntityView.Id}'>{myEntityView.Name}</a>
    </h3>";
In the above, there are zero POCOs or "messenger" types. Just a raw query right where it needs to be taken, without any extra ceremony or implications for other code sites. I actually don't have a single type in the new codebase that is a POCO, other than schema tracking models (which are still useful for interpolating into queries to enforce magic strings). Effectively, the only domain data types are 1:1 with the SQL schema now.

vkou · 2 years ago
>This is really it. It's not a game of all-OO, zero-OO or strictly following a pattern you saw on YouTube that one time. It's all about nuance and minimizing the # of moving parts until each is really serving you some value.

Minimizing the number of moving parts shouldn't be your goal.

Testability of the moving/complicated parts should be.

I don't really care about how you break up your code, but it should be trivially testable, at both the unit, and the integration test level. OOP makes it at least possible for less experienced developers to accomplish both of those things.

It's nice that you got rid of all the indirection and all the nearly-pass-through interfaces in your code sample, but how would you test it? In a unit test? In an integration test? Are you going to need to dial out to a real, live SQL database to do so? For every test? Will you be re-using the database across multiple tests, and thus, require all your tests to avoid object collisions? Paying a startup cost for every test you run? Reusing it for some tests? Who's going to maintain that reused, QA database? Who's going to deal with it when some clown pollutes it with garbage?

These are all solvable problems, they have multiple approaches to solving them, with varying tradeoffs, but they are tradeoffs. 'Simpler-to-write' code is usually not the tradeoff you want to optimize for. You write code once, you test it thousands of times.

bob1029 · 2 years ago
> It's nice that you got rid of all the indirection and all the nearly-pass-through interfaces in your code sample, but how would you test it?

> Are you going to need to dial out to a real, live SQL database to do so? For every test?

Yes. This is actually how we do it. And if you think deeply about it, this allows you to skip seamlessly between unit & integration testing, assuming all of the system components are designed against the same database. The only meaningful difference between unit & integration in this context is how much state you allow to accumulate in the database between invocations. You can reset for each method using a known expected initial state for each, or you can stand up an initial state and fly through an entire playlist of method calls.

If all of your functions take a SqlConnection object as their first argument and you've made sure that 100% of application state resides in the database, what can't be tested in this manner?

naasking · 2 years ago
> In the above, there are zero POCOs or "messenger" types. Just a raw query right where it needs to be taken, without any extra ceremony or implications for other code sites.

This is a terrible idea IMO. Creating a POCO per view is not a big deal, you're already implicitly depending on a specific type contract in the view, except now it's implicit and dynamically typed and so subject to all of the problems that follow from that.

prewett · 2 years ago
Maybe I've been ruined by early exposure to C++, back when C++ was the new hotness, but I find encapsulation and even the namespacing aspect to be helpful. Encapsulation is obviously helpful, but it also comes in handy for some algorithms that are complex enough to require several functions, but which share a lot of state. If I recall correctly, Voronoi tesselation is one of these. Even though an "tesselate" is an action, putting it in a class just makes it easier to think about things. Otherwise as a user of the function I have to first create the shared data object, then know to call the three helper functions. Better would be a high-level tesselate() call, but then that call needs to do that. Now you've just pushed the complexity down to the next maintainer, who needs to figure out how all this soup of functions from all the various algorithms and whatnot that have collected over the years relate to each other. With an object, the newbie maintainer at least knows what the topic of the functions is.