おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

株式会社ウィルネットは設立2周年を迎えました

昨日、2月21日は弊社の設立記念日でした*1

というわけで法人成りして2年経ちました。最初はフリーランスの延長のつもりだったのですが、会社という形態にしたことで意識が少しだけ変わってきた気がします。

これまではなんでも全部自分一人でやるというのが自然だったのですが、会社の予算を使って誰かに仕事を手伝ってもらう、という形態を徐々に受け入れられるようになってきました。空いた時間で少しずつ開発を進めているsavanna.ioも、いまはデザインに関しては本職にお願いするようになっています。

昔は、一つのスキルだけを伸ばすのではなくいろんな分野を学んでいくのがよい、と思ってデザイン含めいろいろ勉強してたのですが、現状では一つのスキルをガンガン伸ばして他は誰か得意な人にお願いする方が効率良いのでは?という気持ちになっています。

予算を使ってコーポレートサイトもちゃんとしたものを作りました。もともとペライチというサービスでそれっぽいものを作っていたのですが、問い合わせフォーム経由でスパムが毎日のように届くのでもう少し自分でコントロールできる環境が欲しくなったのでした*2

株式会社ウィルネット|Webサービス開発コンサルティング

なにか相談がある方は↑の問い合わせフォームからでもtwitterなどからでもよいのでお気軽に問い合わせください。

おかげさまで技術顧問業、savanna.ioの開発、執筆、登壇など相変わらず忙しくしています。直近だとRails Developers Meetup 2019に登壇予定です。テストの話をする予定なので興味ある人はどうぞ。

3期目も頑張ってやっていくのでよろしくおねがいします(\( ⁰⊖⁰)/)

*1:景気よくホールケーキを買おうかと思ったけど太るのでやめました

*2:まれに本当の問い合わせも来るので迷惑メール扱いにできないのがつらい

銀座Railsで「個人でつくるwebサービス」という話をした

昨日行われた銀座Railsで登壇させていただきました。

資料はこちら。

所感

複数の主張したいことを一つの発表に盛り込んでしまったので、ちょっとぼんやりした発表になってしまったかもなーという反省があります。

  • 個人でwebサービス作るのはいいぞ
  • 個人開発をモチベーションを落とさず継続する仕組み
  • Railsは個人or少人数で小規模なサービスを作るのに向いてるめっちゃべんりなライブラリなんですよ
  • turbolinks&stimulusはいいぞ
  • rubocopデフォルト設定でも全然普通に開発できるんですよ

それぞれのトピックごとにもっと深掘りして(もしくは角度を変えて)話せそうなので、どこか別の機会があれば喋ろうかなと思います。

次は3月のRails Developers Meetup 2019で登壇予定ですが、今回とは全く違った話になる予定です。乞うご期待。

turbolinksとform_withを便利に使うためのgemを作った

前提

rails標準のわりに使っている人の少ないturbolinksですが、僕は便利に使っています。turbolinksはご存知の通り、リンクを全部ajaxリクエストに置き換えてページ遷移を早くするライブラリです。

turbolinksが実現している「リクエストは全部ajaxにして、フルのページ遷移を避けたほうが速い」というロジックはformでも同じことが言えます。

Railsは5.1からform_withというviewヘルパーが増えました。これは既存のform_tagform_forを統合するヘルパーという位置づけですが、それ以外に大きな違いとしてデフォルトでdata-remote="true"がformタグに付与される、というのがあります。この振る舞いはformもturbolinksみたいにajaxにしようぜ!というDHHの思想の現れなのだろう、と推測します。実際、form_withのPRでDHHがそんなようなことをコメントしていますね。

Make remote: true the default. Full-page changes after submissions are rough. When using Turbolinks, a normal redirect will generate a Turbolinks.visit() call, and otherwise there's SJR. (We could consider having config.action_view.forms_remote_by_default that you could set to false, for people going old school).

turbolinksはajaxを利用したform submitの一部に対応しています。具体的にはturbolinks gemはredirect_toの挙動を差し替えており、form submitによるajaxリクエストをしたときのリダイレクト処理を非ajax時と同じ*1になるようにしています。書き換えたredirect_toメソッドは、もしget以外のajaxリクエストから呼び出された場合、302を返さずに、クライアントサイドでTurbolinks.visit()という画面遷移用のjs関数を実行するためのレスポンスを返します。この結果、ユーザとしては単にformをsubmitしてからredirect_toしたのと同じような画面遷移になります。

先程「turbolinksはajaxを利用したPOSTリクエストの一部に対応しています」と書きました。つまり対応していない部分があるということです。DHHが書いたコメントにもありますが、バリデーションエラーになった際の対応は、SJR(Server-generated JavaScript Responses)などを利用して各自でエラーメッセージを表示する処理を書かなければなりません。このあたりもうちょっとうまくできるとturbolinksもう少し普及するんじゃないのかな、という気がします*2

最初はSJR部分を普通に書いていたのですが、統一的なインタフェースがあったほうが良さそうだな、と思ったのでgemにしてみました。

willnet/ajax_error_renderer: a validation error renderer for ajax request

ajax_error_rendererとは

render_ajax_errorという、ajaxでエラーメッセージを表示するメソッドを提供する小さなgemです。次のようにAjaxErrorRendererをincludeすると使えるようになります。

class ApplicationController < ActionController::Base
  include AjaxErrorRenderer
end

ajaxでリクエストが来てエラーメッセージを表示する場面でrender_ajax_errorメソッドを使います

class UsersController < ApplicationController
  def create
    @user = User.new(params.require(:user).permit(:name))
    if @user.save
      redirect_to users_path, notice: 'You created a user successfully!'
    else
      render_ajax_error model: @user
    end
  end
end

すると#errorなDOMに動的にエラーメッセージが表示されるようになります

<%= form_with(model: @user) do |form| %>
  <div id="error">
    <%# ここにエラーメッセージが表示される %>
  </div>

  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

基本的な使い方は以上です。簡単ですね。どうぞご利用ください。

*1:ユーザ体験的な意味で。実際の挙動は異なります

*2:とはいえ公式でこう!というように対応するのも難しい、というのもわかる

find_by_sqlしたあとにkaminariでページネーションするには

課題

Railsでページネーション機能を作るときにはkaminari を使うのが定番ですね。Active Recordのクエリメソッドに対してメソッドチェーン形式でpageやperを追加するだけで手軽にページネーションができます。

ただ、find_by_sqlを利用してActive Recordのオブジェクトを作成したときには、戻り値が配列になってしまうのでpageやperなどを後に追加することができません*1。どうしたらよいのでしょうか。

問題のあるやり方

ググるとKaminari.paginate_arrayを使った次のようなやり方がいくつか引っかかります。

Kaminari.paginate_array(array_from_find_by_sql).page(params[:page]).per(10)

Kaminari.paginate_arrayは読んで字のごとく、配列をkaminariで扱えるようにするためのメソッドです。これでとりあえず要件としては満たせますが、レコード件数が多くなってくるとパフォーマンスに問題が出てきます。このやり方だと、find_by_sqlが検索対象のActive Recordオブジェクトを毎回すべて生成する必要があるからです。例えば10000件のレコードが対象となるクエリだとしたら、毎回10000個のActive Recordオブジェクトが生成されるわけです、これはだいぶつらいですね…><。

とりあえずの解決方法

kaminariで生成したモデルオブジェクトは、ビューでpaginateメソッドへの引数になり、ページネーション用のhtmlになります。引数は必ずしもkaminariから作成されたクラスである必要はなく、必要なメソッドが定義されていれば問題ありません。なので必要なメソッドを返すラッパーを用意すればよい、と考えてしばらく↓のようなクラスを作り運用していました(もちろんfind_by_sqlの引数としてlimitやoffsetを使ったクエリを組み立てる必要がありますし、total_countは別のクエリで取得しておく必要があります)。

真の解決方法

