「コピー新規(修正新規)」とは

【背景】

金融系SIerでは「コピー新規」という言葉を聞くことがあります。
(「修正新規」と呼ばれることもあります)
特定の現場だけではなく複数の現場で聞いたことがあるので、一種の業界用語だと思います。
しかし、コピー新規という言葉でWeb検索をかけてもヒットしないので、この記事を書くこととしました。

【言葉の説明】

「コピー新規」とは、「既存のソースファイル(プログラム)を丸ごとコピーし、コピーしたソースファイルに対して必要な個所だけ改修することで、新たなソースファイルを作成すること」を指します。

【コピー新規を行う理由】

コピー新規は、金融系の巨大なレガシーシステム(数百万~数億STEP)を改修する際に、高品質と工数圧縮を両立する現実的な最適解として行われます。
具体的には、以下の2つの理由で行われます。

理由1:システムの既存部分への影響を防ぐ

既存のソースファイルを修正して複数の要件に対応できるようにする場合、システムの既存部分への影響が懸念されます。
しかし、コピーして新たなソースファイルを作成すれば、システムの既存部分への影響を防ぐことができます。

理由2:既存部分を流用することでテスト工数を削減する

本番運用で動いている既存のソースファイルは、品質が保証されたものです。
そのソースファイルの中から使える部分は流用することで、その部分に対するテスト工数を削減することができます。

【コピー新規の欠点】

コピー新規を行うことで、以下の2つの欠点があります。

欠点1:改修した箇所が流用した箇所に影響を与えてバグになるリスクがある

コピーしたソースファイルについて、改修した箇所と流用した箇所は適切にスコープ分割やクラス分割されているわけではないので、改修した箇所が流用した箇所に影響を与える可能性があります。
流用した箇所はテストを省略している(理由2より)ので、影響を与えていてもそれに早い段階で気付くことが難しく、リリース直前やリリース後にバグとして顕在化する可能性が高まります。

欠点2:将来の改修が困難になる

ソースファイルの流用箇所については丸ごとコピーされるため、将来その流用箇所に修正が発生した場合、流用箇所を全て洗い出した上で同じ修正を複数のソースファイルに対して行う必要があります。
このことにより、将来の改修コストが増大します。

【欠点への対策】

コピー新規の欠点に対して、以下のような対策が行われることが多いです。
(これらの対策はコピー新規に限った話ではないですが)

対策1:コーディング規約をガチガチに固める

人によって癖があるソースコードの記述方法について、コーディング規約がガチガチに固められていることが多いです。
例えば、「それぞれのメソッドに番号を割り振り、メソッド内でのみ使われる変数名の先頭にその番号をつける」という規約を設ければ、当該の変数が当該のメソッド外に影響を与えることを防ぐことができます。
また、「『商品』は全て『Shohin』と記述する」というルールを設ければ、『Syohin』『Shouhinn』『Commodity』といった似たような単語を使われるのを防ぐことができ、影響分析が容易になります(単語検索漏れを防ぎやすくなります)。

対策2:影響調査ツールを導入する

巨大レガシーシステム向けに、影響調査ツールを提供するベンダーが複数存在します。
例えばNTTデータ社の「TERASOLUNA DS」の機能として「トレーサビリティー機能」が提供されていたり、NCS&A社が「REVERSE PLANET」というツールを提供していたりします。
これらのツールを用いることで、最新の資産状況が明らかになり、その最新の資産状況で単語検索や構成図確認を行うことができるようになります。

対策3:人海戦術に頼る

コーディング規約や影響調査ツールだけで改修コストの増大に対応するには限界があるので、最後には人海戦術に頼ることになります。
(金融機関に開発資金があるからこそできる対策です)
単純に協力会社やオフショアから開発者を集めるだけでなく、集めた開発者にシステム開発に参加してもらう仕組みを作ることが肝になります。
新たに参画する開発者は少なくとも個社システム独自のことは知らないですし、開発者のレベルにもバラつきがあります。新たに参画する開発者が品質の低いプログラムを開発して後に問題になることは少なくないですし、多数の開発者から一人の有識者に一気に質問が集中することで有識者の本来の業務に支障をきたすこともあります。オフショアの場合は、言語や文化の違いからコミュニケーションが困難になる場合もあります。
そのため、「開発手順の整備」「コミュニケーションの改善(例:窓口役を設ける、英語教育をする)」「独自フレームワークの構築(どのような開発者でも一定の品質の開発ができるようにする、プログラミングせずにシステム開発できる例も)」といった仕組み作りで対応する必要があります。

【リファクタリングしない理由】

