Hi everyone! In this post, I’ll walk you through my internship journey with Servo as part of the Outreachy program. I’ll share what we’ve achieved so far during the internship period and the lessons learned along the way.
What is Servo?
Before diving into the details, let me introduce Servo. Servo is a prototype web browser engine written in Rust and is currently under active development. As of this post, it has 61.8% Web Platform Test coverage
Web Platform Tests (WPT) are a cross-browser test suite that checks how well browsers implement web standards. Simply put, WPT checks how many Web APIs a browser supports correctly, giving an idea of how complete its web platform implementation is.
The Problem Statement
Like many large-scale software projects, Servo uses GitHub Actions for continuous integration (CI) to validate pull requests. One key step in the CI pipeline is linting, which identifies issues such as verbose or suboptimal code, use of outdated APIs, unsafe API usage, and code that doesn’t follow Servo’s style. The goal of my internship work was to fix this and make the linting experience more helpful for both new and experienced contributors.
My Work
-
Implement GitHub inline annotation on Clippy & Mach test-tidy
During the first stage of the project, we successfully implemented GitHub annotations using workflow commands, which was integrated directly into Mach. -
Merging multiple Python configuration projects into
pyproject.toml
Consolidated various Python configuration files to streamline project setup and maintain consistency across the codebase. -
Removing unused Mach commands
Removedcargo-update
,update-cargo
,grep
,ndk-stack
,ndk-gdb
, andtest-android-startup
Mach commands to clean up the CI workflow. PR#3784 and PR#37958 -
Removing test-tidy redundant rule
Updatedtest-tidy
by removing alphabetical order and line length rules to simplify linting. -
Introduce Pyrefly and fix Pyrefly rules in Python directories and script bindings
-
Adopt Ruff ANN rules for strict typing in functions and classes
Enforced stricter type annotations across Python code by adopting Ruff’s ANN rules.
The Challenges
Adopting Pyrefly has some challenges. It follows design patterns strictly, which is better than other type checkers in some ways, but it also has trade-offs.
For example, in Pyrefly you are not allowed to change the name of a signature of a method from the parent class. This is by design, according to their docs here
The guiding idea here is the Liskov Substitution Principle, the idea that a subclass can stand in for a base class at any point without breaking the program.
This rule can cause a program to crash. You might argue that this is not a significant issue because it’s uncommon to use keyword arguments when calling a class method, most people only care about positional arguments. Consider the following example:
class Base:
def f(self, a: int) -> None:
pass
class ArgsWithDifferentName(Base):
def f(self, _a: int) -> None:
pass
def uses_f(b: Base) -> None:
b.f(a=1)
uses_f(ArgsWithDifferentName())
Here, renaming the parameter in the subclass will cause a runtime error when using a keyword argument, demonstrating Pyrefly’s strict enforcement of method signatures.
Gratitude
This internship journey with Servo would not have been possible without the guidance of my mentors, Martin Robinson and Mukilan Thiyagarajan.
They played a key role in helping me navigate the codebase, from reviewing my pull requests to sharing practical advice during our meetings. Their support not only made my contributions possible but also shaped how I approach problem-solving in large-scale open-source projects.
I would also like to thank the Outreachy organizers, especially Omotola E. Omotayo, for hosting regular check-ins and fostering a supportive space for all interns throughout the program.
I’m deeply grateful for this experience and excited to carry these lessons forward in my open-source journey.