Make組ブログ

Python、Webサービスや製品開発、ライブラリー開発についてhirokikyが書きます

10年以上のノウハウを詰め込んだ「自走プログラマー」を執筆しました

f:id:hirokiky:20200220104314j:plain
自走プログラマー表紙

「自走プログラマー」という本が出ます!

この本は僕と清水川さん、tell-kさんで、株式会社ビープラウドの仕事として書いた本です。 自走プログラマーには僕の10年来の開発ノウハウを詰め込みました。清水川さんtell-kさんに至ってはもっと長い経験があります。その3人が、入門本ではない本を本気で書きました。さらにビープラウドのつよつよメンバーが何度も何度もレビューしてくれました。

僕は自走プログラマーを多くの人にぜひ読んでほしいと思っています。ですが、「とにかく買ってほしい」とはあまり思っていません。 なぜかというと、普段、 僕(著者全員)が伝えたいこと・伝えてきたことを書いた本 だからです。 なので「多くの人に読んで欲しい」、「これで助けになってほしい」と思っています。むしろビープラウドでは自走プログラマー(とPythonプロフェッショナルプログラミング)を読んでもらったうえで普段話をしたいです。

プログラミングで手戻りが多くありませんか?

レビューなどで、多くの指摘をうけて 手戻りが大きくなっていませんか ? 実際に 本番環境で運用しはじめてから問題に気づく ことは多くないですか?

自走プログラマーはそういった人のための本です。自走プログラマーの前書きより引用します。

開発現場で起こった実際の問題とその解決法をもとに,文法以外に必要な「プロジェクトの各段階でプログラマーがやること」「その選択をどう判断するのか」「どうコードを実装して実現していくのか」を解説します。

こんな方におすすめ:

  • プログラムを書けるけど,レビュー指摘などで手戻りが多い人
  • 優れたエンジニアになりたい人
  • 設計の仕方や,メンテナンス性の高いプログラムの書き方を知りたい人

自走プログラマーは120個のプラクティスを通して学べる本です。 各プラクティスには具体的な失敗とベストプラクティス、説明が書かれています。 120個のプラクティスはこちらです(以下、長いです)。 気になるものがあるか見てみてください。本の雰囲気も伝わると思います。