しかし、そもそもKaminari.paginate_arrayに次のような形で引数を渡すと、無駄なActive Recordのオブジェクトを生成する必要なく、上記のWrapperForKaminariと同等のメソッドが提供されたオブジェクトが返る、ということに気づきました。

Kaminari.paginate_array(array_from_find_by_sql, limit: 10, offset: 0, total_count: 100)

WrapperForKaminariをgemにしようかな。と思っていたのですが不要でしたね。kaminariべんり。

*1:仮に追加できても、find_by_sqlで発行されるクエリをいじれないので無意味ですね…

現場で使えるRuby on Rails 5 速習実践ガイドを読んだ

著者の櫻井さんから献本いただきました。遅れましたが感想書きます!

現場で使える Ruby on Rails 5速習実践ガイド
大場寧子 松本拓也 櫻井達生 小田井優 大塚隆弘 依光奏江 銭神裕宜 小芝美由紀
マイナビ出版 (2018-10-19)
売り上げランキング: 7,172

所感

全般的に図が多用されていて、読みやすくするための工夫があらゆるところに散りばめられているな、と感じました。本を書く側の視点で「これめっちゃ手間暇かかって大変だったのでは…」と、勝手に執筆時にどれくらい苦労したのか想像して胸が苦しくなるくらい丁寧。そのおかげで読者は詰まることなく読み進められるのではないでしょうか。

また「現場で使える」とタイトルにあるとおり、単にRailsの使い方を説明するにとどまらず、現場で実装に悩みそうだったり、深く考えずに実装するとトラブルになりそうな機能に対してのノウハウが書かれているところが特徴です。大きくは

  • 確認画面をどう実装するか
  • CSVファイルのインポート/エクスポートのやりかた
  • マイグレーションを実装するときの注意点
  • 複雑になってしまったコードをいかに整理していくか

などなど。これらを自分なりのやり方で書いているけど自信がない、という人はこの本を参考にすると安心感が増すのではないでしょうか。

Railsやgemのバージョンアップ戦略について、「Railsでアプリケーションを開発するならば、アプリケーションを放棄しない限り、ずっとバージョンアップをし続ける必要がある」と明言されているのも良いポイントです。現場によってはリリースしたタイミングでバージョンが固定され、何かあったときだけバージョンアップするという戦略をとっているところもあるかと思います。そういった現場だと中のエンジニアが疲弊する未来しか想像できません。辛いですね…。しかしボトムアップでバージョンアップ戦略を変えるように上に働きかける、というのはなかなか大変なことです。この本をうまく活用して、バージョンアップを定期的に行えるように偉い人に働きかけられると良いのでは?と思いました。

全般的に「これから現場に入るひとから、現場での経験はあるけど今のやり方であっているのか確かめたいひと」まで幅広い読者にたいしてオススメできる本だと思います!

ginza.rbでstimulusについて紹介した

今日のginza.rbはBasecamp製のjsフレームーワーク、stimulusがテーマでした。

Ginza.rb 第64回 Stimulusは新たな刺激となりうるか? - Ginza.rb | Doorkeeper

資料はこちら

所感

資料の中にも書いていますが、「サーバサイドな人が片手間で書いているjsをもうちょっといい感じにしたい、しかしvue.jsやreactなどに手を出すほどの余裕がない」というケースだとかなり良い感じで使えるのかな、という印象です。フロントエンド専任の人がいるようなプロジェクトでは特に必要なさそう。

DHHはrailsを「一人でアプリケーションを素早く作るのにとても便利なフレームワーク」として捉えている(出典どこだったか忘れました><)のですが、stimulusやturbolinksにもそれが色濃く反映されているように思えます。DHHの思想のうち、「これはちょっと自分の意見とは違うな」というものもありますが、jsについてのスタンスは完全に一致していてDHHやるなあ。という感じです。一人でサーバサイドとフロントエンド両方にロジック書いていく、というのは大変。

