背景
技術的負債(コードの品質)は、コードを書かない人に伝わりにくい考え方です。
百聞は一見にしかずの精神で具体例なコードを見せて少しでも雰囲気を理解してもらう方法を模索してみました。
良いコードを書くためにChatGPTを使うことが多いのですが、悪いコードをどこまで生成できるか楽しむ要素もあります。
ネタの投稿なので、時間と心にゆとりのない方はそっと閉じてください。
丸投げした結果
まずはChatGPTに丸投げしてみます。
エンジニアでない人に技術的負債を理解してもらうためのサンプルコードを作ってください。
サンプルコードの1つは良いコード。もう1つは技術的負債がたくさんあるコードにしてください。
# 制約
- 言語はPython
- 行数は20行くらい
- 良いコードと技術的負債があるコードの挙動は同じにする
技術的負債があるコードは、将来的にメンテナンスが困難になる、読みにくい、拡張が困難なコードを指します。以下の2つのPythonコードは、同じ機能を持ちながら、一方は良いコード、もう一方は技術的負債が多いコードとして示されています。
良いコード
def calculate_total_price(prices, tax_rate): """商品の価格のリストと税率を受け取り、税込みの合計価格を返します""" total_price = sum(prices) total_price_with_tax = total_price * (1 + tax_rate) return total_price_with_tax # サンプルデータ prices = [100, 200, 300] tax_rate = 0.08 # 税込みの合計価格を計算 total_price = calculate_total_price(prices, tax_rate) print(f"税込みの合計価格: {total_price}円")
技術的負債があるコード
def t(p, tr): tp = sum(p) tpwt = tp * (1 + tr) return tpwt # サンプルデータ p = [100, 200, 300] tr = 0.08 # 税込みの合計価格を計算 tp = t(p, tr) print(f"税込みの合計価格: {tp}円")
解説
良いコード:
- 関数名と変数名が明確で、コードの目的を理解しやすい。
- Docstringを使用して関数の目的とパラメータを説明しています。
技術的負債があるコード:
- 関数名と変数名が不明確で、コードの目的を理解するのが困難。
- コードにコメントや説明がなく、他の人が理解するのが困難です。
このように、技術的負債があるコードは、短期的には時間を節約できるかもしれませんが、長期的にはメンテナンスが困難になり、エラーが発生しやすくなります。
これはこれで使えるような気がします。
でも、なんでしょう。私が昔見た絶望感が出てない気がします。(完全に感情論)
button1、button23と、延々に続くどのボタンを指しているかわからない変数。
if文、for文の組み合わせによる凄まじいネスト。
20行では厳しそうですが、もうちょっと頑張ってみます。
こちらが初期のサンプルコードを作成する
試しに人間が最初にコードを作成してから改悪してもらいます。
- 20行くらいでコードを書かない人でも理解できそうな簡単なコードを作ります。
- ChatGPTを使ってそのコードを改悪します。
forループ、関数くらいは入れて作ります。
20行程度のサンプルコードとして、身長と体重からBMIを計算して出力するプログラムを作りました。
def calc_bmi(height_cm: float, weight_kg: float) -> float:
"""
BMIを計算する
height_cm: 身長(cm)
weight_kg: 体重(kg)
"""
return weight_kg / ((height_cm * 0.01) ** 2)
if __name__ == "__main__":
people_data = {
"id1": {"name": "Taro", "height_cm": 168.2, "weight_kg": 66.0},
"id2": {"name": "Jiro", "height_cm": 182.0, "weight_kg": 76.2},
"id3": {"name": "Takeshi", "height_cm": 175.5, "weight_kg": 86.0},
}
for person in people_data.values():
bmi = calc_bmi(person["height_cm"], person["weight_kg"])
print(f"{person['name']}さんのBMI: {bmi:.2f}")
そしてChatGPTにお願いして改悪します。
エンジニアでない人に技術的負債を理解してもらうためにサンプルコードを改悪してください。
# 制約
- サンプルコードの挙動は変えないで改悪する
技術的負債を増やすために、コードを改悪することは推奨できませんが、教育目的であれば問題ありません。以下のコードは、元のコードの挙動を保持しながら、読みにくく、メンテナンスが困難なコードになっています。
def c(h, w): return w / ((h * 0.01) ** 2) if __name__ == "__main__": d = { "1": {"n": "Taro", "h": 168.2, "w": 66.0}, "2": {"n": "Jiro", "h": 182.0, "w": 76.2}, "3": {"n": "Takeshi", "h": 175.5, "w": 86.0}, } for p in d.values(): b = c(p["h"], p["w"]) print(f"{p['n']}さんのBMI: {b:.2f}")
改悪点の説明:
- 関数名と変数名を短縮して、コードの可読性を低下させました。
- Docstringと型ヒントを削除して、関数の目的と期待される入力を不明確にしました。
- データ構造のキー名を短縮して、データの意味を不明確にしました。
このようなコードは、他の人が理解しにくく、将来的にバグを引き起こしやすくなります。また、新しい機能を追加するのが困難になります。このような理由から、このコードは「技術的負債が高い」と言えます。
いい感じな気がします。もうデータの"Taro"と"Jiro"しか頭に入ってきません。
循環的複雑度を上げるように指定してさらに改悪してみます。
挙動は変えないで循環的複雑度の値が上がるように、さらに改悪してください。
def c(h, w):
if h > 0:
if w > 0:
if h > 100:
if w > 30:
pass
else:
return 0
return w / ((h * 0.01) ** 2)
if __name__ == "__main__":
d = {
"1": {"n": "Taro", "h": 168.2, "w": 66.0},
"2": {"n": "Jiro", "h": 182.0, "w": 76.2},
"3": {"n": "Takeshi", "h": 175.5, "w": 86.0},
}
for p in d.values():
b = c(p["h"], p["w"])
print(f"{p['n']}さんのBMI: {b:.2f}")
ここまでくると絶望しかありません。出力が変わらないことが不思議なレベルです。
友人に見せてみた
趣旨を説明してコードを書いたことがない友人に見せてみました。
ワイ: (丸投げして作った組み合わせを見せつつ) どっちの方が修正しやすそう?
友人: 2つ目の方がなんかスッキリしててやりやすそう
ワイ: ファッ???
ワイ:(サンプルコードから改悪した組み合わせを見せつつ) こっちの組み合わせだとどっちの方が修正しやすそう?
友人: これは最初の方がスッキリしてる感じ
ワイ: お、おう・・・(スッキリか)
私のプレゼン(見せ方)に問題があったと思いますが、読む意思がない人からするとスッキリという印象を抱くのは盲点でした。
自分でコードを書かないポジションの人へのプレゼンでも起こりそうな気がしていて、口には出さないと思いますが、同じ印象をもっても不思議ではないと思いました。
人間の直感的な印象は大事なので気をつけたいと思います。
でも循環的複雑度は直感にも訴えかけられると思いました。
結論
成果はありませんでした。ここまで読んでくれた皆様ごめんなさい。
ChatGPTさん間違えた使い方してごめんなさい。