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.
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?
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.
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.
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.
That's an easy one for me - get to read a lot of crappy code, unfortunately...
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.
Working through a few of these would really feel like apprenticing with a pro.
Maybe it could just merely point to real-world examples, without so much detail. I dunno.
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
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.
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.
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.
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.
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.
> 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).
In my opinion, a good start is learning to use filter/map/reduce instead of big nasty for loops.
And as all solutions, sometimes the cost is too much for the immutability.
Writing tests is a bit glib...
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.
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.
A very well known path to improvement in chess, for example. That's why you are encouraged to train tactics, tactics, tactics.
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.
Know about your problem domain. It has a dramatically bigger impact on your programming than any of the things listed in this article.
The concept of ego depletion is not as certain as it once was.
edit: I consider Deep Work a must-read for anyone serious about doing hard things.
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.
- 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.
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.
Just my experience.
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.
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.
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.”
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.
Hacker News paydirt!
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.