sitateru tech blog

sitateru tech blog

シタテルの技術やエンジニアの取り組みを紹介するテックブログです。

2018年12月11日火曜日

Active Storage移行記:データ移行編

12月 11, 2018

こんにちは、あさのです。

前にActive Storageの記事を書きましたが、今回もそのネタで行こうと思います。

Active Storage移行記:バリデーション編|sitateru tech blog

今回は、Paperclip管理のデータをActive Storage管理に移行したときのお話です。

データ構造

Paperclipではファイルのデータは各モデルのカラムに保存されています。

userクラスのiconというファイルであれば、userテーブルに以下のようなカラムがあります。

  t.string "icon_file_name", comment: "ファイル名"
  t.string "icon_content_type", comment: "ファイルのcontent type"
  t.integer "icon_file_size", comment: "ファイルサイズ"
  t.datetime "icon_updated_at", comment: "ファイル更新日時"

対してActive Storageでは全クラスの全ファイルをまとめて2つのテーブルで管理します。

create_table "active_storage_attachments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
  t.string "name", null: false
  t.string "record_type", null: false
  t.bigint "record_id", null: false
  t.bigint "blob_id", null: false
  t.datetime "created_at", null: false
  t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
  t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end

create_table "active_storage_blobs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
  t.string "key", null: false
  t.string "filename", null: false
  t.string "content_type"
  t.text "metadata"
  t.bigint "byte_size", null: false
  t.string "checksum", null: false
  t.datetime "created_at", null: false
  t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end

たとえば、

  • userクラスのicon
  • user.id=15
  • ファイル名 prof.jpg

というファイルだと以下のようなレコードになります。
2つのテーブルは blob_id で関連づけられるんですね。

id    name    record_type    record_id    blob_id    created_at
15    icon    User    3    16    2017-12-28 19:23:03

id    key    filename    content_type    metadata    byte_size    checksum    created_at
16    3e0ca647-18e3-43ee-85c4-4de552bac46f    prof.jpg    image/jpeg    {}    44202    isFyYNJIs/6pyptfRUMUlA==    2017-12-28 19:23:03

データ移行

さて、それぞれのデータ構造がわかったのでマイグレーションをしていきます。

このあたりの記事を参考にさせてもらい、rakeタスクで実装しました。

namespace :import_to_active_storage do
  desc 'Import to ActiveStorage'
  task run_all: :environment do
    models = ActiveRecord::Base.descendants.reject(&:abstract_class?)
    import_data(models)
  end

  private

  def import_data(models)
    conn = ActiveRecord::Base.connection
    models.each do |model|
      attachments = model.column_names.map do |c|
        c =~ /(.+)_file_name$/ && Regexp.last_match(1)
      end.compact

      model.find_each do |instance|
        attachments.each do |attachment|
          puts "Process #{model.name}/#{instance.id}"

          next unless instance.send("#{attachment}?")
          next if conn.select_one("SELECT `id` FROM active_storage_attachments WHERE `record_type` = '#{model.name}' AND `record_id` = #{instance.id}").present?

          puts "Migrate #{instance.send(attachment.to_s)}"

          ActiveRecord::Base.transaction do
            execute_statement(conn, model, instance, attachment)
          end
        end
      end
    end
  end

  def execute_statement(connection, model, instance, attachment)
    active_storage_blob_statement(connection).execute(
      key,
      instance.send("#{attachment}_file_name"),
      instance.send("#{attachment}_content_type"),
      instance.send("#{attachment}_file_size"),
      checksum(instance.send(attachment)),
      instance.updated_at.strftime('%Y-%m-%d %H:%M:%S')
    )

    active_storage_attachment_statement(connection).execute(
      attachment,
      model.name,
      instance.id,
      instance.updated_at.strftime('%Y-%m-%d %H:%M:%S')
    )
  end

  def active_storage_blob_statement(connection)
    connection.raw_connection.prepare(<<-SQL)
      INSERT INTO active_storage_blobs (
        `key`, `filename`, `content_type`, `metadata`, `byte_size`, `checksum`, `created_at`
      ) VALUES (?, ?, ?, '{}', ?, ?, ?)
    SQL
  end

  def active_storage_attachment_statement(connection)
    connection.raw_connection.prepare(<<-SQL)
      INSERT INTO active_storage_attachments (
        `name`, `record_type`, `record_id`, `blob_id`, `created_at`
      ) VALUES (?, ?, ?, LAST_INSERT_ID(), ?)
    SQL
  end

  def key
    SecureRandom.uuid
  end

  def checksum(image)
    uri = URI.parse("https:#{image.url}")
    body = Net::HTTP.get(uri)
    Digest::MD5.base64digest(body)
  end
