astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

デザインガイドラインについて考えよう

f:id:astamuse:20210331101211p:plain

こんにちは。デザイン部の橋本です。

企業やブランド、サービスのデザインを行う際「デザインルール」や「デザインレギュレーション」、「デザインシステム」といったデザインのガイドラインをつくることがあると思います。

弊社でも上記のようなデザインガイドラインを作成し、業務に役立てています。今回はそんなデザインガイドラインについて、他社事例を含めて、いろいろと調査してみました。

デザインガイドラインでよく整備されているもの

デザインガイドラインとは、色・文字・レイアウトなど様々なデザイン要素について、ルールを綿密に定義したドキュメントのことです。 ※今回は主に “Webページやアプリにおけるデザインガイドライン” について調査しています。

デザインガイドラインで整備するものはプロジェクトの規模で変わってきますが、割と整備されていることが多いものをまとめてみました。

f:id:astamuse:20210330193235p:plain

Brand - ブランド
まずはサービスのブランドイメージを決定します。デザインガイドライン全体を通して、統一感を持たせて、共通のメッセージを発信するのに必要な土台となります。
言語化したり、写真を使用したりしてブランドイメージを伝えます。
Typography - タイポグラフィ
タイポグラフィーをスタイルガイドによってどのように最適化するかは、サイト全体のクオリティーを左右する重要なポイントです。
まず見出しの種類( h1〜h6) についてや、本文テキスト、太字やイタリックなどについての設定を行います。続いて、リンクやリード文などに使えるような小さめのカスタムフォント、そして文字のフォントや太さ、色についての検討を行います。
Color - 色
色はブランドイメージと強く関連づけて利用されることが多いので、カラーパレットの選定は、ブランドの追求結果にもとづいて、慎重に行われます。
カラーパレットを作成する際はプライマリーカラー(サービスを特徴づける色)は必ず設定しますが、セカンダリーカラーは設定していない場合もあります。ブランドの主色をより効果的に見せるために、白、灰色、黒などの中間色を加えます。
Icon - アイコン
アイコンは文字よりもはるかに高い伝達能力を発揮します。ユーザーはアイコンを見るだけで、今何が行われているのか、また次に何が起きるのかを瞬時に把握することができます。
アイコンを決定する際はブランドバリューとアイコンの適合性について考慮する必要があります。
Form - フォーム
フォームはユーザーとサイトを結ぶインタラクティブ性の高いパーツです。
ユーザーはフォームを介してデータ入力をし、サイト側はそれに対する操作を行います。起動時やホバー時の表示、また、パスワードが弱すぎることやIDが無効なことを伝えるためのエラーや警告、「支払い完了」などの成功……このようなフィードバック要素について、あらかじめ決定しておきます。
Button - ボタン
ボタンはカラーパレットやフォーム、トンマナを合わせて構成されるツールです。
ボタンデザインには統一感を持たせ、ユーザーに一貫したサイト体験を提供できるように工夫します。
Margin - 余白
統一された正確な余白は、Webサイト全体にまとまりと洗練された印象を生み出すと言われています。
見出しやボタン、画像、フォームにおける余白など、あらゆる要素の余白について設定します。具体的には余白の設定ルール(5の倍数、8の倍数など)を決めて、適応していくパターンが多いです。
UI Element - エレメント
カードデザインや、テーブルデザイン、ページャーなどのUIコンポーネントを収録します。更新頻度が最も高く、収録数も多い要素です。
Logo - ロゴデザイン
ロゴデザインについては、詳細な運用ルールは別紙でまとめられており、デザインガイドラインでは一部のルール(カラー、余白スペース、最小サイズ)だけ触れられているケースが多い気がします。
Photo - 写真
ビジュアルのデザインは、サービスのイメージを左右する重要な要素です。Webサイトのキービジュアルやサムネイル、挿絵などで使用する画像など、主なビジュアル要素についてもガイドラインを設定します。
写真の選定は感覚的な側面も強いため、はっきりとしたルール作りは難しいのですが、OK例とNG例を示すことで目指すエリアを浮かび上がらせることができます。
Illustrations - イラスト
イラストも写真と同様にサービスのイメージを左右する重要な要素です。使用できるイラストの方向性をあらかじめ決めておくと、サービス全体に統一感を持たせることができます。

整備するものに特別な決まりはなく、なかにはデザインガイドラインを作っていないプロジェクトもあります。

デザインガイドラインのメリット/デメリット

デザインガイドラインのメリット

f:id:astamuse:20210331102226p:plain

メリット1. デザイン品質の担保

制作対象の規模が大きくなると、デザインを複数人で行うことがあります。 こうした時にデザインガイドラインがあれば、個別に作業していても統一感のあるデザインで制作することが可能です。 新しいデザイナーが着任した時には、立ち上がりの拠り所になります。

メリット2. ユーザビリティの担保

デザインガイドラインによって統一感のあるわかりやすいUIが実現できていれば、ユーザーの学習コストが下がり、操作性も向上します。

メリット3. 制作コスト削減

デザインガイドラインによって制作のテンプレートができていれば、新規に制作するページであってもそれらを組み合わせたり引用したりすることで、制作コストを削減することができます。

また、デザイナーだけでなくプロダクトオーナーやディレクター、エンジニアなどデザイン関係者にもガイドラインを浸透させることができれば(ガイドライン制作時から多くの人を巻き込むのが理想的です…!)、共通言語ができることでコミュニケーションロスを最小化でき、デザインのミスも見つけてもらいやすくなります。

デザインガイドラインのデメリット

デメリット1. アップデートが必要

サービスというのは成長していくものなので、サービスが成長すれば、デザインガイドラインもアップデートしていく必要があります。デザインガイドラインの品質を保つためには、デザイナーがある程度手間をかけて運用していかなければなりません。

デメリット2. 表現の幅が狭まる

デザインガイドラインは統一性を高めることでブランディングにも繋がるものですが、細かく決め過ぎると、表現や制作の自由度を奪ってしまい、新しいことやその時々にふさわしい表現や提案ができなくなってしまうこともあります。また、視覚的に変化の乏しい画一的なデザインになってしまい、ユーザーの興味の持続が難しくなる可能性があります。

デザインガイドラインの事例

Material Design Guidelines(Google)

f:id:astamuse:20210330193353p:plain

https://material.io/

マテリアルデザインは、Googleが提唱するデザインシステム。リアルな世界の「物体(マテリアル)」が奥行と厚みを持つように、Web上に表現されたデザインでも奥行と厚みを見せることによって、より操作性の高いインターフェイスを実現しようとするものです。

Human Interface Guidelines(Apple)

f:id:astamuse:20210330193417p:plain

https://developer.apple.com/design/human-interface-guidelines/

Apple社が設けたデザインガイドライン。iPhoneをはじめとする様々なApple社デバイスで動作するアプリに対するルールが規定されています。1978年の初版から更新され続けている歴史あるデザインガイドラインです。

Spectrum(Adobe)

f:id:astamuse:20210330193431p:plain

https://spectrum.adobe.com/

Adobeのデザインシステム。制作ソフト寄りのアイコンが多くあり、見てみると面白いです。「International design」のページには、世界的にユーザーのいるAdobeだからこその配慮などが書かれています。

IBM Design Language(IBM)

f:id:astamuse:20210330193448p:plain

https://www.ibm.com/design/language/

タイポグラフィからモーションUIまでとても細かく表記されているデザインガイドライン。ギャラリーのページは、ムードボードのようになっており、見ているだけでIBMの世界観が伝わってきて、デザイナーが迷った時に、立ち戻って冷静になれるページだと思います。

Orbit(Kiwi.com)

f:id:astamuse:20210330193506p:plain

https://orbit.kiwi/

チェコを拠点とする航空予約サイトKiwi.comが開発したデザインシステム。UIコンポーネントの種類が豊富で、利用ルールも丁寧に定めているため、複雑な画面を設計する際のヒントになります。

さいごに

参考文献・資料

最後までお読みいただきありがとうございました。 アスタミューぜでは引き続き、一緒に働いてくれるデザイナー・エンジニアを大募集中です! 採用サイトもありますので、是非ご覧ください。

BigQueryテーブルのレコードを直にスプレッドシートに出力する

みなさんこんにちは。ご無沙汰しておりますKJです。

前回投稿したのは、昨年2020年春の緊急事態宣言が発令される前だったので、ほぼ1年ぶりになります。 昨年春の時は保育園に預けることができずにお家に自粛していた娘はすっかりFireTVにハマってしまい、 2歳半近くになった今はFireTVのリモコンを使いこなすようになりました。。

