最近イケイケベンチャーのサーバーサイドの求人のコーディング課題をやっていた。
AtCoderやLeetCodeなどのスポットのアルゴリズムの問題ではなく、実践的なガチ目サイトのリファクタリング課題だ。コーディング課題としてはレベルの高い方だと思う。
試行錯誤しながらも、なんとか突破できた。
どんな課題だったか、どんなスキルセットがあれば突破可能か、どんな点に注意したら良いかを書いておく。
目次
どんなコーディング課題だったか
2つ課題があった。
1つは架空ECサイトのリファクタリング。
もう1つは架空情報サイトの性能改善だ。
githubのprivateリポジトリが共有され、READMEを元に開発環境を構築、リファクタリングをして都度都度リポジトリにpushしていく。
改善項目は完全にこちらに委ねられており、何を改善したかを記録しながらリファクタリングを繰り返していく。
良いと思ったタイミングで採点者に連絡し、一定の基準を満たしていれば合格という流れだ。
架空ECサイトのリファクタリング
スケジュール的にタイトで色々な妥協が入ったままリリースされたECサイトがあるという設定。
そのECサイトの問題点を洗い出し、機能を壊さずリファクタリングする。
商品一覧があって、カートがあって、架空の外部決済があって、決済のバッチがあって〜みたいな感じ。
開発環境はDockerで、コンテナを起動するたびにDBに初期データが入る感じだった。
一見まともに動いているように見えるが、様々な問題点を抱えていた。
例えば、テーブルが妙な構成になっており、決済機能が追加されたらあらゆるところに逐一if文が増えるとか。
注文データがテキストのカラムにjsonで保存されており、SQLで抽出できなくない? とか。
Controllerに全てのロジックが書かれていて、クラスや関数の責務が曖昧になっているとか。
どこかで散々見てきた感じのコードになっていた。
架空情報サイトの性能改善
ある商品の情報が50万レコード保存されており、そのサイトの一覧表示、検索結果表示の性能を改善すると言う課題。
ベンチマークを測るスクリプトが用意されており、改善施策毎にベンチマークを走らせて、どの程度改善したかを記録していく。
問題点としては例えば、テーブルをダンプすると、実は肝心なカラムにINDEXが貼られてないとか。
オフセット指定せずSQL実行してるけど、使うデータ少ししかないとか。
N+1っぽくなってる箇所があるとか。
よくあるデータベースの問題がさまざま散りばめられていた。
どのようなスキルを求められたか
これらの問題を解決するのに、どのようなスキルを求められたかまとめておく。
DB設計スキル
性能改善でもリファクタリングでもDB設計は必須である。
基本的なテーブル正規化の仕組みとか、INDEXをちゃんと効かせるにはどうするかとか。
アンチパターンを嗅ぎ分ける能力が問われていると思う。
このDBスキーマで検索するとしたらどうなるか? を常に考える。遅いんではなかろうか。SQLの回数がすごいことにならないだろうか。など。
アンチパターンに関しては下記の書籍で学んでいたので、なんとかなった。
これを執拗に読み込み、日頃からアンチパターンのDB構造に触れておくのをオススメする。
できればアンチパターンのBefore、AfterのER図を書くようにしておくと定着が早い。
とにかくアンチパターンの臭気を感じられるように準備しておく。
アーキテクチャ設計スキル
どこに何を書くべきか。例えばControllerにロジックを書かない、とか言うのはMVCをかじった人なら一度ぐらいは聞いたことがあると思う。
カオスコードにならないために、どこに何を書くべきかという概念は非常に重要である。
これらを軽視したまま複数人で開発を行うと、同じ処理はあちこちに点在する、どこにロジックが書いてあるかが分からない、などなど技術的負債が死ぬほど出来上がる。
Modelに書くべきことは何か。
Viewに書くべきことは?
Serviceクラスの使い所は? 何を書くべきか?
などを自分なりにまとめて明文化しておくことをオススメする。
DDD(ドメイン駆動開発)を一度やると、これらの勘所がつかめると思う。
ぼくが愛読しているのは下記である。
読んだだけでも理解はできるが、血肉にはならない。
読んだら実際に小さいプロジェクトでDDDを実践しなければならない。
ぼくはCS50W Project4あたりの教材を使って、DDDを試している。よくあるTwitterクローンサイトの作成だ。
完全なDDDを実践するのは難しく、うまくいかないことの方が多いが、DDDをやろうとすることで、機能の責務を真剣に考えるようになる。
これをやったことで、課題中でもきちんと責務を分けることができ、クリーンなアーキテクチャでのリファクタリングができた。(と、思う)
テストを書く力
テスト駆動開発、とまではいかないまでも、自分が作ったものや既存機能が壊れていないかを確認できるぐらいのユニットテストを書けたほうが良い。
と言うのも、リファクタリングをしている間に、機能を壊してしまうことがたびたびあったからだ。
そんな時に役に立ったのが、ユニットテストだった。
ぼくはTDD原理主義者なので、自分が作る機能については、必ずユニットテストを書いてからプロダクトコードを書いている。
そのユニットテストのおかげで、壊れた箇所がすぐわかった。
後述するが、プライベートの時間で真っ当な開発時間を確保するのは難しい。
時間がない中で、まあまあまともなコードを書くためにはテストがあったほうが絶対に良い。
子供を寝かしつけた後の夜とか、子供が起きる前の早朝なんかに自分の頭だけでエレガントなコードを書くのは無理だ。疲れてるもん。
そんな時、テストを味方につけることでリファクタリングの難易度を下げることができる。
テストが通っている間は心理的に安心できるので、攻めたリファクタリングができる。
注意点
コーディング課題をやるにあたり、いくつか注意しておく点がある。
提出期限をできるだけ長めに取る
提出期限は可能であればこちらから提示した方が良い。
先方からたとえ提出期限を決められてとしても、後から調整可能であれば調整した方が良い。
ぼくも一度提出期限を延ばしてもらっている。
本業をやりながらコーディング課題をやるのは予想以上にきつい。
特にぼくの場合は子持ちなので、早朝や子供が寝た後の深夜に課題を解いていた。
もちろん日中帯に仕事をした上での活動なので、課題をやるときにはすでに疲れて頭が働かなかったりする。
なので、提出期限は可能な限り長くとり、毎日コツコツやるようにする。
ぼくの場合、テスト込みで2課題で27時間かかり、2週間ぐらい毎日課題を解いていた。(慣れないフレームワークだったってこともあるけど)
27時間プライベートの時間を捻出するのは思ったより難しい。
課題を渡されたら、まず最初にこの課題に何時間かかるか見積もりをし、その見積もりの2倍くらいを提出期限とするのが良い。
タスクをチケット化してプルリクを残しておく
githubのissuesを使って、タスクをチケット化すると良い。
こんな感じにタスクをチケット化→プルリクでチケットをクローズする というgitフローを遵守した。
あれもこれも目につくものをとにかくいじるみたいなことをすると、デグレが起きたときに切り分けできないわ、タスクに集中できないわで散々である。
日頃の厳密なgitフローを遵守し、品質を高める努力をした方が結果的に楽だった。
まとめ
まとめると、サーバーサイドのガチ目のコーディング課題を乗り切るには
- DB設計、アーキテクチャ設計が重要
- テストも書こう
- 時間の捻出が大変なので提出期限は長めに設定
- 課題のタスクをチケット化してきちんとしたgitフローを遵守する
って感じ。
本日は以上!