.NET Developer Skill Matrix

Public example of a role-based matrix for backend engineers working with .NET and C#.

Go developer PHP developer Python developer .NET developer DevOps engineer DBA Legal counsel
Junior Middle Senior TechLead Principal
C# and .NET
Fundamentals and ecosystem
Write maintainable C# code

Uses generics, interfaces, records, nullable reference types, and standard patterns without overengineering.

Handle exceptions correctly

Does not swallow errors, wraps them with context, and doesn't catch Exception without reason.

Apply interfaces and composition

Designs dependencies through interfaces, separates domain, application, and infrastructure layers.

Refactor safely

Improves code structure while preserving behavior and backward compatibility.

Use BCL and NuGet ecosystem effectively

Prefers platform built-ins over external dependencies where reasonable; evaluates packages by activity.

Set language and codebase standards

Defines namespace layout conventions, exception strategy, code review practices, and shared utilities.

Design shared abstractions

Creates reusable components that reduce cognitive load without introducing unnecessary coupling.

Lead review of critical changes

Identifies architectural risks, security issues, and compatibility violations in PRs.

Define project and solution structure

Sets solution/project layout so the team moves consistently without ad-hoc decisions.

Resolve disputed implementation details

Settles team disagreements on technical approaches through principles rather than preference.

Build a code review culture

Creates an environment where reviews add value: teach rather than nitpick, reduce risk rather than slow delivery.

Control technical debt growth

Tracks debt accumulation and includes paydown in the roadmap alongside new features.

Set language standards across teams

Builds an engineering handbook with justified decisions that work across different contexts.

Decide which packages enter the platform

Evaluates NuGet dependencies on stability, support, security, and lock-in risk.

Build an engineering practices roadmap

Plans the evolution of standards accounting for team growth, system scale, and business direction.

Influence decisions through persuasion

Drives correct decisions without formal authority, through argument and trust.

Publicly represent the team's technical approaches

Speaks at conferences, writes articles, contributes to open-source — builds external authority.

Asynchronous programming
Use async/await and Task safely

Adds async code only where justified, avoids deadlocks via ConfigureAwait.

Understand CancellationToken

Passes CancellationToken through the call chain correctly and responds to cancellation.

Build bounded worker pools

Designs pools with bounded concurrency using SemaphoreSlim or Parallel.ForEachAsync.

Implement retry with backoff and timeout

Handles transient failures without overwhelming downstream services; uses Polly or equivalent.

Control Task and connection leaks

Identifies and eliminates unfinished tasks, open connections, and fire-and-forget anti-patterns.

Design async models under load

Chooses Task vs ValueTask correctly, applies IAsyncEnumerable and Channel for data streams.

Analyze deadlocks and contention in async code

Uses dotMemory, PerfView, and ETW to find concurrency issues in production.

Set patterns for common team scenarios

Defines which pattern (WhenAll, Channel, Dataflow, BackgroundService) to use in each context.

Set team-wide async development rules

Chooses base worker, queue, and cancellation patterns so the team doesn't create unstable solutions.

Review high-throughput flows

Identifies risks in critical paths: deadlocks, thread starvation, thundering herd.

Manage async code delivery risks

Ensures CI covers critical async scenarios and that task leaks are detected.

Choose between Thread, ThreadPool, and async I/O

Makes informed decisions considering CPU vs I/O bound load and ASP.NET Core context.

Define platform async processing standards

Chooses load-limiting models across multiple services and teams.

Design fan-out strategies for the platform

Defines how to scale parallel processing without degrading adjacent systems.

Build a graceful degradation model

Sets principles for graceful degradation and circuit breaking at domain level.

Identify systemic async architecture risks

Conducts architectural analysis for hidden race conditions and deadlock risks in integrations.

Data layer
SQL and databases
Write correct SQL queries

Uses JOINs, WHERE clauses, indexes, and parameterized queries; avoids SQL injection.

Write and apply migrations

Creates EF Core migrations, understands application order and schema change reversibility.

Optimize slow queries

Reads query plans, adds indexes, eliminates N+1 via Include or split queries.

Design schema for new features

Normalizes data sensibly, considers nullable, constraints, and deletion scenarios.