end

そこそこ長いですが、結局は各モデルの各ファイルごとにPaperclipのカラム情報とファイルのハッシュ値を取り、
Active Storageのテーブルに入れるという繰り返しです。

なお、各モデルの実装をActive Storage用に書き換えた後ではこのタスクは動かないので、
Paperclip時に実行する必要があります。

実際にやったところ数万件のデータがあったために5~6時間かかるという大引っ越しになってしましましたが、
なんとか乗り切りました。


ということで今回はActive Storageのデータ移行について書きました。

まだもう少しActive Storageネタで書けるかもしれません。

それでは。

2018年12月10日月曜日

縫製工場の類似度算出を深層学習のモデルでやってみた

12月 10, 2018
こんにちは!
シタテル株式会社CTOの和泉です。

今回は研究開発的に、「縫製工場の類似度算出を深層学習のモデルでやってみた」について書きます。
まず初めに結果から。
['loss', 'acc']
[273999.25, 0.695652186870575]
69.6% 成功?
なにが?って話ですよね。
結果的には全然精度出ませんでした。まぁ、のべ1週間ぐらいでやってみた感じですし、サーベイも全然できてないので結果出るわけないのですが。
せっかくやってみたのでブログに恥を晒しておきます。
その道の専門の方にはお目汚しかと思いますがご容赦ください。

やりたかったこと

弊社コンシェルジュが手動で類似分類(グループ化)した縫製工場 のデータを教師データとして学習し、同様の類似分類を行える学習済みモデルを作成する。
類似分類ができれば、これまでのアイテム作成履歴から代替となる工場の提案ができる。かも。

希望的仮定

類似度の判定に用いられる特徴はある程度絞られているのではないかと想定し、学習によって効果の高い特徴が反映されるモデルが作成されれば、人の判断に近づけるのではないか。

結果

教師データに対しては98.8%の正答率に達したが、テストデータについては69.6%にとどまった。残念ながら実用に足るような結果は得られなかった。
images/image-3.png
図1:教師データに対する推論結果のプロット(青が教師データ、点線先のオレンジが対応する推測結果)
images/image-4.png
図2:テストデータに対する推論結果のプロット(青が教師データ、点線先のオレンジが対応する推測結果)
images/image-5.png
図3:学習の経過

何をやったのか

データベースに保有する123の縫製工場を2次元平面上で類似しているものを近くに配置するように、弊社コンシェルジュに依頼して教師データを作成。(図1の青い点が各工場を表す)この際、「類似している=あるアイテムを相談するときに変わりに相談できる工場であるか」とした。作成された教師データが含む縫製工場データは114件。9件は情報不足により配置できなかった。
作成した教師データは各工場ごとに位置ベクトルが与えられた状態になっている。この位置ベクトルを各工場がもつ特徴ベクトル(全313次元のうち、名前などヒューリスティックに取り除けるものを除いて、108次元)から求めるモデルを学習により獲得した。

DNNモデルや条件などのメモ

model = Sequential()
model.add(Dense(DIM, activation='relu', input_dim=DIM))
model.add(Dropout(0.25))
model.add(Dense(DIM*2, activation='relu'))
model.add(Dense(2))

model.compile(optimizer='adam',
              loss='mean_squared_error',
              metrics=['accuracy'])
images/image-6.png
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 108)               11772     
_________________________________________________________________
dropout_1 (Dropout)          (None, 108)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 216)               23544     
_________________________________________________________________
dense_3 (Dense)              (None, 2)                 434       
=================================================================
Total params: 35,750
Trainable params: 35,750
Non-trainable params: 0
_________________________________________________________________
history = model.fit(X_train, Y_train,
            batch_size=50,
            epochs=5000,
            validation_split=0.1, 
            verbose=1)

考察

