Go 2にむけて #
Toward Go 2 by Russ Cox
はじめに #
(この文章は本日行われた Gophercon 2017 での私の発表の書き起こしで、Goコミュニティ全体にGo 2のための議論や計画をする中での支援を求めるものです。 動画が公開されたらこちらにリンクする予定です。)
Rob Pike、Robert Griesemer、そしてKen Thompsonが新しいプログラミング言語について数日議論を重ねた後、2007年9月25日にRobが「Go」という名前を提案しました。
翌年、Ian Lance Taylorと私がチームに参加し、5人で2つのコンパイラと標準ライブラリを開発し、その成果が2009年11月10日のオープンソースリリースとなりました。
それからの2年は、できたばかりのGoのオープンソースコミュニティの支援のもとに、大小様々な変更を伴う実験をしつつGoを洗練させ、その結果が2011年10月5日に提案されたGo 1のリリース計画となりました。
さらにGoコミュニティからより多くの協力をうけ、Go 1のリリース計画を改定しつつ、それを元に実装を進め、最終的に2012年3月28日にGo 1をリリースしました。
およそ5年の歳月にわたる創造的で熱狂的な努力によって、“Go"という名前といくつかのアイデアのリストでしかなかったものが、本番環境で利用可能な安定した言語となりました。その最終成果が、Go 1のリリースです。 またこの成果は、変化や仲間内での試行錯誤から安定性の明示的な変遷の結果でもあります。
Go 1への歳月のなかで、私たちはGoを変更し、皆のGoプログラムをほぼ毎週壊していました。私たちはそういった変更がGoの本番での利用を妨げるものになっていたことは承知していました。 本番環境では言語の変更に追従するために毎週変更することなどはできませんから。Go 1のリリース宣言のブログポストにも書いているように、 Go 1のリリースの主要なモチベーションは、信頼できる製品やプロジェクトや出版物(ブログ、チュートリアル、カンファレンスでの発表、書籍)を作成する安定した基盤を提供し、それによってユーザーの製品がコンパイルし続けられ、何年も変更無しで実行し続けられるという信用を勝ち取ることでした。
Go 1がリリースされた後、私たちはGoが設計された本来の目的である、本番環境での利用に時間を費やす必要があることを認識していました。それから言語の変更そのものからあえて離れ、私たちのプロジェクトでのGoの利用や実装の改善へと注力する対象を移しました。私たちはGoをさまざまな新しいシステムにポートし、Goがより効率的に実行するようにパフォーマンスに致命的なあらゆる箇所を再実装しました。また、競合条件検出ツール(race detector)といった新しい重要なツールも追加しました。
いま、私たちはGoを大規模の本番環境に耐える品質のシステムで使う経験を5年重ねてきました。私たちはどのような機能がそのような環境に適合し、またどのような機能がそうでないかの知見をためてきました。 いまこそ、Goの進化と成長の次なる一歩をすすめ、Goの未来を計画するときです。本日、ここにGoコミュニティのみなさん、このGopherConにいる方も、その動画を見ている方も、このGoブログを後日読んでいる方も、そのすべての方々に、私たちとともにGo 2の計画や実装を進める支援をお願いいたします。
目標 #
私たちがいまGoの目標として掲げているものは2007年当時のものと変わりありません。私たちはプログラマが2つのスケーラビリティを管理するにあたって、より効率的になってほしいと考えています。まず本番環境の規模、特にクラウドソフトウェアなどの多くのサーバーと通信するような並行システムの管理。そして開発の規模、特にモダンなオープンソース開発で例示されるような疎結合で多くのエンジニアが協力しあうような巨大なコードベースの管理です。
このようなスケーラビリティはどのような規模の会社でも関係してきます。たった5人のスタートアップ企業も他の企業から提供される巨大なクラウドベースのAPIサービスを利用するでしょうし、自社開発のものよりも多くのオープンソースのソフトウェアを使うことになるでしょう。 スタートアップ企業が必要とする本番環境の規模と開発環境の規模は、Googleが必要とするそれと何も変わりません。
Go 2に対する私たちの目標は、Goがスケールする際の致命的な点を修正することにあります。
(これらの目標に関しては、Rob Pikeが2012年に書いた記事の”Go at Google: Language Design in the Service of Software Engineering“や、私の2015年のGopherConの発表の”Go, Open Source, Community“を参照してください。)
制約 #
Goの目標は当初から何も変わっていませんが、Goが持つ制約は変わっています。最も重要な制約は、既存で利用されているGoの存在です。私たちは少なくとも世界中に50万人のGo開発者がいると見積もっています。つまり、少なくとも何百万ものGoのソースファイルが存在し、10億行のGoコードが存在するということです。 これらのコードを書いているプログラマやこういったソースコードがGoの成功を表しています。しかし、これらはまたGo 2への制約でもあるのです。
Go 2はこれらすべての開発者とともになければなりません。私たちはこうした開発者に、古い慣習を捨て、素晴らしいと感じられるときだけ新しい慣習を覚えて欲しいとお願いする必要があります。
たとえば、Go 1以前では error
型で実装されていたメソッドは String
という名前でしたが、Go 1では Error
という名前に変更しました。これは error
型と自分自身で書式化できる他の型とを区別するために行われました。
ある日、私は error
型を実装していて、何も考えずに Error
メソッドでなく String
メソッドを実装していました。これはもちろんコンパイルできませんでした。5年経った今でも完全には古い慣習を捨てきれていないのです。
このようなわかりやすい名前にするための変更はGo 1では必要でしたが、Go 2ではよっぽどの理由がない限り混乱の元になるでしょう。
またGo 2は既存のすべてのGo 1のソースコードとともになければなりません。私たちはGoのエコシステムを分断させてはなりません。Go 1で書かれたパッケージをインポートしているGo 2のパッケージ、あるいはその逆、といった混在したプログラムが複数年の移行期間の中で苦労することなく動作しなければなりません。
私たちはそれをどのように実現するかを見出さなければなりません。go fix
のような自動化ツールがある部分それを担うでしょう。
混乱を最小限にするために、すべての変更は慎重な考察や計画そしてツールを必要とします。それは代わりに変更の大きさを制限するものでもあります。大きくても2つか3つであり、確実に5つ以上ではないでしょう。
より多くの自然言語で使われる識別子をサポートしたり、2進数リテラルを追加するといった細かなメンテナンス的な変更は勘定に入れていません。細かな変更も重要ではありますが、それらは正しく動作させるのは比較的簡単です。いまここで触れているのは、大きな変更の可能性、たとえばエラー処理の追加サポートやイミュータブルや読み込み専用の値の導入、何らかのジェネリクスの追加、あるいはまだ提案されていないより重要な変更を指しています。 これらの大きな変更のうち、2つか3つしか導入できません。慎重に選ぶ必要があります。
プロセス #
ここで重要な疑問が湧いてくるでしょう。Goを開発するプロセスはどのようなものなのか。
Goの初期の段階では、私たち5人しかいなかったので、隣り合ったガラスの仕切りがある共有ブース2つで働いていました。 みなで1つのオフィスに集まって、問題を議論して、すぐに机に戻ってその解決策を実装に落とすといったことが容易に可能でした。 RobとRobertのオフィスには小さなソファとホワイトボードがあり、誰かがそこに行ってホワイトボードに例を書き始める、ということがよくありました。例を書き終わるころには、他のみなが一区切りつけて、座って議論を始められるようになっている、というのがいつもの流れでした。 こういった形式張らないなやり方は、今日の世界的なGoコミュニティではスケールしないことは明らかでしょう。
Goがオープンソースとして公開された後の仕事の一部は、私たちの特に形式の決まっていなかったプロセスを、メーリングリスト、イシュートラッカー、50万人のGo開発者がいる世界により形式的な形に移行させることでした。しかし、私が知る限りこれまで明示的に私たちのプロセス全体を説明しませんでした。 意識的にそのことを考えていなかったのでしょう。しかし、振り返ってみると、次の図が、最初のプロトタイプを作ったころからの、私たちのGo開発における基本的な流れです。
ステップ1では、Goを使用することで、これによりGoでの体験を重ねます。
ステップ2では、Goで解決が必要であろう問題を特定し、それを知らせ、他の人に説明し、書き下します。
ステップ3では、その問題に対する解決策を提案し、議論を重ね、議論を元に解決策を改訂します。
ステップ4では、その解決策を実装し、評価し、評価を元に改善していきます。
最後のステップ5では、その解決策をリリースし、言語自体やライブラリへの追加、もしくはみなが日々使うツールの形で提供します。
ある変更に関して同じ人がすべてのステップを行う必要はありません。事実、通常は多くの人々があらゆるステップで協力し合い、一つの問題に対して多くの解決策が提案されます。また、どのステップでも、あるアイデアに関して先のステップに進まないほうがいいとわかったら、前のステップに戻ります。
私たちのこのプロセス全体の流れを話したことはないと思うのですが、一部に関しては話したことがあります。 2012年にGo 1をリリースして、いまこそGoを使いはじめるタイミングで、言語自体の変更をやめるときだ、と言ったのがステップ1についての説明でした。 2015年にGoの変更提案プロセスを導入したときには、ステップ3、4、5について説明しました。 しかし、ステップ2に関しては詳細を話したことがなかったので、ここで説明しようと思います。
(Go 1の開発と言語仕様の変更を止める話は、Rob PikeとAndrew GerrandのOSCON 2012の発表である”The Path to Go 1“を参照してください。提案プロセスに関しては、Andrew GerrandのGopherCon 2015での発表の”How Go was Made“や提案プロセスに関するドキュメントを参照してください。)
問題を説明する #
問題を説明するときには2つの段階があります。第一段階(簡単なほう)は問題がなにかを正確に述べる部分です。開発者はこの点においては十分優れています。 結局、私たちが書くテストは解決される問題を記述したもので、その言語ではコンピューターでさえ理解できるほど正確な記述になっているからです。 第二段階(難しいほう)は、その問題の重要性を説明することです。そのことによって、その問題を解決することやその解決策を保守していくことになぜ時間を費やさなければならないのか、みんなが納得できるようになります。問題を正確に記述することと比較して、私たちは問題の重要性を説明する必要に迫られることはあまりなく、そのことにはそこまで優れていません。コンピューターは「なぜこのテストケースは重要なのですか。これが解く必要のある問題であることに間違いはないでしょうか。この問題を解決することは最重要事項ですか。」とは決して尋ねてきません。いずれそうなることはあるかもしれませんが、今日ではそうではありません。
2011年の古い例を見てみましょう。次の例は、Go 1を計画しているときに私が os.Error
から error.Value
への変更について書いたものです。
この報告では、正確な1行で書かれた問題の記述から始まります。問題点は、非常に低い階層のライブラリですべてが os.Error
のために、os
パッケージをインポートしていることです。
その後下線で示したように5行続き、そこで問題の重要性を説明しています。os
パッケージが利用しているライブラリではAPI内でエラーを表現することが出来ず、また他のパッケージはオペレーションシステムとまったく関係ないにも関わらずos
パッケージに依存しています。
この5行でこの問題が重要であると あなた を説得できたでしょうか。それは私が省略した文脈をあなたがどれだけ汲めるかによります。 理解されるためには、読み手がなにを知る必要があるかを予測しなければなりません。この時の読み手、GoogleのGoチームにいた10人、にはこの50語の文章で十分でした。 同じ問題を昨秋のGothamGoでの聴衆、経験も様々で専門分野も異なる人々、に紹介するときには、より多くの文脈が必要で、その時は200語の説明と実コード例とダイアグラムを使いました。 これが今日のGoコミュニティでの現状で、いかなる問題の重要性を説明するにも、同僚に話すときには省略してしまうような文脈を補足する必要があり、特に具体例によって説明されることが望まれます。
問題の重要性を相手に認識させることは本質的なステップです。問題があまり重要でなく見えたら、ほぼあらゆる解決方法がコストの高いものに見えてしまうでしょう。 しかし重要な問題であれば、通常納得のいくコストの解決策が数多くあるものです。特定の解決策を採用することに合意できない場合に、しばしば解決しようとしている問題の重要性そのものの認識がずれているということがあります。これはすごく重要なことなので、後知恵かもしれませんが、この問題を明確に表しているここ最近あった2つの例を見てみましょう。
例: うるう秒 #
最初の例は時間についてです。
あなたがあるイベントにかかる時間を計測したいとします。開始時間を記録し、イベントを実行して、終了時間を記録し、そして終了時間から開始時間を引きます。イベントが10ミリ秒かかれば、引き算の結果は10ミリ秒、もしくは計測誤差により多少の前後はあるかもしれませんが、同程度の結果となります。
start := time.Now() // 3:04:05.000
event()
end := time.Now() // 3:04:05.010
elapsed := end.Sub(start) // 10 ms
この明白な手順は、うるう秒によって失敗します。 私たちの時計が地球の自転と同期がとれていない時に、うるう秒、公式には午後11時59分60秒、が深夜0時の直前に挿入されます。 うるう年と違って、うるう秒は予測可能なパターンに従っていないので、プログラムやAPIの形にするのが難しくなっています。 オペレーションシステムでは61の秒が存在する分を表現するかわりに、深夜0時になる直前に時計を巻き戻すことでうるう秒を実装しています。これによって11時59分には59秒が2回発生します。 この時計のリセットによって時間が巻き戻ったように見えるので、さきほどの10ミリ秒のイベントは-990ミリ秒かかったように計測されてしまいます。
start := time.Now() // 11:59:59.995
event()
end := time.Now() // 11:59:59.005 (really 11:59:60.005)
elapsed := end.Sub(start) // –990 ms
時刻機構(以下 TOD、time-of-day clock)はこのような時計のリセットをまたぐイベントを計測するには不正確なので、オペレーションシステムではモノトニッククロック(monotonic clock)という2つめの時計を提供しています。 この時計には絶対的な意味はなく、ただ秒を刻み、決してリセットされることはありません。
奇妙な時計のリセットの間以外は、モノトニッククロックはTODよりいいところは何もありません。そして、TODには時刻を表現するのに便利という利点があります。そこで単純化のためにGo 1のtime
パッケージではTODしか提供しませんでした。
2015年10月に時計のリセット、特に典型的にうるう秒をまたいだイベントをGoプログラムで計測が正確にできないということが記載されたバグレポートが上げられました。 提案された解決策は、問題の元のタイトルでもある「モノトニッククロックにアクセス可能な新しいAPIを追加する」というものでした。 私はこの問題は新しいAPIを正当化するには重要性が足りないと反対しました。この数カ月前、2015年中頃のうるう秒では、Akamai、Amazon、Googleは時計を1日かけてほんの僅かに遅らせて、追加の1秒を時計を巻き戻すことなくに吸収しました。 この「leap smear」とよばれる手法が今後広く採用されれば、本番環境で問題となるうるう秒での時計のリセットはなくなるものと思われました。 一方で、Goに新しいAPIを追加することは新しい問題を追加する可能性があります。2種類の時計について説明し、ユーザーにどちらの時計をどのような場合に使うべきかを教育し、多くの既存のコードを変換するといったことを、めったに起こらず、そのうち自然と無くなりそうな問題のためにしなければならないのです。
これに対して私たちは明確な解決策がない問題がある場合にいつも取る方法を行いました。ただ待ったのです。 待つことによって、その問題に対する経験を積み、理解を深める時間を確保し、また良い解決策を見つける時間も確保することができました。 この件では、私たちは待つことによって、Cloudflareの小さな障害という形で、問題の重要性を理解することが出来ました。 彼らのGoコードは、DNSリクエストが2016年末のうるう秒の間に-900ミリ秒かかったと計測しました。これによって同時多発でサーバ内でパニックが発生し、最大でDNSクエリの0.2%に障害が発生しました。
CloudflareはGoがその仕様を意図された種類のクラウドシステムそのものであり、彼らの障害はGoがイベントの計測を正確にできないことによって引き起こされました。 それから、これが重要な点なのですが、Cloudflareは、John Graham-Cummingが彼らの経験を”How and why the leap second affected Cloudflare DNS“というタイトルのブログポストで報告しました。 Goを本番環境で利用した経験を具体的な詳細とともに共有することで、JohnとCloudflareは、私たちがうるう秒の時計のリセットの瞬間に発生する問題は、修正されないまま放置されるにはあまりに重大すぎると理解する手助けをしてくれました。 この記事が公開されてから2ヶ月後、解決策を設計し実装して、Go 1.9でリリースされました。(事実、新規のAPIなしで実装しました。)
例: エイリアスの宣言 #
次の例はGoでのエイリアスの宣言のサポートについてです。
過去数年にわたって、Googleは大規模なコード変更に集中するチームを設立してきました。このチームはC++、Go、Java、Pythonなどの言語で書かれた何百万ものソースファイルや何十億行ものコードのコードベースにわたって適用されるAPIのマイグレーションやバグ修正を担うチームです。
そのチームでの仕事から私が学んだことは、APIで使用する名前を別の名前にするときに、クライアントのコードを一度ではなく複数のステップで更新できることの重要性です。
そうするためには、古い名前を利用してる場合は新しい名前に転送してくれるような宣言を書ける必要があります。
C++にはこういった転送のために#define
やtypedef
やusing
の宣言がありますが、Goには何もありません。
もちろん、Goの目標のひとつは、巨大なコードベースでうまくスケールさせることで、Google社内でのGoのコードの量が増えるに連れ、何らかの転送の機構が必要であることと、他のプロジェクトや会社でも彼らのGoのコードベースが成長するに連れて同様の問題に面していることの両方が明確になりました。
2016年3月に、私はRobert GriesemerとRob Pikeと、Goでどのように段階的なコードベースの変更を行っていくかについて話しはじめ、エイリアス宣言という結論に至りました。これこそ必要とした転送の機構です。このとき、私はGoの進化の方向性をとてもよいものだと感じていました。 私たちはGoの最初期からエイリアスについて話してきました。事実、最初の言語仕様のドラフトにはエイリアス宣言の使用例があります。しかしエイリアス、またのちに型エイリアスについて議論するたびに、私たちはそれの良い使い方をはっきりと挙げることはできなかったので、それを取り入れませんでした。 いま、私たちは、洗練された概念だからという理由でではなく、Goの本来の目的であるスケーラブルなソフトウェア開発における重要な実用上の問題を解決するからという理由でエイリアスの追加を提案していました。 私はこの提案がGoの将来の変更のモデルとして機能することを願いました。
晩春に、RobertとRobが提案を書き、RobertはGopherCon 2016のライトニングトークでそれを発表しました。 それからの数カ月間は良い進展がなく、とてもGoの将来の変更のモデルにはなるようなものではありませんでした。 数多く学んだ教訓の一つは、問題の重要性を正しく表現することの重要さでした。
たった今、私はみなさんに問題を説明し、その際にはこの問題がどのように、なぜ発生したのかのいくらかの背景を共有しました。しかし、その問題がみなさんにいずれ影響するかを判断するのに役立つ具体的なコードは明示しませんでした。 去年の夏の提案とライトニングトークでは、C、L、L1、C1〜Cnといったパッケージを使った抽象的な例をだしましたが、開発者が実際に試せるような具体的な例は出しませんでした。 結果として、コミュニティからのフィードバックのほとんどは、エイリアスとはGoogleでの問題を解決するためのものであって、ほかの人には関係がない、という考えに基づくものになってしまいました。
私たちがGoogleでは、はじめうるう秒での時計のリセットをうまく扱えないことの深刻さを正確に把握できていなかったのと同様に、広くGoコミュニティ全体に段階的なコードマイグレーションの取り扱いと大規模な変更の間の修正の深刻さをうまく伝えられていませんでした。
秋に、私たちは再開しました。私は発表をし、そしてオープンソースのコードベースから引用した複数の具体例を使って問題を説明する記事を書きました。それによってこの問題がGoogleだけでなく、みなに起こりえることを説明したのです。 いまでは、より多くの人々がこの問題を理解し、その深刻性を理解することができています。そしてどのような解決方法が最適かについて建設的な議論も行いました。 その結果として型エイリアスがGo 1.9で導入され、これによってGoがより大きな規模のコードベースにスケールする助けとなるでしょう。
体験レポート #
ここで得られた教訓は、問題の深刻性を異なる環境の人が理解できるように記述することは難しいけれど本質的なことであるということです。コミュニティとしてGoに対する大きな変更を議論するためには、解決したいいかなる問題もその深刻性を記述することに特別な注意を払う必要があります。 もっとも明確な方法は、Cloudflareのブログポストや私のリファクタリングに関する記事のように、その問題がプログラムや本番環境に実際にどのような影響をおよぼすか見せることです。
このような体験レポートは抽象的な問題を具体的なものに変えてくれ、その深刻性を理解する手助けとなります。 またこれらはテストケースとしても役立ちます。いかなる解決策の提案も実際にそのレポートが記載している実世界の問題への影響を計測することで評価できます。
たとえば、最近はジェネリクスについて調査しているのですが、私にはGoユーザーがジェネリクスを使って解決する必要がある詳細で具体的な問題がいまいちうまく想像できないのです。 結果として、ジェネリクスメソッド、つまりレシーバーとは区別されてパラメータ化されたメソッドをサポートするかといった設計に関する質問に答えることができません。 もし実世界での利用例が多くあれば、深刻なものを調査することによってこのような問題に答えることができるでしょう。
他の例としては、いくつかの方法でerror
インターフェースを拡張する提案を見てきましたが、私は巨大なGoプログラムがどのようにエラーを解釈し扱おうとしているかを示している体験レポートを1つも見たことがなく、現状のerror
インターフェースがそういった動作をどのように妨げているかを示しているものはなおさら見たことがありません。
こういったレポートがあれば、まず問題の詳細と深刻さをより良く理解する手助けとなります。これは問題を解決する前にまずやらなければならないことです。
もっと例を挙げることもできるのですが、このあたりにしておきます。すべてのGoへの大きな変更は、Goが今日どのように人々に利用されているか、なぜ十分にうまくいっていないかを記載した1つ以上の体験レポートによって動機づけられるべきです。 疑う余地もなく大きな変更に対して、そのような体験レポートを見ることは多くなく、その中でも実例を伴ったものはほとんどありません。
このような体験レポートはGo 2の提案プロセスの原料になります。そしてみなさんすべてにそういった体験レポートを書いていただく必要があります。それらが私たちがみなさんのGoでの体験を理解する手助けとなります。
50万人のGoユーザがいて、広範囲の様々な環境で働いています。
ご自身のブログや、Medium、GitHub Gist(Markdownにするために.md
拡張子をつけてください)やGoogle Doc、あるいはその他みなさんが好きな公開方法で構わないので、ぜひご自身の記事を書いてください。
記事を公開したら、あたらしいWikiのページに追加してください。 https://golang.org/wiki/ExperienceReports
解決策 #
もう解決する必要がある問題をどのように特定して説明するかはわかったので、すべての問題が言語仕様の変更で解決されるのが最適ではなく、またそれで良いことを簡単に触れたいと思います。
解決したい問題の一つに、コンピューターがしばしば基本的な算術演算の間に追加で結果を計算するが、Goがその結果には直接アクセスさせない、というものがあります。2013年にRobertが2値の結果(「カンマOK(comma-ok)」)表現のアイデアを基本的な算術に拡張するという提案を行いました。
たとえば、x
とy
がuint32
の値であったら、lo, hi = x * y
は積の通常の下の32ビットだけでなく上の32ビットも返すというものです。
この問題は特に深刻なようには見えなかったので、解決策になり得るものを記録しましたが、実装はしませんでした。待ったのです。
最近になって、私たちはGo 1.9でmath/bit
パッケージを設計しました。これは様々なビット操作関数を持っています。
package bits // import "math/bits"
func LeadingZeros32(x uint32) int
func Len32(x uint32) int
func OnesCount32(x uint32) int
func Reverse32(x uint32) uint32
func ReverseBytes32(x uint32) uint32
func RotateLeft32(x uint32, k int) uint32
func TrailingZeros32(x uint32) int
...
このパッケージでは各関数は良いGo実装となっていますが、コンパイラーは可能なときは特別なハードウェア処理に置き換えます。math/bits
での経験から、いまやRobertと私は言語仕様を変えて追加の算術結果を取得できるようにすることは賢いことではないと認識し、かわりに適切な関数をmath/bits
のようなパッケージ内に定義するべきだと理解しました。
ここでの最適解はライブラリの変更であり、言語仕様の変更ではありません。
Go 1.0以降での他の解決したい問題として、ゴルーチンと共有メモリはGoプログラムに競合状態を入れ込みやすく、結果としてクラッシュを引き起こしたり、本番環境で他のおかしな状況を引き起こしたりしてしまう、という事実がありました。 言語仕様の変更に基づく解決策としては、データ競合を禁止するなんらかの方法を探して、競合状態のあるプログラムを書けないようにしたり、あるいは少なくともコンパイルができないようにする、といった方法がありえるでしょう。 Goのような言語にそういった方法をどのように適用させるかはいまだにプログラミング言語の世界では解決していない課題です。 かわりに私たちは新たなツールを配布元に追加して、簡単に使えるようにしました。そのツール、競合状態検出ツール(race detector)は、Goを使う上でなくてはならない存在になりました。 ここでの最適解はランタイムやツールの変更であり、言語仕様の変更ではありませんでした。
もちろん、言語仕様の変更も今後ありうるでしょう、しかしすべての問題が言語仕様の変更で解決されるのが最適解になるわけではありません。
Go 2のリリース #
最後に、私たちはどのようにGo 2をリリースするのでしょうか。
私が思うに、最善な計画としてGo 2の後方互換性がある部分を、Go 1のリリース順序に則って徐々に機能毎にリリースしていくのが良いと考えています。 これにはいくつか重要な性質があります。第一に、この方法はGo 1のリリースを通常のスケジュールを保っていて、状況に合わせてユーザが依存しているバグの修正や改善を行うことができます。第二に、Go 1とGo 2で開発リソースが分かれることを避けられます。 第三にGo 1とGo 2で分岐するのを避けることができ、マイグレーションが容易になります。第四に、私たちが一度に1つの変更に集中し、リリースができるようになり、品質を維持する助けになります。 第五に、私たちが後方互換を保つように機能を設計する励みになるということです。
いかなる変更もGo 1のリリースに入り始める前に議論や計画をするための時間が必要ですが、私は1年後のGo 1.12くらいから小さな変更を入れ始めるのが妥当ではないかと見ています。 またその間に、パッケージ管理のサポートをまず入れる時間を作ることができます。
たとえばGo 1.20くらいで後方互換の仕事が終わったら、後方互換性のない変更をGo 2.0に入れることができるでしょう。 後方互換性のない変更がまったくないとわかった場合には、おそらくGo 1.20をGo 2.0と宣言するだけになるでしょう。 いずれにせよ、Go 1系のリリース順序からGo 2系のリリース順序に移ったタイミングで、最後のGo 1系のリリースのサポート期間を伸ばすと思います。
ここに書いたことはすべてちょっとした推測であり、先に書いた特定のリリース番号は大雑把に見積もっただけのものですが、はっきりと言えることは私たちはGo 1を捨てようというつもりはありません。 そして事実、私たちは可能な限りGo 1を長生きさせたいと考えています。
支援が必要です #
私たちはあなたがたの支援が必要です
本日より、Go 2に向けての話し合いが始まります。それは、メーリングリストやイシュートラッカーのような公開された、オープンな場で行われるものです。それに伴うあらゆる場所で支援してください。
いま、私たちがなによりも必要としているのは体験レポートです。 あなたの環境ではGoがどう動作しているか、そして何よりも、どう動作していないかを是非教えてください。 実例や具体的な詳細、そして実体験を盛り込んだブログポストを書いてください。 そしてそのリンクを私たちのWikiページに追加してください。 これこそが私たち、GoコミュニティがGoに変化をもたらすときの議論の始め方です。
ご清聴ありがとうございました。
By Russ Cox
あわせて読みたい #
- Go 2016 Survey Results
- Participate in the 2016 Go User Survey and Company Questionnaire
- Go, Open Source, Community
- Four years of Go
- Get thee to a Go meetup
- Go turns three
- Getting to know the Go community
- The Go Programming Language turns two
- Spotlight on external Go libraries
- Third-party libraries: goprotobuf and beyond