Take a typical case: an e-commerce team of 45 developers where every security scan triggers a wave of frustration. Alerts pile up, developers work around them, the security team feels ignored, and the time to fix vulnerabilities is counted in weeks. The problem is neither the tool nor the people: it is the lack of structured Dev-Sec collaboration.
This page is for tech leads, security champions and platform engineers who want to organise this collaboration rather than endure it. It covers three pillars: a blameless culture to handle incidents without a blame hunt, automated feedback loops (IDE, merge request) to shorten the delay between error and fix, and clear communication channels with explicit response times.
What you will learn
- Diagnose the systemic causes of friction between developers and the security team.
- Establish a blameless culture with structured, non-punitive postmortems.
- Design feedback loops that catch problems before merge rather than in production.
- Structure communication channels (asynchronous, synchronous, escalation) with suitable response times.
- Measure collaboration itself, not just the number of vulnerabilities.
This lesson assumes an organisation that is already structured: identified development teams with clear roles, and a security team that exists, even without a formal programme. The Security Champions programme provides the relays on the development side that keep the channels described here alive, and team training (in French) the shared skills foundation.
Where does friction between developers and security come from?
Friction between developers and security teams is not a personality clash: it is a systemic problem, born from goals perceived as contradictory and incompatible communication styles. Both teams aim for the same result, a quality product, but do not measure their success the same way.
Developers are assessed on delivery velocity and user satisfaction, security teams on risk reduction and compliance. Without a collaboration structure, these indicators create natural tension. Two recent surveys put figures on the problem:
| Indicator | Value | Source |
|---|---|---|
| Time lost each week to inefficient processes | 7 hours | GitLab Global DevSecOps Report, published November 2025, 3,266 respondents |
| Organisations running at least one service with a known exploitable vulnerability | 87 % | Datadog State of DevSecOps 2026 |
| "Critical" vulnerabilities that stay critical once runtime context is applied | 18 % | Datadog State of DevSecOps 2026 |
These figures describe the same vicious circle: when almost everything is labelled "critical", nothing really is. Developers get tired, work around alerts, and real vulnerabilities drown in the noise. To break out, you must act at the same time on culture (how we react to errors), processes (how we communicate) and tools (how we automate feedback).
How do you establish a blameless culture?
A blameless culture comes from aviation: pilots can report their errors without fear of sanction, because fear of punishment pushes people to hide problems instead of solving them. The same principle applies to software security.
A blameless culture does not mean that anything goes. It means that the default response to an incident is not "who is responsible?" but "what allowed this error?". You look for a process to improve, not a culprit to punish. The diagram compares the two reactions to the same vulnerability.
In a traditional culture, the guilty developer gets a warning, mostly learns to hide errors better, and the problem happens again elsewhere. In a blameless culture, the same vulnerability triggers a systemic analysis: why did the tests not catch it? Why did code review let it through? Which automated guardrail was missing? The incidents and postmortems lesson details this mechanism beyond security alone.
Putting blameless principles in place
A blameless culture cannot be decreed: it is built through concrete, repeated practices, carried by management and checked in practice. The following steps give the usual order.
-
Officially announce the change of approach
Management explicitly announces that security errors will no longer be treated as individual faults, then repeats the message. A single email is not enough: managers relay it in their teams, and the first real situations must demonstrate the change.
-
Train managers in facilitation
Managers are the first who need to change their reflex. Faced with an incident, their first question becomes "what happened?" and not "who did this?". The training includes role plays and ready-to-use phrasing for difficult moments.
-
Document and share postmortems
Each significant incident leads to a written postmortem, accessible to the whole organisation. This transparency shows that the company owns its errors collectively and learns from each one.
-
Value the discovery of problems
When someone reports a vulnerability they introduced themselves, thank them publicly. This positive reinforcement encourages others to do the same.
-
Measure the cultural change
Regular anonymous surveys check that teams actually perceive the change. Key indicator: the share of developers who feel comfortable reporting a security error.
Structuring blameless postmortems
The postmortem is the central tool of a blameless culture. A postmortem that leads to no action and no learning is just a window-dressing exercise. The YAML template below keeps a factual timeline, an analysis centred on processes and actions assigned to roles, never to people.
# Blameless postmortem template (fictional identifiers)metadata: incident_id: "SEC-2026-042" date: "2026-02-10" severity: "high" facilitator: "payments-security-champion" duration_meeting: "90min"
# Factual timeline: no attribution of faulttimeline: - time: "2026-02-10 09:15" event: "SCA alert on a critical CVE in production" facts: "Vulnerable transitive dependency detected in service-payment" - time: "2026-02-10 09:30" event: "Team notified on the security alerts channel" facts: "Notification delay: 15 minutes" - time: "2026-02-10 10:45" event: "Fix deployed to production" facts: "Dependency updated, time to fix: 90 minutes"
# Systemic analysis: processes, not peopleanalysis: what_happened: | A vulnerable transitive dependency came in with the payment SDK update. The nightly SCA scan detected it 3 days after the merge. why_it_happened: - "The SCA scan ran at night, not on merge requests" - "The dependency was transitive (2 levels), absent from package.json" - "The SDK changelog did not mention the dependency change" what_went_well: - "Automatic detection worked" - "Escalation process followed" - "Fix deployed in under 2 hours"
# Improvement actions: assigned to roles, not individualsaction_items: - id: "AI-001" action: "Add the SCA scan to the merge request pipeline" owner: "platform-team" deadline: "2026-02-17" - id: "AI-002" action: "Alert on any new transitive dependency" owner: "security-team" deadline: "2026-02-24"
metrics: time_to_detect: "72h" time_to_remediate: "90min" customers_impacted: 0The postmortem facilitator reframes any discussion that drifts toward finding a culprit, with phrases such as "let's focus on what made this situation possible" or "which process would have prevented this error?". This role is learned: without it, the best document template turns back into a trial.
How do you shorten feedback loops?
A feedback loop is the time between the moment a developer introduces a security problem and the moment they learn about it. The shorter this delay, the simpler and cheaper the fix: this is the shift-left principle applied to communication.
Think of a thermostat that reacted 24 hours late: impossible to keep a stable temperature. Security is the same: instant feedback allows an immediate fix, late feedback arrives when the developer has lost the context. The diagram places the pipeline's feedback points and their typical delay.
A frequent internal target is to detect 80 % of problems before merge, in the IDE or the merge request. It is a team goal, not a published standard: what matters is the trend, measured by the stage at which each vulnerability is detected.
Feedback in the IDE
The most effective feedback arrives in the development environment, before the first commit. Security extensions flag problems in real time, like a spell checker flags a typo. For VS Code, an extensions.json file recommends the extensions to the whole team:
{ "recommendations": [ "semgrep.semgrep", "snyk-security.snyk-vulnerability-scanner" ]}The configuration favours relevance over exhaustiveness: a developer flooded with false positives turns the extension off. Start with high-confidence rules, hide low severity, and exclude code that never reaches production. The keys below are the ones declared by the Semgrep (semgrep.scan.*) and Snyk (snyk.severity) extensions:
{ "semgrep.scan.configuration": ["p/security-audit", "p/owasp-top-ten"], "semgrep.scan.exclude": ["tests", "vendor"], "snyk.severity": { "critical": true, "high": true, "medium": true, "low": false }}Feedback on the merge request
The second feedback point is the merge request, the last chance to catch a problem before the main branch. Feedback there must be contextual: results show up in the merge request security widget rather than in a 50-line report, and only cover what the merge request introduces.
The GitLab CI job below runs Semgrep in differential mode: --baseline-commit only reports results absent from the base commit, and --gitlab-sast produces the report GitLab displays. Tested with Semgrep 1.178.0: on a repository containing an old and a new defect, the full scan reports both, the differential scan only the new one, in GitLab SAST format 15.0.4.
semgrep_mr: stage: test image: semgrep/semgrep:1.178.0@sha256:32e459968daabe7ab86968184a29109b9564aa00392401156f9788452b42786b rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" variables: GIT_DEPTH: "0" # full history: the base commit must be present script: - git config --global --add safe.directory "$CI_PROJECT_DIR" - semgrep scan --config p/default --metrics off --baseline-commit "$CI_MERGE_REQUEST_DIFF_BASE_SHA" --gitlab-sast --output gl-sast-report.json artifacts: reports: sast: gl-sast-report.jsonThe Semgrep guide (in French) details writing custom rules and the public rule registries.
Handling false positives without losing trust
A false positive is an alert that does not match a real vulnerability: an overly generic rule, a special case the tool does not understand, a detection in test code. False positives are the leading cause of alert fatigue, the kind that pushes developers to ignore every notification. An explicit handling path is required.
| Step | Who | What happens | Indicative delay |
|---|---|---|---|
| Report | Developer | Comment in the merge request with the technical justification | Immediate |
| Triage | Security champion | Checks the justification and the rule documentation, then decides: false positive, true positive or escalation | 4 business hours |
| Review | Security team | Complex cases: application context, global rule adjustment | 24 business hours |
| Suppression | Developer | Documented suppression, with the decision reference | After approval |
With Semgrep, suppression takes two forms. A specific line is neutralised by a nosemgrep comment that names the rule; a whole path is excluded in a .semgrepignore file, which uses .gitignore syntax. Naming the rule avoids hiding other detections on the same line, and the decision reference makes the suppression auditable:
// Redirect to a fixed list of internal URLs, never user input.// Decision SEC-2026-042, approved by the security team.res.redirect(INTERNAL_ROUTES[name]); // nosemgrep: javascript.express.security.audit.express-open-redirect.express-open-redirect# Test code and fixtures: never deployedtests/fixtures/Enriching alert context
A raw "SQL Injection detected at line 42" alert does not help a junior developer understand the risk or fix it. Contextual enrichment adds an accessible explanation, the business impact and the fix steps. The file below is an illustrative in-house format: most vulnerability management platforms let you attach this kind of sheet to a rule.
# Enrichment sheet per vulnerability type (in-house format)sql_injection: severity: "critical" explanation: | An attacker can run arbitrary SQL queries on the database: read, modify or delete all data. business_impact: - "Customer data theft (GDPR: fine up to 4 % of worldwide annual turnover)" - "Corruption or loss of production data" remediation_steps: - "Trace the data flow from input to query" - "Replace concatenation with a parameterised query" - "Validate input type and format upstream" example: | # Vulnerable cursor.execute(f"SELECT * FROM users WHERE id = {user_id}") # Fixed cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) reference: "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html"Which communication channels should you set up?
Dev-Sec collaboration does not rest on tools alone: design questions and trade-offs between risk and delay need human exchanges. These exchanges are structured by urgency level, each with its channel and its response time, as the diagram shows.
Using the wrong channel creates friction: an urgent message lost in a general channel, or a simple question that mobilises the whole security team in a meeting. The tabs detail the uses and good practices of each level.
For general questions, clarification requests and non-urgent discussions, with an answer expected within 24 business hours:
- a
#security-questionschannel open to the whole organisation; - merge request comments for anything related to code;
- tickets for formal requests that need follow-up.
Include the full context in the first message, use threads and mention the right people rather than the whole channel.
For complex discussions, design reviews and training, in a slot of at least 2 hours a week:
- security office hours, open to all;
- security-oriented pair programming sessions;
- design reviews before implementing a sensitive feature.
Prepare questions and share the context in advance, then document the decisions taken.
For security incidents and exploited critical vulnerabilities, with an answer expected within 15 minutes:
- the security on-call group, mentioned in the incident channel;
- an incident room (dedicated video call);
- the out-of-hours alerting tool.
Keep this level for real emergencies, immediately give the what, the where and the since when, and join the room rather than multiplying channels.
Running security office hours
Security office hours are a recurring slot where the security team answers developers live. Inspired by university office hours, this format gives access to expertise without the weight of a ticket or a formal meeting.
The format that works best fits in a few rules: 2 hours a week in the same slot, one or two security engineers on rotation, developers who drop in without registering, at most 20 minutes per question and a visible queue. The last minutes are used to note recurring questions, which become documentation.
Track attendance, question types and satisfaction. If nobody comes, the slot is badly chosen or its existence is poorly known: it is a signal to act on, not proof that office hours are useless.
How do you build security into daily workflows?
Dev-Sec collaboration must not be a parallel process: it fits into developers' existing workflows. Three anchor points come back everywhere: a shared dashboard, the merge request and notifications.
A collaboration dashboard
A collaboration dashboard shows what interests both teams at once: not just the number of vulnerabilities, but the health of the collaboration. The panels below can be built in any visualisation tool (Grafana, Metabase) from the data of your scanners and your messaging tool.
| Panel | Question it answers | Indicative thresholds |
|---|---|---|
| Median time to fix | Do we fix quickly? | green under 24 h, red beyond 7 days |
| Share of reported false positives | Are the rules well tuned? | green under 10 %, red beyond 40 % |
| Questions unanswered for 24 h | Can the security team be reached? | green at 0, red beyond 5 |
| Detection stage per week | Is shift-left progressing? | trend toward 80 % before merge |
A checklist in the merge request
The merge request is the ideal moment for security review: the code is ready, the context is fresh, the discussion is natural. A checklist in the merge request template recalls the points to check without slowing the flow. In GitLab, templates go in .gitlab/merge_request_templates/:
## Security checklist
### Data and input- [ ] User input is validated (type, format, length)- [ ] Sensitive data is not logged- [ ] SQL queries are parameterised
### Authentication and authorisation- [ ] Endpoints that require authentication are protected- [ ] Authorisation checks are present- [ ] No secret is hard-coded
### Dependencies- [ ] No new dependency with a known CVE- [ ] Every added dependency is justified and maintained
Security review needed? Add the `security-review-needed` label.Notifications that reach the right person
A security notification that arrives at the wrong time, in the wrong place or without context gets ignored. The Python script below routes each result by severity and time: a critical one goes straight to the incident channel with a mention of the on-call group, a high one goes to the team channel during business hours, the rest joins a daily digest.
Two details change everything. An "@group" mention written as plain text notifies nobody: Slack only alerts a group through the <!subteam^ID> syntax, with the group's identifier. And business hours are computed in an explicit time zone, not the server's own. The routing logic was tested against a fake Slack client on seven cases (critical at night, high on a weekday, high on Saturday, low severity).
"""Route security alerts to Slack based on severity and time."""
import osfrom dataclasses import dataclassfrom datetime import datetime, timefrom typing import Optionalfrom zoneinfo import ZoneInfo
from slack_sdk import WebClient
TIMEZONE = ZoneInfo("Europe/Paris")
# Repository -> team channel and on-call group. The group identifier# (S...) is read in Slack: a plain-text "@group" mention notifies# nobody, only the <!subteam^ID> syntax does.TEAMS = { "payment-service": {"channel": "#team-payments", "oncall": "S0123PAY45"}, "user-service": {"channel": "#team-identity", "oncall": "S0456IDN78"},}SECURITY_ONCALL = "S0789SEC01"
@dataclassclass Finding: id: str severity: str # critical, high, medium, low title: str repository: str file_path: str line_number: int detection_stage: str # ide, mr, build, deploy remediation_url: Optional[str] = None
def business_hours(now: datetime) -> bool: """Monday to Friday, 9 am to 6 pm, Paris time.""" return now.weekday() < 5 and time(9) <= now.time() <= time(18)
def message(f: Finding) -> list: blocks = [ {"type": "header", "text": {"type": "plain_text", "text": f"[{f.severity.upper()}] {f.title}"}}, {"type": "section", "fields": [ {"type": "mrkdwn", "text": f"*Repository:* {f.repository}"}, {"type": "mrkdwn", "text": f"*File:* `{f.file_path}:{f.line_number}`"}, {"type": "mrkdwn", "text": f"*Detected at:* {f.detection_stage}"}, ]}, ] if f.remediation_url: blocks.append({"type": "section", "text": { "type": "mrkdwn", "text": f"<{f.remediation_url}|Fix guide>"}}) return blocks
def route(f: Finding, now: datetime) -> Optional[tuple]: """Return (channel, text) to post right away, or None for the digest.""" team = TEAMS.get(f.repository, {}) if f.severity == "critical": group = team.get("oncall", SECURITY_ONCALL) return "#security-incidents", f"<!subteam^{group}> critical vulnerability" if f.severity == "high" and business_hours(now): return team.get("channel", "#security-alerts"), "New high vulnerability" return None # high out of hours, medium and low: 9 am digest
def notify(client: WebClient, f: Finding, digest: list) -> None: target = route(f, datetime.now(TIMEZONE)) if target is None: digest.append(f) return channel, text = target client.chat_postMessage(channel=channel, text=text, blocks=message(f))
if __name__ == "__main__": client = WebClient(token=os.environ["SLACK_BOT_TOKEN"]) digest: list = [] notify(client, Finding( id="VULN-0042", severity="critical", title="SQL injection in search", repository="user-service", file_path="src/controllers/UserController.java", line_number=142, detection_stage="mr"), digest)Sending the 9 am digest (a scheduled job that empties the list) and storing pending results depend on your infrastructure: they are deliberately left out of the script, which only shows the routing logic.
How do you reduce day-to-day friction?
Beyond processes and tools, collaboration relies on hunting down day-to-day friction. Every obstacle, even a small one, erodes goodwill and pushes developers to work around security controls. The table lists the most frequent ones and their remedy.
| Friction | Effect | Remedy |
|---|---|---|
| Scans too slow | Slower pipeline, late feedback | Differential scan on the merge request |
| Too many false positives | Alert fatigue, ignored alerts | Rule tuning, justified suppressions |
| Outdated documentation | Bad practices repeated | Quarterly review, documentation close to the code |
| Inaccessible expertise | Blockers, risky decisions | Security office hours, security champions |
| Manual approvals | Bottleneck, workarounds | Automation with dated exceptions |
This list is not fixed: each organisation has its own friction points. What matters is a regular ritual (quarterly retrospective, anonymous survey) that surfaces them before they turn into silent workarounds.
A self-service catalogue
Security self-service lets developers handle common requests themselves without waiting for the security team, which can then focus on complex topics. The catalogue below can be implemented in a developer portal (Backstage, for example) or in simple triggerable pipelines; delays are targets to adjust.
| Service | Automation level | Who can trigger it | Target delay |
|---|---|---|---|
| Rotation of a compromised or expired secret | Full | Any developer | Under 5 minutes |
| Exclusion of a test or generated code path | Automatic for standard categories | Tech lead | Immediate |
| False positive report | Approval by a champion | Any developer | Under 4 hours |
| Temporary exception on a vulnerable dependency, dated (30 days at most) with a fix plan | Approval by a champion | Tech lead | Under 4 hours |
| Security review request for a feature | Manual | Any developer | 2 to 5 days depending on the level |
How do you measure collaboration effectiveness?
Dev-Sec collaboration indicators differ from pure security indicators: they measure the quality of the interaction between teams, not just the number of vulnerabilities. The targets in the table are internal starting benchmarks, to adjust.
| Indicator | Indicative target | Measurement |
|---|---|---|
| Time to first answer on the questions channel | Under 4 h | Median time to first answer |
| Office hours attendance | Over 30 % of teams per month | Distinct teams that came at least once |
| Cross satisfaction | Average score of 7 out of 10 or more | Quarterly survey, in both directions |
| False positives handled on time | Over 90 % within 24 h | Share of reports decided on time |
| Detection before merge | Trend toward 80 % | Share of vulnerabilities detected in the IDE or merge request |
| Time to fix | Under 7 days (high), under 30 days (medium) | From report to resolution |
The cross-satisfaction survey is the most telling indicator. Two questions are enough, scored from 1 to 10: "How easy is it to work with the security team?" and "How easy is it to work with the development teams?". Track their quarterly trend rather than their absolute value.
These goals take on particular weight with the 2025 DORA report, renamed "State of AI-assisted Software Development". Its central finding matches this lesson: generative AI speeds up delivery but amplifies the instability of teams whose culture and collaboration are already fragile. Good Dev-Sec collaboration is no longer a comfort, it determines whether AI can be adopted without multiplying incidents.
What rollout plan over 12 weeks?
Improving Dev-Sec collaboration is a cultural change that takes time. The plan below spreads the work over 12 weeks, two weeks per workstream, with a measurement at the start and at the end to objectify progress.
-
Weeks 1-2: diagnosis and announcement
Measure the starting point (time to fix, cross satisfaction, attendance). Officially announce the initiative. Spot possible quick wins.
-
Weeks 3-4: blameless culture
Train managers in facilitation. Run the first postmortem with the new template and share it widely.
-
Weeks 5-6: communication channels
Create the dedicated channels, launch security office hours, announce response times.
-
Weeks 7-8: automated feedback
Recommend the IDE extensions, add the differential scan on merge requests, set up the false positive path.
-
Weeks 9-10: self-service and dashboard
Open two or three self-service services and the collaboration dashboard.
-
Weeks 11-12: measurement and adjustment
Measure the indicators again, rerun the satisfaction survey, choose next quarter's adjustments.
Key points
- Treat each incident as a systemic problem, not an individual fault: a blameless culture brings errors to light instead of hiding them.
- A useful postmortem leads to actions assigned to roles, otherwise it is theatre.
- Shorten feedback loops: IDE extensions, differential scan on the merge request, with an internal target of 80 % detection before merge.
- Handle false positives through an explicit path and named, justified
nosemgrepsuppressions: they are the leading cause of alert fatigue. - Give each type of question a channel and a response time (asynchronous, synchronous, escalation).
- A Slack group mention only notifies through
<!subteam^ID>: check that a critical alert really reaches the on-call group. - Measure collaboration itself (cross satisfaction, office hours attendance), not just vulnerabilities.
Knowledge check
Check that you have the essentials of this guide. The questions only cover what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Platform Engineering
A new full quiz with random questions
Work again only on the questions you missed
FAQ: frequently asked questions
The questions below cover the most frequent doubts about blameless culture, feedback loops, false positives and security office hours.
nosemgrep comment naming the rule, or a path excluded in .semgrepignore). This path avoids alert fatigue, which pushes teams flooded with false signals to ignore every security notification.External resources
The chapter of Google's SRE book and Etsy's article remain the founding texts of the blameless postmortem; the GitLab and Datadog reports provide the figures quoted in this lesson.