Hacker News new | past | comments | ask | show | jobs | submit login
Did OOP do this to us? (apotheon.org)
39 points by 10ren on Feb 21, 2009 | hide | past | favorite | 47 comments



I'm no expert on enterprise software, but this tallies with my casual observations.

Note that Java didn't make enterprise software into a bloated, confusing architectural spaghetti. It just found its niche there. Remember that Java was originally intended for writing embedded software for set-top boxes; then it got repurposed for writing "write once, run anywhere" desktop applets. It is only because the language was a nigh-complete failure in those niches that it became primarily known as a language for server-side enterprise software, which has influenced its development ever since.

So it might be that the enterprise development "pattern", memorably characterized by PG as "maintainable spaghetti", did this to (Java's) OOP, not the other way around. But I would suggest that it's a little of both: Java and the "enterprise pattern" are coevolved, like the chicken and its egg. At this point it's hard to sort out which came first.

UPDATE: I should point out that the author says more or less the same thing:

OOP didn't do this to us; we did it to ourselves. OOP just held our hats.

His title is a rhetorical question, so the fact that I think it's subtly misleading is actually a sign that it's doing its job. ;)


You make a good point. Open (loosely used) enterprise software leads to committees and committees lead to kitchen sink specifications. CORBA, from a previous generation, had exactly that kind of bloat, W3C specifications likewise. The Java libraries show that in spectacular fashion. Maybe Sun thinks we need umpteen hash table implementations, but I think we would be better off with one well done.

Frameworks done by committee are even worse. God save us from W3C and the Apache Foundation.

Counterpoint: software from dictators: Python and Django.


How does Haskell's collective approach to design / standardization stand here?


For those who will vote this up without bothering to read past the title: the conclusion is that no, OOP is not responsible for bad programming. Bad programmers are responsible for bad programming.

News at 11.


OOP has had several phases. The first phase conflated encapsulation into objects with generic reuse. It also advocated complex object diagrams, deep inheritance trees and a variety of over-elaborate approaches.

The second phase was more closely related to patterns and agile methologies. Here, the illusion of OOP creating generic reuse was abandoned, the idea of "containment over inheritance" was broached, and the paradigm became closer to "the right tool for the job".

I am actually amazed that methodologies were able to improve but I can't argue with what I've seen.


I just think it's the case where OOP does not implicitly guarantee the modularity and good architecture of applications, it simply provides a model where for many problem domains and with proper code discipline, it will. Nature always builds a better idiot.

Most importantly, I think it might be some sort of "it's so expensive, it's GOT to work" effect. The complexity of OOP patterns can be pretty ridiculous, so perhaps people dilligently code away using them because they feel like there's nothing better, or they feel like the complexity has to be good for something.


"it's so expensive, it's GOT to work"

Exactly.

“Simplicity is hard to build, easy to use, and hard to charge for. Complexity is easy to build, hard to use, and easy to charge for.”


Where'd you get that second quote? It seems familiar.


As seen on Twitter by Chris Sacca:

http://news.ycombinator.com/item?id=248322


Reminds me of that old Yegge quote:

Java is like a variant of the game of Tetris in which none of the pieces can fill gaps created by the other pieces, so all you can do is pile them up endlessly.

-- Steve Yegge (2007, Codes Worst Enemy)


No. The long answer is that Java did it to us. Like jacquesm below, I have inherited a large Java enterprise project, 400,000 lines of Java, XML, JavaScript, HTML, and Flex. And no comments, JavaDoc, or design documentation. All of this seems to have been written by one person with smaller contributions by a couple of others. There seems to be lots of copy and paste and IDE generated code. The system was designed to be modular with plugin components.

The first culprit is Java itself. The language is missing critical builtin data structures, ArrayList and Hashmap with corresponding literals. If they had been part of the language and not just libraries, Java might have taken a completely different direction. Throw in lack of overloadable operators, first class functions, and descent introspection and you get a language that in not very expressive.