現在のプログラミングの潮流は、「適切にクラス分割して、重複した記述はなるべく排除する」というものです。
記述が重複しそうな場合は、クラス分割をやり直して記述の重複を極力防ぐ(リファクタリングする)のが筋です。
「記述の重複を許容し、その代わりコーディング規約や影響調査ツールや人海戦術で悪影響を防ぐ」というのは、その潮流に逆行しているように見えます。

しかし、金融系のレガシーシステムは、その多くがCOBOLの時代に書かれたものです。
COBOLにはオブジェクトの考え方どころか、メソッドやスコープの概念すらありません。
(言語としての機能の問題だけでなく、COBOLが使われていた当時はオブジェクト指向の概念も広まっていなかったはずです)
当然、テストコードなんてものも存在しません。
仮にjavaでリプレースされていたとしても、そのjavaのソースコードはCOBOLのソースコードを自動変換したもので、中身はCOBOLっぽい構造になっているはずです(1クラスが数千~数万STEPある、変数が全てクラス変数になっている、等)。
その時代に書かれたソースコードが数百万~数億STEPも存在しているので、システム全体をリファクタリングするには非現実的なコストがかかります。

現在ではFintechベンチャーが活躍していますが、ベンチャー企業が手を出しているのは仮想通貨やロボアド等の、金融系から見れば傍流のシステムであり、銀行や証券等の基幹システムに手を出すという話はまだ出ていません。 基幹システムは新規参入の企業が容易に手が出せる規模のシステムではなく、これらのレガシーシステムのソースコードが今風のソースコードに淘汰されるというのも現在では考えにくいことです。 海外ではパッケージソフトで大型リプレースをかけるということも行われているようですが、日本の独自の商取引ルールが詰め込まれた基幹システムをパッケージソフトで代替するというのも困難で、レガシーシステムのソースコードは残り続けると思っています。
(比較的歴史の浅いシステムでは事例がないわけではないです。例えば、大阪証券取引所(現日本取引所)の証券デリバティブ取引システム「J-GATE」をNASDAQ OMX社製のパッケージソフトでリプレースした例はあります。)

そのため、既にリファクタリングが手遅れになってしまったソースコードを受け入れ、そのようなソースコードとの付き合い方を考えることが現実的な解だと思っています。
「コピー新規」は、そのための手段の一つと言えます。


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

プログラミングについて学んでいくと、「なぜコピー新規のようなことをするのか」と疑問が沸いてくると思います。
(私も疑問が沸きました)
今回は、単に言葉の説明だけでなく、そのような疑問にも答えられるように意識して記事を執筆しました。

今回の記事のような内容は、実際に業界に携わらないとなかなか実感が湧かないことで、それを文章に起こすことには意味があると思っています。
業界経験を基にした記事は、これからも書いていこうと思っています!

Excel:VLOOKUP関数と代替関数の使い方のまとめ

表題の通り、VLOOKUP関数とその代替関数の使い方をまとめました。

Excelであるキーに対応する値を取って来たい場合、多くの場合はVLOOKUP関数(第四引数FALSE)を使うと思います。
記述量も少なく関数の内容もわかりやすいので、VLOOKUP関数は広まっていますし、とりあえず検索したい時はVLOOKUP関数(第四引数FALSE)で事足ります。

しかし、高速化する必要がある場合や左側の項目を取得する場合、行と列の両方にキーが存在する場合は、他の方法で検索する必要があります。
一番汎用性が高いのはINDEX関数とMATCH関数の合わせ技です。
MATCH関数は第二引数で指定された範囲から第一引数が存在する位置を返すというもので、INDEX関数は第一引数の範囲から第二引数が示す位置を返すというものです。INDEX関数の第二引数にMATCH関数の結果を用いることでVLOOKUP関数と同じようなことができます。左側の項目を取得したい場合にも対応できます。
また、MATCH関数の第三引数(通常は0)を1にすることで、文字コードの昇順に並んでいる範囲を二分検索で高速で検索することもできます。
更に、INDEX関数は第二引数で行の位置、第三引数で列の位置を指定することもでき、この二つの引数を用いることで行と列の両方にキーが存在するケースにも対応できるようになります。
いざという時に使えるようにしておくと便利でしょう。


使い方をまとめた画像は以下になります。

以下、コピペ用にテキストでも記載します。
セル指定は適宜変更して下さい。

・キー項目が昇順・降順ではない場合の検索

VLOOKUP(B12,$B$3:$D$7,3,FALSE)
INDEX($D$3:$D$7,MATCH(B17,$B$3:$B$7,0))

・キー項目が昇順の場合の高速検索

