[HN Gopher] Want cleaner code? Use the rule of six
       ___________________________________________________________________
        
       Want cleaner code? Use the rule of six
        
       Author : da12
       Score  : 178 points
       Date   : 2022-09-24 14:39 UTC (8 hours ago)
        
 (HTM) web link (davidamos.dev)
 (TXT) w3m dump (davidamos.dev)
        
       | iafiaf wrote:
       | Early in my career, I took to heart such books and articles and
       | often felt guilty and lessor-programmer when I cut corners.
       | Here's my 2 cents now:
       | 
       | - Some of this is the coding equivalent of "6 rules for financial
       | freedom" or "6 ways to find your dream soulmate". Generic advice
       | that doesn't reflect highly nuanced reality.
       | 
       | - These rules are guidelines at best. There are justifiable
       | reasons to break them; which I do often. Albeit this requires
       | experience (and dare I say, wisdom). For example, refactoring
       | code into a separate function levies a cost (of indirection) on
       | the reader. Therefore copy-paste is sometimes fine.
       | 
       | - Clode "cleanliness" is a moving target. For a coder's mental
       | health and value proposition for his project, he/she should know
       | what code can afford to stay dirty.
       | 
       | PS: I love Jonathan Blow's opinions on coding/programming. Here
       | are a few: https://www.youtube.com/watch?v=21JlBOxgGwY
       | https://www.youtube.com/watch?v=ubWB_ResHwM
       | https://www.youtube.com/watch?v=KcP1fXQv0iU
        
         | 29athrowaway wrote:
         | Jonathan Blow is a creative, productive and overall smart guy,
         | but reading his code will make you want to slam your head
         | against the wall.
         | 
         | What irritates me the most are the long, non-linear comments
         | full of distracting noise. It's like reading a choose your own
         | adventure novel.
        
           | jstimpfle wrote:
           | When he talks about his approach he typically mentions the
           | importance placed on getting feedback from an actually
           | working prototype as quickly as possible. Don't judge him by
           | most of the ad-hoc code you've perhaps seen on twitch. But
           | I've seen some really good-looking stuff there as well -
           | straight to the point, no noise, not overabstracted. I would
           | be interested what a finished project looks like.
        
         | BlargMcLarg wrote:
         | Don't forget the most prominent part: 'your clean' and 'my
         | clean' can differ greatly.
         | 
         | You can do your absolute worst and you will still find someone
         | claiming there aren't enough comments, or the naming is bad, or
         | the code is too dense, or the code isn't dense enough, or you
         | should use typed objects instead of tuples and anonymous
         | classes, or your code should be more functional, or your code
         | should be more imperative, or it should be more event-driven,
         | or it requires more logging, etc.
         | 
         | And it turns out, there is almost no research to tell you who
         | is right and who is wrong. The only thing I can safely tell
         | others, is all these discussions and additions will add 900%
         | more work all things considered, and there's no guarantee it
         | will be less bug free or more.
        
       | catlifeonmars wrote:
       | The examples and improvements in the article feel obvious like
       | "common sense" -- which is a good thing. I'm not 100% sold on the
       | reasoning though. It kind of feels like a just-so explanation
       | without much justification
        
       | michaelwww wrote:
       | If you're like me and like to step through code with a debugger,
       | shorter lines are better for setting breakpoints and checking
       | values.
        
         | convolvatron wrote:
         | this is important and true.
         | 
         | but I really wish debugger evolution hadn't stopped at the
         | line.
        
       | [deleted]
        
       | sdoering wrote:
       | Offtopic:
       | 
       | The fact that the site uses a consent solution that fakes a
       | loading screen when trying to configure (read disable)
       | tracking/advertising is an instant bounce for me.
        
         | niea_11 wrote:
         | It's not fake. In my case, it's not working because of my
         | adblocker. When I disable it, it works.
        
       | tgv wrote:
       | The starting assumption is highly dubious: "Short lines of code
       | require less brainpower to read than long ones."
       | 
       | I'm not going to nitpick the incredibly bullshitty term
       | "brainpower" and what is less and if that's actually
       | advantageous, but if you write short lines of code, you're going
       | to write more lines, which requires "more brainpower" to
       | understand. You don't simply "chunk" lines in memory. If that
       | were true, you could just as easily chunk function calls.
       | 
       | That memory plays a role is fairly certain. There is a pretty
       | hard finding from psycholinguistics: it's hard to understand
       | nested structures. The sentence "the rat the cat the cook hit
       | chased escaped" is much harder to understand than it's right-
       | branching equivalent "the cook hit the cat that chased the rat
       | that escaped". However, reading code is not the same as reading
       | natural language.
       | 
       | If you want to know if what you wrote is understandable, try
       | reading your code without falling to back to remembering _why_
       | you wrote it. Try to read _what_ you wrote. Wait a few days if
       | your recollections get in the way.
        
       | hardwaregeek wrote:
       | This seems perfectly reasonable advice. However I do wonder how
       | many people actually struggle with this sort of code quality.
       | It's certainly more than a few, since I've encountered bad code
       | with these issues. But it's not exactly the most pressing issue
       | either. As the author demonstrated, you can refactor this with a
       | little thought. It's the code equivalent of tidying your room,
       | sweeping the floors and putting your stuff away.
       | 
       | Whereas the refactoring issues I'd love to learn more about are
       | the equivalent of a sinkhole in your living room. Stuff like "you
       | have data dependencies that go in, out, left, right, and through
       | the code", or "the codebase is a mishmash of React combined with
       | Vanilla JS that is hooked up to a custom PHP MVC". Basically
       | refactoring that involves issues that cannot be cleaned up all at
       | once, that involve deep architectural decisions and that require
       | some amount of buy-in from the team.
       | 
       | Mostly I'd like to know more about this because I've realized
       | that I'm not very good at it. My inclination is to just refactor
       | everything and that's not a feasible strategy. I also struggle to
       | balance it with getting feature work done. Definitely something I
       | plan on reading more about.
        
         | theptip wrote:
         | I've seen some engineers that think it's clever to put
         | everything into a one-line list comprehension where possible,
         | even if that means rewriting named variables as letters to make
         | them fit. The result is really hard to read.
         | 
         | I've also (more common) encountered engineers that don't
         | actively try to be clever by being terse, but also don't put
         | their mind to writing clearly.
         | 
         | Put differently, I think one has to actively try to write easy-
         | to-read code.
         | 
         | I agree with your point that this sort of micro-style point
         | isn't as big as architectural questions, but it's definitely
         | something you want to teach junior engineers so that it's
         | second nature by the time they are at the level where they are
         | thinking about architecture.
         | 
         | For that you can try reading Bob Martin, Martin Fowler, Kent
         | Beck, Domain Driven Design, Hexagonal, etc. - but you also just
         | need to build for a decade while thinking about that stuff to
         | really master it. Sadly architecture often seems more craft
         | than formal engineering at this level.
        
         | 9dev wrote:
         | What always helps me with architecture redesign projects like
         | that is trying to define a goal to work towards: getting alway
         | from that custom PHP MVC and to a proper Symfony application,
         | for example. If you know where you want to go, it's easier to
         | stay focused and to align what you do with what you want to
         | achieve.
        
         | BurningFrog wrote:
         | My opinion is that each line of code should be easily
         | understandable. Without that, code is very hard to work with.
         | 
         | You're right that other code problems can be worse. But that's
         | no excuse to avoid doing the basics.
         | 
         | To clean up system design issues, you must first know what a
         | better system design would be. It's not enough to realize that
         | what you have is bad.
         | 
         | I do this a lot, and part of my approach is to always be
         | incremental. Improve one detail/aspect at a time. The worst,
         | very tempting, idea in this field is to throw everything away
         | and start over...
         | 
         | > _I also struggle to balance it with getting feature work
         | done_
         | 
         | FWIW, I like to spend 1/3 of my time cleaning up and
         | refactoring.
        
       | irrational wrote:
       | This is the main reason I don't like arrow functions in
       | JavaScript. People overuse them to create "clever" code - lots of
       | things going on in a single line. Then they try to claim that by
       | having everything on a single line the code is easier to read and
       | understand.
        
       | schemathings wrote:
       | For the example in the text I'd typically just include a one line
       | comment above to show what an example string would look like and
       | leave the code as is
       | 
       | # URL with params
       | https://news.ycombinator.com/item?id=32963021&something=valu...
       | 
       | map(lambda x: x.split('=')[1], s.split('?')[1].split('&')[-3:])
        
       | artemonster wrote:
       | I always liked the quote "you need to be twice as smart to debug
       | a code. If you write smart code, you, by definition, cannot debug
       | it" (sorry I have no idea who said this). This is why I still
       | code in C. No smartass bullshit, just plain old undefined
       | behaviour and out of bounds access. Lovin it.
        
         | kazinator wrote:
         | This is from Brian Kernighan (the 'K' in the "K&R C Book" and
         | AWK), known as Kernighan's law:
         | 
         | "Debugging is twice as hard as writing the code in the first
         | place. Therefore, if you write the code as cleverly as
         | possible, you are, by definition, not smart enough to debug
         | it."
        
       | ppierald wrote:
       | If there is some legitimate reason (say performance) to keep a
       | tighter form (inline assembly, Python 1-liner, whatever), then
       | making the unfurled equivalency as a comment nearby to allow the
       | next developer to have a fighting chance would be really helpful.
       | Also, error handling tends to be not included in the 1-liners.
        
       | mkoubaa wrote:
       | A reviewer can usually tell which code is easier to understand
       | side by side, even when it's yourself as the reviewer.
       | 
       | Applying rules like these to your code may or may not result to
       | cleaner code, but that's a testable hypothesis.
       | 
       | I've seen all too often some clean code recommendation or other
       | applied to code and it gets harder to understand. And the person
       | doing the refactoring (often myself) gets caught in sunk cost.
       | 
       | Now my recommendation is always:
       | 
       | 1. Use your intuition to predict if a change makes code cleaner.
       | 
       | 2. Try to make that change, and be open to doing things a little
       | differently that you first imagined.
       | 
       | 3. Test your hypothesis to see what others think. Decide what to
       | do, but be mentally willing to throw it away.
       | 
       | 4. Repeat
       | 
       | Articles like this are good resources to help train your
       | intuition, but there is no substitute to developing your personal
       | and team "flavor profile" for what styles suit your way of
       | thinking.
        
       | jonnycomputer wrote:
       | Then you have to _name_ things. And naming things sucks,
       | especially because not every intermediate has an obvious name for
       | it, distinct enough to distinguish it from the next intermediate
       | chunk.
        
         | overgard wrote:
         | Ok, but if you're reading code for the first time, you're going
         | to have to store the intermediate parts in your working memory
         | somewhere. And if you don't have a mnemonic, like, a name, then
         | "the return value of the lambda after a split" is a lot harder
         | to remember.
         | 
         | Naming things _is_ hard, but it 's also _important_.
        
       | bloaf wrote:
       | I have pretty mixed feelings about this. Personally I find it
       | much easier to debug code that:
       | 
       | 1) fits entirely on my screen and
       | 
       | 2) doesn't involve much state modification
       | 
       |  _Every_ intermediate variable is a chance for me to miss some
       | modification (e.g. it was passed to a func that modifies its
       | arguments) and consequently misunderstand what is happening.
       | 
       | I've been experimenting in Python with the function chaining
       | style of coding enabled by the toolz library. So while not at all
       | idiomatic, the example in the original article would come out as
       | something like this:
       | 
       | https://gist.github.com/ZeroBomb/8ac470b1d4b02c11f2873c5d4e0...
       | 
       | I would say that function-chaining this example would constitute
       | over-engineering, but I have found that writing in this style has
       | really helped me express pretty complex function composition in a
       | way that is still concise without using a bunch of intermediate
       | variables.
        
         | readthenotes1 wrote:
         | Do you really write a very long comment after every function
         | call?
         | 
         | And have you looked at code that's over a year old and modified
         | by other people to see how poorly those comments now match the
         | code?
        
           | bloaf wrote:
           | No. I did that for people having a first exposure to this
           | non-idiomatic currying/function chaining.
           | 
           | In actual code I would have put most of those on one line.
        
       | whiddershins wrote:
       | Code has a typo 'slit' instead of split after bringing up the
       | concept of moving something into a function.
        
       | jwilliams wrote:
       | All comes down to good naming in the end. The craft is finding
       | both compact and specific names.
       | 
       | I think the mantra for all names to be short can be
       | counterproductive here. If the code span of a variable is short,
       | a long name can be fine (and very clarifying, perhaps even
       | resulting in a comment not being needed).
       | 
       | Shorter names for longer spans are much better. But you'd hope
       | they're the very obvious subject of that span.
        
       | djmips wrote:
       | Coming from a background in lower level languages where you can
       | only express one simple thing per line my tendency has always
       | been to be more verbose than my colleagues. The worst time I ever
       | had was when I had to work on someone's Perl code that one time.
       | So I really like this heuristic to make code more readable.
        
       | pavon wrote:
       | Never cleaned up the most obtuse part of that code snippet - why
       | are we only keeping the last three parameters?
        
         | lbriner wrote:
         | I think a contrived example. In truth, there is much more
         | likely to be a way of tidying this up with a nice reuseable
         | function like "get_querystring_params" which returns an array
         | and then take the first 3 with a comment like "only the first 3
         | parameters are used for the search".
         | 
         | Taking a subet of query params smells in its own right so,
         | again, might be a bad example.
        
       | RajT88 wrote:
       | I have written a lot of Powershell in the last few years. I
       | eschew the clever powershell ways of doing things if someone else
       | _may_ end up owning it (think: where-object, foreach-object) in
       | favor of expressions that resemble other languages (foreach,
       | for).
       | 
       | If I'm writing it for myself, and only ever myself, I'll use the
       | more clever powershell ways of doing things. Expressions like:
       | 
       | 1..10 | % {$_}
       | 
       | If you're coming from another language, you're going to have to
       | run it to understand it, or look it up. That is time lost.
        
         | ParetoOptimal wrote:
         | Some things are short and self explanatory though. `1..10` is
         | just syntax sugar for a stream or list from 1-10 right?
        
           | RajT88 wrote:
           | 1..10 is powershell range operator, and % is an alias for
           | foreach-object.
        
         | blown_gasket wrote:
         | I primarily write in PowerShell for end-user shell tools and Go
         | for network services.
         | 
         | Where-Object is going to let you cut down on the number of
         | lines of code compared to foreach() and for(), and in my
         | opinion will make the code more readable.
         | 
         | $vms | Where-Object -Property Name -match "sql"
         | 
         | vs
         | 
         | $vmOutput = @()
         | 
         | for($i = 0; $i -lt $vms.count; $i++) {
         | if($i.Name -match "sql"){                  $vmOutput += $i
         | }
         | 
         | }
         | 
         | vs
         | 
         | $vmOutput = @()
         | 
         | foreach($vm in $vms){                   if($vm.Name -match
         | "sql"){                  $vmOutput += $vm              }
         | 
         | }
         | 
         | For the Foreach-Object point, that cmdlet also give you the
         | option to use begin{}, process{} and end{} blocks. So that you
         | can with begin{} do something before any of your objects are
         | processed, process your objects with process{}, and after all
         | objects have been process do something with end{}. This logic
         | with for and foreach would have to come before and after the
         | for and foreach statements.
         | 
         | I don't see this as a "PowerShell being clever" but more as a
         | PowerShell is a shell that uses pipelines like _nix shells but
         | it has everything as an object unlike_ nix shells. So you get
         | to take advantage of that.
        
           | PeterWhittaker wrote:
           | > PowerShell is a shell that uses pipelines like nix shells
           | but it has everything as an object unlike nix shells. So you
           | get to take advantage of that.
           | 
           | That was one of my favourite PWSH features when I was using
           | it regularly. I'm a UNIX CLI-and-filter guy from way back and
           | after using PWSH for a while I longed for the same power in
           | bash (my shell for reasons of history, availability, and
           | muscle memory, I'm unlikely to change).
        
       | dan-robertson wrote:
       | I want code that is easy to read, write, update, and debug. It
       | isn't obvious that this means it should be 'clean', or indeed,
       | what 'clean' is. Some other things described as clean code (eg
       | uncle Bob) seem pretty bad to me. But then lots of people who
       | complain about that also suggest things that seem bad. Perhaps
       | lots of these things are too insignificant compared to other
       | business or design decisions that one can't really learn from
       | experience or past successful or failed projects.
        
       | retrocryptid wrote:
       | in the example given, I started with 10 things to keep in working
       | memory. now that we've added a named function or a named
       | variable, we have 11. I suggest it is at least as important to
       | name things well (or add comments) as it is to break lines up.
        
         | pavon wrote:
         | I'm working on a project that is following Uncle Bob's Clean
         | Code guidelines of striving to having functions be ideally 3
         | lines or less, and nor more than say 7. I have mixed feelings
         | about it.
         | 
         | My initial prejudices have largely held. I do find the code
         | harder to read and follow. Having to jump around, follow
         | variables that change name as they are passed through
         | functions, keeping track of state that was moved to a class
         | member rather than in a function body (because breaking into
         | pure functions resulted in too many function parameters). I
         | can't fit as much code on screen because of all the additional
         | function definitions.
         | 
         | Lastly, the success of the method relies heavily on how well
         | you name your functions, which is often considered one of the
         | hardest parts of programming. A name that makes perfect sense
         | to me may not be as clear to others, or even to myself in two
         | months. And the devil is in the details - there are so many
         | implied semantic preconditions and postconditions with every
         | function you write, there is no way to fit that into a function
         | signature no matter how well chosen, and if you tried to
         | document them them all your comments would be larger than code
         | itself at this level of granularity. So you still end up having
         | to read all the called code to understand the details of what
         | is happening anyway, which is easier to do with more flat code.
         | 
         | On the other hand, I've found the _process_ of  "extract till
         | you drop" to be very helpful in forcing me to find ways to
         | clean up my code. It naturally tends towards maintaining
         | separation of concerns, finding ways to DRY when the initial
         | structure wasn't conducive to it, and generally disentangling
         | things even more so than when I try to refactor to meet these
         | goals directly. If I had all the time in the world on other
         | projects, I think I would apply "extract till you drop" on my
         | code, then after it is disentangled, recombine it back into
         | reasonable size chunks.
        
           | hardware2win wrote:
           | >guidelines of striving to having functions be ideally 3
           | lines or less, and nor more than say 7. I have mixed feelings
           | about it.
           | 
           | Dont feel bad about it
           | 
           | Those small functions with hard limits are just terrible
           | advice
           | 
           | When you gotta know functions impl., which for me is very
           | often
           | 
           | Then this approach just increases cognitive load
        
             | f1shy wrote:
             | Those guidelines should (my personal position) be taken as
             | advisory, and never as hard rules. Functions should be
             | "small enough, that a less than gifted can understand it".
             | Is difficult to measure in lines. Best example is a big
             | switch with 10 cases. Artificially breaking that in smaller
             | pieces is not helpful. I have a soft rule of 3 to 7
             | different control structures (if, for, case, etc) in total,
             | and 2 or 3 nested.
        
         | artemonster wrote:
         | You forgot a KEY property of a function: this is an
         | abstraction. You don't (and in most cases shouldn't) care how
         | this "black box" does what it does, you identify it by the name
         | and move on. So a function collapses N things to understand to
         | 1, not how you have described it.
        
           | f1shy wrote:
           | Exactly. At each level of abstraction, you don't care what
           | the functions look inside. The name must be enough to
           | understand what it does. You can of course descend one level,
           | but at that point the layer above is no important. Just that
           | the function fills the contract, possibly calling. Still more
           | functions, which at that level are black boxes...
        
         | Wowfunhappy wrote:
         | A nice feature of working memory is that although it's limited
         | to around 6+ "things", each thing can be any size, if your
         | brain considers it a single unit. This is called "chunking".
         | 
         | So, it's easier to remember the three numbers 34, 765, 812 than
         | the eight numbers 3, 4, 7, 6, 5, 8, 1, 2.
         | 
         | Refactoring code into separate functions with descriptive
         | titles is probably a lot like combining numbers.
         | 
         | ---
         | 
         | + Well, the article says 4-6; I'd always heard the average was
         | around 7.
        
       | stagas wrote:
       | Another useful rule is to think it terms of intentions and split
       | to those individual intentions. You can always reduce code down
       | to a single function call, but was that the original intention?
       | Try to think as a reader that just stumbled on it without any
       | other context. `result = DoEverything(payload)` is often less
       | readable than `step1Result = DoImportantStep1(payload);
       | finalResult = DoImportantStep2(step1Result);` if Step1 and Step2
       | mirror the actual process that goes in your mind when solving
       | that particular problem, so when re-visiting you can understand
       | what's going on faster, without having to visit the
       | implementation of the single `DoEverything` function.
       | 
       | Edit: To clarify a bit more, in contrast to the rule of six, i'd
       | definitely keep a line that is more complex than usual but
       | conveys the intention of my thinking, rather than splitting it to
       | multiple lines and losing that important information, losing the
       | original intention.
        
       | Beltiras wrote:
       | Can't get past the obnoxious cookies. Anyone have the text?
        
       | glintik wrote:
       | <<Every line does only one thing>> - that's not related to real
       | clean code. And there are bunch of languages that's OK to have
       | few things on the same line - perl, ruby, groovy, scala and even
       | php.
        
         | gilch wrote:
         | And Python!
        
       | nmz wrote:
       | This is forth code 101, factorization is an absolute must when
       | writing forth code.
        
       | he0001 wrote:
       | This is so subjective. Some people do want to write such code as
       | that is "cleaner" because it's compact. Some wants to explain
       | every single step because that's "cleaner". Some tries to do
       | something in between and it's somehow "cleaner". But in the end,
       | it's mostly subjective.
        
         | overgard wrote:
         | Here's conway's game of life in APL:                   life -
         | {[?]1 [?] [?].[?] 3 4 = +/ +[?] -1 0 1 [?].[?] -1 0 1 [?]"
         | [?][?]}
         | 
         | Is that shorter than essentially every other language
         | implementation. Yep!
         | 
         | However, to even begin to understand it you have to read an
         | article from the original writer:
         | 
         | https://aplwiki.com/wiki/John_Scholes%27_Conway%27s_Game_of_...
         | 
         | To me, that is _objectively_ , not subjectively, less clear
         | than the longer implementations.
        
           | rak1507 wrote:
           | 'However, to even begin to understand it you have to read an
           | article from the original writer' - or just know APL? If you
           | know APL, it's clear.
        
             | overgard wrote:
             | While I'm not going to go collect a bunch of APL
             | programmers to confirm this (where would one even find
             | them?), I highly doubt that claim. Knowing a language
             | doesn't mean dense code is suddenly obvious.
             | 
             | This is a silly example, but years ago I wanted to prove
             | that you could write a non-trivial program in python using
             | a single expression (because python's lambda only allows
             | you to use expressions, not statements). And I managed to
             | do that. And it's hideous. Any python programmer _could_
             | theoretically understand what this is doing, but I doubt
             | they would. Bonus points if you can guess what it does
             | without running it!                   (lambda:
             | not globals().__setitem__('sys', __import__('sys'))
             | and not globals().__setitem__('this',
             | sys.modules[globals()['__name__']])             and not
             | globals().__setitem__('time', __import__('time'))
             | and             #program             [setattr(this, k, v)
             | for k,v in {                     'set_color': (lambda c:
             | w(['*', ' '][c])),                     'abs': (lambda t:
             | (int(t) + (int(t) >> 31)) ^ (int(t) >> 31)),
             | 'w': lambda x: sys.stdout.write(x) == 0,
             | 'smash': (lambda t: -((t * -1) >> 31)),
             | 'color': (lambda n,k: set_color(smash (k & (n - k)))),
             | 'col': (lambda n, k: k <= n and not color(n,k) and col(n,k
             | + 1)),                     'row': (lambda n: (
             | not w(' ' * (40-abs(int(n/2))))
             | and (col(abs(n), 0) or True)
             | and not w("\n")                                     and
             | (abs(n) < 63 or n < 0)
             | and not time.sleep(0.05)
             | and row(n+1))),                     'triangle': lambda:
             | row(-60)                 }.items() ] and triangle() )()
        
               | rak1507 wrote:
               | You don't need a bunch - you need 1. I am an APL
               | programmer, and I can tell you 100% for certain that
               | anyone who knows any APL would find that easy to read.
               | (Want to find more though? Go to one of these:
               | https://aplwiki.com/wiki/Chat_rooms_and_forums)
               | 
               | Knowing a language absolutely does mean 'dense' code is
               | obvious (fwiw that APL code is not at all dense, you can
               | do go a lot shorter). It's comparable to chinese
               | characters or something like that. It only looks
               | dense/unreadable to someone who doesn't know it.
        
               | ParetoOptimal wrote:
               | That example isn't like APL though because of it's
               | verbosity. Also APL has more universal abstractions to
               | express a lot of this kind of logic.
               | 
               | That lack of verbosity makes it easier to pick out the
               | intention revealing code, which isn't the case here.
        
               | overgard wrote:
               | Well, of course that code was meant as a joke, and I did
               | some things intentionally weird. I mention it just to
               | point out that a metric like "it's only a single
               | expression!" doesn't mean that something is actually
               | simple.
        
           | he0001 wrote:
           | Well, to you it is, for someone else it's "clean". APL is a
           | fun language and kind of my point here. Someone thought that
           | other languages were too verbose and tried to write a
           | language that was more expressive which a "normal" language
           | never can achieve.
        
           | ParetoOptimal wrote:
           | Can you objectively say that familiarity isn't the primary
           | reason APL is less understandable?
           | 
           | Put another way, if you had 2 years of APL experience and 2
           | years of Python experience, would you maintain that the APL
           | code is objectively less clear?
           | 
           | Is it really fair to judge languages we aren't familiar with
           | to the extent of the language we are comparing to?
        
             | overgard wrote:
             | Well, it'd be fairly easy to test: teach a first year
             | coding course, give one group python and the other group
             | APL, and monitor the grades and the drop rate.
             | 
             | I can't prove it, but I'm highly confident that python
             | would win. Because it has a lot of analogies to things
             | people already know:
             | 
             | - In simple cases, you can read it as a set of commands,
             | like a recipe. People are familiar with recipes
             | 
             | - Syntax mostly uses well know words (or abbreviations of
             | well known words), or very simple symbols that you'd pick
             | up in basic math (+,-, *, comma, etc.)
             | 
             | - Variable names make clear what the result of a particular
             | expression maps back to in the real world (ideally)
             | 
             | - The whitespace indented structure of the code is fairly
             | obvious if you've ever written an outline
             | 
             | APL has...
             | 
             | - Unfamiliar symbols
             | 
             | - Terse, confusing names
             | 
             | I mean, look at this example for getting the average from a
             | list of numbers:                   {(+[?][?])/[?][?]}
             | 
             | Since I don't know greek or math particularly well, in my
             | head I read that as "plus minus sign with a slash through
             | it and curly w divided by triple equals curly w with a
             | slash through it". I'm nowhere.
             | 
             | Here's the python code:                   def
             | average(numbers): return sum(numbers) / len(numbers)
             | 
             | I couldn't _write_ that if I didn 't know python, but if I
             | had never written a line of code in my life I could give
             | you a decent guess as to what it does. Is it verbose? I
             | guess in character count, but not in any meaningful sense.
             | 
             | You're right, familiarity IS why python is easier to
             | understand. But it's not programming familiarity, it's that
             | almost every aspect of it is more familiar. The only people
             | that (might) find APL more familiar are mathematicians in
             | my opinion, and they could easily understand python.
        
               | mlochbaum wrote:
               | I know a few people who've taught APL to kids or other
               | non-programmers and the most common remark is how much
               | quicker they get it than students who've worked with
               | other languages. I don't know what the result of the
               | experiment you describe would be but you're definitely
               | biased by your experience. And as a counterpoint to your
               | example, here's the code to multiply several numbers by
               | two in both Python in APL:                   x = [1,2,3]
               | [n*2 for n in x]              2 x 1 2 3
               | 
               | We have forums: APL Farm is the most consistently active.
               | If you'd like to talk to real live APLers just hop in.
               | 
               | https://aplwiki.com/wiki/Chat_rooms_and_forums
        
       | thisismyswamp wrote:
       | Skimmed the article and the comments and got no answer - can
       | someone tell me what the rule of six is?
        
         | krapp wrote:
         | The rule is literally described, in bold text, within the
         | article.
         | 
         | Try actually reading instead of skimming next time. Or at least
         | skim more slowly.
        
           | greenpeas wrote:
           | I skimmed the article and missed the definition as well. Now
           | that's certainly my fault, and I knew that if my curiosity
           | were peaked I'd go back and read the article more carefully.
           | 
           | But I'd also like to say that, IMHO, the fonts, spacing,
           | various headings, images and codeblocks with dark background
           | are all mixed up on that page, and the *bold* text does not
           | stand out at all. Every other sentence or piece of
           | information on that page is highlighted in its own way.
        
         | _dain_ wrote:
         | >That gives us a rule for deciding if a line of code is too
         | complex:
         | 
         | >A line of code containing 6+ pieces of information should be
         | simplified.
         | 
         | he put it in bold.
        
       | ravenstine wrote:
       | The main reason I stopped using the old school for loop in
       | JavaScript is that it's doing too much in a single line. If I
       | can't do for-of, I much prefer a while loop because it does
       | effectively the same job as for-in but each step gets its own
       | line. I find it easier to follow at a glance.
        
       | rzimmerman wrote:
       | "Rule of six" is generally interesting - I came upon the concept
       | when reading the book "Nightfall" by Isaac Asimov as a kid.
       | There's a line in the book about the number of stars in the sky,
       | and how people can't really grasp numbers more than 5-10. It got
       | me thinking about trying to visualize a set of 3, 4, or 5
       | distinct objects without splitting them into groups. I genuinely
       | can't do it for more than 5 or 6 of something.
       | 
       | I also remember reading about a study where chess masters and
       | non-experts were asked to memorize chess boards. Average people
       | could only remember 5-7 piece locations where chess masters could
       | remember the entire board. But when the piece layout was random
       | (rather than from real chess matches) the experts weren't much
       | better than the non-experts. It's speaks to the abstractions our
       | brain creates to deal with limited working memory.
       | 
       | That cumbersome line of python is a good example. As an
       | experienced python person, I immediately found myself giving
       | names to the chunks to understand it.
       | 
       | Overall very good advice. Your code should explain the steps it
       | takes to solve a problem (or in a more functional language,
       | explain the solution), not be as terse and clever as possible.
       | Keystrokes are cheap; thinking is expensive.
        
       | hirundo wrote:
       | I know it was just used as an example, but in reality when I see
       | code like this I think "I really don't want to reinvent URL
       | parsing for this, I'll import the library for that instead,"
       | resulting in much cleaner code.
       | 
       | But I do agree on keeping individual lines, if not entire
       | statements, small and simple. The ruby chainsaw is a great tool
       | for that. I find a chain of simple statements arranged in a flow
       | to be more readable than using lots of intermediate variables.
        
       | xfz wrote:
       | I can't access the linked page without accepting cookies.
        
         | gilch wrote:
         | Can't you just use an incognito tab? It'll delete all the
         | cookies when you're done.
        
       | lbriner wrote:
       | Perhaps a more helpful principle I heard a long time ago was that
       | all methods are _either_ a specific method doing a specific thing
       | (like splitting up a string) _or_ they call a series of methods
       | of the first type. When we mix the two, it becomes harder to
       | reason since type 1 is generally logically complex, so keeping
       | these small makes them testable and readable, and the logic of
       | the high level is more easily encapsulating as a series of
       | DoThis(), DoThat(), ThenDoThat() calls.
       | 
       | If I've only helped one person today, it was worth it ;-)
        
         | layer8 wrote:
         | There's a balance to be struck if most of the Do methods need a
         | common and/or interdependent set of parameters. Inlined code
         | can be clearer because you can directly see how/why those
         | parameters are used. You rarely have                 DoThis();
         | DoThat();       DoTheOtherThing();
         | 
         | Instead you usually have something like:                 x =
         | DoThis(a, b, c);       y, z = DoThat(c, x, a);       w =
         | DoTheOtherThing(a, z, x, y, b);
         | 
         | ...and on top of that have to add error handling for those
         | calls.
        
       | Krasnol wrote:
       | Your cookie banner "manage settings" thing never stops loading.
        
         | tmtvl wrote:
         | Yeah, I bypassed it with Firefox's Reader Mode, but the
         | original page slowed FF down to a crawl.
        
       | rekrsiv wrote:
       | The original code is perfectly readable until it does something
       | completely unexpected, and the human parser has to start over to
       | make sure they didn't miss anything. But unfortunately, the
       | context for that "get the last 3 parts specifically" is never
       | explained, so the entire line never makes sense. The human has to
       | think a lot to come up with an (hopefully correct) explanation
       | for the "why".
       | 
       | The solution isn't to extract every token from the expression to
       | separate lines, but to document the "why" of the unexpected
       | token. That can take many forms: a new variable _with a
       | meaningful name_ , a new function _with a meaningful name_ , or a
       | meaningful comment that warns the reader about the upcoming
       | reason for getting just the last 3 parts.
        
       | im3w1l wrote:
       | Give mysterious things room. In this case the most mysterious is
       | [-3:]. That, together with the split, should have it's own line
       | or maybe even multiple (function declaration, comment).
        
         | raldi wrote:
         | Right?! That should instead be -len("foo") or
         | -NUM_PREFIX_PARAMS
        
           | xigoi wrote:
           | That doesn't improve anything in terms of knowing why the
           | number is there.
        
             | raldi wrote:
             | len("foo") certainly does, and the global constant is a
             | great place to put a comment, if the name isn't descriptive
             | enough.
        
         | ParetoOptimal wrote:
         | > [-3:].
         | 
         | That kind of index notation really isn't mysterious if you
         | write a lot of python in my experience.
        
           | im3w1l wrote:
           | The mysterious part isn't what it does. The mysterious part
           | is _why_. Why are we taking the last three url parameters?
           | What are the meaning of those particular url parameters? Also
           | url parameters are normally used as a unordered key=value
           | dictionary, which makes it strange that we rely on a given
           | order.
        
       | [deleted]
        
       | happyweasel wrote:
       | use statically typed programming languages. Favor composition
       | over inheritance . Develop bottom-up (reusable classes) instead
       | of large-scale up-front design. SOLID principles (SRP being the
       | most important). The Bottom-up approach also favors Unittesting.
       | Code reviews, clear code formatting rules (simple editor plugins
       | do the trick). Use static code analyzers. IMHO this kind of
       | object-oriented programming leads to NEW code being written to
       | implement features and NOT old code being tampered with. Ideally,
       | the (tested) units of code (classes) have such clear
       | responsibility that you do not have to touch them once they are
       | implemented. If Super-classes begin to emerge, refactor.
        
         | ParetoOptimal wrote:
         | > Develop bottom-up (reusable classes) instead of large-scale
         | up-front design.
         | 
         | I tend to really hate the UX of bottom-up designed API's and
         | find them incoherent.
        
         | [deleted]
        
       | Waterluvian wrote:
       | I flexibly agree with the "does one thing" approach. But what a
       | "thing" is can be up to you.
       | 
       | Sometimes my one thing is "turns a Json file into an in memory
       | dictionary" which might be three operations on one line.
        
       | 3pm wrote:
       | Reminded me of 'Object Calisthenics' by Jeff Bay. Basically an
       | exercise for a toy project where you adhere to 9 rules:
       | 
       | 1. Only One Level Of Indentation PerMethod
       | 
       | 2. Don't Use The ELSE Keyword
       | 
       | 3. Wrap All Primitives And Strings
       | 
       | 4. First Class Collections
       | 
       | 5. One Dot Per Line
       | 
       | 6. Don't Abbreviate
       | 
       | 7. Keep All Entities Small
       | 
       | 8. No Classes With More Than Two InstanceVariables
       | 
       | 9. No Getters/Setters/Properties
       | 
       | https://williamdurand.fr/2013/06/03/object-calisthenics/
        
         | BlargMcLarg wrote:
         | >Wrap All Primitives And Strings
         | 
         | Gah. I've seen the other side of this, a few people far too
         | trigger happy to make FivePlusVeryLongNounVO/DTO for every
         | little thing, and it gave me some new appreciation towards
         | tuples and primitives. Sometimes you really don't want to go
         | into _another_ new file for an object type which is used in
         | only one specific place. Especially with
         | 
         | >Don't Abbreviate
         | 
         | Meaning the variable name will end up long anyway. With tuples,
         | you get deconstruction without the hassle, too.
        
           | 3pm wrote:
           | > Gah. I've seen the other side of this, a few people far too
           | trigger happy to make FivePlusVeryLongNounVO/DTO for every
           | little thing, and it gave me some new appreciation towards
           | tuples and primitives. Sometimes you really don't want to go
           | into another new file for an object type which is used in
           | only one specific place.
           | 
           | The rules are an exercise for a toy project. Like all similar
           | 'rules' they are just hints to make you think. When done with
           | the exercise, and you see a string with a social security
           | number in a production code, you may consider creating a
           | dedicated SocialSecurityNumber class. The class will
           | guarantee a well formed social security number according to
           | official rules. The class may even offer Area, Group and
           | Serial parts of the social as separate fields. The class may
           | decide to use a string or integers internally, but that would
           | never be exposed to the class consumers. All the code that
           | uses SocialSecurityNumber will not have to guess whether
           | string is valid, if it has dashes etc. The same reason you
           | use built-in types like an Integer (as oppose to a tuple of 4
           | bytes or 32 bits).
        
         | overgard wrote:
         | Oof, these all seem absurd to me.
         | 
         | > 1. Only One Level Of Indentation Per Method
         | 
         | One level of indentation just leads to an explosion of tiny
         | one-use methods with weird names, and now you can't read the
         | code linearly. You will almost certainly never reuse these tiny
         | methods, especially since you're likely consigning them to an
         | instance of a class instead of a free function, so all you've
         | done is forced people to jump around a lot.
         | 
         | > 2. Don't Use The ELSE Keyword
         | 
         | Not using the else statement just obscures the fact that
         | there's a branch in the code. Obscuring something important
         | seems to be the opposite of what you should do.
         | 
         | > 3. Wrap All Primitives And Strings
         | 
         | Ugh, that seems verbose and clunky, especially in a language
         | like Java without operator overloading. I'm all for type
         | aliases or typedef's, or, creating a class if the builtin
         | primitives don't work (I think a Money class makes sense
         | because you don't exactly want to use a float, for instance).
         | But just putting wrappers all over the place sounds grotesque.
         | 
         | > 4. First Class Collections: Any class that contains a
         | collection should contain no other member variables
         | 
         | Why even have a class then? Why not just have functions that
         | operate on a collection? It's much more generic that way, since
         | if you're using iterators or an abstract collection interface,
         | you can potentially allow the user to choose the exact data
         | structure, and you avoid the ceremony of creating a new type
         | that's again just a wrapper.
         | 
         | > 5. One Dot Per Line... Basically, the rule says that you
         | should not chain method calls.
         | 
         | This is the first one I roughly agree with, but I wouldn't
         | consider it a hard rule. Chaining .map and .filter together for
         | instance is a very common pattern.
         | 
         | > 6. Don't Abbreviate
         | 
         | min/max is just as clear as minimum and maximum. I'm not using
         | "index" in my for loop when "i" will do. "n" is perfectly well
         | understood as a count of things. Abbreviations when used
         | properly make code easier to read, not harder.
         | 
         | > 7. Keep All Entities Small... No class over 50 lines and no
         | package over 10 files
         | 
         | Ok, assuming the problem can't be simplified, all you've done
         | is now fractured all that functionality into tens/hundreds of
         | files. How is that easier to follow? Sure, there's balance in
         | all things, but I'd probably rather read a 1000 line class than
         | 20 small files split over 2 packages.
         | 
         | > 8. No Classes With More Than Two Instance Variables... I
         | thought people would yell at me while introducing this rule,
         | but it didn't happen
         | 
         | They were being polite. I'll do it for them. What the fuck?
         | 
         | The example he gives is also awful, where instead of using a
         | string for name, he makes Name a type (ugh) with FirstName and
         | LastName. Not only is that overly ceremonial, but it's _wrong_
         | , there are plenty of names from various cultures that do not
         | fit cleanly into FirstName and LastName. Also, what happens if
         | he wants to store a MiddleName? That's three instance
         | variables! Ohno! OR what if the person has like 10 middle names
         | (this shit happens). Are we going to have 5 nested data types
         | for that?
         | 
         | > 9. No Getters/Setters/Properties ... My favorite rule. It
         | could be rephrased as Tell, don't ask.
         | 
         | My brain feels like it's going to explode.
         | 
         | > It is okay to use accessors to get the state of an object, as
         | long as you don't use the result to make decisions outside the
         | object.
         | 
         | Why else would you want to get the state of an object?
         | 
         | > Any decisions based entirely upon the state of one object
         | should be made inside the object itself.
         | 
         | If your classes are 50 lines long, I guarantee you that other
         | classes will be making decisions on other objects behalf.
         | 
         | > Then again, they violate the Open/Closed Principle.
         | 
         | I think the industry is largely realizing that this is a bad
         | principle, as it implies inheritance. I think most people
         | outside the enterprise java world now realize that using
         | interfaces or free functions is largely better.
        
           | 3pm wrote:
           | Please keep in mind that these were rules that you apply to a
           | _toy_ project. You apply the rules once, blindly, even if
           | they don't make sense to you at the moment. Then, when you
           | work on a real thing, you may remember, for example, to
           | create a dedicated PhoneNumber class with strict rules (e.g.
           | E164) instead of a string that gets shuffled around with no
           | one really knowing whats inside. Or you just forget the rules
           | as a nonsense and move on.
           | 
           | You seem to be criticizing the 'rules' as if they are
           | suggested for production code. You couldn't be seriously
           | thinking someone suggest maximum-of-2-fields as some sort
           | guideline for the real world.
        
       | noncoml wrote:
       | We break everything down and then we reach one of the most
       | difficult problems in software engineering: Coming up with good
       | and short names for all these extra intermediate variables and
       | functions.
        
         | mike_hock wrote:
         | Yes, and a source file littered with tiny helper functions that
         | do very specific things and don't make any sense except in the
         | precise context in which they get called, isn't necessarily
         | more readable.
         | 
         | Here, "query_params" means "extract the last three query
         | parameters, raw (i.e. not unescaped and not broken into key-
         | value pairs)." The transformation shown makes precisely nothing
         | more readable or easy to understand. "The second argument to
         | map()" is just as easy for your brain to group into a black box
         | to be analyzed later as a call to an opaque "query_params"
         | function that you need to read the implementation of to really
         | understand what the code is actually doing.
         | 
         | Of course sometimes it's the best solution to just extract
         | local helper functions, especially if the actual function just
         | becomes too unwieldy and/or the helpers are called from more
         | than one place, but in general I try to extract things that do
         | something more general than the thing I'm extracting it from
         | and have an interface / a purpose that's easy to understand and
         | describe on its own.
         | 
         | To stay with the example, actually extracting the query
         | parameters would be a generic, extractable utility. Half-
         | extracting the last three parameters because the function I'm
         | writing needs precisely that for some reason, is a local helper
         | function, and I'd only extract it if there's a good reason,
         | certainly not to make an already trivial function no easier to
         | read.
        
           | lbriner wrote:
           | I wouldn't attack the example too much, it does seem a little
           | contrived to make the point - the very thing I don't like
           | about contriving examples!
           | 
           | It is hard to know whether the principle is valuable with
           | such a weird example. In this example there are lots of other
           | ways it could have been done more meaningfully but, again,
           | don't know if the example is real.
        
             | mike_hock wrote:
             | What doesn't generalize is that the example doesn't
             | necessarily stand for a simple one-liner, but as an example
             | it has to be trivial, of course.
             | 
             | But everything else generalizes. I took "splitting out the
             | last 3 params" to mean "doing something very specific to
             | the problem at hand" and extracting that isn't _always_
             | better.
        
           | overgard wrote:
           | Your mileage clearly varies, but I found the transformed
           | example much easier to understand. While I suspected it was
           | parsing a query string from the initial code, having that
           | stated explicitly in the variable removed the guessing. I
           | think the main problem is he just didn't go far enough, there
           | was still more to deconstruct.
           | 
           | I suppose the function to parse the query string could have
           | been better, its name isn't very descriptive, and the method
           | with which it parsed wasn't very obvious either (I'd expect
           | to get back a dict or a list of key/value tuples, not a list
           | of strings)
           | 
           | I know a lot of programmers are against comments, but I also
           | think this is exactly the kind of code where a comment is
           | handy..., the purpose of the [-3:] part wasn't obvious to me
           | at all.
        
             | aidos wrote:
             | For me that's the issue with this whole example; it kinda
             | misses the woods for the trees.                   # not
             | interesting (also, use stdlib instead)         def
             | extract_url_params(url: str) -> dict[str, str]:
             | params = url.split('?')[1].split('&')             return =
             | dict(param.split('=') for param in params)              #
             | This is the thing that needs explaining!
             | final_3_param_values =
             | list(extract_url_params(url).values())[-3:]
             | 
             | By decomposing the problem in a more natural way, you can
             | entirely direct the reader's attention to the thing that's
             | causing the dissonance. For some reason it's just the the
             | final 3 values they want (both weird requirements). Make
             | _that_ clear from the code!
        
               | philipwhiuk wrote:
               | > For some reason it's just the the final 3 values they
               | want (both weird requirements). Make that clear from the
               | code!
               | 
               | Yeh, I mean the name should really tell you what those
               | expected to be.
               | 
               | `hopefully_auth_param_values` is almost a better name
        
             | mike_hock wrote:
             | It would presumably be evident from the context in which
             | the `map` call appears what its purpose is.
        
               | overgard wrote:
               | And yet, I know python and have 15 years of professional
               | experience coding, and (in the limited time I wanted to
               | spend on it), I still don't exactly know what he was
               | trying to accomplish. So congrats if you got it, but I'm
               | sticking with my claim that it's fairly non-obvious. I
               | understand that it's pulling out the last three elements.
               | I have no idea _why_ it 's pulling out the last three
               | elements, what purpose that serves and why it's
               | desirable. If those elements were placed in a well named
               | variable, I suspect it'd give a hint as to why the code
               | is doing that. Alas.
        
         | jstimpfle wrote:
         | Typically, relatively unspecific names like "i" or "size" are
         | good enough. It's better than not naming at all and producing a
         | complicated expression tree instead. More specific names cost
         | energy, both inventing and reading them (because they are
         | typically longer). Err on the side of short and not too
         | specific.
        
           | leetcrew wrote:
           | in most situations, I would rather see a complicated
           | statement split over several lines than several simple
           | statements with vague/unhelpful variable names. if the
           | variable name itself doesn't help me understand what it
           | means, I have to remember the full expression anyway.
        
           | kevin_thibedeau wrote:
           | It depends. Go spelunking through old Unix and Gnu code from
           | the 80s and you'll see a lot of maddening usage of single and
           | double letter variables all over the place where a
           | descriptive name would make things much more readable.
        
             | Teckla wrote:
             | Or go spelunking through modern Go code.
             | 
             | Variable names that are so terse it hinders reading and
             | comprehension.
        
         | zhte415 wrote:
         | Just be consistent, whatever it is.
        
         | onion2k wrote:
         | _short names_
         | 
         | You don't really need short names. I wouldn't advocate going
         | full Java naming but trying to compress names just to save a
         | bit of typing is unnecessary. Your IDE will help you out. Just
         | learn to press tab when you've entered enough of the name
         | instead of typing the whole thing.
        
           | patrick451 wrote:
           | Short names are easier to read, because they fit on fewer
           | lines. Doubly so if the statement fits on one line.
        
             | onion2k wrote:
             | Shorter names aren't easier to read if you have several
             | similar names used in close proximity. More characters can
             | reduce the congruence and make it easier to differtiate
             | between names.
             | 
             | I agree about fitting things onto one line though. It's
             | usually better to break things up into to several short one
             | line steps rather than have a long multi line statement.
        
               | philipwhiuk wrote:
               | I tend to think length should be proportionate to
               | broadness of usage within the file/project
               | 
               | If it's used in lots of places, go for
               | MAXIMUM_MONSTER_DENSITY_SCORE_MULTIPLIER
               | 
               | If it's used in a single file/function, then
               | maxDensityMultiplier might be okay. (You can infer the
               | rest from context).
               | 
               | If it's used in a very confined section, then multiplier.
               | 
               | If it's a loop variable then, sure use m.
        
         | nicwolff wrote:
         | Crisis == opportunity tsu
         | 
         | TFA missed the point of splitting complex expressions into
         | separate lines: naming the single-use vars clearly makes the
         | whole calculation easy to follow. In the example given, nothing
         | about the one-line `map split over split of split` tells a
         | reader that it's parsing a query string - just splitting it in
         | two and naming the temp var `query_params` makes it clear.
         | 
         | Although `last_3_query_params` would be more precise, and
         | something that explains why TF you'd want that would be
         | better... tsu
        
         | mixedCase wrote:
         | It takes some deliberate practice and being able to create
         | decent contexts within your code.
         | 
         | It's not a trivial problem but it's not a hard one. It's just
         | that most people don't even try to dedicate a sliver of active
         | brain power to the task because they don't deem it worth it
         | even if they claim to agree on the importance of readability.
        
       | jstimpfle wrote:
       | I like how this article explains that "clean" must be "readable
       | for humans". However, the concerns raised are only superficial.
       | It's much more important to get the larger scale structure right.
       | I recommend drawing diagrams and explaining the architecture to
       | humans. Then again, I'm not saying overdo it, because some things
       | are hard to draw, some are hard to explain. In the end, it's
       | important to get a complete understanding of a certain module,
       | and the code should then be relatively easy to write.
       | 
       | I have learned to take a step back when I find myself having a
       | hard time to get the code "clean". Often I put that thing to rest
       | if possible, maybe for days, months, or even years. There could
       | be a simple solution that solves 80% of the problem, and that can
       | ease the pressure coming from the stakeholders. If it kind-of-
       | works and can be produced in a short time, that is much better
       | than going down a rabbit hole for months, coming out at the other
       | side (probably burnt out) with a solution you can't deploy
       | because it's too complicated.
        
         | 29athrowaway wrote:
         | What is easier to read:
         | 
         | a) 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +
         | 1 + 1 + 1 + 1 + 1
         | 
         | b) 20
         | 
         | If all the code in your project was written like a), how would
         | you feel? Does it make your job easier or harder?
         | 
         | I'll tell you how most people feel when they read code that
         | looks like a):
         | 
         | - The author didn't care about other maintainers.
         | 
         | - The author is selfish and does not have empathy for others.
         | 
         | - The author ruined my fucking day.
         | 
         | - Team members are competing by sabotaging each other's
         | productivity.
         | 
         | - If I clean this, by the time I am done, the author would have
         | pushed 10 more commits that look exactly like this and
         | eventually become my boss.
         | 
         | - The author is wasting everyone's time.
         | 
         | - The author is forcing others to volunteer to clean up after
         | them.
         | 
         | - Why does management tolerate code following the a) style? A
         | simple intervention would make it go away and my job would be
         | so much better.
         | 
         | - It's sad that everyone is too busy looking at Jira and nobody
         | cares about the actual fucking product.
         | 
         | - This is slowing everyone down and I have stuff to do.
         | 
         | - The code is error prone, one day I'll break it.
         | 
         | - Why should I contribute quality code if low quality is
         | acceptable?
         | 
         | As you can see, it objectively fucking sucks. It's draining,
         | demoralizing to read, it's frustrating, it wastes people's
         | time, it gives people the perception that nobody fucking cares
         | and the code is everyone's toilet with no trip lever.
         | 
         | And while it's "superficial", it's the surface that all
         | engineers interact with. If I spread superglue over the surface
         | of your kitchen counter and every dish and utensil in your
         | kitchen every day around lunch time, that problem will also be
         | "superficial", but it will ruin your life.
         | 
         | So, the conclusion is: Just fucking write clean code. Shitty
         | code ruins the morale of people who care, who are the people
         | that want to build great things not the ones cashing a paycheck
         | and resting and vesting.
         | 
         | You are not a full-time architect, you are not in business
         | development/marketing/finance or whatever, you are in the
         | fucking engineering department. Your contribution to the
         | business are your deliverables. The "superficial" stuff you
         | talk about is your job. Do it.
         | 
         | "Ah ah ah, you didn't say the magic word!! ah ah ah!" Don't be
         | the fucking Dennis Nedry of the team. Format your code, make it
         | readable by your team and your future self.
         | 
         | Do you want everyone to love you? Write code like this:
         | 
         | https://norvig.com/spell-correct.html
        
           | jstimpfle wrote:
           | What the f* is wrong with you?
        
             | [deleted]
        
         | Karellen wrote:
         | > Show me your flowcharts (code) and conceal your tables (data
         | structures), and I shall continue to be mystified. Show me your
         | tables (data structures), and I won't usually need your
         | flowcharts (code); they'll be obvious.
         | 
         | -- Fred Brooks, _The Mythical Man-Month_ , 1975
         | 
         | > a computer language is not just a way of getting a computer
         | to perform operations but rather that it is a novel formal
         | medium for expressing ideas about methodology. Thus, programs
         | must be written for people to read and only incidentally for
         | machines to execute.
         | 
         | -- Abelson & Sussman, _The Structure and Interpretation of
         | Computer Programs_ , 1984
        
           | sidlls wrote:
           | The first quote is an over-simplification that often does not
           | hold in practice: one _usually_ needs both. The exceptions
           | are mostly trivial programs.
        
             | gilch wrote:
             | It's the reason Excel is easier to learn than Java.
        
             | Karellen wrote:
             | Most pithy aphorisms are over-simplifications. "Want
             | cleaner code? Use the rule of six" probably is too. But
             | they can still give you a useful way of thinking about a
             | problem that you might have forgotten to consider, and can
             | give you a Platonic ideal as a useful guiding star even if
             | you know you'll never reach that sort of purity in the real
             | world.
        
         | overgard wrote:
         | I tend to find that top-down designs usually end up clunky. In
         | my experience, bottom up designs (starting with specific things
         | and creating new abstractions as they're needed) tends to
         | create simpler and more obvious designs. You also don't waste
         | time on hypotheticals, since every line you write has a
         | purpose. This blog post really nails it:
         | https://caseymuratori.com/blog_0015
         | 
         | In that context, I don't think this is superficial at all. If
         | your code is hard to read, I suspect your design is hard to
         | read too.
         | 
         | Sure, for super important architecture decisions you have to
         | make a few top-down (spatial partitioning structures, database
         | decisions, network architecture, etc.), but I think it's
         | generally better to late-bind on those decisions if you can.
        
           | jstimpfle wrote:
           | It is superficial. No amount of trying to pretty up the code
           | can fix underlying design deficiencies. That's what I said,
           | so we're not even disagreeing here :)
           | 
           | I know the semantic compression post, and I don't think it
           | makes a point for bottom-up design. I find top-down and
           | bottom-up to be quite misleading anyway. Someone told me,
           | they don't like to think of things at the "top" and the
           | "bottom". It's data transformations, maybe more like "left to
           | right".
           | 
           | If you design bottom-up, you end up with lots of artifacts
           | you never needed (and likely still missing the ones you can
           | make use of). If you design top-down, you end up with lots of
           | _code_ you don 't need. (this is where semantic compression
           | comes in, in my understanding).
           | 
           | I suspect that if you like to think bottom-up, maybe that's
           | because you like it more at the bottom (you are a low-level
           | type of guy, or like to make libraries). If you like to think
           | top-down, maybe you like it more at the top.
           | 
           | I like the semantic compression term because it reduces the
           | act of design to the essentials, without introducing fluff
           | terms or opinions. I find myself doing this compression no
           | matter what kind of code I'm writing.
        
             | overgard wrote:
             | Well, you might know _of_ that blog post, but might want to
             | reread it, I don 't think it says what you think it says.
             | The example he steps through is very clearly bottom up. He
             | even outright says it:
             | 
             |  _> This is a very bottom-up programming methodology, a
             | pseudo-variant of which has recently gained the monicker
             | "refactoring", even though that is a ridiculous term for a
             | number of reasons that are not worth belaboring at the
             | moment. ..._
             | 
             |  _> Like a good compressor, I don't reuse anything until I
             | have at least two instances of it occurring. Many
             | programmers don't understand how important this is, and try
             | to write "reusable" code right off the bat, but that is
             | probably one of the biggest mistakes you can make. My
             | mantra is, "make your code usable before you try to make it
             | reusable"._
             | 
             |  _> I always begin by just typing out exactly what I want
             | to happen in each specific case, without any regard to
             | "correctness" or "abstraction" or any other buzzword, and I
             | get that working. Then, when I find myself doing the same
             | thing a second time somewhere else, that is when I pull out
             | the reusable portion and share it, effectively
             | "compressing" the code. I like "compress" better as an
             | analogy, because it means something useful, as opposed to
             | the often-used "abstracting", which doesn't really imply
             | anything useful. Who cares if code is abstract?_
             | 
             | And I don't understand how you'd end up with artifacts you
             | don't need with that approach. Do you mean intermediate
             | steps you refactor? In that case I suppose so, but that's
             | an important part of the process, not a waste. You're never
             | going to get the initial design exactly perfect so you need
             | to iterate on it.
        
               | jstimpfle wrote:
               | What he means there by saying "bottom-up", as far as I
               | can tell, is that you "compress as you go", i.e. it is
               | bottom-up compression, in a similar way to how you can
               | also write bottom-up parsers.
               | 
               | It's not stating that you should start by creating lots
               | of little artifacts before you have a great plan (bottom-
               | up design) nor that you should start implementing the
               | "non-functional requirements" (another confusing term) on
               | a high level before you know the platform and other
               | functional requirements, which is top-down design.
               | 
               | So that's perhaps a justification why I said earlier that
               | these terms are misleading :-)
               | 
               | As to the other thing that I said, that bottom-up tends
               | to create things you don't actually need, it's almost
               | immediately following the definition of bottom-up. You
               | can even find this on the Wikipedia page, which states
               | that in practice, software-development typically uses a
               | combination of bottom-up and top-down for that reason.
               | 
               | (In other words, always strive to know as much as
               | possible about requirements both at the "top" as well as
               | at the "bottom", as early as possible. Or the risk is
               | that the design won't fit some part of the requirements
               | and large parts need to be redone).
               | 
               | I still don't see the connection with what I originally
               | wrote, in any case.
        
               | overgard wrote:
               | > It's not stating that you should start by creating lots
               | of little artifacts before you have a great plan (bottom-
               | up design)
               | 
               | Look, you can disagree with his conclusion, but that is
               | exactly what he's saying. The post is long but it's very
               | clear and methodical about how he's suggesting code
               | should be designed and written. I happen to agree with
               | him.
        
               | [deleted]
        
               | jstimpfle wrote:
               | From the post: "if the reusable code is already suitable,
               | you just use it, but if it's not, you decide whether or
               | not you should modify how it works, or whether you should
               | introduce a new layer on top of or underneath it."
               | 
               | I'm having a really hard time interpreting this as "you
               | should design / program bottom-up".
               | 
               | If you go to Wikipedia, bottom-up design is roughly
               | defined as taking the existing stuff and building new
               | things on top. I still fail to see where the compression
               | part comes in here.
               | 
               | Neither of the two directions will look back at the mess
               | they've made and compress it. They're just ways to
               | develop the system by adding to it incrementally.
               | 
               | But then, these terms are generally poorly understood (as
               | evident from various discussions I've had) and I don't
               | want to claim to have the perfect understanding either.
               | Nor do I want to be "Look"'ed any more. So, whatever.
        
               | overgard wrote:
               | Honestly mate, I just don't really understand the point
               | you're trying to make. Originally it was that it doesn't
               | matter if the line-by-line code is understandable because
               | the high level design is what's important. I think the
               | line by line clarity is important, because if that's
               | confusing in my experience the design tends to be muddled
               | also. But then you have these definitions of bottom up
               | and top down that are very confusing and seem to exist
               | mostly to you. I'll simplify what my definition is:
               | bottom up programming means you write the code without
               | any abstractions first, and then you generate the
               | abstractions as you find commonalities and repeated
               | patterns. I find this to be superior to the other
               | approach, of generating abstractions first and then
               | filling in the code. That is my opinion, I know other's
               | believe differently. And "look" is not an insult.
        
       | upsideDownBlue wrote:
       | I enjoyed the article and agreed that working memory places a
       | fundamental limit on the intelligibility of otherwise equivalent
       | pieces of code. As a former psychologist with experience of
       | memory research (though not quite this area), it might be useful
       | to others if I add that:
       | 
       | - The size of the short-term store is normally said to be 7 plus
       | or minus 2 (the 'magic' number 7)
       | 
       | - The Working Memory model has somewhat overtaken the 'short
       | term' memory model, and it is unusual to see them being presented
       | alongside each other like this (though 'short term memory'
       | remains a useful, good-enough metaphor for explaining certain key
       | aspects of memory)
       | 
       | - Chunking is typically viewed as a memory-supported division of
       | stimuli (what you're reading, hearing etc.) into meaningful units
       | based on LTM memory representations. A good example is a chess
       | expert 'chunking' the layout of a chess board with many pieces in
       | perhaps one or two units (e.g. 'It's the mid game configuration
       | of [famous players] in [famous game], except the king's position
       | is different'). We would expect more expert programmers to
       | 'chunk' increasingly large units, I think (e.g. 'Oh, this is just
       | the [famous sorting algorithm]').
       | 
       | - A single chunk is usually considered to take up a 'slot' in
       | short term memory
       | 
       | If anyone wants papers/sources for the above, let me know.
        
         | gilch wrote:
         | This means it should be the rule of five. We can't count on
         | everyone having full capacity all the time.
        
         | ethbr0 wrote:
         | Not high priority, but I'd love any references or names / key
         | words I could look into it with.
         | 
         | I'm traditional wide comp sci by academic training, but spend
         | my day job as a low-code enabler for non-programmers with
         | varied backgrounds.
         | 
         | The working memory model explains and fits well with what I see
         | them get and struggle with in day to day work, and I'd welcome
         | references I could use to optimize my approach.
        
           | pramodbiligiri wrote:
           | Check out Anders Ericsson's book on Deliberate Practice or
           | Barbara Oakley's A Mind for Numbers.
        
       | kouteiheika wrote:
       | I don't necessarily agree with the step of putting the code in a
       | separate function; that often works, but just as often makes it
       | so that the code can't be read top-to-bottom anymore which hurts
       | readability.
       | 
       | In this case there's, I think, a better alternative; the
       | equivalent-ish code in Ruby for the example code here would be
       | something like this:                  values = s
       | .partition('?')[-1]           .split('&')           .map {
       | |key_value| key_value.partition('=')[-1] }
       | 
       | You can write these nice functional pipelines where you just read
       | the code top-to-bottom and see step-by-step what is being done to
       | the data on each line. You don't have to jump up-and-down around
       | the code when reading it, and you don't have to keep too much
       | context in your head when reading it.
       | 
       | This is one of the reasons why I vastly prefer Ruby over Python
       | for most data processing tasks. I wish more languages would
       | support this style of programming.
        
         | KerrAvon wrote:
         | This style is increasingly common in Swift, especially for
         | Combine pipelines and SwiftUI modifiers.
        
         | WalterBright wrote:
         | D does too:                   import std.algorithm, std.array,
         | std.stdio;         // Print sorted lines of a file.
         | void main()         {          auto sortedLines =
         | File("file.txt")   // Open for reading
         | .byLineCopy()      // Read persistent lines
         | .array()           // into an array
         | .sort();           // then sort them          foreach (line;
         | sortedLines)              writeln(line);         }
        
         | jonnycomputer wrote:
         | I like this style too, though it can make debugging trickier.
        
           | wizofaus wrote:
           | It's been my feeling for a while that debugger technology
           | hasn't really caught up with newer styles of coding (despite
           | them having been around for over a decade). Only being able
           | to set a breakpoint at a line-level or watch values that are
           | assigned to a named variable is incredibly limiting.
        
         | [deleted]
        
         | layer8 wrote:
         | One issue with functional pipelines is that the reader has to
         | keep track of what the types and the data are on each line.
         | It's fine for 2-3 lines, but it can get non-obvious quite
         | quickly. Assigning intermediate points to named variables can
         | be appropriate, or indeed factoring portions of the pipeline
         | out into separate functions.
        
           | mdaniel wrote:
           | That may be true for less capable editors, but IntelliJ (and
           | therefore Rubymine for the cited code block) annotates the
           | stream type variable when it can prove what it is:
           | https://www.jetbrains.com/help/ruby/viewing-reference-
           | inform... regrettably doesn't show an example of what I'm
           | talking about                   .map { | *String* key_value |
           | key_value.partition... }
           | 
           | where _String_ shows up in light grey text indicating that IJ
           | knows `key_value` is a String
        
             | layer8 wrote:
             | That's nice, but it just highlights that the coding style
             | isn't sufficiently intelligible by itself. We can't and
             | shouldn't rely on a static analyzer to be active to make
             | code intelligible everywhere code is displayed (pull
             | requests, diffs, ...).
             | 
             | Doing so favors writing code over reading code, which is
             | generally the wrong bias. Editor support is appropriate and
             | useful to help writing readable code, but it's bad for
             | editor support to become a prerequisite for existing code
             | to be comprehensible.
        
               | _nalply wrote:
               | One way I try to solve that problem in Rust: use repeated
               | `let thing = simple.invocation();`. No need to rename the
               | variable `thing` if not neccessary. Rust allows
               | redefining a variable with the same name. Additionally I
               | sometimes give the thing a type which makes it even more
               | explicit.
               | 
               | Something like this:                   let thing: Thing =
               | source.prepare();         let thing =
               | thing.make_iterator();         let (first, second) =
               | thing.split();         let first = first.filter();
               | let second = second.reverse();         let items:
               | Vec<Item> = first.join(second);
               | 
               | Just to give an idea how to make a complicated ritual
               | explicit and simple.
               | 
               | This simplifies debugging too. You can set a breakpoint
               | and view the variables or you can use println!.
        
               | ParetoOptimal wrote:
               | > editor support should not become a prerequisite for
               | existing code to be comprehensible.
               | 
               | I don't know... i'm frequently torn deciding between
               | whether i think this is true or not.
               | 
               | On another note, readability of code is less about coding
               | style and more about familiarity.
        
               | twblalock wrote:
               | > That's nice, but it just highlights that the coding
               | style isn't sufficiently intelligible by itself. We can't
               | and shouldn't rely on a static analyzer to be active to
               | make code intelligible everywhere code is displayed (pull
               | requests, diffs, ...).
               | 
               | Yes, we absolutely should rely on that.
               | 
               | Why would programmers, who are basically tool-builders,
               | reject tools that help us write and analyze our programs?
               | This is not the 1970s, and bare-bones text editors are
               | not the only option. Why should we, who build tools for
               | others, restrict ourselves to a self-imposed unreasonable
               | simplicity?
               | 
               | You are free to use Vi or whatever, but don't complain
               | when everyone else uses better tools to improve their
               | productivity, and if the code they write is better suited
               | for those tools that is perfectly fine.
        
               | layer8 wrote:
               | I'm all for IDEs for _writing_ software, but not as a
               | prerequisite for _reading_ source code, because the
               | latter happens in all kinds of different contexts. E.g. a
               | software like GitLab shouldn't require an integrated
               | static analyzer for any or all programming languages.
               | That would only increase the barrier to entry for all
               | software that happens to display source code. If source
               | code can't be read and understood as-is anymore, that
               | just impedes interoperability and universality of
               | tooling.
        
               | twblalock wrote:
               | Surely you must understand that the functionality
               | provided by the tools will impact how the code is
               | written, tested, reviewed, and deployed.
               | 
               | There is no ideal world where we can use tools to write
               | code and it would be perfectly readable for people who
               | don't use those tools.
               | 
               | Embrace the modern ecosystem, or build something better,
               | or take a time machine back to the 1970s where there were
               | no IDEs, source control, or CI/CD. Do you really think
               | that would be better?
        
               | layer8 wrote:
               | I disagree, because it reduces the freedom of choice and
               | variety of tools available for software development, and
               | increases the dependency on bespoke tooling. Using a more
               | sophisticated word processor shouldn't reduce the
               | readability of the text produced. If anything, IDE
               | features should be helping to _increase_ the readability
               | of the resulting source code, instead of promoting a
               | coding style that decreases it.
               | 
               | This is about decoupling. The same way one should be able
               | to freely pick a VCS and CI/CD system of choice, one
               | should also be free to pick the IDE, or any other system
               | concerned with source code, of choice. Those should
               | remain loosely coupled, and should each be easy to
               | replace. The more requirements you impose on how source
               | code has to be marked up for intelligible display, the
               | more you restrict the choice of tools it can usefully
               | work with, and the more laborious it becomes to create
               | new adequate tools in that field.
        
         | [deleted]
        
         | khendron wrote:
         | One of the advantages of functions is that a well-named
         | function is self-documenting. If you can take a bunch of lines
         | and wrap them in a function whose name summarizes exactly what
         | it does, then you have improved readability in my opinion. In
         | this example, I don't really need to know the details of how
         | the query parameters are extracted. I just want to know I've
         | got them.
        
           | patrick451 wrote:
           | Yeah, but these little 5 line functions with one caller whose
           | only purpose is to avoid a 50 line function somewhere else
           | are almost never named well. At best, I can infer about 20%
           | of what is going on without stepping into all those little
           | helpers.
        
           | marginalia_nu wrote:
           | Emphasis on well-named. Naming things is hard.
           | 
           | Maybe not relevant in simple toy examples, but you don't have
           | to look far until to find a function that isn't so easy to
           | name.
        
             | nmz wrote:
             | its also a level of indirection... It's sad but functions
             | often slow performance down. but this depends on the
             | language.
        
               | marginalia_nu wrote:
               | Can't be many languages where that matters in 2022. Auto-
               | inlining of short functions was a thing in the 1990s.
        
             | lamontcg wrote:
             | Emphasis also on "bunch of lines".
             | 
             | Usually when I see one-line functions they cost more in
             | short term memory than they save. There's good exceptions
             | to this with something like a horrible conditional that is
             | necessarily horrible so you can bust it out into a method
             | with a doc comment about the horribleness, its history, and
             | its subtle madness. But developers that just break out one-
             | line methods all over the place wind up writing hard to
             | understand code.
        
             | dqpb wrote:
             | > Naming things is hard
             | 
             | Naming is hard unless you have a sensible concept
             | hierarchy, and then naming is easy.
             | 
             | Concept hierarchies are hard, unless you have a sensible
             | system model, and then concept hierarchies are easy.
             | 
             | System models are hard, unless you have domain expertise,
             | and then system models are easy.
        
               | marginalia_nu wrote:
               | Not everything is a neat concept though.
               | 
               | Look at the Porter stemming algorithm for example. It
               | simply does not decompose into something that is easy to
               | put names on.
        
               | dqpb wrote:
               | > A consonant will be denoted by c, a vowel by v. A list
               | ccc... of length greater than 0 will be denoted by C, and
               | a list vvv... of length greater than 0 will be denoted by
               | V. Any word, or part of a word, therefore has one of the
               | four forms: CVCV ... C CVCV ... V VCVC ... C VCVC ... V
               | 
               | Looks fine to me.
        
               | djbusby wrote:
               | Domain expertise is hard.
        
         | leobg wrote:
         | Any way to do that in Python? Basically an anonymous function
         | across multiple lines, which can be collapsed in the IDE view?
        
           | gilch wrote:
           | Python's lambdas can have as many lines as you want. Just
           | wrap parens around it. Hissp uses this form as a compilation
           | target. Its REPL shows the Python compilation. Play around
           | with it til you get it: https://github.com/gilch/hissp
        
           | vorticalbox wrote:
           | Not exactly the same but in python you can use pipe to create
           | pipelines
           | 
           | https://pypi.org/project/pipe/
        
         | e_i_pi_2 wrote:
         | Definitely a good point, refactoring is great and has a bunch
         | of benefits but can also be overdone and create problems. I've
         | heard/read Sandi Metz talk about this as "The Wrong
         | Abstraction"[1].
         | 
         | The basic argument is that any time you do an extract refactor
         | you're creating a new layer of abstraction that the next reader
         | will have to learn and understand. This can also get worse over
         | time as the abstractions drift away from their original
         | purpose.
         | 
         | The solution she provides is to be okay with a little bit of
         | duplication, then as patterns naturally arise in the codebase
         | you can refactor when you know a few use cases and can clearly
         | define the concept.
         | 
         | [1]: https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction
        
           | chrisweekly wrote:
           | Yes! IME (24y and counting in the profession) devs reach too
           | quickly for DRY while neglecting its counterbalancing
           | principle: AHA (Avoid Hasty Abstractions).
        
             | wizofaus wrote:
             | I've seen this complaint before and it doesn't really align
             | with my experience of frequently seeing literal duplication
             | of code where no extra abstraction was required to avoid it
             | - just selecting the code in question and factoring it out
             | into a function, which, providing you named it sensibly,
             | would also make the code easier to understand and debug. I
             | could count on one hand the number of times I've seen code
             | where repeating logic would make more sense than any extra
             | levels of abstraction created to avoid it.
        
               | lamontcg wrote:
               | The point isn't to maintain the repeated code forever.
               | The point is to generate the repeated code and (maybe)
               | live with it for a bit until you understand what the
               | right abstraction is and then remove the duplication with
               | the correct abstraction.
               | 
               | I've lost track of the number of times that I've taken
               | badly abstracted code, reintroduced all the duplication,
               | then refactored to remove the duplication with an
               | entirely different abstraction and the code was many
               | times easier to read.
               | 
               | And have you lived in a codebase for many years, where
               | you've never come back to a method that you extracted and
               | realized that you named it wrong? Or the requirements of
               | the code grew and the duplication wasn't really at the
               | level that you picked and you wind up with a method that
               | grows all kinds of horrible options to change its
               | internal behavior?
        
         | davnicwil wrote:
         | > the code can't be read top-to-bottom
         | 
         | The idea of the technique is to split out code at a different
         | level of abstraction with a clear name communicating what it
         | does, while hiding the details of the how, because you don't
         | need to care about that detail at all to fully grok the code in
         | the calling function.
         | 
         | Where this breaks down is when the code you're trying to split
         | out is _not_ at a different level of abstraction, and how it
         | works _is_ meaningful to the surrounding code in the calling
         | function.
         | 
         | So I think the issue you are seeing isn't with the technique,
         | it's with the technique being misapplied. I think this is
         | likely the only difference between when it 'often works' and
         | 'just as often doesn't' in the code you're working in :-)
        
           | wpietri wrote:
           | This is my experience too. I love hiding detail for
           | readability, but you have to hide the right details!
           | 
           | It's also definitely related to testability for me. If I'm
           | pulling out the right details, then I'll often get a nice
           | cluster of tests that pin down the higher-level concept in a
           | way where it's both the production and test code that gets
           | more readable.
        
           | lamontcg wrote:
           | Each function becomes something new that needs to stick in
           | your brain.
           | 
           | Someone that applies "MORF" to their code winds up nearly
           | inventing their own language in the file that they're
           | writing. All that takes up more memory when you're reading
           | their code, because due to leaky abstractions the actual
           | implementation of whatever the function name that you replace
           | it with is often important.
           | 
           | I have an actual track record of taking code that someone had
           | MORF'd to hell and rewriting it, and making it about 40%
           | shorter, with much fewer concepts to process.
           | 
           | Inventing a term like "MORF" is probably illustrative of the
           | problem itself. Without looking at the blog post what exactly
           | was that acronym again? That is just one more thing for you
           | to try to memorize. The author is riffing on things like
           | "DRY" and "YAGNI" that are well-known, but it isn't really
           | helping with readability when you lift it out of that
           | context.
        
             | usrusr wrote:
             | I agree, one of my favorite ide functions is the inline
             | button. Because all too often that supposedly clear name
             | turns out to be not clear at all.
             | 
             | Perhaps it would not be quite as bad if there was a clear
             | distinction between "this is a function for reuse" and
             | "this is a function that's naturally just elaborate code
             | folding", but that does not really exist. Just stick to
             | single-assignment variables and keep their scopes short and
             | enjoy the good parts of sequential code.
        
           | shellback3 wrote:
           | I agree, my thoughts were along this line especially when I
           | noticed the 'magic' number -3.
        
         | [deleted]
        
         | tomlin wrote:
         | Ruby maps are so ugly.                 .map { |key_value|
         | key_value.partition('=')[-1] }
         | 
         | Reading this literally makes me sick to my stomach. Language
         | design is much more important than language popularity,
         | although it will be popularity that wins. (Yay downvotes for
         | pointing out things everyone can see - highschool dynamics)
        
           | samtheprogram wrote:
           | Just because it's a different syntax than you're used to
           | reading in another language doesn't make it ugly. If you're
           | used to reading it and work in the language regularly, it
           | actually looks quite clean.
           | 
           | This sounds like a Windows user who can't stand macOS because
           | they don't know where anything is.
           | 
           | Your post downvote edit assumes your opinion here is
           | objective. It isn't.
        
         | somehnguy wrote:
         | Looks similar to Streams in Java. We try to use that style
         | where appropriate as it is much more readable and compact than
         | imperative style imo.
        
         | xigoi wrote:
         | Nim, D, VimScript and other languages have "uniform function
         | call syntax", which allows you to chain arbitrary functions
         | like this (not just the ones that the author decided to declare
         | as methods, like in Ruby and other class-oriented languages).
        
           | irq-1 wrote:
           | I wonder how much of Go's simplicity comes from having
           | "package.function" syntax that doesn't allow you to put too
           | many functions in a single line? People complain about Java
           | because of the length of the names. Do they complain about to
           | many functions in line? Maybe with ( ? : ) Lisp has ))))))
           | which seems like a stupid complaint, but maybe that's tied to
           | people having to remember too much?
        
       | aaronbrethorst wrote:
       | My opinion is that maintainable code is written first for reading
       | by humans and second for executing by computers.
       | 
       | Unless I'm writing throwaway prototype code (famous last words,
       | lol), I try to write code such that I will be able to figure out
       | what my intention was 6-18 months from now when I'm staring at a
       | piece of code in a panic trying to debug a production issue.
       | 
       | That doesn't mean I'm going to get it right when I write this
       | code. Instead, I'll be able to better ascertain what my
       | assumptions were, how they fell apart in practice, and what a
       | minimal, correct fix that doesn't make things worse might be.
       | 
       | Edit: Incidentally, this also applies to my commit messages. I'm
       | writing them primarily for my future self so that I can figure
       | out WHY I made a change, not WHAT the change was.
        
         | [deleted]
        
         | Karellen wrote:
         | > My opinion is that...
         | 
         | You make it sound like you came up with that all by yourself
        
       | didibus wrote:
       | What the author is missing is that easy to read/reason/understand
       | about is within the context of making a change to the code to fix
       | a bug, add a feature or make some non-functional improvement to
       | it.
       | 
       | This is what most of the "easy to read" articles forget.
       | 
       | Show me why it is easier to fix a bug, add a feature or make a
       | non-functional improvement to the code with their style than
       | without.
       | 
       | For example, if you've extracted something into its own function,
       | are you then sharing this function and using it in other places
       | as well? If you then change the body of that function, are you
       | now possibly breaking other parts of the code that relied on its
       | old behavior?
       | 
       | If you've introduced a local mutable variable in between two
       | lines, are you then mutating that variable prior/later? Is the
       | query_params different at the end of the function then in the
       | middle? Can you safely use it again?
       | 
       | How easily can you now introduce new behavior before, in the
       | middle, after, and anywhere in-between?
       | 
       | When you modify the behavior to fix a bug, add a feature or make
       | a non-functional improvement, is it an isolated change? How many
       | tests break? Did it require major refactoring to make or very few
       | things had to change? How easy was it to add a test for your new
       | behavior? Was it easy to find the most appropriate place in the
       | code to make the change? Etc.
       | 
       | Sure sometimes maybe you just read code for the fun of
       | understanding what it does, but almost always in practice when
       | you're working on a code base, you only care to understand and
       | reason about the code because you're looking to deliver that next
       | sprint task that involves changing something about it.
       | 
       | I wish more people focused on "easy to change/modify" then simply
       | on "easy to read/understand".
        
         | edgyquant wrote:
         | If you are unit testing you should not have to worry about
         | tweaking a function and it breaking everywhere else.
        
           | Supermancho wrote:
           | > If you are unit testing you should not have to worry about
           | tweaking a function and it breaking everywhere else.
           | 
           | "should" is a word loaded with authority.
           | 
           | Why?
           | 
           | If you believe a unit tests is for turning an impure function
           | into a pure function (so you can just test what it's doing
           | and no other effects), then in many cases tweaking will break
           | existing unit tests. If the function exists, it's assumed
           | it's used by more than the tests for it. Changing the
           | signature or even the internal dependencies necessarily
           | breaks the known contracts with other units.
        
           | sidlls wrote:
           | Unless the test cases are incomplete. Or the CI jobs are
           | configured to run tests independently and off-cadence so that
           | code that would break is tested after a merge. Or the tests
           | have a bug in them. And so on.
        
       | notjustanymike wrote:
       | Engineers would benefit from talking to designers more often. The
       | rule of 5 +/- 2 has been around in UX design forever. When you
       | write code for others you're designing a human interface for
       | solving a problem.
        
       | davesque wrote:
       | I'm sure there are specific programs that would benefit from a
       | treatment from these rules. However, there's one thing pretty
       | fundamental to this article that I have a hard time agreeing
       | with. And that is the notion that there are these three different
       | memory types, two of which can only store "4 to 6" things.
       | 
       | I'm inclined to believe that there are probably many gradations
       | of long vs. short term memory in the structure of the brain. In
       | fact, I bet the gradations even vary by topic and of course
       | depend on what sorts of tasks a person is accustomed to
       | performing from day to day.
       | 
       | I imagine that the "4 to 6" figure fell out of a study that
       | aggregated a large amount of data collected across subjects and
       | that the figure itself can't capture much of the nuance or even
       | the nuance of cohorts.
       | 
       | In other words, it may very well be that a large percentage of
       | people who work professionally as software developers are capable
       | of keeping more than 4 to 6 "facts" about code they're looking at
       | in their head. But that they would also appear to have the same
       | capacity as random people when it comes to arbitrary facts that
       | one would be asked to memorize in a psychological study.
        
       | hedora wrote:
       | I think this article is missing the forest for the trees.
       | 
       | I've found that dividing software into layers, and making sure
       | that each file relies on the same set of invariants from its
       | dependencies, and also maintains a (different) consistent set of
       | invariants for its callers works much better.
       | 
       | For instance, I'd prefer a function that takes a string and
       | confirms it is a valid URL.
       | 
       | That would delegate to URL character esacaping logic and DNS
       | validation. (Are & or ? valid DNS name characters? Will they be
       | in the future? I neither know nor care.)
       | 
       | On top of that, there would be a parser for key=value config file
       | lines.
       | 
       | Then, the example in the article becomes something like:
       | 
       | keyvalue = parseConfLine(input)
       | 
       | URL(keyvalue.value).params[-3]
       | 
       | Plus a few more lines to confirm key is as expected and that
       | value has enough query parameters.
       | 
       | Alternatively, I'd use a perl oneliner with a regexp. I see no
       | purpose for code that lands in the middle ground between these
       | extremes.
        
       | foolfoolz wrote:
       | the only known metric for code complexity is as number of lines
       | grows complexity grows
        
         | marginalia_nu wrote:
         | I'll just leave this here:
         | 
         | https://github.com/KxSystems/kdb/blob/master/c/c/odbc.c
        
           | gilch wrote:
           | And that's... bad? Culture shock, sure, but this looks like
           | fairly clean APL-style C to me. I would have wrapped some of
           | those lines though.
        
             | IshKebab wrote:
             | Of course it's bad. Is that a serious question?
        
               | gilch wrote:
               | No, it was rhetorical, because it's obviously (to an APL-
               | family programmer), _not_ bad!
               | 
               | Your cultural prejudice is showing. There are good
               | reasons APL is written the way it is, and this example is
               | simply bringing those benefits to C by writing it in the
               | dense APL style. There are other APL derivatives, like
               | J[1] that are written in C the same way. These projects
               | are well-maintained. They aren't collapsing under a load
               | of technical debt. The style _works_. To them, it 's
               | clean code.
               | 
               | [1]: https://github.com/jsoftware/jsource
        
             | marginalia_nu wrote:
             | If the thesis is that fewer lines = less complex, APL-style
             | C is the blackest swan I ever saw flying past me.
             | 
             | Is it bad? Apparently it's possible to write software in
             | this style, so it can't be _that_ bad, but it sure takes
             | some getting used to.
        
           | newaccount2021 wrote:
        
         | joshuacc wrote:
         | I'm not sure what you're trying to say, but that's not true at
         | all. There are other metrics for code complexity, including
         | fairly simple but useful ones like number of logical branches.
        
         | f1shy wrote:
         | Irony, right?
        
       | d_burfoot wrote:
       | Split Into Multiples Lines has a real problem, which appears in
       | the example code.
       | 
       | Let say you have a long code block that includes the revised
       | snippet:
       | 
       | > query_params = s.split('?')[1].split('&')[-3:]
       | 
       | > mylist = map(lambda x: x.split('=')[1], query_params)
       | 
       | > ...
       | 
       | > ...
       | 
       | > (some more complex transformations, that only depends on
       | mylist)
       | 
       | When you're reading the later stages of the code, you still have
       | to maintain a memory of what "query_params" does, even though
       | it's no longer relevant. That actually increases the burden on
       | your working memory. The one-liner is more complex to understand
       | initially, but it self-documents that the only info that is
       | relevant to the downstream is the result of the map(...).
       | 
       | In general, the more variables that are declared in a code block,
       | the more effort it is to understand, and the effect is probably
       | superlinear with the number of variables. I'd say if you have to
       | declare more than 5-6 variables, you should split into a separate
       | function.
        
       | twblalock wrote:
       | The "bad" Python code in that example is perfectly fine. I'm not
       | a Python programmer but I can read Python a little bit, and the
       | example uses basic programing concepts like string splitting and
       | array ranges.
       | 
       | If you don't understand that, multiple smaller lines won't help
       | you, because you just don't know what you are doing.
       | 
       | In addition, that code example is easily testable. Testability is
       | more important than readability in modern programs that follow
       | modern CI/CD principles -- and the readability is not really that
       | bad either. Also, modern debuggers don't have issues with
       | nested/lambda statements like these.
       | 
       | If the article's author had a legitimate bone to pick, they would
       | have better examples.
        
         | overgard wrote:
         | The question isn't if you can figure it out, but how long does
         | it take you? The simple version might take me 2 seconds to
         | read. The original version might take me 10 to 15 seconds.
         | Multiply that out over a day and you're hurting quite a bit.
        
         | macintux wrote:
         | > In addition, that code example is easily testable.
         | 
         | I'm skeptical, because typically a line like that is embedded
         | in the middle of a larger function.
         | 
         | Extracting the logic into a dedicated, pure function helps with
         | testing.
        
       | AtlasBarfed wrote:
       | The issue is that short code lines increases the length of code
       | aka wastes vertical screen reasl estate aka visible code, so
       | you're overburdened short term memory has to context switch to
       | scroll.
       | 
       | "Simple, put code in small methods"
       | 
       | Oh great, now I do a nav jump or a string search as a context
       | switch rather than scroll.
       | 
       | Comments? increase vertical screen pollution.
       | 
       | Proper chunking is hard.
       | 
       | Maybe APL was right.
        
       | gilch wrote:
       | The article starts with some reasonable premises, but the
       | conclusion does not follow.
       | 
       | I think most APL programmers would disagree with this take. Dense
       | code has real advantages, and naming everything has real costs
       | that are hard to see. There's nothing magic about a "line" that
       | suddenly allows for chunking. You have to build a parse tree in
       | your head in any case.
       | 
       | I'm reminded of Doug McIlroy's challenge to Knuth.[1] It's worth
       | a read. Would you rather have 6 lines of dense shell, or 10 pages
       | of Faberge egg? I'll take the shell, thanks.
       | 
       | Look at the source code for J (an APL derivative)[2]. It's
       | written in C, but that C was written in APL style by APL
       | programmers. Lines leverage macros and 1-2 character names,
       | making them extremely dense. Some files have a comment on nearly
       | every line. For an average C programmer, this code looks
       | absolutely insane. But it's _not_. The J devs find this perfectly
       | readable and maintainable. It 's clean code! If written with the
       | typical C idioms, it could easily be 10x as long, and therefore
       | _harder_ to maintain. Your first impression is a snap judgement
       | due to a difference of culture. You can learn to read this style
       | with practice. Whatever your current style, that took practice
       | too.
       | 
       | [1]: http://www.leancrew.com/all-this/2011/12/more-shell-less-
       | egg...
       | 
       | [2]: https://github.com/jsoftware/jsource
        
         | overgard wrote:
         | I think the comparative rarity of APL compared to every other
         | programming language in existence says a lot. Even if I were an
         | expert in APL, I can't think of a single place where I could
         | get a job writing it.
        
           | gilch wrote:
           | I think language popularity in industry mostly comes down to
           | path dependence[1]. It doesn't say as much as you seem to
           | think.
           | 
           | A few approaches got lucky in the rapid inflationary period
           | of the personal computer revolution (C), and the advent of
           | the Web (Javascript), and became deeply entrenched in
           | industry, while superior alternatives that had been known for
           | decades missed the boat. Industry languages still haven't
           | caught up to where Lisp, Prolog, Smalltalk, and APL were in
           | the 1970's, but they are clearly (if slowly) trending in that
           | direction.
           | 
           | APL and derivatives are still used extensively in finance, a
           | highly competitive field, to say the least. That's where you
           | find the jobs.
           | 
           | [1]: https://en.wikipedia.org/wiki/Path_dependence
        
           | ParetoOptimal wrote:
           | > I think the comparative rarity of APL compared to every
           | other programming language in existence says a lot
           | 
           | That's just the whole "popularity means it must be good"
           | argument, which I disagree with.
        
             | overgard wrote:
             | I'm not saying popularity = good, what I'm saying is that
             | most people aren't smart or patient enough to use something
             | like APL. Most people have a limit to the amount of density
             | they're willing to deal with.
        
               | ParetoOptimal wrote:
               | > Most people have a limit to the amount of density
               | they're willing to deal with.
               | 
               | Why? I believe if you started teaching someone APL who'd
               | never programmed before you'd never hear "this code is
               | too dense!".
        
               | overgard wrote:
               | Well, I can only hypothesize, but APL is a lot like math,
               | visually anyway. Most people are very intimidated by
               | math.
               | 
               | Math tends to be written for subject matter experts. If
               | I'm reading a paper about graphics, I know that th is
               | "theta" and likely means "angle in radians". If I'm not a
               | subject matter expert, it means nothing. Of course they
               | can write at the top "let th = light incident angle in
               | radians", but I still need to hold that definition in my
               | head while I'm trying to understand the rest of it. If I
               | simultaneously need to remember [?] and e and D, well, I
               | can do that if I've seen those symbols used in similar
               | ways previously, but it's certainly going to increase the
               | amount of things I need to hold in my head if it's
               | something novel. And now I have to memorize what those
               | things mean, and I suck at memorizing personally.
               | 
               | In contrast, most mainstream programming languages you'd
               | just write "angle_rads" or some variant. It's a little
               | longer, but I don't need to remember nearly as much
               | because it's just plain english formatted in a way that
               | at least resembles sentences I might read. I just think
               | APL is too unfamiliar for it to be easy to learn, and so
               | the only people that are going to learn it are those that
               | are very intellectually curious. That's a good thing, but
               | if you're talking popularity? Well, I think we've seen
               | it, and it's not just path dependence.
        
       | overgard wrote:
       | This really resonates with me. I remember when I started
       | programming (at like 10 or so), my dad tried to teach me
       | Smalltalk. Smalltalk is a great language, but there were just too
       | many concepts and abstractions happening on each line of code. To
       | understand even basic code required understanding messages,
       | objects, classes, blocks, etc. Maybe to an 18 year old that would
       | have been ok, but for my 10 year old brain it was too much.
       | 
       | A few months later though, I started with QBASIC. BASIC of course
       | gets an awful rap, but it was so much more intuitive for me at
       | the time. I started out with just global variables and GOTO's
       | everywhere. Over time, I worked up to loops, and subroutines,
       | etc. etc. However, the simplicity of "program runs one line at a
       | time, each line does something obvious" was incredibly important
       | to beginner-me.
       | 
       | Even once I moved to C, when I was an amateur I still had a
       | tendency towards one line per thing happening. I really hated
       | code like                   while(i++ < 10) { doSomethingWith(i);
       | }
       | 
       | (Actually, I still do).
       | 
       | As I got more sophisticated in my 20s, I started packing a lot
       | more ideas into a single line. If I'm being perfectly honest, I
       | think some of it was just showing off. You certainly look clever
       | if you can put 3 list comprehensions on one line or use some of
       | the more advanced collections apis. However, besides
       | understandability, I found that style of code had two really big
       | problems:
       | 
       | 1) It's a lot harder to debug. Either you can't get a breakpoint
       | in the precise place you want, or you can't insert a print
       | statement easily into a complex expression, or iteration
       | variables become implicit and you lose context.
       | 
       | 2) It's hard to add error handling to that type of code. When a
       | lot of things happen in a complex expression, you're depending on
       | the entire expression working.
       | 
       | Luckily I've grown out of that phase, although ironically now my
       | much more mature code looks a lot like the very simplistic code I
       | wrote as a teenager.
        
         | jstimpfle wrote:
         | 3) It's also harder to edit with an editor (like vim), and
         | (IMO) harder to read. I never even do "int x, y = 3;". I always
         | put each variable declaration on its own line.
        
           | mafuy wrote:
           | That avoids bugs and misunderstandings, too.
           | 
           | You know this, but for those unaware, in the previous
           | example, x is not initialized to 3. Similarly, in "int* p1,
           | p2;", p2 is an int, not an int*. Easy to misread.
        
       | gauddasa wrote:
       | The magic number for programmers is 7. 4 to 6 is for the rest of
       | the world.
        
       | jiggawatts wrote:
       | Now I know where Rust got some of its syntax from...
       | 
       | As an aside, when I see samples like this, it makes me _itchy_. I
       | hope and assume that they 're being used as made-up snippets just
       | to illustrate a point, and aren't being lifted from an actual
       | codebase.
       | 
       | Because... _ugh_... isn 't it obvious? Attacker-controlled input
       | such as URLs should never be manipulated with naive string
       | processing! Always use a proper parsing library. Not to mention
       | that complexities of URL encoding, character escapes, etc...
       | 
       | The problem is that the author is using abstractions at the wrong
       | level, with or without his fixes. The correct solution would be
       | something like:                   var uri = new Uri(
       | "http://foo/demo?test=a&blah=b%20c" );         var map =
       | System.Web.HttpUtility.ParseQueryString( uri.Query );
       | Console.Out.WriteLine( "is blah equal to 'b c'?\n{0}",
       | map["blah"] == "b c" );
       | 
       | The above example is C#, but similar code can be written in any
       | language. It's simple, direct, and doesn't violate the "rule of
       | six". It can be read like English:
       | 
       | 1. Construct a URI from a given string.
       | 
       | 2. Parse the query part of the URI into a map.
       | 
       | 3. Test if the 'blah' value in the query is "b c" as expected,
       | with the escaped space decoded properly.
       | 
       | The example of how to apply the "MORF" rule in the article
       | _still_ has low-level operations involved, which doesn 't make
       | the code more readable. It doesn't describe the _intent_ , which
       | is the key thing to writing code that doesn't need comments every
       | second line.
        
         | steveklabnik wrote:
         | ... and Ruby got it from Smalltalk. :)
        
       | standardUser wrote:
       | I see a troubling trend with some coworkers where they seem to
       | stretch the limits of time and space to make every line as dense
       | as possible, usually using lodash. I think it is a point of pride
       | for them, but I think it's obvious that everyone's life would be
       | easier if they just wrote their code out "long form" and, god
       | willing, added some comments for various steps. Instead, I find
       | myself having to re-write ultra-dense blobs of code in order to
       | debug or even simply understand what's going on.
        
         | ParetoOptimal wrote:
         | > I see a troubling trend with some coworkers where they seem
         | to stretch the limits of time and space to make every line as
         | dense as possible, usually using lodash. I think it is a point
         | of pride for them, but I think it's obvious that everyone's
         | life would be easier if they just wrote their code out "long
         | form"
         | 
         | Concision can be used for emphasis as verbosity can be used to
         | obscure.
         | 
         | > Instead, I find myself having to re-write ultra-dense blobs
         | of code in order to debug or even simply understand what's
         | going on.
         | 
         | Is this problem because their method is inherently nee complex
         | or is it due to lack of familiarity?
         | 
         | Perhaps they debug that code differently then you do and it's
         | incompatible with your previous mental model?
        
         | RajT88 wrote:
         | I had a professor who did that. He'd write a solution to the
         | assignments he was giving us, and then spend another 5-8 hours
         | on it trying to make it fit on a single overhead slide.
         | 
         | He was a much beloved professor.
        
         | professorTuring wrote:
         | It is not new, it's been ages since some developers try to show
         | off by bringing "cool" one liners to solve problems. Stretching
         | operators, bringing up imaginative uses for lambda expressions
         | or kind of abusing parts of the language to make some other
         | teammate or reviewer, What did you make there?
         | 
         | I believe, definitely, that they are quite intelligent people
         | that know a lot about the language or maths, but definitely
         | they are not usually making the smartest choice, because you
         | should use "languages" in order for the people to understand
         | you.
         | 
         | So you can call those: "50 cent expressions"
         | 
         | They help no one but their ego...
        
           | ParetoOptimal wrote:
           | At least in Haskell, I feel like this doesn't hold true.
           | 
           | Typically more concise code takes advantage of core language
           | abstractions.
           | 
           | It is actually simpler unless you are unfamilar with core
           | language abstractions, but I'd argue that's a you problem.
        
           | f1shy wrote:
           | In the last times I've been seeing lots of "show off" with
           | nitty gritty features of C++... lambdas, templates and
           | inheritance in a pattern that reminds me to the characters in
           | an Agatha Christie book, where you need a graph to keep up
           | with it... Hope this era ends soon.
        
         | f1shy wrote:
         | Once I read something along the lines of "every programmer goes
         | through that phase were we wants to show how clever he is, by
         | writing whole programs in one line. Until he understands how
         | stupid that is". I do not have the source, regrettably.
        
           | ethbr0 wrote:
           | I was taking my first multi-threaded resource allocation
           | course when I first ran into the famous Kernighan quote.
           | 
           | > _"Debugging is twice as hard as writing the code in the
           | first place. Therefore, if you write the code as cleverly as
           | possible, you are, by definition, not smart enough to debug
           | it."_
           | 
           | It clicked and instantly disabused me of the notion that
           | smart people write code that's any smarter than the minimum
           | required to solve the problem at hand.
        
         | whynotminot wrote:
         | I think there's a real smell with those long, dense lines of
         | code. Tends to mean your data structures are out of control:
         | objects with arrays that point to other objects that then also
         | have arrays on them. My oh my.
         | 
         | Comments being required are also another smell that the code
         | doesn't explain itself. I know this is said so often it's a
         | cliche, but it really is true.
         | 
         | I think both of these things point back to the same problem:
         | out of control data structures.
        
           | guenthert wrote:
           | Sure, if the computer can figure out what a given fragment of
           | code is supposed to do, so can you (a sufficiently clever
           | programmer). The question rather is, do you spend 20s reading
           | a comment or 15m to solve the riddle?
           | 
           | There's a real danger that comments aren't updated when code
           | is, particularly if 3rd parties make those changes. This is
           | one of the corners where there will never be a single answer
           | which is right in all circumstances.
        
             | ethbr0 wrote:
             | > _There 's a real danger that comments aren't updated when
             | code is, particularly if 3rd parties make those changes._
             | 
             | So much this. If it took you everything you learned over
             | the last week + an epiphany to come up with a bit of code,
             | how is someone scanning through supposed to understand it?
             | 
             | Or, in example with hilariously apropos incomplete post-hoc
             | comments, 0x5F3759DF https://en.m.wikipedia.org/wiki/Fast_i
             | nverse_square_root#Ove...
        
             | BurningFrog wrote:
             | Often, breaking out a well named variable or three is the
             | better way to make code readable.
        
             | ParetoOptimal wrote:
             | > There's a real danger that comments aren't updated when
             | code is, particularly if 3rd parties make those changes
             | 
             | That problem exists because no one denies pull requests or
             | rejects code that has outdated comments.
        
       | kazinator wrote:
       | This rewrite is more performant than the original:
       | query_params = s.split('?')[1].split('&')[-3:]       map(lambda
       | x: x.split('=')[1], query_params)
       | 
       | The calculation of query_params, having no dependency on the
       | lambda parameters or anything being mutated, has been lifted out
       | of the lambda, and thus spared from repeated execution by map.
       | The compiler for that language won't do this automatically.
        
         | gilch wrote:
         | What? No it isn't! You didn't parse that correctly.
         | 
         | The query params were never in the lambda to begin with. Python
         | function calls have strict (not lazy) semantics, i.e.
         | "applicative order", i.e. both expressions passed as arguments
         | to map() are evaluated before the map body gets them as
         | parameters, thus the query params would only be evaluated once,
         | even when inlined as they were originally.
         | 
         | Same with the lambda _definition_ : it's evaluated only once.
         | It's just the lambda _body_ that gets reevaluated each loop,
         | and only evaluated for the first time on the first loop.
        
           | kazinator wrote:
           | Sorry; it looked to me like x.split('=')[1], query_params is
           | a tuple being returned by the lambda. But of course that
           | leaves map without the needed argument.
        
       | avnigo wrote:
       | A map lambda example is what I had in mind when reading the
       | article. I'm not a big fan of the temporary variables, though.
       | 
       | Admittedly the example below is not a perfect solution, but
       | that's where I thought the article was heading when splitting
       | that code over multiple lines for readability.
       | map(           lambda x: x.split('=')[1],           (url
       | .split('?')[1]               .split('&')[-3:]           )       )
       | 
       | Is this still too unreadable or more messy?
        
       | xigoi wrote:
       | When I clicked on "More settings" in the cookie dialog, it
       | displayed a loading animation (ignoring my prefers-reduced-motion
       | setting) and got stuck. Just straight-up user-hostile design.
        
       | skitter wrote:
       | In this case `query_params` works well, but it's sometimes hard
       | to find descriptive and reasonably concise names for the
       | intermediate value. In those cases, the ideal would be using only
       | postfix chaining, so that you can read it by only keeping the
       | intermediate value and the next operation in mind:
       | s.split('?')[1]          .split('&')[-3:]          .map(lambda x:
       | x.split('=')[1])
       | 
       | Unfortunately, that's not how Pythons map(), len() and such were
       | designed.
        
         | gilch wrote:
         | Idiomatic Python wouldn't use a map here, but a generator
         | expression:                   (x.split('=')[1] for x in
         | s.split('?')[1].split('&')[-3:])
         | 
         | Removing the lambda cuts down on the noise considerably.
         | 
         | And honestly, with this many splits with fixed indexes, I'd
         | probably use a regex. Now there's a dense language for you.
        
       | cc101 wrote:
       | If I use elaborate camel-case variable names, it seems to reduce
       | the load on my short-term memory because I don't have to remember
       | what a variable name represents. It's meaning is there when I
       | need it and can be forgotten otherwise.
        
       | meitros wrote:
       | This seems like an interesting heuristic for anything that could
       | automatically either generate or "format" code - a little more
       | semantic than just relying on a text parser
        
         | djmips wrote:
         | That is a cool idea. Show the code the way you understand it
         | best without even having to change the underlying text.
        
       | iLoveOncall wrote:
       | Readable code and clean code are two different things and I think
       | this article does a prety poor job at writing clean code.
        
       | gilch wrote:
       | At least for the contrived example from the article, the solution
       | isn't to break up the code, but to use denser code. Use a regex.
       | 
       | Does anybody really think that e.g. sregex[1] is better than just
       | learning and using the regex language directly? Because that's
       | where this kind of thinking leads.
       | 
       | [1]: https://github.com/jwiegley/emacs-
       | release/blob/master/lisp/o...
        
         | toiletduck wrote:
         | I know it's not the point the author is trying to make, but I
         | couldn't help get the feeling this example isn't good enough to
         | carry the point.
         | 
         | from stdlib:                 from urllib.parse import urlparse,
         | parse_qsl            url =
         | 'https://www.example.com/some_pathsome_key=some_value&foo=bar'
         | parsed_url = urlparse(url)       values = [v for _, v in
         | parse_qsl(parsed_url.query)]       print(values)
         | 
         | which I guess you could oneliner back to this..
         | [v for _, v in parse_qsl(urlparse(url).query]
        
         | cavisne wrote:
         | I think for any code thats meant to be read and maintained by
         | someone else a regex is a bad idea.
         | 
         | You are saving a few lines on the surface, but adding a
         | potential backtracking bug in the future.
        
       | charles_f wrote:
       | Ok, quick rules that focus on single lines. That's neat, but from
       | experience most of the complexity comes from the structure more
       | than just how the code is written, conventions about how to write
       | a line of code won't fix corrupt indirections, misplaced
       | coupling, lack of cohesion, undue repetitions, missing tests,
       | etc.
       | 
       | Clean code is not just a few rules about how to write a line. You
       | can write nice lines that still don't make sense and amount to
       | shit code
        
         | ARandomerDude wrote:
         | Ah yes. I remember when I read the _Clean Code_ about 10 years
         | ago and produced the  "cleanest" code I had ever seen - only to
         | have it destroyed by a senior dev during a code review because
         | the task was relatively complex and my overall structure was
         | garbage. One of the saddest days of my career. Probably the
         | most helpful day of my career too.
        
       | readthenotes1 wrote:
       | The author misunderstands Miller's research on working memory,
       | often reported as "7 +/- 2".
       | 
       | But, Miller states that limit is valid only for unrelated items.
        
       | longrod wrote:
       | If you are going for human readability then making your code
       | expressive is the only way. Abstract away the code parts under a
       | layer of very simply named functions/classes and boom! even a
       | child will be able to understand what's going on.
       | 
       | Obviously, that isn't always possible. I find this approach
       | especially useful in writing e2e browser tests. You write an
       | abstraction over the testing framework's (playwright, puppeteer
       | etc) interaction and then use that in your tests.
       | 
       | So instead of writing:                   await page.click(".play-
       | button");
       | 
       | You do:                   await app.play();
       | 
       | This also has the benefit of extreme reusability. Doesn't work
       | for everything though.
        
       ___________________________________________________________________
       (page generated 2022-09-24 23:01 UTC)