wwwave tech ブログ

株式会社ウェイブのエンジニアブログです。 ここではエンジニアの目線から会社の話や技術的な話をしていきたいと思います。

僕と ウェイブと リニューアルプロジェクトと

はじめまして。ナスの漬物といいます。よろしくお願いします。ナスは苦手です。

今回は、自分がウェイブに入って感じたことと、直近のプロジェクトについて技術的な内容に触れながら書いていきたいと思います。

じぶんについて

今年の1月にウェイブへ入社して、もうすぐ1年経ちます。
昨年までは、SIerのSEで業務システムの開発をしていました。いままで会計や、医療系のWebシステムを経験してきましたが、古いシステムのメンテナンス作業が多いため、最新の開発環境に触れる機会がありません。
このままでは自分自身が古いシステムと一緒に枯れていってしまうと感じ、転職を決意しました。

ウェイブとは転職活動中に出会いました。ウェイブへの転職を決意した理由は、最新技術に対する感度が高いこと。社員の成長を経営理念に掲げていること。そして社長が技術に明るいことです。

ウェイブについて

ウェイブでは、KPTAや1on1, 社内勉強会など業務や技術、環境を改善する機会があるので、自分の「考え」や「やりたいこと」をアピールできます。

前職よりも相対的に残業が少ないです。残業は最長22時までという社内規則もあるので、だらだらと残って残業せず早く帰る雰囲気ができていて、定時ダッシュでも気を使わず帰りやすいです。

また、オフィスは新築ビルに移転したばかりで、明るくてきれいなオフィスになりました。机も新調され、広くて仕切りがないので開放感があります。

さらに、ウェイブの文化として各自の趣味を重んじており、休み時間はカードゲームやNintendo Switchでリフレッシュしています!

ウェイブに入って一番大きく変わったことは、技術的な環境です。
まず、開発マシンがWindowsからMacに変わりました。プライベートでも好んでMacを使っているので、好きな環境で開発ができるのはモチベーション面でも、操作性でもすごくやりやすいです。

前職では、.NET, jQueryを使っていましたが、ウェイブでは、Ruby on Rails, React を使うようになりました。最初の頃はRubyの記法やドキュメントを探すのに時間が掛かってしまい、フラストレーションを感じていましたが、今ではスムーズに開発ができています。

前職では、Visual StudioとExcelをバリバリに使っていましたが、開発マシンがMacということもあって開発環境はターミナルとエディタに変わりました。でも、エディタはVisual Studio CodeをVimキーバインドで使ってます!

テスト手法も大きく変わりました。前職では手動でテストをしていましたが、ウェイブではテストコードを書いています。テストコードを書く大変さはありますが、デバッグし直すごとにデータを作り直す必要がなく、煩わしさが減って快適にデバッグできるようになりました。
チェック手段も変わりました。Excelに画面キャプチャを貼るエビデンス作成から、GitLabのマージリクエストを使ったレビューを行うチェック方法になりました。

なにより、前職では"職業プログラマ"といった感じで雰囲気の暗い現場で作業をしていましたが、ウェイブでは趣味を仕事にしている感じで毎日楽しくコーディングできています!

現在について

現在、あるサービスのリニューアルプロジェクトを担当しています。リニューアルと言ってもユーザから見える機能やデザインのリニューアルではなく、システム側のリニューアルです。

Ruby on Railsバージョンアップ

現在Rails 3, Ruby 1.9で動いているシステムをRails 5.1, Ruby 2.4に載せ替えるプロジェクトが進行中です。
今回は、下記について、今回のプロジェクトでどのようにコードをリニューアルしているのかをお伝えしたいと思います。

  • safe navigation operator
  • ActiveRecor::QueryMethods.#order
  • ActiveRecord::FinderMethods.#find_by
  • ActiveRecord::Relation#first_or_initialize
  • ActiveRecord::QueryMethods::WhereChain#where.not
  • minitest

safe navigation operator(ぼっち演算子)

safe navigation operator(ぼっち演算子)は、オブジェクトがnilの場合にメソッドの呼び出しでNoMethodErrorになるのを防ぐことができます。
ぼっち演算子を使うと、オブジェクトがnilの場合にnilを返し、その直後のメソッドは実行されません。

user = User.new(id: 1)
user_id = user.id # => user_id = 1
user_id = user&.id # => user_id = 1
user = nil
user_id = user.id
# => NoMethodError: undefined method `id' for nil:NilClass

user_id = user&.id # => user_id = nil

ちなみに、C#だと、

user_id = user?.id

です。C#だとnull許容演算子として?が使われておりint?とすることでnullを許可する整数型を定義できます。そのためC#では、?.が演算子に使われているのだと思います。

