CI/CD, Publishing & Deployment
16 questions found
What is the purpose of a multi-stage Dockerfile for .NET applications, and how does it reduce final image size?
Advanced
A multi-stage Dockerfile uses one stage with the full SDK image to restore, build, and publish the application, then copies only the published output into a second, much smaller stage based on a lightweight runtime-only base image -- discarding the SDK, build tools, intermediate object files, and source code from the final image, significantly reducing image size and attack surface compared to shipping the entire SDK in production.
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app/publish
# Final, minimal runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Real-world example
A team reduces their production container image from over 1.2GB (SDK-based) to under 220MB using a multi-stage Dockerfile, speeding up deployment pull times and reducing the surface area for security vulnerabilities.
Common follow-ups: What's the difference between the aspnet and runtime-deps base images?;How does layer caching optimize rebuild times in a multi-stage Dockerfile?
Docker & Containerization;.NET CLI
SDK & Project Structure (csproj)
How do feature flags decouple code deployment from feature release in a CI/CD workflow?
Intermediate
Feature flags let you deploy code containing a new feature to production while keeping it disabled/hidden behind a flag, separating the technical act of deployment (shipping code) from the business decision of release (making a feature visible/active) -- this allows continuous deployment of incomplete or in-progress features without user impact, and enables gradual rollout, A/B testing, or instant kill-switch rollback without a new deployment.
builder.Services.AddFeatureManagement();
if (await featureManager.IsEnabledAsync("NewCheckoutFlow")) {
return NewCheckoutHandler();
} else {
return LegacyCheckoutHandler();
}
// Code is deployed; the flag controls whether it's actually active
Real-world example
A team merges and deploys a large in-progress checkout redesign to production daily behind a feature flag disabled for all users, avoiding a risky long-lived feature branch while keeping the feature completely invisible until it's ready and explicitly enabled.
Common follow-ups: How do you clean up feature flags after a feature is fully rolled out?;What's the difference between release flags and permanent operational flags (like a kill switch)?
Configuration & Options;Microservices & Distributed Architecture Patterns
How do you implement automated rollback in a CI/CD pipeline triggered by post-deployment monitoring signals (error rate, latency)?
Advanced
Automated rollback typically involves: deploying the new version (often to a canary subset or staging slot first), running an automated monitoring window that queries metrics (error rate, p99 latency, custom business KPIs) from an observability platform, comparing against a defined threshold or against the previous version's baseline, and automatically triggering a rollback (reverting a slot swap, scaling canary traffic back to zero, or redeploying the previous known-good artifact) if the new version's metrics breach the threshold within the observation window -- without requiring a human to manually notice and react to a problem.
# Simplified rollback logic
- name: Monitor post-deploy
run: |
sleep 300 # observation window
error_rate=$(curl -s "$METRICS_API/error_rate?window=5m")
if (( $(echo "$error_rate > 0.05" | bc -l) )); then
echo "Error rate too high, rolling back"
az webapp deployment slot swap --slot staging --target-slot production --action swap
exit 1
fi
Real-world example
A high-traffic API automatically rolls back a deployment within 3 minutes if its post-deployment error rate exceeds 5%, avoiding hours of degraded service that would otherwise require an engineer to notice an alert and manually intervene at 2 AM.
Common follow-ups: What metrics are most reliable signals for automated rollback decisions?;How do you avoid false-positive rollbacks from normal traffic variance?
Diagnostics & Performance;Health Checks & Readiness/Liveness Probes
What is the difference between dotnet publish's --self-contained and framework-dependent options in the context of container image builds?
Intermediate
For containerized deployments, framework-dependent publishing (the common default) relies on the base image already including the matching .NET runtime (e.g., the aspnet:8.0 image), producing smaller application layers since the runtime isn't duplicated in your app's own files. Self-contained publishing bundles the runtime into your application output itself, which is less useful in containers (since the base image typically already provides the runtime) but can be combined with a minimal runtime-deps or even scratch base image for highly specialized, ultra-minimal container scenarios.
# Framework-dependent (typical for containers, relies on base image's runtime)
FROM mcr.microsoft.com/dotnet/aspnet:8.0
COPY --from=build /app/publish .
# Self-contained (rare in containers, bundles runtime; could use a leaner base)
RUN dotnet publish -c Release -r linux-x64 --self-contained true -o /app/publish
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0 # no .NET runtime at all needed
Real-world example
A team experimenting with ultra-minimal container images tests self-contained publishing against a runtime-deps base image (which has OS dependencies but no .NET runtime), achieving a smaller final image than the standard aspnet base for a specific CLI tool use case.
Common follow-ups: When does self-contained publishing actually help in containerized scenarios?;How does this interact with Native AOT for even smaller images?
Docker & Containerization;.NET CLI
SDK & Project Structure (csproj)
What is the difference between continuous integration (CI) and continuous deployment/delivery (CD)?
Beginner
Continuous Integration is the practice of frequently merging code changes into a shared branch, with automated builds and tests running on every change to catch integration issues early. Continuous Delivery extends this by ensuring the codebase is always in a deployable state, with releases requiring a manual approval step, while Continuous Deployment goes further still, automatically deploying every change that passes all checks directly to production with no manual gate at all.
# CI: runs on every push/PR
on: [push, pull_request]
jobs:
build-and-test: { ... }
# CD (Continuous Deployment): auto-deploys after CI passes on main
on:
push:
branches: [main]
jobs:
deploy: { needs: build-and-test, ... }
Real-world example
A startup practices full continuous deployment, where every merged pull request to main automatically deploys to production within minutes, while a regulated financial services company practices continuous delivery, requiring a manual sign-off before each production release despite otherwise identical automated pipelines.
Common follow-ups: What organizational or regulatory factors push a team toward delivery over deployment?;How does feature flagging reduce the risk of full continuous deployment?
CI/CD
Publishing & Deployment;.NET CLI
SDK & Project Structure (csproj)
What is Native AOT compilation in .NET, and what are its main benefits and limitations?
Advanced
Native AOT (Ahead-of-Time) compilation compiles a .NET application directly into a self-contained native executable at build time, rather than compiling to intermediate language and relying on the JIT compiler at runtime -- this produces faster startup (no JIT warmup), lower memory usage (no JIT infrastructure needed at runtime), and a smaller, single-file deployment artifact. The main trade-off is that because everything must be resolvable at compile time, dynamic features like full reflection are only partially supported, which can break libraries (particularly some serializers) that rely heavily on runtime type inspection -- Native AOT suits microservices, serverless functions, and CLI tools where fast, predictable startup matters most.
<!-- Enabling Native AOT in a .csproj is a single property -->
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
# dotnet publish -r linux-x64 -c Release --self-contained
# produces a single native executable, no separate .NET runtime install needed on the target machine
Real-world example
A serverless function billed by execution time and cold-start latency is republished with Native AOT enabled, eliminating JIT warmup entirely and meaningfully reducing the function's average cold-start time compared to the standard JIT-compiled deployment.
Common follow-ups: What specific JSON serialization approach is compatible with Native AOT's reflection limitations?;How do you validate that a given third-party library actually supports Native AOT before adopting it?
CI/CD
Publishing & Deployment;Diagnostics & Performance