How it started
Today, November 9, is the last day of the Outreachy contribution phase for the December 2026 cohort. Three weeks ago I had never have a merged contribution on Debconf, never translated a Django app, and never read through a DebConf codebase I did not write. Now I have merged code in the DebConf website repository, received real code review from a Debconf maintainer, and understood a registration system well enough to propose redesigning it.
This is the third and final post in my series about contributing to Debian through Outreachy. In the first post I wrote about setting up the project and making my first contributions. In the second I wrote about getting code merged, processing mentor feedback, and learning to trust the process. This one is about what changed between week two and today: the final application, what reading the codebase actually taught me, and what I would do differently if I started over.
What I actually built
The project I contributed to is wafer-debconf, the Django web application that runs the DebConf and MiniDebConf conference websites. My mentor is Antonio Terceiro. Here is what I shipped during the contribution phase:
Yoruba and Arabic translations. I translated the certificates app into Yoruba and started Arabic translations for the MiniDebConf app. This was my first contribution and the one that got me comfortable with the .po file workflow, compilemessages, and the merge request process on Salsa.
No-JavaScript certificate validation. The certificate validation page originally required JavaScript to work. I replaced the client-side logic with a server-side Django view so certificates could be verified even with JavaScript disabled. The mentor review on this one taught me about BuildableDetailView, static site generation, and why DebConf exports pages as static HTML.
CurrentTasksView feature. I worked on a view to show attendees their current registration tasks, which ties into the broader registration flow.
Volunteer notification design discussion. For a feature that would let attendees opt into a public attendee list, a maintainer raised spam concerns. Instead of jumping straight to code, I proposed an opt-in setting that defaults to off and asked for confirmation before implementing. That conversation taught me more about the project's design values than any code I wrote.
Helping newcomers on Element. I spent time in the Debian Element chat answering questions from other applicants and pointing them to where to start. This was not a code contribution, but it mattered. Open source communities run on people helping people, and I wanted to be part of that.
Upstream wafer PR. I also contributed to the upstream wafer project with a merge request for the show_in_attendee_list feature.
The codebase taught me more than the code
The most valuable thing I did during this phase was not writing code. It was reading it.
The internship project I applied for is issue #63: redesigning the DebConf registration flow. Right now, registration works as a multi-step wizard. A single integer in the database tracks how far each user has progressed. Every part of the system reads that integer to decide what a user has completed and what they can access next.
That sounds simple right? until you start reading the actual files. I went through core.py, attendee.py, and models.py line by line, and here is what I found:
The integer means different things depending on configuration. There are two STEPS lists. Online conferences have 9 steps. In-person conferences have 13. The same integer value, say 5, means a user has completed different sections depending on whether DEBCONF_ONLINE is set. Any migration that replaces this integer has to account for both configurations.
Two methods read the integer differently. The method get_context_data checks whether the integer is at or above a given value. The method dispatch checks whether a step is more than one past the last completed step. They look similar at first, but the logic is different, and decoupling them from the integer means understanding exactly what each one does.
MiniDebConf does not use any of this. While DebConf has the wizard and the integer tracking, MiniDebConf uses one flat table with no progress field and no status tracking at all. Knowing this matters because a redesign that accidentally assumes every conference uses the wizard would break MiniDebConf.
Historical migrations are untouchable. Migrations 0008 and 0009 have already been applied to production databases. You do not edit them. You add new migrations on top.
None of this was obvious from the issue description. I only learned it by reading the code. And that reading is what shaped my final application, because I could not write a realistic timeline without understanding the dependency chain: understand the sections, change the redirect, decouple the checks from the integer, replace the integer itself, then update everything that reads it.
Writing the final application
The final application was harder than any code I wrote during the contribution phase. Not because the form is complicated, but because it forced me to be honest about what I actually understand versus what I had only skimmed.
The timeline section took the most work. I had to map 13 weeks of internship to concrete, testable milestones. The first draft was too vague: "work on the registration flow" does not tell a mentor what you will deliver in week 3 or what you will demo at midterm. So I rewrote it around the dependency chain I found in the code.
The structure I landed on: the first two weeks for understanding and documenting the current registration sections. Weeks 3 and 4 for changing how get_success_url redirects users after each step, which is the simplest piece to decouple first. Weeks 5 and 6 for the hardest part: rewriting dispatch and get_context_data so they use per-section statuses instead of the integer. Weeks 7 and 8 for replacing the integer in the model and writing the data migration, which has to handle the two STEPS lists and both configurations. The remaining weeks for updating statistics.py, the badger command, and everything else that reads the old integer.
Every phase produces working, tested, independently mergeable code. That was a principle I kept coming back to. A 13-week internship is long enough to lose direction if you do not have checkpoints, and yet also short enough that a big rewrite without intermediate merges is risky.
I also wrote about my previous Outreachy experience. I contributed to the Debian QA openQA project in a previous round. I was not selected, but I stayed involved, kept attending meetings, and came back for this round. I think that persistence matters more than the selection itself, and I wanted the application to reflect that.
What I would tell someone starting their first Outreachy contribution phase
I have now been through three Outreachy contribution phases, two of those phases are for Debian, one for openQA and one for wafer-debconf. Here is what I wish I had known from the start:
Read the code before you write any. I cannot stress this enough. The issues listed on the project page are entry points, not the whole picture. The real understanding comes from opening the files, following the imports, and tracing how data moves through the system. That understanding is what makes your final application credible.
Treat code review as a lesson, not a grade. When my mentor asked for changes on the certificate validation merge request, my first reaction was that I had done something wrong. I had not. The feedback was about how DebConf's architecture works: static site generation, unique URLs per certificate, proper 404 handling. Every review comment taught me something about the project's design decisions.
Talk about design before you code. The attendee list discussion saved me from writing code that would have been rejected. A five-message conversation on Element/gitlab was worth more than a week of solo coding.
Do not disappear after your first merge request. Stay in the chat. Answer questions from other applicants. Show up to meetings. The contribution phase is not just about code. It is about showing that you can be a reliable member of a community for 13 weeks.
Come back if you are not selected. I was not selected in my first Outreachy round. I stayed, kept contributing to openQA, and came back. If you care about the project, selection is not the only reason to be there.
What is next
The contribution phase ends today. The final application is submitted. Now I wait.
There are roughly 30 applicants for one intern slot on this project. The odds are not in anyone's favour, and I am okay with that. Whether or not I am selected, I now understand a real Django codebase well enough to contribute to it on my own. I know how to read a .po file, how to write a reversible data migration, how to think about static site generation when designing a view, and how to have a design conversation before writing a single line of code.
If I am selected, the internship runs from December 7, 2026 to March 8, 2027. I will keep writing about it here.
If I am not selected, I will keep contributing to Debian anyway. That is what I did last time, and it brought me here.
Thank you to Antonio Terceiro for mentoring, to the Debian community for being welcoming, and to Outreachy for making this possible. And thank you for reading. If you are thinking about applying to Outreachy, do it. The worst that happens is you learn something.
This is the third post in my Outreachy series for this cohort. You can read the first two here:
• https://dev.to/linsmed/my-first-week-contributing-to-debian-outreachy-contribution-phase-experience-2dhd
• https://dev.to/linsmed/my-second-week-contributing-to-debian-merged-code-real-feedback-and-learning-to-trust-the-3one
Top comments (0)