I think a valuable takeaway point for those interested in writing their own compiler is to use appropriate tools to get to the end goal as quickly as possible. Lexing, parsing, and code generation are all very interesting and challenging problems on their own. However, when writing your first compiler none of these stages should slow you down. Then, you can focus on the design of the internal representation and optimizations, which IMO will gain you better understanding of compilers.
I remember reading this a while ago, and seem to remember it being quite good. One problem with linking to it is that it’s oinly been “updated” to LLVM 2.7, which is ancient in LLVM terms. It’s likely that one would learn a lot reading this, but there’s a good chance things won’t compile and you’ll need to go seeking solutions because things have changed a lot.