HireRoo TechBlog

HireRooの技術ブログ

コード品質を評価する仕組みの裏側

こんにちは、株式会社ハイヤールーで副業をしている加納(@ryuichi_74)と申します。 本記事では、最近リリースされたコード品質評価機能に関して執筆いたします。

アルゴリズム形式の試験の際には、従来は正解率と実行速度の二軸での評価が行われてきていました。しかしながら、エンジニアの能力を定量化する際には、保守性や可読性の高いコードを記述する能力というのも、非常に重要になってきます。

一方で、正解率や実行速度と比べると、保守性や可読性は計測方法が不明瞭です。本記事では、様々な保守性や可読性を定量化するための指標を紹介したのち、それらを用いた新機能について紹介させていただきます。

評価指標の紹介

最もシンプルに思いつくのはコードの行数やコメントの割合といったものですが、そのほかにも様々なものが存在しています。

1. 処理パターンの数え上げに準ずるもの

条件分岐などにより、プログラムの処理は様々な動作を取り得ます。その分岐パターンの数え上げに基づく評価指標をここでは紹介します。

最も代表的なものとして、Cyclomatic Complexityが挙げられます。プログラムの線形独立な経路の数を数えるもので、動作検証に必要となる単体テストの数に直結します。

f:id:KKosukeee:20220111191207p:plain
図1: Cyclomatic Complexityの算出例

図1は、2つのプログラムのフロー図です。線形独立な経路の数は、フロー図の中の閉区間の数を数えることで算出が可能であることが知られています。図1のようなプログラム構成の場合、Cyclomatic Complexityはどちらも6となります。

基本的にはCyclomatic Complexityが王道ですが、中には人間の主観的な可読性に特化して少し調整が入ったような指標があります。例えばswitch文などは、条件分岐が増える割には、主観的には可読性はあまり損なわれません。そこで、switch文による条件分岐数は数え上げないような、Cognitive Complexityという指標もあったりします。

f:id:KKosukeee:20220111191238p:plain
図2: シンプルな分岐要素の例

他にもEssential Complexityと呼ばれる指標もあり、シンプルな分岐要素(図2)が直列で並んでいる場合にはそれらの複雑度は考慮せず、条件分岐がネストする複雑さに関してのみ評価するようなものも提案されたりしています。

f:id:KKosukeee:20220111191301p:plain
図3: Essential Complexityの計算例

図3の場合ですと、図1で計算したようにどちらもCyclomatic Complexityは6でしたが、Essential Complexityはそれぞれ1と4となり、差が生じます。

上記のさまざまな指標は似ている一方で計測している事柄の意味合いが異なっているので、ユースケースに応じて適切に使い分けるのが重要になってきます。

2. コードの冗長さを測るもの

ひとつの変数で十分であるのに、複数の変数を定義することでコードが読みにくくなったり意味が解釈しにくくなってしまったりすることがあります。このように、品質に大きく直結する”冗長さ”に関しての評価指標をここでは紹介します。

代表的なものとして、プログラムの単語総数およびそれらのユニークな数を算出することでプログラムの情報量を数値化するHalstead Volumeが挙げられます。

main(){
  int a, b, c, avg;
  scanf("%d %d %d", &a, &b, &c);
  avg = (a+b+c)/3;
  printf("avg = %d", avg);
}

図4: Halstead Volumeを計算するためのCのソースコード例 (Link)

上記のプログラムには main , () , {} , int , scanf , & , = , + , / , printf , , , ; , a , b , c , avg , "%d %d %d" , 3 , "avg = %d" の19種類の単語が存在しています。また、重複を許して数え上げると、これらの単語は42個存在しています。この場合、Halstead Volumeは42×log219 ~ 178.4と計算されます。

3. 主観評価の回帰

f:id:KKosukeee:20220111191326p:plain
図5: 教師あり学習の適用例

ソースコードごとに人間によるアノテーションを行うことで可読性を評価し、その値を用いた教師あり学習を実施することで、自動的にコードからスコアを算出できるようにする手法もあります。有名なものとしては、Buseらの手法Dornらの手法などがあります。

この手法を適用する際にはアノテーション(人間による評価)つきのソースコードの獲得がネックではありますが、我々はコーディング試験の結果や面接官の評価などを収集することで、将来的には独自のデータセットを構築して応用することが可能なのではないかと考えています。

コード品質評価機能

f:id:KKosukeee:20220111191342p:plain
図6: マイクロサービスの関係図

我々はマイクロサービスアーキテクチャを採用しており、コード品質評価機能に関しても、マイクロサービス基盤上で動いています。(過去ブログ) コード品質評価機能の導入にあたり、我々は新たなマイクロサービスを構築し、Challengeサービス(アルゴリズム形式のコーディング試験を行う上でのデータ管理機能)と連携して運用がなされています。現在はアルゴリズム形式のコーディング試験でのみコード品質評価は有効となっていますが、今後はProjectサービス(APIサーバー構築などの少し規模の大きいコーディング試験を行うための機能)との連携なども行っていく予定です。

現状は紹介したの指標のうち、Cyclomatic Complexityを主たる評価指標として採用しています。 例えばソースコードの行数などは指標として非常に理解しやすいのですが、様々な言語に跨いで算出してみると、特定の言語では非常にコード行数が短くなりやすいなどといったバイアスが存在していました。一方、Cyclomatic Complexityに関してはプログラミング言語依存のバイアスが少なく、多言語を跨いだ評価に適した指標となっていることがわかりました。また、品質保証に必要な単体テスト数とも直結するため、採用の観点でも納得感の持てる指標なのではないかと思っています。

f:id:KKosukeee:20220111191400p:plain
図7: 正解率が100%だった回答に対して計算されたCyclomatic Complexity

実際にテストケースに対する正解率が100%だった回答に対してCyclomatic Complexityを計算してみると、かなり分布がばらつくことが見てとれます(図7)。プログラムの動作としては正しくても、その動作をいかにシンプルに実装できるかという点に関してはまた一つ壁があり、評価に値する指標であるということがこの図からも伝わるかと思います。

まとめ

いかがだったでしょうか? ソースコードの分析は自然言語処理的な能力とグラフデータ解析の能力が同時に求められ、非常にチャレンジングで面白い領域だと思います。

HireRooではソースコードおよびそれらに対する評価というユニークなデータが蓄積されていっており、アイデア次第でできることは色々あるのではないかなと思います。例えば我々はオンラインIDEを内製している(過去ブログ)ので、完成したコードのみでなく、コードがいかに書き上げられていったかの情報も全て蓄積されています。このデータを分析し、成績優秀な人はどんなことを考えながらコードを書いていっているのかなどが分析できると、非常に興味深い知見を獲得できるようになってくるのではないかなと思っています。また、紹介した評価指標に挙げられていた「人間の評価をソースコードから直接回帰する」といった取り組みにも挑戦したいと考えています。

また私は副業としてチームに参加しているのですが、働き方も柔軟に調整ができ、非常にやりやすかったです。gRPCやterraformなどをフル活用したマイクロサービス開発の経験(過去ブログ)は、自分のスキル向上にも繋がりました。

HireRooでの開発に興味があるかもしれないなと思う方は、TwitterのDMから連絡をいただければと思います!