トラブルシューティングの極意―達人に訊く問題解決のヒント

第10回[ソフトウェア開発編]悪循環からの脱出―ソフトウェア開発の時短術+見極め

期日までに約束した内容を実現することは、仕事の基本です。しかし、予定どおりにすんなりと進まないのがソフトウェア開発の難しいところです。ソフトウェア開発を予定どおりに進めるためには、見積りや進捗管理の技法を工夫することも大切です。しかし、実際にソフトウェアを作っているのは、管理者ではなく、現場の開発者です。開発者自身が、日々の開発活動の中で発生する、ちょっとしたトラブルをうまく切り抜けることができるか、泥沼にはまり込むかが、開発全体の進捗を大きく左右します。トラブルはつきものです。そのトラブルに正しく対処して、泥沼に入り込まない技を身につけることが一人前の開発者になるということです。

ソフトウェア開発は時間との戦い

仕事を遅らせるやっかいなコード

ソフトウェアにバグはつきものです。今までずっと動いていたプログラムが、なぜかエラーを起こすようになった。完成したと思っていたプログラムが、不具合を指摘されて修正しなければならなくなった。こういう想定外の事態が、開発をジワジワと遅らせます。

多くの不具合は、該当個所のコードを見直せば、簡単に直せる類のものです。ちょっとした勘違いや見落としに起因するバグは、発見さえできれば、調査も修正も容易です。

やっかいなのは、なぜ正しく動作しないのかコードを読んでもパッと見にはわからないバグです。あるいは、簡単に修正できると思っても、実際に修正してみると思わぬ副作用に悩まされるバグがあります。スケジュール遅れの危険な兆候です。

こういうやっかいな不具合を引き起こすプログラムには、次の特徴があります。

  • メソッドが長い
  • if文やfor文が入り組んでいる
  • 変数への代入を繰り返している

こういうわかりにくいコードのまま、バグと格闘するのは無謀な戦いです。数時間がんばっても、状況を悪化させるばかりです。数時間のロスを何回か繰り返せば、予定したスケジュールはいとも簡単に崩壊します。スケジュールがタイトになればなるほど、つまらないバグが増えて、簡単に直せる修正でもミスを繰り返し、仕事が加速度的に増えていきます。悪循環です。

泥沼に入り込む前にすること

長く複雑なコードを相手に格闘するのは、泥沼に入り込む道です。避けなければいけません。

読んでも意味がわからないコード、どこで何が起きているかわかりにくいコードは、修正に取りかかる前に、まず、コードを整理します。

とっちらかった部屋の中をあっちこっちひっかきまわして探し回るよりは、部屋を整理してから探したほうが、探し物は簡単に見つかります。経験的には、入り組んだコードは、コードを整理するだけで、問題が解決することが多いものです。コード整理の途中で、それも早い段階で解決策が見つかることがほとんどです。

次の4つのコード整理のテクニックは効果絶大です。

  • 段落に分ける(空白行を追加する)
  • 説明用の変数を導入する
  • 説明用のメソッドを導入する
  • ガード節で条件分岐を単純化する

もっとも簡単なコード整理は、コードの意味的な切れ目に改行を1つ追加して、コードのかたまりを「段落」に分けることです。

頭の中で、追いかけているロジックの切れ目を「段落」として目に見えるようにするだけで、コードの構造がわかりやすくなります。

