目次
■キャリアとスキルアップ
2023.08.04 2024.06.19 約5分
この記事は、2023年3月8日発売 『Go言語プログラミングエッセンス』著者 mattn 氏と、『実用Go言語』著者 渋川 氏 による connpass勉強会 を再編集し、記事化したものです。 |
▼『実用Go言語』徹底解説&制作秘話 はコチラ
まずはGo言語の基礎をおさらいしていきましょう。
Go言語は、Googleによってプログラミング環境を改善する目的で開発されました。この開発には、C言語を設計した世界的なエンジニアが携わっています。
|
「Explicit is better than implicit」これは開発者 Rob Pike 氏が好む言葉で「曖昧さよりも厳密さの方が良いよね」という意味。彼が創り上げた Go言語の精神を象徴しています。
Google、PayPal、American Express、DropBox、Cloudflare、Facebook、Microsoft、Netflix、Salesforce、Twitter、Uber、Twitch、Dailymotion、SendGrid、SoundCloud、Cookpad、CyberAgent、サイボウズ、DeNA、はてな、LINE、メルカリ、楽天、さくらインターネット、Yahoo! JAPAN、ZOZO、ぐるなび、eureka …… 引用:https://github.com/golang/go/wiki/GoUsers |
コマンドラインプログラム / ウェブ API サーバ / バックエンドジョブ / 広告配信システム / ハードウェア制御 / 機械学習 |
● 静的型付コンパイル型言語
○ ソースコード上に型を明記し代入の束縛を行う ○ コンパイルが通らないと実行できない ● スタティックバイナリを生成する ○ 依存ライブラリが少なくポータブルなバイナリを生成できる ● C言語のような書き味でありながら GC がある ○ ポインタやアドレス参照を使う ○ メモリの開放は自動で行う ● 非同期を簡単に扱えるランタイム ○ goroutine で並行処理し channel による通信を行う |
C言語しかなかった時代の人にとっては夢のような言語に感じるでしょう。ポインタやアドレス参照を使うので敷居の高さは感じるかも知れませんが、比較的、理解しやすい言語です。
● 読みやすさ声に出して読める程の読みやすさ
● 学びやすさ ● 開発者が明示的に処理を中断させるメンタルモデル ● 複雑にしないために予約語を増やさない |
※ 現時点での判断であり良い方法があれ改良される事もある
● なるべく曖昧さを与えない
● 明示的に開発者がコードで意思決定するプログラミング |
厳密さや簡潔さをもたらすので、本来はシェルで実装を済ませるような処理を、あえて Goで実装する人も多いのです。私も同じ。失敗経験はほとんどありません。
2013年にGoが実施した「あなたの課題はなんですか?」というサーベイの結果は、エラーハンドリング / スタックトレース が1位。
Go言語は Javaのように例外を扱いませんし、 Rust のようにリザルト型言語ではありません。関数の戻り値にエラーがあれば対処が必要です。
前述のようにエラーの取り扱いが難しいとされるGo言語ですが、バグを減らすコツはあります。
|
よくあるコードサンプルを例に見てみましょう。
このように、defer を上手く使い、起こるエラーを全て呼び出し元へ返すことで、未想定のバグを減らす事ができるのです。
個人でも高性能なサーバーを保有する時代。そのリソースを最大限に活用するためには非同期プログラミングが有効です。少し難しいイメージがあるかも知れませんが、2つの方法によって非同期プログラミングを簡単に実現できます。
goroutine / channel
● goroutine
非同期にしたい関数に go を付けるだけで簡単に非同期を呼び出せる ● channel 非同期で実行される goroutine の間で安全に通信する方法 |
一方、goroutine や channel は便利だけど、エラーハンドリングが難しいという声も耳にします。goroutine や channel だけで処理するよりも、コンテキスト(goroutine の呼び出し元が goroutine を安全に終了するための機能)と組み合わせて使用することをおすすめしています。コンテキストを適切に使用することで、goroutine が放置されるリスクを避けることができます。
Go言語は…
● 静的型付のコンパイル型プログラミング言語
● スタティックバイナリが吐かれる(デプロイが楽) ● goroutine/channel により簡単に非同期プログラミングができる ● スケールするのでクラウド向き(クラウドでのシステム開発向き) |
簡単非同期プログラミングでシステム開発が楽しくなる!!
1.20でプレビュー搭載されたPGO(Profile-guided optimization)がGA(generally available)に到達しており、default.pgoファイルによる最適化でプログラムのパフォーマンスが向上。ホストアーキテクチャにより異なるがGoプログラムの2-4%の高速化が見込まれる。組み込み関数(The built-in functions)に引数の最大値、最小値を返すmin/maxやパッケージにslicesやmaps、ログを取り扱うlog/slogなども加わる。引用:https://news.mynavi.jp/techplus/article/20230622-2710414/ |
下記の動画で各機能を詳しく解説しています。
min/max / clear / loopvar / pgo / slog / maps / slices / wasi
mattn:Go は I/Oバウンド(ネットワークやディスクへの入出力などがボトルネックになる状況)が多く発生することが予想される場合に適しています。CPUバウンド(計算処理が主体となるフィボナッチ数列のような複雑な計算を多く行う処理)には不向きです。
渋川: I/Oバウンド っていうのは、たとえばWebサーバーのようにネットワークアクセスが多い、あるいはリクエストを投げる側、またはデータベースやファイルの読み書きなどを指しますね。
mattn:「Go言語にジェネリクスが追加されてからしばらく経ちましたが、コミュニティではどういう扱いなのでしょうか?」という質問ですね。Goって新しいバージョンになると、古いアーキテクチャをどんどん切っていくので、古いGoをサポートしたい方にとってジェネリクスはまだ受け入れられていない様子です。私もメインでは使用していないです。
渋川: Goのポリシーだと最新の2バージョンはサポート対象ですよね。現状だと、1.19、1.20がサポート対象なのでアクティブなバージョンはすべてジェネリクスをサポートしていると言えます。それよりも古いバージョンをサポートしたい方もいるんですね。
mattn:そうですね。
― ジェネリクスの使いどころはありますか?
mattn:ジェネリクスを活用すると、コード内で演算を行う際に型パラメータを使用できます。例えば、プラス演算子を使うと、文字列であれ数値であれ出力可能です。any型ではなく comparable型を用いることで、数値を入力したいのに間違って文字列が入力される問題を防げます。
ただし、Goのジェネリクスには一定の制約があります。例えば、可変長の型パラメータは許容されていません。複雑な処理をしようとすると問題が発生する可能性があります。その制約を理解して、正しく使用することが重要です。
mattn:バッファ数は処理の性質によります。まずはバッファなしで実装し、必要に応じてバッファ数を微調整するのが適切です。バッファを過剰に設定すると、本来ブロッキングして欲しい箇所で逆にバグを引き起こす可能性があります。
渋川: あとはCPUのコア数やタスクによっても変わってきますよね。
mattn:シェルは環境変数が未定義だと書きにくいのですが、Go はより明示的に書けます。値を取得したら直ちにチェックするという Go の原則に従うと、コードが簡単に書けるかと思います。
渋川: Windows環境で頻発に起こる、文字コード(シフトJISかUTF-8か)のトラブルも、Goだと大幅に軽減できますよね。環境変数が空のせいで想定外のフォルダを削除してしまうとかも防げそうです。(注釈: rm $WORK/libという命令は、$WORKが定義されていない場合にルートの/libを削除しようとする)
mattn:そうですね。型が明確な Go だと、本来数値で処理すべき部分が文字列のままになるといったバグが少なくなる点もメリットですね。
mattn:お手本にするなら、やはり標準ライブラリもしくは標準ライブラリの開発に携わる方が別で作ったOSSを参照するのがベストです。
渋川: 「クリーンアーキテクチャ」とか「コーディングスタンダード」で検索して出てくるものはあまりおすすめしません。Rob Pikeも「これはGoにおける標準ではない」とコメントを残しています。僕も Go標準ライブラリ、Go自体のソースをおすすめします。
mattn:「Goで作成されたプログラムが長期間にわたり大量のメモリを使用する場合(Go が世代別GC を採用していないせいか)CPU使用率が急激に増加します。軽減する方法はありますか?」という質問ですね。ここはGo自身も長年課題に感じている部分のようで、実は各バージョンアップごとに少しずつ改善されてきているんですよね。今回のバージョンから GC を実行するタイミングが固定ではなくなっています。
渋川: なるべく新しいものを使いましょうと。
mattn:そうですね。
渋川: 大量にメモリを取って大量に返すみたいな行動しちゃうと、Goに限らずどの言語でも、OSとのやり取りに時間を取られるので、sync.Pool を使用して Go 内で使い回すようにするとかですかね。あとは、1.20でExperimentalでリリースされたメモリアリーナもあります。小さいメモリをたくさん確保するXMLは遅いというissueが立っていますし、それを使うExcelパースに関しては僕ももうちょっと改善してほしいと思っています。
mattn:「Go言語で開発したプロジェクトを無料でデプロイできるおすすめのサービスはありますか?(気に入っていた Heroku が有料化されてしまい…有料化されても一番コスパが良いのかしら?)」という質問ですね。もちろん、AWS、Oracle Cloud、GCPなど標準的なプラットフォームで動作する方が良いと思いますが、無料であれば、Fly.io ですかね。完全に現時点での僕の好みになってしまうのですが、コンテナを推奨できる点が良いかと。Vercel もアップロードは可能ですが、実行時にコンパイルが行われるようで、少し動作が遅いんですよね。
渋川: Go は起動時間が短いので、AWS Lambda や Google Cloud Run のようなサーバーレスのサービスを使うと、リクエストごとに処理を行うことが可能なので、かなり実用的だと思います。
mattn:「Go のロギングライブラリ zap は高パフォーマンスを売りにしていますが、高パフォーマンスは必要ですか?また、slog を使う際にも高パフォーマンスを重視すべきですか?」という質問ですね。ロギングを導入することで逆にシステムが遅くなることはよくあるんです。Go は速さより下位互換性を重視しているので、今後も互換性を保ちながら改良されていくと思います。slog に追加された Go1 は恐らく今後も壊れませんが、zap は将来的に古いインターフェースになり、コンパイルできなくなる可能性があります。どちらに投資するかはユーザー次第ですが、僕は slog に賭けたいなと。
渋川: 僕も slog 派。パフォーマンスが気になる場合は両方のライブラリをベンチマークしてみると良いですね。
mattn:「close済み channel に write すると panic になる仕様に悩みます。reader/writerの良い設計はありますか?」という質問ですね。goroutine の外部から channel を閉じると、実行時エラー(パニック)が起こる可能性があります。goroutine の内部で書き込み操作してみてください。要は、 channel を閉じる場所と書き込む場所が違うと、デッドロック(複数の処理が先に進んで消えてしまう状態)が発生しやすく、それがパニックを引き起こします。
渋川: 水道工事と同じで、なるべく上流を止めて制御するほうが良いですね。(下流側から)何かを閉じたいときは、データを書き込む側(ライター)に閉じたい意思を何かしらの方法で伝えて、書き込む側に channel を閉じさせるのが良いです。要はライターに全て集約すると混乱が少ないですね。
渋川: 特定のフレームワークに肩入れしないmattnさんの教義に反するかも知れませんが、いかがでしょう?
mattn:そうですね、私は一通り試してベンチマークするものの、仕事では Echo をよく使います。
渋川: Echo は人気ですよね。
mattn:何が良いかって企業が作ってるのでドキュメントがしっかりしているし、当分は壊れないだろうという安心感ですね。
渋川: 大きなものだと Echo が良いですよね。
mattn:Go 初期から興味を持っていました。初期は「まともに動かない Windows を動かしたい!」という思いで、コントリビュートしてきました。その流れで、標準ライブラリのコードを読み漁りましたが、結局は標準ライブラリのソースが一番勉強になると思います。
渋川: 言語開発者自身の書くコードが、一番 Go を生かすコードになりますよね。
mattn:他の言語のルールを抜け出せない方。Java を意識してGoの良さを活かせず、汚いコードになってしまう人が多いです。Goはフラットに書いた方が綺麗に書けるので。向いてないというよりもったいないと感じることはあります。
渋川: 最近はJavaでも、あまり深くしないのが主流になってきているようですね。
渋川: channel の使い方、非同期が決まると気持ちいいし楽しいですよ。 channel 周りの並列のテクニックは面白いですよ。
転職は思ったよりも、時間がかかる
年収800万円以上のハイキャリアでも、42%が転職に1年以上かかっています。
技術力が高いほど、転職先が限定される
スキルや志向が明確なほど、マッチするポジションは限定されます。
ハイキャリアでも、面接には落ちる
素晴らしいキャリアでも面接に落ちることは珍しくありません。メンタル対策や面接の振り返りをサポートします。
自分のペースで進めたいなら「スカウトをON」
エージェントに頼らずスカウトメールから直接応募も可能です。
Forkwellエージェントは、企業選びから面接対策まで、あなたの転職活動を徹底的にサポートします。「経験チャート」機能を利用すれば、たった3分であなたの経歴を作成できます。