SEO Metadata Taught My CSV Importer to Respect Other Plugins’ Contracts

I thought adding SEO fields to a WordPress CSV tool would be a small expansion: export several post-meta keys and accept them during import. It became a 900-line interoperability change touching a shared field registry, importer, exporter, interface, and tests. The difficulty was not writing strings into post meta. It was preserving the meaning another plugin assigns to empty values, aliases, booleans, schema fragments, redirects, and fields that may be absent because a feature is disabled.
The field list needed one owner
Import and export had grown separate knowledge of columns. That is how round trips drift: the exporter emits one name, the importer expects another, and documentation describes a third. I introduced a field registry that defines supported columns, storage keys, aliases, normalization, and feature availability in one place.
The registry is not glamorous, but it made every later decision reviewable. A field could not quietly exist on only one side of the workflow.
Missing, blank, and false were different values
A missing CSV column means “this file has no opinion.” A present blank can mean “clear the stored value.” A false-like value may disable a feature. Treating all three as empty either preserves data the operator wanted removed or erases data the file never intended to touch.
I made presence explicit before normalization. That rule is now one of my first checks in any bulk update system, because spreadsheets naturally collapse distinctions that applications depend on.
Aliases helped migration but could not become ambiguity
Existing exports and hand-built files used different headings for similar SEO concepts. I accepted documented aliases at the boundary and converted them to one canonical internal field. If two aliases appeared with conflicting values, the importer reported the conflict rather than selecting whichever column happened to be processed last.
This keeps compatibility generous and execution deterministic.
Schema-looking text was not mine to reinterpret
SEO fields can contain JSON, templates, variables, or strings that resemble structured data without being standalone JSON documents. An eager decoder can change escaping or reorder data. I preserved raw values unless the contract explicitly required a transformation and tested export-import round trips with quotes, newlines, backslashes, and template tokens.
column absent => leave existing value alone
column blank => apply the documented clear behavior
column present => validate and store through its field rule
That tiny decision table prevented a surprising amount of accidental data loss.
Plugin availability changed the user experience
The CSV tool should not pretend every integration is active. I surfaced supported SEO columns when the relevant plugin contract was available and kept the base importer usable when it was not. Files containing unknown integration columns produce actionable warnings instead of fatal errors.
This matters on migrations where the source and destination are intentionally different. The operator should be told what was skipped and why.
Security applied to spreadsheets too
Exports may be opened in software that interprets values beginning with formula characters. Imports can touch protected metadata at scale. I kept capability and nonce checks around the operation, applied formula-injection handling to output, and avoided allowing an arbitrary meta-key column to bypass the supported registry.
Round-trip tests became the definition of done
I built fixtures containing ordinary values, blanks, aliases, multiline text, schema fragments, and disabled integration states. The strongest assertion was not that each side ran. It was that exporting known WordPress state and importing the result reproduced the supported meaning without unrelated changes.
Redirects were a useful example of why field-by-field policy matters. A destination URL, redirect type, and activation flag form one concept; importing only one column can leave a contradictory combination. The registry can validate related fields together and report that the row is incomplete instead of saving a half-configured redirect. Flat files do not remove domain relationships—they merely make those relationships easier to overlook.
I also separated portability from completeness. Some plugin state is derived, indexed, or better rebuilt by the destination plugin after import. Exporting every internal key would couple the tool to implementation details and increase the chance of carrying stale caches. The supported set favors editorial inputs and stable public contracts. After import, the integration’s own rebuild or reindex path remains authoritative for derived data.
The admin copy names the integration and version assumptions rather than presenting the extra columns as universal WordPress fields. That small language choice prevents operators from expecting the same behavior on a destination with a different SEO stack and makes migration planning more honest.
Interoperability is a product feature
The business value was portability: editors could move content and SEO work together instead of rebuilding metadata by hand. That value depended on respecting another plugin’s contract more than on supporting a long list of fields.
When I add the next integration, I will begin with ownership, absence semantics, canonical names, and a round-trip fixture. Writing to the database comes later. The hard part of compatibility is not reaching someone else’s data; it is declining to invent what that data means.
Photo by ThisIsEngineering on Pexels.
Written by
Adrian Saycon
A developer with a passion for emerging technologies, Adrian Saycon focuses on transforming the latest tech trends into great, functional products.


