PHPからGoへ:最も時間を要した思考の再構築

私は7年間PHPを書いてきました。そのうち5年間はLaravelを使用していました。Laravelのモノリスからマイクロサービスへの移行を主導するためにGoへ転向したとき、私が携えてきたのはチュートリアルではなく、10年間にわたるPHPの習慣でした。

文法の習得は簡単でした。Goなら午後のひとときで読めるようになります。難しかったのは、コードに対するこれまでの考え方を「アンラーニング(学習棄却)」することでした。

私が習得するのに数ヶ月を要した、マインドセットの転換は以下の通りです:

  • エラーハンドリング PHPでは try/catch を使用していました。エラーはしばしば、グローバルハンドラーへと目に見えない形で送られていました。Goでは、エラーは「値」です。関数は最後の値としてエラーを返します。そして、その場ですぐに処理しなければなりません。最初は余計な手間のように感じました。しかし後になって、これが失敗を可視化してくれることに気づきました。サービスが失敗したとき、エラーメッセージは手がかり(breadcrumbs)のように機能します。膨大なスタックトレースがなくても、どこで失敗が発生したのかを正確に教えてくれるのです。

  • メモリと状態 PHPは「shared-nothing(共有なし)」モデルを採用しています。すべてのリクエストはクリーンな状態で始まります。リクエストが終わればプロセスが終了するため、メモリリークの影響は軽微です。Goは異なります。プロセスは数千のリクエストにわたって生存し続けます。つまり、パッケージレベルの変数はすべてのリクエストで共有されるということです。もし2つのリクエストが同時に map に書き込もうとすれば、プログラム全体がクラッシュします。Goでは、並行性を自分で管理しなければなりません。安全性を保つために、race detectorのようなツールを使用する必要があります。

  • Contextの役割 PHPでは、リクエストが境界線となります。リクエストが終われば、すべてが停止します。Goでは、何も自動的には止まりません。クライアントが切断しても、goroutines が走り続け、リソースを浪費してしまう可能性があります。コード内でキャンセル信号を伝達するために、context.Context を使用しなければなりません。これは、高負荷時でもサービスを健全に保つための背骨のような存在です。

  • 継承よりもコンポジション Laravelはベースクラスの拡張に大きく依存しています。振る舞いを継承することで機能を追加していきます。Goにはクラスの継承がありません。代わりに、暗黙的に満たされる interfaces を使用します。必要なものを、それを使う場所で定義するのです。これにより、コードのテストや差し替えが容易になります。優れたGoコードを書くためには、「あらゆるものを継承して拡張したい」という本能を捨てなければなりませんでした。

  • 巧妙さよりも明快さ PHPではマジックメソッドや動的型付けが可能です。巧妙で表現力豊かなコードを書くことができます。Goは、意図的に「退屈」に設計されています。コンパイラは標準的なフォーマットの使用を強制し、未使用の変数を防ぎます。最初は制約が多いと感じましたが、今ではその価値を理解しています。Goは、コードを書く人ではなく、コードを読む人のために最適化されています。退屈なコードこそ、障害発生時の午前2時でも修正しやすいのです。

新しい言語を学ぶ上で難しいのは、新しい文法ではありません。自分が抱えている「古い前提」なのです。

Source: https://dev.to/econ__11/from-php-to-go-what-took-me-longest-to-rewire-2nfn