Manage transactions and isolation levels

Chooses the right isolation level, avoids deadlocks and long-running transactions.

Set team database standards

Defines naming conventions, required indexes, nullable rules, and migration workflow.

Plan safe production migrations

Applies zero-downtime patterns: expand-contract, column backfill over multiple deploys, lock-free alter.

Evaluate read/write patterns and choose strategy

Decides when to use read replicas, caching, or event sourcing instead of classical CRUD.

Manage connection pool in production

Configures EF Core/Dapper pool settings, controls max connections and health checks.

Control database delivery risks

Ensures migrations are tested, reversible, and won't cause production degradation.

Synchronize migration strategy

Coordinates database changes across multiple services and teams.

Set indexing policy

Defines who adds indexes, how schema review works, and who owns performance regression.

Prevent dangerous schema changes

Restricts or approves DROP, NOT NULL without default, full-table lock changes during peak hours.

Define domain data strategy

Chooses between SQL, NoSQL, and mixed storage based on load, structure, and SLOs.

Design multi-tenant and multi-region storage

Plans partitioning, sharding, and geo-distribution at platform level.

Set backup, restore, and DR requirements

Defines RPO/RTO, tests recovery, and builds runbooks for data incidents.

Build data capacity planning

Forecasts volume growth, storage costs, and initiates architectural changes in time.

Manage data compliance requirements

Ensures GDPR, retention policy, and audit trail compliance across multiple systems.

Queues and events
Write consumers with correct ack/nack

Understands at-least-once semantics, doesn't lose or duplicate messages without reason.

Implement a simple producer

Publishes events with the required structure and handles send errors.

Implement idempotent consumers

Handles duplicates via a deduplication key or idempotent database operations.

Design dead-letter flows

Configures DLQ, lag alerts, and a reprocess procedure for broken messages.

Monitor consumer lag

Sets up consumer lag monitoring, identifies and eliminates accumulation causes.

Design event-driven flows with delivery guarantees

Chooses outbox, saga, or choreography based on consistency requirements.

Define event contracts

Establishes event schemas, versioning strategy, and backward/forward compatibility.

Choose between sync and async integration

Justifies the choice understanding tradeoffs: latency, coupling, failure isolation, observability.

Set queue standards for the team

Defines which broker and pattern to use in each scenario and documents decisions.

Manage queue observability

Ensures lag, DLQ growth, and partition skew alerts; sets up on-call runbooks.

Manage event schema evolution

Coordinates event schema changes between producer and consumer teams.

Synchronize event contracts between teams

Maintains an event registry, aligns on breaking changes, and organizes contract testing.

Define platform messaging strategy

Chooses brokers, delivery patterns, and retention policy for the entire domain.

Design event sourcing at domain level

Defines where to store the event log, how to build projections, and how to ensure replay.

Establish delivery and consistency guarantees

Defines exactly-once or idempotency levels for critical business operations.

Identify systemic event-driven risks

Analyzes cascade failure, ordering issues, and distributed transaction risks.

Infrastructure and delivery
Docker and containers
Build multi-stage Docker images

Separates build and runtime stages, avoids copying unnecessary artifacts into the final image.

Work with the local environment via Docker Compose

Runs dependencies (DB, queues, services) locally, understands volumes and network namespaces.

Optimize images by size and layers

Orders instructions correctly for layer caching, uses .dockerignore.

Configure healthcheck and graceful shutdown

Implements a /healthz endpoint, handles SIGTERM via IHostApplicationLifetime, and waits for requests to complete.

Diagnose issues inside containers

Reads logs, checks resources, inspects the filesystem, and uses exec in prod-like environments.

Set Dockerfile standards for the team

Defines base images, linting rules, and a review checklist for container artifacts.

Design dev/prod environment parity

Ensures local Compose and the production environment differ as little as possible.

Assess image security

Runs trivy/grype, tracks CVEs in base images, enforces non-root user.

Set base images for the team

Selects and maintains golden images, manages their updates and team notifications.

Control container security policy

Defines runtime policy: non-root, read-only filesystem, resource limits, seccomp.

Manage the dependency update process

Ensures regular updates of base images and NuGet dependencies with vulnerability checks.

Integrate container scanning into CI

