入社して1年目の振り返り

こんにちは、サービスエンジニアリング本部の穴澤です。

今年の1月に入社して丁度1年になります。良いタイミングなので1年を振り返ってみたいと思います。 これから転職をしてみようかなと考えている人、新しい事に挑戦したいと思っている人に読んでもらえると幸いです。

1-3月

年始に入社。前日は眠れませんでした。6年働いた前職から転職し、人間関係はゼロから。使っている技術、サービスも違う。大丈夫かなー3ヶ月後生きてるかな?と思っていました。入社後は環境構築しつつ、その後運用業務を上司から少しずつ分けてもらい日々の業務全体をみるようになりました。エンジニアとしての経験を買ってもらって入社したものの、プログラミング言語での経験は主にPHPだったのでエニグモでのRailsを使った開発に戸惑うところも未だにあります。このタイミングでチームに新しく入ってきた方も居たので受け入れ時に必要な情報やドキュメントを徐々にまとめて自分がサービスの説明や開発フローを説明できるものを準備しました。

この頃はまだ前職の人たちとの飲み会が多く、ホームシック的?な気持ちになったのを覚えています。ああ、転職したんだなあ、という気持ちでした。 EM見習いとして入社したので、入社したその月からメンバーの方と一人ひとり話をする時間を持ち始めました。ただし全然みんながなにやってるのかわからん。という感じでした。

4-6月

「社内での情報共有サービスを移行しよう」という動きがありチームを横断した形でエンジニア数名、関係者含めて小さいチームが発足しました。 私自身が刷新に前向きだったのでやってみようと思い手をあげることになりました。実際には部署をまたいで新ツールの使い方のレクチャや 既存のデータの移行スケジュールの共有、他の部署の人たちへのヒアリングや、もっている課題などを集約しデータ移設などは山本さん におんぶにだっこでした。 このチームのおかげで普段接点のない部署の方々とお話する機会があったかなと思います。名前もだいぶ覚えてきました。

夏場に大きな施策が控えており、メンテナンスが必要になったため自分が担当になりました。過去メンテのドキュメントを探しマニュアルや手順書の整備をはじめました。 メンテにはインフラエンジニアや他のサーバサイドエンジニアの方もいましたが「入社して半年でメンテ要員わたしで大丈夫かな?」と思いながらテスト環境で手順書の確認やわからないことを少しずつ潰していきました。

7-9月

サーバーメンテナンスは深夜に実行する事となり、夜に関係者が出社しメンテナンスを終えたのは朝でした。そのまま朦朧としたまま会社を出、ファストフード店でおいしい朝ビールをいただきました!色々と学びが多い月でした。メンテをしたことでちょっとシステムの全体構成やアーキテクチャ全般が「わかった気」になったのを覚えています。

開発業務で機能をゴリゴリ書いている身ではないので案件の調整や小さい運用、リリースの調整やドキュメントの整備、問い合わせやMTG、ちょっとした相談・・・。こういう事に忙殺されているとたまに「わたしエンジニアって言っていいんだっけ?」と悲観的になることもあるのです。なんだか何もしてないな、EMで入ってきてるけどわたし大丈夫かな。と不安になったこともありました。チームの成果を伸ばしたい、組織に貢献したい。なので技術力そのものだけじゃないところで活躍したい。自分の達成チケット数=成果ではないという事を上司に確認したこともありました。その際、「そういう前提であなたを迎えているのですから(大丈夫ですよ)」と言ってくれたのはいまでも覚えています。メンテも経験したことで、この辺りからはっきりと自分の役割を理解してきました。技術的な学習や実際の開発から完全に手をひいたわけではないけれどそこに自分の価値があるわけではない。というのが1年たった気持ちです。

10-12月

リリースに関わる改善をしたいと日頃思っていたので、夏過ぎからまた有志のチームで細々と情報の整理と改善の策を練るところからはじめています。ちょっと不安ですが自分にできることを探していこうと思います。

また春先から頑張ってきた採用活動が実を結び、新しい人が入社してくれました!人材育成も過去の経験をそこそこに活かしながら、今わたしの目の前にいる人の顔をみて、自分ができる「お手伝いをしていこう」と思った今日このごろです。

最後に

異なる環境に飛び込むことは色々考える事も多いし悩みも多いです。それでも今年楽しかったな、ここはやりきったな。という達成感はあります。 それと、自分がどんどん自社のサービス・支える人を好きになってると感じます。携われる領域やできることが些細なことでも一つふえると嬉しいものです。 環境や周りの人を強制的に変えることで視野が広まったり自分が少しずつ変わる変化を、来年も楽しんでいこうと思います。

株式会社エニグモ 正社員の求人一覧

hrmos.co

第4回 Google Cloud INSIDE Digital に登壇しました。「ショッピングサイトにおける商品画像への Could Vision API の活用」

少しさかのぼりますが、11/1に「ここでしか聞けない AI 、機械学習サービスの活用例」をテーマに開催された第4回 Google Cloud INSIDE Digital に 弊社エンジニアの木村が登壇しました。

f:id:enigmo7:20191212163632j:plain

弊社で運営しておりますショッピングサイトバイマへ出品された商品画像に規約違反となるような不適切なものが含まれる場合があるため、その検出に Cloud Vision API を活用した事例についてお話ししました。

発表資料を公開します。

Vision APIの他、エニグモでは DWHとしての BigQuery をはじめ、それに伴ったデータ収集・分析・活用のインフラとしてGCPAWSを活用しています。それらを活用してデータの収集、整備、機械学習モデルの運用、サービスへの組み込みなど大規模データを扱ってみたいエンジニアを募集しています。

↓↓↓

株式会社エニグモ 正社員の求人一覧

hrmos.co

dry-validation (1.3) で Form Object を実装する

dry-validation (1.3) で Form Object を実装する

こんにちは、エンジニアの齊藤です。 この記事は Enigmo Advent Calendar 2019 の12日目の記事です。

本日は、バリデーションロジックの開発で Form Object の設計を支える dry-validation について書きたいと思います。

Form Object について