代替工場を考える際には、アイテムによって代替として扱うものが変わることが想定されるため、全体を2次元平面上に次元縮退してプロットすることが難しいであろうことは当初予定していたとおりであった。
今回は、比較的手軽に作成できる教師データを用いて試してみたが、十分な結果は得られなかった。
統計によるリコメンドではデータの量が問題になるが、良質で量のあるデータが十分にないのが現状。シタテルの取扱量が増えることによりある程度のデータは蓄積されていくが、滝行との連携も含めて扱えるデータ量を増やしていくことが必要である。

工場データベースについて

シタテルは500を超える縫製工場やサプライヤーと連携して衣服づくりサービスを提供しています。
それぞれの縫製工場に対して、設備や人員、アイテムごとの金額感、繁閑の目安など300を超える情報をデータベースに格納しています。
images/image-1.png
工場を検索して絞り込み

(工場名や取引先は非公開のため消してあります)
images/image-2.png
いくつかの工場を並べて比較
弊社コンシェルジュはこれらのデータを使ってご相談いただいたアイテムごとに最適な工場に問い合わせを行います。

2018年12月7日金曜日

CircleCIでAWS ECSに自動デプロイする(おまけで環境面の切り替えも)

12月 07, 2018

こんにちは、シタテルの茨木です。

今回はCircleCIでDockerをビルドし、そのままAWS ECR/ECSにデプロイする為の設定例を紹介します。

Nuxt.jsアプリ用に作ったものですが、言語やAPサーバが違っても共通の部分がほとんどです。

PRマージからの自動デプロイ、いいですよね。

https://circleci.com/docs/2.0/ecs-ecr/
https://github.com/CircleCI-Public/circleci-demo-aws-ecs-ecr

基本的にはCircleCI公式ガイドの上記リンクを参考にしていますが、元ネタはterraform前提だったりでなかなか大仰です。(下記に説明漏れがあったら原典を見てください…)

今回紹介するのはリンク先のスクリプト類を簡素化し、少し機能を追加(リポジトリ名からデプロイ先の環境面を切り替えできるように)したものです。

以下で省略しているけれど必要なモノ

  • ECS/ECRの設定

これだけで別の記事が書けてしまうので割愛します

ecsTaskExecutionRoleの割り当てとか忘れがちですかね

  • githubリポジトリ

言わずもがな。CircleCIとの接続設定はされているものとします

  • Dockerfile

リポジトリのルートに。Dockerfileの書き方は解説しません

  • requirements.txt

リポジトリのルートに。awscliを使えるようにします

https://github.com/CircleCI-Public/circleci-demo-aws-ecs-ecr/blob/master/requirements.txt

  • CircleCIの環境変数設定

github上にaccess_keyを晒さなくていいように、入れておきます

https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-project

この手順で設定した環境変数はCircleCIのジョブから参照できます

.circleci/config.yml:build

version: 2
jobs:
~省略~
  build:
    docker:
      - image: circleci/node:10-stretch
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Setup common environment variables
          command: |
            echo 'export ECR_REPOSITORY_NAME="YOUR-ECR-REPO-NAME"' >> $BASH_ENV
            echo 'export FULL_IMAGE_NAME="${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${ECR=REPOSITORY_NAME}:${CIRCLE_SHA1}"' >> $BASH_ENV
      - run:
          name: Build image
          command: |
            docker build -t $FULL_IMAGE_NAME .
      - run:
          name: Save image to an archive
          command: |
            mkdir docker-image
            docker save -o docker-image/image.tar $FULL_IMAGE_NAME
      - persist_to_workspace:
          root: .
          paths:
            - docker-image
~省略~

さっそくですがビルドジョブです。

  • YOUR-HOGEHOGE箇所はご自身のECS/ECRの設定に合わせて置き換えてください
  • - image: circleci/node:10-stretchnode:10を使っていますが、これはビルドする対象がNuxt.jsアプリだからですね。Dockerビルド時に必要なモノに合わせて修正してください

.circleci/config.yml:deploy

