printfの縦揃えリーダブルコードの解説の著者の須藤です。リーダブルコードの本編ではプログラマそれぞれがリーダブルなコードを書く方法を紹介しています。リーダブルコードの解説ではチームでリーダブルなコードを書く方法を紹介しています。その次のステップはなんでしょう。それは「チーム外とリーダブルコードの知見を共有する」です。情報を提供するところに情報は集まります。自分たちがリーダブルコードの知見を発信することで知見が集まります。集まるのを待つだけではなく自分から知見を探しにいけばさらに知見を集められます。チーム内だけで知見を共有していると、限られたコンテキストでの知見しか得られなくなってしまいます。チーム外の知見も活用することでチームに新しい知見を導入できます。Railsを使ってWebアプリケーションだけを書いている場合はそのコンテキストの知見ばかり集まります。その知見は、Pythonで機械学習するコードを書いたときに得られる知見とは違います。Railsを使ってWebアプリケーションを書いているときに活かせる知見もあるものです。違うコンテキストの知見を集められればもっとリーダブルなコードを書けるようになります。実際に自分が違うコンテキストのコードを書かなくても、違うコンテキストで書かれたコードを読むだけでも知見を得られます。(もちろん、自分が書いた方が得られる知見は大きいです。)そのため、クリアコードのメンバーは他の人が書いたコードを読んで学んでいます。野生のリーダブルコードを見つけて学んでいるということです。(第一の自由)」があるので野生のリーダブルコードを学び放題です。Code)」としてこのブログで紹介することにしました。前から社内では紹介していたのですが、社内で閉じる必要はないので公開している場所で紹介することにしました。「ノータブルコード」という名前にした理由は次の通りです。「リーダブルコード」っぽい!(語感が似ている)「よい悪い」というより「単に私はここに注目したよ!」というだけだから(「note」ってそういうニュアンスだよね?)「notable」には「重要な」という「よい」ニュアンスがあるみたいなので本当はアレなんですけど。それでは最初のノータブルコードを紹介します。ArrowのRのLinux向けパッケージを改良するプルリクエストで見つけたものです。sprintfを使って文字列をフォーマットしているRのコードです。Rのコードですが、sprintfはいろんな言語にある機能なので、Rを読み書きできない人でも読めるはずです。)sprintfはフォーマット文字列に実際の値を埋め込んで最終的な文字列を生成しますが、どの値がどこに対応するかわかりにくくなりがちです。このコードでは縦に埋め込み場所と埋め込まれる値を揃えて対応をわかりやすくしています。src_dir}と書ける)がありますが、このスタイルは初めてみました。1行が長すぎると使えないとか、埋め込む値の式が長いと使えないとか使える機会は限定される気がしますがおもしろいなぁと思いました。おもしろくて思わずコメントしてしまいました。このコードを書いた人によるとリーダブルなコードになるようにいろんな方法を実験していたとのことでした。工夫して書いたものだったんですね。斬新なコメントでコードの可読性を高めるこの新しいコーナー「ノータブルコード」では、私たちが開発の折々に目にした興味深いコードをご紹介しています。世の中の実際のプロジェクトから、興味深い素材を肩の凝らない形でご紹介していきたいと思いますので、楽しみにしていてください。...https://github.com/github/brubeck/blob/master/src/samplers/statsd.c#L140-L241このコードが面白いのは、ずばりコメントの付け方です。そもそもC言語でパーサを書くのは、非常に面倒な仕事です。RubyやPythonなどのより高級な言語と比べると、その手間のかかり具合は歴然としています。")と書けば簡潔に記述できるのに対して、C言語だと「文字列の先頭から空白文字を探索し、見つかったらノードを初期化して連結リストに追加して次に進む。して表現する必要があります。これは書くのも面倒なら、読むのも大変なコードです。プログラムの作成者の意図は手続きの積み重ねの中に隠れてしまっているので、他の開発者はコードを地道に追うことでそれを読み取るしかないからです。Brubeckのコードが斬新なのは、このC言語の技術的な制約をコメントの付け方によって回避している点です。冒頭にコードの一部を引用していますが、Brubeckの開発者はブロックごとに「いまここではプロトコルのこの部分を解析しています」というコメントを付与することで、書き手の意図を読み手に伝える戦略を選びました。ポイントをはっきりさせるため、コードの一部を取り出して構成を以下に図示します。冒頭のコメントが後続する処理の宣言になっていることが明解に見て取れると思います。私たちは、このコードに感銘を受けたので、自分たちのStatsDパーサでも同じようなブロックコメントを付けることにしました。開発の現場では、時としてコメントは「おまけ」として扱われがちですが、創造的な使い方をすることで、コードの読みやすさを一変させることができるのだ、というのが今回の連載の要点です。関連するテクニックについてこのBrubeckの手法に似ているものとして、コミットメッセージの中で記号を使って、変更内容を視覚的に示すというテクニックがあります。中で紹介しているので(記事中の「英語だけで表現することにこだわらない」の節を参照)、興味のある方はご一読ください。#3でLTをしてきた畑ケです。Prometheusは近年注目されるメトリクスモニタリングツールです。特徴としては非常にスケーラビリティが高く、Pull型でメトリクスを収集するため、Prometheusのインスタンス1つで1万インスタンスのサーバー群を監視できます。#3ではPrometheus本体というよりもそれを取り巻くエコシステムを対象としたミートアップでした。Labが出しているLokiというログ基盤を触ったことがあり、触った時の成果の一つがLTのネタになるのではないかとのことでLTに応募してみたところ、LTに採択されました。イベントの内容まず他の方のされた発表をご紹介します。Corporationコンテナを基盤とする環境でPrometheusを運用していると、例えば、k8sのクラスターを作り直したときにPrometheusで収集したメトリクスが消失してしまう問題があるそうです。このメトリクスがk8sクラスターに紐づいてしまっている問題を解消するのに永続化ストレージを採用し、永続化を試みていました。ThanosというPrometheusを高可用性にし、長期間のログ保存を可能にするソフトウェアを用いて方法をPrometheusで収集したメトリクスを永続化する方法を丹念に調査していた発表でした。MetricsというこちらもPrometheusで収集したメトリクスを長期間永続化するソフトウェアを使って、Prometheusを高負荷環境にも耐えられえるようにした試行錯誤を発表されていました。このソフトウェアにたどり着くまでにいくつかのPrometheus関連のメトリクス永続化ストレージを試されたそうです。VMInsert)に分かれており、そのうちVMStorageは持つデータによって状態を持つのでk8sではStatefulSetとしてデプロイする必要があるそうです。k8sのマルチテナント構成では合計1万Pod以上の監視を安定的に行えるようになったそうです。Lokiについての発表です。Lokiについてのデモを通じてどのようなソフトウェアなのかを解説するのが前半の発表でした。後半はpromtail特集でした。promtailはpromtail.yamlにてPrometheusの設定と同様の設定を流し込むことで設定できるそうです。Lokiも時系列データを保持するのにTSDBを使用しており、これに入れたデータをクエリするのにラベルが必要になるのですが、このラベル設計がうまくないと後に目的の時系列データをクエリするのに苦労するようです。以下は筆者が行ったLTの内容です。BitのGo製のLokiプラグインをフィードバックしてみた話をしました。Lokiに転送する方法をまとめたときに紹介しました。Lokiは開発が活発なソフトウェアということもあり、ドキュメントがあまり見当たりませんでした。BitのGo製Lokiプラグインとして仕上げました。cosmo0920LTではフリーソフトウェアにフィードバックする作法を軽く触れました。Lokiもフリーソフトウェアです。フリーソフトウェアの開発は通常開発者同士が同時に同じ場所に集まっているのではなく、住んでいる国や文化、ひいては暮らしているタイムゾーンも異なる場合があります。フィードバックするには言葉で説明しなければいけません。動機や今困っていることを説明します。この機能が必要か?なぜこの問題を報告したのか?このIssueチケットやプルリクエストでは何を問題にするのか?プルリクエストは小粒なtypo修正ではない限り、Issueチケットに関連付けられるものとして出す方が良いと筆者は考えています。この方針はプロジェクトによって異なります。プルリクエストを出す時はプロジェクトの方針を確認してみてください。プルリクエストでは方針を議論するよりも提出したパッチが前もって議論した方針に合っているか、このプロジェクトに受け入れられる品質となっているか?を議論する場だからです。プルリクエストやIssueチケットにチェックリストが付いているのであれば一通り確認すべきです。問題が発生している報告者の環境を開発者が再現するには十分な情報が書き込まれている必要があります。よいIssueチケットは開発者が見た時にどのようにすればこの問題が再現できるか?がチケットを見ただけで理解できるチケットです。レビュアーが見るべき箇所が発散しておらず、実現したい機能が実現できているか、パッチの変更は妥当かのレビューに集中できるものがよいプルリクエストです。BitのGo製プラグインをフィードバックしてみたところ、ユーザーもそこそこ出てきたようです。Loki自体は一個のバイナリですし、promtailもバイナリ一個で済みます。そのため、Dockerコンテナに載せやすいです。LokiはPrometheusファミリーということもありk8sと非常に親和性がよいです。k8sに載せるにはまずDockerコンテナ化しないといけないということで、Dockerコンテナ化の要望が新たにIssueチケットとして切られました。Lokiの公式イメージとして提供されることになりました。k8sでは複数のサービスを連携して動作させる必要がありますが、手動で連携させるには面倒な場合があります。この煩雑さを解決するソフトウェアはいくつか出ています。BitのGo製Lokiプラグインのレシピが提供されることになりました。筆者はこのhelmのレシピがきっかけでk8sをより深く理解することになりました。自身の成果をフリーソフトウェアにフィードバックするだけで終わりではなく、フィードバックすることにより学びのきっかけを頂けました。Lokiを題材にしてフリーソフトウェアの開発元にフィードバックする作法をLTしました。フリーソフトウェアの問題を手元で回避するのではなく、開発元にフィードバックするのはクリアコードが普段実践している開発スタイルです。フリーソフトウェアを普段使っている方でもフリーソフトウェアの開発元にフィードバックする方法が分からず、手元で問題を回避していたり、手元でパッチを持ったままになっている方もいると思います。Gateワークショップを開催しています。こちらも併せて検討してみてください。Actionsが正式にリリースされました。ActionsはGitHubに組み込まれたCI/CD機能でpush等のGitHub上のイベントをトリガーに任意のアクションを実行できるものです。ActionsではDockerが使用できるので、様々な環境上でテストの実行やビルドなどができます。CIがありますが、AppVeyorは無料のプランだと、ワーカーがプロジェクトにつき1つなので、Groongaのように複数のパッケージを作成するためにジョブが多くなるプロジェクトだと、ビルドとテストの完了に時間がかかってしまい効率的ではありませんでした。Actionsでは、リポジトリーにつき20まで並列で実行できます。CIでは、ビルドした成果物を保存する場所がデフォルトで用意されていないため、正常にビルドできるかの確認やテスト実行はできますが、リリース用のパッケージを作成して置いておくということがやりにくいです。Actions上でパッケージを作成し、作成したパッケージを取得してリリース用のパッケージとしてアップロードするということがやりやすいです。Actionsへ移行しています。いままで、GroongaやMroonga、PGroongaのパッケージは開発者の手元でビルド、署名、アップロードを実施していました。GroongaとMroongaは毎月末にリリースがあるため、毎月末に開発者はパッケージを作る作業を実施しなければなりません。この際にパッケージの作成に失敗するような問題が発覚すると、問題の修正作業にも追われることになります。Actionsはpushトリガーでアクションを実行できるので、変更がリポジトリにpushされた段階でパッケージ作成することで、問題の早期発見に繋がり、問題を発生させた変更もすぐに特定できます。Actionsでパッケージを作成すると開発者はパッケージを署名、アップロードするだけでよくなるので、毎月発生したパッケージ作成時間をなくし、その時間を別の作業に充てることができます。Actionsで行うようにしました。この記事は、その過程で得た知見を共有するために記載しています。Actionsの使い方について記載します。ActionsのアクションはYAML形式で記述します。YAML形式なので、GitHub上でコードとして管理できます。ここからは、Groongaのパッケージ作成のアクションを例に説明していきます。Groongaのパッケージ作成のアクションは、以下の場所にあります。何のイベントをトリガーにしてアクションを実行するかを決めるには、on:を使います。Groongaのアクションではpush毎にアクションが実行されてほしいので、on:にはpushを指定します。Actionsでは、イベントが起こった対象を指定することができます。上記のように定義すると、リポジトリーのどのブランチ、タグにpushされてもアクションが実行されます。全てのブランチやタグが対象ではなく、特定のブランチ、タグにpushされたときだけアクションを実行したい場合は以下のようにします。tagpush等のイベントトリガーではなく、定期的に実行したいアクションがあるケースもあります。その場合には、schedule:を使用します。schedule:の書式はcronの書式で指定できます。ここで指定した時刻はUTCなので、UTCと時差がある日本の場合は時差を考慮に入れて設定する必要があることに注意してください。Groongaでは以下のように設定し、毎朝9時にアクションが実行されるようにしています。*onに指定できるイベントは他にもあり、以下のページにまとまっているので、必要に応じて参考にしてください。実行するアクションはjobs:という単位で定義できます。複数のジョブを作成してそれぞれ並列に実行することもできますし、あるジョブの完了を待ってから実行するというようにジョブ同士の依存関係を定義することもできます。Groongaでは、現状、パッケージを作成するというアクションのみを行っていますので、以下のようにbuildというジョブを1つだけ定義しています。build:ジョブの中ではいくつかのステップに分けてアクションを定義します。Actionsのジョブはステップごとに成功、失敗を表示するので、ジョブをステップに分割しておくと、ジョブが失敗したときの原因特定が容易になります。Groongaでは、パッケージ作成を以下のステップに分割して実行しています。依存パッケージの配置aptを用いたインストールpip3を用いたインストール(ドキュメントビルド用に使うSphinxをインストールしています)パッケージのビルドに必要なソースコードをCloneconfigureの生成ソースアーカイブ作成用にconfigure実行ソースアーカイブをビルドdebian/changelogの更新Docker上でパッケージをビルドビルドされた成果物をGitHubへアップロード具体的には以下のように定義しています。name:で分割したステップに名前をつけます。run:で実行するアクションを定義します。}}どのOSでアクションを実行するかはruns-on:で指定します。Groongaではruns-on:ubuntu-latestと定義して、Ubuntuの最新版でアクションが実行されるようにしています。Serverの最新版)やmacos-latestなどがサポートされています。実行するコマンドはruns-on:で定義したOSに応じて定義する必要があります。Ubuntuでアクションを実行する場合は、デフォルトでBashが使われるので、Bashで実行できるコマンドを使って定義していますwindows-latestを指定した場合は、デフォルトでアクションがPowerShellで実行されるので、アクションはPowerShellで実行できるコマンドで定義する必要があります。(コマンドプロンプトを使うように指定することもできます。)アクションを実行するシェルの指定については、以下のページにも説明があるので、必要に応じて参照してください。Actionsでは、よくある処理をまとめて公開共有することが可能で、それらの処理はsteps:の中で呼び出せます。Groongaでは、リポジトリからソースコードをチェックアウトするのにactions/checkoutを、ビルドしたパッケージをアップロードするのにactions/upload-artifactを使用しています。actions/checkoutは@以下にチェックアウトしたいタグやブランチを指定することができます。actions/checkout@masterと指定しています。actions/checkoutやactions/upload-artifact以外にも様々な処理がactionとして以下のURLで公開されています。https://github.com/marketplace?type=actions複数の環境でアクションを実行する複数の環境(複数のOSであったり、ソフトウェアの複数のバージョン)に対して同じアクションを実行したいケースがあります。それぞれの環境用にワークフローを定義しても良いのですが、同じアクションが複数のワークフローにあると、修正漏れが発生しやすくなってしまい、メンテナンスが煩雑になります。同じアクションを複数の環境で実行したい場合は、matrix:という定義が使えます。6,7,8)に対して同じアクションを実行しています。具体的には以下のように定義しています。packages/yum/repositories/strategy:の中にmatrix:を定義します。matrix:の中に変数を定義してそれをsteps:の中で${{matrix.xx.xx}}の形で参照できます。}}を参照して各OSごとのパッケージをアップロードできるようにしています。}}は、packages/apt/repositories/とpackages/yum/repositories/配下のファイルをアップロードしていることになります。Actionsを使ったパッケージ作成の自動化の方法について記載しました。Actionsに興味はあるが、使っていないという方は、この記事に記載されている内容を参考にして自身のプロジェクトで使用してみてはいかがでしょうか。株式会社クリアコード代表取締役の須藤です。2020-03-12開催のRubyセミナー松江で「Rubyと仕事と自由なソフトウェア」というタイトルの話をします。松江に住んでいる人も松江に住んでいない人も私のファンの人は来てね。Rubyアソシエーションの前田さんに声をかけてもらえたので快諾しました。前田さんに声をかけてもらえてうれしかったです。主な参加者は「業務でRubyを使っているプログラマ」で内容はまかせてくれるということだったので「Rubyと仕事と自由なソフトウェア」にしました。業務でRubyを使っているプログラマには自由なソフトウェアのことを考えて欲しいなと思ったからです。前に似たような話をしたことはあったかな?と思い返してみると関西Ruby会議2017での「株式会社クリアコード」が少し近いかも。なんか、こういう感じじゃなくて、プログラマが活用できるような話にできるといいな。オープンソースは自由ソフトウェアの的を外すのかのような内容を少しでも伝えたいな。岡山が開催されました。ロジカルレプリケーションを使った低コスト高速全文検索」)を再演させていただきました。これは、既存の技術を組み合わせて、なるべく楽に高速、高機能な全文検索ができる仕組みを紹介したものです。当日使用した資料は、以下に公開しています。頂いた質問を元に一部加筆しています。#pgcon19jの内容とほぼ同じです。この構成は同期レプリケーションと非同期レプリケーションのどちらを使っているのか?という質問をいただいていたので、この質問についての回答を追記しています。90ページが追記した内容です。)以下に追記した内容について記載します。PGroongaを使った低コスト高速全文検索の構成を使うことができます。同期、非同期のレプリケーションを使った構成にはそれぞれ以下の特徴があるので、これらの特徴を踏まえた上でユースケースに合わせて選択することをおすすめします。同期レプリケーション同期レプリケーションでは、Subscriberの更新を待ってから応答を返すため、非同期レプリケーションに比べて更新性能は落ちてしまう。Subscriberの更新を待つので、PublisherとSubscriberのデータは同一であることが保証される。非同期レプリケーション非同期レプリケーションでは、Subscriberの更新を待たずに応答を返すため、同期レプリケーションに比べて更新性能は高い。Subscriberの更新を待たないので、PublisherとSubscriberのデータが同一でないことがある。(タイミングによっては、更新前のデータが見えてしまう。)同期レプリケーションでは、PublisherとSubscriberのデータが同一であることが保証されるので、更新したデータを即時検索できることがとても重要な場合は、同期レプリケーションを選択することをおすすめします。ない場合は、非同期レプリケーションを選択して更新性能を落とさず検索できるようにすることをおすすめします。まとめ今回の発表にあたって、追記した内容を中心に紹介いたしました。中国地方DB勉強会の皆様、中国地方のコミュニティでの発表という貴重な機会をいただきありがとうございました。最後に、PGroongaを使った全文検索について、興味、疑問などございましたら、是非、お問い合わせください。4.0とGFDLのデュアルライセンスで自由に利用できます。クリアコードはプログラミングが楽しいソフトウェア開発者を1名募集しています。クリアコードは「クリアコードをいい感じにする人」を1名募集しています。クリアコードはフリーソフトウェア開発で培った技術力を提供しています。Thunderbird)とRubyとGroonga(全文検索)に関連した開発を得意としています。