# Standard: Code Quality
## EditorConfig
Every repo must have a `.editorconfig`. Base rules:
```ini
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
```
### Language-Specific Overrides
| Language | Indent | Additional |
|----------|--------|-----------|
| F# / C# | 4 spaces | |
| Python | 4 spaces | |
| TypeScript / JavaScript | 2 spaces | |
| YAML / JSON | 2 spaces | |
| XML / csproj / fsproj | 2 spaces | |
| Makefile | tabs | |
| Go | tabs | |
## Line Endings
**LF everywhere.** Enforced by `.gitattributes`:
```
* text=auto eol=lf
```
Plus language-specific extensions marked as `text eol=lf` and binary files marked as `binary`.
## Formatting
| Language | Formatter | Enforcement |
|----------|-----------|-------------|
| F# | Fantomas | pre-commit hook or CI |
| C# | `dotnet format` | pre-commit hook or CI |
| Python | ruff format | pre-commit hook |
| TypeScript | prettier | pre-commit hook |
| Go | gofmt | pre-commit hook |
## Linting
Enable the strictest reasonable lint rules for your language:
| Language | Linter | Recommended Config |
|----------|--------|--------------------|
| F# / C# | Roslyn analyzers | `TreatWarningsAsErrors` in Directory.Build.props |
| Python | ruff check | Extended rules: E, W, F, I, B, C4, UP, ARG, SIM |
| TypeScript | ESLint | strict config |
## Warnings as Errors
Production code should compile cleanly:
- .NET: `true` in `Directory.Build.props`
- Python: `pyright` strict mode
- TypeScript: `strict: true` in tsconfig.json
## Nullable / Strict Types
- .NET: `enable`
- TypeScript: `strictNullChecks: true`
- Python: type annotations + pyright strict
## .NET Language Selection
**Default: F# for core logic, C# for UI bindings.**
F# is preferred for domain modelling, data pipelines, and Tagless-Final architectures. C# is used for Avalonia/MAUI XAML bindings where tooling requires it.
**Exception — Orleans**: If the project uses Microsoft Orleans (virtual actors, grain persistence), use **C# throughout**. Orleans' serialization code generation, grain interfaces, and source generators are C#-first. F# interop is technically possible but causes friction with `[]`, surrogate patterns, and grain state serialization.
## Silver Thread Principle
Every feature must be implemented as a **silver thread** — an unbroken chain from input to output and back:
```
Trigger (user action, API call, scheduled event)
→ Processing (business logic, data transformation)
→ State change (database, config, in-memory)
→ Presentation (API response, UI update, notification)
→ Observable outcome (user sees the result)
```
**Before marking any task complete, trace the full thread:**
1. **Input**: What triggers this feature?
2. **Processing**: What code runs?
3. **State**: What changes?
4. **Output**: What does the user/caller observe?
**If ANY link in the chain is broken, the task is NOT done.** Common failures:
| Failure | Symptom |
|---------|---------|
| UI exists but not wired | Dead buttons, empty panels |
| Backend processes but UI never reads | Invisible feature |
| Tests pass but feature doesn't work e2e | Integration gap |
| Config saved but never reloaded | Settings don't take effect |
| API endpoint exists but no caller | Unreachable code |
For UI projects, a task is **not done** until:
- Controls are laid out and styled
- Event handlers are wired and functioning
- Data is live (not placeholder text)
- Build is clean (0 errors, 0 warnings)
- The feature is smoke-tested end-to-end
## Architecture: Tagless-Final (Default)
For projects with multiple backend providers or external dependencies, use the **Tagless-Final** pattern as the default architecture. Define capabilities as abstract records of functions parameterized over the effect type. Wire concrete implementations at the composition root.
This applies to any abstraction boundary: API clients, storage, AI backends, search engines, etc.
### F# Example
```fsharp
type EmailProvider<'F> = {
ListMessages: DateTimeOffset option -> 'F
GetAttachments: string -> 'F
}
// Concrete: GmailProvider : EmailProvider
// Test: FakeProvider : EmailProvider
```
### C# Example
```csharp
// Interface approach (when implementations need state/DI)
public interface IEmailProvider
{
Task> ListMessagesAsync(DateTimeOffset? since, CancellationToken ct);
Task> GetAttachmentsAsync(string messageId, CancellationToken ct);
}
// Concrete: GmailProvider : IEmailProvider
// Test: FakeEmailProvider : IEmailProvider (simple class, not mocked)
```
### Python Example (Protocol-based equivalent)
```python
from typing import Protocol
class EmailProvider(Protocol):
def list_messages(self, since: datetime | None) -> list[EmailMessage]: ...
def get_attachments(self, message_id: str) -> list[EmailAttachment]: ...
```
### TypeScript Example
```typescript
interface EmailProvider {
listMessages: (since?: Date) => F;
getAttachments: (messageId: string) => F;
}
```
### Why
- Testability: swap in pure/fake interpreters without mocks
- Extensibility: new provider = new record value, no interface changes
- Composability: pipeline stages stay abstract over their effects
- Clarity: capabilities are explicit, not hidden behind DI registrations
## F# Idiom Standard
For F# projects, the architecture above must be combined with idiomatic F# coding practices. See `skills/fsharp-dev/standards/idiomatic-fsharp.md` for the comprehensive standard covering function size, pipelining, mutation avoidance, task composition, Option/Result combinators, type discipline, module organisation, and deep Tagless-Final implementation patterns.
## C# Idiom Standard
For C# projects, see `skills/csharp-dev/standards/idiomatic-csharp.md` for the comprehensive standard covering immutability (records), non-nullability, primary constructors, LINQ pipelines, pattern matching, value semantics, and practical Tagless-Final with interfaces.
## TypeScript Idiom Standard
For TypeScript projects, see `skills/typescript-dev/standards/idiomatic-typescript.md` for the comprehensive standard covering strict mode, discriminated unions, readonly data, Result types, branded types, and Tagless-Final with capability interfaces.
## Python Idiom Standard
For Python projects, see `skills/python-dev/standards/idiomatic-python.md` for the comprehensive standard covering full type annotations, frozen dataclasses, Protocol-based Tagless-Final, structural pattern matching, Result types, and comprehensions.