【Rails】未経験者がポートフォリオを作って感じたこと、指摘されたこと

## はじめに

ポートフォリオを作って感じたこと、指摘されたことなどをまとめます。内容としてはRailsに限った話ではないです。
よく言われていることではあると思うのですが、実際に自分が体感したのは大きかったなと思いました。

他の方のポートフォリオ作りの参考になればいいなと思います。
が、一方で、これは自分で苦しまないとわからないよなとも思うので、「そうなんだフーン」くらいに流し見してください。


### ポートフォリオについて

隙あらば宣伝。Twitterのハッシュタグつきツイートを自動取得するサービスです。よかったら登録して使ってみてください。
https://hashlog.work/
https://github.com/aiandrox/hashlog

RailsとVueとWebpackerでSPAっぽい感じにしています。ログイン周りはRailsに任せているので完全なSPAではないですが。
正直、Vueに関してはまだまだ自分の知識が足りないのとおまけ的な要素が強いので（テストも書いていない）、この記事ではRailsの部分にしか触れていません。


#### バージョン

- Rails 5.2
- Vue.js 2.6


## 自分が感じたこと

### データベースを後でいじるのは面倒

私は最初にリリースした後に`access_token`を格納するカラムを増やしたのですが、元々なかったカラムだったのでnull制約が付けられませんでした。
また、access_tokenを実際に使う際も、「アクセストークンがある場合」のように無駄な条件分岐をすることになったのでとても苦労しました。
後々使う予定があるデータはリリース時に取得するようにしたほうがよかったなと思いました。


### テストは早めに書くべき

私の場合、根本となる処理がかなり複雑だったので、とにかくまずは実装したいと思い、そちらを先にしました（テストは後でまとめて書けばいいかと思っていた）。
が、後でテストを一気に書くというのは予想以上に疲れました。

また、書いている途中で追加しなければならない点に気づいて実装をいじったりすることになりました。
これにより、テストが通らないのが実装のせいなのかテストのせいなのかわからなくなり、非常に混乱しました。

後半になると、さすがに学んだので完全なTDDとは言えないものの、テスト項目を先に洗い出すように心がけることができました。
テストを先に書くと実装時に考慮すべき点を洗い出せるのがよかったです。テストの中身が書けなくても`it`で項目を挙げるだけでもかなり違いました。


### Slack通知は入れておくべき

メール通知でもいいのですが、Slack通知の方が便利な気がします。エラー通知は来るときは一気に大量に来ます。

エラーログを見ればわかるのですが、いちいちアクセスするのは手間だし、リアルタイムで連絡が来るのがありがたいです。デメリットとしてはSlack通知の音が嬉しくなくなる点です。
定期実行がちゃんと実行されているか確認するときにも、Slack通知にはかなりお世話になりました（リリースした後数日はちゃんと動いていなかったため、起床後の確認が日課になりました）。


### RESTful APIはキマると気持ちいい

最初はそのページで使用するリソースをすべて返すという設計をしていたのですが、RESTfulにした方がリソースがわかりやすくなると言われてなるほどと思ったので実装してみました。
その段階でもリソースの切り分けが大変で、非常〜〜に時間がかかったのですが、認可を設定するときにさらに時間がかかりました。

最初の段階では、

```
GET    /api/v1/users/:user_id/tags
GET    /api/v1/tags
```

これを同じ`TagsController#index`アクションで管理して、`params[:user_id]`の有無によって返すtagsを変えていました。今思うと気持ちの悪い設計です。

が、「userが非公開設定の場合はアクセス拒否」という認可を付けるときに、アクション単位で振り分けができない壁にぶち当たりました。
そのため、前者を`Users::TagsController#index`で管理するようにしました。ネームスペースを使ってエンドポイントを振り分けていくことで、かなりスッキリまとめることができました。

時間はかかりましたが、ちゃんと返すデータを考えて、ひとつずつ挙げていってよかったなと思いました。


## 指摘されたこと

