Developer Communication: How to Think, Report and Express Like a Professional

Developer Communication: How to Think, Report and Express Like a Professional

Master the communication skills no bootcamp teaches: status updates, bug reports, PR reviews, postmortems, proposals to management, and Slack messages that get read.

By Omar Flores

Developer Communication: How to Think, Report and Express Like a Professional

A pilot who can fly a plane but cannot talk to air traffic control is a liability, not an asset. The same is true for developers. Technical skill gets you in the cockpit. Communication skill keeps you in the air.

Most developers learn to code. Very few learn to communicate about code. The gap shows up everywhere: in Slack messages that get ignored, in status updates that confuse managers, in bug reports that send teammates in the wrong direction, in proposals that die in a meeting because nobody understood what was being proposed.

This post is about that gap. Not documentation style guides, not grammar rules — those matter, and other posts cover them. This post is about the thinking that precedes the writing: how to frame a problem, who your audience is, what they need from you right now, and how to give it to them in a form they can act on.


The Core Principle: Communication Is a Service

When you write a message, a report, or a document, you are providing a service to a reader. That reader has a job to do. Your communication either helps them do it or it does not.

This reframes the question. Instead of “how do I explain what I did?” ask “what does this person need to know right now to make a decision or take action?” The answer changes depending on who is reading.

A manager asking “how is the authentication feature going?” needs to know:

  • Will it ship on time
  • If not, why not and what is being done about it
  • Whether anything from them is needed

They do not need to know which JWT library you chose or why you refactored the session store.

A teammate asking the same question needs to know:

  • What is done, what is in progress, what is blocked
  • What interfaces they can depend on
  • Whether anything might affect their work

They do not need the business impact framing.

The information is the same. The form is different. Knowing the difference is the entire skill.


Audience Maps

Before writing anything — a message, a report, a comment — answer two questions:

  1. Who reads this?
  2. What decision or action do they need to take after reading it?

Here is a practical map of the people developers communicate with most, and what each one actually needs.

Your Manager / Team Lead

What they care about: Progress, risks, and blockers. They translate your work into something their manager can understand. They want to know if you need something from them.

What they do not care about: Implementation details, library choices, internal debates you already resolved.

Their unspoken question every time they ask for an update: “Will this be done when I said it would be done, and if not, how bad is it?”

What good communication looks like:

“Authentication is on track for Thursday. Login and registration work end-to-end. Password reset is in progress — I expect to finish it tomorrow. No blockers, nothing needed from you.”

What bad communication looks like:

“I’ve been working on the bcrypt integration and refactored the session middleware. I also looked into the JWT approach but decided to go with sessions because of the CSRF implications. Still need to do password reset.”

The second version buries the lead (on track for Thursday), forces the manager to extract the status, and spends time on decisions they did not ask about.

Your Teammates

What they care about: Technical clarity. They want to know what exists, what the interface is, what you are assuming, and what might break their work.

What they do not care about: Business justification, executive framing, or anything that has no consequence for their code.

Their unspoken question: “Does this affect me, and what do I need to know to do my job?”

What good communication looks like (in a Slack message or PR description):

“Refactored the auth middleware. It now accepts context.Context as the first argument — this was needed to support cancellation in the SSO flow. If you call GetCurrentUser() anywhere, you’ll need to update the signature. I’ve updated all the existing call sites in this PR, but let me know if I missed any.”

What bad communication looks like:

“Updated auth middleware to be more robust and follow best practices.”

The second version gives teammates no actionable information. They have to open the PR, read the diff, and figure out the consequences themselves. That is work you transferred to them that you could have done.

Non-Technical Stakeholders (Product, Design, Business)

What they care about: What users can and cannot do, and when that changes. Risk and impact to the product.

What they do not care about: Technical implementation, architecture, system internals. These are not relevant to their job.

Their unspoken question: “How does this affect what we promised users?”

What good communication looks like:

“The new file upload is working. Users can now attach up to 10MB files in JPEG, PNG, or PDF format. We expect to enable it in production next Wednesday. One caveat: the first upload from a new account will be slightly slower (about 3 seconds) while the storage quota is provisioned. That delay goes away on subsequent uploads.”

What bad communication looks like:

“S3 integration is done. Using presigned URLs with multipart upload. Added virus scanning middleware in the pipeline. Still need to tune the Lambda timeout.”