ユーザー向けのウェブアプリケーションの実装で必ずといって発生するのが、インプット値のバリデーション処理です。 ウェブフォームだけでなく API のリクエストパラメーターや CSV ファイルのコンテンツ等そのケースは様々です。

Enigmo で開発しているウェブアプリケーションの多くは Ruby on Rails で実装されています。Rails アプリケーションでは、Model にバリデーションを実装するのが一般的ですが、モデルのアトリビュートを逸脱したこのようなケースではそれぞれ専用のクラスを実装する必要があります。

Form Object 自体は有名な Rails の Fat モデルを改善する 7 Patterns to Refactor Fat ActiveRecord Models という記事のなかで紹介された設計テクニックの一つです。

Before dry-validation

今回紹介する dry-validation 以外の実装では先程の 7 Patterns... にも登場する virtusActiveModel::Validations を使って実装しています。 virtus でアトリビュートを定義して、通常のモデル同様にバリデーションルールを実装します。
実行方法も通常の ActiveModel のインスタンスと差異がないので、バリデーション専用のモデルを実装しているのと同じです。

app/forms/product_form.rb

class ProductForm
  include Virtus.model
  include ActiveModel::Model

  attribute :product_name, String
  attribute :reference_number, Integer
  attribute :images, Array[Hash]

  validates :product_name, presence: true
  validates :reference_number, presence: true
end

実行結果

[11] pry(main)> form = ProductForm.new(product_name: '', reference_number: '1000'); form.valid?; form.errors
=> #<ActiveModel::Errors:0x000055839f9bacd0
 @base=
  #<ProductForm:0x000055839f9bb090
   @errors=#<ActiveModel::Errors:0x000055839f9bacd0 ...>,
   @images=[],
   @product_name="",
   @reference_number=1000,
   @validation_context=nil>,
 @details={:product_name=>[{:error=>:blank}]},
 @messages={:product_name=>["を入力してください。"]}>

dry-validation 1.0 released

virtus 自体は少し前に開発が止まっていたので、作者が新たに開発している dry-validation が気になっていたのですが、全く違う設計のライブラリのため導入までにはいたりませんでした。 バージョン 1.0.0 (執筆時 1.3.1) がリリースされたのをきっかけにこちらに乗り換えてみることにしました。

dry-validation の良いところはスキーマを定義する DSL の記述がわかりやすく、ドメインロジックのバリデーションルールと明確に処理を分離できる点だと思います。

ここからは dry-validation を使った Form Object の実装方法について説明していきます。

Configurations

dry-validation 自体の共通の設定はベースクラスを用意して定義します。 エラーメッセージの出力時のロケールの設定、共通ルールのマクロやカスタムデータタイプをここで定義します。
例えば、ここに定義している StrippedString は、ありがちな前後のスペースを取り除くのと、空白であった場合に nil に強制する文字列のハンドリングのために利用するデータタイプです。

class ApplicationContract < Dry::Validation::Contract
  config.messages.default_locale = :ja
  config.messages.backend = :i18n
  config.messages.load_paths = [
    Rails.root.join('config/locales/ja.yml'),
    Rails.root.join('config/locales/en.yml')
  ]

  module Types
    include Dry::Types()

    StrippedString = Types::String.constructor { |str| str.strip.presence }
  end
end

Schemas

Schemas は dry-validation の重要な機能でデータをプリプロセスしてバリデーションするための DSL です。 例として以下のようなパラメーターを Schemas を定義してバリデーションします。
params は HTTP パラメーター向けのスキーマ定義のメソッドです。 スキーマ定義の DSL はほぼ記述そのままなので理解しやすいのではないでしょうか? バリデーションの実行結果からクレンジングされた入力値とエラーがそれぞれ .values.errors で取得できます。

{
  "product_name": " Rustic Paper Gloves",
  "reference_number": "59142",
  "images": [
    { "url": "http://ruelarmstrong.com/howard", "caption": "" },
    { "url": "boyer.name/rhett_wunsch", "caption": "" }
  ],
  "description": "                 "
}

app/contracts/product_contract.rb

class ProductContract < ApplicationContract
  params do
    required(:product_name).filled(Types::StrippedString, max_size?: 200)
    required(:reference_number).filled(:integer)
    optional(:images).value(:array, min_size?: 1).array(:hash) do
      required(:url).filled(:string, format?: URI::DEFAULT_PARSER.make_regexp)
      optional(:caption).maybe(Types::StrippedString, max_size?: 500)
    end
    optional(:description).maybe(Types::StrippedString, max_size?: 1000)
  end
end

実行結果

[7] pry(main)> result = ProductContract.new.call(product_name: "  Rustic Paper Gloves        ", images: [{ url: 'http://ruelarmstrong.com/howard', caption: '' }, { url: 'boyer.name/rhett_wunsch', caption: '' }], reference_number: '59142', description: '          ')

=> #<Dry::Validation::Result{:product_name=>"Rustic Paper Gloves", :reference_number=>59142, :images=>[{:url=>"http://ruelarmstrong.com/howard"}, {:url=>"boyer.name/rhett_wunsch"}], :description=>nil} errors={:images=>{1=>{:url=>["は不 正な値です。"]}}}>
[8] pry(main)> ap result.values.to_h
{
        :product_name => "Rustic Paper Gloves",
    :reference_number => 59142,
              :images => [
        [0] {
                :url => "http://ruelarmstrong.com/howard",
            :caption => nil
        },
        [1] {
                :url => "boyer.name/rhett_wunsch",
            :caption => nil
        }
    ],
         :description => nil
}
=> nil
[9] pry(main)> ap result.errors
{
    :images => {
        1 => {
            :url => [
                [0] "は不正な値です。"
            ]
        }
    }
}
=> nil

Rules

スキーマ定義をクリアしたデータをさらに Rules を使ってドメインロジックのバリデーションを実行できます。ここで扱うデータはスキーマ定義をクリアしているのでロジック自体の実装に集中することが可能です。

ここでは例として単純な reference_number が存在するかをデータベースに問い合わせるドメインロジックと、各 imagesの URL の妥当性を判定するルールを追加しました。

