This is the fourth time I have tried to write this essay. It just hasn't been coming together. First I got stuck writing and rewriting intros, then I wrote a long, rambly, ranty piece that I think has the potential to be the core of a good proper essay but has some spicy bits in it that need time to cool off before I can edit 'em properly.
So. No intro, no explanation, no background, no framing, we're just gonna jump right into the things, and I'm going to talk about what I did, why, how it worked, in roughly chronological order. Then a little bit about what I've been learning in the last year or so specifically, and what I want to learn next. Also, why you need to go slow to go fast, but sometimes you need to go fast to go fast.
The Things That I Have Done
Note that these ratings are all based on my subjective experience of speed. I haven't worked out a good way of measuring it.
The OG. I have a 90+ WPM typing speed. This is mostly because I think that typing games are genuinely fun, and have since before I learned to code. (Let's also thank for that: Writing multiple novels in middle school.) Really useful skill – even if I don't necessarily spend a lot of my time typing, the lag between "I think of what I want to write" and "I actually write it" is an important part of the test-refactor loop, and you want that loop to be short.
This one's a bit weird to rate because it's not strictly about code but I'll give it ⭐️⭐️⭐️⭐️⭐️ because typing games very consistently produce typing speed improvements and typing speed improvements will help basically everyone.
This is how I first learned to write code. Specifically the Ruby courses. I'm not sure I could have learned to code without a machine teacher like this. The feedback is instant. And I don't have any guilt or weirdness about a human being grading me or seeing my mistakes.
It might be cheating to include "learning to code in the first place" on this list but I still go back to this style of structured, exercise-based course periodically, and I do think it's useful for drilling the basics really well – conditionals, control flow, library methods.
I'd rate this one ⭐️⭐️⭐️⭐️ (out of 5). It's not the absolute most effective technique I've tried (we'll get there further down) but "small exercises with instant feedback" is a really good way for me to learn.
Reading Reference Documentation
Early on I bought a couple of paper reference books on Ruby. I think one was the pickaxe book and the other was a more reference-y guide to the standard library. Recently I've been learning Elixir and I picked up a similar set of books.
I also read and have learned a lot from online documentation but I really prefer books for foundational learning. First, I find it easier to read a book all the way through than a website. There're just too many distractions on a screen. Second, once I've read a book I find it faster to look up things like "what're the string methods" in a book because the book functions at my desk as an additional screen, and I can find things by section rather than having to know the correct search terms.
I'll call this ⭐️⭐️⭐️ for paper documentation and ⭐️⭐️ for online documentation. I think this is an important technique but I don't think it makes "top five best ways to learn."
I wanted something on here that I would rate a ⭐️ so, sorry big long online courses but you are garbage. I tried to learn some "computer science fundamentals" this way and I did learn something (big O notation!) but not any better than I would have just reading a book. They combine the worst parts of formal education (graded exercises with long feedback cycles, huge amounts of setup before you get to the actual good bits, slooooow) and the worst parts of online education (ignorable!).
Making 'em cohort-based doesn't, in my experience, help that much, because frankly the median class-taker just isn't that useful to talk to.
I've paired a lot. I am dramatically faster at raw input speed than most people I work with because of it. There's so much that you pick up just by osmosis – command-line tools, operating system fundamentals, details about programming language, testing techniques, note-taking meta-habits – there's just a crazy amount of stuff that people know about getting work done that they would never think to write down but that you can pick up when it comes up while working.
As far as I can tell there's basically no skill level where this levels off, at least as long as you change up your pairs every so often. You might eventually run out of things you can learn from one person but get a new pair and then bam – it's level up city again.
Pairing is especially good for things like test-driven development which are really context-sensitive and have a lot of traps you can wander off into. I had been trying to do TDD basically from day one of coding but I didn't really "get it" until I interviewed at Pivotal and experienced it live.
Reading Books About Refactoring & Other Practices
At some point at Pivotal I got told to spend some time outside of working studying and for whatever reason I decided that meant I should read Martin Fowler's Refactoring. Got excited about having names for techniques and applied 'em for a while. Every so often I read something else like this – stuff that's about architecture or structure or is otherwise applicable across many languages.
Probably the most useful book I've read in this vein so far is Working with Legacy Code. I read that one about a year ago for a project and it was an extremely valuable way to spend a couple of hours. The best part of that one – and I think something that this kind of book can get at that nothing I've written about so far can – was it covered both technical and social practices. There's a lot of technical nitty-gritty about bringing wily code under test, but there's also a chapter on "what to do when your team is sad & frustrated by the codebase" that's just dynamite, especially in the context of the crunchier stuff.
That said in general I'm gonna rate this a ⭐️⭐️. I might be under rating how effective this class of techniques is but in general I think it's... fine? I don't regret spending time this way or anything but I just don't think this is as powerful as almost anything I've talked about so far.
By this I mean, a project that is small enough that you can get it from start to, if not finished, at least "a visibly functioning thing" within a few weeks – ideally even shorter. You work on them with the intent of learning as much as possible, but even more so with the intent to finish.
I learned most of what I know about Oauth in about two days trying to write a web application framework in Google sheets – I didn't succeed at the web application framework but I did successfully authenticate with Google. My Zero app is a large-ish example of this kind of project; the intent was to build a real working Phoenix app, and to actually get past the "it's all auto-generated forms and things" stage and into the "having to make decisions about restructuring the codebase" stage. I also tried to write an implementation of the Spring '82 spec for similar reasons – I didn't ever finish that one, but along the way I wrote most of an Otel Reporter for ExUnit and learned a ton about both Otel and the Elixir application model from that.
It's not necessary to actually finish, because a lot of time when you start what you think is a simple project you're going to end up discovering a really hard sub-problem you need to solve along the way. That might end up revealing that the project you thought would be a weekend or a week is way bigger than that, but you should at least finish a sub-problem or two.
I avoided doing this kind of thing very much for a long time because I had a hard time thinking up projects. Thinking in terms of "friend catchers" mostly cured me of that – I have a rough list of things I might build that's longer than I could probably finish in a lifetime. Some people have a standard "thing" that they use to try out new languages and frameworks. "To Do" apps work well for web application frameworks. Simple chatbots – like C.J. Silverio's oft re-implemented "screambot" – work well for languages.
A lot of good courses are organized around projects like this. There's Rebuilding Rails, where you build your own version of something you use a lot. Julia Evans recently released an example in this category, "Implement DNS in a weekend."
I've been spending a lot more time on this in the past two years and it's the only other thing that I think is pairing-level for me in terms of technical progress.
I haven't actually been doing this recently but there was a period where I had a Vim commands flashcard deck that I was practicing daily and that was really great for improving my Vim navigation. I also fixed some of my problems with basic input in Golang by making flashcards for things like for loop syntax and the order of a function signature.
I've been meaning to start this practice back up again but I keep procrastinating on picking a specific flashcard tool and setting up the initial decks. I'd ideally want something that relies on actually typing into a code editor or an editor-like interface, and setting up "type in the answer" cards in Anki and similar tools feels just a little bit more awkward than I want – I keep thinking "oh I should build something better," and then neither setting up the decks nor building the tool.
So I'm gonna give this one ⭐️⭐️⭐ – it'd be a ⭐️⭐️⭐⭐ just on raw effectiveness, but I'm deducting a star for the setup and maintenance friction that keeps me from actually doing the thing consistently.
I have learned just oodles of stuff by writing super crunchy technical posts about things like "exactly what every line is doing in a single class in Rails." I don't write things like that quite as much as I'd like because I'm not sure anyone else gets all that much out of them and they take a long time, but man they are a good way to really nail down the details in my mind.
Part of the motivation for Mastering Neovim was to get to spend some more time on this kind of writing. My guide to building your own Vim configuration got some good feedback, triggered a couple of signups, and gets a surprising amount of search traffic, so it seemed clear that there was interest in my audience, as well as an opportunity to attract new folks. One of the nice things there is that I've gotten corrections from readers – I learn a lot by doing the research and then I learn even more once I release the thing.
Again, though, these take a lot of time to do well, and I think you have to be really clear on what you want to learn for that use of time to be efficient. ⭐️⭐️⭐
I should probably write about what I'm learning at a slightly lower fidelity more often but for whatever reason it doesn't feel like a great fit for this newsletter? Maybe whenever start Season 3 I'll go more in that direction though.
What I've Learned Specifically in the Last Twelve Months
In the past year-and-change I've put deliberate effort into four specific categories of technical skill.
- React (and web stuff)
- Phoenix and Elixir
- Testing (& architecture techniques that support testing)
Vim (really, NeoVim, but you'll note that I'm very inconsistent about how I refer to it) has pretty much been 100% newsletter & writing driven. I've done research in order to answer questions. A lot of this has been driven by conversations with and questions from friends. I picked Vim as an area of focus because, as I mentioned above, it's a good intersection between "things I like learning about" and "things my audience is interested in."
I learned the React that I know now for a contract, and mostly learned by reading docs and then applying what I was reading to the codebase. If I keep doing React work I'll probably build something small from scratch.
Phoenix and Elixir have been a mix of books and practice projects. I'm looking for opportunities to do more professional work in Elixir (e-mail me if you need an Elixir contractor!) and I keep meaning to set up some flashcards.
Testing has been a major area of focus for me this year. It's a little bit hard to talk about because it's so wrapped up in the language work I've been doing. In both of the Phoenix and Elixir a lot of what I've tried to do is learn how to write small, fast tests, and how to write them quickly. For me at least this is one of the highest ROI individual skill for writing code quickly – after stuff like control-flow basics, and dependency management – because if I can write a test I'm like 80% of the way to writing the production code that makes that test pass.
How do I choose what to learn?
This probably deserves to be its own, longer post, about rebuilding individual-contributor technical skills after a stint in management, but the basic answer is:
I spent a lot of time thinking about what I like and don't like in tech jobs, and I realized that what makes me happy is writing code, and that I would get more opportunities to write code if I got better at writing code, so I started putting a lot of deliberate practice into getting better at writing code.
Between spending time in management and working a lot on cloud infrastructure my 2021 skillset was (and still is in many ways) really heavy on skills that support "app development" and relatively light on "actually writing the thing that does the thing." I can set up CI pipelines and write Bash scripts really well but felt like I struggled a lot more with like, implementing an API endpoint.
Even after I'd decided management wasn't for me there was a minute where I thought I wanted to be an SRE but it seems like jobs with that title are mostly writing Terraform and holding a pager and I don't like either of those things. I'll do a little bit of both when it's necessary but the thing that draws me to SRE-ish roles is learning a ton about the weird details of a system, not so much setting up yet another load balancer for yet another service – or worse, refactoring the Terraform to make it easier to set up load balancers for services. I'm also interested in telemetry and observability, but what I really like is using telemetry to understand an application that I've written.
So I've generally been trying to reorient my skills away from "deep infrastructure" and towards "application development," and especially towards "building prototypes." Like I said though there's a lot more going on here and this is a big part of what those three previous attempts at writing this post ran aground on – maybe I'll tackle the topic next week.
Things I'm Planning
Out of the things I've mentioned above, I intend to spend more time in the next year on practice projects and newsletters. I'd eventually like to pair more for learning purposes, especially on practice projects, but there are some logistics I need to figure out first before I can make that a regular thing.
At some point I want to do Virtual Recurse Center Batch (I hope they keep doing them!) but I haven't been in quite the right spot to dedicate six weeks to it yet. This is something I'd like to do in the next year or so, though – at some point, surely, I won't be moving every year!
And then there's the flashcards. I have had "make X flashcards" for some value of X on my todo list for basically the entire 12 months. Maybe later today I'll actually set that up? What I'd really like is to a flashcard system working entirely within Vim but that's one of those "small" projects that probably has a bunch of hard sub-problems inside of it.
Going slow to go fast vs. going fast to go fast
This is a little bit of a side note but it comes up whenever I think about "getting faster." Sometimes getting faster requires going slower than you normally do – taking your time to look everything up can be a really useful, especially if you spend a lot of time writing code you don't entirely understand, or skating over some complexity that you know is going to bite you someday.
But sometimes getting faster requires going fast– faster than you're comfortable with. I got a lot faster when I started just "trying shit" more – writing a bit of code that seemed plausible and then just seeing if it compiled. I try to go look things up only after I've tried one or two naive things. This saves me a lot of time looking things up – sometimes I straight up guess right, and often I'm able to work out what I should be writing instead just from the error messages I get.
It's a little bit like running. Most of your training should be pretty slow, for aerobic base building. But some of your training should be "race pace" or even faster, both because there are certain physical adaptations that only happen when you're working at the edge of your capacity, and because to perform well in a race you need to be familiar psychologically with what it feels like to run hard & keep running while you're tired.
This is why I emphasize "finishing" when I talk about practice projects. I think a focus on getting something complete, even if it's small and kind of bad, is a helpful counterweight to my otherwise perfectionist tendencies. It's really easy for me to focus on doing things exactly right and to spend a lot of time figuring out some detail that doesn't actually matter. I have to deliberately push myself in the opposite direction – making things that kind of suck, but finishing them and shipping them and moving on to the next thing.
📣 What are you learning?
Leave a comment or send me an e-mail. What technical skills have you been working on recently? Why did you choose them? What learning techniques have been effective?