I Don't Use Classes - <AUTHOR> https://spin.atomicobject.com/2020/03/12/why-i-dont-use-classes/#.Xmom-cfXe3U.hackernews <comment><USER2> Hm. Not sold. Sure, you can write terrible code with classes. But that also works great without classes.

> I'm terrible at making good abstractions with classes.

Then don't. Or only do it after everything is done and you have explored the domain. Don't try to be clever from the start. Unless you have understood everything, it will backfire.

> But as other classes extend and override those methods to address their > version of the problem space, they usually end up having some other > responsibilities in addition to the original task.

Inheritance gone wrong. If a derived type behaves differently, it should not be a derived type. See Liskov substitution principle. Just because a hash map is somewhat like a list of key/value pairs doesn't mean it should derive from one.

It could also be a problem of missing separation of concerns/responsibilities, which is entirely unrelated to OOP.

> Secondly, I've noticed that classes have a tendency to grow large.

Yes, I did see many big classes. They were always the result of... missing separation of concerns/responsibilities. The fabled god objects.

The section about state management appears to be very much React-specific, too. So, not about classes.

<comment><USER3> > Then don't [use them, it's that simple!]

The problem with classes in languages that use them as a main abstraction (Java, Ruby, etc) is that people reach for them for all abstraction and software design as their Day 1 tool.

All program state and behavior is now immediately fragmented from Day 1 in arbitrary buckets and my experience some moss quickly grows on them and things are never changed for the life of the program.

Just seems your post doesn't offer much insight beyond the classic OOP advice I've heard for years when people realize it's not that great: oh no, you're doing it wrong, don't do the bad things, just do the good things and ensure everyone else is doing them too. I don't see how that's actionable nor useful.

Limiting class use as much as possible and using functional module pattern as TFA does, on the hand hand, is actionable advice for these common issues with classes.

<comment><USER4> I just don't see much of a practical difference between these two approaches. From a functional perspective, objects are fundamentally a set of namespaced functions that implicitly take the same state/map/JSobj as an argument bound to "this". You can pass that state in explicitly and it just becomes what the author is talking about. And you can have the same state-management nightmares with that approach, because nothing is fundamentally different. Instead of obj.method(arg1) you have namespace::function(obj, arg1). I've worked in a very large frontend codebase using this technique and it's just as much of a pain as implicitly managing state with objects, if not more of a pain b/c it's no longer idiomatic.

State management is hard. Changing how you refer to the state isn't going to make it any easier, just different.

<comment><USER5> The difference with this approach is that _any_ function can take obj as an argument. If it's pass by reference, any function can _mutate_ obj. If obj winds up in an inconsistent state, any function in the entire codebase could have done it.

With a class, however, you normally make the data members private; that is, only the member functions of the class can change them. If the data gets mangled, you now have a very small set of places to look. That difference matters when debug time comes around.

<comment><USER6> If you use a functional approach, you're probably not mutating anything ever. That usually makes it trivial to find the source of a problem, because you can quickly isolate it to a specific call stack.

> _only the member functions of the class can change them. If the data gets > mangled, you now have a very small set of places to look._

But probably anything could have called those mutating methods, so you still need to consider the entire codebase.

<comment><USER5> Anything could have called those mutating methods. But only those mutating methods could have put the object in an inconsistent state. I therefore have to fix the method that did that, and then let anything in the entire codebase freely call those methods.

</comment></comment></comment></comment></comment></comment><comment><USER7> Best thing about 20 years of freelancing has been that I have NEVER been asked about, ridiculed for, or heard complaining about my code. Nobody even sees it, or cares. It's efficient and responsible code in my opinion, but I don't have to care about this strange problem where programmers actually have opinions about how code is put together. I don't know how you guys put up with it, I'd go nuts if someone told me I had to use more classes, or a certain language, or tabs over spaces, or whatever.

<comment><USER8> I think we put up with it because personal development requires outside feedback. I couldn't imagine getting better at the pace I am without both having to justify and explain my decisions to others.

I don't understand your viewpoint at all.

The best part about coding for me is getting a review with a lot of interesting comments about how it could be done differently. It jogs my brain down paths I would never have seen by myself.

<comment><USER9> As a consultant (not the OP) one of the best parts of writing code is having it solve real business problems (and getting paid well for it). It's a point of personal pride to write reasonably good code, but the structure doesn't matter to the person paying the bills - the outcome does.

It's like living in a house: as long as it doesn't fall down and doesn't cost unreasonable amounts to maintain, I don't really care about how well-formed the mortar is, or that the drywall is hung perfectly according to company standards. I just want to live in the house and not worry about it.

<comment><USER8> Well, what I'd say to that is:

* Somebody maintains that code. If it is only you you have a bus-factor of one if none of it is actually reviewed. If it is not you you are imposing a single persons idea of what the business problem is and how it should be solved on the maintainer, which they might not agree with

* Personal development depends on realizing when you are wrong or have used the wrong tool. If nobody challenges your decisions you will probably not realize that as often (or at all) and therefore not improve as much.

* Solving business problems means solving problems for a business. Usually businesses evolve over time, so the business problems might change. Who will understand the code in that case? How do you know the code is understandable to other people if nobody else reviewed it?

And as for the house example: before buying that house I would definitely hire somebody else to check it, and that is legally required in some countries. I don't trust anyone to rate their own work.

In this case the person who hired your consultancy bought the house, and they should definitely have an expert that did not build it check it.

\---

Just a FYI: I'm also a consultant, but work on a project where we have discussions about how to structure the code and do code reviews and so on.

<comment><USER9> Yes yes, I agree with all that. I much prefer to discuss approach with someone senior before tackling hairy problems and have others look at my approach after the fact to improve. But since I often work with clients where I am the dev team, I rely on my professional network to discuss approach.

Agree that getting a technical review from a third party isn't a bad idea.

FWIW this doesn't change my main point: the business owner cares about outcomes, and not code structure (a good outcome is dependent on the code being good enough to maintain e.g. the house not falling down). There's a simple test for this BTW: write lited, beautiful code that doesn't solve the business problem you were hired to solve and see how popular you are with your client.

</comment></comment></comment></comment></comment><comment><USER10> The main benefit of classes is as a way to define custom types. Otherwise you are stuck with the types built into the language, which are not designed to help ensure correct program state, logic, and behavior. When you use classes in this way, I call it type oriented programming (TOP). Why would I compare classes to types? Just pass in the initial value at construction time, define the "operations" on that type by creating methods for your class, and define which other types those operations work with by setting the types of parameters those methods work with. Make the class immutable, always returning a new instance with the new updated value passed into the constructor. Why did I mention these types helping the program be more correct? This should be used as the primary form of contracts. But these contracts are very simple to use, you just specify the relevant type in the parameters and return values, and you are done defining the contract for any given method. For example, let's say a method should only work with integers larger than zero, instead of either accepting an Int or an UnsignedInt, which would allow 0, you could define your own class called PositiveInt. It would be designed to throw an exception if you pass anything smaller than 0 into the constructor. Then instead of writing code inside the method that makes sure the user of the method is following the contract, you just specify PositiveInt as the parameter type. If the contract is violated, the exception will be thrown as early as possible, before the method is even called, helping programmers catch the original source of the problem. This also makes your code more readable, because you can see exactly what each method accepts and returns just by looking at the method signature. When you start thinking this way, you will notice many core types are missing from the language, that should have been there from the beginning. Fortunately you now know how to build them yourself.

<comment><USER11> > The main benefit of classes is as a way to define custom types. Otherwise > you are stuck with the types built into the language

I'm going to stop you right there, because plenty of languages (especially functional ones) have ways of declaring custom complex types without using classes.

<comment><USER10> Which languages would you say are really good for defining your own types? Would they be a good fit for the example I provided where a function needs to accept integers larger than zero? Do they also allow you to define your own operations on those types? That is the part where classes seem to be a good fit, methods are basically operations supported by a type.

<comment><USER12> > Which languages would you say are really good for defining your own types? > Would they be a good fit for the example I provided where a function needs > to accept integers larger than zero?

I'm not an Ada expert, but it has excellent support for range-restricted integer types.[0]

Ada's 'discriminated types' are also fun. They let you create members which only exist when they're applicable. [1]

> Do they also allow you to define your own operations on those types

Looks like Ada supports operator overloading, yes. [2]