Lack of key fundamental data structures, leads to the OOP bandwagon. Interfaces and inheritance are good tools but they should be used in moderation. In my project there is the pattern of BaseInterface, BaseClass, MoreCompleteInterface, MoreCompleteClass, ModuleLevelInterface, and ModuleLevelClass all to implement a DAO with nothingg but getters and setters. In one case this adds up to more than 2000 lines of code that does nothing that could not have bee done with a hash. Just to make sure that the code is completely non-understandable most serious classes have something like this

BaseInterface dataSource;

along with a setter that is filled in by Spring at runtime. I cant even explore the class structure. Contrast this with Python, where it would be a handful of lines of code, maybe 100 or 200 times fewer.

To get around the problems inherent in the language, Sun came up with beans and the Java community went charging off into complexity. And throw in XML configuration just to create an impedance barrier. Since this was all new territory, the result was an explosion of frameworks.

So now I have a stack that includes JBoss, Spring, Hibernate, MySQL, 200k of JS libraries (some of which I never heard of before), Flex, Granite, and more open source Java libraries than I can shake a stick at. I suspect there are many cases of a library being added for a single feature. Java OK, JS not so OK because of download bloat. Maybe Java is not OK either because I have to deal with what broke when I upgrades to a new version.

I particularly dislike the addition of Hibernate because it separates the programmer from the understanding of the database. In my case the database itself was generated from the the .hbm.xml files. The database was created from a Java-centric point of view as a network of classes. As a consequence Hibernate created hundreds of tables. One query to fill in a simple list in the browser created an SQL query with 10 joins. I'm surprised the db didn't roll over dead.

At least I'm being paid well.


My commiserations. Sounds really horrible. But I still think it's more a matter of "enterprise" culture than of Java's deficiencies, even though I agree that Java's lack of expressiveness does contribute to these problems.

However, it's not unimaginable that a posting like yours could be submitted here 7 years down the road with Java replaced by Ruby/Rails or Python/Django. Piling on libraries is widespread in other communities as well. Attempting to avoid any knowledge of the DBMS even counts as cool in Ruby/Python circles.

At least many Java libraries are reasonably high quality. The same cannot be said about something like Django's ORM for example. I looked at the code the other day. They have no optimistic locking, and there is one completely useless existence check before each and every update. It's completely beyond me how anyone could use an ORM that doesn't support optimistic locking. But nobody seems to care. There are even postings in the archives with people claiming it's not necessary for web apps. That's just plain incompetence.

The whole idea of abolishing the separation between the database and the application is a huge disaster in the making. Up until now we could at least throw away legacy applications and still have a reasonably well designed database schema. Not any longer. In my view, that's the main contribution of Rails and it's rip-offs to software design.

So basically what I'm saying is that incompetence is is widespread and coolness trumps competence for extended periods of time, leaving behind a whole lot of crap like that one you're working with right now. The next generation of crap is piled on as we speak and it's written in today's fashion languages.

I'm all in favor of better programming languages because they improve the productivity of good programmers. But they don't make bad or even average programmers any better.


How does a lack of "critical built-in data structures" (as opposted to libraries) lead to poor design?

C has a ridiculously small set of built-in data structures, but the unix core tools written in C are very elegantly designed.


Eh, don't read more into an argument than is really called for.

The crux of the above argument is usually something like:

In most nontrivial programs you'll wind up with a lot of "sophisticated" code that performs some task but has a ton of parameters and behaviors it'd be at least theoretically useful to be able to tune or override at some specific point-of-use of said code.

Sometimes the right decision is to just hard-code in all the non-essential parameters to sensible defaults, and do only what you need; in places where this approach is warranted ("keep it simple, and only do what you need to"), Java is as annoying to use as ever but doesn't do much to shoot you in the foot beyond being more verbose than it needs to be.

It's also sometimes the case that the right decision is to make the code as fully-articulated-as-possible: as many parameters as possible are tunable, and as many behaviors as possible can be overridden. We'll call this the "complicated" scenario.

