吉祥寺.pm 18で「はじめてのB2B SaaSデータモデリング」というテーマで発表してきました

f:id:manchose:20190528004926p:plain

5/17に吉祥寺.pm 18のアンカンファレンス枠で登壇させていただきました。
時間の関係で「なぜこのテーマで発表したのか」について詳しく説明できなかったのでその補足をしつつ、吉祥寺pmの感想を書こうとおもいます。

発表資料

speakerdeck.com

そもそも吉祥寺.pmとは

@magnolia_k_さんが 個人(!) で開催されている色々なテックトークが聞ける吉祥寺のテックイベントです。
もとはPerlに関するテックイベントだったようですが、現在はいろんな人がいろんな興味深いテーマで登壇するイベントになっています。

僕も@soudai1025さんや@shinpei0213さんなどの吉祥寺.pmでの登壇資料でよく勉強させていただきました。

kichijojipm.connpass.com

ちなみに今回ははじめてのホール開催だったようで、まるでカンファレンスのような豪華さでテンションがめっちゃあがりました。

B2B SaaSのデータモデリングをテーマにした理由

この記事の本題、登壇テーマの選定理由です。
端的に言うと、B2B SaaS(とくにエンタープライズ向け)を開発する場合、実運用が始まったら後戻りしにくいため、移行コストの高いデータベースの設計には重点的に時間をかけるべきだと考えているからです。

スタートアップはスピードが命、だが死守すべきポイントを見極める必要がある

スタートアップは資金力やモチベーションが尽きぬうちに会社を軌道に乗せる必要があり、スピードが命だとよく言われます。
一方で短期的なスピードを重視しすぎて1年後や2年後に著しい開発スピードの低下を招いたとしたらそれはシステムの敗北だと思います。
つまり、短期的なスピードを重視したとしても犠牲にしてはいけないポイントを見極める必要があります。
ここの見極めこそ創業エンジニアの重要な役割だと考えており、以降で述べる理由により僕はデータモデリングをそのポイントに置きました。

MVPに必要なシステムの品質が高くなりがち

リーンスタートアップの重要性が広く喧伝され、実用最小限の製品(=MVP)を開発し高速に仮説検証を回すというプロセスが一般的になってきました。
もちろんこの考えはとても重要で、我々も営業やモックアプリケーションの開発を行いながら仮説検証を行ってきました。

一方、B2B SaaS(とくにエンタープライズ向けのSaaS)の場合はMVPの水準が高くなる傾向にあります。
理由は以下の2点が大きいと感じています。

  1. 現場の担当者が独断でSaaSのトライアルをすることができず、情シスや上長の承認が必要
  2. 業務に入り込むSaaSであるためトライアルでも業務が回ることを要求される

特に2が我々の意思決定に与えた影響は大きく、 業務が回ること を要求されるということは 実務で使用してもらいフィードバックをもらうためのシステムの品質はある程度高くないといけないということです。

つまり、トライアルといえど実務で運用され始めたらもう後戻りできないと覚悟し、後戻りがしにくい箇所の設計には時間をかけるべきだということです。
※逆にいうと、同じ課題を解決するにしてもB2B SaaSの場合は要求される機能が多くなるため、解決したい課題を絞りに絞る必要がある = MVPの定義がやっぱり重要だよね、ということでもあるのですが。

Vertical SaaSにはより深いドメイン知識が必要

SaaSにはVertical SaaS or Horizontal SaaSという分類があります。

f:id:manchose:20190528003003p:plain

Horizontal SaaSKibelaSalesforceなど業界を問わずに使われるSaaSで、特定の機能に特化していることが多いです。
一方、Vertical SaaSはoct(建設業界特化)やToreta(飲食業界特化)など業界に特化したSaaSであり、ユーザーの同質性が高まるため、より深い課題解決がしやすいという違いがあります。
弊社A1Aのプロダクト(RFQクラウド)はVertical SaaSと分類されるSaaSです。

Vertical SaaSは業界に特化しているため、顧客からの要望もより深く業務に入り込んだものが多いです。
ドメインを深く理解せずに設計したデータベースだと、顧客の業務に合致していないUXになってしまいます。
そのためドメイン知識を手に入れ、適切にデータモデリングに落とし込むことが重要だと考えています。

吉祥寺.pmに参加した感想

