snaqme Engineers Blog

おやつ体験メーカー snaq.me エンジニアによる開発プロダクトブログ

おいしいおやつの定期便snaq.meマイページの変遷

snaq.meでプロダクトマネージャーをしている渡邉(@nabepiyoo)です。

私からは、技術的な詳細というよりはマイページがどのように変わってきて、それがユーザの皆さんのおやつ体験をどうお手伝いできるのかという話をさせていただければと思います。普段はマイページの課題が何なのか、どんなことをすれば喜んでいただけるのか、データを見つつ優先順位をつけながらチーム一丸となって開発を進めています。

マイページというとどういうものを思い浮かべるでしょうか?

住所情報等の変更手続きができたり、月々の支払い情報が確認できたり、お問合せフォームがあるとか、そのようなことを思い浮かべられたのではないかと思います。お菓子はスーパーやコンビニ、百貨店、街の洋菓子屋さん、和菓子屋さんなどなど、いろんなところで買うことができます。おやつを定期便でお届けするビジネスモデルでユーザの皆さんにご満足いただけるよう、2016年の開始当初からマイページは大きく変化してきました。

2016年に公開されたsnaq.meマイページの歴史

2016年3月にスナックミーは初めてsnaq.meというサービスを公開しました。 初回発送に合わせて評価機能を追加

当時、評価機能を初回発送に併せてリリースしています。今と比べるとだいぶ粗いかもしれませんが、当初からユーザの皆さんにご満足いただけているかどうかを測る重要な仕組みがありました。

2017年当時の評価機能
2017年当時の評価機能

そこから何度もバージョンアップを繰り返し、6年経った今、私たちはおやつを評価するだけでなく、お届けしたおやつを見れたり、特に大好きだったおやつを記録することができるおやつ手帳、高く評価したおやつは大袋をリクエストする機能などをご提供しています。これがこの6年で起きた進化です。

2022年に公開したおやつ手帳
2022年に公開したおやつ手帳

届けてほしいに応えるためのリクエスト機能

snaq.meのマイページでたくさんのユーザさんにご利用いただいている機能の1つにリクエスト機能があります。

2016年のリクエスト機能

こちらも2016年からラインナップ※ にあるおやつが並んでいて、届けてほしいおやつに対してリクエストをすることができるようになっています。

2017年のリクエスト機能
2017年のリクエスト機能

こんなによくご利用いただいているのであれば、きちんとしたUIを整備してお使いいただけるようにしようということで何度もバージョンアップしています。

※ snaq.meにはラインナップという概念があります。お届けできる期間が限られるおやつがあるということです。常時70種類ほどのおやつがラインナップに存在しています。

現在のリクエスト機能

そして2022年には、バイヤーイチオシのおやつという特定のおやつをピックアップしたコーナーも出来ました。従来から多くのおやつが並んでいたのですが、それならイチオシのおやつはどれなの?という疑問にお応えするような形になっています。

好き嫌いを考慮してお届けする

ユーザさんとインタビューをしていると、snaq.meを始める前に苦手なものが届いたらどうしようと思っていたというお声をいただくことがあります。

しかし、snaq.meでは2016年からマイページで好き嫌いをご登録いただくことができます。

好き嫌いは人それぞれで、同じようなおやつをお好みの方でも苦手なものは異なっていたりします。

シンプルに嫌いな原料や成分などで登録することができたり、好きなジャンルを設定することができます。

ですからsnaq.meでは毎回8個のおやつを好き嫌いを考慮して、苦手なものが入らないように、なおかつ毎回同じようなものにならないような仕組みでおやつを選んでお届けしています。

ただ、好き嫌いをご登録いただいていない状態だと苦手なものが入ってしまう可能性はあるので、ユーザの皆さんはこの機会に設定されているかどうかご確認いただけると幸いです。

よりおやつに触れたくなるホーム画面へ

2017年のマイページ
2017年当時のマイページ

私たちはこうしてマイページを変化させてきたわけですが、改めて気づいたことがあります。

おやつはデジタルには存在しないということです。お手元に届くまではおやつについて頭の中で変換する必要があります。

