UISampleSpark: Modern DevOps as a Living Reference
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 Series — 5 articles
- UISampleSpark: A Developer's Swiss Army Knife
- UISampleSpark: Seven Years of .NET Modernization
- UISampleSpark: Constitution-Driven Development
- UISampleSpark: Seven UI Paradigms, One Backend
- UISampleSpark: Modern DevOps as a Living Reference
UISampleSpark: Modern DevOps as a Living Reference
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, UISampleSpark 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 UISampleSpark 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 upgrademultiple times catches dependency chains - Non-root execution: The container runs as
appuser, limiting exploit impact - Vulnerability auditing:
apk auditruns 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/mwhsamplewebVisit 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
UISampleSpark 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 UISampleSpark Has Become
Seven years after its first commit, UISampleSpark 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
DevSpark integration with 16 specialized agents demonstrates structured, governed AI-assisted development covering the complete lifecycle.
Project Health: February 2026
| Metric | Value |
|---|---|
| Constitution Compliance | 100% (30/30 MUST requirements) |
| Build Status | Clean (0 errors, 0 warnings) |
| Test Pass Rate | 100% (240/240) |
| Line Coverage | 94.1% |
| Branch Coverage | 77.3% |
| Method Coverage | 95.9% |
| Total Commits | 657 |
| Active UI Implementations | 7 |
| CI/CD Workflows | 3 |
| DevSpark Agents | 16 |
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, what would become UISampleSpark started as a personal reference project — a developer's Swiss Army knife for testing .NET patterns. Seven years and more than 650 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. UISampleSpark's evolution mirrors the evolution of the .NET ecosystem itself — and it's ready for whatever comes next.
Additional Resources
Explore More
- UISampleSpark: Seven Years of .NET Modernization -- Part 2 of the UISampleSpark Series: Continuous Modernization
- UISampleSpark: A Developer's Swiss Army Knife -- Part 1 of the UISampleSpark Series: The Foundation
- Embracing Azure Static Web Apps for Static Site Hosting -- Discover the Power of Azure for Modern Web Hosting
- UISampleSpark: Constitution-Driven Development -- Part 3 of the UISampleSpark Series: Governance and AI
- UISampleSpark: Seven UI Paradigms, One Backend -- Part 4 of the UISampleSpark Series: The UI Showcase
