Keep the approved original image immutable, and treat every display size as a reproducible derivative. The deciding constraint is a design refresh: once the only surviving file is a small crop, a new layout cannot recover the pixels outside that crop. TL;DR: store one access-controlled source, record a versioned transformation recipe, and publish derived images only after the whole batch has passed validation. For a healthtech team serving patient-facing images, storage and cache cost matter, but a partial refresh can also put the wrong image dimensions on a live page.
Why keep the original image when reprocessing derivatives for new sizes?
An image sized for a narrow appointment card might be cropped around the subject. A later, wider card needs content that the first crop discarded. Upscaling that derivative makes a bigger file, not a wider view of the original scene. Keeping the source allows a fresh crop and lets the team test newer delivery formats against the same input. MDN documents how image formats differ in compression and browser support; format choice belongs at the delivery edge, not in the definition of the source. Consider a refresh that adds a wide preview to a care-team directory: the previous square thumbnail cannot tell a processor which part of the excluded frame should appear next to the clinician's face. The decision requires the retained source and a newly reviewed crop rule, not a bigger square thumbnail.
Pixels do not grow back.
The trade-off is deliberate. Retaining one controlled source uses storage, while retaining every experimental size forever multiplies storage and invalidation work. Neither figure should be guessed: inventory source bytes, derivative bytes, request counts, and cache misses separately. For patient-facing assets, first confirm what the image may contain and who may access it. A derivative inherits the source's access rules; a public cache must never become a shortcut around them. This also means reviewing cache keys and access policy together: a well-compressed derivative is no bargain if its URL exposes an image that should require authentication.
How do you make a refresh repeatable?
Define a source ID and source revision, then bind each output to a recipe revision, width, and format. The key must change when the transform changes. Never overwrite an existing key during a rollout. If two workers receive the same job, both should target the same deterministic result; a retry should not create a second public object or silently switch the image a page references.
Here is a small Go model for naming work. It intentionally does not pretend to be an encoder: decoding, access checks, and object-store publication are separate steps that need their own tested implementations.
package images
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
type Variant struct {
SourceID string
SourceRevision string
RecipeRevision string
Width int
Format string
}
func (v Variant) Key() (string, error) {
if v.SourceID == "" || v.SourceRevision == "" || v.RecipeRevision == "" || v.Width <= 0 {
return "", fmt.Errorf("incomplete image variant")
}
switch v.Format {
case "jpeg", "png", "webp", "avif":
default:
return "", fmt.Errorf("unsupported output format")
}
identity := fmt.Sprintf("%d:%s:%d:%s:%d:%s:%d:%d:%s", len(v.SourceID), v.SourceID, len(v.SourceRevision), v.SourceRevision, len(v.RecipeRevision), v.RecipeRevision, v.Width, len(v.Format), v.Format)
sum := sha256.Sum256([]byte(identity))
return hex.EncodeToString(sum[:]) + "." + v.Format, nil
}
In production, store the source ID as an opaque internal identifier, validate the requested recipe against an allowlist, and restrict the permitted dimensions. A source revision should change if the underlying approved image changes, even when its logical ID does not. The digest is an object identity, not an authorization token. That distinction matters.
Queue one job per required variant. A worker reads the authorized source, decodes it with resource limits, applies the pinned recipe, encodes the result, and writes to a private staging location. Before publication, check dimensions, decode the output again, and compare its content digest when an object with the same key already exists. If the bytes differ for one key, stop the rollout and investigate nondeterminism instead of letting the last worker win. Keep processing metadata and the approved source reference alongside the manifest; do not put patient identifiers in object names or queue payloads.
What should happen when a batch fails?
Leave the current manifest in place. That is the runbook rule.
A refreshed page should switch to the new set of image keys only when every required variant exists and has passed validation. Optional sizes may be deferred if the page has a documented fallback, but the fallback must obey the same access policy. Record the job's source revision, recipe revision, attempt count, and failure category so an operator can tell a transient storage error from a permanently invalid source.
Retries need boundaries. Back off transient failures and send exhausted jobs for inspection; don't loop forever on a corrupt upload. Track queue age and the oldest unpublished refresh, not only total worker throughput. A green worker dashboard can coexist with a refresh that has been waiting for one missing image all day. Alert on that gap, and rehearse recovery with a deliberately failed variant before a design rollout.
How do you verify and roll back without losing control of costs?
Before release, compare the old and new manifests against a representative set of pages. Check aspect ratios, file decoding, access behavior, and whether each requested width actually corresponds to a consumer. Sample cache behavior after deployment: unique keys should prevent old and new recipes from colliding, but a new set of URLs will initially be cold. Record delivered bytes and cache misses by recipe revision so the storage-versus-delivery trade-off is visible rather than assumed.
Roll back by restoring the previous manifest pointer, not by re-encoding files in the middle of an incident. Keep old derivatives through the rollback window and delete them only under an explicit retention policy after references have drained. Preserve the approved original according to the organization's retention and privacy requirements; an immutable processing input is not permission to retain regulated data indefinitely.
The operational test is simple: can the team build a newly sized, authorized image from a known source revision, verify the complete batch, and switch back without changing that source? If yes, a design refresh remains a controlled publication event instead of an emergency image migration.
Top comments (0)