kenju's blog

About Programming and Mathematics

『オブジェクト指向設計実践ガイド』を読んだ

設計力って難しくて面白い。日々そんなことを感じながら広告配信サーバーの設計だったり自分で作っているライブラリの設計だったりを毎日プログラマとしてしているわけで、設計力は徹夜で身につくものではなく、失敗を含めた幅の広い経験を積まないとなかなか一流にはなれないと感じている今日この頃。

とはいえ、数打てば当たるで猪突猛進に進むのではなく、ある程度の方向性と知識を持った上で進まないと、寄り道ばかりしてしまう。そんなことを考えていた時に、

という意味で、今の自分にぴったりな書籍が、この本だった。存在は知ってはいたものの、積読状態だったが、先月・今月あたりで読み終えた。

「単一責任のクラスを作る」とか、「インターフェースから考える」とか、「依存関係は可能な限り取り除く」とか、言うのは簡単だけどそれらを呼吸するように設計できているかと言うと、それはまた別の話。

「ダックタイピング」とか「Module を利用した振る舞いに着目した設計」とか、Ruby の言語特性を活かしたオブジェクト指向設計のプラクティカルな事例を見ることができたのは、非常に学びが多かった。

印象に残っている箇所

本書の所々に印象に残る格言が散りばめられていて、アーキテクトの目線でとらえたアプリケーションの設計を垣間見ることができる。

少し知識を得たころこそ危険なのです。知識が増え、その見返りを求めるようになり、執拗に設計するようになってしまいます。情熱のあまり、不適切な場所に原則を適用し、存在しないところにパターンを見出すのです。(p.25)

これにはどきっとさせられた。『オブジェクト指向入門 第2版 原則・コンセプト』を読んだ時もそうだったけど、デメテルの法則〜とか、SOLID〜とか、デザインパターン〜とか、"知識"を得た途端、身の回りのものがそれまでとは違った見方で見えてくる、というのが学びの一般的な醍醐味ではある。しかし、それを適用したいがために、現実を歪曲してまでその適用事例やパターンを見出しかねない。知識は現象を正しく分析するための手段であって、それ自体が目的であってはならないと、気づかされる一言だったり。

「今日何もしないことの、将来的なコストはどれだけだろう?」(p. 44)

実装していると、リファクタリングしたいところなんぞ山ほど出てくる。それが2008年がinitial commitのレポジトリとかだったりすると、過去のレガシーが至る所にあって手を出したいところだらけ。

しかし、本当に今自分が見ている箇所は今リファクタすべきなのだろうか、今コストをかけて再設計すべきなのかどうか、自分が見逃している挙動や例外パターンはないか、そう言ったことを冷静に判断しないと、ただただ目に見えたゴミを拾う計画性のない Junior Software Engineer にすぎないのだなと、気づかされる。

基本的な設計の質問を、「このクラスが必要なのは知っているけれど、これは何をすべきなんだろう」から、「このメッセージを送る必要があるけれど、だれが応答すべきなんだろう」へ変えることが、キャリア転向への第一歩です。オブジェクトが存在するからメッセージを送るのではありません。メッセージを送るためにオブジェクトは存在するのです。(p.97)

本書で一番目からウロコが落ちた箇所といえば、ここ。まさに自分はいつもクラスから考えると言うか、すでにクラスがそこらへんうようよしていてお互いのコミュニケーション方法を探っているようなイメージだった。だけど、そもそもそこにはどのようなコミュニケーションが発生すべきなのか、そのために呼ぶべきオブジェクトは誰なのか、という発想の転換はなかった。関係ない人をとりあえず良さそうだからとミーティングに呼んでみたけど議論が発散して終わるのと、ミーティングのゴールとアジェンダを決めた上で適切なロールを持った人物をミーティングに呼んで議論が収束して終わるのと、といった違いだろうか。

最後に

とまあ、色々今の自分にとって学びの多い一冊だった。もっと早くに読んでおけばよかったと思っている。

そのほかにも、Tipsレベルで、DIを利用した依存性の抽出方法だとか、ダックタイピングの振る舞いに着目したテスト手法だとかが学べる意味で、本自体がプラクティカルさ(具体性)と知識(抽象性)が絶妙なバランスで「設計」された良書だと言えるだろう。読んでよかった。

