FacebookのエンジニアEvan Priestley氏が「どうやってプログラミングを学んだか」

Evan Priestley 氏がどうやってプログラミングを学んだかを教えてください | Knoh (ノウ)

共感する部分、参考になる部分が沢山あったので、
いくつかまとめて抜粋。*1

一番ためになる教訓は、一番苦労して得るものだ

  • マスターするには気の遠くなるくらいの時間を費やさなければならない

ソフトウェアの開発に最初から最後まで関わるという経験はとても貴重だったんじゃないだろうか。 なぜなら、プロジェクト開始時のダメなデザインのしっぺ返しを、後で自分でモロに受けるからだ。

一番重要で一番やっかいなスキルはシステムを設計するための判断力

  • どうやってシステムをデザインするかは、その後に多大な影響を与える

ぼくのプログラマーとしての成長に一番寄与したのは、「判断力をつける」ということじゃないだろうか

この「判断力」は、プログラマーにとって非常に重要なのだが、そう簡単に教えられるものでもない。 ぼくが知る限り、判断力をつける一番の方法は、自分で設計したシステムを長い間メンテすることだと思う。

「質 v.s. スピード」ではなく「質 = スピード」

  • 「質v.s.スピード」という概念は根本的に間違っている
  • 素早く開発をしなくては環境、あるいは自分の環境の理解の変化にソフトウェアがついてこれず、ソフトウェアが解決すべき問題が解決できなくなり、必然的に質が落ちてしまう
  • 質の高いソフトウェアを書かなくては、なにかある度にインフラが崩壊し、素早く開発をすることができなくなってしまう

片方を犠牲にした場合、知らぬうちにもう一つも犠牲にしているということをお忘れなく。

広く浅く覚えよう/広く浅く全体を見よう

  • 複雑なシステムを理解するには、全体のしっかりとしたモデルを頭の中に持つことがカギとなる。おおまかな全体図をまず把握して、そこから部分的につめていくことが大事だ
  • 浅く広くシステム全体を理解した方が、知識がお互いに補完しあって、大域的な理解や、新しい分野へのとっかかりにつながる

閑話休題

フェイスブックでは、ぼくは「一番 PHP を嫌っていないエンジニア」と自称していた。 ぼくのことを「PHP バカ」と呼ぶ同僚もいた。

どのプログラミング言語を使うかってのは、多くの人が考えるほど重要ではないと思う。 ぼくの経験上、一番PHPをバカにし、言語の重要性をうそぶく連中は、大体自分たちが提唱する言語でもロクな仕事ができないことが多い。

*1:タイトルと記事内容が逸れている気がしますが...

「ライト、ついていますか」を読んだ

ライト、ついてますか―問題発見の人間学

ライト、ついてますか―問題発見の人間学

この本には「問題発見の人間学」という副題が付いている通り、
問題解決ではなく、問題発見の心得に着目した書籍。

TL;DL

これは誰の問題なのか?問題はどこから来たかを自問し、
何が問題なのかを見極める事が重要。

心得

以下、本の中のキーワードをメモ

  • 問題とは「望まれた事柄と認識された事柄の相違」である
    • 問題は見方(欲求・認識)を変えることで解決する可能性がある
  • 解決(方法)を問題の定義と取り間違えるな
    • 人が問題だと言っていることが本当の問題とは限らない
  • 誰の問題か?
    • 本当にあなたが解決しなければいけないのか
  • 問題はどこから来たのか?
    • なぜこの問題が起きたのか
  • 結論に飛びついてはいけないが、自分の第一印象は無視するな
  • 全ての解答は次の問題の出所
    • 新しい視点は必ず新しい不適合を作り出す
    • 最も重要なことは問題は解決されることがないと知ること。しかし、考えることを止めなければは大したことではない
  • もし人々の頭の中のライトが付いているなら、ちょっと思い出させてやる方がごちゃごちゃ言うより有効

問題は解くより発見する方が難しいし、面白い。

See also

宮古島

これぞ秘境の離島!「宮古島」に行くべき7つの理由 | RETRIP

時間があったので、行ってきた。

