2019|sitateru tech blog

sitateru tech blog

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

2019年12月19日木曜日

安全にkubectl applyするコマンドを作った

どうも朝野です。
最近 本番環境でやらかしちゃった人 Advent Calendar が話題ですね。 私も楽しく読ませてもらっています。

かくいう私も先日、kubernetes環境で作成中のアプリケーションを間違って別アプリケーション用のnamespaceにデプロイしてしまうという事故をやってしまいました😇

何をどうミスしたかというと、
  • 新しく作るnamespace以下にkubectl applyでアプリケーションをデプロイしようとした
  • 対象のyamlファイルは別プロジェクトのソースファイルをコピーして作ったものだった
  • yaml内に書かれているapply先namespaceが別プロジェクトのnamespaceのままだった
ということだったのです。
普段kubernetes環境へのapplyは原則CircleCI内で行っているのですが、今回は環境構築中なアプリケーションだったので手動で実行しようとしていたのでした。

上書きしてしまったほうもデモ用環境のようなもので利用者もいなかったため、大事には至らなかったのは不幸中の幸いでした。
実行前に確認しなかった私が悪いのですが、これは何とかして防げるようにしたいなあということで、applyする前にyaml内のnamespaceをチェックしておかしかったら止める、というシェルスクリプトを作りました。

作るにあたっては以下のような前提があります。
  • 環境の切り替え管理にkustomizeを使っている
  • 各環境のyamlは k8s/overlays/{環境名} ディレクトリに置いて、kubectl apply -k k8s/overlays/{環境名}でデプロイ
  • デプロイ先namespaceは {リポジトリ名}-{環境名} に統一する
というわけで作ってみたのがこちら。

#!/bin/sh
# check whether namespace is {repository}-{env} correctly or not

kubectl kustomize $1 > /dev/null 2> /dev/null
if [ $? != 0 ]; then
  /bin/echo 'usage: kubeapply [kustomize_dir]'
  exit 1
fi

PROCEED=0
REPOSITORY_NAME=$(git rev-parse --show-toplevel | rev | cut -f1 -d'/' | rev)
NAMESPACES=$(kubectl kustomize $1 | grep namespace | sed -E 's/namespace:(.*)/\1/' | tr -d ' ' | sort | uniq)
OVERLAY_DIR=$(echo $1/ | tr -s '/' | rev | cut -f2 -d'/' | rev)
FOUND=()

for NAMESPACE in $NAMESPACES
do
  if [ ${NAMESPACE} != ${REPOSITORY_NAME}-${OVERLAY_DIR} ]; then
    FOUND+=("${NAMESPACE}")
    PROCEED=0
  else
    PROCEED=1
  fi
done

if [ ${PROCEED} = 0 ]; then
  /bin/echo "expected namespace is '${REPOSITORY_NAME}-${OVERLAY_DIR}'"
  /bin/echo "but, namaspace '${FOUND[@]}' found in kustomize"
  /bin/echo -n "proceed apply? (yes/No): "
  read ANSWER
  if [ ${ANSWER} = 'yes' ]; then
    PROCEED=1
  fi
fi

if [ ${PROCEED} = 1 ]; then
  kubectl apply -k $@
else
  echo "stop applying"
fi

これを私はkubeapplyという名前にしてPATHが通っているところに置いてます。
$ kubeapply k8s/overlays/dev のように実行すればOKです。

やっていることは、
  1. 引数がkustomizeディレクトリとして正しいかチェック
  2. kustomize結果内のnamespaceを抽出
  3. 抽出されたnamespaceが{リポジトリ名}-{環境名(=引数のディレクトリ名)}と一致しているか確認
  4. すべて一致していれば kubectl apply -k {引数} を実行
  5. 一致していないものがあればプロンプトを表示、yesと回答したらapplyを実行
というフローとなります。

力技っぽさもある気がしますが、これで同じようなパターンの事故はそうそう再発しなくなるので、一歩前進です🤜🏻

kubernetesはyamlを食わせるだけでアプリケーションに必要なものが一式出来上がるのが便利だなあとずっと思っているのですが、その分気軽に環境を吹き飛ばすこともできてしまうので注意して作業しないといけないですね。

, ,

2019年10月11日金曜日

GASでG Suiteグループ追加をやってみた

どうも朝野です。

以前の記事で、作業を楽にするためにRPAを触ってみたという話を書いたのですが
その関係の取り組みの一つとして現在Google Apps Script(GAS)でアカウント管理操作を集約・省力化するという作業を進めています。

Google Apps Scriptはご存知の方も多いと思いますが、Googleの様々なサービスと連携させて動かすことができるスクリプト言語です。

シタテルではG Suiteをバリバリ使っているので、アカウント管理システムもGASを中心として作っていこうと考えています。
そこで、まずはGASで何か作ってみる第1号として、ユーザーをグループに追加するフォームを作ってみました。
グループ追加はG Suiteの管理画面からできるのですが若干作業として面倒だったりするんですね。

というわけでまずはGoogleフォームを作成。
「追加するユーザー」「追加先グループ」の入力欄を作ります。
それからオプション設定の「メールアドレスを収集する」をチェック。

つづいて右上メニューから「スクリプトエディタ」を選択するとGASのオンラインエディタが開きます。

ここでソースコードを編集します。
今回のコードはこのようなものです。
var privilegedRespondent = ['taro@sitateru.sample', 'jiro@sitateru.sample'];