class ProductContract < ApplicationContract
  ...

  rule(:reference_number) do
    next if Product.exists?(reference_number: value)

    key.failure(:invalid)
  end

  rule(:images).each do
    next if ImageChecker.call(value[:url])

    key(key_name << :url).failure(:invalid)
  end
end

実行結果

[88] pry(main)> result = ProductContract.new.call(product_name: "  Rustic Paper Gloves        ", images: [{ url: 'http://ruelarmstrong.com/howard', caption: '' }, { url: 'http://boyer.name/rhett_wunsch', caption: '' }], reference_number: '59143', description: '          '); ap result.errors
  Product Exists? (1.2ms)  SELECT 1 AS one FROM "products" WHERE "products"."reference_number" = $1 LIMIT $2  [["reference_number", "59143"], ["LIMIT", 1]]
{
    :reference_number => [
        [0] "は不正な値です。"
    ],
              :images => {
        0 => {
            :url => [
                [0] "は不正な値です。"
            ]
        },
        1 => {
            :url => [
                [0] "は不正な値です。"
            ]
        }
    }
}
=> nil

Macros

例えば、登録フォームとログインフォーム、ログイン API で ID であるメールアドレスのフォーマットを判定するロジックを共通化したい場合 Macros が利用できます。 マクロのブロックにはパラメーターを使うことも可能です。

app/contracts/application_contract.rb

class ApplicationContract < Dry::Validation::Contract
  ...

  register_macro(:email_format) do
    unless URI::MailTo::EMAIL_REGEXP.match?(value)
      key.failure(:invalid_email_format)
    end
  end
end

app/contracts/login_contract.rb

class LoginContract < ApplicationContract
  params do
    required(:email).filled(Types::StrippedString)
    required(:password).filled(Types::StrippedString)
  end

  rule(:email).validate(:email_format)
end

app/contracts/registration_contract.rb

class RegistrationContract < ApplicationContract
  params do
    required(:email).filled(Types::StrippedString)
    required(:first_name).filled(Types::StrippedString)
    required(:last_name).filled(Types::StrippedString)
  end

  rule(:email).validate(:email_format)
end

実行結果

[15] pry(main)> result = RegistrationContract.new.call(email: 'foo'); ap result.errors
{
    :first_name => [
        [0] "を入力してください。"
    ],
     :last_name => [
        [0] "を入力してください。"
    ],
         :email => [
        [0] "は不正なメールアドレスです。"
    ]
}
=> nil
[16] pry(main)> result = LoginContract.new.call(email: 'bar', passsword: nil); ap result.errors
{
    :password => [
        [0] "を入力してください。"
    ],
       :email => [
        [0] "は不正なメールアドレスです。"
    ]
}
=> nil

Rspec

ユニットテストでは、シンプルにバリデーションメッセージの期待値をテストしています。

spec/contracts/product_contract_spec.rb

describe ProductContract do
  subject { result.errors.to_h }

  let(:contract) { described_class.new }
  let(:result) { contract.call(params) }

  describe 'params' do
    describe 'product_name' do
      subject { super()[:product_name] }

      describe '.filled?' do
        context 'filled with valid name' do
          let(:params) { { product_name: Faker::Commerce.product_name } }
          it { is_expected.to eq(nil) }
        end

        context 'not filled' do
          context 'without key' do
            let(:params) { {} }
            it { is_expected.to eq(['を入力してください。']) }
          end

          context 'nil' do
            let(:params) { { product_name: nil } }
            it { is_expected.to eq(['を入力してください。']) }
          end
        end
      end
    end
  end
end

実行結果

ProductContract
  params
    product_name
      .filled?
        filled with valid name
          is expected to eq nil
        not filled
          without key
            is expected to eq ["を入力してください。"]
          nil
            is expected to eq ["を入力してください。"]

Finished in 0.57222 seconds (files took 9.7 seconds to load)
3 examples, 0 failures

Conclusion

最近の開発で以前 virtus ベースで実装したかなり大規模な入力値のバリデーションを dry-validation を使って実装したのですが、コントラクト自体のテストも実装しやすく柔軟に構成できるため、今後も利用していきたいと思います。 ここでは紹介しきれない機能がまだまだありますし dry-rb 自体興味深いプロジェクトなので、ぜひドキュメンテーションソースを参照していただければと思います。


参考

株式会社エニグモ 正社員の求人一覧

hrmos.co

App Maker 触ってみた

こんにちは、データマーケター?の嘉松です。

データ活用推進室というチーム(1人なう)で、MAツールの導入から運用といったCRMの推進と、データ活用の推進や業務を効率化するためのツール作成など、現場に近い立ち位置で業務を行っています。

背景

社内のG Suiteが、Businessにアップグレードされたことで、App Makerを無料で利用することができるようになった。

目的

無料で利用できるApp Makerを使わないのはもったいない。
もったいないおばけが出る前に、

  • App Maker、どんな感じで使えるのか?
  • そのためには、どんなスキルセットが必要か? 

を、そこはかとなく捉えること。

Google App Maker とは

App MakerはGoogleが提供するとっても簡単にWebアプリケーションを開発することができるプラットフォーム。

ドラッグ&ドロップなどの操作で、Web上にフォームやテーブルなどのインターフェース、データベースを作ることで、Webアプリケーションとして公開することができる。

「非エンジニア職のビジネスマンにとってITスキルを磨く超ステキなきっかけの一つになるだろう」との意見も。

ExcelAccessVBAの代替手段になり得る。

App MakerのベースはGoogle Apps Script(GAS)

App Makerの特徴

HTML&CSSの知識が不要

ドラッグ&ドロップでページとUIを簡単に作れる。

Googleサービスとの連携

App MakerはベースはGASなので、様々なGoogleサービスとガッツリ連携可能。

データベースの作成が簡単

本格的なデータベースをクリックベースでGoogleドライブまたはGCP内に作成できる。

無料で強固なサーバーに設置できる

G Suite Business以上のユーザーであれば追加料金なしで利用可能。

サーバーの準備は全く不要。

App Makerの入り口

App Maker HOME

developers.google.com

Google Developers

appmaker.google.com

アプリ、作ってみた

作ったアプリ