最近はEテレの幼児向け番組である、いないいないばあっ!のDVD ワンツー!パンツー! にハマっています。

www.nhk-ep.com

「わん!ちゅー!! ぱん!ちゅー!!!」と言いながら毎日無限リピート再生しています。

 

一方、その頃私は何をしているかというと、

弊社内に蓄えられているデータから、目的によって様々な集計を行い事業チームに連携しております。 この際、データの集計にはBigQueryをよく利用しており、 事業チームはExcel等で更にアドホックな分析や顧客提案用のグラフに落とし込んでいたりしています。

もちろん、日次や週次など定期的に決まった手段で集計が必要なものや、頻繁に利用される集計であるならば、 データスタジオを利用したり、もしくはダッシュボードを用意するのが便利でしょう。

一方で、顧客提案に利用する場合だと、データの抽出条件や集計条件が案件ごとに異なったりするため、 同一のクエリを再利用できることはほとんどありません。 こういった場合は、データスタジオにビューを定義したり、ダッシュボードを作成する手間をかけるよりも、 CSV等のファイルにエクスポートしたものを渡すほうが手っ取り早いです。 また、受け取る事業チームも使い慣れたツールにすぐに取り込むことができるので、ファイルで連携するのが便利だったりします。

このため、データ集計や抽出の業務は大まかに以下のような流れで運用していました。

  1. 本番環境セグメントでBigQueryで集計
  2. 上記の結果をGCSにCSV形式でエクスポートし、それを本番環境セグメントに配置されているGCEにダウンロード
  3. 本番環境セグメントからローカルマシンにファイルを移す
  4. GoogleDriveにアップロードして事業チームに連携

もしくは

  1. ブラウザUIにてBigQueryで集計
  2. ブラウザからローカルマシンにダウンロード
  3. GoogleDriveにアップロードして事業チームに連携

上記の手順でしばらく運用していたのですが、次第に 「ファイルをダウンロードしてGoogleDriveにアップロードするのが地味に手間」 という風に思うようになりました。 定型的な業務でありながら時間が取られるため、なんとか簡略化できないかと考えたわけです。

結論として、「BigQueryで集計した結果を、GoogleDrive上のスプレッドシートに直にエクスポートする」環境を整えたので、 それをコード例含めて紹介したいと思います。

そもそもですが。。。

実は、BigQueryとスプレッドシートの連携についてですが、 今回対象となる「BigQueryのデータをスプレッドシートで閲覧する」ための機能はGoogleから提供されています。

support.google.com

しかし、この機能を利用するには Google Workspace (旧GSuite) のEnterpriseプランが前提となります。 Enterpriseアカウントがなければこの機能は利用できませんが、 既にEnterpriseプランを利用しているのでしたら、この機能を積極的に利用しましょう。

今回紹介するのは、Enterpriseプランなしで、BigQueryとGoogleDrive、およびスプレッドシートから提供されているAPIを利用して実現します。

本題

以下の流れをプログラムで処理することで、BigQueryのクエリ結果をスプレッドシートで閲覧できるようにします。

  1. BigQueryにて集計したいクエリを実行し、結果をBigQuery上のテーブルに保存する。
  2. GoogleDrive APIを利用して、エクスポート先のスプレッドシートをGoogleDrive上に作成する。
  3. BigQueryのAPIを利用して、上記1で作成したテーブルのレコードを順次取得する。
  4. SheetAPIを利用して、上記3で取得したレコードをスプレッドシートに順次書き込む。
  5. 最後にスプレッドシートのURLを事業チームに連携するだけで、データの連携は完了。

上記のフローのメリットは以下のとおりです。

  • GCPへの接続のみで、本番環境セグメントを意識することなく、データをGoogleDriveに出力できる。
  • 上記1のクエリ実行による課金コスト、およびAPI実行によるデータ取得等のネットワーク通信による課金コストのみ意識すればよい。
    • スプレッドシートへの保存については、現時点ではGoogleDriveの利用容量を消費しませんが、Googleのストレージポリシー変更により、2021/06/01 以降はスプレッドシートの保存にもGoogleDriveの容量を消費するようになります。
  • プログラムで全て解決できるので、手作業の手間がかからない。
  • 本プログラムを実行する環境をGCP内に配置すれば、通信はGCP内で完結するので、ローカルのインターネット速度制約に縛れれない

