
Applying the Gestalt Principles to Code - ewokday
https://yetanotherchris.dev/clean-code/gestalt-principles/
======
jxramos
I forget what book it was that I read but it made this very obvious statement
about source code being a 2D grid. And this is very apparent with the whole
row column cursor tracer shown in editors and in compiler errors. For some
reason I always perceived these things as having significance on a per line
basis and once I read that code is a fundamentally 2D entity it empowered me
to create relationships along the vertical with judicious usage of vertical
alignment and creating vertical harmony in my code. It’s pretty cool to
perceive monospace fonts as allowing a lattice of 2D characters. There’s a
trade off of course with unnecessary vertical white space in that readability
is boosted but the lexer has more characters to ignore.

There was one numerical project I worked on where I had these _x and _y
suffixes showing up very frequently in identifiers. Having the code vertically
aligned actually made spotting copy paste bugs very easy because you can trace
the vertical and find all _x’s lumped together or whatever the case may be.
Inconsistencies stood out more readily I want to say.

One thing I try to do with the visual aspect of my code is to organize things
semantically when possible with a good clear comment that tersely covers the
operation: initialize abc, validate xyz, calculate, compute, guard clause:
ignore case 1, load config, override X, etc

~~~
Sharlin
In The Beginning (tm) source code was very explicitly a 2D grid, with column
numbers being lexically significant in both FORTRAN and COBOL because columns
matter in punch card representation.

Of course, as every Lisp programmer knows, programs represented in almost any
high-level language are actually _trees_ , and to force them into a 2D grid of
symbols is rather unnatural and awkward. Specifically, almost all 2D grids of
symbols are lexically or syntactically invalid programs, and many natural
source code transformations require either manual labor or special support
from the editor.

------
Alex3917
There's at least one more readability error here that wasn't corrected. The
email fields were declared in the order To, From, Subject, Body, but then From
and To are swapped when they are added to the MIME message. This breaks
parallelism.

------
cjfd
100% agree. These optimizations in readability help a lot. Also, a failure to
optimize code for readability makes reading code difficult. I once read some
code where I immediately concluded that something was wrong because a function
was being called from the wrong method. Turned out that it actually was in the
right method but there was no vertical space between two methods giving the
wrong impression of where that particular method call actually was. I feel
quite alone with this opinion though. I see messy code all around me. One
particular thing that everybody seems to agree on but that is pretty much
objectively bad is the common way to line up lambdas. People do

    
    
      some_object.some_method((p, q) =>
      {
         do_things()
      })
    

while the right way of doing this clearly is

    
    
      some_object.some_method(
         (p, q) =>
         {
            do_things()
         })
    

The 'broken lambda' that one tends to see everywhere is driving me nuts.

The principle is that code already should give the right impression of what is
going on at the first glance. Another thing that is quite a spectacular
failure in this regard is the google style guide. The indents of 2 tabs
combined with braces that do not line up must have been optimized to be as
unreadable as possible.

~~~
majewsky
See, that's how different people are. For me, the clearest formatting is
obviously

    
    
      some_object.some_method((p, q) => {
        do_things()
      })
    

Your preferred version has way too much useless whitespace.

------
foreigner
I've occasionally fantasized about a code editor that doesn't allow scrolling,
so you're forced to break code up in to pieces that fit on a single screen. I
think that would greatly improve readability.

That would also help with one of the main arguments against "visual"
programming languages - they get too hard to read as programs get larger.
Simply don't allow scrolling or zooming the source.

~~~
enriquto
> I've occasionally fantasized about a code editor that doesn't allow
> scrolling, so you're forced to break code up

But this is easily hackable around. What you really want is a programming
language that does not allow functions longer than 25 lines.

~~~
foreigner
Ha ha I guess you're right! I can't be bothered to switch to a different
programming language just for this feature tho :-)

Anyway then you would get stuck policing exactly how long a "line" is, do
comments count, etc...

~~~
enriquto
> Anyway then you would get stuck policing exactly how long a "line" is, do
> comments count, etc...

A line is 80 characters. Indent is 8 spaces. Comments do not count.

~~~
foreigner
Case in point, I prefer an indent of 4 spaces.

~~~
enriquto
It does not matter. However hard you "prefer" the value of pi to be 3, it will
stay an irrational number. Eight-space tabs are a constant of the universe.

~~~
indentit
not in this universe they aren't - many popular applications indent 4 spaces
by default when pressing the Tab key, and render physical tabs as four spaces,
for example github ;) tabs of 8 space width are a legacy travesty that wastes
space :)

~~~
enriquto
Ours is indeed a decadent universe that is slowly being corrupted by powerful
forces of evil (such as the 4-space tab brain damage of github). But there are
still beacons of light to guide us:

[https://www.kernel.org/doc/html/latest/_sources/process/codi...](https://www.kernel.org/doc/html/latest/_sources/process/coding-
style.rst.txt)

[https://wiki.musl-libc.org/coding-style.html](https://wiki.musl-
libc.org/coding-style.html)