Ruby で Numbers->SVG のコンパイラを書いた

過去に書いたこれの Ruby 版。 itiskj.hatenablog.com

Ruby で書き換えた理由は、TDD の型の練習だったり、オブジェクト指向開発をライトなレベルで実践したかったり、とまあいくつかあるものの、特に深い理由ではない。気が向いたので書いてみた。

前回同様、TDD で開発し、オブジェクト指向を意識した API 設計にした。 実際書いてみて、すでに仕様は理解していたので、コーディング時間は2時間程度。

流石にこのレベルは簡単に思えるようになってきたので、今度はコンパイラの基本概念や王道デザインなどを学びつつ、minimum Ruby compiler on Ruby か、Ruby で何かの言語のコンパイラを書いてみようと思っている。

github.com

『すごいHaskellたのしく学ぼう!』を読んだ

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

実際読んだのはだいぶ前の話だが。。多分一年以上も前。ようやくメモに起こした。

なぜ読んだか

当時仕事でのキャッチアップのため ECMAScript を集中的に学んでいた。で、その延長で JavaScript 界隈で関数型言語の考え方を応用したフレームワークや開発事例、海外のエンジニアのブログ記事を目にすることが多かった。

例えば、Redux とかは関数型の Immutability をベースにしたデータフローで設計されているし、Elm とかも関数型ベースでかける言語ということで軽くかじった。Immutable.js とかもそう。

その時、自分は HaskellLisp を大昔に中二病で軽く触ったことがある程度(Lisp はまあまあ書いてた気はするけど)で、正直関数型言語で考えることができていたわけではなかった。そこで、一回 Haskell をがっつり書いてみようということで買った書籍のうち、最初に手をつけたやつ。

すごくとっつきやすくて、個人的には翻訳者のユーモアも好きだったので、一気に読んでしまった。

サンプルレポジトリ

この時もサンプルレポジトリに、気になるコードを実際に手を動かしながら書いてみた。

https://github.com/kenju/book_learn_you_a_haskell_for_great_good

新訳版『テスト駆動開発』を読んだ

テスト駆動開発

テスト駆動開発

旧訳版も読んだことがなかった。この本が非常に優れているのは、すごく細かいステップを経ながら、あたかも著者とペアプログラミングをしているかのように手取り足取り TDD のプロセスを疑似体験できること。言語は、なんだっていい。自分の好きな言語でかけば良い。大事なのは、行ったり来たりするプロセスで、何をどう考え TDD を実践しているか、ということ。困った時はどうするか、行き詰まった時はどうするか。現場は完全な TDD ではないのもの、テストを書いてよかったと思う時はよくあるし、逆になんでテスト書いていなかったんだろうと後悔することもある。そんな時に振り返る原点として、この本を今知れたことは大きい。

印象に残っている箇所

TDD は、設計のひらめきが正しい瞬間に訪れることを保証するものではない。しかし、自信を与えてくれるテストときちんと手入れされたコードは、ひらめきへの備えであり、いざ閃いた時に、それを具現化するための備えでもある。

テストを書くのは、自信をつけるため。逆に言うと、不安な箇所をなくすため。「なぜテストを書くのか」について、あるプロジェクトで手戻りが発生してしまった時の振り返りで感じたことなのだが、その矢先にこの本を読んで出会った言葉。その経験があったが故に、余計身にしみて感じた。

テストを書くと、そしてそのテストがあるべき姿になっているならば、本当に自信がつく。びくびく怯えることなく、リファクタリングできる。自分のためにとっても、今後その箇所を触る将来のチームメンバーにとっても、不安をコードベースからなくすと言う観点は、TDD の本質かも知れない。

慎重な登山者の間では、両手両足の合わせて陸地に接していなければならないと言うルールがあるそうだ。大胆にも2つ同時に動かすと、危険が大幅にますというわけだ。グリーンバーから行える変更は、1つだけというテスト駆動開発の基本形は、この登山者のルールに似ていると言えるかも知れない。

この具体例は非常にしっくりくる。テストがこけているとついつい焦ってしまいがちだが、少し落ち着いてグリーンバーからなぜ離れてしまっているのか、急ぎすぎていないか、振り返る余裕がこれを読んで少しできた、かもしれない。

