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

Subroutine that is only used in one place still improves readability: instead of a bunch of statements, you have a name, explicit parameter list and a retugn value.


...and then that subroutine has another subroutine, each has its own *doc comments, and suddenly to understand the code you need to jump between three files and read 50 lines of code that could be five lines of code in one file.

Breaking up code doesn't always make it more clear and readable.

The same goes for OOP and desperately creating objects from things that don't represent real world entities.


I only agree with this in the case of pure functions. However, when your function has a side effect (quite usual in C++ methods for example) then all readability gains are lost.

I much prefer inlined code that reads from top to bottom, rather to have to jump all over the place into single purpose functions.


I like this. I never really thought about it this way, but I'm much more okay with long functional methods rather than long mutating method


it's not when applied once, it's when something that can be understood more easily "locally" is exploded into a billion such pieces - then you have to mentally find all the call sites and usages to mentally build the call graph back up — you still have to understand how the whole ensemble works and bulldozing can (but not always) make understanding the ensemble more difficult especially if done by a poor programmer has the effect of obfuscation rather than clarification.


It's good when it gives a name to what you're doing.

connection = establish_connection_given_these_conditions(cond1, cond2)

Vs the many lines of checking, creating the ConnectionFactory, passing in the the conditions to the ConnectionConditionFactory, and generally having a new statement every time you want to blow your nose. What were we doing again? I dunno.

I've used it. I'll defend it. It's better than comments when done properly as above.


What's the difference between cond1 and cond2 parameters? Can they be swapped? If cond1 and cond2 are booleans, they're almost indecipherable at the call site. Why not cond1 && cond2 (or symbolic equivalent)?

Abstracting away the gruntwork of your connection factory and your connection condition factory into a couple of separate routines - those are resuable bits of code, they'll let you write this code at a higher level of abstraction using reusable abstractions. But pushing the ugly wiring into a non-reusable black box and pretending it's better design - bullshit.


Good point on the conditions. I wanted to avoid pasting prod code I'd been working on and was trying to make an example: I generally use this for things like logging.

It's best when:

1) The logic is very specific to the situation and isn't reusable 2) Isn't important to the function's api contract.


Indeed!

More generally, functions are not only about abstraction, but also about decomposition.

The only thing that may hurt readability is when the decomposition itself is too much nested. Usually, it's best to split a large task purely linearly in subtasks (functions), and have them called all from a single function. That main task function gives you not only an overview of all sub tasks, but also about the dataflow between them.

In constrast, too much subtask nesting (subtasks of subtasks of subtasks) often hide the overall flow, which then makes it hard to get the overall view.


Code maintenance is hard, therefore a bugfix could easily turn the function to establish_connection_given_these_conditions_and_implicit_one(cond1, cond2); Now the identifier is misleading and becomes a landmine waiting to blow your feet off :(

Having done some debugging where identifiers/comments/structure are misleading I am a bit afraid of such code. I am really not sure what is the best way to structure highly imperative code though, because every option I have seen contains some dark corners.


Fair. On of the good aspects is that it makes this chunk testable, rather than having to test the behavior of the long line of statements. If there's a test for it it's a lot harder to sneak in an implicit assumption.


What I meant was that the implicit condition may be too context specific, dunno, maybe some NIC flags or something that are extremely unlikely to be caught by tests (especially written by anyone working on the code piece), code reviews or any such measure. Because the code works seemingly well until underlying conditions stop being satisfied.

Such conditions happen all the time, but are unproportionaly painful to debug in bulldozer code :(

Rant: Even though tests are nearly invaluable, though I personally believe that tests written by the same person writing implementation give a bit false sense of security. I see spec, tests and implementation different representations of author's understanding of the problem. If any two of those (e.g. tests and implementation) are produced from the same mind/understanding then they cannot be used to verify that understanding behind each other is the same.


Good point. I think I should make myself clear: I explicitly meant _procedures_ as in _functions_, most likely, pure ones, and not _methods_ of the same instance, for example. which have acccess to the same dozens of private members. If you can, declare these procedures to minimize their possible side effects - that makes reasoning about them much clearer.


The point is not to do that. When it is split into small pieces and I am validating the code, I care only about whether the content of function does what the name says. I don't need to have it all in head at the same time.


> The point is not to do that.

Then it's not bulldozer code.


With no guarantee that the name corresponds with what it does (particularly 5 or 10 years in the future) and your parameter list doesn't have names at point of use (what does the 'true' argument do?) and may be overloaded.

Factoring things out into single-use subroutines is a real mixed bag. It's not a clear win at all, not at all.


While I don't think refactoring single use function is a win in every case, I think it makes sense when what you're trying to do in the block of code could be conceptually separated from the rest of the function - and named appropriately.

Arguments sometimes make this practice messy, especially if you find out that your detached subroutine requires 5 different arguments from its parent. But IDEs or smart editors with tooltips or goto-definition make this an almost no-brainer in most cases. Besides that, many languages allow you (or even force you in the case of Smalltalk and Objective C) to solve the argument readability problem at the language level. When it's really naked 'true', you could just use keyword arguments.


I used to agree with you but now I resoundingly do not. The only compelling reason I have left is still quite compelling: testability. Without breaking your code I to subroutines it becomes orders of magnitude harder to test effectively or at all.


It comes at the expense of indirection, so it's certainly not free.




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

Search: