足し算ではなく掛け算で実装する

一人アドベントカレンダー 2016 14 日目 by @takkyuuplayer

1行まとめ

足し算ではなく掛け算で実装する。

概要

昔は足し算的に実装していた。最近は掛け算を意識した実装になったと思う。

足し算的実装

例えばユーザーの新規登録を考えるときに

  1. POSTされたフォームデータをバリデーションして
  2. バリデーションが通れば、それをデータベースに保存する

という流れを思いついく。若かりし頃、流石にコントローラにべた書きすることは無いものの、その流れをそのまま実装していた。

sub controller {
  my $self = shift;
  my $params = $self->request->params;
  my $validator = Validator::Signup->new($params); # Validator::Signup は事前に別途作っておく

  if (not $validator->is_valid) {
    $self->template_values->{errors} = $validator->errors;
    return $self->render('signup-form');
  }

  my $handle = Model->create_handle;
  Model::User->create($handle, $validator->params);

  $self->redirect('signup-complete')
}
package Model::User;

sub create {
  my ($handle, $signup_form_values) = @_;

  $handle->insert(user => {
    user_id => $signup_form_values->{user_id},
    email => $signup_form_values->{email},
    ...
  });
}

といったところだ。

掛け算的実装

それを最近は

  • フォームをバリデーションする機能を持った Validator
  • 新規登録用のデータを受け取って、データを保存する Model

の「独立した」2つの機能を組み合わせれば、後は順番通りに呼び出せば実現できる、と考えるようになった。「独立した」機能ということで、モデルはこう書き換わる。

package Model::User;
use Params::Validate qw(validate SCALAR);

sub create {
  my $handle = shift;

  my %args = validate(@_, {
    user_id => { required = 1, type => SCALAR, }
    email   => { required = 1, type => SCALAR, }
    ....
  })

  $handle->insert(user => \%args);

}

モデルの中に validate が追加されただけで、ほとんど違いが無いが、これにはどういうメリットが有るのだろうか?

更新された Model::User->create には2つ大きなメリットがある。

例えば今後 google だとか Facebook といった 3rd party と連携してアカウント作成をしたくなったとする。その際

  • Model::User->create を再利用できる

というのは、渡すパラメータが $signup_from_values に限定されていないことから容易に分かる。

他にも重要なメリットがある。それは

  • 集中すべき範囲を限定できる

事だ。新規登録時にどの情報を必須として扱いどれがオプショナルなのかは Model::User 内に全て記載されていて、変なデータを渡すとバリデーションエラーで弾かれる。だから 3rd party ログインの実装中は「3rd party およびユーザーから何の情報を受け取るべきか?」に集中できる。

連携する 3rd party が増えれば増えるほど、掛け算で効果が現れてくるだろう。

とはいえ、初回でいきなり独立した機能として実装するのは時間のムダではないのか?

断言しよう。独立した機能として実装する方が短時間で実装できる。モデルの作成中にフォームからこういうハッシュが渡ってくるはずだから、こんな感じで insert すればいいか、などと考える必要はない。ただDBやビジネスの制約上「ユーザー登録にはこのデータは必須で、オプションでこういうデータも受け付ける事が可能」ということだけに注力すればよい。逆にフォームを作るときは、ただ「モデルが受け付け可能と言っているデータ」を受け取ることだけに注力すればいい。

これが足し算的実装方針だと、実装中にモデルやフォームを行ったり来たりするハメになる。後で「やっぱりこのデータも必要だ」とフォームを変更したり、逆に「フォームからこんなデータも渡ってくるらしいぞ」とモデルを変更したりしたくなるからだ。

集中すべき範囲を限定できる分、独立した機能として実装するほうが短時間で終わる。その素晴らしさがあまり実感できない場合は、受験で考えると分かりやすいかもしれない。国数英理社それぞれ1時間ずつのテストと、まとめて5時間のテストどちらが良いだろうか?ある教科がものすごく得意で30分で終わらせられる分他の苦手科目に時間多く割ける!という人は別として、どれもやはり1時間くらいかかるのだとしたら分かれている方がやりやすいという人が多いだろう。まとめて5時間のテストだと英語を解いている最中に突然さっきもう少しで解けそうだった数学の問題が気になってそっちに戻ったり、理科は時間がかかりそうだから先に社会を解こうとしたらそれも結構な論述が必要で、一体どっちから先にやっつけるべきか?と混乱しかねない。

かつての足し算的実装とはなんだったのか?

全てをコントローラにべた書きしている状態に比べ「データのインサートをテストしやすい」という利点はあるものの、本質的にはコントローラにべた書きしているようなものだったと思う。 それは再利用性に乏しく、また実装中は複数の気にすべきことが同時に頭の中に存在しており集中力も半減する。

