🚀

なるべくイメージする

に公開

ソフトウェア開発における整理

ソフトウェア開発に携わる中で、技術用語が文脈ごとにどのような意味合いを帯びて使われているのかを意識的に整理し、頭の中に定着させておくことは重要だと感じています。
こうした整理は、認知的負荷を下げ、設計や実装における判断を楽にしてくれるのではないか、と思うようになりました。

ここでは、そのための個人的なメモとして、用語の意味や使われ方を整理しています。
内容については、今後も逐次補足・編集していく予定です。


ライブラリ

ライブラリとは、「自分のソースコードの外部にあり、再利用のためにまとめられたコード」を指します。

必要な書籍(=ソース)を図書館(=library)から借りて使わせてもらう、という比喩がしっくり来ます。
視点としては、自らの開発環境から外側に向けられているという印象が強い概念です。

借りてきたライブラリは、そのまま呼び出すことで機能を利用できますが、
制御の主導権はこちら側にあります
使いたい関数や機能を選び、必要なタイミングで呼び出す、という構造です。


モジュール

モジュールは、「ソースコード内部の構成要素」として用いられることが多く、
リポジトリ内における機能のまとまりを表す単位です。

語源はラテン語の modulus(小さな尺度・単位)で、組み合わせ可能な構成パーツ、というイメージが近いと思います。

基本的には、開発対象となるリポジトリ内部の視点で語られる概念であり、
その切り方や粒度には、プロジェクトごとの設計思想や判断が色濃く反映されるかと思います。


プラグイン

プラグインとは、既存のコードに意図的に設けられた
拡張ポイントに挿入される追加機能のことです。

「フックポイント」や「インターフェース」を通じてホスト側のコードと接続され、
その振る舞いを補完・拡張します。

電源プラグを差し込むような直感的メタファーが想起されるように、ホスト側が明示的に開けておいた接続口に対して、追加的な振る舞いを注入するソフトウェア
あるいはその仕組みを指す概念だと理解すると腑に落ちます。


ビルド

ソースコードは人間にとって可読性が高い形で書かれていますが、
そのままではコンピュータが直接実行できる形ではありません。

そのため、コードはコンパイルやトランスパイルといった処理を経て、
中間形式やバイナリ形式へと段階的に変換されます。

この一連の処理過程全体を「ビルド」と呼ぶようです。


フレームワーク

フレームワークとは、特定のアーキテクチャに基づいて構築されたソフトウェアの土台です。
その枠組みに沿って開発を進めることで、一定の設計方針や実装スタイルが自然と揃うようになります。

「枠を使わせてもらう」という語感の通り、
自由度がある程度制限される代わりに、
設計の一貫性やパターン、開発効率が担保される点が特徴です。

ライブラリとの大きな違いは、
フレームワーク側が主導的にユーザーコードを呼び出す点にあるのではないかと思います。
こちらから呼び出すのではなく、
「決められた場所に実装しておけば、適切なタイミングで実行される」という構造が基本です。


ランタイム

ランタイムとは、ソースコードを実行する際に必要となる
実行環境や基盤システムを指します。

言語仕様に従って、
実際の計算資源上でコードを動かす責任を持つ層、と言い換えることもできます。

文脈によっては「実行時」という時間的な意味で使われることもありますが、
ここでは 実行時に介在するソフトウェア的インフラ という意味合いを中心にしています。

「タイム」という言葉が少し不思議に感じられるのは、
コンパイルタイムとの対比で生まれた概念であることも関係しているらしいです。


API(Application Programming Interface)

API とは、ソフトウェア同士が連携・通信するために公開された接点、
すなわちインターフェースです。

人間にとっての UI(ユーザーインターフェース)が操作の窓口であるのと同様に、
プログラムにとっての API は、内部実装を意識せずに機能へアクセスするための入口として機能します。

APIは、
「この形式でリクエストを送れば、この形式で応答が返る」という
操作の契約 だと思います。

API は「どう使うか」だけを明示し、
「内部で何が行われているか」は利用者から隠蔽します。
実装を抽象化し、信頼可能な操作面だけを外部に提示することが、
API の中核的な役割です。

補足:インターフェースという考え方

「インターフェース」とは、
ソフトウェアにおいて表面のみを対象とし、内部実装と切り離して議論するための概念的な境界を指します。

UI において見た目や操作感が重視されるように、
中身を意識せずに接触・操作できる対象として設計される点が共通しています。

補足すると、API は単なる機能の窓口ではなく、
「外部とどのように対話するか」を設計する枠組みとして捉える方が直感的だと思います。


コンパイル(Compile)

コンパイルとは、人間が書いたソースコードを、
コンピュータが直接理解・実行できる形式へと変換する工程を指します。

多くの場合、プログラミング言語固有の文法や意味規則をチェックしつつ、
機械語や中間表現へと変換する処理を含みます。

重要なのは、コンパイルは単なる「変換作業」ではなく、

  • 文法的に正しいか
  • 型や依存関係が整合しているか
  • 言語仕様に反していないか

といった点を事前に検証するフェーズでもある、という点です。

この段階で検出されるエラーは、
実行以前に防げる不整合として扱われます。


リンカ(Linker)

リンカは、コンパイルによって生成された複数の成果物を
一つの実行単位として結合する役割を担います。

ソースコードは通常、複数のファイルやモジュールに分割されており、
それぞれが独立してコンパイルされます。
リンカはそれらを集め、

  • 関数や変数の参照関係を解決し
  • 外部ライブラリとの結合を行い
  • 実行可能な形にまとめ上げる

