Classi開発者ブログ

教育プラットフォーム「Classi」を開発・運営するClassi株式会社の開発者ブログです。

Flask 2.0.xのアップデート項目紹介

こんにちは、データAI部でPythonエンジニアをしている平田(@JesseTetsuya)です。普段は、PoCとデータをもってくる、というところ以外全部やる、というスタンスで開発業務を行っています。

日頃は、Flask1.1.4を利用していましたが、2021年5月11日にFlask2.0へのメジャーバージョンアップがありました。

メジャーバージョンアップということもあり、多くのアップデート項目がありました。そこで、特に日頃の業務に関わりそうなアップデートについて当記事にまとめていこうと思います。

Flaskとは?

Flaskは、PythonistaのArmin Ronachertによって2010年に初回リリースされました。いまでは、 Armin Ronacherを筆頭にPalletプロジェクトと言う名前でFlaskを含む、Flaskに関連する各ライブラリのメンテナンスがPalletプロジェクトメンバーによって行われています。

Flaskは、WSGI準拠のPythonマイクロWebフレームワークです。Djangoのようなフルスタックフレームワークとは違い、決まったディレクトリ構成もなければデフォルトでのデータベース機能やアドミン画面もありません。

GitHubのスター数も多く、JetBrain社の「Python Developers Survey 2020 Results」調査結果で人気ナンバーワンになっているそうです。

f:id:JesseTetsuya:20210707105027p:plain

軽量のフレームワークであるため、手軽にWEB アプリケーション開発でもAPI開発でも利用しやすくなっています。WEBアプリケーション開発未経験者やPython初学者にとってとっつきやすいフレームワークだと思います。

データサイエンスの界隈では機械学習モデルを構築し、その結果を返すREST APIをFlaskで実装するというユースケースがよく聞かれます。

データサイエンスを担う人が手軽にREST APIを作るニーズが高まりをみせ、最近では、Fast APIというASGI準拠の非同期処理を得意とするStarletteをラッピングしたフレームワークの人気も台頭してきました。

Fast APIにあってFlaskにまだ実装されていない機能を実装したのが今回のFlask 2.0へのversion upに垣間みることができます。

では、Flask 2.0の各アップデート項目をみていきます。

Flask 2.0.xのアップデート項目

ざっと、日頃の私の業務で目にとまった項目をピックアップしました。その他のアップデートはこちらのリンクでご確認ください。

  • Werkzeug 2.0, Jinja 3.0, Click 8.0, ItsDangerous 2.0 MarkupSafe 2.0へのアップデート
  • Python 3.5以下のversionがサポート対象外に
  • Blueprintのネスト記法対応
  • Routingがよりシンプルにかけるように
  • Configファイルの読み込み方法の変更
  • タイプヒント対応
  • 非同期実装が対応
  • Werkzeug 2.0のmultipart/form-data の改善により、大きいファイルのアップロードが15倍速に

Flask 2.0.1とFlask 1.1.4の書き方の比較確認

まずは、Flask 2.0.1とFlask 1.1.4でのRouting、Blueprint、config.from_json()、async defの書き方について比較しながら確認してきましょう。

全て一つのapp.pyモジュールとして実行できるようになっています。

初めてFlaskにふれる方は、下記のコマンドを実行し、各スクリプトをapp.pyにコピペして挙動を確認してみてください。

$ touch app.py
$ pip install Flask
$ pip install "Flask[async]"
$ export Flask_APP=app.py
$ flask run

Routingの確認

from flask import Flask, Blueprint

app = Flask(__name__)

# 日本語文字化け対策
app.config["JSON_AS_ASCII"] = False

api = Blueprint("api", __name__, url_prefix="/flask")

# flask 2.0.x
@api.get("/v2")
@api.post("/v2")
def flask2():
    return "こちらは、Flask 2.0.1です。"


# flask 1.x
@api.route("/v1", methods=["GET", "POST"])
def flask1():
    return "こちらは、Flask 1.1.4です。"


app.register_blueprint(api)

わざわざ、httpメソッドを引数内に指定する必要がなくなりました。このRoutingの書き方は、Fast APIと同様です。


Nested Blueprintの確認

# flask 2.0.x
from flask import Flask, Blueprint

app = Flask(__name__)

parent_api = Blueprint("api", __name__, url_prefix="/parent")
child_api = Blueprint("api", __name__, url_prefix="/child")


@parent_api.get("/")
def parent_flask2():
    return "Hello Parent Flask 2.0.x"


@child_api.get("/")
def child_flask2():
    return "Hello Child Flask 2.0.x"


parent_api.register_blueprint(child_api)
app.register_blueprint(parent_api)

このアプリケーションを起動してhttp://127.0.0.1:5000/parent/http://127.0.0.1:5000/parent/child/にアクセスしてそれぞれ確認してみてください。 Blueprintは、もともと大きいアプリケーションを開発する際にファイル分割、ディレクトリ分割をして各モジュール間の疎結合状態をたもつための機能でした。

Blueprintのネスト化の対応により、各モジュールを各役割・責務ごとに束ねてルーティングをする際、更にもう1階層上のレイヤーでの各役割・責務ごとに各モジュールを実装し、ルーティングの紐付けがしやすくなりました。

# flask 1.x
from flask import Flask, Blueprint

app = Flask(__name__)

parent_api = Blueprint("api", __name__, url_prefix="/parent")


@parent_api.route("/", methods=["GET", "POST"])
def parent_flask1():
    return "Hello Parent Flask 1.x"


@parent_api.route("/child/", methods=["GET", "POST"])
def child_flask1():
    return "Hello Child Flask 1.x"


app.register_blueprint(parent_api)

flask 1.1.4で同じコードを書いてみると、違いが明らかにわかりますね。

一方で、ただでさえ、Flaskの場合はディレクトリ構成に悩むことが多く、これでさらに考えうるディレクトリ構成のパターンが増え悩むことが増えてきますね。


config.from_json()の確認

...
...
# flask 2.0.x
app.config.from_file("config.json", load=json.load)
app.config.from_file("config.toml", load=toml.load)

# flask 1.x
app.config.from_json("config.json")
...
...

