Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

テックブログ運営で大変だった話とプロダクト開発に繋がった話

こんにちは。kyです。 今日はちょっと昔話というか、お話をしようと思います。ですので気軽に聞いてもらえると嬉しいです。

ここでお伝えしたいことは「テックブログの運営で大変だった話」と「課題を解決するプロダクトを作っていった話」です。僕なりに気づいたこともお伝えします。

昔、テックブログを作ったときの意気込みと大変だったこと

前の会社でPyQというWebサービスを数人で立ち上げたのですが、成長するにつれて、もっとお客様にPyQを知っていただく必要がありました。

マーケターのnanaもチームにジョインし「ブログを通して有益な情報を公開して、それでサービスを知ってもらおう!」と決めました。SEOだけを考えた残念な記事を量産するのでなく、ちゃんと問題の解決や知見になる記事を書こう!ということを大切にしました。

テクノロジーに関したWebサービスだったので、テックブログとも言えるブログを作り始めました。ブログについてきちんと取り組むため、こういったことを大切にしました:

  • 独自ドメインでやる
  • 技術の内容とWebサービスのニュース両方を書く
  • 複数人で運用。相互にレビューしてちゃんと良いものを書く
  • エンジニアが技術的な正しさや有益さに、マーケターが読みやすさやSEOに責任を持つ

ブログを作るだけなら良いのですが、このような条件で真剣に運用するとなると、どうでしょうか。とくに最後の1つはとてもチャレンジングだったと思います。コピー&ペーストしたような記事が量産される時代を終わりにしようと、「うちはちゃんと読まれるうえ、読む人の課題解決や成長につながるものを作ろう!」と意気込んでました。

最初は、運営について大変なことがありました。チーム(とくにこの活動を引っ張ってくれたnana)が一生懸命考えて運営体制を作っていきました。

当時の運用フロー

そのためにこのようなフローで記事を書いていました。

  1. 記事をVSCodeで書き「テキスト校正くんプラグイン(textlint)」でチェックする
  2. 記事を書く仕事をAsanaでタスク管理する
  3. 記事のレビューをDropbox Paperで渡してコメントしてもらう(Asanaのタスクにアサイン)
  4. レビューのやり取りを記事に反映して、はてなブログに記事を作成する
  5. はてなブログの下書きプレビューで社内に共有する
  6. 記事を修正して、完成すれば記事を予約投稿する
  7. 記事が投稿されたらツイート、Facebookでシェアをする

これは結構大変そうです。

ですが条件の中で良い記事をちゃんと作ろうとすると、必要な作業だったと思います。実際に良い記事を書いて、多くの方に読んでいただいていました。今もそのブログ、Python学習チャンネル by PyQは多くの方に読んでいただいていますし、実際に役立つ内容が盛りだくさんです。

ですが、もう少しでも良いから運用を改善したいものです。とくに外部の人のチェックがあるインタビュー記事で、さらに運用が大変になったのを覚えています。困っているチームメンバーを見たときに 「絶対に助けたい」と思いました

数年前の話ですので、今なら「Notionで運用できる!」など新しい方法があるかもしれません。Slackのスレッドでコメントしたり、Zennをチームで運用するのも良いかもしれません(独自ドメインでやりたいのでZennやQiitaは避けたと思いますが)。皆さんも、もしかしたら苦労されているかもしれません。

何かを作って解決できないかな、と考えた

そんな困っているチームメンバーを何とかしたい、と思いました。

そこで 「テックブログの運営に使えるWebサービスがあれば良いのでは」 と考えました。いえ、テックブログと言わず、複数人でのメディア運営を何とかできるプロダクトが作れるのではないでしょうか。

こんなものがあれば、協力してブログを運営するプロダクトになるのではと考えました:

  • インストール不要ですぐ使える:
    • Webサービスにすることで、導入の手間を減らす
    • オンラインのエディターで執筆すると、自動で校正される
    • テキストで入力できる(Markdownに対応)
  • 執筆のタスク管理もできる:
    • 記事を執筆する状態を同じサービス上で管理する
    • レビューのワークフローに自動で則る
    • アサインや期限、ステータスを管理し、誰があと何をすれば記事の完成かを分かるようにする
  • 記事のレビュー(コメント)もできる
    • GitHubライクな行のレビューにし、社内のエンジニアも協力しやすくする(案外誰にとっても見やすい)
    • 記事はバージョン管理し、コメントが混ざらないようにする
    • コメントが通知、Slack連携されることで連絡を減らす
  • ワンクリックで配信できる:
    • ブログ上への記事作成をワンクリックでできるようにする
    • 画像も自動で配信する
  • 高速な校正:
    • textlintはJavaScript形態素解析エンジンを使うので、MeCabなどの速いものを使う
    • 独自に校正ツールを実装し、高速化する
  • ディープラーニングによる校正:
    • BERTの技術を活かした文法誤り訂正(GEC)をする
    • 助詞の間違いやタイポなど、ルールや正規表現でカバーできない間違いを検知する

と、若干モリモリではありますが、これを作り始めました

当時のブログ運用は「ある価値を実現しようとして、既存の仕組みで何とかしていた」状態でしたので、マッチするプロダクトがあるだけで何とかなります。最初からすべてを実装する必要はなく、運用しながら改善できる点も取り組みやすかった理由です。

もし皆さんも「既存のツールをいくつか使って何とか運用しているが、大変」という場面があれば、何かプロダクトを作るチャンスだと思います。一般化でき、1つの需要としてまとまりがあれば、きっと他の人も困っています。

使ってもらいながら成長する

これは完全にサイドプロジェクトとして作り始めました。まずアルファ版として作り、PyQチームに使ってもらいました。 その当時でも業務はかなり改善されたので、より便利になる「はてなブログ連携」やディープラーニングの技術を追加していきました。

ただ、狭すぎる需要に特化して作れば、それは「便利な社内システム」ができるだけです。そこで開発が進むごとに、クローズドベータやオープンベータとして多くの方に使っていただきました。 開発はその後も続き、半年前に正式リリースしました。もう前の会社での新規事業(PyQ)も軌道に乗っていたので、退社して今の株式会社ゼンプロダクツを起業しました。

そのWebサービスが、今のShodoです。

テックブログの運営にお使いいただいています。

導入してフィードバックをくれた皆さん、初期のころから使ってくださる皆さん、本当にありがとうございます。プロダクトは人に使ってもらわないと育たないと、よく分かりました。

そして、誰に使ってもらうか、どういった人に使ってもらうかも大切だと思います。なぜなら使う人によってプロダクトの方向性すら左右されるからです。プロダクトマネージャーとしては、誰に使ってもらい、いただいた意見をどう受け取るかという判断が重要です。そのためのブレない指針や心と、その逆の柔軟性も大切です。

今のShodoが最初からあれば、当時の悩みはなかったと思います。ですが悩みもあったからこそ良いプロダクトが生まれましたし、悩みや課題に向き合ったからこそチームがより強くなれたのかなと思います。

おわりに

今回は昔話的な内容でしたので、これが知見だ!と豪語するつもりはありません。ですが、こういった学びがあったよ、というサマリーをお伝えしたいなと思っています。

  • 今ある仕組みやツールで何とか解決している課題があれば、プロダクトを作るチャンス!
  • 何か具体的な悩みや助けたい人がいれば、まずは作って使ってもらおう
  • 広い意見をもらったり、どんな立場から意見をくれたかを考えて「社内システム化」を避けよう

プロセスの説明を端折って結果だけを伝えている気もしますが…、もしこの記事が多くの人に読んでいただけたら、もっと詳しく書こうかなと思います。

今後もShodoを開発して皆さんを助けられたら嬉しいです。もちろん今後、Shodoの方向性が大きく変わる可能性なども十分あります。ですが、最初に思った「この困っている人を助けたいな」という気持ちはずっと大切にしたいです。

プロダクト開発、やっていきましょう!

shodo.ink

Shodoで執筆されました

会社の説明動画をアバターと一緒に撮ったら再生数が50回から1000回になったという、Twitter的な話

アバターと一緒に撮ったら再生数が50回から1000回になった話

こんにちは。

会社説明を人間が一生懸命する動画は50回しか再生されなかったのに、アバターと話すおもしろ動画を撮ったら1000回も再生されました、という話です。

前提に違う点が多くありますので、単純に「アバターで撮影したら成果が20倍!」という話ではありません。ただ私的に面白かったので感じたことをメモした記事だと思ってください。

アバターと会話形式にした動画

とある会社説明の動画を作っているときに、「話の聞き役がほしいな」と思いました。せっかくVRやAI音声合成の技術が発達していますので、新技術で遊んだ動画が作れないかなと思い、試しに撮ったデモ を1000回も観ていただきました。

