Magnolia Tech

いつもコードのことばかり考えている人のために。

Keychron Q60 MAXを買った

Keychron Q60 Max QMK/VIA ワイヤレス カスタム メカニカルキーボード(US ANSI 配列) – Keychron Japan

去年は、Keychron Q60はいいぞ!と言い続けていましたが、ずっとベアボーンモデルのみの販売が続いていました。

「いよいよ終売か?」と思っていたら、確かに終売になりましたが、Keychronの新ラインナップであるMAX版がリリースされました。

従来のQ60に比べると無線化(Bluetoothか、2.4GHzドングル)されたことと、さらなる静穏化がされたことが大きな変化点です。

技適を通過していないので、国内では無線利用は違法になってしまいますので注意しましょう。

また、今回からスイッチがGateron Jupiterシリーズに変わっています。今回はYellow軸を選んでみました。

Gateron Jupiter Switch Set

この組み合わせでの「コトコト」した感じが、非常にいいですね。静音化の効果で音も従来のQ60より、静かになっています。ただ、それでもオフィスとかに持ち込むか?と言われると、そこは静音スイッチに入れ替えないと、配慮がかけた感じがしますね。

HHKB配列のキーボードが欲しい人には、かなり有望な選択肢になると思います。


追記:ちなみに、オリジナルのHHKBや、他のHHKB配列キーボードより圧倒的にKeychron Q60が気に入っているのは、スイッチ一つでWinとMacの配列の切り替えができるところです 普段から両方を頻繁に切り替えながら使う上では最高の機能です

Bizの意思決定はいかに例外を捨てるか、Devの意思決定はいかに例外を拾うか

タイトルに書かれていることが全てなのだけど、時にBizの人と、Devの人の目線というか、着眼点の違い、意思決定に至るスピード感の違いの元は、どこから来るのか?と思った時に、それを表現する言葉がようやく見つかった気がする。

以前書いた関心の非対称性に関するエントリを一言で言うとって話なんだけど。

blog.magnolia.tech

で、結局これをどうやって乗り越えていくのか?ってことなのけどね。

誰も悪意の有る人はおらず、みんな一生懸命なのに、それぞれの価値観の違いが擦り合わないことで上手く行かない、みたいなことにならないためにはお互いの価値観を知ることが第一歩なんですよね。

『systemdの思想と機能 ―Linuxを支えるシステム管理のためのソフトウェアスイート』でsystemdの機能を再確認する

いつの間にかどのLinuxディストリビューションでも標準で使われるようになったsystemd。なんとなくsystemctlを叩いてサービスを立ち上げていたけど、いまいち「なんで必要なの?」とか、「いろいろ機能があるっぽいけど、何ができるの?」といったことをちゃんと勉強しないままここまで来てしまったので、この本でおさらい。

単なる機能紹介ではなく、「なぜsystemdはこんな広範囲な機能を扱うのか?」という機能が作られた背景から説明されているところがいいですね。

この本を読むまでcore dumpまで管理されるようになったことを知りませんでした。


本書を読めば、systemdはサービスが動く環境を用意するために必要な機能を備えていった結果、多数の機能を持つようになった、ということがよく分かります。

プロセスを安全に立ち上げ、維持し、実行状態をモニタリング(監視、ログ)するために必要な一連の機能がすべて揃っていて、ワンストップでsystemdだけで対応することができるところが、その目的だということがよく分かりました

昔は、アプリケーション独自のサービス実行管理は、daemontoolsとかでやってましたね。

systemdのリファレンスとか、実装詳細、コマンドオプションの解説までやるととても終わらないので、全体で200ページ弱の中で駆け足で機能概要と、その必要性が説明されていて、一通り読めばsystemdを使う上でのスタート地点に立てます。その点にフォーカスしているのが本書の良いところ。

サクっと読めます。でもまぁ、それで「systemd、チョットワカル」にはならないところがsystemdの機能の多さなんですけど

特にアプリケーションレイヤーの人は、第10章のjournaldと、第11章のcore dumpあたりを押さえておくと良いと思いました。

BRIMFORD キーキャップ キースイッチ プラーを買ったら最高だった

