LeadershipTechnical ManagerTechnical Leadership

Session 03 - Engineering Standards

"Bất kỳ ai cũng có thể viết code máy hiểu. Lập trình viên giỏi viết code người hiểu." - Martin Fowler


1. Coding Standards cho .NET Projects

Naming Conventions

ElementConventionVí dụ
NamespacePascalCase, theo folder structureMyApp.Application.Services
Class / InterfacePascalCase, Interface prefix IOrderService, IOrderRepository
MethodPascalCase, verb-firstGetOrderById(), ValidatePayment()
PropertyPascalCaseCustomerName, IsActive
Parameter / LocalcamelCaseorderId, customerName
Private field_camelCase (underscore prefix)_orderRepository, _logger
ConstantPascalCaseMaxRetryCount, DefaultTimeout
Async methodSuffix AsyncGetOrderByIdAsync()
BooleanPrefix Is/Has/Can/ShouldIsValid, HasPermission

Anti-Patterns to Avoid

csharp
// BAD: Hungarian notation
string strName;
int intAge;
List<Order> lstOrders;

// GOOD: Descriptive names
string customerName;
int age;
List<Order> activeOrders;

// BAD: Magic numbers
if (retryCount > 3) { ... }
Thread.Sleep(5000);

// GOOD: Named constants
private const int MaxRetryAttempts = 3;
private static readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(5);
if (retryCount > MaxRetryAttempts) { ... }
await Task.Delay(RetryDelay);

// BAD: God method
public async Task ProcessOrder(Order order) 
{
    // 200 lines doing validation, payment, inventory, email...
}

// GOOD: Single Responsibility
public async Task ProcessOrder(Order order)
{
    await _validator.ValidateAsync(order);
    await _paymentService.ChargeAsync(order);
    await _inventoryService.ReserveAsync(order.Items);
    await _notificationService.SendConfirmationAsync(order);
}

Method Length & Complexity Guidelines

MetricTargetTool
Method length< 30 lines (guideline, not hard rule)SonarQube
Cyclomatic complexity< 10 per methodRoslyn Analyzers
Class length< 300 linesSonarQube
Parameters per method< 4 (dùng object nếu nhiều hơn)StyleCop
Nesting depth< 3 levelsRoslyn Analyzers

2. EditorConfig & .NET Analyzers Setup

.editorconfig cho .NET

ini
# Top-level EditorConfig
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.cs]
# Namespace
dotnet_style_namespace_match_folder = true

# Using directives
csharp_using_directive_placement = outside_namespace
dotnet_sort_system_directives_first = true

# var preferences
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion

# Expression-bodied members
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_properties = true:suggestion

# Null checking
dotnet_style_null_propagation = true:warning
csharp_style_throw_expression = true:suggestion

# Naming rules
dotnet_naming_rule.private_fields_should_be_camel_case.severity = warning
dotnet_naming_rule.private_fields_should_be_camel_case.symbols = private_fields
dotnet_naming_rule.private_fields_should_be_camel_case.style = camel_case_underscore
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.camel_case_underscore.required_prefix = _
dotnet_naming_style.camel_case_underscore.capitalization = camel_case

# Severity levels
dotnet_analyzer_diagnostic.severity = warning
dotnet_diagnostic.CA1062.severity = warning  # Validate arguments
dotnet_diagnostic.CA2007.severity = warning  # ConfigureAwait
dotnet_diagnostic.CS8600.severity = error    # Null literal

Directory.Build.props - Central Analyzer Config

xml
<Project>
  <PropertyGroup>
    <AnalysisLevel>latest-recommended</AnalysisLevel>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="SonarAnalyzer.CSharp" Version="9.*" />
    <PackageReference Include="StyleCop.Analyzers" Version="1.2.*" />
    <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.*" />
    <PackageReference Include="Roslynator.Analyzers" Version="4.*" />
  </ItemGroup>
</Project>

Tip: TreatWarningsAsErrors = true buộc team fix analyzer warnings ngay, không để tích tụ.


3. Documentation Standards

Inline Comments - When and How

csharp
// BAD: Comment nói lại code (useless)
// Increment counter by 1
counter++;

// BAD: Comment thay vì rename
// Check if the user can access the resource
if (u.r == 1 && u.a == true) { ... }

// GOOD: Explain "why", not "what"
// Retry 3 times because payment gateway has intermittent 503 errors
// during peak hours (see incident INC-2024-089)
await Policy.Handle<HttpRequestException>()
    .RetryAsync(3)
    .ExecuteAsync(() => _paymentClient.ChargeAsync(amount));

// GOOD: Document non-obvious business rule
// Tax-exempt for orders shipped to specific regions per regulation VN-TAX-2024
if (IsRegionTaxExempt(order.ShippingAddress.RegionCode))
    order.TaxAmount = 0;

XML Documentation cho Public APIs

csharp
/// <summary>
/// Creates a new order and initiates the payment process.
/// </summary>
/// <param name="request">Order creation request containing items and shipping info.</param>
/// <param name="cancellationToken">Cancellation token for async operation.</param>
/// <returns>The created order with payment status.</returns>
/// <exception cref="InsufficientStockException">
/// Thrown when one or more items are out of stock.
/// </exception>
/// <exception cref="PaymentDeclinedException">
/// Thrown when the payment gateway declines the transaction.
/// </exception>
public async Task<OrderResponse> CreateOrderAsync(
    CreateOrderRequest request,
    CancellationToken cancellationToken = default)

