Sign in

What engineering is and is not

Two programs solve the same problem. One runs faster. The other is easier to read and check for mistakes. Which one is better? The question has no answer until you know what the program is for. Engineering is the reasoned choice among options like these, and the word "better" always points back to a goal you have to name first.

Which program is better?

You are handed two programs. They take the same input and return the same output. One is faster. The other is plainer, so a person reading it can follow every step and spot a mistake in seconds. Someone asks you which is better.

There is a strong pull to answer right away. Faster sounds better. Clearer also sounds better. But whichever you pick, you have picked without the one thing that decides it: what the program is for. A program that prices trades a million times a second depends entirely on speed. A program that runs once a night and must never quietly corrupt payroll depends entirely on being easy to check. Same two programs, different right answer.

That is what engineering is not. It is not making a thing as fast, as small, or as clever as it can be. Push any one of those dials to its limit and you usually starve another that mattered more. The work is choosing which dials to turn, and how far, for the goal in front of you.

A tradeoff is a choice where one gain costs another

A tradeoff is a design choice where gaining one property costs you another. More speed for less clarity. More clarity for less speed. Both options are valid, and neither is broken. What separates them is fit: which one serves the goal the work is really there for.

That goal has a name worth using. Call it the parent goal, the larger thing every smaller choice underneath it is meant to serve. You cannot judge a smaller choice until you have named the parent goal above it, because the same choice can be right under one parent goal and wrong under another.

Two real ways to send data between programs

Programs constantly send data to each other, and there are two well-known formats for it that make opposite tradeoffs.

JSON is text. A message in JSON is something a person can open and read directly. When a response comes back wrong, an engineer opens it, sees the bad field with their own eyes, and fixes it. That readability is the whole appeal.

Protocol Buffers is a binary format built at Google. The same message in Protocol Buffers is far smaller and faster for a machine to process, but a person cannot read it without extra tools to decode it first. The bytes on the wire are compact and quick. The direct human readability is gone.

Both formats are correct, and both are used widely every day. One is built around machine efficiency. The other is built around a human being able to read it. Neither is the better format in the abstract, because "better" is still waiting on a parent goal.

Which one is right depends on the parent goal

Picture a public API, the kind outside developers around the world write code against. What is the parent goal there? Those developers succeeding quickly. When something breaks, a developer who can open the response and read it is unstuck in minutes. The handful of bytes a binary format would save is a small child goal at that scale, invisible next to the cost of confusing thousands of people. Readability serves the parent goal directly, so JSON is right.

Now picture internal traffic between a company's own services, machines exchanging billions of messages a day with each other. Here the parent goal shifts. Keeping that system fast and affordable is a large part of the whole point. At billions of messages, the size of each message and the cost of processing it stop being negligible. Now the compact binary format serves the parent goal, so Protocol Buffers is right.

This is the split that happened in the real world. Google built Protocol Buffers for the internal services where size and speed dominate, while most public APIs are built on JSON. Same two formats. Opposite correct answers. The only thing that moved was the parent goal.

Same two data formats, opposite correct answers under different parent goalsTwo columns compare the same two data formats under two different parent goals. A public API whose parent goal is outside developers succeeding quickly is correctly served by JSON, because a human can read the response. Internal services whose parent goal is staying fast and affordable at billions of messages are correctly served by Protocol Buffers, because the messages are small and fast to process. The formats do not change, only the parent goal does. Same two formats, opposite correct answers Public API, outside developers parent goal: outside developers succeed quickly JSON a human reads the response and fixes it saved bytes: a small child goal here Internal traffic between services parent goal: stay fast and affordable at scale Protocol Buffers small and fast for machines to process readability: a small child goal here The formats do not change. The parent goal does, and it decides which one is correct.

The wrong way to choose

There is a tempting way to get this wrong, and it is worth naming because it looks like rigor. You take one child goal, measure it, and declare a winner. "Protocol Buffers produces smaller messages, so Protocol Buffers is better." The measurement is true. The conclusion skips the parent goal entirely.

Put the binary format into a small public API and watch what it buys. It saves a few bytes nobody will ever notice, and in exchange every outside developer now needs extra tooling to read a response they used to read directly. A child goal has been pushed to its best value at the direct expense of the parent goal above it. When improving a child goal makes the parent goal it serves worse, you have improved nothing at all.

Say the goal before you compare options

Every design decision sits underneath a parent goal, and the decision only makes sense once that goal is named. So make naming it your first move. Before you weigh two options, before you measure anything, say plainly what the larger goal is that both options are meant to serve. Once it is named, most tradeoffs stop being a matter of taste and become a plain question of which option serves the goal you just stated.