[レビュー会](https://startup-technology.connpass.com/event/178485/)で具体的に指摘された点や、エラー通知を見て気づいた点などです。


### フォーマットに関するバリデーションがない

ハッシュタグ登録時にハッシュタグとしては不適な値（記号混じりなど）が登録できてしまうという点を指摘されました。
必須項目や一意性についてはよく言われるので対応していました。が、フォーマットについては意外と見落としがちだなと思いました。

型を設定しているので何でもかんでも入るわけではないですが、メールアドレスや最大値のバリデーションなどしておかないとどんな値が入れられるかわかりません（Stringに関しては自由度が高い分かなりシビアに感じます）。
フロントで制御できる部分についてはフロント側で表示するとユーザビリティがよくなるのかなと思います（メールアドレスは`email_field`を指定すればブラウザがよしなにしてくれる）。


### 例外処理が甘い

これはどこまで想定するかというレベルだと思います。
外部APIを利用する場合、独自のエラーを拾って処理を行う必要があります。
どんなエラーが返るのかきちんと認識した上で、クライアントにエラーを表示する必要があると感じました。


### adminに0を使っている

userのroleにenumを使用していたのですが、特に深く考えずに、`{ admin: 0, general: 1, guest: 2 }`というようにしていました。
が、0はデフォルトで入りやすい数字なので、そこをadminにするのは危険という指摘を受けました。
なので、デフォルトで入れるものを0に設定するのがいいです（データベース側でデフォルト値を設定するのは当然として）。


### renderのformatsを指定していない

私の場合はSPAだったので、全てのURLを`static_pages/index.html.erb`に流し、ルーティングが存在しないものはVueテンプレートで404ページを表示するという風にしていました。
なので、

```config/routes.rb
  get '*path', to: 'static_pages#routing_error'
```
```app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
  def top; end

  def routing_error
    render :top, status: :not_found
  end
end
```

このようにしていましたが、`.json`のようなアドレスでアクセスされたときにテンプレートエラーになっていました。
<img width="655" alt="スクリーンショット 2020-06-21 13.30.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322882/2e5387ec-5d63-eb67-b36f-ec07ddc9c4c3.png">
普通のRailsアプリの場合は`*path`を拾う必要はないのでこのようなことは起こりにくいと思いますが、もし拾う場合はどのようなアクセスをされうるか想定しなければならないなと思いました。


## おわりに

実務経験者と未経験者で具体的に何が違うんだろうと考えてみて、持っている観点の数や角度のようなものが違うのかなと思いました。
「enumでadminが0なのはまずい」というのは言われてみれば確かにそうだよなと思ったのですが、実際にコードを書いているときには考えてもみなかったことでした。
そういう視点の少なさが未経験者の弱みの一つなんだろうと思います。

また、本番環境のデータベースを操作する怖さ、Slackのエラー通知に怯えるという経験ができたのは、一つのサービスとして公開したからこそだと感じました。
公開するまでは「こんな機能付けたいあんな機能付けたい」と思っていたのが、「あの不具合に対応しないと」という意識に変わりました。とはいえissueはまだまだ残っているので、落ち着いたら機能も増やしたいところです。

とにもかくにも、まずは就活を頑張りたいと思います。


### おまけ

実装する上で苦労した点をメモしていたので、そのまま残しておきます。

- OAuth TwitterAPI（理解に苦労した。未だに理解できていない）
- ツイート取得。curlコマンドをコードに落とし込む（結局2,3段階に暗号化しているのを再現できなくてgemに頼った）
- TwitterAPIのmodule切り分け。からのclass切り分けは割と簡単だった。肥大化してるけど。
    - サービスクラス便利すぎてヤバイ（実際何でもできるから使い方には要注意らしい）
- トランザクション内の処理（1アクションでいろいろするのと、エラーメッセージの持たせ方）
- cronの例外処理。再試行。
- ツイートを日付で振り分ける（ツイートした日数を取得する）。SQL力の不足を感じた。
- API設計。REST。エンドポイントのイメージができてもルーティングが実装できなかった。
- RSpecのモック（なぜか通らないテスト）
- インフラ（何をどうインストールしてどんなポジションで起動させるのかみたいなイメージ。何のエラーをどこで見ればいいの？）