え!何?なんで今まで買ってなかったの!!

今まで割と簡易なキースイッチプラーを使って、うまくキースイッチを抜けずに苦労していたのだけど、この形のプラー、一発でバシっとハマって、シュッと抜けて最高だ!

キースイッチの交換をする人は、絶対にこの形のキースイッチプラーを買った方がいいよ!!

どんどんキースイッチを買って、どんどん交換する機運か!(待て)

2023年買ったもの(技術書とか)

2023年のお買い物、技術書編です。

技術書以外はこちら

blog.magnolia.tech

とりあえずノータイムで買っちゃえ!損は無いよ!という3冊

他に、類似の本がないか、有ってもこっち買っておけばよくね?という3冊

なっとく!関数型プログラミング

Scalaをベースとした関数型プログラミングの学習本。オブジェクト指向言語であり、関数型プログラミング言語であるScalaの特性を生かして、命令型から宣言型のコードの書き方への変え方を学んでいくスタイル。

前半の凄まじい丁寧な学習のステップと、後半の「ここまで一気にやらなくても良くない?」の落差もすごいけど、ページ数の厚さに躊躇せず、前半1/3くらいを時間をかけて丁寧に学習するくらいが良いと思います。

周りに良い先生が居れば別ですが、そうでなければこの本でじっくり学ぶのはかなりお勧めです。

実践プロパティベーステスト

プロパティベーステストの本がこの先、さらに日本語で出版される日が来るのか分かりませんが、かなり確率は低い気もするので、さっさとこの本を読んで学習しておくと良いと思います。

例題がErlang、Elixirなので、プロダクションコードを書く言語としては決してメジャーとは言えない言語ですが、逆に言えば自分が使っている言語用のプロパティベーステストのライブラリを使って書き換えていく、という学習法が取れるところが良いですね。

ちょうぜつソフトウェア設計入門 - PHPで理解するオブジェクト指向の活用

出版自体は2022年の12月の終わりですが、今年に入っても何度か読み直しましたし、色々な人にお勧めしました。今年の本といってもいいかな。

表紙のかわいさに騙されてはいけません。これ本当にちゃんと理解する気持ちで、コードを書いたり、周辺情報を調査したり、周りの人とディスカッションしながら読み進めたら、平気で1年かかる密度の本です。

現代的なソフトウェアのプラクティスが、ものすごい密度で書かれています。でも表層的に読んで「へーそうなんだ」と思っても、意味はないので自身の血肉となるまで考えながら読むことが大切です。

その分野を知りたい人にはぜひお勧めの本

技術カンファレンスのマスターガイド:企画から運営までの完全手引き

techbookfest.org

残念ながら現時点で入手する方法が存在しませんが、カンファレンスや勉強会を運営する人のためのリファレンスの決定版。長年勉強会を開催してきた人でも、新たな学びや視点がてんこ盛りなので、機会が有ればぜひ読んだ方がいいです。

Functional Programming in Scala, Second Edition

早く日本語版が出ないかなー、出たら色んな人にお勧めするのになーと、邦訳を首を長くして待っている1冊。

Scalaをベースに、関数型プログラミング用のライブラリ(Listとか、Optionとか、Stateとか)を実際に作りながら、その概念を理解する、というストロングスタイルな1冊。

『なっとく!関数型プログラミング』が、表側(利用者視点)から関数型プログラミングを学ぶ本だとしたら、こちらは裏側(ライブラリの提供者側視点)から関数型プログラミングを学ぶ本といえます。 この2冊をセットで読み進めて、そのテストを『実践プロパティベーステスト』による理解を元に、プロパティベーステストで書けばかなりの実力がつくこと間違いなしです。

1st Editionはずいぶん前に日本語訳が出ていて、今でも内容は全然古くなっていないので、Scala3対応や、回答の解説はGitHub上のサンプルコードを読み解きます!という人にはこちらでも大丈夫です。ただ、練習問題の解答例が本誌上に無い中で読み進められる人はなかなか居ないかも……とは思ってしまいました。

スクラムの拡張による組織づくり

