Without a persistent object cache a transient is a row in wp_options with an expiry, and with one it is a cache entry that may be evicted at any moment — the same API, two different guarantees.
$data = get_transient( 'turkerdev_report' );
if ( false === $data ) {
$data = turkerdev_build_report();
set_transient( 'turkerdev_report', $data, HOUR_IN_SECONDS );
}
// false means "not there", which a legitimately false value cannot
// be distinguished from. store an array, or a sentinel.
The false ambiguity is the practical trap and it is the reason to store a wrapper array for anything that could itself be false or empty. Expired transients without an object cache are only cleaned up on a scheduled event, so a site generating many of them accumulates rows and slows down every autoloaded option read. Checking the size of the options table is a two-minute diagnostic that explains a surprising number of slow sites.