The second version would require a non-technical reader to translate every sentence. They cannot. They will ask you questions you already answered, or worse, they will guess.

Other Developers (in PRs, code reviews, technical discussions)

What they care about: Correctness, clarity, trade-offs, and whether the code is maintainable by someone who is not the author.

What they do not care about: Defensiveness, vague explanations, or walls of justification.

Their unspoken question: “Is this code correct, and can I understand it well enough to maintain it later?”


Status Updates That Work

The status update is the most common developer communication task, and the most commonly done badly. Here is a structure that works in any format — email, Slack, standup, written report.

The Four-Part Structure

1. State (what is true right now) Not what you did, not what you plan to do. What is the current state of the thing.

2. Progress (since the last update) What changed since last time. This is evidence that work is happening.

3. Next (what happens next) The immediate next action or milestone. One item. Not a roadmap.

4. Blockers (what you need, if anything) Only if something is genuinely blocking you. If nothing is blocking you, omit this section entirely. If you include it, say what you need and from whom.

Examples

Daily standup (spoken):

“Payment integration is in review. Yesterday I finished the webhook handling and wrote tests for the failure cases. Today I’m working on the reconciliation job. No blockers.”

Thirty seconds. Complete. Actionable.

Weekly written update:

Payments feature — Week 3

State: Integration complete, in QA testing.

Progress this week: Webhook handling, failure case tests, reconciliation job. All tests passing. QA started Monday.

Next: Address QA findings this week, target staging deployment Friday.

Blockers: Need approval from finance team on the refund policy before we can finalize the refund flow. Waiting on Maria — low urgency, not blocking the main path.


Bug Reports: Giving People What They Need to Fix It

A bad bug report is a problem twice: once when the bug happened, and again when every developer who looks at it has to do detective work that should have been done once by the reporter.

A useful bug report contains exactly what is needed to reproduce, understand, and verify the fix. Not more, not less.

The Bug Report Structure

Title: State the behavior, not your interpretation. “Login fails after password reset” not “Authentication is broken.”

Environment: Where it happened. OS, browser, version, environment (staging/production), relevant configuration.

Steps to reproduce: Ordered, numbered, specific. Not “try to log in” — “go to /login, enter email X, enter password Y, click Submit.”

Expected behavior: What should have happened according to the design or specification.

Actual behavior: What actually happened. Exact error messages, exact values, exact timestamps.

Evidence: Screenshot, log excerpt, network request. The thing that proves the bug exists and helps someone locate it in the system.

Impact: Who is affected, how severely, how often. This helps prioritize.

Hypothesis (optional): If you have a theory about the cause, state it clearly as a hypothesis, not a fact. “I suspect this might be related to the session expiry change in PR #812, but I haven’t confirmed it.”

A Real Example

Bad:

Subject: Login is broken

Hi, the login doesn’t work. I tried to log in and it just shows an error. Can someone fix this ASAP?

Good:

Bug: Login returns 500 after password reset on production

Environment: Production, Chrome 124, macOS 14.4. Reproduced on Firefox as well. Staging is not affected.

Steps to reproduce:

  1. Request a password reset for user test@example.com
  2. Click the reset link in the email
  3. Enter a new password and submit
  4. Attempt to log in with the new password

Expected: Successful login, redirect to dashboard.

Actual: HTTP 500 with message “Internal Server Error.” No further details shown to user.

Logs: ERROR auth.service: session.create: pq: duplicate key value violates unique constraint "sessions_pkey" at 2025-06-15T14:32:01Z

Impact: Affects all users who reset their password after June 14 (when the session schema migration ran). Roughly 12 users affected based on support tickets so far.

Hypothesis: The migration on June 14 may have left duplicate session records. The unique constraint on session ID is now triggering on login, which did not happen before.

The second version tells the engineer where to look (the session schema migration), who is affected (post-June 14 password resets), and provides a hypothesis that could save hours of investigation.


Pull Request Descriptions That Get Good Reviews

A pull request description is the author’s argument for why this code should be merged. A reviewer who has to infer what the PR does, why it exists, and how to test it is a reviewer who will take longer to review it, ask unnecessary questions, or miss real problems.

The PR Description Structure

What this PR does: One sentence. Not a commit log. The human-readable purpose.

