現実モデリング

データとかエンジニアリングとか健エミュとか

健常者エミュレータ事例集を支える技術

はじめに

  • この記事では、健常者エミュレータ事例集を構成する技術について記述します。
  • 健常者エミュレータ事例集を構成する技術について俯瞰的に見たのち、個々のコンポーネントについて触れ、技術選定をした歴史的経緯などについてもちょびっと書きます
  • Wordpressの異常な使い方として参考になれば幸いです

システム全容

健常者エミュレータ事例集を構成する技術

健常者エミュレータ事例集の本体のほかにも3つのコンポーネントがあり、それぞれ本体とREST API経由で接続されています。順を追ってみていきます。

健エミュ投稿フォーム

hpe-form-v2.vercel.app

作られた背景

  • 事前知識
    • 2022年3月から2023年2月まで、健常者エミュレータ事例集はSeesaa Wiki上で展開されていた wiki.seesaa.jp

    • Seesaa Wikiにはテンプレート投稿機能があり、事前に作成されたテンプレートを使って記事を作成することが可能だったが、PCからしかテンプレート投稿機能が使えない欠点があった

    • 当時、健常者エミュレータ事例集WikiのPV数は9割をスマートフォンが占めていた
  • 健常者エミュレータ事例集の理念を実現するためには、スマートフォンから記事を投稿できない状況を解消し、新規投稿を促進する必要があった

構成

  • Next.js + Vercel
  • 長所:無料枠の範囲内でできる
  • 短所:オーバーエンジニアリングな点
    • 正直Next.jsを使うほどではなかったかもしれないので、今はReact + Cloudflare Pagesで作り直そうとしている
  • 構成の欠点ではないが、作った当初はフロントエンドが全く分からず適当に作ったため、技術的負債がたまっている。現在では新規に機能を追加するのが難しい状態になっている

健常者エミュレータ事例集本体

  • Seesaa Wikiの機能が不足していたため、2023年の2月にWordpressを中心にする構成に移行した

構成

  • Amazon LightSail + Wordpress + Route 53
  • 長所:料金が定額であり、過剰な料金がかかることがない
  • 短所:過剰なアクセスが集中すると落ちる
    • 実際Xでバズって落ちたことがある
    • CDNを挟めば何とかなるかもしれない

利用しているプラグイン

  • wpDiscuz : コメント機能
  • WP Mail SMTP : ユーザー登録時のメール配信
  • Taxopress : タグ管理
  • Sassy Social Share : SNSシェアボタンの作成
  • Like Button Rating ♥ LikeBtn : 「いいね」「よくないね」ボタンの作成
  • Ivory Search : 検索機能
  • Advanced Random Posts Widge : ランダム記事リストの作成

健エミュ分析基盤

  • 健エミュ内に蓄積されたデータを分析するために作成された

構成

  • dbt Cloud + BigQuery + Looker Studio
  • 長所:激安。ほぼ無料に近い
  • 短所:特になし

参考:

contradiction29.hatenablog.com

健エミュX通知Bot

  • 作った背景:Seesaa Wikiを使っていた当初、Seesaa WikiにビルトインされているTwitter(当時)連携機能が不十分だったため自作した
  • 10分に一回REST APIをたたき、新しい記事があったらスクレイピングして投稿画像を作成し、X APIを使って投稿する仕組みになっている
  • コードはこちら

    github.com

構成

  • AWS Lambda + Serverless Framework + Python
  • 親の顔より見たPythonで作った
  • 長所:激安
  • 短所:特になし

おわりに

  • 追加で知りたいことがある方はこっそり教えてください

社内データ基盤 as a Product : いい感じにやっていけ

  • 筆者がスタートアップの「データ何でもチーム」をやってきた中で、考えてきて実践してきたことを吐き出します
  • ポエムです

data as a product

データメッシュを実現する文脈の中で、「それぞれのドメインに分散されたチームが自身の提供するデータセットに対してproduct thinkingを適用するべきだ」とする考えが生まれてきた。

For a distributed data platform to be successful, domain data teams must apply product thinking with similar rigor to the datasets that they provide; considering their data assets as their products and the rest of the organization's data scientists, ML and data engineers as their customers*1.

(筆者日本語訳)分散データプラットフォームが成功するためには、ドメインごとに配置されたデータチームが自身の提供するデータセットに対してプロダクトシンキングを適用しなければならない。データアセットをプロダクトとして捉え、組織内の他のデータサイエンティストや機械学習エンジニア、データエンジニアを顧客とみなすのである。

この考え方を「data as a product」といい、平たく言えば「データセットをプロダクトとみなし、プロダクトマネジメントをやっていこう」と主張する考え方である。国内の大規模エンタープライズの中でデータメッシュを実現できている企業は聞いたことがない2024年2月現在だが、データメッシュの文脈から外れたとしても、「data as a product」の考え方は有効である*2と僕は考えている。

社内データ基盤 as a product

実際にデータの利用者の立場になって考えてみると、データセットそのものに対してProduct Thinkingを適用するだけではデータの利用者に対して価値を提供することはできないことに気づく。データセットは重要な要素だが、データの利用者は「自分のビジネスに役立つインサイトが欲しい」のであり、「データが欲しい」わけではないからだ。

インターフェースを入り口とし、インサイトを出口とする一連のユーザー体験の中で、データの利用者は価値を得る。この価値を最大化することが、社内データ基盤のプロダクトマネジメントの役割である。大事な事だから繰り返すが、「データを提供すること」は手段であって目的ではない。目的は「ビジネスインサイトの提供」である。

たとえば、BIツールのユーザー発行プロセスが面倒であり、結果的に社内の誰も使えない状態になっているのなら、ユーザー発行プロセス自体を自動化し、オンボーディング体験をよくする事も必要となる。データに触れるインターフェースを入り口とするなら、入り口に至るまでの段階がボトルネックになっている状態を解消することは責務の範囲内になる。

別の例として、「苦労して作ったが、使われていないダッシュボード」があるとしよう。ダッシュボードはプロダクトを構成する一つの機能と捉えられる。このダッシュボードは「利用数が少ないが、プロダクトの方向性と一致する」分類の技術的負債*3であり、優先度が低い場合は削除、優先度が高い場合はUX自体のアップデートが必要となる。プロダクトなので当然技術的負債もあり、社内データ基盤で実現したいゴールと現状から負債を分類し、アプローチする事も責務の範囲内である。

この「インターフェースを入り口とし、ビジネスインサイトを出口とする」一連のユーザー体験を最大化することを目的に、プロダクトマネジメントっぽくやっていくことが、「社内データ基盤 as a Product」の考え方である。

実践してきたこと

上記のような考え方をもとに今まで仕事してきたが、実際にやっていたことは一般的なプロダクトマネジメントの教科書に書いていることばかりであった。曖昧な状況をクリアにし、明瞭なコミュニケーションをとり、ユーザーに価値を届けるためにいろんなことをやってきた。たとえば以下のようなことである:

ユーザーのニーズ調査

  • ヒアリングやエスノグラフィの手法を使ってユーザーのニーズを調べる
  • 実践テクニックだが、ダッシュボードのニーズを調べるならホワイトボードにグラフをラフに書いて「あなたが欲しいのはこんな感じですか?」と聞くとスムーズである。詳しくはこちら

contradiction29.hatenablog.com

ビジネスインパクトを基準にした優先順位策定

  • データ基盤を運営していく上では、「アドホックなデータ抽出で済ませるか、ダッシュボード化するか」が判断の分かれ目になることが多い。後者はテーブルの作成が必要になる場合も多く、アナリティクスエンジニアリングの領域も絡んでくるためである。
  • この場合は「ビジネスインパクト」なんて難しい言葉を使わずに、「他の場面でも使用するイメージが想定できるか」を基準にしてダッシュボード化するか、しないかの判断をしていた。「ビジネスインパクト=PDCAサイクルを回す時間を削減できる程度」として捉え、多くのユーザーに使われるならそれだけ削減できる時間も長くなる=ビジネスインパクトがあるよね、とする考え方である。

品質

  • 当然であるが、データの品質がおかしくなることはある。
  • 特に手入力のデータをソースとするデータセットは被ってはいけないIDが被ったりすることが多いので、適宜dbtのtestを組んだりして品質担保をしたりする。

ユーザーに価値を提供する手段の選択

  • ユーザーにデータを提供する手段は複数あり、それぞれに得意不得意がある
    • ダッシュボード
      • 自動で更新することが可能
      • SQLで完結する範囲内ならなんでもでき、チーム内でも使えるメンバーが多い
      • 柔軟性に欠ける
    • Streamlit
      • 自動で更新することが可能
      • 動的にSQLを生成し、インタラクティブなダッシュボードを作成することができる。ダッシュボードを補完するツールとして利用可能
      • ダッシュボードよりは作成に時間がかかり、管理もダッシュボードと比較すれば面倒ではある
    • Slack API
      • 自動でプッシュ型の通知をすることが可能
      • オペレーショナルアナリティクスの場面で役に立つ。ユーザーに一方的にデータを送りつけることができるため、物忘れの防止に役立つ。
      • 複雑なデータは送れない
    • Google SpreadSheetのIMPORTDATA関数の利用
      • AWS Lambdaの関数URL機能とGoogle SpreadSheetのIMPORTDATA関数を使えば、スプレッドシートに直接データを吐き出すことができる
      • 営業やカスタマーサクセスチームの定常レポートなど、定型的な業務を効率化するために利用できる
    • アドホック抽出対応
      • プログラム化されていない分析を提供することが可能
      • 最も柔軟なデータ提供の形
      • 人的リソースの制約の影響を受ける
  • 実務では手段Aを指定されても最適なのは手段Bだよね、みたいに提案しにいく事も多く、複数の提供手段の得手不得手を理解し、最適なデータ提供の手段を考えるのはパズルのようで面白い

おわりに

雑に要約すると「社内データ基盤もユーザーを持つアプリケーションだから、プロダクトマネジメントしていこうね」という考えでした。それはそうだよね、と思ってくれたら嬉しいです。

*1:https://martinfowler.com/articles/data-monolith-to-mesh.html#DomainDataAsAProduct

*2:この「考え方は有効である」という主張はプラグマティズムの観点に基づくものである。つまり、その考え方があることによって、現実世界での行動はより意味があるものとなる。

*3:https://speakerdeck.com/sansantech/sansan-20231121?slide=14

私的:Notionで社内ドキュメントを書く時に気をつけていること

はじめに

  • この文章は、筆者がおしごとをしている中で気をつけているNotionのドキュメント作成術についてまとめ、記憶を整理するために作成されました。
  • 内容は些細なものであり、特段大きなテクニックはありません。
  • 参考にしてくれるとうれしいです。

0. ドキュメントを書く時の原則

読み手の認知負荷を下げる

ドキュメントは読まれるために書く。だから、できるだけ読みやすいように書く。

書き手の認知負荷も下げる

ドキュメントを書くのは面倒である。だから、できるだけ書きやすいように書く。

1. 最初にドキュメント概要を書く

ブレットを四つを使って、ドキュメントの一番最初に「ドキュメント概要」を書く。

  • ブレット一つ目:このドキュメントの存在意義
  • ブレット二つ目:記述していること
  • ブレット三つ目:(あれば)重要な補足
  • ブレット四つ目:読者に対して期待すること

