コネヒト開発者ブログ

コネヒト開発者ブログ

CakePHP3.4.7にアップデートしました!

f:id:itosho525:20170430222524j:plain

こんにちは。 ℃-uteが解散してしまうのが哀しくてたまらないサーバーサイドエンジニアの@itoshoです。ちなみに℃-uteでは鈴木愛理さんが好きです。

この連載も3回目になります!今回も簡単にアップデート内容をまとめさせていただきました。

CakePHP3.4.7の変更点まとめ

今回は以下の変更がありました。

  • 非推奨メソッド用にAPIドキュメントの改善
  • メッセージがcontextと空文字の両方を持っている場合はTranslatorクラスはキー名を返却するように修正
  • ネストされたリクエストボティのパラメーターがOauth1のクライアント認証アダプターでエラーとなる不具合を修正
  • IntegrationTestCase::enableRetainFlashMessages() 関数の追加
    • assertSession()関数を利用してフラッシュメッセージをアサート出来るようです。
  • Validation::hexColor()関数の追加
  • ネストされたmatching()関数をコールするページネーションクエリが無効なSQLを発行する不具合を修正

詳細は公式のリリースノートをご覧ください。

アップデート時にやったこと

今回も軽微な変更のみだったので、composer updateコマンドを実行後、CIが通っていればOKとして、リリースを行いました。 (リリース後も特に問題は発生しておりません)

おわりに

CakePHPのアップデートが続く限り、このブログは解散しませんので、引き続きよろしくお願いいたします。

JavaScript(React+Flux)のディレクトリ構成がガタガタになりそうだったので反省して改善する

f:id:dachi023:20170524174143p:plain

こんにちは。フロントエンジニアの@ry0_adachiです。

ここのところ、React + Flux Utils *1 の構成を選択することが多く、それ以外を選択することが少なくなってきました。特に大きな不満はないし、慣れてきたので良いかもな〜と思っていたのですが、開発期間が伸びてきて、アプリ自体の規模がそこそこになってくるとFacebook公式のサンプルコードのようにシンプルな構成だけではカバーしきれない状況が生まれることもあります。運用し始めてだんだんとその辺の辛さみたいなものが見えてきたのと、継ぎ接ぎな構成になるのは嫌だったので、このタイミングで1回整理して改善してみようと思いました。

Fluxについて

Facebook提唱のアーキテクチャ、またはフレームワークです。データの流れを一方向にして、シンプルに状態管理が行えることがメリットとしてよく挙げられています。Fluxについてより詳しく知りたい方は以前書いた記事があるので良ければそちらも合わせて読んでみてください。

f:id:dachi023:20170523163305p:plain GitHub - facebook/flux: Application Architecture for Building User Interfaces

本題に入る前に

GitHubで参考になりそうなFlux Utils製のアプリケーションのディレクトリやファイルの構成を見ていると各プロジェクトに最適化されている(しようとしている)ものが多く、参考になる部分とならない部分があります。この辺はフレームワーク側で制限しない限り、それぞれが使いやすいものが生まれ続けるでしょう *2 。この記事の内容についてもハマるものもあるだろうしハマらないものもあるはずなので、こうした方が良い!というわけではなくて、あくまで参考程度に捉えてもらえると幸いです。

最初に考えた構成

最初に作り始めた時は公式のサンプルコードにあったチャットアプリをベースにして、そのあと最低限必要なものだけを足して下記の図のような構成にしました。

チャットアプリ *3 : flux/examples/flux-chat at 3.1.0 · facebook/flux · GitHub

js-root/
  ├ actions/
  │   ├ api/
  │   │   └ TodoApi.js
  │   └ TodoActions.js
  │
  ├ components/
  │   ├ TodoList.js
  │   └ TodoListItem.js
  │
  ├ constants/
  │   └ ActionTypes.js
  │
  ├ containers/
  │   └ AppContainer.js
  │
  ├ dispatcher/    
  │   └ AppDispatcher.js
  │
  ├ stores/
  │   ├ TodoStore.js
  │   └ records/
  │       └ Todo.js
  │
  └ utils/
  • 補足1. CSSは別ファイルに出力、別で読み込んでいるのでこの図では考慮しない
  • 補足2. ActionCreatorにAPIコールの責務を持たせるのでactions/api/を作成する
  • 補足3. APIクライアント以外で各レイヤーに収まらないものはutils/に集約する

この構成にして反省したこと

役割を階層で表現できていると思っていた

actions/api/が悪い例で、下記が理由です。

  1. ActionCreator以外からは呼んではいけません、を階層で表現しようとしていた
  2. 呼んではいけなさそうだな!という雰囲気を醸しているだけで呼ぼうと思えば呼べる

