# Writing Server Code

> Bitwarden server code conventions for C# and .NET. Use when working in the server repo, creating commands, queries, services, or API endpoints. Also use when writing xUnit tests with `SutProvider`/`BitAutoData`, registering DI, or generating entity IDs.

- Skill: `bitwarden/writing-server-code` (Agent Skill)
- Install (CLI): `npx skillmds@latest add bitwarden/writing-server-code`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bitwarden/writing-server-code/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: bitwarden (https://skillmd.com/u/bitwarden)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bitwarden/writing-server-code

---


## Architectural Rationale

### Command Query Separation (CQS)

New features should use the CQS pattern — discrete action classes instead of large entity-focused services. See [ADR-0008](https://contributing.bitwarden.com/architecture/adr/server-CQRS-pattern).

**Why CQS matters at Bitwarden:** The codebase historically grew around entity-focused services (e.g., `CipherService`) that accumulated hundreds of methods. CQS breaks these into single-responsibility classes (`CreateCipherCommand`, `GetOrganizationApiKeyQuery`), making code easier to test, reason about, and modify without unintended side effects.

**Commands** = write operations. Change state, may return result. Named after the action: `RotateOrganizationApiKeyCommand`.

**Queries** = read operations. Return data, never change state.

**When NOT to use CQS:** When modifying existing service-based code, follow the patterns already in the file. Don't refactor to CQS unless explicitly asked. If asked to refactor, apply the pattern only to the scope requested.

### Caching

When caching is needed, follow the conventions in [CACHING.md](https://github.com/bitwarden/server/blob/main/src/Core/Utilities/CACHING.md). Use `IFusionCache` instead of `IDistributedCache`.

**Don't implement caching unless requested.** If a user describes a performance problem where caching might help, suggest it — but don't implement without confirmation.

### GUID Generation

Always use `CoreHelpers.GenerateComb()` for entity IDs — never `Guid.NewGuid()`. Sequential COMBs prevent SQL Server index fragmentation that random GUIDs cause on clustered indexes, which is critical for Bitwarden's database performance at scale.

### Library shape

When creating or modifying code under `src/Libraries/`, read [src/Libraries/LIBRARY.md](../../../src/Libraries/LIBRARY.md) — it is the canonical shape and covers public surface, settings, endpoints, repositories, and cross-library dependencies.

### Comment discipline

Write comments for the non-obvious *why*, not the *what*. Code that speaks for itself gets no comment; a non-doc comment earns its place only when it records a rationale the code cannot express, and it stays to one line when possible. When a `public` type or member needs documenting, terse `///` XML doc comments state its contract instead of restating the signature.

## Critical Rules

These are the most frequently violated conventions. Claude cannot fetch the linked docs at runtime, so these are inlined here:

- **Use `TryAdd*` for DI registration** (`TryAddScoped`, `TryAddTransient`) — prevents duplicate registrations when multiple modules register the same service
- **File-scoped namespaces** — `namespace Bit.Core.Vault;` not `namespace Bit.Core.Vault { ... }`
- **Nullable reference types are enabled** (ADR-0024) — use `!` (null-forgiving) when you know a value isn't null; use `required` modifier for properties that must be set during construction
- **`Async` suffix on all async methods** — `CreateAsync`, not `Create`, when the method returns `Task`
- **Controller actions return `ActionResult<T>`** — not `IActionResult` or bare `T`
- **Testing with xUnit** — use `[Theory, BitAutoData]` (not `[AutoData]`), `SutProvider<T>` for automatic SUT wiring, and `Substitute.For<T>()` from NSubstitute for mocking

## Examples

### GUID generation

```csharp
// CORRECT — sequential COMB prevents index fragmentation
var id = CoreHelpers.GenerateComb();

// WRONG — random GUIDs fragment clustered indexes
var id = Guid.NewGuid();
```

### DI registration

```csharp
// CORRECT — idempotent, won't duplicate
services.TryAddScoped<ICipherService, CipherService>();

// WRONG — silently duplicates registration, last-wins causes subtle bugs
services.AddScoped<ICipherService, CipherService>();
```

### Namespace style

```csharp
// CORRECT — file-scoped
namespace Bit.Core.Vault.Commands;

// WRONG — block-scoped
namespace Bit.Core.Vault.Commands
{
    // ...
}
```

## Further Reading

- [C# code style](https://contributing.bitwarden.com/contributing/code-style/csharp/)
- [Server architecture](https://contributing.bitwarden.com/architecture/server/)

