Do not implement consensus
The practical advice here is short. Do not implement consensus.
Use one of the established coordination stores. Each is years of work with fault-injection test suites behind it, and hand-rolled leader election is a recurring cause of outages in published postmortems. Kubernetes keeps all of its cluster state in one. Kafka used another for years before building its own.
Care more about what you put in it than which one you pick. Consensus is for small critical state that everybody must agree on: who leads, which machine owns which shard, who is in the cluster, flags that must flip atomically.
Do not treat it as a database. Every write costs a round trip to a majority and every member stores a full copy, so throughput is thousands of writes a second, not millions. Putting application data in there is the classic misuse.
Elect your leaders with a lease, the standard pattern. A machine acquires a key with an expiry and keeps refreshing it while it works, and if it dies the key expires and somebody else acquires it.
Read carefully what that does and does not give you. It guarantees at most one holder of the lease. It does not stop a holder that paused for a long garbage collection from waking up after its lease expired, still believing it leads.
Fencing tokens are not optional
Add fencing tokens, then, because they are not optional. Each lease carries a number that only ever increases, the holder includes it in every write to shared storage, and the storage refuses any number lower than the highest it has seen.
Notice what that buys you. A stale leader's writes are refused by the resource itself, so your correctness no longer depends on assumptions about timing. This is the detail that separates a working answer from a plausible one in an interview.
Be honest about what depending on consensus costs. If your coordination store is down, nothing that depends on it can elect a leader or take a lock.
You concentrated availability into one component, which is the right trade when the alternative is split brain, and it needs to be a decision rather than an accident.
Worked example
A team runs a batch job that must have exactly one active instance across 12 workers. The first version uses a Postgres row as a lock with a 60 second timeout, and it works until a worker hits a 90 second garbage collection pause: its lock expires, a second worker takes over, and then the first wakes up and writes results for a job someone else is now running, producing duplicate charges. The rewrite uses an etcd lease with a fencing token. Each acquisition returns an increasing revision number, workers stamp every output file with it, and the output store rejects any write carrying a revision below the highest seen. The same 90 second pause happens again two months later, the stale worker's write is rejected outright, and the incident is a log line instead of a refund run.