たまに書くことがあり、自分で間違えそうということでメモ感覚で追記しておきます。 ふーん、こうなったんだ、という感じで大丈夫かと思います。


非同期処理の確認

最後に非同期処理の確認です。Python自体は、Python3.5以降からネイティブなコルーチンが実装されており、非同期処理の実装が可能でした。

しかし、Flask 1.xでは、フレームワークとしては対応しておらず、ルーティングにコルーチン関数を書いて実行しても対応していないためエラーがでておわるだけでした。

Flaskが非同期処理に対応していないため、FastAPIを選択した方も多いのではないでしょうか。

それが、今回のFlask2.0へのversion upで対応されました。

色んな書き方があるかとおもいますが、一番わかりやすい書き方でみていきます。

# flask 2.0.x
from flask import Flask, Blueprint
import time
import asyncio


app = Flask(__name__)

async_api = Blueprint("async_api", __name__)


async def async_get_data(name, sec):
    print(f"{name}: started")
    await asyncio.sleep(sec)
    print(f"{name}: finished")
    return f"{name}:{sec}sec"


@async_api.get("/")
async def flask_async():
    start = time.time()

    results = await asyncio.gather(
        async_get_data("TaskA", 1),
        async_get_data("TaskB", 3),
        async_get_data("TaskC", 2),
        async_get_data("TaskD", 1),
        async_get_data("TaskE", 2),
    )
    print(results)
    print(f"process time: {time.time() - start}")
    return "All async tasks are finised !!"


app.register_blueprint(async_api)

出力結果

$ flask run
 * Serving Flask app 'app.py' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
TaskA: started
TaskB: started
TaskC: started
TaskD: started
TaskE: started
TaskA: finished
TaskD: finished
TaskC: finished
TaskE: finished
TaskB: finished
['TaskA:1sec', 'TaskB:3sec', 'TaskC:2sec', 'TaskD:1sec', 'TaskE:2sec']
process time: 3.00414776802063

Task A ~ Eまで順番に非同期で実行され、並列処理されているため、Task A、Task D、TaskC、Task E、Task Bの順番で処理が終わっています。

本来、同期処理で一つ一つ処理が終わるまで待って処理していると、全てのタスクが終了するのに9秒かかります。しかし、上記の非同期実装により、マックスでかかる3秒以内で全てのタスクが終了しています。これで非同期処理の挙動確認ができました。

次は、version間でパフォーマンスの違いがあるかどうかを念の為確認しておきます。

Flask 2.0.xのパフォーマンス確認

日頃は、Python製の負荷テストツールであるLOCUSTを利用していますが、まだFlask 2.0.xに対応していないため、Golang製の負荷テストツールVegetaを利用して軽く負荷をかけてどんなものか比較してみます。

先に結論を言うと、大きな差分はありませんでした。 とりわけ、フレームワーク全体のパフォーマンス改善のアップデート項目が明記されていたわけではなかったので悪くなっていなくてよかったという感じです。

Vegetaの使い方をみながら出力結果をみていきます。

Vegetaは、CLIコマンドで軽く負荷試験を実施するのに便利なのでどんなものか軽く知っておくだけでもいつか役に立つときがくるかもしれません。

では、パフォーマンス確認していきましょう。

f:id:JesseTetsuya:20210707104908j:plain

画像は権利関係上使えなかったので、筆者が手書きしました。ベジータ様を書いたつもりです。

「カカロットォォォ、いくぞーーーー!おりゃーーーー!」

事前準備

*Macを前提にしています。

vegetaのインストール

$ brew update && brew install vegeta
$ go get -u github.com/tsenart/vegeta

下記では、Flask 2.0.1とFlask 1.1.4を比較するために2つのvenv環境が必要になります。 Flask 2.0.1用のディレクトリを作成し、下記のコマンドを実行してvenv環境を作成してください。

$ python3 -m venv venv
$ python3 venv/bin/activate
$ pip install Flask==2.0.1

Flask 1.1.4用のディレクトリを作成し、下記のコマンドを実行してvenv環境を作成してください。

$ python3 -m venv venv
$ python3 venv/bin/activate
$ pip install Flask==1.1.4

Flask 2.0.1の検証用コード準備

from flask import Flask, Blueprint
import asyncio

app = Flask(__name__)
api = Blueprint("api", __name__)

    
@api.get("/flask_v2")
def flask2():
    return "Hello Flask 2.0"

app.register_blueprint(api)

Flask 1.1.4の検証用コード準備

from flask import Flask, Blueprint
import asyncio

app = Flask(__name__)
api = Blueprint("api", __name__)

@api.route("/flask_v1", methods=["GET"])
def flask1():
    return "Hello Flask 1.x"
    

app.register_blueprint(api)

Flask 2.0.1とFlask 1.1.4 のパフォーマンス計測結果

負荷試験実行コマンド*

$ echo "GET http://127.0.0.1:5000/flask_v2" | vegeta attack -rate=500 -duration=5s | tee result.bin

$ echo "GET http://127.0.0.1:5000/flask_v1" | vegeta attack -rate=500 -duration=5s | tee result.bin

*5秒間500RPSで負荷をかけるコマンド (rate: Request Per Second (RPS), s: second)

負荷テスト実行結果レポート確認

レポート作成コマンド

$ vegeta report result.bin

Flask 2.0.1の出力結果

Requests      [total, rate, throughput]         2500, 500.22, 338.90
Duration      [total, attack, wait]             6.81s, 4.998s, 1.812s
Latencies     [min, mean, 50, 90, 95, 99, max]  368.671µs, 369.698ms, 296.596ms, 569.892ms, 798.921ms, 2.191s, 4.202s
Bytes In      [total, mean]                     34620, 13.85
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           92.32%
Status Codes  [code:count]                      0:192  200:2308  
Error Set:

Flask 1.1.4の出力結果

Requests      [total, rate, throughput]         2500, 500.25, 364.51
Duration      [total, attack, wait]             6.581s, 4.997s, 1.584s
Latencies     [min, mean, 50, 90, 95, 99, max]  370.878µs, 296.992ms, 276.982ms, 458.868ms, 595.851ms, 1.038s, 4.02s
Bytes In      [total, mean]                     35985, 14.39
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           95.96%
Status Codes  [code:count]                      0:101  200:2399  
Error Set:

各メトリックスのみかたは、下記。

- Requests
    - total … 全実行回数
    - rate … 秒間の実行回数
- Duration
    - total … 負荷をかけるのに要した時間(attack+wait)
    - attack … 全リクエストを実行するのに要した時間(total - wait)
    - wait … レスポンスを待っている時間
- Latencies … それぞれ、平均、50%、95%、99%パーセンタイル、最大値
- Bytes In/Bytes Out … リクエスト/レスポンスの送受信のバイト数
- Success … リクエストの成功率(なお、200と400がエラーとしてカウントされない)
- Status Codes … ステータスコードのヒストグラム(0は、失敗)
- Error Set … 失敗したリクエストとその内容

「Flask 2.0よ、そんなものか、そんなにかわらんではないかっ、貴様っ!!」とベジータ様が申しております。

負荷テスト実行結果をヒストグラムで確認

さて、次はヒストグラムでみてみます。

ヒストグラムレポート作成コマンド

$ cat result.bin | vegeta report -type='hist[0,100ms,200ms,300ms,400ms,500ms]'

Flask 2.0.1の出力結果

Bucket           #    %       Histogram
[0s,     100ms]  159  6.36%   ####
[100ms,  200ms]  397  15.88%  ###########
[200ms,  300ms]  728  29.12%  #####################
[300ms,  400ms]  505  20.20%  ###############
[400ms,  500ms]  336  13.44%  ##########
[500ms,  +Inf]   375  15.00%  ###########

Flask 1.1.4の出力結果

Bucket           #    %       Histogram
[0s,     100ms]  188  7.52%   #####
[100ms,  200ms]  478  19.12%  ##############
[200ms,  300ms]  976  39.04%  #############################
[300ms,  400ms]  440  17.60%  #############
[400ms,  500ms]  191  7.64%   #####
[500ms,  +Inf]   227  9.08%   ######

「なにっ?!Flask 1.1.4のほうが若干安定してるようにみえるではないか、貴様っ!!」とベジータ様が申しております。

負荷テスト実行結果を時系列グラフで確認

時系列グラフのhtml生成コマンド

$ cat result.bin | vegeta plot > plot.html
$ open plot.html

Flask 2.0.1の出力結果

f:id:JesseTetsuya:20210707110159p:plain

Flask 1.1.4の出力結果

f:id:JesseTetsuya:20210707110225p:plain

「はやくしろっ!!!! 間にあわなくなってもしらんぞーっ!!!」とベジータ様が申しております。

最後に

このようにベジータ様がおっしゃっていますが、実際のところ大きな差分はありませんでした。

Flask 2.0.xへversion upするか否かは、利用したいライブラリが対応しているか否か、非同期実装したいか否か、Blueprintをネストしたいか否か、で決めればいいと思いました。

しかし、まだ確認できていない箇所や気になる項目もあり、全ての項目について当ブログ記事にて書ききることができませんでした。

というわけで、私のFuture Workとして箇条書きにて下記に残しておきます。

  • Fast APIとの書き方の比較をみる
  • 非同期処理のパフォーマンスをFast APIと比較してみる
  • multipart/form-dataの15倍速が本当にそうなったかをみる
  • Vegetaで分散アタックする
  • 今年度、どこかの国のPythonカンファレンスで上記の検証結果まとめを発表します
  • Flask全般の使い方については、チュートリアルを今年度、どこかの媒体でまとめて発表します

データAI部では、PoCとデータをもってくる、というところ以外全部やるPythonエンジニアを募集しています。

全部とは?!が気になる方は、下記URLを確認して頂きカジュアル面談の応募お待ちしています!!

hrmos.co

Flask 2.0.xへのメジャーバージョンアップ関連資料

停滞した開発者ブログを復活させるまで

こんにちは、サーバサイドエンジニアのid:aerealです。

この記事ではClassi開発者ブログ (以後、開発者ブログ) の編集長としてClassi開発者ブログが再始動するまで・再始動してからおよそ半年の振り返りを通して企業の技術ブログ運営の裏側についてお伝えしたいと思います。

開発者ブログ再始動の経緯

Classi開発者ブログは2020年3月の新型コロナウイルスの影響で全国の学校が休校になってどうなったか - Classi開発者ブログを境に2020年10月のClassiで発生した2つの問題を繰り返さないために我々が取り組んでいること - Classi開発者ブログまで記事の投稿がありませんでした。

これはご利用いただいているお客様への情報発信と歩調を揃える必要がありとても難しい状態であったという背景があるにせよ、投稿が一時途絶え寂しい状態であったことは事実です。

2020年8月にClassiに参画した筆者は、このブログをClassiに所属するデベロッパーにとって重要な情報発信の拠点として復興させたいという思いを強く抱き、ブログ再開に向けて動き始めました。

まず、ブログをどのような位置付けとするのか・再開するにあたってステークホルダーは誰でどのようなブロッカーが存在するのか、を明らかにするところから始まります。

位置付けは大きく、採用戦略の第一級の手段として扱うのか・Classiに所属するデベロッパーのアウトプットの拠点として扱うのか、といった方針が考えられます。 ざっくりといえば当面は後者としよう、と決めました。詳細は後述します。

ステークホルダーとブロッカーの整理は、いかにもカロリーが高そうで身構えたくなりますし実際その覚悟をしました。しかし開発者ブログに閑古鳥が鳴いている状況を憂うメンバーばかりで目的意識はスムーズに共有できたので少しの旗振りで動き出すことができました。 その結果、開発者ブログについて以下のことを確認しました。

  • 開発者ブログは開発に関わる情報発信の場とする
    • 顧客コミュニケーションの主たるチャンネルではない
  • 編集部によるレビューを必須とする
  • 広報レビューは可能 (can) であり必須 (must) ではない
    • この判断は編集部に任される

Classi開発者ブログの目指すところ

企業が運営する開発者ブログ・技術ブログの目的としてブランディングや採用活性化などが一般に挙げられます。 筆者自身も、以前所属していた企業で開発者ブログの執筆に関わっていた際にこれらにポジティブな影響があることは実感しています。