Rubyは、?をメソッド名に使用できるため、メソッド名との区切りが曖昧になることから?.は使えませんね。 しかし、以下の2種類の書き方が同等だというのを考えると、Rubyで&.が採用されたことに納得します。

name = user && user.name
name = user&.name

リニューアルプロジェクトでは、既存のコードに上記の前者のようなコードがいたるところに出てくるので、片っ端からぼっち演算子を使うように変更していきました。

ActiveRecord::QueryMethods.#order

orderは、Rails 3では引数なしでも実行可能でしたが、Ralis 5では引数が必須になっていました。 Rails 3でorderメソッドの引数を省略した場合、ORDER BY id ASCと解釈されるため、以下が同等になります。

Rails 3

User.order
# `SELECT "users".* FROM "users"
#  ORDER BY "users"."id" ASC` が実行される。

Rails 5

User.order(:id)
# `SELECT "users".* FROM "users"
# ORDER BY "users"."id" ASC` が実行される。

# 引数なしだとエラーになる。
User.order
# => ArgumentError: The method .order() must contain arguments.

ActiveRecord::FinderMethods.#find_by

Rails 3ではカラム名がメソッド名に入ったfind_by_xxxを使っていましたが、Rails 5では、find_byメソッドに置き換えられました。 メソッド名にあった、カラム名は、引数のハッシュのキーとして渡します。

Rails 3

User.find_by_name("太郎")

Rails 5

User.find_by(name: "太郎")

ちなみに、find_byは条件に一致するレコードの中で先頭の一件のみを取得します。つまり以下の2つのコードは同等になります。

user = User.where(name: "太郎").first
user = User.find_by(name: "太郎")

既存コードには、where.firstの方を使ったコードがいくつもあったので、find_byに置き換えていきました。

ActiveRecord::Relation#first_or_initialize

first_or_initializeは、レコードがあれば、firstメソッド、レコードがなければnewを実行します。

既存コード

user = 
  User.where(name: "taro").first || User.where(name: "taro").new

改善後

User.exists?(name: "taro") # => false
user = User.where(name: "taro").first_or_initialize
user # => #<User: id: nil, name: "taro">

first_or_initializeは、name == "taro"のユーザが存在すれば最初のレコードが返され、存在しなければメソッドチェーンの条件で初期化されたオブジェクトが返されます。 冗長なコードがかなりスッキリするので、これも積極的に置き換えていきました。

ActiveRecord::QueryMethods::WhereChain#where.not

Where.notは、SQLのNOTを表現するためのメソッドです。

NOT NULLは、いままで文字列で表現していましたが、これだとシンタックスハイライトが効きません。タイポしてもrubocopの静的チェックに引っかからないので、実行時エラーの危険がありました。

Rails3

User.where("name IS NOT NULL")
User.where("name IS NOT NIL") # => 静的解析ではエラーをチェックできないので、
                              #    実行時まで気づけない。

Rails 5

User.where.not(name: nil)
User.where.not(name: null) # => 静的解析でエラーの指摘が可能。
                           #    それ以前にシンタックスハイライトが効かないこと
                           #    でエラーに気づける。

Rails 5では、ActiveRecordのシンタックスとして書けることで、見やすくてエラーチェックもできて一石二鳥といった感じです。

minitest

リニューアルプロジェクトでは、minitestでテストを書くようになりました。 モックやスタブを扱いやすくするために、mochaも併せて導入しています。 mochaのany_instanceメソッドを使えば、全てのインスタンスに対して、モックやスタブを行うことができたり、withメソッドでモックしたメソッドに渡す引数のアサーションができるようになります。

require 'test/unit'
require 'mocha/test_unit'

class UserDouble
  def name
    "UserDouble"
  end
end

class ExampleTest < Test::Unit::TestCase
  test "stub sample" do
    User.any_instance.stubs(:name).returns('stubbed_name')
    user = User.new(name: "test_name")
    user.name  # => "stubbed_name" が返る。
  end

  test "mock sample" do
    # => User.new(name: "test_user")がすくなくとも1回以上呼ばれることを
    #    テストするアサーション。
    #    このモックの戻り値はUserDouble.newになる。
    User.expects(:new).with(name: "test_user").
      returns(UserDouble.new).at_least_once  
    user = User.new(name: "test_user")  # => assert success
    user.name # => "UserDouble" が返る。
    user = User.new  # => assert failedモックしたnewに渡す引数が期待値
                     #    と一致しない。
  end
end

まとめ

リニューアルプロジェクトでは、細かなコードの改善だけでなく設計の見直しも行なっており、機能によっては全く違った実装になっています。
ユーザから見える機能は、ほとんど変わりませんが、速度面では改善が行われています。

今回のリニューアルプロジェクトに関わらず、ウェイブのサービスは常に改善を行なっています。これからもより良いサービスを目指して改善を進めていきます。

これからもウェイブのサービスをよろしくお願いします。