Back to blog

UISampleSpark: Modern DevOps as a Living Reference

February 6, 202610 min read

Writing code is only half the story. This final article in the UISampleSpark series traces the operational journey from manual builds to fully automated pipelines spanning Docker containerization, three-workflow CI/CD, security scanning, and multi-platform cloud deployment.

UISampleSpark: Modern DevOps as a Living Reference

Part 5 of the UISampleSpark Series — Modern Operations

From Code to Production

Writing code is only half the story. The other half — the half that separates tutorial projects from professional references — is getting that code reliably built, tested, packaged, and deployed. Over its seven-year evolution, SampleMvcCRUD transformed its operations from manual builds to a fully automated pipeline spanning containerization, continuous integration, security scanning, and cloud deployment.

This final article traces that operational journey and reflects on what the project has become: not just a code reference, but a living demonstration of modern software engineering practices.

Containerization: Docker as the Universal Packaging Format

Docker arrived in SampleMvcCRUD in May 2022. By February 2026, it had evolved into a 109-line, multi-stage build with comprehensive security hardening.

The Multi-Stage Build

The Dockerfile uses four stages, each optimized for its purpose:

Stage 1 — Base Runtime: Built on Alpine Linux (

mcr.microsoft.com/dotnet/nightly/aspnet:10.0-alpine
), the smallest .NET runtime image. Alpine's minimal footprint reduces the attack surface and image size. Essential libraries — OpenSSL, CA certificates, ICU for globalization — are explicitly installed.

Stage 2 — Build: The full .NET 10 SDK image with Node.js 20 for npm-based frontend tooling. Dependencies are restored and the solution is built in Release configuration. This stage is large but discarded.

Stage 3 — Publish: The Release build output is published to a deployment directory.

Stage 4 — Final Runtime: The published output is copied onto the minimal Alpine base. A non-root user (

appuser
) is created, file ownership is enforced, and the container runs on port 8080.

Security Hardening

The Docker configuration reflects a security-first philosophy:

  • Alpine Linux: Smaller attack surface, fewer potential vulnerabilities
  • Multi-pass updates: Running
    apk upgrade
    multiple times catches dependency chains
  • Non-root execution: The container runs as
    appuser
    , limiting exploit impact
  • Vulnerability auditing:
    apk audit
    runs during the build
  • Hadolint compliance: Static analysis with documented exceptions for intentional deviations

The Docker Hub Pipeline

Built images are automatically pushed to Docker Hub at

markhazleton/mwhsampleweb
on every push to the main branch:

docker run -p 8080:8080 markhazleton/mwhsampleweb

Visit

http://localhost:8080
and all seven CRUD implementations, the REST API, Swagger documentation, and health endpoints are immediately available — no cloning, no SDK installation.

CI/CD: Three Workflows for Three Concerns

Rather than a single monolithic pipeline, three independent workflows handle distinct responsibilities:

Workflow 1: Test and Build

Triggers: Every push to

main
, every pull request, manual dispatch.

This workflow is the quality gate. It restores dependencies, builds in Release configuration, runs the full test suite with code coverage, generates an HTML coverage report, enforces the 25% coverage threshold, and comments on pull requests with coverage metrics.

The 25% threshold is deliberately modest — a floor, not a ceiling — ensuring testing culture is maintained without creating friction. The project's actual coverage of 94.1% demonstrates what well-practiced testing achieves.

Workflow 2: Security Scanning

Triggers: Weekly schedule, every pull request.

CodeQL analysis identifies security vulnerabilities and code quality issues in C# code. Trivy container scanning supplements CodeQL, analyzing the Docker image for known CVE vulnerabilities in base images, packages, and dependencies.

Workflow 3: Docker Build and Push

Triggers: Every push to

main
, pull requests, manual dispatch.

Builds the multi-stage Docker image, runs smoke tests against the built container, and pushes to Docker Hub on successful builds from the main branch.

Why Three Workflows?

  • Faster feedback: Test failures report without waiting for Docker builds
  • Independent failure handling: Docker Hub issues don't block test results
  • Selective execution: Security scans run on their own schedule
  • Clear ownership: Each workflow has a single responsibility

Deployment Versatility

SampleMvcCRUD demonstrates multiple deployment strategies:

Infrastructure as a Service: Azure VMs with IIS

The production deployment at

https://samplecrud.markhazleton.com/
runs on a Windows IIS virtual machine in Azure. This proves that even a .NET 10 application with Blazor Server, htmx, and React can run on traditional Windows infrastructure.

Platform as a Service: Azure App Service

GitHub Actions include Azure App Service deployment workflows demonstrating PaaS deployment where the platform manages servers, OS updates, SSL, and scaling.

Container Deployment: Docker Hub to Any Platform

The Docker image runs anywhere Docker runs — local machines, cloud VMs, Kubernetes clusters, AWS ECS, Google Cloud Run. The image is self-contained: no external database, no configuration files, no runtime dependencies.

Observability: Knowing What's Happening

Health Checks

The

/health
endpoint returns liveness status for container orchestrators. The
/status
endpoint provides application metadata — version, environment, uptime, and configuration.

Application Insights