一方で、こうしたPRへの寄与はブログの執筆や運用にかなりの熱量と時間を費して得られるものであって、一朝一夕でなるものではありません。 また、採用への寄与やブランディング向上は外部要因が占める割合も小さくなく効果測定が難しいです。 こうした背景により開発者ブログを再興するにあたって目標をブランディングや採用活性化に置くのは実現や評価が難しく頓挫してしまうおそれがあると考えました。

そこで開発者ブログを 「より充実した発信環境を求めるClassiの技術者に向けた執筆の場」 とすることにしました。

これをもう少し分解すると:

  • 読み手にとっての価値より書き手にとっての価値を第一に考える
    • 場を作ったり書くことを継続することに踏み出せないでいるメンバーが主な対象
    • そのために慣れた書き手たちが技術ブログを暖めたり、期待値コントロール、レビュアーとしてサポートする
  • 以下のような点は「できたら良いこと」という扱いにして、当面は第一の目的としない
    • 会社のブランディング・認知向上
    • 採用への貢献

……ということになります。

主要KPIは以下の通りとしています:

  • 執筆者のUU (より多くの人々が書くほど良い)
  • ブログ全体の記事執筆継続率
    • 筆者は問わずブログの記事公開が根付いているか
    • 期間中に書かれた記事数を日数で割る

ただし参考指標としてPVの推移は編集部が責任を持って見ています。 執筆者に対しては積極的にフィードバックしていません。執筆者全員が権限を持っているので本人が望めばはてなブログのアクセス解析機能で参照することができます。

また、以下の指標は当面重視しないことにしています:

  • Twitterやはてなブックマークなどの言及数など、SNSでの反応
    • 上記の通り「会社のブランディング・認知向上」は当面の間、第一の目的としないため重視しない

編集部の運営

以上のような経緯で再開したClassi開発者ブログは、現在この記事を執筆している id:aereal が発起人となった編集部が中心となって運営しています。 編集部はid:sasata299id:tetsuro-ito と発起人となった筆者である id:aereal が務める編集長の計3人からなります。

主な仕事は:

  • 記事のネタと公開スケジュールの管理
  • 執筆された記事のレビュー
  • その他、ブログ運営にまつわる仕事全般
    • 著作権の帰属の整理・明文化
    • ブログホスティングサービスの費用管理 etc.

……です。

記事のネタはAsanaのカンバンで管理しています。一般的なカンバン運用に倣い“Backlog”, “Ready”, “Doing”, “Waiting for review”, “Done”のレーンを作って、現在何待ちなのか一目でわかるようになっています。

Asanaのカンバン。Doingレーンに「開発者ブログ振り返りの記事」。Waiting for reviewレーンは空。Doneレーンに「3月に2020新卒に振り返りを書いてもらう」。
Asanaのキャプチャ

BacklogとReadyを分けているので「とりあえずこういうネタがありそう」というラフなトピックをBacklogに入れておき、具体的に書けそうになったらReadyに移すという運用が可能になり、Slackで湧いたアイデアを取り上げやすくなっていて良いかんじに機能しています。

またレビューが済んで記事の公開準備が整ったらGoogle Calendarに予定を入れます。編集部カレンダーは編集部のSlackチャンネルに流すようにしているので「おっ 今週はこの記事が公開予定だな」と毎日わかるようになっています。

Slackに流れる予定通知のキャプチャ。開発者ブログ編集部カレンダーに設定された「記事公開予定: そーだい塾の記事公開」という予定が通知される。
記事公開予定がSlackに流れる

現在のところ週1くらいのペースなので、ラフに1週間の予定として入れています。 月間ビューだと「この週は記事公開予定がある、この週は空いているので調整しようかな」と一目でわかって便利です。

編集部カレンダーの月間ビュー。「開発者ブログ編集部定例」「記事公開予定: あんずさんの記事」「そーだい塾の記事公開」という予定が入っている。
カレンダーの月間ビュー

これらを毎週15分の定例で確認・最新状態に更新したり声かけする人を見繕ったりします。 アジェンダが決まっており事前に準備をしっかりしておけば15分で十分だろうと見立て、実際に必要十分で延ばそうと話題に上がったことはありません。 15分はなかなかタイトなので事前にアジェンダをしっかり準備しておかないといけない良いプレッシャーがあり編集部一同から好評です。

半年間の振り返り

以上のような経緯で再開したClassi開発者ブログは、現在この記事を執筆している id:aereal が発起人となった編集部が中心となって運営しています。

この記事を執筆している時点では:

  • 執筆者UU: 17人
  • 記事数: 20記事 (1ヶ月あたり約3記事超)

……となっています。

上記からわかるように新しい記事のほとんどを新しい執筆者が書いてくれていることがわかります。 当面は少数の熱量が高いメンバーを中心に記事を執筆していくことになると見立てていたので、これは嬉しい誤算です。

またVPoTのid:nkgt_chkonkとVPoEのid:sasata299がそれぞれ執筆した2020年4月期に発生した2件の問題とそれ以後の体制・改善に関する記事をきっかけにDevelopers Summit 2021へ登壇する機会をいただき全国一斉休校によって教育プラットフォームの「Classi」に起こった大障害 〜サービス・組織をどう変化させたのか〜という発表をさせていただきました。 当面はブランディングへの寄与等は目標に入れないこととしていましたが、早くも結実しつつあること実感し一同で喜びました。 執筆者の二人にとってもプレッシャーのかかる内容だったと思いますが、それを乗り越えて発信してくれたおかげでもあります。

参考:

記事を公開した際には社内のSlackチャンネルで「この人がこういう記事を書いてくれましたよ」という宣伝を私からしていたのですが、その甲斐あってなのかプロダクト開発部以外のメンバーからも「ブログを読んでいるよ」という声を聞く機会がありました。 身近に読者がいることは編集部としても執筆者一同としても励みになります。

編集部の振り返り

最後に先日、編集部で実施した振り返りについて述べます。

再開からおよそ半年が経ったタイミングで「どんなことをやったか」「やったことでどんな影響があったか」「思いがけず起きたことは何か、それを再現しつづける術はあるか」の洗い出しを主眼に置き、より持続的な開発者ブログ運営に繋げるための糧とすべく、YWT (= やった、わかった、Try = 次にやる) 形式で振り返りました。

