Skip to main content

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.

Informações da origem

Repositório
bitwarden/server
Última atividade na origem
26 de agosto de 2026 às 19:23
Idioma detectado do SKILL.md
inglês
Estrelas
20.477
Forks
1.815

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
writing-server-code
description
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.
## 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/)
Ver no GitHub