技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

Excelファイルを設定変更無しで読み取り専用で開く方法

エクスプローラー上でファイルを右クリックし、「新規作成」を選ぶことで、表題のことを実現できます。
 
ファイルを誤更新したくない、しかしファイル設定を変えるのは面倒、という時に使えるテクニックです。

JavaScript:二次元配列(多次元配列)になる場合の長さの判定

JavaScriptで多次元配列を作るためにはpushを使用します。
また、配列の長さを調べるにはArray.lengthを使用します。
 
ここで、pushの方法により、Array.lengthの使い方が異なってくるので、注意が必要です。
(使い方を間違えると、undefinedが出力されます)
 
正しい使い方は下記の画像の通りです。

f:id:akira2kun:20211024212925j:plain

 
1回目のpushでは、二次元目の配列をそのままpushしています。
この場合、二次元目の配列には名前が無いので、「array1[0].length」とする必要があります。
 
2回目のpushでは、二次元目の配列をオブジェクト化した上でpushしています。
この場合、二次元目の配列は、「array2」という名前のオブジェクトの要素にぶら下がっているような形になるので、「array1[1].array2.length」とする必要があります。
 
言われてみれば何てことのないことですが、コードを書く人によってpushの使い方が異なるので混乱しやすいです。

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
function lightTask() {
 console.log("light");
}

function heavyTask() {
 // awaitで呼び出される関数ではpromiseオブジェクトを返す必要がある
 return new Promise((resolve, reject) => {
  const procedure = () => {
   console.log("heavy");
   // resolve(0); // ①
   // reject(1); // ②
  }
  setTimeout(procedure, 1000);
 });
}

// awaitを使用する関数にはasyncをつける
async function exection() {
 let result = await heavyTask()
 //        .then((code) => { console.log("normal:" + code); }) // ③
 //        .catch((code) => { console.error("error:" + code); }) // ④
         ;
 console.log("code:"+result);
 lightTask();
}

exection();
 
【実行結果】
・①と③と④のコメントアウトを外した場合(resolveを.thenで受け取る)
C:\tmp>node sample.js
heavy
normal:0
code:undefined
light

C:\tmp>
 
・①と④のコメントアウトを外した場合(resolveを.thenで受け取らない)
C:\tmp>node sample.js
heavy
code:0
light

C:\tmp>
 
・②と③と④のコメントアウトを外した場合(rejectを.catchで受け取る)
C:\tmp>node sample.js
heavy
error:1
code:undefined
light

C:\tmp>
 
・参考:②と③のコメントアウトを外した場合(rejectを受け取らない)
C:\tmp>node sample.js
heavy
(node:9180) UnhandledPromiseRejectionWarning: 1
(node:9180) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function without a catch
block, or by rejecting a promise which was not handled with .catch(). To termina
te the node process on unhandled promise rejection, use the CLI flag `--unhandle
d-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejectio
ns_mode). (rejection id: 1)
(node:9180) [DEP0018] DeprecationWarning: Unhandled promise rejections are depre
cated. In the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.

C:\tmp>
 
----
 
rejectで返されたPromiseオブジェクトはtry-catchで受け取ることも可能です。
ただし、この場合、戻り値を参照しないように注意が必要で、参照してしまうとUnhandledPromiseRejectionWarningが発生しプロセスが終了してしまいます。
 
以下、サンプルコードです。
 
【サンプルコード】
・sample.js
function lightTask() {
 console.log("light");
}

function heavyTask() {
 // awaitで呼び出される関数ではpromiseオブジェクトを返す必要がある
 return new Promise((resolve, reject) => {
  const procedure = () => {
   console.log("heavy");
   reject(1);
  }
  setTimeout(procedure, 1000);
 });
}

// awaitを使用する関数にはasyncをつける
async function exection() {
 try {
  let result = await heavyTask();
  console.log("code:"+result);
 } catch (e) {
  console.error("error:"+e);
  // console.log("code:"+result); // ⑤
 }
 lightTask();
}

exection();
 
【実行結果】
・そのまま実行した場合(rejectをtry-catchで受け取る)
C:\tmp>node sample.js
heavy
error:1
light

C:\tmp>
 
・参考:⑤のコメントアウトを外した場合(try-catchで受け取る際に戻り値参照)
C:\tmp>node sample.js
heavy
error:1
(node:1600) UnhandledPromiseRejectionWarning: ReferenceError: result is not defi
ned
  at exection (C:\tmp\sample.js:23:25)
  at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:1600) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function without a catch
block, or by rejecting a promise which was not handled with .catch(). To termina
te the node process on unhandled promise rejection, use the CLI flag `--unhandle
d-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejectio
ns_mode). (rejection id: 1)
(node:1600) [DEP0018] DeprecationWarning: Unhandled promise rejections are depre
cated. In the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.