変数を使いまわして代入を繰り返していたら、用途ごとに別の変数を宣言します。それぞれの変数に「目的(意味⁠⁠」を表す変数名をつけます。冗長なようですが、こうやって用途ごとに別の変数を作って、意味のある名前をつけることで、ロジックがわかりやすくなります。また、用途ごとに別の変数を使うほうが、思わぬ副作用が減って、コードが安定します。

段落に分け、説明用の変数を導入すると、コードのまとまりを別のメソッド(下請けメソッド)に切りだしやすくなります。メソッドは「目的(意味⁠⁠」を説明する名前をつけます。こうやって「説明用のメソッド」を導入すると、もとのロジックの詳細が隠ぺいされ、大きな処理の流れがはっきりしてきます。また、⁠説明用メソッド」は、再利用できる場合があります。重複していたコードをひとつのメソッドに一元化できれば、同じ修正をあちこちでやる必要がなくなりコードの変更が楽で安全になります。

複雑なif文を分解整理する技

複雑なif文は、やっかいなバグの温床です。if文を整理するコツは、まずは、説明用メソッドの導入です。条件の判断式、条件ごとの処理内容を、メソッド単位にまとめて、詳細を隠ぺいします。

処理の詳細をメソッドで隠ぺいすると複雑なif文の分岐の構造がはっきりしてきます。

if文の多くは、例外条件の判定と例外的な処理です。多くの場合、例外ケースは、早い段階で判定し、その場でリターンできます。例外条件を最初に判定し、早期にリターンするこの方法を「ガード節」と呼びます。複雑なif文を整理するときに絶大な効果を発揮しますリスト1⁠。

リスト1 if-elseを早期リターンで書き換える
//if-elseを使った分岐処理

if(正常条件) {
  正常時の処理();
} else {
  例外時の処理()
}

//早期リターンを使った、if文の単純化

if(例外条件) {
  例外時の処理();
  return ;
}

正常処理();

基本はコード整理

やっかいなバグと戦う前に、4つのコード整理のテクニックを使って、わかりにくいコードを整理してみましょう。

急がばまわれです。段落に分け、説明用の変数や説明用のメソッドを導入し、if文を単純化するだけで、多くの問題がすんなりと解決します。

複雑なコードのままで格闘するのは、状況をますます悪化させます。⁠時間」という貴重な資源を浪費します。わかりにくいコードと格闘する「時間」の浪費が、ソフトウェア開発のスケジュールを致命的に遅らせます。限られた時間で問題を解決するために、まずやるべきことは、わかりにくく入り組んだコードを整理して、単純でわかりやすいコードに書き換えることです。

上級者が陥りやすい「ワナ」

技術チャレンジの是非

経験を積み、技術力に自信がついてくると、バグや不具合をより高いレベルで解決したくなります。あちこちに重複した似たようなコードをリファクタリングして、きれいに除去したり、不必要に複雑化した分岐を多態やデザインパターンを使ってすっきりさせたくなります。

既存のフレームワークではうまくいかない部分を独自に拡張したり、パターン化できる退屈な作業は自動化したくなります。

技術者として、正しい姿勢です。しかし、その方向が、目の前にあるトラブルを解決する最善の選択肢とは限りません。とくに「一定時間」で解決することが要求される状況では、技術的なチャレンジには、泥沼にはまり込む危険が常につきまといます。

より良くしたい「幻想」のワナ

技術的にチャレンジしたくなるトラブルは、巧妙に仕組まれた「ワナ」です。そこにはまり込むつもりがなくても、気がつくと何時間もチャレンジして、結局、解決ができないまま時間切れ、という恐ろしい「ワナ」です。

なかでも特別に怖い「ワナ」「もう少しで、できそうだ」⁠ここだけ解決すれば、一挙に解決する」という、すばらしいゴールが近くに見える「幻想」です。

私自身、何度もこの「幻想」「ワナ」にはまって痛い目にあってきました。技術的なチャレンジは、一種の麻薬です。やっているときは時間を忘れて集中できますが、気がつくと、貴重な開発時間をまる1日つぶしていたり、一晩徹夜して、翌日の作業の効率を大幅に落としたりしたことが何度もあります。

時間を見極めていますか?

技術者としてそのチャレンジをやめる必要はありません。しかし、一定時間内に結果を出さなければいけないソフトウェア開発で、ましてや想定外のトラブルの解消のために、消費して良い時間は限られています。

プロの開発者としてやるべきことは、チャレンジする「時間」の限度を設定して、それを超えたチャレンジはやらないことです。

短時間で確実にトラブルを解消できる方法がわかっているなら、技術的に泥臭いやり方であっても、そのやり方でいったんはトラブルを解消しておくことが最優先です。

その解決策を確保したうえで、より良い解決策へのチャレンジに使ってもよい時間を逆算します。その時間内でのみ、チャレンジを行います。1時間と決めたら、きっちり1時間だけ技術チャレンジをします。

引き返す勇気と受け入れる力

チャレンジに当たって、泥沼に陥らないために、2つのことをいつも自分に言い聞かせます。

第一は、頂上の直前で「引き返す勇気」です。許された時間を使ってしまったら、⁠もうちょっとでできる」とどんなに思っても、そこでチャレンジをストップしなければいけません。⁠引き返す勇気」が、泥沼化を防ぎ「幻想」のワナから抜け出すために、絶対に必要です。

第二に、⁠中途半端」を受け入れることです。ソフトウェア開発は、すべて「道半ば」⁠中途)であり、いつも「半端」⁠未完成)なのです。限られた時間では、コードをきれいにできる範囲は限られています。そのときに「すべて直す」「まったく直さない」か、という二者択一が良い判断とは限りません。⁠許された時間内で、できるところまでは改善しておく」ことも有力な選択肢です。結果は「中途半端」かもしれません。しかし、なにもしないよりは、状況が改善するなら、その時間の中で、できる範囲のところまではやるべきです。

上級者として腕の見せどころは、いわゆる技術力ではなく、⁠頂上の直前」でも引き返せる決断力であり、⁠中途半端」であるが、ここまではやっておいたほうが全体としては改善する、という俯瞰的な判断力なのです。

浅い解決・深い解決

トラブルを解決したときに「動いた」⁠ああよかった」でおしまいにすると、成長の機会を失います。それが「浅い解決」なのか「深い解決」なのか、自問してみましょう。

「浅い解決」とは、⁠なぜそうなるかはわからないが、いちおう解決した」とか「そのやり方しか思いつかなかった」という解決です。

「深い解決」とは「しくみ」「原理」をきちんと理解し、⁠そうかわかった」と納得できた解決です。また、複数の選択肢をいろいろな角度から検討し、トレードオフに迷いながら選んだ解決策です。

トラブルシューティングは時間との戦いです。いつも「深い解決」ができるわけではありません。しかし「浅い解決」であることを自覚しておくと、後から、なにかのきっかけで「深い解決」に出会える機会が増えます。逆に「動いたよかった」で済ませている限り「深い解決」との出会いは起きません。

問題を解決した後で、それが「浅い解決」「深い解決」か、自問してみることを習慣にすると、技術者として成長の機会が確実に増えるものなのです。

Software Design

本誌最新号をチェック!
Software Design 2022年9月号

2022年8月18日発売
B5判/192ページ
定価1,342円
(本体1,220円+税10%)

  • 第1特集
    MySQL アプリ開発者の必修5科目
    不意なトラブルに困らないためのRDB基礎知識
  • 第2特集
    「知りたい」⁠使いたい」⁠発信したい」をかなえる
    OSSソースコードリーディングのススメ
  • 特別企画
    企業のシステムを支えるOSとエコシステムの全貌
    [特別企画]Red Hat Enterprise Linux 9最新ガイド
  • 短期連載
    今さら聞けないSSH
    [前編]リモートログインとコマンドの実行
  • 短期連載
    MySQLで学ぶ文字コード
    [最終回]文字コードのハマりどころTips集
  • 短期連載
    新生「Ansible」徹底解説
    [4]Playbookの実行環境(基礎編)

おすすめ記事

記事・ニュース一覧