<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sam Li</title>
    <description>The latest articles on DEV Community by Sam Li (@devpro_9167).</description>
    <link>https://dev.to/devpro_9167</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4061577%2F77c7c7cd-df53-42cb-9774-0b3612e3783b.png</url>
      <title>DEV Community: Sam Li</title>
      <link>https://dev.to/devpro_9167</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devpro_9167"/>
    <language>en</language>
    <item>
      <title>Your Free AI Token Allowance Is a Budget, Not a Gift</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Sat, 29 Aug 2026 10:11:11 +0000</pubDate>
      <link>https://dev.to/devpro_9167/your-free-ai-token-allowance-is-a-budget-not-a-gift-2j8e</link>
      <guid>https://dev.to/devpro_9167/your-free-ai-token-allowance-is-a-budget-not-a-gift-2j8e</guid>
      <description>&lt;p&gt;A token allowance is a budget with real economics, and teams that treat it that way finish whole sprints on the free tier while others run dry mid-week. This article covers a two-week experiment in which I metered every request that went through the free models and the free server of an open-source coding assistant called MonkeyCode, because the workflow is the point, not the hype. The outcome was a small estimation script, two workflow rules, and a burn rate per task that predicted the week's exhaustion before the week ended.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;At the time of writing, MonkeyCode offers free model access, a free hosted server option, and a token allowance that the project README lists at ten million tokens. Those terms have changed before and will change again, so check the repository before you build any plan on the exact figure. What mattered for this experiment was not the precise number but the habit of recording a consumption estimate for every task, the way you log cloud spend before the bill arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context resends are the silent tax
&lt;/h2&gt;

&lt;p&gt;The leak that most token budgets ignore is silent context resending. You ask a coding agent to review one changed file, and the runtime quietly re-reads the surrounding modules to rebuild state it already has in memory. Over a week of small reviews, that invisible resending can consume more tokens than the actual changes you asked for.&lt;/p&gt;

&lt;p&gt;The remedy is not a bigger allowance; it is a short state file that lets the next task start from a summary instead of a full re-read. After every task, write at most five lines to STATE.md covering what changed, what is still failing, and what comes next, and the agent should read that file before touching the tree again. The discipline feels over-engineered on day one, and it pays for itself by day three.&lt;/p&gt;

&lt;h2&gt;
  
  
  The estimator that made the budget visible
&lt;/h2&gt;

&lt;p&gt;The estimator below is deliberately crude, because precision is not the goal; the goal is to make every task produce a number that gets logged somewhere. It assumes roughly four characters per token, which is a common rule of thumb, and it records the exit code, the elapsed time, and separate prompt and completion estimates into one JSONL file. That file becomes the audit trail for every decision you make about the agent's budget.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
# meter.py - rough per-task token accounting for coding-agent batches.
# Estimate: ~4 characters per token. Real API usage will vary.
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;CHARS_PER_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;4.0&lt;/span&gt;
&lt;span class="n"&gt;LOG_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;token-budget.ndjson&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;estimate_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;CHARS_PER_TOKEN&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;started_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%dT%H:%M:%S&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
    &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;started_at&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;elapsed_seconds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exit_code&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;estimated_prompt_tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;estimate_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;estimated_completion_tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;estimate_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;estimated_total_tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;estimated_prompt_tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;estimated_completion_tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOG_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;chr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 meter.py review-auth &lt;span class="s1"&gt;'your-agent-cli run --task review-auth'&lt;/span&gt;
python3 meter.py diff-types  &lt;span class="s1"&gt;'your-agent-cli run --task diff-types'&lt;/span&gt;
python3 meter.py add-logger &lt;span class="s1"&gt;'your-agent-cli run --task add-logger'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran that pattern twice a day for two weeks, and the first log already showed something predictable and still annoying: the assistant kept restating context it had already been given. The state file from the previous section fixed that leak within a couple of days, and the daily totals dropped accordingly. The second discovery was retries, the quietest kind of waste because retries look like progress.&lt;/p&gt;

&lt;p&gt;Two failures in that period, an unstubbed network call and a missing fixture, each produced several repeated attempts, and every attempt re-sent the same large context with a slightly different prompt. A one-line rule stopped that category: never repeat a command that produced the same failure twice, because the next move must be a change in approach, not a louder echo. That rule alone cut the most expensive block of the whole experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  A handoff guide for free models
&lt;/h2&gt;

&lt;p&gt;The decision table below is the handoff guide that came out of those two weeks, and it is calibrated to free models rather than to an imaginary infinite quota. Small mechanical fixes are always fine, mid-size tasks are fine with scripted verification, but anything that approaches a cross-cutting refactor should go back to a human reviewer. The threshold that mattered most was not model quality but the token price of a wrong guess.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task shape&lt;/th&gt;
&lt;th&gt;Rough token class&lt;/th&gt;
&lt;th&gt;Verdict for free models&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Typo, lint, and comment fixes&lt;/td&gt;
&lt;td&gt;under 5k&lt;/td&gt;
&lt;td&gt;always fine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependency bump with a green test&lt;/td&gt;
&lt;td&gt;5k to 20k&lt;/td&gt;
&lt;td&gt;fine, verify by script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test generation for one small module&lt;/td&gt;
&lt;td&gt;20k to 60k&lt;/td&gt;
&lt;td&gt;fine, review every diff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-cutting refactor or API redesign&lt;/td&gt;
&lt;td&gt;100k and up&lt;/td&gt;
&lt;td&gt;hand to a human&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When the free tier should not be used
&lt;/h2&gt;

&lt;p&gt;This workflow is not for every team. A repository containing secrets, or an organization whose compliance rules require data to stay inside its own boundary, should not point a hosted free server at its codebase even when the meter looks clean. Anyone whose tasks regularly exceed what a session can hold should look elsewhere, because no estimation script fixes a context window that is simply too small.&lt;/p&gt;

&lt;p&gt;The number that predicted the week's budget was not the token total; it was the burn rate per task, and once that rate became visible the experiment stopped being about any single tool and started being about the workflow around it. Meter your next sprint the same way, and if you try the script against the MonkeyCode free server, check the README first because the terms keep moving. The most useful comment you can leave is your own burn rate per task, because real numbers beat any endorsement.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>workflow</category>
      <category>opensource</category>
    </item>
    <item>
      <title>48-Hour Field Notes: I Stopped Reading AI Review Comments and Started Scoring Them</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Fri, 28 Aug 2026 03:39:17 +0000</pubDate>
      <link>https://dev.to/devpro_9167/48-hour-field-notes-i-stopped-reading-ai-review-comments-and-started-scoring-them-5fg0</link>
      <guid>https://dev.to/devpro_9167/48-hour-field-notes-i-stopped-reading-ai-review-comments-and-started-scoring-them-5fg0</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'endraw'&lt;/p&gt;
</description>
      <category>ai</category>
      <category>testing</category>
      <category>productivity</category>
      <category>devex</category>
    </item>
    <item>
      <title>48-Hour Field Notes: A Free AI Server as My PR Reviewer</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Wed, 26 Aug 2026 11:14:21 +0000</pubDate>
      <link>https://dev.to/devpro_9167/48-hour-field-notes-a-free-ai-server-as-my-pr-reviewer-35dp</link>
      <guid>https://dev.to/devpro_9167/48-hour-field-notes-a-free-ai-server-as-my-pr-reviewer-35dp</guid>
      <description>&lt;p&gt;Friday, 16:47. CI turned red on a one-line change that a human reviewer had already approved. The bug wasn't subtle — a null check on the wrong variable — and it sat in the diff for three days.&lt;/p&gt;

&lt;p&gt;That PR is why I stopped asking whether AI coding servers can write code. The harder question is whether they can review it. The DEV front page has been circling this all week: &lt;a href="https://dev.to/heinrichneb/ai-promoted-every-developer-to-reviewer-nobody-tested-the-reviewer-m4h"&gt;AI promoted every developer to reviewer, and nobody tested the reviewer&lt;/a&gt;. So I spent 48 hours testing mine — a free AI coding server, pointed at a real repo, acting as the reviewer on call.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;The server I used was MonkeyCode's free tier: free model access with a 10M token allowance, plus a free server option. I'm not going to quote a leaderboard score, because one 48-hour run isn't a benchmark. I kept field notes instead. Here's what I tried, what broke, and what I'd repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I built a small Node.js service with five seeded bugs. Not clever bugs — the kind that survive review: a wrong variable in a null check, an off-by-one in pagination, an unhandled promise rejection, a SQL query filtering on the wrong column, and a race condition in a cache write. Each one had a known file:line and a one-line fix. That file was my ground truth.&lt;/p&gt;

