プログラム上の変数やロジックの意味を現実のビジネスルールと合わせることの重要性

通常、初回リリースの時点では、ソースコードは現実のビジネスルールを反映したものになっています。
(なっているべきです)

そして、障害対応や保守開発でソースコードを修正する際は、将来の保守性を犠牲にしないために、ビジネスルールに沿った修正を行うべきです。
ビジネスルールに沿わない修正を行ってしまうと、ソースコードの解読が困難になったり、後々新たなバグが生まれやすくなったりします。


例えば、麺・スープが入った器に具材を入れて料理を完成させるシステムを開発しているとします。
取り扱う料理は以下の通りとします。

また、

という処理が存在するとします。

ある日、「みそラーメンにはこしょうではなく七味を入れたい」という要望が入り、システムを改修するとします。

ここで、「みそラーメンの分類をうどんにする」という修正を行うべきではありません。
この修正でも要望に応えることはできますが、ビジネスルール上はみそラーメンはうどんに分類するべきではないので、このような修正を行うと保守性が低下します。
システムの実装を良く知らない人がソースコードを読んだ場合、「みそラーメンはラーメンである」という予想を立ててソースコードを読むことになるので、「実装上はみそラーメンはうどんである」となっていると、ソースコードの読解を誤るリスクが高まります。
また、将来、「うどんには揚げ玉を入れたい(ラーメンには入れたくない)」という要望が入り、「分類がうどんの場合は揚げ玉を入れる」という処理を追加してしまうと、みそラーメンにも揚げ玉が入るというバグを埋め込んでしまいます。

ここは、処理を以下のように修正し、ビジネスルールを崩さないようにすることが望ましいです。


いかがでしたでしょうか。

プログラムの改修は直せば良いというものではなく、その後の保守のしやすさも考えて直す必要があります。
今回は、実務で目にしたケースを元に、記事を書いてみました。

ソースコード中に顧客情報を記述してはいけない

特定の顧客用に特別なビジネスロジックを用意している場合、以下のようにソースコード中に顧客情報を記述することで実装することはできます。
(顧客番号が’1111111’である場合に特別なビジネスロジックを実行するとします)


しかし、この実装方法には以下の問題があり、望ましくありません。
特別なビジネスロジックを適用する対象の顧客の情報は、(データの管理にDBを使用しているシステムであれば)DBに持たせるべきです。

・頻繁なソースコード修正が必要になる

対象となる顧客が追加されたり削除されたりする度にソースコード修正が必要になる。
コンパイルやデプロイも必要となり、対応工数や対応のリスクが増加する。
→DBで管理すればDB内の情報の補正だけで済む。

・データを一元管理できなくなる

対象となる顧客を調べるためにはソースコードを見なくてはならなくなる。
→DBで一元管理すれば情報の検索が容易になる。

・セキュリティを確保するのが難しくなる

ソースコードが流出した場合に顧客情報も同時に流出してしまう。
→DBで一元管理すれば、顧客情報の流出のリスクを減らすことができる。


対象となる顧客の情報をDBに持たせる場合、以下のような実装になります。

1.特定処理対象顧客テーブルを別途作る場合

※特定処理対象顧客テーブルには、特定処理の対象となる顧客番号を格納する。

2.既存のテーブルにフラグを持たせる場合

※「特定処理対象フラグ」のカラムを追加し、処理対象なら’1’、処理対象外なら’0’をセットする。

いかがでしたでしょうか。

ソースコード中に顧客情報を記述してしまい保守性が低下するという話は良く聞きますし、私自身もそのようなソースを保守開発して苦労したことがあります。
また、最近ではソースコードと共に顧客情報が流出したという事故もニュースになっています。
そのような背景があり、今回の記事を書いてみました。

顧客情報が記述されたソースコードを目にする機会は珍しくないと思いますが、それはアンチパターンであるというのは認識しておくべきでしょう。

システム改修で発生するデータ移行とは