C:\tmp>

コーディング規約の重要性と無い場合の対処

ソースコードを書く上で、好みの書き方は個人によってある程度分かれてくると思います。
例えば、if文で定数を右辺に書くか左辺に書くかは、人によって好みが分かれます。
 
・右辺に書く例
 if (value == 9) {
 
・左辺に書く例
 if (9 == value) {
 
左辺に書く方法は「ヨーダ記法」と呼ばれ、「==」を誤って「=」と書いてしまった時に、代入が行われて処理が継続するのではなく、異常終了でミスを知らせてくれる、というメリットがあります。
しかし、入門者向けの書籍では右辺に定数を書く場合が多いので、左辺に書く方法の方が見慣れていて馴染みやすい、という方も少なくありません。
 
どちらの書き方にも良い点と悪い点があり、どちらが良いかは一概に言えません。
 
----
 
しかし、ソースコードの書き方で、明らかに悪い書き方があります。
それは、箇所によって書き方が混在した書き方です。
一人でソースコードを書いている場合は書き方が混在することは少ないのですが、複数人で開発している場合は書き方が混在することが良くあります。
 
人がソースコードを読む際は、ソースコードの書き方から、記述内容をある程度予測して効率良く読もうとします。
ここで、書き方が混在していると、記述内容の予測を誤り、分析ミスや改修ミスに繋がりかねません。
 
先の例で言うと、if文の定数が右辺に書かれている箇所と左辺に書かれている箇所が混在している場合、定数値と変数値を逆に読んでしまう可能性があります。
これは、分析ミスや改修ミスに繋がりかねません。
注意深く読めば読み誤りは防げますが、ソースコードを読む時にストレスを感じるでしょう。
 
ソースコードを効率良く読めるようにするために、書き方を統一することは重要です。
少なくとも、一つのソースファイル中では書き方を統一するのが望ましいです。
 
----
 
コーディング規約が存在している現場であれば、ソースコードの書き方が統一されやすくなります。
しかし、コーディング規約が存在していない現場もありますし、存在していたとしても規約が十分に網羅的でなかったり、規約が読まれなかったりすることもあります。
 
コーディング規約に頼れない場合は、周りのソースコードの書き方を真似る、というのが良い手になることが多いです。
先輩社員からソースコードの書き方を真似るように言われた人も少なくないと思いますが、そのように言われる理由の一つが統一性の確保です。
 
しかし、全く新しいプロダクトを作成する場合はソースコードの書き方を真似ることができませんし、真似るべきではない書き方というのも中にはあります。
ソースコードの書き方を真似られない場合の対処や、真似るべきではない書き方の判断のためには、一般的に良いとされるソースコードの書き方を覚えるのが良いです。
書籍のサンプルコードは一般的に良いとされるソースコードの書き方がされていますし、良いソースコードの書き方を解説した書籍も存在します。
また、言語によってはコーディング規約が公開されている場合もあるので、その場合はそれを参考にできます。
例えば、C#Microsoft社が公式にコーディング規約(https://docs.microsoft.com/ja-jp/dotnet/csharp/fundamentals/coding-style/coding-conventions)を公開していますし、JavaScriptWordPress社がコーディング規約(https://ja.wordpress.org/team/handbook/coding-standards/wordpress-coding-standards/javascript/)を公開しています。

炎上プロジェクトへの人員追加のまずさと、正しい人員追加の方法

IT業界では、炎上プロジェクトへの人員追加は悪手とされ、遅れが更に拡大する結果になることが多いです。
(一般的には、スコープ見直しやスケジュール延伸が良い手とされます)
 
このことは「ブルックスの法則」として知られており、その法則では以下の理由により人員追加が悪手とされています。
 
1.新たに投入された人員を育成するのに時間がかかる
育成するまでの間、新たに投入された人員がプロジェクトへ貢献するのは難しく、逆に育成コストがかかってしまう。
スキル面で問題のない人員だとしてむ、システム固有の知識を得るのにある程度の時間はかかってしまう。
 
2.人員が増えるとコミュニケーションコストが増大する
人員が増えれば増える程、コミュニケーションに費やす労力が増えることになる。
そのため、人員とパフォーマンスは比例するわけではなく、人員の増加量に応じたパフォーマンスの増加量は徐々に低下する。
仮に、追加人員が全くパフォーマンスを発揮しない場合、コミュニケーションコストが増える分、全体のパフォーマンスはむしろ低下する。
(この問題は、組織を適切に分割することで、ある程度緩和される)
 
3.タスクの分割が難しい場合がある
労働集約的なタスクであれば、一般的に分割は容易である。
例えば、複数のソースコードの修正であれば、ソースコード毎にタスクを分割できる。
しかし、そうではないタスクは分割が難しい場合もあり、例えば、チーム間調整のタスクは分割が困難である。
タスクの分割ができない場合、新たに投入された人員にタスクを割り振ることができず、遊ばせてしまう。
 
----
 
しかし、人員追加は常に悪手ではありません。
1人でこなすことができるタスク量には限界があり、また人員が何らかの理由(体調不良や家庭の事情等)でパフォーマンスを発揮できなくなった場合のリスクヘッジも考える必要があるためです。
悪手なのはあくまでもプロジェクト炎上中に人員を追加することであり、人員は少なければ少ないほど良い、というわけではありません。
 
途中で人員を追加したくなるような炎上プロジェクトは、初めから人員を追加しておけば、問題は無かったはずです。
そのため、プロジェクトの計画時に適切な人員を確保することが重要になります。
人員を追加する際、追加する人員の人数やスキル、人員の育成計画、コミュニケーション計画、各人員のタスク、といったものも合わせて計画することが重要です。
 
仮に、計画時の見積もりが過小であったことが開発途中で分かったのであれば、炎上する前に計画を見直して人員を追加するのが望ましいです。
炎上する前であれば、人員追加が間に合う可能性があります。
炎上するまで放置すると人員追加が間に合わなくなってしまうので、炎上する気配が見えたらなるべく早く計画を見直すことが重要です。

ソース管理のアンチパターン「リポジトリ内のソースが信頼されない」

この記事は「ソース管理ルールが信頼されないことによるデグレード例と対策(https://akira2kun.hatenablog.com/entry/2019/01/03/103732)」の焼き直しです。
 
----
 
ソース管理を行う方法としては、今日ではGitベースのプラットフォーム(GitHub、Bitbucket等)を使用するのが主流です。
しかし、ソースコードCOBOLで書かれているようなレガシーなシステムでは別の方法、極端な話、共有フォルダにソースを格納してライブラリアンが一括管理するような方法が適している場合もあります。
 
しかし、どのような方法でソースを管理するにしても、確実にアンチパターンと言えるケースがあります。
それは「リポジトリ内のソースが信頼されない」というケースです。
 
----
 
ソース管理が上手く行われていないと、リポジトリ内のソースが開発者から信頼されなくなります。
例えば、以下のようなケースです。
 
リポジトリ内のソースが最新化されておらず、本番環境にあるソースが最新であった
リポジトリ内のソースの改行コードに問題があり、そのままでは使用できない
 
リポジトリ内のソースが信頼されなくなった場合、開発者に「裏プロセス」を生み出す動機が生まれます。
ここで言う「裏プロセス」とは、リポジトリの管理者・運用担当者を介さない、開発者だけで完結するプロセスのことを指します。
具体的に言えば「開発者自ら本番環境からソースを取りに行くことで、修正元ソースを入手する」「開発者が手元にソースを持ち、そのソースを修正元とする」といったものです。
 
短期的に見ればこの裏プロセスは上手く行きそうに見えますが、裏プロセスには管理の目が行き届かない、という問題があります。
長期的には見れば、必ずデグレードの危険性が高まります。
特に、同じソースを複数の開発者が開発するようなケースでデグレードが発生しやすくなります。
それどころか、開発者の作業ミスでデグレードが発生することすらあります。
 
----
 
裏プロセスにより開発が行われる状況は、決して放置してはならない状況です。
リポジトリに問題があるのであれば、リポジトリの運用を見直して、その問題を解決する必要があります。
そして、問題を解決した旨を開発者に周知し、リポジトリからソースを取得するように呼びかける必要があります。
 
また、裏プロセスによる開発を行うことができないように環境やルールを整備することも場合によっては必要です。
例えば、「本番環境のソースを開発者から参照不可にする」という方法は環境整備の方法として検討に値しますし、「開発者に修正元ソースと修正後ソースと修正前後コンペアを提出してもらい、リポジトリ内のソースを元に修正されていることを確認する」という方法も不正なソースを元に開発が行われていないか確認する方法として機能します。

EclipseのGradleプロジェクトに赤いビックリマーク(!)が出た時の対処

Eclipseからbuild.gradleを編集(1行スペースを入れる→そのスペースを削除)し保存してみて下さい。
 
なぜ赤いビックリマークが出るのかと言うと、何かしらの原因で設定したクラスパスが失われたからです。
クラスパスはGradleにより設定されるので、Gradleで依存関係の更新を行うコマンドを送信(Eclipseを使用している場合はbuild.gradleの保存が一番楽)すれば、再度クラスパスが設定されます。