<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>QualifyCode — notes on defensible hiring</title>
    <link>https://qualifycode.com/blog</link>
    <atom:link href="https://qualifycode.com/rss.xml" rel="self" type="application/rss+xml" />
    <description>Published detection thresholds, why an integrity flag must never auto-reject, the EU AI Act, and what we removed from our own product.</description>
    <language>en</language>
    <lastBuildDate>Fri, 28 Aug 2026 09:00:00 GMT</lastBuildDate>
    <generator>scripts/generate-feed.mjs</generator>
    <item>
      <title>Our Cheating-Detection Thresholds, Published</title>
      <link>https://qualifycode.com/blog/publishing-our-cheating-detection-thresholds</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/publishing-our-cheating-detection-thresholds</guid>
      <pubDate>Fri, 28 Aug 2026 09:00:00 GMT</pubDate>
      <category>Technology Deep-Dive</category>
      <description>Every vendor claims to catch AI-assisted candidates and almost none will say how. Here are our actual rules, the physics behind them, and the guards.</description>
      <content:encoded><![CDATA[<p>Ask an assessment vendor how their cheating detection works and you will usually get a category name — "AI detection", "plagiarism check", "suspicion score" — and no rule. That is a problem, because these systems fail in one direction. They do not fail by letting someone through. They fail by rejecting a person who did nothing wrong, and that person never finds out why.</p>
<p>So here are ours. All of them. You can decide whether they are reasonable, which is not a decision anyone can make about a number they are asked to trust.</p>
<h2>Rule one: more than 20 characters in under 100 milliseconds</h2>
<p>When more than twenty characters appear between two saves less than a tenth of a second apart, they were not typed. This is not an inference — it is arithmetic. The fastest competitive typists on earth sustain roughly eighteen characters per second. Twenty characters in a hundred milliseconds is two hundred per second, an order of magnitude beyond any human hand.</p>
<p>A first save larger than five hundred characters is treated the same way, for the same reason.</p>
<p>When this rule fires, it is <strong>binding</strong> on everything downstream. Our analysis model is instructed in its system prompt that a paste flag means the bulk-paste probability must be at least 0.95, with the explicit note: do not second-guess physics. A model that talks itself out of a deterministic observation is worse than no model.</p>
<h2>Rule two: above 150 characters per minute, sustained, with almost no deletion</h2>
<p>A three-minute sliding window averaging above 150 characters per minute, with a deletion ratio under two per cent, is the rhythm of copying from another screen rather than composing on this one.</p>
<p>The reasoning here is about revision, not speed. Composition is lumpy: real developers delete somewhere between five and fifteen per cent of what they write while working a problem out, because the first attempt is usually wrong in some small way. Sustained high throughput with near-zero deletion means the text already existed somewhere in finished form. The keyboard is being used as a transcription device.</p>
<h2>Rule three: structural discontinuity</h2>
<p>A method stub becoming a correct, optimised dynamic-programming solution in a single edit is a discontinuity. The intermediate reasoning is absent from the record because it happened somewhere else.</p>
<p>This is the softest of the three and we treat it accordingly — it is a probability, assessed against that candidate's own trajectory on that problem rather than against a template of how a solution ought to develop. Some people really do see the answer immediately. What they do not usually do is see it immediately <em>and</em> write it without a single revision <em>and</em> pass every edge case on the first run.</p>
<h2>The guards matter more than the rules</h2>
<p>A detection threshold without a false-positive guard is a trap, so each of ours has one and they are worth as much attention as the rules themselves.</p>
<p><strong>Boilerplate is protected.</strong> A first save of 50 to 200 characters is classified as scaffolding, not suspicion. Imports, a class declaration and a method stub are how normal people start, and penalising that would penalise good habits.</p>
<p><strong>Speed alone proves nothing.</strong> 125 characters per minute is completely normal and is never flagged. Our analysis prompt carries explicit cadence bands — 0 to 30, 30 to 80, 80 to 200, 200 to 400 — precisely so a fast typist is not treated as a cheat. It is the <em>combination</em> of speed with the absence of revision that carries signal.</p>
<p><strong>Unmeasured is never zero.</strong> If a dimension could not be assessed, it is null. It does not become a zero that drags a composite down, and it does not become a 0.5 that reads as evidence of innocence either. We had that second bug once: an unanalysed problem defaulted to 0.5, which sat just above the 0.4 natural-evolution floor applied downstream, so a problem nobody had analysed quietly registered as clean.</p>
<h2>What we deliberately do not measure</h2>
<p>Each of these would be easy to score and would make a report look more thorough. Each also punishes something that is either normal or good.</p>
<p><strong>Typing speed on its own.</strong> Fast typists exist. <strong>Time spent thinking.</strong> Pauses are how problems get solved; a long session is not a worse session. <strong>How many times they ran the tests.</strong> Iterating against feedback is the job — penalising it selects for guessing. <strong>The final answer in isolation.</strong> It is now the least informative artifact in the session and the easiest to obtain. <strong>Personality or communication style.</strong> Not observable from a code session, and not something we will pretend to score.</p>
<h2>And the ceiling on all of it</h2>
<p>Publishing thresholds only means something if you also publish what the system is allowed to do when one fires. In our case: route the candidate to a human being, and nothing more.</p>
<p>An integrity signal cannot reject anyone here. Configuring one to do so is not a setting we discourage — it is a compile error, re-checked at runtime when stored policy is read. A bulk-paste probability of 0.7 is a suspicion, and a suspicion should never end someone's candidacy on its own.</p>
<p>The stronger evaluation is inspectable: every threshold, trigger and guard is published so a reviewer can judge the method directly and reproduce the deterministic rules.</p>
<p><strong>The full method, including the calibration step and the false-positive matrix, is on one page.</strong> <a href="https://qualifycode.com/how-detection-works" style="color:var(--blueprint);font-weight:600">Read how detection works →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Why an Integrity Flag Always Reaches a Human</title>
      <link>https://qualifycode.com/blog/integrity-flags-should-never-auto-reject</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/integrity-flags-should-never-auto-reject</guid>
      <pubDate>Wed, 26 Aug 2026 09:00:00 GMT</pubDate>
      <category>Hiring Strategy</category>
      <description>Routing to a human is guaranteed by the type system rather than by a policy note — configuring an automatic rejection does not compile. Here is the reasoning.</description>
      <content:encoded><![CDATA[<p>Every screening system that produces a probability eventually faces the same request: can we auto-reject on it. The volume argument is genuinely strong — a recruiter with four hundred applicants and eleven flagged sessions does not want to open eleven sessions.</p>
<p>We built the product so that request cannot be granted, and we want to explain why rather than just claim it as a virtue.</p>
<h2>A probability is not a finding</h2>
<p>When our analysis reports a bulk-paste probability of 0.7, that is an estimate about a person, produced from indirect evidence. It is not a confession and it is not a match against a known source. The honest English translation is "this looks more like pasting than typing, and we could be wrong."</p>
<p>Now consider the asymmetry of acting on it. If the estimate is right and you route the candidate to a human, you lose ten minutes. If the estimate is wrong and you auto-reject, a real person loses a job they were qualified for, is never told why, and has no route to contest it. Those two outcomes are not remotely comparable, and any threshold you pick will produce some of the second kind.</p>
<h2>Why we made it unrepresentable rather than discouraged</h2>
<p>Our first design had integrity flags configurable to three dispositions: reject outright, escalate to a human, or show for information. The documentation strongly advised against the first.</p>
<p>That was not good enough, and the reason is not really about ethics. If a dangerous option exists, somebody eventually selects it — probably at the end of a quarter, under hiring pressure, while tuning a policy at speed. The documentation will not be open. The person clicking will not be the person who wrote the warning.</p>
<p>So the disposition type for an integrity flag now permits only two values: escalate, or inform. Setting one to reject is a type error that fails the build, and stored configuration is re-validated when it is read so an old or hand-edited record cannot smuggle it in either. There is no override flag, because an override flag becomes the normal path within a week.</p>
<h2>The same logic, applied to automation generally</h2>
<p>Automated screening ships switched off. You can run it in trial mode, where it records what it would have decided and affects no candidate, so its agreement with your reviewers can actually be measured.</p>
<p>Going live is then refused by the server — not the interface — until agreement reaches ninety per cent across at least fifty compared decisions, and the response returns your current shortfall so you can see how far off it is. Automation also cannot write "needs review" over a candidate nobody has looked at, cannot overwrite a decision a human already made, and cannot act at all while required evidence is still missing.</p>
<p>And the halt control is open to every member of the workspace, not just administrators. A kill switch that requires the right person to be awake is not a control, it is a hope.</p>
<h2>What a reviewer actually sees</h2>
<p>A flag arrives with its basis attached: the probability, the timestamp, and the code as it stood at that second. Not a verdict — an observation, with the evidence next to it.</p>
<p>The same discipline runs into the interview brief. A probe says "ask them to walk through how they arrived at this." It never says "they appear to have pasted this." A probe phrased as a finding turns the interviewer into someone adjudicating an accusation, which is both unfair to the candidate and useless to the hiring manager.</p>
<h2>The part that is easy to miss</h2>
<p>These constraints are not a tax on the product. They are what makes the detection worth having.</p>
<p>A team that trusts its integrity signals will act on them. A team that has been burned by a false positive stops looking at them entirely — and then you are paying for detection you ignore. Making the ceiling low and explicit is what keeps the signal usable.</p>
<p><strong>These constraints are enforced by the type system, not a policy document.</strong> <a href="https://qualifycode.com/defensibility" style="color:var(--blueprint);font-weight:600">See what that means for an audit →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>The EU AI Act and Your Coding Test in 2026</title>
      <link>https://qualifycode.com/blog/eu-ai-act-coding-tests-2026</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/eu-ai-act-coding-tests-2026</guid>
      <pubDate>Mon, 24 Aug 2026 09:00:00 GMT</pubDate>
      <category>Hiring Strategy</category>
      <description>High-risk obligations for hiring tools apply from August 2026. Most assessment stacks cannot answer the question the rules are built around.</description>
      <content:encoded><![CDATA[<p>This is not legal advice and your counsel will have their own reading. It is an engineering account of what the obligations ask a system to be able to do, and why most assessment platforms cannot do it — written by people who had to build for them.</p>
<h2>The short version</h2>
<p>The EU AI Act classifies employment screening as high risk, and the high-risk obligations apply from August 2026 — that is now. Alongside them, New York City's Local Law 144 already requires an annual bias audit for automated employment decision tools, and GDPR Articles 15, 20 and 22 have applied throughout.</p>
<p>Strip away the vocabulary and these regimes converge on one capability: reconstruct a specific decision about a specific person. What was decided, on what evidence, under which rule, by whom, and whether a human was actually involved.</p>
<h2>Why an assessment score is not an answer</h2>
<p>Six months after a rejection, a typical screening stack can produce a candidate name, a date, and a percentage. That is not a decision record. It cannot tell you what the percentage was compared against, whether the bar had changed since, who applied it, or what the reasoning was.</p>
<p>There is a subtler failure that matters more, and it is the one we would flag to any engineering team building this. If your system recomputes a verdict on every read — score comes in, thresholds get applied, verdict comes out — then changing a threshold today silently rewrites the explanation attached to every past decision. The record still renders. It renders a different reason than the one that actually applied. That is worse than having no record, because it looks authoritative.</p>
<p>We hit exactly this and had to fix it: evaluations are now frozen when they are made, stored insert-only, and stamped with the version of the policy in force at that moment.</p>
<h2>What Article 12 is really describing</h2>
<p>The record-keeping provision reads, to an engineer, like a specification for a decision log. Ours stores, per decision: a structured reason code from a fixed enumeration, the acting reviewer, the frozen evaluation it was based on, the policy version applied, and — kept as a separate field — whether the decision was automated.</p>
<p>That last separation is load-bearing. Decision source and automation are different facts, and collapsing them makes an automated employment decision indistinguishable from a data copy in the audit trail. If you are designing this yourself, keep them apart from the start.</p>
<p>Reasons are an enumeration rather than free text for a similar reason: every analytical question about "why" is a group-by, and prose cannot be grouped. "Above bar", "below bar", "borderline band", "conflicting signals", "evidence gap", "integrity concern" — six values you can count.</p>
<h2>Bias auditing, and the honest limit</h2>
<p>Local Law 144 wants impact-ratio analysis by sex and race or ethnicity. Ours applies the four-fifths rule with the highest-scoring group as the reference — not the majority group, which is the more common and more flattering mistake — suppresses groups below a minimum size so no individual is re-identifiable, and marks a result unreliable rather than presenting a small sample as a finding.</p>
<p>One detail worth stealing: when the reference rate is zero, return nothing. Returning a ratio of 1.0 would read as perfect parity, which is the most dangerous possible answer to that question.</p>
<p>The impact-analysis export applies the four-fifths rule, suppresses small groups and reports sample reliability. A publishable Local Law 144 audit additionally requires candidate-provided demographic inputs and the independent audit process.</p>
<h2>Erasure without destroying the trail</h2>
<p>Two duties appear to conflict: delete the person on request, and retain hiring decisions for discrimination auditing. They are reconcilable if you separate identity from record. On an erasure request the candidate's email is replaced with an irreversible tombstone; the decision survives for audit but can no longer be linked to an identifiable person.</p>
<p>This is also why we deleted a claim from our own marketing site. It used to say "zero data retention", which was untrue and, worse, self-defeating — you cannot simultaneously promise to keep nothing and promise that any decision can be explained. Retention is the product. The controls around retention are what make it acceptable.</p>
<h2>Three questions for your current vendor</h2>
<p><strong>Can you produce every screening decision from the last ninety days, with its reason, its actor, and whether it was automated?</strong> If the answer is a CSV of scores, that is not it.</p>
<p><strong>If you raise a threshold today, what happens to the recorded explanation for a rejection from March?</strong> If it changes, the audit trail is not one.</p>
<p><strong>Can an integrity or cheating signal reject a candidate without a human involved?</strong> If yes, that is an automated decision with legal effect, made on a probability, and it is the specific thing the high-risk framework exists to constrain.</p>
<p><strong>We built the decision log first and the marketing second.</strong> <a href="https://qualifycode.com/defensibility" style="color:var(--blueprint);font-weight:600">See exactly what a decision stores →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Why Our Reports Show Gaps Instead of Guesses</title>
      <link>https://qualifycode.com/blog/why-we-deleted-our-confidence-score</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/why-we-deleted-our-confidence-score</guid>
      <pubDate>Sat, 22 Aug 2026 09:00:00 GMT</pubDate>
      <category>Technology Deep-Dive</category>
      <description>Measured, pending and unavailable remain distinct end to end — producing a report whose numbers always state the evidence behind them.</description>
      <content:encoded><![CDATA[<p>QualifyCode carries three evidence states end to end: measured, pending and unavailable. The state is preserved in the candidate row, report, recommendation and export.</p>
<h2>Why evidence state belongs in the data model</h2>
<p>A screening report may combine an assessment score, code analysis, video context and browser integrity events. Each source completes independently, so its state must travel with its value rather than being filled with a midpoint.</p>
<p>That distinction lets the report separate a completed clean check from an unavailable one, and lets recommendation logic require the evidence it actually needs.</p>
<h2>How the report uses it</h2>
<p>Every metric is nullable end to end, and evidence state remains visible rather than being computed away.</p>
<p>A quality dimension we could not assess shows a dash and the words "not measured", never a zero. The eight-axis quality radar omits an unmeasured axis from the polygon instead of plotting it at the centre, because a point at the centre reads as a catastrophic score. A weighted mean shrinks its divisor rather than counting the gap as a failure. A peer percentile is withheld entirely when there are too few peers, with the reason shown.</p>
<p>And the composite everyone actually looks at is only offered when the minimum evidence exists. Below that, the system declines to suggest a verdict at all. You can still record one — it is simply marked as not backed by those checks.</p>
<h2>The four-tick meter</h2>
<p>The visible consequence is a small component on every row, every card and every export: four ticks, filled where a check ran, dashed where it did not, with the count beside it. Two of four is a common and completely legitimate state.</p>
<p>The meter makes evidence coverage readable at a glance and keeps every recommendation tied to the checks that actually completed.</p>
<h2>The detail that surprised us most</h2>
<p>CSV has no null. So when a screening export writes a blank cell for an unmeasured value, a spreadsheet sorts that candidate <em>below</em> one who genuinely scored badly.</p>
<p>That single formatting quirk can invert a hiring decision, and it happens in a file nobody reviews as software. Our exports now carry an explicit state column alongside every measured value — measured, not measured, or pending — because the honest thing to do in a format that cannot express absence is to add a column that can.</p>
<h2>A report reviewers can trust</h2>
<p>Explicit evidence states make every number easier to interpret, every export safer to sort and every decision easier to reconstruct.</p>
<p>The per-problem composite is a transparent blend of measured authorship and quality signals, scaled to a percentage and labelled as a composite.</p>
<p><strong>See the evidence meter and the unmeasured states in the actual product.</strong> <a href="https://qualifycode.com/product" style="color:var(--blueprint);font-weight:600">Walk through the console →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Gaze Calibration and False-Positive Flags</title>
      <link>https://qualifycode.com/blog/gaze-calibration-false-positive-proctoring</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/gaze-calibration-false-positive-proctoring</guid>
      <pubDate>Thu, 20 Aug 2026 09:00:00 GMT</pubDate>
      <category>Technology Deep-Dive</category>
      <description>Webcam proctoring routinely flags candidates for glancing at notes or sitting under a wide lens. Per-candidate gaze calibration fixes the measurement.</description>
      <content:encoded><![CDATA[<h2>The trade-off nobody advertises</h2>
<p>Remote proctoring has a failure mode that vendors rarely discuss: the machinery built to catch genuine cheating also flags candidates who did nothing wrong. Someone glances down at a handwritten note and is marked as looking away. Someone sitting under an ultrawide lens, or working in a dim room, trips a "suspicious behaviour" threshold. Those false positives do not merely annoy people — they cost you good hires over a lighting problem, and once your team has been burned by one they stop trusting the signal entirely.</p>
<h2>The root cause is a measurement error</h2>
<p>Most systems apply one global "looking away" region. If a candidate's gaze leaves a fixed box, they are flagged, regardless of how that particular person actually sits in front of a screen. But monitor size, seating distance, camera angle and lighting all change what looking at a screen looks like. A threshold that is correct for one candidate is wrong for the next. The problem is not that the detection is too sensitive — it is that it is comparing people against a stranger.</p>
<h2>Calibrate the candidate, then compare them to themselves</h2>
<p>Before every assessment we run a short calibration — roughly ten seconds — that builds a <strong>baseline for that specific candidate</strong>. Three things get captured:</p>
<p><strong>1. Environment quality.</strong> Sharpness, luminance and face-area ratio, so we know the recording is usable and can tell the candidate exactly what to fix — "room is too dark", "move closer" — before they start rather than flagging them for it afterwards.</p>
<p><strong>2. Liveness.</strong> At least two natural blinks, detected via eye aspect ratio. A photograph or a looped video held up to the camera does not blink.</p>
<p><strong>3. A gaze polygon.</strong> The candidate looks at five points on their screen and we build a convex polygon representing <em>their</em> screen region, in their seating position, with their camera.</p>
<h2>Why head pose plus eye direction beats iris tracking</h2>
<p>Gaze is not only where the eyes point — it is where the head and eyes point together. Looking at a screen corner involves turning the head, not just rotating the eyeballs. Systems relying on iris position alone produce noisy estimates at webcam distance, because the raw iris signal is simply too weak at that resolution. We combine head pose from facial landmarks with eye direction from machine-learning blendshapes into one estimate, which is what makes a per-candidate polygon meaningful rather than decorative.</p>
<h2>Scratchpads are allowed, because they should be</h2>
<p>Some of the best engineers think on paper. During calibration a candidate can declare a scratchpad, and we capture a dedicated region below the screen for it. When they look down at their notes mid-assessment, the event is recorded as <strong>note-taking</strong> — a labelled, legitimate thing that appears on the timeline and counts against nobody.</p>
<p>This one change removes the most common false positive in remote proctoring, and it removes it from the group of candidates who prepared most carefully.</p>
<h2>Six false positives this designs out</h2>
<p>Because every measurement is relative to that candidate's own baseline rather than a global constant: a wide-angle webcam no longer reads as "candidate absent"; a dim or backlit room no longer reads as a deliberately obscured camera; a smudged lens no longer sits below a sharpness threshold for the whole session; note-taking is labelled rather than flagged; a static photo cannot pass liveness; and close or tilted seating no longer produces gaze estimates outside an assumed box.</p>
<h2>Reviewer context stays separate from scoring</h2>
<p>Per-chunk video attention analysis is recorded and presented to reviewers with its own badge, while candidate scoring remains grounded in assessment and code-analysis evidence.</p>
<p>Calibration gates the assessment start and supplies the candidate-specific baseline. Attention events then add timestamped reviewer context alongside the code and judge timeline.</p>
<p><strong>Fairness and detection are not in tension — bad measurement is the problem.</strong> <a href="https://qualifycode.com/how-detection-works" style="color:var(--blueprint);font-weight:600">Read our published thresholds and guards →</a> or <a href="https://qualifycode.com/pricing" style="color:var(--blueprint);font-weight:600">see per-candidate pricing →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Code Evolution Telemetry, Described Precisely</title>
      <link>https://qualifycode.com/blog/code-evolution-telemetry-explained</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/code-evolution-telemetry-explained</guid>
      <pubDate>Tue, 28 Jul 2026 09:00:00 GMT</pubDate>
      <category>Technology Deep-Dive</category>
      <description>What we capture, at what granularity, and what it shows that a final-submission platform cannot. The vocabulary here is loose enough to mislead.</description>
      <content:encoded><![CDATA[<h2>Why the terminology matters here</h2>
<p>This category describes its own technology loosely — "keystroke-level", "biometric", "AST analysis" — and a loose claim is one you cannot rely on when a decision is questioned. So this piece states exactly what is captured and, just as importantly, what is not.</p>
<h2>What is captured</h2>
<p><strong>Full-file snapshots on pause.</strong> When a candidate stops typing, the current state of the file is stored. A ten-minute problem produces roughly five to fifteen of these.</p>
<p><strong>Character-level diffs between consecutive snapshots.</strong> Each snapshot is compared against the previous one, yielding characters added, characters removed, elapsed time and therefore insertion rate for every interval.</p>
<p><strong>Execution results, interleaved by timestamp.</strong> Every run against the judge — its status, compiler output, standard error — placed on the same ordered timeline as the edits, so a compile error at 5:00 and the edit that fixed it at 5:30 sit next to each other.</p>
<p><strong>Session events.</strong> Separately from the code record, browser-level events such as leaving the tab or exiting fullscreen while the integrity guard is active. These are surfaced as their own check rather than folded into the authorship read.</p>
<h2>What is not captured</h2>
<p><strong>Not individual keystrokes.</strong> We do not log key events and we are careful not to say we do. It is debounced full-file autosave, and "keystroke-level" would overstate the granularity by a wide margin.</p>
<p><strong>Not an AST.</strong> The diff is textual, at character level. It is not a parsed syntax-tree comparison, and code that does not compile still diffs perfectly well — which matters, because a lot of the interesting moments in a session are code that does not compile yet.</p>
<p><strong>Not the screen, not the clipboard, not the machine.</strong> No screen capture. A paste is inferred from insertion rate, not read from the clipboard. Nothing is installed — it runs in a browser tab.</p>
<p><strong>Not code navigation.</strong> Scrolling, cursor movement and re-reading are not recorded. You will see this claimed elsewhere; we do not have it.</p>
<p><strong>Not biometrics, and not cross-session.</strong> No typing-rhythm identity model, and no comparison against a candidate's previous assessments.</p>
<h2>What the record makes visible</h2>
<p>Two derived signals are close to arithmetic. More than twenty characters appearing between saves less than a hundred milliseconds apart was not typed, because two hundred characters a second is an order of magnitude past any human hand. Sustained typing above 150 characters per minute for three minutes with under two per cent deletion is transcription rhythm rather than composition, because composition involves revising.</p>
<p>Two are estimates and are treated as such: whether the solution built up gradually, and whether any structural jump lacks visible reasoning behind it.</p>
<p>The same record supports something more useful than any individual signal, though — a scrubbable session. You can ask for the code as it stood at second 540, diff it against the previous notable moment, see the judge result that preceded it, and watch the webcam at the same offset. That is what turns a score into something you can interrogate.</p>
<h2>The comparison that makes the point</h2>
<p>Two candidates submit identical code and pass identical test cases. The first took forty minutes across nine iterations with heavy deletion, hit a wrong answer, hit a timeout, rewrote the approach and got it accepted. The second took four minutes with one insertion.</p>
<p>A platform storing only the submission reports these two as the same candidate. They are not remotely the same candidate, and the difference is not a matter of interpretation — it is in the record.</p>
<h2>How it enters a decision, and where it stops</h2>
<p>Authorship and code quality are reported as separate numbers, because they answer different questions and averaging them together would let a beautifully written pasted solution look strong. A dimension we could not measure is reported as not measured, never as zero: a check that never ran cannot clear anyone, and it must not condemn anyone either.</p>
<p>And the ceiling: an integrity signal routes a candidate to a human being. It cannot reject one — configuring that is a compile error rather than a discouraged setting. These are probability estimates on the two soft signals, and a suspicion should not end a candidacy on its own.</p>
<p>Every rule above is published with its threshold, interpretation and guard, giving reviewers an inspectable method rather than a black-box claim.</p>
<p><strong>Every threshold, every guard, and the list of what we refuse to measure.</strong> <a href="https://qualifycode.com/how-detection-works" style="color:var(--blueprint);font-weight:600">Read the method →</a> or <a href="https://qualifycode.com/product" style="color:var(--blueprint);font-weight:600">see the session replay →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>The Hidden Cost of Annual Assessment Contracts</title>
      <link>https://qualifycode.com/blog/hidden-cost-annual-oa-contracts</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/hidden-cost-annual-oa-contracts</guid>
      <pubDate>Tue, 21 Jul 2026 09:00:00 GMT</pubDate>
      <category>Pricing &amp; Economics</category>
      <description>The sticker price is the smallest part. Unused allocation, overage, seat licensing, and the screens you skipped to stay inside a tier.</description>
      <content:encoded><![CDATA[<p>No vendor prices appear below. They change, we cannot verify them, and a stale figure in an article is worse than none — ask your incumbent for a current price sheet and run the shapes below against it. Our own rate is our published rate per candidate, which is the only number here we can stand behind.</p>
<h2>Four structural costs, not one</h2>
<p>An annual contract with an included assessment allocation has a sticker price and three costs that do not appear on the invoice.</p>
<h3>1. Allocation you paid for and did not use</h3>
<p>Included screens are almost always monthly and almost never roll over. Hiring is lumpy, so in any quiet month the difference between your allocation and your usage is simply gone. The invoice does not change, which means your effective cost per screen actually taken rises every time you hire less than you planned.</p>
<p>This is the cost that hides best, because the number you quote internally is the contract divided by the full allocation. Your finance team is looking at the contract divided by the screens you really ran, and those two figures diverge in exactly the quarters when hiring is slow.</p>
<h3>2. Overage on the other side of the same line</h3>
<p>Cross the cap by one candidate in a heavy month and each additional screen bills at an overage rate you did not negotiate as carefully as the base, because at signing it looked like an edge case. Over a year of fluctuating demand this is where the surprise lands.</p>
<h3>3. Seats, which are unrelated to screening</h3>
<p>Several platforms bill per reviewer or admin seat. Growing from five reviewers to ten multiplies that line without changing what you learn about a single candidate. Your cost tracks the size of your recruiting team rather than the volume of work it does.</p>
<h3>4. The screens you did not send</h3>
<p>This is the expensive one and it never appears anywhere. When screening is a scarce resource, teams ration it: send the assessment only to candidates who already look strong, and let the borderline ones through to an engineer's calendar instead. The screen you skipped is precisely the one that would have caught the problem, and its cost is an hour of senior engineering time — roughly our published rate — plus the panel's.</p>
<h2>What consumption pricing changes</h2>
<p>Our model is one credit per candidate, spent when they start. No seats, no platform fee, no allocation, no overage, and credits do not expire.</p>
<p>The direct consequence is that the four costs above go to zero: nothing to under-use, nothing to exceed, no seat count, and no reason to ration. A quiet quarter costs nothing and a hiring surge costs the same per candidate as the first one did. Multiply our published rate by the number of candidates who actually sat an assessment and that is the bill.</p>
<p>The honest counterpoint: a flat per-candidate rate has no volume discount. A team that reliably exhausts a large allocation every single month may well pay less on a contract, and if that describes you we would rather you knew it. What per-candidate billing buys is that the rate never changes and unused capacity never expires — which matters most for hiring that arrives in bursts, and most hiring does.</p>
<h2>Two questions worth asking at renewal</h2>
<p><strong>What did we actually spend per screen last year?</strong> Not the contract divided by the allocation — the contract plus overage, divided by the assessments genuinely taken. That is your real number and it is usually a surprise.</p>
<p><strong>How many candidates did we not screen because of the cap?</strong> Then price those at an engineer's hour rather than at a screen. That figure is normally larger than the whole contract, and it is the reason rationing is the most expensive line in this list.</p>
<p><strong>Run your own volume against both shapes.</strong> <a href="https://qualifycode.com/pricing" style="color:var(--blueprint);font-weight:600">Use the cost model →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Static vs Generated Coding Questions</title>
      <link>https://qualifycode.com/blog/static-vs-dynamic-coding-questions</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/static-vs-dynamic-coding-questions</guid>
      <pubDate>Fri, 17 Jul 2026 09:00:00 GMT</pubDate>
      <category>Platform Architecture</category>
      <description>Every published question eventually circulates. Regenerating the same graded problem in a different narrative removes the return on memorising it.</description>
      <content:encoded><![CDATA[<h2>A published question has a half-life</h2>
<p>Every major platform maintains a static library: content authors write problems, they are loaded in, and candidates see them. The model has a structural problem that no amount of library growth fixes — once a question is published, its lifespan starts running down.</p>
<p>The lifecycle is predictable. The question goes live. The first cohort takes it legitimately. Someone records or reconstructs it and posts it. From then on, every candidate can study the exact solution beforehand, and the question has quietly stopped measuring ability and started measuring exposure. The larger and more popular the library, the faster this happens to its best problems.</p>
<h2>Regeneration instead of replacement</h2>
<p>The alternative is to treat the narrative and the graded problem as separate things. The narrative can be regenerated per assessment; the graded problem underneath stays fixed.</p>
<p><strong>The graded problem:</strong> implement a priority queue with O(log n) insertion and O(1) peek, handling duplicate priorities in FIFO order.</p>
<p><strong>One narrative:</strong> "A launch operations team needs a task scheduler for a capsule. Build a priority queue that handles emergency override commands while preserving chronological order among same-priority tasks."</p>
<p><strong>Another narrative:</strong> "A trading desk needs an execution queue. Build a priority queue that processes market orders before limit orders while preserving timestamp order within each priority level."</p>
<p>Same algorithm, same complexity requirement, same edge cases, same test criteria. Circulating the first gives a candidate approximately nothing on the second.</p>
<h2>The fairness question this raises</h2>
<p>The obvious objection is comparability. If candidates see different problems, is the comparison still fair?</p>
<p>It holds as long as the thing being graded does not move. A variation inherits the original's complexity requirement, its edge-case set — empty input, single element, duplicates, overflow — and its test cases. What changes is the wrapper. The bar does not move; the story around it does.</p>
<p>Worth being precise about the limit, though: this is regeneration of existing catalog problems, not open-ended invention. A variation is anchored to a problem whose difficulty is already known, which is exactly what keeps it comparable. Generating genuinely novel problems on demand would reintroduce the calibration issue this design avoids.</p>
<h2>Variation and process evidence work together</h2>
<p>Regeneration removes the value of a leaked prompt. Recording how the solution is written then preserves the stronger signal: insertion rates, revision, judge feedback and the sequence of failures and fixes.</p>
<h2>Where this leaves a fixed library</h2>
<p>Not obsolete, but degrading. If you run one, assume your most-used problems are compromised and rotate them faster than feels necessary. And do not let question security stand in for authorship evidence — the first is a maintenance problem, the second is the actual question, and only one of them tells you who wrote the code.</p>
<p><strong>Regeneration is the smaller half of the answer.</strong> <a href="https://qualifycode.com/how-detection-works" style="color:var(--blueprint);font-weight:600">See what the session record shows →</a> or <a href="https://qualifycode.com/pricing" style="color:var(--blueprint);font-weight:600">see per-candidate pricing →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>How Candidates Cheat on Coding Tests</title>
      <link>https://qualifycode.com/blog/how-candidates-cheat-online-assessments</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/how-candidates-cheat-online-assessments</guid>
      <pubDate>Fri, 10 Jul 2026 09:00:00 GMT</pubDate>
      <category>Integrity &amp; Anti-Cheat</category>
      <description>Five common methods, the signals each one leaves behind, and how deterministic rules, process evidence and reviewer context work together.</description>
      <content:encoded><![CDATA[<p>Online assessments now operate alongside frontier models, paid services and communities that catalogue questions systematically. The useful response is to understand which traces each method leaves: insertion physics, transcription cadence, structural evolution, candidate-specific calibration and prompt variation.</p>
<h2>Method 1: a model writes the solution</h2>
<p>The candidate pastes the problem into a chat interface and pastes the answer back. This is now the most common form by a wide margin, and traditional pass/fail evaluation has no visibility into it whatsoever — the code compiles, the tests pass, the platform reports success.</p>
<p><strong>What detects it:</strong> insertion physics. Between two saves less than a hundred milliseconds apart, more than twenty characters cannot have been typed. The fastest competitive typists on earth sustain roughly eighteen characters a second; twenty in a hundred milliseconds is two hundred a second. This is a deterministic rule, not an inference, and when it fires it is binding on the rest of our analysis.</p>
<p><strong>The obvious counter:</strong> the candidate retypes it instead of pasting. Which is method two.</p>
<h2>Method 2: transcription from a second screen</h2>
<p>A phone, tablet or second monitor displays a finished solution and the candidate types it in by hand. They really are typing, so anything watching for paste events sees nothing.</p>
<p><strong>What detects it:</strong> the absence of revision. Sustained typing above 150 characters per minute across a three-minute window, with under two per cent of characters deleted, is transcription rhythm rather than composition. Composition is lumpy — real developers delete somewhere between five and fifteen per cent of what they write while working something out, because the first attempt is usually slightly wrong. Near-zero deletion at sustained speed means the text already existed in finished form somewhere.</p>
<p><strong>The guard that matters:</strong> speed alone is never enough. 125 characters per minute is completely normal and is never flagged. It is the combination of speed with the absence of revision that carries the signal, and we say so explicitly because "fast typist" would otherwise be a category of person this system punishes.</p>
<h2>Method 3: the solution arrives with no visible reasoning</h2>
<p>A related pattern that is neither paste nor transcription: a method stub becomes a correct, optimised solution in a single edit, with no intermediate attempt.</p>
<p><strong>What detects it:</strong> structural discontinuity, assessed against that candidate's own trajectory on that problem. Solutions have a shape as they form — a naive pass, a failure, a fix, sometimes a rewrite — and that shape is the hardest thing to fake, because faking it means deliberately producing worse code first and then repairing it.</p>
<p><strong>How confident we are:</strong> considerably less than on the first two. This one is a probability, not a rule. Some people genuinely do see the answer immediately. What they do not usually also do is write it without a single revision and pass every edge case on the first run.</p>
<h2>Method 4: a proxy takes the assessment</h2>
<p>Someone else does the work, either over a screen-sharing session or by simply sitting at the machine. Paid services for this exist and advertise openly.</p>
<p><strong>What detects it — partially.</strong> If the proxy types the solution themselves, none of the three signals above fire, because a competent engineer working a problem out produces exactly the pattern we are looking for. What is available is the webcam record: face absence, additional faces in frame, and attention patterns, all compared against the candidate's own calibration baseline. This is where identity, not authorship, becomes the question.</p>
<p><strong>Reviewer context:</strong> candidate-specific calibration, face presence, additional faces and attention events are timestamped beside the session. Candidate scoring remains grounded in assessment and code-analysis evidence.</p>
<h2>Method 5: the question leaked before they sat down</h2>
<p>Archives of company assessment questions are maintained on forums and in group chats. A candidate who has solved your exact problem fifty times will do well on it, and you will learn almost nothing about whether they can build software.</p>
<p><strong>What addresses it:</strong> not detection — regeneration. Any problem in the catalog can be reissued as a variation: identical graded criteria, complexity requirements and edge cases, wrapped in a different narrative. One candidate gets a route optimiser for drone delivery, another gets one for warehouse robots, and the algorithm underneath is the same. Memorising a specific prompt stops paying.</p>
<p>We would not call this impossible to circumvent, because that claim gets made constantly in this category and it is not true of anyone. What it does is remove the return on studying a leaked prompt.</p>
<h2>The uncomfortable summary</h2>
<p>Two of these five are caught by rules close to arithmetic. One is a probability we treat as a probability. One we can only partially address, using a signal we deliberately keep out of scoring. One is not a detection problem at all.</p>
<p>Together, deterministic rules, process evidence, calibration and regenerable prompts give reviewers a traceable basis for follow-up. Every integrity signal arrives with its evidence and routes through human review.</p>
<p><strong>Every threshold above is published in full, with its false-positive guard.</strong> <a href="https://qualifycode.com/how-detection-works" style="color:var(--blueprint);font-weight:600">Read the method →</a></p>]]></content:encoded>
    </item>
    <item>
      <title>10 HackerRank Alternatives for 2026</title>
      <link>https://qualifycode.com/blog/best-hackerrank-alternatives-2026</link>
      <guid isPermaLink="true">https://qualifycode.com/blog/best-hackerrank-alternatives-2026</guid>
      <pubDate>Tue, 07 Jul 2026 09:00:00 GMT</pubDate>
      <category>Platform Comparisons</category>
      <description>What to evaluate on now that a passing test suite proves little: whether a platform keeps the process, can replay it, and publishes its detection rules.</description>
      <content:encoded><![CDATA[<p>A note on what this is. We sell a competing product, so treat this as an interested party's account rather than a neutral review — we have tried to earn some trust by saying where each alternative is better than us, and by not quoting anybody's prices, since they change and we cannot verify them. Ask each vendor for a current price sheet.</p>
<h2>Why teams are re-evaluating</h2>
<p>HackerRank has been the default for over a decade and was well built for the problem it was built for. Three things changed underneath it, and they changed for the whole category at once.</p>
<p><strong>1. Public question banks are now a liability rather than an asset.</strong> A large fixed library is a large fixed target. Popular problems circulate on forums, in repositories and in group chats, so an assessment increasingly measures how many of them a candidate has already seen.</p>
<p><strong>2. A pass rate stopped being evidence.</strong> Frontier models produce correct, idiomatic, well-named solutions to medium-difficulty problems. If your platform only checks whether test cases pass, it cannot distinguish a candidate who solved the problem from one who requested it.</p>
<p><strong>3. Screening decisions became auditable.</strong> EU AI Act high-risk obligations apply to hiring tools from August 2026, and New York City already requires an annual bias audit. Reporting dashboards are not the same artifact as a decision log.</p>
<h2>Ten alternatives, and what each is actually for</h2>
<h3>1. QualifyCode — for evidence and defensibility</h3>
<p>Us. Every candidate save is stored and diffed character by character, so the session becomes something you can scrub through — the code at any second, what changed since the last notable moment, the judge result, and the webcam at the same offset. Detection thresholds are published rather than described. An integrity signal can route a candidate to a human and cannot reject one. Every decision records its reason, its reviewer and the version of the hiring bar applied, exportable as a decision audit and a four-fifths bias audit. Billed per candidate, credits do not expire.</p>
<p><strong>Where QualifyCode focuses:</strong> Python coding assessment today, with regenerable problem variations, save-by-save session evidence, published thresholds and per-candidate billing. JavaScript, Java, Golang, Rust and C++ are coming soon.</p>
<h3>2. CodeSignal — for comparability</h3>
<p>A standardised rating that lets you compare candidates across companies and cohorts, which is real value. The tension is that a proprietary composite gives a reviewer nothing to examine and a rejected candidate nothing to answer. Licensed by seat, so cost tracks the size of your recruiting team rather than your screening volume.</p>
<h3>3. Codility — the best instinct in the incumbent set</h3>
<p>Codility retains a typing timeline and offers playback, which means it understood earlier than most that <em>how</em> a solution was written matters. Genuine credit for that. The limitation is that playback is a review aid rather than an addressable record — you can watch it, but you cannot diff a specific moment, cite it in a decision or export it as evidence.</p>
<h3>4. DevSkiller — for realistic, repository-shaped tasks</h3>
<p>Puts candidates in a real codebase rather than in front of a puzzle, which tells you about navigation and integration in a way function-level problems do not. The awkward part in 2026 is that a realistic task is precisely what a model handles most convincingly. Their per-candidate billing is also closer to ours than the rest of this list.</p>
<h3>5. HackerEarth — for scale and events</h3>
<p>Strong at volume and at hackathons, with a genuine community side that functions as employer branding. If that is part of why you use it, nothing here replaces that half. Scale is also where an unexamined score does the most damage, because at a thousand candidates nobody is reviewing anything by hand.</p>
<h3>6. Coderbyte — for a lighter entry point</h3>
<p>Lower commitment, and it was early to session playback. Depth of assessment and evidence tooling are correspondingly lighter.</p>
<h3>7. TestGorilla — for hiring across every role</h3>
<p>Enormous breadth of test types across a whole company. If you are screening finance, support and engineering in one place, that breadth is the product. For engineering specifically, a coding test is one type among hundreds and is scored on its output.</p>
<h3>8. TestDome — for the simplest billing in the category</h3>
<p>Per candidate, no seat games, running the same afternoon. We copied the model, so we are not going to criticise it. The difference is what a candidate buys you rather than what a candidate costs.</p>
<h3>9. Vervoe — for AI-graded open-ended work</h3>
<p>Reasonable where output is genuinely all there is. In engineering it inherits the central problem: when a model grades what another model wrote, the loop closes and nobody learns anything.</p>
<h3>10. LeetCode — for practice, and not for screening</h3>
<p>Excellent at what it is for, and the worst possible basis for a screen precisely because of that. It is the most rehearsed problem set in software, so testing candidates on it measures evenings spent on LeetCode.</p>
<h2>What to actually evaluate on</h2>
<p>Feature grids are not very useful here, because most of these platforms have similar features. Four questions separate them, and you can ask them on a call.</p>
<p><strong>Does it keep the process, or only the submission?</strong> Once the answer is free, the process is the remaining signal. <strong>Can you replay a session and address a specific moment in it?</strong> Watching is not the same as citing. <strong>Will it publish its detection thresholds?</strong> A detection claim you cannot inspect is a marketing claim. <strong>Can it produce a decision log with reasons and actors, and a bias audit?</strong> If you are asked in a year, a CSV of scores will not do.</p>
<p>One more, less obvious: <strong>can a cheating signal reject someone without a human involved?</strong> If yes, you have an automated employment decision resting on a probability estimate, which is the specific thing the high-risk framework exists to constrain.</p>
<p><strong>We publish our thresholds so you can judge them rather than trust them.</strong> <a href="https://qualifycode.com/how-detection-works" style="color:var(--blueprint);font-weight:600">Read the method →</a> or <a href="https://qualifycode.com/compare/hackerrank" style="color:var(--blueprint);font-weight:600">see the eight-axis comparison →</a></p>]]></content:encoded>
    </item>
  </channel>
</rss>
