Refactoring English: Month 1

Michael Lynch

Refactoring English:1か月目

一言でまとめると

初めての本のチャプターを公開しました。

ハイライト

  • 本の第1章を公開し、反響に手応えを感じました。
  • ブックカバーのデザイナーへの外注はうまくいきませんでした。
  • PicoShareで大容量ファイルを扱う方法にめどが立ったかもしれません。

目標の達成度

毎月のはじめに、その月に達成したいことを宣言しています。結果は次のとおりです。

Refactoring Englishのチャプターを2つ完成させる

  • 結果:チャプター1つを完成させ、次のチャプターは75%まで進みました。
  • 評価:B

第1章は思ったより時間がかかり、何度も書き直したい箇所が見つかりました。1週間ほど休んで別のチャプターを書いてから戻ると、新鮮な気持ちで見直せたのが良かったです。

デザイナーと協力してRefactoring Englishのカバーデザインを完成させる

  • 結果:カバーデザインは自分で作ることにしました。
  • 評価:C

デザイナーとの作業を途中まで進めましたが、方向性が気に入らなかったためこちらから中断しました。当面は自分でカバーを作ることにしました。

Refactoring English第1章を終えて考えたこと

自身の著書Refactoring Englishの第1章を公開しました。タイトルは「ソフトウェアチュートリアルを書くためのルール」で、長年チュートリアルを読み、自分で試す中で「うまくできている点」「改善が必要な点」に気づいてきた経験をもとにしています。

第1章の反響は予想以上でした

第1章の反響は、期待していた中でも高い方でした。チュートリアルに関するルール集なのでインターネットで大バズりするとは思っていませんでしたが、相性が良さそうだと考えていたいくつかのサイトでまずまずの反応がありました。

いちばん重視している指標はメーリングリストの登録者数です。公開後の1週間で245人の新規登録があり、書籍の総登録者数が31%増加しました。

第1章の公開で、書籍のメーリングリスト登録者数が大きく伸びました。

公開後に記事をどう改善していくのが正解なのか

数年前、Redisの作者であるサルバトーレ・サンフィリッポ氏はプログラミングの仕事を中断してSF小説の執筆に取り組みました。小説の執筆中に、彼はこう述べています

執筆とプログラミングの最も鋭い違いは、一度書かれ、編集され、完成した小説は、ほぼ不変のまま残ることだと思います。

サンフィリッポ氏はさらに、プログラマーは小説家から学び、アプリが完成した後に中核となるロジックを書き換えたい衝動を抑えるべきだと続けました。私はむしろ逆の教訓を得ました。著者こそプログラマーのように、より反復的に本を書くべきだということです。

Refactoring Englishでは、各チャプターをほぼ完成した状態で公開しつつ、読者のフィードバックを反映させながら執筆を進めたいと考えています。

チャプターを流動的な状態で公開することで、これまで経験したことのない問題が生じました。コメント欄の議論が噛み合わなくなってしまうのです。Hacker NewsやLobste.rs、Redditでは、「条件分岐はコンピューターに評価させよう」という私の主張に反対するコメントがありました。そして彼らの指摘は正しいと思います。あれは最も弱いポイントだったので、削除しました。

そうすると、これらの議論を読んだ人が、記事には存在しない主張になぜ反対しているのかと混乱してしまいます。

現時点で考えられる最善の方法は、末尾に注記を加え、現在も本を改訂中であること、オリジナル版へのリンク、そして主な修正点の一覧を掲載することです。

フィードバックをくれる読者をどう見つけ続けるか

今のところ、読者のフィードバックをもとに反復的に改善していく計画はうまくいっていると感じています。

第1章で公開した内容には満足していますが、同時に読者から多くの思慮深い指摘をいただき、改訂に活かせそうです。

「よし、同じチャネルでプレビュー版のチャプターを共有し続ければいいな」と考えました。

しかし、目次を見直して気づきました。最初に共有したチャネルに合うチャプターは、他にはないのです。これらのサイトの多くには「コードが載っていない投稿は場違い」という明文化された、あるいは暗黙のルールがあります。たとえば、なぜ受動態が嫌いなのかを熱く語るチャプターを/r/programmingの読者が喜んで読むとは考えにくいです。

一つのアイデアとして、他の書き手のためにフリーランスで編集の仕事をして、その経験をRefactoring Englishに活かすことを考えています。

ただ、「編集」という言葉は、自分の得意なことを正確には表していない気がします。「エディター」と聞くと、文章を代わりに磨いてくれる人を想像するでしょう。私が本当にやりたいのは、文章の問題点を見つけ出し、書き手自身が改善できるように原則やテクニックを説明することです。これを何と呼べばいいのか分かりません。ライティングのメンターシップでしょうか、コーチングでしょうか。