自走プログラマーの目次

  • 1.1 関数設計

    • 1 関数名は処理内容を想像できる名前にする
    • 2 関数名ではより具体的な意味の英単語を使おう
    • 3 関数名から想像できる型の戻り値を返す
    • 4 副作用のない関数にまとめる
    • 5 意味づけできるまとまりで関数化する
    • 6 リストや辞書をデフォルト引数にしない
    • 7 コレクションを引数にせずintやstrを受け取る
    • 8 インデックス番号に意味を持たせない
    • 9 関数の引数に可変長引数を乱用しない
    • 10 コメントには「なぜ」を書く
    • 11 コントローラーには処理を書かない
  • 1.2 クラス設計

    • 12 辞書でなくクラスを定義する
    • 13 dataclassを使う
    • 14 別メソッドに値を渡すためだけに属性を設定しない
    • 15 インスタンスを作る関数をクラスメソッドにする
  • 1.3 モジュール設計

    • 16 utils.pyのような汎用的な名前を避ける
    • 17 ビジネスロジックをモジュールに分割する
    • 18 モジュール名のオススメ集
  • 1.4 ユニットテスト

    • 19 テストにテスト対象と同等の実装を書かない
    • 20 1つのテストメソッドでは1つの項目のみ確認する
    • 21 テストケースは準備,実行,検証に分割しよう
    • 22 単体テストをする観点から実装の設計を洗練させる
    • 23 テストから外部環境への依存を排除しよう
    • 24 テスト用のデータはテスト後に削除しよう
    • 25 テストユーティリティーを活用する
    • 26 テストケース毎にテストデータを用意する
    • 27 必要十分なテストデータを用意する
    • 28 テストの実行順序に依存しないテストを書く
    • 29 返り値がリストの関数のテストで要素数をテストする
    • 30 テストで確認する内容に関係するデータのみ作成する
    • 31 過剰なmockを避ける
    • 32 カバレッジだけでなく重要な処理は条件網羅をする
  • 1.5 実装の進め方

    • 33 公式ドキュメントを読もう
    • 34 一度に実装する範囲を小さくしよう
    • 35 基本的な機能だけ実装してレビューしよう
    • 36 実装方針を相談しよう
    • 37 実装予定箇所にコメントを入れた時点でレビューしよう
    • 38 必要十分なコードにする
    • 39 開発アーキテクチャドキュメント
  • 1.6 レビュー

    • 40 PRの差分にレビュアー向け説明を書こう
    • 41 PRに不要な差分を持たせないようにしよう
    • 42 レビュアーはレビューの根拠を明示しよう
    • 43 レビューのチェックリストを作ろう
    • 44 レビュー時間をあらかじめ見積もりに含めよう
    • 45 ちょっとした修正のつもりでコードを際限なく書き換えてしまう
  • 2.1 データ設計

  • 2.2 テーブル定義

    • 49 NULLをなるべく避ける
    • 50 一意制約をつける
    • 51 参照頻度が低いカラムはテーブルを分ける
    • 52 予備カラムを用意しない
    • 53 ブール値でなく日時にする
    • 54 データはなるべく物理削除をする
    • 55 typeカラムを神格化しない
    • 56 有意コードをなるべく定義しない
    • 57 カラム名を統一する
  • 2.3 Django ORMとの付き合い方

  • 3.1 エラーハンドリング

    • 63 臆さずにエラーを発生させる
    • 64 例外を握り潰さない
    • 65 try節は短く書く
    • 66 専用の例外クラスでエラー原因を明示する
  • 3.2 ロギング

    • 67 トラブル解決に役立つログを出力しよう
    • 68 ログがどこに出ているか確認しよう
    • 69 ログメッセージをフォーマットしてロガーに渡さない
    • 70 個別の名前でロガーを作らない
    • 71 info,errorだけでなくログレベルを使い分ける
    • 72 ログにはprintでなくloggerを使う
    • 73 ログには5W1Hを書く
    • 74 ログファイルを管理する
    • 75 Sentryでエラーログを通知/監視する
  • 3.3 トラブルシューティングデバッグ

    • 76 シンプルに実装しパフォーマンスを計測して改善しよう
    • 77 トランザクション内はなるべく短い時間で処理する
    • 78 ソースコードの更新が確実に動作に反映される工夫をしよう
  • 4.1 プロジェクト構成

    • 79 本番環境はシンプルな仕組みで構築する
    • 80 OSが提供するPythonを使う
    • 81 OS標準以外のPythonを使う
    • 82 Docker公式のPythonを使う
    • 83 Pythonの仮想環境を使う
    • 84 リポジトリのルートディレクトリはシンプルに構成する
    • 85 設定ファイルを環境別に分割する
    • 86 状況依存の設定を環境変数に分離する
    • 87 設定ファイルもバージョン管理しよう
  • 4.2 サーバー構成

    • 88 共有ストレージを用意しよう
    • 89 ファイルをCDNから配信する
    • 90 KVS(Key Value Store)を利用しよう
    • 91 時間のかかる処理は非同期化しよう
    • 92 タスク非同期処理
  • 4.3 プロセス設計

    • 93 サービスマネージャーでプロセスを管理する
    • 94 デーモンは自動で起動させよう
    • 95 Celeryのタスクにはプリミティブなデータを渡そう
  • 4.4 ライブラリ

    • 96 要件から適切なライブラリを選ぼう
    • 97 バージョンをいつ上げるのか
    • 98 フレームワークを使おう(巨人の肩の上に乗ろう)
    • 99 フレームワークの機能を知ろう
  • 4.5 リソース設計

    • 100 ファイルパスはプログラムからの相対パスで組み立てよう
    • 101 ファイルを格納するディレクトリを分散させる
    • 102 一時的な作業ファイルは一時ファイル置き場に作成する
    • 103 一時的な作業ファイルには絶対に競合しない名前を使う
    • 104 セッションデータの保存にはRDBかKVSを使おう
  • 4.6 ネットワーク

    • 105 127.0.0.1と0.0.0.0の違い
    • 106 ssh port forwardingによるリモートサーバーアクセス
    • 107 リバースプロキシ
    • 108 Unixドメインソケットによるリバースプロキシ接続
    • 109 不正なドメイン名でのアクセスを拒否する
    • 110 hostsファイルを変更してドメイン登録と異なるIPアドレスにアクセスする
  • 5.1 要件定義

    • 111 いきなり作り始めてはいけない
    • 112 作りたい価値から考える
    • 113 100%の要件定義を目指さない
  • 5.2 画面モックアップ

    • 114 文字だけで伝えず,画像や画面で伝える
    • 115 モックアップは完成させよう
    • 116 遷移,入力,表示に注目しよう
    • 117 コアになる画面から書こう
    • 118 モックアップから実装までをイメージしよう
    • 119 最小で実用できる部分から作ろう
    • 120 ストーリーが満たせるかレビューしよう

