My First GitLab Open Source Contribution: From Issue to Merge Request
Contributing to a large open-source project can feel intimidating when you are seeing the codebase for the first time. GitLab is a particularly interesting project to contribute to because it combines a large Ruby on Rails application with databases, CI/CD, frontend code, and many other components.
For my first GitLab contribution, I decided to work on a database-related issue involving PostgreSQL table storage and HOT updates.
This post describes what I learned while going from an issue to an actual merge request.
Finding a Contribution
I started by looking through GitLab issues that were suitable for a community contribution.
The issue I selected was #629994, "Lower fillfactor on the vulnerability identifier tables."
The issue concerned two tables:
- vulnerability_identifiers
- vulnerability_occurrence_identifiers
The issue included measurements showing relatively low HOT-update fractions for these tables. That made it a good opportunity to understand how PostgreSQL storage parameters can affect update behavior.
Understanding PostgreSQL Fillfactor
PostgreSQL stores table rows inside data pages. The fillfactor storage parameter controls how much of a table page PostgreSQL aims to fill when creating or rewriting the table.
For tables that are frequently updated, leaving some free space on a page can be useful because it gives PostgreSQL more room to place a new row version on the same page.
For this contribution, the requested target was a fillfactor of 90.
The migration therefore changes the storage parameter on both vulnerability identifier tables to fillfactor=90.
Why HOT Updates Matter
PostgreSQL supports Heap-Only Tuple (HOT) updates.
A HOT update can avoid creating new index entries when the updated row can remain on the same heap page and the indexed columns do not require new index entries.
This can reduce index-related work during updates.
The issue reported measured HOT-update fractions of approximately:
- 32.0% for vulnerability_identifiers
- 64.2% for vulnerability_occurrence_identifiers
These measurements provided the motivation for increasing the available free space on the pages by using a lower fillfactor.
Implementing the Change
The implementation was intentionally small.
I created a post-deployment database migration.
The migration uses GitLab's database migration framework and applies a fillfactor of 90 to both tables:
# frozen_string_literal: true
class LowerFillfactorOnVulnerabilityTables < Gitlab::Database::Migration[2.3]
milestone '19.5'
def up
execute('ALTER TABLE vulnerability_identifiers SET (fillfactor=90)')
execute('ALTER TABLE vulnerability_occurrence_identifiers SET (fillfactor=90)')
end
def down
execute('ALTER TABLE vulnerability_identifiers RESET (fillfactor)')
execute('ALTER TABLE vulnerability_occurrence_identifiers RESET (fillfactor)')
end
end
The migration includes both up and down methods so that the setting can be applied and reverted.
One thing I learned from this contribution is that a small code change can still require understanding the surrounding database architecture and migration conventions of a large project.
Working With the GitLab Community Fork
Another part of the process was learning how GitLab handles contributions from external contributors.
GitLab provides a Community Fork workflow for contributors. I created my contribution branch:
629994-lower-fillfactor-on-vulnerability-identifiers
and pushed the branch to the GitLab Community Fork.
This was an important part of the experience because contributing to a large open-source repository is not only about writing code. Understanding the project's contribution workflow is equally important.
Creating the Merge Request
After pushing the branch, I opened a merge request against GitLab's main repository.
The merge request was:
!260121 — db: lower fillfactor on vulnerability identifier tables to 90
I linked the merge request to issue #629994 so that the relationship between the implementation and the original issue was clear.
The merge request also included instructions for validating the migration.
For example, the migration can be executed with:
bin/rake db:migrate:up VERSION=20261006165253
The resulting PostgreSQL table options can then be inspected to verify that the fillfactor has been set correctly.
The CI Experience
One of the biggest lessons came after opening the merge request.
GitLab automatically started CI pipelines to validate the contribution.
The initial pipeline contained failures across several different jobs. An important part of the investigation was that multiple unrelated jobs showed the same shell error:
pop_var_context: head of shell_variables not a function context
The error appeared in database setup and migration-validation jobs as well as other CI jobs.
The available logs did not show a PostgreSQL error related specifically to the migration, the two vulnerability tables, or the fillfactor setting.
This was a useful reminder that a failed CI pipeline does not automatically mean that the code being contributed is wrong.
The appropriate response was to investigate the actual failure rather than immediately changing working code.
What I Learned
This first contribution taught me several things that I would not have learned from simply solving coding problems locally.
1. Read the issue before writing code
The issue often contains important context about why a change is required.
In this case, the HOT-update measurements helped explain why changing the fillfactor was being considered.
2. Understand the technology behind the change
The migration itself was only a few lines of Ruby.
However, understanding PostgreSQL pages, fillfactor, and HOT updates made the change much easier to reason about.
3. Large open-source projects have established workflows
Branching, community forks, merge requests, labels, approvals, CI pipelines, and contribution rules are all part of the process.
Knowing the workflow is just as important as knowing Git.
4. CI failures need investigation
A red pipeline is not automatically proof of a bad implementation.
Looking at the actual failure messages and determining whether they are related to the changed code is an important part of software engineering.
5. Small changes can still require careful reasoning
The final migration is small, but database storage behavior can have performance implications at scale.
That is one of the things I find interesting about contributing to mature open-source projects: even a small change can require careful investigation.
What's Next
At the time of writing, the merge request is going through the normal GitLab review and CI process.
Regardless of the final outcome, the contribution has already given me practical experience with:
- GitLab's contribution workflow
- PostgreSQL storage parameters
- Database migrations
- HOT updates
- Git branches and forks
- Merge requests
- CI troubleshooting
- Open-source collaboration
This is my first contribution to the GitLab codebase, and I hope it will be the beginning of many more.
Conclusion
My biggest takeaway is that open-source contribution is not simply about finding a line of code to change.
It is about understanding the problem, researching the existing implementation, following the project's conventions, testing the change, communicating clearly, and working through review and CI.
For anyone making their first contribution to a large project, I would recommend starting with a well-defined issue, learning the project's contribution workflow, and not being afraid of a large codebase.
The first contribution is the hardest part. After that, the process starts becoming much more familiar.
Top comments (0)