RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

新卒1年目が新卒0年目に贈る、5+1冊の「エンジニア虎の巻」

f:id:tech-rakus:20180202113705j:plain

はじめに

こんにちは!新卒1年目のrs_tukkiです。

エンジニアとして入社してからもうすぐ1年。最初は右も左も分からなかった私ですが、先輩や上司の方々にしごかれ指導していただきながら、なんとか機能開発に携われるようになってきました。

さて、今回は技術そのものとは少し違うお話です。

現在2月。
あと2か月もすれば新しく新卒として入社される、言わば「新卒0年目」の皆さんは、期待と同時に、どのように仕事を進めていけばいいのか、どうすれば説明会で会った先輩たちのようになれるか...という不安もお持ちだと思います。
残念ながらそういったコツは実際の業務の中で身に着けていくしかありませんが、そのヒントなら、古今東西のあらゆる書籍に転がっています。

そこで今回は、新卒1年目の私が、新卒0年目のエンジニアにオススメしたい、5冊+1冊の必読書...いえ、「虎の巻」をご紹介します!

まずは「仕事のやり方」から

エンジニア、というと皆さんの中には、ただずっとパソコンに向かってコードを書いていればいい、と思っている方もいるかと思います。
ですが、実際の仕事は何も自分一人だけで進めていくものではありません。 例えば自分たちの作ったものを資料にまとめて営業の人たちに説明したり、発生した問題に対して皆で対応策や回避策を考えたり...と、パソコンを使わない作業も意外と多いのです。

とはいえそんなときに、「自分はプログラミングしかできないから」と投げ出すわけにはいきません。むしろ、直接業務に関係するためにその時になってからでも学ぶことができるプログラミングよりも、
文書の書き方問題に直面したときの考え方
そして、そういった作業をこなしつつ本来の業務を遅らせないための取り組み方
こそが「今、このタイミングで知っておくべきこと」なのだと思います。

ここでは、そういったことを知れる本を3冊ご紹介します。

<文章嫌いではすまされない! > エンジニアのための伝わる書き方講座

<文章嫌いではすまされない! > エンジニアのための伝わる書き方講座

<文章嫌いではすまされない! > エンジニアのための伝わる書き方講座

  • 作者:開米 瑞浩
  • 発売日: 2014/06/28
  • メディア: 単行本(ソフトカバー)

大学時代に真面目なメールを沢山書き、重要なプレゼンを常にやっていた、という人はあまりいないのではないでしょうか。
ですが就職した後は、たとえエンジニアであっても説明のためにメールを送ったり資料を書いたりと、とにかくプログラム以外にも「書く」作業がかなり増えます。
そこで一番重要なのは、「必要な情報だけを、正確に、分かりやすく相手に伝えること」です。
この本では「誰に」「何のために」文章を書くのか、ということから始めて、図や表の作り方など、情報を的確にまとめて伝えるための方法が書かれています。よく、「コミュニケーションは仕事で一番大事なスキルだ」という人がいますが、その意見の是非はともかく、「誤解なく伝えられること」は必ず役に立つはずなのです。

問題解決力を鍛えるトレーニングブック

発売は2002年と結構古い本です。が、今でも十分参考になる内容だと個人的には考えています。
安易に問題と言っても様々ですが、この本ではまず「問題」の定義とは何か?ということから考え始め、いきなり解決に取り組むのではなく、いったい何が原因なのか、どうすれば解決したと言えるのか、といったことを体系立てて「基本手順」としてまとめています。
実際に起こった問題に対する解決の事例も追えるので、エンジニアとしての問題にも十分応用が利く本です。

脳のパフォーマンスを最大まで引き出す 神・時間術

脳のパフォーマンスを最大まで引き出す 神・時間術

脳のパフォーマンスを最大まで引き出す 神・時間術

  • 作者:樺沢 紫苑
  • 発売日: 2017/04/13
  • メディア: 単行本(ソフトカバー)

こちらは逆に昨年4月に発売されたばかりで、私も今まさに読んでいる途中の本です。
一般的に仕事は1日8時間。特にエンジニアだと残業が多く、9時間も10時間も働くことがあるかもしれません。毎日そんなに長い間集中して取り組めというのも無理な話ですし、実際私も集中力が途切れ途切れになって注意されてしまうことがありました。
この本では、いわゆる「生産性」を叩き出す方法として、上手く集中力を維持しながら、効率よく時間を使う方法について解説しています。
「雑念が出たら紙に書け」というのはちょっと意外でした。

評価されるエンジニアとは?

...何やら偉そうなことを 言っているように思うかもしれません。私もそう思います

プログラミングの世界には「まずは動くようになることが大事」という格言があるらしいですが、当然ただ動くようになっただけでは、ただごちゃごちゃしたものが出来上がっているだけです。
自分のコードが先輩や上司に評価されるためには、まずそのコードが読みやすいものでなければいけません。それは技術力とはまた別のところにありますが、例えるなら部屋の整理整頓をきちんとできるか、といったところでしょうか。

このように、これからエンジニアとして働く皆さんが、「デキる」エンジニアになるためには、ただ技術があるだけでなく、それをどう実際の業務で生かすか、というところまで求められているのだと思います。

コードの綺麗な書き方開発への心構えなど...基本的なプログラミングを覚えた人たちが、そういったノウハウを知ることが出来る本*1を2冊ご紹介します。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

エンジニアとして就職すると、当然ながら仕事としてのプログラミング技術が必要で、コードを書くことも多いと思います。
大学では、自分がコードを書いても、それを読むのはせいぜい自分と教授くらいだったかもしれません。しかし、仕事では一つのコードを多くの仕事仲間で編集するので、当然分かりやすさは大事になってきます。
分かりやすい変数名の書き方、分かりやすいコメントの書き方、分かりやすいロジックの組み立て方など...ベテランのプログラマでも意外とできていない「分かりやすさ」のための技術がまとまっている本です。

プログラマが知るべき97のこと

プログラマが知るべき97のこと

プログラマが知るべき97のこと

  • 発売日: 2010/12/18
  • メディア: 単行本(ソフトカバー)

既に沢山のブログで「初心者向けの必読書」として紹介されているので、もう読んだことのある人もいるのではないでしょうか。
実に73人ものプログラマによる97本のエッセイ(日本語版には更に日本人8人による10本)がまとめられています。「浮動小数点数は実数ではない」のような数学に近い話から、「関数の『サイズ』を小さくする」などの実際のプログラミングにも役立つ内容、更には「ハードワークは報われない」などといった働き方に関する話まで。
全てを実践する、というのは無理がある(というか矛盾する話もいくつかある)のですが、「デキる」人たちはこういうことを意識しているんだな、ということを覚えるだけでもいいと思います。

【おまけ】エンジニアじゃなくても読んでほしい「新卒虎の巻」

最後に、オススメの本をもう一冊だけご紹介します。
ですがこちらは、営業職向けの内容も多く含まれているため「エンジニアの虎の巻」とは言えません。
それでも今、このタイミングでなるべく多くの「新卒0年目」に読んでいただきたい、「新卒の虎の巻」です!

入社1年目の教科書

入社1年目の教科書

入社1年目の教科書

  • 作者:岩瀬 大輔
  • 発売日: 2011/05/20
  • メディア: 単行本(ソフトカバー)

オススメ、という割に、この本に書いてあること自体は「仕事は絶対やりきろう」とか「早く出すことを意識しよう」とか「仕事に対する見方を変えよう」とか、割と当たり前に思われることです。
ですが、そんな当たり前が全部で50個。入社したばかりのやる気に満ちている時であればともかく、しばらくすると数多くの「当たり前」に取り組むことを忘れてしまうかもしれません。
そんな時に、この本に乗っていたことを思い出して自分のやり方を見つめ直す。そうしてまた入社したときのやる気を復活させる。そんな読み方ができる本だと個人的に考えています。だからこそ、入社する前の今に読んでおいてほしいのです。 まあ、50個の中には「宴会芸を死ぬ気でやれ」とか「同期とは付き合うな」とか個人的にどうだろうと思うものもあるのですが...

おわりに

私が入社してから今までに読んだ本の中で、「これは絶対に役に立つ!」と感じた5+1冊をご紹介しました。
もちろん、読んでないといけない、というわけではありません。これらの本を全部読んで、書いてある内容を全部実践しようなんて無理だと思います。

ですが、「読んだこと」自体は絶対に無駄になることはないと思うのです。
面白そうだと思った本を一冊流し読みしてみて、これなら自分にもできそうだと思った一つを、機会があったらチャレンジしてみようと思うだけでもいいと思います。ていうか私もそんな感じです。

私の紹介した本が、新卒0年目の皆さんの心の片隅にでも残ってくれれば幸いです。

*1:逆に、プログラミング未経験の方には難しい内容だと思います...

インストール不要!スマホで自作アプリを動かす方法【疑似Webアプリ】

こんにちは。エンジニアのmickey-STRANGEです。
今回はめんどくさがりによるめんどくさがりのためのスマホアプリ開発についてお話したいと思います。
とはいえ、このブログの内容ではスマホアプリは作りません。
タイトル詐欺ぎりぎりですが、嘘はついていませんので、そういう認識でお楽しみいただけますと幸いです。

構成

先に書きました通り筆者は非常にめんどくさがりです。開発環境・実行環境の整備といったところに手間をかけたくありません。
今回使うのはGitHubのみです。
GitHubのみで疑似Webアプリを実現します。
ではGitHubのみで疑似Webアプリをどう作るか考えていきましょう。
Webアプリというと大雑把に考えて3つ、大事な要素があります。

  1. サーバそのもの(apachetomcatなど)
  2. プログラム実行環境(phpJavaなど)
  3. 記憶領域(postgres、Oracleなど)

これらに対応させて考えていきます。
まずサーバにはGitHub Pagesを使用します。 GitHub PagesはGitHubのサービスであり、静的ページをホスティングしてくれるもので、簡単なWebサイトを公開することが出来ます。

そして公開した静的Webサイト(HTML)にjavascriptを書いておきます。つまり実行環境はブラウザです。

最後に記憶領域ですが、アクセスした各端末に必要な情報を記憶させます。
javascriptによる永続の記憶領域として、Web StorageIndexedDBというものがあります。

これらを使用してスマホでも実行可能なプログラムを作成することが可能です。では各要素について見ていきましょう。

GitHub Pages

まずサーバの役割を担うGitHub Pagesです。
上記の通り静的ページのホスティングサービスですが、すごく簡単に言うと「GitHubにHTMLファイルをpushすればWebサイトとして公開してくれる」という認識でいいと思います。

ここからGitHub Pagesの使いかたをご説明いたします。といっても大層なものではなく、2ステップで完了です。

1.まずHTMLファイルをpushします

ここは詳しい説明は省きます。方法は問いませんので自分のリポジトリにHTMLファイルをpushしてください。
めんどくさがりの意見としてましてはGitHub Desktopを今回初めて使いましたが、何も考えなくていい感じがとてもよかったです。GitHub Desktop | Simple collaboration from your desktop

例としてHello,World!的なHTMLを作成しました。

2.pushしたリポジトリを公開する設定をします

リポジトリ画面の上部にある「Settings」リンクをクリックし…

f:id:mickey-STRANGE:20180129194437p:plain

GitHub Pagesカテゴリ内のSource設定を「master branch」に変更して隣の「Save」をクリック。

f:id:mickey-STRANGE:20180129194440p:plain

以上です!
これでGitHub Pagesの設定が完了しました。pushしたファイルにアクセスしてみましょう。

f:id:mickey-STRANGE:20180129202306p:plain

表示されているリンクをクリックすると…

https://mickeystrange.github.io/tools/

Hello,GitHubPages!が表示されればOKです。

発行されるURLはhttps://[ユーザ名].github.io/[リポジトリ名]/ファイル名なので、https://mickeystrange.github.io/tools/index.htmlにアクセスしても同じものが表示されます。
(おそらくファイル名を省略した場合にindex.htmlを自動的に表示してくれているのではないかと。)

HTMLを公開する設定が出来ましたので、あとはjavascriptを作っていくだけです。

これでGitHub Pagesの説明は終わりです。実行環境のブラウザは言わずもがなということで記憶領域の1つ目、Web Storageの説明に移ります。

Web Storage

Web Storageはブラウザの記憶領域にKey-Value Storeで値を保持できる仕組みです。
Web Storage API - Web API | MDN

使いかたはとても簡単で、準備も不要です。

これでもう値の格納と取り出しが出来ています。それぞれ1行だけです。
もう見たままですが一応ご説明いたしますと、
3行目の

localStorage.setItem('key', value);

の部分で'key'というキーで変数valueの値を格納しています。
また、 5行目の

var item = localStorage.getItem('key');

の部分で'key'というキーで格納されている値を変数itemに取り出しています。

localStorageというのはWeb Storageの種類の1つで、記憶領域が保持される期間が永続のものです。
もう1つの種類としてsessionStorageというものがありますが、こちらはブラウザを閉じるまでの間のみ有効なものになります。

このWeb Storageを使用して、ページへのアクセス数をカウントする簡単なプログラムを作成いたしました。

LocalStorage 静的リソースだけでアプリケーションを作るための記憶領域テスト

ページの再表示やブラウザの再起動などをして遊んでいただきますと、記憶領域をたしかに持っていることが確認出来ると思います。
開発者ツールなどで見ることが出来ますが、特別隠すようなものでもありませんのでコードは以下の通りです。
tools/storage_test.html at master · mickeySTRANGE/tools · GitHub

簡単な値の保持だけであればWeb Storageが十分な機能を持っていることをご説明いたしましたが、続いてもっと強力な記憶領域といたしましてIndexedDBの説明に移ります。

IndexedDB

IndexedDBはブラウザの記憶領域にオブジェクトを保持する仕組みです。
Web Storageと比べて最大容量が多い、KVSではなくJSONを保存できる、検索に強い、などの特徴がある、NoSQLデータベースです。

これからIndexedDBの使用方法についての説明をいたしますが、Web Storageの後で見るととても複雑です。
インストールや外部ライブラリは不要(ブラウザの機能なので当たり前)ですが、手順が多いです。

1.DBに接続する

IndexedDBは基本的にリクエストと、そのリクエストの結果によって実行される部分を書いていきます。
今回の例ですと、3行目の

var openRequest = indexedDB.open("sampleDatabase", version);

がDB接続のリクエストで、4-10行目の

// 接続に成功すれば各処理を実行
openRequest.onsuccess = function(event) { 
  db = event.target.result; 
  db.onerror = function(event) { 
    alert("Database error: " + event.target.errorCode); 
  };
}; 

が接続成功時に実行する処理、といった具合です。サンプルは関数の外の変数にDBのインスタンスを保存しています。
11-15行目の

// DB定義を更新
openRequest.onupgradeneeded = function(event) {
  db = event.target.result;
  db.createObjectStore('sampleObject', {keyPath: 'key'});
};

は、保存するオブジェクトの定義を行っています。
アクセスする時のオブジェクトストアの名前を第一引数に、オブジェクトの定義を第二引数に持っています。
サンプルは'sampleObject'という名前のオブジェクトストアで'key'をキーに定義しています。

このonupgradeneededというものは、DBのバージョンが上がった時に実行されるものです。
3行目のopen()の第二引数にversionがありますが、これはIndexedDBのバージョンではなく、自分が作るDBのバージョンです。
バージョン1としてこのサンプルの通りオブジェクトストアを定義し、変更が必要になった時にバージョン2として接続し、onupgradeneededの中で新しくオブジェクトストアを追加したり、オブジェクトストア定義変更を行ったりすることが出来ます。

2.オブジェクトストアにアクセスする

DBに接続出来れば次はデータへのアクセスです。データの格納と取得を見てみましょう。

setValueではDB接続のサンプルで作ったsampleObjectに1つのオブジェクトを追加しています。
keyは'sample'value'hoge'という値を持っています。keyは必須かつ重複不可の制約がありますが、他の要素は何を持っていても構いません、

getValueではsampleObjectから'sample'というkeyPath(サンプルでは'key')を持つ値を検索しています。

IndexedDBの記憶領域は全て永続なので、この構文のみでもアプリを作成することが可能です。
これらの構文を使用し、またページへのアクセス数をカウントする簡単なプログラムを作成いたしました。

Indexed Database 静的リソースだけでアプリケーションを作るための記憶領域テスト

tools/indexed_database_test.html at master · mickeySTRANGE/tools · GitHub

ページのリロード等をしていただきますとカウントが正常に出来ていることが分かります。

3.IndexedDBの注意点

簡単なサンプルをお見せいたしましたが、indexed_database_test.htmlのコードを見ていただきますと、Web Storageの時とは大きく構成が違うことが分かっていただけるかと思います。

そうです、「取得する関数」と「格納する関数」で分けて処理をきれいに書くことが出来なかったのです。

理由がありまして、リクエストを書き、その成功時失敗時に実行する処理を書くと説明いたしました。
実はこれが曲者で、リクエストが完了したかどうかをイベント外から見ることが出来ないのです。

「取得する→1増やす→格納する」という流れを切り分けることが出来ず、
取得するリクエストのonsuccessイベントで「1増やして格納する」という処理の流れになります。
更に言うと取得するリクエストさえ接続するリクエストのonsuccessイベント内で実行しています。

IndexedDBはWeb Storageよりも強力なデータベースが使用できる代わりに、処理の流れを作るところで非常にややこしくなってしまう、というデメリットがあると言えます。

まとめ

自分の書いたコードを簡単にスマホで動かす方法をご紹介いたしました。いかがでしたでしょうか。

一番手軽なのはやはりWeb Storageを使用する方法です。 多分これが一番早いと思います。

これにJQueryやBootstrapなどの部品を組み合わせることで十分なスマホアプリのようなものが作れると思います。
今回の記事とは別に業務を効率化するJSなども趣味で作っていたりしたため、ここ最近でJS面で成長したことを感じています。
自分で使えるツールをもっとpushしてJSライフを楽しんでいきたいです。

OWASP ZAPについて調べてみた

はじめに

開発エンジニアのamdaba_sk(ペンネーム未定)です。前回は「ソフトウェアテストについて簡単にまとめてみた」という記事を書きましたが、その流れで今回はセキュリティテストツール「OWASP ZAP」について少し調べてみました。

※以下は個人的にネットで調べてみた情報をまとめたものであり、実際に開発過程で運用するなどしたものではありません。また日本語サイトを中心に調べているため、機能等の情報は古い可能性があります。

セキュリティテスト

昨今は個人情報の漏えいや顧客情報流出などのニュースをよく耳にします。怖いですね…。

ラクスでも個人情報の漏えいや顧客情報流出などは深刻な問題としてとらえていまして1、その発生を未然に防ぐために開発者には年1度セキュリティ学習と修了テストが課されていたりしています。

またリリース前にはとあるWebアプリケーション検査ツールを使った脆弱性検査を実施し、検出された脆弱性を修正するようにしています。

しかしながら今のツールによる検査は、はたから見ているだけの感想ですが

  • 設定がややこしそう
  • リリース前に修正箇所全体に対して行うため、たくさんあったときに修正が大変
  • ツールの稼働サーバーがチーム間で共有なため、柔軟には使用できない

といった不便さを抱えているように感じます。

OWASP ZAP

そこで上記の不便さに対し、

  • もっと簡単な設定で自動チェックをしてくれるツールはないか
  • もっとこまめに、例えば単体テストの一環としてチェックできないか
  • もっと柔軟に、チームごとに専用の環境を用意できないか

といった要望を満たすツールとして私が目を付けたのが「OWASP ZAP」でした。

OWASPとは

OWASP ZAPではじめる2016年のウェブアプリケーションセキュリティでは

ウェブアプリケーションを作成する開発者や,ウェブアプリケーションに関わる意思決定を行う方々に対し,セキュリティに関する十分な情報を行き渡らせることを目的とし活動をしているのがOWASPです。OWASPは「The Open Web Application Security Project」の略称で,グローバルにチャプター(支部)を展開するオープンコミュニティです。

と説明されています。Webアプリケーションセキュリティの啓蒙団体といったところでしょうか。

OWASP ZAPの概要

OWASPにはWebアプリケーションセキュリティの啓蒙に関して大小さまざまなプロジェクトを展開しています。OWASP Zed Attack Proxy(OWASP ZAP⁠)はその中でも最重要として位置づけられたプロジェクトの一つであり、その成果物としてのツールです。

