I recently was reminded about the excellent book Crafting Interpreters while scrolling through my TikTok feed. I had worked through some of it a few years ago, but I’ve been having some different thoughts about it after getting a some more experience under my belt.
Background
I had started working through it in early 2020 as a fun side project to learn more about interpreters and programming as a whole. I made it through a decent chunk of the first section of the book (making a tree-walk interpreter in Java) before leaving off - this is a bad habit of mine.
I think part of why I left off back then was I got distracted with a different “write-your-own-compiler” series from Nora Sandler. I definitely recommend both resources to people interested!
The main change that I have undergone professionally in the last few years is the shift from more Junior to more Mid/Senior positions. I was working at my first full-time position (that was a continuance of my internship). My responsibilities had expanded to include some server admin stuff, but still primarily were being the sole developer for the ~5 business applications assigned to me among the many my team owned.
Since that team didn’t do any sort of code review - formal or informal - I was primarily left to my own discovery when looking at how to write clean, maintainable code.
I ended up switching companies 6 months later, then quickly started working up that company’s ranks to fill a pseudo team lead role. I continued my self-study about clean code. In the new company’s 15-year-old codebase, I was able to see code that was clean, and code that needed a lot of refactoring. I ended up spend a lot more time mentoring junior developers and reviewing code than I had before.
Cut to now: I am the lead for a team of 2 developers, one of which has around a year of experience in industry, all in the current organization, and without a technical mentor inside the organization until I started a few months ago. We also have a 10-15 year old codebase that definitely feels it.
Code Organization and Structure
Something that I feel Nystrom does really well throughout Crafting Interpreters is structuring the code in a really readable and maintainable way.
Going through the JLox implementation, you really get the sense of everything having limited responsibility and being extremely readable. Nystrom even jokes that “it’s helpers all the way down” in an aside in the later chapters, noting that as an intentional design decision to improve code readability. Functions are generally kept concise, and classes feel very focused.
In our current codebase, there are many classes that are 10s of thousands of lines long, with functions that are hundreds of lines long and more responsibility than I can wrap my head around without a pot of coffee.
Even disregarding the benefits of understanding how a programming language works under the hood, I’m excited to talk about the JLox implementation with my junior, and how we can apply the same ideas in future refactors and new development.
Units of Work
I also really like the chapter structure of the book. Most programmers get used to taking a Jira ticket, working it, and then considering it “done.” One of the constant struggles that my team at a previous company felt was being able to size tickets properly. I was lucky to attend Scrum training with the entire product development organization at that company.
Going into the training, one of the main issues that we had was many tickets would be pointed at WAY more than an individual developer could reasonably do in a sprint. I think about everyone realized we needed far smaller units of work to be able to reliably deliver an increment within the timebox of a sprint, but we all really struggled with the mindset shift.
One of the things that our Scrum coaches suggested was trying to do vertical slicing: batch the database, backend, and frontend work for a small application change into a ticket rather. Frequently, developers get into the habit of doing all the database work for a whole feature, then the backend, then the frontend. If you are able to keep features small, then this works OK. But as an application gets more complicated, that stops working.
Nystrom structures the chapters and sub-chapters in a way that very closely resembles vertical slicing. You introduce a new language feature, then fully implement it and test it before moving on to the next one. It gives the book a really good rhythm, and I’m hoping to establish a similar rhythm for work with my team.
Abstractions
One of the first things that stood out to me as I finished up the JLox implementation I left off on was the quality of the abstractions in the book. This kind of ties back into the code organization, but having good abstractions for the concepts your domain encompasses makes developing easier.
One of the things that my junior has been focusing on in his training lately is abstraction. That’s a big part of the reason why the abstractions in Crafting Interpreters stood out so much: they are small and really understandable. After all, they are all concepts we as programmers deal with daily. This is another case where the JLox code is useful beyond just showing how languages work - it’s a really good, relevant example on how you can apply the concepts of inheritance, abstract classes and interfaces inside a relatively complex Java application.
What’s to come
I still have quite a few chapters of writing the bytecode virtual machine clox
, so I’ll likely have more thoughts as I wrap that up. I do have to say, I’ve found it refreshing touching C code again after not really using it since college.
The TikTok I mentioned at the beginning was talking about how it’s important to code for fun. My JLox and CLox implementations aren’t going to be useful as a tool for anyone, and that’s OK. They are just a way for me to get a bit more joy from programming and maybe learn a bit along the way.
My next plan in interpreters land is to look at writing my own VM in a different language - maybe Go, maybe Rust, TBD.