LoginSignup
14

More than 3 years have passed since last update.

技術書籍執筆を通してプログラミング学習を再考する

Last updated at Posted at 2017-11-30

リブセンスでIESHIL(イエシル)という中古マンション価格査定サイトのエンジニアリングマネージャーをやってるtchikubaです。

今回、社内で月イチでやってる全社会にて「非エンジニアもエンジニアもアドベントカレンダーやろうぜ!」というアナウンスがあったので、今年はもう記事書かずに他の方に譲ろうと思って放置してたら、前半埋めてくれと連絡があったんですがバタバタしてて時間経って登録したらなんと初日しか残っておらず若干のやり辛さを感じながらこの記事書いてます。

さて、私は3年ほど前からプログラミング学習に興味を持っていました。余談ですが、実はリブセンスではCSRの一環でプログラミング教育にも関わっていたりもします。リブセンスのアドベントカレンダーは3枚あって、私の担当のカレンダーは「学」と名付けられていますので「プログラミング学習」に関する話をします。

プログラミング学習に関しては、色々忙しくて個人的にはあまり具体的な行動には移してなかったんですが、1年半前に縁あって、「Rails5について商業記事を連載してくれる人を探している」という話があり、CodeZineで「サンプルコードで学ぶRuby on Rails 5実践入門」という連載を約1年くらいやりました。
この流れで1年前に技術書籍執筆の話があり、3ステップでしっかり学ぶ Ruby入門(通称: 3ステップRuby本)と、たった1日で基本が身に付く! Ruby on Rails 超入門(通称: Rails超入門)を執筆しております。ちなみに今のところ3ステップRuby本は2018年1〜2月に技術評論社より出版予定となっております。

(追記)無事に出版されました!

前置きが長くなりましたが、買って下さい。

ただし、いずれの書籍も「プログラミングをほぼやったことない人向け」なのでQiita見るような人はターゲットから外れているかなと。それでもメリットがあるとすると今後、そういう入門書を書いてみたい!的なモチベーションがある人にとってはこれから紹介するような入門書執筆特有の辛みとか辛みとか辛みとかを垣間見る機会にはなるかも、といったところです。あとは内容が正しくない!的なマサカリを投げたくてしょうがない根暗なありがたい人とか。

まぁ色々言いましたが、買って下さい。

ちなみにリブセンスは目黒駅周辺にオフィスがあります。最近復活したMeguro.rbも一度リブセンスオフィスで主催してます。私もMeguro.rbやこれまた最近復活したEbisu.rbによく出没してます。良かったら是非遊びに来て下さい。
なぜMeguro.rbの話をしたかというと、ここで技術書籍執筆に関するLTを2回行ったからです。その話を集約した感じの内容を本記事ではお届けしようと思います。実は1回目のLTスライドはslideshareに公開しているんですが、2回目のLTスライドはちょっと赤裸々過ぎて公開を断念したのでエッセンスをお届けしようと思った次第です。

技術書籍を執筆すると新たな発見がある

技術書籍ってやっぱり誤った内容を書いたらヤバいので、公式サイトなどできるだけ正確な情報を参照したり、実際の動作を確認しながら執筆することになります。その過程で「なにこれこんなんあったんか!」的な発見に巡り合えたりするのでそこは勉強になります。

サブirb

例えばirb実行中にirbを立ち上げることができるサブirbとか。単にirb上でirbってコマンド打つとコンテキストが違うirbが立ち上がります。jobsコマンドっての使うと既に立ち上げたサブirbのスレッド番号を確認できて、fg [スレッド番号]ってやると立ち上げたサブirbを切り替えることができます。
スクリーンショット 2017-11-30 18.46.48.png

個人的におもろいなーと思ったのは、サブirbを立ち上げる際のirbコマンドの引数にコンテキスト(クラス・モジュール)を指定できることです。
スクリーンショット 2017-11-30 18.47.33.png
サンプルの例だとFooクラスの中でirbが実行されているので、barというFooクラスのクラスメソッドが使えちゃうというもの。
その他細かい使い方とかの詳細はリファレンスマニュアルを参照してみて下さい。

ボールWindows環境はともだち、こわくないよ

