弊社エンジニアが共著で参加した Xamarin の技術書が発売されます

こんにちは、エンジニアの id:ymrl です。

弊社のエンジニアで Microsoft MVP でもある 杉田寿憲 (@toshi0607) が、共著で参加した書籍が発売されるので、その紹介をします。

Extensive Xamarin ─ひろがるXamarinの世界─ というタイトルです。

Extensive Xamarin ─ひろがるXamarinの世界─ Essential Xamarinシリーズ (技術書典シリーズ(NextPublishing))

Extensive Xamarin ─ひろがるXamarinの世界─ Essential Xamarinシリーズ (技術書典シリーズ(NextPublishing))

  • 作者: 榎本温,杉田寿憲,中村充志,平野翼,久米史也,松井幸治
  • 出版社/メーカー: インプレスR&D
  • 発売日: 2018/02/09
  • メディア: Kindle版
  • この商品を含むブログを見る

こちらは 技術書典3 にて販売された Extensive Xamarin の商業出版となります。電子書籍としてもお買い求めいただけます。2月9日発売です。

Microsoft 系プラットフォームと freee

freee は会計と人事労務のクラウドサービスを主に Ruby on Rails による Web サービスとして AWS 上に構築し、提供しています。この説明では、一見すると Microsoft 系プラットフォームとは縁遠く感じられるのではないかと思います。しかし、電子証明書を用いた銀行口座同期e-Tax / eLTAX での電子申告にはデスクトップクライアントが必要であり、クライアントとなる Windows アプリケーションを提供しています。

今回の書籍で、杉田は Xamarin.Mac について書いているのですが、これは先日リリースされた Mac版の所得税確定申告の電子申告 の開発のための情報収集がきっかけとなっています。Mac 版の電子申告クライアントはもちろん、 Windows 版クライアントの開発時のノウハウと資産を活用するために Xamarin.Mac にて開発されました。

というわけで、freee では Windows (Microsoft Platform) アプリケーションエンジニアも募集しています。今回の書籍とあわせてよろしくお願いいたします。

www.wantedly.com

jobs.jobvite.com

早稲田大学で「アルゴリズムとデータ構造」の特別講義を行いました

はじめまして。freee株式会社の浅羽です。普段はSREや情シス等のエンジニアリングマネージャーをしつつ、エンジニアの新卒採用も担当しております。

昨年の話になりますが、機会がありまして早稲田大学の清水先生が担当されている「アルゴリズムとデータ構造」の特別講義を2コマ(12/15, 22)させていただきました。

  • 対象は情報系の大学2年生
    • C言語でアルゴリズムとデータ構造を学習
    • データベースの授業はまだ行われていない
  • 一コマは90分
  • 学生さんはマシンを持参して課題をその場で解くことが可能

資料や練習問題等を公開しましたので、せっかくなのでどういう内容の講義だったかを紹介したいと思います。

講義内容をどうするか?

当然ですが大学の先生が知識や教え方のプロです。1エンジニアがアルゴリズムとデータ構造の講義を普通にやっても、普段の講義のクオリティを超えるのは難しいと思いました。

一方でWebサービスの開発・運営をしている中でどうアルゴリズムとデータ構造が使われているかについては多少はお話できるかなと思い、どう使われているかの実例をベースにお話するほうがよさそうとなり、講義内容を組み立てました。また、なるべくイメージできるように、習得済みのアルゴリズムとデータ構造だけでしっかり理解できるように、なるべく新し概念を入れないようにしました。

とはいえ、RDBMSも少し扱う関係上、どうしても次の年で学ぶ予定のデータベースについて扱う必要があったので、なるべく難しい概念は話さないようにしつつ、まだ講義では習っていない内容については説明しました(したつもりです)。

Webサービスの一般的な要件

