Yuichi Murata's Engineering Blog

Go / App Engine / GCP / Team Build とか

WebCam を表示するだけの OS X アプリを作った

はじめて OS X アプリ開発 & Swift をやってみました。

https://raw.githubusercontent.com/yuichi1004/cam/master/screenshot.png github.com

きっかけは Google Meet の機能不足。スクリーンシェアをしているときこちらのカメラだけ表示が消えてしまうのである。なんとも不公平感があるし、こちらの表情を始めとするメッセージが伝えられなくなるのが不満であった。このアプリを使うと単にフローティングした WebCam が表示される。以上。

OS X (Cocoa) を使った開発はいままでやったことは無かったし、Swift も初めてであった。けれども特段の学習もなく、サンプルコードを参考にここまで実装するのにおよそ半日ほどでできた。 ひとえに Swift に感謝である。Objective-C だったらここまで到達する前に挫折していたと思う。

Swift は今どきの言語を触っていればなんとなく勘で触れるように思った。文法も全く勉強せずに取り掛かったが、クラスの定義、オーバーライド、Getter/Setter などは理解できた。

Movable Panel

フルスクリーンアプリ上でも動作するウィジェット風のウインドウは以下の処理で作成している。

  • canJoinAllSpaces, .fullScreen を指定することでフルスクリーン上でもウインドウが動作する
  • isFloatingPanel を使って常に全面にウインドウが来るようにする
  • isMovableByWIndowBackground を使ってドラッグアンドドロップでウインドウが動かせるようにする
class MoveablePanel: NSPanel {
    override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) {
        super.init(contentRect: contentRect, styleMask: [.nonactivatingPanel, .resizable], backing: .buffered, defer: true)
        
        self.aspectRatio = NSMakeSize(240.0,135.0)
        self.level = NSWindow.Level.mainMenu
        self.isMovableByWindowBackground = true
        self.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
        self.isFloatingPanel = true
        self.orderFrontRegardless()
    }
}

ウェブカメラの表示

ウェブカメラの表示に関してはよくまとまったサンプルがあったので大いに参考にさせてもらった。Thanks fbukevin for cool example for web cam view. 以下のサンプルと同じく AVCaptureVideoPreviewLayer を用いてウインドウ上にカメラ映像を重ねている。

github.com

カメラ映像が左右逆転してしまうのだけ、どう直していいのか分からなかった。識者がいたら教えてほしい。

ステータスバーアイコンの表示

OS X メニューバー右のアイコンの登録も NSStatusBar を使って簡単にできた。 以下ソースコード。予めイメージバンドル StatusBarButtonImage を登録しておく。 あとはメニューを追加してあげれば、クリックしたときに任意のメニューが表示できる。

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        if let button = statusItem.button {
            button.image = NSImage(named: NSImage.Name("StatusBarButtonImage"))
        }
        let menu = NSMenu()
        menu.addItem(NSMenuItem(title: "Quit Cam", action: #selector(NSApplication.terminate(_:)), keyEquivalent:"Q"))
        statusItem.menu = menu
    }
}

まとめ

いままで Objective-C の食わず嫌いで距離をおいていたけど、Swift なら趣味の範囲で OS X/iOS 開発に取り掛かってみるのもありかなと思った。

成果最大化の科学

Engineering Meetup という面白そうな会があったので参加してきました。発表の中であった、「マネジメントは手段。チームの成果の最大化が目的」という treby さんのフレーズが自分の中で響いたのでその話をします。

engineering-manager-meetup.connpass.com

エンジニアのキャリアパスとマネジメント

自分がチームビルディングやエンジニアリングマネジメントに興味を持ったのはここ半年〜1年のことでした。きっかけは、自分のリードしているプロジェクトの成果をいかに最大化するかという課題でした。 自分は人間の機微に気を配ったり、人と話したりするのが得意なタイプではありません。それゆえに、若いときからマネジャーを見据えたキャリアパスというのはあまりイメージに持っていませんでした。

成果の最大化

一貫しているのは、子供の頃からいち早くプログラミングを続けてきた事による技術力に強みを置く働き方です。テックリードとして若手を引っ張りプロダクト開発を推進する。一定の成果は上げられたものの、成果の最大化にはスタンドプレイヤーとしての振る舞いに頼っていたように思います。つまり、ゴールにミートしない場合、「自分が頑張ればなんとかなる」と自分でカバーする仕事の範囲を増やしたり残業を増やしたりしてなんとかしてしまうというものです。

この働き方は、技術者として楽しくはありました。より高い成果を短時間で上げることができましたし、プロダクト開発のスピードも上がりました。多少業務がきつかろうと、自身にとってはどうでも良いことでした。なぜなら、自身で課した高いゴールを達成するのが楽しかったからです。チームメンバーに恵まれた幸運もありました。メンバーの殆どが、触発されて一緒に走ってくれる若手だったためです。もし、よりワークライフバランスをとって働きたい中堅がメンバーにいたとしたら、きっと噛み合わなかったことだろうと思っています。

