A block rendered by PHP at request time returns null from save, which means the post contains only the block comment and its attributes.
register_block_type( __DIR__, array(
'render_callback' => function ( array $attributes ): string {
$posts = get_posts( array(
'post_type' => 'product',
'posts_per_page' => min( (int) $attributes['count'], 12 ),
) );
return turkerdev_render_products( $posts );
},
) );
// save: () => null ← in the JavaScript
Returning null is what exempts the block from validation entirely, which means no deprecation array is ever needed and the markup can change freely — the single biggest maintenance saving available. The cost is that the block runs on every page view rather than once at save, so caching becomes your problem. Anything with a query, a date, or content that changes independently of the post should be dynamic for correctness rather than for convenience.