こうやって見ると、よく書いたもんだなと思います。 著者の3人が個別のプラクティスを分業して書きました。

ちなみに僕の担当は1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 25, 28, 29, 30, 31, 32, 49, 50, 51, 52, 53, 54, 55, 56, 57, 69, 70, 71, 72, 73, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120です!

手取り足取り教えてくれる本ではありません

網羅的かつ段階的に設計やアプリケーション開発を教える本ではありません。 ですので「入門書はとりあえず終わったので次に何かを教えてくれる本が欲しいな」という人には残念ながら向いていません。 本が課題やサンプルを提示して、 「あなたもこれを作ってみよう」という本ではありません

ですが、設計やアプリケーション開発に重要なこと、ノウハウやプラクティスはたくさんお伝えしています。 何か作りたいものがあって、挑戦しているがうまくいかないことが多く悩んでいる人にはオススメします 。 逆に、何か作りたいものも分からない人や、自力でプログラムを書いてみたことがあまりない人にはオススメしません。

もし先輩プログラマーの人でこの本を後輩さんに読ませたいなというときは、この本がアドバイスや導きのキッカケになると嬉しいです。 もしかしたら、キッチリ題材を作って学ばしてあげる必要があるかもしれません。1プラクティスに書かれている内容が、先輩的には十分とは思えないかもしれません。 そのときはぜひ、直接後輩さんや他メンバーにあなた自身のノウハウを教えてあげてください。

"Pythonの本"ではありません

自走プログラマーは、 Pythonに限らない広い範囲で使える知識を読める本 です。 そういう意味では既存のPythonの本とは少し違います。どちらかというと、リーダブルコードに近いかもしれません。

内容はプログラミング言語Pythonを使って書かれていますし、Python製のライブラリーやフレームワークを元にした説明が中心になります。 なので、プログラミングも詳しくないし、Pythonも詳しくないという人には分かりにくいかもしれません。 逆に、RubyRuby on Railsに慣れている人などは勉強になると思います。具体的にPython製のライブラリーを紹介しているトピックなどはありますが、その部分は「Rubyでもこういうライブラリーはあるのかな?」と考えて読んでいただければと思います。

長い長い執筆期間と込めたノウハウ

この本は著者陣が仕事の中で他の人に教えたことを中心に執筆しています。 たとえばコードレビューや、社内でのサポート、社内外の研修、コンサルティングの中で伝えてきたことをまとめています。 ビープラウド社内のメンバーも積極的にレビュー、コメントしてくれて、社内のかなりのノウハウが取り込まれています。

執筆に際しては社内で半年から1年をかけてネタを集めて、選別する時間を設けました。 著者の3人が日々の業務で伝えたことや感じたこと、過去に社内で伝えたことをネタ帳として集めてから執筆しています。 たとえば「ログには5W1Hを書こう」という僕が執筆を担当したプラクティスがあるのですが、これは5年前(2015年)から社内では共有していた知識です。これは社内でも好評で、「ロギングって大事だけど、何を書くべきかを学べる場所がなかった」とフィードバックを受けていました。 そのノウハウがやっと日の目を見ることになって、僕個人としてはとても嬉しいです。こんなにうれしいことはない。。

それ以外にもこの本は紆余曲折あり、一旦けっこう書いた内容を、構成や構造を変えて新しくしたりと大変でした。 執筆メンバーが抜けて再スタートしたり、正直、時間をかなり書けたので費用対効果はかなり悪い本になってしまったなと思っています。 ですがその分、長い時間を使っただけに本当に良い内容だけが本に抽出されたように思います。

ぜひ、この本を読んで日々のプログラミングやチームの改善に役立ててもらえると嬉しいです。