以下に一部抜粋したものを載せます:

  • やったこと
    • 編集部立ち上げ
    • 毎週15分の定例実施
    • Slack上で記事レビュー
    • チームメンバーへ記事執筆の斡旋
  • わかったこと
    • 推進役がいることの重要性
    • 執筆に意欲的な人が多い
    • 記事のレビューが受けられることによる安心感がある
    • 定例は15分でちょうどいい
  • 次にやること
    • 執筆者のUUを増やしたい
    • 1人あたりの執筆数を増やしてリピーター化したい
    • 振り返りでは概ね「想定以上にうまくいっている」と話題になりました。
    • 開発者ブログ執筆に潜在的に意欲的だったメンバーの存在がとても大きく、うまく掬い上げられたからだろう、と結論付けています。

むすび

Classi開発者ブログは「やったら良さそうなこと」「やるべきこと」の淡々とした積み上げと少しの運に助けられ軌道に乗っているだけということがおわかりいただけたかと思います。

衆目の耳目をさらう開発者ブログは類まれなる情熱と資源と少しの運から成り立っているのだろうと推察します。 それらを一度目指せどもうまくいかず頓挫する企業の開発者ブログも少なくないことを知っています。

しかしClassi開発者ブログは当初立てた目標を果たしつつあります。それは身の丈にあったマイルストーンを設定していること、動機の内在化を図ったからといえます。 ゆくゆくはClassi開発者ブログが大きくなることを夢見ないわけではありませんが、それは今掲げている「より充実した発信環境を求めるClassiの技術者に向けた執筆の場」というスローガンの延長線上に大きくなることがあるからでしょう。

筆者は各企業のユニークな取り組みがより世の中で行き交う世界を望んでいます。 そのためにこうした素朴ながらヒロイックではない開発者ブログのありかたをひとつご紹介し「やってみようかな」と思い立ってくれる方が一人でも増えることを願い筆をとりました。おもしろい記事が世の中に増えれば幸いです。

2021年度新卒研修の一環としてそーだい塾に参加しました

2021年度4月に Classi に入社しエンジニアをしています、北村です。

先日新卒研修の一環としてそーだいさんによるそーだい塾に参加しました。今回はそーだい塾の当日の様子や学んだ内容を振り返りつつ、参加レポートを書きたいと思います。

そーだい塾とは

そーだい塾はそーだいさんが講師を務める勉強会の通称です。 Classi では今年度から、新卒研修の一環としてそーだい塾が開かれることになりました。今回はその記念すべき第一回目でした。

そーだい塾については、すでにそーだいさんご本人が書いてくださった記事もあるので合わせてご覧ください。

当日

今回のそーだい塾は一部と二部の計二回、二日間の日程で行われました。

資料

第一部資料

第二部資料

様子

当日は両日とも zoom ミーティングで行われました。 前半45分がそーだいさんによる講義タイム、後半45分が質疑応答タイムとして進められました。

f:id:liaob88:20210610193254p:plain (第二回の様子)

参加については、新卒研修という名目でしたが特に新卒だけに参加対象を絞る理由はなかったので、自由参加形式をとりました。その結果、当日は多くの先輩方も一緒に参加してくださいました。

このことは、

  • 自分一人だけで参加した場合では生まれなかったであろうわいわい感が生まれた
  • 講義の間に先輩方が自身の考えや補足情報を zoom のチャット機能や slack チャンネルに書き込んでくださり学びが多かった
  • 質疑応答の時間でも、自分には持てなかった思考や視点からの質問やディスカッションが行われ、これも学びが多かった

などなど、当初想定していなかった副次的効果ももたらしてくれました。

例えば以下の写真は、自分が

という質問をした際の slack の様子です。

f:id:liaob88:20210610193331p:plain

(第一回実施中の slack の様子)

自分の質問に対して、そーだいさんだけではなく多くの先輩方も一緒になって考えてくださり、ご自身の意見や補足情報を共有してくださっている様子が伝わるかと思います。

自分一人だけでそーだい塾に参加していた場合、もちろんこのようなことはありえなかったと思いますし、自分の講義内容に対する理解もその分浅いものになっていたと思います。

なので今回このように先輩方と一緒になって講義を聞き、議論し、学ぶことができたことは非常に良かったと思っています。

当日出た質問

ここでは資料には載っていない当日出てきた質問をいくつか紹介したいと思います。

Q1

コードに遊びを持たせるというお話の中で decorator パターンという手法が紹介されていました。しかし経験上、全てがdecorator パターンで書かれたプロダクトはあまり見たことがないです。これは、decorator パターンにもメリットデメリットがある、あるいは何か違う理由が存在しているからなのでしょうか?

A

理由は二つある。

一つ目はもちろん decorator パターンにもメリットデメリットがあること。decorator パターンの全てが正しいわけではない。重要なのは decorator パターンが何を解決したいのかを、他のデザインパターンとの比較を通してしっかりと理解し、用法用量を守って使用していくことである。

二つ目は全てのサービスがシンプルを目指しているわけではないということ。プロダクトによっては、戦略として意図的にイージーな設計をしているものもある。なので「今作っているサービスは一体何を解決したいものなのか?それにはシンプルな設計が良いのか、あるいは例外的にイージーな設計の方が良いのか?」を常に考えて実装していくことが不可欠。

Q2

小さくリリースしていく場合、どういう観点でリリースする順番を決めていきますか?

A

複雑で、影響範囲が広く、元に戻すのに時間がかかるような変更、あるいは、リリース後に得られるフィードバックですぐにその是非を判断できる変更やユーザーから遠い変更ほど先にリリースする。

例えばフロントエンドの変更はユーザーに見えてしまうという理由から、影響は広くユーザに近い変更と判断できるので、比較的リリースは最後になりがち。

Q3

スコープを小さくするという観点でDBが状態を持つことは良くないというお話がありました。その対応方法としてテーブルを分割する方法が紹介されていました。

しかし一方で DB は最小限の数に抑える方がいいというお話もありました。スコープを小さくするという観点で言えば、一つの DB にデータが集中することは良くないことであるように感じましたが、DB は最小限の数にした方が良いとされているのはなぜですか?

A

