Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I also think he's overestimating the degree to which all programmers, even good ones, can understand someone else's code easily. In fact, most of the effort being put into understanding how to code properly is meant to make it easy for other programmers to be able to read someone else's code and know what's going on, simply because it's extremely hard to do as things currently stand.

Why is it a best practice to write functions that only do one thing? Because as soon as the function is doing multiple things, it can get very very confusing as to what exactly is going on, even for the best programmers.



> In fact, most of the effort being put into understanding how to code properly is meant to make it easy for other programmers to be able to read someone else's code and know what's going on, simply because it's extremely hard to do as things currently stand.

Yes, and in that connection I want to mention narcissism, which I personally think is a big obstacle standing in the way of collaborative programming, in the way of seeing one's code as community property that must be understood by everyone involved.

Narcissists, more numerous than most people realize, think their code is much better than code created by ordinary people, to the degree that they see no point in making it comprehensible to mere mortals. Also, consistent with the true nature of narcissists, they're secretly insecure about themselves and their abilities, which is the real motive for making their code incomprehensible -- to do otherwise might reveal how perfectly ordinary their code really is, and they really are.

> Why is it a best practice to write functions that only do one thing? Because as soon as the function is doing multiple things, it can get very very confusing as to what exactly is going on, even for the best programmers.

That's a good argument in favor of OO programming, where one writes a class instead of a mere function, thus avoiding the trap of trying to create multi-purpose functions.


> That's a good argument in favor of OO programming, where one writes a class instead of a mere function, thus avoiding the trap of trying to create multi-purpose functions.

I disagree there (that "That's a good argument in favor of OO programming"). What if one creates a multi-purpose class? How is that any better than a multi-purpose function?

IMHO the point -- do one thing -- is applicable to many paradigms.


> What if one creates a multi-purpose class? How is that any better than a multi-purpose function?

Easily answered -- look at a class meant to deal with String objects. It encapsulates a string object plus any number of functions that deal with strings. Compare this to a function that does to a string what a class does, but necessarily under the command of a passed flag that instructs it which action to take. The latter is much less satisfactory from a comprehensibility standpoint.

I say this because I was programming before there were classes, or anything resembling high-level structure (I wrote Apple Writer in assembly language, a language utterly lacking in structure: http://en.wikipedia.org/wiki/Apple_Writer), so I've watched a number of revolutions in programming over the decades. The idea of a class associating a data type and functions meant to deal with it seemed to me to solve any number of problems -- problems I had to deal with in various clunky ways before that development.

> I disagree there (that "That's a good argument in favor of OO programming").

But it is. The idea that all functions having any special meaning to (for example) strings would be enclosed within an object also containing the string to be operated on, greatly reduced the confusion that preceded it. One can make too much out of the OO paradigm, but the basic idea is sound and based on pragmatic thinking.


I'd like to clarify my point -- I'm not arguing against OOP or for another paradigm. What I'm saying is that "do one thing" transcends paradigm. And in fact, its OOP incarnation is known as the single responsibility principle (http://en.wikipedia.org/wiki/Single_responsibility_principle). So when I say that "I disagree that 'do one thing' is an argument in favor of OO programming", what I'm saying is that "do one thing" is important, no matter which paradigm you choose. Neither OOP's strengths and weaknesses nor how it compares to any other paradigm are relevant to my point.

I'm sorry, but saying things like but it is and the basic idea is sound and based on pragmatic thinking without a supporting argument is unconvincing.


Behind the scenes using a method of an object is just syntactic sugar for calling a function with the object as the first argument. The modularity aspect is good but I think OO often leads to code that is difficult to read when there are many levels of inheritance, esp. in languages where you are forced to put everything in classes such as Java. Instead of the old spaghetti code you then get ravioli code: http://en.wikipedia.org/wiki/Ravioli_code#Ravioli_code


To me, one of the advantages of objects is that I can tune the object's functions and data structures to get the behavior I want, then while working at a different level of abstraction, I only need to remember the object's interface, not its internals, which makes it easier to use its abilities in a consistent way.


I agree with this advantage, that's what I meant with "the modularity aspect"; a non-OO programming language with support for modules also offers this. However, as soon as you get to non-trivial and often gratuitous inheritance hierarchies, it gets in the way of at least readability and probably also maintainability. Example: instead of String, you get AbstractMutableStringOfWesternCharacters, all the way to ArrayBasedStringOfASCII and everything in between. Suddenly to understand one function, you have to read and understand n classes, because they are part of the inheritance chain.


Some programmers may be narcissists, sure. But I think the big obstacle standing in the way of collaborative programming is having to be aware of too much other code in the first place.

It's very hard to read code, because code isn't linear--it's more like de-serializing a graph in your head, and so it takes somewhere between O(n log n) and O(n^2) to fully grok a codebase of size N. So we should limit N as much as possible.

Now, as programmers, we theoretically know how to limit N just fine. We can still make programs as big as we like, but as long as there's low coupling between the components making up those programs, each component can be separated into its own codebase. Each component exports a documented API, each other component consumes those APIs, treating their dependent components as black boxes. This is well-known stuff.

But despite "decrease coupling!" being one of the first things pounded into most programmers in Introduction to Blub courses since the 70s, coupling is still way too high, and codebases far too large as a result. Low coupling, when dressed up in terms like "Service-oriented architecture" or "the Unix philosophy", is seen as mystifying and novel to most programming journeymen, like some sort of weird Eastern religion. This probably means that, despite learning what coupling is and being able to recite that definition in a sort of cargo-cult manner, they never really viscerally absorbed what coupling does, the pain it causes, or how to lower it[1].

You shouldn't have to dive into a codebase big enough to drown yourself in. Codebases shouldn't be that big. Each codebase should be more like a little puddle, maintained by a few people (or even just one narcissistic person!), which is small enough that, if someone gets hit by a bus (or gets annoyed with the narcissist), it's both able to be read in one sitting, or able to be re-written from scratch in one sprint.

Effectively, this creates a "bus factor" of infinity: if any component is small enough that it can be learned, or recreated, then the original programmer is unnecessary to that component's upkeep. Because, sort of like an EC2 instance, the component is just as likely to be created anew, as it is to be "closed" and then "opened" again.

---

[1] I imagine that you could imbue such knowledge with a single assignment to the effect of:

* We'll be working with three already-written programs, Foo, Bar, and Baz. They're each available in source-control on Github.

* Foo is componentized already, so it's split into five codebases. You can clone the main one, then run `git submodule update` to get the rest.

* Bar and Baz, meanwhile, are big monolithic codebases. Just clone the whole mess.

1. On each of the original projects, two open bugs is listed in the issues. Fix the first bug in your fork.

2. Now, refactor your fork of Bar into componentized form. Copy the submodule approach of Foo.

3. Now fix the second bug listed on each of the projects' issue-trackers.


Exactly, code should be read like you would a math theorem, one symbol at a time. It's not a sentence where you read character after character and at the end of the line you know what those words mean. Every symbol has a deep meaning and expresses more than you probably think. In the same way, when you read a mathematical demonstration if you don't really stop and think about every single symbol you won't get the reasoning behind the whole thing.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

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

Search: