Quantcast
Channel: Ryuzee.com
Viewing all 197 articles
Browse latest View live

【続編:64MB超え】AzureのBlobサービスにブラウザから直接ファイルをアップロードする

$
0
0

こんにちは。@ryuzeeです。

前回、AzureのBlobサービスにブラウザから直接ファイルをアップロードするという話を書きましたが、残念ながら前回の実装は、AzureのBlobサービスのPUTの制限で64MBまでしか登録することができませんでした。

そこで今回は、AzureのBlobサービスにブラウザ経由で直接64MB超えのファイルを登録する方法を紹介します。なお、Rubyスクリプトの部分(CORSの設定やSASの取得など)は前回と変更ありませんので、フォームのみの修正です。

細かいところはコードを見てもらうとして、分割アップロードの実装において注意すべき点を列挙します。

  • 64MBを超えるファイルをアップロードしたい場合は、最大4MBのファイルのブロックに分割してPUTします。
  • 分割したブロックをPUTする際には、生成したSASのURLの末尾に、comp=block&blockid=ブロックIDを追加しないといけません。
  • ブロックIDは、base64エンコードされた文字列である必要があります。さらに、複数のブロックがある場合、全てのブロックのブロックIDの文字列長は同じでなければいけません。
  • 全てのブロックを送信し終わったら、ブロックリストをPUTする必要があります。URLは、SASの末尾にcomp=blocklistを追加します。
  • ブロックリストの送信の本文はXMLで、それまでに送信したブロックIDを列挙します。
  • ブロックリストの送信の際は、HTTPヘッダーに、x-ms-versionの設定が必要です。

その他、前回の話も含めて検討した方がよさそうな箇所についても列挙します。

  • SASを使って認証URLを取得しますが、このURLを知っていれば有効期間中は、当該ファイル名での書き込みが出来ます。すなわち認証URLの有効期限はあまり長くしてはいけません。
  • いくらJavaScriptで制御したとしても、ユーザーがSASのURLを知るのは簡単なので、巨大ファイルをPUTされたりしそう。
  • アップロード画面で任意のファイル名をユーザーの指定でアップロードできるようにするのは上書きなどの問題がありそう。SASを発行する際のファイル名はシステム側で作成することを推奨。

ちなみに実行するとこんな感じですね。

以上、誰得かまったく分からない話でした…

views/form.erb

<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>

<div>
  <form id="upload-form" method="post" enctype="multipart/form-data">
    <div>
      <label class="control-label col-sm-2"><span>File</span></label>
      <div>
        <input type="file" name="file" id="file" />
    </div>
  </div>
</div>

<span id="progress">0%</span>

<script type="text/javascript">
function segmentation(arrayBuffer, segmentSize)
{
    var segments = [];
    var fi = 0;
    while(fi * segmentSize < arrayBuffer.byteLength){
        segments.push(arrayBuffer.slice(fi * segmentSize, (fi + 1) * segmentSize));
        ++fi;
    }
    return segments;
}

$(document).ready(function() {
  var send_block_list = function(sas_base_url, block_prefix, block_num) {
    var defer = $.Deferred();
    var put_to = sas_base_url + '&comp=blocklist';
    var body = '<?xml version="1.0" encoding="utf-8"?><BlockList>';
    for (var i = 0; i < block_num; i++) {
      body += '<Latest>' + base64encode(block_id(block_prefix, i)) + '</Latest>';
    }
    body += '</BlockList>';
    $.ajax({
      type:   'PUT',
      url:    put_to,
      headers: {
        'x-ms-version': '2015-04-05',
        'x-ms-blob-content-type': 'application/octet-stream'
      },
      data:   body,
      async: true,
      cache: false,
      contentType: false,
      processData: false
    }).done(function(data, textStatus, jqXHR) {
      defer.notify().resolve(data);
    }).fail(function( jqXHR, textStatus, errorThrown) {
      var msg = 'ブロックリストの送信に失敗しました...';
      console.log(msg);
      defer.reject(msg)
    });
    return defer.promise();
  };

  var get_sas_base_url = function(filename) {
    var form = new FormData();
    form.append('filename', filename);
    var defer = $.Deferred();
    $.ajax({
      url: '/sas',
      type: 'POST',
      contentType: 'application/octet-stream',
      data: form,
      async: true,
      crossDomain: true,
      cache: false,
      contentType: false,
      processData: false
    }).done(function(data) {
      data = data.url;
      defer.resolve(data);
    }).fail(function(jqXHR, textStatus, errorThrown) {
      var msg = 'SASのURL取得に失敗しました...';
      console.log(msg);
      defer.reject(msg)
    });
    return defer.promise();
  };

  var send_block = function(sas_base_url, block_prefix, block_index, block) {
    var defer = $.Deferred();
    var url = sas_base_url + '&comp=block&blockid=' + base64encode(block_id(block_prefix, block_index));
    $.ajax({
      url: url,
      type: 'PUT',
      contentType: 'application/octet-stream',
      data: block,
      async: true,
      crossDomain: true,
      cache: false,
      contentType: false,
      processData: false
    }).done(function(data, textStatus, jqXHR) {
      var msg = 'Azureへのデータ送信に成功しました...';
      console.log(msg);
      defer.notify().resolve(data);
    }).fail(function(jqXHR, textStatus, errorThrown) {
      var msg = 'Azureへのデータ送信に失敗しました...';
      console.log(msg);
      defer.reject(msg)
    });
    return defer.promise();
  };

  function block_id(block_prefix, index) {
    return block_prefix + ("0000000" + index).substr(-7,7);
  }

  function base64encode(str) {
    return window.btoa(str);
  }

  $('#file').on("change", function(event) {
    var file = this.files[0];
    if(file != null) {
      console.log("アップロードファイル名は" + file.name + "です...");
    } else {
      return;
    }
    event.preventDefault();

    var block_prefix = 'BlockId';
    var reader = new FileReader();

    reader.onload = (function(file) {
      return function(evt) {
        buffer = reader.result;
        var segments = segmentation(buffer, 4 * 1024 * 1024);

        $.when(get_sas_base_url(file.name))
        .fail(function(msg) {
          $("#progress").html(msg);
        })
        .done(function(url){
          var functions = [];
          $.each(segments,function(i, val) {
            functions.push(send_block(url, block_prefix, i, val));
          });

          // See http://stackoverflow.com/questions/26066198/jquery-when-progress-for-array-of-deferred-and-or-promise
          $.whenWithProgress = function(arrayOfPromises) {
            var cntr = 0, defer = $.Deferred();
            for (var i = 0; i < arrayOfPromises.length; i++) {
              arrayOfPromises[i].done(function() {
                defer.notify(++cntr, arrayOfPromises.length);
              });
            }
            jQuery.when.apply(jQuery, arrayOfPromises).done(function() {
              defer.resolveWith(null, arguments);
            });
            return defer.promise();
          };

          $.whenWithProgress(functions)
          .progress(function(cnt, total) {
            $("#progress").html(Math.round(cnt * 100 / total) + "%");
          })
          .fail(function(msg) {
            $("#progress").html(msg);
          })
          .done(function() {
            console.log('全てのブロックのアップロード完了...');
            $.when(send_block_list(url, block_prefix, segments.length))
            .fail(function(msg) {
              $("#progress").html(msg);
            })
            .done(function(){
              console.log('ブロックリストの登録完了...');
              $("#progress").html('100%');
            });
          });
        });
      };
    })(file);

    reader.onloadstart = function(e) {
      $("#progress").html('0%');
    };
    reader.readAsArrayBuffer(file);
  });
});
</script>
</body>
</html>

RACIマトリクスを使ってスクラムプロジェクトの責任分担を見える化する

$
0
0

こんにちは。@ryuzeeです。

開発プロジェクトを進めるときには、ご存知の通り多くの人が関わります。一方で以前から何度か書いているようにいきなり人が集まっただけの段階だと、まだ組織やチームとしてはうまく機能せずさまざまな混乱が起こることになります。

たとえばスクラムを採用した場合で、かつスクラムに関する経験があまりない状況の場合、それぞれが持つロールを十分に果たせなかったり、そもそもどんな責任を持つべきなのかも分からずに声の大きい人の言いなりになったり、上司や外部からのコントロールを受けすぎてしまったりといった混乱も起こります。

そこで、初期の形成期や混乱期を早めに終わらせるために、それぞれの役割が持つ責任を見える化するのをお勧めします。(混乱した現場でこれをやると、想像以上に全員の認識が違うことがよく分かります)

以下の表はRACI(レイシー)マトリクスと呼ばれるものです。縦軸にタスクを列挙し、横軸には登場人物(ロール)を入れます。セルの中のそれぞれのアルファベットは以下の意味を持ちます。

  • R: 実行責任者(Responsble): 実際にタスクを実行する人。複数人いるかもしれませんし、1つのロールに限定されないかもしれません
  • A: 説明責任者(Accountable): タスクの成果に対して責任を持つ人。承認者とも言えます。通常は1つのタスクに対して1人または1ロールになります
  • C: 協業先(Consulted): 作業に関する知識を持っていて要請に応じて意見やアドバイスを提供します
  • I: 報告先(Informed): 結果や進捗について報告を受ける立場です

この定義にさらに以下のような項目を追加することもあります。

  • F: ファシリテーター(Facilitator): 生産的であるように議論や行動をファシリテーションする
  • V: 検証者(Verifies): タスクの完了をなんらかの定義や基準にあわせて検証する
  • S: サポート(Supportive): タスクの実行において補助的な役割を果たす

あまり細かく定義しすぎると混乱するので、ここではスクラムの特性にあわせてFだけ追加した例になっています。

なお、プロジェクトの最初のうちに見える化しておくと良いと思いますが、一度作れば終わりではありません。 チームの成長度合いや能力によって、場合によってはどんどん権限を委譲していくことも普通にあります。 また組織の事情にあわせて異なる役割分担に変えざるを得ないケースや新たなタスクが増えたりもします。

また、このマトリクスは、「それ俺の仕事じゃないしー(It’s not my business…)」というための道具ではありません。 責任を持つべき人がタスクを進めないからといって、放置しておいて良いわけはないので念のため。

RACI(レイシー)マトリクスの例(スクラムの場合)

RACI(レイシー)マトリクスチームメンバープロダクトオーナースクラムマスターマネージャーアーキテクト
プロダクトのビジョンやゴールIR/AIIC
プロダクトに関する予算の算出CR/AICC
プロダクトに関する予算の確保IR/AIII
プロダクトに関するリスクの管理CR/AFIC
プロダクトの全体アーキテクチャの設計CAFCR
プロダクトに必要な人のアサインCIIR/AC
プロダクトバックログ優先順位付けと手入れCR/ACIC
プロダクトバックログ項目の作成CR/AFIC
プロダクトバックログ項目の受け入れ基準の策定CR/AFCC
プロダクトバックログ項目の完了の承認/否認CR/AFCC
Readyの定義の作成と改善RCFCC
受け入れテストの作成と実行RAFIC
スクラムがうまくいくようにするCCRAI
スクラムチーム全体を改善するRRRAI
妨害の除去RCFR/AR
スプリントで実施するバックログ項目の選定CR/AFIC
スプリントで実施する量の決定R/ACFIC
スプリントで実施するタスクの洗い出しR/ACFIC
スプリントでの割り込みの受け入れ可否の決定R/ACCIC
デイリースクラムR/AIFII
スプリントレビューRAFII
スプリントふりかえりR/ARFII
完了の定義の作成と改善RCFCC
コーディング規約の作成RIICR/A
ユニットテストの作成RIIIR/A
ツールの選択と運用RIICR/A
プロダクトの品質の保証RAFCC
プロダクトの実運用RAFCC
プロダクトの終了IR/AICI

採用とか退職とか評価に関するよもやま話

$
0
0

こんにちは。@ryuzeeです。

以前に、採用プロセスを真剣に考えろという話を書きましたが、ちょっと関連する話を書こうと思います。

採用に関するメトリクスを取ろう

採用プロセスに真面目に取り組んでいる会社ならやっていると思われますが、採用活動をするにあたってはメトリクスを取ることが望ましいです。特に成長中の組織でたくさんの人を採用したい場合や、ある一定規模の組織でそれは顕著です。取るべきメトリクスには以下のようなものがあるはずです。

  • 総応募者数
  • 採用媒体別応募者数
  • エージェント別紹介者数
  • 社員の紹介によって応募が来た数
  • 自社の採用サイトから応募が来た数
  • 各属性で書類選考を通った数
  • 各属性で一次面接を通った数
  • 各属性で二次面接を通った数 (ここは各社によって何回面接があるか違いますが…)
  • 各属性で最終面接を通った数 (同上)
  • プロセスの途中で辞退した数
  • オファーを出した数
  • オファーを受けた数
  • オファーを辞退した数
  • 各採用媒体やエージェントに支払った金額
  • 採用イベントに使った金額
  • 応募からオファーまでのリードタイム(所要時間)
  • 採用活動に関わった社員の時間
  • 社員別の面接の回数やそこでの判断

つまり応募者の数から実際に採用した数、かかったお金、かかった時間のようなものを対象にすると良いでしょう。

採用に関連するメトリクスをとる最大の理由は、当然のことながら「採用活動の質」をカイゼンするためです。なんとなくうまく行っていないと思っていても実際に見えていないものはカイゼンできません。

また実際に時間や費用を測定してみると、想像以上に時間も費用もかかっていることが分かります。 つまり社員が辞めてしまうことは仕方がないにせよ、それを補充するために採用にお金や時間を使うくらいであれば、事前にそのお金を社内のカイゼンに使うという選択肢が存在することが分かるでしょう。

社員の退職理由に関する情報を集めよう

トム・デマルコのピープルウェアでは以下のように言っています。

大部分の企業は、退職の統計さえ取っていない。実際問題として、熟練スタッフが辞めた場合、比重にどれぐらいのコストがかかるかは誰にもわからない。その結果生産性を議論するとき、退職はありえない、あるいは、退職してもタダ同然で補充がきくとの前提で話を進める。

しかし採用で書いた話の流れを踏まえれば、社員がなぜ退職するのかストレートな理由を収集することは重要です。もちろんストレートな理由を収集するには一定以上の信頼関係が必要ではあるのですが、そこで得られた答えが組織のカイゼンに繋がる可能性は多いにあります。

なお、さまざまなWebサイトで退職の理由の統計などが出ていますが、アメリカ海軍に学ぶ「最強チーム」の作り方という本によれば、以下の順番だそうです。

* 第一の理由は「上司から大切に扱ってもらえないこと」 * 第二の理由は「積極的な行動を抑えこまれること」 * 第三の理由は「意見に耳を貸してもらえないこと」 * 第四の理由は「責任範囲を拡大してもらえないこと」 * 第五の理由は「給与」

もちろん、人にはそれぞれ違う理由があると思いますが、その理由が組織の機能不全に関わる問題なのであれば、このような形で情報を収集し続けてカイゼンすれば良いことになります。すなわち組織運営のフィードバックサイクルをまわす上での材料になるということです。

ちなみに上記の理由の上から4つ目までは、その人の上司のふるまいに関するものであることが分かります。つまり退職の分析をしてみると、特定のマネージャーの部下だけがたくさん退職しているというケースもあり得るということです。このような兆候があるのであれば、情報収集のやり方を変える必要があるかもしれません。

きちんと評価されないことは不満に繋がる

上記の理由には出ていませんでしたが、自分に対する評価が妥当だと思えない場合は、当然ながら不満が募ります。 もちろん自分に対しては甘くなってしまうのが人情なので仕方ないところなのです。 しかし、この不満が組織構造に関連している場合もあるので注意が必要です。

たとえば、「期初にゴールの数字を決めて、半年とか一年後に数字だけで評価する」ようなやり方をしていて、途中のビジネスの変化によってその数字の意味がなくなった(やる必要がなくなった)場合を考えてみましょう。 期初のゴールだけを見れば単純に未達になってしまいますが、それだけで評価されれば当然不満になります。一方で評価を下げたくないので、意味のない数字を達成しようとする、というのは今度は会社にとってのムダになります。

つまりゴールがあったとしても、ゴールがいつ正しくないものに変わるかが分からず、新しい適切なゴールが出てくるとも限らない、というのを認める。すなわちMoving Targetであることを共有する必要があるわけです。そして状況の変化に対応しながらゴールを変えていくためには、従業員とマネージャーの間で定期的なコミュニケーションが必要です。これも3か月や半年といった単位ではなく、毎週・隔週・月一回くらいのペースで行われなければいけません。つまりゴールや進捗状況についても、フィードバックサイクルを回すということになります。(評価ミーティングで、まずあなたの仕事を説明してください、みたいなことを言うマネージャーが世の中にはいるらしい…。上司だけの判断で正しく人を評価できるなんてはずはないのです。)

お前は何をいってるんだ?

要は、製品を作るときだけフィードバックサイクルをガンガン回すんじゃなくて、組織運営でも短いフィードバックサイクルを回せ、という話です。

それでは!!

【資料公開】強いチームの作り方(デブサミ2016版)

$
0
0

こんにちは。@ryuzeeです。

2016年2月19日に目黒雅叙園で行われたDevSumi 2016で「強いチームの作り方」というテーマで登壇してきましたので資料を公開します。昨年11月くらいに公開したものから基本的には変わっていませんが組織やチームのカイゼンにご利用いただければ幸いです!

なお、本講演の再演、トレーニング(半日/1日)を提供しておりますので、ご興味のある方はお気軽にお問い合わせください。

組織やチームづくりに役立つ20冊

$
0
0

こんにちは。@ryuzeeです。最近、組織やチームのことを考えるヒントになる本を紹介してほしい、と言われることが多いのでダンプしておきます。あくまで自分で読んだ私見で選んだものなので、この定番がないのは何故だとかはあると思います。

定番

トム デマルコ;ティモシー リスター
日経BP社 2013-12-24 円
「実際のところ、ソフトウェア開発上の問題の多くは、技術的というより社会学的なものである」とあるように、ソフトウェア開発における問題点を人間の観点で整理している名著。第IV部では生産性の高いチームを育てるというテーマで機能するチームの特徴やチームの壊し方(守りのマネジメント・官僚主義・作業場所の分散・時間の分断・品質の削減・はったりの納期・チーム解体の方針)についても説明している。作業場所の分断についてはリモートワークの方法が洗練されてきてる現状はありつつも思い当たることも結構ある。
ロバート・キーガン, リサ・ラスコウ・レイヒー
英治出版 2013-10-24 2700円
分量が約450ページあるうえに理論の話が多いので最初に読むにはハードルが高いかもしれない(しいきなり読むとたぶん挫折する)。ただワークショップの例、分析方法の例、そして事例なども含まれているので参考書籍としては一読しておくとよい。現場のリーダークラスというよりはもうちょっと上の立場の人が対象。
ピーター M センゲ, Peter M. Senge
英治出版 2011-06-22 3780円
「最強組織の法則」の増補改訂版。センゲは、メンタルモデル・自己マスタリー・システム思考・共有ビジョン・チーム学習の5つが組織に必要だという考えを広めた人。

読み物系

パトリック・レンシオーニ
翔泳社 2003-06-17 1728円
ディシジョンテック社という創業約2年のベンチャー企業に立て直しのため畑違いな分野からやってきたCEOがいかに経営陣のチームを変えていくか、というフィクションを元に機能しないチームの特徴や、それへの対応方針を説明している。物語が全体の8割くらいを占めるので、これを読んだあとに他の本で補うといいかも。信頼の欠如 / 衝突への恐怖 / 責任感の不足 / 説明責任の回避 / 結果への無関心という構造は分かりやすい。かなり古い本だがオススメ。
ジーン キム, ケビン ベア, ジョージ スパッフォード
日経BP社 2014-08-18 2376円
著者のジーンキムはDevOps系の著名人。こちらもある日前任者の退職で突然システムの責任者になった主人公が、いかにシステム開発における問題点をカイゼンしていくかという物語を通じてDevOpsを説明している。DevOpsはツールだけでなく主に組織的な問題への対応であり、この中でも組織をどのように変えていくかという話がふんだんに出ている。ツールの銀の弾丸が大好きな人に先に読ませておくべき一冊。

入門系

麻野 耕司
PHP研究所 2015-08-19 918円
すべての組織は病んでいる / 戦略至上主義という病 / 犯人探しという病 / 会議が空回りする病 / 「最近の若者は」という病 / 「何回同じことをいわせるの?」という病 / ものさし不在という病 / 決断が先送りにされる病 という病をあげてそれに対してどう取り組むかを解説した書籍。理論ではなく実践の書籍で1時間くらいで読めると思うのでオススメ。
組織開発とは組織の主に人間的な部分に焦点をあてその変革に取り組むこと。組織開発ではさまざまな手法や理論を使うことになるが、それらの理論を初心者用に解説した書籍。「実際の生産性=潜在的生産性 - 欠損プロセスに起因するロス」というのは分かりやすい。

事例系

エリック・シュミット, ジョナサン・ローゼンバーグ, アラン・イーグル, ラリー・ペイジ
日本経済新聞出版社 2014-10-08 1944円
これは説明するまでもない一冊。グーグルにおける文化・戦略・意思決定・採用・コミュニケーション方法などを詳しく解説している。この中で採用はいちばん大事な仕事と明言している。
ラズロ・ボック
東洋経済新報社 2015-07-31 2138円
こちらはグーグルの人事担当上級副社長のラズロが主にチームでの働き方や、一緒に働く人の採用や評価の仕方・報酬の設定などフォーカスして解説した本。採用面接に関していえば、直感を信じてはいけないという章で詳しく説明されている。一方で多くの日本企業では面接のスキルややり方について教育が行われていないしカイゼンが行われていないことは驚くべきことでもある。
ジェイソン・フリード, デイヴィッド・ハイネマイヤー・ハンソン
早川書房 2012-01-11 1620円
生産性・人を雇う・文化といったあたりの章は組織やチームづくりに大いに役立つ。体系的に組織についてまとめた本ではなく37シグナルズの働き方に関する本で読み物として非常によくまとまっている。
サイボウズの創業者の1人で現社長の青野氏による著作。現在こそ、社員が辞めない会社、辞めても6年の間は戻ってこられる出戻り推奨の会社としても知られているが、2000年代中盤は高い離職率に苦しみ、本業と関係の少ない事業投資によって会社が疲弊していたらしい。そこからどのように考えて現在のような姿に変えていったか分かる生々しい事例。社長自らが育児休暇とったりしてすごい。

組織内での浸透や推進

James O. Coplien, Neil B. Harriosn
翔泳社 2013-08-05 4104円
日本に頻繁にやってきてくれるCopeさんの大作。パターンとは、ある文脈で繰り返し起きる問題を解決する方法であり、本書では組織の漸進的成長のためのパターン、組織構築パターンが多数紹介されており、それらのパターンを組織にどう適用していくかも解説している。ぼくは「自分たちで選んだチーム」「チームのプライド」あたりが好き。
こちらもパターン。タイトルに「アジャイル」とあるがべつに適用範囲がアジャイル開発に限定されるわけではまったくない。ぼくが好きなのは、定番のエバンジェリストのパターンで、何か新しいものを自分が組織に導入するのであれば自分自身をまず最初に説得し、自分の理念を確信し、情熱をもって取り組むことを指す。
Brian W. Fitzpatrick, Ben Collins-Sussman
オライリージャパン 2013-07-20 2376円
さきほどから何度か登場しているグーグルのエンジニア2人が書いた開発チーム向けの書籍。グーグルの一貫性のある考え方がよく現れている。約170ページで非常に読みやすい翻訳でもあるので、分厚いのはちょっと、という人はまずここから読んでみるといいと思う。
マイケル・アブラショフ
三笠書房 2015-05-20 648円
軍隊を舞台にしているのでスマートじゃないんじゃないか?と不安になるかもしれないが全く心配いらない。冒頭で「束縛をゆるめればゆるめただけ、すぐれた結果が出る」「自分のプライドよりもチームの実績を優先させなければならない」などの記述からもわかるはずだ。これもすぐ読めておすすめ。

リモートワーク

ジェイソン・フリード, デイヴィッド・ハイネマイヤー・ハンソン
早川書房 2014-01-24 1620円
リモートワークに関するまとまった書籍としてはこれが初めてだと思う。人材は世界中にいる / リモートワーカーは人柄 / 賃金差別をしない / テストプロジェクトで試すあたりは定番でたぶんソニックガーデンさんも同じような感じではないだろうか。従来型の組織でリモートワークに移行しようとするとマネジメントスタイルを変えないといけないのは言うまでもない。
スコット・バークン
新潮社 2015-02-18 1728円
元マイクロソフトの著者がWordPress.comを運営するAutomattic社にジョインして過ごした約1年半を振り返ってたノンフィクション。「すぐれたリーダーの最後の仕事は、自分がいなくなったあとも万事うまくいくようにしておくことだ」
37シグナルズ本と重なる内容は多いが日本でもここまでできることが分かる良書。特にマネジメントがどう変わるかについて詳細に述べているのが良いところ。採用に時間をかける / 全員がセルフマネジメントする / 成果はチームのものという考えなど、リモートかオンサイトか関係なく役にたつ。

会議

大橋 禅太郎
大和書房 2005-05-18 1512円
日々たくさんの会議がおこなわれている割に問題の解決に至らずだらだら時間が過ぎてしまうというのをよく見かけるがそういう時に使ってみるといいかも。特に苦しい状況のときには。個人的には、言葉のフォーマットを変える「製品にバグが多い」→「どうやったら製品のバグを減らせるか」とか、言えない問題を言ってみる、あたりのプラクティスがいいと思う。
漆原 次郎
東洋経済新報社 2012-01-05 1620円
ちょっと表現が誤解を生む(意思決定者は会議にでないとか、キャッチーにするために仕方ないのか)箇所はあるものの、1日集中討議でその日のうちに結論を出すのを仕掛け化しているのはいい。そして議論を円滑に進行できるように付箋紙や必要な道具がいつも常備されているのもいい。

スライド共有アプリをAzureに対応させた話

$
0
0

こんにちは。@ryuzeeです。最近アジャイル、クラウド、DevOpsの3点セットで仕事しています :-)

さて、自分のスライドはこちらのスライド公開サイトに公開しているのですが、これはオープンソースの自作アプリケーションです。

もともとはAWS専用だった(Amazon S3とAmazon SQSに依存)のですが、今回時間があったのでAzureに対応しました(ソースはこちら)。さきほどAzureに移行していまのところ普通に動いています。 構成としては、ロードバランサー(Traffic Manager)とWebサーバ(Nginx + Docker)2台とDBサーバ(MySQL)2台です。

Azureに対応させること自体は特に難しくなかったのですが、AWSと両方使ってみていろいろ気づいたことがあるので整理しておきます。

項目AWSAzure
オブジェクトストレージAmazon S3を利用。仮想マシンを経由しないでファイルをアップロードするには、Signature v4のAPIを使ってフォームの項目を生成する。保存したオブジェクトをマネージメントコンソールから見ることができるのは楽Azure Blob Serviceを利用。こちらも同様にSASを使ってワンタイムのポスト先URLを生成してあげればOK。ただし64MB以上のファイルをアップロードする場合はブロックに分割して送らないといけない点は面倒。ポータルからはオブジェクトを見ることができない(のは辛い)
キューAmazon SQSを利用。スライドの変換処理のために不可視タイムアウトを設定して同一ファイルの変換が走らないようにしている。キューに入っているメッセージをマネージメントコンソールから見えるのは良いAzure Blob Queueを利用。こちらも不可視タイムアウトの設定が可能。Ruby SDKを使ったが、操作感はほぼAWSと変わらない感じ。ポータルからキューの中身が見えるといいのだが...
仮想マシンAmazon EC2を利用。もともとAWSで動かしていたときはt2.smallで動かしていた。IAM Roleを使うとAPIのアクセスキーとシークレットキーをサーバに埋め込まなくてよいので安心Virtual Machinesを利用。Standard D1(1コア/3.5GBメモリ)を利用。Azureの場合は仮想マシンを立ち上げる際にあらかじめストレージの性能なども仮想マシンの性能にあわせた形でパッケージングされている点がAWSと大きくことなる。だいたいいい感じの仮想マシンがほしい、という場合に分かりやすい。あと東日本と西日本にリージョンがあって、同じ日本なのに西日本の方が値段が2割くらい安い
RDBMSAmazon RDSを利用。MySQL、PostgreSQL、Oracle、SQL Server、MariaDBなど多くのRDBMSがマネージドで提供されているのはやっぱり楽。フェイルオーバーの設定とか自分でやりたくないし。規模が大きければAuroraを使う手もあるAzureにはSQL Databaseがあるが、これはエンジンはSQL Serverということなのでちょっと困った。Azureの場合ClearDBという選択肢もあるようだが、ポータルからアクセスするとあまりにデザインの違うサイトに飛んで行ったりするので一抹の不安を覚える...
ロードバランサーAmazon Elastic Loadbalancingを利用。まぁ特段言うことはない感じTraffic Managerを利用。エンドポイントのURLがスッキリした感じなのはいい(trafficmanager.net)
ポータル画面AWSの場合はサービスによってインターフェイスがかなり違う。ずっと使っているので慣れてはいるが、オプションで指定できる項目も多いので初心者は最初引くかもしれないAzureの圧勝。Azureの場合はどのサービスでもUIのインターフェイスが揃っている点はかなり楽だと思う。このあたりはガイドラインがしっかりしているんだろうと思う。他のWindows製品もみんなそうだし。ただメニューで製品名をクリックしても、その製品が何ができるのかはポータル上では分からない気がする...。ちなみに上のスクリーンショットは自分でカスタマイズしたもの。こういうの分かりやすい
ドキュメント各種サービスやSDKでなんだかんだいってドキュメントが揃っている点は開発者としては楽。サンプルの量も豊富だし特にAPI関連について、どこに何のドキュメントがあるのか分からない上に、機械翻訳されたドキュメントにたどり着いても原文みないと分からない。ここは改善してほしい。

ということで、引き続きいろいろ触ってみたいと思います。

Docker + Capistrano3で簡単にWebアプリをデプロイする

$
0
0

こんにちは。@ryuzeeです。

アプリケーションのデプロイを楽にするためにDockerを使いたいけど、別にクラスタは必要ない規模だったりクラスタの管理もしたくないという人は多いのではないかと思います。 そこで、今回は、DockerとCapistrano3を組み合わせて単にデプロイを楽にする方法を紹介します。

構成図

まず今回の構成図はこんな感じです。AWS上での構成例になっていますが別にどの環境でもあまり関係ない普通のWebアプリケーションを想定してください。

実現したい要件

次に実現する要件です。特に変わったことはありません。

  • いつも同じ方式でデプロイする
  • ダウンタイムなしでデプロイする
  • デプロイに失敗したら簡単にロールバックできるようにする
  • サーバが増えてもデプロイの方式は変えなくて済むようにする
  • サーバを再起動してもサービスは自動で復旧する

方式

では方式を見ていきましょう。

WebアプリケーションのDockerイメージ化

今回は、Dockerを使うので、WebアプリケーションをDockerイメージ化します。今回の構成では、複数コンテナ間の連携はないので簡単です。 なお、デプロイにおけるベストプラクティスとして、開発環境やステージング環境でテストした成果物(Artifacts)を本番に反映するときに何らかの変更をするべきではない、というものがあります。 したがってデータベースへの接続情報などをはじめとする環境固有の情報は全てコンテナ起動時に引数(環境変数)を使って渡せるようにしてください(言わずもがなですが)。

また、イメージを作成する際は、必ずイメージに何らかのバージョン情報をタグとして付与するようにします。latestタグしかないとデプロイするイメージを指定したりロールバックするのが困難になります。 僕の場合は、Railsアプリケーションにバージョン情報を持たせ、イメージをビルドする際にそのバージョンを反映するようにしました。以下は、それを行なうRakefileの例です(Build Onceを実現しつつ、タグを複数つけようとしているので若干複雑にはなっています)。

# -*- coding: utf-8 -*-
require "#{File.dirname(__FILE__)}/lib/oss/version"
require 'open3'

task :default => :build

task :build do
  cmd = "docker build -q -t ryuzee/slidehub:latest . 2>/dev/null | awk '/Successfully built/{print $NF}'"
  o, e, _s = Open3.capture3(cmd)
  if o.chomp! == '' || e != ''
    raise 'Failed to build Docker image...'
  end

  cmd = "docker tag -f #{o} ryuzee/slidehub:#{SlideHub::VERSION}"
  o, e, _s = Open3.capture3(cmd)
  if o.chomp! == '' || e != ''
    raise 'Failed to add version tag to Docker image...'
  end
end

task :push do
  cmd = 'docker push ryuzee/slidehub:latest'
  sh cmd
  cmd = "docker push ryuzee/slidehub:#{SlideHub::VERSION}"
  sh cmd
end

Capistrano3を使って外部からコンテナを起動し切り替える

さて、単にサーバでコンテナを起動するだけであれば、Capistrano3で、単にdocker runコマンドを実行すればOKです。 たとえば、Capistrano3のタスクは以下のような感じになります。おおよその処理の流れとしては以下になります(ただしこのやり方には問題があるので後述)。

  • 必要なイメージをpullしてくる
  • –envオプションを使ってコンテナを起動する
  • コンテナが起動しても、コンテナの中のプロセス(Rails)がすぐに起動してくるとは限らない(特に遅いサーバ)し、エラーになる可能性もあるので、curlを使って指定回数HTTPのステータスをチェックする
  • 一定回数の間にステータスコード200が返ってくればOKそうなので、Nginxのproxy_passの設定を書き換えて、Nginxをリロードする
  • 最後にコンテナが一杯動いていると問題なので、今起動したものとその前に起動していたものを残して強制的に削除する
# 前略
  desc 'deploy'
  task :deploy do
    on roles(:container) do
      execute 'sudo docker pull ryuzee/slidehub:latest'
      prefix = DateTime.now.strftime('%Y%m%d%H%M%s')
      cmd = <<"EOS"
        docker run -d \
        --env OSS_DB_NAME=#{fetch(:oss_db_name)} \
        --env OSS_DB_USERNAME=#{fetch(:oss_db_username)} \
        --env OSS_DB_PASSWORD=#{fetch(:oss_db_password)} \
        --env OSS_DB_URL=#{fetch(:oss_db_url)} \
        -P --name slidehub#{prefix} ryuzee/slidehub
EOS
      container_id = capture("sudo #{cmd}")
      port = capture("sudo docker port #{container_id} 3000").to_s.split(':')[1]
      puts port
      # confirm running
      cmd = "curl -LI http://127.0.0.1:#{port} -o /dev/null -w '%{http_code}\\n' -s"
      cnt = 0
      loop do
        execute "echo 'sleep 20 sec...'"
        sleep 20
        cnt += 1
        if cnt == 10
          error = Exception.new('An error that should abort and rollback deployment')
          raise error
        end
        begin
          container_status = capture(cmd).to_i
          if container_status == 200
            break
          end
        rescue Exception => e
          puts e.inspect
        end
      end
      data = { port: port }
      template 'nginx_default.erb', '/tmp/default', data, true
      execute 'sudo mv /tmp/default /etc/nginx/sites-available/default && sudo service nginx reload'

      containers = capture('sudo docker ps -q').to_s.split("\n")
      containers.shift
      containers.shift
      puts containers.inspect
      containers.each do |c|
        execute "sudo docker rm -f #{c}"
      end
    end
  end
end

なお、この方式には問題があって、サーバが再起動してしまった時の考慮や新しくサーバを増やしたときの考慮ができていません。次はそれを検討しましょう。

再起動などにも対応するために起動処理をホスト側に移す

上の例には問題があるのは説明したとおりで、再起動した場合や新たにサーバを追加した場合を考慮しましょう。 そうすると、以下のような方式に変わります。

  • そのコンテナを動かすサーバにはあらかじめ環境変数を設定しておく。Ubuntuであれば/etc/environmentあたりに設定する。もしくはetcdとかを使っても良い(が小規模環境だと面倒)。
  • AWSを使っているならAmazon EC2の起動時にUser-data経由で環境変数を設定してもOK
  • コンテナを動かすためのスクリプトをサーバ側にインストールしておく。これをサーバの起動時に動かしたり、デプロイ時にCapistrano3経由でキックする。

これを踏まえて以下のような実装になります。

コンテナを動かすためのスクリプト

  • 今回はbashで作っているが別にPerlでもPythonでもなんでもホスト側に入っている言語ならOK
  • コンテナを動かすホスト側に配置する。場所はどこでも良い(たとえば、/usr/local/bin/run_app.sh)
  • このスクリプト自体の配置をCapistrano3のセットアップタスクで配置してもOK。
  • スクリプトでは引数にコンテナのタグを指定できるようにしておくとロールバックしやすい
#!/bin/bash

. /etc/environment

TAG='latest'
if [ -z $1 ]; then
  /usr/bin/docker pull ryuzee/slidehub:$TAG
else
  TAG=$1
  /usr/bin/docker pull ryuzee/slidehub:$TAG
  if [ $? -ne 0 ]; then
    TAG='latest'
    /usr/bin/docker pull ryuzee/slidehub:$TAG
  fi
fi

PREFIX=`date +%Y%m%d%H%M%s`

CONTAINER_ID=`/usr/bin/docker run -d \
        --env OSS_DB_NAME=$OSS_DB_NAME \
        --env OSS_DB_USERNAME=$OSS_DB_USERNAME \
        --env OSS_DB_PASSWORD=$OSS_DB_PASSWORD \
        --env OSS_DB_URL=$OSS_DB_URL \
-P --name slidehub$PREFIX ryuzee/slidehub:$TAG`

echo "Container ID is $CONTAINER_ID"

PORT_STR=`docker port $CONTAINER_ID 3000`
IFS=':'
set -- $PORT_STR
PORT=$2
echo "Listening port is $PORT"

CNT=0
while :
do
  echo 'Confirming the container is listening...'
  CNT=`expr $CNT + 1`
  if [ $CNT -gt 20 ]
  then
    echo 'Failed to launch container...'
    exit 1
  fi
  STATUS_CODE=`curl -LI http://127.0.0.1:${PORT} -o /dev/null -w '%{http_code}\\n' -s`
  echo $STATUS_CODE
  if [ $STATUS_CODE == 200 ]
  then
    break
  fi
  sleep 20
done

sed -i -e "s/proxy_pass http:\/\/127\.0\.0\.1:[0-9]*;/proxy_pass http:\/\/127.0.0.1:${PORT};/g" /etc/nginx/sites-enabled/default
service nginx reload

IFS=$'\n'
CONTAINERS=(`sudo docker ps -qa --filter "name=slidehub[0-9]{22,}"`)
n=0
for line in "${CONTAINERS[@]}"; do
  n=`expr $n + 1`
  if test $n -gt 2 ; then
    echo "Deleting old container $line..."
    docker rm -f "$line"
  fi
done

exit 0

起動スクリプトの設定

上記のスクリプトがサーバの起動時にも実行されるようにしておきます。 以下の例は、UbuntuのUpstartを使った例です。 /etc/initの直下に適当な名前(拡張子が.confである必要はあります)で以下のようなファイルを配置します。 なお、respawnが設定されていると、何度でも実行してしまうので、respawnは指定しないでください。

description "SlideHub container"
author "Ryuzee"
start on filesystem and started docker
stop on runlevel [!2345]
script
  bash /usr/local/bin/run_app.sh
end script

Capistranoからデプロイの実行

ここまでやれば、Capistrano3からのデプロイが簡単になります。タスクは以下のようになるでしょう。

set :image_version, ENV['VERSION'] || "latest"
# (中略)
desc 'deploy'
task :deploy do
  on roles(:container) do
    execute "sudo bash /usr/local/bin/run_app.sh #{fetch(:image_version)}"
  end
end

バージョン(というかタグ)を指定してデプロイしたければ、以下のような形で指定すればOKです。

VERSION=1.0.0 bundle exec cap production container:deploy

なお、より詳しいソースは、こちらのレポジトリを見ていただくと良いと思います。

それでは。

発売のお知らせ:カンバン仕事術――チームではじめる見える化と改善

$
0
0

こんにちは。@ryuzeeです。

既にご存知の方も多いと思いますが、来週3月26日に「カンバン仕事術――チームではじめる見える化と改善」(Marcus Hammarberg、Joakim Sunden著、原田 騎郎、安井 力、吉羽 龍太郎、角 征典、高木 正弘 訳)が発売になりますのでお知らせです。

本書の特徴は以下のとおりです。

  • 364ページと少々厚めの本ではありますが、第1章は物語形式でカンバンに登場する要素の多くを例を交えて説明しているのでカンバンを知らない人にもとっつきやすい
  • はじめて取り組む人のための学習用のリソース(コンセプトを体験するゲーム)から、実際にカンバンを運用した際の話まで含まれており、どこからでも読めるようになっている
  • 原著者の1人Joakimは、海外で有名な音楽サービスのSpotifyでアジャイルコーチをしており、実際の現場での知見に基づく内容になっている(ちなみにSpotifyには「塹壕よりスクラムとXP」「リーン開発の現場 カンバンによる大規模プロジェクトの運営」の作者であるHenrik Kniberg氏もコーチとして関わっており、多くの場所でSpotifyの事例が聞かれるようになってきている)
  • 別に開発チームにしか適用できないわけではまったくなく、どの仕事にも適用可能

なお、いまスクラムを使っているのでカンバンができない!ということはありません(併用できる)し、カンバンは現在の自分たちのやり方を起点としてスタートするので、「いきなりアジャイルなやり方に変えなきゃいけないので無理」、ということもありません。 自分たちのやり方を見える化してもっと良くしていくためのやり方だと理解するといいと思います(見えないものは改善できない!!)。

目次

第I部 カンバンの学習

  • 1章 チーム「カンバネロス」のはじまり
    • 1.1 イントロダクション
    • 1.2 ボード
    • 1.3 ワークフローのマッピング
    • 1.4 作業項目
    • 1.5 コイン渡し
    • 1.6 仕掛り作業
    • 1.7 特急項目
    • 1.8 メトリクス
    • 1.9 見送り
    • 1.10 まとめ

第II部 カンバンの理解

  • 2章 カンバンの原則

    • 2.1 カンバンの原則
    • 2.2 すぐに始める
    • 2.3 まとめ
  • 3章 作業の見える化

    • 3.1 ポリシーの明示
    • 3.2 カンバンボード
    • 3.3 キュー
    • 3.4 まとめ
  • 4章 作業項目

    • 4.1 カードの設計原則
    • 4.2 作業項目カード
    • 4.3 作業の種類
    • 4.4 進捗インジケーター
    • 4.5 作業項目のサイズ
    • 4.6 ワークフローデータの収集
    • 4.7 自分の作業項目カードを作る
    • 4.8 まとめ
  • 5章 仕掛り作業

    • 5.1 仕掛り作業(WIP)を理解する
    • 5.2 WIPが多すぎるときの影響
    • 5.3 まとめ
  • 6章 WIP制限

    • 6.1 WIP制限の見つけ方
    • 6.2 WIP制限を決定する原則
    • 6.3 ボード全体とチーム全体のアプローチ
    • 6.4 列ごとのWIP制限
    • 6.5 人に対してWIP制限をかける
    • 6.6 よくある質問
    • 6.7 エクササイズ:WIP制限だ、徹底的にWIP制限だ
    • 6.8 まとめ
  • 7章 流れの管理

    • 7.1 なぜ流れなのか?
    • 7.2 作業の流れを促す
    • 7.3 デイリースタンドアップ
    • 7.4 次に何をすべきか?
    • 7.5 ボトルネックの管理
    • 7.6 まとめ

第III部 カンバンの応用

  • 8章 サービスクラス

    • 8.1 緊急の場合
    • 8.2 サービスクラスとは何か?
    • 8.3 サービスクラスの管理
    • 8.4 エクササイズ:分類しよう!
    • 8.5 まとめ
  • 9章 計画づくりと見積り

    • 9.1 計画づくりのスケジュール:いつ計画を立てるべきか?
    • 9.2 作業の見積り:相対的に伝える
    • 9.3 見積りのテクニック
    • 9.4 ケイデンス
    • 9.5 カンバン流の計画づくり:苦労は小さく、利益は大きく
    • 9.6 まとめ
  • 10章 プロセスの改善

    • 10.1 ふりかえり
    • 10.2 根本原因分析 10.3 カンバンのカタ
    • 10.4 まとめ
  • 11章 改善のガイドとなるメトリクスの使用

    • 11.1 よく使うメトリクス
    • 11.2 強力な2つの見える化
    • 11.3 改善ガイドとしてのメトリクス
    • 11.4 エクササイズ:計測会議
    • 11.5 まとめ
  • 12章 カンバンの落とし穴

    • 12.1 仕事ばかりで遊ばない ジャックは今に気が狂う
    • 12.2 タイムボックスは役に立つ
    • 12.3 時には変革が必要
    • 12.4 怠け者の言い訳にするな
    • 12.5 まとめ
  • 13章 ゲームで教えるカンバン

    • 13.1 コイン渡し
    • 13.2 ナンバーマルチタスクゲーム
    • 13.3 ドットゲーム
    • 13.4 ボトルネックゲーム
    • 13.5 getKanban
    • 13.6 カンバンピザゲーム
    • 13.7 まとめ
  • 付録A 推奨図書とその他のリソース

  • 付録B カンバンのツール


デイリースクラムのTIPS (2016年版)

$
0
0

こんにちは。@ryuzeeです。

いろいろな現場を支援させて頂いている中でもっともよく頂く質問の1つに、デイリースクラムに関するものがあります。たとえば、「時間どおりに終わらない」「単にみんなが報告するだけで、これならメールでいいんじゃないか」とか「いつも、困っていることは特にない、とみんなが言う」とかです。

そこで、デイリースクラムの運用の仕方を改めて整理しておくことにします。

なぜデイリースクラムが必要なのか

  • 「プロセスで決められているから」というだけの理解ではいけない。これは全てのイベントに共通する
  • 毎日の検査と適応(高速なフィードバックサイクル)をすることで問題を早期に発見する
  • ムダな待ち時間を排除する
  • 自分たちがスプリントゴールとしてコミットしたことに対して全力を尽くす

タイムボックス

  • タイムボックスは厳密に15分を守る。超えてはいけない
  • 早めに終わること自体は構わないが、その場合はチームとして問題がないと言い切れるかどうかを自問する
  • 必ずしも朝やらないといけない訳ではない。全員が参加できる妥当な時間帯にすること
  • 常に時間通りに始める。日によって時間や場所を変えない
  • 遅刻した人がいても待たない
  • みんなにデイリースクラムの開催について絶対はっきり伝えること。たとえばデイリースクラムの時間や場所を貼りだす。その他のミーティングでもそうして良い
  • チームの中にはスプリントカレンダーを掲示しているところもある
  • チャットのボットなどを使って開始前に自動でアナウンスするのも良くある手

事前準備

  • 前回のデイリースクラムから変わった点についてしゃべれるように準備しておくこと。チームにとって価値がある共有すべき情報は何なのか?について考えること
  • 終わったタスクも大事だが、自分が解決した妨害や今もっている問題点はチームにとってさらに重要なのでそれを説明する。また自分がスプリントゴールを守れると感じるかどうかも伝える
  • デイリースクラムの前にタスクのステータスと残り時間を更新すること。これによってチームがどんな状況にあるかに関する最新のスナップショットを持っていることを保証する。デイリースクラム中に、ボードの更新に時間を使うべきではない
  • スクラムマスターはプロジェクトを妨害していることの一覧を用意して更新しておくこと。これには前日までのデイリースクラムで出た話も含まれる
  • デイリースクラムの3つの質問を見える場所に貼っておくと良い。みんながそれを意識するようになる
  • 3つの質問はあくまでフォーマットなので自分たちで拡張してよい

ファシリテーション・進行

  • スクラムマスターはファシリテーションに集中する。管理ではないし、やるべきことを命令するわけでもない
  • チームメンバー全員が話すこと
  • 場を安全に保つこと。悪いニュースをためらわずに言えるようにしないと単なる儀式になる
  • デイリースクラムで問題解決をしないこと。進捗や問題を明らかにすることに専念すること。より細かい話は終了後などに必要な人で続きをすればよい
  • スクラムマスターは関係なさそうな話になったり深入りしすぎた話になったりしたら話を止めること
  • チームメンバー自身がお互いに突っ込んで止めさせるのも良い。例えば2人挙手ルールを使っているチームもある
  • プロダクトオーナーや管理者等がデイリースクラムに口出ししてきたら止めること
  • 出席できないメンバーがいる場合は、居るメンバーが代わりをする。代わりをするメンバーはチームに対して欠席しているメンバーの状況を伝えること(事前準備すること)
  • あまりに欠席が多い場合は妨害事項なので理由を確認して解決すること
  • デイリースクラム中は集中すること。携帯をいじくったりしない
  • みんながスクラムマスターに向かって報告してくるようならチームの会議であることを何度でも意識付ける(スクラムマスターが立つ場所も注意)
  • うまくできるようになってきたら、ファシリテーターは日替わりでメンバーがやってもよい

見える化を進めるために

  • 仕掛り中の仕事を少なく保つこと。本当に進行中の作業のみを仕掛り中とすること(じゃないと昨日も今日も同じタスクしている、という発言がでてくる)
  • 進捗を明らかにするために大きいタスクは分割すること。タスクが大きい見積もり時間を持っていると進捗を把握することが難しい(これも昨日も今日も同じタスクをしている、という話になりがち)

その他

  • 他のメンバーとの会話をデイリースクラム待たないといけないわけではないので、困ったことがあれば随時あげてよい
  • チームのサイズが大きすぎるとその分デイリースクラムでの1人あたりの時間が短くなる。本当にそのサイズが適当なのかをもう一度考える

繰り返し言っておきますが、デイリースクラムをやることに意味があるのではなく、そこから得られるものに意味があるので、それを忘れないよう。

カンバンに関するサービス提供のお知らせ(4月1日)

$
0
0

こんにちは。@ryuzeeです。このたび2016年4月1日にカンバンに関するトレーニングサービス(KANBAN BOOT CAMP)とコンサルティングサービスを開始したのでお知らせいたします。

カンバンとは?

みなさんご存知の通り、カンバンとは宣伝や広告等のために使われる、木・プラスチック・金属等を使った板状の物体です。 近年では低コスト・高耐久性のあるアルミ複合板が広く使用されているようです。 主に屋外に使用されるものを指すことが多くなってますが、広義では室名の札やディスプレイ用のパネルなど屋内で使用されるものもカンバンと見なします。 さらに広義では、見るものに対し、何らかの情報を伝えるための表示物と捉える場合もあります。(以上Wikipediaを元に加筆修正)

カンバンの例

カンバンに関する現状の課題

一方で、現状ではみなさま以下のような課題を持っているようです。

  • 作ったのは良いが、設置場所が悪く誰も見てくれない
  • 作ったのは良いが、誰もメンテナンスするつもりもなく予算もなかったため、何文字か欠落してしまい、お客様ではなくカンバン作成業者ばかり連絡してくる
  • 見てはくれるが、そもそも伝えたいことが伝わらない
  • 他社と代わり映えがなく、自社のことを覚えてくれない
  • ダサすぎてセンスを疑われ、こんなものを出している会社とは付き合いたくないと言われた
  • 目立つように奇抜なカンバンを作ったが、同じビルの下の階の店に吸い込まれてしまう

ダメなカンバンを作らないようカンバン作成に関わるステークホルダー向けの集合トレーニングおよび、このような問題を解決するためのコンサルティングサービスを提供いたします。 なお、当方のコンサルティングでは、本質的な課題を深掘りし、カンバンから派生してビジネスそのものまでアドバイスをいたします。

サービスに関する費用

本サービスでは、他のサービスと同様に従量課金型でご提供いたします。ご利用に際して長期間のコミットは一切必要ありません。 1回あたり(2時間)の費用は以下の計算式となります。

カンバンの縦のサイズ(cm) * カンバンの横のサイズ(cm) * カンバンの厚さ(cm) * 利用色数

(1cm未満は切り上げとなります)

たとえば、縦1m * 横50cm * 厚さ3cm * 色数4色の場合は、以下のようになります。なお消費税等が別途発生いたします。

100 * 50 * 3 * 4 = 60,000円

その他補足

ソフトウェア開発におけるカンバンについても取り扱いいたします。詳細はこちらのページをご参照ください。

また参考書籍として以下が発売になっております。

「カンバン仕事術――チームではじめる見える化と改善」(Marcus Hammarberg、Joakim Sunden著、原田 騎郎、安井 力、吉羽 龍太郎、角 征典、高木 正弘 訳) 本書の特徴は以下のとおりです。

  • 364ページと少々厚めの本ではありますが、第1章は物語形式でカンバンに登場する要素の多くを例を交えて説明しているのでカンバンを知らない人にもとっつきやすい
  • はじめて取り組む人のための学習用のリソース(コンセプトを体験するゲーム)から、実際にカンバンを運用した際の話まで含まれており、どこからでも読めるようになっている
  • 原著者の1人Joakimは、海外で有名な音楽サービスのSpotifyでアジャイルコーチをしており、実際の現場での知見に基づく内容になっている(ちなみにSpotifyには「塹壕よりスクラムとXP」「リーン開発の現場 カンバンによる大規模プロジェクトの運営」の作者であるHenrik Kniberg氏もコーチとして関わっており、多くの場所でSpotifyの事例が聞かれるようになってきている)
  • 別に開発チームにしか適用できないわけではまったくなく、どの仕事にも適用可能

その他ご不明な点がありましたら、問い合わせフォームよりお気軽にご連絡ください。

【資料公開】Chef ベーシックトレーニング

$
0
0

みなさんこんにちは。@ryuzeeです。

これから新たにChefを学ぶ人向けに非常に基本的なトレーニングの資料を作ったので公開します。

資料の構成は以下のとおりです。

  • まずDevOpsの文脈から自動化が必要な背景を説明
  • Infrastructure as Codeについての利点を説明
  • Chefのアーキテクチャ
  • Chefの用語解説
  • Vagrantで仮想マシンを2台使った一番単純なハンズオン(boxも用意済み)
  • Serverspecを使ったCookbookのテストの書き方(VirtualBoxの仮想マシンの中でDockerを使っています)
  • その他

なお、2-3時間でさくっと触りながら全体像を掴むことを目的にしているので、網羅性はありません。

ハンズオン用のVagrantのboxには、あらかじめ、Chef DK(Development Kit)、Dockerなどが含まれており、すぐに触れると思います(ただしboxのサイズは2つで2GBあります)


何かお気づきの点があれば是非フィードバックをお寄せください。

発売のお知らせ:アジャイルコーチの道具箱 - 見える化の実例集

$
0
0

こんにちは。@ryuzeeです。

既にご存知の方も多いと思いますが、昨日4月13日に新刊の「アジャイルコーチの道具箱 - 見える化の実例集」(Jimmy Janlén著、原田騎郎・吉羽龍太郎・川口恭伸・高江洲睦・佐藤竜也訳)が発売になりましたのでお知らせです。なお、こちらの書籍はLeanpubでの電子書籍(PDF)形式のみでの販売です。

本書の内容と特徴は以下のとおりです。

  • 本書は、仕事の見える化を進める際に役立つ方法を図と説明つきで96パターン紹介する書籍です。全体で124ページで、各パターンを1ページあたり1つずつ紹介しています。通読するというよりはパラパラと見ながら気になるパターンを探すとよいと思います
  • 「見えないものは改善できない」というのがカンバンなどの見える化に共通する基本的な考えです。そして見えるようにした後の課題として、どうやってもっと分かりやすくするか、どうやって毎日維持するように定着させるか、という点が出てきます。ちゃんと取り組んでいくとどんどんボードの形は変化していくので、その時のネタになるでしょう
  • 見える化はソフトウェア開発に限った話ではなく、どんな仕事でも使える技術です。タイトルにアジャイルコーチの道具箱、とありますが、非エンジニアの方でも使えます
  • 原著者のJimmy Janlenは、Henrik Kniberg(「塹壕よりスクラムとXP」、「リーン開発の現場 カンバンによる大規模プロジェクトの運営」の著者でSpotifyのアジャイル導入を支援しているコンサルタント)も所属するCrispのアジャイルコーチ。本書の見える化の実例もCrispでの多くの事例が反映されています
  • より詳細にカンバンや見える化の仕掛けや概念を知りたい場合は、カンバン仕事術 - チームではじめる見える化と改善をあわせて参照すると良いでしょう

本書で説明している見える化の方法

本書で説明している見える化の方法を以下に記載しておきます。内容は本を読んでのお楽しみ。

  • 共通のアドバイス
  • ワークフローを見える化しよう
  • コツ
  • ポリシーを見える化しよう
  • アバター
  • 凡例
  • インボックス
  • 大小2つのアバター
  • デモまで何日
  • 自信のニコニコマーク
  • 自信のニコニコマーク(続き)
  • レーンの優先順位
  • 進めないよメモ
  • 依存性のクモ
  • ドット付け
  • 欠勤カレンダー
  • 浪費した日数
  • 期日とチェックボックス
  • 疑問アイコン
  • 緊急レーンとポリシー
  • 爆弾アイコン
  • パーキングロット
  • 成果ポスター
  • カイゼンボード
  • 帽子つきメンバーカタログ
  • チームの習慣
  • ファシリテーターの帽子をまわす
  • 割り込みバケツ
  • デイリースタンドアップタイマー
  • 時間どおりのスタンドアップ + ハイスコア
  • 状態つきパーキングロット
  • 進捗円グラフ
  • 床の上の境界線
  • 二次会
  • チームのリズム/サイクル
  • 改善レーン
  • 今日のヘッドライン
  • 遅刻はケーキ
  • タスクサイズは1日以下
  • タスクには結果を書く
  • ポータブルボード
  • フローグラス
  • 賞賛の壁
  • タスクプログレスバー
  • 今日のすごい人サイコロ
  • ストーリーポイント定規
  • ごほうびビン
  • リリースクレジット
  • タスク見積りボックス
  • WIPの制限
  • スキルごとの色分け
  • 個人用パーキングロット
  • ペアプロマトリクス
  • デイリースタンドアップのルーティン
  • 一時停止アイコン
  • ストレスレベルのメーター
  • 改善のテーマ
  • テストサーバのアイコンと情報
  • デモの準備
  • チームのムードメーター
  • ニュースレター
  • 不在表
  • 探索的テストの車輪
  • 付箋紙入れの封筒
  • スペースで制限
  • ふりかえり用の郵便受け
  • スプリント情報
  • レビューのキュー
  • チームの在席時間
  • 備品置き場
  • 失敗ボード
  • ピラミッドバックログ
  • チューブ投票
  • ペアリングマップ
  • チームのタイムゾーン時計
  • アクティビティ・サイコロ
  • レース場
  • 完成のチェック用紙
  • 内部キューの見える化
  • 今日のスゴ技
  • サーバントマネージャーのドア
  • 砂時計型スクラムボード
  • ラバランプ
  • 到着と出発のタイムライン
  • 私の学びたいこと
  • ユーザーストーリーの色分け凡例
  • ガラス壁に色をつける
  • コンセプトキューブ
  • 今日のゴール
  • ペルソナごとのオポチュニティ
  • 投資時間
  • リリーストレイン時刻表
  • 案内板
  • デイリーチェックイン
  • クリスマスツリー
  • 規律の記録
  • 依存ボード
  • おまけ: Post-it のめくり方

どうやって翻訳を進めたのか

ここから先は余談ですが、いつもどおり、GitHub上で、マークダウンを使って翻訳を進めています。以下がその様子です。 おおよその翻訳は最初の2日で集中的に実施し、その上で2か月ちかく日本語面での見直しや言い回しの改善に時間を使っています。

表記揺れなどの検出やマークダウンからのファイル生成は自動化してあるため、翻訳者は翻訳作業に集中できるようになっています(これはどの書籍でもだいたい同じようなやり方をしています)。

なお本日時点でコミット回数が624回、GitHubのIssueの数が290個(もちろんCloseしたものを含む)という感じでした。

ということで…

カンバン仕事術ともどもお楽しみいただき、ぜひ感想などをお寄せいただければ幸いです。

物理カンバンを作るときに用意しておきたい道具10選

$
0
0

みなさんこんにちは。@ryuzeeです。 今日は物理カンバンを作るときに役立つ道具を10種類紹介します。なお、実際のカンバンの例については、拙訳:アジャイルコーチの道具箱 – 見える化の実例集も参照してみてください。

1. 付箋紙 (3Mの強粘着を強く推奨)

当然のことながら物理カンバンを作るときに一番よく使うのが付箋紙です。 剥がれてなくなってしまってはまずいので強粘着を使うようにしてください。おすすめは当然のことながら3Mのものです。 なお、余談ですが、3Mの付箋紙だけを「ポスト・イット」と呼びます。 サイズは自分のボードに合わせれば良いのですが、よく使われるのは、75ミリの正方形です。 なお、パックで買うと色々な色が含まれていますが、勿体無いからといって全色を闇雲に使わないようにします。 一般的には通常のタスクを黄色に、緊急をピンクに、といった形で色別に用途を定義しておいてください。でないと遠くから見た時に何が起こっているのか判断が付きません。

2. サインペン

当たり前すぎて何を言ってるんだ?という感じがするかもしれません。しかし付箋に文字を書くときに、自分が持っているボールペン等で書いてしまっているのをよくみかけます。普通のボールペンで書いてしまうと遠くから見にくいという問題があるので、適切な太さのサインペンを用意しておきましょう。なおカンバンを見やすくするには、丁寧に書かなければいけないのは言うまでもありません(僕は字が下手なので綺麗に書けと言われると困ってしまうので、現場でアドバイスするときは丁寧に書くように言いましょう)

3. マグネット用紙

金属製のホワイトボードにくっつけられるマグネットもおすすめの道具。 例えば、見える化事例集の中で「人別のアバター」を作る例を紹介していますが、プリンターを使ってそれぞれのメンバーの顔写真をこのマグネットに印刷すればすぐに出来上がります。 その他に自分が見た例としては、カンバンのレーンを頻繁に見直すチームでは、区切り線用に細いマグネットを用意しておいて、それを使ってレーンを表現している例もありました。テープでレーンを作るよりも手間なく作れますのでおすすめの利用方法です。

4. カラーラベル(ドットシール) 9mm

いわゆる普通のドットシール。5mmだと小さすぎ、15mmだと大きすぎるので、9mmぐらいが付箋紙と組み合わせて使うには使いやすい。 使いみちとしては、カンバンのDoingに滞留している日数をドットシールで示す、とか、差し戻しがあったタスクに印をつけておくとか、なんらかの投票をする時に使うとかとか便利です。 赤と青の2種類くらいあるといいと思います。これは付箋紙ほど品質に違いがないので、100円ショップで買ってもよいと思います。

5. 貼れるホワイトボード

カンバンをいろいろなチームが使うようになると、ホワイトボードの取り合いになりますし、いろんな場所にホワイドボードを作りたくなります。 そしてこれを壁に貼り付ければすぐホワイドボードができあがります。 模造紙だとどうしても気軽に書いたり消したりできなくて、形骸化しやすかったりしますが、これを使えばその心配はありません。 ワークショップをやったりするときにも役にたちます。 1点課題としては、静電気で壁にくっつけるので特に湿気の多い時期などは剥がれたりするのと、意外と綺麗に貼るのが難しい点です。前者については割りきってテープや画鋲で貼り付けてしまうのが良いでしょう。 またこれ自体にはマグネットはくっつかないので注意が必要です。

6. マスキングテープ

先ほどカンバンのレーンを区切るのにマグネットが使えると書きましたが、ある程度レーンが固まってくればテープを使ってレーンを作るのが良いと思います。 100円均一で売っている細い(5mmくらい?)テープは粘着度がひくすぎるのと貼るのが意外に難しいので、粘着力の強いものを使うと良いでしょう。

7. 粘着ゴム

3Mの強粘着の付箋紙を使っていても、何度かくっつけたり剥がしたりしているうちに粘着力がおちてふとした拍子に剥がれ落ちてしまったりするかもしれません(書きなおしてもいいんですが)。またその他のモノをカンバンに貼りたいことがあるかもしれません。そういうときに使えるのが粘着ゴムです。ちょっとちぎって剥がれそうな付箋紙の裏に貼ればもう落ちることはありません。なお、磁石がくっつくタイプのボードを使っている場合は小型のマグネットを沢山用意しておくことで代替できます。

8. キッチンタイマー

別に料理をするわけではないのですが、カンバンを使いはじめると、全員ボードの前で朝会や夕会をやったり、ボードの前で計画作りをしたりする機会が徐々に増えていきます(逆にせっかくボードを作っているのにほとんど見に来る人がいない、ボードの前にいる時間が著しく短いといった場合はボードが機能していません)。朝会・夕会・計画作りなどのイベントの際は、タイムボックスを設定して長くなり過ぎないように制御する必要がありますが、その際にキッチンタイマーはすごく役立ちます。いちいちスマホのタイマー機能を使うよりよほど早いです

9. 定規(1m)

カンバンにレーンの線を引いたりテープを貼ったりするときに、よく使います。 手書きだと頑張ってもなかなか綺麗にならないのですが、定規を使えば解決です。1000円くらいなものなので持ってて損はないです。 なお、間違っても朝会に遅刻して来た人のお仕置きの道具などに使ってはいけません。

10. 道具入れ

小学生みたいな話なのですが、カンバンのそばには常にここまで説明したような備品を道具箱に入れて使えるようにしておくことを強く勧めます。例えばカンバンを見てたら急にタスクを思い出したときに、すぐ新たなタスクを付箋に書いたりしたくなります。その時に自席に戻って付箋とペンを探して...などとしていたら時間が無駄です。また常に同じ場所に整理整頓しておいておけば、備品が不足しそうになったときに簡単に調達をかけることもできます。わざわざ買わなくてもクッキーとか海苔の空き缶なんかで大丈夫です

それではよいカンバンライフを!!

物理カンバンの写真10枚(+勝手レビュー)

$
0
0

みなさんこんにちは。@ryuzeeです。

他の会社やチームで使っているカンバンを見れる機会はそう多くはないのではないでしょうか?

そこでFlickrでCreative Commonsライセンスで投稿されているカンバンの写真のうち、役立ちそうなものを10枚紹介します。 それぞれ写真に対して、写真から読み取れる良い点や改善点をコメントとしてつけましたので、自分のカンバンを改善する役にもたつと思います。

その1

  • https://www.flickr.com/photos/drewm/369319379/
  • こんなに沢山の項目が貼られると制御しきれないので、タイムボックス等を導入して、定期的にクリアする
  • レーンは線で区切った方が良い
  • 付箋が反り返っている。正しい剥がし方をしていないかも
  • 付箋の色の違いに意味をつける

その2

  • https://www.flickr.com/photos/dinomite/3219513356/
  • Todo / Doing / Doneだけでなく、Ready for QA(QA待ち)等があるのは良い。もしここに積み上がるようならボトルネックはQAになる
  • 但しQA待ちのレーンが広すぎるかもしれない。この広さが埋まるのはマズイので領域を物理的に狭めてよい
  • これもDoneは掃除しても良さそう
  • これも付箋が反り返っている
  • 付箋の色の違いに意味をつける

その3

  • https://www.flickr.com/photos/jimdowning/6129928164/
  • 各レーンの箇所に書いてある数字はWIPの制限だと思われる。この制限を超えて着手したいと思うようであればプロセスを改善するチャンス
  • 真ん中の上下に分かれたレーンは、流れという観点だと分かりにくい。全てが左から右に流れる方が視覚的に捉えやすい

その4

  • https://www.flickr.com/photos/chrishuffman/2336990347/
  • 左からReady / Requirements / UI Design / Tech Design / Tech Implementation / UI Implementation / Test Design / Test Implementation / Production Readyというレーンになっており、流れが分かりやすい
  • ボード左上に凡例が貼ってあり、付箋の色ごとに内容が違うのが分かるのも良い
  • 下の破線以下の用途が分からないが緊急割り込みタスク用であれば一番上の方が良いかも

その5

  • https://visualhunt.com/f/photo/4269796623/a727bbd9d8/
  • これはスクラム用のボード
  • 一番左がストーリーを指しているが、たぶん別管理しているものを印刷して付箋に貼っている様子。こういうのもアリ。ただ遠くからだと見えにくいのであまり細かくし過ぎない
  • チームの人数にもよるが、IN Progressのものが多すぎる可能性はあり。ストーリーが優先順位で並んでいるなら上から片付く方策を検討しても良い(ストーリーを人単位に割り当てている懸念もある)
  • IN Progressの幅が狭いのはGood
  • 付箋の貼り方はもう少し丁寧な方がいいのでは?

その6

  • https://www.flickr.com/photos/sethandalexa/8425381749/
  • いわゆるTodo(Planned)の手前(左)にアイデアカラムがあるのは良い。ここに色々なアイデアを載せておいて優先順位付けや計画が終わったら隣に移す感じになる。また封筒はボツネタ入れ?こういうのもGood。あとで分析できる
  • 左端のボード外にあるのは凡例だと思われる。これは良い
  • 一方でPlannedの箇所で貼り方がランダムで優先順位が分かりにくい。右隣に近い側の上から並べると優先順位が伝わりやすい

その7

  • https://www.flickr.com/photos/pip/4967680686/
  • Todoの次にReadyカラムがある点はGood。ここの項目が少なすぎる場合は分析や調整系のタスクがもっと必要というサインにもなる
  • 一方でこのボードは整然としすぎており、本当に運用されているのか・みんな見てメンテナンスしているのかという懸念もある。綺麗すぎるカンバンは要注意

その8

  • https://www.flickr.com/photos/edublogger/24408672399/
  • 実用的かどうかは完全に脇においておくとしてこういうボードも面白いかもしれない
  • どちらかというとタスクの見える化よりもチームのワーキングアグリーメントやゴール、メンバーの状況なんかを見えるようにする道具として使うといいのではないか

その9

  • https://www.flickr.com/photos/49942291@N06/6271930071/
  • ボードを作るのに、マスキングテープでレーンを作っている。簡単にできるのでおすすめ
  • 一方でボードは縦長よりも横長の方が取り回しが良いと思われる
  • 朝会やミーティングのときにボードをそのまま持っていけるか、ボードの前でミーティングできるようにしておくと良い

その10

  • https://www.flickr.com/photos/49942291@N06/6271930071/
  • ボードを作るのに、壁にコルクボードを貼って実現している例。付箋はくっつかないので画鋲を使ってとめることになる。剥がれにくいので選択肢としてはアリ
  • インデックスカードを多用しているようなケースだと便利かも

それでは良いカンバンライフを!!

エンジニアのキャリアパスに思うところ

$
0
0

みなさんこんにちは。@ryuzeeです。

世の中ではエンジニアが35歳すぎたらどうする?といった話が最近話題になっていますが、ちょっとエンジニアのキャリアパスについて考えてみました。

なんで35歳が転機だと言われているのか

以下は従来の会社にありがちなキャリアパスの構造です。 最初はアソシエイト(見習い)エンジニアとしてキャリアをスタートして、その後エンジニアとして自分で仕事を進められるようになります。 そしてエンジニアとして優秀で、昇進するという場合に、エンジニア職をやめて管理職にロールチェンジするしかないというものです。 35歳くらいがそのタイミングだ、ということですね。

レベルエンジニア系(個人型)管理職系
5 ディレクター
4 シニアマネージャー
3(シニアエンジニア)マネージャー
2エンジニア 
1アソシエイトエンジニア 

ここでのよくある問題点としては以下のようなものが挙げられます。

  • 組織の制度設計的に、ロールチェンジしないと給与があがらない仕組みになっていることが多い(エンジニアとして活動してもらえる給与の上限が低い)
  • 生活スタイルや家族観については多様な考え方があるとはいえ、一般的には35歳〜50歳くらいまでの間はお金がかかりやすい。そして途中で生活のために今までやっていたことを望むかどうかに関係なく捨てないといけなくなることがある(踏み絵を踏まされる)
  • 優秀なエンジニアがロールチェンジを望まず、かといって行き止まりのキャリアパスを見ると、その組織に長くいる理由がなくなる。従って優秀な人で技術キャリアを追求したい人ほど組織から流出する
  • ロールチェンジを受け入れるにしても、そもそもエンジニア職と管理職系のスキルは延長線上にあるわけではなく、管理職としてのキャリアを再度1から積み上げないといけない
  • そしてエンジニアとして技術的に優秀だった人が、ピープルマネジメントや数字の扱い、戦略的な話についてうまくできる保証がまったくない

あるべき組織内のキャリアパス

上記のような組織内のキャリアパスの問題を解決するには、エンジニアのキャリアパスを行き止まりにしないことです。 管理職として上にいくだけでなく、エンジニアとして上にいくパスも定義して、本人の志向でどちらを選ぶのかを決められるのが望ましいと思います。

レベルエンジニア系(個人型)管理職系
5チーフエンジニアディレクター
4プリンシパルエンジニアシニアマネージャー
3シニアエンジニアマネージャー
2エンジニア 
1アソシエイトエンジニア 

このとき意識しておくべき点は以下のようなことになります。

  • エンジニアを貫くか管理職系にいくかは本人の志向によって決める
  • エンジニアから管理職になったが、やはりまたエンジニアに戻るという選択肢もある
  • ロールチェンジするときには十分な教育が必要(これは従来型のパスだろうと同じだが)
  • 自分が管理職だった場合に、自分よりもレベルが上のエンジニアが管理対象になることがある(部下の方が給与が高いことも当然ある)
  • 要はそれぞれのロールが違って責任が違うだけなので、上司なので偉いとかそういう話ではない
  • エンジニアは多くの場合、技術が分かっていない人から技術的な指示をされることに抵抗感を持つ。すなわち技術的な点の意思決定については現場やチーフエンジニアやプリンシパルエンジニアといった上級のエンジニアに委譲した方がよい
  • 年功序列ではなくて、各レベルで定められたJob Descriptionに合致しているかどうかが次のレベルにいけるかの判断基準になる

個人としての選択

個人としてどう選択するかは、完全に個人の問題であり個人の責任です。

正常性バイアス

心理学によく出てくる話に認知バイアスというものがあり、そのうちの1つに正常性バイアスがあります。正常性バイアスとは

自分にとって都合の悪い情報を無視したり、過小評価したりしてしまう人の特性のこと。自分にとって何らかの被害が予想される状況下にあっても、都合の悪い情報を無視したり、「自分は大丈夫」「今回は大丈夫」「まだ大丈夫」などと過小評価したりしてしまい、逃げ遅れの原因となる(Wikipediaより加筆修正)

というものです。すなわち周りはそんなことを考えていないから大丈夫だ、という話でもなく、会社もそれなりに大きな規模なので大丈夫という話でもありません。 会社の平均存続期間よりも自分が働く期間の方が長いので、どこかで職場を変える・キャリアの方向性を変えるといったことが必要になってきます。

とはいえ、正常性バイアスとは、こういうことをいっても「自分には関係ないし〜」とか「大げさすぎだろ!」みたいな反応を示すことでもあるので痛い目を見るまでは分からないかもしれません。それも含めて自己責任ということです。

社外で通用するようにしておくことの安全性

どこかで職場を変えたりキャリアの方向性を変えることをしないといけないとすると、その可能性を広げるためには社外で通用するようにしておくことです。 技術でも管理でも構わないですが、自分の「社内価値」ではなく「市場価値」を上げる方向で自分のキャリアを考えていくのが安全です。

そう考えた時に言えそうなこととしては以下のようなことが挙げられるでしょう。

  • レガシー環境の中で枯れた(いまさら新規に誰も使わない)技術にロックインされ続けるのは避ける(一方でCOBOLくらいになるとまた別の市場価値がある)
  • マイナーな商用製品にロックインされ続けるのも同様で、オープンスタンダードを知っておくようにした方がいい
  • 業務知識があるから大丈夫、というのはちょっと待った方がよい。業務知識はお客さんの方が詳しい。業界としての知識は役にたつが、独自に作りこみまくった特定のお客さんの業務を知っていたからといって、その業務がずっと継続するのかも分からないし、そもそもそのお客さんが存続し続けるかも分からない
  • 自己申告評価より他人からどう評価されるかが重要なので、目に見えるアウトプットがあるといい(オープンソース、ブログ、執筆、登壇いろいろ手はある)。そういったものを定期的に棚卸ししたり、自分のレジュメを定期的に更新していくのも良い
  • 管理職側で生きていくなら、自分の会社の独自のマネジメントのやり方だけでなく、一般に通用する色々な理論やベストプラクティスを身につけること

とはいえ全ては価値観の問題

「仕事はそこそこのお金が貰えればよくて、苦しくても週末の趣味を楽しみにして我慢するからいいや」も勿論ありですし、「勉強するのもめんどくさいしそこまでやらなくてもなんとかなるだろ」でもそれで本人が良いなら構わないでしょう。 ただ、他人は自分に起こることの責任は取れないというだけです。人のせい・環境のせいにしないで済むように自分のキャリアを考えてみるとよいと思います。


【資料公開】カンバンのキホン

$
0
0

みなさんこんにちは。@ryuzeeです。

2016年5月19日にKanban Casual Talksで登壇してきましたので、その際の資料を公開します。

 

内容としては、書籍「カンバン仕事術」の1章を簡単にまとめたものです。社内などで簡単に説明するのに使えると思います。

書籍についてはこちらから購入可能です。

また資料の中で紹介した見える化の実例集についてはこちらをご参照ください。

【続編:64MB超え】AzureのBlobサービスにブラウザから直接ファイルをアップロードする

$
0
0

こんにちは。@ryuzeeです。

前回、AzureのBlobサービスにブラウザから直接ファイルをアップロードするという話を書きましたが、残念ながら前回の実装は、AzureのBlobサービスのPUTの制限で64MBまでしか登録することができませんでした。

そこで今回は、AzureのBlobサービスにブラウザ経由で直接64MB超えのファイルを登録する方法を紹介します。なお、Rubyスクリプトの部分(CORSの設定やSASの取得など)は前回と変更ありませんので、フォームのみの修正です。

細かいところはコードを見てもらうとして、分割アップロードの実装において注意すべき点を列挙します。

  • 64MBを超えるファイルをアップロードしたい場合は、最大4MBのファイルのブロックに分割してPUTします。
  • 分割したブロックをPUTする際には、生成したSASのURLの末尾に、comp=block&blockid=ブロックIDを追加しないといけません。
  • ブロックIDは、base64エンコードされた文字列である必要があります。さらに、複数のブロックがある場合、全てのブロックのブロックIDの文字列長は同じでなければいけません。
  • 全てのブロックを送信し終わったら、ブロックリストをPUTする必要があります。URLは、SASの末尾にcomp=blocklistを追加します。
  • ブロックリストの送信の本文はXMLで、それまでに送信したブロックIDを列挙します。
  • ブロックリストの送信の際は、HTTPヘッダーに、x-ms-versionの設定が必要です。

その他、前回の話も含めて検討した方がよさそうな箇所についても列挙します。

  • SASを使って認証URLを取得しますが、このURLを知っていれば有効期間中は、当該ファイル名での書き込みが出来ます。すなわち認証URLの有効期限はあまり長くしてはいけません。
  • いくらJavaScriptで制御したとしても、ユーザーがSASのURLを知るのは簡単なので、巨大ファイルをPUTされたりしそう。
  • アップロード画面で任意のファイル名をユーザーの指定でアップロードできるようにするのは上書きなどの問題がありそう。SASを発行する際のファイル名はシステム側で作成することを推奨。

ちなみに実行するとこんな感じですね。

以上、誰得かまったく分からない話でした…

views/form.erb

<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>

<div>
  <form id="upload-form" method="post" enctype="multipart/form-data">
    <div>
      <label class="control-label col-sm-2"><span>File</span></label>
      <div>
        <input type="file" name="file" id="file" />
    </div>
  </div>
</div>

<span id="progress">0%</span>

<script type="text/javascript">
function segmentation(arrayBuffer, segmentSize)
{
    var segments = [];
    var fi = 0;
    while(fi * segmentSize < arrayBuffer.byteLength){
        segments.push(arrayBuffer.slice(fi * segmentSize, (fi + 1) * segmentSize));
        ++fi;
    }
    return segments;
}

$(document).ready(function() {
  var send_block_list = function(sas_base_url, block_prefix, block_num) {
    var defer = $.Deferred();
    var put_to = sas_base_url + '&comp=blocklist';
    var body = '<?xml version="1.0" encoding="utf-8"?><BlockList>';
    for (var i = 0; i < block_num; i++) {
      body += '<Latest>' + base64encode(block_id(block_prefix, i)) + '</Latest>';
    }
    body += '</BlockList>';
    $.ajax({
      type:   'PUT',
      url:    put_to,
      headers: {
        'x-ms-version': '2015-04-05',
        'x-ms-blob-content-type': 'application/octet-stream'
      },
      data:   body,
      async: true,
      cache: false,
      contentType: false,
      processData: false
    }).done(function(data, textStatus, jqXHR) {
      defer.notify().resolve(data);
    }).fail(function( jqXHR, textStatus, errorThrown) {
      var msg = 'ブロックリストの送信に失敗しました...';
      console.log(msg);
      defer.reject(msg)
    });
    return defer.promise();
  };

  var get_sas_base_url = function(filename) {
    var form = new FormData();
    form.append('filename', filename);
    var defer = $.Deferred();
    $.ajax({
      url: '/sas',
      type: 'POST',
      contentType: 'application/octet-stream',
      data: form,
      async: true,
      crossDomain: true,
      cache: false,
      contentType: false,
      processData: false
    }).done(function(data) {
      data = data.url;
      defer.resolve(data);
    }).fail(function(jqXHR, textStatus, errorThrown) {
      var msg = 'SASのURL取得に失敗しました...';
      console.log(msg);
      defer.reject(msg)
    });
    return defer.promise();
  };

  var send_block = function(sas_base_url, block_prefix, block_index, block) {
    var defer = $.Deferred();
    var url = sas_base_url + '&comp=block&blockid=' + base64encode(block_id(block_prefix, block_index));
    $.ajax({
      url: url,
      type: 'PUT',
      contentType: 'application/octet-stream',
      data: block,
      async: true,
      crossDomain: true,
      cache: false,
      contentType: false,
      processData: false
    }).done(function(data, textStatus, jqXHR) {
      var msg = 'Azureへのデータ送信に成功しました...';
      console.log(msg);
      defer.notify().resolve(data);
    }).fail(function(jqXHR, textStatus, errorThrown) {
      var msg = 'Azureへのデータ送信に失敗しました...';
      console.log(msg);
      defer.reject(msg)
    });
    return defer.promise();
  };

  function block_id(block_prefix, index) {
    return block_prefix + ("0000000" + index).substr(-7,7);
  }

  function base64encode(str) {
    return window.btoa(str);
  }

  $('#file').on("change", function(event) {
    var file = this.files[0];
    if(file != null) {
      console.log("アップロードファイル名は" + file.name + "です...");
    } else {
      return;
    }
    event.preventDefault();

    var block_prefix = 'BlockId';
    var reader = new FileReader();

    reader.onload = (function(file) {
      return function(evt) {
        buffer = reader.result;
        var segments = segmentation(buffer, 4 * 1024 * 1024);

        $.when(get_sas_base_url(file.name))
        .fail(function(msg) {
          $("#progress").html(msg);
        })
        .done(function(url){
          var functions = [];
          $.each(segments,function(i, val) {
            functions.push(send_block(url, block_prefix, i, val));
          });

          // See http://stackoverflow.com/questions/26066198/jquery-when-progress-for-array-of-deferred-and-or-promise
          $.whenWithProgress = function(arrayOfPromises) {
            var cntr = 0, defer = $.Deferred();
            for (var i = 0; i < arrayOfPromises.length; i++) {
              arrayOfPromises[i].done(function() {
                defer.notify(++cntr, arrayOfPromises.length);
              });
            }
            jQuery.when.apply(jQuery, arrayOfPromises).done(function() {
              defer.resolveWith(null, arguments);
            });
            return defer.promise();
          };

          $.whenWithProgress(functions)
          .progress(function(cnt, total) {
            $("#progress").html(Math.round(cnt * 100 / total) + "%");
          })
          .fail(function(msg) {
            $("#progress").html(msg);
          })
          .done(function() {
            console.log('全てのブロックのアップロード完了...');
            $.when(send_block_list(url, block_prefix, segments.length))
            .fail(function(msg) {
              $("#progress").html(msg);
            })
            .done(function(){
              console.log('ブロックリストの登録完了...');
              $("#progress").html('100%');
            });
          });
        });
      };
    })(file);

    reader.onloadstart = function(e) {
      $("#progress").html('0%');
    };
    reader.readAsArrayBuffer(file);
  });
});
</script>
</body>
</html>

Amazon Elasticsearch Serviceを使ったログ収集基盤の構成を考えてみた

$
0
0

みなさんこんにちは。@ryuzeeです。 6月10日にAmazon Web Services企業導入ガイドブックが発売になっていますのでよろしくお願いします。

さて今回はAWS上でログ収集と分析をする際に、Amazon Elasticsearch Serviceを使う前提とした場合だとどのような構成案がありそうかいくつか考えてみたのでご紹介します。

なお、検討の材料にしている全体の構成としては、複数のVPC(またはAWSアカウント)があって、さらにオンプレ側とDirect ConnectやInternet VPNで接続しているような、よくあるそれなりの規模の構成になります。 各VPCの中には複数のサブネットがあり、そのうちのいくつかはプライベートサブネットに分かれているものとします(個人的にはインターネットゲートウェイの有無しか違いがないので、プライベートサブネットあまり作りたくない)。

構成パターン1:FluentdのAggregatorを利用する

定番のログ集約用のAggregatorを用意する例。Amazon Elasticsearch ServiceはVPC外のサービスでアクセスするには、インターネットに繋がらないといけない(実際にはAWSの中で折り返し)ので、Aggregatorはパブリックサブネットに配置します。それぞれのVPCやアカウントとはVPCピアリングで直結し、AggregatorまではローカルIPでアクセスする形になります。 Aggregator側からAmazon Elasticsearch Serviceにデータを登録するためには、fluent-plugin-aws-elasticsearch-serviceプラグインを利用します。 またここで併せてログをS3に保存したければ、fluent-plugin-s3を併用すれば良いでしょう。

この構成のメリットは以下のとおりです。

  • Aggregatorまでの転送は定番なやり方で、既存のやり方と大きく変える必要がない
  • Elasticsearch側へのデータ反映までのタイムラグが少ない
  • ネットワーク的な経路を迷う必要がない
  • データ加工を色々やりたければ、ProcessorのノードをEC2で作ることもでき、このあたりも今までと変わらない

一方で以下の点については考慮が必要です。

  • Aggregatorの可用性。複数台で構成することもできるが、EC2のインスタンスを自前で運用しないといけない
  • EC2の料金のインパクト

構成パターン2:S3 + Lambdaを利用する

こちらの例はEC2を使わずに実現する例。各サーバのFluentdでfluent-plugin-s3を利用し、指定したS3のバケットにログをためていきます。プライベートサブネットからの場合はプロキシを通します。 Lamdaでは、S3のバケットにファイルが作られたら、そのイベントをトリガーにしてLambda Functionを起動し、データをElasticsearch側に登録します。詳細なやり方についてはAWSのサイトでも紹介されています。

この構成のメリットは以下のとおりです。

  • EC2がないので運用としては楽
  • データ加工をしたい場合もLambdaである程度自由にできる
  • S3のライフサイクルポリシーを使えば古いログを自動でアーカイブできる

一方で考慮点としては以下が挙げられます。

  • 一旦S3にファイルを出力し、それをトリガーにするのでデータ反映までのタイムラグは多少増える(flush_intervalの設定による)
  • flush_intervalを短く設定すると、大量のファイルがS3上に作られてしまう
  • Lambdaのデバッグはちょっと面倒

構成パターン3: Kinesis Streamsを利用する

Kinesis Streamsは、次々と送られてくる大量のデータをリアルタイムで集めてくれる土管のようなサービスです(Kinesis Firehoseは同じような感じでS3などにファイルを溜めてくれるサービスですが東京にはまだありません)。 各サーバからKinesis Streamsにログを送り続けて、そのデータをKinesis App(EC2上に実装)やLambdaを使って順番に処理していく形になります。 なお、fluentdのプラグインは、fluent-plugin-kinesisとなり、こちらもプロキシに対応しているので、プライベートサブネットからの場合はプロキシ経由にします。

この構成のメリットは以下のとおりです。

  • Kinesis Streams自体が大量データのリアルタイム収集を目的として作られている
  • 従って構成パターン2に比べてタイムラグを減らせる
  • データの処理にLambdaを使う場合は、EC2が不要になり運用は楽

一方で考慮点としては以下が挙げられます。

  • Kinesis AppをEC2上に作ると運用がめんどくさい
  • Lambdaで作った場合は構成パターン2と同様にデバッグがちょっと面倒
  • S3にも保存しておきたい場合は、別のやり方と併用する必要がある

まとめ

さてどれがいいでしょうねぇ…

WEB+DB PRESS Vol.93に「実践見積り」という話を書いた

$
0
0

みなさんこんにちは。@ryuzeeです。 WEB+DB PRESS Vol.93で、原田騎郎(@haradakiro)さんと一緒に、「実践見積り」という話を書きましたので是非読んでくださいませ。発売は今週6月24日(金)です。

内容は以下のような感じです。
  • そもそも見積りとは何なのか?見積りという単語の持つ意味とコンテキスト依存性、見積りがどう扱われるかが与える影響は何か?概算見積りと確定見積りの違いとは?
  • 見積りの不確実性と納期の確率分布
  • 見積りにおける認知バイアスの影響。そのバイアスを避けるためいどうしたら良いか?
  • 絶対見積りと相対見積りとは何か?
  • 見積り誤差はなぜ生まれるのか?
  • 過小見積りと過大見積りはどのような影響を及ぼすのか?。見積り精度はどのように上げるのか?
  • スコープ・費用・納期・品質の関係
  • ウォーターフォール型プロジェクトではどのように見積もるのか
  • アジャイル型プロジェクトではどのように見積もるのか
  • 見積りに関する最新動向
限られた紙面なので、細かいところまで網羅的に解説するまでは至っていませんが、見積りをする上で知っておくべき基礎的な知識、ウォーターフォールとアジャイルにおける見積りの考え方の違いなどについては把握いただけるのではないかと思います。


余談ですが、執筆の見積りは本当に難しいです。最初から書くべきおおよそのページ数・納期・要求されるクオリティが決っている中で、スケジュールを見積もって進めていったのですが全くその通りには進んでいません。(下のコミットグラフを参照すると分かります…。学生症候群気味です) 当然進捗のリスクがあるわけですが、そのとき担当編集者の人がどうしていたかというと、筆者に伝えるスケジュールとは別にさらにバッファを持っていたのです。初校や校正のスケジュールだけでなく、本当に今日印刷所に出さないとマズイという日にちにまでバッファを積んでるのには驚きました…。

ということで質問や感想などありましたら是非気軽にお寄せください。ご購入はこちらから。

オープンソースの全文検索エンジンFessを試してみた

$
0
0

みなさんこんにちは。@ryuzeeです。

このWebサイト、昔はWordpressを使っていたのですが、本体やプラグインのメンテナンスを頻繁にやらなきゃいけなくて面倒なのと性能面などで辛くなって、その後Ruby製の静的サイトジェネレータであるMiddlemanに変更し、その後ビルドの遅さに耐えられなくなってGo言語で作られているHugoに置き換わっています。

静的コンテンツになればAmazon S3などで運用できるので非常に楽なのですが、一方でサイト内を検索したい場合は別の解決策の用意が必要になります。Googleの検索を埋め込んでももちろん良いのですが、調査の一環として、今回はオープンソースの全文検索エンジンFessを試してみました。

Fessの特徴

公式サイトで詳細に紹介されていますが、主な特徴として以下のようなものが挙げられます。

  • 5分で簡単に構築可能
  • Apache ライセンス
  • Java 1.8を利用しておりOSには依存しない
  • Web以外に共有フォルダなどもクロールできる
  • クローリングの設定など必要な設定はすべてGUI上でできる
  • Elasticsearchを検索エンジンとして利用している(内包しているので別途インストールの必要はない)

インストール

Java 1.8以上の環境であれば環境を選ばないということですが、使い慣れているUbuntu 14.04に構築します。なお、Ubuntu16系であればデフォルトでopenjdk-8が利用できるようになっているので多少手順は減ります。

必要なパッケージのインストール

ここでは、Fessを動かすのに必要な、Java 1.8の環境、Proxyとしてapache2、Fessを常時起動しておくためのSupervisorなどをインストールします。

sudo apt-get update
sudo apt-get install -y apt-file
sudo apt-file update
sudo apt-get install -y software-properties-common
sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install -y language-pack-ja
sudo apt-get install -y unzip
sudo apt-get install -y openjdk-8-jdk
sudo apt-get install -y apache2 supervisor
sudo a2enmod proxy proxy_http proxy_html xml2enc
sudo update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java

Fess本体の配置

Fess本体はどこに配置しても構いません。今回の場合は/opt/fessに配置します。

wget https://github.com/codelibs/fess/releases/download/fess-10.1.1/fess-10.1.1.zip
unzip fess-10.1.1.zip
sudo mv fess-10.1.1 /opt/
sudo ln -s /opt/fess-10.1.1 /opt/fess

ここで、/opt/fess/bin/fessを実行すれば起動しますが、一旦先に進めます。

Supervisorの設定

SupervisorでFessをデーモン化します。

/etc/supervisor/conf.d 以下にfess.confなどの名前で以下のような中身のファイルを作成します。なおuserの箇所はfessディレクトリの持ち主にあわせてください。Vagrantの場合は、vagrantで、AWSの場合だとubuntuになるはずです。

[supervisord]
nodaemon=false

[program:fess]
command=/opt/fess/bin/fess
user=vagrant
autostart=true
autorestart=true
stdout_logfile=/var/log/fess.log
redirect_stderr=true

できあがったら、Supervisorを念の為再起動します。

sudo service supervisor restart

これでFessが自動で起動しているはずです。

Apacheの設定

この時点ではFessは8080ポートでListenしているので80ポートでアクセスできるように手前にApacheなどのHTTP Serverをかまします。/etc/apache2/sites-enabled にある 000-default.conf を以下のように修正します。

<VirtualHost *:80>
    #ServerName www.example.com
    DocumentRoot /var/www/html
    ErrorLog ${APACHE_LOG_DIR}/fess_error.log
    CustomLog ${APACHE_LOG_DIR}/fess_access.log combined
    <Location />
        ProxyPass http://localhost:8080/
    </Location>
</VirtualHost>

ここまでできたらApache2を再起動して、ブラウザでアクセスしてみます。なお、現時点ではなにもインデックスされていないので検索してもなんの結果もでません。

インデックス関連の設定

次に管理画面にログインしましょう。右上のリンクからログインします。初期値はユーザーIDとパスワードともにadminです。 ログインが終わったら、いろいろな設定をする前に、左メニューの「ユーザー」の箇所にアクセスしてパスワードを変更しておきます。

そしてクローラの設定をするために、左メニューのクローラからウェブを選択して、新規に設定をおこないます。

設定する項目は順に以下のようになります。

  • 名前:このクロールの設定の名前。自分で区別が付く名前を好きにつけます
  • URL:起点となるURL
  • クロール対象とするURL:起点からページを辿っていくときに対象にするURLを指定
  • クロール対象から除外するURL:タグ別のページなどクロールしなくてもよいページを指定
  • 検索対象とするURL:画面から検索したときに対象とするURLを正規表現で指定
  • 検索対象から除外するURL:検索結果に出さないURLを正規表現で指定
  • 設定パラメータ:クロールする際の設定値を渡せる。たとえば field.xpath.default.content=//*[contains(@class,‘article_body’)] のように指定すると、検索対象を特定の要素に限定できる
  • 深さ:何階層たどっていくかを指定
  • 最大アクセス数:1度のクローリングで何ページまでクロールするかを指定
  • スレッド数:クローラの同時並列数を指定
  • 間隔:1回のページ取得の間にどれくらい待ち時間を入れるか指定
  • ブースト値:クローリングで取得した結果の重み付け。値が大きければ検索結果として上位にでるようになる

ここまで設定が終わったら実行してみます。通常Webクローラは毎日0時に動くことになっていますが、システム→スケジューラの順にたどると、Default Crawlerの設定がありますので、ここで「今すぐ開始」をクリックして実行します。どのくらいの時間がかかるかはクロール対象のページ数などによって変わります。

画面デザインの修正

Fess自体はテンプレートにBootstrapが使われているので、レスポンシブルにも対応していますし、CSSなどを用意すれば簡単にデザインを変更できます。 たとえばヘッダーを変更したければ「システム」→「ページのデザイン」とたどり、「ページファイルの表示」の箇所から「ヘッダー」を選択して編集ボタンをクリックして編集します。

同じように他のパーツを編集したり、ロゴイメージをアップロードして差し替えるといったことが可能です。

ここまでやれば以下のような感じになります。

まとめ

5分で使えるという謳い文句どおりにすぐ使える全文検索でした。オフィスドキュメントやファイルサーバなんかにも対応しているのでWebサイトの検索にかぎらず色々使いどころがありそうです。

Viewing all 197 articles
Browse latest View live