一方で、以降の実装例を見れば分かると思いますが、下記のようなデメリットもあるので、この点はご了承ください。

また、以下の実装例はPython3.7+になります。

実装詳細

利用するライブラリのインストール

まずは予め利用するライブラリをインストールしてください。 なお、以下ではpipを利用していますが、仮想環境を利用する場合は、適宜ツールに合ったコマンドに置き換えてください。

$ pip install google-cloud-bigquery
$ pip install google-api-python-client
$ pip install gspread

今回、スプレッドシートを操作するにあたり、gspreadを利用しています。 Googleが提供するSheetAPIを直接利用してもよいのですが、こちらは柔軟な操作ができる分、API仕様が複雑です。 今回のようにスプレッドシートを作成して、シートにデータを記入するだけなら、gspreadで簡単に実装できます。

gspread.readthedocs.io

また、GCPのプロジェクト設定にて、スプレッドシートAPIを有効にしてください。

BigQuery APIを利用して取得対象のテーブル情報を取得

まずはBigQueryにアクセスするためのclientを作成のうえ、テーブル情報を取得します。 この時点ではテーブル情報までで、実レコードは取得していません。

import google.auth
from google.cloud import bigquery

table_name: str = ... # 取得対象のテーブル名をデータセット名含めて指定する

credentials, project_id = google.auth.default()
bq_client: bigquery.Client = bigquery.Client(project=project_id, credentials=credentials)
bq_table: bigquery.Table = bq_client.get_table(table_name)

clientを作成するにあたり、ハマりがちになるのがクレデンシャルの取得でしょう。 環境によってユーザアカウントやサービスアカウントを直接利用していたり、 もしくは、Application Default Credential を利用していたりなど様々かと思います。 私が観測している範囲のみになりますが、 google.auth.default() により大方問題なくクレデンシャルを取得できるかと思います。 詳細はこちらをご参照ください。 (今回、エンドユーザ認証は想定しておりません!)

エクスポート先のスプレッドシートを作成

では次にgspreadを利用して、スプレッドシートを作成します。 以下のコードではエクスポート先のシートの作成も合わせて行っています。

import gspread
from gspread.models import Spreadsheet, Worksheet

file_name: str = ... # エクスポート先となるスプレッドシートのファイル名

credentials, _ = google.auth.default(
    scopes=[
        'https://spreadsheets.google.com/feeds',
        'https://www.googleapis.com/auth/drive'
    ]
)
gspread_client: gspread.client.Client = gspread.authorize(credentials)

# スプレッドシートを作成する
spread_file: Spreadsheet = gspread_client.create(file_name)
default_sheet: Worksheet = spread_file.get_worksheet(0)

# レコード出力先のシートを作成し、スプレッドシート作成時にデフォルトで作成されるシートを削除する
to_sheet: Worksheet = spread_file.add_worksheet(
    title=bq_table.table_id, rows=(bq_table.num_rows + 1), cols=len(bq_table.schema)
)
spread_file.del_worksheet(default_sheet)

gspreadのクライアントを作成するにあたり、bigqueryのクライアントと同様クレデンシャルが必要になります。 ここでは再度、新規にクレデンシャルを取得していますが、bigquery.Client作成時のクレデンシャルを流用しても問題ないと思います。 ただし、scopesに https://spreadsheets.google.com/feeds, https://www.googleapis.com/auth/drive の指定を 忘れないようにしてください。

次に、スプレッドシート作成後、エクスポート先となるシートを作成しているのですが、

to_sheet: Worksheet = spread_file.add_worksheet(
    title=bq_table.table_id, rows=(bq_table.num_rows + 1), cols=len(bq_table.schema)
)

シート名はbqテーブルのテーブル名(bq_table.table_id)とし、 さらにエクスポート元のテーブルのレコード数とカラム数に合わせて、シートの行数と列数を決めています。 カラム名の行を初めに入れるため、rowsパラメータに指定する値を「レコード数 + 1」としています。 (もちろん、シート名はご自由に指定しても問題ありません)

なお、スプレッドシートの制約上、1ファイルにつき最大500万セルまでしか作成できません。 このため、「(レコード数 + 1) * カラム数」が500万以上になるとエラーが発生します。 また、スプレッドシートを作成した際、初期シートとして「1000行 * 27列(AからZまで) = 2万7千セル」が作られているため、 シート追加時には、この初期シートのセル数を含めて500万セルを越えないよう、注意する必要があります。

