RubyKaigi 2024のファインディブースで出したRuby Code Quiz解説

こんにちは、あるいはこんばんは。 @gessy0129です。

沖縄、行ってきました!

観光、しませんでした!

とても楽しかったです!

今回、ファインディブースでは、Ruby歴×Ruby関連のカンファレンス参加回数は?というアンケートと日替わりでRuby Code Quizを実施しました! 全体的なブースの話はDevRelのまっきーがまとめているのでそちらをご覧ください!

note.com

本記事では、エンジニアなら気になるであろうFindy Ruby Code Quizの解説をしていきたいと思います!では、早速見ていきましょう!

一日目 - Rubyバージョンの組み合わせ

問題

下記、実行結果のRubyバージョンの組み合わせを答えてください。 なお、ひとつのバージョンはひとつの選択肢にのみ回答できます。

Ruby versions: 3.3.0 or 3.2.0 or 3.1.0 or 3.0.0

irb> Time.new("2024-05-15T11:11:11Z")
=> 2024-05-15 11:11:11 UTC

irb> Time.new("2024-05-15T11:11:11Z")
=> 2024-01-01 00:00:00 +0000

irb> Time.new("2024-05-15T11:11:11Z")
<internal:timev>:310:in `initialize': invalid value for Integer(): "2024-05-15T11:11:11Z" (ArgumentError)

irb> Time.new("2024-05-15")
<internal:timev>:409:in `initialize': no time information (ArgumentError)

選択肢

  1. ①: 3.2.0、②: 3.0.0、③: 3.1.0、④: 3.3.0
  2. ①: 3.3.0、②: 3.2.0、③: 3.1.0、④: 3.0.0
  3. ①: 3.1.0、②: 3.0.0、③: 3.2.0、④: 3.3.0
  4. ①: 3.0.0、②: 3.1.0、③: 3.3.0、④: 3.2.0

正解と回答率

正解

  • ✅ 1. ①: 3.2.0、②: 3.0.0、③: 3.1.0、④: 3.3.0

回答率

  1. 14.15%
  2. 53.77%
  3. 19.81%
  4. 12.26%

解説

RubyのバージョンアップのNEWSを追いかけていれば解けるという問題です。

Ruby3.3.0のNEWS
https://github.com/ruby/ruby/blob/v3_3_0/NEWS.md

Time.new('2023-12-20')
#  no time information (ArgumentError)

これを知っていたら④が3.3.0だとわかります。

Ruby3.2.0のNEWS
https://github.com/ruby/ruby/blob/v3_2_0/NEWS.md

Time.new now can parse a string like generated by Time#inspect and return a Time instance based on the given argument. 

Time.newはinspectが出力するstringからパースできるようになった。これで、①は3.2.0以上とわかります。

Ruby3.1.0のNEWS
https://github.com/ruby/ruby/blob/v3_1_0/NEWS.md

At the same time, time component strings are converted to integers more strictly now.
Time.new(2021, 12, 25, "+07:30")
#=> invalid value for Integer(): "+07:30" (ArgumentError)
Ruby 3.0 or earlier returned probably unexpected result 2021-12-25 07:00:00, not 2021-12-25 07:30:00 nor 2021-12-25 00:00:00 +07:30.

そもそも3.1.0までは文字列だけをいれるユースケースはなかったようです。3.0.0以前は文字列だけだとよくわからない挙動になっていたけど、この対応でチェックを厳密にしたことで文字列だけだとエラーになったのではないかと推測します。変更意図などはCommiterの方々に解説求めたい部分です。

ただ、このNEWSの内容を全てを記憶してるのは難しすぎになってしまうので、回答の選択肢を4択にしています。4択なので3.3.0の挙動を知っていたら1番目と3番目の2択に絞れると思います。
これに加えて、3.2.0の挙動を知っていたら①は3.2.0以上とわかるので1番目が答えだとわかります。また、3.2.0の挙動を知らなかったとしても、①〜③は全く同じコマンドを実行しており①が正しく挙動しているので①の方が新しいバージョンだと推測できます。これでも1番目が答えだとわかるようになってます。

二日目 - 代入されているオブジェクトはどれ?

問題

fooに代入されているオブジェクトは次のうちどれでしょうか? (Ruby: 3.3.0で実行した場合)

irb(main):001> foo = ???
irb(main):002> foo.include?(3/2)
=> true
irb(main):003> (foo + [3]).inject(:+)
=> 9
irb(main):004> foo.last
=> NoMethodError

選択肢

  1. 1..3
  2. [1, 2, 3]
  3. Set.new([1, 2, 3])
  4. Enumerator.new { |y| [1, 2, 3].each { |i| y << i } }

正解と回答率

正解

  • ✅ 4. Enumerator.new { |y| [1, 2, 3].each { |i| y << i } }

回答率

  1. 25.37%
  2. 4.88%
  3. 39.02%
  4. 30.73%

解説

メソッドに対しての出力結果から元の値を当てるという問題形式を使ってみたかった問題です。 そこから逆算して、ちょっと似ているクラス(Range, Array, Set, Enumerator)を当てるようにしてみました。

irb(main):002 の出力は引っ掛けで、3/2 => 1になるのですべてtrueになる。
が、直感的には3/2 => 1.5という考えを利用して、1..3では?と思わせて問題を難しくしました。

irb(main):003 は、同じ値は重複しないというSetの特徴を利用から、Setを除外できるようになっている。また、Rangeには+メソッドがないのでエラーになる。

irb(main):004 は、Setには順番がなく、Enumeratorは外部イテレータなので最後の要素が特定できないという特徴を利用している。

実行結果を見るのが一番早いですね。

選択肢1

irb(main):001> foo = 1..3
=> 1..3
irb(main):002> foo.include?(3/2)
=> true
irb(main):003> (foo + [3]).inject(:+)
(irb):3:in `<main>': undefined method `+' for an instance of Range (NoMethodError)
irb(main):004> foo.last
=> 3

Rangeには+メソッドがないのでNoMethodErrorになるのと、lastメソッドがあるので結果が返ってきます。

選択肢2

irb(main):001> foo = [1, 2, 3]
=> [1, 2, 3]
irb(main):002> foo.include?(3/2)
=> true
irb(main):003> (foo + [3]).inject(:+)
=> 9
irb(main):004> foo.last
=> 3

lastメソッドがあるので結果が返ってきます。

選択肢3