電子書籍は発売中です! (2020年2月22日より発売)物理は2月27日に発売です!

Epub/PDF版は技術評論社のサイトから購入できます(2月22日より)。 PDFで欲しい!という方は技術評論社からご購入ください。

他に知りたいこととか気になることがあれば何でも @hirokiky に聞いてください。

Pythonのリストはイテレーターでない。わかりやすい(はずの)イテレーターとイテラブルの説明

こんにちは。 Pythonに関する書籍を書く際や、人に説明するときに「リストはイテレーター」と言っていませんか? それは間違いです。僕自身も勘違いしちゃうことがよくあるので、記憶を整理する意味でもブログにまとめておきます (もし間違えていたら教えてくれると嬉しいです)。

とくに、「リストはイテレーターです」、「rangeはイテレーターです」、「dict.keys()はイテレーターを返します」、など間違いが多いので注意しましょう。

リストはイテレーターではない

まず、リストはイテレーターではないということを感じてもらいます。 以下をPythonインタプリターで実行してください。 TypeError: 'list' object is not an iterator とエラーが表示されるはずです。

>>> l = [0, 1, 2, 3]
>>> next(l)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator

はい。Pythonさんもリストはイテレーターではないと言っています。

「forループで使えるものはイテレーターじゃないの?」 という疑問があるかもしれません。うーん、惜しい。 その答えは イテラブル(Iterable) です。

リストもrangeオブジェクトもdict.keys()も、「イテラブル(Iterable)」と言えば間違いありません。 イテラブルは for in ... に渡せるものです。

早く答えが欲しい人へ

早い説明をします。イテラブルかイテレーターかの簡単な判別方法をお伝えします。 超ざっくりと説明すると、以下です。

  • iter(...) できるものは、イテラブル
  • next(...) できるものは、イテレータ

本に説明を書くときとかは、一度上記を実行しておくと安心かもしれません。 往々にして説明したいのは「イテラブル」だと思いますが。

じゃぁイテレーターって何?

イテレーターは反復するもので、その進行状況を持っているものです。 forループは指定されたイテラブルからイテレーターを取り出して、ループの1回1回を処理していきます。 おっと、意味がわかりませんね!(ブラウザを閉じないで!)

ではfor文の気持ちになってリストの処理をしましょう。 for文は以下のようにして、リストをループします。

>>> l = [0, 1, 2, 3]  # これはリスト
>>> l_iter = iter(l)  # リストからlist_iteratorを取り出す
>>> next(l_iter)
0
>>> next(l_iter)
1
>>> next(l_iter)
2
>>> next(l_iter)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

このようにリストイテレータを順次 next(...) することで1つ1つリストの要素を取り出しています。 すべての要素が取り出されると、イテレーターは StopIteration を送出します。

なぜこのようになっているかというと、簡単に言えばforループの繰り返し処理を制御するためです。 リストをループするときに、ループがどこまで進んでいるかを管理するためにリストイテレーターが使われています。

もしリスト自身がイテレーターであれば、1度ループしただけでリストはループできなくなってしまいます(ループの進行状況が管理されるので)。 そのために、リスト自身ではなくリストイテレーターさん(つまりイテレーター)にループの状況を管理してもらっています。Pythonってすごい!

イテレーターは同時にイテラブルでもあります。 なので、イテレーターは for in ... に渡せます。この場合、イテレーターは iter(my_iterator) をすると my_iterator 自身を返します。

また、for文は以下のようにも模式的に書けます。 この my_for 関数は、第一引数にイテラブルを、第二引数にforブロックの処理に相当する関数を渡します。

>>> def my_for(iterable, iter_func):
...     it = iter(iterable)
...     try:
...         while True:
...             el = next(it)
...             iter_func(el)
...     except StopIteration:
...         pass
...

そうすると、forを使わずにforのような動きができます (あくまでも模式的にですが)。

>>> def print_pow(el):
...     print(el ** 2)
...
>>> my_for([0, 1, 2, 3], print_pow)
0
1
4
9

イテレーターって何があるの