例えば、チーム内の個人情報保護ガイドラインの冒頭に書く「ドキュメント概要」は以下のようになる


  • このドキュメントは、データ分析チーム内における個人情報の取り扱い方針を確立し、個人情報を取り扱う社員の負担を軽減するとともに、 個人情報漏洩などの事業上のリスクを削減することを目的に作成された。
  • このドキュメントでは、総務省ガイドラインによる個人情報の定義、社内における個人情報の定義、およびその取り扱いについて記述している。
  • この文章で定められた方針は、全社レベルでの個人情報取り扱いガイドラインが作成されたのちに破棄され、再構成される想定である。
  • このドキュメントは、現実世界における個人情報保護法関連、およびデータ基盤の利用状況に基づき更新されることを想定している。アップデートがあり次第更新すること。

ドキュメントの最初にドキュメント概要を書くと、以下のようなメリットがある。

  • 書き手にとっての利点
    • この文章で何をしたいのか、何の情報を伝えたいのか明確に文章化することにより、文章を書く方針がずれなくなる
  • 読み手にとっての利点
    • この文章が何の目的で作成されたのかわかるため、時代遅れになった場合に更新するかどうかの判断がしやすくなる
    • この文章で伝えたいことが最初にわかるので、この文章を読むべきかどうかが最初にわかる

2. ブレットは三段まで

ブレットを使う際、四段以上は入れ子にしないようにしよう。

  • ブレット一段目
    • ブレット二段目
      • ブレット三段目(←ここまでOK)
        • ブレット四段目(←これ以上組み込むと読みづらい)

経験則ではあるが、ブレットが四段以上入れ子になると読みづらさが急増する。四段以上になりそうになったら、段落を再構成しよう。

3. トグルは「閉じてても本筋には影響ない補足情報」を入れる

トグルは便利なので、見出しにされることがある。読みづらいのでやめよう。トグルを見出しに使いたくなったら目次をつけよう。

www.notion.so

トグルの中にトグルがあるととても読みづらくなる。可能な限りやめよう。トグルを使うのは「閉じていても本筋には関係がない、補足情報」に限ろう。

4. 絵文字を使わない

ノンデザイナーズ・デザインブックには、以下の四つの原則が書かれている。

このうち「反復」の原則は以下のようなもの:

デザインの視覚的要素を作品全体を通して繰り返すことです。色、形、テクスチャー、位置関係、線の太さ、フォント、サイズ、画像のコンセプトなどを反復させることができます。反復は、組織化を促進し、一体性を強化します

『ノンデザイナーズ・デザインブック』P13

絵文字は恣意的に、何の法則もなく使われがちであり、「反復」の原則を侵犯する。可能な限り絵文字は使わず、別の方法での構造化を考えた方がいい。

おれはデータエンジニアでもアナリティクスエンジニアでもなく、データを使ってビジネスをいい感じにしていきたいだけなんだ

キャリアの方向性について方向性が掴めてきたので、殴り書きしてみる。

俺はデータを使ってビジネスをいい感じにしていくのがやりたいんだ。

データエンジニアをやりたい訳ではない。アナリティクスエンジニアをやりたいわけでもない。どちらも単体では価値を出すことはできないし、データエンジニアとアナリティクスエンジニアはぶっちゃけ境界が曖昧だし、実務上兼ねることはよくあるし、役割上分けているのは採用しやすいからだと勝手に思ってる。同様の理由で、データアナリストも違う。データで価値を出したいなら全部やらないといけない。ビジネスユーザーやML担当者の話を聞いてデータモデリングをやり、データソースからデータをDWHに取り込み、ダッシュボードを作ったりする役の全部をやらないといけなくて、境界線はビミョいものになる。

そういうロールじゃなくて、なんかこう、データを使ってビジネスをいい感じにしていくのがやりたい。データ・パイプラインを一つのプロダクトとして、そのプロダクトをいい感じに成長させて、ユーザーの価値に貢献していくのが俺はやりたいんだ。価値貢献がゴールなのだから、手段はもっと自由に考えてもいいはずだ。データPdMは?と思ったけど、一般的にPdMにはエンジニアリングの権限がない場合が多いから、それも違う。自分でやらなくてもいいならそれでもいいが、大抵の場合は自分でなんとかする必要がある。

