What One CS Department Learned From a Year of AI Code Detection

Between August 2023 and May 2024, a public research university with roughly 27,000 undergraduates ran detecting AI-generated code in student submissions as a formal part of its grading workflow for the first time. Eleven assignments. Three courses. 4,118 submissions. I was brought in as an outside analyst to build the measurement plan and report the results back to the department's curriculum committee.

By the end of the second semester, 155 submissions (3.8% of the total) had resulted in an academic integrity finding. Another 319 were flagged by something and then cleared, left inconclusive, or handled informally between instructor and student. That ratio, roughly two cleared files for every one that held up, is the single most useful number the pilot produced. It's also the number nobody wanted to talk about at the first committee meeting.

Can a detector tell you whether a student used ChatGPT? Not by itself, and anyone selling you that is selling you something else. What a good AI code detector does is rank submissions by probability so a human can look carefully at the twenty files that matter instead of skimming the eight hundred that don't.

Detecting AI-generated code in student submissions at 4,118-file scale

The department chose three courses deliberately: CS 1500 (introductory programming, Python, three sections), CS 2100 (data structures, Java), and CS 3100 (algorithms, C++). The spread matters because the failure modes are different at each level. An intro student asking a model to write a loop often submits something that runs and is wrong in ways a grader notices immediately. A third-year student asking a model to implement Dijkstra's algorithm gets something closer to correct than they could write in the time allowed.

I'll be honest about the sample. This was eleven assignments out of a possible forty-two, chosen by instructors who volunteered rather than by random assignment. Courses whose faculty were already worried about AI are overrepresented. Any rate I report is a rate for selected assignments in selected courses, not for the department.

The mechanics were unremarkable. Submissions came in through the LMS, got exported as zip archives, and ran through three passes: peer similarity, web and repository matching, and AI generation scoring. The department had used MOSS for peer comparison since roughly 2016, and the TAs knew the email-a-tarball-to-Stanford workflow well enough to grumble about it. For this pilot they moved the whole pipeline into Codequiry, which runs the peer, web, and AI passes against the same submission set and returns one report instead of three unrelated ones.

Codequiry AI code detection report with average AI score, highest file score and a risk distribution
AI code detection: probability scores per file, flagging submissions likely written by ChatGPT, Copilot, Claude or Gemini.

Four signals, and what each one can actually tell you

The pilot tracked four distinct signals. Conflating them was the most common mistake we made in the first weeks, and it's the most common mistake I see in policy documents that treat "AI detection" as one thing.

SignalWhat it catchesWhat it misses
Peer similarity (token and AST comparison)Copying between students in the same cohort, including renamed variables, reordered functions, and reformatted codeAnything written from scratch, by a person or a model
Web and repository matchingSnippets lifted from Stack Overflow, GitHub, older tutorial sites, and prior-semester solutions posted publiclyAnything generated privately and never published
AI generation scoringWhole-file generation, and heavier paraphrase-and-retype workflowsShort functions, heavily edited output, and code a student genuinely researched first
Interview and oral defenseThe gap between code a student can explain and code they can'tNothing, if you actually run the interview. That's the cost.

Turnitin was already in use across the university for written work, and its AI writing indicator was licensed. It contributed almost nothing here, because it is a prose classifier. It returns no usable signal on a Java file. That was a surprise to exactly one committee member and a known fact to everyone who had tried it.

Reading the AI score bands

Codequiry's AI pass returns a per-submission score and a per-file breakdown. We binned all 4,118 submissions by score and then tracked what happened to each bin after human review. Review meant a course coordinator plus one instructor who had not taught that student, working from a written rubric that asked four questions: does the code match the student's prior submissions in style and vocabulary, can the student explain specific design choices, does the file contain structural tells, and does the timeline of edits in the version history make sense.

AI score bandSubmissionsShareReviewedFinding upheld
0 to 202,91570.8%413
20 to 5070317.1%889
50 to 702766.7%12124
70 to 901583.8%15871
90 to 100661.6%6648
Codequiry AI detection table listing submissions with AI score ranges and review statuses
Per-submission AI scores with ranges and review statuses, so graders start conversations instead of guessing.

Two things stand out. First, the low bands were not clean: of the 4,118 submissions, 41 that scored under 20 were still reviewed because a peer or web match pulled them in, and three of those became findings. AI scoring alone would have missed them. Second, look at the 70-to-90 band. Eighty-seven of those 158 submissions were cleared. That band is where a threshold-based policy does the most damage, and it's where most institutions set their cutoff.

Where the detector was wrong, and why

The clearest false positive I reviewed was a sophomore in CS 2100 who had learned Java partly through a single YouTube series and had adopted its commenting conventions wholesale. Her files had uniform block comments, consistent two-space indentation, and generic parameter names, the exact combination the classifier weights heavily. The try/catch block in her linked-list implementation caught a NullPointerException that could not, given the surrounding null checks, ever be thrown. That's a classic model tell. It's also what you write when a tutorial taught you to always wrap the traversal.

She explained every line in about nine minutes and drew the pointer diagram on the whiteboard without prompting. The finding was dropped. She also told the coordinator she had started running her own code through an online AI checker before submitting, because the constant uncertainty was making her anxious. I have thought about that sentence more than any other in the dataset.