もともとは「こんなことしちゃう俺デュフフ」的なもので、ツイートする行為自体もちょっと痛々しいネタです。ネタに走れば良いというわけではありませんが、少し今回の反応を考える意味はありそうです。

もともとあった真面目な動画はあまり観られなかった

それ以前にYouTubeで投稿してツイートした動画は、50回ほど観ていただきました。

www.youtube.com

単純には比較できませんが、あまり伸びなかったのは事実です。

もちろん真剣に説明した長めの動画ですので、50回観ていただいただけでも非常にありがたいです。さらにYouTubeにアップロードしたものをツイートしたので、上記の動画と再生数だけを比べて良し悪しの判断はできません。

ただ、「新しく知ってもらいたい」という目的であれば、真面目なだけではダメなようです。

何が違ったのか? Twitter受けとは何か

アバターと会話する奇々怪々な動画は、Twitterという文脈で良かったということでしょうか。

以前、ある方に「SNSを眺めている人には、噛み砕いた流動食を口元まで持っていく」必要があると教えていただきました。ものの例えとして「それくらいの気持ちで発信しなさい」という意味合いです。たしかに今回の動画は「脳死でも楽しめるコンテンツ」ではありますし、見たいなと思わせる何かもあります。

取っ掛かりとして面白かったのは、会社やWebサービスではなく「アバターと会話する妙なお兄さん」 という部分です。さらに音声合成というのもあって、アバターメタバース音声合成に興味のある人が楽しんでくれた印象でした。

それでは「会社紹介になっていないだろう!」という心配もありますよね。そこで動画を観てくれた人に直接、感想を聞いておきました。その人が内容をハッキリと覚えていないのは事実でしたが、「こういう事業をやっているんだなぁ。AIを使ったWebサービスもできる時代なんだ」という印象は伝わったようでした。

もちろん会社やサービスの紹介という部分を疎かにしたり、面白さを求めて過激になったりするのは良くありません。そして仕事としてやるのであれば、より関心を持っていただいたり、具体的なアクションに繋がらないと意味はありません。

ともあれ、まず知ってもらう必要はありますし、ハードルを下げる何かは必要なようです。道化とまでは言いませんが、「楽しんでいただけますよ」という雰囲気は必要なようです。まぁ考えてみると、自分自身もTwitterでは面白いネタや体験談のマンガ、ニュースの速報を見がちな気がします。反応しやすいですからね。

立派に主張するような発見はとくにありませんが、真面目だけではウケないのがSNSという世界のようです。

おわりに

今回の動画自体がある種のデモですので、ちゃんとした動画はどこかでお見せできると思います。ぜひ @hirokikyをフォローしてください!

また、Shodoに興味を持っていただけた方は、無料から使えますのでぜひご利用ください。

shodo.ink

執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

4年間、同じ服を着て気づいたこと

追記:結局、またさらに1年同じ服を着ていました

私の娘は2歳なのですが、父が黒いTシャツ以外を着ている姿を見たことがありません。

この4年間、同じ服をずっと着ていました。

今日はその4年間、同じ服を着て気づいた発見を皆さんにお伝えしようと思います

同じ服ってどういうレベルで?

事前に仕様の説明から入らせてください。

この4年は以下の服を着ていました:

  • 基本
    • 黒の無地クールネックTシャツ(ヘインズのプレミアムジャパンフィット)
    • ジーパン(EDWIN 402)
  • 上着
  • 温度調節用

僕に会ったことがある人は「あー」と納得いただけると思います。

ケンオールパーカーを着がち

2019年に書いた記事のスタイルから基本的に変わっていません。

どれくらい同じ服だったのか

完全に誰得の情報なので、読み飛ばしてOKです。同じ服を着る生活をしてみたい人は参考にしてください。

  • 黒のTシャツとジーパンは、ほぼ100%毎日着ていました。唯一スーツを着るときに白の下着を着ただけです
  • 型番の誤差として、Tシャツにはスリードッツのジョシュが8枚中2枚、ジーパンはアメリカンイーグルのスリムが5本中2本入っています
  • 黒ワイシャツやタートルネックなどは春や秋ごろに、Tシャツだけだと寒い日などに着ていました
  • 靴はM996からAllBirdsに途中で変わりました
  • 寝間着はイベントのTシャツなど比較的自由ですが、最近はグレーの無地スウェットを多く着ています
  • 下着については同じものを買い続けることが難しく、Amazonで似た黒の靴下やパンツをまとめて買っていま

意外とジョブズもゆるい