TODOリストから次に書くテストを選ぶ時には、どうすればよいだろうかーーそこから何かを学ぶことができ、かつ実装に自信が持てるようなテストを選ぼう。

これも1つ目の「自信を持つ」に繋がる話。

独りでプログラミングしている時、コーディング時間のうまい終わらせ方はあるだろうかーー最後に書いていたテストを失敗する状態にして終えよう。

自分はいつも「きりがいいところまで」といってコーディングしてしまいがちなので、この観点は真新しかった。キリがいいところまでかけば気分がいいが、ついつい時間を非効率に過ごしてしまいがちだし、時間をおいてプロジェクトを戻ってきた時、「さて、どこから始めようか」と思うことは、振り返ってみるとたしかに多い。

TDD においてテストは目的を達成するための手段であり、その目的とは、大いなる自信を伴うコードだ。

これも「自信を持つ」に繋がる話。

サンプルレポジトリ

いつもの例に漏れず、今回も手を動かしながら作って見た。 言語は、RubyPython で。他の言語で写経する方法、t_wada さんも本書のどこかで触れていた気がする。

https://github.com/kenju/tdd_ruby

補足

http://t-wada.hatenablog.jp/entry/tddbook

t_wada さんの訳者解説にあたっての工夫や背景、読むだけで熱くなる。ぜひ本書を読んだ方で上のブログ記事を読んだことがない方は、是非読んでほしい。

補足2

そして、t_wada さんの書かれた訳者解説が、また熱い。

その中でも、今回読んだ中で一番印象に残った一言は、これだった:

テストを書いても設計を改善しないのであれば、それはただの回帰テストであり、現状の追認でしかありません。テスト駆動開発における質の向上の手段は、リファクタリングによる継続的でインクリメンタルな設計であり、「単なるテストファースト」と「テスト駆動開発」の違いはそこにあります。

仮に第1版を読んだことがあったとしても、この解説を読むためだけに第2版は買ってもよかった、と思っただろう。

スライド『Black Belt Online Seminar Amazon CloudWatch』メモ

資料:https://www.slideshare.net/AmazonWebServicesJapan/black-belt-online-seminar-amazon-cloudwatch

CloudWatch

  • システム監視(死活監視、性能監視、キャパシティ監視)
  • Metrics, Namespace, Dimension が基本的概念
  • 標準メトリックスの他、独自メトリックスも監視可能
  • メトリックスデータの補完は2週間まで
    • それ以上保存する場合は get-metric-statistics でデータを取得し別の場所に補完
  • データ保管粒度は最短1分感覚
  • CWはデータポイントを基準にステータスを判断
    • データ量が足りない時は INSUFFICIENT ステータスとなる。これは障害を表すステータスではなく、データポイントが不足している状態を指す
  • 初期費用なしの従量課金

CloudWatch Logs

  • ログ管理プラットフォームサービス
  • Log Group, Log Stream, Log Event が基本的概念
  • ログ内容はタイムスタンプとログメッセージ(UTF-8)で構成
  • CW Logs Metric Filter を使うと、特定文字列のエントリ頻度によりアラーム作成が可能
  • Metric Filter をトリガーにしたSNS連携も可能

CloudWatch Events

  • AWS上のリソースの状態監視サービス
  • AWSリソースに対するイベントをトリガーにアクション実行

『メタプログラミングRuby 第2版』を読んだ

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

メタプログラミング周りを中心に、Ruby のオブジェクトモデル関連の API についてより詳しくなりたかったので、一周した。

感想

一通り読んだ感想としては、

  • メタプログラミングは別に黒魔術でもなんでもなく、動的言語の特性をふんだんに生かした最適化のためのプログラミング手法だと気付いた
  • Ruby 2.x に対応しているので、知ってはいたけど使ったことのない Refinements についても触れられていたのが良かった
  • メタプログラミングAPI の紹介にとどまらず、それらが実際に利用されているライブラリのコードを掲出しながら Practical な事例を紹介している点が非常に良かった
  • これ見たらDSL描きたくなるよね
  • Rails の歴史を知ることができたのも意外なポイント。Rails 2 時代に流行ったメソッドがなぜ消えて言ったのか、ActiveSupport::Concerns がどのような課題意識のもと追加されたのか、ActiveRecord と ActiveModel に別れているのはなぜか、といった観点は昔から Rails に携わって来たわけではない自分にとって新鮮な視点だった

