TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

587

こんにちは、CTOの藤川です。 これまでエンジニアの肩書において、エンジニア専門職の上級職としてリードエンジニアという役割を設定しておりました。 リードエンジニアと言うと、世間の見方としては「技術力に優れたエンジニア」というやや漠然とした役割と想像しているのではないでしょうか? Webサービス開発における「技術力」とは 「良いサービス」とは 「良いサービス」を提供するために、エンジニアに求められるスキル Webサービス企業におけるエンジニア属性:「テック」と「サービス」 「テックリード」と「サービスリード」の役割 リードエンジニア テックリード サービスリード 「サービスリード」とは、その一文字を直す作業に意義、喜びを感じられること 一緒に良いサービスを作りませんか? Webサービス開発における「技術力」とは そもそも「技術力とは何ぞや?」というのが曖昧なことが多いです。 というのも、私共が担っているビジネスは、Webサービスを提供しています。Webサービスは24時間365日運用されていますので、必要な技術も多岐に渡ります。毎日新機能を開発していますし、継続的かつ安定的に運用していく必要がありますし、セキュリティ要件は日々変わっていきます。 僕らはコンピュータサイエンスの力の恩恵を受けつつ、特には恩返しを試み、サービス実現としてのソフトウエアエンジニアリングを推し進め、ユーザーさんに使い続けていただくサービスを継続的に開発運用していくことが求められます。 我々の場合は、フリーミアムなECプラットフォームですから、多くのショップオーナーさんのビジネスや生活を支えることを生業として、結果として生まれた流通総額(GMV)という成果に連動してお給料をいただきます。 また、サービスそのものは外部環境に合わせて変化していくことが必要ですので、それをスピーディーに推進する力がエンジニアには求められます。Webサービスで何もしないのはビジネス的にもセキュリティ的にも退化を意味します。常に時代の変化にアンテナを張り、技術を用いて変化を生み出していきます。また、そのような技術的問題点を一切感じさせることなくユーザさんには安心して使っていただけるように良いサービスとして提供していく必要があります。 「良いサービス」とは (Photo by WOCinTech Chat ) ここで言う、良いサービスとは、 サービスクオリティ (目的に対しavailableではなく、usefulであること。) プロダクトクオリティ(UIを通じた高い品質のサービスを安心、安定して使えること) サイト信頼性 (変化と継続) スケーラビリティ (何が起きても困らない) セキュリティ などで構成されます。 「良いサービス」を提供するために、エンジニアに求められるスキル これらを元に私共が提供するサービスを実現することに関して、エンジニアに求めるスキルについて軽く想像するだけでも、結構たくさん思いつきます。 Webアプリケーションの技術開発力(PHP等のサーバサイド言語、JS、動画アプリケーション) よいWebサービスを作るサービス開発力(UI/UX、ビジネスフロー、サービスを実装に変える力) HTML5、iOSやAndroidアプリケーションのアプリ開発力(主にパフォーマンス、安定性、OSの知識等) Webフロントエンド、iOSやAndroidアプリケーションのUI開発力(クリエイティビティの実現、UI安定性) サーバインフラの実現力、DevOpsの開発力、AWS等のクラウドの知識 セキュリティをしっかり維持する力、もしくは過去の問題の発見力 サービスの継続的運用に関するコミットメントを持てる力 (なにげに、これは特殊技能だと思います) 機械学習やAIなどを実現する技術力、統計的知識 変化するフロントエンドをキャッチアップし、クリエイティビティと結びつける構築力 要件をチームにわかりやすく翻訳する翻訳力、ドキュメント力 未来に向けたアーキテクチャ設計、変化を実現する力(これはスキルだけあってもできない) プラットフォーム開発力、外部パートナーを巻き込むAPI開発力など プロジェクトマネジメント、アジャイル等工程設計力と実践力 テストやQAなどプロダクトクオリティを実現する方法論に長けていることと、チームメンバーを引っ張る力 PHPそのもの、フレームワークそのものの成長性維持に貢献する知識、人脈、コミットメント、コミュニティ活動ができる力 Webサービス企業におけるエンジニア属性:「テック」と「サービス」 ここに挙げた通り、一つのWebサービスの成長に必要なスキルへの関心について、一人のエンジニアがすべて満たすのは現実的に不可能です。 Webサービスのエンジニアに期待される能力のわかりやすい事例として、深夜に起きていること(障害などではなくアラートなど)の適応力は、何より我々のビジネスそのものへの関心がなければ、到底できるものではありません。「このアラートは今は気にしなくても大丈夫」の一言をSlackに投げるためのバックグランドにある技術知識と当社のサービスの経験こそがWebサービスのエンジニアとして求められる部分です。 もちろんやることはそれだけではないですから、エンジニアそれぞれ特技にあわせて役割を定義し、貢献、成果を評価できる体制をつくり、その総体としてエンジニアチームを構成していくことになります。それに必要な貢献を期待できる能力に、投資的にお給料を先払いするのに必要な肩書を上級技術職として割り当てていくことになります。 それに必要な能力を抽象的にまとめていった時に、Webサービス企業のエンジニアに求められる能力属性には「テック」と「サービス」という括りがあることにたどり着きました。 テックのスキルが優れているだけで良いサービスが作れるほどWebサービスは甘くありません。一方で、良いサービスを作るためにはテックの適応力は不可欠です。また時間軸と共に変化していく技術に対する適応性、応答性は、テックのスキルが優れている人材が得意とする分野です。 「テックリード」と「サービスリード」の役割 今回、テックとサービスにおける理想像をまとめて、それらを達成している模範となるべきエンジニアを「リードエンジニア」と定義し、更に細かい属性として、「テックリード」と「サービスリード」という肩書を定義しました。 Webサービス企業のエンジニアはテックへの適応力とサービス開発力は不可分です。しかし、その人の魅力の出自はテックなのかサービスなのか、どちらに立脚するかで肩書を変えています。今回、その役割定義を行いました。 その定義が以下になります。まずは、リードエンジニアの役割です。 リードエンジニア 事業に配属され、事業の中での開発やサービス運営をリードする以上の成果を期待されたロール。 サービスの継続的発展を実現するための技術力や、BASEのサービス哲学を具現化するためのサービス開発力などを兼ね備え、圧倒的な主体性でサービスの成長に取り組む模範となるべき役割を担う。 「圧倒的な主体性」こそがリードエンジニアにとって重要なポイントです。 そして、テックリードです。 テックリード 高い技術力を持ち、時代の変化を読みながら、適切な技術を、適切なタイミングと適切な方法で自社サービスに取り入れ、スケーラビリティの実現や高いセキュリティを通じてサービスの進化に寄与することが期待される。サービスの継続的進化を担い、メンバーの技術的成長の模範となる役割 「適切な技術を、適切なタイミングと適切な方法」を模索することを強く求めています。技術変化の激しい業界だからこそ、タイムラインに日々流れてくるような素敵な技術ありきではないということです。また、チームに変化を生み出すスキルがテックリードには重要です。それが「サービスの継続的進化を担う」という部分に期待されています。 該当職種はWebアプリケーションエンジニア、フロントエンド(Web、App問わず)に加えて、SREのようなDevOpsエンジニアや技術基盤を担うアーキテクトなども想定しています。 そして、今回追加したのがサービスリードです。 サービスリード BASEのサービス哲学を熟知し、高い顧客体験を実現するサービスを開発することで、体験の質を通じたサービス信頼性を実現する。 顧客に支持され続けるサービスの継続的発展に寄与し、サービスづくりの模範となる役割。 当社のサービスでビジネス要件をサービスとして設計する際に、ユーザーへのベネフィット実現を前提としてすべてを再構築する必要があります。つまり「本当にその要求はユーザベネフィットが提供できるのか?プロダクト側で全部洗い直せ」という工程が必要なのですが、そのあたりのアレンジをエンジニアという役割の中で期待できる人材がサービスリードです。 例えば、ついつい受託出身だと(僕自身がそうなのですが)、ビジネス要件は実現すべき前提条件として盲目的に受け入れてしまいがちですが、そのまま無理やり実装したところで戦闘力のある良いサービスが提供できないのであれば、すべての話をひっくり返さなくてはいけないかもしれません。そこへのサービス思考力が「高い顧客体験」の実現には不可欠です。その視座を元に高いプロダクトクオリティを実現することがサービスの継続的発展に繋がります。 また、顧客ニーズに耳を傾け、時には己の判断で即座に修正を行える信頼というのも必要です。その修正の粒度や判断力が顧客ニーズや会社のOKR達成に対して不適切であれば、その行動を誰かがマネジメントする必要があります。例えば大技主義のアーキテクトであったり、スキルが高いがゆえに過剰に作り直したくなる病などを持っている人は、サービスリードには適していません。目の前で困っている人、その裏側にいる人を想像し、適切な粒度の修正を行うという判断力は、テックを中心とした思想とは少し違う価値観が求められることが多いようです。 この役割は基本的にはWebアプリケーションエンジニアやフロントエンド(Web、App問わず)などを想定していますが、もしかしたらフロントエンドを担うデザイナー職もこの範疇に入るかもしれません。(デザイナーとデータサイエンティストは違う役割構造を定義することになるとは思いますが) 「サービスリード」とは、その一文字を直す作業に意義、喜びを感じられること サービスリードという肩書は検索してみると、いくつかのWebサービス企業の記事で見つかり、参考にさせていただきました。 しかしながら、なかなか一般的なエンジニアの評価スキルとして見えないことが多いです。世の中の判断軸が技術偏重とまでは思いませんが、技術系メディアが書籍等を作るのに必要なテクニカルな部分にアテンションが集中しやすいというのがあるのかもしれません。テックへの貢献は、GitHubで言うPublicリポジトリにおける貢献や活躍が多くインターネットで共有されています。サービスへの貢献は、主に企業内のPrivateリポジトリへの隠れた貢献であることで、なかなか共通基盤としての技術者評価に結びつきにくく、Webサービスを作ることはコードさえ書ければ誰でもできるんじゃないか?と、その質やコミットメントの部分を無視されてしまい、当たり前の行動のように見えてしまうことで適切に言語化されていないというのがあるように感じています。 サービスで実現したい哲学(その機能は何のためにあるのか、何を実現したいのか)を顧客が所有する仕事に慣れている受託の方やSIerの方で、僕の面接を受けている人であれば聞いている方もいらっしゃるかもしれませんが、「その一文字を直す作業に意義、喜びを感じられること」がWebサービスに従事するエンジニアに求める部分です。今回、その模範となる役割がサービスリードそのものです。今回テックリードとサービスリードの肩書を持つエンジニアができましたが、当社のエンジニアはテックリード職であってもサービス属性は実はすごく高いです。そもそも、それが見えないと採用していませんから、テックリードとサービスリードは敵対しない概念だと書いたとおり、その人の魅力はどこが主軸か?で分けています。 一緒に良いサービスを作りませんか? この2つの概念は敵対するものではなく、両立が望ましいわけですが、そうは言っても、その人の得意技として技術変革を主軸に活躍する人もいれば、技術は手段としてサービス実現に活躍する人もいるわけで、その両属性をチームとして評価しますよ!というのが今回のテックリードとサービスリードというリードエンジニアの属性化に繋がっています。今後はそこをしっかり明確化していくことで、サービスづくりをしたいエンジニアももっと増やしていきたいと思っています。 この両者の人材がバランスよく融合することで、サービス全体のクオリティが実現できるというのが、この記事に書いてた論点です。それぞれの属性を強みとしたエンジニア、それぞれ、ちゃんと高く評価してるんですよという当社にご興味を持ってくださったお方がいらっしゃいましたら是非、お話しましょう。 採用情報はこちらから! jobs.binc.jp
こんにちは。BASE で Design Group に所属している 三佐和 です。主に ネットショップ作成サービス「BASE」 のフロントエンドを担当しています。 背景 BASE のデザインチームはここ最近で人数が急激に増え、活動が活発になってきており、その中のプロジェクトの一つとして、現在スタイルガイドの刷新に取り組んでいます。 しかし、人数が増えていく一方で、コーディングのルールの統一をコードレビューや個人の裁量に任せていたり、マークアップからリリースするまでに時間がかかってしまうことが問題になってきていました。 そこで、新しいスタイルガイドでは、デザイナーやエンジニアの作業工数を短縮し、より効率よく開発を進めるために、コーディングルールの整備とリグレッションテストを導入することにしました! やったこと stylelint を使ってコーディングルールを管理 BackstopJS でテストを行うことでデグレを防ぐ 前提として、 nodejs や yarn などの一般的なフロントエンド開発環境が整っているものとします。 stylelintを導入 stylelint を使って、これまで個人の裁量で保たれていたコーディング規約への準拠を機械的に行えるようにします。 導入 stylelintをインストール その他必要なプラグイン等をインストール stylelint-scss (SassのSCSS記法のためのプラグイン) stylelint-order (プロパティをアルファベット順に並び替えるプラグイン) stylelint-config-standard (CSSのコーディング規約集) prettier-stylelint (コードフォーマッターprettierとstylelintを組み合わせて使用する) yarn を使ってインストールします。 $ yarn add -D stylelint stylelint-scss stylelint-order stylelint-config-standard prettier-stylelint 設定 既存のコーディングルールに合わせて、 .stylelintrc を設定していきます。 BASE の場合はプロパティをアルファベット順にソートしたかったので、 stylelint-order を使用したりしています。(私はいままで abc の歌を歌いながら順番にプロパティを並べていました........) // .stylelintrc { " parser ": [ " css " ] , " plugins ": [ " stylelint-scss ", " stylelint-order " ] , " extends ": [ " stylelint-config-standard ", " ./node_modules/prettier-stylelint/config.js " ] , " rules ": { " indentation ": 4 , // インデント " order/order ": [ " custom-properties ", " declarations " ] , // アルファベット順でソートする " order/properties-alphabetical-order ": true , // アルファベット順でソートする " length-zero-no-unit ": true , // 値が「0」なら単位を省略する " number-leading-zero ": " always ", // 小数点の頭の「0」は省略する " color-hex-length ": " short ", // HEX形式のカラーコードは3文字で表記する " shorthand-property-no-redundant-values ": true // ショートハンドでプロパティを書く } } 設定ができたら、 package.json の scripts にコマンドを追加して、 yarn lint-sass で実行できるようにします。 // package.json // ... " scripts ": { " lint-sass ": " prettier-stylelint --quiet --write src/sass/**/* " } // ... 実際の使用方法 まずは思うがままに sass(scss) を記述します。 . btn--submit { color : $white ;  background-color: $green ; border : none ; } めちゃくちゃですね!でも大丈夫です、lint を実行すると... $ yarn lint-sass こうなります。 . btn--submit { background-color : $green ; border : none ; color : $white ; } 完璧ですね!もう歌は歌わなくても大丈夫になりました! 結果 常にコーディングルールを意識する必要がなくなったので、マークアップのスピードも上がりました! 2. BackstopJSを導入 BackstopJS を使って、戻りが発生しないよう、動作チェックする仕組みを作ります。 導入 BackstopJSをインストール stylelintと同様に、yarn を使ってインストールします。 $ yarn add -D backstopjs backstop.json にオプションを設定し、スナップショットを撮る スナップショットを使ってテストする 設定 backstop.json に必要なオプションを設定していきます。 // backstop.json { " viewports ": [ { " label ": " sp ", " width ": 320 , " height ": 480 } , { " label ": " pc ", " width ": 1024 , " height ": 768 } ] , " scenarios ": [ { " label ": " reference ", // スナップショットの名前 " url ": " テストするURL ", " hideSelectors ": [] , " removeSelectors ": [] , " selectors ": [ "# test - sandbox " // スナップショットを撮影する部分 ] , " readyEvent ": null , " delay ": 500 , " misMatchThreshold " : 0.1 , " onBeforeScript ": "", " onReadyScript ": "" } , { " label ": " test ", // スナップショットの名前 " url ": " テストするURL ", " hideSelectors ": [] , " removeSelectors ": [] , " selectors ": [ "# test - sandbox " // スナップショットを撮影する部分 ] , " readyEvent ": null , " delay ": 500 , " misMatchThreshold " : 0.1 , " onBeforeScript ": "", " onReadyScript ": "" } ], " paths ": { " bitmaps_reference ": " ./backstop_data/bitmaps_reference ", " bitmaps_test ": " ./backstop_data/bitmaps_test ", " compare_data ": " ./backstop_data/bitmaps_test/compare.json ", " casper_scripts ": " ./backstop_data/casper_scripts " } , " engine ": " phantomjs ", " report ": [ " CLI ", " browser " ] , " cliExitOnFail ": false , " casperFlags ": [] , " debug ": false , " port ": 3001 } 設定ができたら gulpfile にタスクを追加します。 // gulpfile.coffee // ... exports.backstopref = () => connect.server( { root: './build' , livereload: true } ) backstopjs( 'reference' ).then () -> connect.serverClose() exports.backstop = () => connect.server( { root: './build' , livereload: true } ) backstopjs( 'test' ).then () -> connect.serverClose() // ... そして、 package.json の scripts にコマンドを追加して、 yarn backstop-ref で参照用画像の作成、 yarn backstop-test でテストが実行できるようにします。 // package.json // ... " scripts ": { " backstop-ref ": " gulp backstopref ", " backstop-test ": " gulp backstop " , } // ... 実際の使用方法 はじめに、テストの元データになる参照用の画像を作ります。 $ yarn backstop-ref BackstopJS は、この画像をもとに比較テストを行ってくれるので、予期せぬ変更を行ってしまった場合にテストが失敗し、それに気づくことができます。 試しに、このボタンの border-radius を 8px から 0px へ変更してみましょう。 そしておもむろにテスト実行用のコマンドを叩きます。 $ yarn backstop- test // ... compare | ERROR { requireSameDimensions: false , size: isDifferent, content: 0.32%, threshold: 0.1% } : reference bbq_reference_0_test-sandbox_0_sp.png compare | ERROR { requireSameDimensions: false , size: isDifferent, content: 0.32%, threshold: 0.1% } : test bbq_test_0_test-sandbox_0_sp.png compare | OK: reference bbq_reference_0_test-sandbox_1_pc.png compare | OK: test bbq_test_0_test-sandbox_1_pc.png report | Test completed... report | 2 Passed report | 2 Failed report | Writing jUnit Report report | Writing browser report report | jUnit report written to: ~~/backstop_data/ci_report/xunit.xml report | Resources copied report | Copied configuration to: ~~/backstop_data/html_report/config.js COMMAND | Executing core for `openReport` openReport | Opening report. report | *** Mismatch errors found *** COMMAND | Command `report` ended with an error after [ 0.51s ] COMMAND | Error: Mismatch errors found. at ~~/backstopjs/core/ command /report.js: 113 : 17 at < anonymous > :null:null COMMAND | Command ` test ` ended with an error after [ 5.371s ] COMMAND | Error: Mismatch errors found. at ~~/backstopjs/core/ command /report.js: 113 : 17 at < anonymous > :null:null [ 15 : 02 : 59 ] ' backstop ' errored after 5.38 s 差分が発生したので、テストが失敗し、レポート用の HTML が出力されました。 これを開いてみると、差分のある箇所がピンク色になっています!分かりやすいですね! 結果 作業を開始する前にスナップショットを作成しておくことで、行った作業でのデグレに早く気づいたり、防ぐことできるようになりました。 まとめ 今回、stylelintやBackstopJSを利用したことで、実装者の負担が以前よりも軽くなりました! 実装者が増えても差分が生まれにくく、デザインの一貫性を保つことができる仕組みを考え、引き続きスタイルガイドを作成していきたいと思います。また同時に、それらをメンテナンスしやすい環境も整えていきたいと思います!
こんにちは!BASE Product Division サーバーサイドエンジニアの 東口 です。主にEコマースプラットフォーム「BASE」の決済領域の開発をしています。本ブログでは、PHPerKaigi 2018での登壇記事等も書いています。 devblog.thebase.in BASEのサーバーサイドの多くはPHPで書かれているので普段触る機会の多い言語はPHPなのですが、先月からGoを書きたい有志が集まって定期的にGo言語勉強会を行っています。 きっかけ 初めたきっかけは2018年4月15日に開催された Go Conference 2018 Spring でした。スポンサー企業のラインナップや実際に会場の熱気に触れて、改めてGo言語の勢いや実用性を強く再認識しました。それまでGoを書きたいと思っていたこともありカンファレンスに参加していた同僚と勢いで始めることを決めました。 学生版goconやろうとしている方に刺激を受けて「Goならわかるシステムプログラミング」社内読書会を勢いで作った #gocon — Kazuki Higashiguchi (@hgsgtk) 2018年4月15日 やり方 対象書籍として、 『Goならわかるシステムプログラミング』 を進めていくことにしました。この書籍を選んだ理由は、Goという言語自体の書き方ではなくGoを使って目的のあるコードを書ける点でした。また、個人的にPHPを触ってると意識しにくい低レイヤの仕組みについて知っておきたいという意図もありました。 Go Conference 2018 Spring の翌週から毎週1回2時間定期的に時間を取って実施しています。 集まってやるということで発表の時間を作ろうかという検討もありましたが、身内でやる分にはまずは各自好きなペースでもくもく進めていく方法で進めることにしています。 また、読書会中の会話や詰まったときの内容をGo言語について話すslackの #golang チャネルにて話しています。ここで各自詰まったところについて話していたりしているので後続の人がそこを見て参考にできるという利点が有りました。 良かったこと 学習のペースメーカーになる 定期的に時間をとることで途中で止まりがちな本の写経をする習慣ができ、週一回Goを触るペースメーカー的な存在になっています。 議論ができる 全員が同じ書籍をベースにやっているので、「このページで使われている関数の内部実装がどうなっているか」等、一人で進めているとスルーして先に進めがちな箇所について疑問を深掘りする機会になっています。 Goに触れた経験のあるエンジニアが増えた 本読書会を期に、「実はGoやりたかった」というエンジニアがGoを書き始めました。 元から社内slackに #golang チャネルというGo言語について話すチャネルがあったのですが参加者が7人くらいで更新頻度もあまり高くありませんでした。 しかし、読書会を通じてGo言語に触れたエンジニアが増え#golangチャネルに人が増えGoの話題について話す機会が増えました。Go言語のイベントの共有や「MySQLドライバをGoで書いてみたら面白そう」といった、こういうチャレンジをしたら面白そうと言った会話がslackでされています。 まとめ プロダクションでGoを採用している箇所はスポット部分に限られるので、この読書会を通じてGoをプロダクション環境や趣味のプロダクトに適用するきっかけになればと思っています。 読書会やもくもく会といった勉強会を始める際どういう風に運営するかみたいなことを考えて止まってしまうかもしれないですが、やってから考えるくらいの気持ちで始めてみてはいかがでしょうか?
iOSエンジニアの 大木 です。 日々の開発で、ちょっとした微修正でメソッドを追加・削除すると、差分コンパイルが効かずビルド10分待ちとなり、開発効率の低下が問題となっていました。それを解決するためEmbedded Frameworkを導入したところ、差分ビルドが成功し1-2分になったというお話です。 私が入社したのは、2017年の2月でした。そして、アプリはもともとObjective-Cで書かれていて、Swiftコードの占める割合は10%以下だったようです。それから1年近くで、90%以上Swiftコードに置き換えました。 その時点で、ビルドにかかる時間の長さが目立つようになり、ちょっとした不具合の調査や微調整しているだけなのに10分近く待たなければいけないケースが目立つようになってきました。 問題としては以下の2つがありました。 1つのメソッドを追加しただけなのに、何故か10分近くかかるため、微調整するのに不向き タイプミスしたので一旦途中キャンセルして修正し再ビルドしたが、差分ビルドが効かず10分待ち 前提 日々の開発で行うDebugビルドを対象としています。そのため、AdHoc配信などでビルドする場合のケース(Archive)はCircle CIでおこなっているため特に対象にしていません。差分ビルドが効くようにして開発効率を改善していきます。 環境 MacBook Pro (15-inch, 2016) macOS High Sierra(10.13.4) Xcode 9.2 Swift 4.0 Swift:Objective-Cの比率は、9:1 ビルド対象のSwiftファイルは約850ファイル 一般的なビルド設定やSwift-Objective-Cのブリッジングに対する改善はすでに行なっていた New build systemを使っている 改善の流れ ビルド時間計測 ビルドが遅くなっている原因コードを探して修正する これ以上速く出来ない? コードをモジュール単位に分割 差分ビルドが効くようになり、開発効率改善! ビルド時間計測 パイク: Cプログラミングに関する覚え書き にもある通り、推測するのではなく、どこで時間がかかっているか計測してみます。 XcodeのBuildSettingsの OTHER_SWIFT_FLAGS に -Xfrontend -debug-time-function-bodies を追加すると、閾値以上のビルド時間がかかっているメソッドを、Xcode上でWarningレベルで報告してくれます。 ちなみにBASEアプリでは、App Extensionsを使用していることもあり、設定の抜け漏れを防ぐため、XcodeのGUI上ではなく、 xcconfig ファイルで設定しています。 // Debug.xcconfig #include "Shared.xcconfig" #include "DebugPlugins.xcconfig" SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG; // 計測用フラグ OTHER_SWIFT_FLAGS = -enable-bridging-pch -Xfrontend -debug-time-function-bodies -Xfrontend -debug-time-compilation -Xfrontend -warn- long -function-bodies= 200 -Xfrontend -warn- long -expression-type-checking= 200 ; GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG= 1 ; 計測結果が含まれるビルドログはXcode上で全ては見えないのと、出力されたファイルを読み解く時間が惜しいという場合は、 BuildTimeAnalyzer-for-Xcode を使うとよいでしょう。 ビルドが遅くなっている原因コードを探して修正する さて計測結果を眺めてみたところ、注文内容と支払い情報を送信するためのHTTPBodyの生成structを返す処理で10秒前後ビルドにかかっていました。 その処理は、Structに (String, Any) のタプルの配列で必要なパラメーターをセットしているのですが、型情報がわからないままなため時間がかかっていたようです。 そこで、一旦変数に格納することによって、型推論が効くようになり、ビルド時間も大幅に改善しました。 diff --git a/CartClient.swift b/CartClient.swift index 9e2b4c060..93a882f19 100644 --- a/CartClient.swift +++ b/CartClient.swift @@ -42,6 +42,11 @@ class CartClient: APIClient { case .checkout(paymentType: let `type`, payment: let payment, cart: let cart): let orderRequestInfo = cart.orderRequestInfo + // 一旦変数に格納して型情報を用意する + // 住所とメールアドレスは必須なのでforce unwrappingで取り出しても問題ないはず + let familyName = payment.shippingContact!.name?.familyName ?? "" + let givenName = payment.shippingContact!.name?.givenName ?? "" + let tel = payment.shippingContact!.phoneNumber?.stringValue ?? "" return Parameters([ ("payment", type.paymentMethod), @@ -49,14 +54,14 @@ class CartClient: APIClient { ("item_id", orderRequestInfo.itemId), ("shop_id", orderRequestInfo.shopId), ("variation", orderRequestInfo.variationId), ("amount", orderRequestInfo.amount), - ("last_name", payment.shippingContact!.name?.familyName ?? ""), - ("first_name", payment.shippingContact!.name?.givenName ?? ""), + ("last_name", familyName), + ("first_name", givenName), ("postal_code", payment.shippingContact!.postalAddress!.postalCode), ("state", payment.shippingContact!.postalAddress!.state), ("address", payment.shippingContact!.postalAddress!.city), - ("address2", payment.shippingContact!.postalAddress?.street ?? ""), - ("tel", payment.shippingContact!.phoneNumber?.stringValue ?? ""), - ("mail_address", payment.shippingContact!.emailAddress ?? ""), + ("address2", payment.shippingContact!.postalAddress!.street), + ("tel", tel), + ("mail_address", payment.shippingContact!.emailAddress!), ("version", appVersion())]) default: break 改善結果は以下の通りです。 コンパイル時間 内容 9323.63ms 元のコード 265.4ms(-97%) 追加する値を一旦変数に保存(Optionalのまま) 16.6ms(-99.8%) さらにNonOptionalにして保存 他にも修正していくらか改善しましたが、劇的な改善には至りませんでした。 if boolValue = true { のような比較をやめて if boolValue { に変更して中身の判定を行う 条件に一致したコレクションの要素を取得するメソッド filter() を使うだけで条件を満たせるのに compactMap() 使って余計にビルド時間がかかっていた。 100ms以内に収まり喜んだのもつかの間、再度ビルドすると200ms以上時間がかかってしまう問題もあり、これ以上は誤差でしかないと考え修正を打ち切りました。 これ以上速く出来ない? Swiftコードコンパイル時間短縮も大事なのですが、最初に述べた 差分ビルドが効くようにして開発効率を改善 達成するのが第一の目標です。 考えてみると、全てのソースファイルをアプリケーションターゲットでビルドするのは無駄です。APIからデータを取得するコードやデータモデルなどは、最初に定義してしまえばほとんど修正することはありません。 モジュールとして切り出せば、関係ないコードのビルドはFramework側で行うことになり、ビルド時間の大幅な改善が見込めそうです。 iOSには、Cocoa Touch Frameworkとしてコードを共有する仕組みがあります。さらに、Embedded Frameworkという仕組みを使ってXcodeのワークスペースに直接組み込むこともできます。 コードをモジュール分割 Embedded Frameworkを使って、コードをモジュールとして分割していきます。 レイヤー化アーキテクチャー等で、各々理想のアーキテクチャーはあると思いますが、ビルド時間の解決に使うことにします。 iOS SDKにのみ依存し単体で動作する共通コード アプリで定義したデータモデルやAPIクライアントコード等、複数箇所で利用し変更が頻繁に発生しないコード 上記両方に共通していえることですが、設定ファイルの読み込みが必要な部分はアプリケーションターゲット側で読み込み、その結果オブジェクトをフレームワーク側で定義したデータモデルに変換して渡すようにした方がテストもしやすいですし、後々依存関係のエラーで悩む必要も無くなります。 また、再利用可能にすることを考えると、APIのエンドポイントやストレージのパスなども、アプリケーションターゲットから渡すようにした方が良さそうです。 最終的には下記のようにモジュール分割しました。 UIExtensionKitとAppKitという名前のフレームワークを作成し、アプリケーションターゲットはそれをインポートして使うようにしました。 frame "Application Target" { [AppContext] [App] } package "UIExtensionKit" { [Extensions] } package "AppKit" { [AppContext Interface] [Registory] [Data Structures&Logics] } [App] . [AppContext] : Init [App] ... [Extensions] : Use [AppContext Interface] <|.. [AppContext] : Implement [Registory] *- [AppContext Interface]: DI [Data Structures&Logics] . [Registory] : Use [App] . [Data Structures&Logics] : Use 図のApplication Targetというのはアプリケーションのエントリーポイントとなるターゲットです。アプリを起動する場合はこのターゲットを起動します。 UIExtensionKitは、iOS SDKでのみ実装してアプリの業務ロジックは含まないそれ単体で動作するコードです。カラーパレットの定義やViewコンポーネントが配置されているセルのIndexPathを取得する便利メソッドなどがあります。 AppKitは、アプリで定義したデータモデルやそれらを操作するビジネスロジックなどを含みます。 APIリクエストのクライアントだったり、APIのレスポンスを加工したりローカルのストレージにシリアライズしたデータを保存したりするコードがあります。 結果 改善結果は以下の通りです。差分ビルドの結果は、 UIViewControllerに viewWillAppear(_:) を追加してビルドした結果です。 コンパイル時間 ビルド種別 内容 494.4s<約8分> フルビルド 分割していない元のプロジェクト 247.055s<4分8秒> フルビルド フレームワークを使ってモジュール分割したプロジェクト 232.8s<3分52秒> 差分ビルド(成功時) 分割していない元のプロジェクト 86.924s<1分27秒> 差分ビルド フレームワークを使ってモジュール分割したプロジェクト まとめ 差分ビルドが失敗してビルドに時間がかかるという場合は、Frameworkを使ってモジュール分割してみたら改善できたというお話でした。これによって、無駄に残業して仕事をすることもなくなり、効率よく開発できるようになったと思います。 おまけ(モジュール分割に関して) モジュール分割に対応するためには、技術的な課題がいくつかありそれを解決しなければなりません。 ざっと挙げますと アプリケーションターゲットでしか使えないもしくは実体が存在しないオブジェクトやメソッドが存在し、Frameworkやテストターゲットから利用したい場合に簡単にアクセスできそうにない フレームワーク内のコードを動作させるには、アプリケーションターゲットに配置した設定ファイルを読み込む必要がある Objective-Cで書かれた広告や解析SDKを利用する場合、複数のモジュールでインポートすることができない 今回の場合フレームワーク側は、一方的に利用される側なので、何らかの方法でこういった依存関係を解消する必要があります。 それには、依存関係逆転の原則(DIP)という方法を使いこの問題を解決しました。フレームワーク側にAppContext Interfaceという抽象インターフェースを定義することにより、アプリケーションの詳細に立ち入らず依存関係のエラーを防ぐことができるようになりました。 フレームワーク側に用意する抽象インタフェースの例は、下記のようなコードです。 これを、アプリケーションターゲットやテストターゲットが実装すれば、フレームワーク側は抽象インタフェースを通して処理を行えるようになりました。不必要にSDKをインポートして依存関係のエラーになることもありません。APIのエンドポイントも渡すようにすれば、切り替えのたびにフレームワーク側をビルドし直す必要もありません。 // AppKit.frameowrk public protocol AppContext { var apiConfig : APIConfig { get } var tracker : Tracker { get } var legacyImageSliceFetcher : LegacyObjCImageSliceFetchable { get } var bundleResolver : BundleResolvable { get } var keychainResolver : KeychainResolvable { get } var realmContainer : RealmContainer { get } var legacyAPIBridging : LegacyObjCAPIBridging { get } var appConfig : AppConfig { get } var traits : Traits { get } var userDefaultsResolver : UserDefaultsResolvable { get } var activity : Activity { get } } public enum TrackerActions { case setUser(userId : String ?) case error(Error) case errorWithAdditionalUserInfo(Error, userInfo : [String: Any] ?) } // FabricのCrashlyticsはFrameworkの中に重複してリンクできないので、アクションを通じて処理を行わせる(処理の詳細実装自体はこのフレームワークを利用する側にある) public protocol Tracker { func dispatch (action : TrackerActions ) } フレームワークを利用する側(アプリケーションターゲットやテストターゲット)は、下記のようにこのインタフェースを実装すれば良いことになります。 // アプリケーションターゲット import AppKit final class AppContext : AppKit.AppContext { var apiConfig : AppAPIConfig = APIConfig() // [...] } // テストターゲット import AppKit final class MockAppContext : AppKit.AppContext { var apiConfig : AppAPIConfig = MockAPIConfig() // [...] } フレームワーク側で解決できない依存関係は、利用する側で抽象インタフェースを実装することにより解決できるようになりました。 しかしこの実体をどう渡せば良いのでしょうか? 下記のようなコンテナをフレームワーク側に用意し、それを利用するようにすればどこからでも呼び出せるようになります。 public struct Registory { private static var context : AppContext ! public static func setContext (_ context : AppContext ) { self .context = context } static func resolve <D> (keyPath : KeyPath <AppContext, D> ) -> D { return context[keyPath : keyPath ] } } これでフレームワークの外側から必要な処理を行えるようになりました。 参考文献 パイク: Cプログラミングに関する覚え書き 実践ドメイン駆動設計 (Object Oriented SELECTION) ヴァーン・ヴァーノン var a = Array.from(document.querySelectorAll("pre.code")); a.forEach(function (pre) { if (pre.attributes['data-lang'].value) return; var xhr = new XMLHttpRequest(); xhr.open("POST", "https://plantuml-service.herokuapp.com/svg"); xhr.onload = function () { if (xhr.status === 200 && pre instanceof HTMLPreElement) { pre.parentNode.replaceChild(xhr.responseXML.documentElement, pre); } } xhr.send("@startuml\n" + pre.innerText + "\n@enduml"); });
(BASEオフィス内の光景) 初めに こんにちは!BASEでBack-end Engineer Groupに所属している 菊地陽介 です! 今年度からBASEでは世のエンジニアに興味を持ってもらおうと、積極的に技術ブログを発信していこうという運びとなりました。本記事を読んで少しでも興味を持って頂けましたらぜひ 私まで ご連絡ください! さて、私はRedashエヴァンジェリストとしてRedashを社内に普及させましたので、その話について書こうと思います。「Redashって何?」、「Redashサーバってどうやって立てるの?」、「Redashの便利機能は?」などの疑問については先に素晴らしい記事がたくさんありますので、そちらを参考にされてください。 この記事では、Redashをどのように社内に普及させたのかということについて述べたいと思います。 背景 私がBASEに入社したのは、今からちょうど一年前の2017年4月でした。 当時は何か新機能をリリースしても出しっ放しで、効果があったのかなかったのかの検証が十分になされておらず、次のアクションに活かせていないなと感じていました。日々、「なんだか知らないけど今日は数値が良いような気がする」といった漠然とした感想を持つだけでした。 ちょうど、前職でRedashを導入したことで社内に良い影響を与えることができたという成功体験があったので、BASEにも導入して普及させようという運びになりました。またRedashを導入することは、数値分析をしやすくなること以外にも様々な効果が期待できるので本記事でご紹介します。 Redashを導入するメリット Redashを導入することで享受できるメリットは多々あるのですが、特に私が一押しのメリットは下記になります。 非エンジニアがエンジニアに依頼していたデータ抽出が不要になる このようなお互い不毛なやりとりけっこう多く行われているのではないでしょうか。 カスタマーサポートのRさん(以下Rさん): 「ショップAの商品情報一覧が欲しいのですが、データを抽出してもらえないでしょうか?」 頑固エンジニアのHさん(以下Hさん): 「はい、どうぞ」 〜3時間後〜 Rさん: 「ショップBの商品情報一覧が欲しいのですが、データを抽出してもらえないでしょうか?(さっき頼んだばかりだし頼みづらいな、、)」 Hさん: 「はい(またかよ)、今忙しいのであとで渡します」 〜3時間後〜 Rさん: (依頼してから3時間経ったけどまだ忙しいのかな、それとも忘れてるのかな、リマインドした方がいいのかな、、、。あー、めんどくせええ。) それがRedashが普及するとこんなふうになり、業務効率化がはかれます。 Rさん: 「ショップAの商品情報一覧が欲しいのですが、データを抽出してもらえないでしょうか?」 Hさん: 「Redashに任意のショップの商品情報一覧を取得できるクエリを用意したので、これからは自分で抽出できます。」 〜以降〜 Rさん: 「ショップBの商品情報一覧が必要だな。Redashでさくっと抽出しようっと。あー気を遣う必要がなくて楽チン。」 Hさん: 「前まで頻繁にきてたデータ抽出依頼がこなくなったからコーディングに集中できてハッピー。」 BigQueryの高額請求に怯えなくてよくなる BigQueryはクエリが処理したデータ量に応じて課金されるため、うっかり 月に150万溶かす という恐れがあります。それ故に気軽にクエリを叩けないという人も多いのではないでしょうか。私もその一人でした。 しかしRedashでは、強制的にBigQueryのコスト上限設定を適用できるため、不用意にコストのかかるクエリを叩く心配がなくなるのです。 ちなみにこれが前職でRedashを導入した理由でした。 Redash普及のためにやったこと Redashが導入された当初、BASEのslack上にRedashで作ったグラフをアップしたりしてその素晴らしさを共有しようと努めました。しかしみんなの反応は薄く、兼ねてよりRedashに興味を持っていた一部のメンバーが使うだけでした。 世の中には便利なツールがたくさんあるにも関わらず、社内全体に普及するようなツールは少ないように感じています。そもそも新しいツールが普及しない理由としては下記のようなものが挙げられると思います。 そもそも存在を知らない 存在は知っているけどなんだか難しそう 一部の人しか使ってないから使えなくても大丈夫 そこで私はマーケティング理論で有名な「キャズム理論」に思いを馳せ、どうやってキャズムを超えるか考えました。 キャズム理論を応用する 消費者(社員)は下図の5つのグループに分類でき、アーリーマジョリティー層まで普及させることができれば新商品や新サービスは急激に市場に浸透していくと言われています(これはイノベーター理論というやつ)。 (出典: 【キャズム理論】マーケティングの深い溝を乗り越えるには? より引用) しかし、アーリーアダプター層とアーリーマジョリティ層の間(エンジニアと非エンジニアの間)には簡単に超えられない深いキャズム(分かり合えない価値観)が存在し、そこをどうやって超えるかというのが新技術(Redash)が普及するかどうかの大きなポイントになります。 (出典: 【キャズム理論】マーケティングの深い溝を乗り越えるには? より引用) そこで私は、いかにキャズムを超えるかというところに注力しようと心に決め、各部署のコアとなるメンバー(アーリーマジョリティ層)に売り込みにいくことにしました。 ちなみに私が思い描いたイノベータ理論の層と社内のグループの対応表は下記になります。 層 社内のグループ イノベーター層 元からRedashに興味を持っていたメンバー アーリーアダプター層 エンジニア アーリーマジョリティー層 Customer SupportやMarketing、経理・財務のリーダー レイトマジョリティ層 Customer SupportやMarketing、経理・財務のメンバー ラガード層 Customer Experienceのメンバー どのようにRedashを売り込んだか 私は他部署の人がRedashのメリットを最も享受できるのは先に述べた↓であると考えていました。 非エンジニアがエンジニアに依頼していたデータ抽出が不要になる ですので最もデータ抽出を依頼する頻度が高いCustomer Supportのコアメンバーに、定番のデータを抽出するクエリを全て用意して売り込みに行きました。 やはり普段データ抽出依頼にストレスを感じていたようで、すぐにRedashを利用することのメリットを理解してもらえました。同様に普段の業務の中でマーケも法務経理もエンジニアへのデータの抽出依頼が度々発生していたので、各部署のコアメンバーに同じ切り口で売り込みにいき、布教活動に勤しみました。 その後は、私が何も働きかけなくても自動的に社内のあらゆるメンバーに普及していきました。「キャズムを超えた」と感じた瞬間でした。 まとめ Redashが普及した後は「新機能をリリースしたら必ず効果検証しよう」という意識が根付きました。今ではより質の高い効果検証をするためにはどうすれば良いかということをみんな意識するようになり、組織として一段階レベルアップしたなと感じています。他にも「非エンジニアがエンジニアに依頼していたデータ抽出が不要になった」などの業務効率の改善をはかることができ、Redashエヴァンジェリストとしてはやりきったなという思いです。 今回はキャズム理論を応用させてRedashを社内に普及させたかということについて書きましたが、これは他にも応用が効くと思いますので参考にしていただければと思います。 おまけ 社内で新たに一ヶ月で登録されたクエリの数と累積数 Redash普及率80% Redashのアカウント開設数を調べたら80でしたので、BASEチームが約100人であることを考えると普及率は約80%でした。
こんにちは。 ショッピングアプリ「BASE」 のiOSアプリを担当している 竜口 です。 背景:あの改修の効果測定用のログ、送られてる? ショッピングアプリ「BASE」内で、施策の効果測定やKPIの経過観察で様々なログを使用しているのですが、細かい改修などで特定のログが送られない事象があり、効果測定が出来ずに多部署の作業が止まるということがありました。 そこでアプリ(今回はiOSのみ)でログが正しく送られていることを保証するために、ログのテストをするようにしました。 全体の流れ 次のことをしました。 EarlGreyでUITestを実装 テストの中で送られるログをMockに保持 テスト完了後、Mockにあるログの有無を確認 テストをCircleCIで自動化 これで改修によるログの影響を保証出来るようになります!! 1. EarlGreyでUITestを実装 EarlGrey Reference は、Googleが作っているiOSのUI automation test フレームワークです。 書き方は、簡単で以下の例の場合 UIButton.accessibilityIdentifier に特定のIDを指定して、テストでは selectElement で指定したIDの要素を取得し、要素の有無の確認、要素をタップ等できます。 class ViewController : UIViewController { override func viewDidLoad () { super .viewDidLoad() button.accessibilityIdentifier = "BTN" } } class UIAutomationSpec : XCTestCase { func testTapButton () { EarlGrey .selectElement(with : grey_accessibilityID ( "BTN" )) // 要素を指定 .assert(grey_sufficientlyVisible()) // 存在するかを確認 .perform(grey_tap()) // 指定した要素をタップする } } 2. テストの中で送られるログをMockに保持 UITest内でログを送る際に、Mockにログを保存する処理を追加します。 class ViewController : UIViewController { @IBAction func didTouchUpButton (_ sender : UIButton ) { LogManeger.send(log : Log (name : "tap_button" )) } } struct LogManeger { static func send (log : Log ) { isTest { LogMock.store(log) // Mockに保存 } // Logを送る } static func isTest (_ doForTest : () -> Void ) { #if DEBUG if ProcessInfo.processInfo.environment[ "XCTestConfigurationFilePath" ] != nil && enabledUITest { doForTest() } #endif } } 3. テスト完了後、Mockにあるログの有無を確認 テスト開始前に保証するログを指定して、終わった段階でログが揃っているかを確認する。 class UIAutomationSpec : XCTestCase { override func tearDown () { super .tearDown() LogMock.removeAll() } func testTapButton () { LogMock.addExpect(Log(name : "tap_button" )) // 期待するログを指定 EarlGrey .selectElement(with : grey_accessibilityID ( "BTN" )) .perform(grey_tap()) XCTAssert(LogMock.verify(), "Logが揃っていません。" ) // ログが揃っているか確認 } } 4. テストをCircleCIで自動化 実装したテストをCircleCIで動かすようにして、変更があってもログが送られることを保証できるか確認できるようにしました。 なかでもハマったところがいくつかありましたので紹介します。 ローカル環境では通るけどCircleCIでは通らなかった 一番長く戦った凡ミスです。 ローカル環境で実機確認してテスト通るのに、CircleCIで行うと通らないし何故か画面が真っ黒になっていました。 原因は、ローカル環境とCircleCIで実行している環境が違うので、UITestの結果が環境に依存する場合、結果が変わってしまうことでした。 CircleCIは、どこかの場所でこちらが指定したOS,端末のSimulatorで初回インストールとして起動されテストしています。 なのでローカル環境で確認する時に、CircleCIの環境を揃える必要があり、今回のようにローカル環境で実機確認していると結果が変わる場合があります。 テスト通らなかったのはわかるがなぜ通らなかったのかわからない CircleCIでテスト通らなかった際に、どこの箇所で通らなかったのかはわかりますが、どの画面でなぜ通らなかったかはわかりません。 なので EarlGrey.setFailureHandler(handler: ) で通らなかった時のErrorMessageとその画面のスクショをSlackに送るようにしました。 まとめ まだカバーしてる部分は一部重要機能のみですが、テスト導入し今の所ログの不具合も起こらず、何よりUIの保証も最低限できるので安心して開発できるのが良いです! BASEを日本一のサービスに成長させる仲間を募集しています! BASEでは、Web、アプリ、SRE、データ解析など幅広い職種を募集しています。 ご興味を持った方、カジュアルな面談もできますので、一度オフィスに遊びに来てみませんか? base.ac
こんにちは、BASE株式会社 BASE Product div サーバーサイドエンジニアの東口です。主にサービスの決済部分とPHP・CakePHPのバージョンアップをしています。 CakePHP2.10.9のリリースに対して、 Koji Tanaka さんと Kazuki Higashiguchi がcontributeいたしました。 Release 2.10.9 · cakephp/cakephp · GitHub 自分自身は、BASEに入社してからCakePHPを仕事で使い始めて半年、初のコントリビュートでした。備忘録としてコントリビュートに至った経緯を残していきます。 何をしたのか Release 2.10.9 · cakephp/cakephp · GitHub を引用すると下記のBugFixを行いました。 CakeTestCase::getMockForModel() now correctly handles secondary connections. github.com だいたいの流れ きっかけは、テストコードを書いていた際になぜか意図しない挙動をすることに同僚が出くわしたことでした。 実際に起きていたことはIssueに書いた内容ですが、テストケース内で、メインではないDBに接続するModelをmockするメソッドを使用した際に下記のエラーが発生していました。 MissingConnectionException: Database connection "Mysql" is missing, or could not be created. Issueを出すところまで、既にプルリクを何度も送っている同僚の Koji Tanaka さんにざっくり教えてもらいながらやりました。おおまかな流れは以下です。 調査 いけそうな修正方針決める Issueを出す 機能修正する テストを修正する Pull Requestを出す マージしてもらう 今回は、既にテストコードが書いてあり通っていたのでなぜ通っているのかという原因調査が少し大変でした。 マージされた phperkaigiの裏でこつこつ出してたcakephp本体へのcontributeが通った^-^ — kazuki.higashiguchi (@Khigashiguchi) 2018年3月10日 今回のバグの原因調査にあたり、中のコード読んだりデバッグログを仕込んだりしているうちにCakePHPの中身が見えてきたのが良かったです。 最後に 今回のcontributeはちょっとしたBug Fixでしたが、今後も自社が使っているOSSが発展していくようコントリビュート頑張っていきます! エンジニア募集中 BASE株式会社では、OSSにコントリビュートしていきたいエンジニアの方募集中です! jobs.binc.jp
BASE Back-End Engineerの田中 ( @tenkoma )です。アプリケーションのPHPアップデートなどを担当しています。 3/14 (水)に開催された技術勉強会ヒカ☆ラボにて、アプリケーションで利用しているPHP/CakePHPアップデートの取り組みについて発表しました。 【 ヒカ☆ラボ 】【Laravel5、CakePHP3など】ベンチャー企業のリアルなPHP事情 : ATND サービスの裏側の話であり、今まで特に公開はしていませんでしたが、機会をいただいたのでこれまでの取り組みを振り返ることにもなると思い、発表することにしました。 BASEのPHPアプリケーションは2016年末までほぼPHP 5.3で動作していました。2017年にPHP/CakePHPのアップデートを推し進めるプロジェクトが立ち上がり、アプリごとに段階的にアップデートをリリースしています。 今回の発表では何を・どの順番でアップデート作業したかをお話しいたしました。同様のプロジェクトを検討中、実施中の方の参考になれば幸いです。 登壇資料 BASE の PHP アプリ アップデートについて // Speaker Deck エンジニア募集中! BASE株式会社ではモダンな開発環境をつくっていくエンジニアを募集しています。 jobs.binc.jp
こんにちは、BASE株式会社 BASE Product div サーバーサイドエンジニアの東口です。主にサービスの決済部分とPHP・CakePHPのバージョンアップをしています。 下記の記事でも公開いたしましたが、BASE株式会社はPHPerKaigi 2018にプラチナスポンサーとして協賛いたしました。 devblog.thebase.in また、私自身もLTを採択いただいたので、スポンサー担当者兼LTスピーカーとして参加いたしました。 PHPerKaigi 2018 phperkaigi.jp  PHPerKaigi 2018は今年初開催のPHPエンジニアのためのカンファレンスです。 TrackA・Bの2つレーンがあり、TrackAがトーク、TrackBはコミュニケーション中心の企画が行われていました。   トークが1レーンだったこともあり、みなさんレベルの高い厳選されたトーク内容でした!また、TrackBで行われた「Interactive Round Table」等のコミュニケーションの場もとても盛り上がっていました。 会場の様子 TrackAを行っていたメイン会場の雰囲気です。 TrackAの様子 スピーカーが登壇する場所には豪華なバックパネル 会場には、PHPerKaigi 2018とスポンサー各社のロゴが乗ったバナーが設置されていました。 会場バナー また、参加者全員にカンファレンスのハンドバッグが配布され、弊社は5周年ノベルティ・ステッカー・チラシの3つを配布させていただきました。 5周年ノベルティ・ステッカー・チラシ LTの内容 私も恐縮ながら「レビューをもらいやすい細かいプルリクの切り分け方」というタイトルで発表させていただきました。 発表中の様子 普段、やっている人は無意識のうちにやっているけどそれに対するノウハウが無いなと感じていたので、改めて整理したという内容です。 スポンサー担当をしてよかったこと ここからは、私個人が申込から当日まで一貫してスポンサー担当をしていてよかったと思うことです。 カンファレンスに対する主体的な達成感がある スポンサー担当者として参加すると、 カンファレンス成功 = スポンサーをしたことの成功 になるので、主体的に取り組むことになると思います。 当日までのスポンサー準備も含めて、カンファレンスが無事終わった際に「やり遂げたぞ!」という達成感がありました。もちろん、運営されているスタッフの方々の大変さに比べれば大変さはそこまでですが、一人の参加者として参加していた時とは違う気持ちの良い達成感が有りました。 ディレクションの経験になる カンファレンスのスポンサー窓口をされている方とのやり取りや配布物などの準備の段取り、制作が必要であればデザイナーチームとの連携が必要になります。カンファレンス当日に向けてToDoを整理して進行していくため、普段開発のみの方であればディレクターと呼ばれるような方が行っているお仕事を体験できます。 最後に 今回は、記念すべき第一回開催にスポンサーとしてPHPerKaigi 2018に貢献出来たことを光栄に思います。 引き続き、PHPコミュニティの発展に貢献していきます。 エンジニア募集 BASE株式会社では、PHPerを募集しています! jobs.binc.jp
Androidアプリエンジニアの鈴木 ( G_devi ) です。 今まで何回かDroidKaigiに参加はしていたのですが、今回の DroidKaigi2018 は初めてスタッフとして参加させていただきました。 その中で、運営・進め方・情報管理・当日の動き・臨機応変な対応など、いろいろとすごいなーとか、このやり方いいなーとか思ったことがあったのでお伝えしたいと思います。(書ける範囲で) すごかったこと・いいなと思ったやり方etc... どれが一番とか選べないので順番は特に関係ないです globalチームがある 簡単に言うと、英訳してもらいたい文をここにお願いすると分かる人が翻訳してくれる感じです。 DroidKaigiは海外からも参加者や登壇者が来ますし、グローバルな対応が必要になってくるので何にでも英語が必要となります。 登壇者とのメールのやりとり、アンケートなど全体に投げるもの、海外サービス(Sessionizeなど)への質問など英語を用いるところはたくさんあるのですが、担当している人が必ずしも英語ができるわけではありません。(自分もできません) slackの専用のchannelで英訳できる人のグループ(@english)宛に、こんな用途でいつまでにお願いしたいという感じで投げると、誰かがシュッと英訳してくれるという初めて見たときは感動するような流れ! 何よりみんなslackを使い慣れているので引用など諸々見やすく投げるし、感謝と謝罪もキッチリしているし、緊急度と重要度をみんながある程度把握しているため流れがいい。 こんなに素晴らしい流れができてる企業とかないんじゃないか?と思ったくらいです。 こういうのほしいなーという話が出たら誰かが作り始める 多分、いろいろ作られてたとは思うけどとりあえずパッと思い出したのは一つ。 github issueのMilestoneからもうすぐ期限が来そうなものをまとめてslackに投げて教えてくれるというもの。 全体で見ると結構な数のissueが作成されてるうえ、期限遵守しなくてはいけないものもあるため非常に見やすく気づきやすく素晴らしかった。 恐らくスタッフの大半が簡単なスクリプトを書いてシュッと投げるくらいのものはそれほど手間なくできると思うので、誰かに作業が集中してしまわないのが素晴らしい。まあ、忙しい人ほど自分で作ってちゃっちゃと済ませちゃうものな気がするけども。 用語集がある まぁ、当然ですよね? ただこれがちゃんと存在してないと認識のズレや時間のムダにつながるのであることはとても重要だと思います。 用語が増えてきてメンテできずに人によって呼び方が違うものとか増えてくる現場にいた経験をするともう。。。 リアルタイムKPT KPTを雑に書き込めるchannelがある。 確かに、思ったことはその場ですぐに記載したほうがいいですね。 DroidKaigi本番中もちょくちょく書き込まれていて、みんな結構slackのチェック頻度が高かったりもあるので、それを見て参考にもできるしとても良かったと思いました。 今更ながら、何でよくあるKPTの集まりでは時間区切って思い出しつつ書き出すことをするのだろうと。。 あのやり方だと後でこれもあったなと思うことが結構あるんですよね。 当然ながらslackに書き込んで終わりではなく、後で別途まとめると思いますが。 担当ごとに書き込むためのissueもありますし。 当日のインカム運用 当日はスタッフ全員がインカムをつけて緊急時の案内や共有などを行っていた。 slackでいいじゃんと思っていたが、結構動き回ってるうえ一般の人よりチェック頻度が高いと言ってもやはりリアルタイム音声連絡の便利さには敵わないものがありますね。 特に参加者から質問などを受けて自分が分からないときなどは急ぐ必要があるので非常に良かったです。 正直、撤収の途中からインカムを外したら不安になるし聞きたいことあるときに不便だなと思いました。 去年は全員がつけていたわけではなかったようなので、うまく改善がされていってるんだなという印象も受けました。 ただ、どうしても聞き取りづらいというのはよくあるので、いずれgoogle glassをつけて追加でも文字でも見れるようになるといいな〜。 2段階認証 当然だがキッチリしている。 当然ながら外に出せない情報もあるので重要なところですね。 何よりまだ設定していない人に対するリマインドがしっかりしていてよかったと思います。 最新情報の重要性の理解度 みんながみんな、情報が最新であることの重要性を強く認識しているため古い情報を見てズレたことをしてしまうという状況がほぼなかったと思う。 特に、セッションの司会や補助の割り当ては当日中にもちょくちょく更新されたので全員が最新情報をきちんと確認する必要があったが、通知もしっかりしていましたが問題なくみんなチェックしていたようでスムーズでした。 あとは、マニュアル類も都度更新されたりするのでそのチェックも重要でしたがデジタル主体の人が多いとなんと楽なことかと。(やはり便利なので印刷したものもありましたが) とりあえず手軽にgoogle docsは便利ですよね。 スタッフ用アプリ なんと、スタッフが利用するアプリも作られていたのです! マニュアル類へのリンク、Slack起動、Twitterで#DroidKaigi検索、各自の行動予定表、各ルームの進行中・休憩中表示の切り替え、そして何よりプッシュ通知でみんなへの連絡事項が文字で送られてくるというのは便利でした。(ちなみに最後の通知は打ち上げ会場についてでしたw) 半分くらい(?)はスタッフ初参加なようなので、このようにこれを開けばなんとかなるというものがあると迷わずに便利に動けて素晴らしかったです。 各種マニュアルの完成度 各種マニュアルの完成度がハンパないです! 上にも書いたように初めての人が多いというのもありますが、とりあえずこれを見れば大体のことは迷わずできるというレベルで作成されていました。 当日のことだけでなく、準備や撤収に関してもマニュアル化されているので非常に動きやすかったです。 しかも、こういうことも必要ではないかという話が出るとすぐに更新されて通知がくるのがまた素晴らしかったです。 いろいろな面で見習いたいところ。 責任の所在が明らか 全体、そして各担当の責任者が明確で(状況によって変わったりもありますが)その人に質問すれば大体何とかなるという状況。 もちろん質問だらけにならないようにいろいろ記載されているものがあるのですが。 何か発生した際の線引(これはやる。やらないなど)がすぐに分かるというのは素晴らしかったです。 まぁ、これもエンジニアらしく反応速度が早いからうまく回ってるというのもありますが。 やばい問題が起こらないように、そして確実に間に合わせるためにきっちりと内容を把握している責任者がハンドリングしているのは素晴らしかったです。 責任者が自分の担当範囲をキッチリ把握している。できない場合でも周りがフォローできるという体制は普通なようでなかなかできてなかったりしますからね。 受付アプリまで作られている ついに1000人規模になったDroidKaigiですが、その人数の受付を回すとなるとものすごく大変なのは目に見えている。 参加者が提示したQRコードをスタッフの受付アプリで読み取ることによってスムーズな受付が可能だった模様。 "模様"というのも、自分は受付時に別の作業をしていたので現場は見ていないもので。。。 ただ、この辺も前回の反省を踏まえて作るという流れがいいですね。 何より特別な機器を用いなくてもスマホのカメラで可能であり、そのアプリをシュッと作れる人たちというのが強い。 ミーティングはハングアウトでも参加可能 スタッフもほとんどの人が普段の仕事をしているうえで夜に集まるので、どうしても参加できない人もいる。 また遠方の人もいるので、行くことはできないけどハングアウトで参加できるというのはすごく便利。 あと、議案・議事録もしっかりしているため参加できなかったとしてもある程度はきちんと把握できるようになっていました。 (自分もいろいろあってあんまりミーティングの場に行けなかったので非常によかった) 直接会って話すというのも重要なのですが、普段テキストベースでのやり取りに慣れている人が多いのもあり、議事録のチェックでも情報格差がそれほど発生しないようになっているのは非常によかったです。 経緯はともかく結果はしっかりとどこかに明記されているので。 そして、それほど回数も時間も確保できるわけではないのでミーティングのための準備もしっかり行われており、極力無駄な時間を使わないようにしていたのが素晴らしかったです。 これも非常に見習っていきたいところです。 急遽対応すべきことが発生したときの流れが良い 1000人もの人を動かすと、予期できなかったことなどが発生し急遽対応を入れたりします。 うまく人を流すために誘導を配置する必要がでてきたなど。 その作業タイミングで手が空いていそうな人をみつけ簡単に流れを説明するだけで動けるのは非常に良かったです。 何か分からないことがあればインカムで聞けますし。 エンジニアだからとは必ずしも言えないですが、ある程度自分で考えて行動できる人が多かったように思えます。 オープニング動画がかっこいい!!! これはしびれた (まぁ、自分は準備でウェルカムトーク出てないし終わってから見たんですが) www.youtube.com その他感想 ひたすらに物理作業がツライ!!! 普段座ってばかりいる自分なので、、前日の電源を机に配置してテープで足をひっかけないように留めていく作業で疲労困憊。 セッションの司会補助で立ち続けてるだけでも脚がプルプル。 撤収作業で疲労困憊。 最初のほうは緊張もあり相当疲れました。 やはり体力は必要だなーと。 朝が早い! と言っても、8:50厳守なので一般的に見たらそんなに早くはないのですが。 自分は朝に弱すぎるうえ、疲労困憊なので本当に心配でした。。 現地が西新宿で家が池袋なので結構近いのに本気で現地近くに泊まろうかと考えていました。 けど、参加者も泊まる人多いでしょうし、そんな近くに宿泊施設が多そうに見えなかったので断念。 少し歩けばいくらでもあるだろうけどそれなら帰っても変わらないかなーと。 最終的に知り合いに電話で起こしてもらうというリスクヘッジをして何とか乗り切りました。 スタッフの顔と名前が分からない。一致しない。 自分がミーティングにあまり参加できなかったというのもありますが、当日結構みんな初めましてという感じが多かったです。 slackやtwitterのアイコンや表示名は分かるけども実際の人と結びつかないというのはよくありますよねー。 人が増えてくると会社でもそうだったりしますよねー。 とはいえ、それでもうまく回るような感じではあったのですが、やはり顔と名前が一致するとだいぶやりやすくはなると思います。 (覚えるの苦手なんですが。。。) あと、みんなほとんどslackの表示名で呼んでいるのですが、どう読むのか分からなかったりw スタッフ打ち上げ会場に人権(ネット接続環境)がなかった… ほぼネット前提であるAndroidのイベントスタッフの打ち上げにこの状況は驚きましたねw 地下だったのもあり電波がほぼ届かず、現地WiFiもなかったです。(あったとしてもこれだけアクティブな人が80人?くらい集まってたらパンクするでしょうが…) まだ外部と連絡とる必要のある人もいたでしょうしせめて繋がる環境だったほうがよかったのでは?とは思いました。 何より自分が欲しかった。。。w 最後に 個人的に非常に為になり、参考になり、楽しかったです。 他にも良かったと思うところはたくさんあるのですが、とりあえずパッと思いつくところを書きました。 やってみたい人や組織運営に悩んでる人なども一度スタッフとして参加してみると為になるかと思います。 元の仕事や準備など諸々で時間がなくアプリ開発のほうには関われなかったのが残念だったので、来年は多少手を付けられるといいなと思いました。 スタッフのみなさん、登壇者のみなさん、そして参加者のみなさん、ありがとうございました!! (文字だけで寂しいのでスタッフ名札写真でも載せようと思ったら無くしてしまったようだ…泣)
こんにちは、BASE株式会社 エンジニアの東口です。主にサービスの決済部分を担当しています。 この度、BASE株式会社は、 PHPerKaigi 2018 にプラチナスポンサーとして協賛いたします。 phperkaigi.jp PHPerKaigi(ペチパーカイギ)は、現在PHPを使用している、過去にPHPを使用していた、これからPHPを使いたいと思っているエンジニアが、技術的なノウハウを共有するためのカンファレンス(イベント)です。 BASEは、サーバーサイドの大部分をPHPを使って構築しているため、この機会を通してPHPのコミュニティ発展に貢献できればという想いから協賛を決めました。 私自身も レビューをもらいやすい細かいプルリクの切り分け方 | PHPerKaigi 2018 というタイトルで発表いたします。 カンファレンス当日は、皆様にノベルティを配布させていただいたり、弊社エンジニアも参加いたしますので、ぜひお気軽にお声掛け下さい。 イベント概要 日時 2018年3月9日(金)〜3月10日(土) 場所 練馬区立区民・産業プラザ Coconeriホール 主催 PHPerKaigi 2018実行委員会 (実行委員長: 長谷川智希 / @tomzoh) 公式HP: https://phperkaigi.jp/2018/
はじめまして、2017年9月に入社したBack-End Engineer の田中です。アプリケーションが使うPHP/CakePHPのバージョンアップを担当しています。 BASE ではサーバーサイドアプリケーションの大部分が CakePHP2 を使って構築されています。 日常的にCookbookやCakePHPコアのコードを読んでいて、時々typoや不具合を見つけてはプルリクエストを送っています。 PHP 7.2 でテストスイートをパスさせる 修正もしたので、PHP7.2 でも動くはずです。 CakePHPユーザーの方で、ドキュメントにtypoを見つけたことはありませんか? ドキュメントへの貢献についてのページ を読むと、まずはメールを送る必要があるようなことが書いてあり面倒そうに思えますが、実際のところプロジェクトがGitHubに移行してからはいきなりプルリクエストを送っても問題ないようになっています。この記事ではドキュメントをPCで修正してプルリクエストを送る方法を説明します。 この記事の内容は2018/02/08にランサーズ株式会社で開催された CakePHPクックブックを直してみよう勉強会 - connpass で解説した内容に多少の修正を加えたものです。 CakePHP のドキュメントを直す CakePHP Cookbook 日本語ドキュメント はボランティアベースで翻訳されています。 翻訳率はかなり高いですが、typoや読み取りづらい部分が見つかることがあります。 CookbookはGitHubでホストされているので、GitHubアカウントさえあれば、修正してプルリクエストすることができます。以下の記事をご覧ください。 3分でできるCakePHP公式ドキュメントの誤訳を直す方法 - Qiita こちらの方法だと、PCにリポジトリをコピーしなくてもよいので、簡単に修正依頼できますが、表示確認ができません。 マークアップやサンプルコードを含めて修正するときは、表示確認したくなります。 その場合はローカルPCで作業する必要がありますのでその方法を説明します。 目次 リポジトリをコピーして作業を始めるまで テキストを修正してローカルビルドする プルリクエストを作成する 補足 リポジトリをコピーして作業を始めるまで 事前の準備として git インストールとSSH鍵の設定が必要です。以下の記事が参考になります。 monsat.hatenablog.com 準備が終わったら、 Cookbook 用のリポジトリにアクセスします。 github.com ページ右上にある Fork ボタンを押しましょう。 そうすると、自分専用のリポジトリが作成されます。 github.com 次にリポジトリをローカルPCにクローンします。 [your-name] はあなたのGitHubアカウント名に置き換えてください。 $ mkdir -p ~/src/github.com/cakephp $ git clone git@github.com:[your-name]/docs.git ~/src/github.com/cakephp/docs 常に最新のソースファイルから作業に着手するために、オリジナルのリポジトリを upstream として登録しておきます。 $ git remote add upstream git@github.com:cakephp/docs.git 最後にDocker をインストールします。ドキュメントをビルドするために必要な環境を簡単に構築できるようになっていますので、手元のPCにインストールしてください。 Docker Store から、お使いのPCのプラットフォーム向けインストーラをダウンロードして、インストールしてください。 https://store.docker.com/search?type=edition&offering=community テキストを修正してローカルビルドする まずは、手元でドキュメントをビルドして、ブラウザで確認してみましょう。以下のコマンドを実行します。 $ cd ~/src/github.com/cakephp/docs $ docker build -t cakephp/docs . $ docker run -it --rm -v $(pwd):/data cakephp/docs make html-ja 2行目の docker build ... でビルドに必要なツールをインストールして、3行目の docker run ... でビルドします。 生成されたWebページは build/html/ja ディレクトリ以下にあります。 build/html/ja/index.html をブラウザで開くと生成されたドキュメントを確認できます。 次にドキュメントのソースを修正します。今回はCakePHP3 Cookbook で見つけたtypoを修正してみましょう。 *1 upstream/3.0 *2 からブランチを作成します。 # オリジナルのリポジトリの状態をローカルに取得します $ git checkout 3.0 $ git fetch upstream $ git merge upstream/3.0 $ git checkout -b fix-ja-console Switched to a new branch 'fix-ja-console' ソースを開いて修正します。ドキュメントソースの reStructuredText形式 に対応しているエディタを使うのがよいでしょう。以下はPhpStormでの例です。 直したらビルドします。 $ docker run -it --rm -v $(pwd):/data cakephp/docs make html-ja 2回目以降は docker build 不要です。 表示が確認できたらコミットします。英語である必要がありますが、typoなら 「Fix typo」でOKです。 $ git commit -am '[ja]Fix typo' プルリクエストを作成する あとは、ブランチをプッシュして、プルリクエストをつくるだけです。 $ git push origin fix-ja-console cakephp/docs: CakePHP CookBook を開くとプッシュしたブランチが表示されるので「Compare & pull request」を押し、「Create pull request」を押すとプルリクエストが作られます。 *3 github.com あとはマージしてもらえるのを待ちましょう。 補足 日本語以外の版もビルドしたい場合は以下のコマンドを実行します。 # 英語 $ docker run -it --rm -v $(pwd):/data cakephp/docs make html-en # すべての言語 $ docker run -it --rm -v $(pwd):/data cakephp/docs make html エンジニア募集 BASEではECプラットフォームを一緒に作ったりCakePHPにコントリビュートしたいエンジニアを募集しています! 採用情報 | BASE株式会社 *1 : typo程度ならローカル作業不要ですが、修正箇所を見つけてないので… *2 : 3系の最新版のドキュメントでも 3.0 ブランチを更新していきます *3 : 2コミット以上のプルリクエストだと、プルリクエストのデフォルトタイトルがブランチ名になってしまうので、「[ja]Fix typo」などに変更しておきます
CTOの藤川です。今回、デブサミにお誘いいただいて登壇させていただくことなりました。 初日 2/15の13:05~13:50の回でE会場とのことです。 BASE社におけるフィンテックへの取り組み 今回お話しようと思ってるプレゼンテーションに類するお話に、以前、情報処理学会の学会誌にフィンテックについての寄稿をさせていただいたことがあります。 フィンテック:3.フィンテックスタートアップのビジネスモデルFintech:3. Business Model of Fintech こちらではさすがに自社の自慢話をするのは難しかったので、freeeさん、マネーフォワードさんのアカウントアグリゲーションのビジネスから始まり、何が重要か?という部分でメルカリさんの事例を挙げさせていただきました。 まだ、当時はメルカリとフィンテックの繋がりというのはあまり着目されていなかったタイミングでの寄稿だったと思っています。 メルカリさんの例で書いていたのは、要はポイント経済圏のことなわけですが、フィンテックと呼ばれるサービスは、Webサービスを通じなんらかしらの経済合理性や集客力を金融事業に結びつけていく試みですので、その部分を抽象化して、沢山の方々のビジネスに対する共感をいただくことは可能だと思います。 なので、そこについては聴講者の方々には、絶対に損をさせないようにしますってのがまず大前提として。 (我ながらタイトルがいまいちでしたね。BASEのPRみたいなタイトルにしてしまったし、何か特別なことをやってるようなタイトルにしてしまった。それでもお申込みが満員のようで感謝です。) もう一つは、確かにBASEのPR面の野望はありまして、採用面接をしている時に、無料ECのプラットフォーム事業者という認識はあっても、フィンテック企業だという認知はほとんど得ておらず、しっかり面接でお話することで、我々の狙いを理解いただくことで会社に興味を持っていただくということが多いです。 逆を言えば、1時間、話をしなければ、我々の魅力を知っていただけてないということになり、それでは転職市場に対しても強烈な機会損失をしていると考えられます。 その部分の認知を変えたいというのもあります。 無料EC自体は、これまで沢山の会社がやってきていますし、ショッピングカートのUIのパラダイムもずっと変わっていないし、敵と思わしき企業はビジネスが完成された会社ばかりで、ある意味、ECプラットフォーム市場はコモディティ化していいると言っても過言でないでしょう。 なのに何故、今更ECなの?となるわけですよね。 わかります。 SNSやゲームなどのネットワーク性が前面に出ているサービスと比べても、ただのツールという感覚が強いのかなと思います。そもそも決済やEC業界が好きだという人でなければ、あまり魅力的なサービスのように見えない部分もあります。 一方でEC業界の玄人のおじさん達に言わせると「こんなビジネススキームで儲かるわけがない」となるわけです。 BASEのサービスデザインにおいて、無料ECという仕組みは情報発信プラットフォームの一つであり、BASEという名前からもなんとなく感じ取れるかもしれないですが、インターネット的な世界を作りたいという社会変革を目指したグランドデザインの方にビジネスとしての主眼があります。 会社としての理念が、「価値の交換をシンプルに。世界中の人々が最適な経済活動を行える社会へ」という抽象概念で語られているのは、そこにポイントがあります。 何故、BASEは無料でいいのか?という部分にこそ、他社の有料サービスが考えているビジネスモデルとは違う思想が存在しており、それこそがフィンテック企業として注目いただいている肝になっております。 僕が面接で使うメタファは、銀行で通帳を作る時の「口座維持手数料」って無料ですよね?それ単体だと銀行だって赤字ですよね?BASEも同じなんですよ、というところなんですが、当然我々は銀行ではないのですから、Webサービス企業としての「BASE」がビジネスとして成立するための世界観を、どううまく伝えられるか、そんなあたりにチャレンジしていきたいと思います。(書いててプレッシャーが...) 要は、フィンテックベンチャーの肝は「ただの家計簿アプリ」「ただのフリマアプリ」「ただのネットショップ構築サービス」「ただの決済サービス」ではないんですよ!、、、、ってところなんで、その辺の整理をしていけたらいいし、そのプレゼンテーションを通じて、フィンテックへの魅力を伝えられて、聴講者の方のビジネスにメリットを提供できれば幸いです。 あ、HRの方で採用ページを作ったそうなので、是非見てみてください! jobs.binc.jp
本記事は古い記事となっております。記事執筆当時と比べてサービスそのものの仕様は変更されており、本内容については無保証とさせていただきますので、本記事をご参考いただくのは自己責任でお願いいたします(本記事の内容については、お問い合わせいただいても対応しかねます)。またセキュリティやApps本来の機能の都合で、予告なく挙動が変更になるかもしれないのでご了承ください。 BASEと連携し、皆様が運営されているWebサービスに商品を販売する機能を追加したい!であるとか、Webサービスのユーザーに簡単に商品を販売する提供し、購入連携を実現したい!というご相談をいただくことがあります。 そのような連携をするメリットとしましては、 商品販売に関するあれこれを実装、維持、管理しなくてもよい。特にセキュリティ管理周りを外注したい! 販売機能がコア機能ではないからプラスアルファの価値を提供したい お金の支払い周りや購入トラブルに関する問い合わせ等が煩雑すぎるから、そこをBASEに外注したい クレジットカード決済やキャリア決済などを提供したい などなど、BASEを活用するメリットがそのまま、皆様のWebサービスに組み込めることが考えられます。 このような取り組みに対する一つのご提案をさせていただけたらと思います。 BASEには、ネットショップに慣れてきたオーナー様が、あれをやりたい、これをやりたいというニーズにお応えするための機能拡張「BASE APPS」というものがあります。 その中に「広告効果測定」という機能があります。このAPPSを使うと、購入者様が商品を購入する際の購入完了画面で、任意のタグ、JavaScriptを実行する機能を提供することができます。 いささか裏技的ではありますが、この機能を流用し、Webサービスとの購入連携を実現しようというのがこの記事の趣旨となります。 購入フローは、こちらになります。 Webサービスは登録済み商品のページURLを通じてBASEショップにリダイレクトする(別窓が良い気がします) 商品の購入完了ページで、Webサービスに購入完了通知を送る。 Webサービスは通知を元に、BASE APIに購入情報を問い合わせて、サービスを提供する というフローです。 材料としては、 皆様のWebサービス BASEのショップ管理画面内にある「広告効果測定」APPS BASE API の組み合わせになります。 ■広告効果測定APPS 広告効果測定APPSは、BASEのショップ管理画面からご利用いただけます。 BASEの商品購入の完了画面に、ここに書いた任意のHTML / JavaScriptが出力されます。iframe内に出力されるのでWebページ経由のWebhookとして皆様のサービスに通知を送ることができます。 購入完了画面のページのURLには、ショップのIDが入っているのでリファラーもしくはJavaScriptでパースすることで、どのお店からのリクエストかも判別可能です。 (なお、購入画面のURLはどこかのタイミングで変わる可能性がありますので、その際は、下記のdevelopers登録のアドレス宛に一ヶ月前ぐらいには、連絡いたします) ■BASE APIを使うためには プラットフォーム様がBASE Developersからアカウント申請をお願いします。 developers.thebase.in BASE APIの利用申請が承認された後、BASEが提供するOAuthログインのコードを書くと、皆様の管理画面から商品を売りたいユーザさんに対して、BASEショップとの連携機能を追加することができます。 ユーザさんからOAuthを通じてBASEショップのアクセス権が移譲されることで、 商品登録 / 削除 / 在庫数の変更 購入者情報の取得 などができるようになります。これらの処理を自動化することで、商品購入と連携して購入者様にサービスを提供することができるようになります。 (そのため、商品を売りたいユーザさんには予めBASEショップを開設いただくことが必要です) ■本機能実現についての検討事項 以上のようにシステム連携自体は簡単にできますが、いくつか検討すべき事項があります。 エンジニアをかかえるWebサービス様が販売責任を負う形で商品を販売されるのであれば簡単です。 それに対して、皆様のユーザーにこの機能を提供しようとすると、誰が何をするか?という部分が少し煩雑になります。 検討部分として、整理すべきポイントは以下のようになります。 BASEアカウント保有者 = ショップオーナーさんの責任 - 特商法の記載(販売主体 / 返品責任は誰が追うか?) - お金の流れ(BASEから振り込むので済むなら簡単だけど...) - 購入者情報の管理(個人情報管理) システム連携に必要な作業 - 広告測定appsの設定 - 連携タグの設置 API連携して、サービス提供に必要な機能 - 商品の登録、削除、更新 - 購入者情報の取得 ( その他 APIのドキュメント ) 本文章の内容としては以上になります。 今後、より高いニーズに応じて機能追加も検討していこうと思っておりますが、ひとまずこちらをご検討くださいませ。
BASE本社で12月19日にPHP Wayというイベントを開催しました。 PHPで成長したWebサービスを他の言語に移行させる話題を見ることがありますが、PHPを使い続ける企業がどのようなことを考えて、その選択をしているのか?ということを共有するイベントでした。 どこか自信を見失いがちなPHPの利用について、適切に状況判断するための材料を共有し、PHPを使うサービスにエンジニアとして関わっていくにあたって無駄に悲観的に思わないようにするのをイベントのゴールとして設計しています。 (左からコネヒトCTO島田さん、BASE藤川、サイバーエージェント SGE CTO 白井さん) BASE社の発表資料はこちらです。 20171219 / phpway / BASE,Inc. from 真一 藤川 一度採用した開発言語、実行環境やフレームワークは、一定のライフサイクルの後に、それを採用していることそのものが技術的負債と呼ばれるようになりがちです。これは技術の進化やセキュリティトレンドが存在する以上、インターネットに繋がるシステムとして避けられない部分でもあります。 一方で、お客さまやお客様のお客様に向けてサービス性を実現するために「維持する」という行為もエンジニアリングという視点では重要で、その部分がネットでは軽視されがちかなと思う風潮もあります。その中で、PHPはちゃんと進化していて、長く使えるという面でも柔軟性が非常に高い言語だと思います。 このような部分は、もはやベテランは当たり前すぎて語らないってことなのかもしれませんが、キャリアの浅い若手エンジニアだと日常のアテンションについつい関心を持っていかれがちになって、大切なことを見失うこともあるかなと思うこともありますので、あえて当たり前のことを共有しておくのも大切かと思いました。 ネットで発言する開発者が、全員がサービスのような毎日継続的に開発をする人だけじゃないですし、その場その場で新しい技術を使い分けていくことで自身の商品性を保ってる人たちもいると思うので、新しいものは後出しなんだから素敵な仕様なのは当然であるという1つの価値観だけで開発技術を捉えるのではなく、それぞれの論点をちゃんと整理して一番大切なものは何なのか?が共有できるといいなって思いました。 そういう意味で、今回ご登壇いただいたコネヒト CTOの島田さん、サイバーエージェント SGE CTOの白井さんにはWebサービスやソーシャルゲームという、一度当たるとライフサイクルが想像以上に長いサービスを運営していくにあたっての技術選択基準についてお話いただきました。そこにPHPの選択も決して間違っていないということがご理解いただけると幸いです。 BASE社は、今後も「ちゃんとPHPを使うこと、あらゆる技術を適材適所で使うことを身に付け、時代の変遷に対して正しく技術選択ができるエンジニアを成長させ、どこのWebサービスの会社にでも転職できるエンジニアを育てる」ことを社員のスキル形成を前提として、彼らがそれでもBASE社で働く喜びを持ち続け、共に戦っていけるように、しっかりサービスを成長させていきたいと思っています。 関連リンク: コネヒト CTO 島田さんの記事 tech.connehito.com サイバーエージェント SGE CTO 白井さんの発表資料 ameblo.jp
初めまして、BASE iOS エンジニアの大木です。 6/5-6/9の日程で行われたWWDCに参加してきました。 初参加だったのですが、いつもの業務から離れてiOSの技術的な内容を聞いたり同じiOSエンジニアと議論したり大変有意義な時間を過ごせました。 発表内容は他の方やメディアがまとめていると思いますので、ここではそこにはあまり触れず雰囲気をお伝えできればと思います。 会場について 宿泊先 初日 セッション Lab The Bash トラブル 荷物検査の後、iPhoneを失くしかける ホテルの差し込み型のカードキーでドアが開かなくなる ホテルのWi-Fi/ポケットWi-Fiが繋がりにくくなる まとめ 会場について 今回はSan Franciscoではなく、San JaseのMcEnery Convention Centerで開催されました。 前日から会場にチェックインできるので、夕方に現地に着き宿泊先に荷物を置いてから会場に向かいました。 もっと前から現地に行き観光に行った方もいたようで、若干羨ましかったです。 会場の外でチェックインして、バッチをもらいました。 前日に備え腹ごしらえをするために歩き回るGuys 宿泊先 Appleからメールで送られてきた予約フォームにあった会場に近いThe Fairmontというホテルに宿泊しました。 会場へはゆっくり歩いても、徒歩5-10分以内につくので大変助かりました。 http://www.fairmont.jp/san-jose/ www.fairmont.jp ホテル下にMUJIも! Tech Museumも近い! Singleで予約してもベットは2つ! 初日 前日の腹ごしえの時に徹夜しなくても入れるという情報をもらったので、宿泊先から会場へ歩いていけることもあり、明るくなってから列に並びました。 一応朝4時ごろに目が覚めたのですが、列に並び始めたのは6時でした。 建物は朝7時くらいに開場され、スタッフに拍手で迎えられました。 入場すると朝食が振舞われていたので食べつつ、ホールが開くのを待ちました。 真ん中の一番後ろの席を確保 Keynote、Platforms State of the Unionを観た感じ、VRKit/ARKit/Core ML、App StoreのリニューアルやXcode/Foundation/Swiftの改善などアプリを作るための基礎となるものの発表が多かった気がします。 また、Keynote後のランチ中に、WWDCアプリを確認するとセッション内容が更新されていたので、Keynoteの内容についての感想やセッションどこに行くかなどについて話したりしました。 ランチボックス セッション 自分は初日の発表を聞いて下記のことが気になったのでそれらのセッションを中心に回りました。 JSONマッピングのためのDecodableプロトコルって、ダーティーなJSONでも対応できるの? ARKitでマーカーっていらないの? 認識性能は? Core MLって端末上で動くらしいが、機械学習の学習済みモデルってどうインポートするのか? また、画像認識や自然言語処理の特定機能の処理にそれがどう関わってくるのか? UIKit周りのアップデート 動画とスライド、あとサンプルコードもいくつか公開されているので、気になる方はチェックしてみると良いかと思います。 Design and Development Videos - Apple Developer Lab セッションとは別に、Apple社員に質問ができるLabというのもありました。 デザイン系やAppStore系のLabは予約が必要なので早起きする必要があります。 予約フォームは毎朝7時以降に専用のページにアクセスするのですが、私は残念ながら予約できませんでした。 3つほどエンジニアリング系Lab(それしか回ってない)を回ったのですが、UIKit and Collection View Labは、結構並びました。 いくつかした質問紹介すると、 Q. CollectionViewを入れ子で使うことが多いのだけど、これってパフォーマンス的に問題ないの? A. あまりよくないね。CollectionViewは一つにするべきだよ。 このViewはレイアウトを色々カスタマイズできるので、 必要な種類のCellを用意してセクションを分けたりしながらレイアウトするべきだよ Q. UITableViewライクなレイアウトを作成することがよくあるのだけど、 パフォーマンスの観点だとセパレータってDecorationViewで作るべきなの? CollectionViewCellの要素として作るべきなの? A. 今ってどっちで作ってるの? (CollectionViewCellの要素として作ったコードを見せながら) 全然これで問題ないよ! Cell初期化の時に作ってるみたいだしね あと、ApplePayボタンに関して、利用ケース別にどのボタンタイプを使うべきか質問をしにいった時に、 自分が実装したAutolayoutのコードを見て、Appleのエンジニアが Human Interface Guidelines を確認していました。(幅と高さが最低いくつ必要かというのを確認してたっぽい) The Bash 最終日前日には、酒が飲めたりライブ見たり遊具ありのパーティーがありました。 会場近くの屋外広場で開催されてました。 開場直後のステージ Fall Out Boyが演奏してました。チケット手に入れる手間もなく観られてよかったです! https://itun.es/jp/pwyTb www.setlist.fm トラブル 荷物検査の後、iPhoneを失くしかける 荷物検査の後、ゲートに移動したらiPhoneがないことに気づきました。 荷物検査のところで失くした場合、係員に伝えると探してくれます。 iPhoneの場合、指紋認証でアンロックして写真アプリを開いて、自分の写真を見せるように言われるので、セルフィーなど一枚残しておくと良いです。 その時、別のiPhone持ちながらiPhone失くしたって伝えたので、「持ってるじゃない」言われたりしました。 ホテルの差し込み型のカードキーでドアが開かなくなる Fairmontホテルは、差し込み型のカードキーなんですが、滞在2日目の夜くらいから開けにくくなったのでフロントにいって交換してもらいました。 その時、ID(旅行客の場合パスポート)を見せるように言われたため、パスポートは肌身離さず持っておいた方が良いと思いました。 ホテルのWi-Fi/ポケットWi-Fiが繋がりにくくなる WWDCの会場にいる場合は良いのですが、ネットに繋がらないと色々辛くなります。なのでプリペイドSIMを出発前か現地到着後に購入しておくと良いと思いました。 現地調達する場合、T-MobileストアとかSIMを購入できる場所がどこかにあるはずなので見つけて購入すると良いでしょう。 下のような感じで色々と辛くなります。 Labの予約ができなくなる 宿泊先が遠いのに、Uber/Lyftが呼べない はぐれる まとめ 滞在中は、サンノゼ空港から宿泊先のホテルまで、$26と吹っかけられたり、(スルーしてLyft使いました$15.5) 下記のイベントに参加したり www.meetup.com スポーツバーで、偶然NBAの試合見たりしました。 Warriorsが逆転勝利して盛り上がってました! Geo Blocked | NBA.com 色々ありましたが、WWDCに参加できて本当によかったと思います。 まだいったことがない人がいれば是非参加することをオススメします。
BASE CTOの藤川です。 リブセンスさんが運営されている転職ドラフトという転職サイトで、全員のプロフィールを読んでいて薄々気がついていたことに改めて気がつかされたのですが、BASEの方でサーバサイドに使っているメインの技術はCakePHPというフレームワークでありPHPの技術なのですが、 新卒の就職先がRubyを使っていて、今、25〜27歳ぐらいになっている若手エンジニアにPHPの経験がない人が増えている! という大きな問題にぶちあたりました。我々は転職いただく方の前職については、いくつか期待している流れがあります。決済、EC視点ではEC-cubeなどでPHPを扱っていた会社からの転職組というのが重要な人材供給源だったりするのですが、それ以外に「モバイル、スマホアプリ、最強のUX」というスタートアップ的な視点においては、モダンなスタートアップのサービスに携わっている経験は魅力的です。(別にそれだけで採用したりはしませんが) つまり第二新卒などの若手エンジニアを採用しようとすると、仕事道具としてPHPを今更学んでいただくところから始めなくてはいけません。 最近、Railsで作られてるマストドンを個人で運営している関係で、がっつりソースコードを見ていますが、ビジネス要求に照らし合わせると、ぶっちゃけRailsもCakePHPもできることはあんまり変わらないわけです。 もちろんRailsは高い生産性を発揮する素晴らしいプロダクトで、作っていて楽しいWAFだとは思いますが、スケールしたシステムにおいて抱えている問題解決の会話において、言語やWAFの重要性は、割とどうでもよくなってくる。 つまり、起きる問題が言語の問題ではなく、アーキテクチャに起因することの方が多いので、日々の問題解決において話している内容は言語の話ではありません。 マストドンがRailsなのも、改めて考えてみれば、丁度10年前のツイッターと同じなわけで、このプロダクトは、N+1問題を抱えたタイムライン型プロダクトとして超勉強になって、あぁこうやってタイムラインは遅延してたのねということが実感できます。 ですが、Twitterのその後が脱Railsをしたことも加えると、成長するサービスにおいてフルスタックなフレームワークが寄与する範囲は多くはないかなと思うわけです。 (それは僕らの問題としてCakePHPにも全く同じことが言えるわけです。つまり僕らは僕らでCakeに依存してはいけないわけですね。やっぱり生産性の高いWAFはどこかで効率性を引き換えに利便性にロックインされてしまうので地味に脱するのは難しいんですよ。) またクラウドの発達でAWSやGCPなどを素早く扱えることの方が重要性を増してきて、Web開発言語はどこかグルー(糊)としての役割のほうが大きくなってきました。 ということで、我々としては20代の若者に対しては、言語を学ぶことが重要なんじゃなくて、Webをとりまくアーキテクチャをしっかり知っていくことが大切なんだ!という言葉と、メルカリさんだってPHPでしょ?SlackもFacebookもPHPでしょ!?成功するサービスにPHPは少なくないんだよ!というポジショントークを交えながら、「正しいWebエンジニアのあり方」を説くわけです。 そして、その先にある、「じゃあ、2017年の段階で、PHPを学ぶにはどうしたらいいですか?」という質問に答えるための記事がこの記事です。 とりあえず、なにやら最新情報はないものかとTwitterとマストドンに投げてみました。 内定者にphpのいい本ありますか?と聞かれて、うずらさんのYAPCの資料を検索してくださいとお伝えするなど。 — えふしん (@fshin2000) 2017年4月26日 「うずらさんの資料」とはこれです。 ■半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情(5.6対応)(YAPC公式サイトの紹介) 半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情(5.6対応) - YAPC::Asia Tokyo 2014 2014年のYAPCで、PHPネタでベストトーク賞を取るという伝説のプレゼンテーションですね。 YAPC::Asia 2014 - 半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情 from Junichi Ishida その他、教えてもらったURLを貼っておきますね。 まだマストドンのtootの著作権がはっきりしてないので基本URLだけにします。creative commonsなどで再利用権を主張できるようになるといいですね! ■PHP The Right Way ja.phptherightway.com ■短気なプログラマのためのPHPUnitクックブック https://leanpub.com/grumpy-phpunit-jp ■パーフェクトPHP(書籍) gihyo.jp ■効率的なWebアプリケーションの作り方 ~PHPによるモダン開発入門(書籍) https://www.amazon.co.jp/%E5%8A%B9%E7%8E%87%E7%9A%84%E3%81%AAWeb%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9-~PHP%E3%81%AB%E3%82%88%E3%82%8B%E3%83%A2%E3%83%80%E3%83%B3%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-%E5%B0%8F%E5%B7%9D-%E9%9B%84%E5%A4%A7/dp/4774150827 パーフェクトPHPを書かれた小川さんの本 ■PHP公式マニュアル。 https://secure.php.net/manual/ja/index.php ふつうに良い。おおむねこれで足りる。 ■PHP公式マニュアル https://secure.php.net/manual/ja/appendices.php うずらさん談:「php.netは、付録という名前の本編を全部読んでいただきたい。」 ■PHP入門(とほほのWeb) http://www.tohoho-web.com/php/ ■PHP The Wrong Way http://www.phpthewrongway.com/ うずらさん談:「ひねくれた(?)人なら、the wrong wayもよむべき(?)」 道具としては若干枯れた印象も強いPHPですが、歴史も長い言語なので、それ故にモダンな開発言語で意識されるような「正しく扱うスキル」が求められます。 他にもオススメの本やWeb記事がありましたら、是非はてブで教えてください! 追記 :この記事を読んでいただいた方から、おまえPHP7のやる気ないだろ!って感想をいただいたので、全然そんなことないですよ!ってことを書いておきます! 今回の話は、若い人がPHPをネガティブに捉えたまま近寄らない事態になるのを防ぐために、あえてそっち側に寄せて書いてあります。それ以上に今回、記事を書いていて思ったのは、書籍などの大きな情報発信について、2014年ぐらいを境にPHPに関する情報発信が途切れているイメージを持ったこと。 それでは「新しい人」が入ってこないのも当たり前なので、盛り上げていかないと!自分たちもPHP7.xのムーブメントは大切なことなので、情報発信も含めた技術的チャレンジをちゃんとやっていき、あわよくばPHPを使っているエンジニア業界を牽引できるようにしていきたいと思っています!そういう部分も含めて、一緒に将来を作っていっていただける方を募集しております! www.wantedly.com
こんにちは。BASEでAndroidアプリ開発をしている鈴木です。 https://twitter.com/G_devi 突然ですが、デザイナーからこんな感じにしてくれと頼まれたことはありませんか? GridLayoutですね 一番上のヘッダー部分が全幅なうえ、Grid部分の余白を全部同じサイズに調整するのが地味に手間がかかる。 どうせならヘッダーも同じサイズの余白をつけるか、Gridの左右は半分でもいいじゃん・・・と思うのは実装者だけですね。はい。 というわけで、楽に対応できるItemDecorationを作りました! どうやってるのか 簡単に説明すると RecyclerVIewの左右に設定したい余白の半分のpaddingをつける。 もう半分は子Viewで RecyclerViewのclipToPaddingをfalseにする。 子Viewにpadding領域はみ出してもいいよ設定 全幅のViewの左右Marginに設定したい余白の半分の負数を設定する。 お言葉に甘えてはみ出すよ GridItemの左右に設定したい余白の半分のpadding(DecorationでのoutRect)をつける。 GridItem間はお互いの余白を足して設定したい余白になる 中身 ItemDecoration public GridDecoration( int sideMargin) { this .halfMargin = sideMargin / 2 ; this .halfMinusMargin = - 1 * halfMargin; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int adapterPosition = parent.getChildAdapterPosition(view); GridLayoutManager lm = (GridLayoutManager) parent.getLayoutManager(); GridLayoutManager.SpanSizeLookup ssl = lm.getSpanSizeLookup(); int spanCount = lm.getSpanCount(); if (ssl.getSpanSize(adapterPosition) >= spanCount) { // 全幅(ヘッダーとか) ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); params.setMargins( this .halfMinusMargin, params.topMargin, this .halfMinusMargin, sideMargin); view.setLayoutParams(params); return ; } outRect.left = this .halfMargin; outRect.right = this .halfMargin; } RecyclerViewへの設定 recyclerView.setClipToPadding( false ); recyclerView.setPadding( halfMargin , parent.getPaddingTop() , halfMargin , parent.getPaddingBottom() ); recyclerView.addItemDecoration( new GridDecoration(sideMargin)); こんな感じです githubに置いたので他の部分も見たい人はぜひ GridDecorationTest/GridDecoration.java at master · g-devi/GridDecorationTest · GitHub 失敗例 // 上で全幅は除外してる想定 // 左端のView if (isLeft(ssl, spanCount, adapterPosition)) { outRect.left = sideMargin; outRect.right = sideMargin / 2 ; return ; } // 右端のView if (isRight(ssl, spanCount, adapterPosition)) { outRect.left = sideMargin / 2 ; outRect.right = sideMargin; return ; } outRect.left = sideMargin / 2 ; outRect.right = sideMargin / 2 ; パッと見、うまくいってると思ったが・・・ よくこのGridのViewに横幅に合わせた正方形の画像を置いて、下にテキストを表示するようなレイアウトがありますよね? それを当てはめてみると・・・ 2行目の4つのViewを見てください。 オレンジの部分が横幅に合わせた正方形のViewなのですが、左右端と間の2つでサイズがズレてますよね? これはoutRectを設定したことで左右端のViewの描画幅が狭まっているためです。 よく利用させていただいているこのアプリを使うと分かりやすいです。 play.google.com spanCountとspanSizeを設定してる時点で幅は決まっており、その範囲内でどれだけ余白をもたせるかのoutRectですね。 ってことで、Gridの全ViewのoutRectのleft, right合計が一緒でないといけないためoutRectだけでの設定は物理的に無理でした・・・ やっぱりRecyclerView自体にpadding設定しなきゃってことで上の対応になったわけです。 補足 上のコードだけだと、いわゆるdividerを入れていないため上下のViewがくっつくのでその辺はお好きに。 サンプルだととりあえず "outRect.bottom = sideMargin" を入れて表示してます。 とりあえずItemDecoratoinだけで設定してみたかったので、githubのコードではItemDecoration内でRecyclerViewのpaddingを設定したりしてますが、実際に使うときはRecyclerViewをカスタムするか、もう一個上でDecorationのセットも含めて実行してくれるものを作ったほうがいいかと思います。 レイアウトxml側でRecyclerViewのclipToPaddingやpaddingLeftなどを設定したり、全幅のものにマイナスマージンつけたりしてもいいのですが、作る度にそれぞれ設定すると手間なのでItemDecoration上でできるようにしました。 手間でなければお好きなほうで! 最後に そのうち、GridのViewがCardViewのパターンについても書きます。 旧バージョンを考慮するとCardViewの周りにshadowのための余白がつくので調整が必要になりますからね・・・ zeplinとかでの指定ではshadowを無視したCardViewの端から画面端までの余白とかになりますからね・・・ BASEではAndroidエンジニアも募集しているので、気になる方はぜひ!! www.wantedly.com
SREチームの小林(し)です。 BASEでは独自ドメインで運用されているショップさんでHTTPSで表示できる機能を実装しました。 「BASE」が独自ドメインのSSL証明書の無料発行・自動管理を開始 ‐常時SSLで安心安全なネットショップ運営を 去年の3月にサブドメインで運用されているショップさんに関しては全てHTTPS化は実装していましたが、独自ドメインで表示されているショップさんはHTTPの表示のままでした。今回から独自ドメインを利用されているショップさんもHTTPSでアクセスが可能となり、全てのショップさんでHTTPSでのアクセスが可能となります。 今回の機能ではHTTPSアクセスに必要な証明書の取得は無料で行い、かつ管理は僕たちがやりますのでショップさんの方で証明書の取得・管理は不要です。 利用方法などはマニュアルにお任せし、今回は裏側の実装について紹介します 証明書 今回証明書を発行するにあたり、 Let’s Encrypt を利用させていただきました。Let’s Encryptは去年サービスが開始された無料で証明書が取得できるサービスです。 ただ同時に何枚も無制限に取得できるわけではなく、いくつかの制限が存在しています。逆に制限さえきちんと守れば同じアカウントからであれば証明書を複数枚取得することは許可されているようです。(運営に問い合わせ済み) この Let’s Encryptから発行された証明書を使うことで、BASEの独自ドメインのショップをhttpsでアクセスすることを可能にしています。 外部サービスとの連携 取得はAmazon SQSと連携した取得専用のデーモンが自動で行なっています。 Let’s Encryptは初回はACMEアカウントと呼ばれる専用のアカウントが必要です。このアカウントが証明書を取得する際に認証として使われます。 PHPcon2016で 発表した資料 でもお話しましたが、BASEのサービスはロードバランサーに複数台のnginxを使っています。今回はnginxの設定を一部変更し、Let’s Encryptからのacme challengeと呼ばれる証明書取得確認のアクセスがきたら、取得専用のサーバにプロキシするようにしました。 取得専用サーバではacme challengeアクセスの受け皿としてドメインごとにディレクトリを作成しレスポンスを返しています。直下で認証が行われると証明書を取得するための一連の動作が専用のデーモンによって行われる仕組みです。 nginxへの同期と証明書の読み込み Let’s Encryptの証明書は取得後「/etc/letsencrypt」ディレクトリに保存されます。これらをバランサーとして動作しているサーバに定期的に同期を走らせています。 nginxでの証明書読み込みは ngx_mruby をnginxにモジュールとして組み込むことで動的に行なっています。これにより同期された証明書がアクセスのたびに動的に読み込まれ、nginxを停止することなく、またnginxのメモリを圧迫することなく独自ドメインでのHTTPSアクセスができるようになりました。 まとめ HTTPSはSEOでの効果もあるということなので、今回実装した機能はショップ運営の大きな助けになると思います。 独自ドメインを使っている方はぜひ利用してみてください! BASEでは一緒にネットショップを開発・改善するエンジニアを募集してます。ご興味のある方はぜひ遊びにきてください。 BASE株式会社:採用情報
こんにちは、iOSエンジニアの遠藤(秀)です。 3/2(木)〜 3/4(土)の3日間に渡って開催された世界的なイベント「 Try! Swift 2017 」に参加してきました。 2日目のセッション「Swiftで堅牢なカラーシステムを構築する」について、まとめてみました。 セッション概要 これまで以上に多くの企業が、新しく増え続けるユーザーに今までよりも魅力的なアプリだとアピールするために、アプリを再設計しています。この講演ではあらゆる規模のプロジェクトにスケールできる堅牢なカラーシステムを構築するための戦略について議論します。これらのアプローチはデザイン上の決定を迅速に繰り返すのに役立ち、実行時にカラーパレットのテーマを変更するようなこともできるかもしれません。さらに、iOS 10で導入された新しいカラーフィルターのアクセシビリティ機能を使用して、色覚の問題を抱える人を支援することにも応用できることを示すデモンストレーションも行います。 キーワード 、まとめ ・プロトコル、エクステンションで堅牢なカラーシステムにする。 ・ランタイムでカラーテーマを変更する。 ・色覚障がいを含む、全てのユーザについて考慮すべき。 BASEアプリでのカラーマネージメント BASEアプリでは、なるべくコード量を減らすためにclrファイルを作成して、xibファイル側に色情報を持たせるように移行している途中でした。 ・ clrファイルの作成方法 セッション中の説明にもあったとおり、clrファルでカラーパレットを共有したとしても、カラーパレットに変更が起こったときには、全てのxibファイルを書き直す作業が必要になります。また、プログラマティックにカラーテーブルをダイナミックに変更することができません。 これらの問題を解決するので手法として、今回のセッションはとても参考になる内容でした。 エクステンションの作成 UIColorのエクステンションを作成してstructを定義します。 extension UIColor { struct Palette { static let ceruleanBlue = UIColor(red: 0.0 / 255.0, green: 158.0 / 255.0, blue: 220.0 / 255.0, alpha: 1.0) static let cannonPurple = UIColor(red: 147.0 / 255.0, green: 78.0 / 255.0, blue: 132.0 / 255.0, alpha: 1.0) static let mulberryRed = UIColor(red: 197.0 / 255.0, green: 81.0 / 255.0, blue: 82.0 / 255.0, alpha: 1.0) static let fireBushOrange = UIColor(red: 225.0 / 255.0, green: 148.0 / 255.0, blue: 51.0 / 255.0, alpha: 1.0) static let saffronYellow = UIColor(red: 242.0 / 255.0, green: 190.0 / 255.0, blue: 46.0 / 255.0, alpha: 1.0) static let sushiGreen = UIColor(red: 118.0 / 255.0, green: 184.0 / 255.0, blue: 59.0 / 255.0, alpha: 1.0) static let black = UIColor(white: 44.0 / 255.0, alpha: 1.0) static let white = UIColor.white } } ・クラス変数 / クラスメソッドにて色を取得します。 class var primaryText: UIColor { return Palette.black } class func contentBackground() -> UIColor { return Palette.white } ・デザイナー・エンジニア間でZeplinを使用している場合、カラーテーブルをエクステンションとして出力することが出来て便利です。 カラーテーマの適用 ・アプリの外観を変更(LINEの着せ替えとか。)するような場合、ノティフィケーションを使用してカラーテーマを変更します。 最初にColorUpdatableプロトコルを定義します。 /// A protocol which denotes types which can update their colors. protocol ColorUpdatable { /// The theme for which to update colors. var theme: Theme { get set } /// A function that is called when colors should be updated. func updateColors(for theme: Theme) } ノティフィケーションの名前を定義するためのプロトコル、ColorChangeObservingを定義します。 /// A protocol for responding to `didChangeColorTheme` custom notifications. protocol ColorThemeObserving { /// Registers observance of `didChangeColorTheme` custom notifications. func addDidChangeColorThemeObserver(notificationCenter: NotificationCenter) /// Removes observance of `didChangeColorTheme` custom notifications. func removeDidChangeColorThemeObserver(notificationCenter: NotificationCenter) /// Responds to `didChangeColorTheme` custom notifications. func didChangeColorTheme(notification: Notification) } ヘルパー用の関数を作ります。 private extension ColorThemeObserving { /// Returns the theme specified by the `didChangeColorTheme` notification’s `userInfo`. func theme(from notification: Notification) -> Theme? { // . . . return theme } /// Updates the colors of `ColorUpdatable`-conforming objects. func updateColors(from notification: Notification) { guard let theme = theme(from: notification) else { return } if var colorUpdatableObject = self as? ColorUpdatable, theme != colorUpdatableObject.theme { colorUpdatableObject.theme = theme colorUpdatableObject.updateColors(for: theme) } } } カラーテーマが変更されたノティフィケーションを受信します。 extension UIViewController: ColorThemeObserving { @objc func didChangeColorTheme(_ notification: Notification) { updateColors(from: notification) } } テーブルビュー、コレクションビューでも同様にノティフィケーションを受信します。 extension UITableViewController { @objc override func didChangeColorTheme(_ notification: Notification) { updateColors(from: notification) tableView.reloadData() } } extension UICollectionViewController { @objc override func didChangeColorTheme(_ notification: Notification) { updateColors(from: notification) collectionView?.reloadData() } } テーマ毎に色の変更を行います。 extension UIColor { class func backgroundContent(for theme: Theme) -> UIColor { switch theme { case .light: return Palette.white case .dark: return Palette.black } } // . . . } ビューコントラー側でテーマカラーを適用します。 extension ViewController: ColorUpdatable { func updateColors(for theme: Theme) { view.backgroundColor = .contentBackground(for: theme) childView.updateColors(for: theme) // . . . } } 色覚障がいについて ・男性の8%、女性の0.5%が何らかの色覚障がいを持っているとのことでしたが、実際には地域によって割合は異なります。 色に意味を持たせて、緑色は正常、赤色は異常という文化が広がっていますが、それらを識別できないと、そのメッセージは伝わりません。 iOS10では、色覚障がい者向けのカラーフィルター設定を持っており、全ての色のコントラストに適用します。 色覚障がい者向けのフィルター設定を適用します。 import InclusiveColor extension UIColor { class func primaryText(for theme: Theme, blindnessType: InclusiveColor.BlindnessType = .normal) -> UIColor { let color: UIColor = { switch theme { case .light: return Palette.black case .dark: return Palette.white } }() return color.inclusiveColor(for: blindnessType) } }