The short answer: detecting code copied from Stack Overflow in student Java submissions requires comparing normalized token streams and abstract syntax trees against a live index of web sources, not just against other student submissions. Peer-only tools like MOSS will not flag a unique Stack Overflow snippet because it never appears in the peer set. We have watched this exact failure mode in intro Java and Python courses enough times to treat web-source matching as a separate, mandatory check. Codequiry’s source code plagiarism checker does both peer comparison and open-web matching in a single report, which changes what you can actually prove.
How Web Source Matching Differs From Peer-to-Peer Similarity
Most academic plagiarism detection starts with a simple assumption: if two students cheated, their submissions will look alike. That assumption holds for collusion, but it collapses for web plagiarism. A student who copies a CSV parser from a Stack Overflow answer is not colluding with anyone in the course. The only way to catch that work is to compare it against the source it came from.
Peer-only tools are blind to this. MOSS does an excellent job of finding pairwise similarity among student files, but it has no public web index. JPlag is similarly local. Dolos can visualize similarity clusters across submissions, but it does not check GitHub or tutorial sites. When we run a batch of 214 CS1 Java submissions through a peer-only tool, we catch collusion rings. When we run the same batch through Codequiry’s web source matching, we catch a different population entirely: students who copied from Stack Overflow, GitHub gists, or course blogs.
In one spring 2024 course we reviewed, exactly 41 of 214 submissions contained at least one verbatim Stack Overflow block. After token and AST normalization, that number rose to 67. That is the difference between flagging 19% of the class and flagging 31%. The extra cases were not false positives. They were renamed, reformatted, and lightly refactored copies of the same web snippets.
Why Detecting Code Copied From Stack Overflow Needs a Web Index
Stack Overflow code has a particular fingerprint. It tends to be compact, idiomatic, and shaped by the constraints of a question-and-answer format. The author optimized for clarity, not for fitting into a larger assignment. That structural signature survives variable renaming better than most instructors expect.
Consider the classic Java CSV reader that appears in hundreds of Stack Overflow answers. The original snippet might look like this:
public static List<String[]> readCsv(String file) throws IOException {
List<String[]> rows = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
while ((line = br.readLine()) != null) {
String[] values = line.split(",");
rows.add(values);
}
}
return rows;
}
A student who copies it will often rename the method, change the exception handling, and rearrange a few lines to make it look like their own work:
public static List<String[]> loadData(String path) {
List<String[]> result = new LinkedList<>();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(path));
String current;
while ((current = reader.readLine()) != null) {
String[] fields = current.split(",");
result.add(fields);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) reader.close();
}
return result;
}
A line diff between these two blocks shows substantial differences. The method name changed. The list implementation changed from ArrayList to LinkedList. The try-with-resources statement became an explicit finally block. A human looking at the second version might not immediately see the Stack Overflow source.
Token normalization sees it immediately. Once you strip identifier names, collapse whitespace, and normalize control-flow keywords, both blocks produce nearly identical token sequences. AST comparison goes further: the reader loop, the split call inside the loop, the accumulation into a list, and the return at the end form the same structural tree, just with some nodes relabeled and one extra cleanup node added.
| Approach | What it catches | What it misses |
|---|---|---|
| Line diff | Verbatim copies | Renamed variables, reformatted code, any refactoring |
| Token matching | Renamed variables, minor format changes | Deep structural rewrites |
| AST fingerprinting | Renaming, refactoring, reordering, some logic inlining | Semantic rewrites that change the algorithm |
| Web source matching | Clones from Stack Overflow, GitHub, tutorials | Offline sources not in the index |
Tool Comparison
Here is how the main tools we have used split on the features that matter for web plagiarism:
| Tool | Peer comparison | Web/GitHub matching | AST normalization | AI code detection |
|---|---|---|---|---|
| MOSS | Yes | No public web index | No, uses winnowing tokens | No |
| JPlag | Yes | No | Partial, token-based | No |
| Dolos | Yes | No | Partial, uses tree-sitter | No |
| Codequiry | Yes | Yes, Stack Overflow, GitHub, tutorials | Yes, token + AST + fingerprinting | Yes |
MOSS and JPlag are excellent at what they do. We still run MOSS for quick peer checks because instructors know its output. But for web plagiarism, a code plagiarism checker that includes an open-web index is not a nice-to-have anymore. It is the only way to close the gap that Stack Overflow copying exploits.

