Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

Thank you for your interest in improving this library!

## Developer Documentation

In-depth developer guides are in the [`/docs`](./docs/README.md) folder:

- [Local Development](./docs/local-development.md) — build, test, and format commands
- [Testing](./docs/testing.md) — how to write unit tests
- [Benchmarks](./docs/benchmarks.md) — how to write and run benchmarks
- [Composite Actions](./docs/composite-actions.md) — reusable CI actions catalogue
- [Workflows](./docs/workflows.md) — CI/CD pipeline overview
- [Branch Strategy](./docs/branch-strategy.md) — branch lifecycle and environments
- [Versioning](./docs/versioning.md) — branch naming and the version pipeline
- [API Documentation](./docs/api-documentation.md) — DocFX and the API reference site
- [Extensibility](./docs/extensibility.md) — how to add new encoding algorithms

## Guidelines

- **Follow code style:** Use `.editorconfig` and run `dotnet format`.
Expand Down
23 changes: 23 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Developer Documentation

Welcome to the developer documentation for **PolylineAlgorithm**. This section covers everything you need to contribute code, tests, benchmarks, and automation to the project.

## Contents

| Document | Description |
|---|---|
| [Local Development](./local-development.md) | Build, test, and format the codebase locally |
| [Testing](./testing.md) | How to write and run unit tests |
| [Benchmarks](./benchmarks.md) | How to write and run performance benchmarks |
| [Composite Actions](./composite-actions.md) | Reusable GitHub Actions used across workflows |
| [Workflows](./workflows.md) | CI/CD pipelines and how they connect |
| [Branch Strategy](./branch-strategy.md) | Branch lifecycle and environment mapping |
| [Versioning](./versioning.md) | Branch naming convention and the version pipeline |
| [API Documentation](./api-documentation.md) | How DocFX generates and publishes the API reference site |
| [Extensibility](./extensibility.md) | How to add new encoding algorithms |

## Quick Links

