###### tags: `blog`
# 見習いプログラマのgolang日記
## はじめに
この記事は[Recruit Engineers Advent Calendar 2017](https://adventar.org/calendars/2324) 12日目の記事です.
2017年度入社の與那城です,今はリクルートテクノロジーズのAPソリューショングループで見習いプログラマをしています.
この5ヶ月くらいgolangのコードを書いていたのでその記録を振り返るための記事です.
## 前提
* Tour of Goをダラダラやっていてほとんど覚えていない程度
* プログラミング経験自体は6年程度
* C++, JavaScript, Pythonをやっていた.その他の言語も触っていたが常用はしてなかった.
## Task1: SlackBotを作る
7月にslackのpostをbacklogに投稿し, postについたコメントをbacklogにも投稿するものを作ることに.
当時のスキルセットとしてはPythonでやるのが一番速かった気がしますが, golangをやっていきたかったのでgolangで作ることにしました.
この時はとりあえず書くぞと言いながらライブラリの調査もろくにせずに走りはじめました.
### API Clientを作る
http://deeeet.com/writing/2016/11/01/go-api-client/
この記事を見ながらbacklogのAPI Clientを作り始めました.
見よう見まねでコードを書いてなんとなく動くようになり, 必要なエンドポイントだけ実装してとりあえず終わりました.
READMEからもやる気の無さが伺えます.
[orisano/backlog](https://github.com/orisano/backlog)
<iframe id="ghcard-orisano-1" frameborder="0" scrolling="0" allowtransparency="true" src="https://cdn.jsdelivr.net/github-cards/1.0.2/cards/default.html?user=orisano&identity=ghcard-orisano-1&repo=backlog" width="400" height="153"></iframe>
deeeetさんの記事にも書いてありますが, 公式で提供されている場合にむやみに作るべきではありません.
この時, 理解せず記事に書いてある通り`context.Context`対応のコードを書いていました.
実際に使うと`context.Background()`しか渡さないし使いづらいと思い,デフォルトでは`context.Background()`を内部で渡すようにし,明示的に指定したい場合は`WithContext`が末尾についた関数を使ってもらう感じにしました.
しかし, コードを修正しシグネチャが変わった時に毎回`WithContext`版のシグネチャも修正しないといけなくなり「これは辛いなぁ」と思いました.
人間がやるべき仕事ではない!自動化しよう!ということで作りました.
[orisano/nocontext](https://github.com/orisano/nocontext)
<iframe id="ghcard-orisano-2" frameborder="0" scrolling="0" allowtransparency="true" src="https://cdn.jsdelivr.net/github-cards/1.0.2/cards/default.html?user=orisano&identity=ghcard-orisano-2&repo=nocontext" width="400" height="153"></iframe>
golangのastが意外と簡単に扱えるところ,goimportsのソースコードからの呼びやすいところなどがあり,
ソースコード生成系のツールなどが非常に作りやすいなと思いました.
結局作りはしましたが, ほとんど使うことはありませんでした.
### Slack Botを作る
`github.com/nlopes/slack`を使い簡単に作りました.
mainの制御系とslackとのやり取りをするgoroutine, slackのpostをdownloadするgoroutine, backlogに投稿するgoroutineに別けて実装しました.
ここで困ったこととして
* slackのpostがmarkdownで保持されていると思ったら謎のjsonで保持されていたこと
* いずれかのgoroutineでerrorが起きた時にすべてのgoroutineを終了するために様々なチャンネルが必要であること
がありました. 前者はただ変換が面倒くさいだけでしたが, 後者に関してはこの時点で勉強不足というだけでした.
いろいろ遠回りをしましたが, Slack Botを作るというTaskが終わりました.
## Task2: Azure Media ServiceのGo SDKを作る
Azure Media Serviceを使いたいという話になり,
https://docs.microsoft.com/en-us/rest/api/media/operations/azure-media-services-rest-api-reference
とにらめっこしながらAPI Clientを作り始めました.
[OData](http://www.odata.org/)を知らず, どういうレスポンスが返ってくるかも知らず, どうやって認証したら良いのかもわからず, TenantIDとは何か, ClientIDとは何か, どのエンドポイントにアクセスすれば良いのかわからず手間取ったの覚えています.
とりあえず認証ができないと話にならないので調査をはじめました.
https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-libraries をみて, ADAL(Active Directory Authentication Library)というライブラリを使うのが良いということがわかりましたが公式にはgolangのライブラリはなく, 仕方なくNode.js実装をgolangに移植しました.
[orisano/go-adal](https://github.com/orisano/go-adal)
<iframe id="ghcard-orisano-3" frameborder="0" scrolling="0" allowtransparency="true" src="https://cdn.jsdelivr.net/github-cards/1.0.2/cards/default.html?user=orisano&identity=ghcard-orisano-3&repo=go-adal" width="400" height="153"></iframe>
認証ができるようになったので, 実際にリクエストを送りどのようなレスポンスが返ってくるのか確認し必要な機能の実装を始めることが出来ました.
[recruit-tech/go-ams](https://github.com/recruit-tech/go-ams)
<iframe id="ghcard-orisano-4" frameborder="0" scrolling="0" allowtransparency="true" src="https://cdn.jsdelivr.net/github-cards/1.0.2/cards/default.html?user=recruit-tech&identity=ghcard-orisano-4&repo=go-ams" width="400" height="153"></iframe>
### テスト
ある程度機能が実装できたあたりから, 技術顧問である@t_wadaさんからテストのレビューをしてもらいました.
その際に調べて出てきた記事, 教えて貰った記事を共有しておきます.
* https://golang.org/pkg/testing/
* https://testing.googleblog.com/2010/12/test-sizes.html
* https://blog.gopheracademy.com/advent-2015/symmetric-api-testing-in-go
* https://speakerdeck.com/mitchellh/advanced-testing-with-go
* http://deeeet.com/writing/2016/10/25/go-interface-testing/
### エラーハンドリング
以下のようなコードで,最終的に`main`まで返ってきた`err`を`log.Fatal`するパターンを良く書いていました.
```go
foo, err := Bar()
if err != nil {
return err
}
```
しかしこのようなやり方だとlog.Fatalがどういう文脈で起きたエラーかわからず調査に時間がかかることがあり困っていました.
これをきっかけにgolangのエラーハンドリングについて調査しました.以下の2つが日本語で読める良質なものでした.
* http://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike
* http://deeeet.com/writing/2016/04/25/go-pkg-errors/
jxckさんの記事に書いてある方法は同じような例外を簡潔に処理できる.`bufio`の内部も同様な実装になっています.
deeeetさんが紹介していた方法は自分でstack traceを書くようなものですが,`github.com/pkg/errors`を使い始めてからどこで起きたerrorなのかすぐわかる様になりました.
またgo-amsの機能で並列アップロードする部分があり, 並列で走らせたときのerrorってどうするんだっけと調べたところ,
* http://deeeet.com/writing/2016/10/12/errgroup/
で紹介されている`golang.org/x/sync/errgroup`が良さそうだと言うことがわかりました.
### 共通化
2つめのAPI Clientを作っていてこういう機能が共通, こういう機能があると嬉しいみたいなことを思ってきたのでライブラリ化しました.
[orisano/httpc](https://github.com/orisano/httpc)
<iframe id="ghcard-orisano-5" frameborder="0" scrolling="0" allowtransparency="true" src="https://cdn.jsdelivr.net/github-cards/1.0.2/cards/default.html?user=orisano&identity=ghcard-orisano-5&repo=httpc" width="400" height="153"></iframe>
これも作った後に気がついたのですが, `github.com/dghubble/sling`や`github.com/go-resty/resty`がやりたいこととしては同じでした.
せっかく作った`github.com/orisano/httpc`はgo-amsで使うようにしました.
## 趣味
4月に@t_wadaさんの講義を聞いてから`write code every day`を実践していていろんなコードを趣味で書いてきたので紹介させてください.
車輪の再発明ですが, リトライポリシーに則ってリトライしてくれるライブラリ
[orisano/go-retry](https://github.com/orisano/go-retry)
<iframe id="ghcard-orisano-6" frameborder="0" scrolling="0" allowtransparency="true" src="https://cdn.jsdelivr.net/github-cards/1.0.2/cards/default.html?user=orisano&identity=ghcard-orisano-6&repo=go-retry" width="400" height="153"></iframe>
socket.io-clientのgolang実装
[orisano/gomasio](https://github.com/orisano/gomasio)
<iframe id="ghcard-orisano-7" frameborder="0" scrolling="0" allowtransparency="true" src="https://cdn.jsdelivr.net/github-cards/1.0.2/cards/default.html?user=orisano&identity=ghcard-orisano-7&repo=gomasio" width="400" height="153"></iframe>
[DAMMアルゴリズム](https://ja.wikipedia.org/wiki/Damm%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0)のgolang実装
[orisano/go-damm](https://github.com/orisano/go-damm)
<iframe id="ghcard-orisano-8" frameborder="0" scrolling="0" allowtransparency="true" src="https://cdn.jsdelivr.net/github-cards/1.0.2/cards/default.html?user=orisano&identity=ghcard-orisano-8&repo=go-damm" width="400" height="153"></iframe>
## 終わりに
ただこれまでのことを垂れ流すだけの記事になってしまいました.
この5ヶ月で少しはgolangが書けるようになったかと思います.
書いてみてすごくシンプルな言語で,変数名やpackage名以外で悩むことがあまりないなと感じています.
formatで悩まなくてもいいところや, python2,3のような悩むところもないのが非常に良いです.
とはいえちゃんと学ばずにやってきたので,
今はプログラミング言語Goを読み進めつつ,標準ライブラリの実装なども読みながら勉強しています.
これからもgolangやっていきます.
ありがとうございました.