「同じ服を着る人」として有名なスティーブ・ジョブズマーク・ザッカーバーグもゆるいところがあります。

ザッカーバーグ氏はグレーのTシャツしか着ないと言われつつ、Metaへの社名変更発表時に黒のシャツを着ていたりします。スティーブ・ジョブズも実は靴の型番を変えていたり、休暇中に白いシャツや半ズボンをはいた写真が残っています(娘のエリンと金閣寺で撮った写真など)。

まぁそういった「同じ服を着ていると認識されている人たち」も本当に100%ずっと同じ服ではないので、僕も「同じ服を着ていた」と言って差し支えないと思います。

4年間、同じ服を着て気づいたこと

本題です。

こういった記事は良い点ばかり列挙しがちですので、今回はとくにマイナスの面も意識して紹介したいと思います。 4年間、同じ服を着て気づいたことを5つ聞いてください。

スーツは要る

断言しますが、スーツは要ります。

少なくとも、友だちの結婚式に行けるようなスーツは必要です。 他にも大事な商談のときや、銀行に行って会社の融資をお願いするときなど、スーツを着るべきときは現実に存在します。

スーツを着るべき場面を、セットアップやジャケットだけで誤魔化すのは少し難しいと思います。 「考えることを減らすため」に同じ服を着ているフシもあるので、「スーツでなくて良いだろうか?」と気になるほうが本末転倒です。

「絶対にどんな状況でも大丈夫な服」を着続けたい、と考えないほうが良いです

そんなものはありません。

ジーパンで結婚式にはいけませんし、スーツでプログラミングをしたくありません。先述したような季節用のワイシャツなどもそうで、あまり厳密に「絶対にこのTシャツとジャケットしか着ない!」とすると状況や気温にあわせられなくて、逆に考えることが増えます。

仮に「袖が着脱可能なセットアップ」と言えるものを用意しても、僕はあまり好きじゃありません。それはすでに「1つの服」ではないからです。であれば、状況や季節に応じた服を着たほうがシンプルです。生き方をシンプルにしようとして手段や振る舞いが複雑であれば意味はない、と考えています。

スーツを着る場面では、スーツを着るのがシンプルな生き方です。

着たい服を買えない苦しみはある

1ヶ月、プロテインだけ食べて生活してください。 食事から得られていた幸せの大きさに驚くと思います。

それと同じで、「着たい服を着る」という幸せは案外大きかったんだと気づきました 僕は欲しいTシャツがあっても「これは着られない」と諦める必要があります。

ふと買い物に行ったときやネットで見かけたとき、推しがグッズを出したときなどありますよね?ほとんどの場合、諦める必要があります。これが案外、寂しいものです

そういった日常の小さな喜びが減ってしまいます。ある意味では、その可能性を断つことで余計なことを考えずに済んでいると言えます。

公私の区別をつけにくい

意外な盲点なのですが、精神的に公私の区別をつけにくいです。

休日も平日も同じ服に着替えますので、「よし今日は仕事だぞ」というようなメリハリが生まれません。とくに最近は自宅で仕事をする機会も多いので、何らかの切り替えスイッチは欲しいものです。

人間には着ている服や周囲の状況から自分の気持ちや感情を調整する機能があるように思います。僕の場合は部屋を1つ仕事用にしていますが、同じ服を着て生活するのであれば何かしらの切り替えスイッチは用意したほうが良いです。

考えることは案外増える

「同じ服を着れば考える回数が減る」とも言い切れません

たしかに毎朝服について考えることは減ります。そこは楽です。ですが「同じ服を着る」という思想自体を考える機会は増えます。たとえば前述の通り服が買えないなと残念に思ったり、定期的に今の型番の服で良いか検討したり、このブログ記事を書いたりします(逆にめちゃくちゃ考えてるが!)。

だんだん慣れて考えることは減り、4年も経てばほぼゼロになりますが、最初の1年くらいは考えることが増えると思ってください。

服飾について一番考えない状態が何かと言うと、「着たい服を深く考えずに買って、毎朝何も考えずにあるものを着る状態」だと思います。つまり、普通でいることです。

とはいえ、僕はズボラなわりに「この着合わせで良かったかな?」と心配になったり、「今日はこの服で良かったかな」とずっと気になる人間なのです。「この服を着ているということは、こういう感情だと予想されるのではないか?」とまで考えることもありました。

そういった憂慮の余地がなくなったのは、同じ服を着ていて良かったことです。 ですが「同じ服を着ることについて考える」機会は、始めてから1年はとくに増えます。