確かに責務が集中するという観点で言えば 1 つの DB にデータが集まることは良くないと言える。

しかし、DB を管理するという観点で言えば、 1 つの DB のみ面倒を見れば良いという点でスコープが小さいと言えるので最小限の数にした方が良いと言える。

但し、確かに DB にも得意不得意があり、正しい役割分担をさせる必要があるのでバランスをとる必要はある。例えば MySQL は画像の保存は苦手であり、それに関しては S3 などを使用する方が良いと考えられる。

印象に残った話

ここでは特に自分の中で印象的だった内容を二つ紹介していきます。

リリースはソフトウェアエンジニアの価値提供の手段

自分がそーだい塾の中で一番大事だと感じたのが「リリースはソフトウェアエンジニアの価値提供の手段」というお話でした。

これは「ソフトウェアエンジニアは、どんなに良い機能を開発したり良い改善を行って価値を創造したとしても、リリースを行わない限りはその価値をユーザーに届けることはできない。つまりリリースをすることはソフトウェアエンジニアの価値提供の手段である。もし価値をより多く届けたいのなら、より多くのリリースをしていくことが必要である。」という事実を説明しています。

自分はこれまで「開発すること」に自分の意識や時間のほとんどを割いてきました。それゆえリリースをきちんと想定した上での行動を取れたことはほとんどなく、その場限りの対応で乗り切ってきたと思います。コードを書いてある程度満足し、それが自分の仕事であるとさえ考えていたと言われても否定できないです。

なので今回の勉強会を通して、リリースが「ソフトウェアエンジニアの価値提供の手段」として位置付けられて説明されたことは、自分が普段行なっている仕事について改めて考え直す良い機会になりました。

今後は日々リリースすることまでを考えて業務に取り組み、より多くの価値を届けることを意識しようと思えました。

リリースをより多く行なっていくために「小さく、素早く、始める」

「リリースをより多く実施していくためにはどうすれば良いの?」という問いに対する答えとして示されたのがこの「小さく、素早く、始める」というお話だったと思います。

例えば今回の勉強会ではその例として「段階的リリース」という手法が紹介されています。あるリリースを、変更内容によって細分化することで、リリースによる影響範囲をなるべく小さくする方法です。これはバグの早期発見と早期解決という結果にも結びつきやすく、結果的にリリースのスピードが高まり、リリースの数を増やしていけるというメリットがあります。まさに「小さく、素早く、始める」の恩恵を享受できる良い例です。

個人的には「小さく、素早く、始める」という話は、普段の機能開発において、小さな変更でPRを作って行くことで開発スピードやその正確性を上げていくことが良しとされることと似た話だなとも感じました。

「小さく、素早く、始める」というのは UNIX 哲学の中で登場する概念ということも学んだので、自分も早速 UNIX 哲学の本を購入し勉強することにしました。

さいごに

正直自分はこれまでリリースに関して特別に勉強した覚えはなく、開発の最後の1工程という認識しか持てていなかったです。

なので、今回のそーだい塾を通じてリリースの重要性とその実施の手助けとなる多くの手段を学ぶことができたことは非常に良かったと思います。

しかし教えていただいただけでは意味がありません。今回教えていただいた内容を活かすためにも、今後の日々の業務で、できるところから「小さく、素早く、始める」を実践し、より多くのリリースをすることで、 Classi のソフトウェアエンジニアとしてより多くの価値を届けていきたいと思います。

Classiのデータ分析基盤であるソクラテスの紹介

こんにちは、データプラットフォームチームでデータエンジニアをやっている滑川(@tomoyanamekawa)です。
データプラットフォームチームはデータAI部のメンバーで構成されていて、データ分析基盤を中心としたデータ活用に関するシステムに責務を持つチームです。

データAI部が出来てから3年が経ち、データ分析基盤を今の形で運用をして1年半が経過しました。
データエンジニアの採用活動の中でデータ分析基盤を紹介する必要がある一方、説明コストが高く困っていました。
そこで今回は「ソクラテス」と呼んでいる社内のデータ分析基盤について紹介します。
(データAI部ではシステム基盤に哲学者の名前をつける慣習があります。)

続きを読む

高校生にビジュアルシンキングの面白さを伝えたくて意識したこと

はじめまして。Classi株式会社 UXデザイン部でUXデザイナーをしている原田です。

私は2021年3月に北海道旭川東高等学校(以下、旭川東高校)さまにて「描いて思考力を深める『ラクガキ講座』」なる講座の講師を高校生の皆さん向けに開催いたしました。個人的にとても楽しく、充実した時間を過ごすことができたので、今回は私がUXデザイナーとして場の作り方や講師として意識したことをお話します。

講座について

実施した背景と講演レポートはこちらに詳細な内容が掲載されておりますので、ぜひご覧ください。

旭川東高校 「描いて思考力を深める ラクガキ講座」レポート | Classi

意識したことの4項目

振り返ってみると、私はこんなことを意識し実行していました。

1)先生とのヒアリングを通して、やる目的とゴールを明確にする

2)高校生でも気軽に参加しやすいようにハードルを下げる

3)Miroを使ってインタラクティブな仕掛けを用意する

4)偶発性やハプニングを利用し演出する

それぞれどういうことなのかご紹介していきたいと思います。

1)先生とのヒアリングを通して、やる目的とゴールを明確にする

このお話を持ちかけてくださった先生と最初のZoomMTGで「参加いただく生徒にどのようになって欲しいのか」をまず話していただきました。こちらがその当時の一番最初のメモ書きです。

f:id:hhhhhhiroko:20210527200520p:plain

はじめは拙い内容ですがここに想いが凝縮されています。きっかけは「グラフィックレコーディング(以下、グラレコ)の講座をやりませんか?」というお誘いでしたが、ヒアリングをしてみると必ずしもグラレコをやる必要はありませんでした。そこで実施する目的やゴールイメージを対話し丁寧に解きほぐし、ビジュアルシンキングの考えに触れることで生徒の思考力を深める一助になるという思いでアイデアをお互い出し合い、画面投影でNotionをリアルタイムで編集し下記のように「目的」と「ゴール」を設定しました。

f:id:hhhhhhiroko:20210527200459p:plain

2)高校生でも気軽に参加しやすいようにハードルを下げる