Adds automated image scanning to the pipeline, configures severity thresholds and notifications.

Define platform container strategy

Chooses runtime (Docker, containerd), orchestration, and image registry strategy.

Set base image policy for multiple teams

Standardizes approved base images, enforcement mechanisms, and update processes.

Design observability in containerized environments

Standardizes log format, metrics exposition, and tracing injection at platform level.

Synchronize container standards across teams

Produces RFCs and ADRs for platform decisions, obtains buy-in from team tech leads.

Build and CI/CD
Understand and read the pipeline

Understands CI steps — lint, test, build, deploy — and knows what each does and where to find failure causes.

Fix flaky tests in CI

Finds the root cause of unstable tests: race conditions, ordering dependencies, or external services.

Configure a pipeline for a new service

Sets up lint, test, build, and deploy steps with the right environment variables and secrets.

Speed up slow CI flows

Parallelizes steps, caches NuGet packages and dotnet artifacts, reduces cold start time.

Implement canary or blue/green deployment

Rolls out changes gradually and can roll back without downtime.

Design deployment strategy for services

Chooses rolling, canary, or blue/green based on SLO, traffic volume, and criticality.

Set pipeline standards for the team

Defines mandatory steps: lint, security scan, test coverage, smoke test after deploy.

Manage rollback procedures

Documents and tests the rollback procedure, ensures the team can roll back under pressure.

Implement automatic quality gates

Blocks deploys on coverage regression, CVEs above threshold, or failing smoke tests.

Set deployment governance for the team

Defines what production-ready means: checklist, approvals, observability requirements.

Establish release gates and approval flows

Balances delivery speed with risk control: who approves, when it's automatic.

Control production readiness of new services

Ensures every service has a healthcheck, metrics, alerts, runbook, and documentation before deploy.

Synchronize delivery process with other teams

Coordinates release windows, feature flags, and dependencies in joint deliveries.

Define platform delivery strategy

Chooses the deployment model and CI/CD tooling at multi-team level.

Design progressive delivery

Sets principles for feature flags, traffic splitting, and automated rollback at platform level.

Set SLO for pipeline reliability

Defines acceptable build time, flaky test ratio, and MTTR for CI infrastructure.

Build change management policy

Defines rules for hot fixes, emergency deploys, and change freezes balancing speed and safety.

Identify delivery process bottlenecks

Measures DORA metrics, finds constraints, and initiates improvements at platform level.

Quality and reliability
Testing
Write unit tests for business logic

Covers happy path and main error branches using xUnit or NUnit without testing implementation details.

Use Theory and parameterized tests

Structures test cases via [Theory]/[TestCase], avoids duplication, keeps tests readable.

Write integration tests with real dependencies

Uses Testcontainers.NET or in-memory DB — tests real behavior, not just mocks.

Mock external services correctly

Chooses between Moq/NSubstitute and HTTP stubs (WireMock.NET) depending on the test level.

Write benchmarks for critical paths

Measures performance via BenchmarkDotNet and tracks regressions.

Define test strategy for the team

Sets the balance between unit, integration, and e2e tests accounting for speed and confidence.

Introduce contract testing

Uses Pact or schema-based approach to verify compatibility between services.

Evaluate coverage meaningfully

Distinguishes useful coverage from meaningless, focuses on critical scenarios.

Design testable architecture

Embeds dependency injection and clear boundaries in design so testing doesn't require heroics.

Set the mandatory delivery quality bar

Defines what a PR must have: minimum coverage, required scenarios, performance tests.

Control regression coverage

Ensures every incident results in a test that prevents recurrence.

Remove valueless tests

Reviews and deletes tests that only slow CI without improving confidence.

Manage test debt

Tracks known coverage gaps and includes remediation in the roadmap.

Build a test confidence model for multiple services

Defines how the combined test suite across levels ensures delivery confidence.

Set reliability testing standards

Defines load testing, chaos engineering, and failure injection requirements for critical systems.

Design chaos and fault injection

Implements systematic failure experiments to find weaknesses before production incidents.

Build acceptance criteria for critical systems

Sets technical launch conditions for services with high SLOs: tests, load, DR.

Metrics and observability
Add structured logs