こちらの記事をはじめ、参加された方の素晴らしいまとめ記事がありますので、僕からは主催者の@magnolia_k_さんの運営について触れたいと思います。

今回、アンカンファレンスという正式枠ではない枠での登壇になったので、聴衆は少ないかな?と思っていました。
しかし、以下のように@magnolia_k_さんがツイートしていただいたおかげで19時なのに多くの方に集まっていただけました。
時間をかけて準備した資料ではあるので、やっぱり多くの方に聞いていただけるのは嬉しいです。
また、豪華な登壇者に混じって登壇することにビビっていたところもあったのですが、少なくとも主催者の方から期待されていると感じられると「がんばろ」と思えて、モチベーションがあがりました。

このように@magnolia_k_さんの大人力の高さも吉祥寺.pmの魅力だな〜と思いました。
次の次、吉祥寺.pm 20くらいでまた登壇したいなー。

あとbuildersconにCfP出してみたらと言っていただいたので近いうちにCfP出そうと思います!

A1Aは全職種募集してます

A1AでRFQクラウドを開発してくれるエンジニア、デザイナーを募集しております。
少しでもご興味をお持ちいただけた方はご飯いきましょう。(もちろん弊社持ちです)
A1Aに興味がなくても、僕は一応元食べログエンジニアなので、おすすめのご飯ごちそうします!
気軽にDMなりなんなりでご連絡くださいませ。

www.wantedly.com

エンジニア副業Nightで「副業を始めるときにぶち当たった壁とその超え方」というテーマでLTしてきました。

本日、サイボウズ社にて開催されたエンジニア副業Nightに参加してきました。
ちょうど勤務先のSpeeeでも副業申請が可能になったこともあり、今までの自分の副業経験を元に 「副業を始めるときにぶち当たった壁とその超え方」 というテーマでLTしてきました。

engineer-parallel-work.connpass.com

持ち時間が3分で内容をかなり絞っていたので、補足も含めてブログにまとめておきたいと思います。

副業を始めるときにぶち当たった壁とその超え方

副業のモチベーション

僕が副業をやるようになったのは「主業務で成長させにくいスキルを身につけるため」です。(+お小遣い稼ぎをしたかったのもあります)
前職が巨大なメディアの開発エンジニアだったため、幅広い経験を積みやすい環境とはいえず自分のスキルに不安がありました。
そこでインフラを含めてサービスをイチから立ち上げる経験をするためにWebメディアの立ち上げに参画しました。
また、WebエンジニアからAndroidエンジニアに担当が変わったときも同様に、ビルド環境やAndroidアーキテクチャ設計の経験を積むためにAndroidアプリの請負をやりました。

副業の壁とその超え方

1. 希望する仕事が見つからない

いまとなってはまあまあの数の副業をやりましたが、最初はそもそも副業自体が見つからないという壁にぶち当たりました。
この壁は新卒1〜2年目の間は相当に厚く、なかなか打破できませんでした。
クラウドソーシングサービスを使って案件を手に入れるという方法をとることも可能でしたが、副業という関わり方には融通が必要だから請負だと厳しいんじゃないかという思いがあったので自力で探すしかありませんでした。

人に会うたびに手を挙げた

これを打破するためにしたことは、スタートアップ界隈の人との飲みに参加し「RubyRailsAndroidなら開発できますよ!副業やりたいんで案件やエンジニア探している人がいたらぜひ!」と声を上げ続けることでした。
エンジニアを探しているスタートアップはいっぱいあるはずですが、「やりたい」と声をあげてくれるエンジニアが少ないので結果マッチングしないということが起きていると思います。
例えるなら、「異性との出会いがほしい男女は無数にいるがマッチングアプリでは出会いたくないため、お互いマッチングしない」みたいな状況だと思います。これについても僕は声をあげ続けたいと

僕がこれをやりはじめた結果、2ヶ月に1回くらいはそういう引き合わせがあるようになったので、効果は高かったなと思っています。 これに近い内容は@hisaju01さんや@y_okadyさんもおっしゃっていたので、やっぱり愚直にやるしかないよな〜と再確認しました。

2. 納期が不安

副業がOKだったとしても平日の日中は主業務がある方が多いと思います。
そうすると土日 or 平日の夜に仕事をするしかないわけですが、それも主業務の忙しさ次第で取れる時間も変わってきます。そう考えると納期を守れるかが心配になります。