ランニング計算機

1キロのペース(○分○秒)を入力すると、フルマラソンの完走タイムを計算してくれるアプリ。

f:id:enigmo7:20191209170733p:plain

ランニング計算機

作った感想

  • テキストボックス、ボタン、ラベルなどの作成は、パーツ(Widgets)から作りたいパーツを選んで配置するだけ(ドラックアンドドロップ)なので、スーパー簡単。

パーツの一覧

f:id:enigmo7:20191209170936p:plain

  • 処理、このアプリの場合はボタンを押した時の計算処理は、下のようなウィンドウ内のエディタにGASで記載する。このエディタの使いっぷりが良くない。ウィンドウ、小さいし。 

f:id:enigmo7:20191209171034p:plain

  • UIの見栄え(デザイン)は、基本的には最小限の変更しかできない。ガッツリ見栄えを良くしたいなら、CSSを使えばできる。

まとめ

  • G Suite Businessで無料で使えるApp Maker、どんなものかと触ってみた。
  • UIの作成は、パーツの一覧からドラック&ドロップするだけなので超簡単。
  • 実行したい処理はGASで記述が必要なので、普通にプログラミングが必要。
  • UIの見栄えのカスタマイズは最小限。その範囲だと結構ショボい。CSSでリッチにできるけど、そうすると本末転倒か?
  • App Makerは、G SuiteのBasicからBusinessにアップグレードすることで利用できる数少ないサービス。
  • Googleさんもきっと力を入れて、より一層使いやすくなってくると思う。
  • ということで、今後の進化に期待!!

株式会社エニグモ 正社員の求人一覧

hrmos.co

3年放置してた_variables.scssを整理したよー。

こんにちは、デザイナーの本田 です。 この記事はEnigmo Advent Calendar 2019 の2日目の記事です。

今日はエニグモが運営しているファッションメディアSTYLE HAUS の色管理方法について紹介していきます。

放置されていた_variables.scss

Sassファイルの中で重要な_variables.scssファイル。このファイルにデザインを管理する様々な変数を定義すれば、変数の値を編集するだけで全体的なデザインの更新ができるようになります。 ただ、最初に設計した後、放置されやすいのも事実です。

STYLE HAUS の開発はECサイトBUYMA の開発の空いた時間にちょこっと進めるスタイルなので、デザインの全体を見返すタイミングがなかなか作れず、3年ほど設計を見返せずにいました。

f:id:enigmo777:20191126205423p:plain
variable.scssを3年放置した図

また、変数も最初のうちは問題なかったのですが、繰り返し修正や変更があり、名前がわかりにくいものに。 メインカラーがピンクのサービスなのもあり、ピンクだけで9種類も増えていました。

$pink:         #d04589;
$pink-dark:    #c82676;
$pink-dark2:   #c3196d;
$light-pink:   #f5edf1;
$light-pink2:  #f2e2ea;
$light-pink3:  #f3dee8;
$light-pink4:  #f8f3f6;
$white-pink:   #FCF7FA;
$pink-neue:    #d77eaa;

ぱっと見では$light-pink4$white-pink の判別が・・・ とりあえず、色だけでも変数化すればサイト全体のデザイン見直しに役立つと思ったので、取り組みました。

やったこと

1. サイト上で使われている色の洗い出し

_variables.scssを使わない代わりに、カラーコードはベタ書きで運用していたため、実際に使われている色の洗い出しをしました。 全部で20色以上・・・あったのでこのあたりでストップ。

2. カラーパレットの作成

被ってしまう色がメインのピンクと無彩色だったので、2色のみ100〜900まで作成しました。

// Gray scheme
$color__white:     #ffffff;
$color__black:     #000000;

$color__gray--100: #f6f6f6;
$color__gray--200: #efefef;
$color__gray--300: #dddddd;
$color__gray--400: #bebebe;
$color__gray--500: #969696;
$color__gray--600: #888888;
$color__gray--700: #555555;
$color__gray--800: #3a3a3a;
$color__gray--900: #212121;

// Pink scheme
$color__pink--main:#dc4991;
$color__pink--100: #fcf7fa;
$color__pink--200: #faf6f8;
$color__pink--300: #f5edf1;
$color__pink--400: #f3dee8;
$color__pink--500: #feb4d5;
$color__pink--600: #d77eaa;
$color__pink--700: $color__pink--main;
$color__pink--800: #c3196d;

f:id:enigmo777:20191127100704p:plain
grayは綺麗なカラーパレットに。ピンクは無理やり作ったので若干バランスが...

3. 命名規則の統一

$red だけだと、どの赤なのかわからなくなるので、他の色も揃えました。 色自体はそのままですが、ソースコードを見たときに「カテゴリ指定の色が使われているんだな」ってことが伝わるようになると思います。

// Before
$beauty:     $pink;
$fashion:    $red;
$lifestyle:  #c2a20b;
$celeb:      $pink;
$children:   #d4669c;
$men:        #5a72c0;
$baby_kids:  #f07468;
// After
$category__beauty:   $color__pink--main;
$category__fashion:  #e82152;
$category__lifestyle:#c2a20b;
$category__children: #d4669c;
$category__men:      #5a72c0;
$category__baby_kids:#f07468;
$category__horoscope:#8765c5;

また、メインのカラーはいろんな場所で使いまわしているので、最初に使った変数を他でも利用すると関連性がぱっと見でわかるようになります。

$color__pink--main: #dc4991;
$color__pink--700:  $color__pink--main;
$category__beauty:  $color__pink--main;

4. 残す変数名を決める

2,で作ったカラーパレットは、全体のデザインを知っている場合は使いやすいのですが、アサインされたばかりの人やエンジニアからしたらどの色を使えばいいのかわかりづらいので、 わかりやすい変数名はそのまま残す方向にしました。

// Others
$pink:              $color__pink--main;
$pink-dark:         $color__pink--800;
$light-pink:        $color__pink--300;
$white-pink:        $color__pink--200;
$red:               $category__fashion;
$green:             #89d045;
$blue:              #4589d0;
$white:             $color--white;
$back-black:        $color__gray--800;
$light-gray:        rgba(239,239,239, 0.3);
$default:           $color__gray--800;
$text:              $color__gray--800;
$hr:                $color__gray--300;
$dark:              $color__gray--800;
$super-light:       $color__gray--200;