スクラム開発をどうやってスケールさせていくか?その方法論はいくつか有るけど、その中でも「Scrum@Scale」をベースにコミュニケーションを軸とした組織の作り方、運用の仕方を解説していく本。

組織論みたいな本、なかなか苦手なのですが、これは開発組織をスケールさせるための本、ということで非常に興味深く読むことができました。

ソフトウェア設計のトレードオフと誤り

個人的には、本書の中心的なテーマであるトレードオフではなく、「7章 日付と時間のデータを効率よく扱う」の内容が一番刺さった。この章、全然トレードオフの話をしていなくて、いかに日付や時刻、時間を扱うライブラリの設計が難しいのか?ということが延々と語られる章になっている。他の章も十分に役に立つのだけど、これだけ身近なテーマである「日付と時間」がここまで大変な設計の上に成り立っているのか!ということはみんな理解しておいた方が良いと思う。

ブログのエントリも凄いブクマを集めたけど、みんなそれだけこのテーマに関心が高い、ということなのだと思っている。

blog.magnolia.tech

レガシーコードとどう付き合うか

若干、タイトルと中身のメインテーマがずれていると思うけど、投資フェーズと組織の開発戦略、採用を絡めて論じる、という切り口がすごく新鮮で、これは(それぞれの会社ごとに状況は違っているかもしれないが)、読んでおいて損の無い1冊だな、と思いました。

オブザーバビリティ・エンジニアリング

どんどんアプリケーションを構成するコンポーネントが増えていくなかで、「監視、つらい」という場面も増えてきました。

本書は、構造化ログ、トレース、OpenTelemetryというキーワードを元に、どうやってアプリケーションのリアルな振る舞いを追いかけ、おかしな挙動を速やかに検知するのか、ということを理解するための本です。

いやー、ほんと、異常終了すればスタックトレースと、アクセスログを見ればよかった時代は、もう遠い過去の世界なんですね、というか、なんでこんなに辛いんだっけ?というと、それを軽減するための仕組みを導入していないだけでは?という話に行き着くので、まずは読んでおきましょう。

マスタリングLinuxシェルスクリプト 第2版

令和最新版のシェルスクリプトの入門書とリファレンスがセットになった1冊。

ブログのエントリでも書きましたけど、なんかコンテナの勉強しているのか、シェルスクリプトの勉強をしているのか自分でも分からなくなってきたので、もう一回腰を据えて勉強するぞ!と思って読みました。

blog.magnolia.tech


Rustとか、コンテナとか、クラウドとか、その辺が全然フォローできていないんですけど、それはそれであくまで自分の興味の向くままに読んだ結果なので。

積んだまま読めていない本も本当にたくさん有るし、読んでも紹介するほどでもないな、と思った本はブログにも書いてませんしね。

というわけで、来年も色々な本を読んでいきたいと思っています。

なお、技術書を除くと、この2冊がよかった。

前者は小沢健二のソロデビュー時のマネジメントや、数々のコンピレーションアルバムの編集をやっていた井出靖さんの視点から見た90年代の東京の音楽シーンの回顧録。 後者は三体の前日譚というか、直接の繋がりは無いけど、いかにも劉 慈欣らしい、静かなムードの中で進むSF。

『井出靖 / Rolling On The Road 僕が体験した東京の1960年代から90年代まで』

『劉 慈欣 / 三体0【ゼロ】 球状閃電』


なお、2022年版はこちら

blog.magnolia.tech

2023年買ったもの(技術書以外)

2023年に買ったもの

技術書以外編

技術書編は、こちら

blog.magnolia.tech

WH-1000XM5

特に説明不要なノイズキャンセルヘッドホンの定番。それまで使っていたWH-1000XM3が完全に壊れてノイズキャンセルどころか、謎のノイズが出てしまう症状が出たため買い替え。

Bluetooth接続が圧倒的に早くなったのと、マルチポイント接続により同時に複数の機器と接続できるようになったのは、割と頻繁に接続先を変える自分はとても利便性が上がったので、それだけでも買い換える価値が有った。

ノイズキャンセルの効きも良くなり、地下鉄や飛行機などの、ノイズが多い移動手段が多い人にはぴったりの一台。