請負をしないことにした

請負は正直きつかったです。。
週1リモートで請負をやりきるのは難易度が非常に高く、正直言うとメインの業務に支障がめっちゃでていました。
でもかっこ悪くてそんなこと言えないので根性で2〜3日徹夜するしかなかったです。
僕が感じた副業で請負をすることの難しさは次の2つです。

1. 要件が詰めきれない

請負の場合、依頼者がプロダクト開発に疎い場合もままあります。
その場合、要求定義や要件定義を積極的に自分でしにいく必要があるのですが、土日だけしか時間が使えないとなるとなかなかそれが進みませんでした。

2. 週一での工数見積りを出しづらくて無理してしまう

せっかく案件が入ってきたなら、それは手放したくありません。
しかし、正直に週一での工数見積をすると「リリース日は1年後ですね!」みたいになってしまいます。
それだと案件が逃げてしまうので、無理をして「平日は2時間はとれるから、土日で16時間とれば3ヶ月でいけますね」みたいな見積もりを出してしまい、結果自分の首を締めてしまいました。

少人数のスタートアップを手伝うことにした

少人数のスタートアップはエンジニアが足りないことが多いので、週一リモートでも歓迎してくれるところはたくさんあります。
実際Findyさんも副業エンジニアでサービス3つをリリースしているそうです。

3. いくらくらい請求すればよいか

これも自社サービスのエンジニアをしていると疎い部分です。

フリーランスのエンジニアに相談する

手を挙げ続けたおかげでフリーランスの知り合いが増えたこともあり、金銭的な面は何度か相談して自分の中での基準を作りました。
このあたりは自分のスキルとその市場感にもよるので一概にはいえませんが、少なくとも 報酬なしで手伝う のはやめたほうがいいと思っています。
理由は簡単で報酬なしだと責任感やコミットが弱くなるからです。

まとめ

他の登壇者の方も、僕が感じていた副業に関するボヤッとした思いに近い思いを持っていることがわかり、「やっぱりみんなそういうところで悩むんやな〜」ってわかったのが非常に大きな収穫でした。
また、Findyさんの例のように副業を受け入れる側の企業のお話を聞けておもしろかったです。
僕が何回も副業に挑戦しながら失敗してきた経験から導き出した考えですので、もっといい働き方や受け入れ方が議論されていくといいですね。

Railsで定数を参照するときは完全修飾名を使った方が良い

発端

とある機能を開発していたところ、以下のエラーが出てwebsocketサーバーが起動しなくなりました。

NameError: uninitialized constant Concerns::XXXable

エラーの発生箇所は、とあるController内の include Concerns::XXXable でした。
Concerns::XXXable 自体は定義してあり、そのあたりの修正もしていなかったため困惑しましたが、どうやら調べていくとquerlyというgemをインストールしてから発生していることがわかりました。
なぜgemをインストールしただけでエラーが発生するようになったか、調査に結構時間がかかったので原因と対策をまとめました。

結論

原因: gem内でトップレベルにConcernsというnamespaceが切られていたこと

ざっくり説明すると、querly内のトップレベルのnamespaceにConcernsというnamespaceが切られていたため、その中で Concerns::XXXable を探そうとして、上記エラーが発生していました。

※すでに当該コードは下記PRで修正されています。

github.com

対策: 定数を参照するときは完全修飾名を使うようにする

標題の通り、 Site::Concerns::XXXable のように完全修飾名を使うことで予期せぬバグは発生しなくなります。
そもそも Concerns::Site::XXXable の方が良くないか?っていう話はいったん置いておきます。

詳しい原因

原因を詳しく説明すると、こんな感じです。

うちのアプリのwebsocketサーバーは、起動時に Rails.application.eager_load! を実行していました。
Rails.application.egaer_load!を実行すると

  1. autoload_pathsを先頭から探索
  2. const_missingが発生したmoduleからトップレベルの名前空間につくまで順に親を探索

の順に定数を探索しにいきます。

querlyをインストールしていない状態で include Concerns::XXXable をしているControllerを読み込むと

  1. Concernsが見つからない
  2. autload_pathsを探索してもConcerns::XXXableが見つからない
  3. 上記Controller内から順に親namespaceを探索し、Site::Concerns::XXXableが見つかる