In the "complicated" scenario, you have at least the following choices for architecture:

- approach 1: a tower of interfaces and base classes; each interface+base class combination augments or extends some of the behavior or adds or removes points-of-articulation from the layer beneath it. Once you've built the tower, whenever you get to a point where you need to call on this functionality you pick the appropriate item from the tower, subclass+augment its behavior further (if necessary), and go to town.

- approach 2: similar to 1, but make heavy use of reflection (at least at certain points) to try and achieve some level of genericity (in the core methods and algorithms) and to make the implementation less dependent (at time of coding) on the specifics of any particular interface implementation.

- approach 3: have methods that're like void openConnection(IConnectionSpecification connectionSpecification, Map<String,Object> options), and then be smart about how you extract options from the options Map.

approach 1 and approach 2 have their problems but fit well into the tools Java gives you. Approach 1 is more suited for writing code that other people will use (ie, call into), whereas approach 2 is more suited for writing code that other people's code will be used in (ie, called) (yes, that's a bit vague, but it's good enough for today).

Approach 3 is common in the scripting languages, because it allows for a lot of ad-hoc extension / parameter setting without excessively cluttering the interface.

Approach 3 isn't impossible in Java, but the lack of literal syntax for eg maps and lists makes it unwieldy. If it were possible to write something like:

- ConnectionBroker.openConnection(cSpec, {"port":80,"username":"username","password":"password"});

you'd see a lot more use of Approach 3 in Java. As-is, you'd have to explicitly construct a Map of the right type, then one-item-and-line-at-a-time populate it with keys and values, and so on, at which point (especially if used often and in a big project) you're better off coding up an IConnnectionOptions interface, etc., and going from there.

So it's less: lack of data structures == bad design.

It's more: lack of literal syntax for the basic data structures means that it's a royal pain to do certain ad-hoc friendly tasks in an ad-hoc way. Assuming laziness (always a good assumption), given an idiomatic way to handle cases with lots of potential parameters most programmers would handle things the ad-hoc way, and save the "over-engineered" tower-of-interfaces for the situations which are actually necessary (eg: real-honest-enterprisey-software).


Excellent exposition of what I was trying to say.

The overarching concept is the ""expressiveness" of a language. Concept is "How easily can I express my goal in the language. A crude measure of the relative expressiveness of two languages is the ratio of the number of tokens in the respective implementations of a program. I remember reading a study a decade ago that ranked C at 1, Java at 2, Python at 5 (roughly). Having hashes and their literals as part of the language makes it more expressive than one where hash is in a library. Look at JavaScript where the literals for arrays are used as a serialization protocol.

Expressiveness goes beyond just literals. In python functions are first class objects, so there is no need for the thunk/functor/inner class kludge that you have in Java. Or look at list comprehensions which are way more powerful than for each.

The expressiveness shapes how programs are coded. Programmers will use a concise, powerful idiom because it saves time and thinking and the program is shorter. There is a corresponding obligation on the language developer to make sure the expressive idiom is almost always the best way to go.


The "crude measure" that you cite which "ranks C at 1, Java at 2, Python at 5 (roughly)" shows that on average, every line of code in Python is worth 5 lines of C. Let assume that this is true.

Naturally, this seems to imply that more powerful languages require less code to perform the same task. Python provides a good example of this, but it is not always true.

C is not a very expressive language. It is often cited as a portable assembly. Yet, decades of unix/GNU utilities show that powerful programs can be written in small amounts of C, which are combined to build elegant and decoupled systems.

Java is more expressive line by line, but its implementation of OOP requires so much boilerplate that it requires no less code than C to represent the same simple design as the C equivalent. Additionally, because each line of Java represents 2 lines of C, the resulting programs are far less efficient.

It is important to consider why this true. Java attempts to abstract many of the details of the underlying C implementation, allowing it to be much more expressive. This abstraction allows the higher level programmer to think about business logic rather than pointer arithmetic.