Logs enough via Serilog/NLog to understand runtime behavior, without sensitive data or noise.

Use the team's metrics and dashboards

Reads existing Grafana dashboards and understands basic RED metrics for their service.

Diagnose incidents from traces and metrics

Reads OpenTelemetry distributed traces, correlates logs, and isolates bottlenecks or regressions.

Build alerting for new services

Creates alerts on error rate, p95 latency, and saturation with correct thresholds and runbooks.

Interpret latency distributions

Distinguishes p50 from p99, understands tail latency and the effect of GC pauses on graphs.

Define SLIs and SLOs for services

Chooses meaningful quality indicators, aligns targets with product, and sets error budgets.

Set metric naming conventions

Defines naming standards, labeling, and cardinality limits for Prometheus/OTLP.

Design distributed tracing

Instruments services with correct span context, baggage, and sampling strategy via OpenTelemetry.

Lead incident postmortems

Writes blameless postmortems, identifies systemic causes, and defines action items.

Set team observability standards

Defines the mandatory minimum: what to log, which metrics to instrument, how to trace.

Control alert fatigue

Regularly reviews alerts: removes noise, improves thresholds, adds runbooks.

Ensure team on-call readiness

Ensures every service has a runbook, alerts with descriptions, and an escalation path.

Build postmortem culture

Normalizes blameless review, tracks action item completion, and shares learnings with the team.

Define platform reliability strategy

Builds a unified approach to SLOs, error budgets, and incident management for the entire domain.

Set cross-service SLOs and error budgets

Establishes dependency SLAs between teams, links budgets to delivery priorities.

Design centralized telemetry

Defines platform tools for collecting, storing, and visualizing observability data.

Build a production feedback loop

Creates a system where production signals regularly influence team priorities.

Set incident management standards

Defines severity levels, escalation matrix, communication protocol, and learning system for incidents.

Teamwork
Autonomy
Break down tasks independently

Splits a task into clear steps and clarifies ambiguities before starting work.

Escalate blockers in time

Doesn't stay stuck silently for more than a day — signals the blocker and proposes options.

Drive a task from design to delivery

Defines the approach, aligns on it, and brings it to production without losing intermediate steps.

Find and align on a solution independently

Proposes a concrete option with tradeoffs rather than asking an open-ended 'how do I do this'.

Manage their own backlog

Keeps tasks up to date and prioritizes within the sprint goal independently.

Initiate improvements without being asked

Notices system problems and proposes solutions without waiting for someone to file a ticket.

Manage parallel tracks

Runs multiple tasks simultaneously without losing context or creating a team bottleneck.

Make decisions under uncertainty

Moves forward with incomplete information, records assumptions, and is ready to revise.

Drive the team's technical track independently

Manages the technical backlog, sets direction, and maintains pace without daily management.

Prioritize the technical backlog

Balances new features, tech debt, and reliability work in a business context.

Manage risks without constant escalation

Independently assesses and mitigates technical risks; escalates only significant ones.

Unblock others without losing their own progress

Helps the team move forward without taking over others' work entirely.

Set the agenda independently

Identifies what problems matter and initiates work without external prompting.

Initiate cross-team improvements

Identifies systemic problems and organizes multiple teams to solve them.

Manage strategic technical risks

Keeps long-term risks in focus: vendor lock-in, architectural debt, skill gaps.

Build consensus without formal authority

Drives decisions through argument, trust, and engaging key stakeholders.

Work with long planning horizons

Sees and manages technical strategy 12–24 months ahead.

Artifact quality
Write PRs with change descriptions

Describes what changed, why, and how to verify — reviewers shouldn't reconstruct context.

Write clear commit messages

Follows the agreed format, describes the essence of the change rather than the work process.

Describe context and tradeoffs in PRs

Explains why this solution was chosen and what alternatives were considered.

Document non-obvious decisions in code

Adds a comment about 'why', not 'what' — where the logic isn't self-evident.

Keep tickets up to date

Updates status, adds discussion outcomes, doesn't leave tasks in an undefined state.

Write clear design documents

Describes the problem, proposal, alternatives, and risks to get quality feedback.

Record decisions in ADRs

Documents significant architectural decisions with context so future engineers understand 'why'.

