Try   HackMD
tags: blog

見習いプログラマのgolang日記

はじめに

この記事はRecruit Engineers Advent Calendar 2017 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

deeeetさんの記事にも書いてありますが, 公式で提供されている場合にむやみに作るべきではありません.

この時, 理解せず記事に書いてある通りcontext.Context対応のコードを書いていました.
実際に使うとcontext.Background()しか渡さないし使いづらいと思い,デフォルトではcontext.Background()を内部で渡すようにし,明示的に指定したい場合はWithContextが末尾についた関数を使ってもらう感じにしました.
しかし, コードを修正しシグネチャが変わった時に毎回WithContext版のシグネチャも修正しないといけなくなり「これは辛いなぁ」と思いました.
人間がやるべき仕事ではない!自動化しよう!ということで作りました.

orisano/nocontext

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を知らず, どういうレスポンスが返ってくるかも知らず, どうやって認証したら良いのかもわからず, 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

認証ができるようになったので, 実際にリクエストを送りどのようなレスポンスが返ってくるのか確認し必要な機能の実装を始めることが出来ました.

recruit-tech/go-ams

テスト

ある程度機能が実装できたあたりから, 技術顧問である@t_wadaさんからテストのレビューをしてもらいました.
その際に調べて出てきた記事, 教えて貰った記事を共有しておきます.

エラーハンドリング

以下のようなコードで,最終的にmainまで返ってきたerrlog.Fatalするパターンを良く書いていました.

foo, err := Bar()
if err != nil {
    return err
}

しかしこのようなやり方だとlog.Fatalがどういう文脈で起きたエラーかわからず調査に時間がかかることがあり困っていました.
これをきっかけにgolangのエラーハンドリングについて調査しました.以下の2つが日本語で読める良質なものでした.

jxckさんの記事に書いてある方法は同じような例外を簡潔に処理できる.bufioの内部も同様な実装になっています.
deeeetさんが紹介していた方法は自分でstack traceを書くようなものですが,github.com/pkg/errorsを使い始めてからどこで起きたerrorなのかすぐわかる様になりました.

またgo-amsの機能で並列アップロードする部分があり, 並列で走らせたときのerrorってどうするんだっけと調べたところ,

で紹介されているgolang.org/x/sync/errgroupが良さそうだと言うことがわかりました.

共通化

2つめのAPI Clientを作っていてこういう機能が共通, こういう機能があると嬉しいみたいなことを思ってきたのでライブラリ化しました.

orisano/httpc

これも作った後に気がついたのですが, github.com/dghubble/slinggithub.com/go-resty/restyがやりたいこととしては同じでした.
せっかく作ったgithub.com/orisano/httpcはgo-amsで使うようにしました.

趣味

4月に@t_wadaさんの講義を聞いてからwrite code every dayを実践していていろんなコードを趣味で書いてきたので紹介させてください.

車輪の再発明ですが, リトライポリシーに則ってリトライしてくれるライブラリ
orisano/go-retry

socket.io-clientのgolang実装
orisano/gomasio

DAMMアルゴリズムのgolang実装
orisano/go-damm

終わりに

ただこれまでのことを垂れ流すだけの記事になってしまいました.
この5ヶ月で少しはgolangが書けるようになったかと思います.
書いてみてすごくシンプルな言語で,変数名やpackage名以外で悩むことがあまりないなと感じています.
formatで悩まなくてもいいところや, python2,3のような悩むところもないのが非常に良いです.

とはいえちゃんと学ばずにやってきたので,
今はプログラミング言語Goを読み進めつつ,標準ライブラリの実装なども読みながら勉強しています.
これからもgolangやっていきます.

ありがとうございました.