ESL at Work: Pushback Phrases for Code Review

2026/02/27

BUILDesl🍞

Last week I was reviewing a PR where a teammate chose to leak host environment variables into a Bazel sandbox — --action_env=PATH, --action_env=CC, --action_env=CXX — instead of setting up a proper cc_toolchain. Classic shortcut. Ship now, fix later. There was even a TODO ticket filed for the “real” fix.

My instinct: why not just do it right? We’ll need cc_toolchain eventually anyway. The passthrough breaks hermeticity globally — not scoped to one target, not behind a flag, just a blanket “trust the host” on every build command. And our .bazelrc was clean until this PR. First crack.

I knew what I wanted to say. I could think it in Chinese. I could explain it to a rubber duck. But the moment I went to type a code review comment, I froze.

Not because I didn’t have the technical argument. Because I didn’t have the English.

The ESL pushback problem

Here’s something native speakers don’t think about: disagreeing in your second language is a high-wire act.

In your first language, you’ve spent decades calibrating tone. You know the difference between “I think we should reconsider” and “this is wrong” and “hm, have you thought about…” — not because someone taught you, but because you’ve heard thousands of conversations and absorbed the social gradients.

In your second language, you’re working from a much smaller phrase library. And the gaps are brutal. You either sound too weak (“maybe we could possibly consider…”) and get steamrolled, or too blunt (“this is the wrong approach”) and burn a relationship. There’s no middle gear.

Code review is where this hits hardest. You’re writing, not speaking, so you’d think it’s easier. But written feedback has no tone of voice, no facial expression, no body language to soften it. The words have to do all the work. And if your word bank has three options — “this is fine,” “this is wrong,” and silence — you’re going to have a bad time.

When shortcuts compound

Before the phrase bank, the philosophy question. Because not every shortcut deserves pushback.

Sometimes “ship the working thing” is exactly right. Prototypes. Time-boxed experiments. Things that are going to be thrown away. If the blast radius is small and the cleanup is genuinely cheap, pragmatism wins.

But some shortcuts compound. They accrue interest. And the interest rate is higher than people think.

Three red flags that a shortcut will cost you:

1. Global blast radius. The --action_env=PATH flag doesn’t affect one target. It affects every bazel build in the entire repo. Every developer, every CI run, every future package that gets added. The scope of the shortcut is the entire build system. That’s not “taking on a little tech debt” — that’s signing a loan with the whole codebase as collateral.

2. First crack in a clean surface. Our .bazelrc had zero environment leaks. Zero. That’s not a default state — someone made deliberate choices to keep it that way. The first exception is the hardest to argue against (“it’s just one line”) and the easiest to repeat (“well, we already have one…”). This is the broken windows theory, except for build configs.

3. “We’ll need the real thing anyway.” If the proper solution is on the roadmap regardless, the shortcut isn’t saving work — it’s adding work. You’re now maintaining the hack AND eventually building the real thing AND migrating from one to the other. Three things instead of one.

When all three are true, the shortcut isn’t pragmatic. It’s expensive.

The phrase bank

This is the part I wish someone had given me years ago.

The idea is simple: pre-loaded phrases, organized by temperature, that you can adapt to any code review pushback. Not scripts — templates. You swap in the technical details, but the framing is already done. The social calibration is built into the phrase.

For ESL speakers, this turns real-time English generation into pattern matching. And pattern matching is fast.

Temperature 1: Genuinely curious

This is your default. You’re not attacking — you’re asking to understand. It gives the other person room to explain something you might have missed. And if they don’t have a good answer, the question does the pushing for you.

“Can you help me understand the hard parts here? Why not set up cc_toolchain directly instead of the env passthrough? We’ll need it eventually for mai_kernels / CUDA anyway — curious what makes it not worth doing now.”

“I might be missing context — what’s the tradeoff that makes [shortcut] better than [proper approach] here?”

“Interesting approach. What happens if [consequence of shortcut]? Asking because I want to make sure we’re not boxing ourselves in.”

“I see the TODO for the proper fix — is there something blocking it from being done in this PR?”

Why this works: The word “curious” is magic in code review. It signals “I’m not judging, I’m learning.” Even if you’re 95% sure the shortcut is wrong, leading with curiosity gives the author a face-saving way to reconsider.

ESL note: “Can you help me understand” is the single most useful phrase in professional English. It works for disagreement, clarification, and genuine confusion — and the listener can’t tell which one you mean. That ambiguity is a feature.

Temperature 2: Respectful but pointed

You’ve asked the gentle question, or you already know the answer and want to move faster. This level names the specific concern.

“Can you walk me through why cc_toolchain isn’t feasible here? The --action_env=PATH on build breaks hermeticity globally, and our .bazelrc is currently clean — I’d rather not introduce the first exception unless there’s a strong reason.”

“I think this introduces [specific technical risk]. Have we considered [alternative]? Happy to pair on it if the setup is tricky.”

