DEV Community

bestbee
bestbee

Posted on

Your Free AI Capacity Isn't the Bottleneck. Plot Branch Age From Git First

A platform lead pinged me last week with a screenshot: a free AI tier offer, a token allowance in the millions, and a question I've heard too many times.

"Can we take this and ship faster?"

I asked for one number instead: how long does a branch live before it merges? He didn't know. Neither did anyone on his team. That's the problem. You can't decide whether free capacity helps you if you've never measured where the delay actually sits.

So let's measure it. From your own git history. Before you sign anything.

The number that decides it isn't tokens

Token allowances are a supply-side number. Your throughput is a demand-side and review-side number. They're not the same axis, and confusing them is how teams end up with a generous free lane and the same release cadence.

What actually gates your output? Usually one of these:

  1. Model wait — you're queued, throttled, or rationing requests.
  2. Review latency — changes sit waiting for a human to read them.
  3. Rework — reviewed changes go back for a second pass, or get reverted.
  4. Nothing — you're already shipping as fast as your risk tolerance allows.

Free capacity only helps in case 1. In cases 2 and 3 it makes the queue longer, because more generated diffs arrive at a reviewer who was already the constraint.

So the first question isn't "how many tokens?" It's "which of those four is my binding constraint?" And git can tell you.

Measure branch age in ~30 lines of Python

Branch age is a proxy: the wall-clock gap between the first commit on a branch and the merge commit that closed it. It's not review latency exactly, but it's free, retroactive, and hard to fudge in a status meeting.

First, sanity-check that your history has merge commits at all:

git rev-list --merges --first-parent -n 5 HEAD --pretty=format:'%h %cI %s'
Enter fullscreen mode Exit fullscreen mode

If that prints nothing, you squash-merge or commit straight to trunk, and this method needs the fallback at the bottom of this article.

Now the script. Save as branch_age.py, run inside the repo:

import subprocess, statistics, json, datetime as dt

def git(*args):
    return subprocess.run(["git", *args], capture_output=True, text=True, check=True).stdout

def ctime(sha):
    return int(git("show", "-s", "--format=%ct", sha).strip())

def collect(limit=200):
    merges = git("rev-list", "--merges", "--first-parent",
                 f"-n{limit}", "HEAD").split()
    rows = []
    for m in merges:
        parents = git("rev-list", "--parents", "-n1", m).split()[1:]
        if len(parents) < 2:
            continue
        base, branch = parents[0], parents[1]
        commits = git("rev-list", "--reverse", f"{base}..{branch}").split()
        if not commits:
            continue
        hours = (ctime(m) - ctime(commits[0])) / 3600
        files = {f for f in git("show", "--name-only", "--format=", m).splitlines() if f.strip()}
        rows.append({
            "merge": m[:7],
            "merged": dt.datetime.utcfromtimestamp(ctime(m)).date().isoformat(),
            "branch_hours": round(hours, 1),
            "commits": len(commits),
            "files": len(files),
        })
    return rows

if __name__ == "__main__":
    rows = collect()
    hours = [r["branch_hours"] for r in rows]
    files = [r["files"] for r in rows]
    summary = {
        "merges": len(rows),
        "branch_hours_p50": round(statistics.median(hours), 1),
        "branch_hours_p90": round(sorted(hours)[int(len(hours) * 0.9) - 1], 1),
        "files_p50": round(statistics.median(files), 1),
        "files_p90": round(sorted(files)[int(len(files) * 0.9) - 1], 1),
    }
    print(json.dumps(summary, indent=2))
    for r in rows[:10]:
        print(r)
Enter fullscreen mode Exit fullscreen mode

Run it: python branch_age.py. Two outputs matter — the percentiles, and the shape of the top rows.

What the output looks like

I ran this against a fixture repo I built specifically to test the script: 28 merges across nine weeks, synthetic but shaped like a normal product team. Illustrative output, not a production measurement:

Metric p50 p90
Branch age (hours) 6.5 31.0
Files changed per merge 4 22
Commits per branch 2 7

Read that p90 again. Thirty-one hours. If a generated diff lands in a reviewer's queue that's already nine hours deep, cutting model wait from four minutes to forty seconds moves that number by roughly nothing. You'd have bought speed in the one place that wasn't slow.

