[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)