DEV Community

DarkveilCorvyn26
DarkveilCorvyn26

Posted on

Consistent Listing Photo Sizes: Named Aspect Ratios and Upload-Time Moderation

Short answer: for a logistics marketplace, normalize dimensions and moderation at upload, then keep a deliberately boring on-demand path for legacy and reprocessed images. Store the requested named aspect ratio as data, not as a crop hidden in a URL. That gives reviewers one predictable queue and gives clients stable contracts when a carrier uploads a photo from a battered phone at 03:00.

The incident signal is a shape mismatch. The page that fires is rarely “the image service is down.” It is usually a listing whose gallery has three different shapes: a square thumbnail, a tall phone original, and a landscape proof-of-delivery shot. CSS can hide the mismatch until a slow connection exposes a 12 MB upload, or until a moderation model sees a padded canvas instead of the pixels it was trained on. A 413 response from the edge is a capacity symptom, but it is also a product decision that arrived too late.

No guesswork.

In a real queue, the failure chain is easy to miss because every individual component reports something plausible. The upload gateway accepts the bytes, the decoder records a portrait orientation from EXIF, the cropper produces a square, and the moderation worker approves it. Later, a listing renderer asks for landscape; a cache miss starts another decode, that worker races a retry, and the seller sees a blank tile while the original remains perfectly healthy. The alert says “cache fill latency,” which is technically true and operationally useless. I want the event log to carry the media ID, idempotency key, requested ratio, policy version, source checksum, and publication state so the responder can answer one question quickly: which page fired, and is the listing allowed to show anything yet?

For each listing, accept the original once, record its media type and dimensions, and derive a bounded set of renditions. A named ratio such as square, portrait, or landscape should map to a versioned policy: target width, target height, crop anchor, and maximum bytes. Do not let a client submit arbitrary ?w=...&h=... values; that turns cache keys into an unbounded attack surface. The named value is part of the marketplace contract, so changing its meaning deserves a migration and a rollback pointer, not a silent tweak in a frontend release.

How should a marketplace API handle named aspect ratios and listing photos?

Make the contract explicit and idempotent. The upload endpoint can return 202 Accepted with a media ID, while a status read reports pending, approved, or rejected and includes the policy version used. A repeated request with the same idempotency key must not create a second moderation job. The listing should reference a rendition only after the moderation decision is approved.

Here is the small part I want to be boring: a policy table and a worker that refuses unknown names. The image decoder, resizer, and scanner can be swapped behind these interfaces; the API contract remains stable.

package media

import "fmt"

type RatioPolicy struct {
    Width, Height int
    MaxBytes     int64
}

var policies = map[string]RatioPolicy{
    "square":    {Width: 1200, Height: 1200, MaxBytes: 500_000},
    "portrait":  {Width: 1200, Height: 1600, MaxBytes: 700_000},
    "landscape": {Width: 1600, Height: 1200, MaxBytes: 700_000},
}

func policyFor(name string) (RatioPolicy, error) {
    p, ok := policies[name]
    if !ok {
        return RatioPolicy{}, fmt.Errorf("unknown aspect ratio %q", name)
    }
    return p, nil
}

func ModerateUpload(ratio string, bytes int64) error {
    p, err := policyFor(ratio)
    if err != nil {
        return err
    }
    if bytes > p.MaxBytes {
        return fmt.Errorf("image exceeds %d byte policy", p.MaxBytes)
    }
    return nil // decode, orient, resize, then scan before publication
}
Enter fullscreen mode Exit fullscreen mode

The limits are examples of a policy shape, not universal numbers. Your mileage may vary with camera sources and carrier networks. Keep the policy version beside every rendition so a later resize does not silently change the evidence a reviewer approved.

Keep it boring.

Upload-time versus on-demand processing

Upload-time processing wins when “not yet moderated” must mean “not visible.” It bounds the work per listing, makes rejection immediate, and prevents a burst of gallery views from starting thousands of decodes. The trade-off is queue latency and a need for durable retries; a busy depot can produce a sharp burst even when traffic charts look calm.

On-demand processing is useful for a new ratio, a changed byte budget, or old inventory that was ingested before the policy existed. It is a poor publication gate: two viewers may request the same missing rendition simultaneously, and a transient worker delay becomes a user-facing blank. Use a single-flight key of (media_id, policy_version, ratio) and persist the result. Stick with on-demand only when stale or missing images are an acceptable state and the listing can remain hidden until the job completes.

The practical split is simple: moderate and create the default rendition on upload; enqueue optional ratios asynchronously; run backfills through a rate-limited queue. Never couple moderation approval to a best-effort cache fill.

Verification, rollback, and the 3 a.m. question

Test the contract with fixtures that include EXIF orientation, transparent PNGs, animated formats, and files whose declared MIME type disagrees with their signature. MDN's format guidance is a useful baseline, but your decoder and scanner must define the accepted set. Record duration, queue age, rejection reason, output bytes, and policy version. Alert on queue age and publication blocks, not merely worker CPU; those are the signals that tell an incident responder which page actually fired.

Before changing a policy, run it in shadow mode against a sample of existing originals and compare dimensions, byte sizes, and moderation outcomes. Rollback should be a pointer change to the previous policy version, leaving already-approved renditions readable. If a new worker emits bad output, stop new jobs, keep serving the last approved rendition, and replay only items whose checksum and policy version are known. That is safer than deleting derivatives and discovering during a shift change that the source was never retained.

The catch is storage and operational complexity: retaining originals plus several renditions costs space, and strict upload gating can slow a legitimate listing. This approach is not suitable when sellers need an instant draft preview with no moderation; in that case, expose a private, clearly unapproved preview and keep publication behind the same gate.

References

Top comments (0)