という風にちゃんと読み込めます。

一方、querlyをインストールしている状態で include Concerns::XXXable をしているControllerを読み込むと Rails起動時にquerlyが読み込まれquerlyのConcernsが読み込まれてしまうため、 上記2の段階でquerlyのConcernsを参照してしまい、定数が見つからないというエラーが発生していました。

(全然わかりやすく説明できない...)

トップレベルにConcernsみたいなnamespaceが切られているgemをインストールしていなくても、Concerns::XXXable を読み込みにいく前に自分でConcerns(Rails標準のapp/controller/concernsなど)を読み込んでしまうと同様のエラーがおきました。

まとめ

定数を参照するときは完全修飾名を使うようにしよう!という話でした。
説明がぜんぜんうまくできませんでした...!

Lean DiagramをWebで書きたい!ので10plateでテンプレート作った

あけましておめでとうございます。
新年早々、初詣もそこそこにサブプロジェクトとして開発中のプロダクトの要件定義やコンセプトメイキングをしていました。
Lean Startupのやり方を参考に進めていたところ、Problem/Solution Fitを達成するために使うLean Diagramというものを知りました。
このLean Diagramの図を作成する良さげなツールがなかったので、10madoさんが最近リリースした10plateを使ってLean Diagramのテンプレートを作ってみました。

10plate.io

Lean Diagramテンプレートの使い方

使い方は至って簡単で、Lean Diagramを開いて、以下のGIFのように右サイドバーのテキストエリアに入力するとLean Diagramの各項目にテキストが入力されます。以上。

f:id:manchose:20180102110254g:plain

10plateを使ってみた感想

1. シンプルで使いやすい

htmlとcssでbodyタグの中身のみ記載してあげることでデザインができあがります。
そこにテンプレート化したい箇所を{{ラベル名}}のように囲ってあげると上記GIFのように右サイドバーに入力欄が表示されます。
特に説明を見ずとも作ることができ、またドキュメントもしっかり用意されているので使いやすかったです。
作ろうと思ってからだいたい1時間で作成できました。

2. テンプレート化する際の表現力が豊富

テンプレート化する際のオプションが豊富に用意されており、デフォルト値を入力しておいたり、プルダウンの選択式にしたりとかゆいところに手が届いています。
APIもすでに用意されているので使い方は色々ありそうです。
詳細はドキュメントをご参照ください。

まとめ

表現力が豊富でAPIも用意されているので応用範囲の広いサービスだなという所感です。
こういうテンプレート作成サービスはキラーコンテンツ(キラーテンプレート)があるとどんどんテンプレートが増えていきそうなので、誰かバズるやつを作ってくれないかな〜って思ってます。
canvacacooは作図ツールなので、こういう人によって図が変わらないものを作ったり共有する場合には不向きです。
そのため、Lean Diagramなどのフレームワークには10plateを使った方が便利だと思いました。

今回作ったテンプレートは高さが伸びたときにデザインが少し崩れたり細部を表現できていなかったりするので、svgcssでデザイン修正をしようと思います。
ソースコードは↓です。普段cssを書かないのでかなり適当です。そのあたり詳しい人レビューコメントとか書いていただけるとめっちゃうれしいです。

github.com

あとはリーンキャンバスのテンプレートなんかもあると便利そうなので作ろうと思います。

AWS WAFで環境毎にCDN(CloudFront+S3)を立てた

この記事は Speeeアドベントカレンダー 18日目 の記事になります。 昨日(一昨日)は、@bino98さんによる、「Slackでヘタクソな日本語/英語の校正をしてくれる「Botlint」を作ってる」でした。

この記事ではAWS WAFを使って環境ごとにCDN環境を構築する方法についてまとめます。

やりたいこと

僕の担当サービスでは、Railsアプリのpublicディレクトリに配置したアセットをnginxで配信するという構成になっていました。

これを速度改善の一環として、S3に配置したアセットをCloudFront経由で配信するという構成に変更することになりました。 そのために行ったことをまとめておきます。

要件

ステージング環境もできるだけ本番環境に近づけるため、ProductionだけでなくStagingとStaging2(ステージング環境が2つある)にもCDNをたてることにしました。
StagingとStaging2には社外からアクセスしてほしくないので自社IPからのみ許可します。
また、S3バケットにURLで直接アクセスすることは制限します。

