AI is making it easier than ever to contribute to open source projects, and this has amazing potential for people to learn and projects to expand the pool of folks who can help make things better.
There’s a lot of friction right now in this though for 2 reasons:
- Good code requires contextual judgment and that takes time to develop; historically folks tended to develop these skills in parallel with technical skills
- Many of the social rules of open source are unwritten or specific to a particular project, and navigating this is also a skill
A common complaint of open source maintainers in 2026 is the additional burden of reviewing a higher external contributions, many AI generated, and with authors not fully able to navigate the process of code-based open source contributions.
I’ve been on each side of this equation, both as a newcomer to a project who wants to help, but also as a frustrated maintainer managing their limited time. In this blog post I’m going to talk about the steps that I take as a contributor to a new project to try to reduce friction.
Why maintainers become frustrated
As a maintainer, my first reaction to a pull request from a newcomer used to be excitement - open source is a social experience and helping someone get involved is really rewarding - I love helping people take their first steps into open source, and who knows, they might even become a regular contributor.
Things have changed a lot in the past year or two; now the first thing I do is try to work out “has this person used AI and does it look like they read the code before submitting this PR?”
It’s frustrating. I don’t mind reviewing code which has been generated by AI but then fully understood by a human who is able to make any requested changes, explain the reasoning behind the design choices, and have ownership of the code even if they didn’t write it themselves.
But when the author hasn’t engaged with the code, the review process can be a real drag. Some authors abandon their PR upon receiving any feedback, others respond with more AI and no real human engagement. In these cases, I’d rather save the time and effort, and just make the change using AI myself - I’ll certainly do a better job. It’s also a chunk of emotional energy - I want to be welcoming and friendly, and if I come across a heavily AI-generated PR, there’s still the chance that the author may be willing to engage during the review process, and so I don’t want to say “no” immediately or reply grumpily, as I still want to be part of creating a welcoming environment.
I don’t have a good solution here other than to give these PRs as quick an initial review as possible to gauge the vibes of the author based on their next response. Some projects I’m involved with have turned on automatic AI code reviews for new contributors, to deal with the sheer volume of new contributions.
I’m not always in the maintainer role though, and when dealing with author people’s codebases, I’m now the external contributor using AI to generate code to submit PR. The rest of this post covers the steps I take to try to not frustrate other maintainers.
Step 1 - Build trust gradually
Code contributions can feel satisfying but aren’t always the most helpful, and there are so many other ways to contribute to a codebase. I don’t always make PRs. I know they’re a time commitment - after generating the code, I also need to stick around for the iteration of review and updates and I don’t always have time for this. If I find a bug, I’ll often just report it, with a simple reproducible example. I might also take the time to work out with AI the source of the bug, and if I’m confident about the explanation, include that in the issue too. If the fix is simple, I’ll submit a PR, but if there are complex design decisions to be made, I’ll often get package author input.
Of course, not all contributions have to be code, and a great non-code contribution is triaging other people’s bugs, by adding simple reproducible examples if they’re missing.Or explanations of the cause if I know it, to make it easier for maintainers to get it fixed faster.
I also try to stick to the principle of starting small and building up trust. I’ll submit a PR for a documentation fix if I spot a typo, or fix an existing bug. I rarely submit PRs for feature implementations, unless I’ve been contributing for a while or have buy-in from the package maintainers by opening an issue and asking if they’d be happy for me to make a PR.
Generally, I want to demonstrate that I’ve read any contribution guidelines, can submit code with the correct level of testing and in the style of the rest of the codebase. And on that note…
Step 2 - Learning the codebase/fitting it
The single biggest piece of advice I follow is “learn to fit in”. Essentially, what I’m aiming for is that my PR looks like other PRs which have been merged. This extends to the smallest details - even if it means using `=` instead of `<-`, you’ve got leave your principles at the door, or run the risk of doing the equivalent of going into someone else’s house and rearranging their furniture!
I like to think of it as if I’m some sort of spy or undercover agent who is trying to slide into their ecosystem so smoothly that my PR barely feels external. My favourite word I use to describe what I’m trying to be here is “idiomatic”. There are often multiple ways to write “good” or “correct” code, so which particular way of doing that is being used most in this codebase?
So, how do I do that? A few things:
- look at past PRs from maintainers to see if they have linked issues, how much testing is done, what the PR descriptions look like and how much detail is in there
- do the same but with PRs from non-maintainers to see where gets pushed back on most for additional details etc
- skim the unit tests; what is tested and what isn’t?
- skim the code; how many comments are there, how modular is the code, what conventions seem to be used (e.g.
lapply()vs.purrr::map(); how is code split out into files etc?) - which previous PRs can I use as a “template” for what to do here?
Step 3 - Writing the code
Now I know how to blend in, I can write some code. The approach I take varies depending on how much I understand the underlying code and what I’m doing.
If it’s a bug fix, I tend to generate it entirely with AI. I use Claude and whatever the latest model is with reasoning set to at least medium. I’ll let Claude work through some root cause analysis, and repeatedly question it on its assumptions. If I don’t know enough to verify the conclusion myself, I’ll sometimes ask it to try to prove the opposite of its conclusion, or explore more alternative hypotheses. Once I’m satisfied with the conclusions, I’ll have it write the fix, usually questioning it about alternative approaches and design choices.
Feature implementation is a whole lot trickier, and I actually use a Claude skill I wrote which designs “rapid learning” sessions for me. I only use this when I’m so unfamiliar with the codebase that I wouldn’t feel able to review the produced code effectively. Essentially, I have Claude spend time looking at the problem, and then walking me through understanding the relevant code areas so I’m able to fully understand the context. We’ll set up a concrete goal, set the time frame for the learning, and before Claude implements the fix, it’ll have me read certain subsections of code and then ask me what needs to change and where and why, and it’s only at that point I actually use it to make the change.
Doing this means I’m able to fully take ownership of the code and review it before I submit the pull request.
Step 4 - Reviewing the code
This is the most essential stage and is where it’s easy to suddenly realise that the skillset needed to contribute code to open source hasn’t shrunk, it’s just changed.
Previously my code contributions were bottom-up and I’d work through a problem with step-by-step adding code but now it’s more the case of having a finished solution and working backwards to figure out why and how and what.
If I’ve been using my “rapid learning” Claude skill, I’ve already got the reasoning in my head, but if not then it’s a case of reading through the code line by line and looking at all of the changes just to try to understand why this is the right change. I might ask the AI agent about the reasoning, but this is a point where I also look for analogous similar code or think about the different ways that I could have implemented this change. I’ll make sure that the comments are only on none-obvious changes and the testing matches the rest of the code base. This is where the work that I did in step 2 becomes useful, because to successfully review the code, I have to know what I’m aiming for here.
If the generated code is complex and I’ve spent a while iterating on a solution, I’m pretty awful at giving it an honest review immediately after generating it, so at this point I might leave it a few days before coming back to it to have a bit of distance from it and so I read it properly rather than skim-reading it.
Reviewing is the hardest task of the lot, and it’s the kind of thing that I’ll do at the start of the day when I’ve got the most energy. If I don’t do this, I find myself missing small details really easily.
Step 5 - PR description
The last stage is submitting the actual pull request. I’ll often try to include a description of the changes that I’ve made, again, as per other pull requests in the repository. It’s so important here not to just add in a ton of extra detail that isn’t needed though, which seems to be the default LLM behaviour here. Worse, LLMs often generate inaccurate PR descriptions which don’t represent the final state of the PR after changes have been made.
As a reviewer I find lengthy descriptions really add to my cognitive load, and they put me off even attempting to review the PR.
If there were any decisions made by AI that I’m not sure about myself, at this point I will explicitly call them out by adding review comments in the GitHub UI to my own pull requests. This reduces the chance of subtle errors slipping past maintainers but it also signals my willingness to engage with the review process and the fact that I’ve engaged with the code.
Conclusion
It’s not perfect, and I’m still working on these skills, but I believe that doing the above has enabled me to make PRs to open source projects that have helped me both grow as a developer but also given something useful to the project. The core thread running through all of this is keeping the human firmly in the loop.
It reminds me a bit of visiting Toulouse in 2019 for useR! conference. There’s a stereotype I’ve heard repeatedly throughout my life about shopkeepers being rude in France, and I’d attributed it to overgeneralisations stemming from the historical rivalry between England and France, and so I tried to not pay it too much attention. But when I was there, I really did notice some weird vibes when out and about. I was a bit irritated - I can be a little sensitive sometimes and I always believed I was being polite with every request in my broken French having a “s’il vous plait” at the end.
But later during that trip, I learned that going into a shop and launching straight into a request without so much as a “bonjour” is widely considered kinda rude, and once I started making an effort to say hello first, those weird vibes mostly disappeared.
When I think about it, I actually quite like that social rule and think it applies much more broadly in life whether travelling internationally or working on open source - engage as a human, and you’ll get much better results.
Huge thanks to Alenka Frim and Josiah Parry for giving an earlier draft of this a read over!