Two classes do similar things but with mismatched method names and signatures — sortBy() vs orderUsing(), valueOf() vs evaluate().
Equivalent operations have equivalent signatures; a shared superclass or interface emerges naturally.
Smellier version
class CSVExporter { writeAll(rows) {} }class JSONExporter { dump(data) {} }
Fresher version
class CSVExporter implements Exporter { write(rows) {} }class JSONExporter implements Exporter { write(rows) {} }
Substitution becomes copy-paste; consumers can't treat the two interchangeably; abstraction over them is impossible.
Aligning the interfaces may force renames across both classes and every consumer; for classes that have stable independent APIs and only superficial similarity, the alignment is invented coherence.
Polymorphic use becomes possible; new alternatives plug in without bespoke adapters.
Forcing two classes into a shared interface because they look similar — even when their underlying contracts genuinely differ — creates an abstraction that hides important distinctions.