I share some of the criticisms already stated in other comments. The biggest offender here in my opinion is that it jumps to too many subjects. Trees as graphs, yes, but they are a whole subject of their own. This post should broken down in at least 4 or 5 posts. Moreover, its mixes vastly different concerns: byte level optimization is an interesting topic too, but it has nothing to do with graph per se.
On the positive side: relevant illustrations. I also liked that the code implementation is one that use explicit edges. Too often in books, it is either the adjacency matrix or the linked list ones. The former is inconvenient when you need to add a node, and the latter doesn't let you put weight on edge. So these "academic" implementations are not well suited for groking concept by playing with code.
I work a lot with graphs and there is a big value in having a general enough graph implementation. Last week I implemented a trie class with it, and this week a transducer. Both in two hours, debugging included. Other things such as serialization came from free from the library. Of course, that not optimal data structure, but it is fine for my use cases.
In your post, you don't use the fact that trees are graph to avoid duplicate code explanations, instead you explain it first for a tree, then details the graph one. It should be the reverse: how to use a graph implementation to create a tree. And then only if this implementation does not fit the memory or performance requirements of the app, rewrite a more specialized data structure.