オレオレシステム論を考えてみる

思い起こせばプログラミングを始めて１０年以上になりました。
（SEとしては５年目）
自分なりにシステム論をまとめておこうと思います。
特定の環境に寄った話ではなく、
システム開発における課題になる項目を経験則に基づいて理想論を書いてみます。
※多分に偏った考え方を含んでいる可能性があります。

この辺違うとか足りないとか、こうゆうのあるよとか大歓迎

# 理想のシステムとは
一般的に理想的なシステムは次のようなものになると思います。
1. 要件を満たす 
2. バグが（少）ない
3. セキュリティ的に堅牢
4. モジュールが再利用できる
5. 継続的開発ができる（バージョン管理、自動テスト、自動デプロイ）
6. 冗長化されている（利用顧客が増えても大丈夫）

このうち、1は必須でしょう。2は言うもがな。
3はインフラとかミドルウェアの設定で行う場合もあり、システムで保存している情報に応じてどこまで担保するか決めるべきです。
4、5は開発サイクルをあげるために考慮しておいた方が良いと思います
5はプログラム的というよりも開発ツールで対応、
6はインフラ部分になります。

どんなシステムでもPQCD（値段、品質、コスト、納期）はあるため、
どこを優先してどこを削るかを考えるべきです。

システムの品質という意味で1,2,3,6を決めたものを一般的にSLA（Service Level Agreement）と呼びます。
SLAに関しては開発の指標になるため、開発前に設定すべきです。

# インフラ（サーバ・ネットワーク構築に関して）
インフラ構築運用には以下の形式があります（これで多分全部？）

* オンプレミス（自社でサーバを持つ、ルーティングなどネットワークの設定も必要、冗長化とか故障とかもしたら大変）
* プライベートクラウド（物理、ネットワーク部分の構築運用は他社に任せる。DBサーバの論理設計およびをアプリケーションサーバを実装）
* パブリッククラウド（プライベートクラウドとの違いはハードウェアが複数の会社で共有で物理的ではなく、論理的にセパレートされているということ）
* サーバレス（ビジネスロジックの実装以外すべてクラウド運用会社に任せる）
* Baas（ビジネスロジックの実装もクラウド運用会社側から提供された手段で実装）

自由度（インフラを自由に弄れる）と運用コストはトレードオフだと思います。
一般的に次のような順位になると思います。
システムの要件にあわせて決めましょう。

自由度（高→低）：
オンプレミス＞プライベートクラウド＝パブリッククラウド＞サーバレス＞Baas

運用コスト（安→高）：
サーバレス＞パブリッククラウド＞プライベートクラウド＞オンプレミス＞Baas

# よくある要件
システムによって、何を優先させるべきかSLAを決めるべきです。

## 社内システム
勤怠システムなどがこれに該当します。機密情報・個人情報の塊のため、高いセキュリティが求められます。自社内にサーバを持つ場合が多いです。
ただし、外部から参照されることはないため、内部ネットワークのみからアクセスさせることでセキュリティを確保できます。

## 業務システム
業務内容によりますが、業務システムは基本的に止まってはいけないため、高い稼働率を求められます。自社でインフラを運用するのが厳しい場合はプライベートクラウドを利用するのも手でしょう。

## 新規サービス（スタートアップ）
セキュリティの担保は扱っているユーザ情報によります。
新規ユーザ獲得のための仕組み（グロースハック）や収益の仕組みも考える必要があります。
既存ユーザを逃さないようにUI、UXは高い品質が求められます。
また、サービスリリース時にはすでに商標登録されていないかなど気をつける事項も多いです。

# バグを減らす
~~バグがないシステムなんてありません~~
技術進歩に伴い、プログラミング言語自体が変化したり
OSがアップデートしたりするため、バージョンアップのタイミングで意図せず不具合が混入することになります。
実運用していく限り、バグ０はほぼ不可能でしょう。諦めましょう。
（特にスマホはバージョンアップのサイクルが早いため、顕著です。）

かといって、バグを減らす努力をしないのは論外なので
納期と費用対効果を考えて対策をすべきです。