README Template cho mỗi Service/Project

markdown
# [Service Name]

## Overview
[1-2 sentences mô tả purpose]

## Tech Stack
- .NET 8, ASP.NET Core Minimal API
- SQL Server, Entity Framework Core
- MassTransit + RabbitMQ

## Getting Started
1. Prerequisites: .NET 8 SDK, Docker
2. `docker compose up -d` (dependencies)
3. `dotnet run --project src/MyService.Api`

## Architecture
[Link to architecture diagram]

## API Documentation
- Swagger UI: https://localhost:5001/swagger
- [Link to Postman collection]

## Configuration
| Variable | Description | Default |
|----------|-------------|---------|
| `ConnectionStrings__Default` | SQL Server connection | ... |

## Testing
- `dotnet test` - run all tests
- `dotnet test --filter Category=Integration` - integration only

4. Git Conventions

Commit Messages - Conventional Commits

<type>(<scope>): <description>

[optional body]

[optional footer]
TypeKhi nào dùngVí dụ
featFeature mớifeat(orders): add bulk order creation endpoint
fixBug fixfix(auth): resolve token refresh race condition
refactorCode refactoringrefactor(payments): extract PaymentValidator class
perfPerformance improvementperf(reports): add caching for dashboard queries
testAdd/update teststest(orders): add edge case tests for discount calc
docsDocumentationdocs(api): update Swagger descriptions for v2
choreBuild, CI, dependencieschore(deps): update EF Core to 8.0.4
ciCI/CD changesci: add SonarQube analysis to PR pipeline
main ─────────────────────────────────────────────→
  │          │                    │
  ├─ feat/order-bulk-create ─────┤ (short-lived, < 2 days)
  │                              │
  ├─ fix/token-refresh ──────────┤ (merged same day)
  │                              │
  └─ release/v2.3.0 ────────────────→ (only if needed)

Rules:

  • Feature branches sống < 2 ngày - merge sớm, merge thường
  • Dùng feature flags thay vì long-lived branches
  • Squash merge cho clean history
  • Protected main branch - require PR + 2 approvals + CI pass

5. Definition of Done Checklist

Team-Level DoD

markdown
## Definition of Done - [Team Name]

### Code Quality
- [ ] Code follows team coding standards (EditorConfig + analyzers pass)
- [ ] No new SonarQube issues (quality gate passed)
- [ ] Self-review completed before requesting PR
- [ ] PR reviewed and approved by >= 2 team members

### Testing
- [ ] Unit tests written for new logic (coverage >= 80%)
- [ ] Integration tests for API endpoints
- [ ] Edge cases and error scenarios covered
- [ ] All existing tests pass (no regressions)

### Documentation
- [ ] Public API has XML documentation
- [ ] README updated if new setup steps needed
- [ ] Swagger/OpenAPI spec updated for API changes
- [ ] Architecture Decision Record (ADR) written for significant decisions

### Deployment
- [ ] Database migration tested on staging
- [ ] Feature flag configured (if applicable)
- [ ] Environment variables documented
- [ ] CI/CD pipeline passes (build + test + analysis)

### Acceptance
- [ ] Acceptance criteria verified
- [ ] Demonstrated to PO in Sprint Review
- [ ] No known defects remaining

6. Quality Gates in CI/CD Pipeline

Pipeline Stages & Gates

┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│  BUILD   │───→│  TEST    │───→│ ANALYZE  │───→│  DEPLOY  │
│          │    │          │    │          │    │          │
│ dotnet   │    │ Unit     │    │ SonarQube│    │ Staging  │
│ build    │    │ Integr.  │    │ CodeQL   │    │ Canary   │
│          │    │ Coverage │    │ License  │    │ Prod     │
└──────────┘    └──────────┘    └──────────┘    └──────────┘
     │               │               │               │
   Gate 1          Gate 2          Gate 3          Gate 4
  Build OK      Tests pass     Quality gate     Smoke test
  No errors     Coverage>80%   No critical      Health check
                               No high vuln     Error rate<1%

Azure DevOps Pipeline Example

yaml
stages:
  - stage: Build
    jobs:
      - job: BuildAndTest
        steps:
          - task: DotNetCoreCLI@2
            inputs:
              command: build
              arguments: --configuration Release /warnaserror
          
          - task: DotNetCoreCLI@2
            inputs:
              command: test
              arguments: >
                --configuration Release
                --collect:"XPlat Code Coverage"
                --logger trx
          
          # Gate: Coverage check
          - script: |
              coverage=$(cat coverage.xml | grep line-rate | head -1)
              if (( $(echo "$coverage < 0.80" | bc -l) )); then
                echo "##vso[task.logissue type=error]Coverage below 80%"
                exit 1
              fi

  - stage: Analyze
    jobs:
      - job: SonarQube
        steps:
          - task: SonarQubePrepare@5
          - task: DotNetCoreCLI@2
            inputs: { command: build }
          - task: SonarQubeAnalyze@5
          - task: SonarQubePublish@5
          # Gate: Quality gate status
          - task: sonar-buildbreaker@8

Nguyên tắc: Quality gates phải fail the build - không chỉ warning. Nếu chỉ warning, team sẽ ignore.


Session tiếp theo: Session 04 - Architecture Governance sẽ đi sâu vào cách quản trị kiến trúc phần mềm.