&lt;p&gt;Then I wrote a drill. It sends a diff to the server's endpoint, records latency and response size, and saves the review. Run it five times, score the reviews against the ground truth, and you get a picture of what the server actually catches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# review_drill.sh — send the same diff to an AI coding server N times&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENDPOINT&lt;/span&gt;:?set&lt;span class="p"&gt; ENDPOINT to your server&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MODEL&lt;/span&gt;:?set&lt;span class="p"&gt; MODEL to the model id&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;DIFF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;:?pass&lt;span class="p"&gt; a diff file&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; PROMPT &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' || true
Review this pull request diff. List concrete bugs only.
For each: file:line, why it breaks, one-line fix.
Ignore style and naming. If nothing is wrong, say so.
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;1 2 3 4 5&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s%N&lt;span class="si"&gt;)&lt;/span&gt;
  curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENDPOINT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; m &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MODEL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; p &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--rawfile&lt;/span&gt; d &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIFF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="s1"&gt;'{model:$m, messages:[{role:"user", content:($p+"\n\n"+$d)}], temperature:0}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"run_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json"&lt;/span&gt;
  &lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s%N&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;end &lt;span class="o"&gt;-&lt;/span&gt; start&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;1000000&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;
  jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.choices[0].message.content'&lt;/span&gt; &lt;span class="s2"&gt;"run_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"run_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.md"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'run %s: %s ms, %s words\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ms&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"run_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.md"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script assumes an OpenAI-compatible chat endpoint; if your server speaks something else, swap the payload. The important part isn't the curl — it's the repetition. One review is an anecdote. Five reviews of the same diff give you a spread.&lt;/p&gt;

&lt;p&gt;Scoring is a two-liner against your ground truth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# score a run: does the review mention each seeded bug's file:line?&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; bug&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bug&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; run_1.md &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"HIT  &lt;/span&gt;&lt;span class="nv"&gt;$bug&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MISS &lt;/span&gt;&lt;span class="nv"&gt;$bug&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; ground_truth.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I tried
&lt;/h2&gt;

&lt;p&gt;Three scenarios, in order.&lt;/p&gt;

&lt;p&gt;First, a small diff: one file, fifteen lines changed. This is the easy case, and it should be the floor. The drill measured how fast the server responded and whether it found the seeded bug inside the changed lines.&lt;/p&gt;

&lt;p&gt;Second, a large refactor: six files, a moved function, a renamed variable. This is where reviewers earn their keep. The bug was hidden in a file that looked untouched.&lt;/p&gt;

&lt;p&gt;Third, a diff with the test suite attached. I wanted to know whether the server would use the tests as evidence or ignore them and pattern-match on the diff alone.&lt;/p&gt;

&lt;p&gt;Sample output — the format the harness records, not a product benchmark:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run 1: small diff     -&amp;gt; 2/5 hits, 1 false positive, 41s
run 2: large refactor -&amp;gt; 1/5 hits, 0 false positives, 88s
run 3: with tests     -&amp;gt; 3/5 hits, 2 false positives, 63s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numbers are specific to my repo and my afternoon. The shape is what matters: the small diff was fast and noisy, the refactor was slow and quiet, and the tests helped — but only when the server actually read them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke
&lt;/h2&gt;

&lt;p&gt;Three things broke, and none of them were the model being "dumb."&lt;/p&gt;

&lt;p&gt;The harness broke first. My first version piped the response through &lt;code&gt;jq&lt;/code&gt; without checking for an error object. The server returned a 200 with an empty message, &lt;code&gt;jq&lt;/code&gt; printed null, and the script kept going. The review file was empty and the run counted as a success. Lesson: validate the response body before you score it.&lt;/p&gt;

&lt;p&gt;The context window broke second. The refactor diff, plus the prompt, pushed past what the server could take in one pass. The response came back, but it only covered the first two files. The seeded bug was in the fifth. The review wasn't wrong — it was blind. You can't fix that with a better prompt; you have to split the diff.&lt;/p&gt;

&lt;p&gt;The third break was the quiet one: false confidence. On the run with tests attached, the server found three of five bugs and invented two more. The invented ones sounded plausible. Without a ground-truth file, I would have merged its advice. That's the real risk of a free AI reviewer — not that it's useless, but that it's convincing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd repeat
&lt;/h2&gt;

&lt;p&gt;I'd repeat the ground truth. Without a seeded bug list, you're grading a reviewer on vibes. The drill is only as honest as the file you score against.&lt;/p&gt;

&lt;p&gt;I'd repeat the five-run spread. The first run of any diff is the worst — cold start, prompt quirks, bad luck. By run three, the answers stabilized. If you only run the drill once, you're measuring noise.&lt;/p&gt;

&lt;p&gt;I'd repeat the latency log, because it caught the one thing the reviews couldn't: the server got slower as the session went on, and the harness couldn't tell me whether that was the server, my network, or the model. Instrument the client, not the server, and you at least know which side of the wire to blame.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should not use this
&lt;/h2&gt;

&lt;p&gt;The drill tests review quality on seeded bugs in one small codebase. It doesn't test code generation, refactoring, or long-running agent tasks. It doesn't test security review, and it doesn't test domain expertise.&lt;/p&gt;

&lt;p&gt;Don't send a diff with secrets, customer data, or proprietary code to a free hosted server. Free tiers are for code you'd paste into a public gist. If that's not your code, keep the review local.&lt;/p&gt;

&lt;p&gt;Don't use this for compliance-sensitive review. A general model doesn't know your auth model, your data retention rules, or your team's definition of "done." It can catch a null check. It cannot sign off on a control.&lt;/p&gt;

&lt;p&gt;And if you need a review in under ten seconds, every time, a free server is the wrong tool. The drill showed me variance, not guarantees.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The trend this week is that AI promoted every developer to reviewer. Nobody tested the reviewer. I spent 48 hours testing mine against a file of seeded bugs, and the three failures taught me more than the hits. The harness is above; the ground truth is your own bug history. Run it against whatever free server you already have access to — MonkeyCode's free tier is where I ran mine — and let the next 48 hours tell you whether the reviewer earns its place in your repo.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your Git History Knows Which AI Coding Server You Should Pick</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Tue, 25 Aug 2026 09:02:24 +0000</pubDate>
      <link>https://dev.to/devpro_9167/your-git-history-knows-which-ai-coding-server-you-should-pick-58c6</link>
      <guid>https://dev.to/devpro_9167/your-git-history-knows-which-ai-coding-server-you-should-pick-58c6</guid>
      <description>&lt;p&gt;The argument started in the team channel at 9:40 on a Tuesday. One engineer pasted a link to a free AI coding server and called it good enough. Another replied that self-hosting is the only responsible choice. By lunch, nobody had mentioned the actual codebase.&lt;/p&gt;

&lt;p&gt;That's the pattern I keep seeing. Teams treat the free-versus-self-hosted question as a matter of principle. It isn't. It's a measurement problem.&lt;/p&gt;

&lt;p&gt;Your repo has a shape. That shape decides the answer before you ever open a pricing page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three dimensions that decide the answer
&lt;/h2&gt;

&lt;p&gt;Token appetite. How many tokens does your average diff consume, plus the instructions, plus the back-and-forth? Context churn. How often does a fresh conversation have to re-learn your codebase? Latency tolerance. Does a slow response break your flow, or just delay a batch job?&lt;/p&gt;

&lt;p&gt;A team shipping small, focused pull requests has a different appetite than a team doing weekly mega-refactors. The same free tier can feel unusable in an interactive session and invisible in an overnight test run. Most comparisons skip this part because it requires actual work.&lt;/p&gt;

&lt;h2&gt;
  
  
  A fit test you can run from your terminal
&lt;/h2&gt;

&lt;p&gt;Here's the reproducible part. The script below reads your git history and estimates the token appetite of your last N commits. It calls no model and needs no API key. It uses one heuristic: roughly four characters per token, a conservative average for mixed code, prose, and symbols.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# fitcheck.sh — estimate your repo's token appetite from git history&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;50&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Analyzing last &lt;/span&gt;&lt;span class="nv"&gt;$N&lt;/span&gt;&lt;span class="s2"&gt; commits in &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---"&lt;/span&gt;

&lt;span class="nv"&gt;total_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; file_hits