いずれにせよ、興味があればぜひご連絡ください。ブログ記事やドキュメントなど、ソフトウェア関連の文章であればお手伝いできます。より多くの読者を引きつけ、文章を魅力的にする方法をお伝えします。端的に言えば、このブログの書き方が気に入っていただけたなら、そこで使っているテクニックをお見せできます。

  • 料金は2回のレビューで100ドルです。
    • 初稿のレビューと、フィードバックを踏まえた修正稿のレビューが含まれます。
  • 対象は2,500ワード(単語)までです。
  • 料金をいただくのは、主に真剣に取り組んでいただくためです。もし100ドルが予算を超える場合は、ご相談に応じます。

Reedsyでブックカバーのデザイナーを雇った苦い経験

11月に本の執筆から気が散ってしまったことの一つに、「プロがデザインしたカバーが必要だ」と思い込んだことがあります。動き始めたのは11月でしたが、実際の作業は12月に行われました。

デザイナーはReedsyというプラットフォームで見つけました。Write Useful Booksコミュニティで何人かに勧められたサービスです。よく聞いた評判は「値段は高いが、それだけの価値はある」というものでした。

デザインブリーフに希望内容をまとめて、Reedsy上の4人のデザイナーに送りました。予算は350〜650米ドルと記載しました。1人は上限より20%高い金額を提示し、返信も「もちろん対応できます」というだけでブリーフを読んだ形跡のない定型文でした。別の1人は予算が低すぎると辞退し、1人は返信すらありませんでした。

唯一まともな提案をくれたのがGaryでした。提示額は350ポンド(434米ドル)。彼はブリーフの具体的な内容に触れた丁寧なメッセージをくれ、ポートフォリオには何十ものブックカバーがあり、Reedsyでの評価は満点の5.0でした。1か月のスケジュールで、料金は3回に分けて支払うという提案でした。条件は良さそうだったので、彼に依頼することにしました。

Garyとの仕事

1週間経ってもGaryから連絡がありませんでした。初回の支払いが自動で引き落とされた後、初稿の目安を尋ねると、翌日にコンセプトを送るとの返事があり、実際に送られてきました。

Reedsyで雇ったデザイナーから届いた初期のブックカバー案

サンプル1は見栄えは良かったのですが、ブリーフで参考として挙げたBeautiful Codeの露骨な模倣でした。残りは物足りなく感じましたが、ブリーフにもっと時間をかけなかった自分が悪いと思いました。

コンセプトを見返すうちに、自分が伝えたいのは「丁寧で意図的な仕事」というイメージだと気づきました。枯山水のサンプル6と粘土の型のサンプル5は方向性が近いと感じたので、その路線を深掘りしてほしいと伝え、石を彫る彫刻家のイメージを提案しました。

さらに1週間が経ち、Garyからは枯山水のアイデアを少しだけ変えたものが届きました。彫刻家のコンセプトでは、ノミと原石が写った画像を試していましたが、写真の石はまったく彫られていない状態で、「丁寧な仕事」というイメージは伝わってきませんでした。

翌週はクリスマスで、12月30日の完了予定に間に合うのか不安になってきました。クリスマス前の月曜日にGaryからメールがあり、12月27日に仕事に戻るので予定どおり進んでいるとのことでした。

しかし27日の終業時刻になってもGaryから連絡はなく、困った状況に陥っていることに気づきました。

私のいる米国東部時間では金曜の午後4時でしたが、Garyは英国にいるため、彼の営業日はとっくに終わっていました。Reedsyは月曜の正午(東部時間)に自動で請求する予定でした。Reedsyで請求に異議を申し立てられるのは請求の24時間前までなので、完成品を受け取るための営業日は残りゼロでした。

Garyが成果物を納品していないので、最終支払いを1週間延ばしてほしいとReedsyのカスタマーサポートに頼みました。ReedsyからはGary本人と話し合ってほしいと言われました。次の営業日までGaryの返事を待っていたら支払いの移動には手遅れになると説明しましたが、サポートはそれでもGaryと解決するよう求めました。

金曜の午後5時(東部時間)にGaryにメールすると、「コーポレートアワーでは働いていない」ので週末に作業して予定どおり終えられると返事がありました。彼は好意で支払いを延期してくれましたが、私がReedsyに苦情を入れたことに不満そうでした。

一方で私はなるべく通常の勤務時間内で働くようにしており、週末をGaryと一緒に急ぎの作業に費やす気にはなれませんでした。月曜に確認すると、Garyは2つのコンセプトを更新して送ってきていましたが、どちらもかなり微妙でした。一つは明らかにAI生成で非現実的に見え、もう一つも私が求めたトーンとはかけ離れていました。

画像がAI生成かどうか、ブリーフで指定したライセンス要件を満たしているかどうかをGaryに尋ねると、彼ははぐらかすようになりました。そこでプロジェクトの中止を申し出ました。すでに支払った231ポンド(287米ドル)はそのまま渡すので、最終支払いをキャンセルして終了させてほしいと提案すると、彼は同意し、それで終わりになりました。