“The TODO ticket makes sense as a plan, but I’m worried about [specific consequence] in the meantime. How long are we expecting to live with this?”

“This works, but it [specific downside]. Since we’ll need [proper approach] anyway, what would it take to just do that now?”

Why this works: You’ve named the cost. Not “this is bad” — “this breaks hermeticity globally” or “this is the first exception in a clean config.” Specificity is your friend. It’s hard to dismiss a concrete concern.

ESL note: “I’d rather not” is polite but firm. Softer than “we shouldn’t” but stronger than “maybe we could avoid.” File it away.

Temperature 3: Direct

For when the gentle versions didn’t land, or when the risk is high enough that you need to be clear.

“What’s the blocker on doing cc_toolchain directly?”

“I’m not comfortable merging this without [condition]. Here’s why: [one sentence].”

“This is the first --action_env in our .bazelrc. I think we should keep it at zero. Can we explore [alternative] instead?”

“Strong preference for doing the proper fix in this PR rather than adding a TODO. The blast radius of this shortcut is [scope].”

Why this works: Short and specific. No hedging. You’re stating a position, not asking a question. This is appropriate when you’ve already done the temperature 1-2 dance and need to be clear.

ESL note: “I’m not comfortable merging this” is a phrase that carries weight without being aggressive. It’s a professional way to say “I will block this PR.” Learn it.

Temperature 4: The block

You almost never need this. Save it for actual safety issues, data loss risks, or changes that would be very expensive to revert.

“Requesting changes — I don’t think we should merge this as-is. [One sentence on why]. Happy to discuss synchronously if a comment thread isn’t the right format.”

“Blocking on [specific issue]. I think the right path is [alternative]. Can we sync on this?”

ESL note: “Requesting changes” is GitHub’s built-in mechanism. Using it is not rude — it’s the system working as designed. But pair it with “happy to discuss” so it doesn’t read as a wall.

Structural phrases

Beyond temperature levels, these are general-purpose connectors that make any feedback sound more natural:

Instead of Try
“This is wrong” “I think there’s a concern here”
“You should do X” “What about doing X?” / “Have we considered X?”
“This will cause problems” “I’m worried about [specific thing] happening”
“I disagree” “I see it differently — here’s my thinking”
“Why did you do it this way?” “Can you walk me through the thinking here?”
“This is not good enough” “I think we can do better here — what if we [suggestion]?”
“You forgot to…” “One thing I didn’t see — [thing]. Was that intentional?”
“Fix this” “Mind updating this to [specific change]?”

The left column is what your brain produces in real-time when you’re tired and thinking in your first language. The right column is what lands well in American tech culture. The gap is not about being “polite” — it’s about giving the other person a path to respond without getting defensive.

The meta-skill

Here’s what I’ve figured out after two years of doing this: the phrases aren’t the hard part. The hard part is choosing the right temperature in real-time while also processing the technical content in your second language.

Your brain has finite bandwidth. Technical analysis, English production, and social calibration are all competing for the same working memory. Something has to give.

Pre-loading the phrases means the social calibration part is handled. You pick a temperature, grab a template, fill in the technical details. Two of the three problems are solved before you start typing.

Is this “authentic”? I don’t know. Is a pianist’s muscle memory “authentic”? They practiced those scales a thousand times so their fingers could move without conscious thought, freeing their brain to focus on musicality. Same thing. You practice the phrases so your brain can focus on the argument.

How I actually use this

I keep a text file. (Yes, a text file. I’m that person.) When I hit a code review that needs pushback:

  1. Read the PR. Form the technical opinion.
  2. Pick a temperature. Usually start at 1.
  3. Open the phrase file. Find a template that fits.
  4. Fill in the specifics. Edit until it sounds like me.
  5. Read it out loud. If it sounds like a robot, rewrite.
  6. Post.

Step 5 is important. These templates are starting points, not scripts. If you copy-paste them verbatim every time, people will notice. The goal is to internalize the patterns — the way “Can you help me understand” opens a disagreement, the way “I’m worried about” names a concern without accusation — until you don’t need the file anymore.

I still need the file. I use it less than I did six months ago. That’s progress.

The shortcut that started this

I ended up leaving a temperature 1 comment on that PR. Something like: “Can you help me understand why cc_toolchain isn’t feasible here? The --action_env passthrough breaks hermeticity globally, and I’d rather keep .bazelrc clean if we can.”

The author’s response: they hadn’t realized the scope was global. They thought --action_env on build only affected the specific targets using C++ compilation. It didn’t — it affects every action in every build. Once that was clear, they agreed to do the proper fix.

That’s the thing about the “curious” approach. Half the time, the person who chose the shortcut didn’t fully understand the cost. They’re not being lazy — they’re missing context. And a question that surfaces the context does more work than a directive ever could.

The other half of the time, they did understand the cost and made a deliberate call — and then you learn something about a constraint you didn’t know about. Either way, you win.