目的とゴールが定まったので、次は教室内に掲示する告知チラシの作成になります。

今回の講座は希望者制のため、生徒の皆さんにとって興味のある内容でなければ参加いただくことは到底できません。ソレはコワイ。なので、なるべく多くの方に関心を持っていただけるような工夫をする必要がありました。掲載内容を色々考えてみて、実際に掲示されたチラシがこちらです。

f:id:hhhhhhiroko:20210527200437p:plain

ここで工夫したのは「自分でも大丈夫という意識」と「近所のねえさん感を出す」ということでした。私は学生とも先生とも異なる立場ですが、そういう立場だからこそ学べることがある!と思い気さくな感じを随所に散りばめてみました。その甲斐もあったのか(?)、当日は55名の参加があり、具体的にはこういう箇所で参加ハードルをさげる工夫ができていたかなぁと思います。

  • 講演タイトル「ラクガキ講座」のライトでキャッチーな参加しやすさ
  • はじめてスケッチノートした全然うまくない作品を掲載しておく
  • ノートの記録も掲載し、授業でも役立つかも?の意識づけ
  • 講師紹介の文末に講座と関係ない好きな芸人とおやつの話を出す
  • 持ち物に「お気に入りのペン」を指定し、筆箱の中で完結ができるようにしておく

3)Miroを使ってインタラクティブな仕掛けを用意する

当日はZoomを利用したオンライン開催で実施していました。 黒板やホワイトボードを活用した動きのあるスクライビングができないため、資料をすべてMiroで作成し、PCで画面を共有、iPadで手描きで補足するという行為をしていました。

f:id:hhhhhhiroko:20210527194541p:plain

このように既存の内容に線を引くという行為だけで生徒の皆さんの目を引きますし、なにより私がこのスライドを用いて何を伝えたいのかがより明確になります。異なる場所にいても手描きの線が同期的に出現することによって、この講座の橋渡し的なつなぐ存在になっていたのではないかと個人的には感じます。

f:id:hhhhhhiroko:20210527200152j:plain

4)偶発性やハプニングを利用し演出する

講座のプログラムはもちろん事前に台本のようなタイムスケジュールを用意し進行をしていきますが、偶然の出会いや、その場で初めて遭遇するようなことをうまく活用すると場そのものが停滞せず活性化していきます。講座自体は私が一方的に話すだけではなく、生徒の皆さんも実際にトレーニングとして色々描いてみてそれをグループ内でシェアをするということをしていました。そこで意識していたのは予定調和で進むのではなくその空間にあるものや突発的に出たワーディングを使い臨機応変に行動し、場そのものに動きをもたせることです。例えば、発表する順番を私の気分でいきなり決めたり(ひどい)、生徒の1人が代表で発表する際も、講師が指名するのではなく目にとまった生徒さんから生徒さんに指名いただくといったことをして、適度な緊張感もありつつも、常にドキドキ、わくわくできるように努めていました。

f:id:hhhhhhiroko:20210527200235p:plain

まとめ

このような状況下でもあるため、普段なかなか高校生の皆さんと接する機会もなかったのですが 今回のことを通じて私自身が学ぶことが多くあり、旭川東高校のみなさまには感謝の気持ちでいっぱいです。 開催された日は緊急事態宣言中でもあったためオンライン開催にせざるを得なかったのですが、状況が収束したらリアルに対面をしてパワーアップした講座ができたらいいなぁと思います。

当たり前にリリースしていく ~ 新卒研修編

f:id:Soudai:20210519162123p:plain

当たり前にリリースしていく ~ 新卒研修編

 開発支援部基盤バックエンドチームのみんなの頼れるお兄さん id:Soudai です。Classiでは例年新卒採用を行っており、新卒研修の一環として今年からそーだい塾を行いました。当日の資料はこちらです。

 

 この記事では当日の資料にはない部分で、研修中に触れた内容について解説していきます。

YAGNIを言い訳に使わない

 研修中の最初の質問は「変更に対する可用性をどこまで許容しますか?」でした。 質問を噛み砕くと どこまで設計にこだわりますか? という質問でもありました。結論としてはYAGNIを言い訳に使わず、最大限考え抜くべきとした上で私なりのアドバイスをしました。

いつ設計を諦めるか

 最初から最高の設計が思いつけば問題はありません。しかし、考えがまとまらないときにどこまで考え抜くかは悩ましい問題です。 そこでアドバイスとして次の2つをしました。

  • 先に諦める条件を決める
  • チームを頼るときに質問を工夫しよう

 諦める条件を先に決めてから考えましょう。例えば納期から逆算して時間制限を設ける、特定のドキュメントを読んでみて理解を深めた後に思いつかなかれば諦める、などです。今回の場合、15分間悩んだら相談する、などを例にあげて『最初のうちは悩んでもなかなか答えはでないから早め早めに諦めよう』と伝えました。立場や課題などのケースバイケースではありますが新卒1年目でなかなか答えにたどり着けないのは当たり前のことです。どんどんチームを頼りましょう。

 そしてチームを頼るときは質問の仕方を工夫しましょう。自分がアイディアを持っている場合はそのアイディアを、そもそもアイディアすらない、暗中模索であるならばその状態を正確に伝えましょう。

考え方を変える

 諦めるまでの間、最大限考えても行き詰まることはあります。そんな時は考え方を変えるということが大事です。例えば以下の具体的なアドバイスをしました。

  • 制約から考える
  • 課題の本質から分解する
  • リカバリできる範囲に小さく分解して試してみる

 やりたいことを考えるときに制約から考えることで道筋が見えてくることもあります。 また考えがまとまらない時は認知している課題のスコープが大きすぎたり、本質から遠すぎたりすることもあります。そしてリリースしてみて気付くことも沢山あります。これらの手法は経験によって最適な方法を判断することが多くあるため、まずは早めに相談してチームで考え方の検討から相談するのが良いでしょう。