通常のシステムでは、データベースやファイル等にデータを蓄積します。
そして、システムの改修の際に、そのデータのフォーマットを変更することがあります。

データのフォーマットの変更には、「これまでに蓄積されたデータをどのように扱うのか」という問題が付きまといます。
開発者は、データを移行するのかしないのか、移行するのであればどのように移行するのか、ということを判断する必要があります。
ここでは、データ移行について、具体的な例を用いて見て行こうと思います。

【例】

商品購入履歴テーブルにて、清算番号をキーとし、いつ誰が何をどれだけ購入したのかを管理しているとします。
システム稼働開始時は、商品コードから単価は一意に求まるとします。
また、当該テーブルを見て、各々の顧客が過去に商品を購入したことがあるかどうかを見る必要があるとします。

ここで、各々の清算において値引きを可能にするシステム改修を行うとします。
また、売り上げを正しく求めるため、清算毎の単価を新たに管理する必要が出てきたとします。

この場合、商品購入履歴テーブルのフォーマットを以下のように変更する必要が出てきます。

ここで、過去に蓄積されたデータの扱いについて、以下のような方針を考える必要が出てきます。

【対応方針】

1.データ移行せず、旧商品購入履歴テーブルをそのまま残す

データ移行が困難であり、プログラムの改修箇所が少ないのであれば、データ移行しないという選択肢があります。
データ移行が困難な状況としては、データ量が多く時間がかかる、システムを停止できる時間が少ない、といった要因があります。

この方針の場合、過去の購入履歴を参照するプログラムに対して改修が必要になります。
新商品購入履歴テーブルと旧商品購入履歴テーブルの両方を見る、という改修を行う必要があります。
例えば、顧客コード”0000001″の顧客が過去に商品を購入したかどうかを見たい場合、新商品購入履歴テーブルになければ旧商品購入履歴テーブルを見る、とする必要があります。

この方針のデメリットとしては、プログラムロジックが複雑になり、保守性が低下することが挙げられます。
もし、自動化されていない運用作業で商品購入履歴テーブルを見る必要がある場合、その作業が煩雑になることにも注意する必要があります。運用ミスの原因にもなり得ます。

2.データ移行する

データ移行する場合、旧商品購入履歴テーブルのデータを、新商品購入履歴テーブルに移し替える必要があります。
システムを一時的に停止し、移行用のプログラムを実行させ、旧商品購入履歴テーブルのデータを新商品購入履歴テーブルのフォーマットに合わせた形で移行します。
(失敗に備えてバックアップを取得することも重要です)

データ移行する際には、フォーマットをどのように合わせるのかを考える必要があります。
今回の例で言うと、移行したデータの単価に何をセットするのか、ということを考える必要があります。
移行日以前の単価を見る必要が無いのであれば、nullで問題ありません。
見る可能性があるのであれば、現在の単価(過去に単価が変動しているのであれば当時の単価)をセットする必要があります。


いかがでしたでしょうか。

今回の記事の通り、データフォーマットを変更する場合は、変更後に追加されるデータのことだけでなく、それまでに蓄積されたデータをどうするのかということも考える必要があり、その分の工数やリスクを考慮する必要があります。

今回書いたようなことは記事として書かれることが少なく、経験則に近いものがあります。
経験則を記事に起こして共有可能とすることは重要だと思うので、今後も経験則を記事にしていきたいと思います。

Webデザインの超概要

Webデザインを学習するにあたり全体像を見渡す記事が必要と感じたので、作成しました。
情報処理技術者試験の参考書のようなノリで書いていきます。

1.近年のWebページの傾向

エンドユーザーの目に触れる部分について、HTMLの機能をそのまま使ったシンプルなページは少なくなり、ボタンやラベル等の見た目を装飾したページやアニメーションを付けたページが主流になっている。
PCと共にスマートフォンも一般的に使われるようになっており、スマートフォンの表示への対応(レスポンシブデザイン)も必要になっている。
また、従来のページでは、「①ブラウザからサーバへリクエストを送信」「②サーバからブラウザへレスポンスを送信」「③ブラウザでページを更新」を繰り返していたが、③を部品の入れ替えのみで済ませページ更新を伴わないSPA(Single Page Application)が採用されることも増えてきた。SPAでは、簡単な中間の操作では①②を伴わないこともある(ブラウザ上のメモリに保持したデータの操作だけで処理が完結する)。

