The submission was 180 lines of Java for a week-four CSV parser. It compiled on the first try, which almost never happens in week four. And down in the exception handling there was a comment: // TODO: handle the case where the file has a BOM.
I knew that comment. I had read it in a Stack Overflow answer from 2013 about reading UTF-8 files in Java. The student had never mentioned a BOM in class, and when I asked what a byte order mark was, he said "some kind of file thing." He wasn't lying. He had pasted the answer, comment and all.
Detecting code copied from Stack Overflow is the practice of matching a submission against indexed web sources: answers, GitHub repositories and gists, tutorial sites, blog posts, and the solution repos that bootcamps and universities leave public by accident. The machinery is the same as peer comparison, pointed at a corpus that is enormous, mostly untagged, and changing every day.
What detecting code copied from Stack Overflow actually means
Two checks, two different questions. Peer comparison asks "did you copy from the person sitting next to you." Web comparison asks "did you copy from someone who has never met you." They fail in opposite directions and you need both.
Run MOSS (Alex Aiken's Measure Of Software Similarity, Stanford, in circulation since 1994) on a cohort of 120 submissions and you learn a lot about seating charts. Run it on one submission and it tells you nothing at all. If nine students paste the same answer, peer similarity lights up and you might conclude the check worked. If one student pastes it, the score is zero, the TA signs off, and the assignment goes back with a 94 on it.
That gap is the whole reason web checking exists.
Why reformatting and renaming don't hide a paste
Every serious tool in this space starts by tokenizing. Whitespace, comments, and formatting disappear in the first pass, which means the student who "rewrote" their paste by re-indenting it has done exactly nothing. Their token stream is byte-for-byte identical to the original.
Here's a helper that shows up in roughly every intro algorithms course, in one form or another:
public static int digitSum(int n) {
int total = 0;
while (n > 0) {
total += n % 10;
n = n / 10;
}
return total;
}
A student who renames things consistently will produce this:
public static int sumDigits(int value) {
int acc = 0;
while (value > 0) {
acc += value % 10;
value = value / 10;
}
return acc;
}
A line diff sees two different programs. A token-level matcher with identifier normalization sees the same token sequence twice, because n and value both collapse to a single ID token:
public static int ID ( int ID ) { int ID = NUM ;
while ( ID > NUM ) { ID += ID % NUM ; ID = ID / NUM ; }
return ID ; }
That normalization step is the difference between a matcher that works and one that a motivated student defeats in ten minutes. Plain k-gram hashing without it is fragile. Normalizing every identifier to one symbol is too aggressive, since it can make two unrelated programs look alike. The tools that get this right do something in between: normalize consistently-renamed identifiers, preserve distinctive ones, and compare AST shape as a second signal so that a genuine refactor (loop to recursion, for instance) doesn't read as a copy.
MOSS's approach is worth understanding because it's still the reference implementation in most people's heads. It hashes overlapping k-grams of tokens and uses winnowing (Schleimer, Wilkerson, and Aiken, SIGMOD 2003) to keep only a sparse subset of those hashes. Winnowing comes with a guarantee: any shared run of at least k + t consecutive tokens is guaranteed to be found, where t is the window size. That guarantee is why MOSS is hard to fool with small edits but also why it flags long boilerplate runs.
The corpus problem, or why one checker finds the answer and another doesn't
Peer comparison has a closed corpus: the 120 files you already have. Web comparison has an open one. Stack Overflow alone holds tens of millions of answers. The public data dump is large enough that indexing all of it for a single class is not something anyone does on a laptop in an afternoon, and GitHub's public code is orders of magnitude bigger again.
So the tools that do this credibly index in layers. A crawl of Stack Overflow and the tutorial sites that dominate intro-course searches. Some sampled or API-mediated view of public GitHub. And, increasingly, the domains that keep showing up in your own reports. That last layer matters more than people expect. Once three submissions in your cohort trace back to the same Medium article, that article belongs in the corpus for every future check you run.

Reading a web match report without overreacting
A web match report is not a verdict. It's a list of fragments with sources attached, and the column that matters is not the percentage. It's the token count.
A 100% match on 9 tokens is a coincidence. for (int i = 0; i < n; i++) appears in a couple of million public files. A 100% match on 340 tokens spanning four methods and a class declaration is not a coincidence under any reasonable definition of the word. Same percentage, completely different meanings.
I sort by token count and ignore anything under about 50, which is where the noise floor sits in my experience. Then I open the diff and read it as a human being.
| Longest matched run | How I read it |
|---|---|
| under 25 tokens | boilerplate. Ignore. Every Java student closes a Scanner the same way. |
| 25 to 60 tokens | a common solution shape. Worth a second look only if the surrounding structure matches too. |
| 60 to 150 tokens | open the diff, then ask the student to explain it. |
| over 150 tokens, distinctive names intact | a paste. Including the comments is a confession. |

The license question nobody asks
The misconception I run into most, from students and from working engineers alike, is that Stack Overflow code is public domain. It isn't. Since 2018 new contributions are licensed CC BY-SA 4.0, and everything older is CC BY-SA 3.0. Both require attribution, and the ShareAlike term is the one that should worry you, because a derivative work is supposed to carry the same license.
For a classroom assignment, this rarely reaches a courtroom. For a company shipping a product, it's a completely different conversation.
I've watched a contractor paste a 40-line answer into a billing service, credit nobody, and move on. Nobody noticed for two years. Whether that's a legal problem depends on how original those 40 lines actually were, and courts have been inconsistent about short functional snippets. Google v. Oracle in 2021 turned on API declarations and didn't settle much beyond that. But an acquirer's due-diligence engineer asking "where did this come from" and getting a shrug is a bad Friday afternoon for somebody.
Where web plagiarism detection gets it wrong
False positives cluster around canonical solutions. A "reverse a linked list" assignment has maybe three sensible implementations, and every TA on earth has written all three. A student who arrives at the textbook answer independently will match the textbook, and the match is real. It just doesn't mean what the score implies.
Expect matches against your own materials, too. If your reference solution lived on a course page in 2019 and got archived somewhere, every student who internalized your lecture structure will match it. That's not plagiarism. That's you being a good lecturer.
And the AI layer makes all of this muddier rather than cleaner. Ask a chatbot for a Java CSV parser and you'll get something that reads like a blend of the top five Stack Overflow answers, because structurally that's close to what it is. A web check may or may not find the ancestor. An AI code detector is looking at a different signal entirely, and running both tells you more than running either one alone. When I want that combined view without juggling three dashboards, I run the cohort through Codequiry, which does peer, web, and AI scoring in a single check and lets me sort by token count rather than percentage. It's also the version of this workflow I can point a TA at, which matters more than the algorithm.

A two-stage workflow that holds up in a grade appeal
Automated screen, human review. Never a grade change straight off a score. Every appeal I've lost was one where I skipped the second stage, and every appeal I've won had a diff, a transcript of the student's explanation, and a policy that was published before the assignment went out.
The conversation has one rule: ask them to explain the code, not to confess. "Walk me through what n % 10 does in this loop" settles in ten seconds what an accusation takes an hour to establish. If they can explain it, you have a hard conversation about citation. If they can't, you have a hard conversation about something else.
The other half is policy. A course that explicitly permits adapting up to five lines from an online source, with a // adapted from: <url> comment, will see far fewer incidents than one that pretends the internet doesn't exist. I've watched one instructor drop her reported cases by roughly two thirds after adding that clause, because the honest students started citing and the dishonest ones had one less excuse.
One tuning note, since it cost us a semester. Our first year of web checks ran with the minimum fragment length set to 8 tokens, and the reports were unreadable: every student using BufferedReader was flagged against the same tutorial. We moved the threshold to 25 and the noise mostly went away. That number is empirical and it's calibrated to intro Java, so treat it as a starting point rather than a standard.
Frequently Asked Questions
Is copying code from Stack Overflow plagiarism?
Legally it's a license question, because the content is CC BY-SA and requires attribution. Academically it's a course-policy question, and most CS departments treat an uncited paste as a violation regardless of how the source is licensed. The one place these two answers reliably agree: pasting without a comment saying where it came from is a problem.
Can MOSS detect code copied from Stack Overflow?
No. MOSS compares submissions against each other within the set you give it. It has no web index, so a single submission pasted from an online source will score against nothing. You need a source code plagiarism checker with web indexing for that case.
What match percentage should trigger a review?
I'd argue none. Percentage hides the thing you actually care about, which is how many consecutive tokens matched and whether the matched region carries any original structure. A 30% overall similarity with one 200-token contiguous match is far more suspicious than an 80% figure scattered across a dozen 10-token fragments.
Does a web check also catch AI-generated code?
Sometimes, by accident. A model trained on public code will reproduce recognizable structures, and a web match may surface the ancestor. But that's an unreliable proxy. AI detection looks at distributional properties of the code itself rather than its provenance, and the two signals complement each other.
None of this is a reason to stop assigning the CSV parser. It's a reason to know what's in a submission before you put a grade on it. If you want to see the report shape on your own material, you can run a check against a handful of past submissions and read the diffs yourself in about ten minutes.