IF(VLOOKUP(B21,$C$3:$C$7,1,TRUE)=B21,VLOOKUP(B21,$C$3:$D$7,2,TRUE),NA())
IF(LOOKUP(B25,$C$3:$C$7)=B25,LOOKUP(B25,$C$3:$C$7,$D$3:$D$7),NA())
INDEX($D$3:$D$7,MATCH(B28,$C$3:$C$7,1))

・キー項目が右側に存在する場合の検索

IF(LOOKUP(B35,$C$3:$C$7)=B35,LOOKUP(B35,$C$3:$C$7,$B$3:$B$7),NA())
INDEX($B$3:$B$7,MATCH(B38,$C$3:$C$7,1))

・列検索と行検索を同時に行う場合の検索

INDEX($B$2:$D$7,MATCH(C46,$B$2:$B$7,0),MATCH(B46,$B$2:$D$2,0))


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

キーに対応する値を取って来るだけでも、意外と奥深いことが分かったと思います。
VLOOKUP関数(第四引数FALSE)で大抵の場合は事足りるとは言え、遅い、左側の項目を取得できない、行と列を同時に検索できない、という不満はいずれ持つと思います。
そのような時に、INDEX関数とMATCH関数の合わせ技でサクっと対応できることがあるので、覚えておくと便利だと思います。

次回も、役に立つ情報を提供していきたいと思います!

磁気ディスクの構成とアクセス方法

今回は、磁気ディスクの構成とアクセス方法について、わかりやすく図解してみました。

ITパスポートや基本情報処理技術者では頻出ですので、これらの試験を受けるのであれば是非理解しておいた方が良いです。
実務ではあまり使うことはないと思うのですが、全く使う機会がないわけではありません。
例えば、メインフレームで磁気ディスクの容量計算を行う時にこの知識が必要になりますし、フラグメンテーションとデフラグは普通にPCを使っていても知っておいた方が良いことです。

・磁気ディスクの構成

・磁気ディスクのアクセス方法


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

ITパスポートを受験する知人の様子を見ていて、磁気ディスクは馴染みが無くて少し難しいと感じたので、今回の記事を執筆しました。
もしかしたら試験では理解できずに丸暗記で済ませてしまった知識かもしれませんが、知っておくに越したことは無い知識です。
今回の記事を通して、磁気ディスクについての知識が少しでも広まれば幸いです。

これからも、試験や実務で役に立つ知識を発信していきたいと思います!

ExcelのVLOOKUP関数の高速化

今回はExcelの小技ということで、検索でよく使うVLOOKUP関数の高速化についてです。

VLOOKUP関数は、第四引数にTRUEかFALSEかを設定します。
通常はFALSEで使用すると思うのですが、TRUEを指定するとあいまい検索になります。
「あいまい検索」と言われると部分一致検索のようなものを思い浮かべるかもしれませんが、実際は二分検索だそうです。
二分検索については情報処理技術者試験でも出題されるので各自調べてほしいのですが、平たく言うと検索対象のデータが昇順にソートされていることを条件に高速に検索する方法です。
Excelの場合は文字コード(SJIS)の昇順に並べる必要があります。

FALSEの場合は線形検索(上から順次検索)になるので、検索対象のデータ量がN倍になると検索にかかる時間もN倍になります。
しかし、TRUEの場合は二分検索になるので、検索対象のデータ量がN倍になっても検索にかかる時間はlogN(底は2)倍の増加で済みます。
例えば、データ量が2倍になった場合は、FALSEだと2倍の時間がかかるようになりますが、TRUEだと1.414…倍になります。データ量が3倍になった場合は、FALSEだと3倍の時間、TRUEだと1.732…倍になります。

注意点としては、TRUEにした場合は満たす値が無かった場合にも値を返すようになるということがあります。
検索条件を満たす値が無かった場合は、満たす値未満の最も大きな値を返します。
例えば、以下のようにデータ並んでいて11を検索した場合は、けが返ります。
10 け
12 こ

FALSEの場合と同じように一致するデータが無かった場合に#N/Aとしたい場合は、以下のようにする必要があります。
=IF(VLOOKUP(検索値,範囲,1,TRUE)=検索値,VLOOKUP(検索値,範囲,列番号,TRUE),NA())
まずは検索条件と同じデータが存在するかどうかを調べ、存在する場合のみ検索をする、存在しない場合は#N/Aとする、ということをしています。
VLOOKUP関数を2回発行しているのでデータ量が少ないとFALSEの場合よりも時間がかかる場合もありますが、データ量が増えてくると効果を発揮します。

ここで注意点なのですが、漢字をキーにして検索する場合は、フィルタからの並び替えは不可です。
フィルタから並び替えると、文字コードの昇順ではなく、読み仮名の昇順に並んでしまうためです。

