Ramblings on 4 years of nvim-surround
During one of my first interviews ever (for a software engineering internship), I had the chance to pick an engineer's brain about company culture, the internship, etc. While most of his sales pitch felt like generic boilerplate, one thing really piqued my interest---he proudly boasted that their interns worked on real projects, and some of their interns' code was still running in production, long after they had returned to university. If I got the chance to work there as well, then I might also contribute to a real-world project with long-lasting effects.
Up until that point, I had never really considered what would constitute a successful internship for me; I saw internships only as a means of getting experience (and a bit of cash). After that conversation, I realized that it would be very cool to write stable, long-lasting code that persists. Maybe it would be as an intern, writing code that outlived my internship; or after graduation, writing code that outlived my tenure at the company. The craziest thought, however, was the concept of writing code that would outlive me.
When choosing my major for university, I started out with "Mathematics of Computation", because I was decent at math in high school, and had enjoyed the applied math courses I had taken1. As my first year was online due to COVID-19, I ended up writing a lot of my notes, homework, and exams in LaTeX. Somehow, I stumbled upon Gilles Castel's blog, which showed me the unreasonable effectiveness of using Vim for text editing. After reading through Gilles' posts (and some encouragement from a friend), I fell down the Neovim rabbit hole, then the Linux rabbit hole shortly after. Working on my tools became addicting, as I continuously polished my setup to perfectly suit my tastes, and no-one else's.
At the same time, Neovim was going through a bit of a transformation as the
long-awaited v0.5 was getting released, introducing support for Lua as a
first-class language for configuring the editor. Many folks (myself included)
were very excited for the Lua support, and there was a growing frenzy around
rewriting a bunch of popular Vim plugins in Lua2. I was a big fan of
vim-surround, and had the foolish
thought: Why not try my hand at rewriting vim-surround in Lua? Not only would
it let me give back to the open source community, it also might help me achieve
that goal of writing long-lived software. It just so happened that I had lots of
free time (due to failing every internship interview that year), so that summer
nvim-surround was born.
At first, I was pretty happy tinkering with the new Lua APIs for interacting
with Neovim, and re-implementing the small subset of vim-surround that I
personally found useful. Before long, I found myself reading through
:h vim-surround to see what extra power I could get out of the plugin. Every
time I learned something new that I wanted, I would re-implement it in my Lua
clone. At some point, I felt pretty happy about it, and
shared it with the rest of the world.
Its reception was astonishingly positive for how bad the plugin was3, and I
remember being fueled by the feedback to make things as good as possible, as
fast as possible. Some days4 I recall working on it during the entire day,
into the early hours of the morning, sleeping for not nearly enough hours, then
waking up and going straight to my computer to check for new GitHub issues.
nvim-surround has had no business being anywhere near as successful as it has
been, and I attribute it largely to good naming and lucky timing.
I think that there were a few interesting lessons that I picked up along the
way. First, once I had the idea to "rewrite vim-surround in Lua", it was
crucial to have the blind confidence to think that I could do it (although not
the arrogance to think I could do it well). Had I known a priori how much
time and effort would eventually be sunk into the end product, I might not have
started at all. While it was incredibly bad at first (and stayed that way for a
while), I'm very glad I just put it all out there for others to use and
critique.
Secondly, for personal projects, I think it's quite helpful to work on the software that I actually want. I had already been spending lots of time tinkering with Lua and Neovim configurations before I decided to try my hand at a surround plugin. Being super familiar with the environment meant that I could focus on learning a very small amount of other skills. Moreover, dogfooding my own product meant that I would oftentimes find issues in the plugin myself and resolve them. Building something that I personally used everyday helped me develop empathy for my users, and oftentimes gave me the context needed to make difficult design choices (e.g. minting a new feature vs. telling the user to use some workaround).
Third, I wish I had said no to more feature requests when I was first getting started with the project. In efforts to implement every minor request, I lost sight of program simplicity. At first, these were fairly easy to accommodate, but they eventually cost me---I had to mint a few breaking changes to back out my poor design choices, before I could implement new features. Had I been willing to sacrifice short-term user sentiment in favor of long-term maintenance, I could've avoided such regressions.
While I can see all of these benefits now, none of these things were obvious to me at the time. During my second year of university, when all of my peers and I were populating our resumes to scrounge for internships, I recall quipping that I had "exactly zero marketable skills". While everyone else was learning useful things like microservices, frontend development, game development, etc., I was working on "that weird Vim stuff". In hindsight, I feel like I learned a myriad of important things that don't exactly translate well to a resume5: learning the Diataxis style for technical documentation; taking semantic versioning seriously and communicating breaking changes; designing a configuration schema that prioritized the simple case while allowing power users to do some pretty complex operations.
Thankfully, I eventually lucked out and got my first internship at AWS, opening
the doors to a stable6 career in software. I truly think that this project
was most if not all of the reason I was able to make it so far in the first
place. Thanks to everyone that has helped shape nvim-surround and my
career---the contributors, the bugfinders, and the users.
Four years on, I feel like I've finally gotten a taste of what it's like to
write and maintain long-lived software. These days, I'm no longer frantically
responding to bug reports at 3 in the morning, nor do I agonize over UX details
for hours in front of a whiteboard. While imperfect, nvim-surround more or
less just works, and doesn't require too much maintenance from me.
Time to think of the next thing to work on.
Footnotes
Basically, I didn't know any better. At that point in time, I hadn't taken any pure math classes, which is what I actually ended up gravitating towards during university.↩
I'm not entirely sure why this happened, since all of the old Vimscript plugins are still compatible with Neovim, and in my opinion hold up pretty well. I vaguely remember some claims of better runtime performance being thrown around, but it probably doesn't matter in almost all cases.↩
By the time I had posted publicly about it, there were:
- So many comically bad bugs that would crash the plugin
- Zero tests
- Zero type annotations
I don't know what possessed me to push this stream of commits between 7PM and 5AM...↩
I'm not a recruiter, so take this with a grain of salt. I weakly think that it's quite difficult to figure out what experience bullets are "real" instead of just made-up fluff.↩
So far.↩