JavaScript:async・awaitの例外処理

JavaScriptのasync・awaitについては以前の記事で簡単に触れましたが、例外処理を行う場合には別途注意が必要であるため、続きの記事を書きます。

async・awaitで例外処理を行う場合、メソッドチェーンで受け取る方法と、try-catchで受け取る方法があります。
これらの方法について、紹介していこうと思います。

サンプルコードはNode.jsで実行しています。


awaitで呼び出される関数では、Promiseオブジェクトを返す必要があります。
通常はresolveで返しますが、例外処理を行う場合はrejectを返す必要があります。
これは、例外をメソッドチェーンで受け取る場合も、try-catchで受け取る場合も変わりません。

なお、rejectで返されたPromiseオブジェクトをメソッドチェーンでもtry-catchでも受け取らなかった場合は、UnhandledPromiseRejectionWarningが発生しプロセスが終了するので、注意が必要です。


rejectでPromiseオブジェクトを返された場合は、メソッドチェーン「.catch」で受け取ることが可能です。
「.catch」で受け取った場合は、「.catch」内の処理が実行され、戻り値は取得できません。
(戻り値はundefinedになります)
resolveで返された場合は、「.catch」内の処理が実行されません。

なお、メソッドチェーン「.catch」の前にメソッドチェーン「.then」を入れることで、resolveで返されたPromiseオブジェクトをこれで返すことができます。
「.then」で受け取った場合は、「.then」内の処理が実行され、戻り値は取得できません。
(戻り値はundefinedになります)
また、rejectで返された場合は、「.catch」の前に定義した「.then」内の処理が実行されません。

以下、サンプルコードです。

【サンプルコード】

・sample.js

【実行結果】

・①と③と④のコメントアウトを外した場合(resolveを.thenで受け取る)

・①と④のコメントアウトを外した場合(resolveを.thenで受け取らない)

②と③と④のコメントアウトを外した場合(rejectを.catchで受け取る)

参考:②と③のコメントアウトを外した場合(rejectを受け取らない)


rejectで返されたPromiseオブジェクトはtry-catchで受け取ることも可能です。
ただし、この場合、戻り値を参照しないように注意が必要で、参照してしまうとUnhandledPromiseRejectionWarningが発生しプロセスが終了してしまいます。

以下、サンプルコードです。

【サンプルコード】

・sample.js

【実行結果】

・そのまま実行した場合(rejectをtry-catchで受け取る)

・参考:⑤のコメントアウトを外した場合(try-catchで受け取る際に戻り値参照)


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

JavaScriptのasync・await、特に例外処理は文法に少し癖があり、誤った記述をしても異常終了しないことがあるので、実際のシステム開発でも障害になりやすい所です。
awaitで待っていないように見える、例外発生時にだけ予期せぬ事象が出る、といった場合は、async・awaitの書き方が正しいか疑ってみましょう。

ドキュメント作成時の基本的な心構え

ドキュメント作成のコツについて一般的なことを書きます。
ドキュメントの良し悪しで意図が早く正確に伝わるかどうかが変わってくるので、スムーズに仕事をこなすには欠かせないスキルです。

ドキュメントには設計書からユーザ向けのマニュアル、運用手順等、色々種類がありますが、今回はどのようなドキュメントについても通じることを書きます。

【根→葉の流れで構造的に書く】

小説のように文章をつらつらと書くのではなく、適度に見出しを付けることが重要です。
また、その見出しは、根→葉の流れで構造的に書くことが重要で、見出しだけを見れば何がどこに書いてあるのかがわかるようにすることが理想です。
プログラム改修の設計書で言うと、以下のように書くのが良いでしょう。
 
 1.背景
  ……
 2.リリース日
  ……
 3.修正箇所
  3.1.プログラムA
   3.1.1.hoge対応
    ……
   3.1.2.fuga対応
    ……
  3.2.プログラムB
   ……
  3.3.プログラムC
   ……

【箇条書きや表や図で完結に書く】

・箇条書きの使用