「いつも最高の自分」ではない

ネットで同じ服を着ることについて調べると、利点として「いつも最高の自分でいられる」というような表現を目にします。

断言しますが、いつも最高の自分ではありません(たぶんこれを言う人はエアプ勢だと思います)。 「いつも、いつもの自分でしかない自分」が正しいと思います。

誰だって良いスーツを着ているときやドレスを着ているときが最高だと思います。それはそうですよね。他にもオシャレな服屋さんでマネキンが着ているセットを買うほうが、誰にとってもより良い状態だと思います。

バッチリした服をバッチリ着て最高と思っている人の姿:

スーツをバッチリ着たほうが最高なのは間違いない

良い服だね!と褒められることはなくなります。「同じ服を着ているね」と言われたことすらありません。おしゃれをしたいのであれば「同じ服を着るのがファッションだ」なんて思い違いをせずに、素直におしゃれをするほうが良いです。

ただ、自分の中で外さない、好きなスタイルで居られることは事実です。その状態に無思考でなれる、というのが良い点です。

同じ服を着る生活はおすすめできる?

おすすめできません

多くの人にはできません。

準備や制約がある一方、とくに内面的なメリットは大きいと感じられず、誰かから褒められることもありません。僕にとっては結構快適ですが、不自由があるのは事実です(むしろ不自由を選択している)。

もし「仕事では決まった服やスーツを着ている」という人がいれば、案外それが良い答えだなと感じます。考えることを減らせますし、仕事モードに入りやすいです(もちろんプログラミングにスーツは向いていません)。

もともと同じ服を着たい傾向があるのであれば、試す価値はあります。自分のもともとの精神構造が重要なポイントです。たとえば「すでに靴下は黒の1種類で統一している」ような人であれば、Tシャツやズボンも統一して良いと思います。ただ、あまり真剣になりすぎる意味はないと思います。

僕は気に入っているのでこれからも続けると思います。ですが、より楽な気持ちでいようかなと思います。まぁ、どんな服を着ていても、僕は僕です

一番気づいて良かったのは、そういう部分なのかもしれません。4年かかってそれかよ、という内容ですね。

ここまで読んでくれてありがとうございます。


もし気になることや相談したいことがあれば @hirokiky に聞いてください。同じ服を着ることについては、平均的な人よりも続けているほうだと思います。

執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

JavaScriptのClipboard APIでリッチテキスト(書式付きテキスト)をコピーする

クリップボードにリッチテキストをコピーする方法を説明します。今回はとくにClipboard APIという現在推奨された方法で実装します。

「リッチテキストをコピー」というのは、ペーストしたときにWYSIWYGエディターへ書式が有効なまま入力されることを言っています。たとえばWordでコピーをすると、見出しや太字などを含めてコピーされるのと同じことです。

Clipboardを使ってリッチテキストをコピーする

標準のClipboard APIを使ってリッチテキストをコピーするには、以下のようにします。

const body = '<h1>見出し</h1><strong>太字</strong>'

const blob = new Blob([body], { type: 'text/html' })
const blobPlain = new Blob([body], { type: 'text/plain' })
const item = [new window.ClipboardItem({ 'text/html': blob, 'text/plain': blobPlain })]

await navigator.clipboard.write(item)

(たとえば「コピー」ボタンをクリックしたときなどに実行してください)

MIMEタイプに 'text/html' と指定して、Blobを渡すことでできます。こうすることでHTMLと認識させられるので、WordやWYSIWYGエディターへペーストしたときに書式が有効となります。Google Chromeの場合、コピーしたHTMLは自動でサニタイズされます。

実装するときは 'text/plain' としてもコピーしてください。そうしないと、メモ帳などにプレーンテキストとして貼り付けられるときに動作しません。

Firefoxは未対応

2022年1月26日時点ではFirefoxがHTMLのコピーに対応していません。

window.ClipboardItem が存在するかなどをチェックして、ない場合は navigator.clipboard.writeText を使ってプレーンテキストのみコピーするのが良いと思います(細かい実装の話はお任せしますが)。

Google Chromeで対応された履歴はこちらです。

chromestatus.com

Google ChromeSafariではできるので、そこまで困らないのが事実です(僕はFirefoxユーザーですが)。

document.execCommandは使わない