airbnbで宿を予約、LCCで格安航空券をと、リーズナブルに行けたし、 思ったより物理的な距離も精神的な距離も近かった。

1月はほぼほぼ雨かくもりぽいけど、気温は東京と比べて10度以上暖かいので最高。

「Team Geek」を読んだ

Team Geek ―Googleのギークたちはいかにしてチームを作るのか

Team Geek ―Googleのギークたちはいかにしてチームを作るのか

「良いチームを作るための指南書」、「他人とうまくやるためのHack」
geekに限らず、ソフトウェア開発に限らず参考になると思う。

TL;DL

  • 良い・成功するソフトウェアは 「HRT = 謙虚 + 尊敬 + 信頼」の文化が根付いている
  • ソフトウェアは人で成り立っている
    • チームが目的を達成するためにHRTは必要不可欠なもの
    • あらゆる人間関係の衝突はHRTの欠如によるもの
  • ソフトウェアを書く理由は、ユーザ生活を豊かにするため

HRT

  • Humility
    • 世界の中心は君ではない。君は全知全能ではないし、絶対に正しいわけでもない。常に自分を改善していこう。
  • Respect
    • 一緒に働く人のことを心から思いやろう。相手を1人の人間として扱い、その能力や功績や高く評価しよう。
  • Trust
    • 自分以外の人は有能であり、正しいことをすると信じよう。そうすれば、仕事を任せることができる。

See also

2015年

2015年にあったことのまとめ

iOS

前半は業務でがっつりiOSを触っていた。
無事リリースも出来、その後、収益に直結する広告基盤の実装を終えた時には、
少し自信がついた。

また、個人的でもiOSSwiftばかり触っており、

Scala移植の

とか作って、ちょくちょくGitHubに草を生やしていた。

Android

Androidデビューを果たし、戸惑いながらもなんとか頑張っている。
Apple信者なので、Android端末自体そもそも触ったことがほとんどなかったが、
UI/UXともに何かと新鮮で楽しみながら開発出来ている。

この1年は"iOSを頑張る"と目標としていたが、
同時にAndroidにも触れることが出来たのは、貴重な経験で良かった。

output

前述のGitHubでのライブラリ作成など、前年までと比べて比較的コードを書いていた。
ただ、作って。はい終わりが多く、もっとコードベースの大きな、保守しなければならない何かを来年以降は作っていきたい。
(OSSへのPRも今年初めて行った。)

また、初Advent Calendarに参加した。
(去年Swift Advent Calendarに参加しようとしたら、一瞬で枠が埋まってしたまったので今年は参加できてよかった)

全部ギリギリに書き上げ、日付オーバーしたのもありますが‥

あ、あと、読書系の備忘録になっていますが、このはてなブログも今年始めた。

転職

会社は働きやすく、休日に旅行するぐらい仲が良く楽しかったが、この環境に甘えているな、
今とはまた違う気持ちで働いてみたいと考えて転職することにした。

12月末日が最終出社、1月は長めのお正月休みを頂き、2月から社会復帰します。
新しい環境に関しては、2月以降にブログにまとめようかなと思います。

引っ越し

杉並区 > 豊島区に引っ越した。
池袋は学生時代からよく行っているし、新鮮味はないのだが、歩いて帰れるのはとても良い!!

まとめ

今年は新しいことにチャレンジする事が出来、その中でoutputは出来た。
後半は引っ越し関連でバタバタして、積み本になってしまった本が大量にあるので来年は消化していきたい。

来年

"大量のinputを怠ることなく、前年より質の高いoutputを行う。> 勉強会で発表する"
"iOSを頑張る"
"Storeにアプリを出す"

「Application Architecture for .NET」を読んだ - 3部(後編)

.NETのエンタープライズアプリケーションアーキテクチャ 第2版 (マイクロソフト公式解説書)

.NETのエンタープライズアプリケーションアーキテクチャ 第2版 (マイクロソフト公式解説書)

「Application Architecture for .NET」を読んだ - 1部
「Application Architecture for .NET」を読んだ - 2部
「Application Architecture for .NET」を読んだ - 3部(前編)

