モダンな技術で安全なアプリ開発


本記事の目的

アプリを安全に開発するために学んだことをまとめます

安全とは

以下の事項に着目しました
1. 開発したアプリが意図した通りに動作する
2. 実装した内容を明確に把握できる
3. 機能向上やバグフィックスが行える

安全の重要性

  1. アプリ全体が意図した通りに動作することで、人間の要求を満たせる
  2. 既存の実装を明確に把握することで、その後意図した実装を行える
  3. アプリは機能向上とバグ改善が避けられない

実現のために

基本的なツール、設計手法、マインドセットは以下のものだと考えています
1. Gitを使う
2. テストを書く
3. 可読性を保つ
4. 凝集度・結合度

以下では一つずつ見ていきます

Gitを使う

Gitはバージョン管理ツールです。これによって、実装内容のセーブと差分比較が行えます。

実装内容をセーブできるメリット
1. 新しい実装内容を、それまでの実装と明確に区切って実装できる(安心していじれる)
2. 間違えた修正を行った時に、戻りたいセーブ地点まで完全に戻れる

差分比較のメリット
1. どこでバグを生んだか見当がつけやすい(変更差分のある所にバグはある)
2. チームレビューや自分で見直す時に、変更内容を把握できる

メリットをなくさないために
セーブ地点でちゃんと変更内容をコメントする
変更内容を細かく区切って、一つの変更ごとに一つのコミット(セーブ)を行う

テストを書く

テストは大きく分けると以下の2つがあります
1. ユニットテスト
2. インテグレーションテスト

ユニットテストは、関数などのモジュール毎に動作を確認するために行います。
インテグレーションテストは、ユニットテストで確認したモジュールを組み合わせてちゃんと動作するか確認を行います

テストを書くメリット
1. モジュールが意図した通りに動くか(テスト本来の役割)
2. 意図した動作ができているかのテストを書くことで、テストケースが仕様書になる(実装内容の把握)
3. リファクタリングでバグを生んでないか確認できる(テストがこけてはダメ)

インテグレーションテストは複数のモジュールが組み合わさるので、変更に弱くテストの寿命が短くなります。
そのため、インテグレーションテストでは、必要不可欠な機能の動作確認を行えるレベルで行うべきです。

メリットをなくさないために
ユニットテストは可能な限り細かく行う(初めが肝心)
機能改善をしたときにはテストはこけるので、新しくテストを書き直す
Gitでリモートレポジトリにpushしたときに、CIツールを用いてテストを自動化することでテストを要所要所で走らせる

可読性を保つ

可読性とは、実装内容の理解のしやすさの度合いです。実装内容の素早い理解は開発速度も上げます。

以下のことを心がけることが必要条件です
1. 変数、関数、クラスなどの名前はその内容が把握できるようにつける(プログラムはそれらから構成される)
2. 直感的な実装方法を選ぶ(レビュー時に説明・理解できない内容は複雑な実装!!)
3. モジュールは大きくなりすぎないように作る(複雑なものは理解しづらい、疲れる、楽しくない)

結合度・凝集度

抽象的な概念ですがイメージとしてこの二つは、コードのスパゲッティ化を抑えるための概念です。
以下で結合度が低い、凝集度が強いについて説明しますが、一般にこれは良い特徴です。
これらの特徴を持ったモジュールの設計を行うと、それぞれのモジュールの影響範囲がどこまで及ぶか把握しきれず、それらから構成されるプログラムの実装内容は、把握したくても把握できないアプリになってしまいます

結合度が低い良いモジュールとは、他の部分の実装内容に依存せず、それだけで動作できるモジュールです。
アンチパターンの代表は、他のモジュールのデータを操作したり、複数のモジュールがグローバル変数を共有したり、他のモジュールの実装ありきでモジュールを作成するなどです。他のモジュールの実装を意識して、新たに実装を行うのは結合度が高くなるので避けるべきで、そのことに気づいたときに踏みとどまるべきです。

凝集度が強い良いモジュールとは、その中の機能がすべて関連し合っていて、変更するときにすべて同時に変更されるようなモジュールです。オブジェクト指向のクラスで例えると、その中のメソッドがすべて、そのクラスが果たすべき役割を果たすのに必要なものだけが集まっているものです。良いモジュールであれば、そのモジュール内のメソッドはすべて一つの目的を達成するために存在しているので、一通りみればそのモジュールの役割が理解できるものになっています。

要するに良い特徴とは、一つのモジュールは一つの役割を担当し、他の役割は他のモジュールに任せることで、他のモジュールに影響を与えず実装もわかりやすいということです。どちらの概念も一つのモジュールは自分の役割に専念し、他の内容の事を考えないということです。もし、他の機能が必要なら、その機能を満たしているモジュールを呼び出して使うことで解決します。

したがって、アプリは単一の責任を持ったモジュール群のオーケストレーションで意図した動作を発揮することが大事です