詳細設計書を書かせることが偶にある

詳細設計書のほとんどは要らないという説には全く同意する。ソースコードで表現すればいいじゃないかと思う。

しかしこれまでに僕は詳細設計書を書かせて提出を求めたことが何度かあった。それはその人のソースコードを書く能力が極めて未熟だったときだ。
たとえばテキストファイルを処理するプログラムで、ある行が"END"だったらその行は処理しない、みたいなやつで下記のようなコードが出てくる。

with open('hoge.sh', 'r') as f:
    for line in f:
        if (line.strip() == "END") :
            break
        行ごとの処理

「このbreakはcontinueの間違いじゃないか?」と指摘しても書いた本人がいまいち要領を得ないようなそんな状況。本人がbreakとcontinueについてちゃんとわかっておらず、ググって出てきたコードをコピペしながら作るとこうなる。概要の設計があって、そこから実装させてコードを見ると高確率で間違っているのだ。テストするとNGがわりと出てくるので直させると、なにかよくわかってないまま試行錯誤でコードを弄ってたまたまテストがOKになったら「できました」と言って提出してこられてしまう。

そしていちばん困るのは本人が仕様を勘案した結果"END"の行で処理を終えてしまうように敢えて実装したのか、それとも本人は単に"END"を無視したかっただけのところ、よくわからないままこのようなコードを書いたらたまたまテストを通過したのでOKにしているだけなのかがわからないところだ。人生長く生きていると何度も何度もこのレベルの人が実戦投入されている現場に出くわしてしまうのである。


"BIG THINGS"という本にピクサーがアニメーション映画を作るときにプロトタイプを8回つくってから本番の作画や音声の制作に入るらしい。そのプロトタイプは設計書と言えるだろう。なぜそのようなことをするかというとプロトタイプを作りなおすのはその後のプロセスに比べてローコストだからだ。ローコストな時点で間違いに気付いて修正できれば、トータルのコストを抑えることができる。
コードを書くのに本当に慣れていない人が現場に居る。その人はコードを書くのに僕らの10倍のコストがかかる。僕が30分で書くコードと同じものを作るのに2日かかってしまう。その上そのコードで間違いを指摘しても、(コードの意味を理解することができていないので)指摘が伝わらないなんてことが起こる。一方で日本語で詳細な処理内容を書くのであればその1/5くらいの時間できる。そのような場合に、まず詳細な処理内容を書いてもらって、そこでレビューして方向修正するようなことが必要になるのだ。


まあでも大抵のケースで詳細設計書というものは「アセンブラでコードを書いていた時代には必要だったけどいまは不要なもの」だと思う。C言語バージョン管理システムも便利なライブラリもなかった時代、アセンブラでコードを書くのはわりと大変な作業だったし、アセンブラのソースを見ても、なにをやっているのか、処理内容がバグなのか意図的にそうしているのかを見極めるのは難しかった。設計とコードの間に、抽象度とか表現される内容にギャップがある場合には設計段階での抽象度を設計書として残しておく必要があると思う。


たとえば「与えられた数字の平均を計算する」みたいな日本語で1行で表現できるような内容、6502のアセンブラでCoPilotに書いてもらったら下記のようであった。そして、もちろん複数のバグがある。

; 6502 Assembly Program to Calculate the Average of a List of 16-bit Numbers

    .org $8000       ; Program start address

    LDX #$00         ; Initialize index register X to 0
    LDY #$00         ; Initialize index register Y to 0
    LDA #$00         ; Clear accumulator A
    STA SUM          ; Clear SUM
    STA SUM+1        ; Clear SUM high byte
    STA COUNT        ; Clear COUNT

LOOP:
    LDA NUMBERS,X    ; Load low byte of number
    CLC              ; Clear carry flag
    ADC SUM          ; Add to SUM low byte
    STA SUM          ; Store low byte of SUM
    BCC NO_CARRY     ; If no carry, skip next instruction
    INC SUM+1        ; Increment high byte of SUM if carry

NO_CARRY:
    LDA NUMBERS+1,X  ; Load high byte of number
    ADC SUM+1        ; Add to SUM high byte
    STA SUM+1        ; Store high byte of SUM

    INX              ; Increment index register X
    INX              ; Increment index register X again (16-bit number)
    INY              ; Increment index register Y (count)
    CPX #NUM_COUNT*2 ; Compare X with total number of bytes
    BNE LOOP         ; If not equal, repeat loop

    LDA SUM          ; Load low byte of SUM
    LSR A            ; Logical shift right (divide by 2)
    ROR SUM+1        ; Rotate right through carry (divide high byte by 2)
    LSR A            ; Logical shift right (divide by 4)
    ROR SUM+1        ; Rotate right through carry (divide high byte by 4)
    LSR A            ; Logical shift right (divide by 8)
    ROR SUM+1        ; Rotate right through carry (divide high byte by 8)
    LSR A            ; Logical shift right (divide by 16)
    ROR SUM+1        ; Rotate right through carry (divide high byte by 16)

    STA AVERAGE      ; Store low byte of average
    STY AVERAGE+1    ; Store high byte of average

    BRK              ; Break (end of program)

NUMBERS:
    .word $1234, $5678, $9ABC, $DEF0 ; Example 16-bit numbers

NUM_COUNT = 4      ; Number of 16-bit numbers

SUM:
    .res 2          ; Reserve 2 bytes for SUM

COUNT:
    .res 1          ; Reserve 1 byte for COUNT

AVERAGE:
    .res 2          ; Reserve 2 bytes for AVERAGE

    .end

このプログラムは、メモリに格納された16ビットの数字列の平均を計算します。NUMBERSセクションに数字列を定義し、NUM_COUNTに数字の数を設定します。プログラムは、各数字を合計し、その合計を数字の数で割って平均を計算します。

アセンブラが主流の時代、設計と実装のギャップは今日よりも随分と大きかったので、詳細設計書を残すことは必須としても納得性があったのではないか。あんまり落ちはない。