🐕

サンプルコードをそのままプロダクションコードに持ち込まない

2024/12/12に公開

サンプルコードをそのままプロダクションコードに持ち込まない

多くの方は知らないことを書籍や公式サイトなどのインターネット上のドキュメントを参考にプログラムを書いていると思います。そのドキュメントに掲載されているサンプルコードを利用することもよくあると思います。これをそのままプロダクションコードに持ち込むのは避けた方がいいというお話です。

一番言われているのが著作権の問題。掲示されているサンプルコードを自由に使っていいと明文化されているドキュメントはかなり少ないです。明示されていないものはコピペをやっていいかは怪しいです。今回の主題はこれではないので著作権の問題は割愛します。また、サンプルコードには脆弱性があったり、エラー処理を除外しているものがあり、これらもプロダクションコードにそのまま持ち込んではいけませんが、この問題も割愛します。

何が問題なのか?

サンプルコードとプロダクションコードでは目的が大きく違います。要はサンプルコードをそのままプロダクションコードに持ち込むのは、目的が合わなくなってしまい、のちのちのプログラムの変更に支障をきたすようになります。

例えば C# は var という型推論する機能があります。これを説明するサンプルコードは次のように書かれます。

// 明示しなくても int 型になる。
var i = 1;

このコメントは var を知らない人向けの説明です。サンプルコードがそれを知らない人向けにわかりやすく解説することを目的としているのに対し、プロダクションコードは保守性 (読みやすく変更しやすい) を目的としています。

プロダクションの開発では var を知らない C# プログラマーはあまりいません。もしいたとしても、ペアプロやレビューもしくは教育などを通じて知識を共有すればよいのです。プロダクションコードで var を書くたびにいちいち型をコメントに書いていたら、コードを読む際のノイズになりますし、コードの修正で右辺の型が変わってしまったとき、コメントまで丁寧に治すプログラマーは半分もいません。コメントがさらなるノイズになります。

さすがに「これくらいは当たり前だろう!」と思っているでしょうけど、この例に限らず似たようなことをしているプロダクションコードは見かけます。

他の例として、.NET 標準の DI を紹介します。.NET 標準の DI は次のようにコードで構成を組み立てていきます。(公式サイトの ASP.NET Core での依存関係の挿入から引用しています)

Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency>();

// ...

ここで「DI の構成は Program.cs に書くんだな」と理解してサンプルコードのままプロダクションコードに持ち込むのは危険です。

実際のプロダクションコードでは DI に登録するクラスが 100 個くらいあってもおかしくありません。すべて Program.cs に書いてしまうと、修正のたびに修正すべき場所に加えて、そこから遠く離れている Program.cs も同時に修正しなければなりません。

EC サイトでカートの改善を行っている開発者にとって、カート以外の DI の構成は興味の範疇外でしょう。ですので、巨大になった Program.cs の多くはコードを読んだり修正したりする際のノイズになります。

実際のプロダクションコードではカート機能のサービスクラスの近くで DI の構成をします。

Carts/CartService.cs
class CartService : ICartService
{
    // ...
}
Carts/CartExtensions.cs
public static class CartExtesions
{
    public static IServiceCollection AddCartServices(this IServiceCollection services)
    {
        return services
            .AddScoped<ICartService, CartService>()
            .AddScoped</* 他にもいろいろ */>();
    }
}
Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

// 直接 `AddScoped<>()` で登録するのではなく、カート機能が追加されたくらいが分かるように書く。
builder.Services.AddCartServices();

// ...

このように書くことで、カートを改善する開発者は Carts ディレクトリの中のコードを中心に読んで修正していけばいいわけです。(説明のためにカート機能をディレクトリで分離しましたが、実際には.NET プロジェクトで分離したほうがよいでしょう) 新しい機能を追加するのでなければ Program.cs を読む必要はありませんし、たとえ Program.cs を修正するにしてもそれほど長くないので修正が容易になります。

実際に .NET 標準のライブラリは Program.cs 内ですべてのサービスを登録するのではなく、標準設定でよければ一行で機能を追加できるようになっています。サンプルコードに挙げた AddRazorPages() がそうです。このメソッドの中でたくさんのサービスを登録しています。

Program.cs
builder.Services.AddRazorPages();

DI の使い方のサンプルコードは理解するために利用し、実際のコードの書き方はよく使っているフレームワークの類を参考にした方がよいのです。

短さは百難隠す

昔 HONDA CITY という乗用車がありました。CITY (二代目) は車両重量がとても軽いという特徴がありました。その代償としてボディ剛性が低く、ジムカーナ (駐車場のようなところにコースを作ってタイムを競う競技) で利用するとボディが歪んでいるのが分かるそうです。ボディが歪んでいるということは、サスペンションが設計通りの動きをしないということで、操縦が難しくなったりしてタイムが出にくいものです。でも軽さはそのボディ剛性が低いデメリットを打ち消してなお余りあるメリットとして存在し、きびきび動き加速性能や減速性能に優れ、実際速かったようです。販売が終了しても結構後まで利用されていてびっくりしたものです。

ソースコードも同様で、コードの短さは多くの問題を隠します。コードが長くなるにつれて、非線形で問題が深刻になっていきます。結果よく言われているように可読性が低下し、修正が困難になっていきます。

サンプルコードは説明のためになるべく短く、説明に不要なコードを極力取り除いています。短いので、保守性の悪さに気が付きにくいです。規模の大きいプロダクションコードでそのまま持ち込み続けると、どんどん保守性が悪化します。

最後に

プロダクションコードで重要なことの一つに保守性がありますが、サンプルコードは保守性は重要ではありません。サンプルコードで重要なのはそれを知らない人に対する理解を早めることです。プロダクションコードで重要な可読性とはまた違うものです。

サンプルコードとプロダクションコードは目的が大きく異なります。サンプルコードをそのままプロダクションコードに持ち込まないようにし、プロダクションコードの目的に合わせて修正します。

もちろん、最初はわからないからサンプルコードをそのまま真似して、理解が進んでからリファクタリングしていくようなやり方も OK です。

このブログで紹介した例だけでなく、ほかにもたくさんあります。Unity だと MonoBehavior を継承したコンポーネントにあらゆる処理を詰め込んでいるプロダクションコードをよく見かけます。そのようなコンポーネントの修正は熾烈を極めます。Unity の書籍などのサンプルコードの多くはそうなっていますが、プロダクションコードではサンプルコードはあくまで理解を深めるための参考で、保守性を考えてコードを書きます。

Discussion