I can't think of any code which I read and reread. For that matter, there are few books that I reread frequently.
The closest is for a software library which I use only occasionally, where it takes a few non-intuitive lines to get things set up correctly. Someone developed an easier API for it, which is closer to the way I think about the problem. When I need to X, I consult that software to see how to implement it.
Usually you don't just go read source code for the heck of it. You read the source code either to contribute a feature or fix a bug. There's no point in reading the src otherwise because you won't find a good enough reason to try to fully understand it.
skimming through the source is one thing, but reading with the intent to thoroughly understand it to a level that you can comfortably modify it is something else.
It's the only language I've used so far where I feel comfortable going straight to the github source and figuring out how a library works instead of searching stackoverflow. In fact, since I've been writing a lot of code in Go, I've noticed my SO usage has dropped considerably.
Without having a specific goal it's difficult to read code. "Learning how it works" is not precise enough. It's not useless you learn stuff but you have nothing obvious to show yourself as an achievement. Rewritring or "deconstructing" the code is helpful to understand it.
I've been reading the code for Pound lately, trying to get a handle on where I'm getting some latency events. I'm really wishing I had something like dtrace for OpenBSD, since all I have is ktrace. You can get timing info out of it, but to get what I really want, I'm going to have to add some utrace calls at the appropriate points.
Or because the code is bad and you keep getting issues because of it.
I think some of the best code I use is that which I don't look at frequently, because it means it's behaving well on its own / in concert with other components, and is abstracted enough that it doesn't require changes.
This is actually one of my goals too. If I can recommend, the godot engine[https://github.com/okamstudio/godot] is my main source of inspiration and learning.
If you're not already following it, check out Handmade Hero (https://handmadehero.org/). Great, free video series from Casey Muratori that guides you through building a game from scratch.
The code I happen to use at work. This might sound silly, sometime I read code just to understand one specific behavior, say one method and come back to it later because I need to know something else.
I pretty much read the code for every dependency I use. It helps me understand how it works and what the docs are fibbing about.
If I can't read the code because it's proprietary, I always strace it to see what it's doing.
For me, it makes it easier to reason about the behavior of my program.
Currently my favorite code to read includes: key/value databases written in C and other "lower level" languages, and the golang standard library packages.
Would you mind going into detail about how you use strace in that context? Or about how you learned to do that? That's a very handy skill which I need to learn.
Start with htop. You can get that package from apt in a debian-based distro: `sudo apt-get install htop strace`.
When you launch htop, you will see a list of all your running procs. If you select a proc with the arrow keys, you can hit "s" to start a strace and then hit f4 to follow the strace as it dishes output.
This won't work for all procs, you will need root to strace certain procs.
This is a good place to start as it's visual and easy to follow. Later you can start traces from the shell with proc IDs and even get system call info from GDB.
The Stripe bindings (primarily Ruby, Node and PHP). The API docs don't show how to do anything so being able to see what is expected and the format from the function header is very helpful when helping out in IRC. Reading through the pull requests is also helpful in learning / speculating on some new features (this is pretty rare though)
I re-visit my old code. Due to how I'm constantly thinking about how much better it could have been. Not that I doubt my abilities. Its just that I wish I knew back then what I know now. Not only my coding skills, but about the problem I was trying to solve.
Django and Django Rest Framework. DRF docs are pretty bad (though as the saying goes, better than none at all), so I end up reading the code to figure out how to do what I need.
I read Django code when things go wrong or to implement e.g. an auth backend, or see how come get_by_native_key() doesn't always need to take a native key as a parameter.
I write a lot of code and have an interest in new languages. I've looked in to Go quite a bit (especially since I spend a lot of time working with Docker) and I guess I might be missing something. Taking a random method here:
Some questions that come to me when looking at that code:
- In general, why is everything abbreviated? Storage isn't an issue and IDEs solve the typing problem. It feels really old school. Plus you lose a lot of meaning throughout the code (r.prevRune is actually an index, so why not reader.previousRuneIndex, or at least r.prevRuneIdx)
- What is int64? A cast? Why would you need to cast what appears to already be an int?
- what is a Rune? Why are we setting it to -1? -1 just seems like a special number that would make this more readable as a constant with a relevant name.
- Where does copy come from? why does it appear to work backwards compared to any other language array copy implementation?
- Why does the last return statement return nothing?
I'm not here to start a language war, I truly believe it is my inability to see the forest for the trees, but I find Go code to be hard to read and understand.
1. Go encourages short variable names. I personally think that the names used in the example you pulled are a touch shorter than I myself would use, and "prevRune" is a little bit ambiguous, but I don't really mind it since the context makes it more clear.
2. Yes, int64() is a cast to an int64 type. The bare "int" type in Go is architecture-dependent, so the compiler will complain if you try to assign an int to an int64 without the cast.
3. Rune is a number representing a UTF-8 codepoint. It's an alias for int32 the way "byte" would be an alias for in8. (http://golang.org/doc/go1#rune)
4. copy() is a built-in function. There's only a small handful of built-in functions defined, and they're all documented here: http://golang.org/pkg/builtin/
5. The last return statement doesn't specify any values because the method signature has already assigned names to those: n for the int, and err for the error, so an empty return means "return the values of the variables n and err", which default to their respective type's 0 values (0 and nil for int and error). Returning the values explicitly still works as seen in that example, and is slightly shorter than "n = 0; err = io.EOF; return".
Go can be tricky to read if you're not familiar with some of the semantics, but once you've ramped up a little bit, then things just start falling into place.
I frequently search Google for a class name and append 'site: github.com' in order to search all the source hosted on GitHub for examples on how others are using said class.
I read the express.js source quite a few times. I wanted to reason out how one goes from raw node to something like express. I found it well written, but to really understand it I found myself making toy versions of each functionality.
http://www.nickstefan.net/blog/view/express-under-the-hood
When I feel burnt out, I find it refreshing to take a break and look at something I've already accomplished. Sometimes that means source code I've written.
Looking through my code reminds that I can do hard things. I can solve hard problems, even elegantly. Other times it reminds me how painful a project was to finish or I chuckle at how BAD of a coder I was back then compared to now. It helps me feel like I'm making progress despite the relentless stack of issues, problems, and assignments.
And when I do feel motivated to be a better coder, I try to explain somebody else's source code to somebody else. It helps me be a better reader and writer.
I used to read the Neo4j source (I wanted to write my own graph database). Then I was always reading the Django ORM source as reference for a Neo4j / Django ORM integration I was working on (https://github.com/scholrly/neo4django).
I've found that diving into a code base is a great way to learn about a new topic or language, and to get rid of the "magic".
Back when I was playing with PLDI, I would read the source code for the simpler garbage collectors out there as well as several I wrote myself. Amazing how touchy and error-prone it can be. It taught me a lot, because programming a garbage collector is as one source who I can't remember off the top of my head put it: programming with your back against the wall. Every memory allocation is serious business.
I read a lot of JRE/JDK source code (through IntelliJ, which probably comes from OpenJDK) just to see how they implement their classes. For example, I found out a few days ago that Collections.sort uses a dual pivot quicksort that I had never heard about before. I'm going to check out some Go stdlib next, I had no idea that was open source. Oh, and possibly the OpenBSD kernel.
63 comments
[ 2.9 ms ] story [ 121 ms ] threadThe closest is for a software library which I use only occasionally, where it takes a few non-intuitive lines to get things set up correctly. Someone developed an easier API for it, which is closer to the way I think about the problem. When I need to X, I consult that software to see how to implement it.
https://www.google.co.in/search?q=code+reading+the+open+sour...
I think some of the best code I use is that which I don't look at frequently, because it means it's behaving well on its own / in concert with other components, and is abstracted enough that it doesn't require changes.
Unfortunately, he's up to day 75 and i'm still on day 11.
If I can't read the code because it's proprietary, I always strace it to see what it's doing.
For me, it makes it easier to reason about the behavior of my program.
Currently my favorite code to read includes: key/value databases written in C and other "lower level" languages, and the golang standard library packages.
When you launch htop, you will see a list of all your running procs. If you select a proc with the arrow keys, you can hit "s" to start a strace and then hit f4 to follow the strace as it dishes output.
This won't work for all procs, you will need root to strace certain procs.
This is a good place to start as it's visual and easy to follow. Later you can start traces from the shell with proc IDs and even get system call info from GDB.
I read Django code when things go wrong or to implement e.g. an auth backend, or see how come get_by_native_key() doesn't always need to take a native key as a parameter.
* Do the tour (tour.golang.org)
* Read 'How to Write Go Code'
* Try to write a simple, concurrent program (I went with a web crawler)
* Read 'Effective Go', stdlib code
And now I'm trying to make the concurrent web crawler distributed! It's a lot of fun so far.
https://github.com/golang/go/blob/master/src/strings/reader....
Some questions that come to me when looking at that code:
- In general, why is everything abbreviated? Storage isn't an issue and IDEs solve the typing problem. It feels really old school. Plus you lose a lot of meaning throughout the code (r.prevRune is actually an index, so why not reader.previousRuneIndex, or at least r.prevRuneIdx)
- What is int64? A cast? Why would you need to cast what appears to already be an int?
- what is a Rune? Why are we setting it to -1? -1 just seems like a special number that would make this more readable as a constant with a relevant name.
- Where does copy come from? why does it appear to work backwards compared to any other language array copy implementation?
- Why does the last return statement return nothing?
I'm not here to start a language war, I truly believe it is my inability to see the forest for the trees, but I find Go code to be hard to read and understand.
2. Yes, int64() is a cast to an int64 type. The bare "int" type in Go is architecture-dependent, so the compiler will complain if you try to assign an int to an int64 without the cast.
3. Rune is a number representing a UTF-8 codepoint. It's an alias for int32 the way "byte" would be an alias for in8. (http://golang.org/doc/go1#rune)
4. copy() is a built-in function. There's only a small handful of built-in functions defined, and they're all documented here: http://golang.org/pkg/builtin/
5. The last return statement doesn't specify any values because the method signature has already assigned names to those: n for the int, and err for the error, so an empty return means "return the values of the variables n and err", which default to their respective type's 0 values (0 and nil for int and error). Returning the values explicitly still works as seen in that example, and is slightly shorter than "n = 0; err = io.EOF; return".
Go can be tricky to read if you're not familiar with some of the semantics, but once you've ramped up a little bit, then things just start falling into place.
I frequently search Google for a class name and append 'site: github.com' in order to search all the source hosted on GitHub for examples on how others are using said class.
Looking through my code reminds that I can do hard things. I can solve hard problems, even elegantly. Other times it reminds me how painful a project was to finish or I chuckle at how BAD of a coder I was back then compared to now. It helps me feel like I'm making progress despite the relentless stack of issues, problems, and assignments.
And when I do feel motivated to be a better coder, I try to explain somebody else's source code to somebody else. It helps me be a better reader and writer.
I've found that diving into a code base is a great way to learn about a new topic or language, and to get rid of the "magic".