IPAテクニカルウォッチ「ウェブサイトにおける脆弱性検査手法の紹介」でも取り上げられて、使いやすく、検知制度が高く、効率性が非常に高い初級者向けのツールという評価を受けています。またありがたいことに無料で使えるオープンソースウェブアプリケーション脆弱性診断ツールで、Github上ソースコードが公開されています。

用途としては「開発者が開発時に簡易的なウェブアプリケーション脆弱性診断を実施する」ことを特に意識して作られており、セキュリティの専任ではない開発者でも簡単に利用できるようになっています。

なお、OWASP ZAPが検査可能な主な脆弱性は以下のとおりです。

OWASP ZAPの機能

表1はOWASP ZAPの主要な機能をまとめたものです。

表1 OWASP ZAPの主要機能(OWASP ZAPではじめる2016年のウェブアプリケーションセキュリティより引用)

項番 機能名 概要
1 スパイダー機能 指定のURLを起点とし,オートクローリング(自動で脆弱性診断対象のリンクを辿り,存在するURLを洗い出す。)を行います。
2 動的スキャン機能 クローリングして明らかになった任意のURLに対し,ツールが準備している脆弱性診断用の検査文字列を用いて自動で脆弱性診断を行います。
3 ローカルプロキシ機能 ローカルプロキシとしてOWASP ZAPを動作させ,手動で脆弱性診断を行います。やや玄人向けです。
4 ディレクトリ探査機能 指定のURL配下に不要なディレクトリが存在しないかを確認します。
5 アラート機能 脆弱性診断結果に関する簡易の報告書を作成します。
6 ZAP Script機能 OWASP ZAPの機能を拡張し,OWASP ZAPをより自分好みにカスタマイズできます。JavascriptPythonRubyなどのさまざまな言語に対応しています。情報源としては,OWASP ZAP ScriptsというGroupがあります。

OWASP ZAPはJavaで作られており、クロスプラットフォームで動作します。またwindows 環境では特にありがたいのですが、利用しやすいようにグラフィカルインターフェースを備えています。

詳しい手順はその他の記事や公式のドキュメントを参照していただきたいと思いますが、項番1(スパイダー)の実行後に項番2(動的スキャン)を実行する自動脆弱性診断を行うだけなら

  1. 検査対象URLの入力
  2. 実行ボタンクリック

という非常に簡潔な手順で実施できます。

一方でRESTfulなAPIによる操作もできるようで、curlなどでコマンドラインからリクエストを投げれば上の手順をスクリプト化できます。さらにデーモンモードで起動しておけば、JenkinsなどのCIツールと連携して最初から最後まで自動で実施することもできます(e.g.JenkinsとOWASP ZAPで自動診断 - Qiita)。

またJenkinsと連携した上の例ではOWASP ZAPのAPIスクリプトで直接呼び出していますが、 公式のZAP Jenkins pluginというものもあるようで、そちらを利用するという手もあるでしょう。

その他、手動の単体テスト実施時にローカルプロキシ機能を使用してチェックするとか、いろいろ設定をいじって使ってみるとか、使い方の幅も広そうです。

おわりに

以上簡単ながら「OWASP ZAP」について調べたことをまとめました。

設定も簡単、精度もよく、自動化も可能と、とっても便利そうです。ただあくまで簡易チェックツールということで、

  • 開発中は「OWASP ZAP」で簡易検査
  • リリース前は精密チェックができる別のツールで本検査

という風な使い分けをしたらいいんじゃないかと今の段階では思っています。

参考

  1. OWASP Zed Attack Proxy Project
  2. IPAテクニカルウォッチ「ウェブサイトにおける脆弱性検査手法の紹介」
  3. OWASP ZAPではじめる2016年のウェブアプリケーションセキュリティ
  4. JenkinsとOWASP ZAPで自動診断 - Qiita
  5. zap plugin - Jenkins - Jenkin Wiki

◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com


  1. 本ブログで「意図しない処理が実行されるCSRFとは?概要と対策」や「要注意!新人エンジニアが発生させた2大脆弱性」といった記事があるように、個人レベルでも関心の高い話題です。

戦闘力53万風のマイクロサービス

f:id:Y-Kanoh:20180111033033j:plain

こんにちは!エンジニアのY-Kanohです。

弊社のエンジニアは、業務終了前にその日の稼働報告を社内システムに入力することになっています。 しかしながら、この入力を忘れるメンバー(主に私)が多く、チームのリーダーに指摘されてから、数日前の仕事状況を思い出して記入することが度々ありました...。(すみません。)

そこで、チームで導入されているチャットツール、「MatterMost」に稼働報告を忘れている人へ通知をするようにしてみました。

Mattermost | Open Source Collaboration for Developers

しかし、ただ作るだけではすぐ終わってしまったので、かねてから興味があったDockerを使ってマイクロサービスとして作り直しています。

今日はそのお話です。

概要

大雑把に言えば、作成したBotシステムは下図のようなイメージです。

f:id:Y-Kanoh:20180111024417p:plain

プログラムは毎朝始業直前に動かすようセットします。 稼働状況は社内のDBに登録されているので、まず社内DBを参照し、昨日の入力を忘れているメンバーを見つけ、チャットに通知します。

MatterMostには外部連携機能があり、外部システムからの投稿が可能です。*1

設計

概念図

最初は、「動けばいい」の信念のもと、上記図のBotシステムをそのまま1コンテナにしただけの設計でした。

マイクロサービスを目指すにあたり、そのコンテナ内容を機能ごとに分割し、以下のような設計で実装しています。*2

f:id:Y-Kanoh:20180111024243p:plain

作成したコンテナは以下の7つです。

  1. 管理用BFFコンテナ
  2. Bot用BFFコンテナ
  3. API Gatewayコンテナ
  4. API Gateway情報DBコンテナ
  5. 社内DBアクセス用コンテナ
  6. チーム情報DBアクセス用コンテナ
  7. チーム情報DBコンテナ

※社内DBには稼働情報、チームDBにはチームメンバー情報やチームが使用しているチャット情報が格納されています。

これらのコンテナ群は、大きくBFFAPI Gatewayバックエンドの三種類に分けることができます。

BFF

BFFとは、Backends for Frontendsの略で、つまりフロントエンドのためのバックエンドです。*3 *4

BFFは、クライアントの種類ごとに、クライアントとバックエンドの間で、データ加工や画面作成などの処理を行います。

BFFを使用することで、バックエンドから受け取ったデータを、各BFFが対象とするデバイスやユーザ向けに適した形に加工して表示させることができます。

API Gateway

機能を各コンテナに分けるうえで、一つ問題がありました。

コンテナ同士はやり取りする相手のコンテナ接続情報を知っておく必要があります。そのため、やり取りするコンテナを増やすたびに、接続先情報をそれぞれ定義する必要があるため、 コンテナ依存関係の定義が複雑になってしまいます。