構成と設定の概要

上記要件を考慮した結果、以下のような構成・設定になりました。

f:id:manchose:20171219105014p:plain

構築の流れ

1. Amazon S3の設定

バケット作成時にアクセス権限を以下の設定にします。

このバケットにパブリック読み取りアクセス権限を付与しない (推奨)

こうすることで、S3のURLでオブジェクトにアクセスすることができなくなります。
全ての環境(Prod/Stg/Stg2)で同じバケットを利用してパスで出し分けることも考えたのですが、以下の理由でバケットをわけることにしました。

  • URLを変更することで別環境のアセットにアクセスできてしまう
  • terraformでバケットを作成する場合、すでにS3バケットが存在してしまうとコケる。

2. AWS WAFの設定

consoleでCreate Web ACLsをクリックするとセットアップウィザードが立ち上がるので、以下の項目を設定していきます。

IP Match Condition

許可/拒否に使うIPアドレスを登録します。
今回は3つIPアドレスを登録しました。

Rules

上記で登録したIPアドレスそれぞれに対して条件式を登録します。
それぞれに以下のRulesを作成しました。

When a request {does} {originate from an IP address in} {IP Match Condition}

これで「リクエストが上記で登録した3つの場合は」という条件式が登録されました。

Whitelist方式かBlacklist方式かを選択

上記で作成したRule(条件式)に当てはまった場合に「アクセスを許可するのか/アクセスを拒否するのか」を選択します。
また、Ruleに当てはまらなかった場合に「アクセス許可するのか/アクセス拒否するのか」も選択します。

3. Distributionの設定

Distributionも環境毎に作成しました。
理由は以下の2つです。

  • 本番にはWAFを設定しないので、少なくとも本番とそれ以外でDistributionをわける必要がある。
  • 1つのDistributionに複数のOriginを設定して送り先を振り分けるのは面倒そう。
Restrict Bucket Access

この設定をすることで、S3へのアクセスはオリジンアクセスアイデンティティからしかできなくなります。
Distributionにオリジンアクセスアイデンティティを作成するという項目が表示されるので、作成して紐付けることでバケットがCloudFrontからのみアクセスを受け付けるようになります。

SSL証明書バージニア北部で発行

ACMSSL証明書を発行して利用する場合、バージニア北部で発行する必要があります。
Asia/Tokyoで発行したものを使おうとしてハマってしまったのでご注意ください。

4. Route53にAliasレコードを追加

Route53にType AでAliasレコード(AWSが独自で作っているもの)を作成する必要があります。
今回でいえば環境ごとのアセットのドメイン(assets.my-domain.jpやassets.stg.my-domain.jp)へのリクエストをDistributionのxxxxxx.cloudfront.netに投げるように設定します。

5. Asset SyncでS3にアセットをアップロード

最後にCapistranoでのデプロイ時にAsset Syncで画像を指定バケットにアップロードするようにすれば準備は完了です。
manifest.jsonファイルはRailsアプリと一緒に配布する必要があるという点には注意する必要があります。
配布を忘れるとdigestの紐付けができなくなり404がでてしまいます。

まとめ

開発を手伝った韻ノート を使ったラップ講座をしようと思っていたのですが、全然まじめな内容になってしまいました。
次こそはラップ講座をしたいと思います。
明日(今日)は @nakana0226 さんで「スポンサー活動を本気でやってみた1年でした」です!お楽しみに!

Bootstrap Night! Vol.2 に参加しました。

12/12(火)に開催されたselfreeさん主催のBootstrap Night! vol.2に参加してきました。

selfree.connpass.com

esa.ioの人の発表の中で

イベント参加後にblog書くのは比較的ハードルが低いし、主催者側うれしいし(略)オススメ

というお話があったのでその気になって参加レポを書いてみます。
(遅れて参加してしまったので主にesa.ioさんの話を書いていきます)

なぜ参加したか

職場の状況が色々かわった結果、プライベートでSaaSを作りたいという熱が高まってきました。
ただ、以下のような不安がありました。

  • 自分ひとり(ないしデザイナーさんとふたり)で運用がまわるのか
  • アウトバウンドの営業をかけないといけないのではないか(平日の日中は動けないのでそれだと厳しい)
  • サーバー費用の負担はきつくなかったか(サービスの性質にもよるが)