Structure artifacts for team review

Chooses the right format: RFC, design doc, spike summary — based on complexity and audience.

Set PR and ticket formatting standards

Creates templates and checklists and explains the value of good artifacts to the team.

Control the quality bar of team artifacts

Returns context-free PRs and tickets for rework; sets expectations by example.

Prepare executive summaries for non-technical audiences

Translates technical decisions into business language: risks, timelines, impact.

Build a written communication culture

Promotes async-first, decision documentation, and process transparency in the team.

Set RFC and ADR process standards

Defines when an RFC is needed, how it is reviewed, and how decisions are recorded and accessible.

Write strategic documents for multiple audiences

Adapts a technical document for engineers, management, and external partners.

Ensure transparency of technical decisions

Creates a system where important decisions are accessible, understandable, and justified for the whole organization.

Shape engineering culture through documents

Writes principles, manifesto, and best practices that become a reference point for teams.

Technical documentation
Update README for their changes

Updates run instructions, env variables, and dependency information with every change.

Document configuration and parameters

Describes the purpose of env variables, valid values, and default behavior.

Write a runbook for a new service

Describes how to start, stop, roll back, and diagnose a service in production.

Keep API documentation up to date

Keeps the OpenAPI spec (Swagger) in sync with the code on every contract change.

Document operational procedures

Describes routine operations: migrations, planned restarts, secret rotation.

Keep architectural documentation current

Syncs diagrams and descriptions with the actual system state, prevents staleness.

Write an onboarding guide for a service

Creates a document that lets a new engineer quickly get context and become productive.

Build the team's knowledge base

Organizes documentation so what's needed is easy to find and stays current.

Set team documentation standards

Defines structure, required sections, and update process for different document types.

Control completeness of operational docs

Ensures every production service has a runbook, alert descriptions, and escalation path.

Build a knowledge management approach

Chooses tools and processes for accumulating and sharing knowledge within the team.

Prepare documentation for audits and compliance

Ensures documentation meets external requirements: security reviews, vendor audits.

Define domain technical knowledge strategy

Sets how knowledge is accumulated, structured, and transferred between teams.

Manage documentation of cross-system decisions

Ensures cross-team architectural decisions are recorded and accessible.

Set ops documentation requirements

Establishes runbook, postmortem, and incident report standards across multiple teams.

Publish engineering decisions externally

Shares team experience through blogs, conferences, and open-source — builds external technical authority.

Communication
Clearly describe task status

Communicates what's done, what's left, and whether there are blockers — without needing prompts.

Ask clarifying questions instead of assuming

Clarifies requirements before starting work rather than reworking after finishing.

Explain technical decisions to non-technical colleagues

Adapts the level of detail to the audience without diving into implementation details when results matter.

Conduct constructive code reviews

Gives specific, actionable feedback — identifies the problem and suggests a path to resolution.

Give and receive work feedback

Raises issues directly and without aggression; accepts criticism without defensiveness.

Lead technical discussions to a decision

Structures discussions, keeps focus, and helps the group reach a concrete conclusion.

Build trust with product and adjacent teams

Syncs regularly, keeps partners informed, and avoids surprises.

Manage disagreements without escalation

Finds compromise or persuades with arguments without involving management unnecessarily.

Represent the team's position

Speaks for the team at cross-team meetings, communicates priorities and constraints.

Sync the technical agenda with management

Regularly translates technical priorities and risks into language non-technical managers understand.

Manage stakeholder expectations

Signals risks, schedule changes, and technical decision impacts in advance.

Mediate team conflicts

Helps team members resolve disagreements and creates a safe space for discussion.

Build psychological safety

Creates an environment where people aren't afraid to share ideas, admit mistakes, or ask questions.

Act as the technical voice of the domain

Represents technical positions at director, partner, and external audience level.

Lead cross-team dialogue

Creates forums for knowledge sharing and solving common problems between teams.

Build alliances for systemic change

Convinces key stakeholders of the need for change and builds a coalition of support.

Manage communication under uncertainty

Provides clear direction to the team and partners while strategy is still forming.

Publicly represent engineering decisions

Speaks at conferences, runs a technical blog — builds the company's reputation in the engineering community.