Writing Technical Documents
Writing is the single greatest career multiplier for engineers. Code ships features; writing ships influence. A well-written document reaches people you have never met, survives long after you leave a team, and compounds in value over time.
Why Writing Beats "Just Talking About It"
- Scale — a document reaches 50 people with zero extra effort; a meeting does not.
- Clarity — writing forces you to organize your thoughts. Fuzzy thinking becomes obvious on paper.
- Persistence — Slack messages vanish. Meetings are forgotten. Documents remain searchable for years.
- Equity — writing gives introverts, non-native speakers, and remote engineers an equal voice.
Writing Clearly vs. Writing Smart
Many engineers confuse "sounding smart" with "communicating well." The goal is never to impress; it is to transfer understanding.
Writing Smart (Bad)
"The system utilizes a polymorphic event-driven paradigm leveraging asynchronous message propagation to facilitate decoupled inter-service orchestration."
Writing Clearly (Good)
"Services communicate by sending events through a message queue. This keeps them independent — one service can change without breaking another."
The Curse of Knowledge
Once you understand something deeply, you forget what it was like not to understand it. This is the single biggest barrier to clear writing. Countermeasures:
- Name your audience explicitly. "This document is for backend engineers who have not worked with our payment system." Write that sentence at the top.
- Define every acronym on first use. Even "obvious" ones like API or CI. Your reader may be from a different team.
- Ask someone outside the project to read a draft. If they get confused, the writing is at fault — not the reader.
- Read your draft aloud. Sentences that make you stumble will make readers stumble too.
Audience Awareness Matrix
| Audience | Cares About | Skip | Format |
|---|---|---|---|
| Fellow engineers | Implementation details, trade-offs, APIs | Business justification | Design doc, RFC |
| Engineering manager | Timeline, risks, staffing needs | Code-level specifics | One-pager, email |
| Product manager | User impact, scope, dependencies | Internal architecture | Short summary + diagrams |
| Executive | Business value, cost, timeline | Everything technical | 3 bullet points |
"The most valuable skill I developed as a senior engineer was not learning a new framework — it was learning to write a one-page document that saved weeks of misalignment." — Staff Engineer at a FAANG company
Design Documents / RFCs
A design document (or RFC — Request for Comments) is a written proposal for a significant engineering change. It exists to align the team before anyone writes code, saving days or weeks of wasted effort.
Why They Matter
- They surface disagreements early, when changes are cheap (words on a page, not code in production).
- They create a searchable record of why decisions were made — invaluable six months later.
- They force the author to think through edge cases, alternatives, and risks before building.
- They give the whole team a voice, not just whoever is loudest in a meeting room.
Complete Design Document Template
Common Mistakes
How to Get Buy-In Before Writing Code
The informal chat is critical — it surfaces major objections before you invest in a full document.
Writing Good Pull Requests
A pull request is a communication artifact, not just a code delivery mechanism. A great PR tells a story: what changed, why, and how the reviewer should approach it.
Title Conventions
| Pattern | Example |
|---|---|
type(scope): description | fix(auth): prevent token refresh race condition |
[TICKET-123] description | [PAY-456] Add retry logic to payment webhook handler |
| Imperative mood | "Add feature" not "Added feature" or "Adding feature" |
PR Description Template
PR Size Guidelines
| Size | Lines Changed | Review Quality | Recommendation |
|---|---|---|---|
| Small | < 200 | Thorough | Ideal. Ship often. |
| Medium | 200 – 400 | Good | Acceptable for features. |
| Large | 400+ | Superficial | Split if possible. Nobody carefully reviews 1,000 lines. |
Self-Review Checklist Before Requesting Review
- Re-read every line of the diff yourself — you will catch obvious issues.
- Run the test suite locally. Do not waste a reviewer's time with a broken build.
- Remove debug code:
console.log,print(), commented-out blocks. - Check for TODOs you forgot to resolve.
- Verify the PR description is complete and links to the right ticket.
- Add screenshots for any UI change, including before and after.
Writing Good Tickets / Bug Reports
A bad ticket wastes two people's time: the author's (who will be asked clarifying questions) and the assignee's (who cannot start without those answers). A good ticket is self-contained.
The "What + Why" Rule
Every ticket must answer two questions: What needs to happen, and Why it matters. Without "why," engineers cannot make trade-off decisions.
Bad Ticket
Title: Fix login
Description: Login is broken. Please fix.
Good Ticket
Title: [BUG] Login fails for users with "+" in email address
Description: Users with "+" in their email (e.g., john+test@gmail.com) get a 400 error when logging in. This affects ~2% of our user base (~1,200 users).
Bug Report Template
Severity Guide
| Severity | Impact | Example | Response |
|---|---|---|---|
| Critical | System down, data loss, security breach | All users locked out of accounts | Drop everything |
| High | Major feature broken, no workaround | Payments failing for 10% of users | Fix this sprint |
| Medium | Feature broken, workaround exists | Search filters reset on page refresh | Prioritize in backlog |
| Low | Cosmetic, minor inconvenience | Tooltip text has a typo | Fix when convenient |
Technical Writing Style
Good technical writing is invisible. The reader absorbs the information without noticing the writing itself. Here are the rules that make that happen.
The Core Rules
{"error": "rate_limit_exceeded", "retry_after": 30}" is concrete and useful.
The Inverted Pyramid: Conclusion First
Journalists put the most important information first. Engineers should too. Your reader may stop at any point — make sure they get the key takeaway even if they only read the first paragraph.
Before & After: Rewriting Bad Paragraphs
Before
It should be noted that the implementation of the caching layer, which was originally designed to improve performance, has been observed to cause issues in certain edge cases where the data that is cached becomes stale due to the fact that the invalidation mechanism is not triggered when updates occur through the admin interface, which is a pathway that was not considered during the initial design phase.
After
The cache does not invalidate when data is updated through the admin interface. This causes stale data in certain edge cases. We missed this pathway during initial design.
Fix: Add cache invalidation hooks to the admin update endpoints.
Before
In order to ensure that the system is able to handle the expected load, it is recommended that we should proceed with the implementation of a rate limiting solution that would be capable of restricting the number of API calls that can be made by a single user within a given time period.
After
We need rate limiting. Without it, a single user can overwhelm the API.
Proposal: Limit each user to 100 requests per minute. Return 429 Too Many Requests with a Retry-After header.
Documentation
Documentation is a product. It has users (your teammates, future-you, new hires) and it can be good or bad at serving them. Treat it with the same care as production code.
The Documentation Quadrant
All documentation falls into one of four categories. Each serves a different need and has a different structure.
README Anatomy
- Project name + one-line description. What is this? A reader should know within 5 seconds.
- Quick start. The minimum steps to get it running. Clone, install, run — three commands or fewer.
- Prerequisites. Runtime versions, OS requirements, required environment variables.
- Usage examples. The most common use cases with copy-paste code.
- Configuration. Table of config options, defaults, and descriptions.
- Architecture overview. Brief description + link to detailed docs for larger projects.
- Contributing. How to set up the dev environment, run tests, submit PRs.
- License.
Architecture Decision Records (ADRs)
An ADR captures a single architectural decision and its context. They are short (one page), immutable (you write a new ADR to supersede an old one), and numbered.
Runbook Template
A runbook is a step-by-step guide for handling operational tasks or incidents. Write it for the on-call engineer at 3 AM who has never seen this system before.
| Section | Content |
|---|---|
| Trigger | When does this runbook apply? (e.g., "Alert: payment_queue_depth > 1000") |
| Impact | What happens if we do nothing? (e.g., "Payments queue indefinitely, users see timeouts") |
| Diagnosis Steps | How to confirm the issue and determine root cause |
| Resolution Steps | Exact commands to run, in order, with expected output |
| Escalation | Who to contact if resolution steps don't work |
| Post-Incident | What to document, follow-up actions |
API Documentation Checklist (OpenAPI / Swagger)
- Every endpoint: method, path, summary, description
- Request parameters: name, type, required/optional, constraints, example
- Request body: schema with examples for every content type
- Response codes: all possible status codes, not just 200. Include 400, 401, 403, 404, 422, 429, 500
- Error format: consistent error schema with
code,message,details - Authentication: how to authenticate, token format, where to pass it
- Rate limits: documented per-endpoint if they vary
- Pagination: format (cursor vs offset), default/max page sizes
Technical Presentations
A great technical presentation is not about showing everything you know. It is about choosing the one thing your audience needs to understand and making it impossible to misunderstand.
The SCR Structure
Situation → Complication → Resolution. This narrative structure works for everything from a 5-minute demo to a 45-minute conference talk.
The world as it is today → Complication
The problem or tension → Resolution
Your solution + results
Bad Opening
"Today I'm going to talk about our new caching layer. So I started by looking at Redis and..."
Good Opening (SCR)
"Our API p99 latency hit 2 seconds last month [Situation]. Users started churning and our SLA was at risk [Complication]. We added a caching layer that dropped p99 to 200ms — here's how [Resolution]."
The "So What" Test
For every slide, ask: "If I remove this slide, does the audience miss something critical?" If the answer is no, remove it. Ruthlessly. Every slide must earn its place.
Rule of Three
People remember three things, not seven. Structure your talks around three key points, three benefits, three steps. "Our new system is faster, cheaper, and easier to maintain."
Presenting to Different Audiences
| Audience | Focus On | Avoid | Slide Count (30 min) |
|---|---|---|---|
| Engineers | Architecture, trade-offs, code samples, benchmarks | Marketing language, hand-waving | 15-25 |
| Product/Design | User impact, UX flow, timelines | Implementation details | 10-15 |
| Executives | Business impact, cost, risk, 2-3 metrics | Technical depth | 5-8 |
Demos That Don't Fail
- Record a backup. Have a screen recording ready. Technology fails at the worst moments.
- Use a dedicated demo environment. Never demo on production or your local machine with 47 Chrome tabs.
- Script every step. Write down each click and keyboard input. Practice the exact sequence 3+ times.
- Pre-load data. Don't type during the demo if you can avoid it. Have test accounts, sample data, and URLs ready.
- Zoom in. Increase font size to at least 18px. The person in the back row cannot read 12px code.
Explaining Complex Topics
The Feynman Technique for Engineers
- Write the concept name at the top of a blank page.
- Explain it as if teaching someone who knows nothing about it. Use simple words. No jargon.
- Identify gaps. Where did you struggle? Where did you hand-wave? Go back and learn those parts.
- Simplify and use analogies. If you cannot explain it simply, you do not understand it deeply enough.
The "5 Levels" Method
Practice explaining the same concept at five levels of complexity. This builds mental flexibility and prepares you for any audience.
| Level | Audience | Example: Explaining a Database Index |
|---|---|---|
| 1 | Child | "It's like the index at the back of a book — instead of reading every page to find what you want, you look up the page number." |
| 2 | Teenager | "When a database searches for data, it normally checks every row. An index is like a sorted shortcut that lets it jump to the right row immediately." |
| 3 | Undergrad | "An index is a B-tree data structure maintained alongside the table. It maps column values to row pointers, reducing search from O(n) to O(log n)." |
| 4 | Grad student | "Indexes trade write performance and storage for read performance. Covering indexes can serve queries entirely from the index without touching the heap. The query planner decides when to use an index based on selectivity." |
| 5 | Expert | "Let's discuss partial indexes, expression indexes, the impact of index bloat on vacuum, and how the planner estimates costs using pg_statistic histograms." |
Layered Explanation: High-Level First
Start with the 30,000-foot view. Let the listener ask for more detail. Do not start at the bottom.
Using Analogies Effectively
Meetings That Don't Suck
When to Meet vs. Go Async
| Use a Meeting | Go Async |
|---|---|
| Brainstorming — ideas build on each other in real-time | Status updates — write them down |
| Sensitive conversations — feedback, conflict | Information sharing — send a doc |
| Decision with debate — when there's real disagreement | Decisions with consensus — "any objections?" in Slack |
| Relationship building — new team, 1:1s | Reviews — code review, doc review |
Meeting Types & How to Run Them
Decision Meeting
- Share options + recommendation before the meeting
- Time: 30 min max
- Goal: leave with a documented decision
- Invite: only decision-makers (3-5 people)
Brainstorm
- No judging ideas during generation
- Time: 45-60 min, with a timer
- Goal: a prioritized list of ideas
- Invite: diverse perspectives (5-8 people)
Information
- Ask: "Could this be an email?" (Usually yes.)
- Time: 15 min max
- Goal: shared understanding
- Invite: make it optional
Agenda Template
How to Decline Meetings Politely
- "I'd love to join, but I'm in a focus block. Could you share notes afterward?"
- "I don't think I can add value here — would it be OK if I skip and catch up async?"
- "Could we try this as a shared doc first? If we still need a meeting, I'm happy to join."
Status Updates & Standup
The Formula: Done / Doing / Blocked
A standup update is not a diary of your day. It is three things:
Async Standup Example
Weekly Status Email Template
Communicating Delays Early
The worst time to mention a delay is at the deadline. The best time is the moment you realize it might happen. Use this formula:
- State the delay. "The payment dashboard will miss our Friday target."
- Explain why. "The Datadog API doesn't support the query type we need. I need to build a custom aggregation."
- Give a new estimate. "New ETA: next Wednesday."
- Offer options. "I can ship a simplified version by Friday if that helps, and add the full feature next week."
Code Review
Code review is a conversation, not an inspection. The goal is not to prove you are smarter than the author. It is to collaboratively improve the code and share knowledge.
How to Give Good Reviews
- Ask questions instead of making statements. "What happens if this list is empty?" is better than "You forgot to handle the empty case."
- Suggest, don't demand. "Consider using a map here for O(1) lookup — what do you think?" not "Use a map."
- Praise good code. "Nice use of the builder pattern here — makes the test really readable." Positive feedback reinforces good practices.
- Focus on what matters. Logic errors, security issues, and missing tests matter. Semicolons and import order do not (let your linter handle those).
- Explain the why. "This could cause a memory leak because the listener is never removed" teaches more than "Remove this listener."
Review Comment Taxonomy
Prefix your comments to set expectations:
| Prefix | Meaning | Must Address? | Example |
|---|---|---|---|
| blocking | Must fix before merge | Yes | "blocking: This SQL query is vulnerable to injection." |
| suggestion | Improvement idea | No | "suggestion: A switch statement might be clearer here." |
| nitpick | Minor style preference | No | "nitpick: I'd name this userCount instead of cnt." |
| question | Need more context | Respond | "question: Why do we retry 5 times here? Is there a reason for that specific number?" |
| praise | Positive feedback | — | "praise: Great error handling here. Love the structured error types." |
How to Receive Reviews
- Don't take it personally. The reviewer is critiquing code, not you. Separate your identity from your code.
- Explain your reasoning. If you disagree, explain why — with data, links, or benchmarks, not feelings.
- Know when to push back. Not every suggestion is correct. It's OK to say "I considered that, but chose this approach because..." and hold your ground.
- Thank the reviewer. Especially for thorough reviews. A quick "Thanks for the detailed review!" costs nothing and builds goodwill.
The "Sandwich" Doesn't Work — Be Direct but Kind
The compliment sandwich (praise → criticism → praise) trains people to brace for criticism whenever they hear a compliment. Instead:
- Be direct: state the issue clearly.
- Be kind: assume the author is competent and had reasons.
- Be constructive: suggest how to fix it.
Sandwich (Avoid)
"Great PR overall! One small thing though — this has a race condition that could corrupt data. But really nice work!"
Direct & Kind
"blocking: There's a race condition here — if two requests hit this endpoint simultaneously, both could read the same counter value before either writes. Consider using a database lock or atomic increment."
Commit Messages
Why Good Messages Matter
Six months from now, someone will run git blame on a confusing line and find your commit. A good message answers "why was this change made?" A bad message forces them to reverse-engineer the entire diff.
Conventional Commits
| Type | When | Example |
|---|---|---|
feat | New feature | feat(auth): add OAuth2 login with Google |
fix | Bug fix | fix(cart): prevent negative item quantities |
refactor | Code change that neither fixes nor adds | refactor(api): extract validation middleware |
docs | Documentation only | docs: update API rate limit section |
test | Adding or updating tests | test(payment): add integration tests for refunds |
chore | Tooling, CI, dependencies | chore: upgrade Node to 20 LTS |
perf | Performance improvement | perf(search): add index on users.email column |
Anatomy of a Great Commit
Bad
fixed stuff
wip
update
changes
asdf
Good
fix(auth): prevent token refresh race condition
Two concurrent requests could both see an expired token and both attempt to refresh it, causing one to fail with a 401. This adds a mutex around the refresh logic so only the first request refreshes and subsequent ones wait for the result.
Closes #456
Key rules: imperative mood ("add" not "added"), what + why in the body, reference the ticket.
Naming Things
"There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton
The "Read It Aloud" Test
If you read your code aloud and it sounds like English, the names are good. If it sounds like an alien language, rename things.
Fails Test
if (proc(u, d, 3))
"If proc u d three..." — meaningless.
Passes Test
if (isUserAllowedToDelete(user, document, maxRetries))
"If is user allowed to delete user document max retries" — makes sense.
Naming Patterns
| Pattern | Use For | Examples |
|---|---|---|
isX / hasX / canX | Booleans | isActive, hasPermission, canEdit |
getX / fetchX | Retrieving data | getUser, fetchOrders |
setX / updateX | Modifying state | setTheme, updateProfile |
handleX / onX | Event handlers | handleClick, onSubmit |
parseX / formatX | Data transformation | parseCSV, formatCurrency |
toX / fromX | Type conversion | toJSON, fromDTO |
validateX / checkX | Validation | validateEmail, checkInventory |
createX / buildX | Object creation | createOrder, buildQuery |
Terrible vs. Great Names
| Terrible | Great | Why |
|---|---|---|
data | userProfiles | Says what the data is |
temp | unprocessedOrders | "Temp" conveys nothing |
flag | isEmailVerified | What flag? For what? |
doStuff() | sendWelcomeEmail() | Names the action precisely |
Manager | OrderProcessor | "Manager" is a code smell — too vague |
list2 | filteredResults | Numbers in names mean you need better names |
x | retryCount | Single letters only for loop counters and coordinates |
Code Comments
When to Comment: Why, Not What
The code already says what it does. Comments should explain why — the intent, the context, the business reason, the non-obvious constraint.
What (Useless)
// Increment counter by 1counter += 1;
Why (Useful)
// Retry count must not exceed 3 per payment provider SLAcounter += 1;
Comment Types
| Type | Purpose | Example |
|---|---|---|
| Doc comment | Public API documentation | /** Creates a new user. Throws if email exists. */ |
| Why comment | Explains non-obvious reasoning | // Using insertion sort here because n < 10 always |
| TODO | Known missing work | // TODO(alice): Add pagination when list exceeds 1000 |
| HACK/FIXME | Known technical debt | // HACK: Workaround for upstream bug #789, remove after v3.2 |
| Context | External links, RFCs | // See RFC 7231 Section 6.5.1 for 400 status semantics |
Self-Documenting Code: It's a Spectrum
The idea that "good code doesn't need comments" is a dangerous half-truth. Good naming and small functions reduce the need for comments. They do not eliminate it.
Code that always needs comments, no matter how clean:
- Regex patterns —
// Matches ISO 8601 dates with optional timezone - Workarounds for bugs in external libraries or APIs
- Performance optimizations that sacrifice readability
- Business rules that are not obvious from the domain ("accounts inactive for 90 days are purged per legal requirement X")
- Concurrency logic — why this lock, why this order
Managing Up
Managing up is not about sucking up. It is about building an effective working relationship with your manager so they can support you, shield you from distractions, and advocate for your career.
The No-Surprises Rule
Your manager's worst nightmare is being blindsided in a meeting. Keep them informed of:
- Risks — "This timeline is tight; if the API integration takes more than 3 days, we'll miss the deadline."
- Blockers — escalate if something is stuck for more than a day.
- Wins — don't wait for the performance review. Share good news as it happens.
- Mistakes — own them early. "I introduced a regression. Here's what happened and here's how I'm fixing it."
1:1 Structure
Your 1:1 is your meeting, not your manager's status update. Come with an agenda.
Making Your Work Visible (Without Bragging)
- Send weekly summaries. A 5-line email with shipped items, in-progress work, and blockers.
- Present your work. Volunteer for demo days, tech talks, or sprint reviews.
- Write things down. Design docs, post-mortems, and wiki pages carry your name and persist.
- Attribute team wins. "We shipped X" (not "I shipped X") builds trust and makes you look like a leader.
Giving & Receiving Feedback
The SBI Model
Situation → Behavior → Impact. This structure keeps feedback specific, objective, and actionable.
"In yesterday's sprint review..." → Behavior
"...you interrupted Carol twice while she was presenting..." → Impact
"...which made it hard for her to finish her point and seemed to frustrate her."
SBI works for positive feedback too: "In the incident response [S], you created a clear timeline and kept everyone calm [B], which helped us resolve it in 30 minutes instead of hours [I]."
Radical Candor: The 2x2
The goal is the top-right quadrant: you care about the person and you tell them the truth. Most engineers default to "Ruinous Empathy" — staying quiet to avoid discomfort while the person's work suffers.
Receiving Tough Feedback Without Being Defensive
- Listen fully. Do not interrupt. Do not start composing your rebuttal while they are talking.
- Say "Thank you." Even if it stings. Thanking someone for feedback makes them more likely to give it again.
- Ask clarifying questions. "Can you give me a specific example?" "What would good look like?"
- Take time to process. "I appreciate this. Let me think about it and follow up." You do not need to respond immediately.
- Separate signal from noise. Not all feedback is correct. But look for the kernel of truth even in poorly delivered feedback.
Saying No
Why Engineers Struggle With "No"
- Helpfulness bias — we want to be the person who solves problems.
- Fear of conflict — "no" feels confrontational.
- Lack of prioritization — if everything is important, you can't say no to anything.
- Imposter syndrome — "If I say no, they'll think I'm not capable."
Practical Scripts
Influence Without Authority
The Five Pillars of Influence
- Reliability. Do what you say you will do, every time. This is the foundation. Without trust, no one listens to your ideas.
- Lead by example. Don't complain about documentation — write better documentation. Don't complain about code quality — write cleaner code and share why.
- Write proposals, not complaints. "Our deploy process is slow" achieves nothing. "Here's a proposal to reduce deploy time from 45 min to 10 min, with a proof of concept" gets attention.
- Build allies. Have coffee chats with engineers on other teams. Understand their priorities. When you need cross-team support, you will already have relationships.
- Make others successful. Help a teammate debug a tricky issue. Review PRs thoroughly and quickly. The person you help today becomes your advocate tomorrow.
Cross-Team Collaboration Pattern
Instead of "We need your team to build an API endpoint for us," try: "We'd like to integrate with your user service. We've drafted the API contract and can implement the client. Would your team be open to reviewing the contract and adding the endpoint? We estimate it's ~2 hours of work on your side."
Conflict Resolution
Technical Disagreements
When two engineers disagree on a technical approach, the goal is the best outcome for the project — not winning the argument.
Interpersonal Conflicts
- Assume good intent. Most conflicts stem from miscommunication, not malice. "They probably didn't realize how that came across."
- Address directly. Talk to the person, not about them. A private 1:1 resolves most issues. "Hey, I wanted to talk about what happened in the review — I felt dismissed when my suggestion was shut down without discussion."
- Focus on behavior, not character. "When you rewrote my code without discussing it" (behavior) vs. "You're a control freak" (character).
- Escalate correctly. If direct conversation fails, involve your manager. Not as a weapon ("tell them to stop") but as a mediator ("I need help resolving a communication issue").
Async Communication
Async-first communication respects everyone's time and focus. A well-written async message replaces a meeting. A poorly written one generates three follow-up messages and a meeting anyway.
Writing Messages That Don't Need Follow-Up
Every async message should be self-contained. The reader should be able to understand and act without asking a single clarifying question.
Needs Follow-Up
"Hey, can you check that thing from earlier? I think there's an issue."
Which thing? What issue? Earlier when?
Self-Contained
"@alice, the payment webhook handler (payment-service/webhooks.py, line 42) is returning 500 for Stripe's `charge.succeeded` events. I checked the logs — it's a null pointer on `event.data.metadata`. Could you look at this? I think the null check on line 45 needs to handle missing metadata."
Slack / Teams Etiquette
| Do | Don't |
|---|---|
| Use threads for discussions | Reply in the main channel — it's noisy |
| Front-load the ask: "Question: Can we deploy Friday?" | Send "Hi" and wait for a response before asking |
| Use emoji reactions for acknowledgment | Reply "OK" / "Thanks" / "Got it" (noise) |
| Set your status when in focus mode | Expect instant replies from others |
| Move to a call if messages go back and forth 3+ times | Debate complex topics in a text thread |
Use @here sparingly for time-sensitive items | Use @channel in a 200-person channel for a non-urgent question |
When to Go Sync
- You have gone back and forth 3+ times without resolution.
- The topic involves nuance, emotion, or ambiguity that text cannot convey.
- Someone is blocked and needs an answer urgently.
- You are giving sensitive feedback — never do this over chat.
Writing Effective Emails
Subject Line Formula
Your subject line determines whether the email is opened, ignored, or lost in a sea of unread messages.
| Pattern | Example | Why It Works |
|---|---|---|
[Action Required] | [Action Required] Approve Q2 infrastructure budget by Friday | Clear urgency and ask |
[FYI] | [FYI] New on-call rotation starts Monday | No action needed — read when convenient |
[Decision Needed] | [Decision Needed] PostgreSQL vs MySQL for new service | Signals a decision point |
[Blocked] | [Blocked] Need prod access to deploy payment fix | Signals urgency |
BLUF: Bottom Line Up Front
Military communication puts the conclusion first. Engineers should too. Don't bury the lead.
Buried Lead
"Hi team, as you know we've been working on the payment migration for the past few weeks. We encountered some issues with the Stripe API integration, specifically around webhook handling. After investigating several options and discussing with the Stripe support team, we realized that..."
(5 paragraphs later)
"...so we need to delay the launch by one week."
BLUF
"The payment migration launch is delayed by one week, from March 28 to April 4."
Reason: Stripe's webhook API does not support idempotency keys the way we expected. We need 3-5 extra days to implement our own deduplication layer.
Impact: No user-facing impact. The old system continues to work.
Action needed: None — just informing you.
Email Rules
- One ask per email. If you need two things, send two emails. Emails with multiple asks get partially answered.
- The 5-sentence rule. If your email is longer than 5 sentences, consider whether it should be a document with the email as a summary.
- CC with intention. CC = "for your awareness." To = "I need you to do something." BCC = only for large distribution lists.
- When NOT to email: urgent issues (use Slack/call), sensitive feedback (do it face-to-face), complex technical discussions (use a document).
Remote Collaboration
Documentation as Communication
In a remote team, if it's not written down, it didn't happen. Every decision, every architectural choice, every process should live in a searchable, linkable document — not in someone's memory.
Timezone-Aware Scheduling
- Publish your working hours in your Slack profile, calendar, and team wiki.
- Rotate meeting times so the same timezone doesn't always get the inconvenient slot.
- Record important meetings for those who can't attend live.
- Use timezone-explicit language: "3 PM UTC" not "3 PM" (which 3 PM?).
- Leave a handoff note at the end of your day for teammates in later timezones.
Combating Video Call Fatigue
Building Rapport Remotely
- Virtual coffee chats: 15-minute casual 1:1s with no agenda. Just get to know people.
- Start meetings with 2 minutes of small talk. Don't jump straight to business.
- Use your real name and face as your profile picture. Avatars create distance.
- Celebrate wins publicly in team channels. "@alice shipped the new onboarding flow — huge improvement!"
- Over-communicate context. In an office, tone and body language fill gaps. Remotely, words are all you have. Be explicit about intent: "Not urgent, just curious" or "I'm not criticizing the approach, just trying to understand the trade-off."
Self-Promotion for Introverts
Doing great work is necessary but not sufficient for career growth. If nobody knows about your work, it doesn't count toward promotions, raises, or opportunities. This is unfair, but it is reality.
The Brag Document
Keep a running document of your accomplishments. Update it weekly. Use it for performance reviews, resume updates, and 1:1s.
Weekly Wins Log
Every Friday, spend 5 minutes answering: "What did I accomplish this week that I'm proud of?" Write it down. Over a year, this becomes a goldmine for performance reviews.
Sharing Learnings (Without Feeling Weird About It)
- Write internal wiki posts. "How I debugged the Kafka consumer lag" is useful and makes your expertise visible.
- Give brown-bag talks. 15 minutes on something you learned recently. Low stakes, high visibility.
- Write blog posts. External visibility builds your reputation and helps you when job searching.
- Contribute to team retrospectives. Sharing what you learned from a mistake shows maturity and growth.
The Introvert Advantage
Introverts often excel at written communication, deep analysis, and thoughtful responses. Lean into these strengths:
- Write design docs that speak for you
- Send thorough code reviews that demonstrate expertise
- Share insights in async channels where you can compose your thoughts
- Prepare thoroughly for meetings so your few contributions are high-impact
Technical Leadership Communication
Architecture Reviews
As a tech lead, you review designs more than you write code. Your job is to ask the right questions, not to have all the answers.
- "What happens when this fails?" — every system fails eventually.
- "How do we know this is working in production?" — observability should be designed in, not bolted on.
- "What's the migration path?" — you cannot stop the world to deploy a new system.
- "How does this scale to 10x current load?" — design for realistic growth, not infinite scale.
- "What's the simplest version we could ship first?" — reduce scope to reduce risk.
Cross-Team Alignment
Saying "I Don't Know"
One of the most powerful things a senior engineer can say. It models intellectual honesty, prevents bad decisions based on guesses, and invites others to contribute.
Insecure
"I think maybe... well, it's probably... I mean, in my experience, it should work, I guess."
Confident
"I don't know the answer to that. Let me investigate and get back to you by tomorrow with data."
Mentoring Communication Patterns
| Instead Of | Try | Why |
|---|---|---|
| "Just do X" | "What approaches have you considered? What trade-offs do you see?" | Teaches thinking, not just doing |
| Fixing their code | "What would happen if the input is null here?" | Guides them to discover the issue |
| "That's wrong" | "Interesting approach. Have you considered how it handles [edge case]?" | Preserves their confidence |
| Giving the answer immediately | "Where would you look to figure that out?" | Builds self-sufficiency |
Building Consensus on Technical Decisions
- State the problem clearly. "We need to decide how to handle authentication for our new microservice."
- Present options with trade-offs. Not your preference — a balanced analysis. Use a comparison table.
- Name your recommendation and why. "I recommend Option B because it balances security and implementation speed."
- Invite disagreement explicitly. "What am I missing? Does anyone see a problem with this approach?"
- Set a decision deadline. "If no objections by Friday, we'll go with Option B."
- Document the decision and rationale. Write an ADR. Include the rejected alternatives and why they were rejected.