Two Birds Flying the Same Way Is a Bug, Not a Coincidence

What happens when two unrelated bird classes end up with identical flying code, and why that duplication is the opening problem for the Dependency Inversion Principle.

September 14, 20264 min read15 / 20

Four principles into this series, the bird hierarchy looks solid. Flying and eating each got their own interface, each class implements exactly the behaviors it actually has, and nothing is faked. One more requirement is about to expose a crack that was there the whole time.

The New Wrinkle

Add a new bird to this zoo: an owl. Owls fly, so Owl implements Flyable, same as Crow, Sparrow, and Parrot already do.

Then comes the actual wrinkle. Crow and owl, it turns out, fly in exactly the same way. Same wing pattern, same mechanics, identical logic. Separately, parrot and sparrow turn out to match each other too.

Nothing about the interface design breaks here. Crow, Owl, Parrot, and Sparrow all still correctly implement Flyable. The problem is what's sitting inside each of their fly() methods.

class Crow : public Bird, public Flyable { public: void fly() override { // identical logic to Owl::fly() } }; class Owl : public Bird, public Flyable { public: void fly() override { // identical logic to Crow::fly() } }; class Parrot : public Bird, public Flyable { public: void fly() override { // identical logic to Sparrow::fly() } }; class Sparrow : public Bird, public Flyable { public: void fly() override { // identical logic to Parrot::fly() } };

The exact same code shows up twice: once for Crow and Owl, once for Parrot and Sparrow. That's not a design flaw in the interface. It's a violation of a much older, simpler rule: DRY, Don't Repeat Yourself.

Why This Duplication Actually Hurts

It's tempting to shrug this off. Four classes, two small duplicated blocks, what's the harm? The harm shows up the moment either shared flying style needs to change.

Fix a bug in how crow-and-owl-style flying works, and that fix has to happen twice, once in Crow, once in Owl, by hand, in two separate files. Forget one of the two, and the bug is only half fixed, silently, with no compiler warning that anything was missed. The more classes that share this logic, the more places you have to remember to fix, and the easier it is to miss one.

The Fix Pattern, Borrowed From Outside Code

There's a familiar shape to this problem outside programming. Picture two teams inside the same company, one running social media, one designing posters, and both of them, it turns out, write their own marketing copy as part of their job.

The obvious fix isn't asking each team to keep duplicating that work forever. It's carving out a third team whose only job is writing content. Team A and Team B stop repeating that work internally. They both just ask Team C.

That's the exact shape of the fix for Crow and Owl: pull the shared flying logic out into its own place, something both classes can lean on, instead of each carrying its own copy of the same code.

The next post picks up exactly there: what that "third team" actually looks like in code, and why building it the naive way runs straight into the next SOLID principle.