じゃぁイテレーターって何があるの?と思われるかもしれません。 たとえばジェネレーターや BytesIO などがイテレーターです。よく「ループしたら使い切られる」やつがイテレーターです。

  • イテレーターではない(イテラブル)
    • 文字列
    • リスト
    • タプル
    • rangeオブジェクト
    • 辞書、辞書.keys()、辞書.values()、辞書.items()
    • 集合
  • イテレーター(イテラブル)
    • リストとかタプルとかrangeとかを iter したら返ってくるやつ
    • ジェネレーター
    • ファイルとかのI/O

ちなみに yield を使った関数は「ジェネレーターを返す関数」であり、イテラブルではありません。

ロジクールの新作キーボードMX Keys は本当にガチプログラマー向けなのか?

今日はキーボードの話をします。

f:id:hirokiky:20191115220707j:plain
MX Keysキーボード

はい。これが ロジクールMX Keys X800というキーボードです 。つい最近の19年9月27日に販売されはじめたそうです。 パット見の印象は正直派手さのない印象ですが、 クリエイター、プログラマーといったパワーユーザー向けの製品だそうです

www.logicool.co.jp

というのもこのキーボード、どうやらPRの一貫かでロジクールさんから送られてきました。 僕で大丈夫なんでしょうか。気の利いた発言ができない僕なので、少し申し訳ない気もしつつ受け取りました。

自宅のキーボードはMiSTEL BAROCCOで満足しているので、ちょうど何も無かったオフィスの環境でMX Keysを試しました。 macOSと繋いで使ってみたときの感想になります。

ロジクール MX Keysキーボードの感想

f:id:hirokiky:20191114145245j:plain

正直に言うと、 僕個人はぺらぺらしたキーボードはあまり好きではありません 。 そしておそらくですが、プログラマーでガチでキーボードが好きだという人は、ブロックっぽいキーボードが好きだと思います。 なので、 このロジクールMX Keysはそもそも(コアな趣味の)プログラマーの選択肢に入らないのでは? とすら思います。

ロジクールMX Keysの打鍵感は、けっこう良い

でも、 使ってみると意外と良い感触です

数日使った感じも、かなり打鍵感は良いです。ペラペラキーボードにありがちな、ペチペチした感触ではありません。 クニッと、ポコッと、良い抵抗感で入っていく感じです。薄いキーボードと、普通の分厚いキーボードの間くらいの印象です。

キートップも緩やかにくぼみがついていて、フィット感も良いです。 薄いキーボードの中では生涯で一番良い感触 だと思います。

キー配列、レイアウトについて

キー配列はJISですが、僕は日英両対応なので問題なく良かったです。 ただ僕は普段、Dvorak配列を当てはめて使ってるので、Qwerty配列的に使いやすいかどうかは知りません。

ハードウェアレベルでのキーマッピングができないのもオタク的には物足りない感はありますが、ぶっちゃけmacならKarabinerElementsを使えば良いですよね。 (ハードレベルでキー変えるとか入力パターン設定とか、冷静に考えてそんなに要る?って思う)。

僕的にロジクールMX Keysのイケてないところ

ただ、僕的にはやはりイケてないところはあります

  • 横が長すぎ
    • ラップトップの上にキーボードを載せて使えない
    • HHKB、BAROCCOのような短いキーボードが好きな僕にとっては大きすぎ
    • テンキーって要らないですよね。数字もブラインドで入力できれば不要です
  • 分割キーボードでない
    • 慣れると分割キーボードが楽すぎる
  • 良いけどやっぱりペラペラキー

細かな良いところ

まぁ悪いところばかりではありません。

  • Bluetoothキーボードってやっぱいいよね
    • Bluetoothでありながらスラッとしたデザインでまとまってて良い
  • macOSのMissionControlを(ドライバーを入れれば)キーから呼び出せたりは便利
  • キーボードのバックライトが上品かつカッコイイです。
  • Escキーが大きいのも良い
    • が、Emacsユーザーなのでそれほど興味はない

総評: わりとオススメはするが、僕にとっては、ギリ候補にはなるレベル

ロジクールMX Keysを買うとして、どうでしょうか? 比較的安価な値段で、とてもクオリティ高く使いやすいです。14,500円分の価値は十分あると思います。素直に良い製品だなぁと思います。

例えばMacbookのうっっすいキーボードが嫌で、 Apple純正のワイヤレスキーボードを買おうかなと思ってる人にはロジクールMX Keysもオススメします 。 ドライバーもよくできてて使い勝手もいいので、テンキーが好きならMX Keysはオススメです。