freeeのサービスインフラの話というよりは、なるべく一般化してどういう要件が求められているかを最初に説明しました。とはいえ全部を話そうとするとそれだけで一コマ使えそうなボリュームになりそうなので、そのあとにアルゴリズムとデータ構造の話につながるようなトピックだけを選んでお話しました。

  • リクエストに対してレスポンスを速く返したい
  • 何かしらのデータを読み書きすることが多い
  • ユーザが識別されることが多い(ログイン機能等)

アルゴリズムとデータ構造の紹介

全部を紹介するのは当然難しいのと、受講済みの講義内容だけでイメージできるように、以下の3つに絞ってお話をしました。最初はもっと用意しないとまずいかなと思いましたが、演習も含めると十分な量となりました。

  • RDBMSで使われているアルゴリズムのうちの一部
    • B+Tree
    • ソート
    • テーブル結合
    • いかに速くデータを検索・操作するかの一例として紹介
  • KVS
    • セッション管理を例に紹介
    • ユーザの識別の一つの手法として紹介
  • キュー
    • 非同期処理を実現するためのコンポーネントとして紹介
    • レスポンスタイムを早くするために急いで処理しなくても良いものは後回しにするよ、という説明

f:id:y-asaba:20171215161519j:plain

練習問題

テーブル結合を3つのアルゴリズムを実装してもらい、入力データが大きくなった場合にどれくらい実行時間が変わってくるかを計測してもらいました。

Nested Loop Joinに関しては内部表のスキャンはフルスキャンのみ実装している前提なので、実際はB+Treeからのフェッチでそこまで重くならないこともあるよ、とは口頭で補足しつつ、データが数十万件くらいになると大きく実行時間が変わってくるのを体験してもらいました。

GitHubに解答例を置いておきました。 github.com

講義後の感想

学生さんは非常に熱心に聞いてくれていて、授業の最後にアンケートを取らせていただいたのですが、おかげさまで満足していただけたようでホッとしております。幾つかフィードバックをいただいたので、たとえば演習時間をもう少し確保するなどを取り入れて次回の改善に結びつけたいと思っております。

宣伝

freeeでは現在2019年新卒エンジニアを積極採用中です。もし興味ある方はfreeeの新卒採用ページをぜひ御覧ください!

Webpackのビルド時間を短くするための取り組み

メリークリスマス。freeeでエンジニアをやってます @yo_waka です。 この記事は freee Developers Advent Calendar 2017 の25日目です。

Webpack でビルドしてますか?僕は今日もビルドしています。
弊社ではフロントエンドのビルドに Webpack を用いているのですが、サービスの規模が大きくなるとともにビルド時間が長くなってきて困ってきました。

会計freee というサービスのフロントエンドの規模的にはこのような感じです。

  • JSコード行数: 275421行
  • Webpack エントリポイント数: 108

そこそこですね。 煩悩の数だけJSのエントリポイントがあります。
エントリポイントが多い理由は、元々は Rails の標準である Sprockets のみでビルドしていたものを少しずつ移行していったためです。
元々SPAで作られているアプリケーションではないのと、機能依存の処理もかなり大きいため1ファイルにするデメリットも多いため、機能の数だけエントリポイントが存在しています。

こちらが現在のビルド時間です。 f:id:yo_waka:20171225110354p:plain

以前から HappyPack という並列実行ライブラリを使っていました。
HappyPack は loader ごとに並列で実行してくれるのですが、前述の通り babel-loader のトランスパイル対象のJSファイルが多いこと、処理するエントリポイントが多いことが問題になってきたので効果が少なくなっていました。 (多量の loader を使っている環境では高速化の恩恵にあずかれるかもしれません)

どうにかならないかなと Webpack のドキュメントを見ていたら、thread-loaderとcache-loaderを使ってみるとよいとの記述を見つけて試してみたのがこちらの記事になります。

webpack loader の仕組み

Webpack は Loader API という仕組みを提供しており、これに則って loader は作られています。
基本的に loader はコールバックを実行する関数を提供するのみです。

以下は全てのファイルの先頭に「Powered by freee」というコメントを追加するだけの単純な loader です。
content にはビルド対象となるファイルコンテンツが渡ってきます。

module.exports = function(content, map, meta) {
  var callback = this.async();
  someAsyncOperation(content, function(err, result) {
    if (err) return callback(err);
    callback(null, result, map, meta);
  });
}

function someAsyncOperation(content, callback) {
  var comment = '/** Powered by freee. */¥n';
  callback(null, comment + content);
}

この loader を loaders ディレクトリに置いたとして、webpack.config.js の resolveLoader に指定してあげれば使えるようになります。

module.exports = {
  resolveLoader: {
    modules: [
      'node_modules',
      path.resolve(__dirname, 'loaders')
    ]
  }
}

変わった仕組みとして、Pitching loader という仕組みがあります。
これは「pitch」という関数を loader に生やすと、loader の実行前にpitch関数のみがloaderとは逆の順番に実行されるというものです。
通常はuseディレクティブに指定された loader は最後から最初に向かって実行されますが、pitch関数は最初から最後に向かって実行されます。
また、pitch関数で return されると、以降のpitch関数のチェーンおよび loader の実行がスキップされます。

以下は公式ドキュメントにも記載されている処理フローです。

|- a-loader `pitch`
  |- b-loader `pitch`
    |- c-loader `pitch`
      |- requested module is picked up as a dependency
    |- c-loader normal execution
  |- b-loader normal execution
|- a-loader normal execution

pitch関数を実装することでこんな感じにできます。

|- a-loader `pitch`
  |- b-loader `pitch` returns a module
|- a-loader normal execution

以下の loader の処理をスキップしつつ独自にloaderを実行するようなインターセプタを作ることが可能です。
thread-loader はこれを利用して並列に動くワーカにloaderの処理を実行させて高速化を図っています。

thread-loaderの内部実装

以上を踏まえて、thread-loader の実装を見て行きます。

import { getPool } from './workerPools';

function pitch() {
  const options = loaderUtils.getOptions(this) || {};
  const workerPool = getPool(options);
  const callback = this.async();
  workerPool.run({
    loaders: this.loaders.slice(this.loaderIndex + 1).map((l) => {
      return {
        loader: l.path,
        options: l.options,
        ident: l.ident,
      };
    }),

    /** 中略 */
  }, (err, r) => {
    if (r) {
      r.fileDependencies.forEach(d => this.addDependency(d));
      r.contextDependencies.forEach(d => this.addContextDependency(d));
    }
    if (err) {
      callback(err);
      return;
    }
    callback(null, ...r.result);
  });
}

function warmup(options, requires) {
  const workerPool = getPool(options);
  workerPool.warmup(requires);
}

export { pitch, warmup };

pitch関数が実装されていますね。
worker に渡ってくる this.loaders というのは webpack.config.js の useディレクティブで指定された loader 配列です。
各 loader に渡されたオプションをworkerに渡し直しています。

warmup という関数をビルド前に呼ぶことでworkerプロセスを事前に起動することができるようです。
コードを見ると、worker が作られる際に child_process#spawn が呼ばれ、pipe で入出力を繋げられるようになっていました。
README にも書かれていますが、子プロセスの起動オーバーヘッドは数百msになるため、warmup を使った方がよさそうです。