並列である複数の事柄を書く時は、箇条書きを用いると文章が見やすくなります。
例えば、
「条件Aの時か、条件Bの時か、条件Cの場合に、hoge処理をする」
という文章は、以下のように書くとわかりやすくなります。

以下の何れかの条件に当てはまる時に、hoge処理をする。
 ・条件A
 ・条件B
 ・条件C

・表の使用

2×2かそれ以上の項目・条件が絡み合う事象を書く場合は、表を用いると状況をわかりやすく整理できます。

例えば、下記はリスクの対応策を表にまとめたものです。

これを表を使わずに表現すると、
「脅威に対しては回避と転嫁と軽減の戦略があり、回避とは…」
といういかにも冗長な文章になってしまいます。

・図の使用

複雑な事象を言葉で説明するのは難しいですが、図解するとわかりやすく説明できることが多いです。

例えば、理解が難しい概念である「モジュール強度」と「モジュール結合度」を図解した記事がこちらです。
「モジュール強度とモジュール結合度」の図解

日本語を読んでも何が言いたいのかわかりにくいと思いますが、図解することで意図を理解しやすくなると思います。

【誤解のない表現を使う】

・客観的な数値を示す

「性能が大幅に良くなった」「キャパシティに与える影響は軽微である」
といった主観的な表現だと、それぞれの受け手毎に異なる解釈をされる可能性があります。

ここは、異なる解釈をされないように、
「性能が10倍になった」「ディスク容量が100GBであるがデータ増加量は10MBである」
といった形で、客観的な数字で表すことが重要です。

・複数の意味で取られる日本語を避ける

例えば、「私は何度もコンソールがエラーを出力する所を見ました」という文章の場合、「何度も」の係り受けが曖昧であるため、以下の2つの意味で取られる可能性があります。
 ・私は「コンソールがエラーを出力する所」を何度も見た
 ・私は「何度もコンソールがエラーを出力する所」を見た

このようなことが設計書や手順書を書く中でも起こり得ます。

出来上がった文章を読み返して、複数の意味に取られないか考える癖を身に付けることが重要です。複数の意味に取られかねない時は、上手く書き換える必要があります。
特に、文章を短く区切る書き換えは、文章が分かりやすくなる可能性が高くお勧めです。
例えば、上記の例の場合、「コンソールは何度もエラーを出力しました。私はそれを見ました。」と書き変えると、意味が明確になります。

・要件、仕様等を明確にする

要件定義書なら誰がいつどのようにシステムを使うのか定義する必要がありますし、画面設計書なら画面項目のフォーマットや桁数等を定義する必要があります。

ドキュメントによって絶対に定義しなければならないポイントがあるので、そのポイントを落とさないようにすることが重要です。
もし定義が不十分だと、情報の受け手に自分の意図とは異なる解釈をされてしまう恐れがあります。

【情報の受け手に合わせて粒度を変える】

システムの利用者にとっては、システムをどのように操作すれば良いのかがわかれば良いので、詳しい仕様は冗長な情報になります。
逆に、システムの開発者にとっては詳しい仕様こそが重要になります。

このように、情報の受け手によって、どのような粒度で情報を求めているかが異なります。
受け手にとって粒度が細かすぎる場合は知りたい情報を誤解なく手早く知ることが困難になりますし、粗すぎる場合は知りたい情報を十分に知ることができなくなります。
ドキュメントを作成する際は、情報の受け手が誰であるかを想像して、適切な粒度で情報を提供することが重要になります。


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

今回は、私がドキュメントを書く時に意識していることを列挙してみました。
ドキュメントはチームで仕事を進めていく上では欠かせないものですし、未来の自分が内容を思い出すためにドキュメントを読むこともあるので、今までドキュメントを重視してこなかった方は是非意識して書いてみてください。

なお、Slackのようなチャットツールでの会話でも、この記事で挙げたドキュメント作成のスキルを活かすことができます。
特に昨今はテレワークが普及しており、チャットツールでの会話も増えているかと思いますので、チャットでの意思疎通がスムーズにできるだけでも仕事ができる人だとみなされやすくなると思います。


