For most of the last decade, adding video to an app was a delivery problem. You transcoded once, stored the result, streamed it, and made sure playback did not stutter on a mid-range Android phone. Generation belonged to somebody else: a studio, a stock library, a marketing team. That division of labor has collapsed. Today a user can type a sentence, wait ninety seconds, and expect a personalized clip that looks like a small crew produced it. The difficult part is no longer pixels. It is orchestrating a long-running, nondeterministic, comparatively expensive pipeline inside a product that trains users to expect instant feedback.
Three shifts made this possible. Hosted generation endpoints removed the need to own GPUs. Cheap, elastic inference made per-request rendering economically viable for consumer apps. And user expectations moved: if a fitness app can generate a custom workout summary, people quietly assume their language tutor, real estate tool, or recipe app can generate a video too. Once one competitor ships it, the feature stops being novel and starts being table stakes.
It helps to separate the problem into layers, because teams usually underestimate the wrong one.
A synchronous HTTP handler that waits two minutes for a render. Prompts edited directly in client code with no version history. No idempotency key, so a double-tap produces two identical, separately billed jobs. No cost ceiling, so one power user with a loop script becomes a finance conversation. And no provenance metadata, which becomes a problem the moment a platform asks where the clip came from.
A production-grade AI video feature is a pipeline, not a function call. Thinking in stages gives you clear boundaries for retries, observability, and ownership.
| Stage | Responsibility | Typical failure |
|---|---|---|
| Intent capture | Collect text, images, duration, aspect ratio, tone | Ambiguous input the model cannot resolve |
| Prompt compiler | Turn user intent into a versioned, structured request | Silent prompt drift between releases |
| Job queue | Persist, prioritize, and dispatch work | Duplicate jobs, lost jobs after deploys |
| Model routing | Pick the right model for quality, speed, or cost | Hardcoded model IDs that vanish |
| Render | Produce raw clips | Timeouts, partial renders, content refusals |
| Post-processing | Stitch, caption, normalize audio, add overlays | Out-of-sync captions, clipped audio |
| Storage and delivery | Host masters and derivatives, sign URLs | Broken links after retention windows |
| Playback | Stream adaptively in the client | Buffering on mobile networks |
Each arrow between stages is a place where a job can disappear. The single most valuable design decision is to persist state between every stage so any failure can be replayed without regenerating expensive work. Treat rendered clips as build artifacts: durable, addressable, and cheap to reuse.
Never render inside a request-response cycle. Push a job record into a queue and return a job ID immediately. Redis-backed queues (BullMQ, Celery, Sidekiq-style systems) work well for moderate volume; cloud queues such as SQS or Pub/Sub give you managed durability when throughput spikes. Add a separate queue for post-processing, because ffmpeg work and model inference have very different resource profiles and should scale independently.
Every generation request needs a client-supplied idempotency key. Store it with a unique constraint and return the existing job when the same key arrives twice. Pair that with exponential backoff and a retry budget of two or three attempts for transient provider errors, and no retries at all for content refusals.
def enqueue_generation(user_id, payload, idempotency_key):
existing = jobs.find_one(idempotency_key=idempotency_key)
if existing:
return existing
job = jobs.insert({
'user_id': user_id,
'prompt_spec': payload,
'idempotency_key': idempotency_key,
'status': 'queued',
'attempts': 0,
})
queue.publish({'job_id': job.id})
return job
Webhooks are cheaper and faster, but only if you verify signatures, tolerate out-of-order delivery, and reconcile with a periodic poll as a safety net. A hybrid works best: accept webhooks for speed, and run a reconciliation sweep every few minutes that asks the provider about anything still marked in progress beyond its expected duration.
Keep the master render, one web-optimized H.264 derivative, one vertical crop, and a poster frame. Move masters to cold storage after the retention period and delete derivatives you can regenerate. Sign every URL and set short expiries; long-lived public buckets are how generated content leaks.
Most quality complaints trace back to post-processing, not generation. Standardize a small ffmpeg step: normalize loudness, burn or sidecar captions, enforce a consistent frame rate, and apply your brand safe-area overlay. Run this as a deterministic transform so that the same input always produces the same output, which makes bug reports reproducible.
Generation takes time and will always take time. The user experience question is what people do during that window.
A spinner that spins for ninety seconds reads as broken. Replace it with named stages: understanding your request, composing shots, rendering, adding captions. Even an approximation is better than silence, and it sets the expectation that this is work rather than a stall.
Render a low-resolution preview or a single keyframe before the full clip. Users who can see the first frame will wait far longer than users staring at a skeleton loader. If your provider supports progressive output, stream partial results.
Treat pending renders like background exports. Let users navigate elsewhere, notify them on completion, and add a lightweight activity list where they can review, rename, or delete results. Cancellation should be a first-class action that also stops billing on the backend.
When a render fails, do not dump a provider error string into the UI. Map internal error codes into a small set of human explanations with a clear next step: retry, adjust the prompt, or shorten the duration. Log the raw error separately for engineering.
Auto-play is a trap. Captions, a muted-by-default player, and a visible play control make the feature usable for a much larger audience. On mobile, prefer vertical output, cap bitrate, and avoid loading a 40 MB master into a feed where three clips are visible at once.
Ad hoc prompt strings in client code are the fastest route to unpredictable quality and unmaintainable releases. Replace them with a compiled prompt specification.
Keep a template with variables for subject, setting, camera movement, lighting, duration, aspect ratio, and negative constraints. The client submits structured fields; the server compiles them into the final prompt and stores the compiled version, the template version, the model ID, and the seed.
{
"template": "product_demo_v4",
"subject": "ceramic coffee grinder on a walnut counter",
"shot": "slow push-in, shallow depth of field",
"duration_seconds": 6,
"aspect_ratio": "16:9",
"negative": ["text artifacts", "warped hands", "flicker"],
"seed": 44817
}
Storing the seed turns a lucky render into a repeatable one. When a user says the second take was worse than the first, a seed plus a template version lets you reproduce the good one instead of guessing.
Treat prompt templates like APIs. Version them, keep changelogs, and run a small regression set whenever you bump a version. When a provider deprecates a model, your routing layer should already know which template can move to its successor without breaking the look.
The moment a user generates more than one clip, they expect the same face, the same jacket, the same lighting. This is the hardest quality problem in the field, and it is solved with technique rather than luck.
For anything episodic, build a shot list as data before rendering anything. A structured list of shots with duration and continuity notes will save more time than any single prompt tweak.
You can usually get two of the three. Decide which one your product protects before you write code.
Generate a cheap, low-resolution draft for review, then render the final only after the user approves the concept. This single pattern often cuts spend dramatically because most rejected ideas never reach the expensive tier.
Not every request needs the best model. Route simple templates to fast, inexpensive models and reserve premium rendering for hero content, paid tiers, or explicit user choice. Keep routing rules in configuration, not in code, so you can rebalance without a deploy.
Reuse assets aggressively: the same intro animation, the same voice-over, the same lower-third overlay. Cache rendered segments by a hash of their inputs so a re-render only regenerates what changed.
Enforce per-user and per-organization limits on concurrent renders, daily output minutes, and maximum clip length. Instrument cost per successful generation and per active user, and alert when either drifts. A feature is not production-ready until finance can predict what a thousand extra users will cost.
Generated video touches consent, likeness, and copyright in ways text generation rarely does. Guardrails are part of the product, not a compliance afterthought.
Regional rules vary. If your audience is global, keep policy configuration separate from rendering logic so you can adjust per market without rewriting the pipeline.
Exact visual assertions are brittle. Instead, test the contracts: does the job reach a terminal state, does the derivative exist, is the duration within tolerance, are captions present, is cost recorded. Add a small golden set of twenty to thirty representative prompts and run them on every release, comparing outputs with perceptual hashes to detect provider drift.
Emit structured events for queue wait, render time, post-processing time, retry count, failure reason, and cost. Trace a single job end to end so a support ticket becomes a five-minute lookup instead of an archaeology project. Build one dashboard that answers: how many jobs succeeded, how long did users wait, and what did it cost.
Shipping without a cost ceiling. Storing prompts only in client builds. Treating all failures as retryable. Forgetting that deletion requests must reach cold storage too. And assuming the model you integrated with today will behave identically next quarter.
Users tolerate roughly one to three minutes for something they explicitly asked for, provided they can see progress and leave the screen. Anything beyond that needs a notification-based flow rather than a loading screen. Draft previews are the most effective way to make the wait feel shorter.
Usually not at the start. Hosted models let you validate demand without capital expenditure. Self-hosting becomes worth considering when volume is stable, latency requirements are strict, or you need fine-grained control over model variants and data handling.
Combine three things: a canonical reference image, a fixed seed within a scene, and shorter clips stitched together rather than one long generation. Add an automated continuity check so drift is caught before a user notices it.
The rejected renders. Most teams budget for successful outputs and forget that iteration multiplies spend. Draft-tier rendering, caching by input hash, and per-user ceilings address this far more effectively than optimizing individual model prices.
Only if it is fast and forgiving. A first-generation experience that fails, times out, or produces something embarrassing is worse than no feature at all. Consider demonstrating pre-rendered examples first, then inviting users to generate their own.
Start with one to simplify operations, then add routing when you have evidence: a specific use case where quality, speed, or cost clearly diverges. Keep the routing layer abstract so adding or removing a provider is a configuration change, not a refactor.
The teams that ship AI video successfully are rarely the ones with the most impressive demo. They are the ones who treated rendering as a pipeline with clear stages, honest costs, and graceful failure, then spent their creative energy on the part users actually remember.

Alexander
Vidu Q4 alternatives compared: Kling 4.0, Seedance 2.5, Wan 3.0, and Runway Gen-4 — 4K support, input modes, durations, audio, and what each costs in credits on Domer.
The top 4K AI image generators on Domer compared: Flux 3 Klein, Nano Banana 2.1, GPT Image 2.5, GPT Image 2, and Nano Banana Pro — 4K credit costs, reference limits, and speed.
Nano Banana 2.1 vs GPT Image 2.5 Flare compared: credit costs per resolution, aspect ratios, reference images, text rendering, and which tasks fit each model.