ですからホーム画面に来ると何か分かりやすく新しいおやつの情報を知ることができるとか、クリアしていくことでお届けするおやつがより好みになっていくチャレンジができたり、こんなおやつがあったな、また食べたいなと思い出していただけるように表現し始めたのが2022年です。

先ほどお伝えしたような評価やリクエスト、好き嫌い登録といったものはチャレンジに含まれており、これらを行なっていただくことで、自然と好きなものがお届けされるような仕組みになっています。

8月には夏まつりをデジタルでも直営店でも行いまして、期間中にはホームの背景が夏まつり仕様になっていました。

2022年夏まつり期間のマイページ
2022年夏まつり期間のマイページ

2016年当時のマイページから、現在ユーザの皆さんがご覧いただいているマイページはほんの少し綺麗になりました。なるべく直感的に理解でき、子供の頃に待ち遠しかったおやつの時間のように楽しい体験をしてもらえるようなマイページを届けるということを今snaq.meでは実は行っています。

今後も引き続き、おやつに触れたくなるようなマイページを整えていきます。

最後に

そんなスナックミーではもりもりコードを改善し、開発していきたいエンジニアを募集中です。 採用の最新情報はこちらにありますので、ご興味ある方はご確認ください!

engineers.snaq.me

何でも良いので話したいよという方はカジュアル面談でお話ししましょう😄 meety.net

ユーザーに最適なおやつを届ける「アサイン」の歴史

こんにちは、スナックミーでデータ周りを担当するエンジニアをしている加藤です。 スナックミーでは、ユーザーのみなさんにお届けするおやつを様々な情報をもとに選定することをアサインと呼んでいます。

過去にもこのアサインに関する記事がいくつか書かれていますが、今回は少し俯瞰した目線で、その遍歴とこれからについて書いていこうかなと思います。

おやつを選定する「アサイン」

スナックミーでは、ユーザーごとに適したいくつかのおやつを選定してお届けするサービスを展開していますが、この「適したおやつ」を見つけるためには非常に多くの情報を扱う必要があります。

  • 好きなおやつは入れてほしいし、嫌いなおやつは入れないでほしい
  • できれば避けたい成分は入っていてほしく無い
  • リクエストしたおやつは入れてほしい
  • 過去に食べて苦手だったものは避けたい

などなど。 こういった情報から適したおやつを選定するためのアルゴリズムを弊社では独自に開発しており、これをアサイアルゴリズムと呼んでいます。

現在進行形でこのアルゴリズムは進化し続けており、より適したおやつ選定となるように開発が続けられています。

手作業によるアサイ

概要のところで多くの情報を扱う必要があることを書きましたが、サービス開始当初はなんと手作業にてアサインが行われていました。ユーザーのデータを1件ずつ確認し、どういう商品が良いか、どういう組み合わせが良いかを決めていくという非常に属人性が高くかつ時間のかかる作業だったわけです。

仮に1人1分で終えたとしても、ユーザーが500人もいれば8時間以上もかかる作業になります。 現在はありがたいことにさらにユーザー規模が拡大しているため、手作業で行おうとすればとてもじゃないですが1日では終わらないですね・・・。

アサイアルゴリズムのシステム化

当初から急務としてアサインのシステム化が進められ、およそサービス開始3ヶ月にしてシステムのβ版が導入されました。

その後も改良が続けられ、最終的にはAWS Batch上で動くシステムによっておよそ2000人のユーザーへのアサインが5~10分程度で完了するようになりました。

フルリプレイスとアーキテクチャの更新

しかしユーザー数のさらなる増加への対処や、度重なる改修による開発困難性の増加もあり、アサイアルゴリズムのフルリプレイスが行われました。

アーキテクチャからコードの構成まで議論を重ねて考え直し、1月ほどかけてリプレイスを実施しました。

結果的にはアルゴリズムの速度も開発容易性も増加し、現在もアサイン周りの改良を続けていますが、これまでよりも少ない工数で実装できていると感じています。

このアサインの改善については以下の記事で詳しく紹介しています。 labs.snaq.me