- [Contributing Guidelines](../CONTRIBUTING.md)
- [API Reference Site](https://petesramek.github.io/polyline-algorithm-csharp/)
- [GitHub Issues](https://github.com/petesramek/polyline-algorithm-csharp/issues)
136 changes: 136 additions & 0 deletions docs/api-documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# API Documentation

This document explains how API documentation is generated and published for PolylineAlgorithm.

## Toolchain

Documentation is built with [DocFX](https://dotnet.github.io/docfx/), a static site generator for .NET API references and Markdown guides.

## Repository Layout

```
api-reference/
├── api-reference.json # DocFX build manifest (site generation)
├── assembly-metadata.json # DocFX metadata manifest (API extraction only)
├── toc.yml # Top-level table of contents
├── index.md # Landing page
├── favicon.ico
├── media/ # Images and other static assets
├── guide/ # Markdown guide articles
│ ├── toc.yml
│ ├── introduction.md
│ ├── getting-started.md
│ ├── configuration.md
│ ├── advanced-scenarios.md
│ ├── sample.md
│ └── faq.md
├── 0.0/ # Auto-generated API metadata for version 0.0
├── 1.0/ # Auto-generated API metadata for version 1.0
│ └── *.yml # DocFX apiPage YAML files (one per type)
└── _docs/ # Build output (excluded from DocFX content, gitignored)
```

## Two DocFX Manifests

### `assembly-metadata.json` — API Extraction

Used by the `documentation/docfx-metadata` composite action during CI builds to extract XML documentation comments from source code and produce DocFX-compatible YAML files.

```json
{
"metadata": [{
"src": [{ "src": "../src", "files": ["**/*.csproj"] }],
"dest": "temp",
"outputFormat": "apiPage"
}]
}
```

- **Input:** All `.csproj` files under `src/`
- **Output:** YAML files in `api-reference/temp/`, then copied to `api-reference/<version>/`
- **When it runs:** Automatically after every successful `build` workflow run. The resulting YAML files are committed to the repository under `api-reference/<friendly-version>/` (e.g., `api-reference/1.2/`).

### `api-reference.json` — Site Build

Used by the `documentation/docfx-build` composite action to build the full documentation site.

```json
{
"build": {
"content": [
{ "files": ["index.md", "toc.yml", "guide/*.{md,yml}"], "exclude": ["_docs/**"] },
{ "dest": "", "files": ["*.yml"], "group": "v1.0", "src": "1.0" },
{ "dest": "", "files": ["*.yml"], "group": "v1.1", "src": "1.1" }
],
"output": "_docs",
"template": ["default", "modern"]
}
}
```

- **Input:** Markdown guide articles + versioned API YAML files
- **Output:** Static HTML site in `api-reference/_docs/`
- **When it runs:** During `release.yml` (automatic on every push to `preview/**` or `release/**`) and `publish-documentation.yml` (manual trigger).

## Publishing Flow

```
src/ changed
[build.yml] → docfx metadata → commit YAML to api-reference/<version>/
[release.yml] or [publish-documentation.yml]
docfx build → api-reference/_docs/
GitHub Pages → petesramek.github.io/polyline-algorithm-csharp
```

## Adding a New Version to the Site

When bumping the version to `X.Y`:

1. The `build` workflow automatically generates metadata YAML files into `api-reference/X.Y/` after the first build on the new branch.
2. Add a new entry to `api-reference.json` under `build.content`:
```json
{ "dest": "", "files": ["*.yml"], "group": "vX.Y", "src": "X.Y", "rootTocPath": "~/toc.html" }
```
3. Add a matching group definition:
```json
"vX.Y": { "dest": "X.Y" }
```
4. Add the new version to `api-reference/toc.yml` so it appears in the navigation dropdown:
```yaml
- name: vX.Y
href: X.Y/PolylineAlgorithm.html
```

## Writing API Documentation

All public types, interfaces, and members must have XML doc comments. DocFX picks these up automatically:

```csharp
/// <summary>
/// Encodes a sequence of coordinates into a polyline string.
/// </summary>
/// <param name="coordinates">The coordinates to encode.</param>
/// <returns>An encoded polyline string.</returns>
public string Encode(IEnumerable<(double Latitude, double Longitude)> coordinates) { ... }
```

After merging a change, verify the rendered documentation at the [API Reference Site](https://petesramek.github.io/polyline-algorithm-csharp/).

## Local Preview

To preview the documentation locally:

```bash
dotnet tool update -g docfx
docfx build ./api-reference/api-reference.json --serve
```

Then open `http://localhost:8080` in your browser.
133 changes: 133 additions & 0 deletions docs/benchmarks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Benchmarks

This guide explains the benchmark project structure and how to write and run performance benchmarks.

## Project Structure

All benchmarks live in the `benchmarks/` directory:

```
benchmarks/
└── PolylineAlgorithm.Benchmarks/
├── PolylineEncoderBenchmark.cs # Benchmarks for AbstractPolylineEncoder
├── PolylineDecoderBenchmark.cs # Benchmarks for PolylineDecoder
├── PolylineEncodingBenchmark.cs # Benchmarks for PolylineEncoding helpers
├── Program.cs # BenchmarkSwitcher entry point
└── PolylineAlgorithm.Benchmarks.csproj
```

The project targets `net8.0`, `net9.0`, and `net10.0` and references the main `PolylineAlgorithm` library along with the `PolylineAlgorithm.Utility` helper project.

## Framework

Benchmarks use [BenchmarkDotNet](https://benchmarkdotnet.org/). Key packages:

| Package | Purpose |
|---|---|
| `BenchmarkDotNet` | Core benchmarking framework |
| `BenchmarkDotNet.Diagnostics.Windows` | Windows-specific diagnostics (ETW) |

## Writing a New Benchmark

1. Create a new `.cs` file in `benchmarks/PolylineAlgorithm.Benchmarks/`.
2. Add the standard copyright header.
3. Annotate the class with `[MemoryDiagnoser]` if you want allocation tracking.
4. Use `[Params]` to parameterize input sizes.
5. Mark benchmark methods with `[Benchmark]`. Mark one with `[Benchmark(Baseline = true)]` when comparing variants.
6. Use `[GlobalSetup]` to prepare shared data once per parameter combination.

Example:

```csharp
//
// Copyright © Pete Sramek. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//

namespace PolylineAlgorithm.Benchmarks;

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;

/// <summary>
/// Benchmarks for <see cref="MyEncoder"/>.
/// </summary>
[MemoryDiagnoser]
public class MyEncoderBenchmark {
private readonly Consumer _consumer = new();

[Params(1, 100, 1_000)]
public int CoordinatesCount { get; set; }

private (double Latitude, double Longitude)[] _data = [];

[GlobalSetup]
public void Setup() {
_data = [.. RandomValueProvider.GetCoordinates(CoordinatesCount)];
}

[Benchmark(Baseline = true)]
public void EncodeArray() => new MyEncoder().Encode(_data).Consume(_consumer);
}
```

## Running Benchmarks Locally

Benchmarks **must** run in Release configuration to produce meaningful results:

```bash
dotnet run \
--project ./benchmarks/PolylineAlgorithm.Benchmarks/PolylineAlgorithm.Benchmarks.csproj \
--configuration Release \
--framework net10.0 \
-- --filter '*'
```

### Useful CLI flags

| Flag | Description |
|---|---|
| `--filter '*'` | Run all benchmarks |
| `--filter '*Encoder*'` | Run benchmarks whose name contains `Encoder` |
| `--runtimes net8.0 net9.0 net10.0` | Run on multiple runtimes |
| `--exporters GitHub` | Export results as GitHub Flavored Markdown |
| `--memory` | Enable memory diagnoser output |
| `--iterationTime 100` | Iteration time in milliseconds |
| `--join` | Merge results from multiple runs |
| `--artifacts <path>` | Output directory for results |

### Example: multi-runtime run with GitHub export

```bash
dotnet run \
--project ./benchmarks/PolylineAlgorithm.Benchmarks/PolylineAlgorithm.Benchmarks.csproj \
--configuration Release \
--framework net10.0 \
-- --runtimes net8.0 net9.0 net10.0 \
--filter '*' \
--exporters GitHub \
--memory \
--iterationTime 100 \
--join \
--artifacts /tmp/benchmarks
```

## Benchmarks in CI

The `pull-request` workflow runs benchmarks on Ubuntu, Windows, and macOS when `vars.BENCHMARKDOTNET_RUN_OVERRIDE == 'true'` or when building a release branch. Results are uploaded as artifacts (`benchmark-<os>`) and written to the workflow step summary as a Markdown table.

Relevant workflow variables:

| Variable | Description |
|---|---|
| `BENCHMARKDOTNET_WORKING_DIRECTORY` | Working directory for `dotnet run` |
| `BENCHMARKDOTNET_RUNTIMES` | Space-separated runtimes to bench |
| `BENCHMARKDOTNET_FILTER` | Filter expression passed to `--filter` |
| `DEFAULT_BUILD_FRAMEWORK` | Framework used with `--framework` |
| `BENCHMARKDOTNET_RUN_OVERRIDE` | Set to `true` to force benchmark run on PRs |

## When to Add or Update Benchmarks

- Add a new benchmark file when introducing a new encoding/decoding code path.
- Update an existing benchmark when changing the algorithmic implementation of an existing path.
- Attach benchmark results to pull requests that affect performance-sensitive code (see [CONTRIBUTING.md](../CONTRIBUTING.md)).
Loading
Loading