// グループ追加,フォーム送信時に実行
function addUserToGroup(event) {
  console.log('addUserToGroup()');
  var message = "グループ追加フォームからの通知です\n\n";
  const respondent = event.response.getRespondentEmail();
  if(privilegedRespondent.indexOf(respondent) === -1) {
    console.log('Respondent ' + respondent + ' not allowed');
    message += 'あなた( ' + respondent + " )にはアカウントを作成する権限がありません\n";
    sendResult(respondent, message);
    return;
  }
  const response = parseResponse(event.response.getItemResponses());
  const userName = response['追加するユーザー'];
  const groups = response['追加先グループ'].split(',');
  for (var i = 0; i < groups.length; i++) {
    var groupName = groups[i].trim();
    try {
      var member = addGroupMember(userName, groupName);
      console.log(' added member', member);
      if(member == undefined) {
        message += "ユーザーをグループに追加できませんでした。入力内容を確認してください\n";
        message += ' ユーザー: ' + userName + "@sitateru.com\n";
        message += ' グループ: ' + groupName + "@sitateru.com\n\n";
      } else {
        message += "ユーザーをグループに追加しました\n";
        message += ' ユーザー: ' + userName + "@sitateru.com\n";
        message += ' グループ: ' + groupName + "@sitateru.com\n\n";
      }
    } catch(error) {
      console.log(' error', error);
      message += "ユーザーをグループに追加できませんでした。入力内容を確認してください\n";
      message += ' ユーザー: ' + userName + "@sitateru.com\n";
      message += ' グループ: ' + groupName + "@sitateru.com\n";
      message += ' エラー: ' + error.toString() + "\n\n"
    }
  }
  sendResult(respondent, message);
}

// Googleユーザーをグループに追加
function addGroupMember(userName, groupName) {
  if(userName && groupName) {
    const userEmail = userName + '@sitateru.com';
    const groupEmail = groupName + '@sitateru.com';
    console.log(' add ' + userEmail + ' to ' + groupEmail);
    const member = {
      email: userEmail,
      role: 'MEMBER'
    };
    return AdminDirectory.Members.insert(member, groupEmail);
  }
}

// メール送信
function sendResult(recipient, message) {
  MailApp.sendEmail({
    name: 'アカウント管理システム',
    to: recipient,
    subject: 'グループ追加フォーム実行結果',
    body: message
  });
  console.log('send result');
}

// フォームの入力内容をオブジェクトにして返す
function parseResponse(itemResponses) {
  var obj = {};
  for (var i = 0; i < itemResponses.length; i++) {
    var itemResponse = itemResponses[i];
    var question = itemResponse.getItem().getTitle();
    // var type = itemResponse.getItem().getType();
    var answer = itemResponse.getResponse();
    obj[question] = answer;
  }
  return obj;
}

あとはスクリプトにグループを操作する権限を与えるために、メニューの「Googleの拡張サービス」を選択して「Admin Directory API」をONにします。

コードは長いですが、GASでユーザーをグループに追加するには AdminDirectory.Members.insert() を実行すればOKです。公式のサンプルはこちら

今回のコードではグループ追加フォームとして実用上あったほうがいい機能もいろいろつけてみました。
event.response.getRespondentEmail() でフォーム送信者のメールアドレスを取得し、あらかじめ定義しておいた privilegedRespondent にいる人でなければ実行しない
・「追加先グループ」はカンマ区切りで複数指定できるように
・最後に実行結果をメール送信 MailApp.sendEmail()

と、こんなもので実際に利用を始めています。

こちらにリファレンスがありますが、GASはかなりの種類のGoogleのサービスを操れるのが強みです。↓のページで左メニューの「G Suite Services」や「Advanced Google Services」を開いてみよう!
https://developers.google.com/apps-script/reference

これからもいろいろなものを作って楽をしていこうと思います!

, ,

2019年9月9日月曜日

SQLをつかってJSONで格納されたデータを検索してみた

こんにちは!

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

今回は、データベース(MySQL)にJSON形式で格納されているデータをSQLで検索する方法を紹介します。
シタテルでは、AWS上のデータベースに蓄積されたデータをredashというツールを使い、SQLでデータ抽出したあと、データ分析できるようになっています。

やりかた

今回はMySQLの


たとえば、格納データが以下の2レコードあったとします。

{"maxCount":1000,"targets":{"ladies":false,"mens":true,"kids":false,"baby":false}}

{"maxCount":50,"targets":{"ladies":true,"mens":false,"kids":false,"baby":false}}


ここで、maxCountが50のデータを検索したいときは、
JSON_EXTRACTを使用し、以下のように抽出できます。(テーブル名:table_a、JSON格納カラム名:detail)

select * from table_a where JSON_EXTRACT(detail, '$.maxCount') = 50


targetsのladiesがtrueの検索をしたいときは、以下のようになります。

select * from tablename where JSON_EXTRACT(detail, '$.targets.ladies') = true


