[HN Gopher] Holding a Program in One's Head (2007)
       ___________________________________________________________________
        
       Holding a Program in One's Head (2007)
        
       Author : yamrzou
       Score  : 70 points
       Date   : 2024-09-16 08:19 UTC (2 days ago)
        
 (HTM) web link (paulgraham.com)
 (TXT) w3m dump (paulgraham.com)
        
       | nickdrozd wrote:
       | > You never understand other people's code as well as your own.
       | No matter how thoroughly you've read it, you've only read it, not
       | written it.
       | 
       | There is certainly some truth to this. On the other hand, it's
       | possible to become blinded to defects in code you've written
       | yourself. You see what you intended for the code to do rather
       | than what it actually does. Reading someone else's code, it can
       | be easier to see what's really going on, since you just see
       | what's there.
        
         | hinkley wrote:
         | I, for instance, learned years ago to refuse to help people
         | debug promise chains in languages with async-await semantics.
         | Rewrite it and then get back to me if it still doesn't work.
         | They usually transcribe the intent of the chain and not the
         | letter of it, fixing the bug they can't see. And if not the
         | error makes enough sense they can figure it out.
        
         | saghm wrote:
         | I often tell younger engineers that the human brain is the
         | slowest, lowest-memory, and most error-prone runtime for a
         | program. If they're stuck trying to figure out a bug, one of
         | the most effective things they can do is validate their
         | assumptions about what's happening, because there wouldn't be a
         | bug if everything was happening exactly according to
         | expectations.
        
           | wruza wrote:
           | That's why I learned to log literally everything into stdout
           | _unless_ a process is time-sensitive _and_ it's deep
           | production _and_ it passed the mark where bugs and insights
           | occur once a month+ _and_ there's zero chance someone asking
           | me what exactly happenes with X at Y afternoon-ish last
           | Friday.
           | 
           | The obvious exception are recursive number-fiddling algos
           | which would spam gigabytes of output due to big N.
           | 
           | This way I can just read assumptions and see branches taken
           | and what's wrong as if it was written in plain text.
           | 
           | When I see klocs without a single log statement, to me it's
           | readonly and not worth touching. If you're stuck with a bug,
           | log everything and you'll see it right there.
        
             | jimbokun wrote:
             | For large systems the cost of maintaining all of those logs
             | in a searchable system can be prohibitive.
        
               | lanstin wrote:
               | Just reduce the time horizon you keep the logs until you
               | can afford it. Also, as he mentioned, once a system is
               | getting bugs infrequently, you can lower the log level.
               | My standard is to have a log msg for each branch in the
               | code. In C, I would use macros to also have a count of
               | all the fmt strings the log package encountered (so I
               | still got a sort of profile of the logic flows
               | encountered, but not have the sprintf expense), but I
               | haven't figured out an efficient way to do that in Go yet
               | (i.e. not using introspection).
        
           | nfw2 wrote:
           | This is one of the reasons I think that the push towards
           | server-side UI is misguided. It's much easier to walk through
           | the runtime of a program running locally than it is to step
           | through a render that's distributed across a network.
        
           | alphazard wrote:
           | > wouldn't be a bug if everything was happening exactly
           | according to expectations
           | 
           | This isn't quite true, especially concerning distributed
           | systems. It's relatively common for a software system to be
           | _broken by design_. It 's not that the developer didn't know
           | how to use the programming language to get the computer to do
           | what they want. It's that what the developer wanted reflects
           | a poor model of the world, a logical inconsistency, or just a
           | behavior which is confusing to users.
        
             | saghm wrote:
             | Keep in mind I said that this is advice I give junior
             | engineers specifically; they shouldn't be the ones
             | responsible for designing distributed systems in the first
             | place. For someone in that part of their career, this
             | advice is meant to help to learn the skills the need to
             | solve the problems they're dealing with, and it's not
             | intended to be universal to all circumstances, just a
             | useful thing to keep i mind.
        
             | monocasa wrote:
             | That sounds distinctly like an expectation that didn't
             | hold.
        
               | Stefan-H wrote:
               | "a poor model of the world, a logical inconsistency, or
               | just a behavior which is confusing to users" I expect
               | when I pull from the queue (but it was designed non-
               | atomically) that I will be guaranteed to only grab the
               | item once and only once, even after a failure. That
               | expectation is wrong, but the developer may have
               | implemented their intent perfectly, they just didn't
               | understand that there are error cases they didn't account
               | for.
        
         | jmkr wrote:
         | There's some idiom that says something like "you don't
         | understand something if you can't explain it." I think this is
         | the real point of code review. To make a case for why your code
         | is valuable. If it's just a blob of 1000 lines of "refactor" or
         | "adding feature." It means nothing. A good commit has some kind
         | of abstract tailored to what work was done.
         | 
         | Then a review becomes something like "the claim was made this
         | function does this, does it look like the function does what it
         | says it does?" If you can understand it in context, then you've
         | added trust and knowledge of how it works.
         | 
         | However it seems to often be the case a review turns into "I
         | like this word better than that word" so back to the explaining
         | it point, it becomes a bit of a self review with the hope it
         | might be helpful to somebody else in the future.
        
           | lanstin wrote:
           | There is a balance there. You can have 1000 trivial commits
           | making it hard to see the fact you just have 10 features, and
           | ten mind-breaking commits making it hard to see what each
           | feature entails. (Then there's the 50 commits "try this"
           | where you are fighting CI or CD and can't test without going
           | thru a git commit/push.)
        
         | xanderlewis wrote:
         | The comparison with mathematics also makes sense here. It's
         | much easier to spot typos in other peoples' work than your own
         | for exactly that reason: when you read back what you wrote, you
         | read back _what you meant to write_ rather than what's actually
         | there.
         | 
         | Open any textbook (even a fourth edition of a famous one,
         | written by an expert) and you'll find countless typos. In fact,
         | in a certain sense, the more expert you are the less suitable
         | you are as a proof reader for such books.
        
           | lanstin wrote:
           | One of my undergrad tutors taught complex analysis with a
           | book she had written, and she offered a reward for any one
           | who found an error. She said the best students never claimed
           | the reward, only the people that had to study each word
           | carefully.
        
         | stonemetal12 wrote:
         | I believe that is the source of "the worst code I have seen is
         | code I wrote six months ago" feeling. It has been long enough
         | that the code has fallen out of your memory, making you less
         | blind to the defects. You can now see it as an outsider would
         | see it.
        
       | corytheboyd wrote:
       | > [...] bottom-up programming, where you write programs in
       | multiple layers, the lower ones acting as programming languages
       | for those above
       | 
       | I like to explain this as "hide the bad parts behind a good API".
       | Anything interesting is going to require "bad parts", which just
       | means the low-level, difficult work. From there, compose up and
       | up until a high level orchestration is achieved. It works so much
       | better than the bad parts being distributed everywhere! That's
       | what you'd also call a "leaky abstraction"
        
         | reddit_clone wrote:
         | This is what I believe as well. Also throw in some Functional
         | Programming (isolating pure functions and side-effecting
         | functions) for extra benefits.
        
           | kevindamm wrote:
           | Both excellent points, and I would add a recommendation for
           | high level organization: to consider the data then the
           | process. If you can draw a graph where every data structure
           | is only connected to a process, and vice versa, and that
           | processes may take multiple inputs but only produce a single
           | output type, it will make holding the entire system in your
           | head a lot easier, even for very scaled-up systems.
           | 
           | Bonus points if you can distinguish between essential state
           | and circumstantial state.
        
         | kmoser wrote:
         | Said another way: push interface up and implementation down.
        
         | WillAdams wrote:
         | This seems to be one of the core lessons behind John
         | Ousterhout's _A Philosophy of Software Design_
         | 
         | https://www.goodreads.com/book/show/39996759-a-philosophy-of...
         | 
         | and I find that the mechanism of "Literate Programming":
         | 
         | https://literateprogramming.com/
         | 
         | is a useful one for doing this since it allows one to write
         | about both how the low level details are implements in a
         | function, _and_ how the written function is used in a way which
         | allows the twain to support each other.
        
           | zimpenfish wrote:
           | That needs to be http://literateprogramming.com/ at the
           | moment because their certificate / HTTPS setup is borked.
        
         | nfw2 wrote:
         | I think that many, perhaps even most, engineers incorrectly
         | believe that the main purpose of abstraction in code is simply
         | DRY, as if the goal is to save keystrokes or something.
         | 
         | In my view, the purpose of abstraction is to compress the
         | concepts of your application into digestible and manueverable
         | chunks, and DRY is just a heuristic for beginners to follow to
         | help point to where appropriate abstraction boundaries may be.
         | 
         | I hope the various theories behind what constitutes good code
         | will make their way out of scattered blog posts and into CSE
         | curriculum.
        
           | 4star3star wrote:
           | I think DRY should be supplemented by "Don't make me read it
           | multiple times". Repeat yourself, by all means, if that makes
           | it so that I don't get all twisted up jumping between so many
           | files that I can't keep the main thread straight while I read
           | your code.
        
           | jimbokun wrote:
           | Good API is for encapsulating and communicating ideas between
           | other programmers or teams.
           | 
           | DRY is for improving communication with your future self.
        
           | lanstin wrote:
           | New software demands a new vocabulary, and if those new
           | concepts correspond exactly to implemented code, everything
           | becomes very clear and possible to share with new people.
           | 
           | As well, the division of a project into layers where each
           | layer has a manageable amount of concepts, 5-7 for normal
           | layers, becomes much easier for people to learn and use
           | correctly. If I have to keep 12 things in mind while using a
           | layer, it's going to be a lot harder to get correct.
        
         | joe_the_user wrote:
         | _" Hide the bad parts behind a good API... It works so much
         | better than the bad parts being distributed everywhere!"_
         | 
         | This doesn't work for the "bad things" I know of. All of the
         | low-level bad parts which are truly bad/mucky, are bad because
         | they can't be entirely hidden. They are leaky abstractions and
         | by that fact, they ... leak. They impact every part of the
         | system so they are "distributed everywhere" in the sense that
         | the maintainer of a large program has to consider them
         | everywhere even if they only directly show up in a few places.
         | 
         | Just as an example, programming languages are abstractions over
         | the complex structure of "raw" memory. Pythons hide this
         | abstraction more than for example c but once you reach a
         | certain scale the use of python, you have to consider the
         | details of its use of memory. And that's memory in general,
         | which people work at to make reasonably regular as well as
         | fast.
         | 
         | That's not saying you can't have an API that makes the low-
         | level problems less.
        
       | mingusrude wrote:
       | I once watched a presentation by Dan North where he said that a
       | microservice should never be bigger than your head. What he meant
       | was that all the code for the microservice should fit on your
       | screen and you should be able to put your head against the screen
       | and it should cover the code.
       | 
       | Yes, this was in the microservices-heyday.
        
         | fredrikholm wrote:
         | A quote originally (AFAIK) from the wonderful book 'Practical
         | Common Lisp'.
         | 
         | https://gigamonkeys.com/book/
        
           | jmkr wrote:
           | This is probably a common; lisp/scheme type of thought. Dan
           | Friedman also said something about how he only likes code
           | that he can hold in his head to think about in the shower. I
           | forgot the source, but it's in one of the talks. I think
           | Sussman has also said something similar.
        
         | dkarl wrote:
         | Anything you create in software should be able to fit in
         | someone else's head. I.e., you should be able to think of it as
         | a tractable arrangement of abstractions that let you reason
         | about it in a precise, non-leaky way.
         | 
         | Those abstractions don't just poof into existence after the
         | project is complete, though. You have to design them into the
         | system and communicate them to the people who need them.
         | 
         | The abstractions also need to be precise and non-leaky enough
         | to be useful. One of the most dangerous talents in software is
         | the ability to create the illusion of tractability by using
         | vague language. You can create an absolute mess and then
         | describe it to management in a way that makes it sound well-
         | understood. This is the most lucrative and destructive skill a
         | consultant can have.
        
           | ellis0n wrote:
           | Unfortunately, all the market giants follow this destructive
           | practice to rewriting 95% of all code every five years to
           | multiply added value.
        
       | kuharich wrote:
       | Past comments: http://news.ycombinator.com/item?id=45698,
       | http://news.ycombinator.com/item?id=2988835
        
       | kmoser wrote:
       | "Use succinct languages" is somewhat at odds with "Write
       | rereadable code." There's a point beyond which making your code
       | more succinct makes it more difficult for a human to parse. This
       | can be somewhat mitigated by comments but I'd rather just read
       | more readable code than more succinct code.
        
         | dkarl wrote:
         | Succinct languages don't force you to be succinct. They only
         | allow you to be succinct where it helps.
         | 
         | (I'm sure there are exceptions, but in the set of languages
         | that enable succinctness, the subset that force succinctness is
         | surely small.)
        
         | jmkr wrote:
         | I think this is addressed in the link under that section
         | 
         | https://paulgraham.com/power.html
         | 
         | > I think that the main reason we take the trouble to develop
         | high-level languages is to get leverage, so that we can say
         | (and more importantly, think) in 10 lines of a high-level
         | language what would require 1000 lines of machine language.
         | 
         | ...
         | 
         | > 5. Write rereadable code. All programmers know it's good to
         | write readable code. [...] If you're writing for other people,
         | you may not want to make code too dense. Some parts of a
         | program may be easiest to read if you spread things out [...]
         | Whereas if you're writing code to make it easy to reload into
         | your head, it may be best to go for brevity.
        
         | jimbokun wrote:
         | I completely disagree.
         | 
         | It's possible to write code so succinct that making it more
         | verbose would make it more understandable.
         | 
         | But overly verbose code is far, far more common. And having a
         | naturally succinct language does not stop you from keeping your
         | code verbose. Just gives you more ways to make your code
         | succinct that aren't possible in non-succinct languages.
         | 
         | (Not this is orthogonal to weaker or stronger compile time type
         | systems. Type systems that catch more errors at compile time
         | are very useful. But some strongly compile-time typed languages
         | are still quite succinct.)
        
         | ellis0n wrote:
         | You always have comments. I've always been fascinated by GitHub
         | repositories like "odd-even" where the code is just a few
         | lines, but the README takes up several times more text. I even
         | started a small collection of these fun React modules :)
        
       | rshudson wrote:
       | Take a look at the list of people who read the draft of this
       | post.
       | 
       | > Thanks to Sam Altman, David Greenspan, Aaron Iba, Jessica
       | Livingston, Robert Morris, Peter Norvig, Lisa Randall, Emmett
       | Shear, Sergei Tsarev, and Stephen Wolfram for reading drafts of
       | this.
        
         | akira2501 wrote:
         | People who are famous for running companies and not writing
         | code.
        
           | jimbokun wrote:
           | Robert Morris, Peter Norvig and Stephen Wolfram didn't write
           | code?
           | 
           | (I don't recognize some of the others.)
        
         | joshdavham wrote:
         | I'm always amazed at the impressive list of people pg gets to
         | review his essays. His recent essay "Founder mode" was no
         | exception.
        
       | norir wrote:
       | I more or less agree with the main thesis although I would point
       | out that typical software development practices in industry make
       | this very difficult. Fred Brooks told us to plan to throw out the
       | first draft. Industry says ship it.
        
         | ellis0n wrote:
         | That is true. Then these drafts travel, fly, and feed you.
         | Don't be surprised by plastic in every micron of space -- it
         | was just a draft. By the way, the first refrigerators without
         | Freon also caused harm, but back then people were only thinking
         | about reliability and didn't have the resources to tackle the
         | full scope of the problems. Now the goals have changed driven
         | by the pursuit of profit
        
       | amelius wrote:
       | It's (mostly) not about the code. It's about the data structures
       | and their relations.
        
       | kulor wrote:
       | I call this program in one's head "suspended comprehension" as it
       | has a degree of transience and fragility that requires effort to
       | resume where you left off.
        
       | snowwlex wrote:
       | > You can magnify the effect of a powerful language by using a
       | style called bottom-up programming where you write programs in
       | multiple layers, the lower ones acting as programming languages
       | for those above. If you do this right, you only have to keep the
       | topmost layer in your head.
       | 
       | And if you do it wrong (overengineering), you would need to learn
       | not just one, but as many programming languages as there are
       | layers...
        
         | snowwlex wrote:
         | That's IMHO what OOP (as understood in Java world) tends to
         | evolve to...
        
       | jimbokun wrote:
       | A good way to keep a program in your head:
       | 
       | Break it up into a few or several smaller programs that interact
       | through clean interfaces. Then you can keep one smaller, simpler
       | program in your head at a time, then integrate them at the end
       | once all the smaller programs are working.
        
         | minkles wrote:
         | Those are called functions.
        
           | Aeglaecia wrote:
           | imagine having the brain to synthesise every function across
           | a codebase , most humans gotta settle for interfaces :p
        
             | minkles wrote:
             | Interfaces are just collections of function prototypes.
        
           | jimbokun wrote:
           | Sometimes.
           | 
           | Sometimes it is one or more programs writing to a queue or
           | topic, and other programs reading from that topic.
           | 
           | Or programs writing to and others reading from a Unix pipe.
           | 
           | Or programs talking to each other using HTTP.
           | 
           | Or Erlang processes communicating concurrently on one machine
           | or across a network.
           | 
           | Or different programs sharing one database.
           | 
           | Or many objects communicating by passing messages in a small
           | talk program.
           | 
           | There are many ways to encapsulate programs and have them
           | interact.
        
             | minkles wrote:
             | _> Or programs writing to and others reading from a Unix
             | pipe._
             | 
             | write(message), read() -> message
             | 
             |  _> Or programs talking to each other using HTTP._
             | 
             | request() -> response
             | 
             |  _> Or Erlang processes communicating concurrently on one
             | machine or across a network._
             | 
             | sendMess(message), waitMess() -> message
             | 
             |  _> Or different programs sharing one database._
             | 
             | execute(query) -> response
             | 
             | ...
             | 
             | I'm a mathematician at heart so I'm staying away from
             | category theory as long as possible.
        
             | ellis0n wrote:
             | Totally agree and it depends on the current context of the
             | programmer. For example, in the ACPUL language the program
             | is split into boot images, files, modules, functions and
             | expressions. All of these represent different levels of
             | context encapsulation
        
       | ellis0n wrote:
       | A great article that accurately describes the challenges I've
       | faced over the past 12 years while creating the best project for
       | programmers and the various attempts to find work and funding,
       | observing all the madness in the organizations It seems like the
       | best of it was left in 2007, from which we still draw inspiration
       | and knowledge, while since 2008 we've been living in a new world
       | of marketing and wars. The insatiable interests of faceless
       | systems are destroying everything alive in this great science of
       | "programming".
        
       | btown wrote:
       | IMO this is one of the most important skills for a developer to
       | have. In an age of Github Copilot and similar systems, it's both
       | far more viable (because, per OP's #1, you won't get distracted
       | by the "side quests" of implementing utilities you need, when you
       | can just tab-complete to get them) and far more vital (because
       | with that productivity increase comes an increase in the
       | complexity of problems you'll be asked to tackle at any given
       | level of seniority).
       | 
       | My advice on this would be: never be afraid, and even force
       | yourself to, follow the chain of how a certain function is
       | implemented in third-party libraries you are using. Set up your
       | IDE to be able to click into functions, make that muscle memory,
       | and perhaps even have a dedicated project you can bring up for
       | your site-packages or node_modules. Don't just rely on
       | documentation, see how things actually work a few levels deep.
       | You'll gain a deeper understanding of the code you're using, as
       | well as incredible practice for when you need to do this on an
       | existing first-party codebase.
       | 
       | Oh, and if you can, get one or more large 4k monitors, and split
       | your IDE into 4 quadrants! It's certainly possible to hold a
       | codebase in your head on a small laptop screen, but being able to
       | see the code you're working on alongside its dependencies and
       | dependents makes this far easier!
        
         | lanstin wrote:
         | This is one of my standard interview questions, how do you
         | familiarize yourself with a multi-million line code base with
         | partially correct partial documentation (in addition to
         | generated documentation). Looking for interesting tooling, and
         | stepping thru the framework code to see how it all comes
         | together (and enough concrete details to verify the claimed
         | work history).
         | 
         | I'm not sure about the advantage to seeing all the code at once
         | for understanding it. Surely for refactoring or massive
         | editing, but to encode it into your brain, I feel the crucial
         | thing is more abstract than actually seeing the code.
         | 
         | I personally find the large screen is something that's more
         | useful when I am moving between different low-concentration
         | tasks (confluence, slack, teams, emacs, whatever) but for deep
         | problem solving/thinking, just 1 window I can see everything
         | completely with a full-screen emacs is more conducive. I assume
         | other editors than emacs would have this same facility.
         | Extremely rapid answering of different questions about the code
         | via LSP type facilities is more key than seeing a bunch of text
         | at once, so I can follow the flow of my thoughts. Sometimes,
         | when I'm trying to make sure that 2 or 3 functions have the
         | same flow/logic (yeah, haven't found the abstraction to replace
         | them with) I'll want a big side-by-side comparison of 2 or 3
         | places at once.
         | 
         | This ability to follow into third party code you use was one of
         | the initial attractions of Gentoo - you can build all from
         | source, keep source and symbols around, and edit/trace the code
         | for anything on the system. Reading a lot of code I think makes
         | my own code better.
        
       | rswail wrote:
       | I'd add "Focus on the Nouns, not the Verbs", or more prosaically,
       | the data and the changes that occur to data, not the process of
       | changing it.
        
       | PaulHoule wrote:
       | I'll call out                  7. Don't have multiple people
       | editing the same piece of code.         You never understand
       | other people's code as well as your own. No matter        how
       | thoroughly you've read it, you've only read it, not written it.
       | So        if a piece of code is written by multiple authors, none
       | of them understand        it as well as a single author would.
       | 
       | On some level it's true but it is also true that most of the
       | world's code is in some sort of maintenance mode and the original
       | developer is not always available. When I work on code that I
       | think would be difficult to maintain I write a lot of comments to
       | explain invariants that should be followed or anything strange.
       | (Hmm, there are three different kinds of "row" talked about in
       | this source file)
       | 
       | If you have a front end-back end system and you want to do
       | something simple like add a field to a form there's a strong case
       | for a single developer or maybe paired developers to make all the
       | changes to make that change happens as opposed to assign two
       | developers (not paired) to communicate with each other about
       | doing the task, or worse yet, assign two _teams_. You might have
       | had two people build out the front-end and back-end systems but
       | for sustainable maintenance one person should be able to ship a
       | feature.
        
       ___________________________________________________________________
       (page generated 2024-09-18 23:01 UTC)