Why: The problem being solved or the feature being added. Link to the ticket, but also explain it in prose. The reviewer should not have to open Jira to understand the context.

How: A brief explanation of the approach, especially for non-obvious design decisions. If you chose approach A over approach B, say why. If you know there are trade-offs, name them.

How to test: Step-by-step instructions that let a reviewer run the changed behavior themselves. A reviewer who cannot reproduce the change cannot verify it.

Screenshots or recordings: For anything visual. Always.

Checklist: Tests written, documentation updated, migrations run, no debug logs left.

Example

Bad:

Fixed auth bug

Good:

Fix: Prevent duplicate session on password reset

What: Clears the existing session before creating a new one during the password reset flow.

Why: After the session schema migration on June 14, users who reset their password received a 500 error on subsequent login. The root cause was a unique constraint violation: the old session record was not being deleted before a new one was inserted, resulting in two records with the same session ID.

How: Added a DeleteSessionByUserID call in auth.Service.ResetPassword() before the new session is created. This is safe because the reset link itself invalidates the previous password, making the old session unusable regardless.

Trade-off considered: We could instead handle the conflict with an upsert. I chose delete-then-insert because it is clearer about intent (we want to invalidate the old session) and avoids accidentally updating a session that should be gone.

How to test:

  1. Reset password for any test account
  2. Log in with the new password
  3. Verify: successful login, no 500 error
  4. Verify in DB: only one session record exists for that user

Checklist:

  • Regression test added (TestResetPassword_ClearsOldSession)
  • Tested on staging
  • No debug logs

Code Review Comments That Build, Not Damage

Code review is collaboration, not judgment. The goal is better code and a stronger team — not demonstrating that you spotted something.

The way you phrase a review comment determines whether it results in improvement or defensiveness.

The Three Levels of Review Comments

Level 1 — Must fix: A real problem. Correctness issue, security flaw, performance problem that matters.

Format: State the problem, explain why it matters, suggest a fix.

“This query is not parameterized, which makes it vulnerable to SQL injection. The user-supplied email value is interpolated directly into the string. Use a prepared statement or the ? placeholder instead.”

Level 2 — Should consider: A trade-off or a better approach, but not a blocker.

Format: Ask a question or frame it as a suggestion, not a demand.

“I wonder if caching this result would help here — the DB hit happens on every request to /api/user, which is called frequently. Could be worth measuring. Happy to merge as-is and track this separately if you prefer.”

Level 3 — Nit: Style, naming, minor clarity. Not a blocker. Mark it explicitly.

Format: Label it “nit” so the author knows this is low priority.

“Nit: getUserFromCtx could be userFromContext to match the naming in other middleware files. Up to you.”

What Not to Do

Do not leave comments like these:

  • “This is wrong.” (What is wrong? How should it be fixed?)
  • “Why did you do it this way?” (This sounds like an accusation. Ask “have you considered X?” instead.)
  • “This is unreadable.” (Rewrite it and explain the improvement.)
  • “There’s a better way.” (Then show it.)

A comment with no path forward is feedback without value. It creates friction and erodes trust without improving the code.


Proposing Technical Changes to Non-Technical Management

This is where many developers fail badly. They present a technical proposal to a manager and open with the technical problem. The manager does not have enough context to evaluate the technical problem. They disengage, delay, or defer to someone else.

The solution is to lead with business language and arrive at technical detail only when the business case is established.

The Technical Proposal Structure

The Problem (in business terms): Start with what is currently going wrong or what opportunity exists. Frame it in terms the decision-maker cares about: user experience, reliability, cost, speed of delivery, risk.

The Impact (now and projected): What does this problem cost today? What does it cost in six months if nothing changes? Make it concrete. Not “performance will get worse” but “our p95 response time is 1.8 seconds today; at current growth we project it reaching 4 seconds by Q4, at which point conversion historically drops 12%.”

The Proposed Solution (high level): What you want to do, in one or two sentences. Not the technical design — the outcome. “We want to add a caching layer that serves the most common API responses from memory instead of querying the database every time.”

What This Requires: Time, people, cost. Be honest. If it is two weeks of engineering work plus $200/month in infrastructure, say that.

The Alternative: What happens if we do not do this. Managers need to understand the cost of inaction, not just the cost of action.

