I’ve spent most of my career working on Gecko, the browser engine behind Firefox. Years in a big, mature codebase teach you a lot. You learn how things fit together, where the hard problems hide, and which ideas have already been tried (sometimes more than once).
They also change how you think, and you don’t really notice it happening. Some abstractions become so familiar that they stop looking like design choices. Threading models, IPC boundaries, ownership rules, API layers: after a while, that’s just how things are done. Most of the time this helps. But it also makes it harder to imagine doing things any other way.
Getting involved with Servo was one of the reasons I joined Igalia. Early in my career, there was a point where I finally understood enough of Gecko to make real changes in it. It took a while, and I remember how good that felt.
Working on Servo gave me some of that feeling again. Obviously I knew a lot more this time. Still, I was back to reading unfamiliar code and slowly figuring out how a browser engine works, only now most of it was Rust. A lot of the problems looked familiar to me, mostly storage, concurrency and async stuff. But the code around them was organized differently, and I kept running into things I’d stopped thinking about years ago.
Rust is a big part of why. The compiler wants you to be explicit about who owns what and how long it lives. In C++, I’ve become used to certain ownership patterns over the years. Rust made me think about some of them again, and a few times I realized that the design I had in mind wasn’t as obvious as I thought.
Comparing the two engines is interesting in itself. Gecko has decades of development, compatibility requirements and real-world experience behind it. Servo tackles many of the same problems with different abstractions and constraints. I think Servo has a lot of potential, and working with both engines has made me rethink some of the assumptions I’d developed over the years.
After so many years of maintaining production code, it’s refreshing to be a beginner again in some areas. The experience is still there, but it’s easier to ask basic questions. Why is this boundary here? Does this operation really need to be async? Do we actually need this lock?
I especially enjoyed asking those questions while helping with IndexedDB in Servo. I’ve worked on IndexedDB in Gecko for many years, so seeing it built in another engine made me look at old decisions differently.
Threading is a good example. In Gecko, IndexedDB work can be spread across a pool of up to 20 threads, and I’d always assumed more parallelism was better. Then I ran some cross-browser comparisons under heavy multi-database load and, out of curiosity, limited Firefox to a single thread. In that test, the single-threaded configuration finished fastest. I didn’t expect that at all. It opened up questions about responsiveness and scheduling that I’d like to explore in Servo some day. That’s probably worth a separate post.
Lately, other projects, mostly on Firefox, have kept me busy, so I haven’t had much time for Servo. But I’m looking forward to getting back to it. Some of the work there was new to me, but what I enjoyed most was seeing familiar things again, from a different angle.