Gonjitti Mega Hit Blog
Published on

【リファクタリング】システム構築とは、際限なき増改築の家と同じ


ざっくり言うと

  1. なんでプログラミングの開発が遅くなるのか。六法全書のたとえ。
  2. リファクタリングとは、既存の機能を維持したままコードをきれいにする行為。
  3. プログラミングは過去の巨人が作り出した叡智の上に載せた我が家。九龍城を避けるのに必死なのだ。


六法全書において、矛盾なく条文を直すようなもの


プログラミングはプログラマーにしか理解できない。


外から見るとキーボードをいつもより多めに叩きがちな会議嫌いの変人だ。しょっちゅう営業と喧嘩することでも知られる。


あらゆるシステムには共通の傾向があり、新機能をリリースするのが次第に難しくなる、というものがある。


気づいたことはないだろうか。あれ、エンジニアに前依頼したときより同じような機能なのに開発スケジュールがどんどん伸びていることに。


なんで同じ機能なのにだんだん時間がかかるのか。Twitterでうまい例えをしていた人がいた。


非プログラマの方に「どうしてそんなに変更するの難しいんですか?」って聞かれたら、「六法全書を思い出して欲しい、条項の中から一文探し出して、それが他と矛盾しないかもチェックして欲しい、もちろんその変えるべき条項文は依頼側は分からないので、そっちで全て調べてください」って伝えてる。

Togetter(非プログラマの人に「なぜ変更は難しいのか」と聞かれたら使う「六法全書の例え」「時刻表の例え」が秀逸)


これはわかりやすい。六法全書は矛盾が合ってはならない。システムも一つでも矛盾があればすべて動かない。進捗は常にゼロかイチ。動くか動かないか、である。


古くは建築の例えがよく使われていた。個人的にも建築の例えが一番しっくり来る。システム開発は過去の偉人の叡智を利用したバーチャル建築だ。バーチャルだから他の人に状況が伝わりにくいところは建築よりたちが悪いのだが。


それでも建築学から得た知見は多い。エンジニアにとって必読書(もちろん読んでますよね)、と言われるCode Complete 第2版 (全2巻)では、ソフトウェア開発をソフトウェアコンストラクション(建築)と呼んだ上で、設計ミスで誰一人渡れなかった橋、タコマ海峡橋を取り上げている。



設計ミスがもたらした災厄であるが、問題の温床となったセブンペイ、ドコモ口座など、システムは作る者があらゆる観点から検証していないと、見てくれがおしゃれでもゴミの塊になるのだ。


ではシステムの開発速度を落とさないために何ができるのか、それはリファクタリングである。



ビジネスサイドに知ってほしい、リファクタリングとは


リファクタリングとはなにか。これは、プログラムの全体の機能を変えることなく、コードに手を加えて「理解や修正をしやすくする」行為である。新しい機能を全く加えないのがミソである。


コードを書いたことがある人ならば誰もが経験するが、どんなに綺麗なコードを書いたとしても、しばらく立って振り返ると一体何をしているのかわからないコードがどんどん生まれてくる。すべてを完璧に記憶している人間などいないし、ましてや他の人が書いたコードなんて全くわからない。


リファクタリングは、コードの理解と修正を容易にするために存在する。言い切ってもよいが、プログラミング技法はほぼこれを解決するために存在する。


よくスーパープログラマと呼ばれる人を組織に迎えると、うまくワークしないことがある。これはリファクタリングの要素が欠けていて、その人以外理解できないコードが秘伝のタレのように残り、くさるからである。これを防ぐために、リファクタリングを定期的に行わなければならない。


ビジネスサイドに1点理解してほしいのは、システムには立ち止まらなければならないことがある、ということである。新機能を加えながらリファクタリングを行うことは、ほぼ不可能だ。もちろん、プロとして可能な限りスピードは出す、ビジネス目標も理解しているが、それでもシステムの新機能開発を止めないと全部崩壊する時来るので、プロとして開発をストップさせる必要があると警鐘を鳴らす。


せっかくなので多少の実例を出してみよう。関数の抽出、と呼ばれる方法である。本著は初版はjava,今回紹介する第2版(javaScriptのES6)でサンプルコードが書かれており、どちらか馴染みのある方を読んでみても良いだろう(初版も第二版もそれぞれの良さがある)。


関数の抽出とは、コードの断片で特定の機能を果たしているものを、目的にふさわしい名前をつけて独立した関数にする試みである。下図はレシートを発行するコードである。コード自体が意図を伝えるようになるのが、beforeとafterの差分でわかるだろうか。


  • before(注文の明細を商品ごとに出力している)

function printRecipt(order) {
  for (const item of order.items) {
    // 明細をログに出力
    console.log(item.price)
    console.log(item.amount)
    console.log('--合計金額-----')
    // 合計金額を出力
    console.log(order.item.price * order.item.amount)
  }
}

  • after(明細と合計金額を別の関数として独立させる。レシートには明細と合計金額を書く必要があると宣言的に記述できる)

function printRecipt(order) {
  for (const item of order.items) {
    // 明細をログに出力
    printDetail(item)
    // 合計金額を出力
    printSum(item)
  }
}

// 詳細を出力
function printDetail(item) {
  console.log(item.price)
  console.log(item.amount)
}

// 合計金額を出力
function printSum(item) {
  console.log('--合計金額-----')
  console.log(item.price * item.amount)
}

ちなみに更に簡単に書くこともできる。for文の見直しとprintDetailの中にprintSumを書いて呼び出しを一つにした。これはコード量に注目したパターンであり、理解すべき量が減るのでありがたい。

  • after2(for文のインライン化)

function printRecipt(order) {
  order.items.map((item) => printDetail(item))
}

// 詳細を出力
function printDetail(item) {
  console.log(item.price)
  console.log(item.amount)
  // 合計金額を出力
  printSum(item)
}

// 合計金額を出力
function printSum(item) {
  console.log('--合計金額-----')
  console.log(item.price * item.amount)
}

こうしたひとつひとつの改善の積み重ねが、巨大なシステムをメンテナンス可能にするのである。



巨人の肩にのったバーチャル我が家


システムは、あらゆる先人たちが残してくれたライブラリの上に建つ、バーチャル我が家であり、バーチャルサグラダファミリアである。


油断して適当な建築を繰り返すと、それは見るも無残な香港の九龍城砦となる。



これをイチからきれいにするには、もうシステムの作りなおししかないだろう。事実、下記の九龍城砦も全部取り壊しになり、今は公園になっている。


リファクタリングは全エンジニアが通るべき道である。エンジニアの人はともかく、プログラミングを理解したいビジネスサイドの人にもぜひ読んでみてほしい。技術書特有の軽妙な言い回しがくせになるだろう。