[https://www.freebsd.org/cgi/man.cgi?query=style&sektion=9](https://www.freebsd.org/cgi/man.cgi?query=style&sektion=9)

[https://man.openbsd.org/cgi-bin/man.cgi/OpenBSD-
current/man9...](https://man.openbsd.org/cgi-bin/man.cgi/OpenBSD-
current/man9/style.9?query=style%26sec=9)

------
FluffyKitty
Wow, this article does such a good job of concisely explaining these concepts
I've been trying to put into words for so long! Definitely going to be sharing
with some of the juniors.

~~~
speedplane
> Wow, this article does such a good job of concisely explaining these
> concepts ... Definitely going to be sharing with some of the juniors.

Not sure this is the type of article for juniors. I don't think it's really a
handbook for writing more pleasing code. It's more a statement, that the way
we structure and style our code is a design decision. Code's visual design is
an area that many programming languages have experimented with mostly over
trial and error, but it's not something that is well studied.

New programming languages come out all the time, and they often tout
advantages such as speed or security, but they rarely try to justify design
decisions based on visual design and readability. It seems like a ripe field
to dive deeper into.

~~~
FluffyKitty
>Not sure this is the type of article for juniors.

Most definitely. I worded that poorly. I meant "my juniors" as in those with
less experience than I, but not fresh out of the oven devs.

------
chillacy
The article focused on the surface level of code, which I think is important.
But the next level is where you can apply the same principles to the class
hierarchy or how data flows.

I'm not sure if it's the right word but I've taken to using the word
"locality", since it has prior usage in a similar concept in
[https://en.wikipedia.org/wiki/Locality_of_reference](https://en.wikipedia.org/wiki/Locality_of_reference)

------
aasasd
Tell ya what: for the principle of proximity to be properly implemented, empty
lines should be of different height.

~~~
coldtea
That's how I keep them -- e.g. different number of line breaks between
different items (e.g. 2 lines between import statements and class definition,
but 1 line between methods inside the class).

~~~
aasasd
That's too crude. What I'm saying is, it should be around 0.5 line height for
grouping inside a method, ~1-1.2 between items of a class, 2-2.5 around
classes.

Of course, coders' tendency to nest breaks the idea a bit on levels deeper
than a method. Though I think with a lot of fiddling some variation can still
be achieved.

------
hellllllllooo
Does anyone who writes code for a living here actually spend 90% of their time
reading code?

~~~
kstenerud
I spend 90% of my "code time" (meaning time interacting with actual source
code in some way) reading it rather than writing it.

At the moment I'm reading
[https://github.com/golang/go/blob/master/src/encoding/json/e...](https://github.com/golang/go/blob/master/src/encoding/json/encode.go)
to diagnose a problem in my code.

Overall, the readability is good. My only complaint is the terse variable
names, which makes the more complex methods more time consuming to follow
(because you have to keep going back to refer to their definitions to remember
what they are). For example,
[https://github.com/golang/go/blob/master/src/encoding/json/e...](https://github.com/golang/go/blob/master/src/encoding/json/encode.go#L704)

~~~
seanmcdirmid
The variable naming scheme in that code is incredibly consistent with respect
to type. That should alleviate some of the problems, and I think the code
would be more annoying to read if the variable names weren’t terse (like
Simonyi Hungarian).

~~~
kstenerud
Type isn't the important information; PURPOSE is.

When you dive into the code, especially at the point I chose, you are
presented with:

    
    
        e.string(kv.s, opts.escapeHTML)
    

And so, you have the following questions: What is e? What is kv? What is s in
kv? To answer these, you have to scan upwards. e is encodeState. OK, not too
bad. kv is maybe key-value of something? it comes from sv (string value
maybe?), which comes from:

    
    
        sv := make([]reflectWithString, len(keys))
    

So a string value? Or a list of string keys? A list of structs, OK. s might be
a string value inside, which has ... some meaning I guess? Digging around, I
see it defined as:

    
    
        type reflectWithString struct {
         v reflect.Value
         s string
        }
    

So it is a string, but I still don't know what its purpose is. After a bunch
of digging around to see how it's used, it looks like s is a string
representation of the value, but I'd need to look over more code to be 100%
sure.

Now, contrast this with:

    
    
        encoderState.string(keyValue.asString, opts.escapeHTML)
    

Now I know what this calling string() on the encoder state, using the string
representation of the key value. "string" is still a bit cryptic. It could be
renamed. Looking inside "string" I see that it's writing things, so maybe a
better name is:

    
    
        encoderState.writeString(keyValue.asString, opts.escapeHTML)
    

With this version, I know without having to look at any other lines that this
code is supposed to write the string representation of a key value to the
encoder state, doing HTML escapes. I don't need to look inside the guts of
anything to figure this out. I don't even have to leave this line.

This is code U/X.

~~~
seanmcdirmid
Type is purpose. And anyways, the English alternatives are still vague and
aren’t really improvements. KV is incredibly standard, even in English
conversations (KV store is said more often than key value store). E is not
descriptive, but neither is encodeState (WTF does that even mean?). S is a
perfect abbreviation for a generic string, calling it string instead isn’t
much better, really! It’s obvious in this case that it is only meant to be a
generic string (what else could be meant by reflectWithString?).

I do prefer method names to be descriptive, so verbs are nice. Well, unless we
are dealing with very generic lambda code, then there might not be better
options beyond f, g, and p.

~~~
seanmcdirmid
Also, as a rule of thumb: the more rarely something is used, the more
descriptive its name needs to be. If something is used often in some code,
terseness is more forgivable and even desirable in many cases.

A great example of this is "i" as a standard indexer, or "x" and "y" as
horizontal and vertical coordinates. They are used often therefore terseness
can be applied and is useful. Also why "KV" is often used to be mean key-
value, the concept is just very pervasive. The terseness for variable names in
the linked to code seems more acceptable because those variables of
consistency and repeated usage.

------
majewsky
FYI: "Pragnaz" is not a word. You probably mean "Prägnanz".