以前まではClipboard API経由でリッチテキストをコピーできませんでした。少し前の記事ですと document.execCommand('copy') とイベントリスナーを使って何とかする方法も書かれていますが、現在は避けたほうが良いかと思います。

document.execCommand 自体が非推奨になっていますので、できれば Clipboard を使いましょう。

developer.mozilla.org

まとめ

Clipboard APIを使ってリッチテキスト(書式付きテキスト)のコピーはできます! Firefoxは執筆時点で未対応ですが、そこは割り切れる範囲かなという気もします。

document.execCommand('copy') はなるべく使わないようにしましょう。

今後、対応されていくと良いなと思っています。

caniuse.com


執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

Pythonで`{文字列: 数値}`の辞書から数値が最大のキーと値を取る

{文字列: 数値} のような辞書(Dict[str, int])があるときに、数値が最大・最小のキーと値を取得する方法です。

たとえば文字の出現回数をカウントしているときなどに使えます。

>>> d = {"a": 3, "b": 2, "c": 1}
>>> k, v = max(d.items(), key=lambda x: x[1])
>>> k
'a'
>>> v
3

他にも sorted()min() でも使えます。

collections.Counter とも併せて使えるので便利です。

>>> from collections import Counter
>>> d = Counter("aaaaabbbcc")
>>> k, v = max(d.items(), key=lambda x: x[1])
>>> k
'a'
>>> v
5

タプルのリストが便利に使える

タプルのリストから最大値、最小値を取ると便利に使えます。

たとえば辞書のキーにできないオブジェクトの場合は、第一要素に数値を持つタプルを使うと似たことができます。 この例では辞書がひも付いているとしました。

>>> l = []
>>> for i in range(5):
...     l.append((i, {}))
... 
>>> l
[(0, {}), (1, {}), (2, {}), ...]
>>> max(l, key=lambda x: x[0])
(4, {})

key= を指定しない場合、第1要素の数値が同値の場合に第2要素が評価されますので注意してください。

タプルのリストや key= 引数をうまく使えば、何らかの値に数値がひも付いているときに最大値や最小値を取得できます。

執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

社会って論理的じゃないよね。僕らの弱さ、そして小野マトペ氏の裁判に感じること

皆さんのコメントを読んで、反省しました

皆さん、たくさんのコメントありがとうございます。 僕自身の考えや文が稚拙であったことを反省しました。

まず、僕は「社会は論理的でないところがある」ように書きましたが、これは良くなかったなと考えを改めました。 僕の間違いは、自分は論理的に考えていると思っていたものの、社会の中にある論理や合理性を軽視していたということです。 自分に理解できないものを、論理ではないものだと考える、感情的なものでした。 さらに文自体も感情的で良くなかったと反省しております。

皆さんのコメントのおかげで気づけました。ありがとうございます。 僕は、自分以外の世界というのに少し感覚が弱かったというか、軽視していたのだと思いました。

この記事について、削除してしまおうかとも思いましたが、僕自身が反省して学びこれからに活かすため残しておこうと思います。 内容はそのままにしておりますので、それを前提に読んでいただけると幸いです。

改めて、教えていただきありがとうございました。 そしていつも読んでいただき、ありがとうございます。

本文

社会って案外、論理的じゃないよね、と感じた話をさせてください。

いえ、僕こそホントは論理的でなかったと勝手に反省した 。オチを言うとそういう話です。

小野マトペ氏の裁判が終わり、個人的に「うわぁ」と思ったので感想です。

平たくまとめてるとこういう裁判です:

  • Twitterで「俺はコロナ」とネタをツイート
  • 居酒屋に行って写真をツイート
  • 刑事事件(偽計業務妨害)になる

もとの話はこちらを: note.com

何が怖いのか

まさに自分のことだったからです。

この裁判は控訴棄却で有罪判決となりました。 「ネタであり業務妨害をする気なんてなかった(故意でない)」という点が裁判において認められなかったということですね。

でも僕自身にも、すごく心当たりがあるんですよね。 まるで自分のことかと思いました。

Twitterでその日盛り上がった話題やニュースに、ちょっぴり過激な面白いことを言いたいことってあるじゃないですか。 友だちと飲み会をして「濃厚接触!」なんて冗談を飛ばしたいときもあります(ネットには書かないネットリテラシーを発動)。

もちろん、褒められたことではありません。 でもそんな、なくもないことが刑事事件になってしまう んだなということです。 僕も反省するところです。

たしかに前後関係を繋げていけば「業務妨害になっても構わないと思ってますよね?(未必の故意である)」と追い詰められる状況になります。