However, these abstractions constrain the programmer's ability to write code that the language designer hadn't considered.

Of course, you might consider these to be edge cases and that is a fair point. However, crippling the outliers pushes your userbase towards mediocrity.

As pg stated: "when you damp oscillations, you lose the high points as well as the low."


So we need a high pass filter for languages?


Oh certainly, Java's missing a heck of a lot more kinds of expressiveness than just literals; I was just trying to show, mutatis mutandis, what a Java-with-decent-literal-syntax would be like, as compared to the current state of affairs.


Thank you for letting me know I should have explained myself more clearly.

"Lack of key fundamental data structures, leads to the OOP bandwagon."

  Lack of built-in data structures -> overuse of OOP
"Interfaces and inheritance are good tools but they should be used in moderation. In my project there is the pattern of BaseInterface, BaseClass, MoreCompleteInterface, MoreCompleteClass, ModuleLevelInterface, and ModuleLevelClass all to implement a DAO with nothingg but getters and setters."

  overuse of OOP -> Bad design
Therefore,

  Lack of built-in data structures -> Bad design



"OOP didn't do this to us; we did it to ourselves. OOP just held our hats."

I support the article's claim that programmers are responsible for spaghetti code, but I disagree that OOP in general is responsible for facilitating this behavior. Compare Java's OO to smalltalk's OO.

On the one hand, you have "a halfway point between C++ and Lisp" which inherits a bytecode on virtual machine implementation from lisp and most everything else from C++. [http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/m...]