そこで、今回はバックエンドのコンテナは、互いに通信せず、必ずAPI Gatewayを介してやり取りするようにしました。 これにより、以下のメリットが見込まれます。

  • APIを一元管理
  • コンテナ間の依存関係を簡略化
  • 共通処理を行う場合ここで実行可能
  • BFFからのサービス呼び出しを簡略化

バックエンドのAPIは、必ずAPI Gatewayを介して呼び出されます。Gatewayに来たリクエストは、Gatewayで適切なコンテナへルーティングされるため、Dockerコンテナで面倒なコンテナ同士の依存関係を定義する必要はなくなります。

また、今回は特に実装していませんが、バックエンドへの認証機能や、ログの収集なども、ここで一括して行うことができます。

今回、API Gatewayには、OSSとして開発が進められているKongを使用します。

Kong Open-Source API Management Gateway for Microservices

バックエンド

BFF、API Gatewayの導入により、バックエンドはその他の機能とかなり疎結合になります。

フロント側に適したデータ形式に加工する必要が少ないため、設計の制約が減ります。

そこで、バックエンドのAPIは、RESTの原則に基づいた実装にしました。

docker-compose.yml

docker-compose.ymlは以下の通りです。

前述したAPI Gatewayにより、各コンテナはkongコンテナをlinkさせるだけでバックエンドのAPIが使用できます。

version: '2'
#==============================================================================
# Volumeの定義
#==============================================================================
volumes:
  team_vol:
    driver: 'local'
  kong_vol:
    driver: 'local'

#==============================================================================
# servicesの定義
#==============================================================================
services:
  #############################################################################
  ## API Gateway
  #############################################################################
  # API GatewayのDB
  kong-database:
    image: postgres:9.4
    environment:
      - POSTGRES_USER=kong
      - POSTGRES_DB=kong
    volumes:
            - kong_vol:/var/lib/postgresql/data

  # API Gatewayの初期設定を行うコンテナ
  kong-migration:
    image: kong
    depends_on:
      - kong-database
    environment:
      - KONG_DATABASE=postgres
      - KONG_PG_HOST=kong-database
    command: kong migrations up

  # API Gatewayコンテナ
  kong:
    image: kong:latest
    depends_on:
      - kong-database
      - kong-migration
    environment:
      - KONG_DATABASE=postgres
      - KONG_PG_HOST=kong-database
      - KONG_PG_DATABASE=kong
    ports:
     - "8000:8000"
     - "8443:8443"
     - "8001:8001"
     - "8444:8444"

  #############################################################################
  ## 社内DBアクセス機能
  #############################################################################
  qcp_watcher:
    links:
      - kong
    build:
      context: "./BEService/QcpWatcher"
      dockerfile: "Dockerfile"
    ports:
      - 49513:80
    extra_hosts:
      - qcp:192.168.99.100  #社内DBのホストを指定

  #############################################################################
  ## Team情報管理機能
  #############################################################################
  # Team情報管理DB
  team_db:
    build: "./BEService/Team/DB"
    ports:
      - 54321:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_DB: postgres
    volumes:
            - team_vol:/var/lib/postgresql/data

  # TeamDBアクセスコンテナ
  team:
    links:
      - kong
      - team_db
    build: "./BEService/Team"
    ports:
      - 49514:80


  #############################################################################
  ## Bot用コンテナ(MatterMost連携コンテナ)
  #############################################################################
  mm_bff:
    links:
      - kong
    build: "./FEService/MmBff"
    ports:
      - 49515:80

  #############################################################################
  ## 管理者用コンテナ
  #############################################################################
  admin_bff:
    links:
      - kong
    build: "./FEService/AdminBff"
    ports:
      - 49516:80

実際に作成したBot

毎朝こんな感じに通知してくれるようになりました。

f:id:Y-Kanoh:20180111025537p:plain

より通知を目立たせるために、戦闘力が53万の方にご協力いただいています

f:id:Y-Kanoh:20180108181755p:plain

この方にはbotをマイクロサービスにする前から協力いただいています。最初はアイコン画像だけでしたが、メンバーからの要望により、口調もあの方になりました。

最初は黄色いネズミのアイコンだったのですが、こちらのほうが断然反応していただけました。

f:id:Y-Kanoh:20180108181351p:plain

全員稼働報告が入力できていた場合は、ちゃんと褒めて(?)いただけます。ありがたや。

今後の拡張

Botを機能ごとに分割することで、追加機能の実装もやりやすくなりました。

また、コンテナによって機能ごとにプログラムが隔離されているため、興味がある技術を試しに使ってみることもできます。

そのため、今後は業務に役立ちそうなものを 趣味 自己学習として、拡張できればと思います。

Google Homeにプレゼンさせてみた。

こんにちは。楽楽精算開発チームの岡本です。

会社で購入したGoogleHomeで自由に遊べる権利を頂いたので、色々と遊ばせてもらいました。
先日その内容を社内でプレゼンする機会があったのですが、せっかくなのでGoogleHome自身にプレゼンしてもらいました。

今回は、その際に実施した設定を説明しようと思います。
※設定方法についてはwest-cさんが既に書かれているので、設定した内容だけ書いていきます。

今回やりたいこと

  1. スマートフォンでプレゼン開始を指示する。
  2. ディスプレイにスライドが表示される。
  3. GoogleHomeがスライドの内容を喋り始める。
  4. スライドの内容を喋り終えると、次のスライドに切り替わる。
  5. スライドが終了するまで2~4を繰り返す。

準備するもの

Wan側

  • Action on Google
  • Dialogflow
    • 自然言語の解析エンジンです。受取った音声データを解析してテキストに変換してくれます。
    • また、Firebaseホスティングを用いたスクリプト実行も可能なので、変換したテキストを他のWebサービスに連携させたりもできます。
  • Firebase
    • いわゆるBaasです。
    • 今回はホスティング機能とDB機能を使用します。

Lan側

  • RaspberryPi
    • 超小型のコンピューターです。
    • 今回は以下の機能を実装しています。
      • ディスプレイへのスライド切替え通知
      • GoogleHomeへのメッセージ通知
  • GoogleHome

ざっくりとした処理の流れ

f:id:okana-yg:20171223153335j:plain

  1. スマートフォンGoogleアシスタントからAction on Googleで構築したチャットアプリを呼び出す。
  2. チャットアプリで入力した音声データをDialogflowに連携する。
  3. Dialogflowの解析結果テキストをFirebaseのDBに保存する。
  4. 保存したテキストをRaspberryPiに通知する。
    (通知されたテキストがプレゼン開始キーワードの場合は、プレゼンを開始する。)
  5. スライドの表示/切り替えをディスプレイに通知する。
  6. 喋らせるメッセージをGoogleHomeに通知する。

Action on Googleの設定

Action on Googleで新規プロジェクトを作成しActionApp informationを設定します。

Action

ADD ACTIONSからDialogflowを選択します。
f:id:okana-yg:20171224003323j:plain