ネタが古くてスミマセン。
今回の書籍のようなターゲットがガチの初学者向けの場合、編集サイドから「Windowsを前提として記事を書いて下さい」と言われます。これが結構苦痛でした勉強になりました。公開用スライドでは自粛してますが、ホントは「Windows Subsystem for Linux(旧称: Bash on Ubuntu on Windows)で良いですか?」というやりとりがあったんですが、無事にNG頂きまして2冊ともWindowsで統一してます。

テキストエディタは3ステップRuby本がメモ帳(!)、Rails超入門がVisual Studio Codeです。VSCodeはざっと使ってみた感じ割と便利そうでした。Vim派な私やあなたにもプラグインがあります。

あとWindows環境でちょっと面倒なのが文字コードです。RubyのプログラムはUTF-8で記述するため、出力結果にマルチバイト文字が入っている場合は明示的に-Kuオプションを指定して実行しないとコマンドプロンプト上で文字化けしてしまうので注意が必要です。

なお、公開用スライドは既に情報が古いですが、Windows環境上では昔RubyInstaller2と謳ってたものが正式にRubyInstallerに採用されていて、最新のRuby 2.4.2に追随してますので、現時点で最新が2.4.0のActiveScriptRubyより良さ気かもです。ちなみに環境構築では、3ステップRuby本ではActiveScriptRubyを、Rails超入門ではRubyInstaller2ベースのRubyInstallerを採用してまして、ちょっと読者的には混乱する元になっているかもしれません…(まぁ別の本なので問題ないかな)

ruby style guideを詳細まで良く読むように

プログラミング初学者向けの本なので、色んな書き方があるよ、というアナウンスより、できるだけruby style guideで提案されている書式に従った書き方を提示した方が良いかな、ということで、ruby style guideに色々目を通すようになりました。
例えば、「failよりraiseを使おう(Prefer raise over fail for exceptions.)」ってのは新鮮で(といっても2015年12月20日にマージされているようですが)、古いrubocop使ってるとfail使えって言われる場面もあったりしてこの辺は初学者が少々混乱する元になっているかもしれません。

(特に初学者向けの)書籍は書いても削られる

これは百聞は一見に如かずということで、ホントはgithubのprivateリポジトリで管理している仮脱稿原稿と本脱稿原稿の差分プルリクエストをみてもらいたいところなんですが、リポジトリをpublicにすると流石にダメそうな気がするので、3ステップRuby本のリポジトリを一部画像やコードでお伝えします。雰囲気感じ取って下さい。

スクリーンショット 2017-12-01 1.17.38.png

元原稿がちょっと特殊なテキスト形式なので単純に行数で比較はできないところがあるんですが、仮脱稿時の行数が6,236行あるのに対し、最終的な本脱稿後は追加が+3,387行、削除が-4,388行となっており1,001行が削除されています。これは行数でいうと15%削減になりますが、実際は手順のキャプチャなども削られており、約35%くらいを削っています。
3ステップRuby本は初学者向け&シリーズ本ということもあり、解説図を作成したり手順で必要な実行結果なども全て律儀にスクショ撮ったりしているので、その辺の工数が無駄になってしまうのは正直悲しいものがありました。

特にクラスを扱っている7章では、以下の通り2,013行→589行とガッツリ削ってます。。
スクリーンショット 2017-12-01 2.08.59.png

以下では、特に7章の解説で削られたClassクラス特異クラス/メソッドあたりのサンプルコードたちを掲載しておきます。実はこの章、7節で構成されていたんですが、残念ながらこれらを扱っていた最後の7節は節全体が葬られることになりました。合唱。後で振り返ってみて、初学者向けなのでまぁ致し方ないかなと今は思っていますが…

# クラスを別の書き方で定義する
SpecialClass = Class.new do
  def call_instance_method
    p 'called instance_method.'
  end
end

# Specialclassのインスタンスを生成
instance = SpecialClass.new

# インスタンスメソッド呼び出し
instance.call_instance_method
# 名前のないクラス(無名クラス)を定義する
class_object = Class.new do
  def call_instance_method
    p 'called instance_method.'
  end
end

# 無名クラスでもオブジェクト生成できる
instance = class_object.new

# インスタンスメソッドも呼び出せる
instance.call_instance_method