5万円を超える価格はオーディオ機器としてみるとちょっと高いけど、音楽鑑賞から、オンラインミーティングまでこれ一台でなんでもできてしまうので、コストパフォーマンスは非常に高い。一時はほんと朝から晩までずっと付けっぱなしで過ごしていた。

MDR-7506

自宅でのオンラインミーティングなどで、独立マイクも用意できるし、ノイズキャンセルが不要な場所ではこちらを使うようにしている。

有線の煩わしさは有るけど、軽いのと、充電を気にせず使える気軽さもいいし、どんな環境でも「とりあえずプラグを刺せば音が出てくる」という安心感は、とにかく安定しておいてほしいオンラインミーティング環境では必須。

これも複数のPCを使い分けることが多いので、その切り替えごとに、操作をしたくない、という観点で有線の安心感を選択。

ソニーのヘッドホンといえば、MDR-CD900STも惹かれたけど、接続がミニプラグの方が直接PCに挿せて便利なので、MDR-7506を選択。

TimeTimer

年齢が上がったせいか、だんだんと集中力の持続時間が落ちてきているなーと感じることが多くなってきたので、見やすいタイマーを導入。

厳密に、ポモドーロテクニックを使っているわけではないですが、15分とか、30分とか、「この決めた時間の間は集中する!」と決めると、それなりに集中力も持続するので、重宝しています。

何のギミックも無く、ただぐるっと手でツマミを回した分だけ時間を測る簡単仕様。

あと、デザインがかわいくて、机の上に置いておきたくなるデザイン。

Lenovo ThinkCentre M75 Tiny

今年だけで2台も買ってしまった。

www.lenovo.com

ここ数年色々なメーカーからミニPCが発売されていて、あまりの価格の安さに採算取れるのかな?と心配になるのだけど、サポート面などでの不安も聞こえてくる。

そのため、ちょっと価格帯は上がるけど、Lenovo純正の複数年サポートが付けられる安心感を優先して選択。

主な使い方は、プリインストールされているWindowsを完全に消去して、Linuxをインストールして、個人の開発環境用。 やっぱりWSLより、素のLinuxが動く環境が有った方が余計な苦労をしないで済むのが楽だし、一台の高スペックPCより、複数台のミドルスペックPCの方がコストパフォーマンスも良い。

Lenovoの場合、Linuxからでもファームウェアアップデートが可能なところもオススメのポイントの一つ。

若干ファンが煩いので、普段はテレビボードの下にしまい込んでリモートアクセスでしか使わないけど、そういう使い方ができるのもミニPCの良いところ。

Keychron Q60

Keychron Q60 QMK Custom Mechanical Keyboard – Keychron | Wireless Mechanical Keyboards for Mac, Windows and Android

Keychronから出ているHHKB完コピメカニカルキーボード。

本家は、HHKB Studioを出したけど、割とギミック多めで、それは特に要らないかも...という人向け。

とにかくシンプルなデザインがイイ!有線専用のQ60は既に終売になっていて、今は無線と、更なる静音化のために構造が見直されたQ60 MAXが現行モデルになっています。

なお、本家より便利なのは、Win-Macの配列変更のスイッチがあるところで、これが有るだけでも日常的にWindows機と、Macを切り替えながら使っている自分にとっては最高の機能です。 本家のディップスイッチ方式はさすがになーと思っていたので

今のところ、キーキャップと、スイッチを交換していて、この組み合わせの打鍵感が過去最高に気に入っているので、当面はこの組み合わせ使っていくと思います。

Keychron Q60 Max QMK/VIA Wireless Custom Mechanical Keyboard – Keychron | Wireless Mechanical Keyboards for Mac, Windows and Android

Kailh Clione Limacina Switch tactile

CHERRY INDUSTRIAL KEYS

Cherry Industrial Keysnovelkeys.com

Kailh Midnight Silent V2 Switch / Tactile

Kailh Clione Limacina Switch tactileの打鍵感は最高なのですが、周りに人が居るような環境だとちょっと使うのを躊躇するくらいの軽快な打鍵音が響いてしまうので、そんな時は、同じKALIHのMidnight Silent V2というスイッチを使っています。

