I was going through the image models that stop working tomorrow when one line made me pause.
The model that stops does not have -preview in its name. It stops anyway.
Meanwhile, the model that currently does the deepest reasoning for me does have -preview in its name. And I have been running it in a production pipeline.
I had been treating the suffix as a safety marker. It turns out it was never protecting anything.
GA and preview differ in warning, not in polish
If you read GA and preview as "finished" versus "unfinished," you will pick the wrong model.
In day-to-day work, only one distinction has mattered to me:
Whether the model's shutdown shows up in the deprecation table with a date attached.
GA models get retired too. imagen-4.0-generate-001, which stops tomorrow on August 17, is not a preview model by name. It is a GA model being retired on a published date.
Preview models can quietly get replaced before they ever reach that table. When the image preview models stopped back in June, I found out after a morning batch had already failed.
So the difference is not whether a model disappears. It is whether you are given time to prepare before it does.
Once I framed it that way, the question I ask when picking a model changed. Instead of "which one is smartest," I now start with "when would I be told this one is going away."
Right now, capability order and GA order do not match
Here is the August 2026 lineup sorted by release stage rather than performance. It reads a little uncomfortably.
| Model | Stage | Typical use |
|---|---|---|
| Gemini 3.1 Pro | preview | Top-tier reasoning for work that needs depth |
| Gemini 3.7 Flash | GA (August 13) | Coding, web development, agentic workflows |
| Gemini 3.6 Flash | GA | General purpose with better token efficiency, priced below 3.5 Flash |
| Gemini 3.5 Flash-Lite | GA | Low latency and low cost, for high-volume subagents |
| Gemini 3.1 Flash TTS | preview | Speech synthesis with controllable delivery |
The list does not descend from strongest to weakest. The most capable tier is in preview, and the tier that runs your daily workload is GA.
For an indie developer, that narrows the decision to one thing: the more thinking a job needs, the more likely you are handing it to a model that can change without notice.
In my own setup, anything that runs every day — cleaning up article drafts, drafting the store descriptions I ship to the App Store and Google Play — sits on GA Flash models. Work that runs only a few times a month, like talking through a design problem, goes to the preview Pro model.
The reason is recoverability, not quality. If something I run a few times a month breaks, I can fix it by hand that day. Something that fires at five in the morning does not give me that option.
Count your preview dependencies before you decide anything
Deciding to "avoid preview" is useless until you know how many places already use it.
So I keep a small script that collects every spot where a model ID appears and sorts them into three buckets. There is nothing clever in it. It picks up strings and splits them by the shape of the name.
#!/usr/bin/env python3
"""check_preview_models.py
Collect every place in a codebase that names a Gemini model and
bucket them into GA / preview / alias.
Usage: python3 check_preview_models.py <directory>
"""
import re
import sys
from pathlib import Path
MODEL_RE = re.compile(r"[\"']((?:gemini|imagen|gemma)[a-z0-9._-]*)[\"']")
TEXT_SUFFIX = {".py", ".ts", ".tsx", ".js", ".mjs", ".json", ".yaml", ".yml", ".toml"}
def classify(model_id: str) -> str:
if model_id.endswith("-latest"):
return "alias" # target swaps silently
if "-preview" in model_id or "-exp" in model_id:
return "preview" # warning window may be short
return "ga?" # looks GA, but see the trap below
def scan(root: Path):
found = []
for path in sorted(root.rglob("*")):
if not path.is_file() or path.suffix not in TEXT_SUFFIX:
continue
lines = path.read_text(encoding="utf-8", errors="ignore").splitlines()
for lineno, line in enumerate(lines, 1):
for m in MODEL_RE.finditer(line):
found.append((classify(m.group(1)), m.group(1), f"{path}:{lineno}"))
return found
def main() -> int:
root = Path(sys.argv[1] if len(sys.argv) > 1 else ".")
found = scan(root)
if not found:
print("No model IDs found. Check your environment variables too.")
return 0
for bucket, label in (("preview", "preview (short warning window)"),
("alias", "alias (target swaps silently)"),
("ga?", "looks GA (verify in the deprecation table)")):
rows = [r for r in found if r[0] == bucket]
print(f"\n[{label}] {len(rows)} occurrences")
for _, model_id, where in rows:
print(f" {model_id:38s} {where}")
risky = sum(1 for r in found if r[0] in ("preview", "alias"))
print(f"\n{len(found)} total, {risky} to check first.")
return 1 if risky else 0
if __name__ == "__main__":
sys.exit(main())The reason it walks .json and .yaml files is that model names hide in configuration far more often than in code. Grepping only your Python files feels thorough and still misses the one line that matters.
Aliases get their own bucket on purpose. A name like gemini-flash-latest will point at a different model one day without any shutdown notice arriving. That is a different kind of uncertainty from preview, and mixing the two leads to the wrong fix.
The line the script labels "GA" is the one that breaks tomorrow
I ran it against a small sample project, and the output is the part of this article I most want you to see.
[preview (short warning window)] 2 occurrences
gemini-3.1-pro-preview sample/app/draft.py:1
gemini-3.1-flash-tts-preview sample/app/tts.ts:1
[alias (target swaps silently)] 1 occurrences
gemini-flash-latest sample/conf/models.json:1
[looks GA (verify in the deprecation table)] 3 occurrences
gemini-3.6-flash sample/app/draft.py:2
gemini-3.5-flash-lite sample/app/tts.ts:2
imagen-4.0-generate-001 sample/conf/models.json:1
6 total, 3 to check first.Sitting in the bottom bucket, labeled as probably GA, is imagen-4.0-generate-001. That is the model that stops tomorrow.
The script is not wrong. By name alone, it genuinely is not a preview model. The shape of a model name tells you nothing about whether it is about to be retired.
So I treat this sorting as a tool for deciding what order to check things in, not as a danger detector. Preview entries and aliases get verified first; the GA column gets checked line by line against the deprecation table.
The separate question of a deprecated SDK method versus a retired model is covered in generate_images Survives Until 2027. Your Image Generation Still Stops on August 17. This sorting is the step that keeps those two from blurring together.
Write down where preview is allowed, in your own words
Avoiding preview entirely is not realistic, because the model that thinks hardest lives there.
I draw the line with three rules.
- A person reviews the output before anything continues — preview is fine, since a failure is visible immediately.
- It runs unattended on a schedule — GA only, because I would not notice until the next morning.
- The output is stored and has to be reproducible later — no preview, because the same output may never be producible again.
The third one came out of June. I tried to regenerate a wallpaper app background that a preview model had produced, under the same conditions, and could not. If generated artifacts become assets, the disappearance of the model that made them belongs in the plan.
Writing these rules down matters more than it sounds. While they live only in your head, they quietly bend on the day a deadline is close.
Choosing among GA models, incidentally, tends to be settled by the invoice rather than by capability. How to absorb the pricing of 3.7 Flash, which reached general availability on August 13, is covered in Moving to the Batch Tier Cancels Out the Gemini 3.7 Flash Price Increase Exactly.
One thing to do before tomorrow
Run the script once against your own project and keep a note of just the preview and alias lines it prints.
Nothing needs fixing yet. Knowing the count is what shortens the scramble on the day the next shutdown notice arrives.
I went into that June morning without knowing my own number. These days the script runs on the first of every month, so that particular surprise only had to happen once. Thank you for reading.