You converted work, not removed it
Denormalising does not remove work. It converts read-time work into write-time work, plus a standing liability.
The liability is drift. Your copies will disagree eventually, and your job is deciding how they get updated, how quickly they agree again, and how you find the ones that got away.
Which table is the truth belongs in writing for each duplicated field, before you write any code. Copies are caches. If the name on the post and the name on the user disagree, the user wins, and anything that repairs data repairs towards it. Skip this and you will spend a meeting arguing about which corrupted value is the real one.
Keeping the copies honest
Pick how the copies get updated, and know how each way fails. Updating truth and copies in one transaction is perfectly consistent and couples every write to every copy, so a rename touching 80,000 rows makes renames slow and lock-heavy.
Triggers keep the logic next to the data and hide it from code review, and a chain of them is miserable to debug at 3am.
Choose the one that scales: write the truth, emit a change event, and let a consumer update the copies. You pay with a window, usually seconds, where copies are stale, and that window has to be acceptable to the product rather than merely to the engineers.
Reconciliation matters, because propagating changes in the background fails silently. Run a scheduled job that recomputes the copies from the truth, counts what disagreed, and repairs it.
That mismatch count is a health signal. When it trends upward, something upstream is dropping events, and you would rather learn that from a graph than from a customer.
Duplicate only facts you can recompute, which is the habit this whole lesson comes down to. A copied name is always rebuildable from the user. A counter incremented in place with nothing behind it is, once wrong, wrong forever.
Worked example
Marco's team at a social app maintains follower_count as a column on users, incremented and decremented by application code, because COUNT(*) on a 900-million-row follows table is off the menu. Over a year, missed decrements from a buggy unfollow path let counts drift; the worst account shows 12,400 followers while the follows table holds 11,900, and a creator publicly accuses the platform of deleting followers. The repair has three layers. A nightly job recomputes counts from the follows table for accounts touched that day and logs mismatches, which start at 0.6 percent of active accounts. The counter updates move into the same transaction as the follow-row insert or delete, eliminating new drift. And the mismatch rate becomes a dashboard metric with an alert at 0.05 percent. Four weeks later the nightly repairs fall to near zero, and the metric later catches an unrelated bug in an account-merge tool within two days of its deploy.