工程を担当します。

直感的には、
「部品は揃ったが、まだ組み上がっていない状態」から
「実際に動く一つのプログラム」へ仕上げる工程

と捉えると分かりやすいかもしれません。


オブジェクト指向(Object-Oriented Programming)

オブジェクト指向とは、
ソフトウェアを「処理の集合」ではなく、
責務と状態を持ったオブジェクトの集合として捉える考え方です。

データ(状態)と、それを操作する振る舞い(メソッド)を
一つの単位としてまとめることで、

  • 関心ごとを局所化する
  • 変更の影響範囲を限定する
  • 現実世界の概念と対応づけて考える

といった利点を得ようとします。

一方で、
オブジェクト指向は「クラスを使うこと」そのものを目的とするものではなく、
設計上の考え方・視点を提供する枠組みだと捉えた方が自然です。

言語機能としての class や継承は、その表現手段の一つにすぎません。


ドメイン駆動設計(DDD: Domain-Driven Design)

DDD(ドメイン駆動設計)は、
ソフトウェアの構造を、技術的都合ではなく
解決したい業務や問題領域(ドメイン)を中心に設計しようとする考え方です。

特に重視されるのは、

  • 業務上の概念をそのままコード上の概念として扱うこと
  • 用語やモデルを、チーム内で一貫させること

です。

DDD では、
エンティティ、値オブジェクト、集約、リポジトリなど、
独特の用語が多く登場しますが、
それらは「正しい形」を強制するためというより、

「どこに何の責務を置くか」を考え続けるための言語
として機能しているように思います。

DDD はフレームワークではなく、
また万能な設計手法でもありません。
適用する範囲や深さは、プロジェクトの性質や規模によって大きく変わります。


オブジェクト指向と DDD の関係について(補足)

DDD はしばしばオブジェクト指向と結び付けて語られますが、
両者は同一のものではありません。

  • オブジェクト指向は、設計・実装の考え方
  • DDD は、問題領域をどう捉え、どうモデル化するかという指針

という関係に近いと感じています。

DDD の考え方はオブジェクト指向と相性が良い一方で、
必ずしも特定の言語機能や実装スタイルに依存するものではありません。

重要なのは、
技術よりも先に「何を扱っているシステムなのか」を考える姿勢
そのものにあります。



関数型プログラミング(Functional Programming)

関数型プログラミングとは、
ソフトウェアを「状態を変化させる手続きの集合」としてではなく、
入力から出力への変換の合成として捉える考え方です。

中心となるのは、

  • 関数を第一級の値として扱う
  • 状態の変更(ミューテーション)を極力避ける
  • 同じ入力に対して同じ出力を返す関数(参照透過性)を重視する

といった性質です。

この考え方では、
「何をどう変更するか」よりも
「どのような変換を積み重ねるか」
に意識が向きます。


副作用という観点

関数型プログラミングでは、
副作用(外部状態の変更や I/O) をどう扱うかが重要なテーマになります。

副作用が少ないコードは、

  • 振る舞いを局所的に理解しやすい
  • テストしやすい
  • 並行・並列実行と相性がよい

といった性質を持ちます。

一方で、
現実のアプリケーションは必ず外界とやり取りを行うため、
副作用そのものを完全になくすことはできません。

そのため関数型の文脈では、
副作用を「どこに閉じ込めるか」
という設計判断が重要になります。


オブジェクト指向との対比(整理のための視点)

オブジェクト指向と関数型は、
しばしば対立する概念のように語られますが、
ここではあくまで視点の違いとして整理します。

  • オブジェクト指向

    • 状態と振る舞いを一体として捉える
    • 「誰が責任を持つか」に意識が向く
  • 関数型

    • 状態をできるだけ外に出し、変換として扱う
    • 「どんな変換が行われるか」に意識が向く

どちらが正しい、という話ではなく、
どのような複雑さを扱いたいか
によって向き・不向きが変わると感じています。

実際のプロジェクトでは、
両者の考え方が混ざり合って使われることも珍しくありません。


宣言的という考え方

関数型プログラミングは、
しばしば「宣言的(declarative)」であると言われます。

これは、

  • どうやって行うか(手順)よりも
  • 何をしたいか(結果や性質)

を先に記述する、という姿勢を指します。

この点は、
後述する関係代数や SQL とも共通する感覚があります。


関係代数(Relational Algebra)

関係代数とは、
リレーショナルデータベースの理論的基盤となる数学的枠組みです。

データを「表(関係)」として扱い、
それらに対して、

  • 選択(Selection)
  • 射影(Projection)
  • 結合(Join)

などの操作を適用することで、
新しい関係を導き出します。

重要なのは、
関係代数は 「どのようにデータを取り出すか」ではなく、
「どのような関係を得たいか」
を表現する体系である、という点です。


手続きではなく、性質を記述する

関係代数や SQL は、
内部でどのようなアルゴリズムが使われるかを
利用者が直接指定しません。

代わりに、

  • どの条件を満たす行が欲しいか
  • どの列が必要か
  • どの関係をどう結びたいか

といった 結果の性質 を記述します。

この点で、
関係代数は非常に宣言的なモデルだと言えます。


関数型プログラミングとの共通点

関係代数と関数型プログラミングには、
いくつかの共通する感覚があります。

  • 状態の逐次変更を前提にしない
  • 操作を合成して結果を得る
  • 「どう実装するか」を意識しなくてよい

どちらも、
複雑さを人間の認知から遠ざけるための抽象化
として機能しているように思います。


Discussion