この世界にはデータを使って解くことができる魅力的な問題で溢れている。そういった問題を、フルコンタクトなやり方で解く仕事を表す単語はないから、「データ・ハッカー」とでも名付けてみるのはどうだろう。今までやってきたのは「データを使ってビジネスをいい感じにしていく仕事」だと思っているし、これからもやっていきたい。より良いやり方で、ベストではないけどベターなやり方を追い求めていきたい。

以下余談:

技術の進歩に伴い、データエンジニアリングをやるハードルは下がってきている。BigQuery, Snowflakeをはじめとするクラウド系DWHや、FivetranやAirbyteなどのELTツール、そしてdbtなど、技術選定をちゃんとやれば、運用負荷が低いデータ基盤を構築できる時代に今はもうなっている。実現できれば、運用保守に割く労力は減り、ユーザーとのコミュニケーションに費やせる時間が長くなる。

これからはフルコンタクトスタイルでも十分やっていける時代だ!と個人的には思っている。Web系の産業に勤める人間の思い上がりかもしれない。

Serverless Frameworkで実現するGitHub-flow

三度の飯よりGitHub-Flowが好き

GitHub-flowはシンプルなブランチ戦略で、以下のような手順で本番環境に変更を反映する

  1. mainブランチからfeatureブランチを作成
  2. 変更をコミットし、プルリクエストを作成
  3. レビュワーからの指摘を受けつつ、リモートレポジトリの内容を変更
  4. プルリクエストがapproveされたら、mainブランチに反映

図解するとこんな感じ:

GitHub-flowについて

詳しくは以下参照:

docs.github.com

上記図の「リモートにpushしてPull Requestをオープン」の部分で、テスト用環境にデプロイできればさらに便利になりそうだ。ローカル環境でのテスト実行がうまくいっても、クラウド環境上でうまくいくとは限らない。GitHub Actionsを工夫して書けば、PRを提出した段階でテスト用の環境にデプロイする「ブランチデプロイ」が実現でき、開発フローをのシンプルさを維持できる。実際、オーケストレーションツールのdagsterはこのBranch Deploymentをネイティブ機能として組み込んでいたりする。

docs.dagster.io

このブランチデプロイをServerless Frameworkで実行してみたいと思う。

Serverless Frameworkについて

Serverless Frameworkはサーバーレスのアプリケーションのデプロイを簡素化するフレームワークである。詳細は割愛するが、AWS Lambda関数のデプロイが簡単になったりして嬉しい。自分の業務だと、データパイプラインの一部でAWS Lambdaを使っているため、関数の管理を抽象化する用途でServerless Frameworkを利用している。

github.com

なお、Serverless FrameworkにもBranch Deploymentsの機能はあるが、思っていたのとは違ったのでGithub Actionsで実装する方針をとっている。

www.serverless.com

実装

要件定義:どんなふうにするか

  • PRを提出したら、PRごとに個別に作成されたステージにデプロイされるようにする
  • cron式は除外して、定期実行されないようにする
  • PRをクローズしたら自動的に関数が削除されるようにする

ディレクトリ構成

.
├── .github
│   └── workflows
│       ├── delete_cron_trigger_from_serverless_yml.py
│       ├── sls_branch_deployment.yml
│       └── sls_main_deploy.yml
├── ServerlessFramework
│   ├── serverless.yml
│   ├── DockerImagePattern
│   │   ├── Dockerfile
│   │   ├── lambda_function.py
│   │   └── requirements.txt
│   └── SampleFunction
│       ├── lambda_function.py
│       └── requirements.txt
...

ファイル