KEEBMAT Premium Felt Edition

キーボードと、トラックパットをまとめてデスクマットの上に置きたいので、KEEBMATのKEEBMAT Premium Felt Editionというのを導入しました。

KEEBMAT™ Premium Felt Edition (incl. Free Coaster!)

KEEBMATは、小型キーボードのサイズにぴったり合わせたキーボードマットが有名ですが、フェルトエディションの一番大きなサイズは、完全にデスクマットとして使える大きさで、お勧めです。

ただし、アイロンが無いと綺麗に平らにならないので気をつけてください。

Lamy2000 4色ボールペン

定番中の定番ですが、定期的に書い直していて、通算3本目を買いました。

どんなにPCやスマホタブレットが進化しても、ボールペンの書き味からくるフィードバックにはまだ敵わないんですよね。


まだまだ買ったものが有った気もしますが、ついにキーボード沼にハマったな……というのが今年の一番のトピックですね


2023/12/30 追記 はてブでコメントいただいたので追記

  • WH-1000XM5のマイク WH-1000XM3を買ってしばらくしてからコロナ禍が始まり、オンラインミーティングの環境がイマイチ揃えられなくてしばらく不便な音声環境が続いていた。でもある日「あれ?これ、もしかしてノイズキャンセル用のマイクで通話もできるのか!」と気づいて以来、WH-1000XM3をミーティング用に使うことで劇的に通話環境が改善されたので、さらに音声用マイクとしての改善が反映されているというのも、WH-1000XM5に決めた理由の一つだった。

    www.sony.jp

  • HHKBライク配列のMacと、Windowsの日本語入力について

    Windowsの方でのローマ字入力と、英字入力の切り替えを「CTRL+Space」に設定しておくところがポイントです。これでWindowsMacで同じキー操作になります。あとは、Spaceキーの横のキーをWindowsモードの時はALTキー、Macモードの時はCommandキー、その更に横をOptキーになるように配列を変更しておけば完璧です。それぞれのOSに適した配置になるので、操作性がキープできます(そもそもWindowsはCTRL、MacはCommandになっているショートカットは慣れましょう、でしかないですが)


2022年版はこちら

blog.magnolia.tech

『実践プロパティベーステスト』の例題をScalaで解いていく その3

『実践プロパティベーステスト』の例題をScalaで解いていくシリーズの第3回目です。

前回は、テストが失敗して、収縮した結果を確認する、という内容でした。

blog.magnolia.tech

今回は第3章に入ります。


初めてプロパティベーステストを書き始めたとき、サンプルコードだけを見て、「なるほどこう書けばいいのか!」と納得して書き始めたものの、すぐにピタっと手が止まりました。それは、「テスティングフレームワークが提供するAPIは分かった、でもそれを使ってどんなテストを書けば、プロパティベーステストになるのか?」...このとっかかりが分かりませんでした。

色々なテストコードの事例を見ながら、実際に自分で書いてみて少しずつ感覚をつかんでいきましたが、やはり何らかのとっかかりが有ると理解が早く進みます。


第3章「プロパティで考える」では、以下の4つの観点でプロパティベーステストを書くときの観点を紹介しています。

  1. モデル化

    テストしたいコードと同じ振る舞いをするコード(たいてい効率が悪い)と、結果を比較する手法です。 ビジネスロジックのような固有の振る舞いをコードに落とすような場合には、モデル化できるものは少ないと思いますが、汎用的なライブラリや、他の言語の実装から移植などでは有効です。

  2. 事例テストを汎化する

    これが一番分かりやすい取り組み方と言えます。通常のユニットテストを書いてみて、そのパターンを元に抽象化を行い、プロパティベーステストとしてまとめていく手法です。例えばリストの要素がゼロの場合、一つの場合、二つの場合...と増やしていったときに、その長さに依存しないような抽象化ができれば、プロパティベースのテストとして実装できます。

    ユニットテストベースのテストを考え、そこから抽象を見出す……という流れが遠回りにも感じられますが、具体的なテストから出発する分、結局これが一番早く、確実な手法と言えます。

  3. 不変条件

    一つのテストですべてをカバーするのではなく、複数のテストを組み合わせて、そのコードの振る舞いの確からしさを総合的に判断するための手法です。それぞれ分解した観点ごとに、不変条件(必ず満たさなければいけない条件)を特定し、それらがすべてテストをパスすれば、コード全体としての確からしさが確認できた、と言えるところからの発想です。

    何をもって不変条件が網羅したか?と考えるのは非常に難しいですが、一つの指標としてテストのカバレッジによる判断は有効であるといえるでしょう。

  4. 対称プロパティ

    JSONライブラリのような、エンコーダと、デコーダがセットで提供され、変換⇒再変換により元の結果に戻れば、その結果の正当性が確認できる、という手法です。

    適用できる場面は限られますが、適用できる場合は確実な方法です。

