Fine-tuning is useful when the team already knows what behavior it wants.
It is a mess when the team is hoping training will discover the product requirement for them.
That distinction sounds small, but it changes the whole project. A fine-tune can make a narrow task cheaper, faster, more consistent, or less dependent on a long prompt. It can help a model emit a stable schema, classify a domain-specific label set, follow a house style, extract entities in a particular format, or handle a recurring task that the base model almost understands.
What it cannot do is rescue a vague objective. If the instruction is “make the assistant better,” the model will still learn something. The problem is that nobody can say whether it learned the intended thing.
freeze the baseline first
The first step is not training. It is freezing the baseline.
Before touching a dataset, I want a record of the current system: model, prompt, retrieval path, tool constraints if any, eval set, grading method, and known failure slices. Without that, the fine-tune has no honest comparison. It can feel better because the examples are friendlier, the prompt changed, the grader got looser, or the eval leaked into training.
A baseline record can be small:
{
"task": "classify customer escalation risk",
"baseline_model": "frontier-base-2026-05",
"prompt_version": "escalation-risk-v3",
"retrieval": "none",
"eval_set": "escalation-holdout-042",
"primary_metric": "macro_f1",
"must_not_regress": ["security", "ambiguous_policy", "vip_customer"],
"latency_budget_ms": 150
}
The important part is that the baseline is reproducible. If the tuned checkpoint improves the target slice by 4 points but the prompt also changed, the result is muddy. If the baseline eval was never saved, the comparison becomes a memory exercise.
Fine-tuning already has enough ways to fool you. Do not start by moving the measuring stick.
name the behavioral delta
“Better” is not a training objective.
A useful fine-tuning objective names the behavioral delta:
- reduce false negatives for security escalations
- normalize messy vendor names into a fixed entity schema
- emit shorter support summaries without dropping required fields
- classify internal documents into a stable taxonomy
- produce JSON that matches a schema without repeated repair passes
- follow a specific editing style for one kind of document
Each of those goals implies a different dataset. The security-escalation model needs hard negatives and ambiguous policy cases. The entity model needs label definitions and disagreement review. The JSON-formatting model needs examples of valid output and ugly input. The style model needs before-and-after pairs from the actual style, not generic “write professionally” examples.
The delta also tells you what should not change. If the fine-tune improves escalation recall but floods billing with false positives, maybe it failed. If it writes in the right style but invents missing facts more often, it failed. If it outputs perfect JSON only on examples shaped like the training set, it failed outside the demo.
Fine-tuning should change a defined behavior, not the whole personality of the system.
the dataset is the product work
The dataset is where the real arguments happen.
Labels need definitions. Edge cases need decisions. Duplicate examples need removal. Synthetic examples need inspection. Real examples need privacy handling. Class imbalance needs a plan. Ambiguous examples need either a label, an unknown class, or exclusion with a note explaining why.
If the dataset is sloppy, the model learns the slop.
For a classifier, I would want a dataset summary before training:
class train validation holdout
security_high 420 80 120
security_medium 610 100 140
billing_escalation 980 160 220
product_bug 740 120 180
unknown 300 60 100
The counts are only the start. I also want to know where the examples came from. Production tickets? Synthetic generation? Human-authored fixtures? Historical labels? If synthetic data exists, which prompt created it? Were synthetic examples kept out of the adversarial holdout? Did labelers disagree? Were labels reviewed by someone who understands the product?
Label disagreement is not a nuisance to hide. It is a signal. If two reviewers disagree on whether a ticket is security-high or security-medium, the model may learn whichever label happened to win the spreadsheet. The better move might be to clarify the label policy or add an ambiguity class.
The training run cannot fix a taxonomy that the team has not defined.
keep the holdout away from the generator
Synthetic data can be useful. It can fill rare slices, produce adversarial phrasing, and cover cases that production has not produced yet. It can also contaminate the eval in subtle ways.
If the same teacher model, prompt style, or generation recipe creates the training data and the holdout, the tuned model can look stronger than it is. It learns the generator’s accent. Then real users arrive with different wording, broken grammar, pasted logs, mixed intents, and context the synthetic prompt never imagined.
I would separate the sets by source:
training:
- reviewed production examples
- synthetic rare-class examples
- cleaned historical labels
validation:
- production examples from a separate time window
- reviewed synthetic examples from a different prompt recipe
adversarial holdout:
- real failures
- label-disagreement cases
- hand-authored edge cases
- examples from documents the generator never saw
The holdout should make the team nervous. If every case looks like the training set, it is a rehearsal, not an exam.
checkpoints deserve suspicion
The final checkpoint is not automatically the best checkpoint.
Fine-tuning can overfit fast, especially on narrow tasks and small datasets. A later checkpoint may score better on training loss while getting worse on adversarial cases. The model may learn to imitate label phrasing, output length, or formatting quirks instead of the task itself.
I want checkpoint comparison by slice:
slice baseline ckpt_1 ckpt_2 ckpt_3
security_high_recall 0.82 0.86 0.90 0.91
billing_precision 0.88 0.87 0.84 0.79
ambiguous_policy_f1 0.63 0.65 0.61 0.55
out_of_domain_f1 0.74 0.73 0.68 0.60
schema_validity 0.91 0.96 0.98 0.98
Checkpoint 3 looks great if the only story is security recall and schema validity. It looks worse if the product cannot afford billing false positives or ambiguous-policy regressions. Checkpoint 1 may be the better product choice, even though it is less impressive on the headline metric.
This is also where a prompted baseline matters. If a careful prompt and a schema validator get you most of the benefit with no training job, that might be the better system. Fine-tuning has to beat the strong boring option.
regressions outside the task still count
A fine-tune narrows behavior. That is the point. It can also damage behavior the team assumed would stay intact.
A model tuned for terse summaries might stop preserving uncertainty. A model tuned for JSON might become worse at explaining why a field is missing. A model tuned for a support taxonomy might overroute unusual but legitimate requests into the nearest known label. A style fine-tune might make the output sound right while weakening factual caution.
Those regressions count even when the target metric improves.
I would always keep some out-of-domain and adjacent-domain checks. They do not need to be huge. They need to catch the obvious “we trained the model into a corner” failures.
For example:
target task:
classify escalation risk
adjacent checks:
normal billing request should remain low risk
vague security mention should ask for clarification
pasted stack trace should not become a policy escalation
unsupported refund claim should stay unsupported
multilingual short message should not default to high risk
The fine-tune earns trust by improving the target behavior without breaking the neighbors.
fine-tuning can simplify the product
The best reason to fine-tune is usually simplification.
Maybe the prompt shrinks from 2,000 tokens to 200. Maybe the model stops needing a repair loop for schema output. Maybe latency drops because a smaller tuned model can replace a larger prompted one. Maybe the system stops carrying twenty few-shot examples in every request. Maybe the output becomes consistent enough that downstream code gets simpler.
That is a real architectural win.
The bad reason is impatience. The team does not want to define labels, so it trains. The prompt is a mess, so it trains. The eval is weak, so it trains. The product behavior is disputed, so it trains. That route usually produces a checkpoint that encodes confusion in a harder-to-debug form.
Before fine-tuning, I would ask:
- Is the task stable enough to train against?
- Do we have labels we trust?
- Does a strong prompted baseline already solve it?
- What behavior should change?
- What behavior must stay unchanged?
- What will own rollback and versioning?
- How will we know the checkpoint has drifted?
If those answers are vague, training is early.
the checkpoint becomes infrastructure
Once the checkpoint ships, it is part of the system.
It needs a version. It needs a dataset lineage. It needs an eval record. It needs a rollback plan. It needs ownership for future data. It needs monitoring for changes in input distribution. It needs a way to compare new checkpoints against the current one.
That sounds heavy, but it is the cost of making model behavior a dependency.
A small release note might say:
checkpoint: escalation-risk-ft-2026-05-19
base: frontier-base-2026-05
dataset: escalation-train-017
selected checkpoint: epoch 2
reason: best tradeoff between security recall and billing precision
known weakness: ambiguous policy language remains below target
rollback: route to prompted baseline risk-v3
next data work: review 100 ambiguous-policy cases
That note is more useful than “fine-tuned the model.” It tells the next person what decision was made and where to look when it starts failing.
where i land
Fine-tuning is not a shortcut around product thinking. It is a way to compress product thinking into weights after the team has done the harder work in examples, labels, evals, and baselines.
When the task is stable and the dataset is good, it can be excellent. It can make a system faster, cheaper, more consistent, and easier to operate. When the task is vague, it mostly hides the vagueness.
The model will learn something. Make sure it is the thing you meant to teach.
Related posts

About Jeremy London
Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.