What I Did During GSoC: My Experience at GitLab

This summer (2025), I had the incredible opportunity to be selected as a Google Summer of Code (GSoC) contributor with GitLab!
GitLab, as many of you know, is a complete DevSecOps platform that allows teams to collaborate on code, CI/CD, and much more all in one place. It was exciting (and honestly a bit surreal) to get selected to work with such a massive and impactful open-source project.
The project I worked on involved improving RuboCop, a static code analyzer and formatter for Ruby. My specific task? To create a custom cop that detects dangerous usage of scope
inside GitLab's policy files.
Through this blog, I want to share my learnings, what it's like working in GSoC, and how I tackled things both technically and emotionally. Spoiler: the journey was full of ups and downs, and I truly enjoyed every bit of it.
🧩 Application & Community Bonding Period
It was around 11:30 PM IST when I got the mail saying “Congratulations! You've been selected for GSoC.”
I must have stared at that email for 10 straight minutes.
That night, I didn’t sleep. I went through every doc GSoC shared, revisited the issue idea I had written about, and started preparing for what was to come.
Soon after, I introduced myself to my mentor and asked what I should begin reading, and how we’d stay in touch.
But here’s where things took an unexpected turn…
My mentor informed me that the original project I was selected for had been deprioritized. 😅
He told me they were considering a new idea around authorization that I might get to work on.
We connected on Discord, which became our main communication channel for the rest of the project.
Our first call? Well, let’s say things didn’t go as planned. 😅
My internet was unstable and we couldn’t really talk. My mentor kindly rescheduled for the next Monday.
That weekend was full of self-doubt. “First call and this happens?” But thankfully, our next call went well. We had a casual chat, and I asked more about the new project direction.
My mentor was clear: he wanted me to work on something meaningful something long-term, not just a temporary fix.
He even mentioned that if a suitable project wasn’t finalized, there was a slight possibility that I might not get to work on anything at all.
That scared me.
But here’s the thing my mentor, Ayush Billore didn’t give up on me.
Even though the original idea wasn’t moving forward, he found me a new project to work on.
I’ll always be grateful for that 🙏
💡The Project:
So what did I work on?
My project was to create a custom RuboCop cop: Gitlab/PolicyConditionScope
.
This cop ensures the correct usage of the scope
option inside condition
blocks in GitLab's policy files.
🧠 Why This Matters:
When used incorrectly, the scope
can cause caching issues and unexpected behavior in GitLab’s Declarative Policy framework.
This cop ensures that:
Only allowed references are used inside condition blocks.
The right scope is declared and enforced.
Dangerous patterns are flagged automatically.
I even made a short video demo called:
🎥 Cache Invalidation Issue Due to Wrong Scope
where I explain why this cop is important and demonstrate the kinds of bugs that can happen when it's missing.
🎥 Weekly Calls & Mentorship
Every week, my mentor and I would get on a 30-minute video call.
During those calls, I’d share:
What I had done that week
Problems I was stuck on
Ideas I had
And he’d guide me, help me pivot when needed, and suggest better approaches
His guidance helped me see how open source really works not just the code, but the communication, the MR feedback, and the community process.
Each interaction was full of learnings, and I can't thank him enough for his patience and support.
👩🏻💻 GSoC Work
🗓️ Week 1:
The official coding period started on May 2nd, but I had my final semester exams and was also relocating as college had just wrapped up. So I didn’t get much time to dive deep into the project yet.
Instead, I focused on reading through the documentation, understanding the issue I’d be working on, and getting familiar with what “policy subject” really means in the codebase.
🗓️ Week 2:
This was when the real work started. After settling into my new place, I had a call with my mentor where I shared my understanding of the issue. We aligned on the goals, and I was given some action items to complete that week:
Understand how cache invalidation works in GitLab's declarative policy code.
Learn about Ruby internals like
attr_reader
,attr_writer
, instance variables, and blocks.Get familiar with how to create a new RuboCop Cop.
Create a new MR in the community fork and start small.
I spent the whole week diving into these tasks and learning the basics.
🗓️ Week 3:
I created a video explaining the stale cache issue and why building a RuboCop Cop for this is important.
I also:
Reviewed older MRs that had tried solving this problem in the past.
Started working toward a small working version of my own solution.
Began writing my first blog post about my GSoC journey.
📖 You can read that blog here: My GSoC Journey
🗓️ Week 4:
This week was all about trial and error. I tried handling scenarios where incorrect scope usage was flagged, but ran into challenges like:
The Cop was flagging cases like
project
underscope: :subject
, even thoughproject
was just an alias ofsubject
.It also flagged methods like
group_issue?
,epics_license_available?
, and others that had no clear receiver, making it hard to tell which scope they belonged to.
I reached out to Peter and Diane with these questions to better understand how to handle such edge cases.
🗓️ Week 5:
This week was exciting!
I completed and published my first blog post about my GSoC experience 🎉
I implemented a cleaner solution: the Cop now flags only direct usage of
user
andsubject
, ignoring all other method calls to avoid false positives.I passed the midterm evaluation ✅
I asked more questions about how scope inheritance works in
with_options
,with_scope
, andwith_score
blocks and shared a POC to test how it behaves in practice.
Peter also reviewed my code and suggested some new approaches to explore.
🗓️ Week 6:
This week, I improved the Cop based on Peter’s suggestions:
Implemented dynamic tracking for allowed references like
alias_method ..., :subject
anddef x; @subject; end
.Added logic to check for aliases/methods inherited from parent classes.
Wrote test cases covering all these scenarios.
Opened my MR for community review! 🚀
🗓️ Week 7:
A lot changed this week!
I started my new job, so we paused our weekly mentor calls.
I moved to a new place again (a lot of shifting this summer 😅).
Began writing my second blog post:
📖 How to Find the Right Issues as a First-Time Open Source Contributor
🗓️ Week 8:
This was my final GSoC week!
I published my second blog about finding beginner-friendly issues.
I started writing this blog about my full GSoC experience.
My MR received feedback and needed some more changes, which I began working on.
Lastly, I submitted the final evaluation form and wrapped up the program 🎉
⚙️ Technical Deep-Dive: About the RuboCop Cop
What Declarative Policy is in GitLab
GitLab uses a declarative authorization framework, known as the Declarative Policy, to define user permissions in a clean, testable, and maintainable way. Instead of writing imperative logic scattered across controllers or services, permissions are defined using condition
and rule
blocks that describe when certain actions should be allowed.
Why misuse of scope: :subject
or scope: :user
matters
Misusing scope: :subject
or scope: :user
in policy condition blocks can lead to incorrect caching behavior in the Declarative Policy framework.
For example, referencing @user
inside a scope: :subject
condition causes the cache to depend on the user even though it's only expected to vary by subject. This breaks the contract of scope-based caching and results in inconsistent policy evaluation or cache invalidation issues.
What my RuboCop cop does
My custom RuboCop cop, Gitlab/PolicyConditionScope
, statically analyzes policy files to enforce correct usage of scope:
within condition
blocks.
It checks whether the block references only the allowed instance variables for the specified scope:
@subject
forscope: :subject
@user
forscope: :user
neither for
scope: :global
If an invalid reference is found (e.g., @user
in a scope: :subject
), the cop flags it, helping developers catch misuse early and maintain the integrity of policy caching behavior.
Contributing this feature taught me the true essence of open-source collaboration. I got to write a cop from scratch, understand internal frameworks like Declarative Policy, and engage in multiple review cycles all while learning from some very experienced maintainers.
I also learned how important it is to balance strictness with developer experience. A linter should catch real issues, not annoy developers with edge cases. Designing that balance was tough but rewarding.
There were moments of doubt wondering if I misunderstood something fundamental but the feedback, helped me push through. By the end, I had gone from asking beginner questions to confidently discussing trade-offs and edge cases. That growth both technical and emotional made this journey incredibly fulfilling.
🛠️ What’s Next
I plan to continue contributing to GitLab and finish the RuboCop cop I’ve been working on. My goal is to refine it based on feedback and get it successfully merged. I'm also looking forward to taking on more impactful issues and deepening my understanding of GitLab’s internals.
Let’s Connect!
If you found this blog helpful or have any questions about GSoC, GitLab, Ruby on Rails, or open source in general feel free to reach out. I’d love to chat or help however I can!
Gitlab: gitlab.com/sahilsarawagi
Github: https://github.com/sahilsarawagi
LinkedIn: linkedin.com/in/sahilsarawagi
Twitter / X: x.com/SahilSarawagi
Subscribe to my newsletter
Read articles from Sahil Sarawagi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