また、現在のアサインのアーキテクチャについてはこちらの記事も見てみてください。 labs.snaq.me

「おやつ得点」の導入

上記のリプレイスと合わせてもう一つ大きな動きがあり、それが「おやつ得点」という考え方の導入です。 詳細なアルゴリズムを紹介することはできませんが、ユーザーにとってより最適なおやつをを探すために、データベース上に大量に蓄積されているユーザーの過去のアクション (評価、リクエストなど) データから、ユーザーごとに各おやつの好みの度合いをスコア化し、これをアサインに導入しました。

こうしたML的なアルゴリズムとの融合によっても、ユーザーによりよいおやつをお届けすることを目指しています。

今後に向けて

ここまでざっくりとアサインの遍歴をたどってきましたが、アサインを大まかに表すと

  • 上限の決まった箱 (snaq.meであれば8つのおやつを選定します)
  • いくつかの情報を持ったユーザー
  • いくつかの情報を持ったおやつ
  • 好みなど満たすべき制約条件

が登場する最適化問題を解くことに他なりません。

ただし、その満たすべき条件は非常に多岐に渡ります。

  • 嫌いなもの・苦手なものが入らないこと
  • 好きを優先しつつ、バランスよく入ること
    • いくらクッキーが好きでも、クッキーばかり8つは流石にがっかりしますよね。
  • ユーザー1人1人の最適化と全ユーザーの最適化を同時に満たすこと
    • ある人は好きなものばかり、でも別のある人は嫌いなものばかりではいけません。
  • 好みの優先と在庫の消化を調和させること
    • 好きなおやつを入れることが最優先ですが、現実問題、在庫の多少を無視することはできません。

などなど・・・

今あげたものの他にもいくつかの条件が存在します。これをどんな方法でどれくらいの時間で解くかがアサインなわけです。

その解法は画期的なアルゴリズムかもしれませんし、MLをメインに据えた方法かもしれません。いずれにしろアサインはまだまだ完成形ではなく、日々改良を続けて行く必要があります。

近い将来、さらなる発展を遂げたアサインはどんな形になっているでしょうか? もしかすると人間には理解できない無数のパラメータによって管理されている未来もあるかもしれません。 とにかくワクワクが止まりませんね!

最後に

今回はスナックミーにおけるアサインの遍歴について紹介しました。 過去幾度もユーザーの声やデータをもとに改善が続けられ、色々な課題を解決してきたアサインですが、まだまだ課題は山積みです。

ユーザーにどんなおやつをお届けするかは弊社のサービスの根幹にあたる部分なので、今後も常にどんな形が良いかを考え改善していきたいと思います。

また、そんなアルゴリズムを一緒に開発してくれるとエンジニアをスナックミーでは募集中です!ぜひお待ちしています!

meety.net

engineers.snaq.me

FastAPI schemaの考え方

こんにちは
今回はFastAPIのschemaの考え方を書いていきたいと思います。

FastAPIではpydantic を採用しており、pydanticを利用してschema設定していきます。 今回でいうschemaはDBのschemaではなく、I/Oのリクエスト・レスポンスのschemaの考え方になります。

schemaを考えていくときに押さえていきたいこと

型の設定

昨今、型設定できない開発は開発スピード、不具合の原因、心理的安全性など多方面で良いことがないです。(型に縛られすぎるのも本末転倒なのでほどほどに) そのため、型設定できる方法があるのであれば、導入を考えたい。pythonでは mypy がありますので、mypyを用いて型のチェックをしていきます。

mypyは、プログラムの実行前に型チェックを行います。この型チェックはPythonの型ヒントという記法で書かれた情報をもとにしているので、型ヒントがない場合は、チェックがほぼ行われないので注意が必要です。

$ pip install mypy
box: int = 1
snaqme_price: dict[str, int] = {"box_fee": 1880, "delivery_fee": 330, "discount": 1000}

def total_delivery_fee(box: int) -> int:
    return snaqme_price["delivery_fee"] * box

このような int型やdict型などはさまざまなパターンで肩を決めることが可能です。 mypyに関しては別途詳しく書いていきます

ドメイン

schema管理する上でこのドメインで考えていくことが他の2つより大事だと考えています。 例えば、ユーザー情報を考えていく場合

・ 名前
・ メールアドレス
・ 住所
・ 電話番号
・ 決済情報
・ 生年月日
・ 好きなこと
・ 嫌いなこと
など

上記のような属性があった場合

ユーザーbase
・ 名前

ユーザー基本情報
・ メールアドレス
・ 住所
・ 電話番号
・ 決済情報
・ 生年月日

ユーザーの嗜好
・ 好きなこと
・ 嫌いなこと
など

のように分けられるかと思います。 この分け方が絶対と言いたいわけではなく、ユーザー情報という何も考えず一つのスキーマとして管理し続けるとどんな情報が入っているのかぱっと見わからなくなってしまいます。ユーザー情報 としている部分も将来的には分割が必要になり、肥大化されていく予想がつきます。 そのために細かく分けすぎず、大きく分けすぎずが良いと考えています。データベースと違い、FastAPIのレスポンスなどのスキーマは変更しやすいので、気軽にスタートできるのも特徴の一つになります。

レスポンスとリクエストの区別

スキーマとして管理できているがレスポンスとリクエストを根本から分けていくことで開発を進めやすくなると思います。ここで疑問になってくるのが、レスポンスとリクエストに関して同様なスキーマが存在しする場合どうするか?というところです。

私の考えでは無理に共通化させない、レスポンスはレスポンス、リクエストはリクエストと分ける。同様なものを共通化することは大事になってくる部分は多いと思いますが、レスポンスとリクエストに関しては共通化することで動作に不具合が生じさせる一つの原因になりかねない可能性があると考えています。
理由としては、レスポンスとリクエストは思いのほかコンテキストスイッチが発生しうるためです。コンテキストスイッチが発生する時にスキーマを考えていく場合どうしても片方のスキーマの存在を忘れがちになると思います。

なので、極力共通化しなくても良いと考えています。

まとめ

ざっくりではありますが、FastAPIでのschemaの考え方を共有させていただきました。 schemaの考え方に正解は存在しませんが、アンチパターンは存在しています。アンチパータンは開発スピードや心理的安全性を高められないため、アンチパターンが存在していると思っています。 いろんなschemaの考え方があると思いますので、ぜひ、いろんな方のスキーマの考え方を共有してもらえるとありがたく思います。

弊社では 「おやつと、世界を面白く。」 一緒に面白くしたい仲間をお待ちしてます!!

meety.net

engineers.snaq.me

FastAPI 独自でエラーハンドリングを設定する方法

こんにちは スナックミー CTO の三好 (@miyoshihayato) です
FastAPIを開発するときに独自でエラーハンドリングを設定する方法を書いていきたいと思います。

FastAPIの採用背景は以下をご覧ください

labs.snaq.me

リプレイスの場合、エラーハンドリングはリプレイス前の仕様と同じにする必要があり、少なからずレスポンスはコントロールできる状態が必要でした。リプレイスではなく初めからFastAPIを採用したという方でも、日本語に対応できるのだろうか?エラーコードってどういう仕様なのだろうか?などカスタムしたくなるシーンは出てくると思います。

FastAPIのデフォルトのエラーハンドリング

パスがない場合

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}

Request URL http://127.0.0.1/hello_world

Response body

{
    "detail": "Not Found"
}

パラメータ不足時のエラー

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str = Form(..., title="名前", description="名前")
    price: float = Form(..., title="金額", description="金額")
    is_snaqme: bool = None


@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.post("/items/{item_id}")
def update_item(item_id: int, item: Item):
    return {"item_name": item.name, "item_id": item_id}

Request URL http://127.0.0.1/items/1

Request body

{
    "name": "パイナップル",
    "is_snaqme": true
}

price の値が必要だがrequest bodyに入れない場合

Response body

{
    "detail": [{
        "loc": [
            "body",
            "price"
        ],
        "msg": "field required",
        "type": "value_error.missing"
    }]
}

バリデーションエラー

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
def read_items(q: Union[str, None] = Query(default=None, max_length=5)):
    results = {"items": [{"item_id": "snaqme"}, {"item_id": "otumame"}]}
    if q:
        results.update({"q": q})
    return results

q は最大文字数5文字のところ6文字で入れた場合

Request URL http://127.0.0.1/items?q=123456

Response body

{
    "detail": [{
        "loc": [
            "query",
            "q"
        ],
        "msg": "ensure this value has at most 5 characters",
        "type": "value_error.any_str.max_length",
        "ctx": {
            "limit_value": 5
        }
    }]
}

このような Response body になっています。detail 直下に loc (エラー箇所), msg (メッセージ), type (エラータイプ) で構成されています。バリデーション設定によりその他のエラーメッセージも表示されています。

一方で以下のような

{
    "error": {
        "code": 401,
        "message": "ログイン失敗しました"
    }
}

のようにエラーコードとメッセージだけ出したい場合どのように行うとよいのでしょうか?

独自定義したExceptionを利用

import traceback
from fastapi import Request, Response, status
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

class CustomException(Exception):
    """カスタム例外"""

    # デフォルトを401エラーとする
    default_status_code = status.HTTP_401_UNAUTHORIZED

    def __init__(
        self,
        msg: str,
        status_code: int = default_status_code,
    ) -> None:
        self.status_code = status_code
        self.detail = {"error": {"code": status_code, "message": msg}}


class SystemException(Exception):
    """システム例外"""

    def __init__(self, e: Exception) -> None:
        self.exc = e
        self.stack_trace = traceback.format_exc()
        self.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
        self.detail = {
            "error": {
                "code": status.HTTP_500_INTERNAL_SERVER_ERROR,
                "message": "システムエラーが発生しました。",
            }
        }


class HttpRequestMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next) -> Response:
        try:
            response: Response = await call_next(request)
        except CustomException as ce:
            # カスタム例外
            response = JSONResponse(ce.detail, status_code=ce.status_code)
        except Exception as e:
            se = SystemException(e)  # カスタムシステム例外に変換
            response = JSONResponse(se.detail, status_code=se.status_code)

        return response

CustomException のようにさまざまなパターンで例外処理を作成することが可能です。
呼び出し方は以下のようになります

from config.middleware import CustomException, HttpRequestMiddleware
from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=5)):
    if q != "snaq.me":
        rails CustomException(msg=f"not {q} but snaq.me")
    return {"Hello": "World"}

app.add_middleware(HttpRequestMiddleware)

まとめ

このようにエラーハンドリングを自由にカスタムできチームとして思い通りのエラーハンドリングの参考になれば幸いです。 またエラーハンドリングはレスポンスに特化しましたが、バリデーションもFastAPI既存の仕様に従うかどうかでやり方が変わってきます。 次回以降もFastAPIで培ってきたこと発信します。

弊社では 「おやつと、世界を面白く。」 一緒に面白くしたい仲間をお待ちしてます!!

meety.net

engineers.snaq.me

スナックミーのエンジニアチームで直近動きたいこと (2022年7月)

こんにちは スナックミー CTO の三好 (@miyoshihayato) です
四半期ごとにチームに共有している直近動いていきたいことを一部変えてこちらのエンジニアブログに載せたいと思います。

DBに残らないデータの活用

今回チームにとってしっかり取り組んでいきたいテーマがこちらになります。
弊社は商品開発・ユーザーさんに届ける商品の選定・製造/出荷効率 (リパック以外に自社でおやつも作ってます)など大事な役割が多く存在しています。これらの質の向上や施策の成功確度を高めるためにデータの活用は必須です。
今までも上記に関係するデータを活用し商品開発などは行なっていますが、活用しきれない・できないデータがいくつもありました。昨年から少しずつデータ基盤を構築していき今まで気づけなかったファクトに気づけ、捉えることができなった事象の把握・施策の実施をできるようになってきています。 (メンバーに感謝です)。
このデータ基盤は既存のDBをベースに整えてきてきました。次なるステップとして、DBに保存されないデータをしっかり活用していける基盤を作ることになります。アクセスログ・滞在時間・ヒートマップなど様々ありますが、スナックミーだからできることを考えて構築していきたいと考えています。