ただ、僕が買うとしたら「ギリ候補になる」くらいでしょうか。 理由は、 プログラマー向けな良いキーボードは他にたくさんあるから です。 他にも良い製品はたくさんあるので、それを調べてから買っても遅くはないでしょう。

ロジクールMX Keysに14,500円を出すなら、僕ならもう少し頑張ってHHKB Professinal2を買うか、(当時の定価でなら)MiSTEL BAROCCO MD600が17,000円で買うほうが良いです(好みの問題で)。

f:id:hirokiky:20191115222024j:plain
MiSTEL BAROCCO MD600

「薄型の普通目なキーボードが好き」「でも、打鍵感が良く疲れないキーが欲しい!」という人にはロジクールMX Keysをオススメします 。 でも、プログラマー向けでカッコイイキーボード欲しいなぁと思うなら、HHKB Type-S をまず検討して欲しいです。

PFU キーボード Happy Hacking Keyboard Professional2 Type-S 無刻印/白 PD-KB400WNS

PFU キーボード Happy Hacking Keyboard Professional2 Type-S 無刻印/白 PD-KB400WNS

間違いないから。

他にオススメの記事

ロジクールMX Master3ってマウスも貰ったけど、 そっちはもっと気に入ったから次に書きます

この記事もオススメ

blog.hirokiky.org

Djangoのマイグレーションで実行される処理(とSQL)を確認する方法

Djangoマイグレーションで実行される処理(とSQL)を確認する方法を説明します。

先日の記事では「Djangoマイグレーションを無停止で反映する方法」を説明しましたが、そのためにはマイグレーションでどんなSQLが実行されるかが重要になります。

blog.hirokiky.org

もちろんマイグレーションファイルを開いて見れば分かりますが、Djangoのコマンドから便利に確認したり、実際に発行されるSQLを確認しておくほうが安全です。

実行されるマイグレーションのプランを確認する

Djangomigrate --plan コマンドを使って、実行されるマイグレーションのプランを確認できます。 この例では blog アプリの 00010002 というマイグレーションが適応されていない状態です。

python manage.py migrate --plan
Planned operations:
blog.0001_initial
    Create model Post
blog.0002_post_published_at
    Add field published_at to post

--plan を見るとそれぞれのマイグレーションでどのような処理が実行されるかが分かります。 この例では以下の意味になります。

  • 0001: モデルPostを作成する
  • 0002: postにpublished_atフィールドを追加する

両方のマイグレーションファイルともテーブルやカラムを追加するものなので、(基本的に)Webアプリのリリース前にマイグレーションを実行できます。

マイグレーションで実行されるSQLを確認する

migrate --plan で内容を確認できるとはいえ、実際にマイグレーションで実行されるSQLを確認しておくほうが安心です。 Djangoにおいてマイグレーションで実行されるSQLを確認するには、 sqlmigrate コマンドを使います。 アプリケーション名 <app_name>マイグレーションの名前 <migration_name> を指定して使います。

python manage.py sqlmigrate <app_name> <migration_name>

例えば blog アプリの 0002_post_published_at.py マイグレーションファイルで実行されるSQLを確認するには、以下のようにします。

python manage.py sqlmigrate blog 0002

こうすることで 0002_post_published_at.py が反映されるときに実行されるSQLが表示されます。

BEGIN;
--
-- Add field published_at to post
--
ALTER TABLE `blog_post` ADD COLUMN `published_at` datetime(6) NULL;
COMMIT;

SQLの内容を見ることで、マイグレーション実行時にどのような処理がデータベースに行われるかが分かります。 Webアプリを無停止のままマイグレーションを反映するときは、このSQLの内容に応じてどのようにリリースすべきかが変わります。

本番環境と同じデータベースバックエンドを使って確認しよう

ただし、 sqlmigrate の結果は settings で使われているデータベース(DATABASES 設定の内容)に応じて結果は変わります。 必ず運用している環境と同じデータベースバックエンドを使ってSQLを確認しましょう。 例えば、上記の例はMySQLバックエンドで出力しましたが、SQLite3バックエンドでは以下のような別のSQLになります。

