僕がフリーランスエンジニアとして感じる壁

転職活動をすることを表明しましたが、
今現在自分が壁と考えていることを書きます。

権限の壁

私の経験上AWSの管理者権限をフリーランスエンジニアとして、任されることは少ないです。
AWSのサービスをフル活用して貢献するためには、
AWSやインフラに精通し、社外だとしてもこの人間に権限を委譲した方が生産性が高いと認定された場合に限ります。

こういった権限はAWSだけではなく様々な重要な意思決定に絡んできます。

組織の壁

フリーランスのエンジニアが組織構造に対して助言することはあっても、
そこに対する意思決定までのプロセスが常に公開されていることは少ないです。
ある程度の規模の会社で経営経験があり、経営者に対してのアドバイスを求められる場合にのみ、
それらが委譲されるでしょう。

お金の壁

フリーランスはお客さんの事業へのお金の決裁権は通常は一切持ちません。
たまに予算を任せてくるような案件もありますが、その予算を承認するプロセスに立ち入ることはできません。
圧倒的な政治力があれば、権限の壁や組織の壁と同じく超えることは可能かもしれません。

壁はフリーランスのままでも超えられる

私は一般的にフリーランスに立ちはだかる壁を超えて来ました。
(リーダーは任せられない、マネジメントは任せられない、インフラは任せられないetc)

この三つの壁は越えられないものではありません。
自らの能力が高ければ、フリーランスのままでも突破することのできる壁です。

ただ、これらの壁を越えるには私の現在の能力では少し難しそうです。

私が壁を越えるために

正社員であれば、様々な制約がかかる代わりにこれらの壁を越えるためのサポートが得られると考えています。
そして、これらの壁を突破すれば、視座が高まり解決できる課題も増えると考えています。

私は一度会社にコミットして、この壁と向き合おうと考えました。
3つの壁を超えて、能力を上げて行きたいです。

意識低いrubocopの運用の仕方

rails 界隈では Sider の普及に伴い rubocop を業務で使っている方も増えてきたのではないでしょうか?
しかしながら、一度荒れ果てた荒野にrubocopを放っても成果はほとんど上がらないことでしょう。
必要なのは、すべてのコードを綺麗にするぞ!という高尚な志ではなく、現実を少し改善すれば、それでOKという意識低い気持ちな気がします。

--lint オプション

rubocopには -l または --lint というオプションがあるのをご存知でしょうか?
こいつはlintオプションと呼ばれるやつで、bugに繋がりやすい警告レベルを指摘してくれます。

こいつをまずデフォルトにしましょう。
.rubocop ファイルに --lint と記述すると、rubocopのコマンドが常に--lintオプションがついた状態で実行されるようになります。
.rubocop ファイルを使うことで、プロジェクトのメンバー全員がそのオプションをつけてrubocopを実行することができるため、「 rubocop -l と実行してください!」などと呼びかける必要がなく便利です。

rubocop_todo.yml と向き合う

次にこの状態で、rubocop_todo.yml を生成します。
rubocop --auto-gen-config

プロジェクトにこれ以上 --lint に違反するコードを入れないようにするためです。

rubocopを入れると、rubocop対応で色々と対応工数が増えてしまうと恐れている方もいらっしゃると思いますが、
この --lint オプションは宗教戦争的なものはほぼなく、バグに繋がる恐れがあるか?という観点での指摘です。

さて、auto-genされた rubocop_todo.yml には問題が山積です。
これらは指摘事項を修正していかなくてはなりません。

todoファイルと向き合いきれなかった場合

rubocop_todo.yml ではルールをファイルあたりで抑制してしまうため、
todoファイルを全部修正できない場合は、問題の箇所を特定してコメントアウトで警告を抑制する方法をとったほうが良いでしょう。
じゃないと、 rubocop_todo.yml に記載されたファイルは治安が悪化して行きます。

一日1cop修正

私のプロジェクトでは1日に一つだけ、これを実行していくことを行いました。
修正はコミットを細かく分けて、tmpブランチからcherry-pickをしてmasterにマージする量を調整する形をお勧めします。
(OSSである dev.to では一つのpull requestがマージされたら新しいpull requestを作るという方法をとりました。)

rubocop_todo.ymlを消すことができれば、これでとりあえずrubocop -lに適応した状態を作ることができたということです。

--lint 以外のオプションと向きあう

次の段階としてrubocop -lをやめて、rubocop.ymlを設定していくというステップがありますが、これは次回以降に。
まずは、--lintで怒られないくらいを目指すのはいかがでしょうか?

FactoryBot.lintの困るところ。

associationをデフォルトのfactoryではnilを渡す

一般的にFactoryBotは大きなプロジェクトになってくると無駄なデータを作成する可能性が増えてきます。
例えば、こんな時。

factory :tag do
  article
end

it do
  create_list(:tag, 10)
  # 10個タグがある場合のなんらかのテスト
end

articleがtagを複数持てるDB構造になっているとして、
tagが10個以上付いているページのfeature specなどを書きたい場合がある時に、
上記のような書き方をしてしまうと、tagが10個だけできればいいものの、articleも10個できてしまうのです。

これを抑制するために、

factory :tag do
  article { nil }
end

このようにartilceは敢えてnilを渡して、validateエラーが起こるようにしておきます。
そうするとうっかり、 create_list(:tag, 10) なんて書いてもテストが落ちるようになり、、

it do
  create(: article)
  create_list(:tag, 10, article: article)
  # 10個タグがある場合のなんらかのテスト
end

おそらく、このようなテストになってくれるはずです。

あくまでデフォルトの話であって、traitを使う分には特定のcontextの名前を入れることができるので、問題ないと考えています。