漢字を文字コードの昇順に並び替えるためには、以下の手順を踏む必要があります。

1.「データ」タブ→「並び替え」を選択

2.「オプション」を選択

3.「ふりがなを使わない」を選択

4.「列」を当該列、「並び替えのキー」を「値」、「順序」を「昇順」とする

5.「OK」を押下すると文字コードの昇順に並び替えられる


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

VLOOKUP関数は普段の業務でも良く使用する関数だと思うのですが、調べてみると意外と奥深いことがわかります。
二分検索を使用した高速検索、漢字の文字コード順の並び替えについては、知っておいて損はないと思います。

業務で役に立つ小ネタがありましたら、また紹介しようと思います!

java8:関数型インターフェースの背景にある考え方

【前置き】

Java8から関数型インターフェースが使用可能になりました。
具体的に「ラムダ式」「Stream」「Optional」「Files」と言った方がわかりやすいでしょうか。

関数型インターフェースの使用を半ば強制されるフレームワークが登場していたり(例:Apache Spark)、関数型インターフェースでJavaを書く開発者も増えてきたので、目にすることも多くなってきたかと思います。

関数型インターフェースは関数型プログラミングをサポートするものであるため、従来からJavaでサポートされていたオブジェクト指向プログラミングとは発想が異なります。
そのため、従来のJavaを学習してきた方にとっては抵抗感を感じるものであると思います。

今回の記事では、抵抗感を少しでも減らすために、関数型プログラミングの考え方を簡単に紹介したいと思います。

【サンプルコード】

言葉で説明するよりも先にサンプルコードを見た方がわかりやすいと思うので、サンプルコードを先に紹介します。
年齢のリストから30代の人数を数える、というプログラムです。
ごく短いプログラムですので、お付き合いください。

・FunctionTest.java

・実行結果

【関数型プログラミングの考え方】

関数型プログラミングでは、以下のことを実現しようとしています。
色々難しい用語(例えば「副作用」等)はあるのですが、今回は用語を使わずに簡潔にまとめます。

・内部状態(State)を排除する

最も本質的な考え方です。

関数型プログラミングでは、内部状態を排除することを目的としています。
「内部状態」とは、上記のコードで言うと「count」や「i」を指します。

内部状態が入りこんでしまうと、内部状態により関数の結果が変わってしまうため、内部状態を把握する必要が出てきてしまい、可読性が悪化します。
(把握のために「count」や「i」をトレースする必要が出てきてしまう)
把握しきれずに意図しないバグを出してしまうことも珍しくありません。
内部状態を排除して、品質を上げよう、という発想です。

また、コンピュータにとっては内部状態は重要ですが、人間にとってはやりたいことを実現できれば良く、内部状態は重要ではありません。
重要ではない記述を削減することでコードを完結にしたい、という発想もあります。

Java8のラムダ式では、ラムダ式の外部で定義された変数の値をラムダ式の内部で変更することを禁止されています(コンパイルエラーになる)。
その背景には、内部状態の排除があると思っています。

・自然言語に近い形で処理を記述する

これは、コードが簡潔になった結果生じた副次的な考え方かもしれません。

関数型プログラミングでは、関数を組み合わせることにより処理を実現します。
関数を次々とつなぎ合わせるように記述することで、ソースコードが自然言語に近い形になります。
わかりやすく言えば、ソースコード自体がコメントのようになります。

例えば、サンプルコードでは「年齢のリストから30代の人数を数える」という処理を行おうとしています。
従来のプログラミングでは、これを実現するためにforループとかカウント用の変数を使用しており、何をしているのか把握するためには、内部状態をトレースして意図を汲み取る必要があります。
しかし、関数型プログラミングでは、
「list.stream().filter(x -> x >= 30 && x <= 39).count()」→
「listを30<=x<=39でfilterしてcountする」
と読めるため、
「年齢のリストから30代の人数を数える」
という処理であることを自然に把握することができます。


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

IT業界、特にSIer業界だと、情報処理技術者試験を軸にして知識を身に付けることが多いかと思います。
しかし、情報処理技術者試験では手続き型プログラミングやオブジェクト指向プログラミングを中心とした出題で、関数型プログラミングが扱われることは全く言って良いほどありません。
そのためとっつきにくさは拭えないと思いますが、先進的な企業を中心に関数型プログラミングを取り入れる企業も出てきています。
これからのことを考えると、せめて、関数型プログラミングに対する抵抗感は払拭するべきではないかと思っています。

今回はこれで締めくくりたいと思います。
では、また来週!