BEGIN;
--
-- Add field published_at to post
--
CREATE TABLE "new__blog_post" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "published_at" datetime NULL, "body" text NOT NULL);
INSERT INTO "new__blog_post" ("id", "body", "published_at") SELECT "id", "body", NULL FROM "blog_post";
DROP TABLE "blog_post";
ALTER TABLE "new__blog_post" RENAME TO "blog_post";
COMMIT;

まとめ

以下2つのコマンドを使ってマイグレーションの内容を確認しましょう。

これでマイグレーションSQLの内容が分かったらWebアプリを無停止でマイグレーションをできるかを検討しましょう。

blog.hirokiky.org

DjangoマイグレーションをWebアプリが無停止のまま安全に反映する方法

f:id:hirokiky:20191101104548j:plain

Djangoはデータベースマイグレーションの機能を持っています。 ですが、 実際、Djangoマイグレーションってどう使うの? という疑問が多いかと思います。

docs.djangoproject.com

この記事では、 マイグレーションを稼働中のアプリケーションに、無停止でどう反映すれば良いのか を説明します。 前提としてWebアプリ、データベースは本番環境に1系統づつあるとします。

基本的に無停止でマイグレーションを実行するのは 絶対に安全という方法ではないので、動作確認などをして慎重に反映する必要があります

無停止でマイグレーションを反映する基本

マイグレーション無停止で行う場合、「マイグレーションとアプリのリリースはどちらを先にすべきか」 という話になります (マイグレーションをするということは、アプリケーションの変更も必要になります)。

上記ではない場合、アプリケーションを停止する時間が必要になります(アプリケーションを停止、マイグレーションを実行、アプリケーションを変更して実行する)。

マイグレーションの3種類

アプリケーションを無停止のままマイグレーションを反映するには、まずマイグレーションの種類を判断する必要があります。 ここでは大きく分けて3つのマイグレーションと考えられます。

追加、変更、削除それぞれのマイグレーションをどう反映するのかが重要です。

動作確認は必要

無停止でマイグレーションを実行するのは、Webアプリが誤動作を起こすリスクを持っています。 方法は後述しますが、アプリケーションの作り方によって問題が発生し得ます。また、動作が保証される状態でもありません。 本番環境へリリースする前に、マイグレーションを実行しても問題ないか開発環境で必ず動作確認しましょう

マイグレーションを実行するタイミング

アプリケーションを無停止でマイグレーションを実行するには種類と実行するタイミングが重要です。 それぞれマイグレーションの種類ごとに、稼働中のWebアプリにマイグレーションを実行する方法を説明します。

テーブル、カラムを追加するマイグレーションの場合

テーブル、カラムの追加をするマイグレーションは、アプリのリリースより前に実行できます。

テーブル、カラムが増えたところで、Django製のアプリケーションには基本的に影響がありません。 ですので、Webアプリケーションのリリース前にマイグレーションを実行します。

  1. マイグレーションを実行する
  2. Webアプリケーションをリリースする

f:id:hirokiky:20191101104548j:plain
追加するマイグレーションを無停止で反映

テーブルやカラムが増えても、既存のDjango製のWebアプリケーションの動作や、通信量には影響がありません。 DjangoSQLを実行するときに、取得するフィールド(カラム)を全て指定するからです。

ただし、DjangoのQuerySetでextraやrawを使う場合や、生SQLを実行する場合はこの限りではありません。 アプリケーション内に影響するプログラムは無いかは事前にちゃんと確認しましょう。

カラムの削除をするマイグレーション

カラムの削除をする場合は、先にWebアプリケーションをリリースします。 アプリケーションが先にリリースされることで削除対象のフィールド(カラム)は使われなくなります。

  1. Webアプリケーションのリリース
  2. マイグレーション実行

f:id:hirokiky:20191101104612j:plain
削除するマイグレーションを無停止で反映する方法

削除対象のカラムが完全に使われなくなったことを確認してからマイグレーションを実行できます。 ただし「カラムが絶対にWebアプリ内で使われていない」ことを確認するのは大変です。慎重に確認した後、マイグレーションを実行しましょう。

カラムを変更するマイグレーション

カラムを変更するマイグレーションの場合は注意が必要です。 マイグレーションを無停止で実行できるかどうかはマイグレーションの内容によります。

例えばカラム名を変更するマイグレーションは無停止ではリリースできません。 どちらを先に実行しても、稼働中のWebアプリケーションに影響を与えます。