社会は論理的じゃない、そして僕こそ

僕が感じる「ロジック」とは違う論理があると言うべきでしょうか。そして自分が思っているほど、自分が論理的ではなかったということです。

裁判を見ると、小野マトペ氏が「故意に業務妨害をやったぞ!」と言える決定的な証拠はないわけです。 たとえば「店を名指しした犯罪行為を感じさせるツイート」など明らかなものはありません。

さらに、民事的ないざこざではないので、刑事的な「故意に犯罪をやっているかどうか」が争点なわけですね。

ただ、社会には「社会的に見てどうか?」という倫理による力があって、(情勢も鑑みて)納得感のある正義が勝ったわけです。 僕の学びとしては「ロジカルに言えば問題ないでしょう、とは言えない」、「納得感や社会的な倫理が重要な世界なんだ」という社会の掟に気づいたことです。

でも、そう考えれば、当たり前のことを言ってしまっていますね。

僕の弱点:論理的思考だと思ってしまう

論理的思考こそ至高なのではないのですか? そうなのですが、その思考が危ういんですね。

社会や法における「論理」と僕が感じている「論理」や合理性は違うものなんだなと分かりました。 いえ、むしろ本当はエンジニアの世界にもロジックだけじゃないものが大事なはずなんです。

そうでない、社会のもつ「納得感」が、一般的な世界の枠組みにおいて重要視されるわけです。 実際に小野マトペ氏の記事を読むと、論理や「自分の中にある正義」を大切にされてると分かります。

社会的な倫理観や、情勢、思惑や納得感を考える(おもんばかる)のも重要なんですね。 人の気持ちや常識、そしてそれらを動かす修辞や弁論も重要なわけです。

論理的思考は僕たちの世界においては絶対の理!と考えるのも、むしろいびつなんでしょうね。 正直、僕自身も上手に生きられてるとは思いません。

僕が学ぶこと

僕が学ぶこととしては、論理的思考だけで物事は決まらないと受け入れるだと思います。 実際に世の中は社会的な倫理観や納得感、そしてそれらを動かすレトリック(修辞)や弁論術やリテラシーが重要なようです。

飛躍してしまえば「有罪かどうかも修辞や弁論に左右される」ということですよね。 「いやいや、そのために弁護士がいるんでしょ!」と思った方は、そのとおりです。 小野マトペ氏のまとめ記事にも、弁護士を選ぶことが相当重要であったと反省が書かれています。

むしろそれこそが学びです。

「己の潔白と事実を説明して共感してもらえば分かってもらえる」と思っていませんか? そんなことはなかったんですね。 そもそも、社会的な通念を重んじるのも大切なんですね(当たり前のことですが)。

僕は、自分の「論理が正しい」思考を改める必要があると思いました。 そして合理的な時代の今だからこそ、修辞学(レトリック)や弁論術、そして法学や倫理こそ学ばないといけないのかな、ということです。

おわりに

ぜひ小野マトペ氏のまとめ記事を前半から読んでください。

あと、これに関したおすすめの本です。 このリンクから買ってくれると嬉しいです:


執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました

DjangoのQuerySetでグループごとに最大・最小のデータのみ取得する

DjangoのQuerySetでグループごとに最大・最小のデータのみ抽出する方法を紹介します。 この記事はDjango Advent Calendar 2021 3日目の記事です。

グループごとの最大・最小のデータとは何でしょうか?

たとえば以下のような場合に必要となります。

  • ブログ記事ごとに最新のコメントのみ取得
  • ユーザーごとに金額が最大の購入履歴のみ取得
  • ページごとにリビジョン番号が最大の差分データのみ取得

実際に仕事をしているとたまに欲しくなりますよね。

今回は Parent モデルというグループごとに、 Child モデルの number が最大になる Child の一覧を取得します。

class Parent(models.Model):
    pass
    
class Child(models.Model):
    parent = models.ForeignKey(Parent)
    number = models.IntegerField()

Meta__str__ は表記上省略)

QuerySetでグループごとに最大・最小のみ取得する

最大のみ取得する処理は以下のようになります(解説は後述します)。

children = Child.objects.all()
sub_qs = children.filter(
    parent=models.OuterRef("parent"),
    number__gt=models.OuterRef("number"),
)
qs = children.filter(~models.Exists(sub_qs))

実際に動作を確認してみましょう。 p1p2というグループごとに最大の値を持つ c1_4c2_2 のデータを作りました。

