(ヘアメイク:浦杉美和子)
小野:過去の記事のフィードバックで、知人で某とあるSIerの営業の経験が長い方がですね、Facebookで記事を引用して「SIerの営業としては、美しいコードかどうかじゃなくて、美しいコードよりもバグがないコードが、お客さんに納得してもらえるんだ」というようなことを書いてて。まるで美しいコードとバグがない安定したコードが相反するみたいな感覚でいて、「美しいコードだから安定してるんだ。両者は対立しませんよ?」みたいなことを返信しました。でもたぶんそれが出てきちゃうってことは、SIの営業、しかもかなり有名で、SIerの営業はかくあるべしっていうお手本みたいな人が、そういう発言を普通にしちゃうってことは……。
編集部:ちょっと軽い眩暈を覚えるくらい、共有されてないってことですよね、美しいコードについて。
小野:「美しさなんか関係なく安定してるかどうかだ」と。
編集部:そこは比べることじゃない。
小野:美しさがあるから安定してるのに、美しさよりも安定性だって言うのっておかしいと思いませんか。だけど、美しさなんて関係ない、要するに美しさよりも安定性だから、そんな趣味的な話じゃなくて…と思っている人は他にもたくさんいるはずなので、今回は「美しくないことによって、こういうことが実際に起きるでしょ」「それって他人事じゃないでしょ」っていうような話をすべきだと思っていて。今日はもうそこに絞って、対談なので途中でいろんな話が出てくるかもしれないけど、基本はその話をすべきかなと思ってます。よろしいでしょうか?
有馬:はい。
小野:純粋に美しいか美しくないかとかじゃなくて、日本のIT業界の非常に多くの割合を占めるSIにおいて、その美しくないことが実際にはダメージとして、痛みがあるということをですね、たとえを交えながら話していければなと思っているんです。前回も少しは話していたんですが、もうちょっと具体的に話さなきゃいけない。前回は美しいか美しくないかっていうことで話をしてたんだけど、どういうものが美しくてどういうものが美しくないのかっていうところで、具体的なことを言わないと、どのくらいのレベルのこと話してるのかとか、自分の仕事で言うとどの辺なのかとか、たぶん読んでいて「どこのこと言ってるのかな?」って思った人もいると思うんですよね。だから我々が言っている、この間の対談で出てきたような問題を順番に話していきながら、具体的なイメージが伝えられれば、というふうに思うんですよね。
重複のあるコード
小野 そこで、美しくなくて課題があるもののたとえっていうのをいくつか考えていきたいと思います。まずは、重複したコード。要するに同じようなソースコード、プログラムが、複数の箇所に分散している、これは美しくないコードとして、実際にダメージが発生する1つのわかりやすい例なんですが、実際その重複したコードで痛い目にあったとか、こういうことがあり得る、実際あったみたいなのを、ちょとSIの現場的な話として、有馬さんにお話を聞ければと思うのですが、いかがでしょうか?
有馬:SIの現場でも、重複したコードってのはすごくよく見ます。たとえばSIのコードで言うと、Webの画面だと、A画面とかB画面とか作っていくんですけれども、基本的に同じような処理を書くことが多いです。ですので、A画面の処理、B画面の処理というのは、微妙な差異はあるものの、基本的にはほとんど同じコードで実現することができる。それは業務処理の特徴かもしれませんけど、そういったものはよく見ます。
小野:同じような処理なんだけども、一部違うみたいな話ってのは、本当は重複を避けて美しくする手法がいろいろある。オブジェクト指向の世界ではGoF(Gang of Four)のデザインパターンが有名で、ソフトウェアの設計技法として、それなりにプログラミングしている人の中では、日本語で言うと四字熟語みたいな感じで共有されている。その言葉を使えば一言で済むみたいな、そういう設計の技法みたいなもの。そのGoFのパターンの中には、重複を避けるためのパターンがたくさん含まれているんですが、なぜそうなっているかというと、要は重複を避けることがすごく大事なことなんですね。今のSIにおいて、「似たような処理がA画面、B画面にありますね」というとき、仮にコードが重複していても、それ自体が即ダメだというより、重複によってこういう問題が起きるからダメだ、という話しだと思うんですね。
有馬:そうですね。
小野:コードが重複していることによって、こういうことが起こるから問題なんだ、その「こういうこと」の部分って言うと。
有馬:分かれていることについては、やっぱり改修の時ですね。変更する時に問題が発生することが多いです。重複コードっていうのは、AもBもほとんど同じような機能、ソースコードで作られています。それを修正する際に、本当であればもし共通的に、重複ではなくて処理が括り出されているならば、ある1箇所の機能の修正だけで、A画面、B画面ともに修正可能に、正しく動くようになります。ただ、調査や見積もりする人が、「今回の変更は、Aのこの部分を直せばいい」と、ソースコード、もしくは仕様書だけを見て判断することが多いです。そうしてAをまず修正しました、テストします、ちゃんと動いた、変更OKとなるんですけれども、実際にはそれと同じような処理を実はBでもやっているので、Bも直さなければいけないということに、その修正時点では気付けないっていう問題があります。これは重複コードにより発生していると思います。
小野:それで気付かないと何がまずいんだっけ?気付かなくても問題が起きなければまずくないじゃないですか?
有馬:基本的に修正の内容にもよるんですが、共通的に直さなければいけない機能ってやっぱりあって、「こういう修正をしてほしい」っていう連絡があった時に、A画面だけのものであれば当然いいんですけれども、だいたい同じようなコードでできているので、修正するとしたら、全画面で動作するようにするって求められることが多いです。その時に、全画面の修正対象を把握するのはとても難しいです。今はA、B、2つのたとえ話をしてますけど、これが100画面あると、いくつか漏れてしまうってことは当然、出てくる。
編集部:前回、見積もりの話がでたじゃないですか。それなんかもたぶん、今、公開されているA画面を直したいっていう要求だけなんだけれども、Aを直したらBもCもDもみたいになって、そうすると見積もりが膨れるっていう問題。その問題は前回の話で把握してるんですけど、もう1つ、改修を始めた時に、最初の見積もりには入っていなかったけれども、ここもここもここも直さなくっちゃいけないじゃんっていうのが、後から最中に出てくるみたいな問題があるということでしょうか。
有馬:そうですね。開発途中でわかれば、それはSIer側の負担や再見積もりなどで修正するとか、対応はできるんですが、リリースしてからだと、お客様の障害につながってしまう。
編集部:そうすると3つあるんですね。見積もりがそもそも高くなるっていうのはでも最初に見抜けてる時ですよね。後、改修してる最中にここもここもっていうのも、それ自分のところで負担して、直さなくちゃいけないっていうのはやっぱり辛いことですよね。たぶんユーザーさんにとって痛みっていうのは、その後の見つけられなかったものがリリースした後に出てきちゃった時に、ユーザーが痛い。
有馬:そうですね。
小野:1番はある意味、痛みの一種のでしょうけどね。高いお金を払うことになるから。
編集部:そうですね。一言で言うと、「高いお金かけて改修したのになんか障害でてるんじゃん」っていう裏にはそのようなことがあるっていう。
小野:その辺のコードが共通してれば、基本的に同じような処理が1箇所にしかなくなるので、そこ直せば全部「せーの」で直る。分散してれば、「あ、こんなとこにもあった」っていう、漏れが発生したりとか、予期せぬバグが発生したりすることがあるわけですよね。だから見積もりの精度が下がったり、思わぬバグが出たりっていうようなことがあるということですよね。……という話を書けば、具体的にどんなことで、確かに「自分とこでもあったな」っていうのはあると思うんですよ。つまりあるところ直したら、まさかのこんなところでバグが出て、なんでかって言うと、似たようなロジックってのが実はこっちにもあった、それが見抜けなくて、見つけられなくて、そっちの直し漏れが原因で障害につながった、っていう。たぶん、まったくそんなことゼロです、重複してるコードがあるけど、抜け漏れは絶対起こりませんってことは、あんまりないですよね。
有馬:重複コードによる変更漏れというのは、年に2回から3回は必ず起きますね。やっぱり長い間そのシステムを担当している人っていうのはもうそれに慣れてますので、当然ここを直したらここってわかるんですけど、ただ初めての人、特にSIの場合はたくさん人が入れ替わります。ですので、そういう人たちがいきなり、ここの部分の修正はここにも影響あるよねっていうのはなかなかわからなくて、当然レビューでもそれは見つけることはできなくて、出てしまうと。
小野:その問題なんですけど、前回の公開されている記事に対するコメントとかTwitterとかでも、本来SIerであればあるほど、いろんな人が入れ代わり立ち代わりっていうような形で出入りすることもあるわけだから、本当は美しさがよりシビアに求められるはずなんだけどってコメントもあったんですよね。それってまさに今みたいな問題で、たとえば重複してるんだけども、その現場に長くいる人ってのは、どことどこで重複してるかっていうことを把握してて、これ直すんだったらあそこだぞっていうふうに言えるけども、そうじゃない人、システムとか現場に長くない人が入った時に、ちょっと探して「ここでした」って言って直しても、「いや、ちょっとそこだけじゃなくて、こことこことここがあって、もうそれはこのチームでは暗黙の……」とかいうことが起こる。重複がなければ、「知ってる人は知ってる」とかっていうことがなくなるから、属人的じゃなくなる。そういうコードは長くいる人の知見がなくても、より安定性をもって直せる、安心して直せるっていうところはありますよね。「まさかこんなところで」みたいなことが、すごく詳しい人がいないと起こり得ちゃうのってのは、やっぱりユーザから見てもこわいっていうのはあるんですよね。そういうのが重複コードの話でいいですかね。
あと他にもいろいろとあると思うんですけども、順番で見ていきましょう。あ、ちなみにこれ答え合わせしたんですよ、後でちゃんと。リファクタリングって名前の有名な本があるんですよ。コードを美していくリファクタリングのやり方が書いてある本なんですが、改めて読み返してみると、望ましいコードであるためにはこういう要素が満たされているコードでなければいけない、という話もそこにまとめられているんですね。リファクタリング本のそれと自分たちがつくった汚いコードのリストで答え合わせをしたら、だいたい同じことが書いてあって、「まぁそうだよね」みたいな話があったんですけど。後はね、ロジックの重複もあるけども、もっと根本的なところで、ソースコードがすごい複雑で、なにがなんだかわかんないみたいなコードっていうのも実際あり得るんですね。SIだけにあるとかじゃなくて、ソフトウェア開発で、みんな少数精鋭でレベル高くやってる時には大丈夫な場合もあるけど、やっぱり中には経験が浅い子もいて、レビューを完全にしてるわけじゃなくてっていう時に起こり得る問題、それか大規模開発でいろんな人を投入してる時には容易に起こり得るんですね。
あとは使う技術の選択を間違えて、それに向いた技術、そのソフトウェアに向いた技術を使えばすごく簡潔に書けるんだけども、向いてない技術を使ったがために、すごく複雑になっちゃったケース。C言語でものすごい複雑な画面を作ろうとしたとか、ツリー型のオブジェクトをがんばってハンドリングしようとしてぐちゃぐちゃになったとか、そういうのは1つの例になると思うんだけど、結果的に人のアサインを間違えたとか、使う道具が間違えてた等、あとはスキルが未熟であった等々の理由によって、コードがなにやってるかわかんなくなっちゃってる状態っていう、これがやっぱり一番、わかりやすい汚さ。なにをやってるかわかんない。けどとりあえずテストを通って動いてる。この汚さって、これはもう明確に痛みがあるわけですよね。あと、SIで仕様と実際のソースコードとが、どうしても時間の経過とともに完全に一致してない状態が起こったりした時に、ソースコードからもう1回、仕様を起こそうっていうことって、あったりしますよね。特に何やってるかわからないと仕様を起こすのがすごく大変だったりとか、その辺どうですか?
有馬:元々、詳細設計書とかがある場合は、毎回メンテナンスしていくんですけど、そこに書かれてる仕様とソースコードっていうのは、仕様は合ってるけれども実装とはかなり乖離したものになってきます。最初に作った人と、後から修正する人の経験や実装スキルの違いがあるので、やっぱり付け加えていったコードは、すごく汚されていくっていうイメージがすごくあります。
小野:そうすると、最初の時はまだいいかもしれないけど、段々いろんな人が手を入れて直していく、仕様もできるだけアップデートはするんだろうけど、だんだんちゃんと完全な同期をとってアップデートできなくなったりする、現実的に。
有馬:現実的には、アップデートするのは原則ですが、ソースコードと仕様書はどうしてもずれていきます。やっぱり理由は変更案件ですね。改修量が少ない案件でも開発途中での仕様変更っていうのは発生するので、機能仕様書をレビュー後に入ったソースコードの改修が最終的に仕様書に反映されないってことは、少ないながらも現実的にあって、そうなるともうリリース後に修正されることはないので、小さなズレが段々起きてくるっていうのはすごくありますね。
小野:そういうことがあると、ソースコードが実際には、コンピュータ的に、実際の動作を最も正確に書き表してるものなんだけども、何をやってるかはプログラムを見ないとわかんないってことが起こり得る。その時にプログラムが何やってるかわかんないと、そもそも何してるかわかんないっていうことが起こり得るわけですよね。
有馬:起こりえます。設計書には外部設計書や詳細設計書などがありますが、改修するときに、どの設計書を変更すべきかは明確に決まっていないこともありますので、変更を重ねていったシステムの場合は設計書ではなく、プログラムを見て判断することは多々あります。また、そのプログラムが何やっているかわからないということも、実際にありえます。
編集部:前に、神林さんと井上さんの小野さんでのパネルディスカッションの時に、詳細設計の話が出て、小野さんが「詳細設計そこまで書くならもうお前プログラム書けよ!」っていうところで、会場がどっとウケるっていう場面があって、そこで私、「詳細設計、へー」ってのと、プログラムの関係っていうのを知ったんです。それはちょっと面白いと思いました。
小野:さらにプログラムに近い、プログラム仕様書なるものがあって、そこは本当にプログラムが日本語で書いてあるだけ、ifはもしとか。
有馬:ifとか、whileは、そのままifって使うこともあります。
小野:本当に、本当にそこまで書いたらプログラム書けよっていうのが、プログラム仕様書。
有馬:でないと設計書に「もし」って書いちゃうんで。条件か設計書の注釈かわからなくなってしまいます。
編集部:それは、設計する人と、プログラムする人が別であるがゆえに、こういうプログラム書いてほしいんだっていうのを伝えるために、詳細設計っていうのとか、もっともっと掘り下げてプログラム設計、「こういうの、こういうやつ、ああいうやつにして」っていうものが詳細設計。それを見て、プログラマーが。
小野:そのパターンと、詳細設計のさらに具体的なものとしてのプログラム仕様書があって、本当にもうそこまで書くならプログラム書けよっていうのはプログラム仕様書で、っていうことですよ。
有馬:おそらく多くの場合、過去のCOBOL案件ではプログラム仕様書を書いて、COBOLのプログラミングに落としてたっていう背景があって、それをJavaでも同じように見積もりといいますか、開発の一式、ドキュメント一式に入ってるがために、最近の言語でも同じようにしてるっていうところもあると思いますね。本当はなくてもいいはず。
小野:ちょっと話を戻すと、重複の問題とか、そもそもコード自体が何やってるかわからないみたいなものすごくベーシックなレベルの問題とか、さらに仕様書とプログラムの挙動までずれているとか、当然こういうぐちゃぐちゃなものってのは、そこから手を入れたらまさかの挙動するっていうようなリスクも高くなってきますよね。本当は冒頭の重複を避けるプログラミングをしていくには、設計をする人が、こうした問題を避けるための美意識、美的感覚を持ってないと駄目なわけですよね。設計レベルで、1個1個の画面が、それぞれ似たような処理を書くっていうふうにデザインされちゃってたら、重複はどうしても発生してしまう。ここはA画面はB画面とかれこれのロジックは一緒であるから、そこは共通のcommonの中にあるペケペケを使うべきであるみたいなことが書いてある場合も当然あるわけでしょ。
有馬:あります。そこは設計の仕方でもあるんですけれども、コンポーネントに分割することはよくあります。ただ、そのcommonのペケペケの単位が大きすぎることがたまに見られます。業務処理が丸ごとそのコンポーネントになっている感じです。その中で修正できるならいいですが、大きすぎるがゆえに修正をいれづらいので、コンポーネント自体をコピーして作ってしまって重複がさらに増大っていう状況があります。
小野:共通のコードとして1ヵ所にだけ書いていこう、同じことを書くなっていう、プログラムの原則があります。DRY(Don’t Repeat Yourself)というもの。ちゃんとその原則を守ろうとするチームがあったとして、パッケージとかで実際そういうことが多いんだけども、その大事さをお客さんも理解してくれて、必ずそういう原則でやってください、重複コードをなくすという形で設計してくださいねっていうのがあるとして、詳細設計とかも、そういう重複をなくすようなデザインができる人がやってたりしたら、修正する時に1箇所で済むし、影響範囲もわかるし、どこに何があるかってことが明確にってことは実現していける可能性はありますよね。
有馬:そうですね。できるだけ共通化していきたいっていうのはSIもあるんですね。元々はすごい重複コードが多いから、次のリニューアルはきれいにしようって、みんな思ってます。ただ、それを意識して設計書、詳細設計とかを書いたとしても、どうしてもプログラムに落とした時には理想通りにはいかないです。設計書を書いていく時は「これはすごくきれいにコードに落とせるはずだ」と、コードをあまり書いたことがない人が書いていくんですけれども、やっぱり実際にプログラミングをする、プログラマーがそれをもらって書く時には、その通りには当然書けない。やっぱり自分で考えながらそこを埋めていくと、コードの共通化はその人の作業範囲で閉じてしまう。そうすると、全体で見た時に共通化はうまくできないっていうような状況だと思いますね。
(ヘアメイク:浦杉美和子)