株式会社スタメンのブログ - TECH PLAY

TECH PLAY

株式会社スタメン

株式会社スタメン の技術ブログ

248

こんにちは、スタメンの松谷です。 弊社は「TUNAG」という 社内SNS を提供しています。TUNAGではアプリケーション フレームワーク として、 Ruby on Rails を使用しています。TUNAGの主要機能に Facebook のニュースフィードに該当する「タイムライン」があり、社員同士のコミュニケーションや、会社からのお知らせが共有されます。 タイムラインに投稿が蓄積されるにつれ、過去の投稿を振り返りたいというニーズが増えたので、 全文検索 (Elasticsearch)を導入して検索を可能にしました。 導入に際して、 Rails から Elasticsearch を扱う方法をまとめました。これから Rails にElasticsearchを導入しようとしている方の参考になれば幸いです。 TL;DR 概要 Ruby on Rails で作られた TUNAG に Elasticsearch で、 全文検索 を導入した。 対象読者 これから Rails に作られたサービスに Elasticsearch を導入しようとしている方 動作環境 Ruby on Rails 5.1.6 Amazon Elasticsearch Service (Elasticsearch 6.0) なぜ Elasticsearchか? 以下の理由でElasticseachを選択しました。 形態素解析 や n-gram など 自然言語 的な解析をサービスに合わせて選択することができる ノードを増やすことで簡単にスケールアウトができる 検索が高速でパフォーマンスが高い 検索クエリの表現力が高い インデックスを複数もつことでマルチテナント対応が可能 スコアリングして検索順位のチューニングが可能 利用したgem 今回 Rails で 全文検索 を実現する上で以下の2つのgemを利用しました。 elasticsearch-model elasticsearch-dsl elasticsearch-model は、 Rails の MVC の「モデル」とElasticsearchの統合を簡素化することを目的としています。 このgemのモジュールを検索対象のクラスにincludeすることにより、 ActiveRecord を継承したモデルに対応するデータをインデックスにインポートする機能が用意されるなど、Elasticsearchに関連する機能が拡張されます。 elasticsearch- dsl は、Elasticsearchに対するクエリの作成と実行のサポートを目的としたgemです。表現豊かなElasticsearchのクエリを Ruby の DSL で感覚的に構築することができます。 運用上気にしたこと TUNAG のクライアント企業様毎にインデックスを分けてデータの分離を実現 サービス公開後でも、サービス停止することなくインデックスの変更を実現 実装の詳細 以下で Rails にElasticsearchを導入した際の全体的な流れを紹介していきます。 検索対象モデル 「タイムライン」は複数のモデルから構成されています。一部を紹介すると、 タイムラインへ投稿したユーザ(User) 投稿フィード(Feed) 投稿フィードに対するコメント(Comment) 投稿に関連するデータ(例: 利用した社内制度 Menu) などが存在します。 全文検索 では、これらのモデルのデータを検索対象とし、横断的に走査してキーワードに合致した投稿(Feed)を取得します。 # == Schema Information # Table name: feeds # # id :integer not null, primary key # content :text(65535) # status :integer default("active"), not null class Feed has_many :comments belongs_to :user belongs_to :menu end データのインポート ActiveRecord ::Base を継承したモデルに、elasticsearch-model の Elasticsearch::Model::Importing を Mix-In することで、 インスタンス のデータを Elasticsearch::Model::Importing#import で、Elasticsearch に インポートすることができます。 TUNAG では、Feed に関連するアソシエーションを含めてインデックスにインポートしたいので、 Elasticsearch::Model::Importing#import で中で、 JSON にシリライズする際に呼ばれる Elasticsearch::Model::Serializing#as_indexed_json をオーバーライドしています。 下記は、Feedクラスがincludeするモジュール Serchable を定義しています。(実際の実装を省略しています。) module Searchable extend ActiveSupport :: Concern <200b> included do include Elasticsearch :: Model <200b> def as_indexed_json (options = {}) as_json( only : [ :content , :status ], include : { comments : { only : [ :content ] }, menu : { only : [ :title ] }, user : { only : [ :name ] }, }) end end end Feed .first.as_indexed_json # => { # "content" => "お疲れ様でした!来週は新入社員歓迎会をします!", # "status" => "active", # "comments" => [ # { "content" => "参加します!" }, # { "content" => "最近会ってないですね。来週楽しみです。" }, # { "content" => "お疲れさまです!参加予定です。" } # ], # "menu" => { "title" => "イベント出欠" }, # "user" => { "name" => "松谷 勇史朗" }, # } インデックスの更新 初回のインデックスへのインポートが完了した後も、新しい投稿が発生したり、既存の投稿が編集されたタイミングで、該当のインデックスを更新させる必要があります。 ActiveRecord ::Base を利用したモデルの場合、after_commitコールバックを使用してインデックスの更新を行うことができます。TUNAGでは、ActiveJobを使用して、非同期でインデックス操作を行っています。 module Searchable extend ActiveSupport :: Concern <200b> included do after_commit ->(record) { Elasticsearch :: IndexerJob .perform_later( ' index ' , record.id, ElasticsearchIndex .alias_name(record.company))}, on : :create after_commit ->(record) { Elasticsearch :: IndexerJob .perform_later( ' index ' , record.id, ElasticsearchIndex .alias_name(record.company))}, on : :update after_commit ->(record) { Elasticsearch :: IndexerJob .perform_later( ' delete ' , record.id, ElasticsearchIndex .alias_name(record.company))}, on : :destroy end end class Elasticsearch :: IndexerJob < ApplicationJob def perform (operation, record_id, index_name) logger.debug [operation, " ID: #{ record_id }" ] case operation when / index / record = :: Feed .find(record_id) Elasticsearch :: Model .client.index index : index_name, type : ' feed ' , id : record.id, body : record.__elasticsearch__.as_indexed_json when / delete / Elasticsearch :: Model .client.delete index : index_name, type : ' feed ' , id : record_id else raise ArgumentError , " Unknown operation ' #{ operation } ' " end end end マッピング の定義 マッピング とは、Elasticsearch の インデックスにおいて、ドキュメントをどのような構造で表現するかを定義することです。 具体的には、フィールドとその型、Analyzerなどを設定します。 Elasticsearch::Model::Indexing.settings メソッドで設定することができます。 ここではFeed、Comment、Menu、Userの4つのテーブルそれぞれのフィールドに、それぞれAnalyzerとしてkuromojiを設定しています。 module Searchable extend ActiveSupport :: Concern included do settings index : { number_of_shards : 1 , number_of_replicas : 0 } do mappings dynamic : ' false ' do indexes :content , analyzer : ' kuromoji_analyzer ' indexes :status , type : ' keyword ' indexes :comments do indexes :content , analyzer : ' kuromoji_analyzer ' end indexes :menu do indexes :title , analyzer : ' kuromoji_analyzer ' end indexes :user do indexes :name , analyzer : ' kuromoji_analyzer ' end end end end end 複数テーブルの横断的検索 elasticsearch- dsl を使ってクエリを構築します。ElasticsearchのBoolクエリを使えば複雑な検索も可能になりますBoolクエリは以下の4種類あります。 must: AND条件 filter: AND条件 should: OR条件 must_not: NOT条件 例として、 (statusがactiveなFeed) && (FeedのcontentフィールドまたはCommnetのcontentフィールドまたはMenuのtitileフィールドまたはUserのnameフィールドにキーワードが含まれる)の条件をelasticsearch- dsl で構築しました。 multi_matchクエリは、複数フィールドへのマッチを許可するクエリです。Boolクエリを用いて「投稿したユーザから検索(例: 自分の投稿)」、「投稿したユーザの属性(例: 所属部署)による検索」など、複雑な検索を実現することができます。 このように、Elasticsearch側で 関連する複数のモデルを対象とした、複雑な検索が実現できるため、 Rails の Controller 等で検索結果をさらに絞り込む必要性が無く、シンプルな実装と高いパフォーマンスを得ることができます。 Elasticsearch :: DSL :: Search .search do sort do by :created_at , order : ' desc ' # 新しい順 end query do bool do must do term ' status ' : ' active ' # 有効なフィード end must do multi_match do query keyword fields %w( content comments.content menu.title user.name ) operator ' and ' end end end end end マルチテナント対応 Elasticsearchではインデックスを複数もつことが可能です。TUNAGでは、データ分離のためクライアント企業毎にインデックスを分割しており、検索結果に他クライアント企業の情報が紛れ込む可能性はありません。 class Feed < ApplicationRecord include Searchable belongs_to :company # クライアント企業毎にインデックス名を割り振る index_name = " company_ #{ company.id }" end インデックスの無停止再構築 サービスを運営していると、機能の仕様変更などで、モデルとElasticsearch インデックスの マッピング 定義の変更や、検索対象フィールドの変更が発生し、インデックスを再構築する必要性がでてきます。この場合、インデックスを0から再構築するのに時間もかかりますし、インデックス切替の度にメンテナンスをすることもユーザに迷惑をかけるため、サービスへの影響を与えずに、手軽にインデックスを更新することが求められます。 TUNAGでは、 Index Aliases and Zero Downtime | Elasticsearch: The Definitive Guide [2.x] | Elastic を参考にして、Elasticsearch が提供する Index Aliases 機能を用いて、無停止でのインデックスの再構築を行っています。 Index Aliases は、Elasticsearch の インデックスに対し、 エイリアス (別名)をつける機能です。具体的な手順としては下記となります。 現在有効なインデックス(company_01_feed_20180420) への エイリアス として company_01_feed_current を作成しておき、各種コードからは company_01_feed_current を参照するようにしておきます。 新しいインデックスを構築する際は、company_01_feed_20180423 を作成し、データインポート後を行った後、company_01_feed_current の参照先を company_01_feed_20180420 から company_01_feed_20180423 へ切り替えます。アプリケーションから参照している エイリアス 名は同じですが、Elasticsearchの内部的にはAliasが指しているインデックスは異なるという点を活かしています。 切り替え後に、古いインデックス(company_01_feed_20180420) を削除して、メンテナンス完了です。 エイリアス の切り替えは、 Elasticsearch::API::Indices::Actions#update_aliases を利用しており、update_aliases は、Elasticsearch側でアトミックに処理されることが保証されているため、安全に切り替えを行うことができます。 また、TUNAG では、ElasticsearchIndex という ActiveRecord ::Baseを継承したモデルを作成し、インデックスを管理しています。 namespace ' elasticsearch:feed:rebuild ' do desc ' 企業毎にインデックスを新たに作成し、古いインデックスから切り替える ' task feed : :environment do Company .all.each do | company | ElasticsearchIndex .rebuild!(company) end end end class ElasticsearchIndex < ApplicationRecord include Elasticsearch :: Model def self . rebuild! ( company :, model :) next_index = create!( model : model, company : company, rebuild_at : Time .current) next_index.create_index next_index.import if current_exists?(company) current_index = current(company) switch(current_index, next_index) current_index.delete_index else next_index.add_aliases end next_index.active! end def self . switch (current_index, next_index) raise ArgumentError " Not allowed switch index other company index " if current_index.company != next_index.company __elasticsearch__.client.indices.update_aliases body : { actions : [ { remove : { index : current_index.name, alias : alias_name(current_index.company)}}, { add : { index : next_index.name, alias : alias_name(current_index.company)}} ] } end def self . current (company) company.elasticsearch_indices.active.last end def current_exists? (company) current(company).present? && __elasticsearch__.index_exists?( index : current(company).name) end def create_index self .class.__elasticsearch__.create_index!( force : true , index : name) end def import company.feeds.import( force : true , index : name) end end 終わりに 今回は、Elasticsearchを Rails アプリで実運用したときの事例を紹介しました。elasticsearch-modelで、 ActiveRecord とElasticsearchの連携がスムーズに実現し、elasticsearch- dsl でElasticsearhの機能を Ruby で簡単に操作できてとても開発効率が向上しました。今後は、スコアリング、アナライザの選定、検索結果のハイライトなど未だ使っていない機能の調査をし、サービスの質を向上させてTUNAGをもっと良くしていこうと思います! スタメンでは一緒に働くメンバーも募集しております!是非 Wantedly をご覧ください。
こんにちは、スタメンの小林です。 登壇から少し時間が経ってしまいましたが、2018/03/10 に スタメンオフィスにて、開催された「 第3回名古屋若手Webエンジニア交流会 」で講演した際の資料を公開します。当日は、名古屋を中心に活躍する若手エンジニアがたくさん集まってくれました。 少しでも参考になればと思い、赤裸々に自分の若い頃を振り返ってみましたが、少しでも参考なる方が増えればと思い、こちらでも紹介させていただきます。 https://speakerdeck.com/lifework/di-3hui-ming-gu-wu-ruo-shou-webensiniajiao-liu-hui-20180310 キャリアパスついて 資料の中の、技術、人事、事業の3軸は、以前キャリアパスを相談されたときに、大きく分けての3つの軸があると図示したことに起因しています。 技術は、もちろん プログラミング、セキュリティ、インフラなどの各種技術でのスキルアップを意味します。最初はアサインされたチームの担当エンジニアとして何らかの技術スキルを伸ばすところから始まる方がほとんどだと思います。 人事は、採用・教育・評価に代表されるチームのマネジメントです。30歳前後でチームを任されて、徐々に技術から離れるキャリアを選ぶ方も多いし、逆に技術にこだわりを持ってチームマネジメントよりも、現場での経験を嗜好する方もいます。会社の状況的に選択の余地が無い場合もありますが、エンジニアは転職によってキャリアチェンジすることが可能で、他の業界よりも意識的にキャリアを選ぶことができると思います。 事業は、企画・ディレクション・予算管理などの領域です。エンジニアとしてプロダクトを創っていると、UIや仕様にアイデアが反映されることもありますし、それが続くと新機能の要件定義やプロダクトのロードマップを任されることになり、事業よりの仕事が増えることがあります。エンジニア経験があることは、社内外のエンジニアとコミュニケーションが取れたり、世の中の技術的なトレンドを把握できるなど、事業を企画・運営する上で大いに役立ちます。いつしか、企画方面が主になり、コードを書かなくなるエンジニアも多いと思います。 私は、20代のエンジニアから始まり、30代でエンジニアチームのマネジメント(いわゆる VPoE)を経験することで人事と事業の経験を深め、40代はスタメンのCTOとして技術面を伸ばしています。20代はもっと事業よりのキャリアを嗜好してきましたが、実際に事業方面の経験を積む中で、自身の適正・興味が技術+人事にあることに気づき、40代直前にスタメンにCTOとして参加するキャリアにつながっています。 経験したからこそ、自分の適正や嗜好に気づくことも多いと思います。食べ物の好き嫌いは食べてみないとわからないように、キャリアや価値観は、試行錯誤する中で、明確化していきます。特に若い内は自分の可能性を広げ、適正に気づくためにも、広く経験してみるのが良いと思います。 ITエンジニアは、非常に歴史の浅い職業です。もしかして、定年退職した人も数えるぐらいしかいないのではないでしょうか。このため、キャリアパスについても確固たる方向性があるわけではありませんし、むしろ技術の多様化と深化が進むにつれて、様々なキャリアが生まれていくのだと思います。資料に書いた内容も、こういったキャリアを歩むべきだという教科書ではなくて、「こういった考え方を持った人もいるんだ」的な話のネタとして、キャリアについて迷うエンジニアに読んでいただき、参考になれば嬉しいです。 20代について 謎 の ウニ と ナマコ のイラストがありますが、資料だけではわかりにくいと思うので補足させてください。 この業界で活躍している人たちを観察してみると、皆さん若い頃に頭角を現して、抜擢されて、成果を出して、さらにチャンスを得て成長ペースを加速させていく・・・ という方が多いと思います。 若い頃に頭角を表しているときは、頑張って成果をだしているからこそ(多めに)自信を持っていたり、事業や技術に当事者意識が高いからこそ他のメンバーへの要求水準が高かったりして、言動が尖っていて、他のメンバーとぶつかることも多いかと思います。でも、立場が変わって組織や事業の責任者になるにつれて、リーダーシップやフォローワーシップを学び、自分の成果からチームの成果へ意識が移っていき、様々な経験を積むことによって(中身の熱意は変わらないまま)言動が丸くなっていくのではないでしょうか。 これって、はじめ尖っていたウニが、成果を出して立場が変わる中で荒波に揉まれ成熟していく(トゲの使い方がうまくなっていく)感じがしていて、 ウニ + ナマコ みたいな例えになりました。逆に、はじめ丸く小さいナマコは、あとで尖ることはないんだと思うんです。だからこそ、チャンスが多く生まれるIT業界では、多くの経験を積んで抜擢されてチャンスを得るためにも、生意気なぐらい尖って成果を出したほうが良いと思っています。 皆さんの周囲にいる尊敬する先輩たちは、20代のころ間違いなく、尖っていたと思います。はじめから成熟している人なんて、(ほとんど)いないはずなので、若いうちは尖って行きましょう。 20代に本とコードをたくさん読み、多くの偉大な先輩方に出会ったことが、自分自身の視野/視座を上げ、後に大きく活きました。毎日、検索しながら仕事をしていると思いますが、情報収集する際は一次情報を意識して読むことが、効率的に理解を深められると思います。GitHub リポジトリの README や Rails ガイド 、 AWS ドキュメント などの 公式ドキュメント はもちろんですが、ソースコードが最も正確、最新な一次情報源です。ガンガン読みましょう。 逆に、Stack Overflow や Qiita など、部分的な情報は二次情報といえます。二次情報は、正確性に欠け、場当たり的で、古くも更新されていません。二次情報は問題解決のきっかけとして利用し、常に一次情報(本家の正式なドキュメント)にアクセスするようにしましょう。 また、本は一つの分野・トピックを知るには非常に効率的です。いろんな本を読むと、本を選ぶ選球眼も身につき、さらに効率的に本から情報収集できるようになります。学生の頃、所属していた研究室の教授から「なんでもいいから月に1万円本を読め」と教えられ、新卒から数年ぐらい続けました。また、尊敬する人、すごいと思った人にオススメの本を教えてもらい、とりあえず買って見る。ということも続けています。本は、専門家が知識をまとめたものです。こんなに効率的な学習方法は無いと思います。自分の視野を広げるため、知識を深めるために、たくさん読書しましょう。 新卒で入った会社の先輩が、打ち上げでごちそうになってお礼をした際、「恩は、俺に返すんじゃなくて、後輩に同じことをしてあげて返してね」とおっしゃったことをよく覚えていて、今でも実践してます。多くの尊敬する先輩に出会い、先輩のようなエンジニアになりたい、あんな仕事がしたいと思えたことが、今につながる原点になっています。 仕事観、人生観は20代の前半で形成されるとも言われます。前述したような、私の習慣や考え方は、20代の前半で得た物が多いです。20代は自分の可能性を広げられるように、尖るほど行動し、本や人から多くのことを貪欲に学ぶのをオススメします。 30代について 30代は、ほんとに多くのことを学びました。 まず、挫折について。誰しも大小の差はあると思いますが、挫折すると思います。でも、挫折するような環境だから成長もするし、成功へ近づくのだと思う。挫折から自分の適正を認識することもるし、必ずしも努力と根性で挫折を乗り越える必要はないと思う。大事なのは、挫折から学ぶこと。 プレゼン資料中の「楽しむことをあきらめない。」というフレーズは、2006年春のモスバーガーの採用キャッチコピーです。初めての転職直後に、心身ともにしんどかったときに、たまたまオフィス近くのモスバーガーで、このコピーに出会えたことは運が良かった。このコピーをきっかけに、吹っ切れたのをよく覚えています。ベンチャーでの仕事って、挑戦的な試行錯誤も多いし、うまくいかないことが多いと思います。 どんなときも「楽しむことをあきらめない。」ように、前向きに捉えることはすごく大切です。このコピーに出会ってから10年以上立ちますが、今では自身の人生観としてしっかり根付いています。 昔読んだ日経ビジネスのインタビュー記事の中で、「20代は自分のために仕事をし、30代は組織のために仕事をし、40代は業界のために仕事をし、50代は世のために仕事をしないさい」というような言葉がありました。 今思うに、20代の頃は自分のために仕事をしていました。 30代は、チームで仕事をすることの強さ、やりがい、楽しさを実感したことが大きかったです。また、チームをマネジメントする中で、人を観察し、加点法で捉えて、長所を見つけ、長所を活かしたアサインをすることで、チーム全体の成果を最大化することを学びました。 私は、マネージャーとして「 触媒 」でありたいと思っています。少量の触媒が、化学変化を劇的に加速・最大化することがあります。私がマネージャーとしてうまく振る舞うことで、触媒のようにチームが有機的に連携して、成果を最大化できたらな。。。。と工夫する日々です。 30代はホントいろいろなことが起きました。 CTOと主夫を両立するエンジニアが名古屋で創業するまで に書いたとおり、前半34歳までは自分でイメージしていた人生でしたが、35歳以降は毎年想定外でした。でも、周囲を見ていると、30代は内容こそ違えど、良くも悪くも想定外の出来事が起きるのだと思います。転職して環境が変わったり、自分自身の役割が変わったり、結婚や出産などで家庭環境が変わったり、人生の変数が増えるため、想定外の出来事も起きやすくなりますよね。 正直しんどいと思える日々もたくさんありましたが、多くの出来事や出会いから自身の価値観や多様性を理解できたのは大きかったと思っています。皆さんも30代はいろいろな変化が訪れると思いますが、起きた変化は変えられないので、ぜひ前向きに捉えて未来に活かしてください(投げやりですみません... 40代について いま、41歳なので、40代はまだ始まりなのですが、CTOとしてスタメンの創業に参加したことで、これまでで一番新鮮な仕事をしています。「塗り絵 と違う 白いキャンパス の緊張感。」と資料に書きました。これは、前任者が始めたことを維持・拡張していくのと、創始者としてゼロから始めるのは、こんなに違うのだと実感したことです。未来を考えて、方向性を決めること。決めた方向性が会社や事業の未来に大きく影響すること。でも、怖くても、決めないと、前に進めないこと。(絵は描かないんだけど)白いキャンパスに、最初の線を引くような緊張感&ワクワクは、創業以来今でも続いてます。 また、私が一番コードをたくさん書いたのは、スタメンを創業してからの1年の39-40歳です。Web業界には、エンジニアの35歳定年説は無いと思います。むしろ、これまでの経験が武器になるし、まだまだ成長できると思います。35歳は熟成なんです。ただし、エンジニアとして、知的好奇心や成長意欲を失わないこと、経験にすがって過去の実績に油断したり、老害にならないことは、意識したいと思っています。詳細は、 スタメンを創業して1年半の振り返りとこれから でも振り返っていますので、よろしかったらこちらも。 皆さんもご自身の10年前、20年前を振り返ってみて、もっと大人になっていると思っていたのに、、、と思われるかもしれませんが、私も同様で、20代に頃に思っていた40歳よりも、実際の自分は全然大人ではありません。スーツをピシッと着こなしてるかと思ったら、Tシャツ、フリース、パーカーばっかり着てます。 でも、思っていたよりずっと熱意は失ってないし、むしろできることが増えて、やりたいこと、チャレンジしたいことが増えています。実際、2017年は20代の頃よりもたくさんコードを書きましたし、創業から1年半経っても、スタメンでやりたいことは全く尽きません。10年後に「よくやった!」と自分を褒められるように、40代も頑張ってみようと思います。 最後に この業界は非常に変化が激しく、今日の当たり前もあっという間に、普通ではなくなります。同様に、理想とするキャリアも変わっていきますし、現在は想定できない新しいキャリア、仕事もできていると思います。 とはいえ、未来のキャリアは過去と現在の自分の延長にあるものです。資料にある通り、仕事をする上での価値観を知って、自身の強みを把握し、その上で成果を出してキャリアを自ら選択できるように準備しておく。それでも、準備した通りに行かないのが人生なのでしょうが、準備していないと環境の変化に翻弄されてしまうかもしれません(それが悪いとも言い切れないけど)。 未来に突然変異は起きない、未来は徐々に作るもの、だからこそ思い通りに行かないかもしれないけど、自分の軸に沿って未来を描いて、行動してみましょう。10年後に、いい仕事したなぁ。と思えるように。 最後に、実際に小林に会って、もっと詳細を聞きたいという方がいらっしゃったら、ぜひ、 Wantedly などから、ご連絡ください。スタメンのオフィスで、まったりお話ししましょう ♪
こんにちは。スタメンでデザイナー兼フロントエンドエンジニアをしているナガキです。 以前、 スタメンの開発環境について お話させていただきましたが、デザイナー兼フロントエンドエンジニアという立場から、スタメンではどのように TUNAG をデザイン、開発しているかご紹介させていただきます。 デザイン制作 大きな機能開発が必要な場合、まずは手書きのワイヤーフレームで必要な要素や画面遷移、機能を確認します。その後、実際にデザインデータを作成しますが、この時に主に使用するソフトは、 Sketch Adobe Photoshop Adobe Illustrator の3つです。 Photoshop は画像の加工に、Illustrator はアイコンパーツの作成や印刷物の作成に使用していますが、メインに使うのは Sketch です。 Sketch + Zeplin TUNAG は Web 版とアプリ版があり、Web 版には PC とスマホが、アプリ版には iOS と Android があるため、全部で4パターンのデザインが必要になります。共通のパーツを使いまわしながら、解像度に依存しない UI デザインを作るには Sketch が最適です。 Sketch で作成した UI デザインを Zeplin に取り込み、セールスやディレクター、エンジニアを交えて仕様とデザインの確認を行います。スタメンには名古屋と東京に拠点がありますが、物理的な距離や立場を気にせずディスカッションができるため、Sketch + Zeplin でのデザイン制作はオススメです。 デザインカンプを見ながら気になった部分にコメントし、細かい仕様を確認することで、アウトプットのイメージが統一できます。 フロントエンド開発 細かな仕様とデザインが固まったら、いよいよ実装。 TUNAG のフロントエンドは、 HTML (ERB) Sass (SCSS) JavaScript jQuery React で実装しています。 Web システムの開発現場では、マークアップエンジニアが静的な HTML を先に用意し、それを後からバックエンドのエンジニアが動的なデータに差し替えていく、という流れが多いように思いますが、スタメンではどちらかというと逆。 (フロントエンドエンジニアのリソースが足りていないということもありますが…)バックエンドのエンジニアがロジックを書き、ビュー側もある程度作り上げたものを、フロントエンドエンジニアが微調整する、という開発フローが多いように感じます。 さらに、小さな機能追加・改修であれば、各エンジニアがバックエンドからフロントエンドまですべての開発を担当します。 この場合のフロントエンド開発は、前項のようなデザイン制作過程を飛ばし、既存機能のデザインや使い勝手をベースに、カスタマイズして作り上げます。見た目や使い勝手は、 Pull Request の段階で確認し、必要に応じて修正します。 このように小規模なフロントエンド開発を積み重ねることで、デザインなどに苦手意識の強いエンジニアであっても、着実にフロントエンドの開発スキルが身に付きます。 また、フロントエンドエンジニアもバックエンドのロジックやコードを意識する必要があるため、お互いが非常に近い環境で開発を進めることができます。 Sass (SCSS) スタイルは BEM (MindBEMding) の命名規則に従って SCSS で記述しています。 親セレクタを参照する " & " を利用することで、非常にスマートでシンプルな書き方ができるため重宝しています。 .block { & __ element { // ... } &--modifier { // ... } } 現在は Sprockets でコンパイルしていますが、Webpack (Webpacker) でのビルド環境も整ってきていますし、PostCSS も一般化しつつあるので、このあたりの環境も見直したいと考えています。 JavaScript スタメンの開発環境について でふれた通り、 TUNAG の JavaScript は jQuery を利用したものがほとんどで、一部機能に限って React で実装しています。 ほとんどのコードが jQuery に依存しており、さらに ES6 (ES2015) 以降のシンタックスに対応していないのがネックだと感じていましたが、最近、とある追加機能開発のために新たな JavaScript のビルド環境を構築しました。 現在そのビルド環境では、React + Redux での SPA を開発していますが、一段落がついた段階で、順次既存コードのリプレイスも行っていきたいと考えています。 新機能の開発についても追ってお話できればと考えていますので、ぜひ楽しみにしていてください。 おわりに デザイナー兼フロントエンドエンジニアの立場から、スタメンの開発環境を掘り下げてご紹介させていただきました。 バックエンドのエンジニアやビジネスサイドとも密接な関係にある、スタメンのデザイナー/フロントエンドエンジニアに興味を持っていただけたら幸いです。 スタメンでは、一緒にサービスを作る デザイナー、フロントエンドエンジニア を募集しています。一緒にサービスを作り上げながら成長しましょう! もちろん、 バックエンドやアプリ 、 インフラ のエンジニアも大募集中です。まずは気軽に、オフィスに遊びに来てください!
はじめまして! 1月からスタメンのエンジニアをしているシュール( @shule517 )です。 よろしくお願いします。 「名古屋の勉強会をもっと楽しみたい! 応援したい!」という思いで、 趣味で 名古屋勉強会らむだ ってサービスを公開してます。 これが初めて作った Rails アプリで、当時はまだ「 Windows + VisualStudioCode」で開発をしていました。 あの頃の自分に教えてあげたい。 Rails やるなら「 Mac + RubyMine」だ! というわけで、 今回はRubyMineのおすすめポイントを紹介していきたいと思います。 スタメンでは、エンジニア全員がRubyMineユーザ! スタメンでは、エンジニア全員が「RubyMine」を使って Rails アプリの開発をしています。チームとしてRubyMineを使う利点として、3つあります。 単純にRubyMineが強力で便利! 「こういう使い方すると便利だよ」と情報を共有しやすい! ペアプロ する時に 人のPCを操作しても 困らない! ① Ruby 以外の言語にもサポート! RubyMineという名前ですが、 Ruby 以外の言語にも対応していて「erb / js / html / css 」でもコードを書く時にサポートしてくれます! なので、RubyMineを入れておけば Rails 開発に困ることはほとんどないです。 ② 動的言語 な Ruby でも 定義参照ができる! [⌘+B or ⌘+クリック]で クラス、メソッド、変数の定義元にジャンプすることができます。 スタメンで書いたコードはもちろん、 Rails など 読み込んでいる Gem のコードにもジャンプすることができます。コードを追う時に便利ですよね! 「動的な Ruby で、定義参照なんてできるの?」って思っていましたが、大半は定義元にジャンプできます!大規模な開発をしていると同じメソッド名が複数のクラスで定義されていることもありますが、RubyMine では、オブジェクトの型を反映して定義先を探してくれます。 動的にコードを生成した場合など一部できない部分はありますが、信頼できるレベルです。 すごい! ③ 開きたいクラスが パッと開ける! Rails を開発していると、クラスが数百、数千あるので、探すの大変ですよね。。 そんな時は、クラス検索(⌘+O)しましょう! あいまいな検索ができて、EventControllerを検索する場合に「event con」で探せるのが便利! ④ MVC の関連ファイルも パッと出せる! Controllerを開いている時に、関連したViewを開きたいってことよくありますよね。 「Switch to View」で開けます! よく使うので、僕はショートカットに登録してます。 M・V・Cや、テストコードとの行き来は 全てショートカットでできます! ⑤ デバッグ が強力! Ruby のコードはもちろん。.erbでも ブレークポイント を設定して、 デバッグ ができます。 Console画面で rails consoleのような操作感で 変数の中身が見れたり、メソッドを叩いたりできます! 知らない機能の流れを追ったり、不具合の問題調査するのに めっちゃ便利です。 ⑥ コードの差分が見やすくてGood! 画面の左右に修正前後のコードが表示され、分かりやすく表示されます。 「⌘+↓↑」に前の差分・次の差分へ移動するショートカットを登録しておくと便利です。 さらに、コードの差分を表示しながらコードを修正できるなど、高機能です! 最後に、エンジニアを目指している方へ 来週よりスタメンの新卒 会社説明会 がはじまります。 日程は こちら を参考にしてください。 特に エンジニアを目指している方は、↓こちらのイベントがおすすめです! エンジニアの大先輩である CTO小林より会社説明&体験談が聞けます。 服装自由、履歴書不要なので 気軽にきてね! 02/20 (火) 10:00〜12:00 会社説明会【名古屋】:『ITベンチャーでエンジニアが急成長する3つの理由』 ブログを最後まで読んでいただいて、ありがとうございました!!
こんにちは。スタメンエンジニアの松谷です。 2018/1/27(土)に、弊社で Rails5.2 の新機能 ActiveStorage を題材にした開発イベントを開催しました。 ( 【開発イベントレポート】ActiveStorageを使ってWikiアプリを作ろう! ) そのときに調べてわかったことを書いていきたいと思います。 ActiveStorageとは ActiveStorageとは、Rails5.2から利用可能なファイルアップローダです。クラウドストレージサービスへのファイルのアップロードとそれらのファイルをActive Recordオブジェクトに添付することを簡単に行えます。 今のところ、Amazon S3、Google Cloud Storage、Microsoft Azure Storage のクラウドストレージが標準で対応されています。またダイレクトアップロードという、ブラウザから直接クラウドストレージへアップロードする機能や、複数のストレージサービスを同期できるミラーリング機能を備えています。 ActiveStorageを使う準備 rails new でRailsアプリケーションを作成します。 $ bin/rails new --skip-turbolinks --skip-test . run bundle exec spring binstub --all * bin/ rake : spring inserted * bin/ rails : spring inserted rails active_storage :install Copied migration 20180128024947_create_active_storage_tables.active_storage.rb from active_storage この時 active_storage:installが実行され、 20180128024947_create_active_storage_tables.active_storage.rb というマイグレーションファイルが作成されています。 このマイグレーションファイルから、 active_storage_blobs と active_storage_attachments というテーブルを作成することがわかります。 データベースの作成とマイグレーションを実行します。 $ bin/rails db :create ... $ bin/rails db :migrate == 20180128024947 CreateActiveStorageTables : migrating ======================== -- create_table( :active_storage_blobs ) -> 0 .0011s -- create_table( :active_storage_attachments ) -> 0 .0012s == 20180128024947 CreateActiveStorageTables : migrated ( 0 .0024s) =============== このテーブルに対応するモデルが以下の2つのモデルになります。 * ActiveStorage::Blob * ActiveStorage::Attachment ActiveStorageの内部実装を覗いてみる ActiveStorage::Blob ActiveStorage::Blobレコードには、ファイルに関するメタデータ、Content-Type、サイズ、クラウドストレージの識別用のkeyを含まれます。 ActiveStorage::Blobのレコードは、次の2つのメソッドで作成できます。 `create_after_upload!` ファイルがクラウドストレージにアップロードされた後に作成します。 `create_before_direct_upload!` クライアント側からファイルを直接クラウドサービスにアップロードする前に作成します。 この方法では、ActiveStorage付属のJavaScriptの実装が必要になります。ブラウザから直接クラウドストレージにアップロードするため、ファイルサイズが大きくても、アプリケーションサーバーに負担をかけることはなく、また高速です。 ActiveStorage::Attachment ActiveStorage::Attachment は ActiveStorage::Blobと他のモデルを関連づけるための中間テーブルです。これを利用すると例えば、同じActiveStorage::Blobオブジェクトに異なるレコードを関連づけることも可能です。(その場合は、 has_many_attached:users、dependent:false を宣言して、1つのレコードを削除してもActiveStorage::Blobレコードは削除しないようにしたほうが良いでしょう。) 例として、Userというモデルに複数のファイルをアップロードする場合を示します。 has_many_attached を記述することで、Userのレコードとファイルの間に1対多の関係を設定します。 class User < ApplicationRecord has_many_attached :images end 以下のようにコントローラに記述することで、imagesと一緒にuserを作成することができます。 class UsersController < ApplicationController def create user = User .create!(user_params) redirect_to user end private def user_params params.require( :message ).permit( :title , :content , images : []) end end viewでの画像の表示は以下のようにします。 ...省略... <% @user .images.each do |image| %> < %= image_tag url_for(image) %> <% end %> ...省略... また、 user.images.attach(*attachables) を呼び出して、新しいファイルを既存のuserに追加できます。引数 *attachables にはActiveStorage::Blobオブジェクト、File オブジェクト、ActionDispatch::Http::UploadedFileオブジェクトなどを渡すことができます。用途としては、例えばUserのレコードを作成する前に画像を先にアップロードして、Userのレコード作成時にファイルと関連付けることができます。 このように、Userモデルのカラムにファイル用のカラムを追加する必要がなく、 has_many_attached を一行書くだけここまでの機能が使えるようになるのはとても便利です。 各モデルの関係性 ここで各モデルの関係性を整理してみます。実際にRails(ver 5.2.0.beta2)のソースコードを読んでみるとモデル間の関係性がわかると思います。 class ActiveStorage :: Attachment < ActiveRecord :: Base ...略... belongs_to :record , polymorphic : true , touch : true # この例だとrecordは User のレコードを表しています belongs_to :blob , class_name : " ActiveStorage::Blob " ...略... end class ActiveStorage :: Blob < ActiveRecord :: Base ...略... has_many :attachments ...略... end 上記のコードからわかるように、ActiveStorageはポリモーフィック関連を使って、ファイルの処理を全てActiveStorage::Attachment、ActiveStorage::Blobに任せる実装になっています。 終わりに 今回は、Rails5.2の新機能 ActiveStorage の機能の一部を紹介しました。まだ正式リリース前なので、今回紹介した内容とは異なってくる可能性はありますが、先取りして機能が開発されていく過程を追うことはとても楽しいです。 今まではRailsアプリでファイルアップロード周りは、それぞれのプロジェクトに合わせたライブラリを選択していたと思いますが、ActiveStorageという Rails way に乗って素早く開発できるようになればいいなと思っています。 弊社は今まで、Active Recordオブジェクトにファイルを関連付ける場合は、新しくファイル用のモデルを作成して、そのモデルに、ファイル情報を格納していました。今回それがActivieStorage::Blob、ActiveStorage::Attachmentに統一されることで、見通しがよくなりそうです。ですが、バリデーションなどファイルの種類毎に異なる実装もActiveStorage::Blob、ActiveStorage::Attachmentに集約してしまうと、柔軟に要件に対応できません。 ここの解決作は未だ見えていないので、引き続き、ファイルアップロード周りをActiveStorageに置き換えられるかどうかを検証していきます。 スタメンでは一緒に働くメンバーも募集しております!是非ご覧ください。 → スタメンの採用情報
こんにちは。スタメンエンジニアの松川です。 今回は、前回、市川の方からスタメンの開発コンセプトにあげられた ・ツールやライブラリをちゃんと使いこなす に着目したいと思います。 前回、紹介があった通り、スタメンではアプリケーションフレームワークとして、Ruby on Railsをメインに使用しています。 スタメンでは、「少ない人数で いかに(ラクをして)サービスの開発に注力するか」という観点の元、ライブラリとして複数のgemを活用して開発を行なっています。 gem gemとは、サードパーティ製のライブラリのことです。日本語では"宝石"を意味しており、数々の宝石のように優れたオープンソースのライブラリがこの世には存在しています。 主に活用しているgem スタメンでは、様々な場面でお世話になっているgemがあります。ログイン、非同期処理、Modelの状態遷移、decoratorなど自分達で実装するのは、実装工数が多く、共通化されたgemを使用すると恩恵が生まれやすい部分に活用しています。 ログイン部分では、Railsでログイン認証周りの管理を簡単に実装できるメジャーgemであるdevise 非同期処理部分ではdelayed_job、Modelの状態遷移ではaasm、decoratorではdraperをそれぞれ使用しています。 今回は、ログインに使用しているdeviseを取り上げて、ご紹介したいと思います。 https://github.com/plataformatec/devise deviseに全く触れたことがないという方やdeviseを試しに入れてみたのだけど使い方、全体像がよく分からないという方を対象にしていますので、概略の説明になっています。deviseのモジュールの中身の話については、次回以降、機会があればご紹介出来ればと思います。 devise Railsでログイン認証周りの管理を簡単に実装できるメジャーgem gem ' devise ' スタメンとして、deviseを導入した経緯としては、スタメンが運用しているTUNAGというサービスが、複数の役割を持ったユーザーのログインを想定したプロダクトであったためです。deviseは、wardenというシステムを元にして開発されたgemであり、複数の役割を持ったユーザーのセッション管理を容易にする機能を持っています。 例として、下記のような役割が別れたユーザーのログインを想定したサービスの場合、deviseの力が発揮されます。 ControllerやViewの内部で、 current_user user_signed_in? current_admin_user admin_user_signed_in? のようなhelperメソッドでセッションを複数保持した場合でもログイン状態のユーザーを個別に参照できるため、ログイン管理の煩雑さから解放されます。ログインユーザー個別の情報にアクセスしたい場合でも、 current_user.feeds current_user.reports などのassociationで辿れるため、可読性&安全性が高まります。 構成モジュール  deviseは、10個のモジュールにより構成されており、それぞれが異なる機能を提供しています。  それぞれの機能は、Modelに下記のような形で設定することでON、OFFを管理することが可能なため、非常に便利です。 devise :database_authenticatable , :registerable , :recoverable , :rememberable , :trackable , :validatable database_authenticatable(パスワードの暗号化など) registerable(サインイン処理) recoverable(パスワード再設定) rememberable(セッション期間の延長) trackable(サインイン回数やサインイン時間、IPアドレスの記録) validatable(パスワードやメールアドレスの登録検証) confirmable(アカウントの認証) lockable(アカウントのロック) timeoutable(セッションの時間管理) omniauthable(外部サービス認証)  deviseをフルに活用すると、下の図のような登録フローやパスワードの再設定フロー、アカウントのロック機能などを実装することが可能です。例えば、パスワード認証を許可せずに外部サービス認証のみ許可するということもdeviseを使用することで可能となります。 ログイン管理をしたいModel(User、Customer etc)が決定したら、 rails generate devise User 上記のコマンドによって、deviseの認証に使用するmodelとDBのカラム(migrationファイル)が自動生成されます。 # == Schema Information # # Table name: users # # id :integer not null, primary key # email :string(255) default(""), not null # encrypted_password :string(255) default(""), not null # reset_password_token :string(255) # reset_password_sent_at :datetime # remember_created_at :datetime # sign_in_count :integer default(0), not null # current_sign_in_at :datetime # last_sign_in_at :datetime # current_sign_in_ip :string(255) # last_sign_in_ip :string(255) # created_at :datetime not null # updated_at :datetime not null # class User < ApplicationRecord devise :database_authenticatable , :registerable , :recoverable , :rememberable , :trackable , :validatable end devise :database_authenticatable , :registerable , :recoverable , :rememberable , :trackable , :validatable の表記で活用する機能の取捨選択が可能となります。ただし、導入する機能によっては、新たにカラムを追加する必要があるものやカラムを追加していない場合には、代替のカラムで機能が実現されている可能性があるため、注意してください。  deviseの実際の実装方法については、公式ドキュメントやqiitaの解説記事に詳細が載っていますので、割愛しますが、deviseの利点は、カスタマイズ性の容易さも挙げられます。認証機能を実現しているDevise::SessionControllerのメソッドをオーバーライドすることにより、ログイン画面で表示させたい情報やログイン後にやっておきたい同期・非同期処理を組み込むことも容易に可能です。 class Users :: SessionsController < Devise :: SessionsController def new super end def create super do |user| # add some process end end end  また、外部サービスを使用した認証の場合でも上記のControllerに認証アクションをまとめておくことで、コードの見通しも良くなります。  その他にもdeviseで標準で用意されているメールテンプレートやログイン・パスワード設定画面なども独自にカスタマイズすることが可能なため、基幹部分のロジックを崩すことなくサービスに合わせた形で認証機能を実現することが可能となります。  この基幹部分を崩すことなく、様々なカスタマイズが可能な点が多くのWebサービスにこのdeviseが採用されている意味だと感じます。 deviseと連携可能なgem  TUNAGでは使用していませんが、deviseは外部サービスとのOAuth認証のためのgemが数多く公開されています。 TwitterアカウントやFacebookアカウントを使用してサービスにログインさせたい場合でも、公開されているgemを使用してログイン実装することが可能です。 gem ' devise ' gem ' omniauth ' gem ' omniauth-facebook ' gem ' omniauth-twitter ' 終わりに  今回は、ログイン部分に使用しているgem deviseについて紹介しました。優れたgemを使いこなすことで、サービスの開発効率や品質を向上することが出来ます。その他にも、サービスを開発するにあたり、様々なgemを活用して開発の効率化をしています。  次回以降、deviseの詳細なモジュールの中身の話や様々な認証方式(SAML認証やOpenID Connect)についても紹介していきたいと思っています。 スタメンでは一緒に働くメンバーも募集しております!是非ご覧ください。 → スタメンの採用情報
こんにちは。 スタメンでエンジニアをしてます 市川 です。 今回はスタメンの開発環境や利用しているツール等を紹介したいと思います。 こんな環境で開発してるんだなぁーと、なんとなくイメージいただれば幸いです。 コンセプト : いかにサービスの開発に注力するか ベンチャーは人手も時間も足りません。 スタメンでは、少ない人数で いかに(ラクをして)サービスの開発に注力するか をコンセプトに、下記のような方針をとっています 便利な外部サービスがあれば積極的に使う サービスの根幹部分についてはしっかり作り込む 自動化出来る部分は進んで自動化して効率化する ツールやライブラリをちゃんと使いこなす 言語、フレームワーク、DBについて プログラミング言語 : Ruby , JavaScript , Swift , Android Java アプリケーションフレームワーク : Ruby on Rails スタメンで開発している TUNAG ( ツナグ ) は Ruby on Rails の 5.1 を利用しています。 Rails は DRY ( Don't Repeat Yourself : 同じ記述を繰り返さない ) 、CoC ( Convention over Configuration : 設定よりも規約 ) という設計思想の通り、開発効率が高く、保守もしやすいです。 その他、ドキュメントや事例が豊富である、大抵のやりたいことは既に gem がある、使っているエンジニアも多く、採用や教育に困らない等々、スピード感が命のベンチャーでの採用事例が多いのも納得です。 また、TUNAG は Web アプリの他に、iOS と Android のアプリもあり、それぞれ Swift , Java を利用して開発を行っています。 スマホアプリについては別の機会に詳しく書かせてもらいますので、どうぞご期待ください。 JS ライブラリ : jQuery, React フロントエンドの JS ライブラリは jQuery と React を利用しています。 TUNAG はタイムラインの機能があり、そこで React を利用しています。 React は学習コストも低く、DOM 操作から解放されるのが一番のメリットだと感じています。 Rails と React の連携ですが、あまり良い話を聞かない webpacker は使わず、素の webpack + babel でトランスパイルした JS を Rails の assets の中に書き出し、 react-rails を利用してERB 内に React のコンポーネントを配置しています。 下記のようなイメージです。 app ∟ assets ∟ javascripts ∟ webpack ∟ app.js ( webpack + babel でトランスパイルすると、こいつができる ) frontend ( React 用に追加したディレクトリ ) ∟ react node_modules src ∟ ここに React のコンポーネント群配置 package.json webpack.config.js 個人的には Rails の sprockets を捨てて、全部 webpack でやるようにしたいなと思う今日この頃です。 デザインテンプレート : Bootstrap デザインテンプレートについては、管理画面のみ Bootstrap を利用し、エンジニアでもそれっぽい画面を自作できるようにしています。 ユーザー向けの画面はデザイナーが SCSS で作成したテンプレートを Rails に組み込んでいます。 DB : MySQL DB は MySQL の 5.7 を利用しています。ベンチャーで Web アプリを作るとなると PostgreSQL か MySQL のどちらかになるかと思いますが、初期の開発メンバーがより精通して使い慣れているという事から MySQL を利用しています。 NoSQL : Redis Redis はキャッシュ用途で利用しています。 データの永続化を実現したかったので memcached ではなく Redis を採用しました。 また String 以外の型も扱えてソートやランキングなどを実現できるので、今後拡張していきたいと考えています。 サーバー構成について AWS 本番およびステージング環境では AWS を利用しています。 オンプレに比べてクラウドでは、欲しい時に簡単にサーバーのインスタンスを立ち上げる事ができ、スペックが足らなくなった際にも簡単にスケールアップ、スケールアウトできるのがメリットだと感じています。 また、使ったぶんだけの従量課金制なのも良いですね。 簡単ですが TUNAG の現状の AWS 構成図になります。 よくある Web アプリの構成かとは思いますが。。。 現状サーバーは chef で管理しているのですが、今年こそは docker 環境に移行したいと考えています。 その他 RDS for MySQL を Aurora に移行したり等、やりたい事・出来る事はまだまだたくさんあります。 スタメンでは AWS Activate ( スタートアップ支援プログラム )を利用させてもらっています。 一部のアクセラレーターやインキュベーター、VC ファンドとの提携が条件にはなりますが、2年間有効な 15,000 USD ぶんのクレジットを提供してもらえるプログラムで、インフラコストを抑える事ができ、とても助かっています。 エラー監視 : Bugsnag エラーの監視には Bugsnag を利用しています。 GitHub と連携してエラー発生時に Issue を自動で作成したり、Slack と連携して通知をとばす事も出来るのでとても便利です。 Ruby 用に gem が用意されているので導入も簡単です。 開発環境について バージョン管理 : Git リポジトリ : GitHub リポジトリ運用 : GitHub flow CI : circle ci デプロイ : capistrano リポジトリの運用は GitHub flow で行っています。  GitHub の Issue 毎にブランチを作成し、master へマージする前には Pull Request を作成して必ずレビューを行うようにしています。 master へマージした際は circle ci でテスト ( rspec ) を実行し capistrano が本番環境へデプロイを行うようにしてあります。 開発用 PC : MacBook Pro 開発環境は Mac に Rails の環境一式を構築しています。 テストは rspec での自動テストと個人環境で puma を立ち上げて行う他、AWS 上にステージング環境を用意してあるので、そこへデプロイしての確認も行っています。 開発用 PC は CTO のがんばりにより、最新の MacBook Pro ( 2.8GHz Intel core i7 , 16GB ) が支給されました!!! マシンのスペックは開発効率に直結するので、ここは譲れないですね。 IDE : RubyMine 開発環境は RubyMine を利用しています。コードのジャンプ機能がとても便利で、gem とかのコードを読むのが捗ります。 年額で ¥10,000- くらいかかりますが、毎日さわるメインツールなので、すぐに元はとれるかと。 現状、エンジニアは皆 RubyMine を使っているので、ペアプロする際も統一されているので便利です。 開発の流れ scrum 開発手法はスクラム開発を採用しています。 スプリントは 2 週間に設定しています。 割り込みが多々発生してスプリントが崩壊したり、見積もりの精度が甘かったり等、改善改善の毎日です。 GitHub Issue 管理 : waffle.io タスクの管理は GitHub の Issue をカンバン方式で管理できる waffle.io を利用しています。 スタメンでは大まかにタスクを下記の 5つに分類しています。 ( GitHub の Issue では label に相当 ) Product Backlog ( いつかやるモノ , プロダクトバックログ ) Next ( 今スプリントでやるモノ , スプリントバックログ ) In Progress ( 今とりかかっているモノ ) Needs Review ( Pull Request でレビュー中のモノ ) Done ( リリースが完了したモノ ) スプリントミーティング時に Product Backlog の中から今スプリントでやるべきタスクを抜き出しNext に移すようにしています。 チャット : Slack チャットツールは slack を利用しています。 シンプルで洗練された UI が使いやすいです。 また、外部サービスとの連携が容易なのも魅力です。  スタメンでは AWS ( Cloud Watch ) 、Bugsnag 等と連携させ、AWS での障害発生時、TUNAG でのエラー発生時に通知をとばしている他、TUNAG 内で特定のイベントが発生した際に通知をとばしたりもしています。 終わりに 以上、駆け足ですがスタメンでの開発のイメージをお伝えできたのなら幸いです。 今後、エンジニアの人数が増えたり、サービスが成長して行ったりと、環境はどんどん変わっていきますが いかにサービスの開発に注力するか という部分はぶらさず、チャレンジを続けていけたらと思います。 少しでもスタメンに興味を持っていただけましたら、 株式会社スタメンの最新情報 – Wantedly で、スタメンでの仕事をもっとよく知っていただき、ご応募いただければ幸いです! よろしくお願いします!
こんにちは 株式会社スタメンでCTOをしている小林です。 今回、スタメンのコーポレートサイトがリニューアルされるに伴い、スタメン開発チームのブログを開設しました。ちょうど年末ですし、良い機会なので創業してからの1年半を振り返って見たいと思います。 創業するまでの振り返りは、 CTOと主夫を二人三脚するエンジニアが名古屋で創業するまで を御覧ください。 創業時の計画とミッション 2016年8月の創業時に、自分自身のミッションとして下記と捉えていました。 創業プロダクトのTUNAGを創ること ビジネスモデルを理解し、必要な機能の絞り込みと将来の発展性の予測。 インフラ、アーキテクチャの設計と実現に向けた技術選定。 エンジニアとして、設計、実装、テスト、インフラ構築を行いリリースすること。 エンジニアチームを創ること エンジニアチームのビジョンを考える 採用や育成の方向性の策定 プロジェクトマネジメントとラインマネジメント エンジニアとして経営参加すること 経営の意思決定にエンジニア的な視点を加えること 開発力と営業力の双方を会社の強みにすること 家庭と仕事を両立すること ライフワークバランスの一つのカタチを皆さんに提示すること 仕事について、家庭を言い訳にしないこと 家庭について、仕事を言い訳にしないこと 実際にやってみてどうだったか 創業プロダクトのTUNAGを創ること 創業プロダクトのTUNAGは、エンゲージメント経営を支援することを特徴とした社内SNSです(詳細は こちら を参照)。スタートアップベンチャーは、とにかく構想をカタチにしないと意味がありません。創業CTOの最初の仕事はサービスを作ることです。 創業直後は人も、お金も、時間もかけられません、速く確実に創るために、以下のことを行いました。 ビジネスモデルをCEO加藤とCOO大西とディスカッションして深く理解する。 経営陣が同じゴールとサービスイメージを描けることが大切。 コンセプトを画面や仕様まで具体化し、リリースまでの道のりを描く。 必要な機能と工数を概算して、採用計画とスケジュールを決め、事業計画に反映する。 サービス全体の設計を行い技術選定を行う。 自身にノウハウがあり確実に速くサービスを実現できる技術として、Ruby on Rails と Amazon Web Service を技術の中心に決める。 ひたすらコーディング。同時に細部の仕様を決めていく。 結果、当初想定した創業半年後の2016年2月に最初のリリースを行い、現在に至るまで改良&運営を続けています。 これまでの SNS や ECサイトを作ってきた経験がサービス全体の設計や技術選定等で大いに活き、全体的なアーキテクチャは当初予定から大きくずれることなく実現し、現在でも拡張し続けています。これまでの各社での経験が各所で活きました。まさに Connecting The Dots で不思議なものです。 ただ、当初約半年(27週間)と見積もった開発規模は、リリース時期こそ見積もり通りとなりましたが、これは結果で、実際には社外の知り合いに助けてもらったり、エンジニアを採用するなどして、開発リソースを当初予定より大幅に増やしたことでやっと間に合ったのでした。 事業は構想段階では抽象的で曖昧で、実装する過程で具体的な仕様にしなければなりません。同時に、仕様を考えたり、作ってみると足らない機能に気づき工数が増えてスケジュールが遅れます。マクロにサービスやスケジュールの全体像を俯瞰しつつ、ミクロにUIや機能を作り込む必要があります。こういうとき、タスクが増え過ぎたり、並列すると生産性が落ちます。やらないこと、忘れても良いことを意図的に決めることをいつも意識しました。 また、30代に入ってからはコーディングする時間よりは、マネジメントする時間が多かったのですが、この1年半はこれまでで一番コードを多く書いた日々でした。コーディングすることの面白さや奥深さを改めて実感し、もっともっとエンジニアとして成長したい(しなければならない)と感じています。プログラマの35歳定年説なんてスタートアップベンチャーには全く存在しません。ガンガンコードを書きましょう。 TUNAG の技術的な詳細や、開発環境、開発プロセス、利用しているツールなど、ここでは書ききれない内容がたくさんあります。今後このブログでご紹介していきたいと思いますので、興味を持っていただけましたら幸いです。 エンジニアチームを創ること スタートアップベンチャーにとって採用は非常に大切なミッションです。どれだけ頑張っても一人で開発できる規模は限界があります。仲間が集まらなければ事業を大きくすることはできません。TUNAG の開発と平行して、開発チームの立ち上げも行いました。 スタメンのエンジニアを採用にするにあたり、下記を重視してきました。 仕事についての価値観 何かしらやり切った経験 エンジニアリングについての熱量 仕事についての価値観は、仕事を通して何をしたいのか、日々仕事をすることをどう捉えているかなど、仕事についての人生観です。 スタメンは毎日終電が続く会社ではありませんが、プロジェクトの終盤などではどうしても忙しくなる時期もあります。また、新しい分野での事業や、未経験の技術を用いた開発では、うまく行かないことも多々あります。だからこそ、創業期のベンチャーでは、何事も積極的に、主体性を持って取り組み、日々の挑戦を楽しむことができることが重要です。 何よりそういう人たちと一緒に仕事することは楽しく、互いに切磋琢磨することで強いチームになります。 また、上記のような価値感はある日突然できるものではないと思っています。子供の頃の体験、学生時代の勉強、アルバイト、部活や、社会人になってからの仕事など、趣味、スポーツ、仕事、何かしらにひたむきに取り組み、成果がでるまでやりきった経験をお持ちの方が多いと思います。面接の場では過去に熱心に取り組んだ経験についてお聞きすることが多かったです。 そして、仕事の価値観がベンチャーに合い、何かしらやりきった経験がある方が、エンジニアとしての仕事に対して熱意やこだわりを持っている場合、本人の成長と会社としての成果が重なって WINxWIN な関係を築けると考えています。 そんな感じで採用活動を続けた結果、私含めてエンジニア4名、フロントエンジニア兼デザイナ1名の計5名のチームになりました。 開発チームに限りませんが、ピンチを救うように助っ人が登場したり、既存メンバーが急成長して乗り切ったり、まるでバトル系の少年マンガのようにチームが育っていきました。こんな創業直後の小さい会社に飛び込んできてくれた現在のメンバーに感謝するばかりです&今後もよろしく。 ベンチャーにとって人材は非常に重要です。いい人がいれば良いサービスが生まれますが、良いサービスであっても良い人がいなければ廃れてしまいます。 引き続き採用は最重要なプロジェクトで、来年も積極的に採用していきます。創業期の小さなチームでの仕事はエキサイティングで楽しいだけでなく、非常に濃い成長の機会となります。興味を持っていただきましたらぜひ下記よりご応募ください!! 名古屋で自社サービスの開発・立ち上げに挑戦したいエンジニアを募集! ≪名古屋≫ 急成長する HR Techを支えるインフラエンジニアを募集! エンジニアとして経営参加すること エンジニア的な観点で経営の意思決定に参加すること。と書いてみましたが、これが何を意味するのかを簡潔に書くには自分自身の経験は浅いと感じます。 ただ、自分自身がこうありたいという姿は、過去接してきた尊敬する先輩CTOの方々に重なります。先輩方に共通するのは、会社を運営する際の様々な場面で技術的な観点で組織や事業の将来を語り、意思決定に寄与し、実現に導くリーダーシップと行動力でした。エンジニアとして、経営メンバーとしての全幅の信頼であり、私もそうありたいと思っています。 スタメンの経営理念は「一人でも多くの人に、感動を届け、幸せを広める。」です。 この理念を実現するため、TUNAGを始めとした様々ななサービスを提供していきますが、有望な事業(案)を技術不足を理由に諦めたり、伸ばしきれないことが無いように、強い開発チームを作っていく必要があります。 エンジニアチームの実力が、その会社の事業の上限を決めると思います。スタメンの可能性を広げるためにも、まずは私が先陣を切って、TUNAG の普及に貢献し、エンジニアのロールモデルでありたいと思ってます。 家庭と仕事を両立すること 創業のときに書いた CTOと主夫を二人三脚するエンジニアが名古屋で創業するまで では、下記のように書きました。 ベンチャーでの仕事はハードワークが続きますが、必ずしも深夜残業や休日出勤だけが解決策ではありません。いままでよりも、計画的に、効率的に、これまでの経験を活かして最大限生産性を高めることで成果を出し続けたいと考えています。また、各種クラウドツールの発達により、自宅でできる業務も随分広がってきました。こういったツールを使うことによって、夕方早めに帰宅しても、夜に開発や打ち合わせに参加することもできます。こうやって家庭と成果の最大化を両立することが私のライフワークバランスです。 家庭を妻に任せて一心不乱で仕事することよりも、若さに任せて不眠不休で仕事することもよりも、家庭と仕事を両立しながらベンチャーで成果を出すことは難しいように思えますが、家庭があっての仕事ですし、仕事の先に家庭が崩壊するならば大きな後悔が残ると思います。何よりも、良いエンジニアでありながら、良いパパでいたいですし。 これまでとは違った難しさ、苦労があるかと思いますが、これを乗り切れたら全部ハッピーだと思ってがんばってみたいと思っています。 創業直後は、家庭と仕事の思考の切り替えに苦労したものの、徐々に切り替え&両立に慣れてきました。これはスタメンに仲間が増えて、信頼して任せられるようになったことが大きいです。いつもありがとう。 想定外としては、スタメンが現在のオフィスに移転し、オフィスにキッチンができてからは、 たまにオフィスで料理 してます。凝り性なので、料理するからには真面目に作るんです。ちゃんと主夫もしてますよ!とアピールする絶好の機会になってます。「小林の料理が食べたい!」という人は、ぜひスタメンに入社してください! 入社祝いを作ります! まとめ と これから スタートアップに転職するのと創業に関わるのは違う。創業以来、何度も思いました。 これまで5社のベンチャーで仕事をしてきましたが、前任者が方向性を示したことを拡張していくのと、ゼロから決めるのは違いました。技術選定、仕様策定、チームの立ち上げ、すべてをゼロから始める。真っ白いキャンパスに最初の線を描くような緊張感がありました。 でも、ほんと様々なことについて、これまでの仕事での経験、出会ってきた人々からの教え、スタメンに入ってきてくれた社員の貢献に助けられました。さらに、TUNAG を導入してくださったお客様の皆様には、本当に多くの勇気や学びを頂いています。TUNAG を始めとしたスタメンでの仕事で、頂いたご恩を少しでもお返しできるように、地道に頑張っていきたいと思っています。 最後に、この業界に入ってからずっと目標としていることがあります。それは、IT業界のエンジニアが以下の三冠になることです。 子どもたちが将来なりたい職業。 彼氏/彼女にしたい人の職業。 結婚したい人の職業。 これ本気です。エンジニアには、それぐらい夢も希望も理想もある職業だと思います。世のエンジニアがもっと楽しく、良い仕事をして、世に貢献して、社会から評価されることで、幸せになって欲しいと思います。そのためにも、まずはスタメンのエンジニアがこれを実現できるように、経営者として、エンジニアとして恥じない仕事をしたいと思ってます。 スタメンは、創業してまだたったの1年半です。経営理念の「一人でも多くの人に、感動を届け、幸せを広める。」は、まだまだ全然実現できていません。やりたいこと、やるべきこともたーーくさんあります。もっとたくさんの頼もしい仲間が必要です! 少しでもスタメンに興味を持っていただけましたら、 株式会社スタメンの最新情報 - Wantedly で、スタメンでの仕事をもっとよく知っていただきご応募いただけないでしょうか! よろしくお願いします! というわけで、スタメンのエンジニアブログとして、創業以来の1年半を振り返ってみました。 今後、各メンバーから、様々な記事を書かせていただき、スタメンでのエンジニアライフをご紹介したいと思いますので、今後ともよろしくお願い致します!