~省略~
  deploy:  
    docker:
      - image: circleci/python:3.6.1
    environment:
      AWS_DEFAULT_OUTPUT: json
    steps:
      - checkout
      - setup_remote_docker
      - attach_workspace:
          at: workspace
      - restore_cache:
          key: v1-{{ checksum "requirements.txt" }}
      - run:
          name: Install awscli
          command: |
            python3 -m venv venv
            . venv/bin/activate
            pip install -r requirements.txt
      - save_cache:
          key: v1-{{ checksum "requirements.txt" }}
          paths:
            - "venv"
      - run:
          name: Load image
          command: |
            docker load --input workspace/docker-image/image.tar
      - run:
          name: Setup target environment variables
          command: |
            echo 'export TARGET=`echo $CIRCLE_BRANCH | sed -e s/develop.*/dev/ -e s/release.*/stg/ -e s/master/prd/`' >> $BASH_ENV
            source $BASH_ENV
      - run:
          name: Setup common environment variables
          command: |
            echo 'export ECR_REPOSITORY_NAME="YOUR-ECR-REPO-NAME"' >> $BASH_ENV
            echo 'export ECS_CLUSTER_NAME="YOUR-ECS-CULSTER-NAME-PREFIX${TARGET}"' >> $BASH_ENV
            echo 'export ECS_SERVICE_NAME="YOUR-ECS-SERVICE-NAME-PREFIX${TARGET}"' >> $BASH_ENV
      - run:
          name: Push image
          command: |
            . venv/bin/activate
            eval $(aws ecr get-login --region ap-northeast-1 --no-include-email)
            docker push $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/$ECR_REPOSITORY_NAME:$CIRCLE_SHA1
      - run:
          name: Deploy
          command: |
            . venv/bin/activate
            export AWS_DEFAULT_REGION="ap-northeast-1"
            export ECS_TASK_FAMILY_NAME="YOUR-ECS-TASK-NAME-PREFIX${TARGET}"
            export ECS_CONTAINER_DEFINITION_NAME="YOUR-ECS-CONTAINER-DEF-NAME-PREFIX${TARGET}"
            export EXECUTION_ROLE_ARN="arn:aws:iam::$AWS_ACCOUNT_ID:role/ecsTaskExecutionRole"
            bash ./deploy.sh
~省略~

デプロイジョブです。

  • ブランチ名から環境名へ

echo 'export TARGET=`echo $CIRCLE_BRANCH | sed -e s/develop.*/dev/ -e s/release.*/stg/ -e s/master/prd/`' >> $BASH_ENV

ブランチ名に応じた値(developブランチなら"dev")を環境変数に入れています。これをsuffixとして使い、デプロイ先のクラスタ名等に反映しています

  • YOUR-HOGEHOGE箇所はご自身のECS/ECRの設定に合わせて置き換えてください

余談ですが、ECSはクラスタ・サービス・タスク・タスク定義といろいろ設定しないといけないのですが、なかなか直感的にわかりにくくてイマイチな印象です

.circleci/config.yml:workflows

~省略~
workflows:
  version: 2
  build-deploy:
    jobs:
      - build:
          filters:
            branches:
              only:
                - develop
                - /release\/.*/
                - master
      - deploy:
          requires:
            - build
          filters:
            branches:
              only:
                - develop
                - /release\/.*/
                - master

この辺はよしなに

deploy.sh

#!/usr/bin/env bash
set -eo pipefail
# more bash-friendly output for jq
JQ="jq --raw-output --exit-status"

deploy_cluster() {
  make_task_def   
  register_definition

  if [[ $(aws ecs update-service --cluster $ECS_CLUSTER_NAME --service $ECS_SERVICE_NAME --task-definition $revision | \
      $JQ '.service.taskDefinition') != $revision ]]; then
    echo "Error updating service."
    return 1
  fi

  # wait for older revisions to disappear
  # not really necessary, but nice for demos
  for attempt in {1..30}; do
    if stale=$(aws ecs describe-services --cluster $ECS_CLUSTER_NAME --services $ECS_SERVICE_NAME | \
        $JQ ".services[0].deployments | .[] | select(.taskDefinition != \"$revision\") | .taskDefinition"); then
      echo "Waiting for stale deployment(s):"
      echo "$stale"
      sleep 30
    else
      echo "Deployed!"
      return 0
    fi
  done
  echo "Service update took too long - please check the status of the deployment on the AWS ECS console"
  return 1
}