factory :tag do
  trait :with_article do
    article
  end
end

↑みたいなのはOK

FactoryBot.lintの困るところ

で、この方針でfactorybotを運用すると困ることがあり、、
FactoryBot.lintは factory_botで作成されたDBテスト用のファイルの治安を守るためのFactoryBotがデフォルトで持っているlint機構です。

factory :tag do

  article { nil }
end

何と、こんなFactoryBot.lintは許されないのです。
validateエラーを敢えて起こしているのに、validateエラーになっちゃう。

ので、 strategy: :build のオプションを使ったlintを実行しています。
しかしながら、buildのlintは意味があるような無いような。。。
そもそも、関連にnilを渡すようにするのって多分気持ち悪いって方が多い気もします。(僕も最近まではそうでした。

皆さんはどのようにFactoryBot.lintを運用されていますか??

2018年夏フロントエンド×Railsの事情

このテーマ。僕はずっとずっと悩んで来た問題です。railsdm周りで話題になってたっぽい。

ベストプラクティスというものはまだ見えない。
でも、いろいろな実績が出て来たし、自分でもいろいろやって見たのでまとめて行きたいと思います。

1. sprokets + turbolinks + webpacker + stimulus

basecamp wayですね。
それぞれハマりどころが多くrailsに精通していないと使いにくいと思います。
DHH信者にオススメの設定です。

気持ちはわかるけど、染まって見るのもなしではないかな。

2. webpacker + react(or other javascript view library) (+ some cssloader || sprokets)

部分的にreactなどのviewを使う設定。場合によってはcssもstylesheet_pack_tagを使うパターン。
個人的にはこのパターンってreact使う必要ある?という気もします。
が、例えばKPIに直結する非常に重要な機能だけをSPAにして、
UXを最大化するのに使える構成ではあると思います。

この場合はsproketsを捨てない方が無難な気もしますが、
reactの中でcssを使いたい場合や、依存ライブラリをいい感じにシュッとしたい場合はwebpackerに頼ってしまうのもいいでしょう。

3. webpack + rails(without sprokets and webpacker)

例えばsproketsを捨てるのようなやつ。
railsでレンダリングするのは完全に捨てないまでも、
webpackerを通じてwebpackをコントロールするのに融通が効かないのは嫌だという場合にオススメな構成。

確かにハッシュダイジェストは捨ててもええっすね。

4. webpack + rails api

railsは完全にapiに成り下がるパターン。
フロントエンドは完全にjavascript wayに集中できる。
しかしながらSPAで全部作るのは大規模案件には骨が折れる作業にはなりそう。
特に管理画面系なんかはまだrailsに一日の長がある印象。

管理画面は別でrailsでサクッとmicroserviceで作ったりrails engineを採用したり工夫すると工数の削減も進むかも。
BFFとしてのrails apiといったところ。

ただ、そもそもBFFをrailsがある必要がどこにあるだろう。
他の言語でもswaggerやgraphqlを利用すればrails並みに工数を削減することもできるだろうしね。

けど、それでもrailsがいいって時はこれが無難ですよね。

まとめ

個人的にはオススメは4。逆に1という手もある。
一般的には2,3が多いけど、2,3が最も辛い。

再現テストの話

テストコードで再現テストをしてから、bugfixするやつ。

不具合にテストを書いて立ち向かう - t-wadaのブログ

これうまく行ったら超気持ちいいけど、いつも気持ちいいとは限らないのでメモ。

バグのパターンがユニットテストとしてエッジすぎ

だいたい再現テストコードがエッジなパターンすぎて、
仕様を説明する助けとして不適切になってしまう。

describe 'somemethod' do
  subject { describe_class.new(params).somemethod }
  context 'A' do
   let(:params) { 'a' }
   it { is_expected.to eq ('A') }
  end

  context 'B' do
   let(:params) { 'b' }
   it { is_expected.to eq ('B') }
  end

  # 再現テストのコンテキストがハイコンテキスト
  context 'B and hogehogehoe' do
   let(:params) { 'a' }
   before do
     # ハイコンテキストな記述 
   end

   it { is_expected.to eq ('A') }
  end
end

↑この程度なら全然いいんだけど野生のコードはこんなのじゃすまない。

このままマスターに入れてマージすると、仕様を説明することを阻害するよなぁ的に。

しかし、マージしておかないと同じバグを繰り返すかもしれないので、対策としてそのハイコンテキストなテストを別ファイルに切り出したりしてる。

コンテキストやフィクチャでテストのファイル分ける

1つクラスに1つのテストファイルがデフォルトですが、
テストファイルがって大きくなってきたら、コンテキストやフィクスチャ、メソッド単位でテストファイルを分割しても問題ないわけです。

なので、コンテキストが揃わないケースでは、別ファイルにコンテキストを分けてしまう。

そうすれば、仕様のキャッチアップにはよりプリミティブな浅いコンテキストのテストケースを読めるようにしておく。

そして再現テストは別ファイルで自動テストされるようにしておく。

というわけです。

なんかうまくいかない時に選ぶ他の手段

その他にもこのケースには下記の対応方法があるけど、場合によってはこれらも選んじゃう。

まとめ

基本的にバグに対して再現テストを書いて立ち向かって見てるけど、、、、
書きあがったテストコードがぐちゃぐちゃしちゃうのが悩み。

ぐちゃぐちゃでもいいから、別ファイルにテストコードを分けてコミットして再現テスト -> 修正すると良さそう。

そのやり方も万能じゃない。

そもそも再現テスト書いて、不具合に立ち向かうのをサボりたくなるときもある。