The Ask: End with a specific request. “I’d like your approval to start this in the next sprint” or “I need a decision by Thursday so we can plan the next cycle.”

Example

Bad opening:

“We need to migrate from Redis to a distributed cache because our single-node Redis instance is becoming a bottleneck under the load profile we’re seeing, and the current architecture doesn’t support horizontal scaling without major refactoring.”

Good opening:

“We have a reliability risk I want to make you aware of. Roughly 30% of our API traffic goes through a single server that does not have a backup. If that server goes down — which has happened twice this year — the entire app is unavailable until we restart it. I want to propose a change that eliminates that risk and should also make the app 40% faster for users.”

The second version has the manager’s attention. Now you can explain the technical detail — because they have a reason to care.


Postmortems: Writing Without Blame

A postmortem is not a forensic report of who made a mistake. It is a learning document that answers: what happened, why the system allowed it to happen, and what we are changing so it cannot happen the same way again.

The language of a postmortem is the language of system behavior, not personal failure. Not “John pushed a bad migration” but “a migration ran in production without a review step.” Not “the team failed to monitor the disk” but “the alerting system had no rule for disk utilization above 85%.”

Postmortem Structure

Incident summary: What happened, when, how long it lasted, who was affected. Three sentences maximum.

Timeline: Chronological sequence of events. Include when the issue started, when it was detected, what actions were taken, and when it was resolved. Use UTC timestamps. Be specific.

Root cause: The fundamental reason the incident occurred. Distinguish between the immediate trigger (“a process consumed all available memory”) and the root cause (“the memory limit was not configured for this service, and there was no monitoring to detect the growth before it became critical”).

Contributing factors: Other conditions that made the incident possible or worse. There are almost always multiple.

Impact: Users affected, revenue affected, SLA violations, data integrity issues if any.

What went well: Genuinely good things that limited the damage. Detection that worked, escalation that was fast, a runbook that was actually useful.

What went wrong: Systemic gaps, not personal failures.

Action items: Specific, owned, time-bound. Not “improve monitoring” — “Add disk utilization alert at 80% threshold in Datadog — owner: Ana — due: June 20.” Each item should be a change to the system, process, or tooling.

Language That Helps

Use passive voice deliberately to describe system states without assigning blame:

  • “The alert was not triggered” instead of “nobody set the alert”
  • “The rollback procedure was not documented” instead of “Carlos forgot to document it”

Use “we” to describe team actions:

  • “We did not have a staging environment that matched production” instead of “the team failed to set up staging correctly”

This is not dishonesty or avoidance. It is accurate. The problem is almost never a single person — it is a system that allowed the problem to occur. Fix the system.


Slack and Async Messages That Get Responses

Most developer Slack messages fail because they optimize for the sender’s convenience, not the receiver’s ability to respond. Sending “hey, got a minute?” makes the receiver do all the work of finding out what you need, evaluating if they can help, and deciding when to respond. That is work you transferred to someone else.

Principles for Async Messages

Lead with the actual question. Not “hey” or “quick question” — the question itself. The recipient can evaluate whether they can help without a separate exchange.

Give enough context for a complete response. If the person needs to ask you three follow-up questions before they can answer, you have written an incomplete message.

State the urgency honestly. “This is blocking me” means something different from “curious when you have time.” Say which it is. Do not say everything is urgent — people tune it out.

One topic per message thread. A message with three unrelated questions is a message that gets answered partially and then forgotten.

Examples

Bad:

“hey do you have a sec”

Good:

“Quick question about the payment webhook: should we retry on a 422 from Stripe, or treat it as a permanent failure? I’m about to implement the retry logic and want to get it right. No rush — end of day is fine.”

Bad:

“The build is broken and also I had a question about the database migration we’re doing and also wanted to check if you saw the Slack from product about the deadline”

Good:

Three separate messages (or one message that explicitly lists three numbered items, making it easy to respond to each).

The “No Hello” Pattern

Do not send “hi” and wait for a response before stating your question. In async communication, this doubles the time to get an answer. Send the full message immediately. The person will respond when they can, with everything they need to give you a useful answer.


Technical Proposals in Writing

When you need a decision on a technical approach — which database to use, whether to introduce a new dependency, how to handle a cross-cutting concern — a written proposal beats a meeting. It forces clarity, creates a record, and lets reviewers think before responding.