make_task_def() {
  task_template='[
    {
      "name": "%s",
      "image": "%s.dkr.ecr.%s.amazonaws.com/%s:%s",
      "essential": true,
      "portMappings": [
        {
          "containerPort": 80
        }
      ],
      "environment": [
        {
          "name": "TARGET",
          "value": "%s"
        }
      ]
    }
  ]'

  task_def=$(printf "$task_template" $ECS_CONTAINER_DEFINITION_NAME $AWS_ACCOUNT_ID $AWS_DEFAULT_REGION $ECR_REPOSITORY_NAME $CIRCLE_SHA1 $TARGET)
}

register_definition() {
  if revision=$(aws ecs register-task-definition --requires-compatibilities FARGATE --cpu 256 --memory 1024 --network-mode awsvpc --execution-role-arn $EXECUTION_ROLE_ARN --container-definitions "$task_def" --family $ECS_TASK_FAMILY_NAME | $JQ '.taskDefinition.taskDefinitionArn'); then
    echo "New deployment: $revision"
  else
    echo "Failed to register task definition"
    return 1
  fi
}

deploy_cluster

デプロイジョブから呼び出すスクリプトです。

task_templateの内容を色々弄るとカスタマイズできます

設定可能な項目はこちら

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/create-task-definition.html

ここでは、environmentで環境変数TARGETを設定しています

ECSがdocker runする際に引数として付与され、docker内のアプリから環境変数として参照可能になります

実際には、nuxt.config.js内でTARGETを参照し、APIサーバの向け先を切り替えたり等しています

以上、ご参考になれば幸いです。

2018年12月6日木曜日

チームでのUI/UX改善のための取り組み「マイオペ触る会」のご紹介

12月 06, 2018

こんにちは!

シタテルでエンジニアをしている建山です。

主に工場向けのマイオペというシステムの開発を行っています。

シタテルでは、チームがプロダクトなどでわかれており、今担当しているマイオペチームでは、
デザイナー、エンジニアが一緒になって開発をしています。

その中で、「マイオペ触る会」というのを隔週で行っています。

その取組の目的や方法をご紹介します。

マイオペ触る会って

普段開発しているプロダクトを、開発しているメンバー全員でユーザーの気持ちになって操作する時間を設けています。

目的としては、以下のような感じです。

・ユーザーの目線に改めてなって自分たちが作っているものを使うことで、気づいてなかったユーザーの課題やUI/UXの問題を洗い出す
・ユーザー目線を忘れないようにする意識付け

方法として、それぞれ行いたい時に非同期でやろうとしたのですが、開発の忙しさに追われてやらないことがあったため、みんなで同じ時間に集まって行うようにしています。

マイオペチームは拠点がバラバラなため、触る会の際は、zoomを繋ぎっぱなしにして話しながら進めています。

以下のような、googleスプレッドシートを活用し、どういうストーリーで操作したかの簡単な説明、画面ごとの問題や気になったことを各自書いていきます。

終わった後はイシュー化

終わった後はgithubでイシュー化して、議論検討したり、ユーザーインタビューしてみたり、開発計画に組み込んで進めたりしていきます。

やっていてよかったこと

目的にも書いていますが、開発しながら毎日自分たちのつくっているものには触れ合っていますが、あらためて、ユーザーの気持ちになっと触る時間を設けると、また初心に帰ることができるし、新鮮な気持ちでユーザーの課題が見えることもあります。
また、触る会をきっかけに チーム全体でもユーザー目線で活発な議論が発生していると感じていてやっていてよかったと思う点ばかりです。

まとめ

今回は、チームで取り組んでいる「触る会」をご紹介しました。チームでは他にも、いろいろな取組をしていますので、また追々紹介したいと思います。
チームや状況に合わせて、引き続きいいやり方を模索しながら改善していきたいと思います!
優秀なUI/UXデザイナーがチームにいますので、きっと記事を書いてくれると思います。笑

2018年12月5日水曜日

Krispで雑音に気を取られずにWeb会議

12月 05, 2018

こんにちは!
シタテル株式会社エンジニアのいしづかです。

シタテルは熊本と東京の2つ拠点があり、エンジニアもそれぞれいます。

また、週に2回はリモートワークも認められているため、物理的に人が集まらないことは、もはや日常です。

毎日の朝会や、様々なミーティングなどがオンラインビデオチャットで行われています。

