All posts
Mental Models March 2026 / 3 min read

Systems Thinking: Seeing the Forest When Everyone Else is Staring at the Trees

On a 737, the number that decides whether it flies isn't a weight on any scale — it's where the weight sits. A queue throughput problem I chased for a week turned out to be the same lesson, moved into software.

On a 737 you can weigh every component on the aircraft and still not know whether it will fly. The number that decides flight is not a weight. It is where the weight sits: the center of gravity, relative to the wing. Load the same cargo two feet further aft and the same airplane, the same total mass, handles differently on rotation. Weight-and-balance taught me to stop reading the parts list and start reading the relationship between the parts. That habit moved into software with me, and it is most of what I mean by systems thinking.

Here is where it bit me. We had a queue worker chewing through manufacturing jobs. Each one took a CAD parameter set, drove a kernel rebuild, and wrote geometry back. Throughput was bad, so I did the obvious thing and profiled the worker. Found a slow query, indexed it. Found a redundant deserialize, cached it. Each fix made the worker measurably faster. Throughput barely moved. I had spent a week weighing components and the airplane still would not rotate.

The problem was not in any worker. It was in where the load sat. Jobs landed on a single Redis list, and one upstream tenant submitted in bursts of four hundred. Every burst parked behind one slow job at the head of the line, and the other nine workers sat idle holding nothing. The center of gravity of the whole system was that head-of-line position, and not one of my flame graphs pointed at it. A profiler measures parts. It cannot see a relationship between a queue, a burst pattern, and an idle pool.

The number is in the relationship

This is the trap. Every tool we reach for measures a tree. APM spans, flame graphs, slow-query logs, a memory profiler: each one zooms into a component and tells you how heavy it is. They are good tools. I would not work without them. But they share a blind spot. They cannot show you the thing that has no single location. Head-of-line blocking lives between the queue and the workers. A retry storm lives between a timeout setting and a downstream's recovery time. A cache stampede lives between an expiry and the request rate. None of these has a line number.

On the aircraft, the center of gravity has no part number either. You compute it from the arrangement. The fix for the queue was the same kind of move. I sharded the one list into per-tenant lists and let workers pull round-robin, so one tenant's burst could no longer sit on the head of everyone else's work. Total compute unchanged. I added nothing to make any worker faster. I changed where the weight sat, and the pool filled.

The discipline is boring and hard to keep. Before optimizing a part, I now make myself draw the arrangement: what feeds what, what waits on what, where the load concentrates. Usually the worst number on the page is not a part at all. It is a place where two parts meet.

Where this breaks down

The view is only useful when you have walked the trees. The reason I could spot the head-of-line problem was eight years of staring at queues and locks until the shapes were familiar. Hand a junior engineer the same dashboard and "think in systems" is useless advice. They have no library of arrangements to recognize yet. And the view fails me too, on systems I do not know well. Put me in front of an unfamiliar Kubernetes scheduler and I will confidently point at the wrong relationship, because the map in my head is of a different airplane. I have done exactly that, more than once, and only found out after the change made nothing better.