実例で学ぶ「なぜハードコーディングは良くないのか」と、ちょっとの考察
ハードコーディングとは
本来ソースコード上に直接書くべきでない定数や文字列をそのまま書き込むこと。
マジックナンバー(一見すると何の数値かわからないもの)や、環境設定キーを直接書き込むことなどが例に挙げられることが多い。
実例と修正例
がむログという個人ブログを運営していて、そこでハードコーディングしてしまっていると思われる実装があったので修正した。
↓これからコードで説明するFooterの実物
ハードコーディングの例
以下、Footerコンポーネント
export function Footer() {
return (
<footer className="bg-gray-950 text-white pt-4 pb-6 md:py-16">
<div className="pb-2 md:pb-8 flex flex-col items-center">
<h3 className="text-2xl md:text-4xl font-bold tracking-tight leading-tight">
gAmuLog.
</h3>
<div className="mr-2 text-sm">がむログ</div>
</div>
<div className="flex justify-center">
<FooterItems />
</div>
<div className="mx-20 mt-8 md:mx-[36rem] flex justify-between">
<Link href="https://zenn.dev/gamuprog" target="_blank" rel="noopener noreferrer">
<SiZenn className="text-4xl md:text-6xl" />
</Link>
<Link
href="https://twitter.com/gamu_prog"
target="_blank"
rel="noopener noreferrer"
>
<FaXTwitter className="text-4xl md:text-6xl" />
</Link>
<Link
href="https://github.com/gamuprog"
target="_blank"
rel="noopener noreferrer"
>
<FaGithub className="text-4xl md:text-6xl" />
</Link>
</div>
</footer>
);
}
export default Footer;
修正例
export function Footer() {
return (
<footer className="bg-gray-950 text-white pt-4 pb-6 md:py-16">
<div className="pb-2 md:pb-8 flex flex-col items-center">
<h3 className="text-2xl md:text-4xl font-bold tracking-tight leading-tight">
gAmuLog.
</h3>
<div className="mr-2 text-sm">がむログ</div>
</div>
<div className="flex justify-center">
<FooterItems />
</div>
<div className="mx-20 mt-8 md:mx-[36rem] flex justify-between">
<Link href={externalURL.zenn} target="_blank" rel="noopener noreferrer">
<SiZenn className="text-4xl md:text-6xl" />
</Link>
<Link
href={externalURL.twitter}
target="_blank"
rel="noopener noreferrer"
>
<FaXTwitter className="text-4xl md:text-6xl" />
</Link>
<Link
href={externalURL.github}
target="_blank"
rel="noopener noreferrer"
>
<FaGithub className="text-4xl md:text-6xl" />
</Link>
</div>
</footer>
);
}
export default Footer;
export const externalURL = {
twitter: "https://twitter.com/gamu_prog",
github: "https://github.com/gamuprog",
zenn: "https://zenn.dev/gamuprog",
};
何が良くなかったのか
修正点は見てもらった通り、外部URLをFooterコンポーネントに直で書くのではなく、定数などをまとめるファイルに定義してからそれを呼び出す形でコーディングした点である。
今回の例はマジックナンバーという感じではなかった。よくハードコーディングというと、消費税を例にしたマジックナンバーが現れるコードでの説明が多い。
マジックナンバーのよくないところは明快で、何のことか分かりづらい、変更内容が膨大になる、セキュリティに問題が起こる可能性がある、などだ。
参考
外部URL直書きは変更内容が膨大になる可能性がある!
今回、各種URLをconstants.ts
にまとめてそれをimportする形でリンクを作成したが、こうすることで、他のコンポーネントも同じconstants.ts
を参照してリンクを作成できる。そして、たとえばもし自分のZennのURLが変わったら、このconstants.ts
を変更するだけで一括で複数のコンポーネント内のリンク先を変更できる。
ただ、今回の例はURLなので、マジックナンバーのように「それが何を意味しているかわからない」といった問題はあまり起こらなそうとは感じた。「変更内容が膨大になる可能性を秘めてしまっている」というのが一番の問題点であった。
もう一歩だけ踏み込んで考える
なぜ、「変更内容が膨大になって良くない」かを一般化して考える
さっきまでその説明をしていたのに何をいっているんだと思われそうだが、URL直打ちという具体例から、より一般化して問題点を考察する。
ずばり、「責務の分離」ができていない
ハードコーディングをしていたコードは、責務の分離ができていなかったことが根本の原因で、副作用として「変更内容が膨大になる可能性」が発生していた。
Footerコンポーネントの責務は「Footer内のアイテムの表示と、アイテム押下時の適切な画面遷移(外部サイト移動含む)」だ。
このコンポーネント内の、Zennアイコンボタンに求められているのは
「https://zenn.dev/gamuprog
に遷移すること」ではない。
「がむのZennのページに遷移すること」である。
そのページのリンクが今はたまたまhttps://zenn.dev/gamuprog
であっただけなのだ。
がむのZennのページのリンクは時間の経過によって変更される可能性のある定数である。
であれば、「Zennのリンクである」と理解できるような定数名を用意しそれをFooterコンポーネントで用いるのが良い。そこまでしか責任を持たない方がいい。
そして、「各種定数に、具体的に最新で正しいURL文字列を定義する」という責務を持ったconstants.ts
を作成することで、責務の分離が達成される。
変更されうるURL文字列は様々なコンポーネントで利用される可能性があるからこそ、「各種定数に、具体的に最新で正しいURL文字列を定義する」という責務に集中した定数管理コンポーネントを作成し、各種UIコンポーネントがその定数管理コンポーネントを参照するという形にすることで、コード全体で柔軟性が保たれる。
終わりに
今回はハードコーディングがなぜよくないのか、ということについて、実例を挙げながら少し深掘りました。普段はがむログで記事を投稿しているので、興味があればぜひ見てみて下さい🙌
↓このブログ自体を開発してきた流れなども開発日記として記事にしてます👀
Discussion