まとめ

足し算ではなく掛け算で実装する。

次回予告

明日の担当も @takkyuuplayer です。

どこでも同じ環境で開発する

一人アドベントカレンダー 2016 13 日目 by @takkyuuplayer

1行まとめ

Windows, Mac, Linux どこで開発することになっても、同じ環境を作りたい

概要

基本的に開発環境は Vagrant 上の ubuntu 16.04 だが、ホストマシンが Windows になったり Macになったりしている。どんな環境でもさくっと同じ環境を作れるようにしておきたい。

Vagrant 上の Ubuntu 環境構築

dotfiles の管理

.vimrc とか .zshrc とかいわゆる dotfiles は

$ git clone git@github.com:takkyuuplayer/dotfiles.git
$ make

で一通り揃うようにしてある。これ自体は別に Ubuntu に限定せず、 macや共用の開発サーバーでも同様に実行して、どの環境でも同じシェル操作を可能にしてある

Ubuntu 自体の環境構築

Itamae を使って Ubuntu 16.04.1 LTS (Xenial Xerus) の環境構築をしている。最新の Ubuntu LTS を追うようにしており、下位互換性は気にしていない。昔は centos を利用していたが, 標準リポジトリで入るパッケージのバージョンがいちいち古く、最新にするのが面倒なのでそのあたりの更新が早い ubuntu に移行した。

$ sudo apt-get update
$ sudo apt-get install -y ruby2.3 ruby2.3-dev git make
$ sudo gem2.3 i bundler --no-rdoc --no-ri
$ git clone git@github.com:takkyuuplayer/ubuntu-provisioning.git
$ make
$ make vagrant

で一通り揃う。

Macをホストマシンとする場合

Mac で使うパッケージの管理は Homebrew 及び mas-cli を使っており

$ git clone git@github.com:takkyuuplayer/dotfiles.git
$ make mac

で一通り揃う。はずだったのだが、一度 homebrew のバージョンアップをした際にmas-cli の方は壊れたのでやめてしまった。機を見てMac App Store も管理できるよう再構築しようと思う。

$ make brew_dump

で一通りバックアップが取れるようになっている。

Windows をホストマシンとする場合

Chocolatey を用いている。Chocolatey のインストール後、パワーシェルを管理者権限で開き

$ Invoke-WebRequest -Uri "https://raw.githubusercontent.com/takkyuuplayer/chocolatey-recipe/master/packages.config" -OutFile "packages.config"
$ choco install .\packages.config -y

で一通り揃う。バックアップは ChocolateyGUI の export 機能を使っている。

Linux Mint をホストマシンとする場合

こんなの を作っていたが、まだ動くのかは知らない。

まとめ

コマンド1発でプロビジョニングできるようにしておくと楽に元の環境に戻せるので、さくっとPCの初期化してみたり、新しいモノを試したりできる。おすすめだ。

次回予告

明日の担当も @takkyuuplayer です。

ホームページのSSL化

一人アドベントカレンダー 2016 12 日目 by @takkyuuplayer

1行まとめ

HTTPS必須の時代へ備えよ

概要

常時SSL化は必須?!Google ChromeでHTTPだと警告が表示されるようになる | WEB上手 の記事にあるように時代はHTTPSだ。 昔は有料だったSSL証明書も今は Let's Encrypt - Free SSL/TLS Certificates を使って無料で発行できる。そこで http://www.takkyuuplayer.comHTTPS 対応した。

Let's encrypt でHTTPS対応

http://www.takkyuuplayer.com は さくらVPS上のUbuntu 16.04で動いている。nginxをリバースプロキシとしてつかい、 takkyuuplayer/homepageをデプロイしている。

そのサーバー構成はすべて itamae-kitchen/itamae でやっているので Let’s Encrypt の導入も当然 Itamae を用いて行う。Certbotを参考にしつつ、手順をレシピを書くとこうなる。

  1. let's encrypt インストール
  2. let's encryptが使う <DOCUMENT_ROOT>/.wellknown/ を外からアクセス可能にする
  3. www.takkyuuplayer.com での証明書発行
    • certbot のコードをそのまま使うと、 interactive mode となり itamae でプロビジョニングできないので、 Certbot command-line options を参考に interactive にならないようにオプションを追加してある。
    • また証明書が入っている/etc/letsencrypt/live/www.takkyuuplayer.com を nginx から読み込めるよう permission を 755 にした。もしかしたら nginx をオーナーにするとかグループにするほうがセキュリティ上良いのかも。755だとどういうリスクがあるのか分かる人教えてください。
  4. 発行した証明書を用いてHTTPS公開
  5. httpへのアクセスをすべてHTTPSへリダイレクト

これで HTTPS 化は完了だ。

証明書の自動更新

更に cron を用いて証明書の自動更新を行う。やっつけだがこんな感じでやってみた。

  1. SSL証明書の自動更新

色々試したせいか let's encryptの制限に引っかかり、動かせなかったのでちゃんと思った通りになるのかは知らない。2ヶ月後くらいに証明書の期限がどうなっているのか調べてみようと思う。

次回予告

明日の担当も @takkyuuplayer です。

オープンソースは使うだけではなく、中身を読んでどう動いているのかを知るべし

一人アドベントカレンダー 2016 11 日目 by @takkyuuplayer

1行まとめ

オープンソースは使うだけではなく、中身を読んでどう動いているのかを知るべし

中身がどう構成されているかを知らないと、無駄なコードを書いてしまう

例えばユーザーの新規登録機能。それは登録フォーム表示のテンプレートやメール送信の機能など、複数の部品から構成されているはずだ。

f:id:takkyuuplayer:20161211153056p:plain

新規登録の機能自体は中身がどう構成されているかを知らなくても、とりあえず動きはする。 では中身を知らなくてもいいのか?というともちろんそんなことはない。 中身がどういう部品で構成されているかを知らないと、本来再利用できるはずの部品を作り直してしまう。 あるいは、新規機能を開発する際に使うであろう「この機能はどう分割しておけば、それぞれの部品が再利用可能なものになるのか?」という知見が貯まらない。

オープンソースのライブラリを使えるだけで、それを理解した気になっていないか?

オープンソースのドキュメントを見て使い方が分かっただけで、どう実装されているかも知らずにそれを理解した気になっていないだろうか? それでは勿体無い。オープンソースは多くの人の手によりレビュー・改善されており「機能はこう分割して実装しておくといいぜ!」という知見の宝庫だ。 是非ともコードを読んでその知見も含めて吸収し、普段の仕事に活用したい。

次回予告

5日ほどサボってしまいましたが、明日の担当も @takkyuuplayer です。

DSL乱立時代が来るという予測

一人アドベントカレンダー 2016 5 日目 by @takkyuuplayer

1行まとめ

今後主にB2B向けWEBサービスとして、左側にテキストエディタ、右側にプレビューっていうのが流行るのではないかという予測

使いやすいUIよりも、必要な事ができるかどうかが重要

B2B向けのサービスは「多少使いにくくても必要なことができる」事が、B2C向けサービスに比べ重視される傾向にあると思う。 前職でとある勤怠管理・経費申請システムがリプレイスされたのだが、リプレイス後のシステムUIは直感的ではなく使いにくかった。 しかしリプレイス後のシステムは今まで出来なかった事ができるようになり、人事や経理の仕事量を大きく削減したとのことだ。 私も使いにくい使いにくいと思ってはいたが、月に1度くらいしか使わないのにも関わらず、半年もたたないうちに慣れて使えるようになった。

そもそも機能として足りないものはどうしようもないがが、ちょっと使いにくいくらいであれば人は慣れてしまう。 エンジニアがデザイナーの意見も聞かず急いで作った管理画面が、使いづらくも長く生き続けるのはそのためだ。

時代は「PC使えて当たり前」から「プログラミングできて当たり前」に変わる

何年前なのかは定かではないが、とりあえず昔はPC無しで仕事をするのが当たり前だった。 しかし今ではPCを使わずに仕事をするのはあり得ない時代となっている。 高校で情報の授業が必修化されたのは2003年4月以後のことだ。それからたった10数年だ。

最近はPCの使い方はおろか、プログラミングが必修科目化されようとしている。 その流れを見るに「PCは使えて当然」という時代は「プログラミングができて当然」あるいは「入社時は使えなくとも、プログラミングは覚えるべき」に置き換わる。 私生活でプログラミングは必要ないかもしれないが仕事場においてはプログラミングは必須のスキルになるだろう。 ちょうど私生活ではスマホタブレットしか使ってない人が、仕事場においてはPCを使うようにだ。

そこでB2B向けサービスのUIはどうなるか?

私は「左側にテキストエディタ、右側にプレビュー」を推したい。

みたいな感じだ。「必要な事をできるようにする」ための開発にかかる時間は 新しいUIを開発するよりも新しい文法(ルール)用のパーサーを作る方が簡単に思える。

必要とされる価値を早く提供するための手段として「新しい文法」が生み出され、 その文法を使ってもらう競争力の源泉として「直感的な文法」が重視される時代が来ると思う。 無論テキスト編集中はリアルタイムに補完なりエラーチェックをしてくれる必要があるのは言うまでもない。

まとめ

DSL乱立時代、来い

次回予告

明日の担当も @takkyuuplayer です。

素振りをしてから本番コードを書く

一人アドベントカレンダー 2016 4 日目 by @takkyuuplayer

1行まとめ

新しい技術はいきなり本番コードで試そうとせずに、まずはそれだけを使って何か書いてみる

概要

本番コードを書いていて「これ使えば良さそう」と今まで知らなかった新しい技術に手を出したくなる事がある。 そういうときは素振りと称してまずはそれだけを使って何か書いてみて 本当に思った通りに使えるのかどうか確かめよう。

素振り

例えば初めて野球をするとき、まずは素振りをしてバットの振り方を覚えてから打席に立つ。

それと同じでまずは、新しい技術だけを使ってみてその使い方を覚えてから本番コードを書く。 本番コード内で「これ使えば良さそう」と思ったのには何か理由があるはずだ。 素振りでその直感が正しいのか確かめよう。 もしかしたら思っていたような使い方ができないのかもしれないし、より良い使い方が見つかるかもしれない。

本番コード内でいきなり試そうとするのは、射撃に例えるなら「初心者が1km先にある的に当てようと銃を構えている」状態だ。 下手な鉄砲も数撃ちゃ当たるという諺があるように、確かに時間をかければ当たるかもしれない。 当たればまだいい方で、当たる前に弾薬が切れることもある。

外れ続けている間、その理由はよくわからない。銃の撃ち方が悪いのかもしれないし、軌道上の強風によりずれているのかもしれない。 そもそも使おうとしていた銃はライフルではなく拳銃で、1km先には絶対に届かないのかもしれない。 確実に的に当てたいならば、使っている銃をよく理解し十分に近づいてから的を狙う必要がある。

エンジニアの人生は短期決戦ではなく長期戦だ。 長期戦の中で弾薬を切らすことなく正確に的に当て続けるために、素振りを続けよう

次回予告

明日の担当も @takkyuuplayer です。

定時に帰って勉強する

一人アドベントカレンダー 2016 3 日目 by @takkyuuplayer

1行まとめ

残業してる暇があるなら、勉強したほうがいい

概要

学生時代は勉強(インプット)しているだけで褒められたが、社会人はアウトプットしないとだめだ

というような記事がたくさんある。それは正しいとは思うが、残業してまでアウトプットを増やすのは間違っていると思う。 特に学校を卒業したばかりの若手が、先輩の仕事を見て「全然だめだ・・・もっとアウトプット出さないと・・・」 と残業してまで何らかの成果を出そうとしているのは(焦る気持ちは分かるが)残念だ。

その時間をインプットに回して、明日からの生産性を改善させてほしい。その理由を述べる。

若手に求めるのは結果よりも、結果に結びつく成長

企業としてすぐに結果を出せる即戦力が欲しいのであれば、中途採用をしている。経験の浅い若手に求めるのは「成長」である。 仕事中に書くコードの中でなんかよく分からないけど「既存のコードをコピペしたら動いた」「ライブラリなり既存関数なりに、この引数を渡したら動いた」 みたいな部分が絶対にあるはずだ。定時を過ぎたら残業などせずに是非ともそういう部分について「なぜ動いたのか?」をコードを読んで詳しく追求して欲しい。 それは今後の生産性に大きく関わってくる。

毎日2時間残業する人と、毎日2時間勉強する人のアウトプット量の差

生産性 = 1時間あたりに可能なアウトプットの量
成長率 = 1日後の生産性が前日比どれくらいか?

とする。仮に毎日2時間残業する人と勉強する人がいたとして、残業する人の成長率を0.1%(※仕事している以上何らかの成長はしているはずだ) 勉強している人の成長率を0.3%とする。横軸を仕事をした日数、縦軸をアウトプット量にしたグラフは次のようになる。 2年もしないうちに勉強した人のほうがアウトプット量が多くなる

f:id:takkyuuplayer:20161204013150p:plain

無論、結果に結びつく勉強をした場合の話だ。 例えば「センター試験に向けてに勉強している!」と言っている受験生が「今は技術・家庭科を重点的に暗記している」などと言い出すと???となるように 勉強の内容が仕事に関係ないものなら、アウトプットとしては現れてこないだろう。 いろいろ勉強したくなるかもしれないが、若いうちは業務に関係ある勉強をしたほうがいい。 分からなかった場合、近くに聞ける先輩エンジニアがいるため吸収力が段違いだからだ。

また勉強すべきだというのは若手に限った話ではない。中堅以降は吸収力の差により若手よりも成長は鈍化するだろうが、それでもいずれ逆転の瞬間が訪れる。 (ひょっとすると年を取った分体力がなくなり、残業中の生産性が著しく低下しており若手より逆転が早いかもしれない)

まとめ

残業してる暇があるなら、勉強したほうがいい。その差は後々生産性の差、アウトプットの差として現れる。

次回予告

明日の担当も @takkyuuplayer です。