In a system where instances scale up and down and get rescheduled to different hosts, a hardcoded IP address is useless within minutes. Service discovery keeps a live, queryable registry of "which instances of service X are healthy and reachable right now," so a caller can look up a current address instead of assuming a fixed one — typically backed by a coordination store like etcd or Consul, itself built on Raft consensus.
Worked example: instead of hardcoding IPs, services register in a registry (Consul, etcd, Kubernetes DNS) and look each other up by name, so instances come and go as they scale or fail. Gotcha: stale entries point traffic at dead instances until health checks evict them, so discovery must be paired with health checking and short TTLs; the registry itself must be highly available or the whole mesh loses its address book.