VOYAGE GROUP エンジニアブログ

voyagegroup_techのブログ
VOYAGE GROUPエンジニアブログです。

2013年08月

レガシーコードは生き続ける!?

こんにちは、リサーチパネルの佐々木です。

今回は循環的複雑度(Cyclomatic Complexity)を使って、レガシーコードの変遷を追ってみようと思っています。というのも、リサーチパネルで動いているコードはVGの中では古い方(所謂レガシーコードってやつ)で、日々そんなコードと格闘しています。

そこで何か良いものがないかと思ったところで、循環的複雑度(Cyclomatic Complexity)って測定で計ってみようかと思い、あるプロジェクト(社内システム)の過去コードをほじくり返して、その辺を数値を今回計ってみます。(たぶん、どんどん複雑度が増しているんだろうなと思いつつ...)

ソースコードの一部の循環的複雑度は、ソースコード内の線形独立な経路の数である。実際、if文やfor文のような分岐点のないソースコードの場合、その複雑度は 1 であり、そのコードには1つの経路しかない。コードに1つのif文が含まれていれば、コードには2つの経路があることになる。つまり、一方はif文での条件が真となる場合の経路で、もう一方はそれが偽となる場合の経路である。
循環的複雑度 - Wikipedia

ちなみに対象プロジェクトは PHP で書いているので、計測にはphpmd (PHP Mess Detector)を使ってみようと思います(単に使ってみたかった...)。phpmdは、潜在的なバグになりそうなコードを洗い出してくれるツールで、巨大なメソッドや未使用な変数を指摘してくれます。

ということで、今回のその中の循環的複雑度(それ以外も出すことできますが)を出してみたいと思います。なお、数値のレベルとしては、phpmd では以下のようになっています。

循環的複雑度(CC)状態
1-4複雑度が低
5-7複雑度が中程度
8-10複雑度が高
11-複雑度がとても高い

他にも、以下のような考え方もあるので、その辺の数値も使って分類してみたいと思います。(11辺りから少しずつ気を付けた良さそうですね。)

循環的複雑度(CC)状態
1-10シンプルなプログラム。リスク小
11-20やや複雑なプログラム。リスク中
21-50複雑なプログラム。リスク大
51-テスト不可能なプログラム。リスク特大

ただ、デフォルトだと10以上を閾値として結果を出すため、問題のないレベルも出力するようにruleset.xmlを作成し、phpmdの実行結果を集計してみました。(phpmdの詳しい使い方は他に譲ります)

  • ruleset.xml
    <!--?xml version="1.0"?-->
    <ruleset xsi:nonamespaceschemalocation="
                         http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:schemalocation="http://pmd.sf.net/ruleset/1.0.0
                         http://pmd.sf.net/ruleset_xml_schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://pmd.sf.net/ruleset/1.0.0" name="sample PHPMD rule set">
        <rule ref="rulesets/codesize.xml/CyclomaticComplexity">
            <properties>
                <property value="1" name="reportLevel">
            </property></properties>
        </rule>
    </ruleset>
  • 実行コマンド
    phpmd <対象プロジェクトディレクトリ> text ruleset.xml
結果は...

循環的複雑度(Cyclomatic Complexity)の推移

徐々に複雑度の高いプログラムが増えてきているようですね。あと、複雑なものはそのまま残り続けているようです。自分が対象プロジェクトの改修を進めようとする際、どうしても複雑なプログラムは触りたくないし、修正する時も継ぎ接ぎ的な改修になっていた気がします。

こういった複雑なコードは、意識的にリファクタリングをかけていかないと、恐らく残り続けてしまうので、その辺りを改善するには、
  • テストコードを書く
    • 複雑なコードの場合は経路が増えテスト自体が書きづらくなるので、テストをしやすくしようとするとシンプルになっていく。
  • 定常的にチェックする仕組みを作る
    • 「リリース前のコードレビューする」や「CIなどで日々計測・警告をする」といった仕組み作ることで、早めに複雑なコードを除いていく。
といったところでしょうか。これらの対応が良いか検証まではできてないのですが、VG全体としてはこういった流れは強くなっていると感じています(きっと)。

今回phpmdを使って計りましたが、これはコードのある一側面を計測したものですし、全てのプロジェクトで同じ状態になるわけではないでしょうが、コードの質を保つ一指標として見ても良いのかと思っています。