例えば、CRMについて。CRMは施策に対して効果があったかどうかというのはメールなどの open rateやclick rate が一般的になるかと思います。
確かに、open rateやclick rateは大事で悪いというわけではありません。ここで伝えたいことはその先で存在する意図したアクションをしてくれているのかどうなのかというのは実は分かりません。
施策を打ったが実は解約に繋がっていた、クリックしたのに何もせず直帰した、評価率向上に寄与したかったがリクエストの数も一緒に増えたなどネガティブ面もあれば、ポジティブな面の発見を総合的に判断できるようになります。

スナックミーだからできるアプローチを考え実行していきたいと考えています。

内製の強化 (+ 内製必要ないところの脱却)

2022年7月現在 社員 7名 + 業務委託 5名 計12名で開発をしているのですが、いくつか内製して開発しています。

  • 商品管理 (原料の発注〜出庫、製造、パックの数)
  • 商品の自動選定・出荷作業 (音声ピッキング)
  • ユーザーさんが利用するマイページ (サブスク、ECのような仕組み)
  • メールやLINEをセグメント配信するCRM
  • データ基盤

など. 必要に応じて開発をし、どの機能もスナックミーをワークさせるために大きな役割を果たしています。
ここでいう内製の強化というのは、上記システムの更なるアップデートになります
0 -> 1で作ったシステムや 1 -> 10 としてきたシステム様々ありますが、1 -> 10, 100 にするべきものはスケールアップしていき、 0 -> 1  で開発したがSaasなどで代用し他の開発に注力できる場合は置き換える。
また、1 -> 10 にもできるが、100 にするにはいつもよりパワーが必要で他が疎かになる可能性があるものを見極める
今いるメンバーの開発を最大限に活かし、会社, 個としてインパクトを最大化できる観点を持ち続け開発に取り組みたいと考えています。

他チームとスクラム

スナックミーはいろんなチームが存在するためいい意味でそれぞれ独立して仕事に取り組んでいます。ですが、他のチームとエンジニアが組むことで新しいシナジーを生み出せています。(マイページによるユーザー体験からオペレーションの製造・出荷まで)
数年前はできなかった(うまくワークしなかった)取り組みを今はできるようになってきたりしているので、より一層力を入れていきたいと考えています。
おやつを製造してユーザーさんにお届けするサービスを展開しているスナックミーは エンジニアだけではいいシステムは作れず、他のチームが存在しスクラムを組むことで1 + 1 が2以上の力を発揮できるようになります。
そのため、他チームとスクラムすることは今までやってこなかったわけではなく、より一層大事にしたい & 以前までのチーム目標の表現方法を少し変えているだけで、「他チームとスクラム」は根幹であると考えています。

まとめ

今年も半分が過ぎ、個として、チームとして、会社としてできる幅は日を重ねるごとに大きくなっています。その中で今回3つ大事にしたいことを書きました。いつもと変わらない姿勢もあれば、今まで培ってきたことをさらに飛躍させるための取り組みもあります。
一つ壁を乗り越えると新しい景色 + 新しい壁が現れます。常にこの繰り返しかもしれません。しかしチームと乗り越えた壁をチームと一緒に見る新しい景色を見続けたいので、少しでも興味持った方はぜひお話ししたいです。

おやつと、世界を面白く。」 一緒に面白くしたい仲間をお待ちしてます!!

meety.net

engineers.snaq.me

FastAPI 採用経緯

こんにちは スナックミー CTO の三好 (@miyoshihayato) です
スナックミーは昨年からPHP -> FastAPIにReplaceをすすめ、すでに多くのAPIがFastAPIで本番稼働しています。

ここではなぜReplaceしようと考えたのか、FastAPIを採用したのかなどを記載させていただければと思います。

Replaceしようと考えた理由

  • 高学習コスト (メジャーではないPHPフレームワークの利用のため)
  • コードの質 (スピードを意識しすぎて コードの質を担保できなくなった 当時は 速 ≠ 質 と勘違い )
  • 心理的安全性 (テスト含めリファクタの難易度が高い)