境界値テストを行う場合には有効桁数に注意する

境界値テストについては、以前の記事にてデシジョンテーブルと共に紹介しました。
しかし、以前の記事では紹介しきれていないことがあるので、別に記事を書いて紹介しようと思います。


表題の通りなのですが、有効桁数については、コーディング時にも気を付ける必要がありますし、境界値テストを行う際にも気を付ける必要があります。
有効桁数を見誤ると、境界値付近の値が与えられた場合に意図せぬ挙動を示すことがあります。

例えば、株価に応じて処理を変える必要がある場面において、以下のような条件指定をしたとします。

そして、1つ目のif文と2つ目のif文の境界値を見るために、株価が99円・100円・101円・102円のテストケースを用意したとします。

これは一見正しそうに見えるのですが、有効桁数が見落とされており、バグを引き起こすコーディングであり、バグを発見できないテストケースです。
実は株価は1円単位とは限らず、0.1円単位のこともあります。
もし、ここで100.1円という株価が与えられた場合、1つ目のif文も2つ目のif文もすり抜けてしまい、意図せぬ分岐に進んでしまいます。
そして、テストケースでは100円より大きく101円より小さい値が設定されていないので、このバグを見つけ出すことができません。

正しくは、以下のようにコーディングするべきです。

そして、1つ目のif文と2つ目のif文の境界値を見るテストケースとしては、最小桁数での境界を見ることができるように、株価が99.9円・100.0円・100.1円のテストケースを用意するべきです。


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

今回紹介した例は、私が本当に目にしたことがあるバグです。
金融案件のように数字を扱う案件では発生しがちなバグだと思うので、有効桁数に気を付ける必要があるということを心に留めておきましょう。

ヘッダレコード・データレコード・トレーラレコードとは

企業間でやりとりするファイルで見かけることがあるフォーマットとして、レコードが「ヘッダレコード」「データレコード」「トレーラレコード」に分かれているフォーマットがあります。
ファイルの中間部分にあたる「データレコード」に取り扱うデータを格納し、ファイルの冒頭と終了にあたる「ヘッダーレコード」「トレーラレコード」で日付や件数といった付加情報を与える、というのが特徴です。

以下で、「ヘッダレコード」「データレコード」「トレーラレコード」の簡単な説明と使用例を記載します。

【それぞれのレコードの説明】

・ヘッダレコード

ファイルの1レコード目のレコード。
一般的には、そのファイルが何日のデータなのかが記載される。

・データレコード

ファイルの中間レコード。
実際にやりとりするデータの中身が記載される。

・トレーラレコード

ファイルの最終レコード。
一般的には、そのファイルのデータレコード件数が記載される。

【フォーマット例】

取扱商品ファイルを想定した例を記載します。
可変長のCSVファイルを想定します。
(固定長の場合も多いです。固定長の場合は、カンマ区切りではなくバイト数で区切ることになります。)

・ヘッダレコード

1項目目:ファイル区分(1がセットされる)
2項目目:ファイル作成日付(YYYYMMDD)

・データレコード

1項目目:ファイル区分(2がセットされる)
 ※5がセットされることも多いです
2項目目:商品コード(7桁の数値)
3項目目:商品名(全角文字)
4項目目:値段(数値)

・トレーラレコード

1項目目:ファイル区分(9がセットされる)
2項目目:データレコードの件数(数値)

【ファイルのレコード例】

【ファイル受信時の処理例】

ヘッダレコードやトレーラレコードは、受信側のチェック処理に利用できます。
以下、ファイルを1レコード目から順番に読む場合の処理例について記載します。

・ファイル区分が1の時

1レコード目がファイル区分1でなければ異常終了。
(送信側が中間ファイル等誤ったファイルを送信することを想定)
バッチ日付と比較し一致しなければ異常終了。
(送信側が誤って過去ファイルを送信することを想定)

・ファイル区分が2の時

業務上必要な処理を実行。

・ファイル区分が9の時

