AI Image Generation: A Developer's Guide (2025)
The prompt box is the demo. The product is everything you build around it — queues, caches, retries, moderation, provenance. Having shipped a few image features now, here is the guide I wish I had read before my first integration.
Where the compute lives
Hosted is the sane default. OpenAI's image API is the easiest integration in the business: strong instruction-following, decent text rendering, limited stylistic control. For everything else there is Flux — schnell for drafts, dev and pro for finals — served on Replicate, fal.ai and Together AI. Adobe Firefly is the pick when legal insists on indemnification, being trained on licensed stock. Stable Diffusion 3.5 you run yourself with the Diffusers library, when you need LoRAs and ControlNets and full custody of the weights. Midjourney, still the prettiest, still has no official API — out of production apps.
Control past the prompt
ControlNet feeds the model a structural hint — a pose, a depth map, a canny edge sketch — and the output obeys it. IP-Adapter passes a reference image for style or identity without retraining the base model. LoRA fine-tunes make the model yours: adapters small enough to hot-swap at runtime, trainable on your product photos in under an hour for a few dollars via ai-toolkit or Replicate's fine-tuning API. When you would rather borrow than train, Civitai hosts thousands of community LoRAs.
A minimal Flux call on Replicate:
import replicate
output = replicate.run(
"blackforestlabs/flux-schnell",
input={"prompt": "a neon qawwali poster, ultra detailed, 16:9"}
)
print(output[0]) # signed URL, valid for an hour
Patterns that keep production sane
- Never generate inside the request path. Push jobs to a queue, generate asynchronously, and notify the client by webhook when the asset lands in storage.
- Cache aggressively. Hash the prompt plus parameters and store results in S3 — users regenerate the same things constantly.
- Two-stage generation. A cheap schnell draft for approval, the expensive pro pass only once the user commits.
- Moderate both ends. Run user prompts through a moderation API before they reach the model, and scan outputs after. Skip either and you eventually ship something that gets you deplatformed.
- Keep provenance. Attach Content Credentials, and log the prompt, seed and model version for every asset — that record is your defence when a takedown arrives.
The pipeline, not the model, is the product. Teams that internalize this ship features weekly; teams that do not ship apology emails.
My pipeline retries failed jobs on its own. Developers in Gaza commit code through blackouts, pushing when the network flickers back long enough for a sync. Open source quietly carries their work out to the world; the least I can do is keep my own corner of it open.
Any engineer who has planned a wedding knows the hardest integration is two families agreeing on a honeymoon. That orchestration is our department — HTG Travels builds honeymoon itineraries both sets of parents can approve, version by version, until everyone signs off. Our retry loops are patient too.