The four-state decision table

Fill this in with your own percentiles. Don't borrow mine.

Your reading Binding constraint Free capacity helps? Do this instead
p50 branch age < 8h, p90 < 24h Review, lightly Marginal Pilot free capacity on one low-risk repo; keep review SLA
p50 < 8h, p90 > 48h Long-tail risk review No Split large merges; cap files per change before adding capacity
Model wait share > 25% of branch age Model access Yes Take the free lane; re-measure in 30 days
Rework rate climbing, files p90 > 20 Rework No — it worsens Add a diff-size gate first; capacity will amplify the problem

One formula makes the model-wait column concrete:

model_wait_share = (minutes waiting on model) / (branch_hours * 60)
Enter fullscreen mode Exit fullscreen mode

If that's under 10%, free tokens are a rounding error in your cycle time. State that plainly in the decision doc, because "free" is very hard to argue against in a room that hasn't done the arithmetic.

Treat this table as a conversation tool, not objective truth. It's there to force the argument onto a measured axis, not to end it.

Where the free option actually fits

Free hosted capacity is a strong fit for exactly the state it's designed for: your team is bottle-necked on access, not on reading. Spiky workloads, evaluation runs, prototypes, and internal tooling all qualify — the blast radius is small and nobody queues behind a reviewer.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

MonkeyCode is relevant here because it offers free model access and a free server option, which makes the low-commitment version of this experiment possible: point the pilot at one repo, keep the script running, and find out whether access was ever your constraint. I haven't benchmarked either offer, and the token allowance and server capacity are operator-supplied figures rather than numbers I measured — so verify the current terms yourself before you put them in a plan.

What I'd actually do with it is narrow. Pick the repo with the lowest p90 branch age. Run the pilot there for two weeks. Compare p90 branch age before and after. If it doesn't move, free capacity was never the lever, and you've learned that for the price of a calendar entry.

The free server matters for a different reason: a persistent runner lets the pilot survive laptop lids and CI timeouts, which is what usually kills a two-week measurement halfway through. Again — confirm what's on offer rather than trusting my description of it.

Sensitivity: two variables flip the call

The decision is fragile in useful ways. Run these two toggles before you commit.

  • Halve model wait. If docs still get merged on the same day, capacity isn't the constraint. Stop.
  • Double files per merge. If review time scales with diff size in your history, then more generated diffs increase cycle time. That's an argument to cap diff size first, not to buy speed.

Break-even framing: free capacity pays for itself in cycle time only when model_wait_share × review_parallelism > 1. If a reviewer can only read one diff at a time, extra supply just lengthens the queue.

Hard gates, owner, expiry

Write these down before the pilot starts, or the pilot becomes permanent by inertia.

  1. Gate: pilot only on repos where p90 branch age is under 48 hours today.
  2. Gate: no pilot on the repo that owns your payments or auth path.
  3. Owner: one named engineer, not a committee, responsible for re-running the script.
  4. Expiry: the adoption decision expires in 60 days. Re-measure or drop it.
  5. Exit: if p90 branch age improves by less than 15%, revert to the previous lane and record why.

That expiry line is the one people skip. A free tier doesn't need to win an argument to become load-bearing — it just needs nobody to schedule the review.

Who should not use this method

Be honest about the limits.

  • Squash-only or trunk-based repos. No merge commits means no branch age. Fallback: use git log --first-parent --format='%ct %h' and measure the gap between consecutive commits touching the same directory. Coarser, still directionally useful.
  • Teams under three engineers. A single reviewer's calendar dominates everything; the percentiles will just describe one person's week.
  • Rebase-heavy workflows. Commit timestamps get rewritten and branch age under-reports. Note that in your write-up rather than pretending the number is clean.
  • Anyone who needs a cost answer, not a throughput answer. This method tells you where the delay is. It won't tell you the dollar cost per merged change — that's a different measurement with different data.

Git history is a lagging, partially fictional record written by people under deadline. It's still better than the alternative, which is deciding on vibes and calling it strategy.

So: what's your p90 branch age? If you don't know it, you don't yet have a reason to accept or reject any free capacity offer — including this one.

If you want a low-stakes place to run the first measurement, a free server and free model access are a reasonable place to start; just run the script before you run the pilot.

Top comments (0)