チーム開発とクソコード


今までパッケージソフトとかWebサービスの開発をしてきた中で、ビジネス上の納期や要求を満たすためにひどいコードを書くっていうのは自分の経験ではあまりなかった気がします。なにかひどいバグがあって、とりあえずのパッチを当てて間に合わす、ということはたまにあるけれど。SIの世界は知りませんよ。


そもそもコードを汚くかけば納期に間に合うということもないし、ビジネス上の近道になるということもない。コードをきれいに書こうが汚く書こうが無理なものは無理。第一汚いコードを意図的に書くというのも意外に難しいということは、普段まあまあきれいなコードを書いている人ならわかってくれるんじゃないかと思います。


仕様変更に設計がついていけてなくておかしいとかならともかく、関数が1000行あるとか、newした瞬間全てが終わるとか、変数のスコープがびっくりするくらい広い、みたいなコードについてははビジネス上の要求ではなく、単にそのコードを書いた人が良いコードの書き方を知らなかったと思うしかありません。


ただそういったコードを書くのが下手くそな人たちが単にくそなのかというとそうでもなく、コードを書くのは下手だけどドメイン知識がすごいとか、DBやSQLなら異常に詳しいとか、その人が製品触るとなぜかバグが見つかるとか、あとはもう技術どうこう関係ないけどいちいち面白いとか、いろいろあります。


コードについて語ろうとすると当然コードを書くのが得意な人が強いので、クソコードを書く人はクソ、良いコードを書ける人間だけ雇え、となりがちだけど、コードを書くのが得意、コードにしか興味がない、みたいな人だけ集まるのも多様性に欠けて危険な感じがします。エンジニアとして、チームの一員としての価値の発揮の仕方は別にコードを書くことに限らないと思うので、コード書く力だけ偏重するのはやめたいです。


というわけでクソコードを解決するためには、ビジネス上の都合とか技術的負債がどうとか議論するのも良いですが、良いチーム作ってコードレビューで元気に殴り合いつつ、コード書くのが得意な人がリードしてコードの品質を底上げしていこうと考えるのがテンションあがって良いと思います。


さて git blame するか。

Scala 版 factory_girl みたいなのを作りました。

https://github.com/tototoshi/seedbed

Scala で factory_girl っぽいものを作りました。*1かなりお手軽な感じになっています。
依存ライブラリは特にありませんのでどのデータベースライブラリを使っていても大丈夫です。
動作は postgresql と h2 でのみ確認してますが、他のデータベースでも動くと思います。


とりあえず snapshot 版を sonatype にあげときました。

resolvers += "Sonatype snapshots" at "http://oss.sonatype.org/content/repositories/snapshots/"

libraryDependencies += Seq(
  "com.github.tototoshi" %% "seedbed" % "0.1.0-SNAPSHOT"
  // and database driver
)

使い方

以下のサンプルコードでなんとなくわかってもらえると思います。
define でテーブルごとのデフォルト値を定義、 create でデータを1件挿入、get/list はデータベースからレコードを取得するのに使用します。


データの取り扱いは割り切って Map[String, Any] でやってしまいます。基本的に型安全とかよりは手軽さを重視しています。動的言語な気分で使ってください。API は factory_girl より phactory (PHP の factory_girl 的なやつ) に近いです。


あくまでテスト用ライブラリなので、メインのプロダクトコードに入れるのはやめてください。

/*
CREATE table beatles (
  id serial primary key,
  first_name varchar(10) not null,
  middle_name varchar(10),
  last_name varchar(10) not null,
  birth_date date not null
);
*/

import seedbed._

// データベースの設定
trait TestDBConfiguration extends Configuration {
  val driver: String = "org.postgresql.Driver"
  val url: String = "jdbc:postgresql://localhost/seedbed_test"
  val user: String = "user"
  val password: String = "password"
}

// Seedbed クラスのインスタンスを生成
val s = new Seedbed with TestDBConfiguration

// デフォルト値の設定
// めんどくさければ s.define("beatles") だけでも OK。DB のメタデータを見てよしなにしてくれます。
s.define("beatles", Map(
  "first_name" -> "John",
  "middle_name" -> "Winston",
  "last_name" -> "Lennon",
  "birth_date" -> new SimpleDateFormat("yyyy-MM-dd").parse("1940-10-09")
))

// データベースへレコードを1件挿入
s.create("beatles")
// デフォルト値を一部書き換えて挿入
s.create("beatles", Map("first_name" -> "Paul"))

// データベースから first_name=Paul なデータを取得
s.get("beatles", Map("first_name" -> "Paul")) //=>Some(Map(first_name -> Paul, ...))
s.get("beatles", Map("first_name" -> "John")) //=>Some(Map(first_name -> John, ...))

// データベースからデータをまとめて取得する
s.list("beatles")

予定、めも

  • association とか sequence の機能はそのうち欲しくなったら実装します。
  • joda-time くらいには依存してもいい気がしている。

*1:skinny には FactoryGirl http://skinny-framework.org/documentation/factory-girl.html があるけれど、skinny-orm 依存なので自分の用途には合わない。

+n month の罠

PHP Advent Calendar 2013 - Qiita の 23 日目です。
strtotime の話をします。

+n month の罠

日時を扱うのは大抵どの言語でも難しいです。PHPでももちろんそう。

PHP で日付の足し算をするのには strtotime を使います。

echo date('Y-m-d', strtotime('2013-01-31 +1 day')) . PHP_EOL; //=> 2013-02-01

strtotime はすごく便利ですが、うかつに月の計算をすると予期しない結果が返ってきます。

echo date('Y-m-d', strtotime('2013-01-31 +1 month')) . PHP_EOL; //=> 2013-03-03
echo date('Y-m-d', strtotime('2013-02-28 +1 month')) . PHP_EOL; //=> 2013-03-28
echo date('Y-m-d', strtotime('2013-03-31 +1 month')) . PHP_EOL; //=> 2013-05-01

1月の1ヶ月後が3月、3月の1ヶ月後が5月になっています。これは一体。。。

nヶ月後っていつなのか

冷静に考えてみると、月の日数って月によって違うし、nヶ月後の日付って人間の意思なしには決められないものなんですね。
1月31日の1ヶ月後はいつなのか、30日後にしたいとき、31日後にしたいとき、2月の月末にしたいとき、いろいろあると思います。

ちゃんと +n month する

この +n month の罠にハマらないために、n月 +1 month が n+1 月となることを期待して +n month するときは月初を起点にすることを心がけましょう。

echo date('Y-m-d', strtotime('2013-01-01 +3 month')) . PHP_EOL; //=> 2013-02-01
echo date('Y-m-d', strtotime('2013-02-01 +3 month')) . PHP_EOL; //=> 2013-03-01
echo date('Y-m-d', strtotime('2013-03-01 +3 month')) . PHP_EOL; //=> 2013-04-01


なぜ、これだとうまくいくのか。PHP は +1 month するとき、現在月の日数を足し算するからです。うるう年とかも考慮されてるのでご安心を。

echo date('Y-m-d', strtotime('2013-01-31 +1 month')) . PHP_EOL; //=> 2013-03-03
echo date('Y-m-d', strtotime('2013-02-28 +1 month')) . PHP_EOL; //=> 2013-03-28
echo date('Y-m-d', strtotime('2013-03-31 +1 month')) . PHP_EOL; //=> 2013-05-01
echo date('Y-m-d', strtotime('2013-01-31 +31 day')) . PHP_EOL; //=> 2013-03-03
echo date('Y-m-d', strtotime('2013-02-28 +28 day')) . PHP_EOL; //=> 2013-03-28
echo date('Y-m-d', strtotime('2013-03-31 +31 day')) . PHP_EOL; //=> 2013-05-01


とまあこの件に関しては PHP の仕様がおかしいとはあまり思わないんですが、にしても PHP の日付・時刻周りはひどすぎですね。なんか使いやすいライブラリないのかな。

sbt-native-packager でお手軽に Scala アプリケーションの deb, rpm, zip を作成する

Scala Advent Calendar 13 日目の記事です。


sbt-native-packager の紹介をします。


https://github.com/sbt/sbt-native-packager
http://www.scala-sbt.org/sbt-native-packager/

sbt-native-packager とは

rpm, deb, universal(zip) パッケージが簡単に作れる sbt プラグインです。

サンプルアプリ

実際にいじってみましょう。
Hello というだけの簡単なアプリをパッケージングしてみます。

// src/main/scala/example/Hello.scala
package example

object Hello {
  def main(args: Array[String]): Unit = {
    println("Hello! Scala.")
  }
}

設定

sbt の設定はこんな感じ。特に難しくはないでしょう。

// project/plugins.sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.6.4")
// build.sbt
import com.typesafe.sbt.SbtNativePackager._
import com.typesafe.sbt.packager.Keys._

packageArchetype.java_application

name := "hello"

packageDescription := "hello"

maintainer := "tototoshi"

Debian パッケージを作成する

Debian パッケージは debian:packageBin コマンドで作成できます。
いくつか依存しているコマンドがあるようなので(fakeroot とか)怒られたら適宜インストールしてください。

> debian:packageBin

これで target/hello-0.1.0.deb ができるのでインストールしてみます。

> sudo dpkg -i target/hello-0.1.0.deb
$ hello
Hello! Scala.

めでたし。
rpm は試してないけど rpm:packageBin を使えば出来るそうです。

zip アーカイブを作成する

Mac, Windows 向けには zip アーカイブ(universal パッケージ)で配布します。

> universal:packageBin

このコマンドで target/universal/hello-0.1.0.zip ができます。


展開すると bin, lib というディレクトリができていることがわかります。
./bin/hello は sbt-native-packager が勝手に作ってくれた起動スクリプトです。
./bin/hello.bat もあるので Windows でも使えますね。sbt-start-script は Windows基本的に無視だったので Windows 派はこれだけでうれしいですね。

├── bin
│ ├── hello
│ └── hello.bat
└── lib
    ├── hello.hello-0.1.0.jar
    └── org.scala-lang.scala-library-2.10.2.jar

以上、sbt-native-packager の紹介でした。
sbt-start-script の後継として位置づけされているようなので、sbt-start-script を使っている方は乗り換えを検討してみてください。

アルパカの話をしました @ 怖いScala

怖いScala(http://connpass.com/event/4112/) で自作のブラウザ操作用簡易言語 alpaca の紹介をしてみました。
ブラウザを使った自動テストをしたいけどいろいろしんどいので挫折して、もう手動テストを高速に行うで妥協しよう、となったときの産物です。


http://tototoshi.github.io/slides/kwi-scala-alpaca-life/


作ってみたら最終的に文法がどうみてもcapybaraですな感じになりました。
なのでもう別にcapybaraでいいんじゃないかな!
でもcapybara以上に手軽に使えるので個人的にはかなり重宝しています。


こういうのを作るのもScalaでやるとお手軽で良いですね(いい話)ってことで、よろしくお願いします。