シタテルではZoomというソフトを使っていますが、ここで悩まされるのが周囲の雑音です。

「発言時以外はマイクをミュートにする」というルールはあるものの、電話が鳴るとそっちの音が勝ってしまうこともしばしば。。。

「あ、ここのところなんですけd プルルルルルルル!!!」

ツライ。

そんな中、Krispというノイズキャンセリングのソフトがイイ!と聞いたので使ってみました。

結論から申し上げますと、なかなかよかったです。

Krisp

Krispは元TwilioのエンジニアだったDavitさんらが開発したアプリで、周囲のノイズ音を除去してくれます。ディープラーニングを用いて、ノイズ音をリアルタイムに除去しているそうです。

動画が出てましたが、パトカーのサイレンが鳴り響いていてもクリアに通話できているというスゴさ。

これはWeb会議の救世主ではなかろうか・・・!

残念ながら今現在ではmac版しか無いので、それ以外のOSユーザーはこのオドロキを体験頂けませんが、シタテルの開発陣は全員mac利用者なので問題ナシです。

Let's install !!

インストールはこちらのページより。
今ならDownload for freeなので無料で使えます。

pkgファイルが落ちてくるのでインストールします。

↑アイコンが表示されるので、SPEAK without noise をONにすれば準備完了です。

↑これはZoomの設定画面ですが、マイクとスピーカーにKrispがいるので、それを指定します。

これで準備完了です。

使ってみる

東京のエンジニアとZoomをつないだ状態で、以下のことをやってみました。

  • わざわざ談笑しているところにmacbook片手に近づく
  • 目の前で蛇口をひねって水をジャーっと出してみる
  • 会社の電話を鳴らしてもらう

結果は良好でした!!

すぐ周りは談笑しているのに「その部屋に他には誰もいないと思える」ほど静かだったそうです。

蛇口の水音、全然しない、と!!マジですか!!

もう手放せません。

まとめ

Web会議は環境がものすごく重要です。雑音が多くて声が聞き取れないとかなりのストレスになります。

Krisp入れたばっかりなのでまだまだ試さないといけないですが、みんな入れてこそ価値が発揮されるので、これから布教活動をしていこうと思います。

他にどのような工夫ができるか

快適なミーティングをやるために、飽くなき探究を続けていきます。

2018年12月4日火曜日

シタテルで使っているデザインツールのご紹介

12月 04, 2018

こんにちは!シタテル株式会社UI/UXデザイナーの田仲です。
今回はシタテルのデザイナーが使っている、デザインツールをご紹介します。

Sketch


デザインの作成はSketchをメインで使っています。去年まではPhotoshopがメインでしたが、下記で紹介するAbstract導入をきっかけに、Sketchをメインにしました。


Photoshopに比べると、とても軽いのでストレスなく使える点が気に入っています。アップデートが頻繁にあるため、その度に少し迷うこともありますが、アップデートでの変化を発見することが楽しみのひとつでもあります。
便利なプラグインもたくさん出てきているので、個人で試してチームにも導入していけたらと考えています。

Abstract


Sketchのバージョン管理ツールです。同じSketchファイルを複数のデザイナーが修正したり、サポートしあえるようにしています。


先日SketchのバージョンにAbstractが追いついていないことがあり、気がつかずSketchのバージョンを上げた時にSyncできない問題が起きてしまいました。(その時はSketchダウングレードして解決しました。)気軽にSketchのバージョンを上げられないという点は、微妙だなと感じています。
現在は複数人で作業しているプロジェクトは1つしかいない状態なので、もう少し共同で作業するプロジェクトを増やし、お互いの知見を増やすことにも活かしていけるような使い方をしていきたいなと考えています。

Adobe Creative Cloud


PhotoshopやIllustratorは、Sketchでは作業しにくい写真の加工、パネルや印刷物の作成を中心に使っています。


他にも、XDは気になる機能が次々に出ており、将来的にツールをAdobeに集約できると、データの互換性やコストなどの面でメリットがあると考えています。まだAbstractとSketchの方が便利だという結論から移行は考えていませんが、最新動向には日々注目しています。

Zeplin


デザイナーが作ったデザインをエンジニア確認できるツールです。開発内でのレビュー、最終的にエンジニアが実装するときに使っています。