irb(main):001> foo = Set.new([1, 2, 3])
=> #<Set: {1, 2, 3}>
irb(main):002> foo.include?(3/2)
=> true
irb(main):003> (foo + [3]).inject(:+)
=> 6
irb(main):004> foo.last
(irb):4:in `<main>': undefined method `last' for an instance of Set (NoMethodError)

同じ値は重複しないというSetの特徴から003の結果が6になります。

選択肢4

irb(main):001> foo = Enumerator.new { |y| [1, 2, 3].each { |i| y << i } }
=> #<Enumerator: ...>
irb(main):002> foo.include?(3/2)
=> true
irb(main):003>  (foo + [3]).inject(:+)
=> 9
irb(main):004>  foo.last
(irb):4:in `<main>': undefined method `last' for an instance of Enumerator (NoMethodError)

正解ですね!!

有識者の方に解説モトム

Setには順番がないと書きましたが、firstはあります。なので、こうなります。

irb(main):001> foo = Set.new([1, 2, 3])
=> #<Set: {1, 2, 3}>
irb(main):002> foo.first
=> 1

なんでfirstがあるのか、詳しい方のコメントを待っています。Enumerableを継承しているからと言われたらそれまでと言えばそれまでですがw

三日目 - 正しい出力結果は?

問題

次のコードを実行した出力結果で正しいのはどれでしょう? (文字コード UTF-8、Ruby: 3.3.0で実行した場合)

hoge = [?1,?2].zip([?3,?4], [?5,?6]).transpose.flatten
hoge.reject!
hoge = hoge.map{|n| n.ord}.sort
puts hoge.join(", ")

選択肢

  1. 1, 2, 3, 4, 5, 6
  2. 6, 5, 4, 3, 2, 1
  3. 49, 50, 51, 52, 53, 54
  4. 54, 53, 52, 51, 50, 49

正解と回答率

正解

  • ✅ 3. 49, 50, 51, 52, 53, 54

回答率

  1. 17.19%
  2. 14.06%
  3. 60.94%
  4. 7.81%

 解説

問題文のコードを読みにくくするため、わざとインデントやスペースを入れていない。というちょっとイジワルをしました。
[Integer, Integer]に対して、なにかしら変換するのはひっかけるポイントが少ないので、String→Integerへ変換するようにして、複雑さを増すようにしています。

# zipメソッドを使う人はあまりいなそうなので、採用
> hoge = [?1,?2].zip([?3,?4],[?5,?6])
=> [["1", "3", "5"], ["2", "4", "6"]]

# ノーマルなRubyistは2次元配列を扱うことが少ないはずなので、transposeメソッドを採用
irb(main):003> hoge = hoge.transpose
=> [["1", "2"], ["3", "4"], ["5", "6"]]

# Rubyistはflattenを結構使っている気がする。ここは配列を入れ子にしたくないから、採用
irb(main):004> hoge = hoge.flatten
=> ["1", "2", "3", "4", "5", "6"]

# なにかやっていそうだけど、Enumeratorに変えているだけ
irb(main):006> hoge = hoge.reject!
=> #<Enumerator: ...>

# ordメソッドが、StringからIntgerへ変換することを知っているかチェック
# sortメソッドが降順 or 昇順どちらで並び替えているか試す
irb(main):007> hoge = hoge.map{|n|n.ord}.sort
=> [49, 50, 51, 52, 53, 54]

irb(main):008> puts hoge.join(", ")
49, 50, 51, 52, 53, 54

さいごに

4/1 からクイズ作成を開始して、約4日ほどでほぼほぼ今の形まで出来上がりました。ラフで色んな案を出して、煮詰めていく過程がとても楽しかった(らしい)です。

また、 knuさんがブースに遊びに来てくれてPostしてくれていたのがとても嬉しかったです!来ていただいてありがとうございました!

問題は@hamchance0215@aiandrox@sontixyouが作成してくれました!ありがとうございました!

5/28には「After RubyKaigi 2024〜メドピア、ZOZO、Findy〜」として、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2024の振り返りを行います。

LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!!

findy.connpass.com

ファインディでは、これからもRubyを積極的に活用して、Rubyとともに成長していければと考えております。

そして、一緒に働くメンバーを絶賛募集中です。

興味がある方はこちらから ↓

herp.careers

RubyKaigiで紹介されたGem「PBT(Property Based Testing)」を試してみた

こんにちは!ファインディでTeam+開発チームのEMをしている浜田です。

以前公開した記事「ファインディはRubyKaigi 2024 にPlatinum Sponsorsとして協賛します!」で紹介した通り、ファインディはRubyKaigi 2024に協賛しており、現地で参加してきました!

tech.findy.co.jp

今週(5/20〜25)はRubyKaigi 2024の振り返りも兼ねてRubyKaigiに関連した記事を投稿していきます!

この記事では、私が聞いたセッションの中の1つ「Unlocking Potential of Property Based Testing with Ractor 」で紹介されたGem「PBT」を試してみたので共有します。

Unlocking Potential of Property Based Testing with Ractor

Unlocking Potential of Property Based Testing with Ractor 」は、Property Based TestingをRubyで実行するためのGemを作成し、さらにRactorを活用することでテストパフォーマンスを向上させる話でした。

speakerdeck.com

セッションの中でも触れられていましたが、Property Based Testingを聞いたことがある方は20%ほどとのことだったので、この記事でも簡単に紹介したいと思います。

Property Based Testingでは、従来の入力値を開発者が列挙してテストする手法(セッションではExample Based Testingと呼んでいました)とは異なり、入力値の性質(プロパティー)を指定してテストケースを自動生成して網羅的なテストを行うことができます。

私は以前ファインディ主催で開催した「t-wadaさん、ymotongpooさんに聞くテスト戦略最前線」というイベントでt-wadaさんから紹介されて興味を持ちました。 スライドも公開されているので、興味がある方はぜひご覧ください。

speakerdeck.com

この記事では、Gem「PBT」を使ってProperty Based TestingをRubyで実装しつつ、従来のテスト手法であるExample Based Testingとの違いを比較します。

試してみた

それでは早速試していきます。 今回使用したRubyと主要なGemのバージョンは次の通りです。

  • Ruby 3.3.1
  • PBT 0.4.1
  • RSpec 3.13.0

今回は入力値を3倍にして返却する tripleというメソッドを実装します。単純にするため入力値は整数のみ考慮します。