Railsでturbolinksとstimulusを使い、なるべくサーバサイドにロジックを寄せてフロントエンドは控えめしておく、という戦略を取れると、大抵のwebサービスを素早く作れて幸せになれるのでは?と思っています。が、特にturbolinksについてネガティブな印象を持っている人が多く、それに引きずられてstimulusも普及しづらい気がしているので、そのへんの情報をちゃんとまとめたブログエントリを書くか、どこかで登壇しようかな、と考えています。乞うご期待。

良いエンジニアを採用するにはどうしたらいいか

以前ソフトウェア開発者採用ガイドの読書感想文を書いたときに反響が思ったより大きかったので、エンジニア採用というテーマは関心が高いのだなと感じました。

上記感想文のエントリでも書いていますが、お手伝いしている会社の方などから「どうやったら良いエンジニアを採用できますか?」と聞かれることがよくあります。先のエントリでは「頑張るしかないですねとしか答えようがない」と書きましたが、頑張るとはいったい何を頑張るのか、きちんとまとめておいたほうが良いなと思いエントリをしたためる次第です*1

あくまで僕はこう思いますという話で、この通りにしたからといって必ず良いエンジニアを採用できる保証はありません。あしからず。

想定読者

良いエンジニアを採用したい偉いひと、もしくは人事のひとです。

前提: 良いエンジニアとは

このエントリでの「エンジニア」とはいわゆるweb系のエンジニア(例: サーバサイドエンジニア、フロントサイドエンジニア、iOSエンジニア、Androidエンジニア、インフラエンジニア)を指します。

さらに「OSS活動をしたり、勉強会に積極的に参加・登壇するなど、目に見えるアウトプットのある勉強熱心なエンジニア」のことを「良いエンジニア」としています。もちろん、勉強熱心に見えて実は大して能力がない人もいるでしょうし、特にアウトプットはないけど優秀な人もいるでしょう。前者は選考でうまく見極めてください。後者は運以外で採用する方法が思いつかないので今回は取り上げません。

どうやったら採用できるか

大きく2つの施策に取り組む必要があると思います。

  • 良いエンジニアとの接点を作る
  • 社内の開発環境を整える

良いエンジニアとの接点を作る

企業が社員を採用しようとしたとき、まず人材紹介会社や求人サイトを利用するのが一般的なやり方かと思います。しかし良いエンジニアは人材紹介会社や求人サイトをあまり使いません。それらを使わずとも転職することが難しくないからです。もちろん人材紹介会社と求人サイトを活用すること自体はよい*2のですが、良いエンジニアを獲得したい場合は他のやり方も並行して実施する必要があります。

特に統計をとったわけではないのですが、自分の周りの良いエンジニアは社員の紹介経由で転職することが多いです。また、気になっている会社の社員に対してSNSで連絡を取り、遊びに行って会社の様子を知り、そこから選考に進む、というケースもよくあります。

このような転職経路をとるエンジニアを獲得するには「エンジニアたちと普段から接点を作っておくこと」と「エンジニアが転職を考えたときに選択肢の一つにしてもらえるポジションにいること」の2つが重要です。

ではどうやったら接点を作ったり、よい会社だと認知したりしてもらえるのでしょうか?たとえば次のようなものが考えられます。

  • テックブログの開設
  • カンファレンスのスポンサーをする
  • 勉強会の参加

テックブログの開設

エンジニアが会社選びをするときに重視することの一つとして「どんな技術を使っているか」があります。これは「どんな事業を行っているか」よりも遥かに重視されます。テックブログに今使っている技術や達成したこと、便利な知見について書いておくと、外部のひとからはその会社の技術スタックを想像しやすくなります。

また、社員がアウトプットする機会を増やすことにもなりますし、エントリがバズれば会社自体の認知度も上がります。やらない理由はないのではないでしょうか。

ただし、ブログを継続して書き続ける文化がないと逆効果です。例えば最終更新日が1年以上前のテックブログだと「あーこの会社はブログエントリを継続する暇もないくらい忙しいんだな」と判断される可能性があります。テックブログをブランディングとして活用するには最低でも月に一回程度の投稿は必要でしょう。