コメントができる点はinVisionと変わりがないのですが、サイズやカラーコードがわかるだけでなく、アイコンのダウンロードなどもできるので、デザイナーとエンジニアのやりとりを減らすことができています。今後はよりエンジニアとどうやったらもっと便利になるかを話し合い、ガイドラインなどとしても活用できるようにしていけたらと考えています。

InVision


プロトタイピング・レビューに使用しています。


InVisionはコードなどの情報がないので、デザイナーとエンジニア以外の生産や営業などのメンバーがレビューに参加する際に使っています。シタテルでは開発以外の職種のメンバーともデザインの段階から頻繁に意見を交わしています。これからも部署問わず、よりよいプロダクトをつくり上げるためにコミュニケーションを活発にしていけたらと思います。

以上、シタテルで使っているデザインツールのご紹介でした。
シタテルでは、積極的に新しいデザインツールや機能を試し、今よりよりコミュニケーションしやすい&デザインしやすい環境となるものがあれば乗り換えていきたいと考えています

2018年12月3日月曜日

HDEさんのオフィスに遊びに行ってみた!

12月 03, 2018

こんにちは!
シタテル株式会社CTOの和泉です。

HDEさんのオフィスに遊びに行ってみた!

https://www.hde.co.jp/

images/image-0.png

HDEはクラウドセキュリティ、メールのSaaSサービスを展開している企業で、創業から20年以上の歴史があります。

先日参加した、SaaSカンファレンスで汾陽(かわみなみ)さんにお会いして、遊びに行かせてください!のノリで押しかけました。

丁寧すぎる道案内

渋谷駅からHDE HQ までの順路

自社ブログに道順を写真付きで乗っけるというアイディア!
(私が知らないだけで珍しくないのかも?)

images/image-1.png

GoogleMapで行こうかと悪魔がよぎりましたが、
ブログの説明と写真を頼りに渋谷駅ハチ公前から行ってみました。

これが、RPGみたいで楽しい。写真と道の形状や看板を見比べたりしながら、迷うことなくとたどり着けました!

受付からのドクターペッパー

受付のオープンスペースの写真撮り忘れた!

待ち合わせの時間に受付を訪れると、素敵な展望のオープンなスペースに汾陽さんがおられました。

そして真っ先に紹介されたのが、、、

ドクペ!ドクペ!ドクターペッパー!

images/image-2.png

の自動販売機!!

社員と 来訪者はなんとただで 飲める。驚愕!!

images/image-3.png

いただきました。

どうしてこうなった - 経緯

  • 社長がドクペが好きだった
  • 社員が6缶ぐらい入って冷やせる自動販売機を社長の誕生日にプレゼントした
  • 社員に好きに飲んでいいよと開放
  • 社員が増えてきて冷蔵庫にドクペがストックされるようになる
  • 社長が自腹で無料で提供し続ける限界が訪れる
  • 福利厚生の一部としてやろう
  • 自社ラベルのドクペ作りたかったけどライセンス許可(本国)おりなかった
  • 自販機ならいける?いける?いけた!

みたいな。

何この素敵な会社!

執務スペース

images/image-4.png

受付もそうでしたが、オープンで素敵なオフィス!

週に2回はランチが出たり、毎週のようにピザパーティーが開かれたりしているようです。

でも、集中部屋やスペースもきちんと完備されていて 個人の特性に配慮されてるなーと感じました。

images/image-5.png

真ん中に写っている着物の方が小椋社長。

そしてこの会社、外国人エンジニアがかなりいます。社内公用語も英語化しています。

グローバルインターンシッププログラムというものを作って、アジアや世界各地からのエンジニア候補を受け入れています。

その結果として優秀な方が多く採用できているとのことでした。したたか!

これは、シタテルも真似したい!真似するぞ!

20年を超える歴史のある企業で、汾陽さんも20年以上勤務されているとのことで、紆余曲折あった話などいろいろと教えていただきました。

まとめ

以上、ドクターペッパーのことしか伝わっていないのではないかという不安もありますが、本当にオープンで素敵な企業という印象を受けました。

社長がCTOでテクノロジー出身というのもあるのか、エンジニアが働きやすい環境が整備されていて。今後のシタテルのオフィスを考えていく上でもとても参考になりました。

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