ガチガチにやるなら、チェックツールなどで「Action以外がAPIクライアントのメソッドをコールしているか」といった処理をするべきで、わざわざディレクトリの構造を複雑にしてまでやることではありませんでした。今は普通にコーディング規約として文字に起こしてチームメンバーに共有しておく、くらいで良いかなと思います。

Componentの中でのレイアウトとコンポーネントの切り分け

Componentの種類を「パーツを組み合わせたもの」と「パーツ」の2つではっきり分けるべきでした。参考にしていた公式のサンプルコードにはMessageSection.react.jsThreadSection.react.jsがComponentとして用意されていて、こういう風にすれば良かったのかと後から気づきました。

共通で使えるComponentなのかどうかが分からない

components/に置かれたものが「プロジェクト全体で共通的に使えるもの」なのか「特定の画面のみで使われるもの」なのかの判断をぱっと見で付けられなくなってしまったのでもう1つディレクトリを切ったほうがよかったです。

より実用的なディレクトリ構成を考える

反省点を改善できるように修正をしたのが下記の図になります。

js-root/
  ├ actions/
  │   └ TodoActions.js
  │
  ├ components/
  │   ├ common/
  │   │   ├ TextField.js
  │   │   └ Button.js
  │   └ todo/
  │       ├ Section.js
  │       ├ TodoList.js
  │       └ TodoListItem.js
  │
  ├ constants/
  │   └ ActionTypes.js
  │
  ├ containers/
  │   └ AppContainer.js
  │
  ├ dispatcher/    
  │   └ AppDispatcher.js
  │
  ├ stores/
  │   ├ TodoStore.js
  │   └ records/
  │       └ Todo.js
  │
  └ utils/
      └ api/
          └ TodoApi.js

改善されたこと

actionsの中はActionCreatorだけになった

actions/api/utils/api/にしたのでactions/にはActionCreatorだけが入ることになりました。API関連はActionCreatorから切り出されたような形になったので、例えばAPIクライアントを別パッケージにします〜となった場合でも切り出すイメージが湧きやすいかもなぁ、とそんなことも思いつきました。

ComponentにSectionを設ける

Sectionを設けたことでレイアウトやパーツの集合体を定義するクラスが明確になり迷うことがなくなりました。Sectionがレイアウトやパーツの集合体でそれ以外はただのパーツである、という認識をしっかり持てたことで前に比べるとチーム開発もしやすくなったかもしれません(今は私1人でJS書いてるのでまだ試してませんが…)。

共通なComponentとそうでないものはディレクトリを分ける

プロジェクト内で使い回しの効くComponentを切り出したことで「前にこんなの作ったけ?」と考える時間が減ったのと、共通化しようという意図が見えやすくなりました。この辺はUIを考えてくれているデザイナーさんに相談しながら少しずつ共通化して切り出していくようにしています。

余談ですが、Componentが共通化される条件は「ロジック的に使い回せるか?」よりも「UIとして使い回せるか?」を判断軸にしています。これは、CSSとComponentの組み方の乖離 *4 が大きくなると実装が後々大変になるだろう、と判断してそうしています。

まとめ

事前に想定できるケースを洗い出しておくのは前提として、最初に決めた構成のまま突き進むのではなく状況に合わせて適宜変更していく・変更しやすい状態を保つことが重要です。また、途中で開発チームの規模が大きくなるかもしれないので複数人でも開発しやすい状態などを考慮しておくことも重要です。開発者が考える時間を減らしてあげることは開発効率の向上にも繋がるのでしっかりとメンテナンスしていきたいですね。

脚注

*1:Flux UtilsはFluxのリポジトリ(facebook/flux)に含まれています。

*2:現状は決定版となる構成が特にない状況だと認識しています。

*3:2017/05/25: masterにはなかったので3.1.0のタグから引っ張ってきています。

*4:CSSは共通化しているのにComponentはされていない、といったズレが生じている状態。

CakePHP3.4.6にアップデートしました!

f:id:itosho525:20170430222524j:plain

こんにちは。 GWはずっと欅坂46の『不協和音』を聴いていたサーバーサイドエンジニアの@itoshoです。 前回のエントリーからCakePHP3アップデートの連載?を担当しています。

そして、早速GW中に新しいバージョンである3.4.6がリリースされましたので、今回も簡単にではありますがアップデート内容をまとめさせていただきました。

CakePHP3.4.6の変更点まとめ