App information

重要なのはAssistant app nameの項目だけです。
Assistant app nameで設定した名前でGoogleアシスタントチャットアプリを呼び出すことになります。
今回は「発表アプリ」にしたので、スマホに「OK Google 発表アプリにつないで」と話しかけるとチャットアプリが起動します。
それ以外の項目については適当に設定してしまって問題ないです。
f:id:okana-yg:20171224001105p:plain

以上でAction on Googleの設定は完了です。

Dialogflowの設定

Action on GoogleAction設定から、Dialogflow画面に遷移できるので、新規プロジェクトを作成しIntentsFulfillmentを設定します。

Intentsの設定

チャットアプリで入力した音声データに対して、どのように振舞うのかを設定します。今回は以下の2つを設定します。
f:id:okana-yg:20171224132107j:plain

  • Default Welcome Intent
    チャットアプリが起動した時の振舞いを設定します。
    今回は、アプリ起動時にチャットアプリが「発表アプリを起動しました。」と応答するようにText responseを設定しました。
    f:id:okana-yg:20171224132545j:plain
  • プレゼン開始
    チャットアプリにプレゼン開始を指示した時の振舞いを設定します。今回は以下のような振舞いを想定しています。
    1.チャットアプリに「プレゼン開始」と音声入力する。
    2.「プレゼン開始」のキーワードをFirebaseに保存する。
    3.チャットアプリが「プレゼンを開始しました。」と応答する。

    「プレゼン開始」の音声入力でsaveDataというアクションが実行されるように設定します。
    saveDataの内容についてはFulfillmentで設定します。
    f:id:okana-yg:20171224140841j:plain
    アクション実行後、チャットアプリが「プレゼンを開始しました。」と応答するように設定します。
    f:id:okana-yg:20171224144012j:plain

Fulfillmentの設定

Inline EditorENABLEDに変更し、index.jspackage.jsonを以下のように設定します。

/** index.js **/

'use strict';

const firebase = require("firebase");
const functions = require("firebase-functions");
const DialogflowApp = require("actions-on-google").DialogflowApp;

// Firebaseへの接続情報
var config = {
    apiKey: "XXXXXXXXXXXXXXXXXXXXXX",
    authDomain: "XXXXXXXXXXXXXXXXXXXXXX",
    databaseURL: "XXXXXXXXXXXXXXXXXXXXXX",
    projectId: "XXXXXXXXXXXXXXXXXXXXXX",
    storageBucket: "XXXXXXXXXXXXXXXXXXXXXX",
    messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX"
};
firebase.initializeApp(config);

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
    var app = new DialogflowApp({request, response});
    let actionMap = new Map();
    // saveDataアクションを登録
    actionMap.set("saveData", function(app) {
        // Firebaseにキーワードを保存する
        firebase.database().ref("/googlehome").set({word:"プレゼンを開始します"});
    });
    app.handleRequest(actionMap);
});

Firebaseへの接続情報Firebaseの画面でウェブアプリに Firebase を追加をクリックして表示されるコードを使用します。

/** package.json **/
{
  "name": "dialogflowFirebaseFulfillment",
  "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "~6.0"
  },
  "scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
  "dependencies": {
    "actions-on-google": "^1.5.x",
    "firebase": "^4.8.0",
    "firebase-admin": "^4.2.1",
    "firebase-functions": "^0.5.7",
    "apiai": "^4.0.3"
  }
}

設定後、DEPLOYをクリックしFirebaseスクリプトをデプロイします。

以上でDialogflowの設定は完了です。

Firebaseの設定

FirebaseDatabaseにデータ保存場所とアクセスルールを設定します。

データ保存場所の設定

Database > データから保存場所を作成します。今回は/googlehome/wordに作成します。
f:id:okana-yg:20171224151113j:plain
作成した保存場所には、PUTでデータを保存出来ます。

curl -X PUT \
  https://XXXXXX.firebaseio.com/googlehome/word.json \
  -H 'content-type: application/json' \
  -d '"保存キーワード"'

保存したデータはGETで取得できます。

curl -X GET \
  https://XXXXXX.firebaseio.com/googlehome/word.json \
  -H 'content-type: application/json'

アクセススールの設定

Database > ルールからDBへのアクセスルールを設定します。
今回は設定簡易化のため/googlehome配下のデータには認証無しでアクセスできるように設定しています。