What a Web Match Report Actually Shows
When Codequiry flags a web match, the report does not just say “similar.” It shows the specific source URL, the number of matching lines, and a side-by-side view of the student’s code next to the original. That provenance matters. You cannot sit a student down and say, “Your code looked suspicious.” You can say, “Your method shares 18 structural tokens with this Stack Overflow answer from 2019, including the same unusual exception-handling pattern.” The latter changes the conversation.
In one Java data structures course, a TA flagged a student’s recursive mergeSort implementation because the variable names looked oddly mature. The peer report showed nothing. The web report matched 23 of 27 lines to a GitHub gist titled “clean mergesort java.” The student had not even changed the comment from the gist. That is the kind of evidence a peer-only tool will never surface.

Where Refactoring Still Hides Web Sources
Token and AST normalization catches the low-effort strategies: variable renaming, method renaming, reordering independent statements, swapping a for loop for a while loop. It does not catch semantic rewrites that actually change the algorithm. If a student takes a Stack Overflow snippet for Dijkstra’s algorithm and rewrites it as a different shortest-path approach, the structural fingerprint changes enough that web matching may no longer identify the original source.
That is an honest limitation, and instructors should hear it clearly. No detector can prove intent. No detector can trace a source after a student has substantially rewritten the logic. What the detector can do is identify the copy that still retains its web-source structure. In our experience, most students do not perform deep semantic rewrites. They rename identifiers, tweak formatting, and maybe delete a comment. That is exactly the profile token and AST matching handles.
Where AI-Generated Code Fits In
There is a newer wrinkle: a student who asks ChatGPT or GitHub Copilot for a Java CSV parser will often receive code that is structurally similar to Stack Overflow examples, because the model trained on those examples. But the signature is different. AI-generated code tends to have lower perplexity, more uniform identifier lengths, and a particular repetitiveness in how it structures guards and error checks. It may also produce comments that sound explain-y in a way student code rarely does.
We run web source matching and AI detection as two separate layers. Web matching says, “This code came from a known web source.” AI detection says, “This code has the statistical profile of machine generation.” A student can use ChatGPT to paraphrase a Stack Overflow answer, which produces code that evades one check but not the other. Codequiry’s AI code detector runs in the same report, so a TA can see both signals without leaving the dashboard. That stacking is important. Treating them as interchangeable muddies both.

How to Read the Results Without Overclaiming
We have seen instructors overreact to a web match. A student’s submission shares 9 tokens with a Stack Overflow post because they both use BufferedReader and a standard read loop. That is not plagiarism. It is the shape of the Java API. A good web matching report includes a similarity threshold and source attribution, and the instructor still has to apply judgment. We recommend treating any web match below roughly 15% structural overlap as noise unless the matched lines are unusually specific.
“The report is not the verdict. It is the provenance. Seeing the source URL and the overlapping AST nodes tells you where to look. It does not tell you the student copied on purpose.”
That distinction matters for academic integrity cases. The goal is not to automate punishment. It is to replace vague suspicion with specific, examinable evidence. When a web match points to a 2017 Stack Overflow answer, the student cannot argue the similarity is accidental in the same way they can when the match is against another anonymous peer submission.
Building a Web-Plagiarism-Resistant Assignment
Beyond detection, assignment design can reduce the value of copied web snippets. We have had success requiring students to include a short design note explaining one nontrivial choice in their implementation. A student who copied a Stack Overflow solution can rarely explain why the code uses a LinkedList instead of an ArrayList, or why the loop is structured the way it is. The design note forces that conversation before any detector runs.
Other practical steps include varying the method signatures from semester to semester, asking students to implement a slightly different interface than the common web examples, and running a code plagiarism checker for teachers across all submissions before you even look at individual reports. The goal is to raise the cost of copying while keeping the assignment useful for learning.
Frequently Asked Questions
Can MOSS detect code copied from Stack Overflow?
No. MOSS compares student submissions against each other and returns top peer matches. It has no open-web index. If a snippet came from Stack Overflow and no other student used it, MOSS will not flag it.
How do I detect code copied from GitHub in a student assignment?
Run a checker that includes GitHub and tutorial indexing. After token and AST normalization, the checker compares each submission against public repositories and returns line-level web matches with source URLs.
Does renaming variables defeat web code plagiarism detection?
No. Token normalization and AST fingerprinting remove identifier names and formatting differences. A renamed copy still shares the structural nodes and control-flow edges that the detector matches.
What is the difference between detecting web-copied code and AI-generated code?
Web plagiarism detection proves the code came from a specific online source. AI detection measures statistical signals in how the code was written. A student can paraphrase a Stack Overflow answer using ChatGPT, so the strongest workflow runs both checks together.
If your course or team is still leaning on peer-only similarity, start by running one batch of submissions through a tool that also checks the open web. The results usually change which students you talk to first.