nunulkのプログラミング徒然日記

毎日暇なのでプログラミングの話題について書きたいことがあれば書いていきます。

Gleam の第一印象

はじめに

この記事について

Gleam というまだ若いプログラミング言語を最近知った(2年前にブクマしてたんだけど忘れていた)ので、どんな感じなのかちょろっと触ってみました。

Gleam とは

gleam.run

非常に簡潔ながら Rust っぽい文法を持つ関数型プログラミング言語です。Erlang VM 上で動くので、Erlang や Elixir のモジュールを組み込むことができるほか、ErlangJavaScript にトランスパイルすることができます。

新しいプログラミング言語に出会うと、とりあえず動かしてみるのが趣味なんですが(新しいラーメン屋ができたらとりあえず行って食べてみる感覚で)、Gleam からは非常に満足する第一印象を得られました。

私は Nim と Clojure がとくに好きなんですが(Elixir も好き)、その2つに比肩するのではないかという大ヒットです(何年ぶりだろうこの感覚…)。

後述しますが、他の言語ではあまり見かけないようなちょっと変わったところもあり、クセになる味といった感じの趣です。

下記のサイトを見るとほとんどのことはわかるので、気になった方は見に行ってみてください。

https://gleam.run/book/tour/

お断り

Gleam はまだ若い言語であるため、シンタックスハイライトに未対応です。とはいえコードが読みにくいのもアレなので、本記事では文法の似ている Rust のシンタックスハイライトを借りて表示しています。ご了承ください。

目次

Gleam の主な特徴

  1. 関数型である(関数が第一級オブジェクトである)
  2. 変数はイミュータブル、再束縛不可
  3. ユニークな型定義
  4. if/else, for, while などがない
  5. ErlangJavaScript の関数を使える

以下、各項目について書きます。

1. 関数型である(関数が第一級オブジェクトである)

関数型の定義にはバリエーションがあるかと思いますが、ここでは「関数が第一級オブジェクトである」ということにします。つまり、関数を変数に入れられる、引数に取れる、ということです。他にも、関数合成やカリー化を標準ライブラリに備えています。

import gleam/function
import gleam/int
import gleam/io

pub fn run() {
  let increment = fn(x) { x + 1 }
  let double = fn(x) { x * 2 }

  let calc = function.compose(increment, double)
  let result = calc(10)
  io.println(int.to_string(result))
  // output: 22

  let add = fn(x, y) { x + y }
  let add_n = std_function.curry2(add)
  let result =
    [1, 2, 3]
    |> list.map(add_n(1))

  list.map(result, int.to_string)
  |> string.join(", ")
  |> io.println
  // output: 2, 3, 4
}

return キーワードはなく、最後に評価された式の結果が返り値になります。

関数型言語に必須(?)のパイプ演算子も標準で備わっています、見やすさ・書きやすさが段違いですね。

2. 変数はイミュータブル、再束縛不可

代入か束縛か、というのは言語によって異なりますが、Gleam の場合は束縛です。理解が曖昧ですが、束縛は基本的にイミュータブルで変更不可だと思うので、再束縛不可というのは自家撞着ですが、同じ変数に別の値を複数回結びつけることができない、ことを強調したいのであえて「再束縛不可」と書きました。

細かい話はおいといて、コードを見てみると以下のような違いがあります。

代入するタイプの言語だと以下のコードはOKそうですが、Gleam ではコンパイルエラーになります。

let n = 10
io.println(int.to_string(n)) // 10
n = n + 1
io.println(int.to_string(n)) // 上の行でコンパイルエラー

上のようなケースでは再び let を使って別の(同名でも可)変数に束縛します。

let n = 10
io.println(int.to_string(n)) // 10
let n = n + 1
io.println(int.to_string(n)) // 11

3. ユニークな型定義

これは最初面食らったんですが、ユーザー定義型を作るときに列挙型っぽい作りにできるのがユニークだなと思いました。

一般的な直積型の型だとこんな感じです。

app/user.gleam

import gleam/int

pub type User {
  User(id: Int, name: String, email: String)
}

pub fn to_string(user: User) {
  "User{id: " <> int.to_string(user.id) <> ", name: " <> user.name <> ", email: " <> user.email <> "}"
}

オブジェクトの各フィールドには . でアクセスします。

app.gleam

import gleam/io
import app/user

pub fn main() {
  let alice = user.User(1, "Alice", "alice@example.com")
  io.println(user.to_string(alice))
}

ファイル名がそのままモジュール名になって、型や関数を呼び出すときは module + . を付けて呼び出します。フィールドアクセスと書き方が同じなのでじゃっかん読みづらさはありますが、慣れの問題かと思います。

上の書き方だとコンストラクタを一つ持つクラスっぽいですよね。でも、Gleam ではコンストラクタを複数書くことで、列挙型を定義することができます。

実際、Rust では enum で定義されている Option 型が Gleam では以下のように定義されています。

pub type Option(a) {
  Some(a)
  None
}

型定義はジェネリクスっぽくも使えます(要は any)。専用の記法(よく見る <> で囲まれてるやつ)がないので、最初ちょっと混乱したんですが、上の a は型アノテーションです。

一部のみ特定の型を指定することもできます。下記はキーを文字列で固定したキーバリューペアです。any で指定した値はなんでも入れられるわけではなく、同型の値しか入れることはできません。

app/key_value_store.gleam

pub type KeyValueStore(any) {
  KeyValueStore(items: Map(String, any))
}

pub fn from_list(items: List(#(String, a))) -> KeyValueStore(a) {
  KeyValueStore(map.from_list(items))
}

pub fn get(store: KeyValueStore(a), key: String) -> Result(a, Nil) {
  map.get(store.items, key)
}

#(String, a) はタプルです。

app.gleam

import gleam/io
import app/user
import app/key_value_store as kvs

pub fn main() {
  let alice = user.User(1, "Alice", "alice@example.com")
  let store = kvs.from_list([#("alice", alice)])
  let alice = kvs.get(store, "alice")
  io.debug(alice)
  // Ok(User(1, "Alice", "alice@example.com"))
}

Ok は Rust でもおなじみの Result 型で、Ok または Error が返ります。

4. if/else, for, while などがない

ループがないのは関数型なのでさもありなんですが、if/else までないのはちょっと驚きました。条件分岐は case 式で行います。

Result 型の値が返ってきた場合、以下のように条件分岐できます。

let alice = kvs.get(store, "alice") // Result(User, Nil)
case alice {
  Ok(user) -> io.println(user.to_string(user))
  Error(_) -> io.println("alice not found")
}
// User{id: 1, name: Alice, email: alice@example.com}

余談ですが、Ok(user) の部分はデストラクチャで、オブジェクトからフィールドの値を取り出すことができます。

let user.User(id, name, email) = alice
io.println("id=" <> int.to_string(id) <> ", name=" <> name <> ", email=" <> email)
// id=1, name=Alice, email=alice@example.com

閑話休題

case 式は値を返すので、以下のように書くこともできます。

io.println(case alice {
  Ok(user) -> user.to_string(user)
  Error(_) -> "alice not found"
})

// またはパイプ演算子を使う

case alice {
  Ok(user) -> user.to_string(user)
  Error(_) -> "alice not found"
}
|> io.println

case 式は各アームで同じ型の値を返す必要があるので、エラーのときはなにも出力したくない場合は最初の書き方にするしかなさそうです。

case alice {
  Ok(user) -> io.println(user.to_string(user))
  Error(_) -> Nil
}

5. ErlangJavaScript の関数を使える

Erlang VM 上で動くので、Erlang や Elixir の資産が使えるのはわかるんですが、JavaScript まで使えるとは驚きました。

以下のような JavaScript モジュールを置いておきます。

src/module.mjs

export function now() {
    let now = new Date();
    return now.toISOString();
}

Gleam 側から以下のように呼び出します(本当は .mjs ファイルだけ別の場所に配置したかったんですが、ビルド時になぜかコピーされなかったので仕方なく src 直下に配置しました)。

src/app/js.gleam

import gleam/io

pub fn run() {
  let now = now()
  io.println("now: " <> now)
}

@external(javascript, "../module.mjs", "now")
pub fn now() -> String

JavaScript として実行すると、

$ gleam run --target javascript
   Compiled in 0.01s
    Running app.main
now: 2023-11-05T11:55:20.196Z

と出力されます。デフォルトでは Node.js が呼ばれるようです。本記事執筆時点では node 以外では deno が使えました。

$ gleam run --target javascript --runtime deno

所感

文法が Rust っぽいところが書きやすく、かつ変数はすべてイミュータブルで所有権もないので、書いていてストレスはほぼ感じません。型アノテーションは任意なので、気を抜くと書き忘れるんですが、型が自明なところを省略できるのはかえってメリットなのかな、という気もします。

言語仕様がシンプルで覚えることも少ないので、すぐに書くスピードが上がります。逆に、シンプルすぎるがゆえにマクロがほしいと思うところもありましたが、まだメジャーバージョンが 0 な若い言語なので、今後どんな風に言語仕様を整えていくのか、非常に楽しみです。

Nim にしろ Clojure にしろ Elixir にしろ、私が気に入る言語はメジャーになったことがないので、Gleam もマイナー言語として成長していくんだろうと思いますが、開発陣のみなさんにはがんばってほしいですね。

私がテストファーストでコードを書く理由

はじめに

この記事について

ちょっと前に X でテストファーストであることの意義みたいな話が流れていたのを見たのをきっかけに、テストファーストおよびテスト駆動開発について日頃思っていることをだらだら書いてまとめておこうと思います。

概要としては、テストファーストで書こうが書くまいがどちらでもよくて、要はスムーズに開発できる方法論を自分なりに確立できていればそれでいいんじゃないか、ということです。

前提として、私は「基本的にはテストファーストで書くが、いつもテストファーストで書くわけではない」派です。

テストファーストとは

プログラムを書くときに、テストコード(自動テスト)を先に書き、プロダクションコードを後に書くやり方のことです。反対に、プロダクションコードを先に書いて、テストコードを後に書くやり方はテストラストと呼ばれます。

テスト駆動開発との違いとしては、テストコードから先に書いてからプロダクションコードを実装する点は同じですが、テスト駆動開発では最初にテストを失敗させ、最低限の実装でテストをパスさせ、さらにテストを書いて失敗させ、…みたいなサイクルが決まっている点とそのサイクルにリファクタリングが組み込まれている点です。

テストファーストとテストラストの違いがもたらす結果

まず、テストファーストとテストラストのアウトプットのみに注目してみます。

そもそも自動テストがないプロジェクトもまだまだあるようですが(なかには自動テストを不要とみなしているマネジャーやエンジニアもいるようですし)、手動テストであっても納品前(リリース前)にテストがされていれば、品質を保つことは可能です。リリースしたコードセットが正しく動くことを確認できていれば、手動でも自動でもどちらでもいいわけです。その意味で、テストを先に書こうが後に書こうが、コード(動作)の正しさを確認できていれば、結果に変わりはないはずです。

実装コストとメンテナンスコストを考えると、結局のところ、どっちが自分に(自分たちに)とって素早く手間なく仕事を終わらせられるか、将来の仕事のコストを高めずに治安を維持できるか、というのが問題になります。

ハードウェアと比較した上でのソフトウェアの特徴として(ハードウェアはあまり詳しくありませんのでイメージで)、

  • すぐ変更できる
  • 構造が不安定である

という点があって、ソフト(柔軟)であることのメリットが、ソフトウェアが頻繁に変更される要因になっている一方で、そのメリットがもたらす負の結果として、壊れやすい・シンプルに保ちにくい、という問題を抱えることになります。

私自身のこれまでの経験では、プログラミングパラダイムによらず高品質で低コストなソフトウェアを作ることは可能であり、実装方法については実装者が実装工程を円滑に進めるための好みの問題であると思っていて、その意味でいうと、テストファーストとテストラストの違いがもたらす結果は、自分自身の経験からくる感想レベルの域を出ないんですが、以下の2点に集約されるかなと思います。

  1. テストファーストでは同じブランチ(プルリクエスト)に必ずテストが存在する(ただし、テストケースが十分かどうかは未定)
  2. テストラストではテストの書き漏れが発生する可能性や別のブランチ(プルリクエスト)に書かれる可能性がある

これはプロジェクトによりますが、テストがなくてもレビューを通すチームであれば、2 のパターンでも問題ないでしょう。私はプロダクションコードとテストコードは同一ブランチ(プルリクエスト)でレビューしたいので、セットになっていてほしいです。テストラストでもテストコードがセットになってなければレビューを通さない、という基準でやっていれば防げますし、逆に、テストファーストでやってたとしても私のように「いつもテストファーストで書くわけではない」場合は同様に漏れる可能性はあります。

なので、これはあくまでも頻度と可能性の問題になります。

実装過程におけるテストファーストの利点

次に、テストファーストが実装過程にもたらす利点を挙げます。

これもあくまで私自身が感じている利点であり、テストファーストで書いたからといって必ず得られるものではないかもしれません。

  1. 事前に実装の全体像が見えないケースで、少しずつ明らかにしながら実装を進められる
  2. リファクタリング時にバグを混入させる可能性を下げられる
  3. テストしやすいモジュール構成にしやすい

以下にひとつずつ詳説します。

1. 事前に実装の全体像が見えないケースで、少しずつ明らかにしながら実装を進められる

たとえばウェブAPIの開発で、機能の概要だけ理解していて、詳細(どう実装するか)が決まってない場合、テストファーストで書くと、インタフェースに近い部分から段階的に実装することができます。

例として Laravel でなんらかの予約処理を実装すると仮定して、テストコードを書いてみます。

<?php

class ReservationControllerTest extends TestCase
{
    function test_create(): void
    {
        $user = User::factory()->create();

        $params = [
            // not yet
        ];

        $response = $this
            ->actingAs($user)
            ->postJson('/reservations', $params);

        $response->assertCreated();
    }
}

とりあえず、「/reservations に POST でなんらかのリクエスト(JSON 形式)を送ると 201 が返ってくる」というところだけ確定させられます。

あとは、このテストが失敗しないようにプロダクションコードを書いていき、適宜必要になったタイミングでデータベースのテストやレスポンスデータのテストを追加したり、リクエストパラメータに複数のパターンが想定されるならパターンを増やしていったりしていけばいいわけです。

2. リファクタリング時にバグを混入させる可能性を下げられる

テストが失敗すればリファクタリングに失敗したとみなせるので、意図せず不具合を起こした際に即時に気づけます。

テストラスト方式でも、プロダクションコード→テストコード→リファクタリング、のフローであればリファクタリング時に間違いを検出することはできますが、テストファーストの場合、プロダクションコードの完成前の段階で小刻みにリファクタリングできるのがメリットです。とくに 1 に挙げたようなどうやって実装するか見えてないケースでより威力を発揮します。手探りでプロダクションコードを実装していくので、最初から最適な設計になることはほとんどなく、プロダクションコード→リファクタリング→プロダクションコード→リファクタリング→…の繰り返しを何度もすることになるので、テストがあり、そのテストが落ちなければリファクタリングが上手くいっている、という安心感が得られます。

テスト駆動開発はテストに関する技法ではなく、詳細設計と実装を手助けする技法であり、リファクタリングする際にもっともその恩恵を感じられるはずです。

3. テストしやすいモジュール構成にしやすい

複雑な条件判定のような処理はユニットテストがあったほうがいいですが、ユニットテストを書くにあたっては、依存するデータが必要最低限であることが望ましいです。

自分自身もそうですが、これまで関わってきたチームではユニットテストカバレッジはそれほど高くなく、必要であれば書く、という方針のチームがほとんどでした。この方針は合理的であると思っていて、フィーチャーテストに対してテストが書かれていて(ウェブAPIであれば最低でも各エンドポイントに対して一つ)、かつその中で使われる各ユニットが最低でも1回は呼び出されていれば(できればステートメントカバレッジはほしいですが)、わざわざユニットテストを書かなくてもいいと考えます。

ただし、条件分岐が複雑だったり、金額計算のようなミスの許されない部分だったり、ユニットテストが必要だと判断すれば、もちろんユニットテストを書きます。

あるていどテストファーストに慣れてくると、たとえ実装の全体像が見えていない段階だとしても「あ、この仕様に対してはユニットテスト買いといたほうがいいな」という判断ができるようになります。

上に挙げた「なんらかの予約処理」を再び例に取ると(ここではある施設の利用予約とします)、ある条件で予約時に出すオファー金額に割引が適用されるとします。割引条件を判定するモジュールにはユニットテストを書いておきたい、と思ったとして、あらかじめその部分だけを取り出して先にテストを書いておきます。ここで「その部分」をあらかじめクラスあるいはメソッドとして定義する動機が発生するので、最初からテストしやすいモジュール構成になっていきます。テストラストの場合でも、同様の判断ができるとは思いますし、テストファーストでもできないこともあるでしょうし、あくまでも実装者の判断力次第にはなってしまいますが、複雑な部分を最初に検証する癖をつけることによって、判断力も自ずと上がっていくような気もします。

仮に、以下のような条件のときに10%オフになるとします。現実にはこういうルールは存在しないかもしれませんが、あくまでも例ってことでご容赦を。複数のOR条件が入ってくるとテストを書く意義が一気に増すので無理矢理入れてみました。

  • 予約しようとしている施設が割引を許可している
  • 以下のいずれかの条件を満たす
    • 予約しようとしている施設を以前に利用したことがある
    • 会員ステータスがシルバー以上である(ブロンズ、シルバー、ゴールドの三種がある)
  • 利用予定日までの日数に関して、以下のいずれかの条件を満たす
    • 施設側が設定した日数があればそれに従う
    • 施設側が設定した日数がなければ3日前以内とする

条件判定の依存は「施設側の設定」「会員の利用履歴の有無」「会員ステータス」「予約日」の4つであることが判ります。

<?php

class DiscountOnReversationSpecificationTest
{
    /**
     * @param FacilitySetting $facilitySetting
     * @param bool $isUsed
     * ...
     * @param bool $expected
     * @dataProvider data_satisfied
     */
    function test_satisfied(FacilitySetting $facilitySetting, bool $isUsed, /*...*/, bool $expected): void
    {
        $specification = new DiscountOnReservationSpecification($facilitySetting, $isUsed, /*...*/);
        $actual = $specification->satisfied();
        $this->assertSame($expected, $actual);
    }

    public function data_satisfied(): array
    {
        return [
            '適用(施設側許可有り、施設利用済み、シルバー以上、3日前)' => [
                'facilitySetting' => new FacilitySetting(isDiscountAllowed: true, ...),
                'isUsed' => true,
                // 他のパラメータを列挙
                // ...
                // 最後に期待される返り値
                'expected' => true,
            ],
            // 他のパターンを列挙
        ];
    }
}

パラメータは今後変わるかもしれませんが、最初の時点で思いついた形でとりあえず(テストを)実装しておきます。最初に雑にでもいいのでテストを書いておくことで、少しずつ最適な形が見えてきます。しかも、テストが通れば壊れていないことが保証されるので、最適な形が見えてくるまでいくらでもリファクタリングできます(とはいえ、スケジュールの都合もあるでしょうから、実際にはいくらでもというわけにはいきませんが)。

おわりに

最初に書いたように、テストファーストであれテストラストであれ、品質の高いプログラムを早く完成させられればそれでいいわけですが、自分自身の経験から、テストファーストで書くと色々メリットがあると感じているので、まだ試してない、あるいは少し試したがやめてしまった、という方には、ぜひトライしてみてほしいという思いを込めて記事にまとめてみました。

とはいえ、推奨するというほどの熱量はなくて、どうやって内部品質を上げていけばいいのか悩んでいるくらいなら「ものは試し」で、やってみてから判断してもいいんじゃないかっていう思いです。あるていどできるようになってもフィットしないと思えば元に戻ってもいいですし、テストラストで最適解を探っていく、でもいいと思います。それぞれにフィットするやり方はあると思うので。

余談ですが、テスト駆動開発難しい、という意見をたまに見かけるんですが、どの辺が難しく感じるのかまでは書いてなかったりするので、もし言語化できてる方がいたら話を聞いてみたいと思っているのでご協力いただけないでしょうか。

いいよという方がいたら X でコンタクト取ってください、よろしくお願いします。

nunulk(decayed) (@nunulk) / X

最後に宣伝ですが、Techpit にこんな教材を出してます。テスト駆動開発に興味のある方はチラ見してみてください(Laravel 8 までしか対応してないですが、おおむね最新版でも使える内容になっているはずです)。

www.techpit.jp

目標達成したので減量のためにやったことをメモ

はじめに

この記事について

2023/09/27 に目標の 60kg(-18kg) を達成したので、これまでやってきたことをメモしておこうと思います。今回もプログラミング成分はほぼありません。

目次

減量を決意したきっかけ

これまで体型や見た目にはほとんど気を使わず生きてきたんですが、75kg 超えたあたりで(最大78kgまでいった)腹が出すぎて立った状態で靴下を履けなくなり、足の爪を切るのに難儀するようになり、これはさすがにまずいと思い、減量を決意したのが2020年の終わりあたりでした。

基本方針

自分はわりと自堕落で、なにごとも継続することができにくいので、とにかくゆるふわな目標と方針を掲げました。時間はかかりますが、急激なダイエットによってリバウンドするよりは辛くないだろうと踏んで(ダイエットとリバウンドの無限地獄は避けたかった)、こちらの戦略を採りました。

  • 月に 900g の減量を目標とする
  • ダイエット(食事制限)を中心とする
  • 無理なダイエットはしない、習慣化を重視する
  • 運動は最低限、自分が続けられる量(時間)に留める

ダイエット

  • 外食は極力控える(とくに揚げ物、大量の炭水化物)、月に1,2回ていど
  • 毎月の目標(-0.9kg)を達成したらご褒美に旨いもの(≒ ハイカロリー)を食べる
  • ビールはやめない
  • ジョギングした日の昼食は Huel (0.5人前、200kcal)で固定

まず、レコーディングダイエットの効果は大きいと考え、自分のスマートフォンに「あすけん」をインストールしました。

www.asken.jp

カロリーコントロールと PFC バランスコントロールのためにおおいに役立ってくれました。理論的にはいろいろあるらしいですが、あんまり考えすぎるとめんどくさいことになるので、基本的には「摂取カロリーを抑える」「PFCがグリーンになるようにする」の2点だけ気をつけてました。それでも脂質はどうしてもオーバーすることがあり、炭水化物は逆に少なすぎなことが多く、けっこうな頻度であすけんの女を何度も泣かせてしまいました。

余談ですが、ネットでは「あすけんの女」として有名なキャラクターにはどうやら「未来(みき)」さんという名前があるようです。目標達成したときの彼女の笑顔が眩しかったので、このスクリーンショットは保存しておくことにします。

自分は美味しいものを食べるのが好きなので、それを諦めると長期戦を戦えないと考え、ビールもそうですが、甘いもの(とくに夏場のアイス)もどうしても食べたいときは我慢せず食べてました。ただ、ラーメンやカレーは、外食するのは月に1度ていど、それ以外はインスタントやレトルトに限定しました。外食がとにかくハイカロリーなのが多いので、そこだけ徹底していた感じです。

1日の摂取カロリーは、普通の日は 1500 - 1600 ていど、ジョギングする日(週2日)は 1200 ていど、くらいに収めるようにしていました。外食する日は 1800 - 2000 って感じです。

年をとると新陳代謝が落ちるので、おそらく基礎代謝は 1400 - 1600 くらいなので、ジョギングの日に 400-600g くらい落としてそれ以外の日は現状維持かプラスになるサイクルを繰り返しました。

飲み会の翌日はどうしても体重が増えますが、幸い交友関係が狭く飲み会のお誘いも年に数回なので、そこは諦めて、増えた分を2,3日でリカバリーするようにしてました。

運動

今年のはじめに Fitbit Inspire 3 を購入しました。これで消費カロリーが可視化されるので、あすけんに登録する摂取カロリーとの差分が取れるようになりました。

  • 筋トレ10分を週に5回
  • VR卓球20分を週に5回
  • ランニング40分を週に2回

週に5回のやつは達成できた週のほうが少ないくらいで、ときにはゼロの週もあったくらいです。

ランニングは子供の頃から嫌いで続けられる自信がまったくありませんでした。色々と思案した結果、ゴミ捨てのついでに走りに行くことにしました。週2日だけだし、これなら続けられるかも?っていう期待がありましたが、結果的には、早く走りに行きたくなるほどのめり込み、走りに行くついでにゴミ出しをする、主格逆転状態になるほどでした(ゴミ出しを忘れるほど)。

筋トレ

当初は FitCoach をインストール&課金して、トレーニングメニューを提供してもらっていましたが、最終的にはそれをもとにして自分でメニューを組み立てました。

fitcoach.fit

    • Squat
    • Reverse lunge
    • Alternate side lunge
  • 腕・肩
    • Narrow push-up
    • Pike push-up
    • Triceps extension push-up
  • 背中
    • Superman

これでだいたい10分くらいです、回数的には各パート10-15回くらい。筋肉の維持は労力的に大変なので、方針としては本当に最低限の「脚・腕・腹・背中」のみにし、VR卓球の前に必ずやる(筋トレのあとに有酸素運動をすると脂肪燃焼効率が高まる、みたいなのをどっかで読んだので)ようにしました。筋トレなので消費カロリーはとくに気にしていませんでしたが、だいたい 40 kcal くらいです。

VR卓球

2022年10月に PICO4 を買い、ボクササイズやリズムゲームをやってましたが、ElevenTableTennis というゲームにハマって、最近はこれしかやってないです。ボクササイズもカロリーの消費効率高いんですが、いかんせんキツいので20分継続するのが大変です。卓球はその点でそれほどキツくなく、相手(CPU)のレベルも変えられるので、楽しく継続できています。ちなみに20分での消費カロリーはだいたい 150 kcal くらいです。

ランニング

脂肪燃焼効率を高めるために、心拍数が 155 bpm を超えないように気をつけながらスローペースで走っています。走るタイミングは週2回(最近週3回に増やした)、ゴミ出しをする日の朝食前です。朝食前か後どちらが減量にいいかというのは科学的にまだはっきりしてないようなんですが、自分は単純に腹が痛くなったりするので食後に運動したくない、というだけの理由で前にしています。

ワークアウトの前と後、朝食に適したタイミングは? | 記事「OGC」.オンラインストア (通販サイト)

朝は空腹のまま有酸素運動してもいい?究極のダイエット・メソッド「ファステッドカーディオ」の真実|ハーパーズ バザー(Harper's BAZAAR)公式

時間は40分、距離はだいたい 4km といったところで、消費カロリーは 300 kcal ちょっとです。

ちなみに、昔から長距離走が嫌いで、ジョギングも、あんな単調な運動が習慣化できるわけがないと思ってました。なので、ゴミ出しのついでに走ることにしたんですが、最初の頃は面倒くささが勝っていたものの、いまでは走るついでにゴミを出すようになり、週に2日走るのが待ち遠しくさえなりました。自分で自分に制限をかけてはいけないですね、完璧に習慣化できました。途中からずっと週2日だと足りないと感じていたんですが、ラスト2ヶ月体重が落ちにくい状態が続いたこともあり、9月に入って3日に増やしました。

習慣化を促進するためにもう1つ工夫したのは、Fitbit API を使ってランニングの内容を SNS にシェアするようにしたことでした。

最近お気に入りの Rust で CUI のアプリを作り、Fitbit API から直近のランニング活動の詳細を取得し、Mastodon(最近 Misskey 対応しました)に投稿するようにしたことで、途中で止めないようにできたかもしれません。

https://github.com/nunulk/running_tracker/

距離やスピードはあまり重要ではないですが、心拍数の範囲を 115 - 150 bpm の間にどれだけ収めることができるか、というゲーミフィケーション要素を入れたことで、前回は何分だったから今回ちょっと増えたぞ、とか、ピークタイム(150超えた時間)が多すぎたので次回はもうちょっと自重しよう、みたいな楽しみ方ができたのが大きかったです。

misskey.dev

おわりに

気づいたら3年弱の長丁場だったわけですが、コロナ禍で外出が減ったこともあり、上手いこと健康的な食生活と運動を習慣化することができ、だいたい計画どおりに目標体重に到達できました。考えてみれば、渋谷や赤坂で働いていたときに、毎日旨いメシを腹いっぱい食べていたことが肥満の原因になっていたわけで、在宅勤務はちょうどいい機会でした。

今後は 61kg を超えないように引き続きいまの生活習慣を維持しながら適度に旨いメシを食っていきたい所存です。応援してくれた人たちにこの場を借りて感謝を捧げます、どうもありがとう。

減量目標達成記念天丼

「Laravel 概論」という教材(?)を出しました

はじめに

この記事について

Techpit にて表題のテキストをリリースしたのでお知らせです。

www.techpit.jp

タイトルに「教材(?)」と書いたのは、Techpit で出すのでいちおう教材ではあるんですが、胸を張って教材と呼ぶにはちょっと内容が薄いかなという含意があります。正直にいえば、お話をいただいたときも、書いているときも、こうやってリリースしたあとも、本当に価値があるものが (書ける|書けてる|書けた) んだろうか、という迷いがありました(いまもあります)。

そういう意味で、この記事では、最後のあがきとして自分を納得させられるような言い訳半分、本教材の紹介半分になるような感じになりました。

Techpit とは

https://www.techpit.jp

「サービスを作りながらプログラミングが学べるプログラミング学習教材のマーケットプレイス」で、これまで2冊の教材を出させていただいています(いずれも Laravel 関連)。

自分の理解としては、ドットインストールのような初学者向けのサービスを利用したのちに次のステップに移るための少し高度な内容を扱う教材が多い印象で、実際、基本的な文法は身につけたものの、自力でアプリケーションを作れるまでには習熟できていないレベルの学習者に向けて「サービスを作りながら」というコンセプトをを大事にしているプラットフォームです。

経緯と目的

Techpit から依頼を受けて書きました。個人的にはいまさら概論?という気もしなくもなかったんですが、新しく PHP や Laravel を学び始める人がひとりでも増え、また、迷わず学習できることを願い、引き受けました。

Laravel が PHPデファクトスタンダードフレームワークになったことはいまや異論を挟む余地はないかと思いますが、私が使い始めた頃(7年前くらい?)は話題にはなっていたものの CakePHPSymfony の案件がまだまだ多く、新規のプロジェクトの中ですら数えるほどしかなかったので、この5年くらいの間でだいぶん浸透したなぁと感慨深く感じています。

そういう意味では、長らくデファクトスタンダードと呼べるような存在がなかった群雄割拠な PHP フレームワーク界にさっそうと現れ、瞬く間にその地位を固め、GitHub のスター数で Ruby on RailsDjango を凌ぐポイントを叩き出した Laravel が、当たり前に存在し、当たり前に使えるいま、バックエンドのプログラミング言語PHP を選ぶ理由としては「Laravel があるから」で十分なのかな、という思いもあります。

こんな動画が出るくらいには現在の PHP は進化しています。

youtu.be

個人的には、Laravel の登場がなければ PHP ユーザーはいまよりもっと少なくなっていただろうと思うので、Nikita Popov 氏と並んで作者の Taylor Otwell 氏には感謝しています、Laravel と出会わなければ私もとっくに PHP とお別れしていたでしょう。

ちなみに、Nikita Popov 氏は PHP 7 から 8 にかけてのアグレッシブかつドラスティックな進化を牽引した立役者のひとりです。

Thank you, Nikita! • PHP.Watch

本教材の内容

詳しくは冒頭に貼ったリンクから教材の紹介ページを読んでもらえばわかりますが、簡単に紹介すると、

  • Laravel はどんなフレームワーク
  • Laravel の歴史
  • Laravel の主な機能(の一部)
  • Laravel を選ぶ理由と適さないケース
  • Laravel 学習ロードマップ

といったものになっています。

公式ドキュメントはもちろんのこと、インターネット上のリソースをいろいろ読めば、本教材に書かれていることの大部分は知識として得ることができるものです。しかしながら、いちおう自身で7年使ってきた経験で得たものもいくぶんか入れ込んでいますし、上に挙げた内容を一気に知れるコンテンツは他にないと思うので、これから Laravel 使ってみようかなと思っている方にとって、簡潔に全体像を把握できる内容になっているはずです。

書かなかったことのうち少し補足

言語選定について

自分はキャリアを C/C++ でスタートしたっていうこともありますが、静的型付け言語への信頼感はいまだに強くあります。一方で、コンパイルにかかる時間が惜しくて動的型付け言語に移ったという経緯もあるので、両者を単純に比較することは難しく感じています。実際、Rust(最近のお気に入りです)プログラムのコンパイルにはそれほどストレスを感じません(大きいアプリケーションは未経験というのはありそうですが)。

けれども、現状ではまだインタプリタ型のほうが、ウェブアプリケーションにおいては第一の選択肢であると思っていて、ハイパフォーマンスを求められる場合はその限りではありませんが、多くのウェブアプリケーションでは実行時パフォーマンスよりも開発時パフォーマンスを優先したほうがいいと思っているので、その点で PHP を推しています。

書けばすぐ動く、というのは塵も積もれば山となりますし、タイプヒンティングはないよりもあったほうがいいですし、Laravel の開発効率は非常に高いです。

正直にいえば、いまからプログラミングを始めるのであれば Python も有力な第一候補だと思います、機械学習や AI の領域において圧倒的な存在であり、それらと連動したアプリケーションであれば使用する言語を統一できるからです。ただ、自分も機械学習と連携したプロダクトに関わったことがありますが、たいていの場合、分担が別になると思うので、機械学習(や AI)も UI もひとりでやるのでない限り、言語は別になっても構わないと思います。

フレームワーク選定について

いちおう教材内では「明らかにオーバースペックであると判断した場合はマイクロフレームワークの採用を検討するといいでしょう」と書いたんですが、プロジェクト発足当初ではマイクロフレームワークで十分という判断がなされても、サービスの成長や方針の転換などによって多機能化が求められるケースもあるので、もし自分が選定する立場であれば、迷わず Laravel を選びます。もちろん、機能面だけでなく速度面の要求もあれば一概にはいえないですが、ただ単に「オーバースペックだから」という理由だけでマイクロフレームワークを選ぶことはしないです。

一方で、「アプリケーションの大部分を自分たちでコントロールしたい場合」はもう少し判断が難しくなるかなと思います。20年前まだ SI の仕事をしていたとき、プロダクトに OSS を組み込むなんてもってのほかで、自作するかカスタマーサポート付きの有償ライブラリしか使わせてもらえないプロジェクトは多々ありました。ライブラリに不具合があった場合リスクになるから、ですね。2023年現在そういったプロジェクトが存在するかはわかりませんが、少なくとも PHP が選択可能であれば OSS ライブラリも選択可能になるはずなので、言語は PHP だがフレームワークは不可、という状況にはなりにくいかなとは思います。が、いまだにオレオレフレームワークにこだわる管理職(経営層なのかな?)がいるという話もまれに聞くことがあるので、そういうケースでは、やむを得ないでしょうね。

マイクロフレームワークに関しては以前は Slim 推しだったんですが、バージョン4になってちょっとややこしい作りになってしまったので、いまは特に推しフレームワークはない状態です。

学習ロードマップについて

教材内にも書いたとおり大事なのは「公式ドキュメント」と「古い知識を新しい知識でアップデートし続ける必要」の2点なんですが、特に後者に関して軽く考えていると痛い目をみるというか、もうちょっと強調して書いておくべきだったかな、と思う点は、Laravel は開発サイクルが速く古いバージョンはどんどん切り捨てられていくので、継続的なアップデートが欠かせないということです(ライブラリ本体と知識両方)。

リリース計画は公開されていますし、多くの場合そのスケジュールに従ってリリースされているので、事前に自分たちの計画に盛り込んでおくことは容易なはずなのに、それを疎かにしてしまうチームは多いです。それがなぜなのかは謎なんですが、現実にそういったプロダクトは多々ありました。個人的には LTS(長期間のサポートがある特定のバージョン)は考えずにメジャーアップデートにはできるだけ速やかに追従していくことを勧めていますが、それは、LTS の期限が切れる直前に慌ててアップグレードするとその分変更量も増え、不具合の出るリスクがあがってしまうからです。

Laravel 以外のフレームワークでもそういったことは起こり得ますが、フレームワーク自体の更新速度によるため、やはり Laravel と付き合うためには継続的な更新が不可欠だと考えています。なので、ここまでやったら学習は終わり、ではなく、GitHub などで変更点を追う習慣や破壊的変更があった際にどのように対処するかの訓練などを含めて、学習が永久的に続くことを念頭に置いてほしいです。

おわりに

値段は980円と技術書のカテゴリでは安い部類ではありますが、冒頭に書いたとおり本当にそれだけの価値があるものが書けたのかどうかは自信がありません。その辺は売上とレビューの結果でわかるとは思いますが、しばらくはもうちょっと価値のある情報を提供できないか思案することになりそうです。ウェブ上のコンテンツなので改訂も比較的簡単にできるのがいいですね。

台湾に行ってきました

はじめに

この記事について

2023年8月に初めて台湾に行ってきたのでその記録を残しておこうと思います。本当は2020年に行く予定だったんですが、コロナの影響で断念したので、今回はそのリベンジということになります。プログラミングの話は一切でてきません、あしからず。

きっかけは2019年(だったかな)のPHPカンファレンス沖縄に台湾から何人かのウェブエンジニアが来ていて、夜に飲みに行ったときに次回はぜひ日本から来てください、みたいな話になったことです。

本記事では、{Laravel x Vue} Conf Taiwan 2023 の感想、と言いたいところですが、あんまり書くことがないので、どちらかというと台湾(台北)に滞在した感想(おもに食べ物)について記します。

あと、せっかく行くので、カタコトでも現地の人とコミュニケーション取れるようにと中国語(大陸普通語、台湾華語)の勉強も3ヶ月くらいやったので、その辺のことも書いておきます。

{Laravel x Vue} Conf Taiwan 2023 とは

laravelconf.tw

前回までは Laravel Conf Taiwan だったんですが、今回 Vue.js のコミュニティと合同で開催、2つのセッションレーンを同時並行でやるっていう形式でした(なんと Vue.js の作者 Evan You さんの基調講演もありました)。

私は Laravel 側の会場にしかいなかったので Vue.js 側がどんな感じだったのかわからないんですが、Laravel 側は立ち見も出るくらい盛況でした。

海外のカンファレンスに参加する難しさ

あんまり書くことがないといいつつ、ひとつだけ普段のイベント参加とは異なる状況になったので、その辺のことは書いておこうと思います。

それは、参加できるかどうか前日までわからなかったことです。

1ヶ月くらい前に上のランディングページから申し込みしようと思ったら、台湾国内で発行されたクレジットカードでしか決済できない旨のメッセージが表示され、チケットの購入ができませんでした。

しかたなく、運営スタッフのメールアドレス宛にメールしたものの、返信がしばらくなく、やっと連絡が来て何度かやりとりしている間もなかなか次のメールが来ずにやきもきしましたが、海外だとこういうこともあるよな、と気持ちを切り替えて、運良く参加できたらラッキーくらいの心構えで待ちました。

参加できるかどうかわからないままでしたが、仮に参加できなくても旅行を楽しむつもりで航空券やホテルの予約を取り、最終的には、前日に最後のメールを送ったら返事が来て、無事に参加(見学)できることになったのは奇跡的でした、最終日メールしてなかったらおそらく参加できていなかっただろうと思うので、ダメ元で出してよかったです。

{Laravel x Vue} Conf Taiwan 2023 poster

台湾の印象

今回は(って旅行はどこへ行くにしてもほとんど毎回そうだけど)食べ物目的だったので、事前に食べ物情報を仕入れていました。情報源は主に YouTube で、ゾロさんのチャンネルは大変参考になりました。とにかく台湾グルメの動画が大量にあるので時間泥棒でもあるんですが、いくつかピックアップして観ました。

www.youtube.com

知り合いで台湾に行ったことある人の話を聞くと、台湾グルメの好き嫌いはだいぶ個人差が激しそうだなっていう印象はあったものの、わりとなんでも食べる人間なので、なんとなく大丈夫だろうと思ってたんですが、案の定大丈夫どころかだいぶ満足しました。炭水化物と脂質多めなので、ハイカロリー ≒ 旨いっていう公式どおりではあるんですが、ダイエット中の身にはなかなか堪える料理が多かったのは間違いなくて、帰国して測ったら 1.5kg 近く増えてました。でも、道行く人々を観察するとそんなに太ってる人はいない印象なので、家庭料理だともうちょっとヘルシーなのかもしれないです。

滷肉飯,乾拌麵,水餃,鍋貼,小籠包あたりの鉄板メニューはどれも美味しかったですが、個人的なナンバーワンは朝食で食べた 蔥抓餅 っていう惣菜クレープみたいなやつで、あまりに美味しかったので、2日連続で食べちゃいました。

蔥抓餅

写真だとわかりにくいですが、クレープよりは分厚い生地にトッピングで選んだチーズと卵焼きが挟んであって、辛いソースがかかってます。

Taiwan the Lucky Land

Taiwan the Lucky Land っていう観光客向けのキャンペーンをやっていて、当選すると20,000元(約4000円)の入ったプリペイドカードがもらえて、しかも確率がそうとう高そうだぞっていう情報を事前に入手していたので、当たる前提で予算を組んでたんですが、残念ながら外れました(笑)。悠遊卡(EasyCard: Suica みたいなやつ)が使えるところはけっこうあるので、2025年6月までやってるので台湾に行く人は登録しておきましょう。

5000.taiwan.net.tw

SIM

SIM は eSIM じゃなくて物理 SIM で、5日間無制限(ただし、2G超えると低速化)で1080円、事前に利用登録しておけば、現地で SIM を差し替えるだけで使えて便利でした(手持ちの端末がデュアルSIMだったので入れておいて切り替えるだけで済みました)。

Uber

あと、Uber がめちゃくちゃ便利で、この時期の台湾は日中や夕方に突然スコールが降ることがあって、傘を差しててもずぶ濡れになるので、あとはホテルに帰るだけってタイミングで雨が降ってるときなんかに Uber を使いました。帰りの飛行機が早朝で地下鉄もまだ走ってなかったので、空港までの移動も Uber があって助かりました。

行き先を選んで2,3回タップするだけで、あとは来た車に乗って目的地に着いたら降りるだけ。交通網の発達している台北とはいえ、乗り換えを含むと時間的にはあまり変わらないことが多かったので、暑さや雨のことを考えるとドアトゥドアで移動できる手段として最強でした。もちろん料金は高くて地下鉄より10倍くらい高いですが、それでも市内の移動なら200-300元(1000-1500円くらい)なので、地下鉄がべらぼうに安いって感覚です。

Dusk Diver 2

台湾のゲームメーカーが作った Dusk Diver っていうアクションRPGがあって、西門町っていうエリアを 3DCG で再現しているっていうことだったので、続編である 2 をプレイしてから行きました(まだクリアしてないけど)。

街並みがほぼ忠実に再現されてたり一部実名で登場する店があったりして、プレイしていてテンション上がりました。

実際に行って、登場する店を巡ってみましたが、肝心の主人公たちの拠点となるコンビニの場所がわからずちょっと悔しかったです(おそらくコンビニではなく別の店だった可能性)。

美觀園

西門町は新宿歌舞伎町と秋葉原が合体してちっちゃくなったような印象の雑多な感じで、ダンまちのデカい横断幕が掛けてあったり、路上で喫煙してる外国人がいたり、タピオカミルクティー(珍珠奶茶)を注文するために並んでいる若い女性の集団がいたり、大通り沿いにはブランドショップや小洒落たシネマがあったり、まぁ楽しかったですね。バッティングセンターもあったので(ゲームセンター的なのに併設されてた)一回30元で4ゲームやりました。

Animate

西門町に(の近く、かな)アニメイトがあるっていうのも Dusk Diver で知ったので、行ってきました。日本の漫画の繁体字中国語版がたくさんあり、どれを買うか迷ったんですが、ゴールデンカムイ(黃金神威)、乙嫁語り(姊嫁物語)、ルックバック(驀然回首)の3冊を買いました。一冊140元くらいだったかな、時間があるときに翻訳しながら読みたいと思います。

かかった費用

(各金額はおよそ)

  • 航空券 100,000(EVA AIR 羽田↔松山)
  • ホテル 47,000(4泊5日)
  • SIM: 1,080
  • 食費・雑費: 20,000(およそ4000元に換金、うち1000元を悠遊卡にチャージ、残り3000元を現金に)
  • Uber: 3,000(3回分/クレジットカード)
  • お土産: (クレジットカード、計算してないけど合計で 10,000 はいってないはず)

ホテルは台北市内だとけっこう高くて、選んだところはかなり安い部類でしたが、施設・サービスともに問題なく快適でした。立地もすごくよかったです。カンファレンス会場まで歩いて15分ていどだったので、ここを選んだんですが、もう一回行くってなってもここを選ぶかも(蔥抓餅の店も歩いて行けるし)。

食事は1食につき 80 - 200元(高くても1000円くらい)、飲料はコンビニだと30元(150円くらい)、カフェだと100元(500円くらい)もあれば十分って感じでした。ほとんどローカル食堂やカフェ、コンビニでしか飲食はしてなかったので、悠遊卡に1000元チャージ、現金は3000元しかなかったんですが、4日間では使い切れませんでした。ローカル食堂では現金のみなので、現金もそこそこ持っておいたほうがいいでしょう。あとは、LINE Pay が使えるってことだったので、いちおう使えるようにしておきましたが、使う場面はほとんどなかったです(LINE Pay が使える店はだいたい悠遊卡が使えるはず)。

国語学

2019年のときに一度「台湾行くぞ」ってなったので、そのときにも少し学習したんですが、今回実質やり直しだったので、約3ヶ月半自習+1ヶ月オンラインでのマンツーマンレッスンで本当にカタコトですが喋れるようにしてから行きました。

とりあえずの指標として HSK3 級がちょうどよかったです。

ただ、台湾で使われている標準語(台湾華語)と中国で使われている標準語(普通語)は微妙に違うので、HSK用の学習と並行して台湾華語の学習もする必要がありました。ただ、いちばん大きな違いは文字の違い(繁体字簡体字)ですが、日本人にとっては繁体字のほうが判別しやすいと思われるので、そこはあまり問題にならないと思いますし(どうせ分からない字は分からない)、また、その他の違いは、使われる単語が一部異なるとか、発音が本当に微妙に異なるとか、その程度なので、普通語だけやって行っても問題ないとは思います。個人的にはなるべく現地の言葉を使いたいっていうのがあったので、最後の1ヶ月くらいは台湾華語に完全にフォーカスしました。

語学でも YouTube にお世話になって、こちらのチャンネルがとても参考になりました。

www.youtube.com

オンラインはこちらの月30回プランに申し込んで、ほぼ毎日30分間主に発音を鍛えてもらったり旅行時に覚えておくといいフレーズ集などを教わったり、現地の習慣やおすすめのお店などを聞いたりしました。

taiwan-sanpo.com

おかげで、口頭での注文や支払いも支障なくできたし、コミュニケーションへの不安がほとんどなかったのでアクティブに行動できたのでよかったです(実際には、相手の言ってることがほとんど聞き取れない、ちょっと複雑なお願いや主張はできない、など問題だらけでしたが)。

事前情報では、有名繁華街だと日本語がわりと通じるみたいな情報もあったんですが、自分が行った店はお茶屋さん以外ではほぼ使えなかったです(英語を喋れる店員さんのほうが多かった)。

中国語は発音(とくにイントネーション)が大事らしく、カタカナ中国語だと通じないことが多いとのことだったんですが、練習の甲斐があったのかそれとも実際にはそうでもないのかわかりませんが、とにかく自分の中国語が通じないっていうのはほとんどなかったです(たぶん)。

おわりに

行く前に食べる予定の食べ物リスト作ってたんですが、いくつか食べそびれたものがあったので、近いうちにまた行きたいと思います(少なくとも臭豆腐は食べたい)。

あとはリスニングもうちょっとできるようにならないとコミュニケーション取れるところまでいけないので、いつになるかわからないけど、ちゃんとできるようになりたいです。

今度は台中や台南にも行きたいなぁ。

というわけで、報告は以上です。書き忘れたことがもしあれば追記するかもしれません。

滷肉飯

ジェネレーティブAIとソフトウェアエンジニアの未来について

はじめに

この記事について

最近あちこちでジェネレーティブ AI によって奪われる仕事はどれだとか、プログラマーの仕事も奪われるだとか、色々見聞きして盛り上がっている感じがするので、自分もいっちょ噛みしたいなと思って書きました。

とくに慧眼とかはないので、戯言の類です。

ジェネレーティブ AI とは

いまさら解説の必要もないでしょうし、自分もそんなに深い知識があるわけではなく、世間に広く流布している情報以上のことは言えないので、省略しますが、プログラマーにとってインパクトがあるのはいまのところ、GitHub Copilot と OpenAI ChatGPT の2つでしょうか(類似プロダクト含む)。

GitHub Copilot は VS Code とか PhpStorm に入れて使ってますが、タブ押すだけでコード片が出てくるので、ちょっとタイプしてタブ、またちょっとタイプしてタブ、みたいな感じでさくさくコードが書けて快適ですね。

ChatGPT のほうは、こういうの作りたいんだけどサンプルコード出して、みたいな指示から得られた回答を叩き台にして利用したりしてます。

目次

コンピュータによって中間業者が不要になる世界

全部が不要になるわけではなくて一部だけなんだろうと思うんですが、これはもうとっくに始まっていて、生産者と消費者が直接繋がるのは EC でできているし、Twitter 転職では転職エージェントは不要です。

基本的には中間業者が間に入れば中間マージンが発生しその分製品やサービスに乗るコストが増えるので、コストに見合った価値を提供できるかにかかってるわけです。

出版界隈では(あまり詳しくないですが)、小売店(書店)が在庫を抱えないで済む仕組みを取次業者と一体となって作ってて、なかなか共依存関係が解消されない、みたいなのは聞いたことがありますが、デメリットもある代わりにメリットもあるからこそこうした仕組みが続いてるんだと思うので、業界の商習慣みたいな変わりにくいものがある場合は、消費者にとってメリットが大きかったとしても不要にならないんだろうとは思います。

ただ、コンピュータとインターネットは、あるジョブの物理的な制約が少なければ少ないほどそのジョブを代替できる可能性が高まるので、コンピュータで転送可能なデータで表現できるものはなんでも、今後中間業者を必要としなくなる傾向は変わらないと思います。

逆に、輸送とか保管とか、物理的な制約のあるジョブは置き換え不可能ですし、現状データ化の難しい部分は人間が手伝ってやる必要があって、様々な中間業者が持っている「目利き」のスキルとかはなかなか一般化しないと思うので、中間業者は多かれ少なかれ残るだろうし、減るとしても緩やかに減っていくんでしょう。

ジェネレーティブ AI によって奪われるソフトウェアエンジニアの仕事

コンピュータによる作業の代替には、中間業者という側面に加えて価値提供の手段の変換という側面もあって、あるジョブが提供する価値が変わらなければそのジョブをどうやって実行するかという点は問題になりにくいんだろうと思います。

サイゼリヤはお店の厨房ではほとんど調理らしい調理してないそうですが、料理のクオリティは(価格に比して)高いと認知されていると思います。レストランにおいては、シェフがどのように料理を作っているかを気にしない人が少なくないんだろうと思います。同一のメニューであればいつ行っても同じクオリティの味が食べられる、というのがプロにお任せする価値のひとつですが、それは逆に、ロボット技術が進化したら人間よりも正確に味を再現できる可能性もあるので、悩ましいところです。

いずれにしても、サービス提供者が内部でどのような工程や手段でそのサービスを成り立たせているかは利用者にとってはほとんどブラックボックスですし、詳らかに知りたいと思っている人も少ないんじゃないかっていうことです。

一方で、プログラマの提供するサービスである「コードを書く」というジョブは高度な技能が必要なものではありますが、部分的には汎用的なコードが存在し(そういうものの一部はすでにライブラリ化されていますが)、それらは全員が自分で書く必要があるものではありません。

書かれたコードが GitHub Copilot が生成したものだろうが、自らタイプしたものだろうが、同じ機能を提供できるならどちらでもいいのはもちろんですが、自分では書けないものを AI なら書ける、ということが起こっているので(もちろんその逆もあるけど)、これは大変だ、失業だって思ってる人もいるみたいです。

プログラマの中には、実際にコードを書いているんだから我々は生産者だって思ってる人がいるかもしれませんが、ほとんどの人は「何を作るか」という部分にはあまり関与してなくて、他のだれかからこういう仕様でお願いねって言われて「どうやって作るか」の部分を担当していると思うので、実質的には生産者じゃなくて中間業者に近い存在だと思います。

余談ですが、四半世紀以上ソフトウェア開発に携わってきて思うのは、ソフトウェアを作る工程はどうしてここまで分業が進んでしまったんだろう、ということです。

工程ごとに作業者が変わるっていうのは別にどの業種でも普通のことなんでしょうけど、自分にはアメリカの丸亀製麺に近いものを感じるときもあって、同業者がこの辺どう感じているのか知りたいと思うときがあります。

blog.btrax.com

それぞれの工程が非常に複雑で技術的に網羅するのは大変だ、というのはそうなんでしょうけど、全部が全部ってわけじゃないよなぁとも思います。

閑話休題

極端な話、「何を作るか」を握ってる人が AI の力を借りて「どうやって作るか」を気にしなくて済むようになった世界では、ソフトウェアエンジニアは不要になるでしょう。いまでも、ライブラリがどうやって動いているのか理解していなくてもソフトウェアは作れますし、ほとんどの人は部品や基盤の仕組みをいちいち検証したりはしないはずです(不具合に遭遇して初めてソースコードを読む、みたいなのはあると思いますが)。自分は、興味のあるライブラリのコードはざっと目を通しますが、あくまで興味であって、それをしないと仕事の質が落ちる、みたいなことはないと思っています。

そう考えると、ジェネレーティブ AI の普及によって極端に状況が変わるとは思えなくて、自分で書かなくていい部分が増えるだけなんだと思います。で、それがゼロになるにはしばらくかかるでしょうし、もしかしたら永遠に来ないかもしれないし、当面は自分たちの仕事が楽になっていくって考えたほうがハッピーなんじゃないかなと思います。

これまでも大部分の人は Google を始めとする検索エンジンを活用して他人の書いたコードを参考にしてきたわけで(まるまる利用するとコピペプログラマなどと揶揄されるわけですが、AI の生成したコードをそのまま利用するのはなんて呼ばれるんだろう?)、本質的にはそれと変わらない気がします。

とはいえ、一人のソフトウェアエンジニアが提供できる価値の総体が減っていくのは間違いないので、これまで10人必要だった仕事が5人で済むようになるとか、そういうのは起こるかもしれません。

ソフトウェアエンジニアは需要に対して供給が不足しているそうなので、短期的には仕事を失う人も出るかもしれませんが、長期的にはいいことなんじゃないかと思います。

状況の変化を受け入れるか別の価値を生み出そうとするのかはその人次第な気もしますし、変化は緩やかに起こるでしょうから、中長期的に適応していけばいいんじゃないかと思います。

ソフトウェアエンジニアとして提供できる価値

以前から自分はソフトウェアエンジニアは「何を作るか」にもっと積極的に関与したほうがいいと思っていて、自社開発にしろ受託開発にしろ、利用者(もしくは将来の利用者)との接点を増やすことで提供できる価値が増えるんじゃないかと思います。

現状でも、何かを作りたいが作れないっていう人がいて、その人がそれを作れる人にお願いして作る、というパターンはよくありますが、もしその人が自分でやってみようかなと思って AI を使って作れるようになってしまうと、作る人はいらなくなってしまいます。それでも、自分でもやろうと思えば作れるけど、時間がないからとか、人に頼むと自分より速くあるいは正確に作れるから他人に頼む、みたいなこともあると思っていて、そういうときに、頼まれる人になれるかどうかっていうのはどの辺で差が出るだろう、っていうことを考えたりします。

その観点でいうと、やはり速くて正確に作れる人っていうのは依然として価値が高くて、AI を使うと逐一対話していかないといけないので、一を聞いて十を知ることができる人間のほうがまだ優位性はあるんじゃないかと思いますし、人間は本人が思っているより、求めるものの言語化ってできないもんだと思ってるので、AI の場合は「この人こう言ってるけど、本当はこういうことなんじゃないかな」っていう推論(忖度?)は苦手で、基本的には言葉どおり受け取るので、そこも人間が優位にあるかなと思います。

つまり、いかに抽象的な命令に対して的確なアウトプットを出せるかっていうのが、ソフトウェアエンジニアに求められる技能のひとつになるんじゃないかということです。

自分はたまに整体に行くんですが、整体師の方に痛めてる箇所を口頭で伝えて触診を受けると、原因は痛い箇所ではなく別のところにあって、そのせいで患部に負担が来てる、みたいなことがあります。

ソフトウェアの場合は、エラーメッセージから直接的に原因になってる箇所を即座に特定するのが難しいことがあり、そういうのはやはりプロフェッショナルじゃないとわからない部分だと思うので、訓練された人間のほうが解決までのスピードは速そうです。

あと、これもけっこう大事なことなんじゃないかと思うんですが、優秀なコーチとしての役割です。

人間がやるにしろコンピュータがやるにしろ、それなりの規模のソフトウェアを作るのは長距離走的な忍耐と執念が必要で、疲れたり、最悪途中で投げ出したりすることになりかねないので、そういう局面でアドバイスや励ましができる人っていうのは必要なんじゃないかと思います。ただ、チャットの相手が人間なのか AI なのか区別できなくなるほどに日常に溶け込んでくれば、人間である必要はなくなってくるかもしれませんが、AI は自ら主体的にケツを叩いてくれるわけではないので、キーポイントはケツを叩けるかどうか、になるかもしれません。

ChatGPT とそれに類似する GPT たちは、総じてポジティブなことしか言わないように訓練されているようですが、ポジティブな言葉が常に人を奮い立たせるとは限らないですし(だからといってネガティブなことを言えばいいというわけではなく)、一緒にやっていきましょうの度合いというか、熱量みたいなのは人間ならではである気がします。

要するに、主体性、コミュニケーション能力、熱量、みたいなところが(いまのところは)人間ならではなのかな、と思った次第です。

さて、5年後10年後、ソフトウェア開発の世界がどうなってるのか本当に楽しみです(私は引退したので高みの見物をさせていただきます)。

おわりに

いつも以上にだらだらと書いてみましたが、明日から某コミュニティ有志の開発合宿(前回は1行もコード書かなかったけど)があるので、ジェネレーティブ AI とソフトウェア開発の未来みたいな話は出るでしょうから、そこでの議論を経て追記・整理するかもしれません。

an image of some software developers discussing and drinking a glass of beer generated by Stable Diffusion

何人かのソフトウェアエンジニアが床の上でビールを飲みながら議論している画像 by Bing Image Creator

(Bing Image Creator は日本語で指示したらアジア人っぽい人たち出してきた)

フリーランスソフトウェアエンジニアの生存戦略について

はじめに

この記事について

とあるイベントで表題の件で話をしてくれっていう依頼があったので、これまでの自分のフリーランスソフトウェアエンジニアとしてのキャリアを振り返りつつまとめてみました。

個人的な考え方(基準とか)が大部分で、生存者バイアス的なものもあるかもしれませんが、同時に、他の人にも通用する(当てはまる)部分もあるんじゃないかと思うのでフリーランスやってみたいけど不安に感じてる方とか、フリーランスになってみたもののうまくいってない方とかの参考になれば幸いです。

自分は長らくウェブアプリケーションエンジニアとして生きているのでおおむねそっち方面に偏っていると思いますが、昔はハードウェアに近い低レイヤの仕事だったり、SI だったり、コーポレートエンジニアだったり、色々やってきたので、広く「ソフトウェアエンジニア」の括りで考えてもそれほど遠くないはずです。

ちなみに、タイトルは「やがて至る無形の位への道」です。

スライドは公開しませんが、この記事は残しておきます(少なくとも2023年中は)。

無形の位とは

自分はあまり剣術には詳しくないんですが、「むけいのかまえ」「むけいのくらい」などと呼ばれる一刀流、新陰流、などに出てくる究極の構えと言われている境地です。

構え自体に善し悪しはなく、どの構えでも自分の手にぴったりと合い、気持ちにかなった構えを用いるべきである。このように心と構えとが一致した構えをこそ当流においては「無形の構え」と呼ぶのである。

竹田隆一・長尾直茂「一刀斎先生剣法書訳注」より

相手の出方次第でどうにでも対処可能な技術があればこそ、の構えです。

ぶっちゃけていうと、ソフトウェアエンジニアは当分の間安泰だと思うので、生存戦略とか考える必要性はあんまりないと思うんですが、フリーランス(業務委託)ってなると、不景気になって雇用が不安定になると正社員よりさきに煽りを食う存在なので、少しでも有利になるように戦略はあって損はないとも思います。

話の要諦としては、「あまり深刻に考えず、自然体で楽しく仕事できたら楽だからそこを目指していきましょう」ということです。

目次

1. 戦略とは

前提として、戦略とはなにかを知っておく必要があると思うので、ざっくりですが定義をしておきます。

  • 目標や目的を達成するための技術・理論
  • 元は軍事用語
  • 戦争の目的は勝つこと
  • 戦略とは、勝つためにどのように戦うかの方針

また、戦略はそれ単独で存在しうるものではなく、必ず目的や資源などと一緒に考える必要があります。

孫子の有名な言葉で「彼を知り己を知れば百戦して殆うからず」というのがありますが、「相手」と「自分」を知ることが目的達成には必要です。

戦略とそれ以外の要素の関係は以下のようになります。

  • 目的: 戦略を用いて達成したいこと
  • 戦術: 戦略に基づいて実際に実行する打ち手
  • 資源: 戦略を実現するために必要な源泉
  • 兵站: 資源を効率的に活用するための流通

まとめると「戦略とは、目的を達成するための理論であり、実現のためには確実に戦術を実施しなければならず、十分な資源と兵站がなくてはならない」ということになります。

前述のとおり、ソフトウェアエンジニアは現状十分な需要があるので、生き残るという目的に対してはとくに戦略など立てなくても達成可能と思います。

しかし、自滅だけは需給バランスは関係ないので、生き残れないケースとしていちばん可能性があるのは、身体もしくは心が耐えられなくなってソフトウェアエンジニアを辞める、ということではないかと思います。

というわけで、「いかに身体を壊さず、諦めずにフリーランスで戦い続けられるか」が最低限達成したい目的になるんだろうと思います。

2. 目的

目的のない戦略は意味がないので、まずはどういう目的を達成したいかを考えます。上に挙げた最低限の目的である「いかに身体を壊さず、諦めずにフリーランスで戦い続けられるか」でもいいですし、もう少し稼ぎたいとか、自由に働きたいとか、そういうことです。

これまでも同様のテーマで話したことがあるんですが、毎回出すのはこの図です。

https://ja.wikipedia.org/wiki/生き甲斐 より

みんなそれぞれ働く目的が違うと思うんですが、自分がいまなんのために働いているのか、というのを意識しておくのは重要で、一生一貫して同じ目的を目指すことってあんまりないと思いますし、ステージや状況によって少しずつ変化する中で、この図を常に頭に置いておくことは、いまはどういう状況なのかとか、最低限これだけは守りたいとか、次はここを目指したいとか、そういう判断や決断の基準となる立ち位置を示してくれるものと思っています。

自分自身を振り返ってみると、若いときはとにかく稼ぎたいと思って「Profession = 稼げること + 得意なこと」を軸に働いていましたが、歳を取るにつれて「Mission = 好きなこと + 求められること」にシフトしてきました。

いまでも「得意なこと」は意識してますが、「稼げること」はほぼ眼中になくて、「好きなこと」>「求められること」>「得意なこと」>「稼げること」の順になっています。

3. 資源

自分は、働く上で重要な資源は以下の8つと考えています。2つずつペアにしているのは、強い関連があるからです。

  • 身体と心
  • お金と時間
  • 能力と経験
  • 人脈と信頼

身体と心

何はなくとも身体と心。前述のとおり、自滅パターンは避けなければならないので、これが最重要と考えます。体も心も壊したときに回復に時間がかかるケースがあり、最悪働くことすらできなくなってしまうので、ダメージがでかいです。健康第一。

お金と時間

若い頃には月8000円まで書籍の予算を確保するようにしていました。仕事をする中でスキルが足りない部分があれば、自覚してからあまり時間を置かずに勉強したほうがいいので、そういうのがあったときはすぐにお金と時間を投下しました。生活を圧迫するほど費やすのは本末転倒ですが、捻出できるギリギリのラインを見極めて、惜しまず使っていきましょう。問題は使い方で、いい本を選ぶ、効率的に時間を使う、無駄なことを省く、みたいなテクニックがあるといいですね。めちゃくちゃ勉強してるのに、一向にスキルが上がらない、ってことは避けたいです。

能力と経験

能力といっても、テクニカルスキル、ヒューマンスキル、コンセプチュアルスキル、みたいに様々なスキルがあるので、得意なことを伸ばしつつ、不得意な部分を強化する、みたいな戦略が必要です。詳しい話は今回はしないですが、キャリア戦略と密接に関係する部分だと思うので、自己分析をして、いま持ってるスキル、これから獲得したいスキル、みたいなのを洗い出してみるといいでしょう。

経験は積めば積むほど自分の資産になっていくので、色々経験しておくといいんじゃないでしょうか。

この辺は色々書きたいことはありますが、また別の機会にでも。

人脈と信頼

とくにフリーランスだとこの2つはめちゃくちゃ大事です。まぁ、まったく関連のない企業を渡り歩くことも不可能じゃないので、適当な仕事して契約切られまくりながら生き残ってる人たちもいるんだろうとは思いますが、本人もやってて疲れると思うんですよね。自分もこれまで酷いフリーランスと仕事したことがありますが、若い人は仕方ないにしても、年の行ってる人もけっこういて、いままでどうやって食ってきたんだろう、って最初の頃は思いましたが、20年前からいままで大して変わってないので、そんなもんなんだな、という気持ちです。

自分はわりと人付き合いが苦手なので、自分のことを評価してくれる顔の広い知り合いが何人かいて仕事を紹介してくれるので、そういう人たちは大事にしていきたいと思います(とはいえ、なかなかタイミングとかで紹介受けられないことも多々あるので、そこは申し訳ないなと思いつつ、許してください)。

4. 状況

資源は自分の内側の話でしたが、今度は外側の話です。

労働も需要と供給で成り立っている以上需要がないと始まらないので、自分はこれができる・これがやりたいと思っても、金を出すからやってほしいと思ってる人がひとりもいなければ仕事にならないわけです。

なので、相手(自分の外側)の分析っていうのは欠かせなくて、経済みたいなマクロの視点から求人みたいなミクロな視点まで、先読みに必要な情報を得る習慣は持っておくといいんじゃないでしょうか。

といいつつ、自分はメインで PHP 使っていますが、PHP の将来性はだいぶ薄いわけで、戦略的にはあまりいいとは言えないなぁと思いつつ、他にやりたい言語もないし(Rust が流行ったら書きたいけど、ウェブアプリケーションで使われるようになるかな、という点であんまり期待はしてない)、Laravel がとにかく便利で使ってて楽しいので、Laravel の仕事があるうちは様子見、という感じです。毎年新しい言語を学ぶ、という習慣があったおかげで(最近はやってないけど)、キャッチアップはわりとすぐできるようになっているので、明日から違う言語やるぞってなってもそんなに困らなそう、という見通しが立ってるのが大きいですが。

まぁ、20年前に絶滅すると思っていた COBOL ですら生き残ってるので、PHP もまだまだイケるでしょ、という楽観的希望的観測もありますが、選択肢が多いのを重視しているので、もうレガシーな会社しか PHP は使ってない、みたいな流れになったら他へ移りたいと思ってます。

また、今回のテーマは「フリーランス」ってことなので、フリーランスと正社員の違いは最低限知っておく必要があって、自分はもう経験的に正社員にはなれないと諦めてるのでしょうがないですが、それ正社員でよくね?みたいな仕事をフリーランスでやってたり、せっかくフリーランスなのに特性を活かせてない、みたいなのは、自覚的にやってるならいいですが、無知のせいでそうなってるともったいないと思いますね。

なので、正社員とフリーランスの違いは押さえておく必要があって、漠然としかフリーランスのことをわかってない場合は、たとえば以下のような比較をしておいてください(下記はあくまで傾向です)。

契約 責任 報酬 安定 仕事の幅 育成
正社員 無期 高い 低い 高い 広い 期待できる
フリーランス 有期 低い 高い 低い 狭い 期待できない

とくに、ジュニアレベルでフリーランスになるのが流行ってる(た?)みたいですが、個人的にはぜんぜんおすすめしなくて、短期的には利益高くできるかもしれないですが、中長期的にはあんまりベネフィットないと思います。

いちばんの理由は、業務委託の立場で携われる仕事の領域が狭いことと、あまり教育コストを払ってもらえないこと、です(将来的に変わるかもしれませんし、当てはまらない事例もありますが)。

仕事の領域が狭いのは経験とスキルに直結しますし、教育コストが低いってことは成長機会が少ないってことなので、スキルが圧倒的に足りないうちは、正社員になって時間をかけてレベル上げするのがいいと思います。

逆に、フリーランスの良さとしては、安定を捨てて自由になる、というところがあって、自分は一時期5案件掛け持ちしてたことがありますが、そういうのはフリーランスじゃないとなかなかできないことなんじゃないかと思います。

5. 選択

資源と状況を把握したら、それらを判断材料にして選択する段階に入ります。

自分は学者ではないので一般論はわからないですが、自分の経験から言えることは、複数の選択肢からどれかひとつを選ぶ際、以下の事柄を意識するといいんじゃいかと思います。

  • 目的を見失わない
  • トレードオフになるものとならないものを分けて考える
  • 選択肢のメリットとデメリットを理解しておく
  • 選択肢のリスクとリターンを予想しておく
  • いつでも方向転換できるように準備しておく
  • 退路を断つという戦略もあり

選択はしばしばトレードオフになりますが、一見なりそうでもよく考えるとならないものもあると思っていて、わかりやすい例だと、選択肢 A だとやりたいことやれるけど報酬が低い、選択肢 B だと報酬は高いけどやりたいことができない、みたいなとき、交渉すれば A の報酬を B と同様に上げてもらうこともできるかもしれない、みたいなことです。

メリットとデメリットに関してはメリット優先で考えて、デメリットもあるけど、メリットがデカいのでそっちを取る、っていう意識はいつもしています。

個人的にはあまりやったことないですが、退路を断つっていうのが効果的な局面はあって、自分のような意志の弱い人間にとって、踏ん切りがつかない場面で清水の舞台から飛び降りる、みたいなときは、退路がないほうが楽というか、やるしかない、みたいな覚悟が生まれて、結果的に上手くいったということもありました。どっちかっていうと乱暴な戦略なのであんまりおすすめはしないですが、失敗時のリスクを回収できるなら、採用してもいいかもしれないです。

そのときどきの選択に後悔しないようにするために、あらかじめできることをやっておく、というのは自分の性格的な理由から必要なことなのかもしれなくて、スパスパ決断できる人っていうのは、あんまりこういうことをやらなくてもいいのかもしれないですけどね。

あと個人的に大事にしてるのは、究極形をイメージしておく、というのがあって、いま現在の自分のレベルや達成度みたいなものは究極形からものすごく距離が離れているけれども、少しずつでも速く近づくために方角を意識しておく、ということです。ikigai のベン図と組み合わせてコンパスの役割をします。

  • やりたいと思えない → どうすればやりたくなるか
  • できない → どうすればできるようになるか
  • 需要がない → どうすればほしいと思ってもらえるか
  • 儲からない → どうすればお金を払ってもらえるか

自分としてはいちばん上は意識していて、こないだバックエンドエンジニアとして参画した現場で、諸事情によりブリッジSE的なロールを与えられたんですが、まぁぶっちゃけあんまり乗り気ではなくて、他に人がいないからやる、みたいな感じで引き受けて、でもやるからには楽しんでやりたかったので、相手がベトナム人のチームだったので、それを機にベトナム語の勉強を始めました。結果、メンバーとのコミュニケーションが楽しくなり、ブリッジの仕事自体を楽しむことができました。どの辺から興味を持てるかっていうポイントを探して、どんな仕事でも楽しむっていう姿勢は大事にしてますね。

現実を究極に近づけるためになにをすればいいか、私なりのポイントをいくつか考えてみましたが、こんな感じでとても平凡なものになってしまいました。 私は要領が悪いので、愚直にやるしかないっていう部分はあるんですが、シンプルでわかりやすいのはいいんじゃないかっていう気もしてます。

  • 健康第一
  • 目的を明確にする
  • 自分を知り相手を知る
  • 技術を磨く
  • 縁を大事に
  • 師範を見つける

最後に、自分の選択基準的なものを列挙して終わりにしたいと思います。

  • 疲れたら休息優先
  • 内発的動機づけ(楽しさ)重視
    • Passion > Mission > Profession > Vocation
  • 人重視(何をするかより誰とするか)
  • 繋がり重視(頼まれたら基本的に断らない)
  • チャレンジ重視(迷ったら困難な方を選ぶ)

他にもいくつかありますが、大きいところではこんな感じです。

おわりに

上にも書いたとおり、何を大事にするかっていうのは人それぞれだし、タイミングや状況によっても変わってくる部分もあると思うので、これまで述べてきた諸々を踏まえた上で、参考になる部分は参考にしていただければと思いますし、当面の間は食いっぱぐれることはない業界だと思うので(中にはニッチは技術のみ持っていて市場が急速に縮小している、みたいなケースもあると思うので、そういう場合は危機感持ったほうがいいでしょうけど)、気楽に構えていればいいんじゃないかと思います。

とはいえ、自分で「行きたい道を行けている」という安心感みたいなのがあると精神衛生上いいと思うので、指針はあったほうがいいですし、無理しない範囲でストレッチをかけていくっていうことも必要なんじゃないかと思います。

では、みなさん、良いエンジニアライフを。