.NET Development Workflow
Workflow Overview
┌─────────────────────────────────────────────────────────────────┐ │ DEVELOPMENT WORKFLOW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ │ │ │ Understand │ Read requirements, explore codebase │ │ │ Task │ │ │ └──────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ Implement │ Write code following patterns │ │ │ Changes │ │ │ └──────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ Validate │────▶│ Report │ │ │ │ Build/Test/ │ │ Results │ │ │ │ Analyze │ │ │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────┐ ┌──────────┐ │ │ │ PASS? │───NO───▶│ Fix │ │ │ └─────────┘ │ Issues │ │ │ │ └──────────┘ │ │ │ │ │ │ YES │ │ │ │ │ │ │ ▼ │ │ │ ┌──────────────┐ │ │ │ │ Ready to │◀──────────┘ │ │ │ Commit │ (re-validate) │ │ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘
Phase 1: Understand the Task
Feature Implementation
-
Read the feature requirements/user story
-
Identify affected components
-
Check existing patterns in codebase
-
Plan the implementation approach
Bug Fix
-
Reproduce the bug
-
Identify root cause
-
Find related code
-
Plan the fix
Refactoring
-
Understand current implementation
-
Identify what needs to change
-
Ensure test coverage exists
-
Plan incremental changes
Phase 2: Implement Changes
Follow Existing Patterns
// Find existing patterns // Look for similar implementations in the codebase // Follow established conventions
// Example: If services follow this pattern public class ExistingService : IExistingService { private readonly IRepository _repository; private readonly ILogger<ExistingService> _logger;
public ExistingService(IRepository repository, ILogger<ExistingService> logger)
{
_repository = repository;
_logger = logger;
}
}
// New service should follow same pattern public class NewService : INewService { private readonly IRepository _repository; private readonly ILogger<NewService> _logger;
public NewService(IRepository repository, ILogger<NewService> logger)
{
_repository = repository;
_logger = logger;
}
}
Make Small, Incremental Changes
-
One logical change at a time
-
Build after each change to catch errors early
-
Run relevant tests frequently
-
Keep commits focused
Phase 3: Validate Changes
Validation Steps
1. Build (catch compilation errors)
dotnet build --no-incremental
2. Run tests (verify behavior)
dotnet test --no-build
3. Static analysis (code quality)
dotnet build /p:TreatWarningsAsErrors=true dotnet format --verify-no-changes
Quality Gates
Gate Requirement Blocking
Build 0 errors Yes
Tests 100% pass Yes
Critical Warnings 0 No
All Warnings < 10 No
Phase 4: Fix Issues
Build Errors
-
Read error message carefully
-
Go to the file and line indicated
-
Fix the issue
-
Rebuild to verify
Test Failures
-
Read the assertion failure
-
Check expected vs actual
-
Determine if test or code is wrong
-
Fix and re-run test
Analysis Warnings
-
Review each warning
-
Apply fix or suppress with justification
-
Use dotnet format for auto-fixable issues
Validation Before Commit
Checklist
-
dotnet build succeeds with no errors
-
dotnet test passes all tests
-
No new critical analyzer warnings
-
Code follows existing patterns
-
Changes are focused on the task
Commands
Full validation
dotnet build --no-incremental &&
dotnet test --no-build &&
dotnet format --verify-no-changes
Best Practices
Code Organization
// Group related code // 1. Fields private readonly IService _service;
// 2. Constructors public MyClass(IService service) => _service = service;
// 3. Public methods public void Execute() { }
// 4. Private methods private void Helper() { }
Error Handling
// Be specific with exceptions public User GetUser(int id) { var user = _repository.Find(id); if (user == null) throw new EntityNotFoundException($"User {id} not found"); return user; }
// Use guard clauses public void Process(Request request) { ArgumentNullException.ThrowIfNull(request); ArgumentException.ThrowIfNullOrEmpty(request.Name);
// Main logic
}
Async/Await
// Always use async suffix public async Task<User> GetUserAsync(int id) { return await _repository.FindAsync(id); }
// Don't block on async // BAD var user = GetUserAsync(id).Result;
// GOOD var user = await GetUserAsync(id);
Dependency Injection
// Register services services.AddScoped<IUserService, UserService>(); services.AddSingleton<ICacheService, MemoryCacheService>(); services.AddTransient<IEmailSender, SmtpEmailSender>();
// Inject via constructor public class UserController { private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
}
Common Patterns
See patterns.md for detailed implementation patterns.