スプレッドシートを特定のGoogleDrive上に移動

実は上記の処理でスプレッドシートが作成される場所は、 利用したクレデンシャルに紐づくアカウントのGoogleDriveホーム直下になります。 このままだと、ファイルの共有が難しいので、 「ファイルを共有する」もしくは「共有状態に予め設定している場所に移動する」必要があります。 今回は後者の方針をとります。

import googleapiclient.discovery as discovery

gdrive_service: discovery.Resource = discovery.build('drive', 'v3', cache_discovery=False)
gdrive_files: discovery.Resource = gdrive_service.files()

spreadsheet_file: dict = gdrive_files.get(fileId=spread_file.id, fields='parents').execute()
previous_parents: str = ",".join(spreadsheet_file.get('parents'))

directory_id: str = ... # 移動先となるGoogleDriveのディレクトリID

# 親を変更することで、ファイル移動を実現する
gdrive_files.update(
    fileId=spread_file.id, fields='id, parents',
    addParents=directory_id, removeParents=previous_parents
).execute()

ここで、directory_id は移動先となるGoogleDriveのディレクトリIDとなります。 ディレクトリIDには、URLの https://drive.google.com/drive/folders/ 以降の文字列を指定してください。 ファイルを移動するにあたり、移動先のディレクトリに対して、 クレデンシャルが紐づくアカウントに編集権限が予め付与されている必要があります。

シートの1行目にカラム名を記載する

テーブルをレコードを出力する前に、各カラム名を先に出力します。 これは既に取得しているテーブル情報から対応できます。

from typing import Dict, List
from gspread.models import Cell

# カラム名->列番号 の対応関係を作成する
column_mapping: Dict[str, int] = {}
column: int = 1
for field in bq_table.schema:
    if field.mode.upper() == "REPEATED":
        raise Exception()
    if len(field.fields) > 0:
        raise Exception()
    column_mapping[field.name] = column
    column = column + 1

# スプレッドシートの1行目に各列のタイトルを追加
sheet_header: List[Cell] = [
    Cell(row=1, col=column_number, value=header_name)
    for (header_name, column_number) in column_mapping.items()
]
to_sheet.update_cells(sheet_header)
to_sheet.freeze(rows=1)

ここで、カラムが配列もしくは構造体の場合は例外を発行しています。 配列および構造体が絡むと出力時の形式が煩雑になるので、今回は対象外にしています(処理としては例外が投げられることでプログラムが中断されます)。 また、ここで作成した column_mapping は最後のエクスポート処理でも利用しています。

bqテーブルよりレコードを取得してスプレッドシートに出力

前置きが長くなりましたが、実レコードをbqテーブルより取得して、シートに書き込むのは以下のようになります。

row_iterator: bigquery.table.RowIterator = bq_client.list_rows(
    bq_table, start_index=0, max_results=bq_table.num_rows
)

row_number: int = 1
sheet_cells: List[Cell] = []
for record in row_iterator:
    row_number += 1
    for target_key, target_value in record.item():
        column_number: int = column_mapping[target_key]
        sheet_cells.append(
            Cell(row=row_number, col=column_number, value=target_value)
        )

to_sheet.update_cells(sheet_cells)

list_rows()bq head コマンドに該当するもので、このAPIでレコードを取得する分にはクエリコストはかかりません。

スプレッドシートに書き込む際は、 Cell オブジェクトに行番号と列番号、およびセルの値を指定した上で、 Worksheet.update_cells() の引数に指定するリストに渡します。 このAPIの呼び出しは、たとえ1セルだけであっても1秒程度時間かかるので、 更新するセルをある程度の件数まとめた上で呼び出すようにするとよいでしょう。

これで、bqテーブルのレコードをスプレッドシートにエクスポートするプログラムが作成できました。

その他、気をつけること

既に記載したことも含まれますが、実運用時は以下の点を考慮する必要があります。

  • スプレッドシートは最大500万セルまでしか作成できません。500万セルを超過するような場合は、bqテーブルのレコード数を絞る、もしくは、スプレッドシートの出力先を複数ファイルに分割する必要があります。
  • 1セルあたり最大文字列長は5万文字となりますので、超過しないよう制御する必要があります。
  • SheetAPIの流量制限はかなり厳しく設定されています。スプレッドシート書き込み時にエラーコード429が返ってきたらAPIリソース超過になります。しばらく待ってから処理を再開するよう、制御する必要があります。(半年ほど前はリソース超過時に500エラーが返却されることもありましたが、最近は安定しているように思えます)
  • 上記の実装例ではbqテーブルのレコードを全て取得してからスプレッドシートに書き込んでいますが、レコード数が多いとメモリが足りなくなる可能性があります。適宜、ページングしながらスプレッドシートに書き込むようにしてください。