f:id:hirokiky:20191101104027j:plain
変更するマイグレーションを反映する方法

  • 無停止でリリースできない
    • テーブル、カラム名の変更
    • カラムの型を変更
  • 無停止でリリースできる(可能性がある)
    • カラムの最大長などを変更
    • インデックスの追加、削除

マイグレーションの内容を理解して、無停止で安全に反映できるかを確認しましょう。 カラムの最大長変更なども、稼働中のアプリケーションに影響を与えますので注意しましょう。

マイグレーションが複数ある場合

カラムの追加するマイグレーション、削除するマイグレーションが複数ある場合、Webアプリが無停止のままでは同時に反映できません。 その場合はひとつひとつマイグレーションの実行と、アプリケーションのリリースをする必要があります。

Djangoマイグレーションを1つづつ実行する方法は別のブログ記事で説明します。

まとめ

Webアプリを無停止のままマイグレーションを反映するには、マイグレーションの内容を理解してリリースする必要があります。

改めてになりますが、本番環境にリリースする前には開発環境で動作確認しましょう。 開発環境で、無停止でリリースできることを確認してから本番環境でリリースしましょう。

Djangoマイグレーションの内容を確認する方法や、1つづつ反映する方法は別のブログ記事で説明します。

続きはこちらです!

blog.hirokiky.org

ブログ記事に書くPythonコードはPEP8を厳密に守るべき?

ブログに書くコードスニペットはPEP8を厳密に守るべきでしょうか。

pep8-ja.readthedocs.io

ブログ記事に書くPythonコードはPEP8を気にしすぎなくて良いのでは

僕はブログ記事に書くPythonコードは、PEP8をそこまで気にしないで良いと思っています。 ブログに書くコードはブログの記事中で読まれるので、普通のコードのようにエディターなどで読まれるコードとは違うからです。

  • エディターでプログラムを読むときとブログで読むときは違う
  • 開発プロジェクト内では必要な、書き方の統一がブログには不要
  • てきとうに伝えたいだけなので厳密に気にするのは面倒

もちろんブログ記事内でも読みにくいコードは記事にとって良くないですし、PEP8を全く考慮しない書き方を「この書き方が良いものだ」と伝えるのは良くないです。 ですが、自分の記事で厳密に気にしすぎたり、まして他人のブログ記事に「PEP8準拠していないぞ」とわざわざ言う必要は無いと思います。 僕もPEP8は守ろうと思っていますが、ブログ記事内で守っていないこともあります。

PEP8を守って読みにくいとき

ブログ記事にPythonコードを書いて、PEP8を守ると読みにくくなるときがあります。 例えば以下のようなPythonコードを見ると、 main() 関数の上2行が間延びして見えるときがあります。

import os

HERE = os.path.dirname(__file__)


def main():
    print(HERE)

このくらいのコードをいくつか連続して書くと、ブログ記事は読みにくくなると思います。 PEP8違反にはなりますが、行を詰めたほうが読みやすい場合もあると思います。

HERE = os.path.dirname(__file__)

def main():
    print(HERE)

むしろ import os も分かりきっているので書かなくて良いかなという気すらします。 ブログという媒体においては、下の例のほうが大切だと思います。

なにかの戒律を厳密に守る(強いる)ことよりも、場所や場合によって一番良い方法で書くのが良いのではないでしょうか。

PEP8を読んでみる

PEP8を開いてみると、あまり厳密に気にしすぎるのは違うよねと書いています。

### 一貫性にこだわりすぎるのは、狭い心の現れである

スタイルガイドは一貫性に関するものです。
このスタイルガイドに合わせることは重要ですが、プロジェクトの中で一貫性を保つことはもっと重要です。
一番重要なのは、特定のモジュールや関数の中で一貫性を保つことです。

しかし、一貫性を崩すべき場合があることも知っておいてください
 - つまり、このスタイルガイドが適用されない場合があります。
疑問に思ったときは、あなたの判断を優先してください。
他の例を調べ、一番良さそうなものを決めて下さい。そして、躊躇せずに質問して下さい!

https://pep8-ja.readthedocs.io/ja/latest/

まとめ

PEP8は可読性のために大切だと思いますが、ブログ記事に書くPythonコードまでそこまで細かく気にする必要は無いと思います。

他にオススメの記事

blog.hirokiky.org