ただ動けばいいやでソースコードを書いていると後々自分たちの首を絞めることになります。
（[技術的負債](http://qiita.com/erukiti/items/9cc7850250268582dde7)と呼びます。）
手遅れになる前に対策をお勧めします。

## 継続的開発ツール（CI）を導入する
テストを自動化することで、バージョンアップ時や修正時にデグレしないか確認することができます。後になるほど導入するのが困難になるため、初期開発時に導入すべきです。

メジャーどころで次のようなCIツールがあります。

* [Jenkins](https://jenkins.io/)
* [Circle CI](https://circleci.com/)
* [Travis CI](https://travis-ci.org/)

## ソフトウェアメトリックスツールを導入し、品質を定量値で可視化する
ソフトウェアメトリックスと呼ばれる指標でソースコードの品質の定量評価が可能です。
下記のようなツールで計測が可能です。

* [Lizard](https://github.com/terryyin/lizard)
* [eclipse metrics plugin](http://www.atmarkit.co.jp/ait/articles/0606/10/news016.html)
* [grant-plato](http://qiita.com/shoito/items/cca3ae1cc6b0895c9455)

特に下記指標に関してはこまめに確認するようにしたほうが良いでしょう。

1.ファイルあたりのコード行数（LOC）
2.凝集度
3.循環的複雑度

他にもxcodeプロジェクトのクラス依存関係を可視化するツールとかもあります。

* [Objective-C And Swift Dependencies Visualizer](https://github.com/PaulTaykalo/objc-dependency-visualizer)


## スマホ開発の場合
機種依存やOSの差異が激しいため、下記はチェックするようにしましょう

* 売れ筋の機種、シェアが高い機種を優先的に対応する
* OSの最低サポートバージョンx以上と明記する（ユーザシェア率確認しつつ）
* ユーザの端末設定状態にも気をつける

~~企業的な都合で特定の機種は必ず対応しなければならない人は頑張って下さい~~
また、スタートアップでAndroid,iOS両方対応の場合ならAndroidファーストの方が良いです。~~（iOSはAppleの審査が長すぎるため）~~
最近２日程度になりました。~~でもAndroidの方がリリースまで数時間なので短い~~

## リファクタリング
かといって~~不幸にも~~既存プロジェクトの改修を迫られた場合、
リファクタリングの難易度は次のようになります。

データベース>>>アプリケーションサーバ>フロントエンド

特にデータベーステーブル構造が非正規でJOINしまくり前提だと末端のフロントエンドに至るまで全て影響が出ます。
~~誰だこんなテーブル設計したやつ~~

モジュールの影響範囲を洗い出し、
システム的に影響範囲が狭い部分から徐々に改修していくしかありません。
~~あまりにもひどい場合は作り直したほうが、データ移行込みでも案外安くつくかもしれません~~

まず、状態を把握することが何より大事です。

* MySQLの場合はWorkbenchツールにてER図を自動出力
* APIインタフェースを把握する（リクエストメソッド、リクエストパラメータ、レスポンスパラメータ）

また、DB再設計時にはRDB以外にNoSQLという選択肢もあります。
格納できるデータの自由度が高い反面、
トランザクションが無いため、アプリケーション側の実装が悪いとデータの不整合が伴う問題をはらんでいます。
保存するデータにあわせて利用するか決めましょう。（併用もありです。）
小規模システムならばNoSQLオンリーのほうが逆に実装も少なく、小回りが利いて良い場合もあります。

# モジュールが再利用できる
基本的に独立性が高いほど良いモジュールということになります。（他モジュールへの依存度が少ない）
実装部が変わっても良いように呼び出しインタフェースは決めておきましょう。
実装モジュールにデザインパターンが適応できるのであれば、適応したほうが楽できることがあります。
何のモジュールかわかるようにモジュールのドキュメント化も大事です。
メンテナンスもしやすいようにバージョン管理もしましょう

* [GoFデザインパターン](http://www.techscore.com/tech/DesignPattern/index.html/)（クラス設計ノウハウ）
* [Swagger](http://swagger.io/)（API定義フォーマット）
* [JavaDoc](http://qiita.com/maku77/items/6410c67ce95e08d8d1bd)、[Doxygen](http://www.doxygen.jp/)（ソースコードコメントからモジュールのドキュメント生成）
* [git](https://git-scm.com/)、[svn](https://subversion.apache.org/)（バージョン管理）

最近よく思うのが、車輪の再発明が多いと思う点です。
ひどい時は社内の別チームで同じようなモジュールを作ってたりしてます。
できれば、プラットフォーム別で汎用化できるモジュールは
チームをまたいでgitなどの共通のリポジトリで管理することで資産価値が高まります。
（良いコードを書く練習にもなるでしょう）

# セキュリティ対策
ユーザ入力（入力フォーム）に起因する脆弱性が非常に多いです。
また、システムの初期開発から気をつけていなければ対策が難しいものも存在します。
運用時にはアクセスログを仕掛け、どのユーザがどのようなリクエストを投げてきたか確認することが不正アクセスを見抜く上で重要になります。
セキュリティに関しては考えるとキリがないため、費用対効果を考えて実施すべきです。
（特にスタートアップのシステムの場合は最初は考えない場合が多い）
よくある攻撃は以下のようなものがあります。

* XSSサイトスクリプティング
まず。Formなどの入力欄に不正なJavascriptなどを混入させるシステムに保存させます。
共有ページにて混入されたデータを別ユーザがブラウザで開くことでJavascriptが実行され、ユーザのセッション情報などを盗み出す手法です。
Javascriptを実行するようなタグの入力受け付けないようにしたり、
ブラウザ表示時にエスケープして無効化する対策が必要です。

* セッションハイジャック
ログインできるようなシステム（といってもユーザ個別の情報を表示、保存する場合が大半なので大抵のシステムが該当します。）には、
セッションと呼ばれるデータでユーザ別のログイン状態を保持しています。
このセッション情報を盗み出したり、ログイン状態でシステムへのリクエストパラメータを改ざんする等で別のユーザとしてシステムを操作したり、ユーザの情報を盗み取る手法です。

* 盗聴
httpでの通信に関してはwiresharkなどのツールを利用することで
通信内容（パケット）の盗聴が可能です。
パスワードやクレジットカードなどの重要な情報の通信に関しては暗号化された通信が必須となります。（https,ssh,wssなど）

* SQLインジェクション
入力フォームなどから不正なSQL文を実行するような入力情報を送信する攻撃手法です。
システム側が意図していないSQLが実行されることで、システムのデータを破壊したり、データを盗み出すことが可能です。
アプリケーションサーバ側でプレースメントを使うなどで無害化することで対策は可能です。

* コマンドインジェクション
入力フォームなどから不正なOSコマンドを実行するような入力情報を送信し、システムを破壊したり、システムの情報を盗み出す攻撃手法です。
システム側がOSの機能を使っている場合（OSコマンドを実行するなど）に発生する脆弱性です。
対策にはシステム側で有効なコマンドかチェックする仕組みが必要です。

* CSRF
システムの入力フォームをコピーし、
外部のサイトからフォームを実行する（リクエストを送信する）攻撃手法です。
対策はシステムからワンタイムのトークンを払い出し、フォーム実行時に照合することで可能ですが、実装が非常に難しいです。
実装が難しい場合は、システム運用に支障が出るAPIのみは最低限かけるようにしましょう。

* DDoS
システムに対して、サービスと関係のない大量の通信負荷をかけて
システムをダウンさせる攻撃手法です。
不正な通信を行っているIPに対して、IP制限をかけることで対策が可能です。

* 標的型攻撃（フィッシング）
よくあるのがメールやSNSにて特定の標的に対して
偽サイトやウィルスサイトのURLを送付したり、
ウィルスソフトをダウンロード＆インストールさせて情報を盗み出すというものです。
特に本物サイトそっくりな偽サイト（フィッシングサイト）などにログインフォームを設けて
ログイン情報を盗み出すという手法もあります。
怪しいメールや見覚えのないメッセージは開かないのが基本ですが、
最近の標的型攻撃は巧妙なため、本物そっくりなメールなどの見分けは難しいです。
添付のURLに関してはURLのドメインやhttpsであるかなどを確認するようにしましょう。

* 中間者攻撃（改ざん、盗聴）
通信時にプロキシと呼ばれる中間サーバを経由させることで
通信内容の盗聴や改ざんを行う攻撃手法です。
フィッシングサイトもこれに該当します。

* リバースエンジニアリング
ソフトウェアを解析することで情報を盗み出す手法です。
バイナリ化したり、ソースコードを難読化するという対策が考えられますが、
Javaベースのアプリケーションの場合、デコンパイルをすることで解析が可能になります。
Javascriptの場合はブラウザ上のデバッガーなどで容易に解析が可能です。
そもそも重要な情報をパッケージソフトウェア内やブラウザに格納しないほうが無難でしょう。
例えばAndroidの場合はapkファイルを入手することでデコンパイルが可能です。

* ショルダーハッキング
もっとも原始的な方法です。
物理的に相手の背後から入力情報を盗み見るという手法です。
原始的ゆえに注意していなければ気づきにくいです。
大事な情報を入力するときは周囲に人がいないことや隠しカメラなどがないかを確認しましょう。

根本的な対策としては下記のような考え方があります
1.そもそもユーザの個人情報など重要な機密情報は扱わない（盗む価値のデータがない）
2.ネットワークを分離して外部からアクセスさせない
全体的な対策が難しい場合でも局所的にでも重要な情報を保存する場所を隔離し、アクセス可能なところを分離するということで不正アクセス元の絞り込みができます。

最近は脆弱性診断やウィルスチェックを簡単にできるサービスも出てきました。

* [walti.io](https://walti.io/)
* [virustotal](https://www.virustotal.com/ja/)

# その他
大事だと思ったこと。

## 外注（アウトソーシング）に関して
よく使われる契約書は以下のものがあります。（一部口約束等でスキップする場合もあります。）

* 秘密保持契約書
* 要件書（＋設計書）
* 見積書
* 発注書
* 納品書
* 検収書

当たり前ですが、発注側、受注側ともに
納期、対応内容、保守期間、金額、プラットフォーム（ブラウザ、機種）、言語、OS、使用ライブラリ（＋ソフトウェアライセンス）
は確認しましょう。
また、発注側も検収前にテストの義務があります。
経験則ですが、下記のテストは最低限やったほうが良いでしょう。

* ホワイトボックステストでの単体テスト
* ブラックボックステストでのシナリオテスト

発注側もシステムに関してはお金払って発注しても
検収の手間を考えると全然楽じゃないなんてことはよくあります・・・
（人月の神話というのがあるように単純に人を増やせばいいってもんでは無いのです）
デザイン部分やフロントエンド、技術的に極めて難易度が高いモジュール部分のみ依頼するというのも手かもしれません

## ドキュメントに関して
要件書や機能一覧すらないシステムがざらにあります。
（不幸にも私が担当したプロジェクトはほとんどがそうでした）
~~これ何するシステム？~~
担当者がいたのなら聞けばいいのですが、元いた担当者がドロンしてた場合が最悪です。

特に遭遇した一番厄介なケースが下記

1. 保守運用に必要なアカウント情報が残っていない
2. プログラムの設定情報がわからない
3. 特殊なライブラリを使っている場合、ライブラリの提供元に問い合わせ(特にサポートとか切れてる場合とかは最悪のケース)

1、2に関しては本当にどうしようもないので前の担当者に電話で問い合わせして聞きました。

# 最後に
いろいろ書きましたが、常に勉強することが大事です。
普遍的な考え方を一通り書いたつもりですが、
開発に関して便利なツールやサービスが次々と出てきています。
作らないで利用するというのも選択肢なので、常に視野を広く持ちましょう。
（[ITFFF](https://ifttt.com/)など既存サービスの組み合わせでソリューションになったりしてたり、[sercus](http://sercus.jp/)なんていうサービスのキュレーションサービスもあったりしてます）