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.

소스 정보

저장소
bitwarden/server
최근 소스 활동
2026년 8월 26일 19:23
감지된 SKILL.md 언어
영어
스타
20,341
포크
1,797

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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/)
GitHub에서 보기