どうやって手法を選ぶか

 AプランとBプランがあったとき、どっちが良いか?という判断が必要です。ではどうやって選ぶのでしょうか。

  • EASYよりもSIMPLEを選ぼう
  • 使いたい手法が解決する問題のスコープを意識する
  • ロールバックできる範囲でリリースしてみて判断する

 特に3つ目は重要で 結果がすぐわかることはとりあえず試し、結果がわかるまでに時間がかかることは丁寧に判断する ことが肝要ですよっと言う話もしました。

 だからこそ小さく始めたり、解決したいスコープを知るためにも 作る前の段階からサービスに関わる ということも大事ですよと言う話もしました。

如何でしたか

 40分程度の発表に対して50分程度の質疑応答と大盛りあがりのそーだい塾でした。 ここで紹介した話も質疑応答の中のほんの一部ですし、次回のそーだい塾も盛り上がること間違いないでしょう。

 もし、参加したい!と思った方のためにこちらのリンク紹介しておきます*1

www.wantedly.com

参考リンク

shimooka.hateblo.jp

jkondo.hatenablog.com

www.shinryo.com

soudai.hatenablog.com

soudai.hatenablog.com

soudai.hatenablog.com

soudai.hatenablog.com

Worse Is Better - 過去を知り、未来に備える。技術選定の審美眼 2019 edition / Worse Is Better - Understanding the Spiral of Technologies 2019 edition - Speaker Deck

*1:録画あるので入社すると見ることも出来ます

Classi新卒1年目が技術的負債の返済プロジェクトにチームとして関わって感じたこと

自己紹介

こんにちは、Classi株式会社2020新卒エンジニアのid:kiryuanzuです。 以前寄稿した記事ではClassiの部活動の一つであるCTF Cafeについてお話しましたが、今回は2020新卒として印象に残ったことをテーマとして、自分が現在配属している「学習記録チーム」で初めて経験したプロジェクトとチームで日々行なっていることについて紹介したいと思います。

技術的負債を返済するぞ! 〜名付けて「借金返済チーム」〜

7月まであった新卒研修が終わり、学習チームに配属された筆者はチームの先輩と共にECS化に取り組むことになりました。その内容に関しては、自分と同じくECS化に取り組んでいた同期の小川が丁寧に紹介しているのでぜひご一読ください。

tech.classi.jp

10月頃にはリリース作業まで無事終わり、新しい課題に着手する上で新しく学習チーム内で作られた小さなチームに加入することになりました。 そのチーム名は「借金返済チーム」です。名前通り「学習チーム内の技術的負債をどんどん返済していくぞ!」という目的で結成されました。

Classiでは現在、プロダクト再建の試みが沢山行われており、その動きに加われることは大変興味深く、新卒の人間がどこまで貢献できるか心配な面もありましたが、できるだけ頑張っていこうと当時感じたのをよく覚えています。

自分以外のメンバーの中にはClassiのプロダクトを以前から長く触れているシニアエンジニアの方、SoftBank(SB)から出向で来られた新卒3年目の方と未経験中途で入社された2年目の方がいます。私含めた3人はジュニアクラスのエンジニアという比較的若手が多い構成です。

初めての大型プロジェクト

借金返済チームにして加入してすぐ、技術的負債を返済するための大掛かりなタスクをチーム全員で任されることになりました。 スケジュール上の都合として、10月からスタートし12月までに終わらせる方針で動くことになり、まずは調査で判明した膨大なタスク量を切り分けてタスクを消化するための行動を起こすことから始まりました。

具体的には、GitHub Projectsでタスクを一つ一つ切り分けて担当者を決め、カンバン方式で管理するようし、Googleスプレッドシートでバーンダウンチャートを作成して日毎のタスク消化量と目標の消化量を可視化させながら進めるようにしました。そのようにして、チーム内で今課されている膨大なタスクの「見える化」を行うようにしました。

進めていく中で、スケジュール的に不安を感じる時期もあり、その時はチーム外から助っ人を呼んで対応を行いなんとか軌道修正することができました。このような動きができたのも、膨大なタスク量の消化具合を可視化させることで進捗管理する意識が高まったことで、適切な判断を行うことができたのではないかと考えています。

このプロジェクトを経験したことで、チーム開発の中でどのようにしたら効率よく物事を進めていくことができるのかを以前より考えられるようになりました。

今振り返ると、膨大なタスクを地道に消化していく様はまさに地道に借金を返済していく様子と似ていたのではないかと思います。自分含めてチーム内では「借金返済チーム」という名前を大変気に入っていて、「今日も頑張って借金を返済していきましょう!」を合言葉にモチベーションを上げながらみんなでプロジェクトを無事終わらせることができました。

チーム内での活動 〜改名! 学習記録チーム〜

このような大きめのタスクが終わった後、学習チームの中にあるそれぞれのサービスごとに技術的負債を返済していく方針に変わることになり、借金返済チームは、学習チーム全体の技術的負債を返済するチームではなく一番関わることの多かった学習記録サービスを担当するチームとして生まれ変わることになりました。

大型のプロジェクトを経てチームメンバーとの絆は深まりましたが、さらにチームとして切磋琢磨していきたいと考え、様々なアプローチを現在行なっています。 具体的には、毎朝15分の読書タイムを設けて読後に感想を話し合う会や、「Angularまなび会」と題してフロントエンドの技術を勉強する試みをもくもく会形式で週一で行なっています。

また、チームメンバーの気分や調子をできるだけ把握しておこうと決め合って朝会の時に今日のタスクを話した後に自分自身の今の気分を伝え合う試みもしています。

このような取り組みを続ける中で、お互いがしたインプットを共有し合うことでみんなで新たに学びを得ることができたり、チームメンバーのパーソナリティを理解し合おうとする空気が作れているように思います。

まとめ

先ほども述べましたが、みんなで一緒に学んで成長していこうといったような空気があり、新卒という立場としてもこのような環境に身に置いて学べることは大変価値のあることだと感じています。そして、その状況を優しく見守ってアドバイスしてくれる先輩エンジニアの存在もあり良いチームになっていると常々感じています。

4月からはついに入社2年目に突入し、新しい動きや気付きが求められるようになっているのを実感する日々です。今も現チームで業務に励む日々ですが、今後は組織の体制の変化などにより、チーム異動等のイベントで今の環境から大きく変わることもあるかもしれません。そのような様々な状況に出くわす中でも、エンジニアとして成長し続けられるよう頑張っていきたいと思います。

© 2020 Classi Corp.