これをファイルの下に残すことで、「テキストってどの黒だっけ?800?900?」「ボーダーの色って何色だっけ?」と悩むことが少なくなるかなと思います。

5. ちょっとずつリファクタリング

あとはプロジェクト毎に少しずつリファクタリング・・・! ここのモチベーションを保つのは、結構むずかしいかなと思っていたのですが、 ソースレビューの際に、カラーコードをベタ書きしている箇所はレビュアーから指摘してもらえたので、毎回モチベーション高く取り組むことができました。

結果

_variables.scssに指定した色変数は38個あったのですが、全体を見直した結果49個になりました。 同じ色を重複して書いているので、実際の色自体は28と変化なしでした。

感想

プロジェクトにアサインされて、結構最初に取り組んだのですが サービス全体のデザイン理解にとても役立ちました。

最初にもうこれ以上色は増やさないぞ!!という気持ちで作ったので、UIを考えるときに、色について悩むことがなくなりました。 1回も使ってない色ももちろんあります。($color__pink--400 とか)

おそらく今後も使わないですが、この変数名を残すことによって$color__pink--300-2 とかめちゃくちゃな命名規則が発生しなくなると思うので残しています。

ソースコードからデザインデータまで徹底して整備すると、色だけじゃなくフォントや余白も統一したくなって、デザインデータもかなり整理することができました。

f:id:enigmo777:20191126215502p:plain
シンボル化されたデザインパーツ(Sketch)

参考URL

Sassの色管理方法について考えてみた | 株式会社インディバル

変更に強いSassの色管理プラクティス - Qiita

株式会社エニグモ 正社員の求人一覧

hrmos.co

OSS 初心者が初めてのコントリビューションを通して学んだ3つのこと

こんにちは、 サーバサイドエンジニアの伊藤です。

新卒Rubyエンジニアがオススメする実務で役にたった技術書5選 この記事を書いた時から、ちょうど1年が経ちました。 本当に、時が経つのは早いですねー。。。

そんなこんなで、今年もこの季節がやって来ました。12月と言えば、そうAdvent Calendar の時期ですね!!!

ということで、Enigmo Advent Calendar 2019 を公開します!!!

Enigmo Advent Calendar 2019 記念すべき1日目は、

私、伊藤が 「OSS 初心者が初めてのコントリビューションを通して学んだ3つのこと」 をお届けします。

そもそものきっかけ

弊社では、EC サイトのBUYMA だけではなくSTYLE HAUS というファッションメディアの開発も行なっています。

STYLE HAUS の開発時、記事パーツのアイコン画像をYaml で管理するため、zilkey/active_hash というgem を導入しました。

基本的には ActiveHash*1 を用いることで、スムーズに開発を行うことができました。 ただ、ActiveHash で生成したデータを、sort しようとした時ActiveHash::Base.order が存在しないことに気がついてしまいました。

確かに、ActiveHash を利用して生成したデータは Array なので、Enumerable#sort_by を用いることで、sort は可能です。

↓こんな感じ。

w/ Enumerable#sort_by
Image.all.sort_by(&:title)

ただ、RailsActiveRecord にどっぷりハマった身としては、どうしてもActiveHash::Base.order を使いたい。。。

↓こんな感じ。

w/ ActiveHash::Base.order
Image.order(:title)

そこで、隣の席の先輩エンジニアに相談してみました。

伊藤:
なんで、ActiveHash::Base.order ってないんですかねー? ActiveHash のREADME にもActiveRecord-like model って書いてあるのに。。。

隣の席の先輩エンジニア:
そんなの知らん。無ければ自分でメソッド生やしたらいいやん。

伊藤:
ヒィィィィィ(゚ロ゚;ノ)ノ

当時の私にはメソッドが無ければ自分で実装するとう言う発想はなく、 まさに青天の霹靂でした。

と言うわけで、

やったこと

  1. 本家 ActiveRecordorder メソッドの実装を読み込む。
  2. TDD を意識して、RSpec を実装する。
  3. (2) で実装したテストをパスさせるように、ActiveHash::Base.order を実装する。
  4. PR を作成する。
  5. 祈る。そして、待つ。

何だかんだ最初は OSS と聞いてビビっていましたが、 基本的にやった事はこれだけです。

パッチを投げてからActiveHash の戻り値を chainable にするため ActiveHash::Relation が導入されたので、 実際は(4)と(5) の間でレビューを受けてorder をcahinable に対応する処理を追加実装しました。

今回実装の内容は割愛しますが、ご興味のある方はこちらをご覧下さい。

github.com

学んだこと

ソースコードへの抵抗がなくなった。

恥ずかしながらこれが私にとって初めての OSS コントリビューションでした。*2

こちらで ActiveRecordソースコードをがっつり読み込むまでは、どちらかと言うとネットに落ちているブログ記事や公式ドキュメントを読むだけで終わっていました。 それで理解できなければ、先輩エンジニアのところに泣き込んでいました。。。*3

しかし、今回ソースコードを読み込んだ事で、ソースコードに対しての畏怖の念は無くなりました。 現在では公式ドキュメントはもちろん、それでも分からなければソースコードを読み本当の仕様を確認できるようになりました。

OSS への PR の出し方、所作を学ぶことができた。

PR のコメントの文面は OSS ごとに特有のものがあると思います。 すでに、当該の OSS でマージ・クローズされている PR のフォーマットを参考にしました。 内容に関しては、MECE を意識しました。*4

MECE とは、

  • Mutually(お互いに)
  • Exclusive(排他的に)
  • Collectively(集合的に)
  • Exhaustive(余すところのない)

要は、「内容に漏れがなく、重複がないように。」と言うことです。 レビューしてくれる方が実装内容および、 PR コメントを見ただけで全てが理解できるように意識しました。

OSS を身近に感じることができた。

以前は OSS コントリビューターさんやメンテナーさんは、雲の上の存在。 いや、むしろその存在すら意識していなかったかも知れません。。。