データの連携というのは単純作業で地味な上、データサイズが大きいと時間がかかったりで、チリツモなストレス要因だったりします。 今回紹介したコードがみなさまのデータ連携の一助になれば幸いです。

最後になりますが、アスタミューゼでは、引き続きエンジニアを積極募集中です。是非、下記バナーよりお尋ねください。

アスタミューゼのデータエンジニアによる、データベース問答

今回のブログ担当のt-sugaiです。今回、弊社のじんからインタビュー形式で、データエンジニアが何を考えているのか、という記事にしてはどうか、という提案があり、インタビューを受ける形になりますが、インタビュー形式でお送りします。

ーー今日はよろしくお願いいたします。

t-sugai:よろしくお願いいたします。

ーーt-sugaiさんは当社においてデータベース大臣の異名をお持ちです。これについてはいかがお考えですか。

t-sugai:物々しい二つ名なのと、自分自身は普通のデータベースの利用者だと思っているので、非常に恐縮です。
 一方で、キャッチーな名前をつけていただいたことで、「データベースのことはt-sugaiに聞いてみようかな」というのが共有されているのは嬉しいです。

ーーさっそく本質的な質問から始めさせていただいてもよろしいですか。データベースってなんでしょう。

t-sugai:(しばらく宙を見つめてしまう)
 現実問題として、「データベース」という語の利用は、非常にハイコンテクストでして、誰がどんな場面で発した言葉か、を非常に気にしてしまうのですけれども。

ーー話し手と受け手で齟齬があるということですね。

 極端なことをいえば、プログラミングスクールで使われる場合と、図書館情報学の講座で使われる場合、などというとわかりやすいでしょうか。
 前者は単純に「SQLで問い合わせができる、いわゆるRDBMS、おそらくはMySQLかPostgreSQL、もしかしたらSQLiteかOracle、SQL Serverかもしれない」あたりの具体物を表していることがほぼ明確です。
 一方で、後者の場合にはもう少し抽象的で、なんならコンピューターシステムであること自体も仮定していないかもしれません。デューイ十進や日本十進分類法などの方法で図書が分類された物理的な目録、図書カードの整理された棚などの一式を以て「データベース」と表現している可能性もあると思います。
 データエンジニアの周辺でも最近はKVSやNoSQL、DWHなども含めて、データの蓄積と検索ができるようなものを全部含んでいるような場合もありますし、単純なミドルウェアの話ではなくて、「データを蓄積/検索できるようにした一連のシステム」を「データベース」と呼ぶことも多いです。アスタミューゼの持っているイノベーションデータベースも、概ね「ある程度抽象化された一連のシステム」として認識されているかと思います。

ーー外側からデータベースを、利用者として見るときと、開発者が内側からデータベースを見るときに生じる齟齬を解決するためには、丁寧な意思疎通が必要になってくると感じています。そういった際に気をつけてらっしゃることはありますか。

t-sugai:データベースに限らず、いろいろなところでこの問題は話題に上がっているとおもいます。ソフトウェア開発一般、またはプロジェクトマネジメントなどの場合でもそうだと思うのですが、「お互いをリスペクトする」というのと、「より知っている方(開発者側)が歩み寄る」ということになりますね。
 あとは、利用者というのが「SQLを書く人」なのか、「ORMを使ったアプリケーションコードを書く人」なのか、はたまた「綺麗にUIまでつくってもらった何かの検索システムを使う人」なのかによって、利用者に対する期待値も異なります。特に前者2つに関してはデータベースに関する知識をある程度持っていただきたいので、できる限り情報提供をしたり、キャッチアップのお手伝いをしたいと考えています。

ーーデータベース内の実体とそれらの間の関係は、人間のある種の世界認識を反映するものに当然ならざるを得ないと思うのですが、それについてはいかがですか。

