PA Dev — 08/02

Context

Today’s work was about making EXIF/colour profile handling real, reliable, and survivable, not just “working on the happy path”. The focus wasn’t adding flashy features, but ensuring PA can evolve its data model without breaking years of historical data.

This was a classic systems day: UI, backend, migrations, backward compatibility, and build tooling all intersecting.


What I worked on

1. Implemented “Get EXIF” end-to-end (UI → FilesAPI → PA DB)

  • Added Get EXIF action at the Item level in Order Detail.
  • PA acts as an orchestrator:
    • Collects image UUIDs from the item
    • Calls FilesAPI /api/v1/exif/get-bulk
    • Persists results into a new EXIF table
  • UI shows a clear execution summary:
    • Requested / Found / Not found / Persisted
  • Confirmed idempotent behavior (safe to re-fetch).

Key takeaway: UI feedback is not truth; persistence is.


2. Introduced a new EXIF table (without breaking the past)

  • Added a dedicated table for per-image EXIF / colour profile data.
  • Designed it to be:
    • Keyed by image_uuid
    • Upsert-friendly
    • Explicit about data source (icc_header, legacy_migration, etc.)

This creates a clean separation between:

  • Historical/denormalized metadata
  • Newly fetched, authoritative EXIF data

3. Designed backward compatibility as a first-class feature

Reality check: there is too much historical data to migrate all at once.

So instead of forcing migration, I:

  • Defined a single “effective colour profile” resolution rule:
    1. New EXIF table (if exists)
    2. Legacy image fields / metadata
    3. Fallback (“Unknown”)
  • Implemented this as a shared backend accessor so:
    • Order Detail UI
    • Future APIs
    • Reportsall use the same logic.

Result: PA works correctly even with zero migrated rows.

Migration becomes an optimization, not a requirement.


4. Built a safe EXIF backfill command

  • Added pa:exif:backfill command:
    • Dry-run by default
    • –commit to persist
    • Supports limits / incremental runs
  • Backfills only what PA already knows (no FilesAPI calls).
  • Marks migrated data explicitly as legacy_migration.

This allows gradual cleanup without operational risk.


5. Fixed multiple production-level failures

Along the way, several real-world issues surfaced:

  • foreach() on non-array when FilesAPI returned unexpected shapes→ Fixed with strict validation and normalization.
  • Model method mismatch (getRawOriginal() not available in this codebase)→ Replaced with safe getOriginal() / attribute access.
  • Console helper mismatch (newLine() not supported in this Laravel version)→ Replaced with compatible output calls.
  • Frontend build failures due to unsupported JS syntax:
    • Optional chaining (?.)
    • Nullish coalescing (??)→ Rewrote logic using explicit, boring, compatible JavaScript.

Each fix reinforced the same lesson: assume less, validate more.


What I learned

  1. Backward compatibility is not a fallback — it’s a design choiceIf migration is required for correctness, the system is fragile.Correctness must come first; migration can follow.
  2. “Effective data” matters more than “where data lives”Defining a single resolution rule (new → legacy → fallback) simplified:
    • UI logic
    • Debugging
    • Future changes
  3. Tooling constraints shape architectureOlder Babel / Laravel versions force discipline:
    • No experimental syntax
    • Clear, explicit code
    • Fewer hidden assumptions
  4. Logs and summaries are part of the featureBeing able to say “Persisted: 1” and trust it is just as important as the API call itself.

End-of-day state

  • EXIF fetching works and persists correctly.
  • UI reflects new data after refresh.
  • Old data continues to work unchanged.
  • Migration is optional, incremental, and safe.

The system is now more honest about its past and more flexible about its future.