今回は以下の変更がありました。

  • CSRFとセキュリティトークン用のフィールドのautocomplete属性を明示的に無効にした
    • 新しいSafariのバックボタンの挙動で、blackholedエラーが発生する問題を解決しています。
  • SQLServerの接続ドライバーに多数のオプションが追加
    • 具体的にはコネクションプーリングやログインタイムアウト等のオプションが追加されました
  • APIドキュメントの改善
  • FixtureのDB接続のコネクションが常に別名を使うように修正
    • これによりテスト時に利用するDBが定義されていない時に、誤って生(≒本番)データに接続する危険が回避されます。
  • 翻訳パッケージ(Aura.Intl)が、Cake3.3で生成されたキャッシュファイルからinitializeする時に発生していた不具合を修正。
  • and*()メソッド及びor*()メソッドに追加される条件句(conditions)をいずれも後ろに追加するように修正
    • 以前は and*()メソッドは条件句を後ろに追加、or*()メソッドは条件句を前に追加(prepend)していました。
    • 生成されるSQL文としては順番が逆でも影響はありませんが、混乱するので統一したようです。

詳細は公式のリリースノートをご覧ください。

また、リリースノートには記載されていませんが、GitHubのREADMEにCakeのロゴが追加されたようです!

アップデート時にやったこと

3.4.5へのアップデート同様、今回も軽微な変更のみだったので、composer updateコマンドを実行後、CIが通っていればOKとして、リリースを行いました。

おわりに

日々開発しているシステムに不協和音が起きないようにフレームワークのアップデートは早め早めに実施していきましょう。(自戒も込めて)

CakePHP3.4.5にアップデートしました!

f:id:itosho525:20170430222524j:plain

こんにちは! 3月からコネヒトで頑張っているアイドル大好きエンジニアの@itoshoと申します。 最近はBiSHさんの『プロミスザスター』をヘビロテしています。

いきなりですが、皆さんは普段Webアプリケーションフレームワークのバージョンアップをどういうタイミングで実施していますか? 影響が少なそうなマイナーバージョンアップであっても、ついつい先延ばしにしてはいないでしょうか。

しかし、そうやって先延ばしにしていると、いざバージョンアップしようと思った時に影響範囲のチェックや動作確認のコストが大きくなりがちで「まだ暫くこのままでいいか…」と二の足を踏んで、気付けばいつの間にか最新バージョンに追従出来ないレガシーなシステムを生み出してしまうことになりかねません。(僕も過去にそのような苦い経験がございます…)

コネヒトではWeb APIの開発をCakePHP3を利用して行っているのですが、そのようなシステムを生み出さないように、新しいバージョンが出た際は積極的にバージョンアップするように努めていまして、今日はつい先日実施した3.4.4から3.4.5へのバージョンアップの内容について書きたいと思います。

ちなみに、CakePHPのHTTPリクエスト / レスポンスのインターフェイスが3.4系からPSR-7準拠に生まれ変わっており(素晴らしい!)、今後deprecatedなメソッドが増えることが予想されるので、まだ3.3以下のバージョンを利用されている方はお早めのバージョンアップをオススメします!

CakePHP3.4.5の変更点まとめ

詳細な情報は公式のリリース情報をご覧いただければと思いますが、今回は以下の変更がありました。

  • PaginationHelperで複数の値が設定されたソートリンクがデフォルト以外のモデルだと正しく生成されない不具合の修正
  • CSRFエラーとMissing Controllerが発生した際のエラーメッセージの改善
  • Diactorosというライブラリ起因で発生するHttp\Client\Requestクラス内でのエラー修正

また、公式ページに記載されているのは上記の3つなのですが、changelogをみてみると.travis.ymlからHHVMの記述が消えているので、興味ある方は実際のコードを読んでみると思わぬ発見があるかもしれません。

アップデート時にやったこと

3.4.4からのアップデートで、今回は軽微な不具合の修正のみだったので、composer updateコマンドを実行後、CIが通っていればOKとして、リリースを行いました。(アップデート後も特に不具合は起こっていません)

こういう時、きちんとテストコードを書いておくと安心ですね!

おわりに

今回はマイクロバージョンアップなので、問題なく最新バージョンにアップデートすることが出来ましたが、これからCakePHPをバージョンアップした際はこのブログでご報告させていただいて、微力ながらCakePHP界隈を盛り上げていくことをプロミスしますので、今後ともよろしくお願いいたします!

追伸

この記事をしたためている間に3.4.6がリリースされたので、それについても近日中にまとめたいと思います!

チームでのAPI開発の強い味方!!REST APIクライアント「Paw」と「Insomnia」を比較してみた

f:id:supermanner:20170502152129j:plain