class PropertyBasedTest
  def self.triple(value)
    # valueを3倍にして返す
  end
end

それではまずいつも通り入力値を開発者が列挙してテストするExample Based Testingでテストを書きます。なお、テストはRSpecを使用します。

tripleのテストを書く場合、皆さんはどのような入力値のパターンでテストを書きますか? 今回は、正の整数 / 負の整数 / 0 の3パターンのテストケースを書きました。 Example Based Testingの場合、開発者がテスト条件を満たす任意の値を選んでテストコードを書くため、適当に選んだ22 / -5 / 0を指定しました。

RSpec.describe PropertyBasedTest do
  describe '.triple' do
    context 'when example-based testing' do
      subject(:add) { described_class.triple(number) }

      context 'when input value is 22' do
        let(:number) { 22 }

        it { is_expected.to eq 66 }
      end

      context 'when input value is -5' do
        let(:number) { -5 }

        it { is_expected.to eq(-15) }
      end

      context 'when input value is 0' do
        let(:number) { 0 }

        it { is_expected.to eq 0 }
      end
    end
  end
end

実行結果は次の通り。無事テストが通りました🙌

PropertyBasedTest
  .triple
    when example-based testing
      when input value is 0
        is expected to eq 0
      when input value is 22
        is expected to eq 66
      when input value is -5
        is expected to eq -15

Finished in 0.1124 seconds (files took 4.22 seconds to load)
3 examples, 0 failures

ところが、tripleメソッドには未知のバグが埋め込まれていました。 3の倍数、もしくは3のつく入力値が与えられた場合は異常終了するというバグです。

今回のテストではたまたま3の倍数や3がつく数値をテストケースに含めていなかったため、このバグを見逃してしまいました。

このような見逃しを回避するための手法として、Property Based Testingが有効です。 Property Based Testingは、入力値の性質(プロパティー)を指定してテストケースを自動生成し、網羅的なテストを行うことができます。

では、PBTを使ってテストを書いてみましょう。 テストコードを見ていただければわかる通り入力値の具体的な値は指定しておらず、整数であること(Pbt.integer)のみを指定しています。

RSpec.describe PropertyBasedTest do
  describe '.triple' do
    context 'when property-based testing' do
      it do
        Pbt.assert(verbose: true) do
          # 入力値が整数であることを指定
          Pbt.property(Pbt.integer) do |number|
            result = described_class.triple(number)
            # 結果が3の倍数であることを確認
            expect(result).to eq number * 3
          end
        end
      end
    end
  end
end

実行結果は次の通り。正しくエラーが検出されました。

Randomized with seed 19396

PropertyBasedTest
  .triple
    when property-based testing
      example at ./spec/models/property_based_test_spec.rb:42 (FAILED - 1)

