ソースコードgrep地獄を助けるSourceGraph

# はじめに
コードを書いている時間と比較して、コードを「理解している」時間はどれくらいかかるのでしょうか？
[Software社が自社サービスのユーザ約25万人から集計した結果](https://www.software.com/reports/code-time-report)からは、以下のようになっていました。
* **コードを書いている時間：56%** (1日あたり平均52分)
* **コードを「理解している」時間：44%** (1日あたり平均41分)

コードを「理解している」時間はコードを書いている時間に匹敵するほどになっていることがわかります。
このコードを「理解している」時間は、具体的には次のようなものがあります。
* コードを表示して「読んでいる」時間
* プルリクのレビューをしている時間
* コードの理解のためにドキュメントを読んでいる時間

今回は上記のうち**コードを表示して「読んでいる」時間**を効率化できないか考えてみます。

# コードを「読んでいる」時間について
コードを「読んでいる」時間のよくあるケースとして、コード変更前の下調べ作業があります。
以降ではインフラエンジニアの味方[Hashicorp社のGitHubレポジトリ](https://github.com/hashicorp)を例に、下調べ作業のユースケースをいくつか挙げ、その実現方法を記載します。

### 利用しているモジュールのバージョンを確認する
実装は全て自前で行ることはまれで、何かしらのモジュールを利用しているはずです。
バグや脆弱性のfixや機能追加を目的として、利用モジュールをバージョンアップするケースはたびたびあるかと思います。
バージョンアップの下調べ作業としては、現在利用しているモジュールとそのバージョンの調査があります。
この調査は各言語で依存関係を記述するファイルまたはコマンドが利用できます。
例えば[Terraform](https://github.com/hashicorp/terraform)ですとGoで記述されていますので、go.sumを参照すればバージョンが一通りのバージョン確認ができそうです。
https://github.com/hashicorp/terraform/blob/main/go.sum
```go:go.sum(抜粋)
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
︙
```

### ネーミング変更の影響規模を確認する
単純なバージョンアップだけではなく、変数や関数を変更したくなる場合もあるかと思います。
諸般の事情でモジュールが作り直しになり名前が変更になった、というケースもこちらに該当します。
（[Log4jに対するLog4j2](https://logging.apache.org/log4j/2.x/)など）

Terraform内で`Help`を含む関数に対して、**「`Help`は危険、`Support`に変更しよう」** という流れが生まれ変更を検討するとしましょう。
変更するかの判断材料としては、どれだけ修正が必要なのかという規模の情報が参考になります。
以下のようにgrepで全ファイルの検索を行い、変更が必要なのは53箇所であることが抽出できました。
ファイル内及第点です。

```bash
$ git clone https://github.com/hashicorp/terraform.git
$ cd terraform
$ grep -rH "^func.*Help" ./*
./internal/command/apply.go:func (c *ApplyCommand) Help() string {
./internal/command/cliconfig/credentials.go:func (s *CredentialsSource) CredentialsHelperType() string {
./internal/command/cliconfig/credentials_test.go:func (s *mockCredentialsHelper) ForHost(hostname svchost.Hostname) (svcauth.HostCredentials, error) {
./internal/command/cliconfig/credentials_test.go:func (s *mockCredentialsHelper) StoreForHost(hostname svchost.Hostname, new svcauth.HostCredentialsWritable) error {
︙
$ grep -rH "^func.*Help" ./* | wc -l
53
```


### ネーミング変更の影響規模を確認する、全リポジトリで
非常に重大な問題が見つかりく、自社の全リポジトリを調査し問題が無いことの証明が必要としましょう。
例としてはクラウドサービスのアクセスキーのハードコーディングなどです。
[Hashicorp社のGithubリポジトリ](https://github.com/hashicorp)の数は執筆時点で974件です。
grep地獄が始まります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/8bfca249-e41d-96ed-8af7-f0df9b1a084f.png)
git cloneでローカルに全てのソースコードをダウンロードする必要があります。
1件を1分以内に作業できたとして、、1000件近く行うとなるとcloneだけでも16時間以上かかる計算になります。
また、実装言語は1つとも限りません。言語ごとにgrep条件を変え`Help`を含む関数を調べ上げていく必要があります。

サンタがやってくる前に家に帰れるような、全リポジトリを効率的に調査する方法はないのでしょうか。
調べてみたところ、**SourceGraph** という検索サービスで実現できそうです。

# SourceGraphについて
### 概要
SourceGraphはSourceGraph社が提供するソースコードの横断検索ツールです。
予めリポジトリからソースコードをcloneし、入力した条件をもとに検索結果を表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/42b375a1-6c7b-896d-1ffd-831ceb76beab.png)

### 基本的な使い方
SourceGraphをすぐに試せる環境として、GitHub上のパブリックリポジトリを検索できるサイトが公開されています。
本記事でもこのサイトを利用して説明していきます。

https://sourcegraph.com/

トップページにアクセスするとGoogleのような検索画面が表示されます。
試しに`Help`と入力して検索してみると、GitHub上のパブリックリポジトリからHelpを含んだソースコードが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/6f77e255-aa8e-9695-dd9a-d9ae73ddd774.png)


本記事では割愛しますが、プライベートリポジトリを検索したい場合は有償ライセンスを購入するか、
機能制限のあるオープンソース版を用意することで実現できます。

https://github.com/sourcegraph/sourcegraph

### 何が嬉しいか
**調査時間が短くなる**：リポジトリから予めソースコードをcloneしてindexを作ってくれるため、検索処理の時間が短いです
**複数言語に対応できる**：ソースコードの構文を解析してくれるため、変数や関数などを絞り込み条件にして検索することができます

# SourceGraphでの全リポジトリ検索

### とりあえず検索してみた
先ほどのユースケースに戻ります。
以下のように検索条件を入力し、`Help`を含む関数を検索してみます。

https://sourcegraph.com/search?q=context:global+Help+repo:%5Egithub%5C.com/hashicorp++type:symbol+select:symbol.function+count:2000&patternType=standard&sm=0

変更が必要なのは1504箇所であることがわかりました。
注目すべきは検索時間です。爆速です。
![image.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/2ba8b76b-cb1c-e3ec-8955-73fcab45d7bc.jpeg)

### 検索条件の解説
今回利用した検索条件について解説します。
```
Help repo:^github\.com/hashicorp  type:symbol select:symbol.function count:2000

Help : 検索キーワード
repo:^github\.com/hashicorp ： GitHub上のHashicorpに所属するリポジトリを検索対象にする
type:symbol : 構文解析した結果（変数や関数）を利用
select:symbol.function : 関数を検索対象にする
count:2000　: 検索結果を2000件まで表示(デフォルトは500件)
```
その他の検索条件については[SourceGraphのドキュメント](https://docs.sourcegraph.com/code_search/reference/queries)に記載されています。

# おわりに

本記事ではコードを「読んでいる」時間が、SourceGraphを利用することで全リポジトリを効率的に調査できることを記載しました。
おかげでサンタがやってくる前に家に帰れそうです。
それではみなさん、**Merry Christmas !!**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/6027456c-fa70-f2f7-b16d-5d18487b0399.png)