
Sudarshan Dhakal
Senior Software Engineer · C# / .NET · Hammond, LA
Eleven years building on .NET, the last six at Veriforce. There I led the breakup of a 200K-line monolith into 40+ event-driven services, moved 25TB of documents out of SQL Server, and built the GitHub Copilot agents and skills our team now uses for day-to-day modernization work.
- services carved out of a 200K-LOC monolith
- 40+services carved out of a 200K-LOC monolith
- ~100M documents moved from SQL Server to S3
- 25TB+~100M documents moved from SQL Server to S3
- GitHub Copilot custom agents built and in use
- 20+GitHub Copilot custom agents built and in use
- shipping C# / .NET in production
- 11 yrsshipping C# / .NET in production
Case studies
Three projects that show how I work.
01 · Veriforce · ArchitectureBreaking up a 200K-line .NET monolith without a rewrite
Incremental, strangler-fig decomposition of the core compliance platform into 40+ event-driven services while it kept serving 100K+ users.
- services in production
- 40+
- services in production
- downtime deployments
- Zero
- downtime deployments
- less memory / CPU after the .NET 10 upgrades
- ~35% / ~30%
- less memory / CPU after the .NET 10 upgrades
Toggle details
Breaking up a 200K-line .NET monolith without a rewrite
Incremental, strangler-fig decomposition of the core compliance platform into 40+ event-driven services while it kept serving 100K+ users.
- services in production
- 40+
- services in production
- downtime deployments
- Zero
- downtime deployments
- less memory / CPU after the .NET 10 upgrades
- ~35% / ~30%
- less memory / CPU after the .NET 10 upgrades
The problem
The contractor-compliance platform ran as one 200K+ LOC .NET application: one deployable, one database, and every release coordinated across every team. A big-bang rewrite was not an option for a system customers rely on for compliance.
What I did
- Put a gateway in front of the monolith so endpoints could move to new services one at a time while the monolith kept serving everything else.
- Drew service boundaries along DDD bounded contexts. Each service owns its data in PostgreSQL, and cross-context communication moved from shared tables to events on Kafka and RabbitMQ.
- Used CQRS where read and write load diverged, keeping hot read paths off the transactional model.
- Every extraction ships through the same CI/CD pipeline with xUnit and integration tests and staged rollouts, so no single cutover is a big risk.
- Clientsweb · API · integrations
- Gatewayroutes per endpoint
- Monolithshrinking scope
- New services.NET 8/10 · DDD/CQRS
- Event busKafka · RabbitMQ
02 · Veriforce · DataMoving 25TB of documents out of SQL Server
Migrated ~100M compliance documents stored as database blobs into Amazon S3 with restartable, idempotent pipelines and no downtime.
- moved to S3
- 25TB+
- moved to S3
- documents
- ~100M
- documents
- downtime
- Zero
- downtime
Toggle details
Moving 25TB of documents out of SQL Server
Migrated ~100M compliance documents stored as database blobs into Amazon S3 with restartable, idempotent pipelines and no downtime.
- moved to S3
- 25TB+
- moved to S3
- documents
- ~100M
- documents
- downtime
- Zero
- downtime
The problem
About 100 million documents lived as binary columns in SQL Server. The database had grown past 25TB, which made backups and restores slow, storage expensive, and put heavy I/O on the same server handling transactional traffic.
What I did
- Batch pipeline copies each blob to S3 under a deterministic key, so any batch can be re-run safely. A retry never creates a duplicate.
- Progress is checkpointed per batch, so the job can stop, fail, or be throttled at peak hours and pick up where it left off.
- Each object is verified in S3 before the database row is repointed and the blob removed.
- The application read path moved to S3 while the migration ran, so there was never a maintenance window.
- SQL Serverdocument blobs
- Batch readercheckpointed
- S3 uploaddeterministic keys
- Verifysize · hash
- Repoint + purgerow → S3 key
03 · Veriforce · AI-assisted engineeringMaking Copilot agents reliable on a legacy codebase
Built 20+ GitHub Copilot custom agents and a library of agent skills that encode how we upgrade, extract, test, and review .NET services.
- custom agents
- 20+
- custom agents
- use in upgrades, extraction, tests, review
- Daily
- use in upgrades, extraction, tests, review
- agent mode, coding agent, CLI, code review
- 4 tools
- agent mode, coding agent, CLI, code review
Toggle details
Making Copilot agents reliable on a legacy codebase
Built 20+ GitHub Copilot custom agents and a library of agent skills that encode how we upgrade, extract, test, and review .NET services.
- custom agents
- 20+
- custom agents
- use in upgrades, extraction, tests, review
- Daily
- use in upgrades, extraction, tests, review
- agent mode, coding agent, CLI, code review
- 4 tools
- agent mode, coding agent, CLI, code review
The problem
With 40+ services and a monolith still being dismantled, a lot of the work is repetitive but risky: .NET version upgrades, EF6 to EF Core, pulling code out of the monolith, writing tests around old behaviour. Off-the-shelf AI assistance got the easy parts right and missed our conventions on the rest.
What I did
- Split the work into narrow custom agents (upgrade planner, service extractor, test writer, PR reviewer, incident triage) instead of one general-purpose agent.
- Wrote agent skills (SKILL.md) that capture how we actually do things: .NET upgrades, EF6 to EF Core, config migration, our service template, PR review, and Datadog log and trace analysis.
- Characterization tests are written before any refactor, so every agent change is checked against existing behaviour.
- Agent-authored PRs go through the same CI gates and human review as everyone else’s. The agent saves time; it does not skip review.
- Task / issue
- Custom agent+ matching skills
- Tests firstlock current behaviour
- Change on a branch
- CI + human reviewthen merge
How I work with AI
I use AI heavily, and I treat its output like a new teammate’s pull request: useful, fast, and reviewed. The work is making it reliable. That means narrow agents, written-down conventions, and tests that decide whether a change is correct.
- Planagent drafts, I approve
- Skills loadour conventions
- Tests firstcharacterization
- Implementsmall, reviewable diffs
- Gatesbuild · tests · analyzers
- Human reviewthen merge
Narrow agents beat one big one
An agent that only upgrades EF6 to EF Core is easier to trust, test and fix than one that “modernizes the service”.
Conventions live in skills, not in people’s heads
If a reviewer keeps leaving the same comment, it becomes part of a skill so the agent gets it right the first time.
Tests decide, not the model
Behaviour is locked with tests before the refactor. A confident explanation from the agent is not evidence.
Same bar as human code
Agent PRs get the same CI gates and code review. Nothing gets a shortcut because a tool wrote it.
From the skill library
Each skill is a SKILL.md an agent loads only when the task needs it.
- Upgrades
dotnet-version-upgradeef6-to-efcorenewtonsoft-to-system-text-jsonconfig-to-options- Extraction
extract-bounded-contextservice-templateevent-contract- Quality
characterization-testspr-reviewapi-docs- Operations
datadog-log-triagedatadog-trace-analysisrelease-workflow
What I don’t hand to an agent
- Cutover and rollback plans for data migrations
- Authentication, authorization and anything security-sensitive
- Service boundaries and other architecture decisions
- Anything that touches production directly
---
name: ef6-to-efcore
description: >
Upgrade a data-access project from Entity Framework 6 to EF Core
following our conventions. Use when a project references EntityFramework 6.x.
---
1. Run the characterization-tests skill first; stop if tests don't pass on main.
2. Swap packages: EntityFramework -> Microsoft.EntityFrameworkCore.SqlServer.
3. Replace EntityTypeConfiguration<T> with IEntityTypeConfiguration<T>;
one configuration class per aggregate, in /Persistence/Configurations.
4. Register the DbContext through AddDbContextPool in Program.cs, never in a static.
5. Lazy loading stays OFF. Add explicit Include() calls and note each one in the PR.
6. Build, run the full test suite, and open a draft PR using the pr-review checklist.Experience
- Mar 2020 – Present
Senior Software Engineer · Veriforce LLC
Contractor-compliance SaaS platform
- Led the incremental decomposition of a 200K+ LOC .NET monolith into 40+ event-driven services behind a strangler-fig gateway, with zero downtime across every extraction.
- Drove successive runtime upgrades to .NET 10 across every service, lowering memory ~35% and CPU ~30%.
- Migrated 25TB+ (~100M documents) from SQL Server to Amazon S3 with idempotent, checkpointed pipelines and per-object verification, with no downtime.
- Delivered 99.9% availability and sub-200 ms p95 latency (Datadog) across core APIs serving 100K+ users, profiling and rewriting the SQL Server and PostgreSQL queries behind the slowest endpoints.
- Jul 2017 – Mar 2020
Software Engineer · Emerson Automation Solutions
AgileOps alarm-management software for industrial operations
- Launched a real-time monitoring dashboard (.NET Core, React) giving operations teams live visibility across 500+ manufacturing assets, shortening incident response 30%.
- Integrated React incrementally into a legacy ASP.NET MVC application, converting its 2 largest pages to reusable components and halving UI development time.
- Jun 2015 – Jun 2017
Software Developer · Lamar Advertising
- Built an inventory and scheduling system (ASP.NET WebForms, JavaScript, SQL Server) handling 50K+ daily transactions for digital billboards across 200+ locations.
- Streamlined the client registration workflow, taking onboarding from 15 minutes to 3 and deflecting 40% of support tickets.
Education: B.S. Computer Science, Southeastern Louisiana University. The full history is on the resume.
What I work with
- Languages & frameworks
- C#.NET 8/10ASP.NET CoreEF CoreLINQDapperMediatRTypeScriptReactAngular
- AI tooling
- GitHub Copilot agent modeCopilot coding agentCopilot CLICopilot code reviewClaude CodeCursor
- Agent engineering
- Custom agentsagent skillscustom instructionsCopilot modernization for .NET
- Cloud
- AWS LambdaECS/ECRSQS/SNSS3RDSAPI GatewayAzure FunctionsAzure Application Insights
- Data & messaging
- SQL ServerPostgreSQLRedisCosmos DBAmazon S3KafkaRabbitMQAzure Service Bus
- Architecture
- MicroservicesDDDCQRSevent-driven designClean Architecturestrangler-fig migrationOAuth 2.0/JWT
- Delivery
- DockerKubernetesGitHub ActionsAzure DevOpsJenkinsOctopus DeployDatadogSerilogxUnitNUnitMoqTDD
Get in touch
Email is the fastest way to reach me. I usually reply within a day.