t-sugai:まさにその通りで、データベースのベテランは必ず「テーブル設計ではなくて、まずはデータモデルを設計しろ」と言います。
 データモデルというのは、つまり現実世界のものごとを「データ」という観点で抽象化するということです。例えば、現実の売買取引の場合、通常データベース化されそうな「何の商品を」「いくら(単価)で」「いくつ(数量)」、「何月何日何時何分何秒に」売ったのか、というような属性だけでなく、売り手や買い手の属性として、「店舗の住所」や「店主の名前、年齢、性別、家族構成 etc...」と、言ってしまえば無数の属性を想定することができてしまいます。その中で、「今回我々が気にしている属性はどれで、気にしても仕方ない属性はどれか」ということをひとつひとつ考えているわけで、これは世界認識の書き起こしに他なりません。
 私自身は元々知識表現的な分野が好きで、例えば学生のころに友人に聞かれて面白いな、とおもったのが「ラーメンとは何か」っていう問いがあります。これ、うどんやちゃんぽんなどをうまく排しつつも、みんなが「ラーメン」だと思っているものを漏れなく内包的に表現する、というのはなかなか難しいんです。
 似たような話で、こちらはもうちょっと有名な方が「カレーの定義は何か」ということを考えていて、「カレーとは、唐辛子を含む2種類以上の香辛料を使用し加熱調理された副食物の内で、おおよそ等量の米飯と共に食べた場合にそれぞれを単独で食べた場合より美味しくなる料理の総称である」という有名な一節があります。ただ、この定義で行くと「麻婆豆腐もカレーである」ということになるんですよね。
こういう、そもそも自分自身の世界認識を問い直すようなことを日常的にやる癖がある人、哲学とかが好きな人はデータベース設計に向いていると思いますね(笑)

ーー過去に質問応答システムの開発に携わっていた際に、適切な応答を生成するためにどのようなデータをどのような形態で保持しておくべきか、といった点について、質問とその応答の対象となる領域において、どのような知識を、すなわち世界認識を計算機上に保持しなければならないか、精密な設計が必要だったことを思い出します。その一方で、世界の見方を計算機上で表現するとともに、運用のためには、例えば頻繁に更新されるテーブルには、正確な表現というよりはむしろ運用のための工夫が必要といったジレンマがあるのではないかと思うのですが、こういった点についてはいかがでしょうか。

t-sugai:テーブル設計で一番頭を悩ませる部分ですよね。正確な表現というのは基本的に論理の世界、頭の中の世界だけのことを考えていればいいのですが、運用するときには現実世界の物理法則に縛られます。これもひとつのインピーダンスミスマッチだと考えることもできると思います。
 ここで重要になってくるのが計算機に対する知識で、必要とするシステムの仕様だと、応答性能はどのくらいの時間に納めなければならないか、使えるお金がいくらで、そうすると使えるハードウェア性能はいかほどのものか。データの量と複雑さ、検索質問の複雑さはいかほどか。それらの制約問題を解くとしたらどこを譲らずに何を諦めるのか……突き詰めるとどんな仕事でも同じような判断をしているとは思いますし、かなり抽象的な話になってしまって恐縮なのですが、データベースも同様なんです。ということで。

ーー様々な制約の中で、できるだけ最適に近い解を探し出すのはすべての仕事に共通するものということですね。ところで、個人的にとても印象に残っているのは、ある眼鏡店に、先日17年ぶりに訪れたときに、17年前の私のデータがデータベースからちゃんと出てきたことです。これは恐るべきことだと思うのですが、実際のところどうでしょう。

t-sugai:これに関しては、人によって感じることが異なりそうですね。役所での仕事や、基幹システムに携わっている方なら、エンジニアでなくとも「それが仕事なんだから当たり前だろ」と言われるのじゃないでしょうか。でも、特にWeb系のエンジニアなどをやってると「そんな昔のデータを取っておくなんて正気か?」という気持ちになるのもすごくよくわかります。
 前職では、PC98の時代から量販店でエンドユーザー向けに売られるようなパッケージのソフトウェアを開発・販売していたのですが、その頃にハガキでユーザー登録された人のデータなども顧客DBにはきちんと残っていると聞いたことがあります。後にその会社がWebサービスを提供するようになってからアカウント作成した人も、ハガキでユーザー登録した人も、等しく顧客DBで管理されるということで、基幹システム的なものをつくるというのはエンジニア的な観点からすると恐ろしさは感じますよね。究極的には、自分が死んだあともデータが残るつもりで設計しなければいけないですし。