こんにちは!今年もコナン映画にいってきました、コナンでは服部派のエンジニア結城(@super_manner)です(*´ڡ`●) さて、今回はAPIをチームで開発するうえでつよーい味方になるツールを2つ使い比べた結果をご紹介しようと思います!!

そもそもPawとInsomniaとは?

双方ともREST APIクライアントです。

Paw paw.cloud Insomnia insomnia.rest

APIを作成していると、POSTする必要があったり、User-AgentやRequestHeaderによる制約を受けたりで プラグイン追加が加速したりしますよね。
うっかりそのまま他のサイトを閲覧して全部がxmlで表示されたりすることもしばしば。
そんな煩わしさも、これらのクライアントを使うことで開放されるのです!!
APIをメインに開発されている方にはもはや必需品になっているかもしれませんね。 百聞は一見にしかずなので、試しに国土交通省のAPIを叩いた画面を見てみましょう。 *1

PawでGETした様子 f:id:supermanner:20170427205919p:plain InsomniaでGETした様子 f:id:supermanner:20170427210037p:plain

このように、各種設定をしてあげることで簡単に何が返ってくるかを確認することができます。

共有ツールとしてのおすすめポイント

わかりやすいUIなので、初めて使う方でも迷うことなく使っていただけるのがいいところです。
以下、参考画像はInsomniaのものですが、Pawも同じ機能がついています。(詳しい比較は後述します)
このように、階層構造でendpointを管理できたり、パラメータやヘッダ情報もまるっと共有できます! f:id:supermanner:20170501183349p:plain f:id:supermanner:20170501192159p:plain こちらは、環境変数を管理できる機能です。共通して使う変数と、そうでないものを切り分けできて便利です。 f:id:supermanner:20170501205120g:plain

また、snippetを自動生成してくれるのでPull Request等に貼り付けると、レビュワーに優しいですね\(^^)/ f:id:supermanner:20170501195328g:plain

2つのツールの機能比較

さて、ツールの紹介が終わったところで本題です。
コネヒトのAPI開発陣はトライアル期間を経て2つのツールの選択をせまられました… ツールの選択はいついかなるときも難しいものですよね。
ということで、2つのツールを比較し、できること、できないことを表にしてみましたので御覧ください。

Paw Insomnia 備考
RequestHeaderの設定
パラメータの設定
cookieの設定
環境ごとの変数の設定 例えばですが, {local/dev/stg/prod} 等で別途環境変数をマネージする機能があります
snippet発行 ただし双方で発行できるsnippetの種類に違いがあります
設定のimport/export
複数のapiを同時に叩く
teamアカウントを発行できる teamを作成することで、登録したapiの設定を共有できます
ブランチ機能がある ブランチ機能については後述します
料金 個人プラン:$49.99
チームプラン:月額$10
制限付き$0
有料プラン:月額$5
チームプラン月額:$8

Pawのほうが少し料金が高めですが、その分機能が多いですね。
InsomniaでできることはPawですべて網羅していると言っても過言ではないでしょう。(もちろんsnippetの発行形式など細かな違いはあります)
また、個人でcloudにプロジェクトをもつことのできる個人プランはいいお値段です!
そんな多機能なPawですが、中でも特筆すべき機能としてブランチ機能があります。

Pawのブランチ機能について

公式のドキュメントが非常に丁寧に書いてあるので、基本的な使い方はこちらを参照していただけると良いかと思います。 さぁ、セクションタイトルを見てみましょう!

Manage your workflow with branches

何処かで見たような機能ですね。そうです!我々も馴染みが深いgitと同じです。 開発中のapiなどはいきなりみんなに共有しても「これ開発中です!」と宣言して回る訳にはいきません。
そのような場合は自分用にブランチを切って、そこで登録していけばmasterはスッキリと余分なものが登録されないまま保たれます! チームにとっても、自分にとっても常に動くものが簡単に手に入りますb

Insomniaのデータ共有はどうなっているの?

先程の表で述べた通りInsomniaにもteam機能があり、workspaceとよばれる箱でデータを共有することができます。
が、現時点では手元のデータとremoteのworkspaceの同期だけで、git pullのような機能は実装されていません
つまり、手元で変更してしまうとその時点で残念ながらremoteのデータとはズレが生じてしまい、復旧の術がないということです。
dropboxをイメージしていただけるとわかりやすいかと思います。 cloud上のworkspaceと完全一致していない場合は下図のようにどれだけズレているのかが%で表示されています。
f:id:supermanner:20170429174502p:plain

英語ドキュメントの読み逃しがこわかったため、運営チームに問い合わせをさせていただいたところ、下記の返答を頂いたので将来的には実装されるかもしれませんね!!期待して待ちましょう。

Hi Mana,

Thanks for reaching out :)

There is no official way to do a pull master currently. For now, I recommend duplicating the request (or folder) that you want to edit so that you do not modify the existing data.

I will be sure to consider this use case as I improve the team features in the future.

Let me know if that works for you.

まとめ

2つのツールの機能比較をして、現在私のチームではInsomniaにチーム課金して使用しています。
なぜなら、いまのところREST Clientの使用理由が

  • 実装したAPIの各環境での実行確認をするため
  • テストで使用したrequest情報を完全再現してレビュー時にレビュワーにわかりやすくするため

の2つであるからです。
ですので、機能としてはシンプルで、料金もお財布に優しいInsomniaを採用し日々の開発に役立てています。 しかし今回記事を書くにあたって改めて調べてみるとPawの機能はまだ全然つかいこなせていなかったなぁと思ったので、 今後も使って有用だった機能があれば引き続き紹介していきたいと思います!*2

双方ともまだまだ開発は進んでいるようなので、みなさんもよければぜひ活用して素敵なAPIライフを送ってください!

*1:http://www.land.mlit.go.jp/webland/api.html

*2:pawのdocの厚さがものすごい

デザイナーの生産性を高める3つの改善策

f:id:connehito:20170331142331p:plain

お疲れ様です!デザイナーのきよえし(@kiyoe_furuichi)です。 今回は「生産性」というテーマと戦って得た学びについて書いてみたいと思います。 少し長いので、お茶でも飲みながらゆっくり読んでくださいね。

生産性とは

インプット(成果を生み出すためのヒト・時間・情報など)に対するアウトプットの比率のことを「生産性」と言います。 生産性は、少ないインプットで大きなアウトプットを得られると生産性が高いと評価され、大きなインプットの割にアウトプットが小さいと生産性は低いとされます。

デザイナーのお仕事で例えると、バナーを1日で作成してリリースするのと、半日で作成してリリースをするのとを比較して、成果物は同じものだとすると後者の方が「生産性が高い」ということになります。

きっかけ

「忙しい」を理由に思考時間を減らすのをやめたいと思ったことがきっかけでした。 サービスの成長と共にチャレンジできることが増え、好きな領域で活躍できることが嬉しい一方、目の前のissueに追われ本質からモノゴトを考える余裕がなくなっていました。 そんな状況だと質の高いアウトプットは出せない・精神衛生上良くない = 生産性が低下するという問題意識から、自分の仕事のやり方を見直すことにしました。

実践

生産性を高めるために実践したことは以下の3つです。

  1. RescueTimeで消費時間を可視化する
  2. issueを細かく分解する
  3. 情報を断捨離する

補足すると、今回は特に問題意識の高い「時間の使い方」にフォーカスして実践しました。時間の使い方が上手になると本質的な作業への消費できる時間が増えるため、デザインの質が上がり、生産性が高まると考えたためです。

1. RescueTimeで消費時間を可視化する

RescueTime(以下レスキュータイム)というツールを使って、1日の作業内容のうち、本質的な作業にどれだけ時間を消費できたかどうかを計測します。

例えばこのログをご覧ください。私の記念すべき初レスキュータイムの結果です。

f:id:connehito:20170330201240p:plain (本質的な作業 = Software Development + Design & Composition)

グラフを見ると、1日の作業時間の約30%がCommunication (to Slack)に消費されていることがわかります。本質的な作業よりもコミュニケーションの時間が掛かっているのは健全ではありません。 レスキュータイムのログを毎日退社前に見ながら、1日を振り返って改善ポイントを洗い出します。 ちなみにこの日は以下のような反省をしました。

Slackの閲覧時間が多くなってしまう原因として、モニター上に表示しっぱなしで新着の投稿があると気になって開いてしまうことがあげられる。必要なとき以外は目に止まらないようにウィンドウを非表示にしたり不要なチャンネルをミュートするなどで工夫をしよう。

2. issueを細かく分解する

(このやり方はgithub/ZenHubを利用した進捗管理を行なっている方に限定されます)

issueの粒度を細かくすることでアウトプットのゴールを明確にします。 そうすることで1作業に対しどのくらい時間を消費したかどうかが見やすく振り返りもしやすくなります。

例えば「新しい機能のUI設計」というissueがあるとします。細分化するとそのissueをepic(親)としてUI設計に必要な工程ごとにこのように分けることができます。

  • 【epic】 新しい機能のUI設計
    1. 【issue】 アイデア出し
      • ゴール:頭の中でイメージを固める
    2. 【issue】 ワイヤー作成
      • ゴール:ディレクターにOKをもらう
    3. 【issue】 Sketchデータ作成__iOS
      • ゴール:prott確認ができる状態に落とす
    4. 【issue】 Sketchデータ作成__android
      • ゴール:prott確認ができる状態に落とす
    5. 【issue】 確認・修正
      • ゴール:ディレクター・エンジニアと確認を行い、OKをもらう
    6. 【issue】 Zeplinデータ納品
      • ゴール:ZeplinデータをiOS/androidエンジニア両方に納品する

細分化すると一覧として見たときにすごい量になり管理が難しくなりますが、ZenHub(ゼンハブ)というプロジェクト管理ツールでissue管理を行い、次やるべきissueや進捗が明確になるようにしています。

f:id:connehito:20170330201327p:plain

in Progress が現在着手しているissueで、in Reviewがレビュー中、そしてDesign には1スプリント内に着手するissueが順番に並んでいます。

3. 情報を断捨離する

1に記載したレスキュータイムを始めてから、コミュニケーションで時間を消費しすぎているという課題を発見したため、見るもの・見ないものを分別して情報の断捨離を行いました。 主に以下の3つを意識して時間の節約を行なっています。

① タイムラインは閲覧を控える

Slackやカレンダー、SNSなどタイムラインを追うツールはマインドシェアを取られがちなので必要以上には見ないようにしています。
特にSlackの分報の使い方には注意しています。気軽に気づきやつらみを共有することを目的とするチャンネルですが、ついおしゃべりをしてしまうので時間を区切って見るなど常駐しないように気をつけています。

② 大事なことを見逃さないように工夫する

タイムラインの閲覧を控えると、タイムラインが流れてしまい大事な要件を見逃してしまうことがあります。うっかりしないよう、以下のSlackの設定で予防できるようにしました。

  • Slackの /remind機能を利用し、タイムラインを見る頻度を減らす
  • 必ず見るChannelは通知が来るように設定し、見逃しを予防する
  • 定期的に見ないChannelはミュートし、見るべきものだけにフォーカスできるようにする

③ 1次情報をなるべくチェックする

タイムラインの閲覧を制限すると得られる情報に偏りがでてしまうため、議事録やメモなど、まとめられた資料を読んでキャッチアップしています。 コネヒトではDocBase(ドックベース)というドキュメント共有ツールを利用しているので、そちらに投稿されたものは毎日数分時間を取って読んでいます。

f:id:connehito:20170331142058p:plain

成果

上記3つを3ヶ月間実践した結果、3つの改善をすることができました。(333!)

1. 本質的な作業への消費時間が増えた

RescueTimeを見ると時間の使い方が大分良くなったことがわかります。
1日の作業時間のうち、本質的な作業に消費する時間が40%から64%と約1.5倍の改善を達成することができました。

f:id:connehito:20170330201425p:plain (本質的な作業 = Software Development + Design & Composition)

2. デザインの質が上がった

1issueに掛ける時間を増やしたことで、制作後の手戻り回数を減らすことができました。さらに 1スプリント内に消化できるストーリーポイントの合計が22ptから48ptと2倍近く増え、着手できるissueをかなり増やすことができました。(まぐれかもしれないので、引き続き邁進します)

f:id:connehito:20170330201435p:plain

3. 気持ちに適度な余裕ができた

時間に余白をつくることができたため、納品が近い成果物があった場合も気合いで解決することがなくなりました。切羽詰まるような不安要素がなくなり、健全な気持ちでモノゴトに立ち向かえるようになりました。

終わりに

生産性というテーマと向き合う中で、デザイナーとして一歩成長することができました。サービスのことを俯瞰して考える時間が増え、自分自身が納得感を持てるアウトプットを出せるようになったからです。
とはいえ、今回ご紹介した改善策は、"マイナスをゼロに戻すためのもの"だと考えています。
「生産性を高める」ということにおいて、良いアウトプットを出すためにはやっぱりスキル面を磨かないといけません。そのために、今後はゼロからプラスにするための改善に取り組んでいこうと考えています。最後まで読んでいただき、ありがとうございました!

参考

Kotlin1.1の新機能について

f:id:tommy_kw:20161106193050p:plain

こんにちは!@tommykwです。先日のDroidKaigiとても楽しかったですね。2日間参加させていただき、どのセッションも素敵なセッションで学びのある時間を過ごせました。スタッフ、スピーカー、参加者の皆さん、ありがとうございました!

さて、Kotlin1.1が3/1にリリースされました。早いものでもう3週間ほど経ちますが、すでに利用されている方も多くいらっしゃるのではないでしょうか。Kotlin1.1では多くの機能がリリースされましたが、今回はKotlin1.1で導入された便利な新機能をいくつか情報共有したいと思います。

今回紹介する新機能

  • Alt+Enter
  • _キーワード
  • 型エイリアス
  • ::キーワード
  • データクラス継承

Alt+Enter

Alt+EnterとはIntentionと呼ばれるもので、Android Studio上でAlt+Enterすることによって簡単にクイックフィックスできる機能です。 高速コーディングを支える技術で、Kotlin1.1で新しく追加されたクイックフィックスについて紹介したいと思います。

  • Add Android View constructors using @JvmOverloads

Android開発でカスタムViewを作る場合はコンストラクタを複数定義する必要があり、とても冗長になります。 それをKotlinの@JvmOverloadsを使うことによって簡潔に記述できます。 f:id:tommy_kw:20170317080730g:plain

簡単ですね。しかし、以下のように既にコンストラクタを定義した場合はクイックフィックスできません。 この状態からコード最適化できるとリファクタリングが容易です。

class CustomView: View {
    constructor(context: Context) : this(context, null) {
    }
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
    }
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
    }
}
  • Convert to range check

範囲指定しているif文をRange型に変換します。 f:id:tommy_kw:20170317082139g:plain

  • Merge 'if's

ネストしたif文をマージします。 f:id:tommy_kw:20170317082603g:plain

  • Use destructuring declaration

ラムダで分解宣言を使うことによって、オブジェクトからデータを取り出して別の変数に代入します。 f:id:tommy_kw:20170317224853g:plain

よく見ると、fun main(args: Array<String>) {} と記述するとKotlinアイコンが表示されますね。 エラー、警告表示があった時に取り敢えずこのショートカットを使えば役に立ちますし、特にカスタムViewのクイックフィックスは Kotlinっぽく記述できてコーディングが捗りそうです。

_キーワード

Swiftでもお馴染み_キーワードで、パラメータを省略するためのキーワードです。 ママリQにて、Kotlin1.1にアップグレードした際にLintで未使用な変数である旨の警告が大量に表示されました。そんな時に利用したのが_キーワードになります。使用用途としては以下の通り、ラムダの引数、無名関数の引数、分解引数で利用できます。

  • ラムダの引数
// v, hasFocusパラメータを定義する
view.setOnFocusChangeListener { v, hasFocus ->
}

// v, hasFocusパラメータを省略する
view.setOnFocusChangeListener { _, _ ->
}
  • 無名関数の引数
// xパラメータを定義する
val function = fun(x: Int) {}

// xパラメータを省略する
val function = fun(_: Int) {}
  • 分解引数 その1
// a, b, cパラメータを定義する
val (a, b, c) = listOf(1,2,3)

// bパラメータを省略する
val (a, _, c) = listOf(1,2,3)
  • 分解引数 その2
// a, b, cパラメータを定義する
listOf(Triple(1, 2, 3)).forEach { (a, b, c) -> }

// a, cパラメータを省略する
listOf(Triple(1, 2, 3)).forEach { (_, b, _) -> }
  • onClickイベントのunusedなView引数

AndroidでよくあるパターンのonClickイベントのView引数がunusedになる問題ですが、

fun onPhotoClick(@Suppress("UNUSED_PARAMETER") view: View) {
    // do something
}

残念ながら以下の様にメソッドの引数には適用できません。unusedを回避するには@Suppress("UNUSED_PARAMETER") アノテーションをつけるのが良さそうです。

fun onPhotoClick(_: View) {
    // do something
}
  • _キーワードに型を明示的に定義

Function型の引数が複数ある場合に{}{ _ -> }で記述するか悩むケースがあります。 以下はRxJavaを用いたサンプルになります。

// RxJava1の非同期Extension
fun <T> Observable<T>.async(onNext: (T) -> Unit, onError: (Throwable) -> Unit) {
    this.subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(onNext, onError)
}

// APIクライアントからメンバーカウントを取得する
apiClient.memberCount()
    .async(
        { result ->
            if (result.count > 0) {
                // do something
            }
        }, 
        { e ->  } // 未使用のeが未使用となる
    )

上記の{ e -> }{}に変えても良いですし、{ _: Thowable -> }と型指定した宣言をしても良いのではないでしょうか。 多少冗長になりますが、個人的には可読性が上がる場合もあるかなと思います。

apiClient.memberCount()
    .async(
        { result ->
            if (result.count > 0) {
                // do something
            }
        }, 
        { _: Thowable ->  }
    )

型エイリアス

型エイリアスは既存の型を別の型として提供でき、関数、コレクション、コンパニオンオブジェクト、ネストしたクラスなどに適用できます。 トップレベル宣言、メンバー宣言、ローカル宣言をすることができますが、Kotlin1.1ではトップレベル宣言のみサポートされています。

以下はKotlinのstdlibのコード内で型エイリアスを利用しているケースですが、本来importする必要があるライブラリを型エイリアスを使うことによって、利用する側はimport不要で利用出来るのが良いです。

TypeAliases.kt

@file:kotlin.jvm.JvmVersion

package kotlin.collections

@SinceKotlin("1.1") public typealias RandomAccess = java.util.RandomAccess


@SinceKotlin("1.1") public typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public typealias HashSet<E> = java.util.HashSet<E>


// also @SinceKotlin("1.1")
internal typealias SortedSet<E> = java.util.SortedSet<E>
internal typealias TreeSet<E> = java.util.TreeSet<E>

さらに、以下のAnnotations.ktを見ていただくといくつかのアノテーションをサポートしていることがわかります。 型エイリアスは@Deprecated, @SinceKotlin, @Suppressアノテーションを許可されていて、別名の型として利用する機会はあると思いますが、アノテーション付きで定義できるところが良いなという印象です。

Annotations.kt


@Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS)
@MustBeDocumented
public annotation class Deprecated(
        val message: String,
        val replaceWith: ReplaceWith = ReplaceWith(""),
        val level: DeprecationLevel = DeprecationLevel.WARNING
)

@Target(CLASS, ANNOTATION_CLASS, PROPERTY, FIELD, LOCAL_VARIABLE, VALUE_PARAMETER,
        CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, TYPE, EXPRESSION, FILE, TYPEALIAS)
@Retention(SOURCE)
public annotation class Suppress(vararg val names: String)

@Target(CLASS, PROPERTY, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, TYPEALIAS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public annotation class SinceKotlin(val version: String)

このような未定義を表したカスタムViewクラスも定義できますし、@Suppressアノテーションもうまく利用できるかもしれません。

@Deprecated("unused view")
typealias DeprecatedView = View

::キーワード

::キーワードが追加されました。これはJavaの<expression>::classメソッド参照と<expression>::<member name>メンバー参照をサポートしています。 毎回ラムダで少し冗長に記述するのではなく、::キーワードを使ってより簡潔に記述できます。

// ラムダ
listOf("a","b","c").map { it.toUpperCase() }

// メソッド参照
listOf("a","b","c").map(String::toUpperCase)
data class Member(val id: Int)
// ラムダ
intArrayOf(1,2,3).map { Member(it) }

// メソッド参照
intArrayOf(1,2,3).map(::Member)

また、KClassを取得するのに、.javaClass.kotlinがありましたが、::classを使ったほうがよさそうです。 以下のコードからもわかるように、.javaClass.kotlin::classでは動作が異なるため、.javaClassはDeprecatedになっていく予定のようです。

class Foo {
   fun bar() {
      val a: KClass = this.javaClass.kotlin
      val b: KClass = this::class
 
      a.memberProperties.forEach { property: KProperty1<Foo, *> ->
          property.get(this)
      }
 
      b.memberProperties.forEach { property: KProperty1<out Foo, *> ->
          property.get(this) //Error: Kotlin: Out-projected type 'KProperty1<out Foo,*>' prohibits the use of 'public abstract fun get(receiver: T): R defined in kotlin.reflect.KProperty1'
      }
   }
}

データクラス継承

データを保持するクラスとして便利なdata classがありますが、これは継承することができませんでした。以下の通り、Kotlin1.1からsealed classやopen classを継承できるようになり、柔軟なデータクラス設計ができるようになりました。

open class Base(
    open val id: Int, 
    open val name: String
)

data class Member(
    override val id: Int,
    override val name: String,
    val age: Int
) : Base(id, name)

val member = Member(1, "foo", 20)

今までは生成されるコードの一貫性を保つために、クラスを継承できない仕様になっていました。インターフェースは実装することができましたが、 sealed classやopen classを継承できるのはとても便利です。

Kotlin 1.0.7、1.1.1リリース

早いですね!1.0.7は現時点でバージョン1.1にアップグレードできないユーザが利用できるように、Gradleおよびアノテーション処理に関連する修正をバックポートできるバージョンです。1.1.1は1.1.0のバグ修正がメインで一部のIDE、Compiler、Librariesの修正を行っています。特に問題ないのであれば、1.1.1の利用をオススメします。

まとめ

Kotlin1.1の新機能について簡単に説明しましたが、いかがでしょうか。本来であれば、Kotlin1.1のメイン機能であるCorutinesについて情報共有できれば良かったのですが、正直まだ使いどころがわかっていません。こちらは引き続き理解を深めていきます。また今回紹介していない新しいスコープ関数provideDelegateオペレータなどすぐに利用出来る便利な機能がたくさんリリースされました。これらをうまく利用することで、Kotlinの言語機能を最大限に活かせそうです。それでは楽しいKotlinライフをお過ごしください!