&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;0 &lt;span class="k"&gt;$((&lt;/span&gt;N &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;diff_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git show &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;--unified&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="s2"&gt;"HEAD~&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;chars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$diff_text&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;chars &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
  &lt;span class="nv"&gt;total_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;total_tokens &lt;span class="o"&gt;+&lt;/span&gt; tokens&lt;span class="k"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; tokens &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; max_tokens &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$tokens&lt;/span&gt;
  &lt;span class="k"&gt;fi

  while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; f&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;file_hits[&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file_hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git show &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="s2"&gt;"HEAD~&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nv"&gt;avg_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;total_tokens &lt;span class="o"&gt;/&lt;/span&gt; N&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Average diff tokens per commit: &lt;/span&gt;&lt;span class="nv"&gt;$avg_tokens&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Largest diff tokens: &lt;/span&gt;&lt;span class="nv"&gt;$max_tokens&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;repeated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;for &lt;/span&gt;hits &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file_hits&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; hits &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 1 &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;repeated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;repeated &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;fi
done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Files touched more than once: &lt;/span&gt;&lt;span class="nv"&gt;$repeated&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Projected monthly burn (20 commits/day): &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;avg_tokens &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt; tokens"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x fitcheck.sh
./fitcheck.sh 50 /path/to/your/repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is an estimate, not a bill. Tokenizers vary by model, and real prompts add overhead this script deliberately ignores. But the relative shape is what matters. A repo projecting 2 million tokens a month and a repo projecting 200 million are not in the same conversation.&lt;/p&gt;

&lt;p&gt;The 4-characters-per-token rule is deliberately conservative. Code is denser than prose, and a unified diff carries context lines that a real assistant would also see. If anything, the script undercounts what a full prompt with instructions and file excerpts would cost. That bias is fine for a fit test. You want the order of magnitude, not the invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the numbers
&lt;/h2&gt;

&lt;p&gt;If your average diff stays under roughly 2,000 tokens and your monthly projection fits inside the free allocation, the free tier is structurally sufficient. The only remaining question is latency, and that needs a live test. If your projection blows past the allocation by an order of magnitude, no amount of enthusiasm fixes the math. You need self-hosting, a paid tier, or a serious change in how you chunk work. And if the same files keep reappearing in commit after commit, you're a candidate for context reuse — but only if the server you pick actually supports it. That's a feature question, not a pricing question.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fit signal&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Projection under 60% of free allocation&lt;/td&gt;
&lt;td&gt;Good fit&lt;/td&gt;
&lt;td&gt;Run a 30-minute live latency test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Projection between 60% and 100%&lt;/td&gt;
&lt;td&gt;Borderline&lt;/td&gt;
&lt;td&gt;Watch real usage for two weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Projection over 100%&lt;/td&gt;
&lt;td&gt;Bad fit&lt;/td&gt;
&lt;td&gt;Self-host, pay, or split work into smaller units&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same files touched repeatedly&lt;/td&gt;
&lt;td&gt;Context reuse candidate&lt;/td&gt;
&lt;td&gt;Verify the server caches context before relying on it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strict data residency or audit needs&lt;/td&gt;
&lt;td&gt;Bad fit regardless of math&lt;/td&gt;
&lt;td&gt;Self-host or use a compliant paid option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The case that keeps coming up
&lt;/h2&gt;

&lt;p&gt;MonkeyCode keeps showing up in my feeds, so I ran the framework on it. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The project is open source, and it offers two things that matter here: free model access with a 10-million-token allocation, and a free server option so you can try it without standing up your own inference host. I'm not going to recite the marketing numbers back at you. Run the script instead.&lt;/p&gt;

&lt;p&gt;Take a worked example. A mid-sized service repo with 7,000-token average diffs and 20 commits a day projects to roughly 3.1 million tokens per month. That fits inside a 10-million allocation with room to spare. The verdict is a trial, not a migration. A monorepo with heavy generated code, averaging 90,000 tokens per diff, projects past 40 million. The free tier runs out before the work does. That's not a product failure. It's a fit failure, and the script catches it in under a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should not use the free option
&lt;/h2&gt;

&lt;p&gt;Teams with data-residency requirements, because your code crosses a network boundary. Anyone generating code for regulated systems where provenance and audit trails matter. And repos so large that the context window alone would consume the allocation before the first useful answer. Also, if you're wiring an AI server into a CI pipeline, a free server without an SLA is a risk you're pricing, not a cost you're avoiding.&lt;/p&gt;

&lt;p&gt;The free-versus-self-hosted debate is usually a proxy for something else. Nobody wants to be the person who picked the wrong tool. The fix is to stop picking and start measuring. Your git history has been keeping score for years. Read it.&lt;/p&gt;

&lt;p&gt;Clone the project, run fitcheck.sh against your own repo, and let the numbers pick your side.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
      <category>git</category>
    </item>
    <item>
      <title>The Fit Score: When a Free AI Coding Server Earns Its Place in Your Repo</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Tue, 25 Aug 2026 04:46:54 +0000</pubDate>
      <link>https://dev.to/devpro_9167/the-fit-score-when-a-free-ai-coding-server-earns-its-place-in-your-repo-3kmh</link>
      <guid>https://dev.to/devpro_9167/the-fit-score-when-a-free-ai-coding-server-earns-its-place-in-your-repo-3kmh</guid>
      <description>&lt;p&gt;Picture a team that wired a free AI coding server into its review loop. Two weeks later, the sprint stalls. The quota resets on a Tuesday, mid-refactor, and every "just ask the assistant" habit the team built turns into a queue of blocked tasks. Nobody read the reset policy, because nobody expected a free tier to become load-bearing.&lt;/p&gt;

&lt;p&gt;Free is a pricing model, not an architecture decision.&lt;/p&gt;

&lt;p&gt;The mistake wasn't the tool. It was treating fit as a property of the product when it's actually a property of the pairing between product and repo. Every week brings another coding model, another free tier, another leaderboard telling you the last one was wrong. The useful question is narrower: does this particular server fit this particular codebase, right now?&lt;/p&gt;

&lt;p&gt;I use three axes to answer it, and one script to make the answer explicit.&lt;/p&gt;

&lt;p&gt;Data sensitivity comes first. If your codebase contains customer data, internal credentials, or unreleased product logic, every prompt you send to a managed endpoint is a data-transfer decision. Some teams can accept that. Regulated ones cannot, no matter how good the model is.&lt;/p&gt;

&lt;p&gt;Latency tolerance is second. A self-hosted model on modest hardware answers in seconds, but it competes with your build for CPU and memory. A managed server shifts that cost elsewhere and adds network round-trips and shared-queue variance. The real question is whether your workflow can absorb a slow tail. Interactive completion, probably yes. A CI gate that blocks merges, probably not.&lt;/p&gt;

&lt;p&gt;Workload shape is third. Free tiers are designed for spiky, low-volume use. If your team produces a steady, predictable stream of requests, the free allowance becomes a ceiling you will hit at the worst moment. If your usage is genuinely intermittent, the ceiling rarely matters.&lt;/p&gt;

&lt;p&gt;Here is the script I use to turn those axes into a number. It is deliberately crude — the weights encode my own priorities, and data risk dominates because it is the one failure you cannot roll back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;fit_score.py — decide whether a free managed AI coding server fits your repo.

Example:
  python3 fit_score.py --data-sensitivity 4 --latency-tolerance 2 &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;      --workload-shape steady --monthly-tokens 4 --ci-integration 3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;

&lt;span class="n"&gt;WEIGHTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_sensitivity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# 1 = public code, 5 = regulated data
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_tolerance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# 1 = CI gate, 5 = interactive only
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workload_shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# spiky vs steady request volume
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monthly_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# 1 = tiny usage, 5 = heavy usage
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ci_integration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# 1 = no CI, 5 = merge-blocking gate
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--data-sensitivity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--latency-tolerance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--workload-shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spiky&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;steady&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spiky&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--monthly-tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--ci-integration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;workload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;workload_shape&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;steady&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_sensitivity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data_sensitivity&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_tolerance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;latency_tolerance&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workload_shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monthly_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monthly_tokens&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ci_integration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ci_integration&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Low risk. Wire it in, monitor quota, move on.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;3.2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Borderline. Run a two-week staged trial with a hard rollback.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Do not default to free. Self-host, pay for a tier, or skip AI assistance.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fit score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Verdict: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;verdict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the weights to match your constraints. The point is to make the decision explicit instead of vibes-based.&lt;/p&gt;

&lt;p&gt;Once the score says "low risk," test the server before you trust it. This probe measures time-to-first-token and total completion time against any OpenAI-compatible endpoint. It is an example harness — adjust the headers and path to the provider you are actually testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# probe.sh — measure TTFT and total time for an OpenAI-compatible endpoint.&lt;/span&gt;
&lt;span class="c"&gt;# Usage: ENDPOINT=... MODEL=... API_KEY=... ./probe.sh&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENDPOINT&lt;/span&gt;:?set&lt;span class="p"&gt; ENDPOINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MODEL&lt;/span&gt;:?set&lt;span class="p"&gt; MODEL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;:?set&lt;span class="p"&gt; API_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;PROMPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Write a Python function that parses an nginx access log line and returns a dict. Keep it dependency-free.'&lt;/span&gt;

curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'\nTTFT: %{time_starttransfer}s\nTOTAL: %{time_total}s\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MODEL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;messages&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: [{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;role&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;content&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROMPT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}], &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;max_tokens&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 300}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENDPOINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/chat/completions"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it ten times, at different hours, and record the tail, not the median. A server that is fast at 2 p.m. and slow at 4 p.m. is still slow at 4 p.m. if that is when your team works.&lt;/p&gt;

&lt;p&gt;This is where MonkeyCode enters the evaluation. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is an open-source project that fits the pattern above: it offers free model access and a free server option, and the project currently advertises a 10-million-token free allowance. I have not independently verified the quota, the hardware behind the server, or the permanence of the offer — which is exactly why you should run the probe before building anything on it.&lt;/p&gt;

&lt;p&gt;On paper, MonkeyCode targets the quadrant the framework calls low-risk: spiky, low-volume, non-regulated teams that want to evaluate coding models without standing up a GPU box. That is a real niche. The free server removes the setup barrier, and the token allowance covers exploratory use. The same caveats apply as to any free tier: quotas reset, latency varies, and a product that is free today can change its terms tomorrow. Treat it as an evaluation vehicle, not infrastructure.&lt;/p&gt;

&lt;p&gt;Who should not use this approach? Teams with regulated data, because a managed endpoint is still an external endpoint. Teams with steady high-volume workloads, because a free allowance is a ceiling, not a plan. Teams that need offline guarantees, because free does not mean air-gapped. And teams that already have a working self-hosted setup — if your local model answers fast enough and your data never leaves the building, a free server is a downgrade dressed as a discount.&lt;/p&gt;

&lt;p&gt;If you are already running a probe like the one above, the MonkeyCode repo is worth a look as a data point, not a promise. Run the fit score, run the probe, and let the numbers argue with your assumptions. That is the whole method.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>Free AI Coding Server or Self-Hosted? Score the Tradeoffs Instead of Arguing</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Sat, 22 Aug 2026 14:39:04 +0000</pubDate>
      <link>https://dev.to/devpro_9167/free-ai-coding-server-or-self-hosted-score-the-tradeoffs-instead-of-arguing-20g1</link>
      <guid>https://dev.to/devpro_9167/free-ai-coding-server-or-self-hosted-score-the-tradeoffs-instead-of-arguing-20g1</guid>
      <description>&lt;p&gt;Picture two teams starting from the same commit this month. Team A signs up for a managed free tier, ships a feature in a day, then hits a quota wall during the demo. Team B rents a GPU box, spends two weeks wiring auth, model routing, and disk cleanup, and ships nothing yet. Both made rational choices. Both optimized the wrong constraint.&lt;/p&gt;

&lt;p&gt;The mistake is treating "free" and "self-hosted" as properties of a tool. They are not properties. They are constraints you trade against each other. A free managed server trades data locality and quota headroom for zero operations. A self-hosted box trades your engineering hours for control. The real question is not which option is better. It is which constraint your team can absorb without breaking.&lt;/p&gt;

&lt;p&gt;So stop arguing. Score it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seven dimensions, one weighted sum
&lt;/h2&gt;

&lt;p&gt;Score seven dimensions. Data sensitivity: how much of your code can legally leave your network. Ops capacity: who owns upgrades, restarts, and disk fills. Latency budget: interactive coding punishes round trips. Cost ceiling: hard zero, or a soft budget that can absorb surprises.&lt;/p&gt;

&lt;p&gt;Usage shape: steady trickle or spiky bursts, because quotas punish spikes. Lock-in tolerance: how painful switching is later. Feature velocity: how fast you want new models without touching infrastructure. Each dimension gets a weight from 1 to 5 for how much it matters to you, and each option gets a score from 1 to 5 for how well it satisfies that dimension. Multiply, sum, compare.&lt;/p&gt;

&lt;p&gt;That is the whole framework. Seven numbers, one spreadsheet. The arguments disappear once the weights are on the table, because most disagreements are really disagreements about weights, not about tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The artifact: a scoring script
&lt;/h2&gt;

&lt;p&gt;Here is the decision script I use. It is deliberately boring: no dependencies, no model calls, just a weighted sum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;decide.py — score AI coding tooling options against your constraints.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="n"&gt;OPTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;managed_free&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_sensitivity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ops_capacity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_budget&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost_ceiling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage_shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lock_in_tolerance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feature_velocity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;self_hosted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_sensitivity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ops_capacity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_budget&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost_ceiling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage_shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lock_in_tolerance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feature_velocity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paid_managed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_sensitivity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ops_capacity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_budget&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost_ceiling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage_shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lock_in_tolerance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feature_velocity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;WEIGHTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_sensitivity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ops_capacity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_budget&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost_ceiling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage_shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lock_in_tolerance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feature_velocity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dims&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;OPTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save it, run it, and you get a ranking. Then override the weights with a JSON file that reflects your actual pain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;weights.json
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"cost_ceiling"&lt;/span&gt;: 5, &lt;span class="s2"&gt;"ops_capacity"&lt;/span&gt;: 4, &lt;span class="s2"&gt;"data_sensitivity"&lt;/span&gt;: 2, &lt;span class="s2"&gt;"feature_velocity"&lt;/span&gt;: 4, &lt;span class="s2"&gt;"usage_shape"&lt;/span&gt;: 1&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python3 decide.py weights.json
managed_free    87
paid_managed    67
self_hosted     66
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A startup with no ops and a hard zero budget gets a clear answer: managed free. Now flip the weights for a regulated team with strong infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;weights.json
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"data_sensitivity"&lt;/span&gt;: 5, &lt;span class="s2"&gt;"latency_budget"&lt;/span&gt;: 5, &lt;span class="s2"&gt;"cost_ceiling"&lt;/span&gt;: 2, &lt;span class="s2"&gt;"usage_shape"&lt;/span&gt;: 4&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python3 decide.py weights.json
self_hosted     89
paid_managed    78
managed_free    74
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same script, opposite answer. That is the point. The framework does not tell you what to pick. It tells you which constraint you are actually optimizing, so the decision survives contact with reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where MonkeyCode fits in the managed-free row
&lt;/h2&gt;

&lt;p&gt;MonkeyCode is a concrete instance of the managed-free row: free model access plus a free server option, so you do not provision or maintain anything to run a real experiment. As of this writing, the operator advertises a 10 million token allowance on the free path. Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;The interesting part is not the token count. It is that the free server collapses the cost of trying to nearly zero. When the managed-free row wins on paper, the cheapest way to confirm it is a two-day spike with your real code, not a benchmark. Benchmarks measure the model. A spike measures the workflow: does the tool fit your repo, your review process, your tolerance for weird output?&lt;/p&gt;

&lt;p&gt;That is also why the open source part matters. You can read what the server actually does before you trust it with a repository. Trust is not a feature flag; it is something you verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should not use this approach
&lt;/h2&gt;

&lt;p&gt;Three groups should ignore the managed-free row entirely. First, teams whose code cannot leave their network: regulated finance, classified work, pre-IPO secrets. No score overrides a legal constraint. Second, teams whose usage is predictably spiky and whose demos cannot fail: a quota wall at the wrong moment is worse than a bill. Third, teams that refuse to change workflow: a new tool is a tax on muscle memory, and if nobody will pay that tax, the tool is dead on arrival.&lt;/p&gt;

&lt;p&gt;And who should not self-host? Anyone without a named owner for upgrades, security patches, and disk fills. A model server is a service you now operate. If your team cannot staff that, self-hosting is a hobby, not an architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The closing move
&lt;/h2&gt;

&lt;p&gt;Run the script. Change the weights until the result matches your actual pain, not your feed's opinion. Then pick the option that loses the least. If you want to test the managed-free row cheaply, MonkeyCode's free server is a reasonable place to run that two-day spike — and because it is open source, you can check what it does first. The decision is yours; the script just makes it honest.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Free Tier Isn't Free If It Doesn't Fit</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Fri, 21 Aug 2026 13:11:39 +0000</pubDate>
      <link>https://dev.to/devpro_9167/the-free-tier-isnt-free-if-it-doesnt-fit-594o</link>
      <guid>https://dev.to/devpro_9167/the-free-tier-isnt-free-if-it-doesnt-fit-594o</guid>
      <description>&lt;p&gt;A backend team picks the cheapest AI coding option, hits a hard token cap at 3 PM on release day, and spends the evening pasting stack traces into a free chat window. Another team buys a GPU, self-hosts an open model, and loses two weekends to driver hell before the first useful suggestion appears. Both teams optimized for price per token. Both ignored fit per workflow.&lt;/p&gt;

&lt;p&gt;The AI badge on a model card tells you what the vendor measured, not what your workflow needs. The same logic applies to access tiers. A free tier is not a price; it is a constraint set, and constraints are only cheap when they match your usage shape. This is a decision framework for choosing between free hosted access, paid APIs, and self-hosted models, plus a script that turns your habits into a score.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Four dimensions decide the fit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Volume shape comes first. Bursty users generate a lot in short windows — a prototype weekend, a refactor sprint, a learning session. Steady users feed the model daily, like a CI job. Token caps punish the steady user and barely touch the bursty one. If your monthly burn is under eight million tokens and irregular, a free allowance is a feature, not a compromise.&lt;/p&gt;

&lt;p&gt;Data gravity is second. Can your code leave your machine? Proprietary algorithms, regulated data, and client contracts often answer no. That single constraint eliminates every hosted option, free or paid, and forces self-hosting. For everyone else, the question is about comfort, not compliance.&lt;/p&gt;

&lt;p&gt;Ops budget is third, and it is the one people forget. Self-hosting shifts cost from money to time. Someone must patch the box, watch the GPU temperature, and restart the service at 2 AM. If your team has zero infrastructure hours to spare, the "free" server is the most expensive option you can choose.&lt;/p&gt;

&lt;p&gt;Failure tolerance is fourth. What happens when the provider rate-limits you mid-sprint? If the answer is "we wait," hosted is fine. If the answer is "we miss a deadline," you need a fallback or a local model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The artifact: a fit-score script.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;Estimate which AI coding access model fits your workflow.

Inputs: monthly token estimate, data sensitivity flag,
latency budget in ms, ops hours available per month,
and whether usage is steady (daily) or bursty.
&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fit_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens_per_month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sensitive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;latency_ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops_hours&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;steady&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;free_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;paid_api&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tokens_per_month&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8_000_000&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;steady&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;free_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;tokens_per_month&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;50_000_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;paid_api&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sensitive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;free_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;paid_api&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;latency_ms&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;free_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;paid_api&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ops_hours&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;free_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;paid_api&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;ops_hours&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;paid_api&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self_hosted&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# tokens/month, sensitive, latency_ms, ops_hours, steady
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fit_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The weights are deliberately simple. You can argue with them; that is the point. The script exists to force a conversation about your numbers instead of the vendor's marketing.&lt;/p&gt;

&lt;p&gt;Run a realistic case. A solo developer writes six million tokens per month, mostly in bursts, with no sensitive data, a 900 ms latency budget, and two spare ops hours. The output is &lt;code&gt;{'free_hosted': 6, 'paid_api': 3, 'self_hosted': 2}&lt;/code&gt;. The free bucket wins on every dimension. A regulated fintech team with eighty million tokens per month, sensitive code, and a 300 ms budget lands at the opposite end: self-hosting is the only defensible choice.&lt;/p&gt;

&lt;p&gt;How do you get the token estimate if you have never measured it? A rough proxy works: count the characters in your weekly diff output, divide by four, and multiply by the fraction of that code you actually paste into a model. &lt;code&gt;git log --since='1 month ago' -p | wc -c&lt;/code&gt; gives the raw number in one command. It is an estimate, not a meter, but it beats guessing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where MonkeyCode sits in this framework.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One option in the free-hosted bucket is MonkeyCode, an open-source project that currently offers free model access with a 10 million token allowance and a free server option, so you do not need to provision your own box to start. Disclosure: This article was prepared as part of MonkeyCode's product outreach. I am not claiming it is the best option for every team; the framework above is the point. The allowance fits a bursty pattern — a few focused sessions per week — not a pipeline generating code all day.&lt;/p&gt;

&lt;p&gt;The tradeoffs look like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Free hosted&lt;/th&gt;
&lt;th&gt;Paid API&lt;/th&gt;
&lt;th&gt;Self-hosted&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Upfront cost&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;GPU + setup time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token ceiling&lt;/td&gt;
&lt;td&gt;Fixed allowance&lt;/td&gt;
&lt;td&gt;Pay per use&lt;/td&gt;
&lt;td&gt;Hardware bound&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data leaves machine&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ops burden&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency control&lt;/td&gt;
&lt;td&gt;Provider&lt;/td&gt;
&lt;td&gt;Provider&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical failure&lt;/td&gt;
&lt;td&gt;Cap mid-sprint&lt;/td&gt;
&lt;td&gt;Bill shock&lt;/td&gt;
&lt;td&gt;Driver hell&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The free server option removes one objection to hosted tools: you do not need to maintain a box. You still give up data locality and latency control, and the allowance is finite and provider-controlled. Those are real costs, and the framework accounts for them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who should not use this approach.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your code is under a compliance regime, stop reading and self-host. If your monthly token burn is steady and above the allowance, a paid API with predictable pricing beats a free tier that interrupts your day. If your team has no infrastructure skills, do not romanticize the GPU; a hosted option, free or paid, is the rational choice. The free tier is a fit for the bursty, non-sensitive, low-ops developer. It is a trap for everyone else.&lt;/p&gt;

&lt;p&gt;The badge on the model card never told you this. The vendor's benchmark never told you this. A ten-line script with honest inputs tells you more than both. If your numbers land in the bursty bucket, run the script, then give the free tier a weekend test — the math will tell you quickly whether it fits.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
    <item>
      <title>The MiniMax H3 Leaderboard Is Loud. My Repo Is Louder.</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Fri, 14 Aug 2026 11:08:21 +0000</pubDate>
      <link>https://dev.to/devpro_9167/the-minimax-h3-leaderboard-is-loud-my-repo-is-louder-4e59</link>
      <guid>https://dev.to/devpro_9167/the-minimax-h3-leaderboard-is-loud-my-repo-is-louder-4e59</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'endraw'&lt;/p&gt;
</description>
      <category>ai</category>
      <category>testing</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Grade Every New Coding Model Against My Own Bug History — Here's the Scorecard</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Thu, 13 Aug 2026 03:34:31 +0000</pubDate>
      <link>https://dev.to/devpro_9167/i-grade-every-new-coding-model-against-my-own-bug-history-heres-the-scorecard-3e25</link>
      <guid>https://dev.to/devpro_9167/i-grade-every-new-coding-model-against-my-own-bug-history-heres-the-scorecard-3e25</guid>
      <description>&lt;p&gt;A model shipped this week. The screenshots arrived within hours: impossible refactors, one-shot fixes, declarations that everything before it is obsolete. I stopped arguing with launch-week sentiment a while ago. Instead I do something more boring and more useful: I grade the new release against a scorecard built from my own git history, and the scorecard decides whether the model gets anywhere near my working tree.&lt;/p&gt;

&lt;p&gt;This is a different framing from the usual "run a benchmark" advice. I'm not benchmarking. I'm auditing a candidate against incidents I've already lived through — and the grading is weighted, because not all failures cost the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scorecards beat impressions
&lt;/h2&gt;

&lt;p&gt;When I post-mortemed my own bad model adoptions, the mistakes clustered into three buckets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Selection effects in what I saw.&lt;/strong&gt; Public demos are chosen because they succeed. My code has none of that filtering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recall masquerading as reasoning.&lt;/strong&gt; Tasks that look like public GitHub code can be answered from memory. My evaluation tasks must come from private history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Noisy first impressions.&lt;/strong&gt; A model that shines on a blank file can still wreck a mature module with house conventions. The second case is what I actually pay for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A scorecard handles all three: frozen tasks from private PRs, mechanical checks, and weights that reflect blast radius rather than vibes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Mine your own git history for tasks
&lt;/h2&gt;

&lt;p&gt;The fastest way to build a task suite is to recover work you've already verified. I scan for small, self-contained fixes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Candidate commits: single-file changes, small diffs, likely bugfixes&lt;/span&gt;
git log &lt;span class="nt"&gt;--since&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-6 months"&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/^[0-9a-f]{7,}/ {sha=$1} /\.[a-z]+$/ {print sha, $0}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$1 == 1 {print}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; candidates.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From that list I hand-pick eight episodes: a null-handling regression, a race in a background worker, a migration that needed a rollback path, a refactor tangled in an internal style guide. Each becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;suite/worker-race/
  brief.md        # the task, written once, model-agnostic
  before.patch    # starting state of the code
  check.sh        # mechanical verification, exit 0 = pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two rules keep this honest: every task has a known-good resolution (because I merged one), and &lt;code&gt;check.sh&lt;/code&gt; never gets edited to accommodate a model. A confusing brief is a defect in the task, not evidence about the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Weighted scoring, not pass counting
&lt;/h2&gt;

&lt;p&gt;Raw pass rate hides the failures that actually hurt. I assign each task a weight reflecting what a wrong answer would cost in production, then let a script do the arithmetic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;grade.py — read results/*.json, emit a weighted verdict.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="c1"&gt;# weight = blast radius if this category fails silently
&lt;/span&gt;&lt;span class="n"&gt;WEIGHTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scope&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revertable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;grade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tests_pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;touched_out_of_scope&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scope&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;diff_reverts_cleanly&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;   &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revertable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results/*.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;grade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deliberate asymmetry: &lt;strong&gt;scope discipline is weighted three times higher than correctness.&lt;/strong&gt; In my incident history, the expensive model behavior was never "the fix was wrong" — tests catch that. It was "the fix was right and it also rewrote two files nobody asked about." A candidate at 91% with surgical diffs outranks one at 95% that wanders. The weighting table is the actual decision document; tune the weights to your own incident log, not to mine.&lt;/p&gt;

&lt;p&gt;I also log latency and token counts per task, because "free" and "cheap" claims only mean something measured against your workload's shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: One attempt, frozen conditions
&lt;/h2&gt;

&lt;p&gt;Every (model, task) pair gets exactly one shot at temperature zero. Retries and re-prompting measure my patience, not the model. The runner is a thin loop that applies &lt;code&gt;before.patch&lt;/code&gt;, invokes whatever endpoint I'm testing through a generic wrapper, applies the returned diff, runs &lt;code&gt;check.sh&lt;/code&gt;, and dumps a JSON record into &lt;code&gt;results/&lt;/code&gt;. Swapping in this week's release is one environment variable — the model is the variable; everything else stays frozen so scores are comparable across weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running this without a line item in the budget
&lt;/h2&gt;

&lt;p&gt;Scoring every new release against paid APIs adds up fast, and self-hosting a day-one model eats the hour you were trying to save. For these grading passes I currently use MonkeyCode, which offers free access to a selection of models along with a free server option for the runner side — a full eight-task grading run costs me the time, not an invoice. Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;Caveats I'd apply to any free tier, this one included: treat it as an evaluation environment, not a production commitment, and don't assume a hosted endpoint behaves identically to the vendor's reference deployment. If a candidate passes and later graduates to real use, I re-grade it against the production endpoint before cutover — I've seen endpoint differences flip a verdict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing earns a probation period, not access
&lt;/h2&gt;

&lt;p&gt;A strong score buys the model one week as a suggestion-only assistant: it proposes, I read every line, nothing merges itself. Week two it may open draft PRs on low-risk paths. Proximity to CI-gated auto-merge comes later and stays behind required human approval. The scorecard is a necessary gate, never a sufficient one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this breaks down
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Eight weighted tasks is a smoke screen, not a ranking system.&lt;/strong&gt; It eliminates disasters and wanderers; it won't finely separate two good candidates. Grow the suite before making expensive commitments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-shot, temperature-zero grading is unfair to agent-loop models.&lt;/strong&gt; If your workflow is multi-turn, build a frozen multi-turn variant — but freeze it just as hard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small or mostly-greenfield repos have less to lose.&lt;/strong&gt; This scorecard pays for itself on large, old, convention-heavy codebases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data handling comes first.&lt;/strong&gt; If code can't leave your perimeter, don't send it to any third-party endpoint, free tiers least of all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The feed will crown another model next week. The scorecard doesn't care — it takes an hour, speaks in weights, and grades against bugs I've actually paid for. If you're assembling your own version and want a zero-cost place to run it, MonkeyCode's free model access and server are a reasonable bench; the git history, the checks, and the weights have to come from you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Your Feed Says the New Model Is Great. Mine Says Prove It in Under an Hour</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Mon, 10 Aug 2026 10:38:24 +0000</pubDate>
      <link>https://dev.to/devpro_9167/your-feed-says-the-new-model-is-great-mine-says-prove-it-in-under-an-hour-1ec8</link>
      <guid>https://dev.to/devpro_9167/your-feed-says-the-new-model-is-great-mine-says-prove-it-in-under-an-hour-1ec8</guid>
      <description>&lt;p&gt;Another open-weight release, another week of screenshots. This time it's MiniMax M3 filling my timeline, and the pattern is identical to every release before it: polished demos everywhere, reproducible evidence almost nowhere.&lt;/p&gt;

&lt;p&gt;I've already covered why benchmark passes shouldn't earn merge privileges, and how to build a multi-day harness for comparing free coding models properly. But there's a cheaper question that comes first: is this model even worth feeding into that harness? Most aren't. What follows is the screening protocol I use to answer that in under an hour, before I invest a single weekend hour.&lt;/p&gt;

&lt;p&gt;The output isn't a ranking. It's a short memo: proceed, park, or pass — with receipts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why demos can't answer the only question that matters
&lt;/h2&gt;

&lt;p&gt;Launch-week content almost always answers "can this model do something impressive?" Of course it can. Every frontier-adjacent release can. The questions that actually determine whether a model belongs in my workflow are narrower:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does it survive contact with &lt;em&gt;my&lt;/em&gt; unglamorous tasks — the refactors, the half-documented bugs, the regex archaeology?&lt;/li&gt;
&lt;li&gt;Does it fail in ways I can live with, or in ways that quietly corrupt my codebase?&lt;/li&gt;
&lt;li&gt;Can I check any of this without spending money or owning serious hardware?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one used to be the blocker. It isn't anymore, and I'll come back to why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The protocol: verify, stress, cost
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase A — Bring your own tasks, not its demo (~15 min)
&lt;/h3&gt;

&lt;p&gt;Pull three prompts from work you genuinely did recently. Mine change every time, but here's the set I used this week:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A. A 50-line function from an actual project + "make this testable
   without changing its observable behavior; list every behavioral risk"
B. A flaky-test description from our tracker + "propose three hypotheses
   ranked by how cheap each is to falsify"
C. A confusing build error + "explain the failure chain, then give the
   smallest change that fixes the root cause rather than the symptom"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not grading eloquence. I'm watching for three instant-disqualifiers: referencing functions or flags that don't exist in the material I provided, ignoring an explicit constraint, or producing a fix that addresses the example I gave but obviously not the class of problem. Any one of these and the memo says &lt;em&gt;pass&lt;/em&gt;. Done. The model just saved me a weekend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase B — Interrogate the weakest answer (~15 min)
&lt;/h3&gt;

&lt;p&gt;First replies are marketing. Second replies are character. Take the shakiest output from Phase A and challenge it directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Point to the exact line where your claim is supported by what I gave you."&lt;/li&gt;
&lt;li&gt;"Your hypothesis B contradicts the log I pasted. Which one is wrong, and how would you check?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I'm scoring is &lt;em&gt;epistemic behavior&lt;/em&gt;, not accuracy. A model that says "you're right, I conflated the two call sites — here's the corrected trace" is a candidate. A model that rephrases the same wrong answer with more confidence is a liability wearing a nicer jacket. In my experience this single exchange predicts long-run usefulness better than any single-shot benchmark.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase C — One long task, and an honest look at what the poking costs (~25 min)
&lt;/h3&gt;

&lt;p&gt;Short prompts hide context degradation, so I end with one task that forces the model to hold several files in its head at once — something like "here are three modules; trace where this value can become undefined between entry and the database write."&lt;/p&gt;

&lt;p&gt;And here's where the economics of skepticism have genuinely shifted. Disclosure: This article was prepared as part of MonkeyCode's product outreach. It's relevant to Phase C specifically because MonkeyCode offers free model access alongside a free server option, which removes the two classic excuses for skipping this phase — no API budget, no local GPU. When the weights are open and a place to run them costs nothing, "verify the claims yourself" stops being a luxury and becomes a routine chore. That's the healthiest thing about the current open-model moment: nobody has to trust launch-day threads, mine included.&lt;/p&gt;

&lt;h2&gt;
  
  
  The memo format
&lt;/h2&gt;

&lt;p&gt;After roughly an hour I write five lines. No gut feelings, each line cites something I actually saw:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;What qualifies as evidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Constraint discipline&lt;/td&gt;
&lt;td&gt;Followed every explicit instruction across all prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grounding&lt;/td&gt;
&lt;td&gt;Never cited code or APIs outside the provided context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behavior under challenge&lt;/td&gt;
&lt;td&gt;Located and corrected its own mistake in one or two turns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-context stamina&lt;/td&gt;
&lt;td&gt;Cross-file reasoning stayed coherent; nothing dropped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verdict&lt;/td&gt;
&lt;td&gt;proceed / park / pass, plus one sentence why&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Park" is the underrated verdict: nothing disqualifying, nothing compelling, revisit in a month. The default for hype is &lt;em&gt;pass&lt;/em&gt; — the burden of proof sits on the model, not on me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this protocol breaks down
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An hour can expose a bad model. It cannot certify a good one. "Proceed" means "earns a slot in the real harness," full stop.&lt;/li&gt;
&lt;li&gt;If your workflow is one mature, well-tuned setup you already trust, even a passing triage may not clear your switching costs.&lt;/li&gt;
&lt;li&gt;Free access tiers and free servers are offers, not entitlements — limits and availability can change. Confirm what exists today before designing habits around it, the same skepticism you'd aim at a leaderboard.&lt;/li&gt;
&lt;li&gt;On M3 specifically: everything above was run against my own tasks, not quoted from published numbers. Read the official release notes, then reproduce on your material before repeating anyone's figures — especially figures from strangers on the internet, a category that includes me.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The actual takeaway
&lt;/h2&gt;

&lt;p&gt;The durable skill isn't picking the "best" model each cycle. It's owning a cheap, repeatable filter that answers "is this one good &lt;em&gt;for my work&lt;/em&gt;" faster than the hype cycle can move on. Open weights plus free places to run them have collapsed the cost of that filter to about an hour. The checklist is the only missing piece, and now you have one.&lt;/p&gt;

&lt;p&gt;If cost or hardware has been your reason for taking model claims on faith, the free server option over at MonkeyCode is a reasonable place to run Phase C yourself — but the protocol works anywhere you can get the model to answer the same three tasks twice.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A Staged Gate for Adopting Free AI Coding Models Without Wrecking Your Repo</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Mon, 10 Aug 2026 08:10:53 +0000</pubDate>
      <link>https://dev.to/devpro_9167/a-staged-gate-for-adopting-free-ai-coding-models-without-wrecking-your-repo-1eh8</link>
      <guid>https://dev.to/devpro_9167/a-staged-gate-for-adopting-free-ai-coding-models-without-wrecking-your-repo-1eh8</guid>
      <description>&lt;p&gt;In my last two posts I argued for running your own harness before trusting any coding model, and for refusing to merge model output just because a benchmark says it's good. This post is the practical middle step I skipped: &lt;strong&gt;how do you decide whether a free-tier model belongs in your daily loop at all?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The wrong way to answer that is vibes. You paste three prompts, one looks impressive, and suddenly the model is writing half your PRs. The right way is a staged gate: cheap tests first, expensive tests only for survivors. Here's the exact sequence I use, plus the scoring artifact you can copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The staged gate, in one picture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stage 0: Task taxonomy     → does this model even fit my work?
Stage 1: Static smoke test → syntax, imports, obvious hallucination
Stage 2: Sandbox execution → does it run? do my tests pass?
Stage 3: Diff review       → would I approve this PR from a junior?
Stage 4: Shadow week       → model proposes, human disposes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A model must pass each stage to reach the next. Most free models fail at Stage 2, which is fine — the whole point of staging is that Stage 2 costs you an afternoon, not a production incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 0: Write your task taxonomy before touching any model
&lt;/h2&gt;

&lt;p&gt;This is the step everyone skips, and it's why their conclusions are useless. Write down the five to eight task types that actually make up your week, weighted by frequency. Mine, as an example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task type&lt;/th&gt;
&lt;th&gt;Share of my week&lt;/th&gt;
&lt;th&gt;Failure cost if model is wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Boilerplate / CRUD scaffolding&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test writing for existing code&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refactoring within one file&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-file bug diagnosis&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Config / CI / YAML wrangling&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;High (silent breakage)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unfamiliar-library usage&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code review / explanation&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Your table will differ. The weights matter because a model that's great at boilerplate and terrible at cross-file diagnosis might still be a net win &lt;em&gt;if you restrict it to the first row&lt;/em&gt;. Free tiers especially tend to be uneven — strong on common patterns, shakier on long-context reasoning. The taxonomy turns "is this model good?" (unanswerable) into "which rows of my table is this model good for?" (testable).&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1–2: A minimal scoring script
&lt;/h2&gt;

&lt;p&gt;For each row of the taxonomy, pick two or three real tasks from your own git history — not from a public benchmark, because public tasks leak into training data. Then score outputs mechanically. Here's a stripped-down version of the scorer I run (adapt freely; this is illustrative, not a product):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Score model outputs per task type. Usage: score.py results.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TemporaryDirectory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solution.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pytest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-x&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                           &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;rec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# rec: {"task_type": ..., "code": ..., "test_file": ..., "review_pass": bool}
&lt;/span&gt;        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;exec_ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test_file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exec_ok&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;review_pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
        &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; n=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  mean=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two deliberate choices here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Execution is weighted above review (0.6/0.4)&lt;/strong&gt; because "it compiles and passes tests" is objective, while my review judgment is not. When the two disagree, the task goes into a manual pile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scores are per task type, never aggregated.&lt;/strong&gt; An overall mean would hide exactly the failure modes you need to see. A model scoring 0.9 on scaffolding and 0.3 on cross-file bugs is not a "0.6 model" — it's a scaffolding tool.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Stage 3–4: The human gates
&lt;/h2&gt;

&lt;p&gt;Stage 3 is a diff review with a single question: &lt;em&gt;would I approve this if a junior teammate submitted it?&lt;/em&gt; Same bar, no discounts for "the AI did it." If anything, review harder — model errors are systematic, not random, so one weird import usually means a whole class of confident wrongness.&lt;/p&gt;

&lt;p&gt;Stage 4 is a shadow week: the model proposes changes, you write or heavily edit everything yourself, and you log how often its proposal was usable. If the usable rate doesn't beat your baseline after a week, the model exits the loop for that task type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where free infrastructure fits
&lt;/h2&gt;

&lt;p&gt;The expensive part of this workflow isn't the model calls — it's the iteration. Running Stage 1–3 across six task types and three candidate models means a few hundred executions, and you'll want to rerun it every time a model updates. Paying per-token for exploratory evaluation is how people end up evaluating on three prompts and calling it done.&lt;/p&gt;

&lt;p&gt;This is where I've been using MonkeyCode: it offers free access to a set of coding models and a free server option, which maps neatly onto this workflow — free model access covers the candidate pool, and the free server is enough to host the sandbox executor for Stage 2 without standing up paid infrastructure. Disclosure: This article was prepared as part of MonkeyCode's product outreach. I won't quote quotas, model lists, or limits here because they change; check the current offering yourself before designing around it. If your evaluation volume is small, any free tier — theirs or a provider's own — works equally well for Stages 0–3. The workflow is the point, not the vendor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision table: when a free model earns a seat
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Outcome across your taxonomy&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Passes stages 1–4 on ≥2 high-frequency task types&lt;/td&gt;
&lt;td&gt;Adopt, restricted to those task types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passes on low-frequency or low-risk types only&lt;/td&gt;
&lt;td&gt;Keep for drafts; never let it touch CI config or cross-file changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passes execution but fails diff review repeatedly&lt;/td&gt;
&lt;td&gt;Do not adopt; systematic style/logic drift will eat your review time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fails sandbox execution broadly&lt;/td&gt;
&lt;td&gt;Drop it and retest in a few months; models move fast&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Limitations, and who should skip this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sample size is your enemy.&lt;/strong&gt; Two tasks per type is a smoke screen, not a study. If you can't assemble at least 15–20 real tasks, your results are noise. Be honest about that before acting on them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free tiers change without notice.&lt;/strong&gt; Rate limits, model availability, and server capacity can shift mid-evaluation. Never wire a free tier into CI or anything with an SLA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This doesn't transfer to regulated or high-blast-radius code.&lt;/strong&gt; If a wrong suggestion can touch auth, payments, medical logic, or infra-as-code that deletes things, free-model gating is the wrong conversation entirely — you need audited tooling and human authorship guarantees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solo maintainers gain the most.&lt;/strong&gt; Teams with established review culture already have a Stage 3; adding model evaluation is cheap. If you're solo with no tests, build the tests first — the harness is useless without them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gate is boring on purpose. Boring is what keeps a confident, free, occasionally-wrong model from becoming a line item in your incident review. If you've run a similar staged evaluation, I'd genuinely like to hear where your models failed — the taxonomy rows where free tiers collapse are more useful shared than hoarded.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Model Passed Your Benchmark. Now Stop Merging Its Code Blindly</title>
      <dc:creator>Sam Li</dc:creator>
      <pubDate>Fri, 07 Aug 2026 03:15:45 +0000</pubDate>
      <link>https://dev.to/devpro_9167/the-model-passed-your-benchmark-now-stop-merging-its-code-blindly-3ae2</link>
      <guid>https://dev.to/devpro_9167/the-model-passed-your-benchmark-now-stop-merging-its-code-blindly-3ae2</guid>
      <description>&lt;p&gt;A few weeks ago I wrote about &lt;a href="https://dev.to/devpro_9167/a-reproducible-test-harness-for-comparing-free-ai-coding-models-before-you-commit"&gt;building a reproducible test harness for comparing free AI coding models before you commit&lt;/a&gt;. That harness answers one question: &lt;em&gt;which model should I use?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It does not answer the harder follow-up: &lt;em&gt;once a model generates a patch for my real codebase, when is it safe to merge?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This week there was a great discussion on DEV about "understanding over origin" — the idea that it doesn't matter whether code came from a human or a model, only whether someone actually understands it. I agree with the principle, but principles don't survive contact with a busy afternoon. What survives is a checklist with teeth. So here is the pipeline I bolted onto my model harness: every AI-generated patch has to pass through a scripted review gate before I even read it, and the script produces a scorecard that tells me &lt;em&gt;how carefully&lt;/em&gt; I need to read it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with eyeballing diffs
&lt;/h2&gt;

&lt;p&gt;When a model produces a 40-line diff that looks idiomatic, my brain does a dangerous thing: it pattern-matches on style and skips semantics. The code &lt;em&gt;reads&lt;/em&gt; like something I'd write, so I approve it like something I'd write. The failures I've actually shipped from AI-generated code were never syntax errors — the tests even passed. They were things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A retry loop that retried on the wrong exception type, so real errors got swallowed.&lt;/li&gt;
&lt;li&gt;A query filter that was subtly wider than the one it replaced (tests passed because fixtures were too small to notice).&lt;/li&gt;
&lt;li&gt;A dependency added for a one-liner the standard library already covers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three would have been caught by asking four boring questions &lt;em&gt;before&lt;/em&gt; reading the code. So I scripted the questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The review gate: a reproducible artifact
&lt;/h2&gt;

&lt;p&gt;The gate is a small shell script. It takes a patch file, applies it to a throwaway worktree, and runs four checks. It never touches my working branch, and it prints a one-line verdict at the end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# review-gate.sh &amp;lt;patch-file&amp;gt; &amp;lt;base-branch&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;PATCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;WT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; /tmp/ai-review.XXXXXX&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"== 1. Isolate =="&lt;/span&gt;
git worktree add &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; apply &lt;span class="nt"&gt;--check&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"VERDICT: REJECT (patch does not apply cleanly to &lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
  git worktree remove &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
&lt;/span&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; apply &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"== 2. Full test suite (not just the touched package) =="&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--silent&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"VERDICT: REJECT (tests fail)"&lt;/span&gt;
  git worktree remove &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"== 3. Diff surface audit =="&lt;/span&gt;
git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; diff &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--stat&lt;/span&gt;
&lt;span class="nv"&gt;NEW_DEPS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; diff &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; package.json &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'^+.*".*":'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"new dependency entries: &lt;/span&gt;&lt;span class="nv"&gt;$NEW_DEPS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"== 4. Behavior-change heuristics =="&lt;/span&gt;
&lt;span class="c"&gt;# Flag the patterns that have burned me before&lt;/span&gt;
&lt;span class="nv"&gt;SUSPECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; diff &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^\+.*(catch|except|retry|timeout|WHERE|filter\()'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"error-handling/query lines added: &lt;/span&gt;&lt;span class="nv"&gt;$SUSPECT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEW_DEPS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SUSPECT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 5 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"VERDICT: REVIEW CLOSELY (expanded surface area)"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"VERDICT: STANDARD REVIEW"&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;git worktree remove &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adapt steps 2–4 to your stack. The point isn't my specific greps — it's that the &lt;em&gt;checks run before my opinion does&lt;/em&gt;, every time, identically. Step 1 exists because a model's patch often "works" only against the stale context it was shown; applying it to a clean checkout of current &lt;code&gt;main&lt;/code&gt; is itself a finding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the free compute fits
&lt;/h2&gt;

&lt;p&gt;This loop has two compute-hungry halves: generating candidate patches, and running the gate (full test suites per candidate get slow). Both halves are exactly where I've been using MonkeyCode. Disclosure: This article was prepared as part of MonkeyCode's product outreach.&lt;/p&gt;

&lt;p&gt;Concretely: I use MonkeyCode's free model access to generate two or three independent candidate patches for the same task, and its free server option to run the gate script against each candidate so my laptop isn't tied up running three test suites in parallel. Generating multiple candidates is the underrated move here — when two independent generations converge on the same approach, my review confidence goes up; when they diverge, the divergence points are exactly where I read line-by-line.&lt;/p&gt;

&lt;p&gt;I'm deliberately not quoting quotas, model names, or performance numbers, because those change and you should check the current state yourself. What matters for this workflow is just: the generation step and the sandboxed-test step cost me nothing and don't monopolize my machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision table
&lt;/h2&gt;

&lt;p&gt;After the gate runs, I use this to decide how much of my own attention to spend:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gate output&lt;/th&gt;
&lt;th&gt;My review depth&lt;/th&gt;
&lt;th&gt;Typical action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Patch doesn't apply&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Re-prompt with fresher context; don't hand-fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests fail&lt;/td&gt;
&lt;td&gt;Read the failure only&lt;/td&gt;
&lt;td&gt;Regenerate or discard; never patch the patch blindly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passes, no new deps, low suspect count&lt;/td&gt;
&lt;td&gt;Read every changed line once&lt;/td&gt;
&lt;td&gt;Merge after reading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passes, new deps or high suspect count&lt;/td&gt;
&lt;td&gt;Read lines + write one adversarial test&lt;/td&gt;
&lt;td&gt;Merge only if my new test passes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Two candidates diverge on approach&lt;/td&gt;
&lt;td&gt;Read both diffs at the divergence point&lt;/td&gt;
&lt;td&gt;Pick one, write down why in the commit message&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last row is the one that pays rent. "Write down why" is my personal guardrail for the understanding-over-origin principle: if I can't explain in one sentence why this diff is correct, it doesn't merge, regardless of how green the tests are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations, and who shouldn't do this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The heuristics in step 4 are mine, tuned to my past failures.&lt;/strong&gt; Yours will be different. Start with an empty list and add a pattern every time AI-generated code burns you — the script should grow scar tissue, not ship with mine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Green tests are a floor, not a ceiling.&lt;/strong&gt; If your test suite has weak coverage, the gate's value collapses to step 1 and the greps. Fix coverage first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't run this for trivial diffs.&lt;/strong&gt; A five-line config change doesn't need a worktree and a scorecard; ceremony has a cost too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This is not a security review.&lt;/strong&gt; Dependency additions get &lt;em&gt;flagged&lt;/em&gt;, not &lt;em&gt;audited&lt;/em&gt;. If the model adds a package, that package still needs a real look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your repo's tests can't run in a clean checkout&lt;/strong&gt; (hidden local state, manual env setup), step 1 will fail constantly and teach you nothing. That's actually a useful signal about your repo, but fix it before adopting the gate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Model comparison tells you which generator to trust on average. A review gate tells you whether to trust &lt;em&gt;this specific patch&lt;/em&gt;. The first question is interesting; the second is the one that decides what your users run. If you're generating candidates with free model access anyway, the marginal cost of gating every patch through a clean-worktree script is about fifteen lines of bash — and one honest commit message at a time, it keeps the understanding on your side of the merge button.&lt;/p&gt;

&lt;p&gt;If you've built your own version of this — especially the failure-pattern greps — I'd genuinely like to see what's in your list.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