Garyには全項目で星3つのレビューを付けました。ひどいというほどではありませんが、可もなく不可もなく、スケジュールの連絡が下手だったという評価です。私のレビューは公開されていますが、Reedsyでは私が3.0を付けたにもかかわらず、Garyは依然として5件のレビューで満点の5.0と表示されています。

私が星3つを付けたにもかかわらず、Garyは5件のレビューで依然として満点の5.0と表示されています。

自作したブックカバー

Garyとの契約を終え、自分でカバーを作ってみることにしました。Unsplashで見つけたロイヤリティフリーの画像が、静かで丁寧な仕事という雰囲気をよく捉えていたので、そこにテキストを加えました。

素人っぽい出来なのは承知していますが、Garyに期待していた仕上がりと比べて80%くらいの満足度はあります。しかも無料で、作業時間は1時間でした。今は仮のカバーとして扱い、後で誰かに頼むなり、もっと時間をかけるなりすればいいと考えています。

サイドプロジェクト

PicoShareで大容量ファイルを扱えるようにする

PicoShareは、インターネット経由でファイルを共有するための、ミニマルでホスティングが簡単なウェブアプリです。数年前に作り、今でも毎週使っています。

PicoShareについて少し引け目を感じているのは、大容量ファイルでスケールしにくいことです。共有CPUと256MBのRAMを備えたVMでは、約1GBまでのファイルなら快適に動作します。1GBを超えるファイルをアップロードしようとすると、たいていRAMを使い果たしてクラッシュします。ハードウェアを増強すれば解決できますが、できれば任意のサイズのファイルをアップロードできるようにしたいところです。

この問題は何度か掘り下げて調べており、パフォーマンス低下の原因はPicoShareがすべてのファイルデータをSQLiteに保存していることにあると強く推測しています。変わった選択ですが、これによりSQLiteのデータにファイルデータを含めたアプリの状態全体が収まります。おそらく、PicoShareが大量のデータをSQLiteに書き込もうとしてRAMを使い果たし、落ちてしまっているのだと思います。

SQLiteのストリーミングI/O APIを使えば、もっと効率的にデータベースへ書き込めるのではないかと気になっていました。しかしPicoShareはGoで書いており、当時使っていたGo用のSQLiteドライバーはこのストリーミングI/O APIに対応していませんでした。

幸い、Nuno Cruces氏がストリーミングI/Oに対応したGo用の新しいSQLiteドライバーを公開し、PicoShareを彼のライブラリに移植するのを手伝うと申し出てくれました。9月に一緒に作業を少し進め、一定の成果はありましたが、ストリーミングI/Oを使っても依然として大容量ファイルでメモリを使い果たすことが分かりました。

Nuno氏は、ファイルを分割してチャンクごとにSQLiteに書き込めばRAM使用量を下げられるかもしれないと提案してくれました。実は現在の実装でもすでにそうしていますが、ストリーミングI/Oではセマンティクスが異なるため、繊細なコードを大幅に書き換える必要がありました。そこで力尽きて、一旦作業を棚上げしました。

12月に、気持ちを新たにしてストリーミングI/Oの問題に戻ってきました。すると、チャンク分割の問題は思ったより簡単だと気づきました。PicoShareは大容量ファイルを書き込むときにはRAMを使い果たしますが、読み込むときにはそうなりません。つまり、書き込み側だけをより効率的なストリーミングAPIで実装し直せばよかったのです。

ストリーミングI/Oでファイルをチャンクごとに書き込む方法は、SQLiteのデフォルトAPIで実装していたものよりシンプルでした。当初は、SQLiteデータベースをio.Writerオブジェクトで抽象化し、io.Copyでデータを流し込むのがいちばん簡単だと考えていました。しかし今回は、io.Copyを使わず直接すべて書き込む方が簡単だと気づきました。

ストリーミングI/O版は限定的なテストでは安定していますが、さらに広範なテストが必要です。ネックは自宅のアップロード速度が非常に遅いことで、VNC経由でリモートアクセスできるデスクトップOSをfly.io上で動かす方法を模索しています。

まとめ

何ができたか

学んだこと

  • グラフィックデザイナーに依頼する際の教訓
    • プロに依頼する前に、まずは自分で仮バージョンを作ってみる。
    • 支払いは日付ではなく、プロジェクトのマイルストーンに紐づける。
    • AI生成画像やAI支援による画像合成の使用可否を明確に伝える。
    • 写真やフォントなどのサードパーティ素材については、ライセンス情報を提示してもらうことを明確にする。
      • ブリーフでは、すべての素材が適切なライセンスである必要があると書きました。
      • ライセンスを遵守しているという口約束だけでなく、ライセンス情報そのものを納品してもらうと明記すべきでした。
    • クリスマス直後に終わるようなスケジュールは組まない。
    • Reedsyはクライアントよりもコントラクターを強く優遇する作りになっています。

来月の目標

  • 2024年の年次レビューブログ記事を公開する。
  • 本の別のチャプターを完成させる。
  • 読者のフィードバックをもとにチュートリアルの章を改訂する。

お願い

原文は Michael Lynch により に公開されました。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。