How much time does your team waste decoding the intent behind a review comment?
"You should refactor this." Is it blocking? A suggestion? A passing remark? The developer hesitates, the reviewer gets frustrated that their feedback is ignored. The MR drags on. The problem isn't technical: it's a communication problem.
Conventional Comments offers a simple solution: make the implicit explicit. A structured format that clarifies the intent, criticality, and context of each comment.
In this article: the methodology, before/after examples, and the metrics we observed after adoption by a development team.
Understanding the Problem
How the Merge Request Became Essential
To understand current challenges, a historical detour is in order.
1980s–2000s: the centralized era. With RCS then CVS, one file = one lock. Sequential work was the norm, reviews happened via emails and meetings. SVN improved things with atomic commits, but branches remained slow and painful. Result: no culture of systematic review.
2005: the Git revolution. Instant branches, 100% local work, intelligent merges. The paradigm shifted. Feature branches exploded, parallel work became massive. But with this freedom came a new risk: loss of code coherence.
2010+: the rise of platforms. GitHub, GitLab, Bitbucket introduced the Pull/Merge Request. Discussion in the code, decision history, automated CI, formal approvals. The MR became the mandatory gateway for any change.
Key insight: The easier branches become to create, the more critical structured review becomes.
Today, teams collaborate asynchronously. Discussions in merge requests are the living documentation of technical decisions. Without face-to-face interaction, tone and intent are easily misinterpreted. Reviews also serve as a learning vector: onboarding juniors, challenging senior approaches.
The Communication Gap
Yet traditional review comments suffer from recurring patterns that sabotage their effectiveness:
Emotional friction: "This code won't work in production" without context generates anxiety, not collaboration. The developer becomes defensive.
Ambiguity on criticality: "Consider extracting this logic": blocking or optional? The developer wastes time on non-critical items, or ignores important feedback. Frustration on both sides.
Endless discussions: Without a clear framework, a simple question escalates into an endless thread. The MR stays blocked for days.
Lack of recognition: 12 critical comments, 0 praise. Good work goes unnoticed. Motivation erodes.
False consensus: To avoid conflict, some approve with a superficial "LGTM". Technical debt accumulates silently.
The Solution: Conventional Comments
Before:
"You should extract this logic"
After:
suggestion (non-blocking): Extract the validation logic
Instantly: it's a suggestion, it's not blocking. No more ambiguity.
Format
Each comment follows this structure:
<label> (decoration): <subject>
[optional discussion]Element | Role | Example |
label | Type of comment |
|
decoration | Modifies criticality (optional) |
|
subject | The point raised |
|
discussion | Context, reasoning (optional) | Detailed explanation, links, etc. |
Labels
The specification defines several labels. Here are the ones we use:
Label | Usage | Blocking | Example |
praise | Highlight positive aspects | No |
|
question | Understand a decision | No (awaits response) |
|
suggestion | Propose an improvement | No (author decides) |
|
issue | Necessary change | Yes (must be resolved) |
|
thought | Share an idea or reflection | No |
|
nitpick | Trivial change | No |
|
todo | Small necessary and trivial task | Yes (must be resolved) |
|
Decorations
Decorations adjust the criticality of a label:
Decoration | Meaning | Example |
(non-blocking) | Merge can proceed, author decides |
|
(if-minor) | Fix unless effort is disproportionate |
|
Concrete Examples
Let's revisit the problems identified above and see how Conventional Comments solves them.
Ambiguity on Criticality
const data = await fetchUser(userId);
if (!data) {
throw new Error('User not found');
}Before
"You should use a Result type instead of throw"
After
suggestion (non-blocking): Consider a Result type for error handling
With a Result<T, E>, this would avoid try/catch and make errors explicit
in the type system. Not necessary for this MR if the rest of the codebase
uses throw.
Reference: https://swan-io.github.io/boxed/result→ The intent is clear: non-blocking suggestion, the developer decides.
Emotional Friction
const users = [/* ... */];
users.forEach(user => {
sendEmail(user.email, template);
});Before
"This code will explode in prod with 10,000 users. Not good at all!"
After
issue: Risk of email API overload with large volumes
With 10k users, this synchronous approach will timeout.
Suggestions:
- Use `p-limit` to limit concurrency (e.g., 10 parallel max)
- Delegate to a queue (AMQP) for async processing
Happy to help, I've already implemented this with p-limit.→ The problem is articulated with solutions, not blame.
Judgment Disguised as Question
async function processOrders() {
const orders = await fetchOrders();
for (const order of orders) {
await processPayment(order);
await sendConfirmation(order);
}
}Before
"Why aren't you using Promise.all()? Do you even understand async in JS?"
After
question: Reason for sequential rather than parallel processing?
I'm wondering if there's a dependency between processPayment and sendConfirmation.
If they're independent, we could improve perf with `Promise.all()`.
If it's intentional (rate limit?), a comment would help document this.→ Curiosity rather than judgment, open dialogue.
Lack of Recognition
Before
An MR receives 12 comments: 10 issues, 2 todos, 0 praise.
After
praise: Excellent architecture with the Repository pattern
The domain/infrastructure separation makes this code highly testable.
The interfaces are clear and strict typing prevents runtime errors.
praise: Comprehensive tests including edge-cases
The timeout and retry tests are particularly valuable.
issue: Potential memory leak in the WebSocket listener
[... other comments ...]→ Balancing criticism and recognition maintains motivation.
Implementation and Measurement
Adopting Conventional Comments isn't enough: you need team rules and tracking to anchor the practice.
Team Rules
Rule | Why |
Sincere praise mandatory | Identify positive aspects. Fake praise erodes trust. |
Only comment author resolves | Prevents premature merges. Dev responds, reviewer validates and resolves. |
Reference the fix commit | "Fixed in abc123" → reviewer verifies directly. |
Document exceptions | Urgent merge? Create a ticket and mention it in the thread. |
Example: notification after fix:
# Reviewer
issue: userId variable not validated
# Developer
Fixed in abc123: added Zod validation with z.string().uuid()
Example: documented exception:
suggestion (non-blocking): Refactor toward hexagonal architecture
@dev: Merging to unblock the release, ticket TECH-456 created.
Tracking Metrics
Three indicators to evaluate adoption:
Metric | Calculation | Target |
Adoption rate | Conventional comments / Total | 80%+ after 3 months |
Praise ratio | Praise / (Issue + Nitpick) | Min 0.2 (1 per 5 critiques) |
Resolution time | Merge date − Creation date | 20-30% reduction |
Expected evolution: Praise increasing, Issues decreasing, Questions stable.
Automation
Manual tracking is tedious. I created a CLI tool to automate collection from the GitLab API.
Example output
╔════════════════════════════════════════════════╗
║ ⏱ MR Resolution Time ║
╚════════════════════════════════════════════════╝
▲
12d┤
11d┤
10d┤
9d┤
9d┤━━━━━━━┓
8d┤ ┃
7d┤ ┃
6d┤ ┃
d 5d┤ ┃ ┏┓
a 5d┤ ┃ ┃┃ ┏━━┓ ┏━━━━━┓
y 4d┤ ┃ ┏━┓ ┃┃ ┃ ┃ ┏━━━━━┓ ┃ ┃
s 3d┤ ┃ ┏━━━━━┛ ┃ ┃┗━━━━━━━━━┛ ┃ ┃ ┃ ┃ ┃ ┏━━━━┓
2d┤ ┃ ┏┛ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃
2d┤ ┃ ┃ ┗━┛ ┗━━━┓ ┃ ┃ ┏┓ ┃ ┃ ┃ ┃
19h┤ ┗━━┛ ┗━━┓ ┃ ┗━┛┗━┛ ┗━━━━━┛ ┃
0 ┤ ┗━┛ ┗━
└┬───────┬──┬┬─────┬─┬─┬┬─┬───────┬──┬───┬──┬─┬─────┬─┬┬─┬─────┬─┬──┬┬────┬▶
11-10 11-1711-20 11-26 12-0112-04 12-0812-11 12-1512-18 12-22
date (utc)
Curve smoothing by applying an Exponentially Weighted Moving Average (EWMA)
▲
6d┤
6d┤━━━━━━━┓
5d┤ ┃
5d┤ ┃
d 4d┤ ┗━━┓
a 4d┤ ┃
y 4d┤ ┗━━━━━━━━┓ ┏━━┓ ┏━━┓
s 3d┤ ┗━┛ ┗━━━━━━━┛ ┗━━━┓
3d┤ ┃
2d┤ ┗━━┓ ┏━━━━━┓ ┏━━━━━┓
2d┤ ┗━┛ ┗━━┓ ┃ ┗━┓ ┏━━━━┓
2d┤ ┗━┛ ┗━━━┛ ┗━
1d┤
19h┤
9h┤
0 ┤
└┬───────┬──┬┬─────┬─┬─┬┬─┬───────┬──┬───┬──┬─┬─────┬─┬┬─┬─────┬─┬──┬┬────┬▶
11-10 11-1711-20 11-26 12-0112-04 12-0812-11 12-1512-18 12-22
date (utc)
╔════════════════════════════════════════════════╗
║ 💬 MR Comments Distribution ║
╚════════════════════════════════════════════════╝
Global Statistics
─────────────────
┌─────────────────────┬────────┐
│ (index) │ Value │
├─────────────────────┼────────┤
│ Total MRs │ 58 │
│ Total Comments │ 966 │
│ Average Comments/MR │ 16.7 │
│ Min Comments/MR │ 3 │
│ Max Comments/MR │ 80 │
│ Median Comments/MR │ 9.5 │
└─────────────────────┴────────┘
Evolution of the number of comments per MR over time (still applying EWMA to smooth the curve)
▲
c 51 ┤
o 47 ┤
m 44 ┤━━━━━━━┓
m 41 ┤ ┃
e 37 ┤ ┃
n 34 ┤ ┗━━┓
t 30 ┤ ┃
s 27 ┤ ┗┓
/ 24 ┤ ┗━━━━━━━┓
M 20 ┤ ┗━┓
R 17 ┤ ┗━━┓ ┏━━━━━━┓
14 ┤ ┗━━━━━━━┛ ┗━━┓ ┏━━━━━━━━━━━━━━━━━━━━━┓
10 ┤ ┗━┛ ┗━━━━━━━
7 ┤
3 ┤
0 ┤
└┬───────┬──┬┬─────┬─┬─┬┬─┬───────┬──┬───┬──┬─┬─────┬─┬┬─┬─────┬─┬──┬┬────┬▶
11-10 11-1711-20 11-26 12-0112-04 12-0812-11 12-1512-18 12-22
date (utc)
╔════════════════════════════════════════════════╗
║ 📋 Conventional Comments Analysis ║
╚════════════════════════════════════════════════╝
Global Overview
───────────────
┌─────────────────────────┬─────────┐
│ (index) │ Value │
├─────────────────────────┼─────────┤
│ Total Comments │ 966 │
│ Conventional Comments │ 736 │
│ Unconventional Comments │ 230 │
│ Adoption Rate │ 76.2% │
└─────────────────────────┴─────────┘
Label Distribution
──────────────────
┌────────────┬────────────┐
│ (index) │ Percentage │
├────────────┼────────────┤
│ Praise │ 1.4% │
│ Question │ 54.6% │
│ Suggestion │ 14.9% │
│ Issue │ 21.3% │
│ Nitpick │ 7.9% │
└────────────┴────────────┘
Praise Ratio
────────────
┌──────────────────────────────────┬────────┐
│ (index) │ Value │
├──────────────────────────────────┼────────┤
│ Praise Count │ 10 │
│ Critique Count (issue + nitpick) │ 215 │
│ Ratio (praise/critique) │ 4.7% │
└──────────────────────────────────┴────────┘Interpretation:
Adoption 76% → majority of the team uses the format
Praise ratio 4.7% → 10 praises for 215 critiques, room for improvement
Questions 54% → high ratio, may be explained by onboarding or skill-building context
ℹ️ Tip: Filter out bots (dependabot, rennovate, ...) to analyze only human reviews.
Challenges and Limitations
The method isn't without pitfalls. Some traps to avoid:
Empty Formalism
The label doesn't replace the explanation. issue: Badly named variable without context remains useless.
→ Always explain the "why": userData would be more explicit than data for the business context.
Generic Praise
praise: Nice fulfills the rule but undermines its spirit. Vague praise erodes trust.
→ Be specific: praise: Excellent use of the Builder pattern for the UserDTO
Non-blocking Abuse
If 90% of comments are (non-blocking), the review no longer adds value.
→ Dare to block when justified.
Initial Resistance
The format may seem bureaucratic at first.
→ Lead by example rather than impose. Visible benefits in others' reviews encourage adoption.
Conclusion
The problem was never technical: it's a communication problem. Conventional Comments doesn't reinvent the review, it makes the implicit explicit.
For developers: no more guessing whether a comment blocks the merge. For reviewers: a framework to structure feedback without seeming condescending. For teams: less friction, more learning.
It's not a silver bullet. The format doesn't replace empathy or judgment. But it's a concrete first step toward more effective and more human reviews.
To get started: try it on your next MR. A suggestion (non-blocking): or a sincere praise: . Observe the reaction.
Resources
This article is inspired by a real implementation of Conventional Comments in a fullstack development team. All examples are based on actual situations encountered, with identifying details modified.