DEV Community

Cover image for The Filename Is a Contract: How Naming Rules Quietly Decide Your Upload CSV**

The Filename Is a Contract: How Naming Rules Quietly Decide Your Upload CSV**

The Filename Is a Contract: How Naming Rules Quietly Decide Your Upload CSV

Last March I lost a full afternoon to a filename. I had 40 assets ready for Adobe Stock, ran my metadata build, and submitted. The review came back with a rejection on one file: the title had been auto-derived from the filename, and the filename contained an underscore and a product code fragment. The title read like a serial number, so the reviewer read it like spam. One file, but it made the point: I was letting the filename write my title, and the filename was never written for humans.

That failure is why make_metadata.py in my pipeline treats the filename as a contract. Here is what I learned about how naming rules decide whether your CSV survives review.

The header is checked first, and it is unforgiving

Adobe's upload CSV is 5 columns. Vecteezy is 4. Dreamstime is 15. If the header row is off by one column, or a column is renamed, the platform rejects the file before a human ever sees your work. There is no partial credit.

So the first naming rule in my pipeline has nothing to do with the assets themselves: the CSV filename and its header are generated, never typed. make_metadata.py writes the exact header each platform demands and nothing else. I ran it three times for the same folder:

python make_metadata.py ./my-assets --platform adobe      --out metadata.csv   --ext .jpg
python make_metadata.py ./my-assets --platform vecteezy   --out vecteezy.csv   --ext .jpg
python make_metadata.py ./my-assets --platform dreamstime --out dreamstime.csv --ext .jpg
Enter fullscreen mode Exit fullscreen mode

Same folder, three correct headers, three different column counts. The script enforces the contract so I cannot break it by hand at 1am.

The filename is the default title, so write filenames like titles

Without a titles.json, my script falls back to title-casing the filename and using it as the title. That fallback is fine for a first pass and dangerous for a submit. Reviewers on stock platforms flag titles that look machine-generated: strings of codes, underscores, repeated keyword stuffing.

My rule now: every asset filename is a readable phrase, lowercase, hyphen-separated, no codes, no dates baked into the name. watercolor-botanical-frame-01.jpg, not wc_bot_final_2024_03_v2.jpg. When the fallback fires, the worst case is a plain title, not a spam flag. When I have real titles ready, I pass --titles titles.json and the filename stops mattering for the title column. But keywords still benefit from a clean name because I keep a per-asset keyword map keyed on the filename.

The filename is also the dedupe key

This is the part that bit me hardest. Early on I re-ran a batch after a partial upload and double-posted several assets. On stock platforms, duplicate submissions are one of the fastest ways to get an account flagged. The fix was upload_tracker.py, a plain ledger keyed on filename:

python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
Enter fullscreen mode Exit fullscreen mode

Because the ledger keys on the exact filename, the naming rule becomes an integrity rule. If two files share a name, the tracker cannot tell them apart. If a filename changes between runs, the tracker treats it as a new asset and the old entry becomes an orphan. So: one asset, one filename, forever. Renaming happens before the asset enters output/, never after.

Folder structure is naming at scale

The contract extends upward. My pipeline assumes a fixed layout:

project/
  assets/            raw output (one file per asset)
  output/            ready-to-submit files + metadata.csv
  packs/             finished product zips + covers
  research/          your notes and sources
  scripts/           these tools
Enter fullscreen mode Exit fullscreen mode

make_metadata.py scans a folder and builds one row per file. That only works if output/ contains exactly the files that are ready. Anything else in there becomes a CSV row for an asset I did not mean to submit. The folder is a filter, and the filename decides what passes through it. This whole system ran on 1 vCPU and 2 GB RAM, one process at a time with gc.collect() between assets, so there was no room for a database or a queue service. Folders and filenames were the database.

What this bought me

Since locking these rules in, I have produced about 1,700 assets and packaged 39 products, roughly 856 MB of output, from about 19,000 lines of pipeline code. 58 assets are live on Adobe Stock right now. The first sale was $0.99, and I remember it more clearly than any rejection. The pipeline is not clever. It is four Python scripts, two of which need only the standard library, and the other two need only Pillow and reportlab. The discipline lives in the conventions: fixed headers, readable filenames, one filename per asset, a ledger that remembers what shipped.

If you want the same conventions without rebuilding them, that is what I packaged as digital asset factory. It is the working set: make_metadata.py for the Adobe, Vecteezy, and Dreamstime CSVs, upload_tracker.py for the dedupe ledger, plus md2pdf.py and pack_product.py for turning content into sellable products with covers and thumbnails. It is not magic and it will not make the art for you. It just stops the small naming mistakes from deciding your outcomes.

Get digital asset factory


Building digital products? Free guides & kits for digital product sellers.

Top comments (0)