sls_branch_deployment.ymlの中身は以下の通り: ステージ名はブランチ名からとっており、AWS Lambdaの関数の命名規則上使えない特殊文字は全てエスケープしている。

name: Serverless Framework branch deployment

on:
  pull_request:
    types: [opened, synchronize, reopened, closed]
    paths:
      - "ServerlessFramework/**"
  workflow_dispatch:

env:
  SERVERLESS_ACCESS_KEY: ${{ secrets.SERVERLESS_ACCESS_KEY }}

jobs:
  branch_deployment:
    name: Branch Deployment
    runs-on: ubuntu-latest
    if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.action == 'reopened'
    steps:
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: 3.9

      - name: Install serverless framework
        run: |
          echo "install serverless framework"
          npm install -g serverless
          npm install serverless-python-requirements

      - name: checkout for deployment
        uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
          sparse-checkout: |
            ServerlessFramework
            .github
          path: 'project-repo'

      - name: Remove Cron trigger
        run: |
          cd project-repo
          pwd
          pip3 install pyyaml
          python3 .github/workflows/delete_cron_trigger_from_serverless_yml.py ServerlessFramework/serverless.yml
      
      - name: Make dot env file
        run: |
          touch .env
          echo env_val=${{vars.ENV_VAL}} >> .env
          // 必要に応じて追加
          cp .env project-repo/ServerlessFramework/.env

      - name: Deploy Functions
        run: |
          # Deploy to lambda
          echo "deploy to lambda"
          cd project-repo/ServerlessFramework
          branch_name=${{ github.head_ref }}
          escaped_branch_name=${branch_name//[^[:alnum:]]/-}
          short_branch_name=${escaped_branch_name:0:10}
          sls deploy --stage ${short_branch_name} --verbose

  delete_functions:
    name: Delete Function
    runs-on: ubuntu-latest
    if: github.event.action == 'closed'
    steps:
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: 3.9

      - name: Install serverless framework
        run: |
          echo "install serverless framework"
          npm install -g serverless
          npm install serverless-python-requirements

      - name: checkout main branch
        uses: actions/checkout@v4
        with:
          ref: main
          sparse-checkout: |
            ServerlessFramework
          path: 'project-repo'
      
      - name: Make dot env file
        run: |
          touch .env
          echo env_val=${{vars.ENV_VAL}} >> .env
          // 必要に応じて追加
          cp .env project-repo/ServerlessFramework/.env
      - name: Delete Functions
        run: |
          echo "remove from lambda"
          cd project-repo/ServerlessFramework
          branch_name=${{ github.head_ref }}
          escaped_branch_name=${branch_name//[^[:alnum:]]/-}
          short_branch_name=${escaped_branch_name:0:10}
          sls remove --stage ${short_branch_name} --verbose

ブランチデプロイ環境で関数が定期実行されては困るので、serverless.ymlからcron式を除去する必要がある。 この動作を実行するPythonスクリプトdelete_cron_trigger_from_serverless_yml.pyの中身は以下の通り:

import yaml
import sys

serverless_yml_filepath = sys.argv[1]
# YAMLファイルを読み込む
with open(serverless_yml_filepath, "r") as f:
    raw_yaml = yaml.safe_load(f)

for function_name in raw_yaml["functions"].keys():
    function_detail_infomation = raw_yaml["functions"][function_name]
    if ("events" in function_detail_infomation.keys()) and (
        "schedule" in function_detail_infomation["events"][0].keys()
    ):
        del raw_yaml["functions"][function_name]["events"]
    else:
        pass

# 編集後のYAMLをファイルに書き戻す
with open(serverless_yml_filepath, "w") as f:
    yaml.safe_dump(raw_yaml, f)

本番用にデプロイしたい場合は、発動条件を変更し、ブランチデプロイ用のymlからcron式の除外部分を削除し、ステージ名をmainに変えれば良い。 本番用のGithub Actionsのコードsls_main_deployment.ymlは以下の通り:

name:  Serverless Framework prod deployment

on:
  push:
    branches:
      - main
    paths:
      - "ServerlessFramework/**"
  workflow_dispatch:

env:
  SERVERLESS_ACCESS_KEY: ${{ secrets.SERVERLESS_ACCESS_KEY }}

jobs:
  main_deployment:
    name: main_deployment
    runs-on: ubuntu-latest

    steps:
      - name: checkout for deployment
        uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
          sparse-checkout: |
            ServerlessFramework
            .github
          path: 'project-repo'
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: 3.9

      - name: Install serverless framework
        run: |
          echo "install serverless framework"
          npm install -g serverless
          npm install serverless-python-requirements

      - name: Make dot env file
        run: |
          touch .env
          echo env_val=${{vars.ENV_VAL}} >> .env
          // 必要に応じて追加
          cp .env project-repo/ServerlessFramework/.env
      
      - name: Deploy Functions
        run: |
          echo "deploy to lambda"
          cd project-repo/ServerlessFramework
          sls deploy --stage dev --verbose

ベトナム2018

散歩をしていたらベトナム料理店を見つけて、ブン・ボー・フエを食べた。

ブン・ボー・フエ。辛いフォーみたいなやつ

私は5年前、18歳の頃にベトナムを旅行した。その時のことは今の自分にとっても結構重要な出来事ではあって、今になって文章に起こすことができた。まとめてみたいと思う。

2018年の8月

大学に入学した直後は井の頭公園に住んでいた。ボロい部屋で、蝉を鬱陶しく思っていたことを覚えている。特にやることもなく、部屋の中で大の字に横たわっていた。若さが浪費される感覚があった。だから旅に出ようと思った。旅に出るならベトナムだと決めていた。

羽田空港の倉庫のアルバイトをした。14時間労働を3日やり、残りの金は入学祝いの金で補う。装備を揃え、航空券を買ったら手元に6万円だけ残った。入学祝い金は全て消えた。旅に出る前は、旅に意味があると思っていた。

夜のホーチミンの街の熱気は日本の熱気とは違う。コンクリートの匂いが強かった。

夜のホーチミン

ビザなしで滞在できるのは14日間なので、14日かけてホーチミンからダラットやフエ、ホイアン、ダナンを経由し、ハノイまで縦断した。そして家に帰った。旅に意味はないことがわかった。夏休みが終わった後は文学のゼミに参加し、意味を求める生活を続けた。

その後

ベトナム旅行での経験は、その後の大学生活での経験と混ざり合い、私の中で意味を形成していった。意味が形成されたのは旅行中ではなく旅行後だった。

まず、「常識」が相対化された。例えば、

  • 日本では横断歩道があれば普通渡れるが、ベトナムでは信号がない横断歩道は待っている限り渡れない。原付の波の中を自力で進まなければ道路の向こう側に渡ることはできない。
  • 日本では最終消費者が購入する全ての商品の価格が事前に決定されているが、ベトナムでは価格は交渉を通じて決定されることがほとんどだ。

私が今持っている常識も、この文章を読んでいるあなたが持っている常識も、特定の文化的コンテキストの中で形成された常識に過ぎない。そういう突き放した目線で常識を見ることができるようになった。この考えは「健常者エミュレータ」の考えの前提に当たる。

「意味」の捉え方も変わった。意味はあるとかないとかではなく、見出せるか見出せないかのどちらかである。意味は客観的に規定されるのではなく、主観を通じて自己の信念の中に形成される。「意味がない」のではなく、「私は意味を見出せない」が実際に近い。

自信もついた。手持ち6万円で人口約1億人の国を縦断したのだ。長距離バスはハードだし、コミュニケーションにも問題があった。現地語がわからないし、英語も通じない。言語は単なるコミュニケーションの手段に過ぎないと割り切り、ボディランゲージやスマホの電卓、Google翻訳を活用して意思疎通した。コミュニケーションにおいて重要なのは最初の一歩を踏み出すことだが、その時に必要な勇気はベトナムで培った。

結果的に、私はベトナムに行った意味を見出すことができたんだと思う。ありがとうベトナム。今度はタイか台湾に行こうと思う。

以下余談:

ベトナムは面白い国だ。緑茶に練乳を入れて激甘にしたり、あらゆるものにチリソースをかけたりするのは私の口には合わなかったが、美味しい料理もあった。私はバインミーが好きだった。

バインミー(食いかけ)ベトナムは歩道にゴミとか結構落ちているので、足がむき出しになるサンダルで旅行するのはやめた方がいい

ホーチミンの路上ではぼったくりにあったし、$5で頼んだ現地ツアーをすっぽかされたこともあるが、どちらも日本ではあまり体験できないエキサイティングな経験だった。

ホイアンの市場で食べた海鮮料理は美味しかったし、世界遺産になっているフエの王宮(の中庭の真ん中)は静かでいい場所だった。

海鮮料理

フエの王宮(世界遺産

殺意ドリブン開発

前提:一度生まれたシステムはそう簡単に死なない

昔話をしよう。

あるエンジニアがいた。コードを書くことに没頭した男だ。技術的負債が山ほど生み出された。

善良なエンジニアもいた。男の罪を肩代わりし、レポジトリからコードを抹消し、満足して死んだ。

この話には教訓がある。「一度生まれものはそう簡単に死なない。」

一度生まれたシステムは簡単に死なない。

  • 誤った技術選定で構築されたデータ・パイプライン
  • 有効性を失ったドキュメント
  • 使っていないデータを入力するオペレーション

技術選定を間違えたデータ・パイプラインは必要以上の労力を吸収し、有効性を失ったドキュメントは誤った動作を誘発し、使っていないデータを入力するオペレーションは管理コストを増大させる。

しかし自然に死ぬことはない。殺さない限り、生き続ける。

悪を成すために生まれてくるシステムは存在しない。すべてのシステムは善なる目的のために生まれる。システムそのものが悪なのではない。変化があり、有効性を失う。ただそれだけのことだ。

一度生まれたシステムは、その有効性を失ってもしぶとく生き続ける。

まず殺意

だから殺す。一度生まれたシステムが自然に死ぬことはない。だからこそ、今いる人間が積極的に殺さなくてはいけない。

  • 有効性を失ったドキュメントはゴミ箱に入れる
  • 有効性を失ったオペレーションは削除する
  • 使われていないダッシュボードを削除する

すでにある仕組みを意図的に殺すことは、易きに流れようとする自然の精神的傾向に争うことを意味する。殺意を持ってこの流れに対抗するべきだ。「殺意ドリブン開発」は、殺意を利用して易きに流れようとする精神的傾向に抗う開発手法である。

殺意を持たなければ心は易きに流れる。まず殺意を抱くのが重要だ。「まず、殺すべきである」から初め、次に「殺すのは俺だ/私だ」となる。主体性を持った確信犯である必要がある。他者から強制されるのではなく、自由意志のもと殺すのが肝心だ。

殺す理由は後でいい

殺意を抱いたら、次に殺す理由を見つける。殺意さえあれば、理由は後からいくらでも思いつく。

  • 有効性を失ったドキュメントを放置していては、既存のオペレーションが混乱する
  • すでに使っていないデータを入力するオペレーションが残っているのは、人件費の無駄である

大事なのは殺意であり、殺意の理由は対外的な説明目的を得るためにすぎない。業務上必要だからやるのだ。

跡形を残さない

復活する余地を残さず、徹底的に殺す。跡形を残すべきではない。

  • リポジトリごとdeleteする
  • ゴミ箱をクリーンアップする
  • インスタンスが絶対に残っていないよう確認する(あるいは、IaCを利用して確実に殺せるようにする)