というあたり。個人的には、

  • Blank Slate
  • Clean Room
  • Null Object = Black Hole

といった概念も新たに知ることができた。

サンプルコード

本を読む時、一通り読んだだけだといつもわかった気になって終わってしまうので、いつもなるべくサンプルコードを手で動かしながらやるようにしている。

Repository: https://github.com/kenju/metaprogramming_ruby_2_book

参考:

AWS Lambda の Versioning と Alias 機能についてメモ

資料:AWS Black Belt Techシリーズ AWS Lambda Updates https://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-tech-aws-lambda-updates

tl;dr

  • Lambda は明示的にバージョン管理ができる
  • エイリアス機能を使うことで、「本番用」かどうかなどの宣言的バージョン管理ができる

Versioning

  • ある時点の Lambda Function をバージョンとして管理可能
  • 新しいバージョンはいつでも発行可能
  • PublishVersion を実行することで明示的に発行もできる
  • バージョンを発行するまでは $LATEST が唯一のバージョンとなる
  • 一度発行すると構成も含めて変更不可能

Alias

  • 特定バージョンに対するポインタ
  • エイリアスを作成することで、バージョン番号を把握していなくても指定バージョンを呼び出せる

Functionの指定方法

バージョン発行前、最新バージョンを指定する場合

  • FunctionName
  • FunctionName:$LATEST

    特定バージョン指定

  • FunctionName:1
  • FunctionName:2

    エイリアス指定

  • FunctionName:production
  • FunctionName:v1_2_3

スライド『AWS Black Belt Online Seminar 2017 Amazon Kinesis』メモ

資料:https://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-online-seminar-2017-amazon-kinesis

Amazon Kinesis Streams

  • ストリームデータを処理するためのアプリケーション開発
  • 3AZの永続ストレージに強い整合性でデータを複製
  • 順序付きイベントストリーム
  • ストリームは1つ以上のシャードで構成
  • 保存されるデータの単位をデータレコードと呼び、保存時間はデフォルト 24 時間、最長 7 日間
  • 1 データレコードの最大サイズは 1MB
  • ストリーム内のシャード数をコントロールすることでスループットをコントロール
    • シャードごとの送信キャパシティは 1MB/1sec or 1000PUT, 受信キャパシティは 2MB/1sec or 5 read transaction
  • データ入力時に指定する partition key で保存先シャードにハッシュ分散
  • 全てのデータレコードにはシーケンス番号がアサイ
    • ストリーム内のシャードごとにユニーク

KPL = Kinesis Producer Library

  • Kinesis Streams にデータを送信するOSSライブラリ
  • C++ で書かれている
  • メインプロセスの子プロセスとして実行される
  • Aggregation でデータを1データレコードに集約して、Collection で複数のデータレコードをバッファリングしつつ送信

KCL = Kinesis Client Library

  • Kinesis Streams からのデータを受信するクライアント側のライブラリ

Amazon Kinesis Firehose

  • ストリームデータをS3, Redshift, ESへ簡単に配信
  • serverless ETL
    • Lambda を利用したストリームデータの変形が可能
  • S3, Redshift, ES などの配信先に応じて「配信ストリーム」を作成
  • シャードの作成や partition key の指定は不要

Amazon Kinesis Analytics

  • ストリームデータを標準的な SQL クエリでリアリタイムに分析
  • 分析単位にアプリケーションを作成
    • ストリーミングソースとストリーミングディスとネーションが、それぞれアプリケーション内部の入力・出力ストリームにマッピングされる
  • SQL クエリ実行の前処理として Lambda が使える
  • クエリの複雑さとデータのスループットに応じて処理能力(KPU = Kinesis Processing Units)を自動伸縮

スライド『AWS Black Belt Online Seminar 2017 Amazon DynamoDB』メモ

資料:https://www.slideshare.net/AmazonWebServicesJapan/20170809-black-belt-dynamodb

自分用メモ。

学び:

  • capacity を安易に減らすことはリスクであることがわかった
  • Auto Scaling, LSI/GSI は知っていはいたけどまだガッツリ使っていない
  • Atomic Counter, TTL, DAXあたり知らなかった。これ使えそう

