git blameによるSRP(単一責任原則)の定量化
はじめに
ソースコードを静的解析することでSRP(単一責任原則)を定量的に算出します.*1
svn blameによるSRP算出*2を参考に、git blameによる算出をshで行ってみました.
このSRP値が最大のモジュールが王様モジュールに相当します.
# 単一責務性の違反指数(SRP) # SRP=R+U+((L/100)-5) # R:修正リビジョンのユニーク数 # U:修正ユーザのユニーク数 # L:モジュールのライン数 function get_SRP() { local target_filepath=$1 echo $(( \ $(git --no-pager blame --line-porcelain $target_filepath | sed -n 's/^summary //p' | sort | uniq -c | sort -rn | wc -l) + \ $(git --no-pager blame --line-porcelain $target_filepath | sed -n 's/^author //p' | sort | uniq -c | sort -rn | wc -l) + \ ( $(cat $target_filepath | wc -l) / 100 - 5) \ )) $target_filepath } # SRPが酷い順(大きい順)に "SRP ファイル名" を標準出力 for file in `git ls-files app lib config vendor script | grep -E '\.rb$'`; do get_SRP $file done | sort -k1,1 -nr
※ここではrailsのリポジトリを対象にしたため,rbファイルを対象にしていますが,別言語でも全く同様に扱えます.
関連する過去記事
LDAの各変数の意味と幾何的解釈について
はじめに
LDAの仕組みについて,時間をあけるとすぐに記憶が飛んでしまうためメモ.
ここでは以下についてまとめます*1
- LDAのグラフィカルモデルにおける各変数の意味とは?
- LDAは幾何的に何をやってるのか?
LDAのグラフィカルモデル
まず,各文書についてBag of Words(BoW)表現に変換する*2.
そして,次の仮定をおく.
これをグラフィカルモデルに落としこむと下図になる*5.
すると,グラフィカルモデルにおける各変数の意味は次のようになる.*6
- :文書数
- :文書集合全体におけるトピック数
- :文書集合全体における語彙数*7
- :文書dにおける単語数*8
- :文書における番目の単語の語彙インデックス*9
- :文書における番目の単語の潜在トピック
- :文書におけるトピックの出現確率ベクトル.
- :トピックの出現頻度の偏りを表すパラメータ*10
- :トピックにおける語彙の出現確率ベクトル.
- :語彙の出現頻度の偏りを表すパラメータ*11
LDAの生成過程
ただし,はディリクレ分布,は多項分布を表す.*12
また,とはパラメータとして与える.
補足:パラメータとの意味
多項分布による生成過程を考えて,
の事前分布としてディリクレ分布,ただしを考える.
すると,の事後分布は,となる.
ただし,は回試行の中でが出現した回数.
すなわち,事前分布におけるは,事後分布ではに加算される.
したがって,はデータを観測する前のごとの仮想的頻度を表す.
よって,LDAにおけるとについていえば,次のようになる.
- は,トピックの出現頻度の偏りを表すパラメータ
- は,語彙の出現頻度の偏りを表すパラメータ
LDAの幾何的解釈
簡単のため,全文書集合における語彙数Vが3の場合を考える*13.
このとき,各文書をBoW表現にすることで,上図の語彙座標単体*14に射影できる.
各文書は語彙座標単体空間に偏りをもって分布していると想定*15され,より低次元に射影できるはずである.
この低次元空間(単体)を潜在トピック座標単体と呼び,その基底ベクトルがトピックとなり,トピックは語彙の出現確率分布で表現される.そして,各文書のトピック分布は,潜在トピック座標単体上の点となる.
つまり,LDAのは,文書集合の潜在トピック座標単体上への射影とみることができ,
潜在トピック座標単体は,単語座標単体よりも低次元であるため次元圧縮と見ることができる.
参考文献
トピックモデルによる統計的潜在意味解析 (自然言語処理シリーズ)
- 作者: 佐藤一誠,奥村学
- 出版社/メーカー: コロナ社
- 発売日: 2015/03/13
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (5件) を見る
*1:ほとんどの内容を本ページ末尾の参考文献から引っ張ってきています
*2:文書を形態素解析し,形態素のヒストグラムに変換する.例えば,文書が「もももすももももものうち」であれば,分かち書き「もも/も/すもも/も/もも/の/うち」に対して,形態素ごとにカウントした「もも:2 も:2 すもも:1 の:1 うち:1」がBoW表現である.
*3:話題.音楽,スポーツ,政治…など
*4:出現箇所が異なればその単語のトピックは異なる.例えば,「アップルが新製品を発表しました」と「バナナ,アップル,キウイのミックスジュースがうまい」の「アップル」のトピックは異なる.前者は「企業」トピックとしてのアップルであり,後者は「フルーツ」トピックとしてのアップルである.
*5:これは正確にはSmoothed LDAのグラフィカルモデル.無印LDAの場合はβが存在しない.多くの場合,LDAといえばSmoothed LDAを指すみたい.
*6:ベクトル表記に関して,上図では太字で記し,下では矢印で表記する.…はてなtex記法で太字を表現できなかった…
*7:ここでは「語彙」はユニークな形態素として呼び,一方「単語」はユニークでない形態素として呼ぶ.例えば,「もももすももももものうち」の語彙数は「もも」「も」「すもも」「の」「うち」の5つとするのに対して,単語数は「もも」「も」「すもも」「も」「もも」「の」「うち」の7つとする.
*8:語彙数とは異なるので注意
*9:語彙インデックスは語彙へのマッピング情報を持つ.例えば,語彙インデックス「3」は語彙「すもも」を表すなど
*10:要素値は大きくなるに連れて,文書集合全体においてトピックが出現しやすくなる
*11:要素値は大きくなるに連れて,文書集合全体において語彙インデックスの語彙が出現しやすくなる
*12:各分布の意味はこちらのスライドがわかりやすいです → 3分でわかる多項分布とディリクレ分布 - http://www.slideshare.net/stjunya/ss-29629644/4
*13:すべての文書において,登場する語彙がgame,play,musicの3つしか存在しない状況
*14:各語彙の出現確率の総和が1となるような空間
*15:単語には共起性があるから
gitで変更ファイルの差分行番号を取得するには?
はじめに
gitで変更ファイルの差分行内容とその行番号を取得したい状況が生じたためメモ(下画像は実行結果)
やりかた
git --no-pager diff --no-ext-diff -U1000000 \ | diff-lines.sh \ | grep -E "^[^\"].*\:[0-9]+\:[\+|\-]"
- 1行目について,git diffをファイル全行について標準出力*1.具体的には以下.
- --no-pagerはgit diffを標準出力するオプション.デフォルトだとpagerにlessが設定されてしまうため,パイプに渡せるように指定
- --no-ext-diffは外部diffを無効化するオプション.git diffをvimdiffで見るように設定していたため指定
- -Uは行数を表示するオプション.例えば10を設定すると差分行まわりで10行が出力される.ファイル内容について全行出力したいため大きな値1000000を指定
- 2行目について,diff-lines.shは以下とし*2,pathを通しておく.
- 3行目について,変更のある行の取得.
#!/bin/sh # diff-lines.sh numbers path= line= while read; do esc=$'\033' if [[ "$REPLY" =~ ---\ (a/)?.* ]]; then continue elif [[ "$REPLY" =~ \+\+\+\ (b/)?([^[:blank:]]+).* ]]; then path=${BASH_REMATCH[2]} elif [[ "$REPLY" =~ @@\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,[0-9]+)?\ @@.* ]]; then line=${BASH_REMATCH[2]} elif [[ "$REPLY" =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then echo "$path:$line:$REPLY" if [[ "${BASH_REMATCH[2]}" != - ]]; then ((line++)) fi fi done
補足
ファイル名と行番号だけで良い場合は以下のようにすればOK
git --no-pager diff --no-ext-diff -U1000000 \ | diff-lines.sh \ | grep -E "^[^\"].*\:[0-9]+\:[\+|\-]" \ | ruby -nle 'print $_.split(":").slice(0..1).join(" ")' \ | uniq
*1:Git - git-diff Documentation - http://git-scm.com/docs/git-diff
*2:Using git diff, how can I get added and modified lines numbers? - Stack Overflow - http://stackoverflow.com/questions/8259851/using-git-diff-how-can-i-get-added-and-modified-lines-numbers
SVN/GITリビジョン区間の更新ファイルをパッチ化する
はじめに
SVNもしくはGITにおけるリビジョン区間*1の更新ファイルをパッチ化*2する作業が生じたためシェル化メモ。
やりかた(SVNの場合)
# リビジョン区間(r6000-r6100)における更新ファイルについて、 # ディレクトリ構造を維持したままtar.gzに固める svn diff -r6000:6100 --summarize \ | awk '{print $2}' \ | (mkdir patch_dir; \ xargs -I{} cp --parents {} patch_dir; \ tar czvf patch.tar.gz patch_dir; \ rm -Rf patch_dir)
補足すると,以下の様な流れ
- 1行目で、リビジョン区間(r6000からr6100)の更新ファイル一覧を取得
- 2行目で、更新ファイル名のみを抽出
- 3行目で、作業パッチディレクトリを作成
- 4行目で、更新ファイルそれぞれについて、ディレクトリ構造を維持たままパッチディレクトリにコピー
- 5行目で、パッチディレクトリをtar.gzに固める
- 6行目で、作業パッチディレクトリを削除
やりかた(Gitの場合)
# gitでリビジョン区間(リビジョンハッシュA-リビジョンハッシュB)における更新ファイルについて、 # ディレクトリ構造を維持したままtar.gzに固める git diff --name-only リビジョンハッシュA..リビジョンハッシュB \ | (mkdir patch_dir; \ xargs -I{} cp --parents {} patch_dir; \ tar czvf patch.tar.gz patch_dir; \ rm -Rf patch_dir)
補足:SVNでリビジョン区間の更新ファイル一覧を最終更新リビジョンとともに取得
# リビジョン区間(r6000-r6100)の更新ファイル一覧を最終更新リビジョンとともに取得 svn diff -r6000:6100 --summarize \ | awk '{print $2}' \ | xargs -I{} bash -c "( svn ls -v -R {} | awk '{printf \$1}'; echo ' '{} )"
技術的負債をいかに減らすか
はじめに
技術的負債が大きくなってしまったコード(=保守しにくい品質が低いコード)をいかに改善するか?という話です.コードの保守性を定量評価したいとき、どういう指標を用いるのがいいんだろう??
— Kensho Fujisaki (@ni66ling) 2015年4月19日
コミット時にその指標が悪化してたらリモートリポジトリへのコミットを禁止にしたり、コミッター別にその指標に対する影響度をグラフ化して、技術的負債を改善したい
対処策として,リファクタリングやテスト充実化(★)すればいいじゃないか?という意見が出てくると思いますが,
これを漠然と実行に移すのは,意外と簡単なことではないのではないかなと思っています.
実行に移しにくい理由として,大きく以下を挙げます.
[理由1] ★は短期的に見ると製品価値を生む行為ではないため,関係者(経営者)に対して説得力しにくい
[理由2] 実施後のモチベーションが続きにくい
ここでは,世間的にこの問題に対してどのように対処しているのかを簡単にまとめ,
その上で,どのように対処すべきか考えをまとめたいと思います.
事例
mixi http://alpha.mixi.co.jp/entry/2012/11552/alpha.mixi.co.jp
①ソースコード静的解析による技術的負債の見える化(定量化)
②①の指標改善度により開発者を評価ドワンゴ doda.jp
①技術的負債を短期間返済するための部署立ち上げ
②GitHub/IntelliJなどの開発ツール導入GMOペパボ http://engineer.typemag.jp/article/pepabo-devengineer.typemag.jp
①技術的負債返済のための部署を社長直轄チームとして立ち上げ
②CI導入
③技術的負債返済に関して開発者を評価
各事例における対処策の分類
1.~3.の対処策をまとめると,以下の5点に分類できると思います.
- 技術的負債返済のための部署立ち上げ
- ソースコード静的解析による技術的負債の見える化
- CIによる技術的負債の継続的改善
- 技術的負債返済に連動した開発者評価
- GitHub/IntelliJなどの開発ツール導入
どのように対処するか
はじめの「実行に移しにくい理由」に対して対処できるのではないかなと思います.
[理由1] → 2.による現状把握と,この指標によるデメリットの説明
[理由2] → 3.,4.によるモチベーション維持
対処策を具体化すると,次のようになるかなと思います.
- 2.について
Rubocop*1による各種指標*2を取得
取得した指標について,Rubocop規定値および世間的な標準値をもとに現状を評価し,本問題の重要度を明文化 - 3.について
2.の指標をJenkinsで管理.具体的には,RSpecの自動テストと同様に,コミット時にRubocopで各種指標の取得を実施 - 4.について
3.で取得した値について,各種指標の改善度/改悪度をチーム全体およびコミッター別に計上
技術的負債返済の進捗状況を透明化
以上を実際に試してみて,技術的負債の返済をトライしたいと思います.
*2:Cyclomatic complexity, Assignment branch condition size metric, Perceived complexityなど.http://www.rubydoc.info/gems/rubocop/0.27.0/RuboCop/Cop/Metrics