Contributing
How to contribute to Courier MFT — bug reports, feature requests, and pull requests.
Contributing to Courier
Thank you for your interest in contributing to Courier! This guide covers everything you need to get started.
Code of Conduct
By participating in this project, you agree to treat everyone with respect. Be constructive, be patient, and assume good intentions.
How to Contribute
Reporting Bugs
- Search existing issues first to avoid duplicates.
- Use the Bug Report template when creating a new issue.
- Include steps to reproduce, expected vs actual behavior, and environment details.
- Attach logs or screenshots if they help illustrate the problem.
Suggesting Features
- Check the project roadmap for planned features.
- Use the Feature Request template when opening an issue.
- Describe the use case and problem you're trying to solve, not just the solution.
Submitting Code
- Fork the repository and create a branch from
main. - Pick an issue — look for issues labeled
good first issueif you're new. - Follow the development setup and conventions below.
- Open a pull request targeting
main. The PR template will guide you. - Ensure CI checks pass before requesting review.
Development Setup
Prerequisites
Getting Started
# Clone your fork
git clone https://github.com/<your-username>/Courier.git
cd Courier
# Install frontend dependencies
cd src/Courier.Frontend
npm install
cd ../..
# Start everything with Aspire (Postgres, Seq, API, Worker, Frontend)
cd src/Courier.AppHost
dotnet run
Running Tests
# Full test suite
dotnet test Courier.slnx
# Individual suites
dotnet test tests/Courier.Tests.Unit # Fast, no Docker
dotnet test tests/Courier.Tests.Architecture # Dependency rules
dotnet test tests/Courier.Tests.Integration # Requires Docker (Testcontainers)
dotnet test tests/Courier.Tests.JobEngine # Engine pipeline tests
E2E Tests (Playwright)
Requires the Aspire stack to be running:
cd src/Courier.Frontend
# First time: install browsers
npx playwright install chromium
# Run tests (auto-discovers Aspire ports)
e2e-run.bat # All tests
e2e-headed.bat # Visible browser
e2e-ui.bat # Playwright UI mode
Project Conventions
Architecture
Courier uses vertical slice architecture with strict dependency layers:
Api / Worker → Features → Infrastructure → Domain (BCL-only)
- Domain has zero external NuGet dependencies (enforced by architecture tests).
- Features are organized by domain area (Jobs, Connections, Keys, etc.), each owning its controller, service, DTOs, and validators.
- Infrastructure contains EF Core DbContext and credential encryption. It does NOT reference Features.
Backend
- API responses: Always return
ApiResponse<T>orPagedApiResponse<T>— never throw for business errors. - Services: Concrete classes (no interfaces), scoped lifetime, inject
CourierDbContextdirectly. - Entity IDs:
Guid.CreateVersion7(), set in the service layer. - Soft delete:
IsDeleted+DeletedAtwith EF global query filters. - Timestamps:
CreatedAt/UpdatedAtset manually in service code. - Database changes: Always use DbUp SQL migrations in
src/Courier.Migrations/Scripts/. Never use EF migrations. - SQL conventions: snake_case tables and columns,
UUIDPKs,TIMESTAMPTZ,BOOLEANsoft delete. - Serialization: System.Text.Json everywhere (Newtonsoft only for Quartz.NET store).
- Validation: FluentValidation, validated inline in controller actions.
- Audit logging: All entity CRUD operations should be audit-logged via
AuditService.
Frontend
- Stack: Next.js 15, React 19, TypeScript, Tailwind CSS, shadcn/ui.
- API hooks: TanStack Query hooks in
src/lib/hooks/per domain. - Types:
src/lib/types.tsmirrors backend DTOs exactly. - API client:
src/lib/api.ts— throwsApiClientErrorwith structured error info.
Testing
- Unit tests: InMemory EF database (unique per test), Shouldly assertions.
- Integration tests:
WebApplicationFactory+ Testcontainers PostgreSQL. - Architecture tests: NetArchTest enforces dependency rules.
- E2E tests: Playwright with 4 max parallel workers.
Commit Messages
Write clear, concise commit messages:
- Use imperative mood: "Add S3 connector" not "Added S3 connector"
- Reference issues when applicable: "Fix connection timeout handling (#42)"
- Keep the subject line under 72 characters
Branch Naming
feature/short-description # New features
fix/short-description # Bug fixes
docs/short-description # Documentation
refactor/short-description # Refactoring
Pull Request Process
- One concern per PR — don't mix unrelated changes.
- Update tests for any behavior changes. New features should include unit tests at minimum.
- Update types — if you change a backend DTO, update
src/lib/types.tsto match. - Add migrations — if you change the database schema, add a DbUp script in
src/Courier.Migrations/Scripts/following theNNNN_description.sqlnaming pattern. - CI must pass — the
pr-checkworkflow runs build, tests, and lint. - Review feedback — address all review comments or explain why you disagree.
What We Look For in Review
- Does it follow existing patterns in the codebase?
- Are there tests?
- Is the migration safe (no data loss, backward compatible)?
- Are credentials encrypted at rest if stored?
- Is audit logging in place for new entity operations?
- Does the frontend match backend DTO shapes?
Database Migrations
All schema changes go through DbUp. Never modify the EF model to drive schema changes.
# Add a new migration
# Create: src/Courier.Migrations/Scripts/NNNN_description.sql
# Convention: sequential number, lowercase_snake_case description
SQL conventions:
UUIDprimary keys withDEFAULT gen_random_uuid()TIMESTAMPTZfor all timestampsBOOLEANfor soft delete (is_deleted)BYTEAfor encrypted fieldsJSONBfor structured datasnake_casefor all table and column names
Getting Help
- Questions: Open a Discussion
- Bugs: File an issue
- Features: Request a feature
License
By contributing to Courier, you agree that your contributions will be licensed under the Apache License 2.0.