Why open source contribution is hard
This article is based on my reflection on one of my pull requests.
My pull request is to resolve an usability issue of Visual Studio Code. Basically, if you opened a file in vscode and clicked ‘view its changes from version control system’, a sidewindow showing its last version would appear so you could compare the two versions. The problem was that your previous view position of the file was not preserved, and the vscode would scroll your screen to the cursor position of the file, which surprised the user. I found this issue interesting because it had been opened for 20 days, definitely longer than average fix time, and I wanted to see what made it difficult.
Determining which code triggered the bug was easy. This bug was easy to reproduce, and I managed to locate the code which opens sidewindow within 3 minutes, by using the wording in GUI to search the code base. My next 3 hours were all spent on coming up with a good solution. As an outsider, I know I should be careful to not break project owners’ mental model of how things should work. Oftentimes worked solutions get rejected for the sake of consistency. Guidelines of contribution often explicitly states tons of constraints, and still fails to catch every subtle constraint of the project.
Understanding the related code space and design space, as well as picking a good solution was hard. Back to the code, it turned out they didn’t add a sidewindow, but closed the previous window (they call it editor but I will keep using window here) and opened a new two-column one, and the parameters which the new two-column window received were 1. the file path and 2. the cursor position. In this model, the new window had no idea what the previous view position was. Several possible solutions came to my mind:
- If the new window has a method that can scroll based on the position of another window, then I can add a new parameter to the new one’s constructor (by overloading) and pass the previous window into it.
- If the new window has a method that can scroll based on a Position object, then I can extract the previous one’s view position, add a new parameter to the new one’s constructor and pass the previous position into it.
- Leave the current constructor untouched, and call the new window’s positioning method from the outside code.
I personally prefer the former two approaches since overloading sounds like a pure plus to me, and doing something like a commander outside the function chains always look unrefined. To see if the maintainers would agree with me, I searched the Internet and found a discussion. All of a sudden I got puzzled. Basically the overloading approach is discouraged and the people there briefly mentioned a suggestion to turn to another more configurable API which also opens new window. However, after digging for a while, I found that the current implementation of that API also didn’t consider view position, and to elegantly add a configuration option to preserve view would involve changing interfaces, but interface changes have to be throughtly discussed with maintainers, requiring significant amount of time, which I lacked.
Then I started to examine the last solution. It seemed all I need are a readable property of the previous window and a setting view method for the new window. Wait a second, I don’t know whether you have noticed that now I’m pretending that I knew the view position was a property of a single, concrete, obvious object - the hypothetical window object, but back then I actually struggled to discover how the concept was concretely represented in the code base, especially when there were tons of confusing names. There existed a workspace, an editor, a TextEditor, a TextDocument, a window, an env, and such, and each of them have a legitimate reason to store the view information. My first try went fruitless - I thought the property name might contain line or scroll, so I searched those words across the code base, and got to
revealLine which I later figured out were actually private. Finally, I went mad and I started to throughly read the lengthy code of each of those objects, and found the mysterious
revealRange, the publicly usable view position.
My final pull request only has 10 lines of code change, but as you can see, the involved effort was not trivial. This pull request later got merged into the master branch.
This article might only scratch the surface of the difficulties in open source contribution. The most frustrating thing is that though my activity was basically debugging, current research on debugging couldn’t help me much, if at all.