# クラスを表示するとClassクラスである
p instance.class

# クラス名はnil
p instance.class.name
# クラス定義の外側からクラスメソッドを定義する
SpecialClass = Class.new do
  def call_instance_method
    p 'called instance_method.'
  end
end

# クラスの外側からクラスメソッドを定義できる
def SpecialClass.call_class_method
  p 'called class_method.'
end

instance = SpecialClass.new
instance.call_instance_method

# 定義したクラスメソッドが呼び出せる
SpecialClass.call_class_method
# インスタンスメソッドとクラスメソッドを
# これまでの書き方で再定義する
class SpecialClass
  def call_instance_method
    p 'called instance_method.'
  end

  def self.call_class_method
    p 'called class_method.'
  end
end

# 実行結果は同じ
instance = SpecialClass.new
instance.call_instance_method

SpecialClass.call_class_method
# インスタンスに直接メソッドを定義する
SpecialClass = Class.new do
  def call_instance_method
    p 'called instance_method.'
  end
end

instance = SpecialClass.new
instance.call_instance_method

# インスタンスにメソッドを定義できる
def instance.call_singleton_method
  p 'called singleton_method.'
end

# インスタンスに定義したメソッドを呼び出せる
instance.call_singleton_method
# インスタンスに定義したメソッドは
# インスタンスに閉じたスコープのみ使用可能
SpecialClass = Class.new do
  def call_instance_method
    p 'called instance_method.'
  end
end

instance = SpecialClass.new
instance.call_instance_method

def instance.call_singleton_method
  p 'called singleton_method.'
end

instance.call_singleton_method

# 別のインスタンスを生成
other_instance = SpecialClass.new

# インスタンスメソッドは呼び出せる
other_instance.call_instance_method

# このインスタンスにはメソッドが定義されていないのでエラー
other_instance.call_singleton_method
# インスタンスに定義したメソッドのクラスを確認する
SpecialClass = Class.new do
  def call_instance_method
    p 'called instance_method.'
  end
end

instance = SpecialClass.new

def instance.call_singleton_method
  p 'called singleton_method.'
end

instance.call_singleton_method

# インスタンスのクラス
p instance.class

# インスタンスの特別なクラス(特異クラス)
p instance.singleton_class
# 特異メソッドの定義場所を確認する
SpecialClass = Class.new do
  def call_instance_method
    p 'called instance_method.'
  end
end

instance = SpecialClass.new

def instance.call_singleton_method
  p 'called singleton_method.'
end

# インスタンスメソッドはSpecialclassに定義済
p instance.class.method_defined?(:call_instance_method)

# 特異メソッドはSpecialclassには未定義
p instance.class.method_defined?(:call_singleton_method)

# インスタンスメソッドは特異クラスに定義済
p instance.singleton_class.method_defined?(:call_instance_method)

# 特異メソッドは特異クラスに定義されている
p instance.singleton_class.method_defined?(:call_singleton_method)

以上、削られたサンプルコードでした。
今改めて読み返してみると流石にプログラミング初学者向けにはちょっとアレだったかもしれません。。言い訳をすると、この節はただ書いてて私が楽しかったので「ついつい調子に乗ってしまった」って感じだったかも…

書籍執筆の工程が破滅的に効率がわるdjd;あおいsdふ

あ、手が滑ってしまいました。どうやら疲れてきた模様。

先日「BPStudy#123〜技術書籍執筆の実際、ノウハウ」なる勉強会に行ってきて、技術書籍執筆者の方々とも会話してきまして、執筆者のエンジニア陣は原稿をgithubで管理したり、コミュニケーションにslack使ったり、「例えば」「たとえば」などの表記揺れをtextlintで自動で校正したり、当たり前のように効率的に執筆作業を進めるのですが、テキスト原稿を元に印刷レイアウトに落とし込んだものが最初にあがってくる「初校」と呼ばれる工程が、何とも言えずめんどい。

