Jun 17, 2022

Birds and Frogs

In his widely discussed 2009 essay, mathematician Freeman Dyson suggested that professionals in his field can be divided into two species: birds and frogs. Dyson wrote:

Birds fly high in the air and survey broad vistas of mathematics out to the far horizon. They delight in concepts that unify our thinking and bring together diverse problems from different parts of the landscape. Frogs live in the mud below and see only the flowers that grow nearby. They delight in the details of particular objects, and they solve problems one at a time.

We can apply this analogy to people in the software industry as well. In fact, many organizations have official titles that clearly divide people up into birds (architects) and frogs (engineers or developers). But the analogy also represents anyone who writes code for a living at the individual level. Whether you consider yourself a bird or a frog, doing your job effectively as a software engineer often means learning to be a bit of both.

Photo of a tiny green frog hanging on to a branch

Photo by Noah Negishi on Unsplash

When we design software we need to act like birds. We need to think in abstractions and design patterns. We need to visualize how different areas of the program interact with each other at a higher level. We are architects. We draw boxes and arrows, and we write specs and RFCs.

When we implement our designs we need to act like frogs. We need to think in terms of classes, and functions, and variables. We work on one feature at a time, one module at a time. We are builders. We write code and documentation and review our teammate’s Pull Requests.

But building software is not a linear process. We are not birds first and frogs second, and we don’t internally “hand-off” perfectly crafted designs for our inner frog to implement. Building (and maintaining) complex applications is not only about being both the bird and the frog, but about switching between these two modes of thinking when necessary.

Frogs and Bugs

Imagine there is a bug in your program. Chances are you won’t find it by looking at an architecture diagram—you have to dive into the code instead. Debugging is (quite literally) a frog’s job.

Bug fixing, on the other had, is not always the responsibility of the frog—and yet, it is typically the frog who takes over. Sometimes fixing a bug is simple. Maybe there is an edge case that we can handle with a few if else statements here and there. Sometimes we can fix a gnarly issue by adding just one more parameter to that function that turned out not to be a very good abstraction after all. And sometimes the solution is a bit more involved, but we still try to fix it with as few changes as possible because we want a short diff that is easy to review by our teammates. After all, less code is always better, right?

From the frog’s perspective, all of these solutions are perfectly fine. The frog doesn’t care – and can’t possibly know – how these seemingly tiny changes affect the overall design of the system. But the bird does—and we’re the bird too.

Photo of a flying bird

Photo by Simon Berger on Unsplash

Code has a tendency to pull us in so we can pay attention to the little details. But even though code is the frog’s domain, it’s important to realize when it’s time to take a step back and analyze things through the eyes of the bird.

This challenge isn’t limited to debugging and bug-fixing, of course. Whenever there is a significant change in our codebase (a bug is fixed, an edge-case is handled, a new feature is added), a good question to ask ourselves is “if we had this information when we first designed this feature, would we have built it the same way?.” Sometimes the answer is no, and in those cases, we need to decide whether we want to take on a piece of tech debt or spend extra time rebuilding it.

This decision typically comes down to how much time you have to build things out “the right way,” which, if we’re being honest, is probably not much. But regardless of the outcome, it’s important to think strategically and evaluate the tradeoffs as a bird. This is not a question for the tactical frog to answer.

The ability to zoom in and out of the code at will is not something that comes naturally to most of us. It’s heavy context-switching, it’s hard, and more often than not, we simply don’t have the time to do it. But when it comes to managing complexity in large applications, being able to shape-shift between a bird and a frog is one of the most useful skills we can develop.