これらの傾向は、エンドユーザーに感動を与える(UX、User eXperience)ために生まれたものである。
エンドユーザーに分かりやすさや快適さを提供することで、サービスの利用を促すことを目的としたものである。

なお、上記で挙げたような技術は、PCや回線に負荷をかけるものである。
PCや回線の性能が向上したからこそ、UXを重視したデザインが可能になったということも特筆するべきである。

2.近年のWeb開発の進め方

1で前述した通り、近年のWebページではデザインについて考慮するべきことが増えており、Webデザインの専門性が強くなっている。
そのため、従来のシステムエンジニアに加え、専門のWebデザイナーが開発に加わることが増えている。
システムエンジニアとWebデザイナーが協働する上で、システムエンジニアはWebデザイナーと会話できるようにWebデザイナーの仕事内容を理解するべきであり、その逆もしかりである。

Webデザイナーは主に上流工程を担当する。
ウォーターフォールの場合、開発の進め方は以下のようになる。

要件定義工程でWebデザイナーがUIのデザインを決める

設計工程でシステムエンジニアが要件定義に沿った画面設計を行う

画面設計に沿って実装を行う

実装したものを単体試験・結合試験にて検証する

総合試験にて、Webデザイナーはデザイン通りに実装されていることを確認する

3.Webデザイナーが使用する技術

Webデザインを行う上で、レイアウト配置や配色パターン、フォント、画像、アニメーション等を考える必要がある。
良いデザインを行う上での理論や資格はあるが、個人の感覚による部分が大きい。
(感覚は、既存のデザインから学んだり、エンドユーザーの反応から学んだりすることで鍛えることができる)

デザインは、Photoshop、Illustrator、Adobe XD、Sketchといったツールで表現する。
これらのツールを用いることで、デザインを正確かつ軽快に伝えることができる。
(誤解を恐れずに言えば、Microsoft Power Pointをデザイナー向けに機能特化したツールであるとイメージすれば良い)

4.システムエンジニアが使用する技術

システムエンジニアは、以下の技術を用いることでデザインを実装する。

見た目の装飾やアニメーション
CSSを用いて実装する。
アニメーションはJavaScriptのライブラリで実装することもあるが、CSSもCSS3のバージョンにてアニメーションの機能が追加されたため、CSSで実装することが増えている。

・レスポンシブデザイン
CSS3のメディアクエリと呼ばれる機能を使うことで実装する。
また、レスポンスデザインを実現するためのツールやサービスも存在している。

・SPA
JavaScriptにより実装する。
また、一般的には、Angular、React、Vue.jsといったフレームワークを併用する。
これらのフレームワークを機能強化するために追加して用いるフレームワークも登場しており、例えばVue.jsの場合はVuexやNuxt.jsといったフレームワークを追加で適用することができる。


いかがでしたでしょうか。

体系的な知識はIPAの情報処理技術者試験で概ねカバーできるのですが、Webデザインの分野はカバーされていないので作成してみました。
他チームと調整する場合や学習を進める場合に、こうした体系的な知識は役立つと思います。

「現行機能の踏襲」という要件定義のまずさと、苦し紛れの対応策

私自身は幸運なことに、要件が「現行機能の踏襲」であるプロジェクトに携わったことはないのですが、要件がこれに近いプログラムをプライベートで作成しているので、参考までにその経験を記事として残します。


私が作成しているプログラムはとあるゲーム(RPG)の戦闘部分の計算を行うシミュレータであり、ゲーム攻略のために作成しています。
要件としては下記であり、同じ計算が行われるということ自体に価値があるプログラムです。