今まではどこかで OSS はあって当然のように思っていましたが、微力ながらのコントリビューションを通してその考えを新たにすることができました。

私たちが OSS の恩恵を受けられるのは、多くのコントリビューターさん、メンテナーさんのお陰なんだなぁと。

本当に感謝です。

今後も機会があれば微力ながら OSS に貢献していきたいと思います。

本日はこの辺で、 最後まで読んでいただきありがとうございます!

株式会社エニグモ 正社員の求人一覧

hrmos.co

*1:ActiveHash は、Ruby のHash をActiveRecord-like のモデルとして使用するためのbase class です。

*2:レビューを受けてマージされるまでの間に他の OSS に投げたパッチがマージされてしまいましたが。。。

*3:良くも悪くも弊社のエンジニアは良い方ばかりなので、基本的に質問すれば親切に教えてくれます。

*4:これに関しては一回の PR で学んだと言うよりは、いくつかの PR を経て学んだと言う感じですが。。。

エンジニアインタビュー 第5回 伊藤明大さん編

エニグモBUYMA の中のひとを知ってもらおうと、エンジニアへのインタビューをしてみました。
第5回は、2018年2月入社の検索エンジニア、伊藤明大(通称:めーだい)さんです。

インタビュアー
小澤:2011年4月入社。部長。
伊藤翔:2018年5月入社。新卒2年目。

interview01

職種は?

伊藤翔(以下、翔):
明大さんのポジションは、検索エンジニアですか? Webエンジニアですか?

伊藤明大(以下、明大):
検索エンジニアですかね、わかんないけど(笑)
Webエンジニアってのもよくわからないけど、検索をメインでやっているエンジニアですね。

前職は?

翔:
前職ではどんなことをやられていたんですか?

明大:
前職はSolrやElasticSearchを使った検索だけをやってました。
そのまえはFastっていう製品を使っていましたが、それからOSSに置き換えようという話になってSolrやElasticSearchを利用するようになりました。

翔:
大学卒業してからはずっとエンジニアをやられているんですか?

明大:
前職は2社目で、その前は新卒でSIerに入社してそこに10年ぐらいいました。

翔:
その時も検索を担当されていたんですか?

明大:
全然やってないです。どちらかというとインフラよりで、サーバーをラックに載せたり、ケーブル配線もしてました(笑)
本当になんでも屋みたいな感じでした。

小澤:
開発もしてたんですか?

明大:
開発は開発部隊が別にいたのでほとんどやってないです。お客さんと開発部隊の間を取り持つ窓口みたいな立場ですね。

小澤:
いわゆる「SE」ですね。

interview07

明大:
そうです、まさに「SE」でしたね。
開発部隊が作ったものをお客さんの環境にインストールするわけですよ。それようのツール作ったりとか、手順書を作ったりとか。
いまからは想像できないような作業内容ですね。
お客さんの環境で作業するときは、1人がオペレーター・作業するひとで、1人が手順書読み上げと、、、

小澤、翔:
読み上げ!!

翔:
読み上げっていうのはただ手順を読むんですか?

明大:
そう(笑)
基本的にはコピペ作業なんだけど、コピペしたことが手順書とあっているよね、っていう確認をして、その人が確認して「OK」っていったらオペレーターが「Enter」していいっていう手順ですね。
1年に2回しかリリース作業がないのですが、そこまでやる?っていう作業をしていました。

翔:
それを10年間やってたんですか?

明大:
そうなんだけど、6年目ぐらいかな、あまり進歩ないなと思って別のところを希望したんですよ。そしたらもっとひどい環境でした。
リリース間際のプロジェクトに開発のスペシャリストがくるっていう触れ込みで配属されてしまい、お客さんが期待していろいろ質問してくるんですが全くわからない。
これは地獄だと(笑)。ということで、もとの環境がよく見えて(笑)、戻って3年ぐらい過ごしました。
まあでもやっぱり違うなと思ってWeb業界に転職しました。

翔:
転職したときは検索に携わりたいと考えていたんですか?

明大:
いや全然(笑)
一番は自社サービスを持っている会社を希望していました。
それは1社目で感じたしがらみ、たとえばサーバーを導入するのもいいと思ったものじゃなくて会社が売りたいものを売る必要があるんですよ。
自社サービスだと自分でスペックも価格も考えていいものを選べるだろうなと考えました。
それと、1社目もWeb関連がやりたいと希望してできる部署に配属してもらいました。ですが、仕事がなさすぎて(笑)
配属されて5,6ヶ月仕事がなくて自習していました。そしたら別のところから仕事あるよって話がきたのでそこに行きました。

翔:
1回目の転職ではどういうことをするか決まって入社したんですか?

明大:
もともとは検索というよりはデータ、ビックデータが流行り始めた時期でやりたいなと思っていました。
それでHadoopとかを運用している部署で、そこに検索のチームもありました。それで入ったんですが、検索を担当している人が少なかったので自分が担当することになりました。
自分が希望して検索ってことではないです。それでもやったことがなかったのでやってみたかったです。

interview02

エニグモへの転職について

翔:
2社目からエニグモへ転職されたときの考えはなにかありますか?

明大:
2社目では検索しかできなくて飽きていました。それと、複数のサービスを持っている会社でしたが、各サービスからくる検索案件をさばくだけで終わっていたので、自分からこうしたいってことができていませんでした。
次はサービスに入り込んでサービス提案もできるようなところ、それと服が好きなのでファッションのサービスがいいなと思っていました。

翔:
たしかに、明大さんおしゃれですよね。

明大:
え、(笑)
ありがとう。

翔:
小澤さんと2大ファッショニスタ的な存在と思っていました(笑)

明大:
え、そうなの(笑)

小澤:
私も「服好きですよね」って聞くだけ聞いたことがありますね。
「調子乗って俺より上にでるんじゃねーぞ」って意味でね(笑)

入社後の印象

翔:
入社後の会社の印象はどうでしたか?

明大:
すごく自由だなと思いましたね。いい意味でね。あとはみんな若いなと思いましたね。
前の会社もエンジニアが依頼してくる部署なので、エンジニア以外と働くことがほとんどなくて、エンジニア以外が近くにいるっていうのが新鮮でしたね。
なんかいいなって(笑)

翔:
入社してからの一番の思い出は何ですか?

明大:
開発合宿かなぁ。
開発合宿が初めてでしたし。

翔:
え、そうなんですか。Web業界ってどこも合宿やっているイメージでしたけど。。。

明大:
前の会社はなかったですね。やっているところが発信しているだけですからね。
普段の業務で関わらないエンジニアと関われたし、なによりリフレッシュになりました(笑)

翔:
他の部署の人を含めて合宿やりたいなって明大さんが言っていたと聞いたんですが本当ですか?

明大:
そうですね。ブレストするだけとかの合宿、たとえば経営層で合宿やりましたとかたまにありますけど、そういうのをエンジニア以外も含めてやっても面白いかなと思って言いました。

翔:
なるほど、検討します。

明大:
え!翔くんが仕切るの(笑)

翔:
決裁権はないですけど、提案はできるかなと(笑)
ちなみに業務で思い出に残っているものはありますか?

明大:
業務だと、直近でやっていた検索システムの更改ですかね。
アーキテクチャーからやり直して、6ヶ月ぐらいやって最近リリースできました。
エニグモに入って一番でかくて、やりきったなと。

翔:
やりきったって、大丈夫ですか?(笑)

小澤:
燃え尽きたんじゃなく?(笑)

明大:
そういうわけじゃないです(笑)
当初やりたいと思っていたことを妥協なくやりきりました、という意味です(笑)

interview03

入社後に変わったこと

翔:
エニグモに入ってから変わったことはありますか?

明大:
もともとはスケジュール管理をしっかりしてたんですけど、そんなにやらなくなりました。
良いとも悪いとも思ってないですが、振り返ると変わったなぁと思います。

翔:
スケジュール管理しなくなったと聞くと悪いことに思いますがどうですか?

明大:
なぜかというと、締切りの決まっている作業をほとんどやらなくなったからですね。
先程の検索システム更改は出品リストのリプレイスと一緒にやったので期限は決まっていましたが、それ以外はいつまでってほとんどないですね。

現在取り組んでいる業務

翔:
直近は何をされているのでしょうか?

明大:
検索システムリプレイスが一旦落ち着いたので、在庫のある商品の検索をできるようにするっていう施策をやっています。

翔:
普段の業務はどういうチーム構成ですか?

明大:
ほとんど1人ですかね。
もうひとりいたこともありましたが、別のチームに移ってしまいましたし、上司の木村さんに手伝ってもらうこともありますが、基本は1人ですね。
1人でやるのはいろいろ良くないので誰かいるといいなと思っています。

翔:
一緒にやる人は検索の経験がないとだめですか。経験がなくても大丈夫ですか?

明大:
検索やってましたって人はだいぶレアだと思っているので経験は期待していないですね。
検索やりたいですって人がいればいいですけど、もともとインフラやってたけどもうクラウドの時代だからアプリよりに行きたいなって人、そういう人がSolrのようなミドルウェアから入るのが、入り口としてはよいかなと思っています。
逆に開発ばかりやっていたけどインフラへって人でも入り口としてはいいと思いますね。
間口は広いですよ(笑)

開発について

小澤:
もしかして3社あって一番プログラミングしてますか?

明大:
いま一番書いています!
前職でもAPI担当者と検索インフラって別れていたので、ツールは作っていましたけど、ゼロから検索APIを作ったのは今回初めてですね。

翔:
そうなんですか?!

明大:
既存のAPIの修正をしたりはありましたが、フレームワークから選んで開発もしてって初めてですね。
いまめちゃめちゃ成長してます(笑)

小澤:
シロウトか!(笑)
下から入ってきていま上に登っていますって感じですね。

明大:
最近は自分のことを晩成型なんだなって思っています(笑)

翔:
大器晩成ですね。僕も負けないように頑張ります。

interview07

失敗

翔:
入社後に失敗したことがあれば教えて下さい。

明大:
細かい失敗はあると思うんですが、失敗を失敗と思っていない、反省点として捉えていますね。
直近でいうと、出品リストのリプレイスで、もともとデータベースでやっていたものをSolrに乗り換えますってなったんですよ。
そうすると、Solrのインデックスを作るまでのタイムラグが絶対にあるんですが、それを伝えきれてなかったなと反省しています。

小澤:
なれている人からすると当たり前のことですからね。

明大:
そうですね。検索を長くやっていたせいで見落としてしまいましたね。

エニグモのいいところは

翔:
ではエニグモのいいところを教えて下さい。

明大:
これまでの経験を踏まえると、エンジニア以外との距離感が近いのが一番いいなと思います。
以前の職場ではエンジニア以外が普段何をしているのか全くわからなかったんです。
それがわかるようになってそこから課題感を見つけられたり、エンジニアとしてどうしたらよいかと、そういうことができるのがいい環境だなと思います。

翔:
僕は新卒として入社しているのでこれが当たり前だと感じているのですが、そうではないんですね。

明大:
恵まれてるんだよ(笑)
でも、どっちがいいかはわからないですけどね。もしかしたらエンジニアの世界だけでやりたい、ソッチのほうがいい人っていうのもいるでしょうし。
結局は本人にフィットするかどうかってことなので、いいかどうかはその人次第ですね。

翔:
逆に改善点はありますか?

明大:
入って最初に思ったのは、ドキュメントが少ないですし、メンテナンスされてないのが辛かったですね(笑)
でもそのバランスって難しい、すべて残す必要はないと思いますし、効率よく必要最低限を残したいので、整備したいなと思っていますね。
それと1人で作業することが多いので、作業上の相談、グチ(笑)を話せる人があまりいないですね。
木村さんは協力はしてくれますが一緒のタスクはやっていないですからね。

翔:
一人ぼっちなんですね(笑)

明大:
それは言い方がよくないけど、作業レベルのことって細かいことじゃないですか、それを話す相手がいないのがちょっとつらいですかね。
まあそれはエニグモ全体の話じゃなくて自分の置かれている立ち位置の話ですけどね。