最終レコードのファイル区分が9でなければ異常終了。
(ファイルを全件受信できていないことを想定)
件数がファイル区分2のレコード数と一致しなければ異常終了。
(ファイルを全件受信できていないことを想定)


ここからは応用です。

拡張性を確保するために、データレコードの種類を増やすという手段が有効になることがあります。
種類が異なるデータレコードをファイルに含める場合、データレコードのフォーマットを変えてしまうと、フォーマットが複雑になることで書き込み・読み込み処理が複雑になったり、フォーマット変更時の影響が全種類のデータレコードに出てしまったりします。
しかし、種類毎にデータレコードを用意することで、1つ1つのフォーマットが複雑になることを防いだり、フォーマット変更の影響が当該種類内のデータレコードに限定させることができます。

フォーマットと処理の例としては以下の通りです。
商品の種類毎にデータレコードの種類を変えることで、データ体系の違いを吸収している所がポイントです。
(1つのデータレコードにしてしまうと、1つのデータレコードに「食品商品コード」と「玩具商品コード」が含まれることになり、どちらを書き込むか・読みこむかの判断が必要になってしまいます。また、食品若しくは玩具にだけ新たな項目を追加する時に、追加が必要無い方の種類の商品の考慮も必要になってしまいます。)

【フォーマット例】

・ヘッダレコード

1項目目:ファイル区分(1がセットされる)
2項目目:ファイル作成日付(YYYYMMDD)

・食品データレコード

1項目目:ファイル区分(2がセットされる)
2項目目:食品商品コード(7桁の数値)
3項目目:商品名(全角文字)
4項目目:値段(数値)

・玩具データレコード

1項目目:ファイル区分(2がセットされる)
2項目目:玩具商品コード(6桁の数値)
3項目目:商品名(全角文字)
4項目目:値段(数値)

・トレーラレコード

1項目目:ファイル区分(9がセットされる)
2項目目:データレコードの件数(数値)

【ファイルのレコード例】

【ファイル受信時の処理例】

・ファイル区分が1の時

1レコード目がファイル区分1でなければ異常終了。
(送信側が中間ファイル等誤ったファイルを送信することを想定)
バッチ日付と比較し一致しなければ異常終了。
(送信側が誤って過去ファイルを送信することを想定)

・ファイル区分が2の時

業務上必要な食品向けの処理を実行。

・ファイル区分が3の時

業務上必要な玩具向けの処理を実行。

・ファイル区分が9の時

最終レコードのファイル区分が9でなければ異常終了。
(ファイルを全件受信できていないことを想定)
件数がファイル区分2~3のレコード数と一致しなければ異常終了。
(ファイルを全件受信できていないことを想定)


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

モダンなシステムではあまり使われることが少ないファイルフォーマットかもしれませんが、レガシーなシステムでは使われることが多いフォーマットです。
レガシーなシステムとの連携では頻出なので、このファイルフォーマットの特徴を覚えておくと会話や理解がスムーズになるでしょう。

アジャイル開発の概要

プロセスモデルの一つとして、「アジャイル開発」というものがあります。
ウォーターフォールモデルと比較すると新しいモデルです。

アジャイル開発はその名の通り迅速に開発を行うもので、短い期間(数週間~1ヶ月程度)毎にユーザにとって重要な機能から順番にリリースを行います。
一つの期間はイテレーションと呼ばれ、計画・設計・実装・テストがこの期間の間に行われます。
このように開発を行うことで、ユーザは少ない期間・コストで重要な機能を使用することができるようになります。ユーザの要求の変更にもいち早く対応することができます。
また、システムを実際に使うと新たな要望が浮かぶものなのですが、システムを早い段階で手に取ることができるので新たな要望を早く出すことができます。仮に、せっかくリリースした機能がユーザに響くものでなかったとしても、その問題に少ない期間・コストで気付き、軌道修正することができます。
これらの利点により、重厚長大なウォーターフォールモデルを採用した場合に比べて、ビジネス上で優位に立つことができるようになります。
(特にスタートアップ企業にとってはこれらの利点が重要)