ーー眼鏡店としては、単に新しい眼鏡のデザインを考えることだけではなくて、顧客の視力などのデータを維持し続けることも実は欠かすことのできない事業ということですね。眼鏡店であれば顧客の名前や視力、老眼といったことも考えられますから年齢といったデータが当然想定されうると思うのですが、その一方で、それらを定義するER図の設計は率直に言ってとても難しいと感じます。なぜこんなに難しいのでしょうか。

t-sugai:ER図の設計というのは、一般に論理設計と言われている部分、ないしは論理設計およびテーブル定義あたりを指しているということでいいですね。  このER図の設計は、「サービスが終了するなどしてデータベースが役目を終えるそのときまで、答え合わせができない、ないしは答え合わせにさらされ続ける」という性質をもっています。
 しょうもない設計をしたとしても、アクセス数も増えず、テーブルの行数も増えず、数年未満でお役御免になる、というようなデータベースであれば、アプリケーションから使いやすく、アプリケーションの実装者に理解しやすければ別にそれでいいとも言えます。
 一方で、非常にしっかりとした正規化ができているように見えても、複雑なJOINが必要になりすぎるとか、行数がめちゃくちゃ増えるとか、はたまたサービスを拡張しなければいけないときにうまく追従できないとか、そういうことがあると「イマイチなER設計」って思われてしまうでしょう。
 そんなわけで、本質的に答え合わせに年単位の時間がかかったり、これは職場にもよるのでしょうが、アスタミューゼを含めて私が経験した職場では、「日々さまざまなDBのER図の設計を行う」ということはなく、新規サービスの立ち上げ時であったり、DBを改修するような大幅な拡張があるときにだけしか設計をするチャンスそのものが来ないので、「前回の反省を生かしてやってみる経験」自体を非常に積みにくいという構造があると思っています。
 くわえて、対象のデータベースがどのくらいの期間使われるのか、なんの目的で使われるのか、当初の目的外の使われ方をするかどうかをどこまで織り込むべきか……常に中長期的な視野を求められることになります。また、先ほどの眼鏡屋さんの例でもあったように、データは10年20年と使われ続けることはまれではありません。例えば10年前といえばAWSが日本に上陸した直後くらいのことですし、20年前にはまだ光ファイバーによるインターネットは日本の一般家庭にとっては珍しいものでした。その間にストレージの価格も下がりましたし、そもそも磁気ディスクからSSDが主流になることでランダムアクセス速度はかなり速くなりました。そういった計算機やネットワーク自体の技術革新などもあり、ボトルネックの場所やコストがかかる場所というのが時代とともに変わってきます。つまり、「よいER設計」を目指すというのは、本質的に現在と未来、場合によっては過去を徹底的に考えるという仕事になる、というところが難しく感じる所以ではないでしょうか。

ーーお話を伺いますと、データベースに精通するためには、実に様々な能力が必要になってくると感じます。どういう風に勉強していけばよいのか、特にこれからこの道に入る方に向けて、最後にぜひご助言をいただけませんか。

t-sugai:私自身もそんなに偉そうなことを言えた立場ではなくて、たまたま、時代と、与えられた仕事と、自分の性格がマッチしたところに居させていただいているだけ、と思っています。
 先ほども申し上げたとおり、小手先の知識やノウハウは世界全体の技術革新に呑み込まれて使えない知識になってしまいがちです。一方で、計算機がデータを処理する基本的な仕組みなど何十年も変わらないものはあります。基本的なデータモデリングやデータベースの内部構造を学ぶことはおすすめです。あとは、オブジェクト指向のクラス設計の仕方などを学ぶのもデータモデリングの助けになると思いますね。

ーー今日はどうもありがとうございました。

 こちらこそ、ありがとうございました。 なかなかお話しする機会がないような話ではありましたが、よく「データエンジニアってどんな仕事をしているの?」というようなことを聞かれて何を答えるべきか迷ってしまうのですが、じんさんが良い質問をしてくださるので、普段あまりできない話ができてよかったな、と思います。

 アスタミューゼでは、データエンジニアを積極募集中でございます。特にこの話が面白いなと思っていただいた方、もっと詳しく話を聞いてみたいなと思った方など、カジュアル面談からでもウェルカムです。下記バナーよりどうぞお声がけください。

Copyright © astamuse company, ltd. all rights reserved.