などの理由からReplaceを決断し様々なフレームワークを調査検討し始めました

高学習コスト

メジャーではないPHPフレームワークの利用のため、新しいメンバーが入る時に

のため学習コストが高くなる可能性があったため

では、なぜPHPフレームワークを最初に導入したのかというと、立ち上げ当初は慣れたアセットで行う方がプロダクトが検証しやすいなどの理由から上記のフレームワークを導入しました。

コードの質

立ち上げ当初は1週間単位で方向性・仕様が変わる(市場検証など)ため、ひとまず動くものを作り続けていました。そのため、設計に大きな時間を使わず1に開発、2に開発、3に開発するという状況でした。

またスピードを重視するあまり、質のことをほぼ考えておらず、中長期にスピード低下する危険性などは意識していませんでした。

心理的安全性

実際のコードはスパゲッティコード状態、テストの記入なし、変更の影響範囲が分かりにくいなどから改修・追加開発をするときに難易度が高く、何か不具合があったときに問題解決に時間がかかることが予想してました。 というのもこのPHPのコードは私だけ把握するようにし、理由はPHPのReplaceは決まっている & このコードを理解するより他で稼働しているシステムを理解し追加・改修等に集中するためです。Replace促進するためあえての属人化にしてました。

Bule/Greenデプロイを導入してあった程度で、それ以外はこの開発をする際の心理的安全性は決して高いものではありませんでした。

FastAPIを採用した理由

  • 他のメンバーが学習コスト低くスタートできる
  • APIに特化したい (Railsのような多機能なFWはいらない)
  • パフォーマンスに優れている (レスポンス速度や処理)

などの理由からFastAPIにしました

他のメンバーが学習コスト低くスタートできる

昨今Rust, Go などを使った開発が主流になりつつありますが、

  • 誰も(しっかり)経験したことない言語
  • APIをReplaceするというプロジェクトであること

などの場合、学習コストをあまり当てたくないという理由でRust, Goは対象からなくしました。 その中で候補になった言語は、弊社で本番稼働しているPython, Ruby, JS, (PHP) となり、その中でもFastAPIの自由度の高さがありました。(シンプルでソースコード量が少なく、規制を弊社仕様にカスタムしながら開発できる) 自由度が高い = 複雑さが増す可能性がある ということですが、API開発という観点 & 開発する前に指針を概ね定めるという観点でカバーすると決めました。

※ 新規PJなどではしっかり用途にあった技術選定を行いますので、Rust, Goなど関係なく現時点の最善な選定をします。

APIに特化したい (view開発は求めてない)

今回はAPIのReplaceなので、API以外の機能は特にいくら優れていても加点はほぼないと割り切りました。

パフォーマンスに優れている

APIに関してはパフォーマンスの高さが大事だと考えており、ここでいうパフォーマンスとは

  • レスポンス速度
  • 高リクエストも捌ける

他の後押しポイント

  • FastAPIは2018年にスタートしとしたが github の stars 46.7k
    • (flask (2010年〜) -> 59.4k, django (2005年〜) -> 64.9k) ということで近年の中で勢いがある
  • mypy と Pydanticを組み合わせることで型対応も可能
  • Swagger が標準装備してあり、APIのドキュメント管理が自動化 (実はここほぼで決定打)

など

まとめ

いろんな角度から考察した上で、FastAPIを採用し 現在カバレッジは90%以上確保していたり、かなり安定した運用・開発ができReplaceに関しては今の所うまく行っていると考えています。 今後はFastAPIのドキュメントがまだまだ少ないというのが現状なので、弊社で培ってきた考え方(エラーハンドリング・Pydanticでのバリデーション・schemaなど)を定期的に共有していきたいと思っております。

弊社では 「おやつと、世界を面白く。」 一緒に面白くしたい仲間をお待ちしてます!!

meety.net

engineers.snaq.me

スナックミーのエンジニアチームで直近動きたいこと (2022年4月)