この働き方に限界を感じたのは、プロダクト開発が軌道に乗りさらなる成果を求められたときでした。自分自身限界まで働いていましたし、チームのメンバーにこれ以上の負担を求めるわけにも行きませんでした。一方でこのペースで開発を進めていたのではいつまでたっても使えるモノに仕上がらない。そうした中で、自然と「スケールするチームを作るしかない」という発想に至りました。いままで手を動かしてもらっていた若手たちにステップアップしてもらって、さらなる若手を採用してリードしてもらう。そうすることで全体のスピードを上げ、チーム全体の負荷を下げるしかない。

つまり、自分の中で一貫したキャリア観というよりも、成果を最大化するための手段としてチームビルディングやエンジニアリングマネジメントのいろはを学ぶようになりました。 勉強し始めてみると意外と面白いもので、科学的管理法から始まり、具体的なソフトウェア開発手法まで、科学的に体系化された様々な基礎理論が存在します。「成果を最大化するための科学」がそこにあったのです。

エンジニアリングとマネジメントの共通点

蓋を開けてみるとエンジニアリングもマネジメントも、そこには科学的に体系化された基礎理論があり、そうした基礎理論をベースに問題の解決 (=自分の場合プロダクト開発成果を最大化すること) を図ります。いままで、水と油のように錯覚していた 2 つの要素が実は「とても良く似ている」と思うようになりました。

こうした科学的な側面と問題解決 (≒自分にとっては成果の最大化) という側面に光を当ててみると、自分がいままで割けようとしてきた領域が、実は興味深くやりがいのある仕事なのではないだろうかと思うのです。

なぜアジャイルを認めてくれないのか ~プロジェクトマネジメント型組織における変革の処方箋~

はじめに

この記事では、従来型の組織にアジャイル開発を導入しようとして引っかかる、よくある「障害」とその解消のための方法について話します。

最近シニアエンジニアとして、新しい開発チームの立ち上げに関わる機会が増えてきました。 自分も含めて周囲のエンジニアやマネジャーが「アジャイル」を導入しようとして、うまくいくケースとうまくいかないケースがあります。上位のマネジャーの理解が得られなかったり、逆に現場エンジニアたちの理解が得られなかったり理由は様々ですが、一言で言うなら「組織」に理解がされないということです。自らの失敗を分析したり周囲の話を聞いていると、アジャイルの導入に失敗するケースは往々にして「組織」が何を求めているのか適切に理解して運用できていないケースが多いように思います。つまり組織が「アジャイル」を認めていないわけではなくて、適切にアジャイルな開発が運用できていないから組織の期待に答えられていないだけなのではないか、ということです。

続きを読む

Google Spanner のアーキテクチャを知る

最近 Cloud Spanner のベータ公開によって話題の Spanner。 気になっていたので論文を読んだり勉強会などで情報収集していました。日本語のリソースもそこまで多くないので、調べてわかったことを纏めておきます。

簡単にまとめると特徴は以下のとおりです。

以下で、細かい説明を続けていきます。

続きを読む

タスクキューのリクエストのログを分ける

StackDriver Logging でログを見るときや、ログメトリクスを定義する時にタスクキューのリクエストを別途扱いたい時がある。たとえば、通常のユーザーリクエストはレスポンスタイムの遅延を許容したくないが、タスクキューの場合は問題ないなどのケースである。

一番思いつきやすいのが、タスクキューのパスをログフィルターで除外することだが、もっと簡単な方法があった。以下でタスクキューを除いたログを閲覧したり、ログメトリクスを定義できる。

resource.type = gae_app AND resource.labels.module_id = "xxxx" AND logName = "projects/xxxx/logs/appengine.googleapis.com%2Frequest_log" AND NOT protoPayload.taskName:*

Task Queue の処理には必ず protoPayload.taskName が入る。フィールドがあるかないかのチェックは protoPayload.taskName:* のようにすれば良い。

フィールド内の特定の値をテストせずにフィールドの有無をテストするには、:* 比較を使用します。 Advanced Logs Filters  |  Stackdriver Logging  |  Google Cloud Platform

つまりこれで OK。

App Engine は DoS 攻撃を勝手に防いでくれるのか

App Engine には DoS プロテクションサービスなるものがあります。なにやらいい感じに DoS 攻撃を防いでくれそうな気がします。App Engine は勝手に DoS 攻撃を防いでくれるのでしょうか。

Configuring DoS Protection Service for Go  |  App Engine standard environment for Go  |  Google Cloud Platform

答えは Yes であり No でもあります。 端的にいうと Layer4 以下の DoS 攻撃は防いでくれるが、正規の HTTP 通信を大量に送りつけてくれるような類の攻撃には手動で dos.yaml を書く必要があります。

続きを読む

App Engine で謎の Untraced Time が発生するときは

App Engine を利用していると、時たま何かに引っかかったように処理が詰まることがあります。こんな時にはもちろん StackDirver Trace を使ってボトルネックを探ったりするわけです。 しかしながら時に、何の RPC を呼び出しているわけでもないのに、何故か処理の開始が遅い場合や、レスポンスを返すのが遅いということがあります。

よくありがちな理由が、リクエストが殺到して新規インスタンスがスピンアップするときに処理が遅れることです。私の周りでは良く「スピンアップに引っかかる」と言っています。しかし、具体的に何が起こっているのか、本当にスピンアップが起因しているのかは曖昧のままでした。そのため、今回、真面目に調べてみることにしました。

続きを読む