Hacker News new | past | comments | ask | show | jobs | submit login
How to become a dramatically better programmer (henrystanley.com)
248 points by henryaj on June 11, 2018 | hide | past | favorite | 59 comments

There's one piece of advice specific to programming which is missing from this listing: Read lots of code in lots of different languages. Aim to read, as a general rule, about twice as much code as you write, and to be able to read twice as many languages as you write in typically.

Edit: The average piece of code is written once and read hundreds to thousands of times by people. It's not unreasonable to think of code as a read-oriented activity, not a write-oriented activity.

This is a suggestion I see given a lot, but something that falls flat to me personally. The analogy I would put forth is that it feels like reading lots of different furniture assembly instructions in the the hopes of getting better at assembling IKEA furniture. Such a thing is informative, but probably not nearly as much as just assembling lots of IKEA furniture. To those who make it a point to read lots of code, are there approaches you take or methodologies you use? Or certain things you look for? Or concepts you compare to them to in your native language?

More like encouraging the creators of IKEA furniture assembly instructions to read lots of other instructions so that they can get better at writing instructions.

An analogy for improving one's ability to assemble IKEA furniture, using this pattern, would be to watch others assemble all types of furniture in order to improve your own abilities. Wouldn't it?

I think the idea here is to look at the code for the libraries you're using. The documentation might lead you to believe it behaves in a certain way, but the actual code is the true source of truth. Open it up and have a look instead of wondering.

Something I liked about working at Google is that I never had to wonder what the lifecycle of a certain request looked like. I could always open up the code for the server I was trying to talk to, and piece together what "internal error" really meant, or what "optional" fields were actually required to make the request succeed.

That's excellent point. When I digged into express source I understood several things like how it extends the response object via prototype to add methods like send etc. Also you will understand how exactly next function is implemented and then you can literally understand anything about the library.

A great viewpoint, but not what I think the objective was. Code needs to inform humans first, inform computers second. Because of the human cant understand the code, they can't change it, and being able to change code is what decones maintainable and "good".

To figure out how to write code that informs humans well, you have to read code. See what works and what doesn't for someone who is not the original author.