こんにちは スナックミー CTO の三好 (@miyoshihayato) です
四半期ごとにチームに共有している直近動いていきたいことを一部変えてこちらのエンジニアブログに載せたいと思います。

エンジニアのバリュー

S: Scale Out
N: Next
A: Agile
Q: Quality

今年から弊社はVPoEを設け CTOである私が技術面にフォーカスし、VPoE が組織面にフォーカスする体制になりました。 この背景や行っていることは別途紹介させていただければと思っています。 VPoEを作る前からエンジニアのバリューを作成したいとずっと思っており、これを機にバリューを作成しました。
ちなみにスナックミー全社では以下がバリューになってます。

[おもしろい!]
難易度や工数じゃなく「それっておもしろい?」を軸に“ワクワクファースト”で行動します。
おもしろいほうがやる気が出るし、お客様に喜んでいただけるから。

[やってみよ!]
ごちゃごちゃ言ってないで、“オーナーシップ”を持ってまずやってみよう。
[つぎにいかそう!]
失敗したっていい。学びがあれば儲けもの。
失敗を恐れずどんどんチャレンジしていきましょう。みんな”やさしい”から失敗したって大丈夫だよ!

を頭文字にしたバリューです。 snaqme.com

バリューは覚えやすく自分的に気に入っています。 ですが、このバリューは全社に向けてバリューであり、エンジニアの目線で行くと少し昇華する必要があると感じていました。理由としてエンジニアにとって少しイメージしずらいものにどうしてもなっていたりします。例えば「おもしろい」これは人によって何が面白いのかバラバラです。バラバラというのが問題なのではなく、必ずしもユーザーさんと直接紐づいていないケースがあります。SREだと仲間のエンジニアのパフォーマンス最大化のために動くこと多く、製造や出荷などの社内システムは社員やパートさんが利用するために開発するケースが多いです。最終的にはユーザーさんに紐づきますが、少し感覚距離が遠いことろにいます。
ということから「」をベースにエンジニア向けに昇華させたバリューを作りました。
以下がバリューになりsnaq.meの「SNAQ」それぞれの頭文字にしたバリューにしました

Scale Out

エンジニアリングで事業成長を引っ張っていけるような開発をしていこう。その為にもスケールしやすい仕組みの開発を心がけよう。

Next

日々の挑戦の中で失敗をしてしまった時にでも、次への一歩が踏み出しやすい仕組みづくりを心がけよう。壊しやすい仕組みだったり、最速で開発・改善していけるような環境を持ち続けよう。

Agile

小さく早くスピード感を持って、継続的に改善を積み重ねていこう。

Quality

ユーザーにとって一番良いものを作っていこう。


  • スピードを重視したいが、短期的なスピードではなく中長期的なスピードも重視したい
  • 如何に小さく始められ
  • 失敗しても次に活かせるような仕組み
  • 速さだけでなくシステムの質も忘れない

このあたりをイメージしやすいようなバリューにしてます。

詳しいことは以下に

note.com

グロースハック

バリューを作ったので、バリューを意識した動きにしていきたいですが、もう一つテーマを決めたいなと思い共有しました。
それがグロースハックです。なぜ、グロースハックにしたのかというと、エンジニアは言われたものを開発することは一つの価値ですが、エンジニア自身で価値を社内・社外に提供できるような存在になっていきたいと思っているからです。相互の目線で一つの価値を2倍にも3倍にもすることが可能であり必要だと思っています。どっちが上ではなくどんな価値が最大化されるのかエンジニア自身が考えを共有し結果ユーザーさんの満足度が上がったり、インパクトが上がるもの作り上げていきたいと思っています。

まとめ

今回はエンジニアのバリュー(Scale Out Next Agile Quality )はVPoEが筆頭に考えを共有し一つ形にしましたので、どんどん浸透させていきたいと思います。
また、このバリューに共感でき方はぜひ一度カジュアルに話しませんか? 実際どう体現させているのか、どんなことをしているのかなどお話しします。 おやつと、世界を面白く。」 一緒に面白くしたい仲間をお待ちしてます!!

meety.net

engineers.snaq.me