Scalaへ移植していく

本の中で紹介されていたbiggest関数をScalaに移植します。

package Pbt

object PbtHelper:
  def biggest[A](list: List[A])(using Numeric[A]): A =

    def go[A](l: List[A], m: A)(using Numeric[A]): A =
      val num = summon[Numeric[A]]

      (l, m) match
        case (Nil, i)    => i
        case (h :: t, i) if num.gteq(h, i) => go(t, h)
        case (_ :: t, i) => go(t, i)

    go(list.tail, list.head)

再帰とパターンマッチで書き直すと、割とさっぱりと書けます。 また、特定の型に依存したくなかったので、比較関数を提供するNumericを継承している型に限定して指定できるようにusing Numeric[A]の指定を入れています。ネストされた内部関数にまでusingは効果を及ぼさないので、内部関数の中でも指定しています。

summonはScala3から導入された関数で、usingの引数に型のみを指定した場合に、具体的なインスタンスを取得します。

この辺りの仕組みは、以下の公式ドキュメントに詳しく書かれています。

docs.scala-lang.org

次にテストを書いていきます。エンコードと、デコードは、ちょっと面倒だったので、circeで雑に済ませてしまいました。

import org.scalacheck.Properties
import org.scalacheck.Prop.forAll
import org.scalacheck.Arbitrary.*
import org.scalacheck.Gen.*

import Pbt.*

import io.circe.syntax.*
import io.circe.Json

object PbtTest extends Properties("Pbt Test"):

  property("最大の要素を見つける") =
    forAll(nonEmptyListOf(arbitrary[Int])): (x: List[Int]) =>
      PbtHelper.biggest(x) == modelBiggest(x)

  def modelBiggest(list: List[Int]): Int = list.sorted.last

  property("最後の数を選ぶ") =
    forAll(listOf(arbitrary[Int]), arbitrary[Int]): (list: List[Int], knownLast: Int) =>
      val knownList = list :+ knownLast
      knownLast == knownList.last

  property("ソート済みリストは整列したペアを持つ") =
    forAll(listOf(arbitrary[Int])): (list: List[Int]) =>
      isOrdered(list.sorted)

  def isOrdered(list: List[Int]): Boolean =
    list match
      case h1 :: h2 :: t => (h1 <= h2) && isOrdered(h2 :: t)
      case _ => true // 2要素未満のリスト

  property("ソート済みのリストはサイズを維持する") =
    forAll(listOf(arbitrary[Int])): (list: List[Int]) =>
      list.length == list.sorted.length

  property("何も要素が追加されなかった") =
    forAll(listOf(arbitrary[Int])): (list: List[Int]) =>
      val sorted = list.sorted
      sorted.forall(x => list.contains(x))

  property("何も要素が削除されなかった") =
    forAll(listOf(arbitrary[Int])): (list: List[Int]) =>
      val sorted = list.sorted
      list.forall(x => sorted.contains(x))
 
  property("対称的なエンコードとデコード") =
    forAll: (l: List[Map[String, Int]]) =>
      val encoded = enocde(l)
      l == decode(encoded)

  def enocde(o: List[Map[String, Int]]): Json = o.asJson
  def decode(j: Json): List[Map[String, Int]] = j.as[List[Map[String, Int]]].getOrElse(Nil)