命令的なコードの技術的負債。


2019年9月9日

皆さんこんにちは末ちゃんです。
今日は先週土曜日社内で30分くらい話したことを、ブログ記事にしようと思います。

題して「命令的なコードの技術的負債」

今回例では Pug を使用しますが、PHPはもちろんJavaScriptなどの言語でも応用の利くお話しです。
(今回は完全に静的な状態をサンプルにします。)

技術的負債とは

技術的負債という言葉はよく使うので皆さんご存じかと思います。
平たく言ってしまうと、今後の開発作業を困難にさせる状態のことです。

例えば

  • 作成されないドキュメント
  • コンパイラの警告等が無視された状態
  • 複雑になりすぎて、解析困難なコード
  • コーディングガイドに従わないコード
  • 放置されたTODO
  • 古すぎるバージョン
  • 開発環境と本番環境での差異

このような状態が技術的負債と言われます。

あと、あるあるなのがひとつのサーバーコンテナに複数のサービスが放り込まれた状態
サーバーのバージョンアップにリスクがありすぎてセキュリティリスクが放置され、結局セキュリティインシデントになります。やめましょう。

命令的なコードを宣言的あるいは関数的にしてみる

例えば今回はJSTであるPugを使用すると前提して、以下の様なHTMLを出力するとします。

<div class="container">
  <div class="list">
    <div class="list__item">
      <img src="http://example.com/example.jpg" alt="example image">
      <p>Lorem ipsum...</p>
    </div>

    <div class="list__item list__item--fill">
      <img src="http://example.com/example2.jpg" alt="example2 image">
      <p>Lorem ipsum...<a href="#">Read more.</a></p>
    </div>
  </div>
</div>

これを単純にpugのフォーマットに直すと以下のようになります。

.container
  .list
    .list__item
      img(src='http://example.com/example.jpg', alt='example image')
      p Lorem ipsum...

    .list__item.list__item--fill
      img(src='http://example.com/example2.jpg', alt='example2 image')
      p Lorem ipsum...
        a(href='#') Read more.

これだけでも記述量が減って随分楽になりました。
しかしこの内容が何十件にもなると、ちょっと辛いです。

なのでコンテンツを変数に格納して、eachで回してみました。

-
  const list = [
    {
      id: 1,
      img: {
        src: 'http://example.com/example.jpg',
        alt: 'example image',
      },
      paragraph: 'Lorem ipsum...',
      link: null,
      extendCssClass: '',
    },
    {
      id: 2,
      img: {
        src: 'http://example.com/example2.jpg',
        alt: 'example2 image',
      },
      paragraph: 'Lorem ipsum...',
      link: {
        to: '#',
        label: 'Read more.'
      },
      extendCssClass: 'fill',
    },
  ];

.container
  .list
    each item in list
      if item.extendCssClass.length
        .list__item(class='list__item--' + item.extendCssClass)
          img(src= item.img.src, alt= item.img.alt)
          p= item.paragraph
            if item.link
              a(href= item.link.to)= item.link.label
      else
        .list__item
          img(src= item.img.src, alt= item.img.alt)
          p= item.paragraph
            if item.link
              a(href= item.link.to)= item.link.label

うーん、まだ微妙ですね。同じコードを2度も繰り返してしまっている時点でナンセンスですし、このままではcssのmodifierが1個しかセットできません。

なので、ちょっと改修してみました。

-
  const list = [
    {
      id: 1,
      img: {
        src: 'http://example.com/example.jpg',
        alt: 'example image',
      },
      paragraph: 'Lorem ipsum...',
      link: null,
      extendCssClass: [],
    },
    {
      id: 2,
      img: {
        src: 'http://example.com/example2.jpg',
        alt: 'example2 image',
      },
      paragraph: 'Lorem ipsum...',
      link: {
        to: '#',
        label: 'Read more.'
      },
      extendCssClass: ['fill'],
    },
  ];

.container
  .list
    each item in list
      -
        const extendCssClass = function() {
          if (item.extendCssClass.length) {
            let css = '';
            item.extendCssClass.forEach(funtcion(x) {
              css = css + ' list__item--' + x;
            });
            return css;
          }
        };

      .list__item(
        class= extendCssClass()
      )
        img(src= item.img.src, alt= item.img.alt)
        p= item.paragraph
          if item.link
            a(href= item.link.to)= item.link.label

大分まともになりました。
ここまでくれば、コンテンツの増減はもちろん、コンテンツ構造の変化にもかなり柔軟に対応できそうです。

しかし勘の良いというより、プログラミングに不慣れな方はこう思うはずです。
「めちゃくちゃコード長くなってるじゃん。」

その通りです。最初は、

    .list__item
      img(src='http://example.com/example.jpg', alt='example image')
      p Lorem ipsum...

    .list__item.list__item--fill
      img(src='http://example.com/example2.jpg', alt='example2 image')
      p Lorem ipsum...
        a(href='#') Read more.

だったのが、

    each item in list
      -
        const extendCssClass = function() {
          if (item.extendCssClass.length) {
            let css = '';
            item.extendCssClass.forEach(funtcion(x) {
              css = css + ' list__item--' + x;
            });
            return css;
          }
        };

      .list__item(
        class= extendCssClass()
      )
        img(src= item.img.src, alt= item.img.alt)
        p= item.paragraph
          if item.link
            a(href= item.link.to)= item.link.label

コンテンツの中身がなくてもこんなに長くなってしまいました。
つまりコーディングにかかるコストは増えています。

低コストなプロジェクトでは、命令的かつ冗長的なほうがコストが安い

例えばランディングページなどの1ページだけが対象のコンテンツであれば、一番最初に書いたPugの書き方が一番速くて安いです。
しかし、これが複数ページあるウェブサイトになってくると、ひいてはWebアプリケーションになってくると現実的ではありません。

仮にランディングページだったとして、発生しても修正箇所は目の届く範囲に留まるでしょう。

これが十ページ以上のウェブサイトなどになってくると、その使用箇所は計り知れなくなります。
また、仮に新たにこのコンテンツの中にリストを追加したい。という状況になったときの修正対応は考えたくもありません。

よく一括置換を使うと言いますが、大抵無理です。それでなんとかなることはまずありません。

やっとフロントエンドが時代においついた

昨今のフロントエンドのコーディング事情は5年前から大きく変わっています。
散々お世話になったjQueryを使用した命令的コーディングを使うメリットは基本的にありません。

ReactやVue.jsなどを使用すれば、更に宣言的な書き方もできます。

まとめ

多分昔の時代の人や、あまりプログラミングが分からない人にとっては、なんでこんな面倒な書き方・・・。と思われるかもしれません。
そう感じてるうちは多分今のままで大丈夫です。

しかし、今のウェブサイトやウェブアプリは要求が多すぎます・・・。

IE5やIE6ほどではないにせよ、ブラウザごとの動作の違いや、ダイナミックなアニメーションやリアクティブな動作など・・・。

更新する度にお客様に、「え、こんなにかかるの?」と思われるようなコードを書かないように気をつけたいですね。

Share Button