All writing

Work & Career

Translated from the original Chinese essay · Read the Chinese original →

"Just copy that previous feature, it should be simple, right?"

At that moment, my mind went blank. I felt like I wasn't listening to a requirement, but to a trailer for a future production incident.

As a programmer, this is the starting point of countless pitfalls I've stepped into: a seemingly simple requirement that ends up affecting the whole system. Not only is the scope of changes three times larger than expected, but I also have to bear the burden of all the "why is this happening" questions after launch.

What's worse, when I sort out the logic, clarify the boundaries, and go to explain to the product manager "why it's not simple," they look at me like I'm an alien: "Oh, you're being too sensitive, aren't you?"

But being "sensitive" as a technical person is not a bad thing; it's an instinct to take responsibility for the system. The real problem is that the word "simple" hides a cognitive bias about complexity between different roles.


Our "simple" is not the same thing at all

In the eyes of a product manager, "simple" means the user flow hasn't changed, the interaction doesn't look difficult, and the functionality is similar to before.

But in the eyes of a programmer, "simple" has to pass these checkpoints first:

  • Does it change the database structure?
  • Does it depend on third-party APIs?
  • Will it break the existing logic chain?
  • Can it be compatible with historical data of old users?

So when the product says "just move the button to a different place," the programmer knows this might involve three modules, four services, and a bunch of cache synchronization.

The root of the problem is not who is right or wrong, but that the two roles use different language systems when defining "cost" and "risk":

  • Product focuses more on "whether it's worth doing," emphasizing the value-to-investment ratio;
  • Programmers focus more on "how to do it stably," emphasizing technical debt and evolution costs.

And when we use our own language to say "simple," it sounds to the other person like pretending to understand when we don't.


Programmers are not translators, but they have to learn to translate

I used to complain inwardly: You're not the one writing code, so what gives you the right to say it's simple?

But later I started asking myself: If I don't explain the complexity clearly, how will the other person know "which risks are worth discussing"?

So I tried a different way of speaking, not refuting the other person with "you're wrong," but guiding them to see "what you haven't seen."

For example, when the product says "just add a validation," I don't directly reply "no," but ask like this:

  • "Should this validation be done on the frontend, or does the backend need to intercept it? And if it's intercepted, what should the error message look like?"
  • "Does our current logic assume every step succeeds by default? After adding this, do we need to supplement the failure flow?"
  • "If this step fails, will the user's data get stuck in some intermediate state?"

I found that when I turn "true/false questions" into "multiple-choice questions," the other person is more willing to participate in weighing the trade-offs together.

This is a little trick I later summarized:

Translate technical complexity into multiple-choice questions, then translate the cost of those choices into user experience.

It's not about telling the other person "this is not simple," but rather "the effect you want may have more than one path, let's pick one that is both reliable and effort-saving."


Don't just be the one patching the pot; also draw the boundaries of the pot

Sometimes, technical people easily fall into a misconception: as long as a requirement comes, I have to do it.

But if you don't draw boundaries, no one else will consider the cost for you. Then you become the team's "universal repairman," but you can never define your own work.

So when I was a Tech Lead in a team, I made two small mechanisms:

  1. In every requirement review meeting, the engineer must present a "risk map" Not just the schedule, but clearly state which modules will be affected, where bugs are most likely to occur, and whether a rollback plan is needed.

  2. Create a "change prerequisite checklist" For example:

    • Is there API documentation?
    • Is there test data?
    • Is there a fallback plan?
    • Can it be released gradually?

This is not shirking responsibility, but rather helping the product understand: should a "seemingly simple" requirement be discussed for two more hours to clarify, instead of going directly into scheduling?

When you start proactively setting boundaries, the other person will be more likely to respect your professionalism.


Truly mature technical communication is not about persuading the other person, but about co-creating definitions

Many people think the key to a programmer talking with a product manager is to "make the complex simple."

But I increasingly feel that the real ability is to "make the complex reasonable."

You don't need to dump all the details at once to scare people, but to guide the other person to see those "invisible complexities," and then decide together which parts can be simplified and which cannot be compromised.

This way, you not only protect the bottom line of the system, but also participate in the discussion of product value—from passively responding to requirements, to becoming a co-creator of definitions.

So next time you hear "this requirement is simple," don't rush to complain.

Maybe it's a good opportunity for a technical person to step up and "redefine complexity."


Interactive Easter Egg: What's the worst time you've been burned by "simple"?