What maintainers actually screen for now
For most of open source's history, the expensive part of a contribution was producing it. You had to read the codebase, find where the change belonged, write it in the project's idiom, and get the tests passing. Anyone who cleared that bar had, by definition, understood something. Review could therefore be relatively cheap: check that the patch is correct, check that it fits, merge.
Coding agents removed the expensive part. A competent agent can now clone a repository it has never seen, locate a plausible fix for an open issue, write it in the local style, add a test and produce a tidy pull request, in less time than it takes to read the contributing guide. The bar that used to do the filtering has effectively been deleted.
Maintainers noticed, and the screen moved. The question a reviewer is now asking, before they read a single line of your diff, is not "is this a valid patch". It is "is there someone behind this who can answer questions about it". That is a different test, and it is one you cannot pass by making the patch better.
The reported signal here is consistent and worth taking seriously. As of September 2026, maintainers and hiring managers widely report being able to identify AI-generated pull requests and issue comments, and the reaction to them is strongly negative. Contributions that consist of pasted assistant output in an issue thread, or of a patch the author clearly did not write and cannot discuss, are treated as a cost rather than a gift. Some projects have added explicit policies to their contributing guides; many more have simply become faster at closing things.
The economics explain the hostility better than any argument about authenticity. Review capacity is the binding constraint on almost every open-source project. A contribution the author does not understand does not save the maintainer work — it moves the work onto them, because they must now do the comprehension the contributor skipped, without the context of having written it. A patch that takes four minutes to generate and forty minutes to review is a net withdrawal from a project that is already overdrawn. Maintainers are not being precious. They are protecting the only resource that determines whether the project survives.
Which means the goal of this guide is narrow and practical. Not "avoid AI" — that ship has sailed, and refusing the tooling would put you at a straightforward disadvantage. The goal is to use it in the places where it makes you faster at understanding, and to keep your own hands on the parts of the contribution that carry the signal a maintainer is screening for.
The two failure modes
Contributions get flagged in two distinct ways, and they are not equally damaging.
The drive-by is the obvious one. A contributor opens six pull requests across six repositories in a weekend, none of which they have run, each fixing something an agent identified from a static read of the source. Typos, unused imports, a README badge, a defensive null check nobody asked for. Sometimes they are technically correct. They are still noise, because they were produced without any model of what the project needs, and their volume is the tell: nobody familiar with six codebases has time to be unfamiliar with all six.
The drive-by is cheap to detect and cheap to dismiss. A maintainer closes it in thirty seconds and forgets you existed. That is embarrassing but survivable — the reputational cost is roughly zero, because you never had a reputation with that project to begin with. If the drive-by has a real cost, it is opportunity cost: a weekend spent producing nothing anyone will remember.
The confident-wrong is the dangerous one. Here the contributor engages properly with a real issue, an agent produces a patch that is well-formed and idiomatic, the tests pass, continuous integration is green — and the change is architecturally wrong in a way that is invisible from inside the diff. It fixes a symptom whose cause lives two layers down. It duplicates an abstraction that already exists in a module the agent never read. It defeats an invariant the test suite never encoded because the person who knew about it wrote it into a design document in 2023 and left.
This is a much worse outcome, for a reason worth sitting with. The drive-by wastes thirty seconds of a stranger's time. The confident-wrong wastes an hour of a maintainer's time, and it does so after they have decided to take you seriously. They read the diff carefully because it looked serious. They engaged in the thread because you seemed engaged. Then they asked why you had put the check in the parser and you could not tell them, and the whole interaction reclassified retroactively. Trust that has been extended and then withdrawn does not return to neutral; it lands below where it started. And the same maintainer is now the person whose review your next contribution needs.
The asymmetry matters because it inverts the intuitive risk model. Most contributors worry about looking inexperienced. In practice, looking inexperienced is nearly free — maintainers deal with beginners constantly and most of them are patient about it. What is expensive is looking more competent than you are, which is precisely the failure mode a good coding agent produces, because it makes your output look like the work of someone who understood the codebase.
Green continuous integration is the most misleading signal in agent-assisted contribution. CI verifies that nothing the project already knew about has broken. It cannot verify that your change belongs at that layer, that it does not duplicate something elsewhere, or that it respects a constraint nobody encoded as a test. Treat passing tests as permission to ask for review, never as evidence that the approach is right.
Where an agent genuinely helps on an unfamiliar codebase
The legitimate uses are all comprehension tasks, and they are genuinely transformative — this is the part of the workflow where the tooling earns its place several times over.
Orientation and call-graph tracing. Point the agent at a symbol and ask what calls it, what it calls, and where the value it returns ends up. Ask which module owns a particular concept, where a config value is parsed and where it is finally consumed, what happens between the CLI entry point and the thing that actually does the work. This used to be a fortnight of reading with a notebook. Compressing it to an afternoon is a real advantage and no maintainer has ever objected to a contributor who arrived oriented.
Test-harness discovery. Every project has a way of testing things and it is almost never obvious from the outside. Which fixtures exist, how the integration suite is bootstrapped, which markers skip the GPU tests, where the golden files live, what the convention is for a regression test versus a unit test. Ask the agent to find you the three most similar existing tests to the change you are contemplating. Then read those three yourself — they tell you more about the project's standards than the contributing guide does.
Reproducing a reported bug. Feed the issue thread in and ask for the minimal reproduction, the versions that matter, and what environmental factors could explain the difference between the reporter's machine and yours. Then run it. The reproduction is only worth something once you have watched it fail with your own eyes, but getting from a vague bug report to a candidate reproduction script is exactly the kind of tedious inference agents are good at. Our guide to debugging with a coding agent — reproduce, bisect, fix, prove covers this loop in depth.
Summarising a subsystem's history. This is the most underused and possibly the highest-value application. Ask the agent to walk the commit log and the closed pull requests for the file you are about to change, and tell you why it looks the way it does. Half the time you discover that your intended fix was tried in 2024, merged, and reverted a fortnight later for a reason nobody wrote in a comment. That single query has saved more contributors from the confident-wrong failure mode than any amount of careful coding.
Checking whether the project has capacity for your contribution at all. Before you add to a review queue, find out how long that queue is. This is not about picking a project for career reasons; it is basic courtesy, and it tells you whether to expect a response in four days or four months.
# Are outside contributions actually landing, or piling up?
gh pr list --repo vllm-project/vllm --state merged --limit 30 \
--json number,title,author,createdAt,mergedAt
# Days from opened to merged — the review latency you should expect
gh pr list --repo promptfoo/promptfoo --state merged --limit 30 \
--json number,createdAt,mergedAt \
--jq '.[] | {n: .number, days: (((.mergedAt|fromdateiso8601)
- (.createdAt|fromdateiso8601)) / 86400 | floor)}'
# How deep is the queue you are about to join?
gh pr list --repo vllm-project/vllm --state open --limit 100 --json number | \
jq 'length'
# How many distinct people are merging code? (Review capacity, roughly)
gh api "repos/vllm-project/vllm/commits?per_page=100" \
--jq '.[].commit.author.name' | sort | uniq -c | sort -rn | head -20
# Read the policy BEFORE you write anything — many projects now state
# an explicit position on AI-generated contributions.
gh api "repos/vllm-project/vllm/contents/CONTRIBUTING.md" --jq '.download_url'
Read the output rather than the headline number. A queue of four hundred open pull requests with two active reviewers tells you that a large speculative change is a bad idea and a small, obviously-correct one is a kindness. Projects under formal governance — Ray, for instance, has been stewarded by the PyTorch Foundation since 2025 — add further structural latency through contributor licence agreements and design-proposal discipline, which is not a problem, but it does mean the patch you send on Tuesday is not merging on Thursday. Calibrate your patience to the project rather than to your own timeline.
Where it must not touch the keyboard
Four things in a contribution must be yours, and they are precisely the four that carry the signal.
The architectural decision. Which layer does this change belong at? Should the validation live in the parser, the caller or the schema? Is this a bug in the implementation or in the contract? An agent will happily answer these questions and its answer will be plausible, because plausibility is what it optimises for. But the correct answer depends on things the model cannot see: what the maintainers intend the module to become, which subsystem is scheduled for replacement, which abstraction the project has already decided it regrets. You get those from reading the history, the issue threads and the design discussions, and then you decide.
The trade-off. Every non-trivial change rejects an alternative. If you cannot name the alternative, you did not make a decision — you accepted the first suggestion. Reviewers ask about the rejected path more often than about the chosen one, because it is the cheapest way to find out whether anyone was thinking.
The pull request description. Generated descriptions have a recognisable texture — fluent, complete, evenly weighted, and empty of the specific detail that only comes from having done the work. They describe what the diff does, which the reviewer can already see, and never why it does it that way, which is the only thing they needed you for. Write it yourself, badly if necessary. A description in imperfect English that names the invariant and the rejected alternative beats a polished one that says nothing.
Anything you cannot explain unprompted in review. This is the catch-all and the real rule. If a line exists in your diff and you cannot say why without going back to the assistant, it should not be in your diff. Delete it, or understand it, or ask about it in the thread — asking is always allowed and always respected.
| Activity | Verdict | Why |
|---|---|---|
| Navigate the codebase — trace call graphs, locate owners of a concept, find the test harness | Safe to delegate | Pure comprehension. Nothing leaves your machine, and being oriented faster has no downside for anyone |
| Reproduce a reported bug — build the minimal case from an issue thread | Delegate with verification | Good at the tedious inference, but a reproduction you have not watched fail yourself is a guess. Run it before you claim it |
| Draft the patch — first implementation of a decision you have already made | Delegate with verification | Fine as a starting point once the approach is yours. Read every line, delete what you do not need, and rewrite anything you cannot explain |
| Choose the approach — which layer, which abstraction, symptom or cause | Do it yourself | Depends on project intent and history the model cannot see. This is the decision the whole review is really about |
| Write the PR description — the why, the rejected alternative, the limits of your testing | Do it yourself | Generated descriptions restate the diff. The reviewer needs the reasoning, which only exists in your head |
| Respond in review — answer questions, argue, concede, revise | Do it yourself | This is the conversation being evaluated. Relaying it through a tool is obvious within two exchanges and ends the interaction |
"My first fix came back with eleven review comments and I nearly closed the pull request out of embarrassment. Nine were about naming and where the test belonged. The two that mattered explained an invariant the module was protecting, which I would never have found by reading the code — and which my assistant had cheerfully suggested I break. That review taught me more than the six weeks before it."
— Verified Builder · Bengaluru, IndiaContributions you can defend deserve to be findable. Put yours on a Builder profile.
AI Tech Connect lists AI engineers, founders and researchers across India and the UK — and the people hiring browse it to find them. Founding Builder profiles are free while early spots remain.
Become a Verified Builder →The defensibility test
Run this on your own patch before you open it. It takes about fifteen minutes and it is the highest-return quarter of an hour in the whole process, because every question here is one a reviewer will eventually ask — and it is very much better to discover the gap at your own desk than in a public thread.
The rule is that you answer out loud, from memory, without opening the assistant. If you cannot, that is not a failure; it is a finding, and it tells you exactly what to go and read.
| Question | What a good answer sounds like | What it means if you cannot answer |
|---|---|---|
| Why this file and not that one? | "The validation belongs in the parser because the caller already assumes a validated payload — putting it upstream would mean touching four call sites" | You do not know where responsibility sits in this codebase. Read the module boundaries before you go further |
| What did you reject, and why? | "I tried it in the caller first; the diff tripled and two tests started asserting the same thing in different places" | You accepted the first suggestion rather than making a decision. There is no trade-off to defend, which is what review will discover |
| Is this the cause or a symptom? | "The cause. The field is nullable at the boundary, so every downstream guard was patching the same hole" | The classic confident-wrong shape. Assume symptom until you have traced it, and say so in the description if you are unsure |
| What breaks if you are wrong? | "Worst case, a malformed payload raises earlier than it used to, which changes the error type callers see" | You have not modelled the blast radius. A reviewer will, and they will ask you about it |
| Which test would catch a regression here? | "The new one, and it fails on main — I checked. The existing suite would not have caught this at all" | Your test may be asserting your implementation rather than the behaviour. Verify it fails without your fix |
| Why is this the smallest change that fixes it? | "Two adjacent things also look wrong; they are listed as out of scope with issues opened for both" | Your diff has grown past the problem. Split it — unreviewable size is the most common reason good patches stall |
Run the defensibility test the morning after you write the patch, not the evening you finish it. The overnight gap is a surprisingly accurate simulation of an interviewer opening your link six months later, and it reliably exposes the lines you adopted without understanding. If a line surprises you the next day, it was never yours.
Writing the PR so review is cheap
Reframe what the description is for. It is not a portfolio artefact and it is not a summary of your diff — the reviewer can read the diff. It is a device for reducing the cost of reviewing your change, and the cheaper you make that job, the more likely it is to get done at all. Every question you answer pre-emptively is a round trip that does not happen.
This template travels across projects; adapt the headings to whatever the project's own template requires, and never delete their checklist to substitute yours.
## What breaks
One sentence, in user-visible terms. The symptom, not the stack trace.
## How to reproduce
Minimal steps, pinned versions, smallest input that triggers it.
Fixes #1234
## Root cause
Where the assumption fails, and why it was a reasonable assumption
when it was written. Name the file and the invariant, not just the line.
## The fix
What this changes, in one paragraph. Say which invariant it restores.
If I considered another approach and rejected it, it is here and why.
## What I deliberately did NOT change
The adjacent things that also look wrong, and why they are out of
scope for this PR. Linked issues: #1240, #1241
## How I tested
- New test: tests/test_parser.py::test_handles_empty_payload
(fails on main, passes here)
- Existing suite: full run, 3 pre-existing skips unrelated to this path
- Not tested: the CUDA path — no GPU locally. Flagged for a reviewer.
Three sections do most of the work. Root cause is where you demonstrate that you traced rather than pattern-matched, and the clause about why the original assumption was reasonable is worth including even when it is generous — it signals that you read the history rather than judging the code by its current state.
What I deliberately did not change converts an apparent omission into evidence of judgement. Without it, a reviewer who notices the adjacent mess assumes you missed it. With it, they know you saw it and drew a line, which is one of the few things that reliably distinguishes an engineer who ships.
Not tested is the credibility section and the one most contributors quietly omit. Stating plainly that you could not exercise the CUDA path from a laptop in Chennai, or that you have no Windows machine in Manchester to check the path handling on, is far stronger than implying full coverage and being caught by the project's own CI. Reviewers extend trust to contributors who volunteer the limits of their own work, because it is the clearest available evidence that the rest of the description is honest too. If your team already runs automated review, the same logic applies internally — we cover the mechanics in our guide to AI code review in CI and quality gates that cut noise, not corners.
Pasting raw assistant output into an issue thread — maintainers can tell, and it reads as asking a volunteer to review something you would not read yourself. Opening pull requests across many repositories in a weekend, none of which you have run. Submitting a large generated refactor to a project you have never used. Reformatting a whole file alongside a two-line fix, which makes the real change unreviewable. Opening a competing pull request against a newcomer-labelled ticket someone else has already claimed. And regenerating your patch from a review comment and force-pushing it without understanding the new version — reviewers notice, because the revision satisfies the literal comment and quietly breaks something adjacent.
When review pushes back
A reviewer asks: why did you do it this way? If an agent produced your first draft, this is the moment the whole thing is decided — and the good news is that the answer has nothing to do with how the draft was produced. By the time you pressed submit you had either adopted the reasoning as your own or you should not have submitted. So answer as the author, because you are.
Three response patterns work.
The claim with a rejected alternative. "I put it in the parser rather than the caller because the caller already assumes a validated payload. I tried it in the caller first and the diff tripled." This is the strongest possible answer because it demonstrates that a decision happened, and it gives the reviewer something specific to disagree with, which is what they want.
The honest gap. "I do not know why the original code guards that case. Let me find out before I change it." This is not weakness; it is the single most reassuring thing a first-time contributor can say, because it proves you know the difference between what you verified and what you assumed. Maintainers have limitless patience for contributors who know where their knowledge stops.
The clean concession. "You are right, I will move it — and that means the test moves to the integration suite, so let me know if you would rather it stayed a unit test." Concede fully, then show you have thought about the consequence of conceding. Reviewers remember contributors who make their corrections cheap to apply.
Two answers end the conversation badly. The first is "the model suggested it", which tells the reviewer there is nobody home behind the diff and that every subsequent comment will have to be relayed through a tool they cannot see. Even where it is literally true, it is the wrong sentence: the useful version is "I took that from a draft and I have not verified why it is necessary — let me check." Same admission, entirely different implication, because one of them has a person in it.
The second is silence followed by a force-pushed rewrite. Feeding the review comment back into an assistant and pushing whatever comes out is the second-round tell, and experienced reviewers spot it immediately: the new version addresses the literal wording of the comment while breaking an adjacent case the comment was implicitly protecting. At that point the reviewer knows they are reviewing a tool rather than collaborating with an engineer, and most will disengage rather than say so.
One structural note that matters for contributors in India and the UK working against projects whose maintainers sit in North America or Europe: review conversations happen asynchronously across time zones, so each round trip costs a day. That makes pre-emptive answers disproportionately valuable — every question your description already answers is twenty-four hours you do not lose. It also means a thoughtful reply written the next morning always beats a fast one written at midnight. Nobody is waiting on you in real time, and the extra hour of thinking is free.
If a maintainer explicitly asks whether a contribution was AI-generated, answer straightforwardly and specifically: what you used it for, what you verified yourself, what you did not. Evasion is the only response that guarantees a bad outcome, and it is the one that gets remembered. As of September 2026 a growing number of projects state a position on generated contributions in their contributing guide — read it before you write code, and if the policy rules your approach out, respect it rather than testing it.
What this proves to a hiring manager
A merged contribution with a substantive review thread attached is strong evidence, and none of it is weakened by having used an assistant to navigate the codebase. Hiring managers reportedly assess two dimensions — artefact quality, meaning what was built, shipped, tested and documented, and decision quality, meaning what trade-offs were made and what alternatives were rejected. The review thread is unusually direct evidence of the second, because it is a public record of you defending a decision to someone with no reason to be generous. Our breakdown of what AI hiring managers actually evaluate covers both dimensions in detail.
The presentation rule is short: link the specific merged pull request, not the project homepage and not your contribution graph, because reviewers give each item on a profile seconds rather than minutes. Add one line on the constraint you worked within and one on the trade-off you defended. Our GitHub profile audit covers how to make that scannable.
If what you actually need is the broader path — how to choose projects, climb from documentation fixes to real features, and turn contributions into a first role — that is a different article, and we have written it: landing your first AI engineer role via open-source proof of work. And if your strongest work is locked behind an employment agreement, proof of work when your best work is under NDA covers what you can legitimately show instead.
The short version
The tooling changed what is scarce. Producing a plausible patch is now nearly free, so it no longer signals anything, and maintainers have adjusted by screening for the thing that is still expensive: a contributor who understands their own change and can defend it under questioning.
That is good news, oddly. It means the work that earns trust is the work that was always worth doing — reading the history, tracing the cause, choosing the layer deliberately, naming what you rejected, and stating honestly what you could not test. Use the agent to get to that work faster. Just do not let it do that work for you, because that is the only part anyone was ever assessing. A useful discipline for keeping the boundary clear is to decide the change in writing before you generate any code, which is the habit our guide to spec-driven development with AI coding agents sets out in full.