の続き

第三部サポートアーキテクチャ(10, 11, 12, 13章)のメモ。

TL;DL

  • CQRSはトップレベルのアーキテクチャではなく、テクノロジに依存しないアーキテクチャ
    • ある程度設計パターンに依存するが、CQRSはパターンそのもの。CQRSはシンプルかつ強力で、一般的なアプリケーションに適している
  • CQRSはドメイン層を切り離すことと、読み取り/書き込みに別々のモデルを使用することを提唱する
    • 最大のポイントは、コマンドをクエリから引き離す
    • 読み取り用(1つの中間層がデータを取得する)/書き込み用(1つの中間層がシステム状態を変更するコマンドを処理する)にストレージを用意することになる
  • Eventは分析と実装に対してタスクベースのアプローチを推進する
    • EventSourcingは標準的なOOPモデルやRDBモデルに従ってデータを格納するのではなく、イベントが発生した時にそれらを記憶するだけ
  • CQRSの真価は一方を最適化することでもう一方が機能しなくなるというリスクを負うことなく、コマンドとクエリのパイプラインを自由に最適化できる点にある
  • Cutting Edge - 一般的なアプリケーション向けの CQRS
  • Cutting Edge - CQRS とイベント: 強力なコンビ

10. CQRSの紹介

  • DDDの当初のビジョンに不可欠なのは、境界付けられたコンテキストに対して推奨されるアーキテクチャです
コマンドとクエリの分離
  • 一般的に言えばソフトウェアシステムで実行されるアクションはクエリかコマンドに分類できる
  • クエリ: システムの状態をいかなる方法でも変更せず、データを返すだけ(読み取り)
  • コマンド: システムの状態を変更する。ステータスコードか確認応答を返すとしてもそれ以外のデータは返さない(書き込み)
  • CQRSではDomain層を1つではなく2つ使用する
    • それぞれのDomain層に独自のアーキテクチャとクエリ/コマンドに特化した一連のサービスを割り当てる
    • エリスタックのベースはSQLクエリだけで極限まで単純化されるべき
  • CQRSの利点
    • 設計の単純化
    • スケーラビリティ向上の可能性
エリスタック
コマンドスタック
  • コマンドスタックの関心はアプリケーションの状態を変更するタスクのパフォーマンスだけ。アプリケーション層は従来通り、プレゼンテーションからリクエストを受け取りそれらを実行する手はずを整える
  • コマンドとはバックエンドに対して実行されるアクションのこと(単一方向)
  • ex. タスクを実行したユーザがフィードバックを期待して待つケース
    • コマンドとクエリが同じコンテキストに属するのであれば問題はない
    • 別々なら、1つは対話形式で利用され、もう1つはプログラムから開始される
  • 入力を処理するためのフロントエンドリクエストはアプリケーション層(受信者)に送信されるメッセージと見なせる
    • メッセージはこの後の処理に必要なデータを含んだDTO
public class Message {
    // 必要に応じて一般的なプロパティを定義
    // 但し、クラスはプロパティを持たない単なるマーカーでも構わない
}
  • メッセージはコマンドとイベントの2種類がある
    • コマンドは命令型で明示的なリクエスト
      • 1つのハンドラに送信される
      • システムによって拒否されることがある
      • 効果はシステムの状態に応じて異なることがある
      • コマンドが境界付けられたコンテキストの境界を超えることは無い
    • イベントは既に発生していることの通知としての役割を果たすメッセージ
      • イベントを処理するハンドラはいくつ指定しても構わない
      • イベントのサブスライバがそのイベントが発生した境界付けられたコンテキスト内にいるとは限らない
      • 既に発生している何かを表す単純な理由によりImmutableとみなすべき
  • イベントソーシングは、メッセージを保存することで詳細で包括的な監視ログを形成できること
    • システム内で発生したイベントは後から確認出来る、イベントを再現、推定することが可能
  • コマンドを非同期で実行する必要がある場合は、コマンドではなくイベントとして設計すべき
  • コマンドとイベントはどちらもMessageの派生クラス
  • イベントに関しては何かが発生したことを反映した名前にすること
    • ex. Is-Gold => CustomerReachedGoldStatusEvent