# 感想
このように、```$.

のあとに、キーの名前を指定してあげれば、かんたんに、jsonが格納してあるカラムの中身を検索することができます。(表示の記述方法も同じ)

公式のドキュメントはこの辺あたりのようです。
https://dev.mysql.com/doc/refman/5.7/en/json.html

他にもいくつか関数があるようなので、いろいろ試してみたら、いいSQL+JSONライフが送れるかも知れません!

簡単ではありましたが、ご紹介でした!

RPA触ってみた

どうもお久しぶりです。シタテルの朝野です。

突然ですがRPAってご存知でしょうか?私は数週間前にまともに知りました🙄
"Robotic Process Automation" の略で、PC上での作業をソフトウェアロボットが行うよう自動化することです。

現在私のいるDevOpsチームでは単純な作業を自動化して時間と手間を節約できないかという取り組みを少しずつ進めています。
そんな中でRPAというものの存在を知り、簡単に無償で導入できるものを試してみたので、サンプルと共に紹介します。

今回やってみたサンプルは、シタテルの「マイアトリエ」をブラウザで開いてログインするというものです。
(※サンプル内のメールアドレスとパスワードはダミーの値です)

Javascript for Automation

MacOS Yosemite以降で使用可能なスクリプト言語です。
サンプルコードはこのようになります。

const DELAY = 3;
const Chrome = Application("Google Chrome");

const window = Chrome.windows[0];
const tab = Chrome.Tab({
  url: 'https://atelier.sitateru.com/login'
});
window.tabs.push(tab);

delay(DELAY);

inputText(tab, 'email', 'sample@sitateru.sample');
inputText(tab, 'password', 'sample_password');
submit(tab);

function inputText(tab, elementId, value) {
  tab.execute({ javascript: "document.getElementById('" + elementId + "').value =`" + value + "`;" });
}

function submit(tab) {
  tab.execute({ javascript: "document.forms[0].submit();" });
}

ブラウザ上での入力やクリックはjavascriptをブラウザ内で走らせることで行っています。

実行はコマンドで $ osascript -l JavaScript sample.js と打つだけです。
JavaScriptを書いてコマンド実行なので、RPAわからんけどJavaScriptチョットワカル!という私のような者にはありがたいです。

エンジニアで使う分にはハードルが低いと思うのですが、ノンプログラマーやMac使わない人がいると導入しにくそうなのが難点でしょうか。

SikuliX

http://sikulix.com/

JavaがあればWindowsでもMacでもLinuxでも使えるOSSのRPAアプリケーションです。
IDEを立ち上げて自動化プログラムを作成・実行します。

サンプルコード(コードと言っていいのかわかりませんが)はこのようになります。

自動化したい操作をスクリプトににしてIDE上で書いていきますが、マウスポチポチでもある程度作れるのでノンプログラマーにもいくぶんとっつきやすそうです。

画像処理で有名なOpenCVを使って作られているらしく、操作対象の認識を「デスクトップ画面の中で対象の画像を抽出する」ことで実現しています。
(サンプルコードの click() のカッコ内にあるのは操作したい要素のスクリーンショットなのです)
画面上の文字を認識するOCR機能も一応あるのですが試してみたところ精度が怪しく実用には難しいなという印象でした。

機能面はやや物足りない感触でしたが、OSをまたいで使えることと導入の難易度が低そうなのはメリットですね。

UiPath

https://www.uipath.com/ja/

こちらもIDEで作成・実行するタイプで、GUIでフローチャートを組み立てたりブラウザ操作を自動で読み取って操作を記録したりと、基本シェアウェアだけあって操作感や機能は充実しています。(そのぶん見た目は複雑なので最初は少し戸惑うかも)

個人や中小企業はCommunity Editionという無料版が使えますが、ある程度以上の規模の企業で使う場合は有料になるようです。
https://www.uipath.com/ja/freetrial-or-community

サンプルコード(これもコードと言っていいのかわかりませんが)はこのようになります。

作業の流れがグラフィカルに表示されるので大掛かりなようにも見えますが、Webページ上の要素を選択して操作しているというのは同じですね。

公式コミュニティがあるほかハンズオンセミナーなども開かれているようで、費用面とWindowsのみという点が問題なければ有力な候補になりそうだと思います。


というわけで、今回は無料で使えるRPAを簡単にお試ししてみました。

こういったRPAは使いようで色々な作業の手間を省けそうな予感がしているので、できることを把握してうまく活用していきたいですね。

この記事がRPAに手を出してみる取っ掛かりになれば嬉しいです🤗

2019年6月3日月曜日

書評『進化的アーキテクチャ ―絶え間ない変化を支える』

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

社内の輪読会でオライリー『進化的アーキテクチャ ―絶え間ない変化を支える』を読みました。
琴線に触れた部分・キーワードを中心に簡単にご紹介したいと思います。

どういう本か・総評

システムの「変更しやすさ」(進化可能性)をいかにして追求し、また経年劣化させないか、というところに主眼を置いています。経年劣化から守る仕組みとして「適応度関数」という考え方を導入します。
具体的な手法や考え方は、継続的デリバリーやドメイン駆動設計などの知見を参照しており、これらの知見を俯瞰的にまとめた内容とも言えます。
個人的には、適応度関数を始めとした本書で導入される概念より、参照されている既存の概念(CI/CD、DDDやその他の設計プラクティス)の方が参考になりました。最近の設計や開発プラクティスを俯瞰する本として読むのもいいかもしれません。

適応度関数

アーキテクチャが満たしているべき要件(本書内では次元)を保護するもの。
循環的複雑度・ユニットテスト・監視・メトリクスといったシステム的・自動的に測定可能なものだけでなく、外部組織によるシステム監査などの人手による測定も包含する概念。
守るべき次元を定め、適応度関数として定義、測定し続けることで、外形的にアーキテクチャを保護する。保護することで、壊さずに変更し続けられることを担保する。
ちょっと理想論すぎるかなという印象もありますが、そういう捉え方もあるな、という感想です。

アーキテクチャの分類・マイクロサービス

本書では下記のような各アーキテクチャを対等に並べて、それぞれの歴史的背景や特性を、主に進化可能性の観点から整理しています。
  • モノリス(3層レイヤ化モノリス等を含む)
  • イベント駆動アーキテクチャ
  • SOA
  • マイクロサービス
  • サーバレス
いわゆるレガシー・エンタープライズ感の強いEDAやSOAから、マイクロサービスまで、しっかり比較の土台に載せている点は非常に良いと思いました。
まあ、最終的にマイクロサービス推しであり、そこに多くの紙幅が割かれてはいますが…
マイクロサービスのどういった特性が優れていて、他のアーキテクチャではその特性は何とトレードオフにされているのか、整理して理解することができただけでも本書を読んだ価値があったと思います。
モノリスを構築できないとき、なぜマイクロサービスがその答えだと思うのか。
マイクロサービスのメリットの多くは、ビジネスドメインを中心としてサービス単位を構成することに由来するものです。(この辺は、後述の逆コンウェイ戦略にも通じます)
従い、仮にマイクロサービスであっても、サービス粒度がビジネス的な境界線(境界づけられたコンテキスト)と乖離していてはメリットの多くは享受できません。
また逆に、モノリスアーキテクチャであっても、ビジネスドメインに基づいてしっかりとモノリス内でモジュール化(疎結合化)が成されていれば、マイクロサービスに近いメリットが得られるということでもあります。
そこのところをしっかり意識して、安易に流行りのアーキテクチャというだけで飛び付かないようにしたいですね。
マイクロサービスという名前にひっかからないようにしてほしい。各サービスは決して小さい必要はない。むしろ有効な境界づけられたコンテキストを捉えることが必要なのだ。

サービス境界分割の手法

何を軸にサービス境界を分割するべきかについては下記3つが挙げられています。
  • ビジネス機能グループ
    既存のビジネスコミュニケーション階層を反映する。コンウェイの法則に従う。
  • トランザクション境界
    トランザクションは最も分離しにくい要素である。
  • デプロイメント目標
    デプロイ頻度が異なる箇所を境界とする。
マイクロサービスであればサービス単位として、モノリスであればモノリス内のモジュール構成として、反映していけると良いですね。

コンウェイの法則/逆コンウェイ戦略

システムを設計するあらゆる組織は、必ずその組織のコミュニケーション構造に倣った構造を持つ設計を生み出す。
一般に人はコミュニケーションコストを避ける。また、チームを跨ぐコミュニケーションはコスト高になる。
ゆえに、チーム外の人間が所管する業務フローやコードにはできるだけ影響を及ぼさないように設計・実装しようとする(調整範囲をチーム内に閉じようとする)力学が働く。
そこから転じて、解決してほしい課題に合わせて、極力チーム内で調整が閉じるように、チーム組成をする、というのが「逆コンウェイ戦略」になります。
ただ一方で、チーム内の結束が維持できるのは精々10人程度が上限(一般的なスクラムでもそう言われますね)、とも述べられていますので、そこにはトレードオフがあります。
本書ではマイクロサービスアーキテクチャを進化可能性の面で優れていると評価していますが、マイクロサービスは「ドメインエンティティ」、「デプロイ単位」、「チーム単位」を一致させながら、かつチームを小さく保ちやすく、結果として逆コンウェイ戦略を満たしやすい、という点も強調されています。

結合よりも重複

結合よりも重複が望ましいという考え方は、本書の中で繰り返されています。
マイクロサービスは無共有アーキテクチャを形成する。その目標は、できるだけ結合を減らすことだ。一般的に、結合よりも重複の方が望ましい。
ツールがあまりにもコードの再利用を容易にしてしまったせいで、現代の開発者は、たやすく結合できてしまう環境の中で適切な結合を行うことに悪戦苦闘している。
例えば、 CatalogCheckout サービスと ShipToCustomer サービスの両方が Item という概念を持つ。両方のチームが同じ名前と同じプロパティを持つので、開発者はサービスをまたいでそれを再利用しようと試みる。それが時間や労力の節約につながると考えるからだ。
けれど、それは労力を増やす結果となる。なぜなら、コンポーネントを共有する全てのチームが変更を伝搬しなければならなくなるからだ。一方、コンポーネントを結合せずに各サービスにItem があり、必要な情報だけをCatalogCheckout から ShipToCustomer に渡す場合は、それらは独立して変更することが可能だ。
開発者が再利用可能なコードを作成する場合、開発者は最終的にコードを使用する無数の方法に対応するための機能を追加する必要がある。その将来の保証全ては、開発者がコードを単一の目的のために使用することをより難しくする
共通化をしたくなる(DRYにしたくなる)のはエンジニアの性ですが、共通化は結合でもあり、変更時の影響範囲を広げるものでもあります。不用意に共通化を行うと変更容易性を損ないます。
あえて重複させるという選択肢を忘れないようにしたいですね。

腐敗防止層を設ける

レイヤを間に挟むことで、将来的な変更可能性を残しておく、という手法です。例えばO/Rマッパであれば、DBMSの変更可能性を残しておく、という意味での腐敗防止層と捉えることができます。
最近、RailsアプリからViewを排除してAPIサーバ化し、フロントを別でSPA化する機会がありましたが、この構成におけるAPIコントローラも腐敗防止層だなー、と思いながら書いていました。
RailsのViewではどうしてもViewからModelへの直参照を止められず、結果として密結合になっていきますが、SPA<->API Controller<->Modelの分離が明確になったことで、かなりモデル層の変更がやりやすくなったと感じます。

デプロイに関するプラクティス

「継続的デプロイは開発側の都合であって、ユーザは頻繁な小規模改修を望んでいない」 という観点はもっともだと思いました。
折衷案として機能トグル(コードベースにはマージするがフラグ制御でユーザオープンしない)を用いる方法などが挙げられています。

個人的には、適応度関数はそこまで刺さらなかったですが、個別のトピックは面白い・参考になるものが多かったです。
ただ個別トピックはおそらく殆どに元文献があるので、本格的な設計本をたくさん読んでいる方には物足りないかもしれません。
取っ掛かりをつかみたい人にはおすすめできるかと思います。
よければ読んでみてください。

, , ,

2019年5月8日水曜日

プログラミング・ゲーム

こんにちは。
シタテルでバックエンドエンジニアをしている熊谷です。

今日は、プログラミングを学べるゲームやアプリの紹介をしたいと思います。

まず1つ目は、「Swift Playgrounds」です。

これはAppleが出しているiPad用のアプリで、キャラクターを動かすなどのプログラミングを、ゲーム感覚で学ぶことができます。

アプリ自体は前からあったのですが、最近やっと少し触ることができました。

画面イメージとしては、こんな感じです。


自分はまだ少ししか触れていませんが、初級からだんだん難しくなっていくレッスン形式になっていて、プログラミングに慣れていない人でも入りやすいのではないかと思います。

公式のレッスンの他にもサードパーティのレッスンもあり、センサーやロボット、ドローンを扱うようなものもあるようです。

普段のプログラミングや設計からの気分転換にもなって、良いと思います。

もう1〜2個紹介しようと思いましたが、長くなりそうなので、続きは次回にしたいと思います。

, , , , ,

2019年4月23日火曜日

同時に複数のマシーンで同じ操作をする with Tmux




sitateruのエンジニア北爪です。

複数のシェルで同じ操作をしたい時ってないですか?
  • ログを一箇所に集めてないけど、アプリケーションサーバーが複数台ある
  • 複数台同時にtopコマンドで負荷をまたはtailコマンドでログを調査したい

など調査段階でAnsible, Fabやcapistranoで操作するほどではなく、複数のサーバーを同じようにインタラクティブに操作したいという要求はあるように思います。

今回は複数のシェルに対して、分割された画面をみなが同時に操作、そのコマンドラインの結果を参照することが可能です。

tmuxを風にいうと、一つのpaneに行うキーボード操作をそのpaneが存在しているWindow内すべてのpaneキーボード操作を同期することができます

この機能は、tmuxの synchronize-panes 機能です。tmuxを使ってない人には導入する大きなメリットの一つになるのではないかとお思います。デフォルトです。

http://man.openbsd.org/OpenBSD-current/man1/tmux.1#synchronize-panes

使い方

1. tmuxを起動し、複数のpaneを作成がある状態で
2. tmuxにbindしてあるtmuxのprefixキーを入力した後で、 `:` を入力します
(余談私は ctrl-t にしています)

# act like GNU screen                                                                                                                                       
unbind C-b                                                                                                                                                  

set -g prefix C-t  

3. tmux用のコマンドが立ち上がるので、そこに、 set synchronize-panes onを入力します。
4. そうするとなんと!表示されているpane(分割した画面) に同じキーボード操作が入力されます
5. もう同期したくなくなったら、 set synchronize-panes off にて解除することが可能です。

以上です。



, ,

2019年4月16日火曜日

Vue.jsとScoped CSSとz-indexの話

ウェブサイトのイラスト

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

突然ですが、scoped cssいいですよね

scoped cssは、特にVue.jsにおいては、css設計のスタンダードと言っていいのではないかと思います。

BEM等の学習コストの高い手法を覚える必要がなく、「コンポーネント内でのみcssクラス名の一意性を確保すればいい *1」という、シンプルで管理しやすい設計指針を打ち立てることができ、実に理にかなった手法ですね。
プロジェクト全体でのcss命名規約の統制問題は依然としてありますが、コンポーネントベースでの設計手法自体がコンポーネントを書き捨てる運用と親和性があるため、そこまで大きな問題にはならないのかなと思います。

依然として残る考慮点

一方、scoped cssだけで、コンポーネント内にcss的な関心事を完全に閉じ込められるかというとそうではなく、当然ながら例外もあります。

最も代表的な例としては、cssの仕様上継承されてしまう属性ですね。
font-sizeなど、属性自体が子孫要素に継承されるものとして定義されていますので、セレクタレベルでscopedにしたところで、子孫要素への影響を避けることはできません。

まあこの辺は書いていても直感的に理解しやすいのでそこまで問題になりにくいのですが、個人的に危ないなと思うのは「z-index」「重ね合わせコンテキスト」です。

z-indexの管理

cssをざっくり理解したつもりになっているエンジニアが犯しがちな間違いの一つが、z-indexがグローバルだと思いこんでしまうことだと思います。

そう考えているエンジニアは、Vueプロジェクト全体で単純にコンポーネントごとのz-indexを管理することをを思いつきます *2。例えば、コンテキストメニューのコンポーネントはz-index1000番台、モーダルダイアログのコンポーネントはz-index500番台といった管理です。(モーダル上でコンテキストメニューを開くかもしれない。その場合はコンテキストメニューがモーダルの下に回り込んでほしくない、なんてことを考えているわけですね)

この方式は重ね合わせコンテキストがページ内で1つであるうちは上手く機能しますし、ルール自体はおそらく間違いでもないです。
ただ、重ね合わせコンテキストに関する考慮を漏らしていると、「z-indexで勝っているはずなのに、なぜか後ろに回り込んでしまう要素」が出てくる可能性があり、「なぜか動かない」という状況に見舞われます。実際には仕様理解が足りていないだけなのですが。

理解すべき仕様「重ね合わせコンテキスト」の詳細についてはMDNを読んでください。

https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context

ざっくり言えば、「z-indexは同じ重ね合わせコンテキストの中での比較にしか使われない」「重なる系の属性(potision: absoluteなど)が指定された要素は自身が重ね合わせコンテキストを形成する」ということです。

これもfont-sizeなどと同じで、親要素で指定された属性が(たとえscopedであろうとも)、子孫要素に影響を与えうる、ということですね。

デザインは文脈依存?

scoped cssとコンポーネントベースでの部品化は、確かに今までのcssの不便さや複雑さの大部分を解決しているように見えますが、z-indexの件を一つの例として考えると、「デザイン・見た目・UIは、どこまで部品化しても文脈依存から逃れられない」ということを示している気がします。まさに、重ね合わせ「コンテキスト」ですしね。

z-indexをpropsで渡せば…みたいな発想はもちろんあるのですが、結局その先にあるのはinput要素の再発明だったりするわけです。input要素をwrapしてたはずがinput要素を作っていた…みたいな。

どんなコンテキストでもデザイン破綻せず使うことのできるUI部品の実装は、相当な労力を要します。(まあ、実際にUI部品系のnpmパッケージの中を覗いてみると、普通にz-indexがハードコードされていて変更できなかったりすることが多く、みんなほどほどに手を抜いているんだと思いますが)

プロジェクト内でのデザイン統一・再利用を想定してコンポーネントを分割設計する際に、デザイン的な汎用性を求めるのはほどほどにしておくべきなのかな、と思った次第でした。

他コンポと疎結合に作るとか、ステートレスに作るとか、そちらに労力を割いたほうが実りが大きいんじゃないかな。UIの寿命は短いので、捨てやすいように疎結合に書くのが一番大事かな、と個人的には思います。



*1: slot内のスコープが親子共通だったり、コンポーネントroot要素に親からclassが注入できるといった例外はありますが

*2: 私です


, , ,

2019年4月8日月曜日

地方活性化のイベントに参加してきました

こんにちは、シタテルの藤本です。
イベントの参加のために久しぶりに東京に行きましたが、渋谷駅から出られなくなりそうになりました。
あらためてお上りさんには厳しい街であることを実感しました。



先日「スマホで見つける地方のしごと」というシンポジウムにパネラーとして
参加させていただきました。

内容を簡潔にすると「首都圏での離職と地方へUIJターンを希望する方と地方企業の
マッチングを進めて地方を活性化していこう」というイベントでした。

200名以上の方が参加され、各者の熱いプレゼンもありここから少しづつ
地方へ人材が広がっていってくれるのではないかと感じることができたイベントでした。

そんなイベントについて簡単にレポートしてみたいと思います

プレゼンター、パネラー

プレゼンター、パネラーとして以下の方々が参加されていました。

  • 求人サイト運営企業
  • 地方自治体
  • 商工会議所
  • 地方への移住者(Uターン)

イベントの大まかな流れ

以下の内容で2時間ほどのイベントでした。

  1. 厚生労働省からの本取組みにおける施策のついての説明
  2. 求人サイト運営企業からのプレゼン
  3. 地方自治体、商工会議所、移住者からのプレゼン
  4. パネルディスカッション

各者の主張について

各者の主張について個人的に印象的だったことについて書いていきます。

求人サイト運営企業

求人サイト運営企業の方からのプレゼンで印象に残ったのは以下の2点でした


  • 転職は孤独であり寄り添うことに注力したい
  • 転職は一般的になっている

転職は孤独であり寄り添うことに注力したい

転職にする時は非常に多くの「不安」と戦うことになると思いますが共有・共感者が少ないといった問題があり、そこに「賛成」と後押しすることで生き方を一緒に考えていこうといったものでした。

個人的には人材の流動性が高まることはいいことだと思っているので求人サイト側がこういったことを考えていただけるとありがたいなと思いました。

ちなみに弊社のBaseValueの一つに「よりそう」と言葉が定義されており、大きく以下の3つで構成されています。

  • リスペクトする
  • 声を聴く
  • 感動を与える
詳細が気になった方は以下を参照いただけると嬉しいです。

シタテルの目指す未来|シタテル株式会社

転職は一般的になっている

転職をポジティブに捉えているのは全世代で5割以上となっているということでした。
プレゼンターの方の言葉ですと「ポジティブ、ネガティブというより転職は一般化してきている」といわれていることがとても印象的でした。

地方自治体・商工会議所

地方自治体・商工会議所でも地方企業の魅力や人材育成・確保に向けた種々の取り組みを
されているのがすごく伝わってくるプレゼンでした。
印象的だったのは以下の3つです。

  • 人材育成・確保に向けた種々の取り組みをされている
  • コメリは新潟の上場企業だった
  • 地域資源を生かしたビジネスを展開

人材育成に向けた種々の取り組みをされている

小型ロケットの製造から打ち上げを通じた技術者の育成を行なっており、実際にロケットの打ち上げを成功されている地方がありました。

企業の枠を超えた技術者の連携や情報交換、人脈の育成などを推進されているとのことで地方を活性化していくために技術を磨いていくことは大切だなと感じました。

またUターンを促進するために東京からの無料のバスを出していたり、子供の時からものづくりフェアを実施して小学生に体験し興味を持ってもらい、ものづくりの楽しさを感じてさらに地元企業への魅力に気づいてもらうなどの取り組みをされていることも印象的でした。

コメリは新潟の上場企業だった

家の近くにコメリというホームセンターがあり、品揃えもよく安いのでよく利用させてもらっています。

その企業が新潟の三条市発祥の企業でしかも上場しているのは知りませんでした。
さらに三条市には上場しており本社をおいている企業が多くあり、人口1人あたりの上場企業数は東京、大阪についで3位とのことです。

地域資源を生かしたビジネスを展開

「もくロック」というブロックの木製玩具があるのですがこれの原材料となっているのが
地元の金属加工目メーカーが生み出しものということでした。
地元の資源を使い、地元の企業が新たなビジネスを生み出すということは素晴らしいなと思い印象的でした。

地方への移住者

広島への移住者の方でまちづくりのコンサルタントを行われている方でした。
非常に広島への熱い気持ちを持っている方で印象的だったのは以下の2点です。

  • 経営者との距離が近くなった
  • 「自分がいなくても回る」から「自分がいるから回る」

経営者との距離が近くなった

頻繁に経営者と面と向かって話す機会があり東京にいる時にはなかったことであるということでした。

「自分がいなくても回る」から「自分がいるから回る」

東京よりも人口が少なくなるので自分の役割や影響の割合が多くなるため、自分を必要とされる、頼られることが強く感じられるようになったということでした。

ぜひシタテルへ!

弊社シタテルですが本社は熊本にあります。
私も東京で13年程働いておりましたが2018年より熊本へUターンしてシタテルで働いております。

転職についての不安は色々とありましたが1つだけ挙げるとすると
前職ではほとんど管理業務を行なっており、実務でのプログラミングは5年以上していない状態でした。

そんな状態でどこまでやくに立てるか不安では、転職してまずは1年やってこれることができました。
まだまだ未熟で周りに迷惑をかけてはいますがシタテルのエンジニアの方はみんな教えたがりで優しく、わからないことがあり聞いたら、進んで色々と教えてくれるのでいつも助かっています。

私のように少しプログラムから離れてしまったがまた現場に戻りたいと考えている方もいるのではないかと思いますので、もし首都圏にて「また現場でプログラミングをしたい」、「いろんな技術に触れたい」という方、かつ「地方へ移住してみたい」という方がいらしたぜひシタテルへお話を聞きにいらしてください!

エンジニア│熊本or東京勤務 - シタテル株式会社のWeb エンジニア中途・契約・委託の求人 - Wantedly

少しだけ熊本の風景を

参加したイベント時に使用した風景写真です、熊本には自然が多く、いろんな風景を楽しむことができます。

近くの公園で桜満開になった時


家族でよく出かける天草の海

, ,

2019年3月12日火曜日

初めて見るとぎょっとするかもしれない JavaScript の構文 4 選

こんにちは! エンジニアの諏訪です。シタテルでは主にフロントエンドを担当しています。
今日はフロントエンド制御のエンジンともいえる JavaScript に関する話題です。
1990 年代半ば Web 時代の幕開け (※) とともに誕生した JavaScript ── ほんとうの名前は ECMAScript というのだそうですが ── 今も昔も変わらずクライアント側での処理機構としては唯一無二の立ち位置を保ち続けてきました。
よく考えてみるとこれってとてもすごいことですよね!
※ 余談ですが World Wide Web の誕生から今日 (2019.03.12) でちょうど 30 年だそうです。
さて、上で「今も昔も変わらず」と書きましたが JavaScript 自身はもちろん何度も進化を重ねてきています。
その中でも ES2015 (ES6) では多くの新しい構文が追加され、処理内容によっては従来よりも大幅にシンプルな書き方ができるようになりました。
新しい構文は便利なのですが、勉強を始めたての新人やレガシーなプログラマが初めて見るとびっくりするような記法も。
今日はそんなユニークな (ちょっとぎょっとする) 記法を4つピックアップしてみました。

ドット3つ ...

JavaScript のコードで、3つ並んだドットを見たことがありませんか。
let array = [ 1, 2, 3 ]

let result = [ 0, ...array ]
こんなコードを初めて見た時は「・・・あれ〜?」と脳が一瞬フリーズしてしまうこと必至です。ちなみにだじゃれです。
こちらは スプレッド構文 (spread syntax) と呼ばれるもので、 ES2015 で導入された新しい記法です。
配列やオブジェクトの展開操作を楽にしてくれます。
展開操作って?
大雑把に言うと Array や Object の外側の括弧を取り払って丸裸にします
例えばこういうことです。さっきの Array の例だと:
let array = [ 1, 2, 3 ]

let result = [ 0, ...array ]
console.log(result)
// => [0, 1, 2, 3]
Object の場合だと:
let obj1 = { potato: 2, carrot: 3, onion: 5, niku: 8 }

let obj2 = { water: 65535, spice: Infinity }

let result = { ...obj1, ...obj2 }
console.log(result)
// => { "potato": 2, "carrot": 3, "onion": 5, "niku": 8, "water": 65535, "spice": Infinity }
といった具合です。
応用編として、メソッドの引数に使うことだって可能です:
function createUser(firstName, lastName, bornYear, bornMonth, bornDay) {
    // doSomeProcess
}

let name = ['Colonel', 'Sanders']
let birthday = [1890, 9, 9]
let user = createUser(...name, ...birthday) // => createUser('Colonel', 'Sanders', 1890, 9, 9) を実行するのと同じ

ビックリマーク2つ !!

ビックリマークひとつならおなじみの否定演算子ですが
ビックリマークふたつの記法もあるのか!!とびっくりびっくりしたことがあります。
じつはこれ別に ES2015 の構文ではなく、単に古典的な否定演算子を2つ重ねただけのものです。(本稿のほかの項目と違って JavaScript の黎明期からあるものです。)
Boolean (真偽値) 以外の値を Boolean にキャストしたいときに使うと便利です。
たとえば次のようなコード
function hasName (user) {
  return ( user.name !== undefined && user.name !== null && user.name !== '' )
}
は次のように書けます:
function hasName (user) {
  return !!user.name
}
条件判断をシンプルに書きたいという方、ぜひこの書き方を取り入れてみてください。

(おまけ) いろんな値の truthy / falsy について

ちょっと復習しておきたかったのでまとめてみました。
  • truthy (Boolean にキャストすると true となる) の例
    • true
    • 0 以外の Number (ex: 1, -1, 3.14, -273.15, 6.02e+23, etc)
    • Infinity と -Infinity
    • 空でない文字列 (例: "a", "false")
    • [] (空の Array)
    • {} (空の Object)
    • 1 == true (緩やかな比較)
  • falsy (Boolean にキャストすると false となる) の例
    • false
    • 0 (Number)
    • "" (空の String)
    • null
    • undefined
    • NaN
    • 1 === true (厳密な比較)
注目すべきポイント (落とし穴) は
  • 空文字列は falsy だが、空の配列や空のオブジェクトは truthy
  • “false” や 配列 [false] などは truthy
あたりでしょうか。すぐ忘れそう…

関数のアロー記法 () => {}

先ほど array の話をしたので今度は arrow の話をしなければという義務感を抱いています (嘘です)。
JavaScript といえば無名関数 (ラムダ式) ですが、ES2016 以前は無名関数を記述するには function という予約語を使うのが常でした。
setTimeout(function() {
    console.log('Hello!')
})
ES2015 からはアロー構文 () => {} を使って書くこともできるようになりました。
setTimeout(() => {
    console.log('Hello!')
})
function と () => {} どちらを使ってもよいのですが、1つだけ挙動の異なる点があるので注意が必要です。
何が異なるのかというと、メソッド内での this の扱いです; function () {} の場合はメソッドの呼び出し元のオブジェクトを指しますが、 () => {} の場合はメソッドが記述された文脈上のオブジェクトを指します。つまり、 function () {} の場合は this が動的に変わるのに対して () => {} の場合は this が固定されます。
ローカル変数レベルで後者のような挙動をするラムダ関数を closure というのでしたっけ...λ

テンプレートリテラル `${}`

プログラムを書いていて、動的に文字列を生成しないといけない場面でよくお世話になるのが printf のようなメソッド。メソッド名は言語によって様々ですが、世の中の大半の言語で提供されている構文です。
ところが JavaScript にはこれまで対応するものがなかったのです。そんな中 ES2015 になってようやく登場したのが「テンプレートリテラル」。
なんと文字列全体をバックチック ` で囲います。 (バックチックを約物に用いる言語ってあまりなかった気が…)
そしてプレースホルダは ${} と書きます。
let username = `山田太郎`
let message = `ようこそ、${username}さん!`
console.log(message) // => "ようこそ、山田太郎さん!"
そしてすごいのが、Ruby のヒアドキュメントよろしく改行をそのまま書くことができます
let body = `${client.name}様

いつも大変お世話になっております。
${company.name}の${staff.name}です。

${message.content}`
console.log(body) // => "山田太郎様\n\nいつも大変お世話になっております。\n〇〇株式会社の...
一点だけ残念なのが、ソース全体をインデントしてしまうと、そのインデント分の空白文字も無視されずに含められてしまうこと。そのためテンプレートリテラルで記載した部分はインデントすることができず、ソースの可読性が若干下がります。
Ruby のヒアドキュメントが上手にインデントを除去してくれるのに比べると少し物足りなく感じます。

以上 ES2015 のユニークな構文を一部ご紹介しました。
新しい記法は少し古いブラウザではほとんどサポートされていないものもあって、動かなかったらどうしよう…という不安から新しい書き方で書くことを躊躇してしまいがちですが、その不安を取り払ってくれる polyfill もちゃんとあります。
正しく使えば、より書きやすく読みやすく無理のない安全なコードに近づける新しい構文。しっかりと学び、追いつき、使いこなせるようになりたいものです。
, , ,