・ゲームで行われる計算と同じ計算を実装する

・諸事情によりゲームを解析することはできない

・ゲームには場当たり的な対応やバグが多数含まれており、それも仕様として実装する

 

これを実務に置き換えると以下のような要件であり、限りなく「現行機能の踏襲」に近い状況です。

・現行システムと同じ機能を実装する

・諸事情により現行のソースコードを入手できない

(他ベンダーが開発、ソースコード紛失、等の理由で)

・場当たり的な保守・運用が多数行われており、それも仕様として実装する


私が作成しているプログラムで最も複雑な部分は、攻撃手段毎のダメージ計算式です。
攻撃手段は数百種類存在し、それぞれ異なった計算を行う必要があります。

本来であれば、各々の攻撃手段毎で共通するルールが存在するため、そのルールを抽象化して実装することで保守性を確保することができます(図「本来あるべき姿」)。
(「本来」というのは、「シリーズ初期は」や「運用当初は」といった言葉で置き換えてください)

しかし、実態としては、場当たり的な対応が行われる過程でそのルールを逸脱する計算が行われる攻撃手段が存在します(図「実態1」)。
また、実装の誤りにより、攻撃手段によっては一部計算式が異なる物もあります(図「実態2」)。

ゲームを解析することができれば、既存のソースコードをそのままコンバートしたり、ロジックを真似ることで現行のソースコードと同程度のレベルで抽象化したりすることができるのですが、今回はその手は使えません。
更に、実装を始めた段階では現行のゲームの全ての挙動が判明しているわけではなく、プログラムを作ってみて初めて判明する挙動というのも存在するため、このことも開発の難易度を高める要因となっています。
つまり、十分な抽象化が行えない状況で、将来の変更(新たに見つかった挙動への対応)を見据えたプログラミングを行う必要があります。

私が作成しているプログラムでは、下記のような対応をとっています(図「対応策」)。

1.攻撃手段毎にクラス分けする

一番避けなければならないのが、攻撃手段の分岐を計算式中で多様することでソースコードが複雑化し、保守困難になることである。
そこで、ルールに従った抽象化を行わず、攻撃手段毎にクラス分けした。
重複した記述は多くなるが、ある攻撃手段に対する変更が他の攻撃手段に影響を及ぼすのを避けることはでき、攻撃手段毎の処理内容を追うことも容易になる。
(なお、小規模な開発である場合、「クラス」は「関数」に置き換えても良い)

2.各々のクラスで使用する計算式は部品化する

ダメージ計算式は分解可能であるため、分解した計算式を部品(共通関数)として用意し、その部品を組み合わせることで各々のクラスの実装を可能とした。
攻撃手段毎の微妙な計算式の違いを、部品の組み換えや追加で吸収することが可能になり、保守性が向上する。

このプログラムは公開してから(使い物になる状態になってから)数年経っていますが、今の所は保守困難な状態にはなっていません。
しかし、十分に抽象化を行えなかった影響で、保守性は正直良くありません。
攻撃手段の数だけコピペが必要になるような対応も何回か発生しています。


「現行機能の踏襲」という要件はビジネス上の観点から見ても問題がある(業務改善ができない)と思いますが、実装上も

・共通ルールを見つけるのが困難で、十分な抽象化を行えない

・作ってから初めて明らかになる仕様があり、変更を見こさなければならない

 

という困難さがあり、実装を工夫したとしても十分な保守性を確保するのは難しいです。
保守性が悪いということは、将来の開発でQCDを確保することも困難になります。
「現行機能の踏襲」という要件は、開発コストの低減やリスクの低減に繋がらないと考えた方が無難だと思います。
今回の例のように、現行機能通りにすること自体に価値があるのであれば別ですが、そうでなければ、要件を「現行機能の踏襲」とすることには慎重になる必要があります。


プライベートでもプログラムを作っているので、その中で仕事に活かせそうなスキルや経験が得られることがあります。
また何かあれば、記事にしていきたいと思います。