なんとPDFであがってくるんですよね。PDF編集ソフトとかDropbox上で赤字入れるんですが、これがやばい(ちなみに私は前者でやりましたが、先程の勉強会ではDropboxのがやりやすい的な話がありました)。何故やばいかと言うと、初校から先の工程は実は同様のことを何度か繰り返す(初校→再校→三校→…)ことが多く、履歴が管理し辛い問題が発生しがちだからです。
更に、もし改訂版など出そうものなら更にやばく、最新のテキスト原稿にPDFでやり取りしている修正内容が反映されていないと何が最新か分からなくなって確実に詰みます(この話も先程の勉強会で話題出てました)。

なお、特に3ステップRuby本は結構特殊なところもあったようで、初校が出て来るのに大幅に時間がかかり、構成案検討開始が2016年12月頃で現在「三校」というフェーズでかれこれ1年も経過してまだ発刊できてません。。
この辺りはもっと効率的にやれる方法がありそうな気がしていて、自動で書籍レイアウト組してくれてバージョン管理もgithub相当な良い感じでできる技術書籍プラットフォームみたいなものがあったら、エンジニアはみんなそっちに乗っかるんじゃないかと思ったりします。

個人的にはQiita運営のIncrementsさんとかがやってくれそうな予感がしなくもないですが。絶対ニーズあると思うんですよね。なんならいっそ、eBookメインで販売してネット出版社的な立ち位置を築いてもらえれば、「俺の本、今度オライリーから出るんだぜ」から「俺の本、今度Qiitaから出るんだぜ( ・´ー・`)どや」みたいな感じになって幸せが訪れる気がします。

Qiita運営の方、見てます?

プログラミングを学習する意義を見つめ直す機会に

書籍執筆を通して得られたことの中で、自分の中で最も大きかったことかもしれません。

まず書籍化する作業は、「自分の中にある知識を体系的に再編する知的行為」と言えます。しかも今回執筆した2冊がプログラミング初学者向きであったように、執筆中に想定読者を想像して対話する必要があります。その想像の対話の中では、例えば以下のようなやり取りが発生するわけです。

読者:「文字コードを明示的に指定するrubyコマンドオプションってなんでしたっけ?」
筆者:「-Kuオプションだよ」
読者:「uって大文字でも大丈夫ですか?」
筆者:「えっと、どうだったっけかな…(確認して)あ、大丈夫だね」

いわば「教える」行為と「教わる」行為を筆者の頭の中だけで繰り広げるのってある意味凄いことですよね。本来、「教える」と「教わる」は表裏一体なんだというのが持論ですが、書籍執筆はより抽象度が高いというか、想像力が必要だったりするなと思います。特にプログラミング初学者向けだとそれが尚更求められます。

執筆作業中の頭の中でのこのような対話を通して思いを馳せるようになったのは、自分がプログラミング初学者だった頃のことと、脱・初学者した頃のことです。主に中学生の頃、大学生の頃、社会人エンジニアになりたての頃ですが、13〜25年前のことで実はあまり記憶が定かではありませんでした。

ただ、執筆を通じて振り返る中で、プログラミングの学習曲線がうなぎのぼりに変化するポイントは「実際に手を動かして出来たプログラムと、抽象化された概念が自分の中で腑に落ちた時」だったのでは?と再確認できました。

どちらの書籍でも執筆中に何度も編集サイドから指摘されたのは「話が抽象的過ぎて想定読者には伝わらない」でした。抽象化されたハイコンテキストな会話ができるのは、普段から理解度が高い者同士でやり取りしているのに慣れているからであり、それに慣れすぎてしまうと噛み砕いて説明できなくなってしまいます。説明できないということは、実は理解出来ていない証左となる、ということにもなり、矛盾しているようだけど興味深いなと。

ところで小学校では2020年からプログラミングが必修化されます。その目的は文科省の資料の中に、「プログラミング的思考を育成すること」とあります。プログラミング的思考とは、以下のように定義されています。

「自分が意図する一連の活動を実現するために、どのような動きの組合せが必要であり、一つ一つの動きに対応した記号を、どのように組み合わせたらいいのか、記号の組合せをどのように改善していけば、より意図した活動に近づくのか、といったことを論理的に考えていく力」

これは言い換えれば、先程の「抽象化された概念」を組合せて課題解決する能力とも捉えられるかなと。これはエンジニアの本質であり、プログラミングを学習する理由もそこにあるのではないかと感じています。

大事なことなので最後に再度

買って下さい。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14