>>> p1 = Parent.objects.create()
>>> p2 = Parent.objects.create()
>>> c1_1 = Child.objects.create(parent=p1, number=1)
>>> c1_2 = Child.objects.create(parent=p1, number=2)
>>> c1_3 = Child.objects.create(parent=p1, number=3)
>>> c1_4 = Child.objects.create(parent=p1, number=4)
>>> c2_1 = Child.objects.create(parent=p2, number=1)
>>> c2_2 = Child.objects.create(parent=p2, number=2)
>>> children.filter(~models.Exists(sub_qs))
<QuerySet [<Child: 1-4>, <Child: 2-2>]>

Child: 1-4c1_4)とChild: 2-2c2_2)の取得を確認できました。

SQLは以下のようになりました(読みやすいよう改行を追加しています)。

SELECT "child"."id", "child"."parent_id", "child"."number"
FROM "child"
WHERE NOT EXISTS(
    SELECT (1) AS "a"
    FROM "child" U0
    WHERE (
        U0."number" > "child"."number" AND
        U0."parent_id" = "child"."parent_id"
    ) LIMIT 1
)

この処理では「グループごとに最大・最小の値が複数ある場合に両方とも取得される」点に注意してください。

解説

今回のSQLでは「よりnumberが高い値のある行を除外する」という方法で、グループごとの最大値を取得しています。直感的にはGROUP BYで最大値を計算するサブクエリーを書きたいところですが、実行速度が速い方法を紹介しました。

Djangoではこのサブクエリーを書くために、 OuterRefExists を使っています。OuterRef を使えば外側のクエリー中のフィールドを参照できますので、サブクエリーを簡単に書けます。 今回の場合は OuterRef("parent") でグループにする Parent を指定し、 OuterRef("number") で除外対象の number を指定しています。

実際の環境で高速に動作するかどうかは、データベースの実行計画を確認してください。 データベースやデータの内容によって変わってくると思いますので、仕事の環境で使う場合は実行計画を見ましょう。

GROUP BYを使った書き方も紹介しておきます。

他の書き方

参考として、GROUP BYとサブクエリーを使った方法も紹介します。

children = Child.objects.all()

sub_qs = children\
    .filter(parent=models.OuterRef("parent")) \
    .values("parent") \
    .annotate(max_number=models.Max("number")) \
    .values("max_number")
qs = children.filter(number=models.Subquery(sub_qs))

SQLは以下のようになります。

SELECT "child"."id", "child"."parent_id", "child"."number"
FROM "child"
WHERE "child"."number" = (
    SELECT MAX(U0."number") AS "max_number"
    FROM "child" U0
    WHERE U0."parent_id" = "child"."parent_id"
    GROUP BY U0."parent_id"
)

この処理はサブクエリー内で number の最大値を求めたうえで、その値を条件として Child の一覧を取得しています。こちらのほうが NOT EXISTS を使った書き方よりも理解しやすいですが、処理としては遅くなると思います(実際のデータや環境でお試しください)。

おわりに

DjangoOuterRefSubqueryExists を使ってグループごとに最大・最小の値のみを取得しました。 昔は「Djangoで凝ったSQLを書けない」という印象もありましたが、今のDjangoではクエリーの表現が多彩ですので、今回のようなサブクエリーも直感的に書けます。

ぜひ、業務の中でも活用していただけると嬉しいです (実行計画の確認はしてください)。

ちょっと聞いてください

このブログ記事は私の会社で開発しているShodo(https://shodo.ink/)を使って執筆されました。 Shodoは書いた文章をAIがリアルタイムにチェックしてくれるWebサービスです。

ただのAI校正ツールではありません。 エンジニアの皆さまにも嬉しい特徴がたくさんあります。

と、お勧めできます。

さらにGitHubのようなワークフローで記事を書けるのも特徴です:

  • 記事を書く日程やステータスを管理したり
  • 複数人で記事のレビューコメントやレビュー依頼をしたり
  • 共有リンクからたくさんの人にコメントをもらったり

そんな便利なWebサービスです。

今ならなんと!技術アドベントカレンダーの期間中、Shodoを無料で使えるキャンペーンを実施しています。 これから技術ブログを書く皆さん、ぜひShodoで技術記事を書いて知識やノウハウを共有しませんか?

クーポンコードはこちらから。 ぜひお試しください。

blog.shodo.ink

執筆:Kiyohara Hiroki (@hirokiky)Shodoで執筆されました