{
  "rules": {
    "googlehome": {
      ".read": true,
      ".write": true
    },
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

以上でFirebaseの設定は完了です。

RaspberryPiの設定

以下の内容をスクリプト化し、RaspberryPiのNode.js上で実行します。

  • GoogleHomeへのメッセージ通知
  • ディスプレイへのスライド表示通知
  • FirebaseのDB更新通知の受信

※RaspberryPiに初期インストールされているNode.jsはバージョンが古いので、最新の安定バージョンに更新する必要があります。

GoogleHomeへのメッセージ通知

GoogleHome任意のメッセージをプッシュするNode.js用ライブラリが公開されているので、ありがたく使わせていただきます。
google-home-notifier

  • ライブラリのインストール
$ npm init
$ npm install google-home-notifier
  • 動作確認
/** testMessage.js **/
const googlehome = require("google-home-notifier");
const language = "ja";

// デバイス設定(Google-Homeで始まる全デバイスにメッセージが通知される。)
googlehome.device("Google-Home", language); 
// 通知するメッセージ
googlehome.notify("こんにちは。私はグーグルホームです。", function(res) {
    console.log(res);
});

※test.jsの文字コードはUTF8で作成する。

$ node testMessage.js
Device "Google-Home-XXXXXXXXXXXXXXXXXXXXXX" at 192.168.1.11:8009 <- 応答したGoogleHomeのデバイス名
Device notified

ディスプレイへのスライド表示通知

スライドの表示にはfbiというコマンドを使います。

  • コマンドのインストール
$ sudo apt-get install fbi
  • 動作確認
$ sudo fbi -nocomments -noverbose -a -T 1 -d {出力場所} {画像ファイルのパス}

※コンソール出力の場合は、{出力場所}に/dev/fb0を、HDMI出力の場合は/dev/fb1を設定します。

FirebaseのDB更新通知の受信

Firebaseのドキュメントを元に設定します。

  • ライブラリのインストール
$ npm install firebase
  • 動作確認
/** testFirebase.js **/

// Firebaseへの接続情報(Dialogflowの項目で設定したものと同じ)
var config = {
    apiKey: "XXXXXXXXXXXXXXXXXXXXXX",
    authDomain: "XXXXXXXXXXXXXXXXXXXXXX",
    databaseURL: "XXXXXXXXXXXXXXXXXXXXXX",
    projectId: "XXXXXXXXXXXXXXXXXXXXXX",
    storageBucket: "XXXXXXXXXXXXXXXXXXXXXX",
    messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX"
};
firebase.initializeApp(config);

const path = "/googlehome";
const key = "word";
const db = firebase.database();
// 更新通知を受信した時の処理を記述
db.ref(path).on("value", function(changedSnapshot) {
    // 更新された値をログに表示
    console.log("取得キーワード:" + changedSnapshot.child(key).val());
});
$ node testFirebase.js
# Firebaseに「テスト」をPUTしてDBを更新する
取得キーワード:テスト

index.js作成

実行ファイルとしてindex.jsを作成します。

/** index.js **/

const firebase = require("firebase");
const exec = require("child_process").exec;
const googlehome = require("google-home-notifier");

// メッセージを通知するGoogleHomeの設定
const language = "ja";
const deviceName = "Google-Home-XXXXXXXXXXXXXXXXXXXXXX";
googlehome.device(deviceName, language); 

// Firebaseへの接続情報を設定
var config = {
    apiKey: "XXXXXXXXXXXXXXXXXXXXXX",
    authDomain: "XXXXXXXXXXXXXXXXXXXXXX",
    databaseURL: "XXXXXXXXXXXXXXXXXXXXXX",
    projectId: "XXXXXXXXXXXXXXXXXXXXXX",
    storageBucket: "XXXXXXXXXXXXXXXXXXXXXX",
    messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX"
};
firebase.initializeApp(config);


// GoogleHomeへメッセージを通知
var notifyGoogleHome = function(word) {
    googlehome.notify(word, function(res) { console.log(res); });
};
var totalSpeakTIme = 0;
var speak = function(word, speakTime) {
    // 前回メッセージの終了後、メッセージを通知
    setTimeout(function() { notifyGoogleHome(word); }, totalSpeakTIme);
    // 今回メッセージの秒数分カウントアップ
    totalSpeakTIme += (speakTime * 1000);
}

// ディスプレイへスライドの表示を通知
var notifyDisplay = function(imgPath) {
    // fbiコマンドを使ってディスプレイに画像を表示
    exec("fbi -nocomments -noverbose -a -T 1 -1 -d /dev/fb0 " + imgPath, function(err, stdout, stderr){
        if (err) {
            console.log(err);
        }
    });
};
var totalDisplayTIme = 0;
var show = function(imgPath, speakTime) {
    // 前回スライドの終了後、表示を通知
    setTimeout(function() { notifyDisplay(imgPath); }, totalDisplayTIme);
    // 今回スライドの表示秒数分カウントアップ
    totalDisplayTIme += (speakTime * 1000);
}

// Firebaseの更新通知を受取った時の処理
const path = "/googlehome";
const key = "word";
const db = firebase.database();
db.ref(path).on("value", function(changedSnapshot) {
    // 更新された値を取得
    const value = changedSnapshot.child(key).val();
    if (value === "プレゼンを開始します") {  // 開始キーワードの場合はプレゼン開始
        // 登録された値をFirebaseから消しておく
        db.ref(path).set({[key]: ""});
        // プレゼン中は何もしない
        if (totalDisplayTIme > 0 || totalSpeakTIme > 0) {
            return;
        }

        // プレゼン開始メッセージをGoogleHomeに喋らせる
        speak("承知しました。" + value, 10);
        // 1枚目のスライドを表示する
        show("./img/001.jpg", 15);

        // 以下、プレゼン用スクリプトを記述
    }
});

ファイルが作成できたら、実行します。

$ sudo node index.js

fbiコマンドの実行時にroot権限が必要なのでsudo付きで実行します。

以上でRaspberryPiの設定は完了です。

実際にやってみる

設定完了後、スマホに対して「発表アプリにつないで」「プレゼン開始」と指示すると、index.jsに書かれた内容でGoogleHomeがプレゼンをしてくれます。

長いので冒頭部分だけですが、実際にこんな感じでGoogleHomeにプレゼンしてもらいました。


参考

Google Home開発入門 / google-home-notifier解説
Raspberry Pi でTFT液晶モジュールにいろいろ表示する


*1:Googleアシスタントと会話できるアプリ

開発をアジャイルに!スクラムトレーニング から始める最初の一歩

こんにちは。楽楽精算開発チームの堀内です。

先日、ryuzeeさんこと吉羽さんに社内でスクラムレーニングを実施して頂きました。最初から最後までアジャイルで、全てが楽しく、得るものが多いトレーニングでした。
今日はその紹介です。

前置き

ラクスではプロダクト開発の改善活動の一部にアジャイルの考え方を取り入れて進めています。
実のところ、この改善活動の後ろで我々を支援してくれるのが吉羽さんです。
始まりとしては、プロダクトをもっと良くするにはどうするのが良いかと頭を抱えている頃に、チームメンバーの1人が「改善やチームビルディングに詳しい吉羽さん」に相談してみるのが良いのではないか、と言ってくれたことでした。
「なるほど!」と思い会社に掛け合ってみたところ、タイミングも良かったこともあり、すんなりとOKしてくれました。
早速吉羽さんに相談してみると「目的、ゴールを明確にして、アクションを決めたら一覧にして優先順位を決めて並び替えて、、、」とアドバイスを受けました。アクションを実施するためにチームを組んで少しずつ進めていると、良い感じで改善が進み始めました。そのことを吉羽さんに伝えると、「それがスクラムと言うんです」と。
それを言われて正直なところ「へぇ」という印象でした。スクラムというものが良くわかっていなかったんです。でも、その言葉をキッカケにスクラムアジャイル今でも色褪せることのない良書とされるものを読み漁り「なるほどっ!」と思うようになりました。
改善活動も持続的に続けることができており、今では開発チーム全体に改善の文化が根付いてきたように思います。
そんなアジャイルのやり方を開発以外の方々にも知ってもらいたい、そう思って企画したのが吉羽さんによるスクラムレーニングです。

f:id:yhoriuchi:20171220134510j:plain
レーニングの様子

レーニングの流れ

実施して頂いたのは半日コースのトレーニングでした。 全体の流れは下記。

全体説明

レーニング内容の全体を最初に説明して頂きましたが、中でも最も印象に残っているのは次の言葉でした。
「16時に終わります。終わることをコミットしますので、安心してください。16時から通常業務に戻れます。質問は常に受け付けます。好きなだけ聞いてください。ただし、質問が多ければ予定しているトレーニングの内容が最後までいかないことになります。」
捉え方によっては、最後までトレーニングの内容を受けたければ質問はほどほどに、とも思えますが、アウトプットは途中の状況に応じて変わるということを示してくれていたと感じます。アジャイルで言う「スコープの調整」ということなんだろうなと。
つまり、16時に終わることをコミット(タイムボックスを固定)し、トレーニングの内容(スコープ)は質問の量(状況)に応じて調整する、という事なんだなと。

スクラムの概要説明

これは教科書的というか、ryuzee.comのブログに書かれているような内容で、馴染みのない人にも分かりやすく説明して頂きました。

紙飛行機を使ってスクラムを体験

今回の醍醐味とも言うべき、ゲームを通じてのスクラム体験です。
5〜6人に分かれて、チームで飛ばした紙飛行機の数を競い合うもので、以下のように進めました。 

  1. 見積もり、計画、紙飛行機製作、振り返りを1セットとして4回繰り返す(つまり4スプリント実施)
  2. 紙飛行機を作る上でいくつかのルールが決められている
  3. 決められた距離以上飛ばして始めて成果となる
  4. 最後に全体の振り返り

f:id:yhoriuchi:20171220142527j:plain

各スプリントが始まる前に、何機製作できそうか、各チームが目標となる数字を公表します。
1回目は大体の見積もりは当たっていませんでした。
2回目の見積もり精度はかなり改善されました。
やってみないと分からなかったことが、1度実施して振り返ることで見積もりも製作も上手くなり、改善したということなんだと思います。
3回目は紙飛行機を製作する上で決められていたルールを1つだけ取り除くことができます。これはどのチームも同じルールを外しました。そのルールがボトルネックとなる「制約」と全員が考えていたんですね。3回目はどのチームも良い数字を叩き出します。
4回目は3回目に外した制約がもと通りになり、1、2回目と同じ制約のもとで紙飛行機を製作します。
途中で発生するルール変更が開発で言うところの「ビジネスの状況変化」を表すわけなんですが、ルールを変えることで成果も変わることが体感できました。
制約を外すことで成果が変わることが体感できたことは良かったのですが、それ以上に重要なことは「ルールに書かれていないことは禁止していない」と言うことでした。
例えば、製作に必要なハサミは1つだけというルールはなく、複数必要なら予備があるので使っても良かった、他のチームの紙飛行機を分析してはいけないルールは無い、などです。他にもいくつかあったのですが、決められたルールに縛られると新たな考えを「発想」する事が出来なくなるんだと気づく事ができました。
実際の開発の現場に置き換えるなら、ボトルネックと認識されている「制約」を外すことも重要ですが、「制約」になっていないことに目を向けて工夫することも重要、ということになります。

f:id:yhoriuchi:20171220134517j:plain
チームと結果

最後に

レーニングは予定通り16時に終わりました。全体を通じて要所要所で質問もありました。紙飛行機のワークショップでは時間が若干押しているようでした。でもきっちり16時に終わりました。
レーニング内容のスコープが吉羽さんにより調整されたのか、予め想定されていたバッファに収まったのかは分かりません。
ただ、私たちの学びや満足が成果だとすると、私たちは間違いなく成果を得ることができましたし、予定通り16時から通常業務に戻ることができたため、全て計画通りでした。
アジャイルの原理原則を全体を通じて体感できる有意義なトレーニングでした。
 

その後

後日、参加者にアンケートを取らせて頂いたところ、皆が満足感を得られていたことが改めてわかりました。
また、開発以外の方々とスクラムの考えを共通認識として持つことができたため、ちょっとした会話の中でも「価値」「サイズを小さくする」といったキーワードが聞こえ始めています。
今では改善活動だけでなく、開発をアジャイルで始めることができるようになりました。まだまだ始まったばかりですが、大きな一歩を踏み出しています。

募集

お陰様で順調に伸び続けているラクスは、アジャイルを取り込むことで更に改善し、飛躍していきます。
この変化の真っ只中で成長を実感されたい方、ぜひ一緒に働きましょう!楽しさとやりがいは保証します!

企業の成長を支援するクラウドサービス | 株式会社ラクス
f:id:yhoriuchi:20171220144108p:plain

Google Chromeの旧バージョンをインストールする手順

はじめに

こんにちは。ラクスのstrongWhiteです。 今回はGoogle Chrome (以下、Chrome) の旧バージョンをインストールする手順について書きます。

ここ最近、会社でSeleniumを動かす機会があり、そのときにChromeの旧バージョンが必要になったので、インストールする手順をいろいろ調べていました。 これから調べたインストール手順を書いていきますが、私自身、実際に手順を試していません。 なぜ試さなかったかというと、これから書く手順はレジストリに手を加える必要があり、PCに重大な不具合を引き起こす可能性があるからです。上記を承知した上で試したい方は以下を読み進めてください。

旧バージョンは公式にはない

まず前提として、Chromeの旧バージョンは公式では提供されていません。 Chromeは、セキュリティアップデートなどで最新のバージョンが利用可能になったときに自動更新できる仕組みになっており、常に最新のバージョンを保てるようになっているからです。旧バージョンを利用すること自体、想定されていないということですね。 そのため、旧バージョンをインストールするには有志が公開している非公式なサイトからインストーラを入手する必要があります。

ただインストールするだけじゃダメ

前述の通りChromeは自動更新する仕組みになっているので、ただインストールするだけではダメです。自動更新を停止させる設定を行わなければいけません。 自動更新の停止設定はChrome側からは行えず、ターミナルなどで外部から設定しないとけません。 Windowsだとレジストリを変更する必要があり、Macだとdefaultsコマンドを実行しなくてはいけません。 レジストリは不用意に変更するとPCに重大な不具合が起こる可能性があります。ひとつひとつの操作が理解できた上で慎重に行うことを強く強くオススメします。

自動更新を復活させる方法

自動更新は停止させたあとでも以下の方法で復活させることができます。 Windowsの場合はレジストリエディタからHKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Updateに、DWORD値でDisableAutoUpdateChecksCheckboxValueという値を作成し、0に設定すればOK。 Macの場合はdefaults write com.google.Keystone.Agent checkInterval 1を実行すればOKです。 これらはGoogle Update 関連のタスクを有効にする設定です。

インストールの手順

これまでのセクションを読んで察することができるかもしれませんが、Chromeの旧バージョンをインストールする手順は公式がサポートしている手順ではないのでご自身の責任でお試しください。 以下、調べた手順。

Windows手順

  1. すでにChromeがインストールされている場合はアンインストールする
  2. 旧バージョンのインストーラを取得する
  3. ネットワーク接続を切断する
  4. インストールする
  5. レジストリエディタからHKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Updateに、DWORD値でDisableAutoUpdateChecksCheckboxValueという値を作成し、1に設定する
  6. ネットワーク接続を有効にする

Mac手順

  1. 1~4までは上と同じ
  2. ターミナルを起動し、defaults write com.google.Keystone.Agent checkInterval 0 と実行する
  3. ネットワーク接続を有効にする

※手順を補足すると、インストール前にネットワーク接続を切断しているのは、インストール後にChromeが自動更新しないようにするためです。その他、Windows手順の5とMac手順の2では、Google Update 関連のタスクを無効にする設定を行っています。

おわりに

今回は Chromeの旧バージョンをインストールする手順を書きました。 冒頭でなぜ実際に手順を試していないと書いたのか、ここまで読んでいただいた方は意味がわかったかと思いますが、公式でサポートされている手順ではなく、かつPCが壊れる危険性を孕んでいますので、何度も言いますが試す際は自己責任でお願いします(汗)

参考


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.