Being usable without changes (what you describe) is nice, but not what most people mean when calling code "clean" or "maintainable", or even "good" (that might imply a good API, but not the code itself.

When I used to read a lot of code for its one sake, maybe the majority was in books and papers (like SICP, Norvig, Knuth, Kernighan & Whoever, "Functional Pearls", ...) which explained points the author thought especially important. There's lots more great code to read without the extra commentary (especially nowadays) but you do have to work harder to find the lessons.

To me it was especially valuable to always be asking "how might I write this better (in some sense)?" whether it's great code or not. When it's especially good you'll still think of plausible improvements, and maybe the lesson is in figuring out how they're not actually such an improvement as you think.

> how might I write this better

That's an easy one for me - get to read a lot of crappy code, unfortunately...

I think this is a good point. I have mostly gained knowledge of software design by doing this, because once I work through a process that might happen in the code, I can infer the reasoning behind design decisions.

I am still learning myself, but so far, reading production code now seems much more valuable than the pseudo code often found in blog posts. They each serve different purposes but I have found myself reading too far into pseudo code when trying to understand a concept can add unnecessary confusion. With production code, what you see is what works.

And that just gave me an idea: programming books that only use production code! It might do less handholding and would certainly be harder to write, but maybe the value would make it worthwhile.

I thought of this, too, but what I decided I wanted was a book that wrote the simplest working version of some useful thing (basic HTTP server, JPEG codec, basic CRUD web app, etc.), explained it clearly, then gradually "hardened" it, version by version, into true (actually in use) production code. Each new version would introduce an issue, possible ways of dealing with it (each with pros and cons), a well-explained choice, the code that implements that choice, and a discussion of the implementation. Then on to the next issue and another upgraded version of the code until arriving at the actual, thoroughly real-world-tested code in use in some popular system.

Working through a few of these would really feel like apprenticing with a pro.

All fine points. I don't think it could work like that haha.

Maybe it could just merely point to real-world examples, without so much detail. I dunno.

The GP's suggestion of revisiting code reads to me a bit like the mental equivalent of opening a file to read one line, closing it, opening it and fseek()ing, ad infinitum. Works - and I totally get the GP's approach of repeated "passes" distinguishing increasing levels of significance - but in I've learned the hard way that the overall context switching overhead of this particular approach is so immense that my eyes would glaze over long before I took away anything useful. The real answer there is to inhale the overall structure and reach conclusions about what to prioritize. This most definitely requires a good working attention span, something I've only had access to more recently.

As for your idea, what about: pick a project you think highly of, or find a bunch of code you've read good reviews of¹, and write a reference work on it over a period of time. Maybe publish for free, maybe don't.

I suspect (having never done this idea, and only thought about it for a short time) that the initial tendency could be to simply explain what's going on in the code, in the same sense of "but these comments explain what the [well-written] code's already clearly describing".

What would be even better would be to make the reference serve an introductory context, and allow newcomers wanting to understand the code to use the reference as a fast-track guide. (Oh! I actually have a (maximum-difficulty :D) example of what I mean! 1st two paragraphs: https://bugs.chromium.org/p/chromium/issues/detail?id=671498...)

This would obviously be quite a challenge, not just because of the comprehension/study required but because the goal would then be to "get out of the way", as it were, minimizing/eliminating editorialism etc and just providing enough structural "fluff" for people to stay engaged and maintain flow/focus on the topic (which is very tricky for me to do...).

This could actually be a very interesting pastime to consider. Thanks very much for writing your thoughts.

¹: INCOMING: https://news.ycombinator.com/item?id=9899766 https://news.ycombinator.com/item?id=13854431 https://news.ycombinator.com/item?id=9896369 https://news.ycombinator.com/item?id=14462125 https://news.ycombinator.com/item?id=8573291 https://news.ycombinator.com/item?id=327710 https://news.ycombinator.com/item?id=1770662 https://news.ycombinator.com/item?id=12381609

Agreed. There's certainly value in reading foreign code, but IMHO the value is much greater if you spend a bit of time trying to implement it yourself, and only then see how they implemented it and consider what you could learn from their approach.

For me personally, it's _actively working_ with code more so than merely reading it.

If I read code without a concrete goal, I find that I gloss over important details and fail to really grok the purpose and design of the code, and therefore do not learn all that much. However, if I'm actively debugging some code, or if I need to understand the consequences of calling some function in a library, then I feel like I get a much firmer grasp on the code, and consequently I properly absorb the techniques used in it.

So I guess I'd rephrase the advice as: Read and understand all the code you're working with. Build as many systems as you can to expose yourself to more coding techniques. Don't take any tool or library you call into for granted- be diligent and read / understand your dependencies carefully. When something unexpected goes wrong, initially suspect everything in your stack until debug data tells you otherwise.

The average piece of code is written once and read hundreds to thousands of times by people

I've seen this truism before but I never seen any actual data on this. Do you have any sources for this? My intuition says most code is Enterprise/small business code that is written once and read less than 10 times.

Every time someone steps through your function in the debugger, they read it.

Read lots of code, you get better at reading code.

Write lots of code, you get better at writing code.

Programming involves both, so I think they're both needed, and you're right to call out that practicing to read code is not as often done.

One thing I'll add is make sure to tackle progressively harder problems, both in reading and writing of code.

Another one is to cover the spectrum. Go for hard in the small problems, like hard algorithms, tight inner loops, etc to large systems of interconnected components.

And make sure you learn concepts like fundamental paradigms and styles. This should also be done in the small up to the large.

Yup, should have included that, and I agree that it's essential to read code to become a better programmer.

I'm actually thinking of building a tool that would automate this a little - maybe it would find some code from a well-loved OSS library and get you to mark it up in the browser. Once you're done, you could see other people's comments on it.

I have just experienced a massive boost of understanding in the design area, as a result of reading code instead of a book before bedtime. I wish I was doing this sooner. I can’t speak for the “in lots of different languages” part because I really only know Python and C#.

I would say read and write twice as much code for learning as you do for work.

Like most learning, you have to apply the concepts for them to stick well so just reading isn't helpful until you've practiced the ideas yourself to cement them in your mind.

I kinda want to start a reading club for code. Somewhere between a book club and a comparative lit class. I’m pretty curious how many PRs a group like that would end up filing.

I suppose this article is about programming rather than software engineering but at the same time, I find there are a lot of these articles with heavy emphasis on code. I'd much rather people who want to improve their abilities keep an eye on architecture, and I thought the article yesterday about Uber's reasoning for moving to MySql had some gems in the comments, like this one: https://news.ycombinator.com/item?id=12166585. The code is often secondary to the architectural choices. Get the architecture right, and the language or the quality of the code is frankly, a secondary concern.

> and I thought the article yesterday Link?

https://news.ycombinator.com/item?id=17279687. Or I guess rather I should say the article posted yesterday had excellent commentary in 2016 and 2013 ;-). The money quote was " And in this particular case, I'd say they shouldn't be using the database to do locking around sending a receipt. It should be put into a queue and that queue should be processed separately, which avoids the transaction problem altogether." For all the people who mock the GoF book or the idea of design patterns, that right there ^ is a pattern of software engineering that you can recognize and apply. It requires a layer of architectural thinking beyond just the code, but it is the kind of thinking that when I see it in people separates them as senior from not senior.

I think the problem with the GOF book is that too many people used its respectability for too long to justify terrible designs, in opposition to both common sense and the guidance in the book itself. This has tarnished the reputation of what otherwise might be a pretty useful book.

It has some great points.

> When amateurs [play tennis], they don’t edge each other out by being slightly more skillful. Instead, it’s a contest of who makes the fewest huge, gaping blunders.

I actually find this idea relevant to so many areas in life. Most of us are just about average in most of the things. It is frequent that hunting down the most critical "huge mistakes" is the easiest way to improve dramatically in a certain field. I feel like doing so just gives you the most bang for your buck, and therefore should often take precedence over slightly improving more general abilities (through a lot of hard work, usually).

I'm not sure how generally important this point is: perhaps it matters more for contest-style fields than more open-ended ones. The guy who can't "invert a binary tree" for his Google interview also made Homebrew.

This begs the question: What best practices can significantly reduce blunders and are (more or less) portable across languages?

In my opinion, a good start is learning to use filter/map/reduce instead of big nasty for loops.

Fail fast, avoid mutability, write tests

Fail fast in development. Have fallback code for deployment or you will be in deep trouble.

And as all solutions, sometimes the cost is too much for the immutability.

Writing tests is a bit glib...

Big nasty for loops get replaced by big nasty filter/map/reduce calls. Filter/map/reduce may be less error prone than for loops, but if it would be a big and nasty for loop, it will still be big and nasty.

Worse, "nasty" to me means more than just "what was inside the loop is large". It means that the loop itself was nasty, that is, it wasn't just a straightforward iteration over a collection, or a loop with fixed bounds. That means that it's harder to just replace it with a filter/map/reduce.

I think that for loops can be hard for beginning to intermediate programmers, but with experience, they get easier. I don't remember exactly how many times I've messed up a for loop in the last decade, but the number is either 0 or 1. It's not 2. "Once in a decade" isn't exactly the source of large-scale bugs that people often claim for loops are.

Note well, though: This is not my first decade as a programmer. The number was much higher my first decade. Filter/map/reduce may be easier to learn. You may in fact get to "one bug per decade" faster with them.

You seem to be comparing a well-written, large for-loop (that has complexities dictated by true constraints, rather than programmer skill) with a poorly written functional chain. This isn't quite fair.

The functional style nudges the programmer to think in the smallest semantic steps of transformation, in a way that the for-loop doesn't. Of course the programmer can ignore the nugde and write the same messy for-loop using functional constructs, and a good programmer can equally well write a big for-loop in a very manageable and easy to read style.

But for an unexperienced programmer seeking general advice, I feel that recommending the functional style (with an additional urge to "listen" to what the style is trying to get you to do) is a pretty safe bet.

"It is often easier to not do something dumb than it is to do something smart." - M. J. Ranum?

>It is frequent that hunting down the most critical "huge mistakes" is the easiest way to improve dramatically in a certain field.

A very well known path to improvement in chess, for example. That's why you are encouraged to train tactics, tactics, tactics.

Also an effective chess AI approach, minimax. Algorithm doesn't try to win but tries to cuts off paths that lead to the worst outcomes.

With amateur athletes it’s a balance of both. You have to put in the hours. You can film your gait or your stroke all you want but you have to practice first.

A lot of your practice is trying to cement the last four things you learned. You put in the hours and periodically you check in about improving your technique again. If you feel stuck in a plateau it’s probably time.

The things listed are all nice and all, but here's what I've seen result in dramatically better programming: Know your problem domain. There are, in basically every field of programming, some very domain-specific problems which you need to know about, that will be much more important in whether or not you do good programming than any of the things listed here. If you're handling money with your software, you need to know about different things than if you're not. If you're making front ends in government or legal domains, there are huge implications that will impact very low level technological choices. For example, are you allowed to do a DELETE? Or do accounting or legal requirements mean that you have to make a new row, which is the correction to the original row? If you don't know that about your problem domain, it doesn't matter how good you know your language, debugger, etc. Just one example, and every problem domain has them.

Know about your problem domain. It has a dramatically bigger impact on your programming than any of the things listed in this article.

"Start viewing willpower as a resource that can be depleted, and make sure you aren’t wasting it on trivial things."

The concept of ego depletion is not as certain as it once was.


The basic idea of "I'm tired, and more likely to make sloppy choices" is pretty well established, however.

Some of this hinges on work environment, not just habits. I was glad to see the reference to Cal Newport's "Deep Work", but it's hard to do deep work when you have three meetings scheduled that afternoon.

edit: I consider Deep Work a must-read for anyone serious about doing hard things.

FYI your startups website gets flagged by chrome as your certificate date being invalid.

Yeah, I know. I hit a wall on it and have been taking a break before starting it back up. The cert expired, and when I say "I'm not doing anything with this", I need to mean it.

It'll get fixed in about a month; I'm planning on firing the machine back up with a fresh perspective after the 4th of July.

Things that contribute to the ability to get things done:

- ability to solve problems

- understanding of the data structures you are working with

- ability to formulate solutions that requires less code

- doing one thing at a time

- knowing how to use the debugger

- experience working within the architecture of the application you are working on

About 1,000 other things too.

There was one point where I just disagreed from my standpoint. The pomodoro technique.

In programming for me, it never really worked. I mean, let's say I just got an idea for a specific piece of code, if the pomodoro triggers a break, I just lost my line of thought. When I'll get back after the 5 minute break, I will no longer remember what I was doing or how exactly I thought doing it.

I don't really know if for you folks it worked, but for me for this reason it didn't work and it never probably will.

I can believe it works differently for others, but for me the pomodoro technique works very well. When I'm "in the zone" and banging away, I am much more likely to _fail_ to notice that there is a much easier approach that I could be trying. Interrupting my work occasionally for a few minutes, and then coming back, vastly increases the odds I will realize, "oh wait, instead of spending the next three hours doing this thing here, I could change a bit of this other part of the codebase and not even need that."

Just my experience.

I wonder if there is any data on the efficacy of these techniques.

Deliberate practice makes sense to me, but I really wonder if the others require purposeful study or if you just pick up metaskills if you hack away at your project.

Heck, in 5 years I might be reading an HN article: "Deliberate practice isn't all its supposed to be." And an article about how the original deliberate practice study was flawed because it just forced people into hours of extra practice or something.

It's a question of keeping track of which is the map and which is the territory. The point, I believe, is to be continually and relentlessly introspective, asking yourself how you can do better today than yesterday (and having a realistic view of what 'better' is).

Deliberate practice can be helpful in forcing such introspection, if you let it. If you just go through the motions, it won't help much, certainly not sustained. You end up with cargo-culting.

The classic example is the GoF Design Patterns book - it was a very good book, at least at it's time, you could expand your horizons a lot by reading it, and get a lot of tricks up your sleeve to deal with certain tricky, but relatively common, situations. But if you start actively to impose design patterns on code that doesn't need it so you can tell yourself (or your peers or whoever might listen) that you're a design patterns kind of guy, you end up with overly complex and hard to read (ie: bad) code.

I think the mistake here is that you think “purposeful study” and “pick up metaskills as you hack away at your project” are not both examples of (potential) deliberate practice.

What makes either one deliberate or not is whether you make a point to focus on how well it was done, how it achieved goals (or not) and how to improve. That’s the deliberate part and that’s the part that makes the difference and is missing by default.

As long as you’re constantly evaluating and making attempts to improve, it doesn’t matter so much in what context you’re doing the thing, whether it be for fun, for learning, for work, or whatever.

“Practice” simply means you’re actually doing a thing as opposed to studying it theoretically. Though I think many people have a habit of reading into that word the notion that what you are doing is a throwaway or has very low expectations of success or importance. But keep in mind that what a doctor does every day is called “practice.”

Difficult to control for variables if you were going to try and conduct a proper scientific study.

The main proof we have is the results of people that have implemented the techniques described. But there are also other places where it's possible to take a closer look with a fairly large number of samples. Competitive multiplayer gaming is a good example, there are certainly competitors in that field who practice on a 'raw hours' basis and those that focus on individual skills in an ordered fashion. All that's needed is to go out and match that against their respective results.

Surely five minutes spent learning a debugger isn't really in the spirit of the rest of the post. It should be more like "read the entire man page of the debugger and try some of it out." Five minutes is only enough to learn how to shove the work-flow you already know into a new tool, not to learn the work flow that is actually suited to that tool.

What are some of the "Meta" things for a web programmer (backend in dotnet) who is also interested in systems programming?

Perhaps things like reflection, the clr, and how the clr implementation interacts with the os.

Find a mentor, someone who refined their craft for years and only through experience they know the shortcuts and right implementations. My coding mentors really kicked me in the butt from being a cowboy self-taught coder to one who needs to read more about the theories of coding and philosophies behind them to up my game.

> Everyone’s strategy, therefore, should be to identify and eliminate these big, costly rookie blunders, one by one. This is far more effective than getting quicker, hitting harder, or making that one brilliant shot now and then.

Brilliant observation.

What about dealing with a-holes. That's as important of a skill as anything listed in the article.

I found some it useful for me.

Hacker News paydirt!

The biggest barrier I often see is that people won't get in there and try things. It's code, and it's under version control. You can do basically anything, and the worst usually is you have to pull down a fresh copy. So much indecision and debate and time wasted, because people won't poke at things to figure out how they work.

when i approach a new largish (say < 500k lines) codebase I'm supposed to be working on, I'll often just go on a giant tear and completely refactor it to be the way I like it.

in the process I end up breaking several foundational assumptions and have to spend even more time carefully backing out my changes and trying to get it to work again.

rewriting the whole thing isn't your job (usually). check out a fresh new copy. now you can make a pretty informed decision about the best way to make the changes you're actually being paid to make.

or, more rarely, now I have a less tested but substantially smaller version (once I replaced a 500k library with a 15k version by gutting all the abstraction layers), and if its not a maintenance smallest-delta kind of deal, I just go with that.

anyways, the shortest path to the goal isn't always to chip away gingerly at the surface with your eyes closed and your head turned away - take a nice deep dive, you can always swim back up.

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