とくにesa.ioさんは趣味から始めたサービスだそうなので、このあたりの実際のところどうなの?を聞きたくて参加しました。

esa.io@ken_c_loさんのお話

詳しくは↓をご覧ください。

docs.esa.io

実在する人をイメージし、その人に喜んでもらえるものをつくる

実際に喜んでくれて「これがあることで自分の生活や人生が変わってきている!」と思ってもらえるものをつくるようにする。
逆に、顧客セグメントとかターゲットとかそういう抽象的なものに惑わされて想像上の世界にしかいないユーザーのためのものをつくらないように気をつけている。
というお話をされていました。
この価値観は受託開発として実際にesaを使っている企業で仕事をしていたという点でも一貫していました。
このあたり、競合調査をしていたりマーケティング系の書籍でインプットをしていたりすると知らないうちに忘れていたりするので気をつけたいです。

料金設定にもサービスの哲学が

とくに発表でおもしろかったのが料金設定のお話でした。
esa.ioの料金には◯人まで無料というプランはないのですが、その理由のひとつが

◯人までしかesaのアカウントを渡さないようにする...みたいな使い方が懸念された チームでの情報の透明性を上げるシステムなのに、同じチームでesaの情報を見れる人と見れない人の格差が出てしまうのは本末転倒

というもので、「この料金設定で効果的な使い方をしてくれるか」という観点で料金を決めたそうです。
料金設定もサービスの哲学で決めているという点には驚きましたし、シンプルにかっこいいなって思いました。

懇親会で聞いたこと

CallConnectを運営されているselfreeさんとesa.ioさんに伺いました。
TimeCrowdさんにも伺いたかったのですが、お話をすることができませんでした。

運用はちゃんとまわるのか

運用は問題なかったそうです。
もちろんサービスの性質にもよると思いますが、技術的な問い合わせや個別対応などがけっこうくるのかな?と思っていたので意外でした。

アウトバウンドの営業をかけないといけないのではないか

アウトバウンドの営業は一切していないそうです。 (説明に来てと言われて行くことはあるそうですが)
このあたりは発表でも触れられていて、コミュニティ活動やブログ・イベントでのアウトプットをしているうちに自然に初期ユーザーがついていたそうです。
イベントで発表したら、参加レポで「使ってみた」的な内容を書いてくれた人もいたそうなので、アウトプットはやっぱり重要ですね。(自戒)

サーバー費用の負担はきつくなかったか

ほぼ気にしないでよいレベルだったそうです。

参加して

三社ともスモールチームでSaaSを運営されていて軌道に乗っているサービスなので、非常にためになるお話ばかりでした。
今後もこういうイベントには参加するようにしてアウトプットももっとしていこうと思います。

フォアグラウンドでコマンドを実行した後でバックグラウンドジョブに変更する

バッチの手動実行など終了まで長時間かかるコマンドを実行する場合、 おそらくnohupでSIGHUPを受け付けないようにしてバックグラウンドジョブとして実行すると思います。 しかし、フォアグラウンドジョブとしてコマンドを実行した後で気付くことがたまにあるので、 コマンド実行後にログアウトしても実行されつづけるように変更する方法をメモしておきます。

1. ctrl-z

これでフォアグラウンドのプロセスは一時停止状態になります。 再開するときにはフォアグラウンドでの再開、もしくはバックグラウンドの再開ができます。

2. jobs -s

停止中のジョブ一覧を表示します。 一時停止したコマンドが表示されているので、その一番左の[n]の数字(=Job番号)をメモしておきます。

[1]+  Stopped                 bin/rake -T

3. bg job番号(PIDも可)

上記でメモしたJob番号を引数に指定してバックグラウンドジョブとして再開します。

4. disown %job番号

現在のシェルのジョブテーブルからジョブを削除します。 こうすることでシェルの終了時(ログアウト時)に投げられるSIGHUPを受け取らないようになります。

hオプションを付与してdisown -hと実行した場合は、シェルのジョブテーブルからジョブを削除せずにSIGHUPを受け付けなくすることができます。 このときはnohupと同様の状態になるため、ログアウトするまでは起動中のシェルでジョブを管理できます。

シンプルですが、以上メモでした。