Советы молодым (ruby/rails) программистам, как делать тестовое задание

В прошлом году я писал советы для программистов, которые ищут работу, можно прочесть тут. В этом году (традиция теперь уже) я предложил одному парню сделать ревью его тестового задания. По мотивам пишу эту заметку.

Если ты молодой программист и решил сменить работу или найти первую работу, тебе скорее всего дадут тестовое задание. Если учесть веяния моды (Rails - API), задание, скорее всего, будет “разработать Rails JSON API”.

Допустим, оно будет звучать так:
“Разработать Rails API для каталога фильмов. API должна:

  • иметь пользователей, авторизация через <подставь свою>
  • иметь возможность создать фильм: название, описание, рейтинг
  • дать возможность назначать рейтинг пользователям
  • возвращать средний рейтинг для фронтенда “

Bootstrap

Что будем делать? Начнем с того, что, кто и куда будет посылать? Скорее всего, будет фронтенд с авторизованным пользователем, и он (фронтенд) будет слать JSON запросы. Как удачно, что Rails с 4 версии предоставляет возможность сделать “API” приложение, т.е убрать HTML шаблоны, assets и прочую ерунду, которая нам не нужна, оставить только контроллеры, модели и генераторы JSON - Rails API.

Endpoint / controllers

Всегда начинай задания с написания тестов.

Ок, с каркасом разобрались. Начинаем писать request specs / Cucumber features. Простой запрос с созданием фильма и получением ответа от приложения. Конечно, всё сразу посыпется, потому что ни поста, ни контроллера, ни пользователя нет. Важно начать с тестирования, потому что в этом случае ты становишься пользователем своего API.

Есть первый тест, который означает, что запрос с данными должен уйти в наше приложение. Начинаем строить endpoint’ы.

Думаешь, что это тестовое задание, и не стоит мучаться, "сделаю RootController, создам там пяток non-REST путей и пойдет"?

Плохо:

Хорошо:

Пиши тестовое задание, как будто бы ты пишешь приложение для своего собственного бизнеса.

Делай тестовое задание так, будто у тебя есть время: без вот этого вот: “заказчик не заплатит”, “делаем быстро, потом поправим”. Время есть - используй его. Но не стоит растягивать задание на месяц. Работа занимает всё отведенное для неё время. Сделай себе пометку “я сделаю это задание за 4/6/8 часов”. Укладывайся во время.

Возвращаемся к контроллерам. Скорее всего, ваш (будущий) фронтенд уже придерживается общепринятых правил REST API, многие фреймворки поддерживают его из коробки: Ember, Angular (наверняка имеет). Сразу делай REST API. Что это и как это делать, можно прочитать в интернете, например:

Для организации контроллеров, а их должно быть минимум 1 на каждую сущность, можно прочесть пост о том, как организует свои контроллеры DHH - http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/, хотя мне кажется, это идея избыточна. Можно не делать всё досконально, но идею запомни.

Naming

Дальше, именование методов/контроллеров и всего остального. Важно называть их так, чтобы другие программисты поняли, что он (контроллер/метод) собой представляет. Не нужно описывать, что он делает - GetTopRatingsController - плохо. Опиши сущность - RatingsController. Если ты использовал в качестве названия контроллера первый вариант, у меня для тебя плохие новости: навеняка позже нужно будет реализовать контроллер, который возвращает худшие рейтинги.

Правильные имена - залог успешной поддержки проекта

Validations & persistence

Endpoint’s готовы. Пора приходить к сохранению и считыванию данных. Тут мы приближаемся к моделям. Наверняка их нужно валидировать. Стандартный Rails-way говорит нам о том, что нужно просто добавить validates :something, with_something - и мы в шоколаде. Для небольших сервисов/микро-сервисов это нормально, но когда приложение разрастается, становится сложно.

Пример:
когда пользователь заполняет форму со своими данными, для него все поля должны быть заполнены, но когда администратор заполняет форму за пользователя, некоторые данные (допустим, адрес), должен быть пустой. Как будешь реализовывать? Базе данных без разницы, что хранить, лишь бы не превышал лимиты, установленные на размер полей.

Есть несколько путей:

  • костыльный: т.н. контекст для администратора, и его использовать - http://www.justinweiss.com/articles/a-lightweight-way-to-handle-different-validation-situations/
  • супер костыльный: для администратора пропустить валидации и записать в БД. Никогда так не делай
  • использовать объекты, которые будут валидировать модели в разных контекстах: для администратора будут AdminUserValidator, для пользователя - UserValidator.

Хорошо, что нам в этот раз Rails тоже поможет: с 3 (или 4) версии, можно использовать validations не только в ActiveRecord классах. Почитать, что это и как использовать, можно тут - http://api.rubyonrails.org/classes/ActiveModel/Validator.html.
Один из примеров применения такого подхода - http://blog.arkency.com/2014/05/mastering-rails-validations-objectify/.

Не забывай про N+1 (guide) и SQL injection.

Что ещё?

Не пиши логику сохранения в модели, модель - для сохранения состояния.

Несмотря на то, что Rails-way диктует писать логику работы с данными в моделях, не нужно так делать. Для этого давно уже есть такое понятие, как ООП. Ладно, шучу. Ещё в 2012 году в блоге Code Climate была описана техника разгрузки “толстых” моделей с помощью разных сервисных объектов. Если не знаешь, что это такое, прочитай тут - http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/.

Если бы я проверял тестовое задание, я бы обратил внимание ещё на несколько важных вещей, которые помогают поддерживать проект без головной боли:

  1. Code style. Насколько ты следуешь общепринятым практикам написания кода. Если в проекте отступы пляшут от файла к файлу, если методы разбросаны по файлу лишь бы как, увы, до свидания. Ruby style guide, Rails style guide.
  2. Как ведешь проект в git репозитории, как создаешь коммиты, насколько хорошо написаны сообщения к ним. 1 коммит на 200 файлов? Увы, нет.
  3. README. Если ты разработал API, будь любезен, опиши, как его использовать.
  4. Тесты. Насколько код покрыт тестами, и какими тестами. Написать 15 запросов в одном тесте - нет.

Самое главное запомните:

«Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте» (Джон Ф. Вудс)