Skip to content

Backend Onboarding

You are about to write your first Kotlin change in an Arda backend repository. This page is the reading order. It is short on purpose: eight pages, roughly half a day, and it is the difference between a first pull request that gets reviewed for its logic and one that gets reviewed for its shape.

Set up your environment firstDevelopment Environment Setup and Development Workflows cover installing prerequisites, cloning the workspace, 1Password secrets, and the build/test commands. This page picks up where those leave off: not how to run the code, but how the code is expected to look.

Each entry says why it is where it is. Skipping ahead mostly works, except for the first two.

Data Authority Module Pattern

Read this first. Almost every backend change happens inside a module, and every module has the same four layers with the same responsibilities. Everything below is a detail inside this shape, and it is much harder to place a detail when you do not have the shape.

DAG Package Discipline

The package layout is not filing; it is enforced. Packages form a directed acyclic graph at every aggregation level, so a type in the wrong package is a cycle waiting to happen rather than a cosmetic problem. This page also covers the common.lib.* staging area — where a type lives when it is on its way to common-module but not there yet.

Module Wiring Entry Point

One function per module, a fixed signature, and a defined order of responsibilities: resolve configuration, construct services, register routes and observers, return the service handle. When you need to add a collaborator, this page says where it is built.

Endpoint Definition DSL

Never raw Ktor routing in a module. Routes are declared through the endpoint DSL, and the DSL is not decoration — it is what puts a route in the secured scope, in the generated OpenAPI spec, and in the startup collision check. A hand-mounted route silently has none of those. The page’s decision rubric tells you whether your surface wants DataAuthorityEndpoint or the lightweight serviceDefinition DSL.

API Design

URL shape, required headers, and the error contract. The rule that surprises people: a handler never builds an error response. It returns Result and lets the DSL throw, or it throws an AppError directly. A central StatusPages boundary turns that into the response — attaching the correlation id, setting Retry-After on transient failures, capturing to Sentry, and choosing the log severity. Every one of those is lost by a handler that responds on its own.

Kotlin Coding Standards

The binding language conventions. Start with the ten rules on that page, then follow its reading order — Functions and Result Handling and Nullability and Return Types are the two that change how you write every method.

7. How anything with a lifecycle is stored

Section titled “7. How anything with a lifecycle is stored”

Universe Design and Persistence and Transactions

If an entity is created, updated, soft-deleted, or otherwise evolves, it belongs in a bitemporal Universe — you get identity, version history, audit columns, and tenant isolation without writing them. A plain Exposed table is for immutable append-only records only. Anything else, ask before you build it.

Backend Testing

Kotest FunSpec, isolation mode, what a unit test is expected to cover, and where integration tests draw the line.

Two things to know before your first review

Section titled “Two things to know before your first review”

These pages are binding, not advisory. If a design document you are implementing conflicts with one of them, the pages win and the design needs amending — say so before you implement, rather than implementing the design and discovering the conflict in code review. That has happened, it cost a rewrite, and it was not the implementer’s fault.

If you think a page is wrong, say that too. Some are. Conventions here are written down after the fact by whoever hit the problem, which means they are occasionally stale, occasionally contradictory, and occasionally just wrong. Two of the pages in this list were corrected because a new joiner followed them and the result did not match what reviewers expected. Reporting that is a contribution, not a complaint.

Bring a module to current conventions when you touch it

Section titled “Bring a module to current conventions when you touch it”

You will find modules that do not match these pages — older package layouts, older idioms. The rule is opportunistic compliance: when a project touches a module, bring that module to current conventions as part of the work. Do not raise backward-correction tickets, and do not schedule migrations. Fixing it while you are already in the file is nearly free; scheduling it is not.


Copyright: (c) Arda Systems 2025-2026, All rights reserved