Programming Languages
Welcome to the Languages section of the Dita Engineering Guidelines. Here you will find specific standards, best practices, and tooling requirements for the programming languages we use.
Supported Languages
- Rust - Guidelines for writing safe, concurrent, and practical systems in Rust.
Goals of this section:
- Ensure consistency across codebases.
- Promote language-specific idiomatic practices.
- Define standard tooling and configuration (linters, formatters).
Rust Engineering Guidelines
This section details the standards and best practices for Rust development at Dita. Our goal is to leverage Rust's safety guarantees while maintaining readable and maintainable code.
Core Principles
- Safety First: Avoid
unsafecode unless absolutely necessary and strictly isolated/documented. - Idiomatic Rust: Follow standard conventions. If "fighting the borrow checker", reconsider the design.
- Documentation: All public APIs must be documented.
Tooling
- Formatter: We use
rustfmtwith the standard configuration. - Linter: We use
clippy. CI pipelines should fail on clippy warnings.
Topics
- Project Structure (To be added)
- Error Handling (To be added)
- Testing (To be added)
- Async/Await (To be added)
Rust project structure — rust-project-structure.md
Below is a recommended repository layout for a Rust service that follows Clean Architecture principles and a functional-programming-friendly style. After the tree I describe the purpose and accepted contents of each entry so maintainers and contributors know what belongs where.
/ # repo root
├── .github/ # CI/CD pipelines
│ └── workflows/
│ ├── common.yml
│ ├── feature.yml
│ ├── dev.yml
│ ├── master.yml
│ └── release.yml
│
├── docker/ # Docker build context (multi-stage, test images, compose helpers)
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── README.md
│
├── proto/ # Shared gRPC contracts (.proto files)
│ ├── student.proto
│ ├── instructor.proto
│ └── common.proto
│
├── config/ # Environment-specific configuration
│ ├── application.yml
│ ├── application-dev.yml
│ ├── application-stage.yml
│ └── application-prod.yml
│
├── scripts/ # Dev & Ops helper scripts (migrate, seed, local-run)
│ ├── migrate.sh
│ ├── seed.sh
│ ├── local-run.sh
│ └── README.md
│
├── migrations/ # SQL migrations (for sqlx / refinery)
│ ├── 0001_init.sql
│ ├── 0002_add_instructor_table.sql
│ └── README.md
│
├── src/
│ ├── main.rs # Bootstrap entry: load config, init telemetry, start runtime
│ │
│ ├── bin/ # Optional binaries (e.g., worker, migrate, consumer)
│ │ ├── migrate.rs
│ │ └── consumer.rs
│ │
│ ├── core/ # ❖ Domain & Use Cases — pure, testable, framework-free
│ │ ├── domain/ # Entities, ValueObjects, domain errors, domain events
│ │ │ ├── student.rs
│ │ │ ├── instructor.rs
│ │ │ ├── errors.rs
│ │ │ └── mod.rs
│ │ │
│ │ ├── ports/ # Abstract traits (repositories, message brokers, cache)
│ │ │ ├── student_repository.rs
│ │ │ ├── instructor_repository.rs
│ │ │ ├── message_broker.rs
│ │ │ └── mod.rs
│ │ │
│ │ └── usecases/ # Application layer orchestration
│ │ ├── enroll_student/
│ │ │ ├── command.rs
│ │ │ ├── query.rs
│ │ │ └── tests.rs
│ │ │
│ │ ├── assign_instructor/
│ │ │ ├── command.rs
│ │ │ ├── query.rs
│ │ │ └── tests.rs
│ │ │
│ │ └── mod.rs
│ │
│ ├── adapters/ # ❖ Adapters — implementing external boundaries (infra)
│ │ ├── http/ # Axum controllers + route definitions
│ │ │ ├── routes.rs
│ │ │ └── handlers/
│ │ │ ├── student_handler.rs
│ │ │ ├── instructor_handler.rs
│ │ │ └── mod.rs
│ │ │
│ │ ├── grpc/ # tonic server + client implementations
│ │ │ ├── student_grpc.rs
│ │ │ ├── instructor_grpc.rs
│ │ │ └── mod.rs
│ │ │
│ │ ├── persistence/ # SQLx repositories (Postgres)
│ │ │ ├── student_repository.rs
│ │ │ ├── instructor_repository.rs
│ │ │ └── mod.rs
│ │ │
│ │ ├── kafka/ # rdkafka producers & consumers
│ │ │ ├── producer.rs
│ │ │ ├── consumer.rs
│ │ │ └── mod.rs
│ │ │
│ │ ├── cache/ # Redis adapters (deadpool-redis)
│ │ │ ├── redis_cache.rs
│ │ │ └── mod.rs
│ │ │
│ │ ├── auth/ # JWT / OIDC middleware
│ │ │ ├── jwt.rs
│ │ │ └── mod.rs
│ │ │
│ │ └── external/ # third-party HTTP/gRPC integrations
│ │ ├── payment_service.rs
│ │ ├── logging_service.rs
│ │ └── mod.rs
│ │
│ ├── infrastructure/ # ❖ Composition root: wiring + builders + runtime setup
│ │ ├── bootstrap.rs # app startup orchestration
│ │ ├── config/ # config loader using `config` crate (YAML + env)
│ │ │ ├── loader.rs
│ │ │ └── mod.rs
│ │ ├── db.rs # Postgres connection builder
│ │ ├── cache.rs # Redis pool builder
│ │ ├── kafka.rs # Kafka setup
│ │ ├── server/ # Axum + Tonic server builders
│ │ │ ├── http_server.rs
│ │ │ ├── grpc_server.rs
│ │ │ └── mod.rs
│ │ ├── telemetry/ # tracing, otel, prometheus exporters
│ │ │ ├── tracing.rs
│ │ │ ├── metrics.rs
│ │ │ └── mod.rs
│ │ └── mod.rs
│ │
│ ├── shared/ # Common utils shared across layers (pure helpers)
│ │ ├── error.rs # unified AppError type
│ │ ├── json.rs # serde/json helpers
│ │ ├── time.rs # chrono/date helpers
│ │ └── result.rs # functional Result combinators / error adapters
│ │
│ ├── tests/ # Integration & E2E tests
│ │ ├── integration/
│ │ │ ├── db_integration_test.rs
│ │ │ ├── kafka_integration_test.rs
│ │ │ └── mod.rs
│ │ └── e2e/
│ │ ├── user_flow_test.rs
│ │ └── mod.rs
│ │
│ └── lib.rs # Optional if the service exposes internal lib functions
│
├── Cargo.toml
└── README.md
Development Guidelines
Architecture Decision Records (ADRs)
Architecture Decision Records (ADRs) are short text documents that capture an important architectural decision made along with its context and consequences. We use ADRs to keep track of the history of our decisions and the reasoning behind them.
File Location
All ADRs must be placed in the doc project.
Naming Convention
ADR files should be named using a sequential number and a short descriptive title, separated by a hyphen. The format is NNNNNN-title-of-the-decision.md.
Example: 000001-record-architecture-decisions.md
Statuses
An ADR can have one of the following statuses:
- Proposed: The decision is being discussed and has not yet been agreed upon.
- Accepted: The decision has been agreed upon and should be implemented.
- Rejected: The decision was proposed and discussed but was not accepted. The record is kept for historical context.
- Deprecated: The decision was accepted in the past but is no longer applicable or recommended.
- Superseded: The decision has been replaced by a newer decision. The new ADR should reference the superseded one.
Template
Use the following template for new ADRs:
# NNNNNN. Title of the Decision
Date: YYYY-MM-DD
## Status
[Proposed | Accepted | Rejected | Deprecated | Superseded]
## Context
What is the issue that we're seeing that is motivating this decision or change?
## Decision
What is the change that we're proposing and/or doing?
## Consequences
What becomes easier or more difficult to do and any risks introduced by the change that will need to be mitigated.
How to Submit
- Create a new branch for your ADR.
- Add the new ADR file in doc project.
- Ensure the file name uses the next available number.
- Submit a Pull Request for review.
Git Workflow
This section outlines our Git workflow, integrating branching, committing, and issue management.
- Branching: Create a new branch for each task. See Branching Strategy.
- Committing: Write clear, conventional commit messages. See Commit Guidelines.
- Issues: Link your work to specific issues. See Issue Management.
- ADRs: Reference architectural decisions if applicable. See ADR Linking.
Workflow Steps
- Pick an issue (or create one).
- Create a branch from
dev(for new features) ormaster(for incident fixes). - Implement changes.
- Commit changes following the Commit Guidelines.
- Push branch and open a Pull Request.
- Ensure CI checks (including
commitlint) pass.
Branching Strategy
We use a strategy allowing for parallel development using short-lived branches. The repository has two persistent branches: main and dev.
Core Branches
- master: The production-ready state. Contains stable code.
- dev: The main development branch. All new features are merged here first.
Branch Naming & Strategy
Feature Branches
Used for adding new functionality.
- Source Branch:
dev - Naming Convention: Must include the issue number.
- Format:
feat/issue-<number>/<short-description> - Example:
feat/issue-42/add-login-page
Bug Fix Branches
Used for fixing critical bugs or incidents in production.
- Source Branch:
master(preferred for hotfixes/incidents) - Naming Convention: Must include the incident number.
- Format:
fix/incident-<number>/<short-description> - Example:
fix/incident-808/resolve-memory-leak
Other Branches
For documentation, refactoring, or chores.
- Source Branch:
dev(usually) - Format:
type/<scope>/<short-description>ortype/issue-<number>/<short-description> - Example:
docs/readme/update-setup
Branches should be deleted after merging.
Commit Guidelines
High-quality commit messages are essential for maintaining a healthy codebase. They help new developers understand context, simplify debugging with git blame, and allow for automated changelog generation.
Message Structure
A commit message consists of a Header, Body, and Footer.
<type>(<scope>): <subject>
<body (optional, but recommended)>
<footer (optional)>
1. The Header
The header is mandatory and must be 50 characters or less.
Type
Must be one of the following:
| Type | Description | SemVer Impication |
|---|---|---|
| feat | A new feature | MINOR |
| fix | A bug fix | PATCH |
| docs | Documentation changes | PATCH |
| style | Formatting, missing semi-colons, white-space (no code change) | PATCH |
| refactor | Code change that neither fixes a bug nor adds a feature | PATCH |
| perf | A code change that improves performance | PATCH |
| test | Adding missing tests or correcting existing tests | PATCH |
| build | Changes that affect the build system or external dependencies | PATCH |
| ci | Changes to our CI configuration files and scripts | PATCH |
| chore | Other changes that don't modify src or test files | PATCH |
| revert | Reverts a previous commit | PATCH |
Scope
The scope is optional but recommended. It specifies the "place" of the commit change.
- Examples:
auth,api,ui,database,deps. - Format:
feat(auth): ...
Subject
The subject contains a succinct description of the change.
- Imperative mood: "Add" not "Added", "Fix" not "Fixed".
- No period: Do not end with
.. - LowerCase: First letter is usually lowercase (Conventional Commits doesn't enforce this, but consistency is key. Note: "Seven Rules" says capitalized, Conventional Commits examples often show lowercase. We will follow lowercase to match the
commitlintdefault config unless configured otherwise.). -> Self-correction: The previous file said "Capitalize". "Seven Rules" says Capitalize. Conventional Commits is agnostic. Let's stick to Capitalize as it looks more professional in Git logs, matching the previous rule.
2. The Body
The body is optional but strongly recommended for non-trivial changes.
- Wrap at 72 characters: This ensures readability in all environments.
- Motivation: Explain why you are making this change.
- Contrast: Compare the new behavior with the previous behavior.
- Explanation: Explain what and why, not how (the code explains the how).
3. The Footer
The footer is used for meta-information.
Breaking Changes
All breaking changes have to be mentioned in the footer with the description of the change, justification and migration notes.
- Format: Start with
BREAKING CHANGE: <description> - Alternative: Add
!after the type/scope in the header (e.g.,feat(api)!: ...).
References
- Issues:
Closes #123,Fixes #42. - ADRs:
ADR: 004.
Collaboration
- Co-authored-by:
Co-authored-by: Name <name@example.com>
Examples
Feature with Scope
feat(auth): Add Google OAuth login support
Add support for Google OAuth 2.0 to allow users to sign up
and log in using their Google accounts. This simplifies the
onboarding process.
Closes #101
Bug Fix with Breaking Change
fix(api): Handle null values in user response
Previously, the API crashed when the user had no address.
Now, it returns a null address field instead.
BREAKING CHANGE: The `address` field in the user object
can now be null. Clients must update their code to handle
this case.
Documentation
docs(readme): Update installation instructions
Revert
revert: let us never speak of this again
This reverts commit 6b2a412.
Issue Management
Properly linking Git activity to issues ensures a transparent history and automated workflow states.
Linking in Commits
Reference issues in your commit footer.
Closing Issues
To automatically close an issue when the commit is merged:
Closes #123
Or for multiple issues:
Closes #123, #245
Referencing Issues
To simply reference an issue without closing it (e.g., "See also"):
Refs #123
Linking in Pull Requests
In the description of your Pull Request, you can also use keywords to link issues.
- "Closes #123"
- "Fixes #123"
- "Resolves #123"
This creates a link in GitHub and automatically closes the issue when the PR is merged into the default branch.
ADR Linking
Architecture Decision Records (ADRs) explain the "why" behind significant changes. It is crucial to link code changes to the decisions that authorized them.
Linking ADRs in Commits
If a commit implements a specific ADR, reference it in the footer of the commit message, similar to issues.
Format:
ADR: <ADR-Number>
Example:
feat(database): migrate to postgres
We are migrating to Postgres to support better transaction handling as decided.
Closes #45
ADR: 0012
Linking ADRs in Issues/PRs
When opening an Issue or PR that relates to an architectural decision, include a link to the ADR file in the description.
Example:
Implements ADR-0012: Use Postgres