しかし、アジャイル開発はユーザの要望が頻繁に変わる小規模システムの開発には向くものの、要望が固定されているミッションクリティカルな大規模システムの開発には向かないとされています。
アジャイル開発ではシステムを小出しで開発するので、システム全体の整合性や最終製品の品質を上手く管理しないと、システムの品質が低下する問題やシステムのスケーラビリティが損なわれる問題が発生してしまいます。
そのため、教科書的には、要望が固定されているミッションクリティカルな大規模システムの開発はウォーターフォールモデルの方が向いているとされています。

ユーザーの要件を満たすプロダクトを迅速に開発するためには、それを実現するための手法や考え方が必要です。
アジャイル開発を実現するための手法、またアジャイル開発と親和性の高い考え方としては、以下のようなものがあります。

・アジャイルソフトウェア開発宣言

アジャイル開発の価値観を端的に言い表した宣言である。
原文はこちら。

・エクストリームプログラミング(XP)

迅速にプログラミングを行うための手法。
リファクタリング(保守性を高めるためのコード見直し)、ペアプログラミング(一人がコーディング、もう一人がリアルタイムにコーディングを見ることで、即座にレビュー・知識共有を行う)、テスト駆動開発(テストケース・テストコードを先に設定してからプログラミングを行う手法で、テスト自動化が伴う場合が多い)、等により実現する。

・プロトタイピング

ユーザからのフィードバックを早期に得るために試作品を作成して提供する。
試作品はコーディングして作成するとは限らず、紙芝居のような形でコンセプトを伝えるペーパープロトタイピングと呼ばれる手法もある。

・スクラム

チーム一体となってプロジェクトを遂行して行くことに重点を置くプロセスモデル。
デイリースクラム(始業時に今日の予定と問題点を共有する)、プロダクトバックログ(システムの要求一覧)、スプリントバックログ(イテレーション内で対応する要求一覧)、イテレーション、スプリントレビュー(イテレーション内で開発した機能のユーザとのレビュー)、ふりかえり(イテレーション終了時の改善点の共有)等により実現する。

・バーンダウンチャート

縦軸に「残作業量」、横軸に「時間」を割り当て、残作業量を折れ線グラフにより記述したチャート。
ガントチャート等と比べて、プロジェクトの進捗状況をいち早く把握することができる。
誰もが見える場所に張り出すと効果が高い。

・リーン生産方式

トヨタ生産方式を元に編み出された方式。
開発工程におけるムダを排除することを目的として、製品および開発工程の全体にわたって、トータルコストを系統的に減らそうとするのが狙い。「ムダをなくせ(顧客に価値を与えない作業を無くす)」「学習を強化せよ(反復型開発)」「決断を遅らせよ(状況の変化に合わせた決断)」「できる限り早く出荷せよ(機会損失防止)」「チームに権限を(チームの手で最良のプロセスを採用)」「摺り合わせて作り込め(ユーザとのトライ&エラーの繰り返し)」「全体を見よ(全体最適化)」という7つの原則によって成り立っている。

・チケット駆動開発

スプリントバックログを約2~3時間のタスクである「チケット」に分割し、チケットを管理することで、メンバーの作業把握と成果物の更新理由の把握を容易にする。
チケットによる管理を行う場合は、チケットの完了(Done)の定義を明確にし、完了基準を満たすまでは完了とみなさないようにすることも重要である。

・ストーリーポイント

案件の規模や複雑性を「ストーリーポイント」と呼ばれる直感的・相対的な基準で測ることにより、俯瞰的な視点で直感的に迅速に見積もりを行う。


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

日本ではアジャイル開発はあまり普及しておらず、アジャイル開発を採用しているプロジェクトには巡り合えないかもしれませんが、それでも顧客の要望に沿ったシステム開発を行う必要があることには変わりありません。
そのため、ウォーターフォールの開発だとしても、アジャイル開発の手法や考え方は部分的にでも取り入れることは有効ですし、実際に部分的に取り入れているプロジェクトも少なくありません。
開発者としても、アジャイル開発の手法について知っておくことに越したことは無いでしょう。