def calculate_average(numbers):
    """
    Calculate the average of a list of numbers.

    Args:
        numbers (list): A list of numeric values.

    Returns:
        float: The arithmetic mean of the input list.

    Raises:
        ValueError: If the input list is empty.
    """
    if not numbers:
        raise ValueError("Input list cannot be empty")
    try:
        total = sum(numbers)
        return total / len(numbers)
    except TypeError as e:
        raise TypeError("All elements must be numeric") from e

That snippet is a composite drawn from several flagged intro submissions. The exhaustive docstring, the defensive try/except around code that cannot raise the caught exception, and the docstring describing behavior the function doesn't actually implement are the three hallmarks we saw most. None of them is evidence on its own. Any of them can come from a textbook, a tutorial, or a teaching assistant who over-documented their own starter code.

The detector is a pointer, not a verdict. Our policy says so in the first sentence, mostly to remind ourselves.

That line is now the opening sentence of the department's revised integrity guidelines, which is the right place for it.

The GitHub starter repository nobody remembered posting

In the first Spring 2024 check, 140 CS 2100 submissions matched a public GitHub repository at high confidence on the web pass. All 140 were legitimate. A TA from the previous year had pushed the course's starter files plus a partial reference solution to a personal repo, and it had been indexed. The web-match report traced every one of them to the same commit, which made it obvious within about ten minutes.

Codequiry web results tracing a submission to a Stack Overflow question with line and token counts
Tracing code to its source: a submission matched to a Stack Overflow answer, down to lines and tokens.

The fix was administrative, not technical: the department now keeps a list of known-good external sources and passes it to the checker as an exclusion before each run. Rebuilding that list every semester is annoying and nobody enjoys it. It also prevents a hundred and forty students from getting an email that ruins their week.

One operational detail worth recording: the first Fall 2023 check ran with the minimum-file-length floor left at its 25-line default, which quietly dropped 63 single-function submissions out of the peer pass. We found it three weeks later when a TA noticed two nearly identical files that had never been flagged. The setting is easy to miss in any tool, including the one we used.

AI-assisted or AI-generated? The line the department drew

This took longer to settle than any technical question. The original policy said AI use was prohibited except where an instructor explicitly allowed it, which is unenforceable in a course where 60% of students have Copilot enabled in their editor by default. The committee spent two sessions on definitions and landed here.

Asking a model to explain a concept, generate practice problems, or review your code for bugs is permitted and requires no disclosure. Asking a model to produce code that you then submit, in whole or in substantial part, is a violation. Rewriting model output line by line while keeping its structure counts as submission, because the structure is the assignment. The working test they adopted for ambiguous cases: could the student reproduce the file's architecture on a whiteboard, unaided, thirty minutes after submitting it?

That test is not perfect. It's also the only one that survived contact with actual cases. Forty-one of the 155 upheld findings involved code where the student had clearly edited the output of a model rather than pasted it, and in every one of those the interview settled the question faster than any score did.

What changed the following semester

Three changes, in order of how much they mattered.

Assignment design came first. The department rewrote eight of the eleven pilot assignments to require a short in-class oral defense, a written explanation of one design decision, or a build-on-your-own-previous-code component. Instructors reported that knowing an interview was coming changed submission behavior more than any policy language did.

Second, scoring thresholds were retired as automatic triggers. Nothing above any score auto-files a case. Scores route files to a human, and the rubric decides. That's a slower process and the committee accepted the cost after seeing the 70-to-90 band.

Third, the pipeline itself. Testing Codequiry against MOSS and JPlag 5.0 on the same 4,118 submissions, all three found the same 138 genuine peer-copying cases once the tools were configured comparably. MOSS and JPlag required separate runs, separate file preparation, and a lot of URL pasting into a results page that hasn't changed much since 2015. What pushed the department to keep the commercial tool was that the peer, web, and AI passes landed in one place. A course coordinator working through 158 flagged files at 10 p.m. on a Thursday does not want three browser tabs and an emailed zip. The interface work matters more than the algorithm at that hour, and anyone who has graded at scale knows it.

For departments weighing the same move, the code plagiarism checker that integrates peer, web, and AI signals is the one you'll actually run every assignment. The one you run every third assignment because setup is painful gives you nothing. There's also a practical argument for tooling designed around grading workflows rather than around research output. MOSS was built to answer a research question. That's not a criticism, but it explains a lot about the user experience.

What this data does not support

I want to be careful about the claims that follow from one year at one institution. The 3.8% finding rate is not a national AI-cheating rate. The four courses were volunteers. We validated the AI detector against 474 human-reviewed files, which is a reasonable sample for estimating calibration within this population and too small to say anything about how it behaves on a cohort of 900 in a single course. I'd want two more semesters before I'd publish a false-positive rate as a general number.

The thing I'd tell any department starting this: your first year is not about catching students. It's about calibrating a threshold, writing a rubric that survives contact with a real hearing, and deciding in advance how you'll handle the case where a student genuinely didn't cheat and the tool is confident they did. That case will come up. In our data it came up 319 times.

You can run the same three passes on your own submissions before you commit to anything, which is what I'd recommend doing first.