// Command
public class CheckoutCommand: Message {
    public string CartId { get; private set; }
}

// Event
public class DomainEvent: Message {
    // 一般的なプロパティ
    // イベントが発生した時間を追跡するtimestamp
    // イベントを発生させたuserId
    // イベントをハンドラで処理できるかのversion
}
public class OrderCreatedEvent: DomainEvent {
    // イベントの名前はより詳細で明確に
}
  • コマンドはコマンドバスと呼ばれるプロセッサにより管理
  • イベントはイベントバスと呼ばれるコンポーネントによって管理
  • コマンドバスはメッセージ(コマンドを実行するリクエストとイベントの通知)を受け取り、それらを処理する方法を見つけ出す単一のclass
    • コマンドバスは実際に処理を行うのではなく、処理できるハンドラを選択する
    • コマンドと関連するイベントを処理するプロセスはSagaと呼ばれる
  • Sagaコンポーネントは、論理的に関連するメソッドイベントハンドラを集めたようなもの
  • ドメイン層はコマンドスタックに配置され、はるかに単純なデータアクセス層はクエリスタックに配置される
  • 現実のシステムの多くは読み取りと書き込みに単一のDBを使用する
    • コマンドモデルとクエリモデルを別々に定義したとしても同じDBを共有することは可能(どうのようなシナリオに取り込むかによる)

イベントバス

  • bus: データをやり取りするための伝送路
  • コールバックメソッドを呼ぶタイミングでイベントを発火し、コールバックインタフェースの実装ではなく、イベントを常時監視するメソッドを用意しておいて、発火したタイミングで呼び出されるようにする仕組みとして、EventBus を使うことで、インタフェースによる依存関係を整理したり、コールバック地獄を解消したりすることができる
  • EventBus という中間層を介したコミュニケーションを用いることで、ModelとControllerは直接結合することがなくなることで、Controllerが実装したコールバックインタフェースの管理をしなくても良くなる

11. CQRSの実装

エリスタックの実装
  • エリスタックのドメイン層がほとんど空でDBに対して実行される単純なSQLクエリとデータにをプレゼンテーション層へ届けるためのDTOだけで構成される
  • エリスタックは出来るだけ単純に、上位レイヤーにはDTO(データ転送オブジェクト)を使って返す(それらはViewModel)
  • 読み取りファサードは主にデータモデルとDBコンテキストによって構成される
  • 高階関数で加工すれば、大量のDTOを作成せずに済む
コマンドスタックの実装
  • コマンドとイベントの観点からワークフローを定義することでアプリケーションのユースケースを実装する
  • ユーザからの入力などはコマンドバスに渡されるコマンドになる。さらに処理するには、コマンドバスがコマンドを登録済みのハンドラに渡す。コマンドの処理によりさらに別のコマンドとドメインイベントによって前進するプロセスが開始される
    • コマンドをワークフローのコンテキストで処理するプロセスをSagaと呼ぶ
      • Sagaはコマンドバスと連携することで、ユースケースを実装するために実行しなければならないタスクの手はずを整える

12. イベントソーシングの紹介

  • DDDはデータモデルの作成よりもドメインモデルの作成を推奨する
    • リレーショナルモデルではエンティティとそれらの関係に焦点を当てる
    • ドメインモデルはドメインで観察可能な振る舞いに焦点を当てる
  • CQRSによりコマンドをクエリから離した瞬間にタスクという観点から考えるようになる
    • タスクはドメインイベントとアプリケーションイベントに深く結び付く
  • イベントソーシングはイベントをツールとして使う
    • ドメインモデルではなくイベントを永続化する
    • イベント・ソーシングを知る

      イベントを中心に考える - 様々な出来事(イベント)の結果、状態が変更する - 状態はイベントの結果にすぎず、本質ではないのでないかという考え方

    • イベントを記憶する(状態は記憶しない)

      • 何が起きたかだけを淡々と記憶する
    • イベントソーシングの凄い所
      • 完全な履歴が手に入る
      • 任意の時点を再現出来る
    • 問題点
      • パフォーマンス(最新の状態を得るため、毎回膨大な量のイベントをリプレイスする必要がある)
      • バージョニング(仕様変更が起きたら過去のイベントをどう処理するか)
      • 実装が複雑(イベントを記憶してリプレイスする機構)