Azure Application Insights integration provides request tracing, dependency tracking, and exception monitoring. The connection string is configured through environment variables.

Structured Logging

The February 2026 compliance sprint added

ILogger<T>
to all service classes:

  • Information level: Normal operations like employee lookups
  • Warning level: Expected-but-notable events like employee-not-found responses
  • Error level: Exceptions during database operations with full context
  • Debug level: Verbose tracing for development-time troubleshooting

Log entries use structured parameters (

{EmployeeId}
,
{EmployeeName}
) rather than string interpolation, enabling rich filtering in log analysis tools.

Swagger/OpenAPI

Interactive API documentation at

/swagger
serves double duty: developer tool and observability surface. Enhanced XML documentation provides request/response examples and HTTP status code explanations directly in the Swagger UI.

The Living Reference: What SampleMvcCRUD Has Become

Seven years after its first commit, SampleMvcCRUD has evolved far beyond its original scope:

A Code Reference

The project demonstrates ASP.NET Core patterns developers encounter daily: repository pattern, dependency injection, async/await, DTO/entity separation, and EF Core data access — consistently implemented across 94 C# source files.

An Architecture Reference

The layered project structure — Domain, Repository, Web, Tests — demonstrates clean architecture principles without enterprise framework overhead.

A UI Pattern Showcase

Seven CRUD implementations spanning server-rendered HTML to client-side SPAs provide a tangible decision-making tool no blog post can match.

A Governance Example

The formal constitution with 11 principles and 30 enforceable requirements demonstrates that project governance doesn't have to be bureaucratic.

A DevOps Reference

Multi-stage Docker builds, three-workflow CI/CD, security scanning, health checks, and multiple deployment strategies demonstrate professional operational practices.

An AI Workflow Demo

SpecKit Spark integration with 16 specialized agents demonstrates structured, governed AI-assisted development covering the complete lifecycle.

Project Health: February 2026

MetricValue
Constitution Compliance100% (30/30 MUST requirements)
Build StatusClean (0 errors, 0 warnings)
Test Pass Rate100% (240/240)
Line Coverage94.1%
Branch Coverage77.3%
Method Coverage95.9%
Total Commits657
Active UI Implementations7
CI/CD Workflows3
SpecKit Spark Agents16

Lessons from the Journey

Educational Scope Is Intentional Design

The most important decision was what not to include. By intentionally omitting authentication, every line of code serves the educational mission. This isn't a shortcut — it's a feature, prominently documented.

Consistency Compounds

The repository pattern established in 2019 still serves in 2026. The async/await discipline made every subsequent UI implementation possible. Architectural decisions compound — good ones create capacity for growth.

Upgrade Annually, Not Eventually

Seven .NET framework upgrades proved annual migration is cheaper than deferred migration. Each upgrade was manageable because the gap between versions was small.

Show, Don't Tell

Seven UI implementations demonstrate tradeoffs more effectively than any comparison article. Developers learn by trying React and then htmx, by watching network traffic in both, by reading source code side by side.

Governance Enables, It Doesn't Constrain

The constitution's 30 MUST requirements liberate contributors by making expectations explicit. A new contributor doesn't have to guess whether nullable reference types should be enabled or whether raw SQL is acceptable.

The Road Ahead

Potential future directions include:

  • Blazor WebAssembly: Client-side Blazor running entirely in the browser via WebAssembly
  • API versioning: Demonstrating how to evolve APIs without breaking existing clients
  • Integration testing:
    WebApplicationFactory
    -based tests validating full request/response cycles
  • Progressive Web App (PWA): Demonstrating offline-capable web applications
  • Authentication examples: Separate branches showing security features the main project intentionally omits

Each potential addition will be evaluated against the core mission: does it teach something valuable without obscuring the fundamentals?

Conclusion: The Testament of a Living Reference

When the first commit landed on April 25, 2019, SampleMvcCRUD was a personal reference project — a developer's Swiss Army knife for testing .NET patterns. Seven years and 657 commits later, it has become a testament to the power of continuous improvement, intentional design, and the belief that even a simple CRUD application can serve as a comprehensive platform for learning.

The project proves that technical excellence and educational clarity are not mutually exclusive. A well-governed, well-documented, multi-paradigm project serves learners far better than superficial examples that sacrifice depth for brevity.

From .NET Core to .NET 10. From three UI approaches to seven. From implicit rules to a formal constitution. From manual builds to automated CI/CD. SampleMvcCRUD's evolution mirrors the evolution of the .NET ecosystem itself — and it's ready for whatever comes next.

Additional Resources


UISampleSpark Series

This is part 5 of a five-part series tracing the evolution of SampleMvcCRUD from a simple CRUD tutorial to a comprehensive web UI exploration platform.

  1. A Developer's Swiss Army Knife — The founding philosophy and core architecture
  2. Seven Years of .NET Modernization — Navigating the annual .NET upgrade cycle
  3. Constitution-Driven Development — How governance and AI transformed project quality
  4. Seven UI Paradigms, One Backend — Comparing seven frontend approaches side by side
  5. Modern DevOps as a Living Reference (this article) — Containerization, CI/CD, and cloud deployment

Project Links: GitHub | Live Demo | Docker Hub