翔:
話し相手が欲しいと(笑)

明大:
それはおかしいだろ!そこまで一人ぼっちじゃないよ(笑)

今後について

翔:
マネジメントとかは興味ありますか?

明大:
1社目が一番マネジメントしていました。それは管理職に上がるかどうかっていうキャリアパスしかないので、それをするしかないという環境でしたね。
それが嫌でやめたので、マネジメントと技術と半々ぐらいがいいかなと思っていますね。

interview05

一緒に働きたいひと

翔:
どんな人と一緒に働きたいですか?

明大:
向上心のある人、立ち止まらず常に走り続けている人ですかね。自分なりの考えを持っている人がいいですね。
逆にいうと、言われたことしかできない人はいやですね。
意見のぶつかり合いがあったとして、それが言える人、そういう環境がいいと思う人がいいですね。

翔:
いろいろ新しいこととか挑戦していく人ですかね。

明大:
新しいこともそうですし、いま抱えている問題を自分で見つけて、それをどうするか考えられる人ですね。
あとは刺激しあえる関係性がいいですね。技術的なことや考え方で刺激しあえて、一緒に楽しく仕事ができる人がいいですね。

翔:
経験は関係ないですか?

明大:
経験はおまけぐらいで、やればできるでしょと思っていますね。

ヤンキー感

翔:
体育会系な感じですか?(笑)

明大:
そうじゃなくて、そういう考え方の人なら経験がなくてもやればできるでしょって、おまけぐらいに思ってます。

翔:
情熱的な感じですね。もともと体育会系ですか?

明大:
体育会系の定義がわからないけど(笑)

翔:
ずっとスポーツをやってたとか、

明大:
スポーツはずっとやってたけどね。

翔:
いやなんか、話しかけるときに「は?!」「あ?!」みたいな反応するじゃないですか(笑)

小澤:
そうですね、こわいですね(笑)

明大:
そう? 本当に?(笑)

翔:
最初話したときヤンキーなのかなと思いましたよ(笑)

interview04

小澤:
完全にヤンキー感出てますよね(笑)

明大:
いやそれ、この会社はいって初めて言われるようになったんですよ(笑)
「あ?!」とは言わないし、声が大きいだけだよ。

翔:
はじめのころは話しかけるのにビクビクしていたんですけど、最近はこれが明大さんなんだなって慣れました。 でも他のメンバーも怖いって言ってますよ(笑)

明大:
だから結局1人で作業しているから、あいつ何やっているかわかんねーし、話しかけると怖いしって印象になってるんですよ。

小澤:
でも明大さんは明確に上と下で言葉を分けますよね。それがたぶんヤンキーっぽいと思いますけどね(笑)

明大:
ハハハッ(笑)
でも僕は年上の人に敬語で話されるのがいやなんですよ。気を使ってしまうので。
なので翔くんにはタメ口で話しますし、上の人には敬語で話しますね。

小澤:
そんな上じゃないけどね(笑)

明大:
でも僕が小澤さんにタメ口だったら変じゃないですか?

小澤:
そうかなぁ(笑)

翔:
基準は年齢だけですか?

明大:
年齢と社歴かなぁ。木村さんには敬語だし、、、キャラもあるかな。たとえば山本くんにはタメ口だけど、木村さんと年齢はそんなに変わらないですしね。

応募を考えている人へのメッセージ

翔:
応募を考えている人になにかメッセージはありますか?

明大:
まずは気軽に話を聞きにきていただければいいなと思っています。
いきなり書類応募、面接って重たいじゃないですか。話聞きたいなっていうのでいいと思いますね。カジュアルな感じでいいと思います。

新人にアドバイス

翔:
毎回聞いているんですが、新人、主に私になにかアドバイスいただけますか?

明大:
新人にかぁ。最初は浅く広くやってみて、楽しいと思うことがあったらそれを深めてみるのがいいかなと思います。
いまの所属はあるけど、ある程度したら別のことをやってみて、たとえばインフラでも、検索でもいいですし、別のことやらせてくださいってやってみるといいと思いますよ。
やれとは言わないですけどね(笑)

翔:
ミドルウェアはいいかもしれないですね。

明大:
検索やる?

小澤:
でも怖いからね。伊藤くんビビってんじゃん(笑)

翔:
子鹿のように震えてます(笑)

趣味

翔:
趣味の話をしたいんですが、明大さんよくランニングされていますよね。

明大:
ランニングは、趣味っていうよりは健康のためかな。走ってて楽しくないですしね。
健康って、身体的な面と精神的な部分と。その2つでやっています。

翔:
?! 精神的なって、やっぱり体育会系な発想ですね(笑)

明大:
そんなことないよ。運動して汗かくが一番のストレス発散だと感じていますね。

翔:
あー、ストレス発散ですか。鍛錬なのかなと思いました(笑)

明大:
修行じゃないよ。

翔:
フットサルもやりますよね。

明大:
フットサルも趣味だったけど最近全然やらないですね。
趣味は、、スノーボードですね。昔からずっとやっていて、子供が生まれてから2,3年やれてなかったんですけど去年からまた行き始めてスノーボード熱が熱くなってますね。

翔:
出身が雪国なんでしたっけ?

明大:
生まれは静岡で幼稚園までいて、小学校からはずっと神奈川です。
スノーボードが盛んなところに住んでいたわけではないです。

翔:
Androidっぽい時計をつけていますが、ガジェットも好きですか?

明大:
これはランニング用に買ったアマズフィットっていうブランドで、日本では売ってなくて買ったんですが失敗したと思ってますけどね。
でもランニング用にはあまり使えなくてファームウェアアップデートを待ってますね(笑)
ランニング以外は良くて睡眠の質や健康状態がわかったり、ライフトラッキングに良いツールかなと思ってます。

小澤:
Apple Watch買えばいいんじゃないかと思いますけどね(笑)

翔:
はい、ではこんなところですかね。ありがとうございました!

明大:
ありがとうございました!

interview06

株式会社エニグモ 正社員の求人一覧
https://hrmos.co/pages/enigmo/jobs?jobType=FULL