[0] [https://www.adaic.org/resources/add_content/standards/05rm/h...](https://www.adaic.org/resources/add_content/standards/05rm/html/RM-3-2-2.html)

[1] [https://www.adaic.org/resources/add_content/standards/05rat/...](https://www.adaic.org/resources/add_content/standards/05rat/html/Rat-3-5.html)

[2] [https://www.adaic.org/resources/add_content/standards/05aarm...](https://www.adaic.org/resources/add_content/standards/05aarm/html/AA-6-6.html)

<comment><USER13> Ada is also great at separating the various aspects of OOP into separate language concepts, instead of going "everything is done with classes!!" as many popular languages do, leading to a lot of confusion in this thread.

</comment></comment></comment></comment></comment><comment><USER14> You're always going to be on one side of the expression problem [0]: either your operations live with your datatype (classes), which makes it inconvenient to add a new operation, or they live somewhere else ("functional modules"), which makes it inconvenient to add a new case to the datatype. This choice is probably informed by your expectations of how often you intend to do either of those things, but there's going to be inconvenience regardless.

The big benefit of objects, and therefore classes (or something like them), is polymorphism via dynamic dispatch; it seems a shame to throw the baby out with the bathwater because you don't like inheritance or mutable state, both of which are independent of the choice to use classes.

[0] [https://en.wikipedia.org/wiki/Expression_problem](https://en.wikipedia.org/wiki/Expression_problem)

<comment><USER15> I'm not sure dynamic dispatch is that much of a killer feature. Take C# it default to static dispatch of method and you tend to get very far without explicitly making things virtual.

Event when using interfaces many times it's seems possible to arrange things such that parametric polymorphism would be able to use a static type. There are some boiling proposals for future versions that will help with exactly this, and indeed some of the latest changes to the languages have included allowing more static dispatching in polymorphic constructs like disposal and enumeration.

I'm beginning to suspect that dynamic dispatch could be entirely dropped without losing to much expressiveness.

Have no experience with Rust but isn't that kind of the conclusion from that community?

<comment><USER16> rust has `dyn FooTrait`, which lets you do dynamic diapatch, similarly to `FooInterface x = foo();` in e.g. java. even Haskell has a way of doing this via existential types like `(forall a. FooTypeclass a => a)`.

both of these predominantly statically-dispatched langs added ways to do dynamic dispatch later on, which seems to go against "dynamic dispatch could be entirely dropped without losing to much expressiveness". i prefer static- by-default as well, but it looks like it's hard to do without sometimes.

(of course you can always roll your own vtable-ish thing, so technically you could drop DD from the language without loss of expressiveness, but then you'd likely get multiple incompatible libraries for that...)

and i'd guess that some sort of dynamic dispatch is unavoidable if you want to do some kind of plugin architecture

<comment><USER15> Are you sure that Haskell thing is dynamic? I was under the impression that the type you have there is resolved statically by finding a type class for a

<comment><USER16> existential types allow you to make it dynamic. see [https://wiki.haskell.org/Existential_type](https://wiki.haskell.org/Existential_type), there's a section comparing it to OOP-style dynamic dispatch

simple example copied from the linked article:

      data Obj = forall a. (Show a) => Obj a

      xs :: [Obj]
      xs = [Obj 1, Obj "foo", Obj 'c']

      doShow :: [Obj] -> String
      doShow [] = ""
      doShow ((Obj x):xs) = show x ++ doShow xs

we can have a "heterogenous" list of `Obj`s, like a java `ArrayList<IPrintable>`. each `Obj` is basically a pointer to the actual value of some type T and a pointer to T's Show dictionary (vtable). Which is afaik analogous to how OOP languages do it

</comment></comment></comment></comment></comment><comment><USER17> Classes are a means to an end: objects. Objects give us the three core components of OOP:

* encapsulation

* message passing

* late binding

Things like inheritance tend to muddy the waters. If you aren't benefiting from the above 3 things, then you are probably either using objects wrong or you are applying them to a problem for which encapsulated, late-bound message passing isn't a good solution.

As a corollary, state management and OOP are in my opinion orthogonal. You can write and program with immutable objects just as you can with mutable ones.

I do agree with some other comments that OOP in college isn't taught very well. Most people leave their first OOP class not really knowing anything about OOP or why it's useful. My hunch is that OOP is something that should be taught later in a program, not earlier.

<comment><USER18> IMO, when people talk about OOP they aren't talking about bags of static functions -- that's just a namespace. The interesting objects are those which hold state.

<comment><USER17> You can have an encapsulated object that is immutable and is not a bag of static functions anymore than any object is a bag of static functions. Look at Scala's interfaces for immutable collections.

* they are encapsulated. You don't know how the data is stored internally.

* There is message passing involved (at the JVM level). The methods are virtual and you don't know exactly which method is being called. There can also be multiple implementations using different data structures.

* Late binding is again as defined by the JVM (interface table method lookup)

In contrast, a static function is no different than a global variable. When you call it, you know _exactly_ (sans JVM linking) which function you are calling.

</comment></comment></comment><comment><USER19> Using classes doesn't necessitate using inheritance and polymorphism. Whenever I'm programming in a non-OOP language, I still gravitate towards class-like code. From my perspective, there isn't much different between:

doStuff(&fooStruct, ...) vs. fooClass.doStuff(...)

<comment><USER20> The core difference is in testing. With the later, you need to get your class into the right state to preform the test. You might even need a mock or two. With the non-OOP approach, you set the entire state of the function and see the results.

<comment><USER21> > you set the entire state

And how would you easily create that state?

<comment><USER22> You create a mock :)

</comment></comment></comment></comment><comment><USER23> One of my favorites:

[https://wiki.c2.com/?ClosuresAndObjectsAreEquivalent](https://wiki.c2.com/?ClosuresAndObjectsAreEquivalent)

The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."

Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.

On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.

</comment><comment><USER24> In school they taught classes and inheritance like it was the biggest thing since sliced bread.

I feel like they're but one tool, and necessary in limited cases.

For example I recently encountered an API that had to intake 100+ key/value pairs and manipulate these values, and send them to various places.

I tried to do the mappings and data manipulations and everything in one long piece of code functionally but it led to some serious spaghetti. When I separated things out into classes, it was much cleaner code, with a more well defined domains. It was definitely not perfect but ended up being much cleaner. This is a simple example but classes can be useful. It's arguable that the API could have been better written and that's true but sometimes you have to work with what you're given.

Otherwise I would venture to say most code doesn't require classes.

<comment><USER25> By "I separated things out into classes", do you mean that you used the whole triad of inheritance + polymorphism + encapsulation?

<comment><USER26> Do you need all 3 of them to have a class? In my experience classes are data+ methods that manipulate that data. Like a Vector class would have a rotate,normalize etc methods that return a new Vector.

<comment><USER27> That's a question of definition. There are many who will passionately insist that yes, you absolutely need all of them. I hypothesize but can't prove that these are people simply regurgitating what they were taught in a classroom and who have not yet left Java/C++ to see what else is out there and what else can work. It is perfectly feasible to be educated that way and then spend your entire career in Java or C++ and come to conceptualize everything else as "that crazy minority stuff that isn't like Real Programming", and, in their defense, they are at least correct about the "minority" part. It's easy to forget if you read HN a lot, but inheritance + polymorphism + encapsulation remain the _massively_ dominant paradigm, especially if you assume most C# is still basically written that way despite supporting other paradigms.

Personally I take the loosest possibly view, mostly just "associating data and methods to operate on that data fairly tightly together, where they all get passed around as a unit". Some ability to have "x.y()" result in multiple possible implementations of "y" is necessary, or you just have a weird way of spelling function calls. That's about all I'll require to call something "OO" nowadays.

<comment><USER26> I think the number of developers that when starting a project start creating some inheritance tree is large. Some concepts work fit inheritance very well like GUI widgets or game entities, you would make a disservice not to tell students about the concepts and have them re-invent them.

Not sure how other languages do it, but in my daily work the only time I use inheritance is with an ORM , to get the magic to work you need to extend the base Model class , add a few lines of configuration code and you are done, for more advanced uses I think you have to override some methods.

But except the ORM code I don't think I used inheritance in a long time, though I used interfaces in some other cases.

<comment><USER28> Game entities really don't fit inheritance very well though! Even if using an OO approach it's far more flexible to compose. Otherwise it suffers from the classic large inheritance tree issues of lots of state and functionality leaking up the tree to common base classes. As an example the Unreal Engine used to have a heinously enormous base class called Actor. Another is that all the game entities in EVE Online actually bring the AI code with them. You can make it work but it's in spite of rather than because of inheritance. Anyone teaching students it's useful is doing them a great disservice.

<comment><USER26> I don't have experience with game engines, but I worked with Qt, WinForms, Flex4, Swing GUI libraries, and it made sense. All components inherit from a base widget, This base widget has everything you need, layout, positioning, mouse and keyboard events, painting. If you want to make a custom button that say is rotating when you click it you extend the Button and not start from scratch like you do in html. In html you see many custom components created from nested DIVs, this custom widgets are missing many basic features like accessibility and keyboard shortcuts and are broken for edge cases. As an example PayPal has a money amount text input but the Delete key does not work on it.

Could there be some insane case where the OOP of the GUIO widget is problematic, maybe , like when you want your app to have a window in the shape of a circle ... but I think is fair you get easy to use library for 99% of the cases and for 1% of the cases you might have to get your hands dirty and go outside the standard ways and maybe look under the hood.

<comment><USER28> Yeah GUIs seem to end up fitting reasonably well. They tend to be fairly modelled as specialisations. The problem with games is that it feels like they should but aren't.

<comment><USER26> Maybe the issue is that the engine game evolves, new requirements are added and maybe a developer has a bad day and some ugly code is created.

If we consider a game like Minecraft, I would use something like a base class for blocks and a different base class for mobs, then if you add a new feature you put it in base class and all the objects will get it for free. I am not sure how Minecraft is architected but I loved how all entities including the player are the same, if you can do something to a player(push, drawn, burn, catapult, activate circuits , etc) all mobs would work the same.

I think GUIs work well because is a very well known and studied problem where with game engines you have a generic problem and then each game is bending the engine to try to use it for different type of games that the engine author did not consider at the beginning.

My conclusion is that some OOP is a tool you can use, you should not avoid it because a dude in a blog said so,the same with GOTO - I think I only used GOTO once , it was more efficient and more readable to use goto there then trying to workaround using it by creating variables and then adding checks (the problem was to efficiently break from inside 2 or more nested loops efficiently)

</comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER29> This is like so many "OOP is bad" posts. Yes, OOP is bad.

OOP is bad because inheritance is not as good as interfaces.

OOP is bad because typically it puts state changes inside mutable, shared objects, which makes concurrency difficult.

Java has interfaces and generics -- use those instead of inheritance.

C++ has templates -- use those instead of inheritance.

Design and implement interfaces that operate on immutable, preferably linear data types, and concurrency will be easier.

</comment><comment><USER30> My prefered style is a class that's ideally no more than one level deep. Mix in some traits if you need them. Also, as such as possible, make every class/object immutable and the methods side-effect free. I find this leads to a very natural style in which functions are logically grouped together (also, helps with intellisense) but never modify any state. If there are classes/functions that modify state, make it explicit through naming (I append '_!' for Scala).

Essentially, what I'm describing, is just functional programming where f(a, x) becomes a.f(x). I find the latter easier to read and lends itself to a fluent, lightweight style.

In general, I don't really think the discussion of OOP vs FP or whatever is that meaningful. What is meaningful is mutable vs immutable. Every great developer I've met has agreed with me that mutation should be avoided as much as humanly possible: really the only valid reason to introduce mutation in your program is for performance reasons. And yet, we as an industry still are by and large writing mutation heavy code for no reason at all. Things have gotten a lot better in the last 10 years, but we still have a long way to go.

Edit: I'm mostly talking about Scala in this post, btw. I also want to note that I do think deep class hierarchies are a good solution to some problems. The two examples that immediately come to mind are UI APIs and collection libraries. Both these problems are very naturally solved by OO, the essence of which is the deep (as much as necessary) class hierarchies. Very few problems I've encountered besides the above actually require these subtyping.

An great alternative approach is row polymorphism, ala OCaml. I've not had any professional experience with OCaml or row polymorphism, so I'm not sure how well it actually works in practice.

<comment><USER31> > I find the latter easier to read and lends itself to a fluent, lightweight > style.

Maybe I'm bikeshedding, but I think this is a nontrivial part of why people like OOP languages. Being able to read right to left, without nested parens, makes code comprehension easier.

I think the mathematical notation for function application has held programming languages back. I realize that there's a huge benefit to uniformity of syntax across languages, and that syntax is ultimately much less important than semantics, but I wish we could settle on something better than f(x). I really appreciate languages that are willing to do something different (e.g. haskell and lisp).

<comment><USER30> There's the pipe operator in a lot of languages (|>) to try and solve this problem, but it has always felt clunky to me. Compare:

f(g(h(x)))

x.h.g.f

x |> f |> g |> h

I think most people prefer the second one to all the rest.

<comment><USER32> I think the third example should be:

x |> h |> g |> f

Which is identical to the second, except for a more eloquent choice of the pipeline symbol?

<comment><USER31> That is the version of the |> operator I'm familiar with from Julia.

</comment></comment></comment></comment></comment><comment><USER33> "As I've mentioned above, I like to use modules that expose groups of functions. These functions accept state and other dependencies. These modules tend to look like what my colleague Drew describes as the functional module pattern."

I dislike that.

When I see a class, I expect it exists because there is a central state around which the methods revolve, and are meant to expose or change it.

When I see a function, I expect it to be side effect free.

Sure you can use any paradigm to make any program, but I use paradigms, just like I use design patterns, to communicate intent. Solving the problem is half the battle. In a way, coding style is documentation. I think we should build boring API, and only use cleverness to find solutions, or make the API easier to use and more elegant.

I worked once on a code base that broke a lot of implicit expectations. Like, it could add objects in a registry when you instantiated some classes, get_stuff could set other stuff, do_stuff would not do stuff but actually_do_stuff would, etc.

It was a nightmare to work with, because anything could happen anywhere. You had to look at the code every single time and keep the whole system in your head, which had 900k lines of highly technical code.

<comment><USER34> You misunderstand, I think. Or maybe I do.

If done properly as described, the functions accept state. This means they adjust state by adjusting input/output parameters, not by affecting some internal state. This is basically Dependency Injection extended to state. This is also how classes are implemented (see python's _self_ parameter to all class functions).

<comment><USER33> Yes, but looking at the function, and banning some project specific naming convention, there is no way to know its role is to mutate a state.

While in Python, when you see a class, you just know the methods are mostly about "self".

It helps making sense of the code immensely.

<comment><USER34> Correct, but the effectiveness of the design is separate from its correctness. Latter of which is what I commented on.

For effectiveness, I think it reduces ancillary class members, and encourages (necessitates!) dependency injection.

For example, changing "delta t" in a game object update is trivial: update the one delta-t you have, and pass it into everything that needs it. With classes, there may be a temptation to have a delta-t member variable, which costs many updates. Stupid example, but a mistake which is not possible with Dependency Injection.

But really, proper scoping in typed languages (and naming in dynamic ones) can allude to input vs output parameters. In C/C++ this is passing by ref vs value. (In Google C++ style guide, they recommend use of pointers for "adjusted" or "output" parameters.) In MS C++ from back in the day, I think they had ways to explicitly specify parameters as input, output, or input+output. Does someone have an example of this?

</comment></comment></comment></comment><comment><USER35> JavaScript, and thus Typescript by extension, lived for _many_ years without classes. The addition of classes is relatively recent. It stands to reason that classes are not heavily adopted since since a lot of the foundational work happened when classes were not even an option.

In contrast, I cannot image being able to get far in Java without using classes. The concept of using classes is central to that language.

<comment><USER36> > I cannot image being able to get far in Java without using classes

Well you literally can't write a Java program without writing a class.

</comment></comment><comment><USER37> The #1 complaint I always see with classes is due to inheritance. When classes inherit from classes that inherit from other classes, you can very easily end up with a tangled web of methods over-riding and extending other methods, and run into all sorts of unexpected behaviors and difficulties wrapping your head around what is going on.

This is why (at least in the Ruby world), it's generally considered best practice to avoid using inheritance and just use mixins instead (whenever possible).

Completely avoiding classes because you are using them incorrectly is kind of like throwing the baby out with the dirty bathwater. Don't throw away your tools just because you are using them incorrectly!

<comment><USER38> Inheritance is a debate that already ended long ago with OOP practitioners vehemently against it.

The argument now is different. There is another flaw with classes and that is mutation of internal variables with getters and setters. Classes typically allow you to do this and promote this behavior, structs do not typically allow you to do this and are not used this way, typically.

It makes it so that a class is more than just a data type, it is a mini program with it's own internal state and api that you are passing around in your program. It ups the complexity of your program by 10 fold.

<comment> T-hawk This is a great post. "A mini program with its own internal state and API" is a great description of what goes wrong with overdesigned classes. Encapsulating state into classes originally made sense with early OOP languages, compared to all of it being global which is even worse, but nowadays we've got better ways of managing complexity.

Any time something threatens to be a mutable class-level variable, I refactor that out of the design before proceeding. Most often this refactoring is to separate the data into a struct-like class and the behavioral methods into a static class, with what would have been that mutable class-level variable instead passed in to each behavioral function that needs it.

<comment><USER28> I feel like you missed a bit of the explanation out there. Where does the struct like class end up being instantiated and how is it linked to the class that presumably would have previously contained it? Does the struct like class get put into some kind of data store and the class that needed mutable state then holds a handle or similar?

</comment></comment></comment></comment><comment><USER39> I think the problem is that most developers did not learn OOP correctly. I noticed a lot of developers these days don't understand basic concepts like encapsulation and class dependencies.

When I was studying OOP at university, there was an entire course on UML diagrams and that taught people how to structure OOP software. Good OOP design tends to produce elegant-looking UML diagrams with relatively few interconnections between components.

I don't always physically draw UML diagrams, but when I visualize a project in my head, it looks very much like a UML class diagram. I can't imagine how it's possible to reason about complex software in any other way TBH.

Sadly, if I had to draw a UML class diagram for most real projects which exist today, the relationship lines between UML classes would be crossing all over the place and it would literally look like a tangled mess (definitely more like a graph than a tree). The people who designed the software had no vision for it, they just kept adding stuff without ever actually understanding how it all fits together or thinking about how to minimize complexity and make it more elegant by minimizing the number of components and the interconnections between them.

I think that, in a way, functional programming is more forgiving when it comes to allowing people to keep adding stuff on top without having to visualize the system as a whole. It doesn't mean that FP adds any net value over OOP. I would argue that being able to clearly visualize the whole product and the interaction of data and logic is essential regardless of what paradigm you follow.

</comment><comment><USER40> Classes are the design-time version of instanciated Objects, which are active at run-time.

Objects, are like little minature software programs, _inside of a larger programs_.

Think fractals, that is, smaller things inside of larger ones...

So why have a minature software program inside of a bigger one?

The short answer is _boundaries_.

If the larger program is permitted to modify the internal state of an object, then _that 's really no different than having one big program where all of the variables are global_, and any line of code can make any change to any variable at any time.

Is that OK? For small programs it is, but the larger the program, and the more programmers that work on it, the more it must be segmented into boundaries, and these boundaries enforced (aka, encapsulation) in order to prevent bugs.

That, and the ability to divide code into logical groups which can be individually tested will yield a great deal of clarity and information about a codebase, when done by a competent programmer or programming team.

At 100,000+ lines of code (and sometimes a lot less), _you will trip over your own code unless you segregate it into seperately manageable sub-systems with clearly defined boundaries_. "Divide and conquer" as the old saying goes.

On the other hand, programmers inexperienced with Object-Oriented development can make code more obscure, unreadable, and harder to work with...

It's a double-edged sword...

Use wisely.

<comment><USER41> But Classes and Objects are not the only way to create boundaries, and may not be the best way.

<comment><USER40> Give an example of when they might not be the best way.

<comment><USER42> There are many well known problems with classic OOP:

[http://okmij.org/ftp/Computation/Subtyping/](http://okmij.org/ftp/Computation/Subtyping/)

</comment></comment></comment></comment><comment><USER43> Finally, a man who truly strives to live in a classless society.

<comment><USER44> And as always, first thing a classless society does is to find a way to emulate classes.

<comment><USER43> You win the internet.

</comment></comment></comment><comment><USER45> Neither do I if I can avoid it, I use type hierarchies and generic methods.

Unfortunately it's not very popular; the only supporting languages I know of are Common Lisp, Julia & Perl 6.

I've designed and implemented several scripting languages [0] over the years, but they all have this pattern in common, I've never felt tempted to implement classes.

[0] [https://github.com/codr7/gfoo](https://github.com/codr7/gfoo)

<comment><USER46> Dylan also supports it and it can be done in Python with a library [1]. I don't know if you'd count it but it's also straight forward in Haskell [2] at the type class level.

[1] [https://en.wikipedia.org/wiki/Multiple_dispatch#Python](https://en.wikipedia.org/wiki/Multiple_dispatch#Python)

[2] [https://stackoverflow.com/questions/26303353/can-multiple- di...](https://stackoverflow.com/questions/26303353/can-multiple-dispatch-be- achieved-in-haskell-with-pattern-matching-on-type-class)

</comment></comment><comment><USER47> Surely the ultimate example of this phenomenon is OCaml.

It's wearing OOP right on its sleeve in the name of the language, but its users inevitably say "Oh, we never (or hardly ever) use that part".

<comment><USER48> Absolutely. Objects in OCaml is useful for structural typing, but I reach for classes way less often than I initially thought I would (read: never).

It turns out modules are indeed a better solution for, well, modularization.

OCaml is multi-paradigm in the most broad sense of the word I've experienced, just with functional and immutable as the default rather than the other way around. And it has a pretty great OOP implementation too.

Suggested reading for those wondering why the community deemed the O in OCaml mostly unnecessary and how rest of the language helps achieve the same goals in a different/better way: [https://discuss.ocaml.org/t/objects-use-cases-in- ocaml/2282](https://discuss.ocaml.org/t/objects-use-cases-in-ocaml/2282)

</comment></comment><comment><USER49> There are people who think every carpentering problem can be solved with chisel and the only book they ever red is one that says how to solve every problem with a chisel.

The response to this is not to throw out all chisels.

The response is to learn to use various tools and once you know how to do this, how to apply different tools to different types of problems.

There is no shame in overusing a tool for a period of time. The novice carpenter must be able to play to learn limitations of tools but also to learn which inventive ways to use tools are actually helpful.

Master carpenter will understand that playtime is necessary step in getting to mastery and will not call out novices for their overuse of the chisel but instead will encourage play with the intent to speed up education.

</comment><comment><USER50> > Secondly, I've noticed that classes have a tendency to grow large. They will > collect pieces of functionality that need to live in the context of the > class, usually to access the internal state of that class. This dependency > on the internal state makes it difficult to break the methods up into > logical chunks.

Sounds like they need to pay more attention to the Open-Closed principle :)

It's not really clear to me how a random grab-bag of functions is better than a random grab-bag of methods. If the functions in the too-large module can be broken up into small units without damaging cohesion, why can't the same be done for the class with two many methods?

</comment><comment><USER51> I've been all over the board on this throughout my career. Currently, we are looking at a substantial reduction in the number of types in our codebase. This would imply fewer & larger classes. Which is totally fine, as long as you know how to manage that complexity.

Our approach for managing this complexity is to simply say that each class in the domain model aligns to the bounded context of a particular business unit, process, or activity. So, instead of modeling a bunch of different types within some namespace, the type itself represents the entire extent of that business concern, including any business facts as instance properties. Complex types are modeled as nested classes within each context. Nothing is shared between contexts except for trivial domain-wide concerns in the outer scope. This allows for very easy serialization of business state in and out of a database (among many other things). Consider the power that a method override might have in this realm if you wanted to construct a hierarchy of such contexts.

An example of one of these concrete types might be SelfCheckoutContext (if you were working in a retail business domain). This type could be derived from a wider CheckoutContext which could be simultaneously shared other concrete contexts like HumanCheckoutContext.

In our approach, there is also a lot of stuff that exists outside of each context (Views, Services, Repositories, etc). We basically treat these items as a platform layer for the contexts to operate on top of.

<comment><USER52> I hear some echoes of domain driven design / development in your comment. Have you looked into that?

<comment><USER51> Yes absolutely. We took what we felt was the bare essential idea of DDD (bounded contexts) and ran with it. The fewer patterns the better. Allows you to work with fewer constraints, assuming you are responsible with the added power.

Bounded contexts are IMO the most powerful abstraction out there for accurately constructing a very complex business system. They directly get at the root of the problem, which is aligning the implementation with the same grain as the business. They also permit complete isolation of various business units which might have unique perspectives into what would otherwise be shared (and increasingly-convoluted) models. In our latest implementations, models are never shared between contexts. There might be a few models defined in the outer domain scope for things we believe apply universally to the application. Any cross-context communication is achieved via simple mappers.

</comment></comment></comment><comment><USER53> Typescript works well without classes because you have great type system, you can implement an interface on the fly by just passing an object with the correct properties to fulfill it.

But the class based architecture doesn't necessary have to be as bad as described in the article.

Inheritance is somewhat of the old school of OOP, modern OOP rarely use inheritance, it uses composition, just like a functional code base. In my projects my classes are always marked as final. Can't even remember the last time I inherited a another class, maybe if I interacted with some legacy code.

It is important to separate your classes, what classes can have state & what classes can not have state. Classes with state should be small & classes without can be large, because stateless classes can easily be reused.

Classes have the main advantage that you easily inject dependencies in the constructor (composition), e.g. a repository depending on the database driver.

You can do that with functions as well, e.g currying, but I find it a bit more messy if your language is not specialized against that use case & with a class you "curry" all class methods at once. If you don't inject your depencies in your functions you will have a much harder time to reuse that function & test it.

I agree with that class based GUI code rarely works, this is because GUI (if it is HTML or OpenGL) does not translate well to a class with hierarchies. GUI code have tendency to more be procedural.

</comment><comment><USER54> I've noticed that in large Java hierarchies, where I spend a lot of my time, people often use interfaces where they really should be using modules. Like, there's this vague concept of a "service" and methods of this service are bound together via an interface. Which ends up coupling all kinds of weird dependencies in the class definition.

My guess is that this is a similar case in Typescript that the author is describing.

So, this approach is, instead of having IFooService with two methods, IFooService.doOneThing and IFooService.doAnotherThing you just have a package "fooservice" with functional interfaces DoOneThing and DoAnotherThing. This seems to still play nice with most of the tooling, and often, keeps the code simpler and makes it easier to refactor. DoOneThingImpl and DoAnotherThingImpl can easily share code, but that aspect shouldn't matter at all to the caller.

Another way of thinking about it: don't worry too much about a perfect interface for layering your business logic initially. Start with just some functions, then as you spot patterns you may recognize there's a state pattern that does things better. I don't think this is really specific to Typescript.

</comment><comment><USER55> Good advice. Don't use classes. At least resist using them as much as possible.

Consider this simple aspect: one of the key differences between a class method and a separate funcion is implicit (and sometimes explicit, though that's not much better) self/this parameter that allows access to all object fields. Essentially you're opting in to grow the input of every method call by all the data held by an object. That's a mistake on many levels:

\- it is one of the most important "best practice" advice to only pass the data that function needs to do the job, with classes you're breaking that advice immediately

\- ease of access to additional data doesn't leave room to think if it's actually good idea to do so, factor in laziness and inexperienced contributors and you get methods that grow in complexity much more than they should

\- testing anything becomes torture, as unlike with simple function class methods now depend not only on incoming arguments but also on state of the object itself, i've seen tests with dozens of lines of setup for single line of test

Prefer simple pure functions, prefer builtin data types, prefer immutability, only pass arguments that function needs to do the job.

<comment><USER32> > only pass arguments that function needs to do the job.

Why would users of that function bother to know what data it needs to do its job? It seems you'll end up polluting your whole code with the implementation details of one of its components. What happens of you want to change those implementation details?

<comment><USER55> Because the two alternatives we're talking about here are 'foo(bar, baz)' where foo, bar and baz convey meaning and 'object.doWork()' which means nothing and explains nothing.

Yes, it's a spectrum and there's a lot of space in between those alternatives, I simply believe being closer to the first option is better than to the second.

Pure functions are conductive to transparency, objects, methods and class hierarchies are conductive to obscurity.

</comment></comment></comment><comment><USER56> This reminds me of a podcast I listened to recently about creativity. Maybe its not about the idiomatic question of whether classes are good or bad but more about the act of placing the limitation that helps drive the masterpiece?

[https://www.npr.org/transcripts/719557642](https://www.npr.org/transcripts/719557642)

<comment><USER10> In Kotlin classes and methods are closed by default, making avoiding inheritance and polymorphism the default. It also makes you mark each variable as mutable or immutable, by declaring it with a var or val, making you think more about (and enforce) state being mutable or immutable, thus avoiding a lot of state problems. It also has a concept called data classes, which works really well for most well designed classes. I now consider the case where a class is not a good candidate for data classes to be a code smell. Most classes should be a good candidate for data classes or else you are usually using classes in a way that causes more problems than it solves.

The thing is, it doesn't prevent you from creating classes or methods that are open for inheritance, nor does it prevent you from allowing mutable state, but it does use the defaults and the language design to encourage limitations that are usually good.

Perhaps they could take these concepts a step further by making classes data classes by default, having a keyword for it to not be a data class.

</comment></comment><comment><USER57> I have always associated classes with inheritance. Perhaps there are other scenarios where classes are unrelated to inheritance. I don't know as I intentionally avoid classes all together.

I avoid inheritance because inheritance increases complexity. By _complexity_ I simply mean it in terms of _complicate_ , or _to make many_. I am not speaking to anything related to challenges, easiness, or hardships. Inheritance is a practice of a concept called polyinstantiation, which allows easy dynamic replication of an object in memory so that many child or cloned objects are independently extensible.

* [https://en.wikipedia.org/wiki/Polyinstantiation](https://en.wikipedia.org/wiki/Polyinstantiation)

* [https://en.wikipedia.org/wiki/Complexity](https://en.wikipedia.org/wiki/Complexity)

<comment><USER58> > I have always associated classes with inheritance.

This is a common pattern I've noticed in discussions about OOP. There is a tendency to reduce it to "inheritance," which turns the conversation into a straw man: "here is why inheritance is bad, therefore OOP is bad".

Same goes for identifying classes with inheritance. Their primary feature is grouping of methods and fields into (potentially) encapsulated units, and being a mechanism for abstraction: classes are more general than their instances and serve as a general pattern; upon construction they are 'configured' through parameters to their constructors and particular forms are arrived at.

</comment></comment><comment><USER59> I really like programming in C++ and don't write a lot of classes. They are a useful tool in the toolbox but hardly the only one.

Well, looking again, I do use a lot of structs (or their moral equivalent, using 'class' to define a struct with some protected state) but don't use inheritance much.

</comment><comment><USER60> Most useful programs have state. As the program scales you need to break it up into parts (or 'modules') with clear interfaces. Usually these parts own state.

The problem with OOP is that it assumes you need _lots_ of these parts. Whereas really, you might have a few or possibly a handful. Use, class-like constructs to represent these, and use normal functions within these, with ideally some kind of polymorphism that also doesn't require classes.

For small programs, don't even bother with this. Just use pure functions for key logic, and unit test those, and delay testing anything with state for as long as possible. When you do need to, leverage interfaces and stubs/fakes ( _not_ mocks) to test them.

That's what I do now anyway.

</comment><comment><USER61> My pet theory about the abundance of classes is that they only gained popularity because of their inherent complexity. There's no one, authoritative way to use them, so a bunch of people can went to conferences and wrote papers about SOLID or encapsulation, or whatever. So people thought there must be value in them.

It takes a long time for a lot of developers to realize anything you can do with classes you can do without. And if you structure your code without classes, it tends to be more refactorable because moving chunks of code in and out of different contexts is a breeze.

The only things I find potentially useful about classes are:

\- portability (but you can get that with modules, caveat that not every language does modules well)

\- auto-complete in a noun->verb context

</comment><comment><USER62> Question:

The author writes (as an example of his problems relying on classes):

 _Well, some tables when updating need to do more than just "update" a record, such as insert a log into a side table._

I do not understand exactly if there is a problem here, or anyway I do not understand what the Functional alternative would be.

Assuming this _is_ a problem, I suppose we have three choices:

a) You have to provide implementation for two distinct methods: \- Update(...) and LoggedUpdate(...) (the latter calls the former and then logs the result, most probably).

b) You decide that update() implicitly logs maybe based on a global configuration or similar mechanism.

c) You provide a boolean flag as parte of the method signature update(log).

What would be the proper Functional approach here?

(Caveat - I have no experience with Functional programming)

</comment><comment><USER63> Ever since I discovered Julia a few years ago, I've found I can't stand using classes any more (particularly in Python). Multiple dispatch just feels so much more elegant than trying to dispatch everything on the type of an implicit first argument.

</comment><comment><USER64> My first programming teacher emphasized that this at-the-time-newfangled "object-oriented" programming was _only_ a way of organizing the code, you still had the same data and functions.

Use classes, don't use classes. It doesn't matter.

<comment><USER65> I feel like dynamically-dispatched open recursion gives you a level of flexibility that you can't get in, say, Pascal — you need first-class function pointers if you don't have virtual functions.

BTW, I finally responded to your comment from the other day; sorry about the delay!

<comment><USER64> Well, Oberon had language-level OOP-- records with methods --which is what my teacher was talking about as just a way to arrange the same data and functions, but the OS also had runtime dynamic message dispatch OOP built on top of but not intrinsically requiring the language-level OOP. I suspect you could implement that in Pascal and make all of Hewitt's Actors model with it.

> BTW, I finally responded to your comment from the other day; sorry about the > delay!

Cheers! I'll go check that out :-)

<comment><USER65> Yeah, the Oberon language added function pointers (I don't remember if those were in Modula-2), and the Oberon OS used that form of language-level OOP to implement its runtime dynamic message dispatch OOP. You can't implement that in Pascal because Pascal doesn't allow you to store function pointers in records (because they might be nested functions, with all that implies). So in Pascal the best you can do is write a switch function for each function pointer type (signature) and use an enumerated type or equivalent to dispatch calls.

<comment><USER64> > So in Pascal the best you can do is write a switch function for each > function pointer type (signature) and use an enumerated type or equivalent > to dispatch calls.

Yeah, you would have to spell it out and you couldn't do compile-time optimizations, but you could get all the dynamic flexibility you need?

Really, what I was saying above is just that if your problem is easier to describe as a bunch of interacting "objects" (like a game or a factory sim) then use OOP, but if it's easier to describe as a set of functions and data structures then don't use OOP. :-)

<comment><USER65> Well, sort of; I mean clearly Pascal is a Turing-complete programming language, so it provides you all the flexibility you need. But if you write a switch function for each function pointer type, it means that local changes in an OO language, or in C, map to global changes in the Pascal translation of it, so I'd argue that the OO language, or C, is more _expressive_ in a fundamentally meaningful way.

(Actually existing implementations of Pascal usually added a function pointer type of some kind, but I'm talking about the language standard.)

I think the crucial question is whether the objects have different behavior. If all the objects implement a particular operation in the same way, or in one of two or three ways, there's no advantage to OOP. If their behavior varies in an open, expandable way — like, you might have an arbitrarily large number of monsters whose movement strategy varies arbitrarily — then OOP, or function pointers, can be helpful. If their behavior needs to vary in orthogonal and arbitrarily combinable ways, OOP may not be expressive enough, and maybe you should look at an entity-component-system architecture.

<comment><USER64> That's basically what the Oberon OS messaging system did, it was a little clunky but it worked. The Gadgets widgets system is still my favorite after all these years. It was like OLE but _good_.

I agree, if your code doesn't need objects, there's no advantage to OOP. and ECS is like extra-OOP. :-) (I replied to your reply in the other thread, BTW.)

<comment><USER65> At least the version of the Oberon OS described in the book used the built-in Oberon support for function pointers for dynamic dispatch; were you looking at an earlier or a later version than the one in the book?

<comment><USER64> I'm talking about the Viewer message system. There's a "coarse-grained view" on pg. 46 of [https://inf.ethz.ch/personal/wirth/ProjectOberon/PO.System.p...](https://inf.ethz.ch/personal/wirth/ProjectOberon/PO.System.pdf)

<comment><USER65> Right, that's what I was talking about too; although the version of the book I read was the 1992 edition, this part of the system hasn't changed. It works by using the Oberon language's support for embedding function pointers ("procedure variables") within records, which is precisely what was prohibited in Pascal. You give different windows ("viewers") different behavior by giving them different Handle procedures ("WndProcs", you might say, or "overridden methods") so that, when the system ("controller") sends a message to the viewer, it can behave in a dynamically determined way.

<comment><USER64> Yes, the Handle procedures are associated with their objects by means of function pointers (for e.g. C++-style OOP), but also inside themselves they implement big ol' IF,THEN,ELSE chains to do (Smalltalk/Actor-style OOP) messaging handling.

<comment><USER65> Oh, I see what you mean — instead of having a function pointer per message type, say, stored in a vtable, the handlers for each message type are dispatched by a function, which can do things like delegation (Smalltalk doesNotUnderstand:). Piumarta's "COLA" approach is a sort of hybrid, where you dispatch with a function, but the dispatch function returns a function pointer you can then cache at the call site.

What I was saying about Pascal is that instead you'd have to have a function that dispatched on the _receiver type_ , not the message type. I thought you were saying that the Oberon OS was doing that, even though the Oberon language supported function pointers.

<comment><USER64> Yeah, cheers! ^_^ (It took me _years_ to notice that.)

</comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER38> I came across a random answer on quora as to why classes are bad (as opposed to structs). It gets into the very nature of program design and organization.

[https://www.quora.com/Is-senior-full-stack-engineer-Ilya- Suz...](https://www.quora.com/Is-senior-full-stack-engineer-Ilya-Suzdalnitski- right-that-theres-no-objective-evidence-that-object-oriented-programming-OOP- is-better-than-plain-procedural-programming-And-is-OOP-in-fact-a-trillion- dollar-disaster/answer/Bernard- Yip-2?__filter__=all&__nsrc__=1&__sncid__=4384812242&__snid3__=7127597147)

Be sure to read the comments, it's pretty interesting.

</comment><comment><USER66> The only classes that really make sense are ones that model something in the real world to some extent. These classes are incompatible with the single responsibility principle however and are considered/lead to bad designs with a lot of coupling. Most of my classes get instantiated very few times as well, so the blueprint concept is only sometimes applicable. Inheritance, one of the three pillars, is a trap.

The whole OOP framework is a bit muddled and to make it work you end up in this web of contradictions. You suspect there is a better way but until you find it, you use Java or C# because that's what your company/tools use

<comment><USER18> ...so when do classes make sense then?

<comment><USER66> Polyymorphic implementations are nice - Eg, the State pattern, or using interfaces to abstract out a server during development. Command pattern. Things you have a lot of. It's not the entire app though, just subsets of it

</comment></comment></comment><comment><USER67> The biggest upside of programming with objects/classes is dependency injection.

And no, I don't mean DI frameworks like Spring.

I mean simply the dynamic (i.e. runtime) composition of program behavior, typically by passing collaborating objects into the constructor. (Functional programming languages have something similar with higher-order functions.)

Contrary to prior claims, objects aren't great at representing real world entities: to do that you would also need to add a notion of time and concurrency to those objects (e.g. actors, processes, services). However, they are a great way to represent the components and capabilities of such entities.

</comment><comment><USER68> The fugly state in objects is tricky. One way to look at a class is to treat it as a kind of coroutine. Consider some pseudocode:

        class Stats
           sum Num = 0
           sum_squared Num = 0
           count Num = 0

           acceptNum(x)
              sum += x
              sum_squared += x ^ 2
              count += 1

           mean()
              return sum / count

           variance()
              # I think this is right...
              return (sum_squared / count) - (sum / count) ^ 2

But that could just as well be:

        coro Stats
            sum = sum_squared = count = 0
            result = nothing
            loop
                msg = yield result
                switch msg.name
                case acceptNum
                    sum += msg.x
                    ...
                case mean
                    result = sum / count
                ...

We don't do objects like this. We really want a file object, for instance, to be something like:

        coro FileReader
            # Not opened yet
            raw_handle = popen(...)
            # Reading
            ...
            if msg.name == read
                yield read(raw_handle)
            ...
            # Data exhausted
            ...
            if msg.name == read
                yield eof
            ...
            # Handle closure
            ...
            if msg.name == read
                throw ReadOnClosedFile
            ...

(I think you could handle inheritance with a scheme like this, presumably a "subclass" would simply yield to the "superclass".)

What's nice about that is the state is explicit in the structure of the coroutine. Ideally, we'd have some sort of protocol types that reflect that we're accepting different kinds of messages at different stages in an object lifecycle. In this example, after a file closes, it's a type error to try and read from it.

Instead, we generally munge it with properties, depending on lots of checks instead of structure.

We can solve state through more classes, of course. The Builder pattern handles a specific case of this quite nicely, you have one mutable construct that then returns an immutable construct.

But to use that more universally would lead to a profusion of classes.

</comment><comment><USER69> I like classes. But you must understand your code, even before you write it. If the syntax that you use (the author is using TS, and JS/TS classes are syntactic sugar) matches your mental model of the program then I would say that you are choosing the correct syntax.

If it's your library and your responsibility then I will try to read it your way. Comprehensive testing and consistency in style are more important than any specific syntax or abstraction.

</comment><comment><USER70> I think I understand what the author means.

When writing Java code I prefer to use public members in my data classes and only use getters/setters sparingly. Also, I prefer to have the logic in until classes if I can. I suppose I'm not a huge OOP fan.

Of course I have to follow whatever patterns my teammates follow and hardly anyone writes Java like that... But my personal projects I think have much cleaner and easier to understand code with this approach.

</comment><comment><USER71> Classes are everywhere, but are used in different ways or hidden.

There are types of classes that are hidden and cannot be re-instantiated.

To me, those are modules and the main execution environment.

Whenever i've been caught out when coding its always one or both of these things, so i always wrap these in a class otherwise when it comes to reusing something multiple times you're caught short.

So, i always use classes for base functionality.

But i don't throw them around like sugar candy.

</comment><comment><USER72> I also tend to avoid writing many classes (in Python). Jack Diederich has a great video called Stop Writing Classes:

[https://www.youtube.com/watch?v=o9pEzgHorH0](https://www.youtube.com/watch?v=o9pEzgHorH0)

It's about Python, but the observation can apply to several languages. Pithy quote:

"I hate code and I want as little of it as possible in our product."

</comment><comment><USER73> (Apologies to the Flaming Lips, and I think the pronunciation shift you have to do at the end makes it funnier)

I know a girl who

wrote some code

she'll make a linked list

she'll add some nodes

But she don't use classes

and she don't use C

she don't use Haskell

or any of these

she uses assembly

assembly

assembly

</comment><comment><USER74> The first object oriented programming language I learned (LambdaMOO) didn't have classes. It's entirely conceivable to do nice clean object oriented code using a system which has only prototypes. In fact it encourages one to think about the important parts: encapsulation and delegation without becoming obsessed with classification.

</comment><comment><USER75> [http://nomad.uk.net/articles/developers-who-hate-on-oop- don'...](http://nomad.uk.net/articles/developers-who-hate-on-oop-don't-know- how-to-use-it.html)

</comment><comment><USER76> They're using TypeScript. Ts classes are horrible: you have to explicitly use "this." to access _anything_ in the class - it's as good as it was possible to make it with the JS legacy, but it just doesn't work very well.

Using namespaces with state passed to the relevant function ends up being basically just as easy, and sometimes easier as you end up using more closures and can access enclosed variables directly.

Classes in languages designed for OO work a lot better.

</comment><comment><USER77> I don't either. Almost always an arrow function (or just regular old function) does what I need. Plus, React Hooks!

</comment><comment><USER78> For a small to medium sized codebase, functions and structs are simple, clear and don't obscure state.

</comment><comment><USER79> I use classes a lot in typescript, but not for OOP. Just glorified objects with a constructor and getters.

</comment><comment><USER80> I once tried to drive a nail with a socket wrench and it went poorly. I have since cleared my garage of all socket wrenches because they are clearly useless.

</comment><comment><USER81> It's 2020. You're preaching to the choir on this.

I myself use classes just fine. I love CLOS and I'm having a lot of fun with OOP on Forth as of late.

</comment><comment><USER82> Interfaces are good though.

</comment><comment><USER83> classes are OOP. FP doesn't need them. The end.

</comment><comment><USER84> A whole article and no code examples. Fail.

<comment><USER38> Even if he did present code examples I doubt it would change your mind.

I think OOP people should be aware that there is significant backlash against OOP among smart people who are very familiar with the paradigm and they should take the time to understand why.

The division is real and given the invention of languages like GO and RUST that do not have classes I would think really hard about the topic.

<comment><USER85> GO is simple decomposition of OOP, class was split into structure (with composition) and interface(with multiple inheritance without implicit definition what interfaces can can coop with structure). Go only restricted that you cannot define virtual methods.

<comment><USER38> Rob Pike designed Go with the philosophy for people not to use structs like classes. This is intentional and deliberate.

If you are using GO like you use OO, you've missed the entire point.

[https://twitter.com/rob_pike/status/942528032887029760](https://twitter.com/rob_pike/status/942528032887029760)

<comment><USER85> Please read carefully what I've written. I've never said that struct == class. Secondly Rop Pike wrote under tweet: `But the more important idea is the separation of concept: data and behavior are two distinct concepts in Go, not conflated into a single notion of "class".` which only proves what I presented in first comment.

<comment><USER38> Please carefully read what I wrote. I never claimed you said struct == class.

You literally said go removed virtual methods. This is not what pike is talking about. Pike is talking about putting a function onto your struct. This is legal in go as functions are first class but rob is saying it should be done sparingly because data and behavior need to be distinct concepts.

It is not me who failed to parse your writing, but you who failed to understand pike and my comment.

</comment></comment></comment></comment></comment></comment><comment><USER86> > I Don't Use Classes

OK, cool.

</comment><comment><USER87> classes only encapsulate context. nothing else. you don't have to use them if you don't want to.

</comment><comment><USER88> Yes, I never use classes, only data and pipes.

[The Pure Function Pipeline Data Flow v3.0 with Warehouse / Workshop Model]([https://github.com/linpengcheng/PurefunctionPipelineDataflow](https://github.com/linpengcheng/PurefunctionPipelineDataflow))

1\. Perfectly defeat other messy and complex software engineering methodologies in a simple and unified way.

2\. Realize the unification of software and hardware on the logical model.

3\. Achieve a leap in software production theory from the era of manual workshops to the era of standardized production in large industries.

4\. It's the basics and the only way to `Software Design/Develop Automation (SDA)`, SDA is an innovative and revolutionary approach to develop large-scale software, just like `Electronic Design Automation (EDA)`, because it systematically simulates an integrated circuit system.

</comment><comment><USER89> Sounds like an idiot with opinions.</comment>


Going Public Means Never Admitting You're Wrong - <AUTHOR> http://jordanstaniscia.com/2012/05/going-public-means-never-admitting-wrong/

<comment><USER2> The author makes the point that companies don't admit mistakes because they fear the stock would plummet. I don't think that is entirely accurate, or if it is, the stock plummet is incidental to the actual risk that comes from admitting mistakes: investor lawsuits.

And regarding start-ups and their propensity for owning up and apologizing, I've never really seen much in the way of sincerity from the likes of Path or those in similar flare ups. It's just that an apology is the quickest and cheapest form of damage control when the fickle masses and page view hungry tech bloggers are crucifying you on twitter.

</comment><comment><USER3> Usually I dismiss posts like this. And I have to admit -- I browsed the frontpage several times without clicking on it.

But I think this is very cogent, and one I had totally forgotten about in the whole FB media circus. But this is a rare opportunity for FB to reject precisely the parts of public company SOP that doom them to lack of understanding with their customers, etc.

FB has a hacker / break things culture. Public companies can't admit these things to the public.

Public companies often stagnate by not being able to relate to customers via the full feedback loop that admitting mistakes includes. Some companies get lucky because they insist on getting in touch with what the customer wants in other ways (e.g., through data at Google or through Lean Startup and two- pizza-size teams at other companies, etc.)

But it is an opportunity for FB to buck the norm and continue to take _open_ risks and admit when they make mistakes. It is an opportunity to transform the culture of public companies.

<comment><USER4> > _"Public companies can't admit these things to the public."_

The OP's article is based off of a faulty premise, disproven with a simple Google search. Yes. They can and do admit they were wrong, and apologize publicly. It's ridiculous to suggest otherwise:

[http://www.telegraph.co.uk/news/worldnews/northamerica/usa/8...](http://www.telegraph.co.uk/news/worldnews/northamerica/usa/8972788/FedEx- issue-online-apology-after-video-of-courier-throwing-package-over-fence-goes- viral.html)

[http://www.washingtonpost.com/wp- dyn/content/article/2010/02...](http://www.washingtonpost.com/wp- dyn/content/article/2010/02/01/AR2010020100275.html)

[http://news.xinhuanet.com/english/china/2012-05/05/c_1315692...](http://news.xinhuanet.com/english/china/2012-05/05/c_131569282.htm)

<http://blog.netflix.com/2010/09/apologies.html>

[http://blog.games.com/2011/08/03/nintendo-president- apology-...](http://blog.games.com/2011/08/03/nintendo-president-apology-3ds- price-drop/)

<http://www.cellular-news.com/story/51329.php>

etc, etc...

</comment></comment><comment><USER5> Isn't this conflating Morgan Stanley (and Nasdaq) with Facebook? One could also blame the media for generating an inordinate amount of hype, and then turning around in a day to call the IPO a failure. But, a failure to whom? Most of what I've read here recently suggests that a successful IPO for a company means leaving as little money on the table as possible, which means a stock price that stays relatively flat (or even goes down a tiny bit). Because everyone expected a pop, the IPO is considered a flop. One could look at the situation from another angle and say that Facebook did what was best for themselves, not the share price – which could be seen as a positive signal for people who don't think that companies should be ruled by the short-term ups and downs of the market. I mean, when the valuation of a company is based on an extremely long outlook and yet-to-be-determined strategies and pivots, what do people expect?

</comment><comment><USER6> In general, I tend to think of startups as children, in or around their creative prime. They are still leaning, often naïve, and are expected to make a lot of mistakes (and also expected to handle them well). It seems reasonable to apologize often to potential customers because they are taking a risk and experimenting with you and your service/product. You're not so much apologizing as you are thanking them for helping you determine your target customer and their needs.

There are a lot of reasons why mistakes happen, but for many reasons the startup stage is a good time to make mistakes.Especially before reaching critical mass, at least from a business perspective, I believe it's easier to justify upsetting a few thousand people if it means obtaining learnings which can impact tens or hundreds of thousands of the _right_ customers (and help you serve them better) down the road. I think the smartest startups are able to minimize their mistakes by practicing due dilligence and learning from others who have made similar mistakes.

Large, public corporations, in many ways, face a different set of challenges.They are expected to understand their target customer, deliver product(s), maintain revenue growth, all in the face of one other interesting challenge. When you take your company public, a core group of your customers changes roles from consumer to investor. Different customers have different needs and expect different things. Some need fault tolerance, some crave features, some need speed. Because you don't necessarily choose your 'investors' at this stage, there's no saying your expectations will be aligned.

One challenge of running a company is, not all 'ethical' strategies (such as apologizing, if it can be considered as such) result in either maximizing revenue or maximizing customer happiness. When you have millions of customers, I imagine every important decision can be construed as a mistake by some subset of customers.

We live in a Peter Pan world where many startups wants to stay young forever and never sacrifice their ideals. The truth is, a company is about serving customers well, making a promise, and delivering on it. In order to do that, you need to grow with your customer base and respond to the challenges this entails.

Special thanks to zephyrp for editing.

</comment><comment><USER7> As a counter example to the point made in this article, coming from none other than Microsoft, is the following blog post detailing a leap year bug in Windows Azure that caused downtime recently:

[http://blogs.msdn.com/b/windowsazure/archive/2012/03/09/summ...](http://blogs.msdn.com/b/windowsazure/archive/2012/03/09/summary- of-windows-azure-service-disruption-on-feb-29th-2012.aspx)

</comment><comment><USER8> I still remember this: <http://news.ycombinator.com/item?id=2545823>

</comment><comment><USER9> The FB IPO was not a mistake for FB - all the money went to the company instead of the insiders. The idea that the stock for an IPO has to soar on opening day is flawed - if this happens, the company should have charged more.

</comment><comment><USER10> When many own one, no one is to blame.

<comment><USER6> I appreciate and respect what your comment is trying to accomplish, in terms of associating ownership with the sum of its parts. I think this can also lead to an 'escapist' mentality though, where no one takes ownership for mistakes. Perhaps when many own one, many are to blame, instead of the one?</comment></comment>


Sexual Energy and Creative Genius [mildly  NSFW photo] - <AUTHOR> http://www.mindbodygreen.com/0-4366/Sexual-Energy-and-Creative-Genius.html

<comment><USER2> Not an issue for me personally, but this probably warrants some sort of soft NSFW tag. The first photo in the article doesn't show nudity but it is pretty work-questionable for anyone who might have co-workers shoulder surfing when they happen to hit the page.

</comment><comment><USER3> Until I got to the bottom, I thought this article must have been written by Tim Ferris, because her book follows the "Four Hour XXX" meme.

Either way, probably not a book I'll read. I have a lot on my reading list already.

</comment><comment><USER4> NSFW above the fold (rather mild, but you may get odd looks from your coworkers)</comment>


Zoom 5.0 - <AUTHOR> https://zoom.us/docs/en-us/zoom-v5-0.html <comment><USER2> Looks like a good update and they're moving in the right direction.

I think this should fix the main issues people have had with them (at least the most public problem with 'zoom bombing').

There's not much they can do about having all their development in China, but at least their focus on security otherwise seems to be paying off.

###

Quick Feature Summary:

\- Mandatory GCM encryption requires Zoom clients to upgrade to 5.0 by May 30th [0]

\- Hosts can prevent screenshare, chat, user renaming

\- Hosts can report users to Zoom's Trust & Safety team, who will review any potential misuse of the platform and take appropriate action.

\- All hosts may now turn on the Waiting Rooms while their meeting is already in progress.

\- Lock your meeting after everyone has arrived to prevent any unwanted disruptions.

\- The host may remove a participant and they will be unable to re-enter the meeting.

\- Waiting Room enabled by default

\- Complex Meeting IDs

\- Meeting passwords are now more complex and enabled by default

\- Meeting Registration and Authentication (require email registration/restrict meetings to preset profiles)

\- All cloud recordings are encrypted with complex passwords on by default.

\- Audio Watermarks/Screen Share Watermark (help prevent leaks)

\- Message Preview Options (Users can now enable Zoom Chat notifications to not show chat content while screen sharing.)

\- Host or account admin can disable the ability for participants to show their profile picture or change it in a meeting.

\- Hosts can now select which data center regions they would like their in- meeting traffic to use when scheduling a meeting, and participants can see which data center they are connected to by clicking on the info icon at the top left of the client window.

\- Zoom 5.0 supports a new data structure for larger organizations, allowing them to link contacts across multiple accounts so people can easily and securely search and find meetings, chat, and phone contacts.

[0]: [https://en.wikipedia.org/wiki/Galois/Counter_Mode](https://en.wikipedia.org/wiki/Galois/Counter_Mode)

<comment><USER3> > There's not much they can do about having all their development in China, > but at least their focus on security otherwise seems to be paying off.

They could start moving their development to the US. There are plenty of successful software companies in the US. There's a good ecosystem, a huge amount of talent, and tons of enthusiasm about their problem space.

What they can't do anything about is the trust they lost by misleading their customers about their security model.

<comment><USER4> Dev team is different than actual server location.

Anyone know if they still passing non China video data through mainland China server? Unclear from this.

<comment><USER5> They aren't. They fixed that almost immediately. They said it was just a mis configuration.

</comment></comment></comment></comment><comment><USER6> Most dystopic feature:

> Audio Watermarks: Turn this on to embed a user's personal information into > the audio as an inaudible watermark if they record during a meeting. If the > audio file is shared without permission, Zoom can help identify which > participant recorded the meeting.

<comment><USER7> I wonder how they are doing this. Wouldn't a low quality recording render this fingerprint invisible?

<comment><USER8> I wonder if it can be filtered out, on the other hand. Does anybody have knowledge on audio fingerprinting/watermarking? Or is it perhaps security theater?

<comment><USER9> My Guess is that it is mostly a security through obscurity thing for now, we do not know exactly how they mark the audio. When someone figures this out I believe that it should be possible to filter this out.

It would be interesting if they found a way to watermark the audio in such a way that removing the mark makes the audio unusable.

<comment><USER10> Just compare two records from two different users.

</comment></comment></comment></comment></comment><comment><USER11> A few weeks ago they required login to join a meeting from the browser. This make it harder for older people who I like to talk to. Since then I have moved to jit.si . The quality is slightly lower, but it is much easier to use

<comment><USER12> Jitsi is much worse in a number of ways. If you use it casually with small crowds then it's prob fine. Otherwise the pains of Jitsi are huge for the year I had to use it a lot.

</comment></comment><comment><USER13> I'm very concerned about the China connection. there has been zero analysis of what they actually look at and how they use the data

<comment><USER14> According to their 10-K filings, zoom has more than 700 heads in RnD in China, and about 5 subsidiaries in China IIRC, but none of those are listed as subprocessors of personal data, which I, personally, find somewhat hard to believe

</comment></comment><comment><USER15> Nice to see all the security improvements, however, the new UI seems less crisp, the fonts have become much more blurry, with too much anti-aliasing going on. Also the up-arrows for audio/video settings have been made much smaller and thus harder to hit - you will mute yourself rather than opening the audio settings.

<comment><USER15> The font smoothing/antialiasing problem is not everywhere, which makes it look even weirder. See this dialog for instance: [https://i.imgur.com/L5S8Tqn.png](https://i.imgur.com/L5S8Tqn.png)

Where the text "0 participants per room" have been applied too much anti- aliasing.

</comment></comment><comment><USER16> There was discussion before that Zoom had serious security issues. Have these been fixed in the new version of zoom ? Eg would companies that previously banned zoom now allow zoom 5.0?

Eg some previous issues with zoom [https://news.ycombinator.com/item?id=22736608](https://news.ycombinator.com/item?id=22736608)

</comment><comment><USER17> Not a security expert but deal with PKI (openssl, openssh, etc) / OpenGPG/GnuPG a lot on a daily basis, I just don't understand why Zoom would let their marketing people put buzzword like `GCM Encryption` on such an important landing page, not nitpicking, seriously...

At least, use AES-256 (mode can be optional as most people don't even know what GCM XTS CBC stands for).

<comment><USER18> Because focus groups liked the sound of "GCM Encryption". Be glad they didn't slap on a bunch of Trim Level Designators like "GCM LX Sport Encryption CE".

[https://www.liveabout.com/glx-gls-se-si-lx-what-do-they- mean...](https://www.liveabout.com/glx-gls-se-si-lx-what-do-they-mean-4083747)

[https://en.wikipedia.org/wiki/Trim_level_(automobile)](https://en.wikipedia.org/wiki/Trim_level_\(automobile\))

</comment></comment><comment><USER19> Why are they touting "GCM encryption" without explaining what that means? Do they mean "Galois/Counter Mode" [0]? If so, _why_ is that better than the encryption they were using before?

[0] [https://en.wikipedia.org/wiki/Galois/Counter_Mode](https://en.wikipedia.org/wiki/Galois/Counter_Mode)

<comment><USER20> They were using ECB mode before

</comment></comment><comment><USER21> I was hoping a new major version would help me fix the problem I have with the Zoom desktop application on Xubuntu. Every time I join a meeting, the entire desktop start lagging. Sadly, this update did not help.

</comment><comment><USER22> "How was your experience?" [Great] / [Had issues]

So infuriating. And yes it disappears by itself after a while, but no, I do not need that dialog box.

Anyone know how to turn that off?

<comment><USER23> It's a setting that's exposed on the website ("Feedback to Zoom"), but, at least for my licence, it's locked. Incidentally, I am able to close the box, rather than just waiting for it to go away, in case that's better.

</comment></comment><comment><USER24> Thankfully the major version update doesn't mean a completely new UI, which would have cut their userbase in half.

</comment><comment><USER25> "GCM encryption". What? o_O

</comment><comment><USER26> Still no Mac App Store version? Sigh...

<comment><USER27> Probably due to the Apple tax.

<comment><USER28> No, it's probably because then the app would need to be sandboxed and they wouldn't be able to justify their insane Hardened Runtime exemptions.

</comment></comment></comment><comment><USER29> Can Zoom stop logging me out from current computer (while I'm using Zoom) just because there's another computer on standby where I forgot to log off Zoom (and I'm not in any Zoom call on that computer)?

<comment><USER12> Hah finally a criticism for Zoom I can immediately relate to and is really frustrating. Why log me out like you said if I'm not in a call.

</comment></comment><comment><USER30> Real end to end encryption? No? Must be a hard problem that other people have solved.

<comment><USER31> To my knowledge there is no zoom competitor that has end to end encryption and allows people to join a call from landlines.

<comment><USER32> bluejeans? Skype for business? Webex?

<comment><USER33> All those services don't encrypt connections if they include PTSN phone calls or other features. Example: [https://help.webex.com/en-us/WBX44739/What-Does- End-to-End-E...](https://help.webex.com/en-us/WBX44739/What-Does-End-to-End- Encryption-Do)

</comment></comment></comment></comment><comment><USER34> Some of these features sound like anti-security and definitely anti-privacy features. Will definitely make you think twice about having a "private" meeting on Zoom if they're going to embed my email on a screenshot someone else takes. Great way to get a meeting organizers email...

> Screen Share Watermark Superimposes the image of a meeting participant's > email address onto shared content in the event a participant takes a > screenshot.

<comment><USER35> I wonder how zoom determines if a screenshot is taken.

On Linux/Wayland at least, there's no API for an app to determine that this is happening. So they'd have to show the watermark all the time.

<comment><USER26> Likely by detecting certain keystrokes (that's what Snapchat does on iOS for example). Idk if there are other ways on macOS or Windows.

<comment><USER36> There is an API on iOS: UIApplicationUserDidTakeScreenshotNotification

[https://developer.apple.com/documentation/uikit/uiapplicatio...](https://developer.apple.com/documentation/uikit/uiapplicationuserdidtakescreenshotnotification?language=objc)

<comment><USER26> I was not aware of this- thank you!</comment></comment></comment></comment></comment>


Socialism's DIY Computer - <AUTHOR> https://tribunemag.co.uk/2020/07/make-your-own-self-managed-socialist-microcomputer <comment><USER2> It's like a Socialist version of Halt and Catch Fire.</comment>


The Era of Fragmentation, Part 3: The Statists - <AUTHOR> https://technicshistory.com/2020/05/17/the-era-of-fragmentation-part-3-the-statists <comment><USER2> All of these posts are great - go through the archives if you haven't already!</comment>


How NSA Spooks Spaffed My DAD'S DATA ALL OVER THE WEB - <AUTHOR> https://www.theregister.co.uk/2013/06/12/nsa_logo_scandal/ <comment><USER2> NSA: "Hello, this is NSA"

Artist: "Oh... Hi... What can I do for you?"

NSA: "We'd like to buy 1 license of the _bleep_ picture"

Artist: "I'm sorry, which picture?"

NSA: "You're not authorized to know that code word"

Artist: "..."

NSA: "OK never mind we'll just take it"</comment>