もし投稿が継続できそうになければ、採用の前に投稿を継続できない原因を解消するべきでしょう。そのような会社に入りたい人は少ないはずです。ブログを書くことを業務の一つと位置づけて、きちんと工数を確保することが必要です。

もしかしたら、現在の社内の技術力が低いのでブログを書きたくない、と考える会社もあるかもしれません。しかし、このような態度はよくありません。仮に会社の技術レベルを知らない優秀なエンジニアが入ってきたときに、実態を知って幻滅してすぐに転職してしまう、となったら双方にとって不幸です。技術力が低いとしたらそれを向上するための活動をブログとして公開していくとよいでしょう。

エンジニアと長期的な関係を築きたいのであれば「本当にまずい情報以外はすべて公表する」くらいの態度であるほうがよい結果をもたらします。

「本当にまずい情報以外はすべて公表する」を実践している例として、就業規則をオープンソースとして公開している企業があったり面接時に使用する資料を公開している企業があったりします。このように情報をオープンにする態度は多くのエンジニアにとって好感が持てるものです。

カンファレンスのスポンサーをする

近年では毎日のようにweb技術に関する勉強会が開催されています。ここでは便宜的に、参加者が数百人になるような大規模な勉強会のことをカンファレンス、それ以外の小規模なものをミートアップと呼ぶことにします。

カンファレンスではたいてい企業スポンサーを募っています。ここでスポンサーに名乗りを上げると知名度向上が期待できます。例えば日本最大のRubyカンファレンスであるRubyKaigiをスポンサーしたら「この会社はRubyに力を入れているのだな」と認知されるでしょう。

しかし、単にスポンサーをしただけだと効果は限定的です。カンファレンスによっては、スポンサーになるとブースを出せたり、チラシやノベルティを配れたり、エンジニアの前で話す機会がもらえたりします。ここで工夫してエンジニアに刺さるアピールができると大幅知名度アップ(好感度アップ)になるでしょう。ただ、ここは「こうすれば刺さる!」ような決まった手法はありません。いろんな会社のやり方を研究して、独自のアピール方法を模索する必要があります。自社のエンジニアと相談していろいろ試してみましょう。

ミートアップはどこかの企業の会議室を借りて開催されることがほとんどです。カンファレンスのスポンサーほどではありませんが、ミートアップの会場を貸すだけでもエンジニアにとっての知名度・好感度は上がります。ほどよく大きく、社外の人間を招いても問題ない会議室を持っている会社であれば検討する価値はあると思います。

勉強会への参加

エンジニアの社員が勉強会やその懇親会に参加して知り合いを増やし、知り合いが転職を考えた時に「うちに遊びに来ます?」と声をかけるのはよくある転職パターンです。

社員が勉強会に参加するかどうかはその社員の趣味嗜好次第ですが、ミートアップの参加を奨励したり、カンファレンスへ業務として参加できるような制度があると参加する回数が増えるのではないでしょうか。

例えばSmartNewsさんは、半年に1回、海外のカンファレンスへの参加を補助してくれるそうです。他にも、例えば「RubyKaigi 業務」のようなワードで検索すると、「弊社はRubyKaigiへの参加は業務扱いで、参加費・交通費・宿泊費を会社負担としています」と表明している会社を多数観測できるはずです。

また、単に参加するだけではなく登壇したり運営に関わったりできると会社のアピールにもなるので、それらを奨励することも重要です。

(少し話がずれます)ときどきエンジニア以外のひとが懇親会に参加し採用のために声掛けなどをしている事例を見かけますが、これは逆効果ですのでやめましょう。懇親会に参加しているエンジニアはあくまで技術の話がしたいのであって、転職の話がしたいのではありません。仮に転職を考えていたとしても、そのようなデリカシーのない会社に入りたいとは思わないでしょう*3

社内の開発環境を整える

ここまで、良いエンジニアは他とは違う転職手法をとるために、それに合わせたアピールやブランディング、接点づくりが重要である、という内容を書いてきました。しかし、これだけを行ったのでは不十分です。

