Professional English for Developers Module 5 – Emails and chat
Apologies, bad news and escalations
Send bad news early with what happened, the impact, the plan and the next update, apologise once and specifically, and escalate with evidence and an ask.
What you will learn
- Report bad news early, with what happened, who is affected, the plan and the next update
- Rewrite stacked or vague apologies as one specific apology followed by the fix
- Write an escalation that names the blocker, what you tried and the decision you need
- Schedule incident updates for stakeholders and say in each when the next one comes
Before you start
What you’ll learn · Before you start
What you will learn
- Report bad news early, with what happened, who is affected, the plan and the next update
- Rewrite stacked or vague apologies as one specific apology followed by the fix
- Write an escalation that names the blocker, what you tried and the decision you need
- Schedule incident updates for stakeholders and say in each when the next one comes
Before you start
On this page
Three emails told one client that a release would slip. The one written in four parts had everything a reader needs, and the other two had one part between them, along with three apologies, a guess at the cause and the word “soon” for the next update.
Bad news goes out early, with four parts: what happened, who is affected and how, what you are doing, and when the next update comes. An apology works when there is only one, and it names what went wrong. escalation: Asking someone with more authority to decide or to unblock work, after your own request and your reminders have not worked. ask the person who can decide, with what you tried. Incident updates repeat the four parts on a schedule that you keep.
Bad news goes out early, in four parts
On Thursday at 12:00 Eastern Time, Kavya’s final tests found that Friday’s release would not fix the CSV export for every account. She could write to Daniel, the client, at once, or hope for a fix by Friday and tell him then. A day of notice is worth more to him than a day of hope, so she wrote at once, in four parts, with every time in his zone:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Write it the day you know. Thursday 12:00 Eastern Time. The final tests have just found the problem, and the release is on Friday. Kavya writes now, a day before, instead of hoping the fix arrives in time.
- What happened. "Friday's release of the CSV export fix is moving to Tuesday." The news and the new date come first, so Daniel knows the one thing that changes for him before he reads anything else.
- Who is affected, and how. Exports for 2 of the 30 merchant accounts still show the next day's date after 20:00, and the finance team can keep using the current export for the other 28. Numbers tell Daniel how big the problem is.
- What we are doing. "We are fixing the date rule for those two accounts today and will test it on Monday." What the team knows, what it is doing and when, with no guess about why the rule failed.
- One apology. "I'm sorry for the delay." One sentence, for the thing that affects Daniel, near the end, after he has the facts. No boilerplate and no second apology.
- The next update. "I will send the next update by Monday 17:00 Eastern Time." Daniel knows when he will hear more, in his own zone, so he does not have to chase Kavya for it.
Each part answers a question the reader would otherwise send back: what changed, does it affect me, what are you doing, and when will I hear more. The order matters too. The news comes first, as Microsoft’s style guide asks for anything a reader scans: “Lead with what’s most important.” Say only what you know: a guess about the cause, such as “probably the database team”, has to be taken back in the next update if it is wrong.
The program checks Kavya’s email and two others that told Daniel the same news: one sent late, one in a panic.
// Three emails that tell a client the same bad news, checked for the four parts of a bad-news message (what happened,
// who is affected and how, what we are doing, and when the next update comes, with a time and a zone) and for three
// slips: stacked or boilerplate apologies, guessing at a cause, and times nobody can plan around.
const PARTS = [
['what happened', /\b(?:is|has been) (?:delayed|moving|moved)\b|\b(?:moves|failed|missed|found|is down|will not)\b/i],
['who is affected, and how', /\b\d+ of (?:your )?\d+\b|\baffect\w*\b|\bcan keep using\b|\bcannot use\b/i],
['what we are doing', /\b(?:we are|we're) (?:fixing|testing|replacing|rolling|checking)\b|\bwe will (?:fix|test|release)\b/i],
['the next update, with a time and zone', /\bnext update\b[^.?!]*\b\d{1,2}:\d{2}\b[^.?!]*\b(?:UTC|IST|ET|Eastern Time)\b/i],
];
const APOLOGY = /\b(?:sorry|apologi[sz]e|apologies)\b/gi;
const BOILERPLATE = /\bsorry for (?:the|any) inconvenience(?: caused)?\b/i;
const GUESS = /\b(?:probably|maybe|might have been|we think it was|seems to be)\b/i;
const VAGUE = /\b(?:soon|shortly|some issues|unforeseen|might get delayed)\b/gi;
function check(text) {
const missing = PARTS.filter(([, re]) => !re.test(text)).map(([name]) => name);
const flags = [];
const apologies = (text.match(APOLOGY) ?? []).length;
if (apologies > 1) flags.push(`${apologies} apologies: say sorry once, for something specific`);
if (BOILERPLATE.test(text)) flags.push(`"${text.match(BOILERPLATE)[0]}" is boilerplate: name what you are sorry for`);
if (GUESS.test(text)) flags.push(`"${text.match(GUESS)[0]}": a guess at the cause; say what you know`);
for (const m of text.matchAll(VAGUE)) flags.push(`"${m[0]}" tells the reader nothing they can plan with`);
return { missing, flags };
}
const EMAILS = [
['The late, vague email (Friday 15:00 Eastern Time)', `Hi Daniel,
We are facing some issues with the release and it might get delayed. Will update soon.
Regards,
Kavya`],
['The panicked email (Thursday 18:00 Eastern Time)', `Hi Daniel,
Extremely sorry for the inconvenience caused!! We deeply apologise. Due to unforeseen technical challenges, which were probably caused by the database team, the release is delayed. Sorry again, we are trying our best.
Regards,
Kavya`],
['The four-part email (Thursday 12:00 Eastern Time)', `Hi Daniel,
Friday's release of the CSV export fix is moving to Tuesday. Our final tests found that exports for 2 of your 30 merchant accounts still show the next day's date after 20:00. Your finance team can keep using the current export, which is correct for the other 28 accounts. We are fixing the date rule for those two accounts today and will test it on Monday. I'm sorry for the delay. I will send the next update by Monday 17:00 Eastern Time.
Thanks,
Kavya`],
];
for (const [name, text] of EMAILS) {
const { missing, flags } = check(text);
console.log(`${name}: ${PARTS.length - missing.length} of ${PARTS.length} parts`);
for (const m of missing) console.log(` missing: ${m}`);
for (const f of flags) console.log(` flag: ${f}`);
} Output
The late, vague email (Friday 15:00 Eastern Time): 0 of 4 parts missing: what happened missing: who is affected, and how missing: what we are doing missing: the next update, with a time and zone flag: "some issues" tells the reader nothing they can plan with flag: "might get delayed" tells the reader nothing they can plan with flag: "soon" tells the reader nothing they can plan with The panicked email (Thursday 18:00 Eastern Time): 1 of 4 parts missing: who is affected, and how missing: what we are doing missing: the next update, with a time and zone flag: 3 apologies: say sorry once, for something specific flag: "sorry for the inconvenience caused" is boilerplate: name what you are sorry for flag: "probably": a guess at the cause; say what you know flag: "unforeseen" tells the reader nothing they can plan with The four-part email (Thursday 12:00 Eastern Time): 4 of 4 parts
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node bad_news_check.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
The four-part email has every part and no flags. The late one has none of the parts, and “soon” and “some issues” tell Daniel nothing he can plan with. The panicked one says only what happened, and buries it under three apologies, a boilerplate phrase and a guess that blames another team.
Key idea
Send bad news the day you know it, in four parts: what happened, who is affected and how, what you are doing, and when the next update comes.
Checkpoint
Check: Bad news early, in four parts
One apology, specific, then the fix
“Extremely sorry for the inconvenience caused!! We deeply apologise.” Neither sentence says what the writer is sorry for, and three apologies in one email make the reader wonder how bad things really are. An apology works when there is only one, it names what went wrong, and it comes after the facts, followed by the fix:
| Instead of | Write | Why |
|---|---|---|
| Sorry for the inconvenience caused. | I’m sorry for the delay. | it names what affects the reader |
| We deeply apologise. Sorry again. | I’m sorry I missed the 17:00 IST deadline for the test report. | one apology, for a specific miss |
| Sorry if you felt the update was late. | I’m sorry the update was late. | the condition shifts the fault to the reader |
| Sorry to bother you, but … | Could you check the export by Friday? | asking is not a mistake |
Say I’m sorry when the miss is yours and we’re sorry when it is the team’s. Then move on to the new date and the plan, because the fix is what the reader will act on. Stacked apologies push that plan down the page.
The rest of this lesson uses the same parts for harder messages. Blameless wording describes what the system did, not who made it fail. Escalations ask someone with more authority to decide. And incident updates repeat the four parts on a schedule, saying in each when the next one comes.
Checkpoint
Check: One specific apology, then the fix
Blameless wording: systems and decisions, not people, line by line Pro: opens with a Pro, Premium or Ultimate pass
When something breaks, the first draft of the story usually names who did it. This part rewrites an incident timeline so that it names what to fix instead, checks every line, and shows where the passive voice is the right choice.
In this part:
- A draft timeline that blames, rewritten line by line
- Where the passive voice is the right choice
1 runnable example (JavaScript) · about 3 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Escalating a blocker: the email before and after, and who is copied Pro: opens with a Pro, Premium or Ultimate pass
An escalation is a request to someone with more authority, made after the direct ask and the reminders have not worked. This part checks one escalation before and after a rewrite, and lets you decide who receives yours.
In this part:
- The escalation email, checked part by part
- Who gets it, and who is copied
1 runnable example (JavaScript) · about 7 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Incident updates on a schedule: an outage minute by minute, timed Pro: opens with a Pro, Premium or Ultimate pass
During an outage, stakeholder: A person or a group affected by a project or an incident, or with a say in it, such as a client, a manager or a support team. need the four parts again and again, at times they can count on. This part follows one outage minute by minute, times two teams’ messages against an open incident guide, and ends with an exercise.
In this part:
- The cadence of an open incident guide, measured
- The tense of each part, and the times in it
- Write the first update and the resolution of an incident
- Results of the sample tests
- Three rules to keep from the parts on blame, escalation and updates
1 diagram · 1 runnable example (JavaScript) · 1 exercise with sample tests · about 6 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Practise: a bad-news email in four parts
The exercise holds an email that tells a client’s QA lead about a late build, written in a panic. Rewrite it until the sample tests pass:
Exercise · Medium · JavaScript
Rewrite a bad-news email in four parts
Kavya's team promised Lena, the QA lead of a client in Berlin, a new build of the mobile app by Wednesday 12:00 UTC. On Tuesday the final tests found that the app crashes on Android 14 when a user opens a saved card, so the build moves to Friday. Lena's testers can keep using build 2.3 for everything except saved cards. The team is fixing the crash today and will test it on Thursday, and Kavya will send the next update by Thursday 16:00 UTC.
email.mjs holds the email Kavya first wrote. Rewrite it, inside the quotes, so that it has:
- what happened in the first sentence: the build, and its new day;
- who is affected, and how: Android 14, and what the testers can keep using;
- what you are doing: We are fixing … or We will test …;
- one apology, for something specific, and no inconvenience caused;
- the next update with a time and its zone;
- no guess at the cause (probably, maybe, might), no soon or some issues, no sentence over 25 words, and a closing line before the name.
Starter code · email.mjs
// Rewrite Kavya's email to Lena in four parts. Keep the greeting as the first line and the closing line and the name
// as the last two, separated by \n.
export const email =
'Hi Lena,\n' +
'Extremely sorry for the inconvenience caused!! Due to some unforeseen issues, which are probably in the payment ' +
'SDK, the build might get delayed. We deeply apologise and will update you soon.\n' +
'Regards,\n' +
'Kavya'; The sample tests · email.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { email } from './email.mjs';
// The lesson's four parts of bad news, and its apology rules.
const lines = email.split('\n');
const body = lines.slice(1, -2).join(' ');
const sentences = body.split(/(?<=[.!?])\s+/).filter(Boolean);
const APOLOGY = /\b(?:sorry|apologi[sz]e|apologies)\b/gi;
const CLOSING = /^(?:Thanks|Thank you|Many thanks|Regards|Best regards|Kind regards|Best),$/;
test('the first sentence says what happened: the build and its new day', () => {
const first = sentences[0] ?? '';
assert.ok(/\bbuild\b/i.test(first) && /\bFriday\b/.test(first), `start with the news, the build and Friday: "${first}"`);
});
test('it says who is affected, and how', () => {
assert.ok(/\bAndroid 14\b/.test(body), 'say which phones are affected: Android 14');
assert.ok(/\b(?:build 2\.3|can keep)\b/i.test(body), 'say what the testers can keep using: build 2.3');
});
test('it says what the team is doing', () => {
assert.ok(/\b(?:we are|we're) (?:fixing|testing)\b|\bwe will (?:fix|test)\b/i.test(body), 'say what you are doing: We are fixing …');
});
test('it gives the next update a time and a zone', () => {
assert.ok(/\bnext update\b[^.?!]*\b\d{1,2}:\d{2}\b[^.?!]*\b(?:UTC|CET|CEST|IST)\b/i.test(body), 'say when the next update comes, with a time and its zone');
});
test('it apologises once, for something specific', () => {
const n = (body.match(APOLOGY) ?? []).length;
assert.equal(n, 1, `${n} apologies: say sorry once`);
assert.ok(!/\binconvenience\b/i.test(body), '"inconvenience caused" is boilerplate: say what you are sorry for');
});
test('no guesses, no vague words, short sentences and a closing line', () => {
assert.ok(!/\b(?:probably|maybe|might)\b/i.test(body), 'say what you know, not what the cause might be');
assert.ok(!/\b(?:soon|some issues|unforeseen)\b/i.test(body), 'give facts and times, not "soon" or "some issues"');
assert.ok(!/!!/.test(email), 'no "!!"');
for (const s of sentences) {
const n = s.split(/\s+/).length;
assert.ok(n <= 25, `${n} words: "${s}"`);
}
assert.ok(CLOSING.test(lines.at(-2) ?? ''), 'end with a closing line, such as "Thanks,", before the name');
}); A hint
Start with the news itself: "The new app build is moving from Wednesday to Friday." Then one sentence each for the crash, what the testers can use, the fix, the apology and the next update.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (QuickJS): nothing is sent to mysmartcopilot.com. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Then try the questions on bad news and apologies:
Practice that adapts to you
Practise this lesson
6 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to mid-level): What makes an apology in a work email specific?
It names what went wrong and how it affects the reader, once: “I’m sorry I missed the 17:00 IST deadline for the test report”, not “sorry for the inconvenience caused”. It comes after the facts, and the fix follows it at once: the new time, and what you are doing so that it does not happen again. One apology is enough.
Three longer questions of this module’s bank build on this lesson:
-
Fresher to mid
You promised a client a fix by Friday. On Thursday you learn it will take until Tuesday, because a library you depend on has a bug. Write the first three sentences of your email to the client, and explain each one.
-
Mid to senior
Your launch is next Monday, and the team that must approve a firewall change has not answered for four days, despite two reminders. When do you escalate, to whom, and what does your message say?
-
Mid to staff
During a payments outage you are asked to keep business stakeholders informed while the engineers fix it. What do your first three messages say, and when does each one go out?
Key takeaways
- Send bad news the day you know it, with what happened, who is affected and how, what you are doing, and the next update with a time and a zone.
- Apologise once, for something specific, after the facts, and move straight to the fix and the new date.
- Say only what you know: a guess about the cause, or a word such as “soon”, gives the reader nothing to plan with.
Terms in this lesson
- blameless postmortem
- A review of an incident that looks for the causes in systems and decisions rather than in people, assuming that everyone acted with good intentions on what they knew at the time.
- escalation
- Asking someone with more authority to decide or to unblock work, after your own request and your reminders have not worked.
- stakeholder
- A person or a group affected by a project or an incident, or with a say in it, such as a client, a manager or a support team.
References
- External communication guidelines (PagerDuty incident response documentation) (PagerDuty)
- Managing incidents (Site Reliability Engineering, chapter 14) (Google)
- Postmortem culture: learning from failure (Site Reliability Engineering, chapter 15) (Google)
- Active voice (Google developer documentation style guide) (Google for Developers)
- Top 10 tips for Microsoft style and voice (Microsoft)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress