Most WordPress content models grow by accident. Someone adds a field for a launch, another for a campaign, a stray update_post_meta() call in a theme function, and a year later nobody can say with confidence what a “service page” actually contains. The fix isn’t more plugins. It’s treating content as a typed data model: a defined shape that templates consume and editors fill in.

Start from the data, not the design

Before opening ACF, write down what the content is. A provider has a name, a role, a photo, a bio, and belongs to one or more clinics. That’s the model. The design is one way to render it; there will be others.

When you model the data first, the field group almost writes itself, and you avoid the trap of fields that only make sense for one specific layout.

Name fields like an API

Field names are a contract between the CMS and every template, block and integration that reads them. Treat them that way:

  • Prefix groups by entity: provider_, clinic_, service_.
  • Use nouns, not layout words. provider_photo, never hero_image_right.
  • Keep booleans positive: is_accepting_patients, not hide_booking.

A consistent naming scheme means a developer can guess a field name correctly most of the time, and the REST API output reads like a real resource.

Keep a thin layer between ACF and your templates

Calling get_field() directly in fifty template files spreads assumptions everywhere. A small accessor layer gives you one place to add defaults, formatting and fallbacks.

function ru_provider( int $post_id ): array {
    return [
        'name'      => get_the_title( $post_id ),
        'role'      => get_field( 'provider_role', $post_id ) ?: 'Provider',
        'photo'     => get_field( 'provider_photo', $post_id )
                       ?: ru_default_avatar(),
        'accepting' => (bool) get_field( 'is_accepting_patients', $post_id ),
    ];
}

Templates now call ru_provider( $id ) and get a predictable array. Change a default once, and every template follows.

Version the field groups

Register field groups in PHP (or export the JSON and commit it) so the model lives in version control alongside the code that reads it. A field rename becomes a reviewable change, not a silent edit in wp-admin on a Friday afternoon.

The payoff

Once content is a typed model, a lot of things get easier at once: new templates reuse existing fields, the REST API exposes clean resources for a future front-end, and onboarding a developer means reading one file instead of clicking through the admin. The CMS stops being a mystery box and starts being something a team can build on for years.