class PoolWorker {
  constructor(options, onJobDone) {
    /** 中略 */

    this.worker = childProcess.spawn(process.execPath, [].concat(options.nodeArgs || []).concat(workerPath, options.parallelJobs), {
      stdio: ['ignore', 1, 2, 'pipe', 'pipe'],
    });
    const [, , , readPipe, writePipe] = this.worker.stdio;
    this.readPipe = readPipe;
    this.writePipe = writePipe;
    this.readNextMessage();
  }

worker ではオプションで渡される parallelJobs(デフォルトは200)の数に従って、useディレクティブに指定された loader (thread-loader 以降に指定されたもの)の処理を並列実行します。 (内部では asyncパッケージの 'async/queue' が利用されています)

cache-loaderの内部実装

cache-loader は1ファイルのシンプルな実装になっています。
ここでもpitch関数が実装されていて、キャッシュヒットすると以降の処理をスキップしてキャッシュされた結果を返す、ここでキャッシュヒットしなかった場合は通常フローで loader 関数が実行され、結果をキャッシュ保存する、というようになっています。

function pitch(remainingRequest, prevRequest, dataInput) {
  /** 中略 */

  const callback = this.async();
  readFn(data.cacheKey, (readErr, cacheData) => {
    if (readErr) {
      callback();
      return;
    }
    if (cacheData.remainingRequest !== remainingRequest) {
      // in case of a hash conflict
      callback();
      return;
    }
    async.each(cacheData.dependencies.concat(cacheData.contextDependencies), (dep, eachCallback) => {
      fs.stat(dep.path, (statErr, stats) => {
        if (statErr) {
          eachCallback(statErr);
          return;
        }
        if (stats.mtime.getTime() !== dep.mtime) {
          eachCallback(true);
          return;
        }
        eachCallback();
      });
    }, (err) => {
      if (err) {
        data.startTime = Date.now();
        callback();
        return;
      }
      cacheData.dependencies.forEach(dep => this.addDependency(dep.path));
      cacheData.contextDependencies.forEach(dep => this.addContextDependency(dep.path));
      callback(null, ...cacheData.result);
    });
  });
}

pitch関数が何をやっているのかを理解すると、loader の実装が追えるようになって楽しいですね!
全然話は変わりますが loader は asyncパッケージがふんだんに使われていてそっちのAPIを見るのに時間がかかってしまいました。。。

結果

thread-loader はファイル単位で並列loader実行してくれること、cache-loader はそれをキャッシュして2回目以降は loader 処理をスキップしてくれることから、freee が抱えているビルドの問題を解消してくれると思われ、導入していく気持ちが高まりました。

というわけで結果です。 f:id:yo_waka:20171225123921p:plain

2回目以降のビルドについて58%ほど高速化されました。

webpack.config.js はこのようにしました。

const threadLoader = require('thread-loader');

const jsWorkerOptions = {
  workers: require('os').cpus().length - 1,
  workerParallelJobs: 50,
  poolTimeout: 2000,
  poolParallelJobs: 50,
  name: 'js-pool'
};
threadLoader.warmup(jsWorkerOptions, [ 'babel-loader', 'babel-preset-es2015', 'eslint-loader' ]);

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          { loader: 'cache-loader' },
          { loader: 'thread-loader', options: jsWorkerOptions },
          { loader: 'babel-loader?cacheDirectory=true' },
          { loader: 'eslint-loader' }
        ],
        exclude: [ /third_party/, /node_modules/, /jst/ ]
      },
  ...
}

webpack-dev-server と組み合わせると、それなりに早くはなったものの、まだ快適かといわれるとギャップはあります。 cache-loader について、ファイルごとのキャッシュになっているため、エントリポイント単位のキャッシュにできないか試してみたところ、今度はI/Oがボトルネックになってしまい、あまり早くならず。
この辺りは実装を工夫すれば解決できそうな気はするので、もう少しやってみて公開したいと思っています。

まとめ1

Webpack は難しい。

まとめ2

freee のサービスは会計、人事労務並びに請求や支払いなど複数の業務ドメインが抱える課題を解決することを目指しています。
誰でも使えるUIを目指すと、可能な項目や作業が増えていくので、それをシンプルにするために複雑なUI実装が必要になり、結果としてフロントエンドがボリューミーになっていきます。
よって、JSのビルド時間は何もしないと増えていくので、ビルド周りの改善は都度都度行なっています。

大規模なフロントエンドを快適に実装するための基盤を作りつつ、ユーザーさんに価値を届けたい方、ぜひ一緒にやりましょう。

jobs.freee.co.jp

@ymrl からスタートした freee Developers Advent Calendar 2017 ですが、今年はこれでおしまいです。
来年もまた、freee の開発チームが取り組んだ新しいチャレンジについてお伝えできればと思います。

CTOが1ヶ月間の育休をとってみた話

こんにちは、freee CTOの横路(@yokoji)です。

この記事は freee Developers Advent Calendar 2017 の24日目です。

私事ながらこの11月にはじめてのこどもが生まれまして、この1ヶ月間は育休をとっているので、今回はまさにいま育休をとって感じていることを書こうと思います。

f:id:yokoji:20171223152032j:plain

育休について

日本では育児介護休業法が定められていて、企業で働く両親が協力して育児休業を取得できる仕組みのガイドラインがあります。

政府主導で男性の育児参加への取り組みも進められており、育休取得率は徐々に上がってきていますが、それでも直近で3.16%と、2020年までに13%という目標にはまだまだ届きそうにないのが現状のようです。

(ちなみに、フィンランドでは男性の育休取得率が8割を超えているそうです)

なぜ育休をとろうと思ったのか

ある調査によると、育休を取らない理由の上位に「職場に育休を取りづらい雰囲気がある」というのがありますが、freeeでは育休を取得することがもはや普通になっていて、社長をはじめ、パパママともに多くのメンバーが育休をとっているという環境があります。

freeeにはすでに育休をとりやすい雰囲気があるという前提ではありますが、わたしの場合、育休をとる決め手となったのは

  • 妻が働いていてすぐに復職予定のため、今後の生活基盤を早めに構築して慣れておきたかったこと
  • こどもの成長は最初の1ヶ月がとんでもなく早いと聞いていたので、その時期をぜひ一緒に過ごしておきたいと思ったこと

ということでした。また、妻からは

  • 育児の協力体制づくりが遅れて産後クライシスが起こることを避けたい

というコメントもありました。

1ヶ月間も会社を休んだことがない自分にとって、いない間の仕事についてや復帰後のキャッチアップコストの心配など、たしかに慣れないことではありましたが、仕事の棚卸のよいタイミングだと思い、はじめは1, 2週間くらいと思っていたところを、あえて1ヶ月間の育休をとることにしました。

育休中の仕事はどうしていたのか

育休前は、育児と並行して少し仕事もしようと思っていましたが、実際は最低限必要なミーティングだけハングアウトで参加し、その他はほとんど仕事をしませんでした。 仕事をしなくても安心して過ごせたのは、頼もしいメンバーたちに支えられていたおかげです。

f:id:yokoji:20171223143515p:plain

また、育休中は大量インプット期間にしようと思い読みたい本リストを作っていましたが、育児をしていると意外とまとまった時間はなくて、半分も消化できませんでした。

個人的には、最初から育児も仕事も全力で両立!というよりは、期間を決めていちど育児にとにかくフォーカスしてみたほうが中長期的には効率がいいかもと思い始めています。

現代においてもお産にはリスクがつきものということを実感したのもありますし、産後の妻は心身ともに想像以上に衰弱しているので、育休中は割り切って母子のサポートを第一に考えるのがよいという感覚です。

育休をとってみてどうだったか

慣れないことだらけで、アクシデントだらけの1ヶ月でした。妻が産後2週間で突然大量出血して生死をさまよい(今でも1万人に1人くらいは出産で死のリスクに直面するようです)1週間入院したり、どんな状況でもこどもは2, 3時間おきにミルクをねだるし、おむつを替えた瞬間におしっこうんちするし、なんで泣いてるのか分からないしで、夫婦でてんやわんやして様々なトラブルを踏み抜きましたが、おかげさまで育児への理解と仕事との両立の自信がつきました。1ヶ月を終えてみて、個人的に育休のなかでやってよかったことを振り返ってみます。

いちど一人で全部やってみること

はじめは意図していませんでしたが、出産から2週間後に妻が救急車で運ばれ1週間入院したため、現場責任者として育児その他全てを取り仕切る機会がありました。 ひととおり一人で育児をやりきることで、育児にオーナーシップと自信がつき、妻からの信頼も得られたように感じています。

CEOの佐々木が実践していた、夫婦で時期をずらして育休をとるというのもよさそうです。

f:id:yokoji:20171223152113j:plain

育休中は、仕事を忘れて育児にフォーカスすること

育児も仕事も全力で!みたいなスーパーマンをはじめから目指すのは、万人にはハードルが高いと実感しています。

ライフイベントに集中する時期があって、仕事に集中する時期があって、というのも全然ありだと思っていて、わたしの場合は期間を決めて 集中してひととおりの育児経験をすることで、自信がついて仕事とうまく両立していくイメージがわきました。

育児生活の中でやらないことを決めること

育児には生活の非効率が正当化されてしまう魔力があると感じました。圧倒的にかわいいので、「子供のためなら」とつい全力で育児や家事などの世話をしたくなってしまいますが、今後の仕事との両立を考えると全てに手間暇をかけるには時間がなさすぎます。育休中に、「本当に手をかけたいところはどこか」を夫婦で決めて、それ以外は文明の利器や制度をつかってうまく仕組み化していくとよさそうです。

具体例としては、

  • 食事は無理して全部つくらない。Uber Eats、区の家事補助や調理代行、ベビーシッターなどを使うことを躊躇わない
  • 洗濯は全自動洗濯乾燥機に任せて、衣類の痛みは気にしない
  • 掃除はルンバに任せて、細かいところは大掃除にまとめてやる。水回りは定期的にアウトソースする
  • 母乳だけにこだわらず、ミルクも使って授乳の負担をフレキシブルに分担できるようにする

といったことです。freeeではキッズラインと提携していて、会社がベビーシッターにかかるお金の半額を補助しているので、活用しない手はありません。

また、スマートスピーカーの真価は「ながら」作業であることを育児中に実感しました。こどもを抱っこしたままタイマー・アラームを設定できたり、テレビのON/OFFや音楽の再生が出来たり、最高でした。

復帰後の働きかた

仕事の内容を大きく変えることはしませんが、こどもをお風呂に入れたいので、基本的には早めに出社して18時すぎには時間厳守で帰る予定です。

freeeでは、家族の状況にあわせて出退勤の時間を調整することはすでにみんな普通にやっていることなので、特に違和感はありません。

まとめ

今回1ヶ月間の育休を取得したことで、自分の仕事の棚卸のよいきっかけにもなり、夫婦の信頼関係も含めた今後の生活基盤を早めに構築できたことがよかったです。自分に合った生活リズムを早めに見つけるために、みなさんもぜひ育休とってみてください。

育休をとるならおすすめしたいのは、

  • 育休中の仕事は他メンバーに任せて、母子サポートを最優先すること
  • 育休中に、ひとりで一通りの育児を全部経験する機会をもつこと
  • 手をかけたいところを明確にして、それ以外はテクノロジーや制度を活用して仕組み化していくこと

ということでした。

その他、freeeでは社内SNS上で育児ノウハウを交換できる部屋や、家族連れの定期的なリアル懇親イベントがあり、先輩パパ・ママにいつでも相談するのもとても心強いです。

f:id:yokoji:20171224133743j:plain

そして、自分の子はとてもかわいいです。自分の子なら泣いていてもかわいいですし、見ていて退屈しません。育休中はぜひこどもとの時間を楽しんでください!

f:id:yokoji:20171223151933j:plain

さて、明日はいよいよ最終日です。先輩パパ兼開発本部長の@yo_wakaが、アドベントカレンダードリブンでコードを書くぞ!と宣言していたので、とても楽しみです。