✏️

インプットとしてのコーディングとアウトプットとしてのコーディング

2021/01/17に公開

はじめに

コードを書くという行為がアウトプットと表現されることは多くの人にとって受け入れられることだと思うのだけど、そのように表現されることに対してどうしても違和感を持つシーンにこれまで出会ったことがある。
残念なことに、そのような時の最終産物はあまりいい出来では無いと感じることが多い。
ソフトウェア開発に習熟している人は意識してかどうかは知らないが、自然とできていることだと思うが、あまりこういう話をインターネットで見たことがないので参考になれば幸い。

ソフトウェア開発のステップ

ソフトウェア開発はいくつかのステップで構成されることが多い。それらを分類にはいくつかの観点や考え方があるが、とても大きく分けると『インプット』と『アウトプット』に分かれると思っている。
これ自体は多くの人に共感してもらえると思うが問題は、何が『インプット』で何が『アウトプット』であるかということだと思う。

インプット

私は、『インプット』を『開発対象・開発そのものに関する情報を収集・理解すること』だと思っている。以下などが含まれると多くの方が共感できると思う、

  • ドメインエキスパートへのインタビュー
  • ソフトウェアアーキテクチャの設計
  • データモデリング

所謂『設計フェーズ』に属するものを挙げた。
ただ、それとは異なるステップだが、以下も含まれると考える。

  • ソフトウェアのプロトタイピング
  • バグ改修のためのデバッグプリントの埋め込み

前者は、エンドユーザが求めるものが何なのかを検証するためであったり、ステークホルダに対して要求と相違無いかをするためであったり、検討したアーキテクチャが適当であるかを検証したりするなどを目的にしている。
つまり、ソフトウェアの実装を伴っているが、目的は『開発対象・開発そのものに関する情報を収集・理解すること』だ。コーディングや実装は、そのための手段に過ぎない。

後者も同様だ。ソフトウェアのどこに誤りが存在するかを確認するために、ソフトウェアに命令を追加している。ただこれは、バグに関する情報を収集するための手段であり、目的ではない。

アウトプット

当然だが、アウトプットはインプット以外のすべてになる。
とても大事なことは、『アウトプットに相当するタスクを実施する時は完了条件が明確である。』ということ。
なぜなら、インプットのステップで対象に関する情報はすべて集まっており、どのようにソフトウェアは振る舞えば適切であるかが自明な状態にあるはずだから。

例えば、ヘッドレス CMS の設定をしようと思う時、 API のレスポンスとして期待するスキーマや、CMSのコンテンツ作成画面のレイアウトがどのような状態であるかが明確であるはずだ。
それに合致するような設定をする。これは、『アウトプット』だ。

逆にそれらがわからないときは、リッチテキストエリアを追加してみて、どんな入力がサポートされているかを試してみたり、それを利用想定者に共有して満足するかを確かめたりするはずだ。これは『インプット』だ。

インプット・アウトプットを分ける意味

『この分類になんの意味があるんですか?』と思う人がいると思う。
問題なのは、これらが混同されて、『アウトプット』に『インプット』が混ざることがそれなりに存在していることだと思う。

JavaScript の開発でESLintを用いて console.log が含まれないようにチェックをするというのは、それを強く象徴していると思う。

なぜこのようなことが起こるかというと、ソフトウェアの実装中は『インプット』と『アウトプット』が頻繁に切り替わることが多いからだと思う。
あるレベルでの『インプット』が完了して『アウトプット』のステップに入っても、また別のレベルでの『インプット』が必要になることがある。そうすると、自分は今『アウトプット』をしていると錯覚して『インプット』を混ぜてしまう。

例えば、あるモジュールの設計を完了して、これからソフトウェア実装を開始するとする。
まずは、モジュールに含まれるある関数を対象にしようと考える。その関数に期待する振る舞いは自明だが、その振る舞いを実装レベルでどのように表現するかを検討する必要がある。
この段階で早速『インプット』のステップに入っているのだが、それに気が付かないまま実装を書き始め、「うん?思ったように動かないな?」とか「あれ?このAPIの振る舞いが思ったより違うな?」とかが発生し始めて、その都度各種リファレンスをあたって実装を色々変更しながら、『思ったように動くと思うからヨシ!』で『アウトプット』のステップが完了する。

こうやって出来上がったソフトウェアは責任の所在が曖昧だったり、コードの見通しが悪かったり、誤りが含まれていることが多かったりするという印象が強い。

どうしよう

とにかく大切なのは、『今自分がなんのために作業しているか』を認知すること。
ソフトウェアを単一責任のモジュールの組み合わせとしてデザインするように、タスクを単一目的の作業の組み合わせとしてデザインする。

それをサポートするツールとしては TODO リストなど素朴なものから色々あると思うが、効果があると思ったものを2つ紹介したい。

TDD

TDD自体については今更説明するほどのものでは無いと思うので割愛する。
テストを実装するというフェーズと、そのテストに通すというフェーズに分かれているところが、とてもよくフィットしている。
テストの実装フェーズは、対象のインターフェースや、振る舞いをどのようにするかということをコードで表現しながら検討する『インプット』フェーズだし、テストに通すフェーズは、明確になった要求を適切に表現するという『アウトプット』フェーズとなる。あるいは、こうすれば思ったとおりに振る舞うということを確認するための『インプット』かもしれない。最終的にリファクタリングを実施して、『アウトプット』が完了する。

他にも、新しいライブラリの振る舞いがわからなければ、それを検証するためのテストを書けばいい。不要になったら消せばいい。

今、自分がなんのためのコーディングをしているのかがとてもわかりやすい。

ペア・モブプログラミング

客観的な視点を持った第三者と秩序が存在すれば達成できることが多い。
結果、ペアプログラミングやモブプログラミングを実施することでこれが実現できることが多いと思う。

あるいは、『ナビゲータ (モブ)』と『ドライバ (タイピスト)』という役割で『インプット』と『アウトプット』が分けられることもあると思う。

おわりに

これまで、ソフトウェアの開発とりわけ実装(コーディング)のフェーズで見かけた、『インプット』と『アウトプット』の混同と、それによって引き起こされるソフトウェア品質の低下について記載しました。

良いソフトウェアが適切に責任分割されたモジュールので構成されるように、良い仕事は適切に目的が分けられたタスクで構成されると思います。

日々のソフトウェア開発の参考になれば幸いです。

Discussion