今後もレガシーコードと戯れつつ、モダンなコードへの変遷をどこかで報告できればと思っています!

開発機作業と本番機作業を視覚的に分かるようにする3つの方法

こんにちは、ECナビ事業本部新卒エンジニアの鈴木(とっしー)です。

配属されてから約3ヶ月が過ぎようとしています。時の流れが早いですね...
自分もいーしーきゃんびーの洗礼を受けながらも日々コードを書いています。

先日、先輩とこんな話をしていました。

(回想)
「とっしーのターミナルは、本番機を触っているのか開発機を触っているのか分かりにくいね」
「先輩はどうしてるんですか?」
「開発機はiTerm、本番機はターミナルを使い分けて文字色を変えて視覚的に分かるようにしてるよ」
「なるほど、それいいですね!」
(回想おわり)

確かに、ホスト名をプロンプトに出すようにはしていましたがそれだけだと自分が
どこで作業しているのかを混乱してしまいそうです。


このままでは危険だと思い、自分ももう少しどこで作業してますということを
前面に押し出した方がいいと思い
色々調べてカスタマイズしてみました。

今回カスタマイズしたのは3点
・プロンプト
・vimのステータスライン
・ssh
でitermの背景色変更
では1つずつ紹介していきます。


▼プロンプト
日頃からよく見るプロンプトにもう少しはっきりと「開発機で作業してます」とか「本番機で作業してます」
という情報を見せるようにしました。

開発機の場合はこんな感じです。(緑色だと安心して作業ができますね)

開発機プロンプト

こちらは本番機。 (赤色だと慎重に作業をしなきゃいけないという感じになりますね)

本番機プロンプト

ソースはこんな感じです。
case $HOST in
    'ココに開発機のhostnameを' )
        my_host="開発機"
        my_fg_host=${fg[green]}
        my_bg_host=${bg[reset_color]}
    ;;
    'ココに本番機のhostnameを' )
        my_host='本番機'
        my_fg_host=${fg[red]}
        my_bg_host=${bg[reset_color]}
    ;;
esac

PROMPT="%{$my_bg_host%}%{$my_fg_host%}$my_host%{${reset_color}%}%{${fg[cyan]}%}%n@%m%{${fg[yellow]}%}-> %{${reset_color}%}"

RPROMPT="%1(v|%F{red}%1v%f|)%{${fg[green]}%}[%~]%{${reset_color}%}%{$my_bg_host%}%{$my_fg_host%}$my_host%{${reset_color}%}" 

やっていることは、ホスト名ごとに表示するメッセージと色を変えています。
ホスト名よりは開発機とか本番機とかの方が分かりやすいですね。


▼Vimのステータスライン
(先に、自分のエディタはEmacsなんだけどという方申し訳ありません。)
日頃からよく使用するvim。これを開いているときは、プロンプトの情報を見れないので、

こんな感じで、vimのステータスラインでもプロンプトと同じようなメッセージを出すようにしましょう。


vimステータスライン

ソースはこんな感じ。

if hostname() ==? 'ココに開発機のhostnameを'
    let message='開発機'
elseif hostname() ==? 'ココに本番機のhostnameを'
    let message='本番機'
endif

" ステータスライン表示方法
set statusline=[%{message}]%<[%n]%m%r%h%w%{'['.(&fenc!=''?&fenc:&enc).':'.&ff.']'}%y\ %F%=%l,%c%V%8P\ [%{message}][%{hostname()}]
こちらもプロンプトのときと同じですね。

▼sshで背景色変更
今までは、メッセージを出すだけでしたが、そもそも本番機に繋いだら背景色を真っ赤にするとか
した方がより禍々しい感じがでそうですね。
これをやるためには、ssh-host-color.shを使いました。

本番機ターミナル

より「本番機だぜ!」感が出ましたね。

▼最後に
本番機で作業しているということを意識させるためにあれこれカスタマイズしてきました。
しかしこれはあくまで意識させるためです。一番大事なことはコマンドを打つ場合などは
一旦確認してから
実行することですね。 


ここ2ヶ月で開催された社内勉強会を紹介します。

本日から予想最高気温40度の上海へ出張する@makogaです。

ここのところ勉強会ネタが無かったのでいくつか紹介します。
S1GP20130730_2



















▼GrowthForecastで捗る話
GrowthForecastを使ってログを可視化するといろいろ捗るよねという話。
Fluentdとの相性抜群!

▼RTBにおける最適なフロアプライスとは?
日本最大級のSSPであるFluctのRTBにおいて最適なフロアプライスをどのように決定しているかを実際の数字を使って説明しました。
シンプルなモデルでも良い効果が出るようです。

▼Vagrant, puppet, Fluentdの話
cosmi
で実際に使っている技術の話。
もうVagrantとpuppetなしでは個人環境作ってられないですよね。

▼グローバルアジャイル開発の成功事例から学ぶべきところ
チームの生産性をあげるためのプラクティスが参考になりました。
計測&改善!

▼FANなま for Android/iOSを開発してみて
1人のエンジニアが両OSで開発してみて感じたことを語りました。
iOSはある地点を越えるとぐぐっと成長できるらしいです。
積み上げたものが活かされる感じっていいね!

▼No CloudFormation No AWS
AWSの良いところはプログラマブルなところ。
それを最大限に活かすにはCloudFormationが必須だ!

▼インフラ監視について
監視の仕組みとアラートメールをどう読み解くかについて2年目インフラエンジニアが優しく教えてくれました。
アラートメールが実際に発生したものだったので一部の人たちからは「これは公開xxだ」との声もw


その他にも技術系以外の勉強会がありました。
・非エンジニア向けSQL勉強会
・プロデューサー&ディレクター会
・アプリブレスト会
・アドネットワーク勉強会
・グロースハック勉強会
・砂漠で「砂」を売るための営業術 


知らないことを学ぶって楽しいですね♪

PerlのO/Rマッパ、 Teng の便利な使い方のご紹介

こんにちは、Research Panel Asiaの大山です。 今回は、Perlで最近ホットなO/Rマッパ、 Teng の便利な使い方を共有いたします。

みなさんは、

sqlite> .tables
user_log_2013_03  user_log_2013_04
user_log_2013_05  user_log_2013_06
user_log_2013_07  user_log_2013_08
こんな要領で、定期的にDBにテーブルが作られていくような仕組みを見たことはあるのでしょうか。 最近のMySQLでは著しくパーティショニング機能が改善されていますが、少し前のバージョンを使用していると 制約と制限 の都合で選びにくかったり、テーブルが巨大になるとスキーマの変更が容易ではなくなるなどの事情で、このような構造を選択する開発現場もあるのではないかと思います。

こういった、決まったスキーマの別名テーブルを扱う状況では、O/Rマッパの実装次第ではなかなか面倒なことになります。素SQL+DBIで実装するのも当然有効な選択肢と思いますが、Tengを使うととても簡単に解決することが可能です。

[MyModel.pm]


package MyModel;
use parent qw(Teng);
1;

[MyModel/Schema.pm]


package MyModel::Schema;
use strict;
use warnings;
use Teng::Schema::Declare;

sub get_table {
    my ($self, $table_name) = @_;
    $table_name =~ s/\d{4}\_\d{2}$/xxxx_xx/;
    $self->SUPER::get_table($table_name);
}

sub get_row_class {
    my ($self, $table_name) = @_;
    $table_name =~ s/\d{4}\_\d{2}$/xxxx_xx/;
    $self->SUPER::get_row_class($table_name);
}

table {
    name 'user_log_xxxx_xx';
    pk 'id';
    columns qw( id hoge fuga );
    row_class 'MyModel::Row::UserLog';
};

1;

Teng::Schema の中で、テーブルと行オブジェクトを扱うところの実装がとても単純になっているので、 このように get_table と get_row_class の2つのメソッドをオーバーライドしてあげるだけで、 扱うテーブル名によってちょっとした挙動の変更ができます。

こうすると、


my $handle = MyModel->new({ connect_info => ['dbi:SQLite:']});

my $item1 = $handle->insert(
    user_log_2013_07 => { hoge => 'hoge' }
);
# => MyModel::Row::UserLog インスタンス

my $item2 = $handle->insert(
    user_log_2013_08 => { hoge => 'hoge' }
);
# => MyModel::Row::UserLog インスタンス

$item1->update({ fuga => 'fuga' });
$item2->update({ fuga => 'fuga' });

$item1->delete;
$item2->delete;
といった具合に、いつものような行オブジェクトの生成ができるので、あとは MyModel::Row::UserLog のようなところに実装を追加していけばよいでしょう。 簡単ですね。

それでは、またお会いしましょう。ごきげんよう。

記事検索
QRコード
QRコード