イベントの躍進
  • イベントソーシングに言わせれば、イベントが発生すればそのイベントに関連付けられているデータの集まりが保存さる
  • イベントの永続化に対処するには、イベントを記憶するためのイベントストア要素が必要になる
イベントソーシングアーキテクチャ
  • イベントの再生
    • ビジネスエンティティの状態を再現する
    • 多くのシナリオでは、イベントの再生は非同期処理の一部
    • 多くの場合は、数10個程度のイベントが対象となる為それほど時間はかからない、それが問題となるならデータスナップショットを使用する
      • スナップショットはシリアライズされた状態の集約を含む、永続キャッシュ

13. イベントソーシングの実装

  • 「最後に確認された正常な状態」ではなく、「発生した出来事」を追跡する方がビジネスとして意味がある場合には、イベントソーシングは妥当なパターンとなる
  • イベントソーシングの実装 +α
    • 追跡を目的としてイベントを記憶する + 対象となる集約の「最後に確認された状態」も保存する
イベントソーシングを使用する理由と状況
  • イベントソーシングによりシステムの周りで発生するあらゆる事を追跡出来るようになり、アプリケーションロジックが処理するコンテキスト/コンテンツをアーキテクトが推測できるだけの柔軟性が確保出来る
  • イベントソーシングに適したシナリオ

    • データ処理の意図や目的を記録することが要求されるケース
    • 記録されているイベントに基づいて何らかのタスクを実行するケース(状態を復元したい、ロールバック)
    • イベントソーシングを使用するかどうかを判断する決め手は、ビジネスドメインにおいてイベントを追跡することが妥当であるかどうか
  • Command Bus

public class Bus
{
    private static readonly Dictionary<Type, Type> SagaStarters = new Dictionary<Type, Type>();
    private static readonly Dictionary<String, Object> SagaInstances = new Dictionary<String, Object>();
    private static readonly EventRepository EventStore = new EventRepository();

    public static void RegisterSaga<TStartMessage, TSaga>()
    {
        SagaStarters.Add(typeof(TStartMessage), typeof(TSaga));
    }

    public static void Send<T>(T message) where T : Message
    {
        // Implement
    }
}
  • Saga: Eventを処理するプロセス
public class MatchSaga : SagaBase<MatchData>,
                         IStartWithMessage<MatchStartedEvent>,
                         ICanHandleMessage<MatchEndEvent>,
                         ICanHandleMessage<PeriodStartedEvent>
{
    private readonly EventRepository _repo = new EventRepository();

    public void Handle(MatchStartedEvent, message)
    {
        // SagaIDを設定
        SageId = message.MatchId;

        // 試合情報が更新されている事を通知
        NotifyMathcInfoChanged(message.MatchId);
    }

    public void Handle(MatchEndEvent, message)
    {
        // 試合情報が更新されている事を通知
        NotifyMathcInfoChanged(message.MatchId);
    }
}
// このmessageがbusにpushされるとsagaが起動する
var domainEvent = new MatchStartedEvent(matchId);
Bus.Send(domainEvent);
  • イベントの永続化 EventRepository
    • 通常、イベントはDTO(プロパティ)の集まりであるので、DBのレコードとして簡単に保存可能
  • イベントの再生
    • 記録されたイベントから集約の状態を再現、一連のイベントをループで処理する
public IEnumerable<EventMapper> GetEventStream(String id)
{
    return DocumentSession.Query<EventWrapper>()
                          .Where(t => t.MatchId == id)
                          .OrderBy(t => t.Timestamp).toList();
}
集約snapshotによるイベントソーシング
  • イベントソーシングは自然な選択でないといけない。イベントを追跡する事が重要でその方が簡単であるなら、イベントソーシングを利用しそこから状態を再現するか、集約をスナップショットとして永続化するか

see also