最新のC#をチーム全員で書くための取り組み

この記事は [C#アドベントカレンダー2023](https://qiita.com/advent-calendar/2023/csharplang) の 9 日目の記事です。今回は、C#12 をチームみんなで使っていくために私が実践しているいろいろな取り組みについて紹介します。純粋に技術的な話ではなく泥臭い話も書かれており、他の方の記事とは毛色が異なるかもしれませんが、既存のコードベースに最新の C# を取り入れたいという悩みは多くの開発者に共通しているのではないかと思いました。同じように最新の C# を取り入れようとしている方々の参考になれば幸いです。

## 0. 最新の C# を使う難しさ

個人で最新の C# を使うことは比較的簡単ですが、チームみんなで使うとなるとさまざまな難しさがあります。私が直面したものとしては、

- ASP.NET Core での開発を始めるときに、新しく学ばなければならないこと、アンラーニングしなければならないことが多い
- 非同期（async/await）の難しさ
- パターンマッチやコレクション式などの言語機能が充実し、より簡潔なコーディングが可能になったが、これらをキャッチアップできていない人にとっては全く別の言語を読まされているような感覚で、読み書きが非常に難しい
- .NET Framework で書かれた巨大なコードベースがすでに存在しており、すべてを .NET 8 に置き換えるだけのコストは一気に払えない

などがあります。これらを少しずつ解決していくことで、少しずつ最新の C# を使えるようにしていくのが目標でした。

## 1. 最新の C# を存分に使える環境を整える

.NET Framework がまだまだ現役で使われている大規模なシステムにおいて、いきなり最新の C# や .NET を使い始めたり、すべてのアプリケーションを .NET 8 に移行させるのは容易ではありません。そのため、既存のソリューションから分離された環境で小さく始められるように工夫しました。

### 1.1. ソリューションを別立てする

最新の .NET での開発用にソリューションを別立てしました。使っているフレームワークのバージョンによってソリューションが分かれている形です。そのうえで、以下のようなポリシーで運用しています。

- 現在コアとなっている .NET Framework プロジェクト群は、当面そのままメンテし続ける（無理に移行しない）
- 新規に作成するツール・バッチ・API群は、新しいソリューションで積極的に最新の C#, .NET を利用して開発を行う。
  - まずは小さいものを実際に作って運用してみることで、最新の C# に徐々に慣れながら実績を作る
- SDK 標準のアナライザや [vs-threading](https://github.com/microsoft/vs-threading) などを活用して、ベストプラクティスを常に誰でも適用できるようにする
- 誰でも触れる CI 環境、デプロイフローを整備する（GitHub Actions）

これによって少しずつではありますが、各メンバーが最新の C# を実戦投入できるようになりました。

### 1.2. 既存資産の共有

新しいソリューションがある程度本格的に使われるようになったとき、問題になるのは既存 .NET Framework 資産の活用です。今回は Task 登場以前に書かれたようなコードベースも多く残っており、すべての資産を活用できるわけではありませんでした。そのため、既存資産の共有については以下のような方法をとりました。

- .NET Framework 時代の資産は、新しいソリューションでは無理に利用しない。必要なら現行の .NET に合ったライブラリなどを作成する
  - 社内ライブラリは、最新の .NET ではそもそもの前提が変わってしまっているもの、標準でサポートされたものも多くありました
- 複雑なビジネスロジックやデータアクセスなど、どちらのソリューションでも共通して必要なものは .NET Standard 2.0 に移行し、新旧両方のソリューションから利用する

この方法によって既存のコード自体の新陳代謝を図りつつも、既存のビジネスロジックを有効に活用できる仕組みを整えました。

## 2. スタートラインに立つための知識を共有する

ここまでで最新の C# を使うことは「技術的には可能」な状態になりました。しかしこれまで .NET Framework でバリバリ開発をやってきたメンバーにとって、いきなり最新の C#, .NET で開発を始めることは全く容易なことではなく、サポートが必要です。そこで、勉強会やその他の取り組みを通して、全員が無理なくキャッチアップできるようにしました。

### 2.1. 勉強会の開催

最近の .NET や C# の機能や、 ASP.NET Core での開発で前提となるような基礎知識について共有するための社内勉強会を実施しました。この勉強会は1年以上継続して行っており、これまでに以下のような内容を行いました。

- 新しい社内ライブラリの紹介
- ここ数年の C# の重要なアップデートについて
- async / await の基本、 `CancellationToken` や `ConfigureAwait` など
- ASP.NET Core の DI, Configuration, Logging の概要
- ASP.NET Core のミドルウェアパイプラインの概要
- バックグラウンド処理（`IHostedService`, `BackgroundService`）
- ジェネリックの共変性・反変性
- パターンマッチ
- メモリキャッシュ
- LINQ と非同期ストリーム、 `System.Threading.Channels`
- `Index`, `Range` 構文
- `Span<T>` と `Memory<T>`
- T4 から SourceGenerator までのメタプログラミング変遷
- ユニットテストの基本

勉強会をやる上で、以下のようなポイントに気をつけました。

- パターンマッチや `Span` など、実際に手を動かすことが大切なものに関しては具体的な例題や演習問題を用意することでより理解を深めてもらう
- スライドや録画を全体に共有することで、リアルタイムで参加できないメンバーや、他部署のメンバーにも見てもらいやすくする
- アンケートを実施して進め方や難易度、テーマなどを調整する
  - `Span` のように重要だが日常的には使わないものよりも、実際の業務のニーズに合ったもののほうが理解度も満足度も高かった

この勉強会の実施によって最新の C#, .NET を利用した開発のハードルが下がり、現時点では .NET 7 のプロジェクトが私以外のメンバーによってたくさん作成されています。

### 2.2. チーム横断での取り組み

各チームを横断して最新の C# を取り入れてもらうために、それぞれのタスクで困ったり迷ったりした場合に個別にサポートを行っています。その中で必要なものがあれば勉強会で適宜紹介するなどして、開発のハードルを下げていきました。

また、勉強会の取り組み自体は私が中心的に行ったものですが、 C# や .NET について深い知見を持ったメンバーは複数いるため、そういったメンバーと定期的に雑談の機会を設けることで、私自身もキャッチアップを行い、全体に還元できるようにしています。

### 2.3. 最新情報の紹介

勉強会では多くのメンバーの時間を拘束している以上、長く実践的に使えるトピックを厳選する必要があり、少しマニアックだが重要なトピックであったり、最新のリリースについて紹介する場がありませんでした。そこで「.NET Monthly」という読み物を毎月公開して、 .NET 関連のニュースや .NET に限らない社内事例の紹介を行っています。

## まとめ

最新の C#, .NET の導入に関して、私がこれまで実践してきた取り組みを簡単に紹介させていただきました。全体的にドラスティックな変更や特殊な対応を避け、コストをかけすぎずに無理なく自然に取り入れていけるようなやり方を意識しています。古いコードベースを抱えている、なかなか最新の C# を使えない、というのは C# 開発者に共有した悩みかと思いますし、ここで紹介した以外にもいろいろなやり方・考え方があると思います。もしご意見や「うちではこうしているよ」などがあればぜひコメント欄で聞かせていただきたいです！