DynamoDBとは

  • データの保存先は3箇所のAZに保存されている
  • ストレージのパーティショニングも自動的に行われる
  • Provision Throughput
    • スケールダウンに関しては9回/日しか変更できないので注意
  • 整合性モデル
    • Write = 少なくとも2つのAZでの書き込み完了を確認した時点
    • Read = デフォルトでは結果整合性。Consist Readオプションをtケルト、より強力な整合性モデルとなるが、capacity unitを2倍消費する
  • Provision Throughput or Strage Usage による料金体系
  • Capacity Unit
    • Write: 1 unit = 1KBのデータを1秒に1回
    • Read: 1unit = 4KBのデータを1秒に1回

テーブル設計とサンプル

  • partition key = KV型のアクセスパターン、データ分散に利用される
  • sort key = オプション、1:Nモデルの関連
  • attributes = name/value型、JSON型など多数。items間で不揃いであっても問題ないスキーマレス
  • Primary Keyの持ち方は、partition key or partition key & sort key の2種類
  • Partition-Sort Table (partition key & sort key が primary keyの場合)を使うことで、同一の partition key でのデータの並びを sort key で保証することができる
  • JSON format のドキュメントを DynamoDB の 1 item として保存できる

Partitioning

LSI/GSI

  • LSI = Local Secondary Index
    • sort key 以外に絞り込み検索を行うkeyを持つことができる
  • GSI = Global Seconday Index
    • partition key をまたいで検索を行うためのインデックス
    • 非同期にテーブルからGSIをアップデートする
    • テーブルとは独立したスループットをプロビジョンして利用するため、十分なスループットが必要
  • LSI/GSI は便利だが、スループットやストレージ容量を追加で必要とする
  • また、インデックスの数が増えれば増えるほど書き込みコストがあがる
  • Secondary Indexに強く依存するようであれば、RDBも検討する

高度なテーブル操作

  • Atomic Counter
    • UpdateItem における Add をNubmer型に行うことで、Atomicなカウンターを実現できる

TTL

  • Time to Live (TTL) では、テーブル項目の有効期限が切れ、データベースから自動的に削除できるタイミングを定義できる
  • 利点:Provisioningされたスループットを使用すること無く、関連性のないデータのストレージ使用量と保存コストを減らせる
  • 追加料金無し
  • 既存のテーブルにも設定可能
  • DynamoDB Streams との併用可能
  • TTL は UnixTime で設定
  • すぐに削除されるわけではなく、最大48時間削除までかかる
  • その為、期限切れのものを利用しないようにするには、Query かアプリ側でのフィルタリングが必要

Auto Scaling

  • フルマネージドで WCU, RCU, GSI に対する設定を管理
  • 設定はターゲット使用率、上限、加減を設定するだけ
  • 利用無料
  • マネージドコンソールから可視化された状態で管理できる
  • 即座に容量が拡大するわけではなく、EC2 で Auto Scaling をやるのと同様、時間がかかる
  • 瞬間的なスパイクに対応するにはDAXと合わせてアーキテクチャを組むことが推奨される
  • 下げる回数は現在最大一日9回まで

DAX = DynamoDB Accelerator

  • DynamoDB と各アプリケーションの間にプロキシして、スケールアウトやキャッシュ情報のレプリケーション、障害時のフェイルオーバーなどをフルマネージドで実現
  • Item Cache/Query Cache 2種類のキャッシュをもつ

DynamoDB Streams

  • 24時間以内にそのテーブルのデータに対して行われた変更のストリーム全てにアクセス可能で、24時間経過したストリームデータはその後削除される
  • 容量は自動的に管理される
  • 操作が行われた順番に沿ってデータがシリアライズされる
  • DynamoDB への変更は1秒未満で反映される
  • ViewTypes
  • Streams API
    • ListStreams, DescribeStream, GetShardIterator, GetRecords
  • KCL(Kinesis Client Library)
    • Kinesis API を使い慣れている開発者には、DynamoDB Streams をかんたんに利用できる
  • DynamoDB Triggers = DynamoDB + AWS Lambda
  • そのほか Cross-region Replication にも有利

Cookpad TechConf 2018 で LT『広告配信サーバーと広告配信比率最適化問題』で話してきました

https://techconf.cookpad.com/2018/