The RFC (Request for Comments) Pattern

Large engineering organizations (Google, Rust, Python, React) use RFCs for major decisions. A simplified version works for teams of any size.

Title: The decision being made. Not “Should we use GraphQL?” but “API layer: REST vs GraphQL for the customer-facing API.”

Context and problem: What situation has created the need for this decision. Be factual and brief.

Proposed solution: Your recommendation, with enough detail to evaluate it. What will change, what will be different, what will not change.

Considered alternatives: The options you evaluated and why you ruled them out. This is the most important part. It shows your thinking, prevents re-litigating decided questions, and builds confidence that the recommendation is not arbitrary.

Trade-offs: What you are giving up with the proposed solution. No option is perfect. Acknowledging trade-offs is a sign of clear thinking, not weakness.

Open questions: Anything you are uncertain about and need input on.

Decision and rationale (filled in after review): Once a decision is made, record it here with the reasoning. This becomes the institutional memory.

Example Structure

Title: Authentication: Session-based vs JWT for the API

Context: We are building a new API that will be consumed by both our
web app and a future mobile app. We need to decide on the
authentication mechanism before starting implementation.

Proposed solution: Session-based authentication using signed
HTTP-only cookies.

Rationale:
- Our current web app already uses sessions; consistency reduces
  complexity
- HTTP-only cookies prevent XSS-based token theft
- Session invalidation is immediate; JWT invalidation requires
  additional infrastructure (revocation lists)
- Mobile apps can use cookie-based auth; this is not web-only

Alternatives considered:
- JWTs: rejected because token invalidation requires a blacklist,
  which adds the complexity we are trying to avoid, and stateless
  auth is not a requirement for this system
- OAuth with external provider: out of scope; this is for internal
  API consumers, not third-party developers

Trade-offs:
- Sessions require server-side storage; we will use Redis for this
- Horizontal scaling requires session replication or sticky sessions;
  we plan to use Redis, which handles this

Open questions:
- Session TTL: 7 days with sliding window, or fixed? Needs product input.
- Should mobile apps share the session store with the web app?

Decision: [to be filled after review]

The Mindset Behind Good Technical Communication

Good technical communication is not a writing style. It is a way of thinking about other people’s time, context, and needs.

Think about what the reader already knows. Do not over-explain to an expert or under-explain to a newcomer. Calibrate.

Think about what the reader will do with this information. Write so that they can do it. Cut everything that does not help them do it.

Think about what question you are answering. The best documents are answers to specific questions. “How do I set up the development environment?” is better than “Development environment documentation.”

Think about what happens after they read it. Will they be able to take the action you need? Will they have the information they need to make a decision? If not, rewrite.

Think about the cost of unclear communication. A vague status update leads to a follow-up meeting. A bad bug report leads to a developer spending half a day investigating the wrong thing. A PR description with no context leads to a delayed review. The cost of unclear writing is always paid — either by the writer who rewrites it, or by the reader who compensates for it with extra effort and questions.


What Separates Senior Developers Communicatively

The technical gap between a junior and a senior developer closes over years of practice. The communication gap often does not close at all because nobody teaches it explicitly.

A senior developer:

  • Adjusts vocabulary and depth for the audience automatically, without being asked
  • Writes updates before they are requested because they understand the manager’s need for predictability
  • Frames problems in terms of impact and risk, not just technical description
  • Acknowledges trade-offs instead of defending a single position
  • Writes PR descriptions that make reviewers faster, not slower
  • Delivers bad news early, with context and a plan, instead of late, as a surprise
  • Asks for feedback on proposals instead of announcing decisions
  • Writes postmortems without protecting themselves or blaming others

None of these require exceptional writing skill. They require the habit of thinking about communication as a professional responsibility, not as an afterthought.

The developer who ships excellent code and explains it poorly will always be outperformed by the developer who ships good code and communicates excellently. Communication multiplies impact. It is how your work becomes visible, how your judgment earns trust, and how your influence extends beyond the code you personally wrote.

Writing is thinking made visible. When your writing is clear, precise, and considerate of the reader, it tells everyone around you that your thinking is the same way. That reputation, once established, opens doors that technical skill alone cannot.

Tags

#best-practices #senior #guide #tips #tutorial #full-stack #backend