Failures:

  1) PropertyBasedTest.triple when property-based testing
     Failure/Error:
       Pbt.assert(verbose: true) do
         Pbt.property(Pbt.integer) do |number|
           result = described_class.triple(number)
           expect(result).to eq number * 3
         end
       end

     Pbt::PropertyFailure:
       Property failed after 5 test(s)
         seed: 113103392077172016306707556593348939592
         counterexample: 3
         Shrunk 8 time(s)
         Got RuntimeError: (´Д`) サンッ!!
           app/models/property_based_test.rb:5:in `triple'
           spec/models/property_based_test_spec.rb:45:in `block (6 levels) in <top (required)>'
           (長いので省略)

       Encountered failures were:
       - 614700
       - 307350
       - 153675
       - 76838
       - 38419
       - 4803
       - 301
       - 38
       - 3

       Execution summary:
       . √ -929894
       . √ 440110
       . √ -801140
       . √ -194044
       . × 614700
       . . × 307350
       . . . × 153675
       . . . . × 76838
       . . . . . × 38419
       . . . . . . √ 19210
       . . . . . . √ 9605
       . . . . . . × 4803
       . . . . . . . √ 2402
       . . . . . . . √ 1201
       . . . . . . . √ 601
       . . . . . . . × 301
       . . . . . . . . √ 151
       . . . . . . . . √ 76
       . . . . . . . . × 38
       . . . . . . . . . √ 19
       . . . . . . . . . √ 10
       . . . . . . . . . √ 5
       . . . . . . . . . × 3
       . . . . . . . . . . √ 2
       . . . . . . . . . . √ 1
       . . . . . . . . . . √ 0
     # ./spec/models/property_based_test_spec.rb:43:in `block (4 levels) in <top (required)>'

Finished in 0.08717 seconds (files took 4.23 seconds to load)
1 example, 1 failure

PBTではデフォルトで100通りのテストを実行します。 Property failed after 5 test(s)と表示されているので、5回目のテストでエラーが発生したとわかります。

counterexampleにはエラーが発生した入力値のうちShrunkして発見した最小の値が表示されています。今回は3が入力された場合にエラーが発生したことがわかります。 また、Shrunk 8 time(s)とのことなので、8回Shrunkして最小の値3を見つけたことがわかります。

無事バグが検出できたので、コードを修正して再度テストを実行します。 今回は無事テストが通りました🎉

PropertyBasedTest
  .triple
    when property-based testing
      example at ./spec/models/property_based_test_spec.rb:42

Finished in 0.09535 seconds (files took 4.28 seconds to load)
1 example, 0 failures

このようにProperty Based Testingを活用することで、開発者に依存した入力値ではなく、網羅的なテストを行うことができるため、開発者が予見できていないバグを見つけやすくなります。

今回のサンプルコードは単純な処理なのでテスト実行時間にほとんど差がありませんが、通常はPBTの方が試行回数が多くなるためテスト実行時間は長くなります。そのため、様々なインプットのパターンを網羅すべきテストケースなど適材適所で活用いきたいと思います!


5/28には「After RubyKaigi 2024〜メドピア、ZOZO、Findy〜」として、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2024の振り返りを行います。

LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!!

findy.connpass.com

ファインディでは、これからもRubyを積極的に活用して、Rubyとともに成長していければと考えております。

そして、一緒に働くメンバーを絶賛募集中です。

興味がある方はこちらから ↓

herp.careers

【エンジニアの日常】エンジニア達の自慢の作業環境を大公開 Part3

こんにちは。

FindyのFreelance開発チームの久木田です。

今回は 自慢の作業環境を大公開 のPart3になります。

前回までの記事はこちら ↓

👉 自慢の作業環境を大公開シリーズ

Part3は関西特集ということで、大阪と京都からフルリモートでJOINしているエンジニアたちの作業環境を公開します!

作業環境を大公開

久木田

まずは大阪から JOIN している久木田の作業環境から紹介していきます。

作業環境の全体像はこんな感じになっています。

ディスプレイ3台PC2台おいても広々スペース

会社支給の MacBook Pro 14インチ をノート用スタンドに載せて使っています。

ディスプレイはノートPCのディスプレイを含めて4枚使っていて、左からブラウジング用・コーディング用・Slack用・Datadog等の監視ツールのダッシュボード閲覧用として使っています。

このデスクですが、工務店を営む父親に頼んで、一緒に手伝いつつ、天板の木材から切り出して、取り付けてもらったものになります。

音声環境周りですが、自分は Anker PowerConf S3 というスピーカーフォンを使っています。

スピーカーフォン:Anker PowerConf S3

マイクも色々試しましたが、途中で耳を塞ぐのが個人的に好きじゃなかったのに気がついてから、このスピーカーフォンを愛用しています。

ミュートボタンが Zoom と Google Meet のミュートと連動してくれているのもお気に入りポイントです。

ちなみにカメラは MacBook のカメラの方が、市販されている廉価なWebカメラより性能が良いと思っているので、そのまま使っています。

キーボードは HHKB Professional2を使っていて、トラックボールは Kensington スリムブレードトラックボール を使っています。

HHKBのUS配列が好みです

ボールを横回転させてするスクロールが慣れると快適

キーボードは大学院生の頃からHHKBが好きで(当時は廉価版のHHKB Lite2 for Macでしたが)、この画像のキーボードは社会人になった7年前くらいからずっと使っているものです。

トラックボールは前職の上司の方からお下がりとしてもらって以来、このKensingtonのスリムブレードを使っていて、今使っているものは2代目になります。

トラックボールも親指タイプのものなど色々試した結果、このデバイスに落ち着きました。

広い画面を移動するのに、トラックボールはおすすめです。

そして使っている椅子もこだわっているものの1つになりまして、ハーマンミラーの エンボディゲーミングチェア を使っています。

背面の青いデザインも気に入っています

大阪にハーマンミラーストア心斎橋があり、そこへ行って色々な椅子を試した結果、この椅子を選びました。

背もたれが背骨のカーブにフィットしているので体重重めの自分も快適に座れています。

後ろにデスクトップPCが見えていますが、これは私用のPCで左のディスプレイの入力切替をして使っています。

ディスプレイの奥にあるのが自分でパーツを取り寄せて組み立てたPC

前回や前々回の作業環境紹介では私用と仕事用でデバイス共用している人が多かったですが、自分はmacとwindowsでOSが違っていたり、トラックボールだとゲームがやりづらかったりするので、手元のデバイスも全く別のものを用意して使っています。

最後に机の角に飾っている好きなものごちゃまぜディスプレイを写して自分の作業環境紹介は終わります。

攻殻機動隊・ウマ娘・ガンダム・にじさんじ

金丸

次に京都から JOIN している金丸の作業環境を紹介します。

作業環境の全体はこのようになっています。

ディスプレイとノートPCを最小限のスペースで設置しています

会社支給の MacBook Pro 14インチ をノートPC用アームに載せて使っています。 デスクの幅が120cmのためノートPCを宙に浮かせることでスペースを節約しています。

デスクには COFOの昇降デスク を利用し、定期的に立ち上がって作業をするようにしています。 天板の裏がマグネットになっているため、マグネットフックで雑に収納できるところが気に入っています。

ディスプレイは HP社の曲面ディスプレイ を利用しています。

ディスプレイに設置したライトで常に明るい

WQHD、USBハブ機能、USB-C(USB PD) に対応しているため、給電 + USB ケーブル1本で出来るところが気に入っています。 ウルトラワイドなディスプレイですが曲面になっているため違和感なく使えています。

スピーカーにはクラウドファンディングで購入した ovo を利用しています。

マットな金色がデスクに映えます

コンパクトな本体でも音割れが少なく、気に入っています。

通話には eMeet Luna を利用しています。

有線/Bluetooth切替可能で取り回し◎

本体にノイズキャンセリング機能がついているため、タイピング音などが軽減されるところが気に入っています。

キーボードは分割キーボードの 7sPro を利用しています。

レトロっぽいキーキャップが好み

「HHKBの配列で分割キーボードを使いたい」という要望を満たしているキーボードで、キースイッチを変えながら3年くらい使っています。 ホットスワップに対応しているのでキースイッチの変更が出来るところもお気に入りポイントです。 現在は alpacaスイッチ をセットして利用しています。

マウスは MX MASTER 3Sを利用しています。

勢いよく動かしてもカーソルがブレない点がお気に入りです

トラックパッド、トラックボールと試してみたのですが、結局マウスに戻ってきて以来、このマウスを利用しています。

最後に推しを詰め込んだディスプレイを紹介して、作業環境紹介を終わります。

カニさん・ガブモン・幽遊白書・ズートピア

西村

最後に、京都からフルリモートで Findyの転職サービス を開発している西村の作業環境を紹介します。

妻と同じ部屋でリモートワークをしているため、机を向かい合わせるように設置しています。奥に見えるのが妻側のデスクです。

あまり広くない部屋なので、作業スペースもコンパクトです

ディスプレイは DELL U2720QM を使用しています。4K 27インチのディスプレイなので、これ1つでも十分に作業が可能です。 MacBook Pro 16 をスタンドに載せて、デュアルディスプレイとしています。MacBook は主に Slack 専用としています。

こだわりポイントとしては、目線を下げないためにディスプレイをスタンドの上に置いていることです。 猫背気味で首や肩に負担を掛けがちなので、それを軽減できないかなと考えてこのようにしています。

また、ディスプレイと正対するように座ることも意識しています。 リモートワークを始めた初期に、目線を左右どちらかに寄せ続けて首や腰の片側だけが痛くなってしまった。という苦い経験から、このような工夫をしています。

ただ、少し目が疲れやすく感じるので、本来であればもう少しディスプレイとの距離を取りたいと考えています。 しかし部屋の広さの関係で、これ以上奥行きのあるデスクを置くことができないので、この状態で我慢しています。

キーボードは Keychron K2 を使用しています。

Keychron K2 のキーボードと木製のパームレスト

元々は HHKB を使ってみたいなと思っていたのですが、値段が高くてなかなか手が出せていなかったところに前職時代の先輩に勧められて購入しました。 比較的安価だったので試しに使ってみたところ、とても打ちやすく非常に気に入っています。かれこれ3年以上経ちました。

マウスは Apple の Magic Trackpad を使っています。マウスに対するこだわりはなく、友人からプレゼントしてもらったものをずっと使い続けています。

また、手首から先の負担を減らす目的で、 FILCO のパームレスト を使っています。肌に優しそうという理由で木製のものを選びました。

今回はデスクの写真を撮るタイミングだったのでかなりスッキリとしています。Findyでは社内輪読会も行っており、業務時間中にもチームで同じ書籍を読みエンジニアのレベルアップを図っています。日常的には、その輪読会用の書籍が置いてあったりすぐに読み返したりできるようにしています。

購入して1年ほど経ってから成長の兆しが見え始めたお気に入りのサボテン

まとめ

いかがでしたでしょうか?

やはり、キーボードにはそれぞれこだわりがありましたね!

また、今回の記事を通して意外にもスピーカーマイクを使っているのが自分だけではないことに驚きました。 自分の環境でも記載しましたが、マイクの物理スイッチはおすすめなので是非試してみてください!

現在、ファインディでは一緒に働くメンバーを募集中です。

興味がある方はこちらから ↓ herp.careers

Findy転職フロントエンドの開発生産性を向上させるためにやったこと

こんにちは、ファインディ株式会社でフロントエンドのリードをしております 新福(@puku0x)です。 この記事では、転職サービス Findy の開発チームにおける開発生産性の向上に対する取り組みをご紹介します。

以前の状況

2020年頃の Findy は Ruby on Rails と React のモノリス構成で作られていました。 機能の増加に従いコードが複雑化し、しだいに開発スピードが伸び悩むようになりました。

ここで Findy Team+ で算出した当時のリードタイムを見てみましょう。

2020年のFindyのリードタイム

上記のグラフから次のことがわかります。

  • 改修が本番に適用されるまで 約1週間 かかる
  • プルリクエストがレビューされるまで 約5日 放置される
  • プルリクエストのレビュー完了までに 約2日と半日 かかる

1年を通してプルリクエスト数の平均が2.0を超える日が無かったことからも苦しい状況であったことが推測できます。

モノリスの解体

前述の通り Findy は Rails モノリスで作られており、CI にフロントエンドとバックエンドの両方が含まれていることから、画面の文言を1つ更新するだけでも長い CI 待ちが発生します。

この状況を打破するために、Findy で最初に行われた取り組みは「Rails モノリスの解体」でした。

約3ヵ月かけてバックエンド側を Rails の API モード、フロントエンド側を Next.js で再実装するという大掛かりなプロジェクトでしたが、これによりフロントエンドとバックエンドで独立して動けるようになったため、先述した CI 待ちを大幅に短縮できました。

実際の効果は note の記事 にて弊社 CTO が紹介しておりますので、そちらも是非ご一読ください。軽微な改修であれば1日で修正するスピードを実現し、最速でユーザーに価値を届ける基盤を作ったのがモノリス解体の大きな効果と言えるでしょう。

開発基盤の刷新

2021年5月、モノリス解体後のフロントエンドが晴れてリリースされました。大きな不具合もなく無事に終わったと安心したいところですが、まだ残っている課題を解決しなければなりません。

フロントエンド側に残された課題は次の通りです。

  • バージョンの古いツール・ライブラリが多数
  • コンポーネントの書き方が古い
  • 型(Flow)はあるがうまく動作していない
  • テストが書かれていない
  • 見通しの悪いフロントエンド設計

最初に、依存ライブラリのアップデートや使われなくなったライブラリを削除していきました。また、Dependabot を導入し定期的に更新する体制を整えました。

次に Nx を導入し、モダンな開発環境へ移行していきました。Nx は前職で利用した経験があり、コマンドひとつで TypeScript + React + Jest + ESLint + Prettier が揃った開発環境を作成できるため移行は短期間で済みました。ここで導入した Nx は、後の CI 高速化への布石となります。

Findy には開発当初から React が用いられておりましたが、この時はまだコンポーネントが Class Component で書かれてありました。今後の React エコシステムの発展に追従するべく Function Component への書き換えも同時に進めました。

Flow から TypeScript への移行は直接書き換えるのが難しかったため、一度全てのソースコードから Flow の型アノテーションを外して純粋な JavaScript にし、その後 TypeScript で書き直しました。移行の早い段階で strict: true で書くようにしたため、Null チェック漏れによる不具合を大幅に削減できたと思います。

当時は API レスポンスに対しても手作業で型付けを行っていましたが、API の仕様が複雑であったりドキュメントと同期するのが難しかったことから、後に REST API から GraphQL に移行し、GraphQL Code Generator による型生成が採用されるようになります。

コンポーネント設計の刷新

当時の実装では、コンポーネントは Container Component / Presentational Component のパターンで書かれてありました。メンバーの習熟度やテストのしやすさを考慮し、基本設計は踏襲しつつ、下記に示すようなデータの流入元に着目した三層構造へと拡張しました。

コンポーネント名 カスタムフック名 扱うデータ
Page Component Params Hook ブラウザURL
Container Component Facade Hook API や ストレージ等
Presentational Component Presenter Hook フォーム

各層での責務が明確になったことで実装時の混乱が少なくなったと思います。

余談ですが本設計は筆者が Angular コミュニティから得られた知見を React アプリケーションに転用したものとなります。不思議な縁もあるものですね。*1

テストの拡充

プロジェクト進行中にいくつかの不具合に遭遇することがありましたが、それらのほとんどはテストがあれば防げたものでした。将来的な変更に耐えられるよう、テストの拡充は早期に着手しました。幸いバックエンド側では既に「テストを書くのは当たり前」という文化が根付いていたため、フロントエンドのテスト導入のハードルは高くありませんでした。

当時はまだメンバー全員がフロントエンドのテストに慣れている訳ではなかったため、テストのお手本となるような実装例を増やしていくことから始めました。まず、ユーティリティ等の入出力の関係が明らかなものからユニットテストを書き、次にコンポーネントのスナップショットテスト、それからインタラクションテストや結合テストといったように範囲を広げていきました。

また、テストを書くモチベーションを向上させる工夫として Wallaby.js のような可視化ツールを導入しました。Wallaby.js については次の記事もご参照ください。

tech.findy.co.jp

今では全員が当たり前のようにフロントエンドのテストを書くようになりました。テストによって守られているという安心感もさることながら、テストを書くことが習慣化したことによって「テストを書きやすいように設計が洗練される」といった副次的な効果が得られたのも嬉しいところでした。

CI の高速化

コードベースが増え、ビルドやテストの実行時間が増えていくと、CIの待ち時間も長くなっていきます。CI 時間の増加はリードタイムの増加に直結するため、改善は必須でした。

開発基盤を刷新するにあたり、CI 時間の増加は予め想定されていたため Nx を導入し、 nx affected コマンドによる変更検知によって必要なジョブのみ実行することで CI の高速化を図りました。ジョブの再実行といった変更検知だけでは高速化が難しいものに対しては Nx Cloud を導入し、リモートキャッシュを活用した高速化を実施しました。

結果は次の通りです。CI 時間は平均6分で、キャッシュヒットしない場合は16〜17分かかることもありますが、早い時は2〜3分で終わるようになりました。

ジョブ単位の詳細を次の図に示します。

Nx Cloudによる高速化の内訳

Nx Cloud 導入により300時間以上の CI 時間を削減できました。ここではフロントエンド側のCI高速化を取り上げましたが、バックエンド側の取り組みも記事にされておりますので、ご興味のある方は是非ご一読ください。

tech.findy.co.jp

改善の効果

それでは、モノリス解体をはじめとした様々な取り組みの成果を見ていきましょう。次の図は2023年における転職サービスの Findy のフロントエンドとバックエンドそれぞれのリードタイムを計測した結果を示しています。

フロントエンド バックエンド

リードタイムが100時間を超えていた状況から大幅な改善ができました。特にフロントエンドに関してはマージまでの時間が8時間を切っていることから、高速な開発を実践できていると言えます。

フロントエンドのアクティビティの変化についても見ていきましょう。モノリス解体が実施された2021年と比較すると、近年のアクティビティ量は著しく増加しています。

2021年 2023年

もちろんこれはメンバーの増加が主な要因として考えられますが(採用チームの皆さんいつもありがとうございます)、それだけでなく、一人あたりのアクティビティが増えた(=一人あたりのパフォーマンスが向上した)ことも結果に貢献していると思います。

2021年(1人あたり) 2023年(1人あたり)

2021年当時の倍のアクティビティを示しているので、単純計算で開発生産性も倍になっていると言えますね!頑張った甲斐がありました 💪

まとめ

ファインディでは「モノリスの解体」「開発基盤の刷新」「CI の高速化」を実施し、開発生産性を向上させることに成功しました。

数年前まではファインディも開発スピードに伸び悩んでいたと知って驚いた方も多いかもしれません。ここ数年で大きく成長できたのは社内の優秀なメンバーの協力あってのものだと感謝しています。

ここまでご覧いただいた方は、ファインディはそこまで特別なことをやっている訳ではない とお気付きになられたかと思います。

1つ1つの積み重ねが今の私達の開発を支えています。

日々の改善を大事にしていきましょう。


ファインディは積極的にエンジニアを採用しています。CI/CD を始め、Four Keys、開発生産性、技術トレンド、転職市場など興味のある方は、お気軽にカジュアル面談を受けてみてください。

ファインディの採用情報はこちら ↓

herp.careers

ファインディはRubyKaigi 2024 にPlatinum Sponsorsとして協賛します!

こんにちは、あるいはこんばんは。 @gessy0129 です。

ファインディは昨年に続きRubyKaigi 2024 でPlatinum Sponsorsとして協賛します。RubyKaigi 2018からここまで継続して協賛できているのは、様々な御縁のおかげだと感じています。Rubyコミュニティおよびファインディを応援してくださる方には感謝の気持ちでいっぱいです。

Platinum Sponsorsとして実施すること

今年のファインディは、ブース出展とDrinkupを2件実施させて頂く予定です。

Drinkupはconnpass上にオープンさせて頂いています。

findy.connpass.com
findy.connpass.com

ほぼほぼ埋まってしまいましたが、ぜひご参加頂けると嬉しいです!

ブースには、Findy Team+の開発リーダーである@ham0215をはじめ、@yuichiro826、CTOの@ma3tkやVPoEの@kxmxyxが参加します。僕もいる予定ですので、ぜひ遊びに来てください!

ブースはこちらです!

ブース内容の全貌については、現在、最終の内容をFixさせるための作業を実施中です。ご期待頂ければ幸いです! 一部、公開させて頂きますと、Ruby Code Quizというクイズを用意しております。開催期間中は内容が毎日変わるので、ぜひ毎日挑戦しにきてください!

前年からの振り返り

昨年のRubyKaigiから早一年が経過しました。この間のファインディの変化を、一年前と比較しながらご紹介します。主にRubyに関係することをメインにピックアップさせて頂きました。

プロダクト開発状況変化

Findy Team+を使って、弊社がRubyで開発している主要バックエンドリポジトリの確認をしました。
2023年4月と2024年4月の対比です。

直感的に分かりやすく増加してますよね!

グラフだけだと分かりにくいかもしれないので、具体的な数字で表現させて頂きます。

コミット数:1.49
PR数:1.56
コミット人数:1.34
オープンからマージまでの平均時間:13%削減

人数が増えると開発時間が長くなったり指標が悪化することもあるのかな?と思いながら見ていたら主要スタッツが軒並み向上していて正直言って驚きました。

普段はこういうグルーピングで見ることがないのでなかなかに新鮮な図です。

Ruby biz Grand prix 2023 ソーシャルインパクト受賞

rubybiz.jp

RubyKaigi 2023の参加をきっかけにRuby bizにも応募させて頂きまして、ソーシャルインパクト賞を受賞することが出来ました。本当にありがとうございます。

今回のRubyKaigiにも参加予定の@ham0215からのRubyに関する熱いコメントもあり、これから先、Rubyへの貢献をより一層強めていきたいと考える事が出来ました。

この度はソーシャルインパクト賞をいただくことができ、誠に光栄に思います。エンジニア組織の開発パフォーマンス向上を支援するSaaS「Findy Team+」が、日本発の「Ruby biz Grand prix 2023」で受賞をできましたこと、大変喜ばしい限りです。当社では、今回受賞した「Findy Team+」だけではなく、多くのプロダクトでRubyが使われており、Rubyコミュニティや「Ruby on Rails」をはじめとした多くのライブラリなどのおかげでユーザーへ爆速で価値を届けることができております。今後も「Ruby」への恩返しの意味も込めて、Rubyを活用したサービス開発やエンジニア向けのイベントや事例記事の公開などを行い、Rubyコミュニティの発展に寄与してまいります。

(エンジニア組織支援SaaS「Findy Team+」が「Ruby biz Grand prix 2023」でソーシャルインパクト賞を受賞 | ファインディ株式会社のプレスリリースの受賞コメントから引用)

Rubyを使用した新しいプロダクトFindy Toolsがリリース

Findy Toolsは数ヶ月でプロダクトリリースができ、現在も着実に成長中のサービスです。プロダクトとしては未成熟な部分もありますので、皆様からのフィードバックも数多くお聞きしながらプロダクトに反映していきたいと考えています。ブースなどでぜひお聞かせ頂ければ幸いです!

Findy Toolsに関する具体的な内容はテックブログに記載をさせて頂いたのでそちらもぜひご参照ください!
tech.findy.co.jp

いかがでしたか?ファインディのこの一年の歩みをダイジェストでお届けしました。

ファインディ社は引き続きRubyとともに成長していければと考えております。

そして、一緒に働くメンバーを絶賛募集中です。

興味がある方はこちらから ↓

herp.careers

おまけ

ファインディは4月29日にオフィス移転をしました!移転後のオフィスの会議室にもRubyがあります!Rubyでファインディ株式会社が大きくなったこともあり、会議室サイズは比較的大きめです!

ファインディにエンジニアとして入社していいなと思ったこと3選

こんにちは、2024/3/18 からファインディに入社した本田です。

ファインディでは、Findy Team+ という、エンジニア組織の開発生産性を可視化し、開発チームやエンジニアリングメンバーのパフォーマンスを最大化するためのサービスの開発に携わっています。

今回は、入社して一ヶ月ちょっとが経ったので、入社して新メンバーとしていいなと思ったことをご紹介したいと思います。

オンボーディングがわかりやすい

入社すると初めにオンボーディング用 Issue が作成、アサインされていて、いつまでにどういうことができるようになっているために何をしてくのか、というのが一通りまとまっていて、すごいわかりやすかったです。

オンボーディング用 Issue には、次のようなことが記載されています。

  • 新メンバーがキャッチアップすべき事項の一覧

    • 環境構築手順や必要なツールのセットアップ手順、設定項目など
    • コーディング規約やその元となる思想
    • 携わる各リポジトリの役割や、各アプリケーションのアーキテクチャ
    • 各種定期ミーティングの一覧やその内容
  • いつ頃までにどういう業務ができるようになっているかの期待の目安

    • メンバーの経験、スキル、やっていきたいことなどを鑑みてマネージャーと個別にすり合わせもしている

こんな感じにオンボーディングのゴールと期間が定義されていて、
この後にこれらのゴールに向けて知るべきことやそのための資料が記載されている感じです。

初日からこの Issue を見つつメンターの方と密にコミュニケーションを取りながら環境構築や携わるプロダクトの理解をスピード感持ってキャッチアップできました。

個別に知るべきことを逐次教えてもらうのではなく、全体として何を知ってどういう状態になるべきかがわかると、必要に応じて能動的に情報をキャッチアップする動きもでき、非常にやりやすく感じました。

good first issue からコードで貢献できる

急ぎでない簡易かつ新しく参画した人向けの Issue を good first issue というラベルを付けて管理していて、新メンバーはまず good first issue から着手して、開発の流れや携わるプロダクトのコードベースを理解、習得していきます。私もこの流れで開発を始めました。

各 good first issue の Description は新メンバーでも理解できるようちゃんと書かれていて、前述のオンボーディング Issue で紹介されているドキュメントも読みつつ理解しながら Issue を対応していきます。

実際私も他のオンボーディングプログラムを受けつつ3週間ほど good first issue を対応していきましたが、15営業日で 34 PR を出してコードで貢献できました。

他のオンボーディングプログラムもあって波はありますが平均して3PR/日くらいできています

もちろんオンボーディング期間はしっかりインプットすることが大切ではありますが、それもやりつつ入社してすぐに何かしら貢献できることは自信にも繋がりましたし、いいバランスでインプットとアウトプットを両立できました。

レビューがすごく速い

めちゃくちゃ速いです。 PR のレビュアーをアサインしてだいたい 10 分以内、遅くても 1-2時間以内にレビューされます。 もちろんレビュアーの方の状況にもよりますが、体感7-8割くらいの PR は 10 分くらいでレビューされる印象です。

レビュアー一人当たり4件/日、平均しても1h以内にレビューされてます

これにはいくつかの要因がありますが、一番大きいのはバッチサイズを小さくしてリードタイムを短くすることで手戻りやコンフリクトを防ぐ、ということが文化として根付いていることが大きいと感じています。

そのために、

  • PR の粒度を極力小さくする
  • レビュアーは極力優先してレビューする

といったことが徹底されています。

なお、ここらへんの話は先日開催された Qiita Conference での弊社CTO佐藤の発表で詳しく触れていますので、ご興味ある方はぜひご一読ください。

speakerdeck.com

まとめ

他にもご紹介したいことはたくさんありますが、今回は特に新メンバーにとっての開発者体験について3つピックアップして紹介させていただきました。

入社して1ヶ月経過しての感想としては、ファインディは開発スピードを大切にする組織文化が根付いているなと思いました。 より良いプロダクトを作ってより多くのユーザーに良いプロダクトを提供するためにも、開発スピードはその源泉になるものだと思っています。 それをエンジニアとして各々が理解して体現している組織なので、私にとって新しい挑戦ができる環境だと感じています。

このスピード感を自分も体現し、プロダクトの成長と自分の成長を重ねながら良いプロダクトを作っていきたいです。

ファインディでは一緒に働くメンバーを絶賛募集中です。

興味がある方はこちらから ↓ herp.careers

【エンジニアの日常】エンジニア達の自慢の作業環境を大公開 Part2

こんにちは。

FindyのTeam+を開発している西村(sontixyou)です。

【エンジニアの日常】エンジニア達の自慢の作業環境を大公開 Part1と題して、公開したブログが好評でした。 それに続いて、弊社エンジニア達の作業環境を見ていきましょう!

作業環境を大公開

西村

私は、現在週3日ほど出社と残りはリモートワークしています。そんな私の作業環境をご紹介します。

デスクの全体像はこのような感じです。

デスクは新卒時代の先輩からおさがりです。幅120cmのものを使用しています。

ディスプレイはDELLの27インチ 4Kモニタを2枚使っています。1枚だけ縦置きにしている理由は、省スペース化と首の振り向きが大変だからです。

横置きのディスプレイでは、エディタとSlack専用になっています。

縦置きのディスプレイでは、ブラウザ専用になっています。ウィンドウを垂直に2枚置いて活用しています。

ディスプレイはエルゴトロンのアームで支えています。ディスプレイの縦置きを実現するために欠かせないアイテムです。また、ディスプレイの台座があると場所を取るためです。

Webカメラは10年前に買ったlogicoolのものを使用しています。マイクが内蔵されているため、外付けマイクは用意していません。そろそろ買い替えしたいので、乗り換え先を探し中です。

USBハブには、サンワサプライのUSBハブを使用しています。このハブは、HDMI(4k/60Hz対応)1本とUSB3.0 2本とUSB Type-Cの充電ケーブルを指すことができます。

キーボードはkeychronのK6 Proを使用しています。

キースイッチはKeychron Silent K Pro Switchを使っています。打鍵感がとても良いため、自宅用とオフィス用で2台購入してしまいました。

マウスは、logicool ERGO M575Sを使用しています。

手首を動かさせずに、カーソル移動ができます。そのため、手首の疲れは全然ありません。また、ボールを取り外すことができるため、マウス内部の清掃やボール交換が楽々です。

自分が好きなものをデスクに置くことで、仕事終わりまでモチベーションをキープしつつ仕事ができます。

まずは、フィギュアです。私は海洋堂制作のフィギュアが好きなため、ガチャポンで当てた怪獣ネロンガと恐竜を置いています。縦に長いものはミイラ展で当てた猫のミイラを題材にしたフィギュアです。

デスク横には、PS5を置いています。仕事終わった瞬間に、ゲームプレイできるためとても快適です。

仕事を快適かつ集中できる場所になるように意識して、デスク環境を整えています。

浜田

次は浜田(ham)の作業環境を紹介します。

コロナ禍のときにリモートワークを始め、コツコツリモート環境を整えました。 私のポリシーは「日々使うものは良いものを!」なので、予算の許す範囲で妥協せずに自分が良いと感じるものを選ぶようにしています。

ディスプレイは2枚ないと開発効率がガタ落ちするタイプのエンジニアなのでデュアルディスプレイです。

右側は4Kで31.5インチ、左側は27インチです。 4Kディスプレイは、ブラウザやZoomやGoogle Meetなどを表示しています。 27インチディスプレイは、Slackやメール、エディターなど文章を扱うツールを表示しています。

4Kディスプレイはメーカーなどにこだわりはないのですが、USB Type-Cで出力とPC本体への給電ができるものを選びました。ケーブルが少ないのは正義!!

また、こちらのディスプレイは足が大きかったのでモニターアームを使っています。 デスクスペースを拡げるためにモニターアームを買ったのですが、モニターの角度を簡単に変えられるため、モニターの背面の配線を変えたり、モニター周りの掃除もしやすくなり地味にストレスだったことも解消されました。

27インチディスプレイは解像度などのスペックは劣りますが、文章を書く場合に4K解像度だと文字が小さくて使いづらいので、必要十分なスペックだと感じています。

リモートワークにおいてオンライン会議は生命線です。こちらの画質や音質が悪い場合は相手にストレスを与えてしまう可能性があります。

そのため、Webカメラ / マイクは良いものを選ぶように心がけました。 また、自室では前からの光が当たらず、顔が暗く見えていたためライトも買いました。見た目の印象も大事です。

椅子はErgohuman PRO2 Ottomanを使用しています。足を伸ばせるようにオットマンがついているものにしましたが、全然使わないので不要でしたw

なお、足置きと一緒に使うことで椅子の高さ調整がやりやすくなるので、足置きとセットで使うことがお勧めです。

冒頭に書いた「日々使うものは良いものを!」のキーボードから始まりました。

REALFORCEから始まり、HHKB Professional(手元にないので写真なし)、HHKB Professional Hybrid Type-S、そして今はHHKB Studioを使っています。

また、分割キーボードを使っていたこともありました。

Corne Cherry V3はキーが少ない代わりにレイアーを活用するのですが、使いこなせず挫折しました・・・

HHKBライクな7sProは、気に入っていたのですがキーの反応が鈍いことが多々ありストレスを感じたのでHHKBに戻りました。

髙橋

週に2日程度リモートしている髙橋(@nokv)です。

オフィス勤務が主体のため自宅はシンプルな環境にしています。

デスクの全体像はこんな感じです。

200cm程度の大きいデスクを妻と半分ずつ使っています。

妻はリモートワークが多いため、MTGが被った際はどちらかが移動しないといけないなど不便なこともありますが、作業の合間に会話がしやすくリフレッシュできるので1つのデスクにして良かったと感じています。

自分の作業環境はMacBook Proと外部ディスプレイを縦並びに配置したシンプルなデュアルディスプレイ構成で作業しています。

以前はもう1枚ディスプレイがあり横に配置していましたが、首や目に負荷がかかると感じたため現在の構成にしました。

外部ディスプレイはDELLの27インチの4Kモニタを使っていて、PCへの給電もケーブル1つで可能な点がお気に入りです。

エディタやGitHubなど閲覧頻度の高いものは外部ディスプレイに表示し、首への負荷を軽減するため視点がなるべく下がらないようにしています。

キーボードはNuPhyのHalo75で、スイッチはNight Breezeを使っています。打鍵感と静音性のバランスが良く、長時間使用しても疲れにくいのが気に入っています。

MacBook Pro本来の配置に慣れているため、マウスやトラックパッドを別で用意することはなくいわゆる尊師スタイルにしています。

椅子はHerman Millerのセイルチェアを使っています。椅子の選択は体の負荷に直結するので機能性の高さを重視しました。それに加えて普段はhamさんと同じように足置きを使っています。

性格的にものが多いと落ち着かないので、必要最低限のものだけを配置して作業に集中できるような環境を作っています。

まとめ

いかがでしたでしょうか?

現在、ファインディでは一緒に働くメンバーを募集中です。

興味がある方はこちらから ↓ herp.careers