preview_post_link for Custom Post Types

Two problems here:

#1

You’re missing the $accepted_args argument in:

add_filter( $tag, $callback_function, $priority, $accepted_args );

Check out the Codex here for more info on that.

#2

Note that $link . "?program_year=2016" is problematic, since it gives us this kind of link:

 /?p=123&preview=true?program_year=2016

But using instead add_query_arg( [ 'program_year' => '2016' ], $link ) we get the correct form:

/?p=123&preview=true&program_year=2016

Updated code snippet:

Please try this instead (PHP 5.4+):

add_filter( 'preview_post_link', function ( $link, \WP_Post $post )
{
    return '2016program' === $post->post_type 
        ? add_query_arg( [ 'program_year' => '2016' ], $link ) 
        : $link;

 }, 10, 2 ); // Notice the number of arguments is 2 for $link and $post

where we use add_query_arg() to append the extra GET parameter to the link.