Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

  1. Safety First: Avoid unsafe code unless absolutely necessary and strictly isolated/documented.
  2. Idiomatic Rust: Follow standard conventions. If "fighting the borrow checker", reconsider the design.
  3. Documentation: All public APIs must be documented.

Tooling

  • Formatter: We use rustfmt with the standard configuration.
  • Linter: We use clippy. CI pipelines should fail on clippy warnings.

Topics

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

  1. Create a new branch for your ADR.
  2. Add the new ADR file in doc project.
  3. Ensure the file name uses the next available number.
  4. Submit a Pull Request for review.

Git Workflow

This section outlines our Git workflow, integrating branching, committing, and issue management.

  1. Branching: Create a new branch for each task. See Branching Strategy.
  2. Committing: Write clear, conventional commit messages. See Commit Guidelines.
  3. Issues: Link your work to specific issues. See Issue Management.
  4. ADRs: Reference architectural decisions if applicable. See ADR Linking.

Workflow Steps

  1. Pick an issue (or create one).
  2. Create a branch from dev (for new features) or master (for incident fixes).
  3. Implement changes.
  4. Commit changes following the Commit Guidelines.
  5. Push branch and open a Pull Request.
  6. 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> or type/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:

TypeDescriptionSemVer Impication
featA new featureMINOR
fixA bug fixPATCH
docsDocumentation changesPATCH
styleFormatting, missing semi-colons, white-space (no code change)PATCH
refactorCode change that neither fixes a bug nor adds a featurePATCH
perfA code change that improves performancePATCH
testAdding missing tests or correcting existing testsPATCH
buildChanges that affect the build system or external dependenciesPATCH
ciChanges to our CI configuration files and scriptsPATCH
choreOther changes that don't modify src or test filesPATCH
revertReverts a previous commitPATCH

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 commitlint default 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).

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