もし仮に頑張って良いエンジニアとの接点を作り「よかったらうちに来ませんか?」と口説いたところで、社内の開発環境が整っておらずエンジニアとして働きにくかったり、技術的に成長できなかったら採用には結びつきません。仮に運良く採用できたところで、すぐに辞めてしまいます。もっと良い環境の会社はたくさんあるからです。これでは接点を作った意味がないですよね。

先ほど書いた「社員が知り合いを誘ってくれる」という話も、働きやすい環境があってこそ成り立ちます。不満がたくさんある会社に仲の良い知り合いを誘うような人は少ないでしょう。それよりも、今在籍している社員がより良い環境を求めて旅立ってしまう可能性の方が高いです。

ではどのようにしたら、エンジニアが働きやすい環境にできるでしょうか?

まずはじめに、エンジニアが重視する価値観を理解する必要があります。エンジニアは、技術的な成長機会をとても重視しています。この話題についてはすでに素晴らしい記事が書かれているのでこちらを読みましょう。

ITエンジニア採用に欠かせない原則とは (1/5):IT人材ラボ

その他にも、エンジニアは合理的でない慣習を嫌います。簡単な例を挙げると、顧客と面会する必要がない職種なのにスーツ着用が義務付けれている、とか事前に準備をしておけば5分で終わる会議に準備せず臨みダラダラと1時間かける、というようなことです。

もちろん開発の進め方にも合理性を求めます。例えば、機能の拡張や修正をするときに間違いを少なく実施できるように、事前にテストコードを書いて不具合を検知できるようにしておくというプラクティスがあります。これはエンジニアたちの中で一般的に合理的だと考えられているやり方です。しかし上層部に理解がなく、テストコードを書くための工数を十分に与えられない、という現場があるとします。そうすると現場のエンジニアは修正の結果を修正のたびに目視で確認していかなければいけません。もっと合理的に解決できる手段があるのに…。

このようなケースでエンジニアたちの声に耳を傾け、状況を改善していけるとエンジニアにとって魅力的な職場になっていきます。

状況を改善するための前提として、権限を持ったひとを巻き込むことが大事です。現場のエンジニアだけでもある程度の改善は可能ですが、必ずどこかで行き詰まります。働きやすい環境を作るためには、上で書いたようにエンジニアの工数確保が必要なものがあったり、有償のツールの導入が必要だったりするからです。これらは権限を持った人がいないとできません。もしこれを読んでいるあなたに権限がなければ、権限のある人にこのエントリを読んでもらい「採用するためにはまず社内環境を改善する必要がある」ということを理解してもらってください。

権限を持った人の協力を得られる状況になったら、働きにくい要因を減らしていきましょう。会社によって要因は異なります。洗い出しには、社内のエンジニアにやりにくいと感じている点について聞くのが手っ取り早いです。他にも、定期的にKPTを行って改善点を都度挙げていく、というのも効果的だと思います。ただし、社内のエンジニアは現在の環境に慣れてしまっているため、改善できるポイントに気づかないこともありえます。もしツテがあれば、社外のエンジニアに頼んでアドバイスを貰うのも有益でしょう。

まとめ

良いエンジニアを採用するためには、接点を作ること、社内の開発環境を整えることの2つが重要であるという話を書きました。ここまで書いてきたことを実行すると、ようやく他の会社と同じラインに立てるかと思います。エンジニア採用は年々激化しており、エンジニア採用に力を入れている会社にとって、このエントリで書かれている内容は当たり前のことばかりです。一歩抜き出るには、ここで書かれていることを踏まえ、さらにその会社なりのやり方で工夫していくしかありません。頑張っていきましょう💪

*1:聞かれるたびに何度も同じ回答をするのは面倒なので、一度文章としてまとめておいて、次回以降は「このエントリ読んでください」で済ませたいという思いもあります

*2:アウトプットはないけど優秀な人が運良く来ることがあるかもしれないし、良いエンジニアが来る可能性もゼロではない

*3:エンジニア以外は勉強会やその懇親会に絶対参加してはいけない、ということではありません。採用以外の話であれば快く応じてくれる人は多いはずです