On the other hand you have an open source ecosystem built on top of a VM, where all code is available for modification and extension by the user. [http://www.squeak.org/About/]

Java programs are notorious for being resource hogs and unstable, and smalltalk programs are notorious for the opposite.

This dichotomy suggests OOP is not the evil villain it's made out to be. Rather, I would suggest that the average quality of code produced in a particular language is proportional to how easy it is to read other people's code in that language.

Java makes it very easy to hide your code. Often while working on a large Java project at school, each of my team mates and I divide the project into classes and we are each responsible for a particular set of classes. We don't ever need to look at one another's code to complete the assignment, and thus bugs can creep in.

On the other hand, I work on a mid-size PHP code base which is my responsibility alone. Thankfully, one of my co-workers is an excellent PHP coder. Whenever I make revisions to the code, I ask him to review my code. The mere knowledge that someone else is going to read the code I write is enough impetus for me to write better code in the hope that someday he won't find something I'll need to revise.

My experience is not an isolated case, as can be seen in this article by Jeff Atwood who quotes Code Complete: [http://www.codinghorror.com/blog/archives/000495.html].

This is supported by the generally higher quality of code in very shared languages like C, Smalltalk and Python, than in seldom shared languages like Java, C++ and Cobol.

Clearly, C has a high potential for code hiding, but a community of code sharing (Unix) was built around the language to address this issue.


>Java programs are notorious for being resource hogs and unstable, and smalltalk programs are notorious for the opposite.

I haven't experienced a smalltalk app that couldn't be described similarly - but maybe things are improved in recent times, I should look again.


My experience with the OS/GUI built on top of the Squeak VM has been very positive.


As the author you quoted and reviewed, I'll say this:

1. Thank you for the input.

2. I think you're imagining a greater amount of blame being heaped on OOP than I actually intended. That may be the fault of unclear presentation on my part, though.

3. The second half of your comment is most excellent, and quite worth the read. Thanks for making these points so well.


After reading your OOP and the Death of Modularity [http://sob.apotheon.org/?p=935], I see your point more clearly. (I'm not very perspicacious ;-)

Your point is that OOP "creates a means of loosely coupling code in a given complex system," which is considerably more scalable than tightly coupled code, which in turn leads to ever larger programs.

My comparison of Java and Smalltalk was meant to show that there exists versions of OOP which completely decouple code. In a Smalltalk VM (or at least in Squeak), each object you create is immediately available to you as though it were a command line utility.

This is very much in the spirit of the unix philosophy which you cite in both entries:

"...certain older development philosophies (like "the Unix philosophy") that provided significantly better modularity and significantly reduced complexity." [http://sob.apotheon.org/?p=935]

"Unix is the tradition of decoupled code, whence the very notion of a user environment composed entirely of programs that "do one thing well" comes to mind." [http://sob.apotheon.org/?p=245]


Thanks for coming back after reading Death of Modularity to examine the matter further. I've certainly heard such good things about (properly used) Smalltalk, but while many people compare Ruby to Smalltalk (and Ruby is the language I've used most, lately) I haven't really experienced these Smalltalk systems themselves, so I tend to refrain from saying too much bout Smalltalk (lest I make an ass of myself).


No, ambition and sloth did this to us: Ambition in the scope of the systems we build, sloth in our willingness to give in to whatever gets the job done.

As Alan Kay said, "The revolution hasn't happened yet." He knows that OOP isn't the final answer (although I think we're all thankful for its existence). He is one of a small group, made up of mainly self-funded geniuses and eccentrics, who are working in this space. Everyone else gave up. We had PARC in the 70's and really nothing else since; we're still coasting on what they accomplished there.

Now that we know how important this is, why can't we fund a PARC again? Why can't we build a place where we throw it all out and start over? I know of a few places doing this now, including Viewpoint Research, but we need more.


Funny how people are so eager to attribute bad design to the design philosophy it attempted to follow. This guy at least acknowledges that as a trap, but he doesn't convince me that he isn't falling into it. Furthermore, if you read his other post that he links to, he pretty much admits that the things he blames on OOP are due to incompetent use of OOP:

"The thing that wasn't equal was the tendency of humans to fill a new development technique's capacity for scalability as long as that technique is used. As OOP provided greater ability to write ever-larger programs, humans wrote ever-larger programs — even when they didn't need to do so (and probably shouldn't have done so)."


You did a very good job of paraphrasing much of what I said. I wonder why you phrased it as though you disagreed with me.


I've read tons on the pros and cons of OO, done quite a lot of both OO and non-OO programming, and witnessed many other people try. My conclusion after all of this is that the debate on OO is almost entirely obscured by noise, because only 10-20% of developers are capable of using it even remotely correctly in practice. Many of the people yelling about how horrible it is are not in that 10-20%, either, and nor are many of the people singing (or, at least, "repeating") its praises.

It may be sold as a technique that the masses can use, but this is demonstrably not true, if you examine the code the masses are producing.


Yeah, but as Tim Lister once said (about something else): if everybody gets it wrong, there's something wrong with it.


I tend to agree. But it should be pointed out your observation extends to programming and architecture in general, and OO is just a special case of that. Most people are pretty bad at designing anything much past about ten thousand lines and almost everybody above that scope ends up producing "relatively maintainable spaghetti code", regardless of the language paradigm.

I am also sympathetic to the idea that we shouldn't have architects as a separate, distinct position, but some way of recognizing your good designers and architects seems necessary, because you don't really want just anybody doing it.


I tend to agree You shouldn't, because the original premise is wrong. You can ask most people very intuitive questions about physics (such as, if you are walking along and drop a book, where will the book land?) and they will get it wrong. They are wrong, not the physics.

I agree strongly with your original premise: most don't understand how to use it properly. I recommend Allen Holub for learning.


That's a good argument for science, but a putative engineering discipline must be something that actual, real humans can perform, or we are forced to admit that the standards should be raised and too many people are currently trying to do the engineering.

That's why I point out that it applies to all design philosophies I encounter. I hate to be elitist as a general principle, but I regretfully must come to the conclusion there's a lot of programmers programming who really shouldn't be, based on the evidence.


It may also simply be a communication problem. Here's another example, that I'm more familiar with. In engineering physics textbooks, most treatments of AC circuits are very confusing for the student. At that stage of their careers, we use sin and cos as opposed to a more complex-exponential treatment.

Now, the textbooks all tend to make the same basic treatment. When I was a prof, since I am a "challenged" reader, rather than try to wade through the mess in the book I set apart my own derivation. When I was finished, I naturally had to tie it back into what the textbook was saying so that the students had something to hang onto. But when I went to make the link, what I found actually startled me.

In their derivations, all of the authors made the same phase-shift of the source signals and never mentioned it. I couldn't believe it, and tried to figure out where my mistake was. But I didn't make a mistake.

In a section of the book that didn't really figure in to the future of the students' education--could be easily skipped, in fact, because anybody that needed it would do it over "correctly" anyway-- everybody just sort of did what everybody else did.

Perhaps that's what is going on in OOP. I'm not trained as a programmer--I just pick up what I can. So I'm sure that most of my code is laughable, and I do sometimes paint myself into a corner. But I find that I am sometimes surprised by how easy it is to make big changes if I've encapsulated things. Just my personal experience.


. . . or at least with its choice of target audience.


I've just 'inherited' a fairly large java project, and while I doubt that java is the cause of this kind of problem it definitely makes it very easy to make these mistakes.

Over enforcing the oop concept is a fantastic way of losing track of what is going on with your code, and it pretty much guarantees inefficiency, because 'abstraction' is just another way of saying "I don't want to know". What you don't know you can't analyze and what you can't analyze might very well be the cause of your problem.

Good programmer (tm) can write excellent code in Java, because like any other tool it will respond to its handler. If you are just using classes to pile layer upon layer of unwanted abstractions on top of each other because you fail to grasp your problem properly then you have nobody to blame but yourself.

Again, in some languages this kind of behaviour is actively encouraged and in some it is discouraged, I suspect that java is one of the former.


Java makes excellent code unreadable.


... and makes it possible to hide some pretty bad code/design among the abstractions.


Well, yes: arguably, that very fact is one of the major wins you get from good abstractions.


OOP is an excellent tool when it is used to support separation of concerns. I'm actually working on a system where they lumped all the functions together, didn't try to separate them by obvious domain entities. This is inconvenient, but it is not nearly as bad as a poorly designed OOP framework. The problem that I see with the frameworks is not enough people ask the question "what does it really buy me ?". For instance relational databases give a huge benefit in ACID transactions, a query language that gives correct results, you can dynamically update tables without breaking things, dynamically join tables to give new kinds of results, etc.. So paying the price to use SQL to acess your data is not bad, because you get so much back. But what exaclty are you getting back from using a framework like Hibernate in between you and the database ? And where is the proof that ORM is even a good idea ? There seems to be an idea - if it is OO it is good, and if a lot of people are using it it must be good. I think these two assumptions are the real problem.


In my opinion, a main reason for the situation described by the author is lack of architectural governance in large projects. The most senior programmer often is assigned the role of "the architect", even if he is more of a type that likes to obsess over details rather than the big picture. Such types are prone to dismiss the importance of conceptual integrity throughout a complex project which ultimately leads to the pile of unsustainable mess that many enterprise-class applications have become. Brooks laid out the importance of conceptual integrity pretty well in in the mythical man month. It is often not taken serious enough today.


I'm curious what the alternative is, if any?


As I alluded to in a rant just posted, I think deficiencies in Java as a language created a community of complexity where OOP was taken to the extreme, where interfaces, inheritance, and beans are used with abandon. Contrast this with Python and its community, where there is a less extreme approach to OOP, because the language itself is more expressive.


Use OOP correctly.

Don't turn OOP into the point of your programming style on any project.

Don't use it at all when it's not appropriate.

When you don't use OOP, make sure you're using the right thing for the problem domain at hand.

When you use something programming paradigm other than OOP, make sure you use that correctly, too.


I probably should have said "Avoid Java" in there somewhere, too.


No.

And that was this week's edition of Simple Answers To Simple Questions.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: