TECH PLAY

株式会社スタメン

株式会社スタメン の技術ブログ

231

概要 はじめまして。スタメンでフロントエンドエンジニアをしている @0906koki です。筋トレを週5でする筋肉エンジニアです。 普段はReactとTypeScriptで開発をしていますが、サーバーサイドのRailsもAPIを作成するタイミングで触ります。 弊社のプロダクトである TUNAG では、フロントエンドをReact、Reduxで実装しており、Reduxにredux-sagaを導入しています。今回は、そのredux-sagaについて書きたいと思います。 ※ 本記事はReactとReduxをある程度理解している人向けとなっております。 経緯 Reduxを触っていると、非同期処理などの副作用を、redux-thunkやredux-sagaなどのmiddlewareが受け持つことが多いと思います。また、Reduxを使わずに、Reactライフサイクル内で非同期処理を記述することもあると思います。 Redux有り無しに関わらず、非同期処理によってコードが複雑化するケースが多く、非同期処理をシンプルに保つことはReactの実装においては重要な課題です。そうした課題に対して、redux-sagaは他の手法に比べて、コードをシンプルに保つことができるので、今回は改めてredux-sagaについて学習してみようと思いました。 主なフレームワークとライブラリ react(16.3.2) react-redux(7.1.0) redux-saga(1.0.5) redux-sagaの基本 redux-sagaは、reduxの同期的なフローの中に、非同期処理などの副作用を簡単に組み込めるようにするライブラリです。また、redux-sagaでは、独自のタスクという概念によって、副作用を並列で走らせることができます。 こうした副作用の処理を、同期的なreduxフローのどこで行うかというと、middlewareが担当します。 大まかなreduxの流れに、redux-sagaを入れるとこんな感じになります。 クリックなどのユーザーイベントにより、ActionをDispatchする そのActionをredux-sagaが検知して、タスクを実行 そのタスク内での非同期処理が成功した場合、成功の結果を通知するActionをDispatchする 通常は、ActionをDispatch → Reducerでstateを更新の流れなのですが、redux-sagaを使用すると、特定のActionがDispatchされたタイミングで、そのアクションに対応したジェネレータ関数を実行します。更に、その関数の中でAPI callを行い、ActionをDispatchすることで、APIで受け取ったデータをもとに、stateに更新をかけることもできます。 redux-sagaが提供するタスク実行コマンド redux-sagaでは、タスクを実行するコマンドを、 redux-saga/effects のimportによって使用できます。主なコマンドは以下の通りです。 put(ActionをDispatchする) take(指定のActionのDispatchの監視を行う) takeEvery(同じActionが複数回来た場合に、並列で処理を行う) takeLatest(処理をキャンセルし、新しい処理を行う) call(非同期処理を呼び出して、終了を待つ) fork(タスクの実行を行う) select(storeからstateを取り出す) こうしたコマンドを、ジェネレーター関数の中で使用して、同期的に記述することを可能にします。 redux-sagaのメリット reduxの中で非同期処理などの副作用を受け持つライブラリとして、redux-thunkがあり、よくredux-sagaと対比されるシーンが多く見受けられます。 redux-sagaでは、redux-thunkと比較して、どういったメリットがあるんでしょうか? redux-sagaを導入することのメリットとしては、主に以下のことが挙げられると思います。 redux-thunkはAction Createrに処理を記述する一方、redux-sagaはsagaコンポーネントに記述できるため、複雑化しにくい テストが書きやすい Async/Awaitなどの非同期処理のネストが深くならずに済む redux-sagaをreduxの中で使用する 基本的なredux-sagaの使い方が分かった所で、実際にreduxの中でredux-sagaを使用してみたいと思います。まずは、redux-sagaをインストールします。 npm install --save redux-saga タスクを作成 今回は、データをサーバーからフェッチしてきて、成功したら成功のアクションをDispatch、失敗したら失敗のアクションをDispatchする処理をSaga内で書いていきたいと思います。 Sagaで使用するアクションとして、データのリクエストを行う REQUEST_FETCH_DATA 、データ取得が成功した場合の SUCCESS_FETCH_DATA 、データ取得が失敗した場合の FAILURE_FETCH_DATA を定義します。 その次に、 src/sagas のディレクトリを作成し、配下に sample.js ファイルを作成します。そのファイルに以下のように記述します。 import { call , select , fork , take , takeEvery , put } from 'redux-saga/effects' import fetchData from '../apis' import { REQUEST_FETCH_DATA , SUCCESS_FETCH_DATA , FAILURE_FETCH_DATA } from '../actions/actionTypes' import { successFetchData , failureFetchData } from '../actions' function* runRequestFetchData () { const { text } = yield select (( state ) => state ) const { payload , error } = yield call ( fetchData , text ) if ( payload && ! error ) { yield put ( successFetchData ( payload )) } else { yield put ( failureFetchData ( error )) } } ここでは以下のような処理を行っています。 select でstoreにある text のstateを取得 このstateをfetchDataの引数に入れて、 call によって非同期処理 取得が成功していれば、 successFetchData アクションをDispatch 取得に失敗していれば、 failureFetchData アクションをDispatch 上記のように runRequestFetchData というタスクを用意しましたが、このタスクはどうやって実行されるのでしょうか? このタスクは REQUEST_FETCH_DATA アクションがDispatchされたタイミングで実行したいので、Actionを監視するように処理を記述しなければいけません。監視するには以下のようにタスクを追加します。 // 省略 function* handleRequestFetchData () { yield takeEvery ( REQUEST_FETCH_DATA , runRequestFetchData ) } これだけで、ActionがDispatchされたタイミングで、 runRequestFetchData を実行することができます。簡単ですね。 ※並列で非同期処理を走らせる場合 非同期処理を並列で実行して、データを取得したい場合は、以下のように書きます。 const { firstData , secondData } = yield all ([ call ( fetchData1 , text1 ) , call ( fetchData2 , text2 ) ]) call all を使用した並列処理の場合、 Promise.all と同様に、すべての処理がresolveされるまでか、どちらかがrejectされるまで、値を返しません。 redux-sagaでのテスト redux-sagaでは、テストの書きやすさをメリットとして挙げましたが、どうやってテストを書くのでしょうか? いくつかredux-sagaのテスト用ライブラリは存在しますが、今回は一番スター数の多い redux-saga-test-plan を使ったUnitテストを、先程のコードを交えて紹介します。 import { expextSaga } from 'redux-saga-test-plan' describe ( 'runRequestFetchData' , () => { test ( 'success' , () => { const successResponse = { payload : 'some data' } ; return expectSaga ( runRequestFetchData ) . provide ([ [ call . fn ( fetchData , 'text' ) , successResponse ] ]) . put ({ type : 'SUCCESS_FETCH_DATA' , payload : { payload : successResponse . payload } }) . run () ; }) ; test ( 'failure' , () => { const failureResponse = { error : 'エラー' } ; return expectSaga ( runRequestFetchData ) . provide ([ [ call . fn ( fetchData , 'text' ) , failureResponse ] ]) . put ({ type : 'FAILURE_FETCH_DATA' , error : failureResponse . error }) . run () }) }) 上記の例では、 runRequestFetchData タスクを実行したときに、 put で最終的に返却されるデータと一致しているかテストしています。 redux-saga-test-planを使ったテストでは、callで呼び出すfetch関数と、その関数を実行した場合の返却データをProviderを使用することで、予めセットしておくことができます。上記の例では、成功した場合と失敗した場合のリスポンスデータを、 successResponse 、 failureResponse にセットしています。 そのデータを元に、 FAILURE_FETCH_DATA アクションと FAILURE_FETCH_DATA アクションをDispatchしています。 storeの接続 それでは、最後にreduxとredux-sagaをつなぎこむ実装をしたいと思います。以下のようにすべてのSagaを集約するrootSagaファイルを作成し、rootSaga関数でタスクをまとめます。 // 省略 export default function* sampleSaga () { yield fork ( handleRequestFetchData ) } import { fork } from 'redux-saga/effects' import sampleSaga from './sampleSaga' export default function* rootSaga () { yield fork ( sampleSaga ) } rootSagaが定義されたので、このrootSagaをstoreに接続して、タスクを実行できるようにします。 import { createStore , applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' // sagaMiddlewareを作成 const sagaMiddleware = createSagaMiddleware () // ストアを作成 const store = createStore ( reducer , applyMiddleware ( sagaMiddleware , someMiddleware )) 以上で、Sagaの一連の実装は完成しました。とても処理の流れを追うのが簡単だと思います。 redux-sagaを使用すると、redux-thunkのコールバック地獄や、Reactライフサイクル内での非同期処理の煩雑性から開放されるので、是非使ってみてください。 最後に 今回の記事ではredux-sagaについて書きました。redux-sagaは特徴的な書き方なので、とっつきにくい部分はありますが、今回の記事で少しでもredux-sagaに詳しくなれたら幸いです。 スタメンでは一緒にプロダクト開発を進めてくれる仲間を募集しています!興味を持ってくれた方は、ぜひ下記のエンジニア採用サイトをご覧ください。 スタメン エンジニア採用サイト フロントエンドエンジニア募集ページ
アバター
はじめに 20卒の新卒で入社したモバイルアプリチームのカーキです. スタメンでは昨今の 新型コロナウイルス の感染状況を鑑みて4月から原則リモートで業務を行なっています. リモートでの業務ではZOOMを使用し、 インターン 生も含めた全17名が同じ部屋でカメラをオン、マイクをミュートの状態で業務を行っています. 普段のzoomでの様子 今回はスタメンプロダクト部でのリモートワークの取り組みをご紹介しようと思います. プロダクト部の取り組み リモートでの勤務を行った当初、オフィスでの業務と比較して 雑談がなくなる ちょっとした質問がしづらい などのデメリットをプロダクト部全体として感じていました. どれもコミュニケーションに関することですが、この春入社したばかりの僕のような新人エンジニアにはコミュニケーション不足が生産高に関わってきます. そこでスタメンのプロダクト部ではコミュニケーション不足による、生産高の減少を防止するべく以下のような施策を行っています. おやつタイム オフィスにいるときは何気ない会話が息抜きになっていましたが、家で一人でいると雑談は生まれません.そこでお昼の15時には全員ミュートを外して15分で程度のおやつタイムを設けています. おやつタイムではみんなでお菓子を食べながら雑談をしています. 全員でお互いのバーチャル背景をいじりながら過ごすこともあれば、ラブマゲドン方式で話したいメンバーを指名してグループごとに Remo の部屋に分かれて雑談を行うこともあります(今のところ1組も成立していないのですが…) おやつタイムにRemoを使用した時の様子 定時におやつタイムがあることで、自宅での勤務にもメリハリが生まれていると感じます. チームごとに音声を常時通話 これはプロダクト部全体で行っていることではありませんが、アプリケーションチームでは Discord 、フロントエンドチームでは whereby を使用して常時会話ができるようにしています.常時音声をつないでおくことで、チーム内での質問や共有事項を迅速に行うことができています. プロダクト部内には僕も含め入社して間もないメンバーも多いため、いつでも気兼ねなく質問しやすい環境を音声通話の常時接続で実現しています. オンラインでの社内勉強会 プロダクト部内の勉強会もオンラインで開催しています. まだ今月の勉強会から始めたばかりで、反応が分かりづらいなど、改善点もありますがオンラインでも通常通り実施することができました. 社内オンライン勉強会の様子 誕生日会もオンラインで スタメンではメンバーの誕生日のお昼に誕生日会を開いているのですが、今回はオンラインで誕生日会を開催しました. 今回の誕生日会ではRemoを使用して、誕生日以外のメンバーが5人ずつのグループになって順番に誕生日のメンバーを祝いました. Remoのホワイトボード機能を使用し、それぞれのグループごとにイラストや写真・メッセージでデコレーションを行いました. グループごとにボートにも個性が出ていて面白かったです. 誕生日会のRemoでの様子 誕生日会のRemoでの様子② 最後に また会社での取り組みとしてスタメンには テレサ カバ としてオンライン飲み会を推奨する制度があります.リモートワークでもコミュニケーションを活性化するために実施されています. オンラインの飲み会なので東京・大阪支社のメンバーとも飲み会を楽しむことができ、普段以上に他支社のメンバーとの距離が近く感じています. このようにスタメンではリモートでの勤務が余儀なくされる中で、リモートだからできることを見つけ、施策として行っています. この状況がいつまで続くかは分かりませんが、リモートでの勤務も楽しみながらスタメンらしく実施していきたいと思います.
アバター
こんにちは、スタメンでディレクターをしている @nicka0001 です! 普段は主に TUNAG の改善施策の企画、設計やプロジェクトマネジメントを行なっています。 今回は、サービスを運用していくにあたっての戦略的な機能企画について書いていきます。 戦略の必要性 仕様やUIを決定することは正解がなく、とても難解で怖いものです。 シンプルに便利・不便/使いやすい・使いにくいではなかなか決められない色んな要素を孕んでいます。 だからこそ、常に考えに考えを重ねて最善を尽くしたいものでもあります。 そのためには、 戦略を立て、 ステークホルダー やプロジェクトメンバーに語り、仕様やUIを戦略をベースに議論し、リリースをしたら戦略をもとに評価を行い、最善を突き詰めていく。 そんな健全で活発な PDCAサイクル を回すために、戦略は必要であると考え、戦略の策定を行うために、UXデザインのフローを一部活用しながら企画を行なっています。 リーン開発を行う組織においても、MVP※1 を評価する上で重要な指標になるかと思います。 戦略を立てるための情報収集 事業戦略を聞き、プロダクトの機能案を立てるため、お客様の一番近くで業務を行なってくれている、カスタマーサクセス※2に ヒアリ ングします。 現状のニーズとそれを満たす為に行なっている作業(タスク) タスクの詳細( 5W1H ) そのタスクが必要な背景 等々 できる限り多岐に詳細に、 ヒアリ ングします。 主観や経験則でない、ユーザーについての事実を明らかにすることで、より効果的なア イデア を出すことができます。 何よりもこの情報は、意思決定や自分のア イデア を語るうえでとても頼りになります。 情報の分析とア イデア の発散 さて、ユーザーについての事実情報が集まったところで、要件に落とし込んでいきます。 ヒアリ ングを行ったタスクは想定フローに落とし込み 最低限、実用最低限機能(MVP)、画面遷移の基準として利用し、 ニーズや環境、背景はKA法※3で価値抽出し、 機能の中の一つ一つのUI案や仕様案とその情報優先度を割り出す為に利用します。 これで、画面遷移とコンセプトレベルのMVP案(UI、仕様案)がそれぞれの ヒアリ ング内容に結びついた形で策定できているかと思います。 これにより、画面遷移から、UI、仕様に至る一つ一つに戦略が結びついた形になり、それぞれが目的を達成できているかといった、詳細な評価を行うことが可能になります。 ユーザーの期待を超えるための仮説設定 MVP案が明らかになったところから、 再度ユーザーのニーズや背景、環境と再度向き合い、 仮説を立ててことでよりスムーズかつ、機能が与える価値ををより増幅させるア イデア を出していくことで、ユーザーの期待を超えたプロダクトの提供が可能になると考えています。 ユーザーの動きや環境などの事実情報からどれだけ、迷いや行動を先回りし仮説を立てられるかという能力は、ものづくりを行う人間に求められていくのではないかと思います。 まとめ プロダクトの細部の一つ一つについて、熱量を持って語り、戦略をチーム一丸で達成していきましょう。 スタメンでは、ユーザーに心から気に入ってもらえるプロダクトを目指し、最適な戦略を共に考え、達成していくプロダクト開発メンバーを募集しています!興味を持ってくれた方は下記の採用サイトをご覧ください! スタメン エンジニア採用サイト サーバーサイドエンジニア募集ページ フロントエンドエンジニア募集ページ インフラエンジニア募集ページ モバイルアプリエンジニア募集ページ 参考記事 ※1 MVP Wikipedia 実用最小限の製品 ※2 カスタマーサクセス ボクシルマガジン カスタマーサクセスとは | 顧客の成功体験づくりで注目、CSとの違い・定義・事例解説 ※3 KA法 UX.txt KA法 見出し画像 : Photo by JESHOOTS.COM on Unsplash
アバター
こんにちは。フロントエンドエンジニアの 渡邉 です。 スタメンで開発しているサービスの新機能は、React v16.8で追加された hooks 等を使って開発しています。 その中でも本記事ではuseEffectについて触れていこうと思います。 目次 hooksとは useEffectの第二引数の依存リストを正確に渡す 参考サイト hooksとは フック (hook) は React 16.8 で追加された新機能です。state などの React の機能を、クラスを書かずに使えるようになります。 React 公式ドキュメント useEffect useEffectは第1引数にrender毎に実行する関数を。第2引数はrender時にuseEffect内の関数を実行するか判定する値を配列で渡します。 以下コードのように第2引数に何も渡さないとrender毎に実行されます。 const Counter = () => { const [ count, setCount ] = useState(0); const [ count2, setCount2 ] = useState(1); useEffect(() => { document .title = `You clicked $ { count } times`; } ); return ( <div> <span>You clicked { count } times</span> <button onClick= { () => setCount(count + 1) } >Click me</button> <span>You clicked { count2 } times</span> <button onClick= { () => setCount2(count2 + 1) } >count2 Click me</button> </div> ); } countが変わった時限定で実行したい場合は第2引数にcountを渡す。 これでcount以外のstateが更新されても、useEffectが実行されることはなくなります。 const Counter = () => { const [ count, setCount ] = useState(0); const [ count2, setCount2 ] = useState(1); useEffect(() => { document .title = `You clicked $ { count } times`; } , [ count ] ); // countが変わったら実行される return ( <div> <span>You clicked { count } times</span> <button onClick= { () => setCount(count + 1) } >Click me</button> <span>You clicked { count2 } times</span> <button onClick= { () => setCount2(count2 + 1) } >count2 Click me</button> </div> ); } // 初期 render const Counter = () => { // ... // 初期render useEffect(() => { document .title = `You clicked $ { 0 } times`; } , [ count ] ); // ... } // `<button onClick={() => setCount(count + 1)}>Click me</button>`がクリックされると、Counter関数が呼び出される const Counter = () => { // ... // 再renderでcountが変わっているのでuseEffect内の関数が実行される useEffect(() => { document .title = `You clicked $ { 1 } times`; } , [ count ] ); // ... } // `<button onClick={() => setCount2(count2 + 1)}>count2 Click me</button>`がクリックされると、再度Counter関数が呼び出される const Counter = () => { // ... // count2が変わったが、countは変わっていないのでuseEffect内の関数は実行されない useEffect(() => { document .title = `You clicked $ { 1 } times`; } , [ count ] ); // .. } useEffectの第2引数の依存リストを正確に渡す 第2引数に空配列を渡しているパターンは要注意です。 公式ドキュメント引用 関数を依存のリストから安全に省略できるのは、その関数(あるいはその関数から呼ばれる関数)が props、stateないしそれらから派生する値のいずれも含んでいない場合のみです。 例えば、以下コードにはバグがあります。 const dataPage = ( { dataId } ) => { const [ data, setData ] = useState( null ); const getUrl = () => { return `http: //api/data/${dataId}` } const fetchData = async () => { const url = getUrl() const response = await fetch(url); const json = await response.json(); setData(json); } const testFunc = () => { // 処理 } const testFunc2 = () => { // 処理2 } const testFunc3 = () => { // 処理3 } useEffect(() => { fetchData(); } , [] ); // ... } 上のコードの場合 dataId に変更があった場合でも fetchData は実行されず、古い data が表示され続けてしまう。 このように、propsやstateを参照している関数がuseEffectの外で作成されると、propsとstateが実際に扱われているかを把握するには、アプリケーションが肥大化するに連れ難しくなり、バグを生みやすくしてしまう。 const dataPage = ( { dataId } ) => { const [ data, setData ] = useState( null ); const getUrl = () => { return `http: //api/data/${dataId}` // propsで渡ってきたdataIdを使用している } const fetchData = async () => { const url = getUrl() const response = await fetch(url); const json = await response.json(); setData(json); } const testFunc = () => { // 処理 } const testFunc2 = () => { // 処理2 } const testFunc3 = () => { // 処理3 } useEffect(() => { fetchData(); // 実行 } , [] ); // 空配列を渡しているため、dataIdが変更されても古いdataのまま表示されてしまう // ... } なので、依存リストに正確に渡す方法としての基本useEffect内で実行する関数はuseEffect内で宣言するのと、useEffect内で使われている値はすべて記述することです。 なので実際に先程のコードを修正します。 const dataPage = ( { dataId } ) => { const [ data, setData ] = useState( null ); const testFunc = () => { // 処理 } const testFunc2 = () => { // 処理2 } const testFunc3 = () => { // 処理3 } useEffect(() => { const getUrl = () => { return `http: //api/data/${dataId}` // propsで渡ってきたdataIdを使用している } const fetchData = async () => { const url = getUrl() const response = await fetch(url); const json = await response.json(); setData(json); } fetchData(); // 実行 } , [ dataId ] ); // ... } これによりuseEffect内でしか使われていないので、仮に dataId を使わなくなったとしても気づきやすくなります。 useEffect内に関数を移動できない場合 その場合の選択肢として、 コンポーネント の外部にその関数を移動できないかを考える必要があります。 例えば、 testFunc を外部に移動することによりpropsもstateも参照していないことが保証されるため、依存リストに含めなくても良くなるので、空配列を渡しても安全です。 const testFunc = () => { console.log( 'test' ) } const dataPage = ( { dataId } ) => { const [ data, setData ] = useState( null ); // ... useEffect(() => { testFunc() } , [] ); // ... } propsやstateを参照していて外部に移動できない場合の解決策として useCallback を使用する方法があります。 例えば、親の コンポーネント が、自身のstateを変更させるアクションを子 コンポーネント に渡して、子 コンポーネント で発火させる!などなど。 useCallbackを使う useCallbackとは... 関数自体の依存が変わらない限り関数も変化しないことを保証できる(メモ化されたコールバックを返す) 以下のようにParent コンポーネント で自身のpropsを参照している関数を コンポーネント に渡すして実行する場合 const Parent = ( { id } ) => { const [ name, setName ] = useState( '' ); const hogeFunc = () => { console.log(id) } retrun ( //... <Child hogeFunc= { hogeFunc } /> //... ) } const Child = ( { hogeFunc } ) => { useEffect(() => { hogeFunc() } , [ hogeFunc ] ) //... } render内でアロー関数を利用すると常に新規の関数オブジェクトを作成してしまいます。 そのため、その関数を子 コンポーネント に渡した場合render毎に前回渡ってきた関数と違う関数として扱われてしまいます。 なので上のようなコードだとstateの name が更新されて、Parent コンポーネント が再描画されるたびに、Childに渡る hogeFunc は新規の関数として渡してしまうので、useEffect内は毎回実行されてしまう。 そのようなときにuseCallbackの出番です。 useCallbackは「意味的に同じ関数」が返るかどうかを判別して、同じ値を返す関数が返るべきなら新規のアロー式を捨てて、前に渡した同じ結果を返す関数です。 const memoizedCallback = useCallback(() => { doSomething(a, b); } , [ a, b ] ) // useCallbackの第2引数もuseEffectの第2引数と同じ考え方; 実際にuseCallbackを使って先程のコードを修正します。 const Parent = ( { id } ) => { const [ name, setName ] = useState( '' ); // hogeFuncはidに変更が合った場合のみ新規関数として作られる。 const hogeFunc = useCallback( () => { console.log(id) } , [ id ] ) retrun ( //... <Child hogeFunc= { hogeFunc } /> //... ) } const Child = ( { hogeFunc } ) => { useEffect(() => { hogeFunc() } , [ hogeFunc ] ) // Parentコンポーネントのidが変わった場合のみhogeFuncが新規関数として作成されるので、再描画の度に実行されることはなくなる //... } useCallbackを使い常に新規の関数オブジェクトを作成しなくなることにより利点が他にもあります。 Child コンポーネント がReact.memo()されている場合です。 React.memo()はpropsの変更をチェックして、同じ結果の場合は再描画をスキップして、変更があった場合のみ再描画をするので、無駄なレンダーを減らすことができます。 const Parent = ( { id } ) => { const [ name, setName ] = useState( '' ); const hogeFunc = useCallback(() => { console.log(id) } , [ id ] ) return ( //... <Child hogeFunc= { hogeFunc } /> //... ) } const Child = React.memo(( { hogeFunc } ) => { // hogeFuncに変更がない限りChildコンポーネントは再描画されない。 useEffect(() => { hogeFunc() } , [ hogeFunc ] ) //... } ) React.memo()の詳細は今回省くので 公式ドキュメント にてお願いします。 参考サイト useEffect完全ガイド これを読むとuseEffectマスターになれると思います。 少し長いですが、完全に理解したい方にはおすすめです。 まとめ 最近はhooksのお手軽さからFunctionalComponentで書くことが多いですが、きちんと理解した上で書いていきたいと記事をまとめている時に改めて感じました。 最後に、株式会社スタメンでは一緒に働くエンジニアを募集しています。ご興味のある方はぜひ エンジニア採用サイト をご覧ください。
アバター
こんにちは、スタメンでバックエンドのエンジニアをしている河井です。 今年の3月に AWS 認定ソリューションアーキテクト-アソシエイト(SAA-C01)を受験し、合格したので勉強した内容や受けて良かったことについて振り返りをしてみます。 ※現在は SAA-C02 が最新で、本記事で紹介する動画や模試もアップデートされて対応済みです 受験の理由 AWS の基本的なサービスを体系的に理解するためです。 スタメンに新卒入社して以来 Rails や React を用いた開発を経験しましたが、 AWS はあまり触る機会がありませんでした。 その後バックエンドグループの所属になり、TUNAG の機能開発に加えて クラウド インフラの運用やパフォーマンス監視業務にふれることになりました。 普段の業務で AWS 関連の会話がかなり増えましたが、内容がわからないことも多かったです。 もちろんその都度サービスについて聞いたり調べたりするものの、断片的な知識を集めるのではなかなか包括的な理解が難しいと感じていました。 そこでまずは試験を受けて広く基礎知識をつけようと考えました。 受験時の経験 受験を決めたときのエンジニアとしての経験は ウェブエンジニア歴2年弱 Rails 、React などアプリケーションの開発経験がメイン AWS は ETL 基盤の改善で少し触ったことがある(CloudFormation、Lambda、Glue、 Kinesis など) AWS 認定以外も含め資格試験の経験なし といった感じです。認定試験によく出る範囲の経験はほぼありませんでした。 勉強したこと 何を使ってどんなことを勉強したのかを紹介します。 使ったのは大きく3つで、試験対策本、動画、模擬試験です。 試験対策本 試験対策本は、 SBクリエイティブ の AWS 認定資格試験テキストを使いました。 SAA 向けと、基礎レベルの クラウド プ ラク ティショナー(CP)の2冊です。 www.sbcr.jp www.sbcr.jp まずは CP の対策本を一通り読みましたが、思ったより簡単だったのですぐに SAA の方に切り替えました。 CP の試験を受ける予定はなくても、基礎の基礎を抑えた上で SAA に必要な知識を積み上げられるので読んで損はないと思います。 どちらの本についても内容を細かく覚えようとせず、どんな内容について理解すれば良いのかを把握するためにざっと読みました。 動画教材 本だけだとイメージが湧きづらい、とはいえすべてのサービスを触るほど時間はかけられないという理由から動画教材を使うことにしました。 今回はたまたまツイートで見かけた Whizlabs を選びました。 安かったのと時間がなかったので特に調査や比較などはしていないです。 www.whizlabs.com こちらも最初は全容を把握する目的で1.5〜2倍速で流しました。 各サービスの使い方が動画でわかるので実際に触らなくてもなんとなく感触をつかむことができます。 わかりにくい部分に関しては動画に沿って手を動かしながら理解を深めました。 模擬試験 公式の模擬試験は本番より難易度が低いと聞いていたので使わず、よくおすすめされている Udemy の以下のものを使いました。 【SAA-02版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集(6回分390問) 僕が受けたのは C01 のときだったので5回分(6個目は C02 の対策として追加された)で、中難易度1つ、高難易度4つの構成でした。 順番としては高難易度の4つを先にこなし、受験の前日に中難易度の試験問題を解きました。 試験後に領域別で得点率を出してくれることと、各設問の各選択肢について解説がついているのが非常に勉強になります。 この解説を読みながら、どうすれば正解の選択肢にたどり着けたのかを考えました。 知らない知識があれば本や動画にもどって復習します。 本や動画で流す程度に留めた理由はここで、実際の現場に近いシーンの中でどういうニーズがあってこの機能があるのか?を知った上で知識をつけたほうが効率的かつ実践的です。 知識としては知っていたのに間違えたというときは Well-Architected フレームワーク に沿って考えられていたかどうかを確認しました。 これを繰り返すことで自然と Well-Architected フレームワーク についても理解が深まりました。 受験の効果 一番実感したのは、やはり普段の業務での会話や資料で AWS 関連の話題が出てきたときに何についてのサービスなのか、なぜそれについて話題にするのかを理解できるようになったことです。 また、わからないことがあってもキャッチアップまでのスピードが速くなりました。 以下はスタメンの事業(TUNAG本体、TUNAGのETL基盤、TERAS)で使っているサービスです。 これらの中で試験本番や試験勉強を通じて見なかったのは Glue だけです。(Glue も見落としていたり他の練習問題などで出てきたりする可能性はあります。) 他の点として、設計のベストプ ラク ティスについても理解が深まったので、既存の設計についても改善すべきところを判断できるようになりました。 本番 本番の試験は Udemy の講座で練習した通りの形式だったので特に戸惑うことはなく、難易度についても Udemy の高難易度試験と比べると簡単でサクサク解いていくことができました。 この点でも模試で訓練しておくことは重要だと思います。 まとめ AWS SAA 受験の振り返りをしました。 個人的に思っていた以上の効果があり受験してよかったですし、同じような課題を感じている方にもおすすめしたいです。
アバター
こんにちは、スタメンでモバイル アプリ開発 をしている @temoki です。 スタメンに入社した当時は私一人でモバイル アプリ開発 をしていましたが、おかげさまでこの一年半で仲間も増え、組織上もモバイルアプリグループが発足するにまで至りました。そのため、最近はチームで一緒になって開発を進めていける喜びを感じつつ、複数人でも開発をスムーズに行うための環境づくりも進めています。 その一つとして、このたびモバイル アプリ開発 におけるCI/CDサービスとして Bitrise を採用したため、その背景や理由についてお伝えしたいと思います。 背景と目的 CI/CDサービスを導入を検討しはじめた目的はこの三つです。 リリースの頻度を上げたい 私がいなくてもデプロイできるようにしたい ユニットテスト を定着させたい それぞれの目的についての背景は次のとおりです。 リリースの頻度を上げたい ストアへのリリースはだいたい月に一回というペースで行ってきましたが、メンバーが増えたことで開発スピードも上がり、一回のリリースでの変更量が大きくなったことがリリースへの不安につながってきました。そのため、社内へのテスト配信の頻度をあげたり、ストアへのリリースをより小さく、より頻繁に行う必要があります。 また、せっかく開発は完了している機能があるのにユーザーへの提供が遅くなってしまったり、逆に遅くならないために直近のリリース予定日での機能の詰め込みということも起きるようになってきました。 私がいなくてもデプロイできるようにしたい デプロイ作業自体は fastlane や Gradle によりほぼ自動化してありましたが、アプリ署名のための秘匿情報の管理の観点から、その作業は私の開発機のみで行うようにしていました。しかし、その回数が増えてくると、実行の手間や実行のために開発機のリソースを取られることで、自分の作業に影響がでてきました。 そもそも、チームでの開発となった今、私しかデプロイを行えないということ自体が問題です。 ユニットテスト を定着させたい リリースへの不安を減らすために ユニットテスト の文化を定着させていきたいと思っている中、改修により既存の ユニットテスト が壊れていることに気づくのが遅れるということが起きてしまいました。幸いにも ユニットテスト のビルドが通らなくなっているだけで、テスト自体でエラーが起きるようなことではありませんでしたが、やはり定着させるためには常にテストが実行される環境が必要だと再認識しました。 候補の選定と検証 iOS アプリのビルドを行える macOS をサポートしていることを条件に候補を絞りました。以下が候補に挙がったサービスとその理由です。 GitHub Actions : macOS の無料枠がある Bitrise : モバイルアプリに強いと評判 Circle CI : TUNAG ウェブアプリケーション の開発で使用中 これらをウェブサイト上で機能表を比較してみても大きな差はないため、まず実現したいと思っている次のようなワークフローを作成して比較してみることにしました。 GitHub リポジトリ の特定のブランチへのプッシュをトリガーに、アプリをビルドして Firebase App Distribution で社内配信する 手動のトリガーで、アプリをビルドしてストアへアップロードする GitHub の Pull Request をトリガーに、 ユニットテスト を実行する 以降に記載する比較結果は2020年3月時点のもので、 ソースコード が GitHub のプライベー トリポジ トリにある前提のものとなります。 GitHub Actions GitHub Actions は GitHub Universe 2018 で発表され、2019年末に正式リリースされたばかりの、 GitHub 製CI/CDサービスです。リリースのニュースを聞いた時はモバイルアプリ向けのサポートは先になるだろうと思っていたのですが、プラットフォームとして macOS も含まれ、 iOS や Android のビルドツールもサポートされるなど、良い意味で裏切られたので注目していました。 しかも、 macOS でも 無料枠 ( GitHub Freeプランの場合は2000分、Proプランの場合は3000分の利用時間)があるため、最初に試してみるのは GitHub Actions にすることにしました。 以下にその結果を ○△× でまとめました。 ○ : macOS でも無料で利用できる枠がある ○ : モバイルアプリ向けに必要な環境も充実している Software installed on GitHub-hosted runners ○ : モバイルアプリ向けのアクションも比較的揃っている印象 △ : アクションは YAML 形式のコードでのみ定義できる β版の時は Visual Editor があったが正式リリース時に廃止されたようです △ : ビルドに必要な秘匿情報は GitHub の Secrets に文字列として配置できる iOS のプロビジョニングファイルや、コード署名用証明書などを扱うには一手間かかる × : macOS 環境が高額 Linux の10倍で利用時間を消費するため、 Proプランの場合は実質300分しか無料で利用できない × : 手動でビルドを開始する公式の方法がないのでアクション構築のトライ&エラーがやりにくい GitHub API で 外部イベント repository_dispatch を起こすことで代用できるが面倒。このトリガーではCarthageのキャッシュが利用できないなどの難点も。 一通りのトライアルを終えてみて、×に挙げた部分が運用上のネックとなりそうなことがわかりました。特に、 iOS において ipa ファイルを出力するまでには(Carthageのキャッシュが利用できたとしても)20分くらいはかかる現状もあり、利用時間(=費用)を気にしながら運用することになりそうです。 Bitrise 次は Bitrise です。Bitrise はモバイルアプリ向けのCI/CDサービスを謳っており、日本開催のモバイルアプリ向けのイベントにもよく出展されているため本命のサービスです。BitriseでもFreeプランで macOS を利用可能ですが、1ビルドあたり10分という制約があり、現状のアプリの ipa ファイル出力までを試すには時間が足りないことがわかっていたため、 GitHub Actionsの後に試すことになりました。 今回、検証のために Developerプラン を契約しています。このプランでは、一回のビルドあたり45分制限がありますが、ビルド回数に関わらず定額であるため、利用時間を気にせずに試すことができました。以下が、Bitriseの結果です。 △ : Freeプランでは1ビルドあたり10分の制限があり、 iOS アプリのビルドを試すには厳しい ○ : 定額であるため、利用時間を気にする必要がない ○ : モバイルアプリ向けに必要な環境が充実している ○ : リポジトリ 構成を自動でチェックしてワークフローをある程度自動で構築してくれるため、初期セットアップが楽 ○ : UIがわかりやすい(そしてかわいい) ○ : ワークフローをビジュアルでも YAML 形式のコードでも定義できる ○ : モバイルアプリ向けのアクションが豊富にある ○ : ビルドに必要な秘匿情報はアプリケーションごとに定義でき、かつモバイルアプリとの親和性が高い iOS のプロビジョニングファイルや、コード署名用証明書を設定するための専用の機能がある ○ : 困った時は 仮想マシン に ssh や リモートデスクトップ でログインして状況を確認することができる BitriseのUIはポップでかわいい😍 評判が高いのも納得のクオリティでした。 GitHub Actionsの時には試行錯誤しながらセットアップしていましたが、Bitriseではとても簡単にセットアップを終えることができました。また、定額なのでその後もアプリの社内配信や ユニットテスト をどんどん実行でき、利用時間を全く気にしなくていいのが素晴らしいです。 Circle CI TUNAG ウェブアプリケーション の開発ですでに社内で利用中の Circle CI も候補に挙げましたが、モバイルアプリ向けに想定している実行時間や頻度を考えると、その開発業務にも費用にも大きな影響を与えそうということがわかりました。そして何よりBitriseが想像以上に優秀だったことから、Circle CIの検証はせず、Bitriseを継続していくことに決めました。 さいごに 最後にまとめとなりますが、一言で言えば 「Bitriseはいいぞ!」 です。業務上の選定となるため機能面での比較を真面目に行いましたが、Bitriseを使っているうちに、そのビジュアルデザインのかわいさ、操作性の良さ、細かい部分でのユーザーへの配慮を感じられ、純粋にBitriseというプロダクトを心から気に入ってしまいました。 Continuous happiness ✨ そして、弊社の創業事業TUNAGもエンドユーザーに同じように心から気に入ってもらえるようなプロダクトに育てていきたいと強く思いました。そのためにはさらに強い開発力が必要となりますので、スタメンでは一緒にプロダクト開発を進めてくれる仲間を募集しています!興味を持ってくれた方は、ぜひぜひ下記のエンジニア採用サイトをご覧ください。 スタメン エンジニア採用サイト サーバーサイドエンジニア募集ページ フロントエンドエンジニア募集ページ インフラエンジニア募集ページ モバイルアプリエンジニア募集ページ
アバター
スタメンのデザイナーの @kiyoshifuwa です。 現在、 弊社のデザイナーは社員のわたし1名、学生アルバイトさん1名の計2名です。 主にTUNAGなど各事業のアプリケーションのデザインを行っていますが、実は社内のあれこれも、社内のデザイナーが作っています! 今回の記事では、社内業務の一部をご紹介します。 社外の方に向けて 各種Webサイト 規模の大きいものは外部協力会社様にお願いすることもありますが、小規模であれば社内でデザイン・実装を行います。昨年はTUNAGブランドサイトのトップページ、エンジニア採用サイト、 インターンシップ 募集サイトのデザインをしました。 案内パネル スタメンの セミ ナースペースでは様々なイベントや勉強会が行われます。来社されたお客様をご案内するために、 Wi-Fi とお手洗いのパネルを作りました! 社外報 社員のご家族に、社長の想いや社員の活躍、イベントの様子などを知っていただくために作っています。このような冊子のデザインは、スタメンに入るまでやったことがありませんでした。一から勉強して、試行錯誤しながら作り上げています! ▼新メンバーや各拠点のオフィスを紹介するコンテンツです! スタメン社員に向けて 行動指針ポスター スタメンには6つの行動指針があります。新メンバーでもすぐに馴染み浸透するように、みんなが忘れずに意識できるように、それぞれの指針にあったビジュアルを作り、ポスターにしました。 ▼もちろん名古屋本社だけでなく東京、大阪オフィスにも 掲示 されています!  ▼Enjoy Togetherの人々の中に、実はマネジメントメンバーがいます。こっそり遊び心を加えています! スタメンアワード受賞者ポスター 期ごとに最も活躍したメンバーを表彰する「スタメンアワード」を受賞したメンバーを称えるポスターです!こちらももちろん全拠点に 掲示 されます!常に目に入る位置に 掲示 されているので、日々「次は自分が受賞できるよう頑張るぞ🔥」と思えます。 ▼2019年3rdピリオド 新人賞を受賞したこうきです。おめでとう! 他職種メンバーのお手伝い 社内勉強会 セールスやエンジニアにもデザインやツールの知識が役立つことがあります。「わかりやすいスライドの作り方」や、「 Keynote を効率よく使う方法」などノンデザイナー向けの社内勉強会を自主的に行っています! 登壇資料テンプレート スタメンには職種を問わず自主的に外部発信をする人がとても多いです。資料作りを楽にするため、また他社の方にスタメンの資料だと認識してもらうためにテンプレートを作りました!思ったより社内から反響があったので、作ってよかったな〜と思いました☺️ 実はこんなものもデザインしています 社内イベントを盛り上げるためのデザインも作っています! ス タツ ク 社内横断型の組織活性化PJチーム「ス タツ ク」歴代メンバーを紹介する画像です!撮影は社内で本格的に行いました。 TUNAG用スタンプ スタメンでは、自社で開発している「TUNAG」でコミュニケーションを取っています。投稿へのコメントやチャットでのリアクションにオリジナルのスタンプを使うことができます。たくさんありますが、一部をご紹介します! TUNAG用組織画像 「TUNAG」では、組織の構成も見ることができます。各部署、個性が出まくりの写真をカバー画像に設定しています。プロダクト部では、「強いエンジニア・デザイナー・ディレクター」をテーマにヒーロっぽい画像を簡易的に作りました!背景の爆発はから揚げを加工してできています。 おわりに 以上、スタメンでの社内業務をご紹介しました。 印刷など、全然知識のない不慣れな分野でも自分で調べながらやるしかないので、なかなか大変なこともあります(笑) でも毎回、スタメンのみんなへサプライズを仕掛けているような気持ちになってわくわくします!また、お披露目した後の反応を見るのも嬉しく、やりがいを感じます。困難を乗り越えて、みんなに感動を届けるのはとても楽しいです💪 スタメンにお越しいただく際はぜひ、デザインもご覧になっていってください!
アバター
こんにちは。松谷( @uuushiro )です。 先日コーポレートサイトにて、 新経営体制のお知らせ を公開させていただきましたが、3月より取締役CTOを務めていた小林 一樹が常務取締役VPoEに就任し、 執行役員 CTOには私、松谷 勇史朗 が就任しました。CTO と VPoE を分離することで、小林はVPoEとして組織と事業に、私はCTOとして技術に専念し、組織・事業・技術の意思決定の速度と精度の向上を実現していきます。 今回の就任にあたりまして、私自身の振り返りとこれからの抱負を簡単に述べさせていただきたいと思います。 まず私とスタメンとの関わりですが、スタメンが創業した2016年に学生 インターン として加わり、翌年正社員として入社しました。入社時の記事は こちら です。エンジニア未経験で ベンチャー に飛び込み、今までひたすら業務の中で経験を積ませてもらいました。入社時には自分がCTOとして役割を担うことになるとは想像もしていませんでしたが、創業期はCTO小林の直下で学んでいたので、自然とCTOという役割を意識していたのも確かです。ただ自分としてのCTOのイメージは漠然としていて、「CTOになりたい」と言ってはみても、そもそも「CTOってなんだろう?」「なりたくてなっていいものなのか?」などなど自問自答を続けていました。 そんな中、入社2年目にテッ クリード として技術的な意思決定に関わるようになり、3年目にはマネージャーとして開発チームの運営に関わるようになりました。テッ クリード として、マネージャーとして、どうやったらスタメンに貢献できるか、中長期で私ができることは何なのか、ということを考え続けていたその延長線上にCTOというイメージが徐々にはっきりしてきました。まだまだ手探り状態ではありますが、CTOとしてスタメンに貢献できる姿に確信を持てるように、自分のあるべきCTO像を考え抜いていきたいと思います。 実際に就任してみると、失敗を誰のせいにもできない緊張感、意思決定の影響の大きさ、経営陣としての責任の重さに、ただただ身が引き締まる思いです。正直、前CTOの小林や、社外のCTOの トップランナー の方たちを見ていると同じCTOとして焦りしか感じませんし、今後は自分の限界値が組織の限界値になるプレッシャーを感じています。今はまだまだ理想とは程遠いですが、とにかく自分の成長を止めずに少しでも前進し成果を出していきたいと思います。 今後の技術的課題としては、事業の成長を加速させられるような開発スピードとクオリティの維持、事業規模拡大に備えたスケーラビリティの確保、安心してサービスを利用していただける堅牢なセキュリティ、非連続な進化を遂げるための新しい技術投資、などなど挙げればキリがありませんが、スタメン開発チームのビジョン「エンジニアリングで事業の成長を牽引する」に徹底的に向き合い、チームで前進していけるようにCTOとしてリードしていきたいと思います。 組織としては、スタメンエンジニアの価値観 Star Code にある通り、常にユーザー目線で考え、障害やプロジェクト遅延などの失敗に向き合い、お互い忖度せずに本音で語り合い、日々の自分の仕事に誇りを持てるほどのプロ意識を持ち、そして何よりもエンジニアリングを楽しむ。そんなエンジニア組織をCTOとして皆とつくりあげていきたいです。 社内外問わずスタメンのCTOとして納得してもらえる実力をつけ実績を出せるように、全力で役割を全うしていきたいと思いますので皆様よろしくお願いいたします。 最後に スタメンエンジニア組織としては、0→1 そして 1→10 のフェーズを終え、これからはその先のフェーズ10→100のフェーズになります。プロダクトが複数になり、TUNAG の大規模化と機能拡張に向けて一層強化を進めていくタイミングなので、とてもやりがいのある面白い仕事になると思います。新しい体制で、多くのチャレンジングな技術的課題に向き合い、ユーザーに価値を届けるエンジニア募集しています!興味を持ってくれた方は是非、下記のエンジニア採用サイトを見てください。 スタメン エンジニア採用サイト サーバーサイドエンジニア募集ページ フロントエンドエンジニア募集ページ インフラエンジニア募集ページ モバイルアプリエンジニア募集ページ
アバター
こんにちは、スタメンでバックエンドのエンジニアをしている河井です。 私の所属するバックエンドチームでは、普段からサービスのパフォーマンス監視とチューニングを継続的に行っています。 今回は、データベース負荷のモニタリングに使っている Performance Insights というツールの活用方法をまとめてみます。 Performance Insights について Performance Insights(パフォーマンスインサイト)とは、Amazon RDS で提供されている機能で、DB の負荷のモニタリングが簡単にできます。 ざっくりとまとめると、 指標がシンプルなので DB の負荷状況を直感的に把握できる SQL クエリが表示されるため問題箇所の特定がしやすい リアルタイム更新なので即座に負荷内容を分析できる といったメリットがあります。 有効化については こちら を参照ください。 ダッシュボードの見方 ダッシュボードが見やすくて便利なので、基本的にこちらを使います。 RDS コンソールのサイドバーにある「パフォーマンスインサイト」からアクセスできます。 画面は3つのパートに別れていて、上から、カウンターメトリクス、データベースのロード、SQL ステートメントダイジェストとなっています。 基本的にはデータベースのロードとSQLステートメントダイジェストを使うので、この2つについて解説します。 データベースのロード データベースの負荷を時系列で表示しています。負荷は平均アクティブセッション(Average Active Session、AAS)という指標で表現されています。 平均アクティブセッションとは、一定時間内に処理中または処理開始を待ちのセッションの数で、この数が多いほど DB に負荷がかかっているといえます。 上記画像では、スライス基準という項目が、「待機」 になっています。 スライス基準とは AAS の内訳を何基準で表示するかという設定項目です。 待機基準では、データベースの一連の処理のうちどのイベントでどのイベントでの待機時間が長くなっているかを知ることができます。 各種イベントの詳細については こちら を参照ください。 グラフの上部にある破線は今見ている RDS インスタンスの仮想 CPU 数を表しています。 CPU での待機数だけでこのラインを超えると、CPU 数が不足しているといえます。 上の画像では一箇所そのラインに到達していますが、待機の内訳を見ると CPU の割合は低いです。 そのため、CPU が原因となって負荷が高まっているわけではないと判断できます。 次に、スライス基準を SQL に変えてみます。 SQL 基準を選択することで、各クエリごとにどれだけ待機しているかを知ることができます。 ぼかしを入れてあるところには SQL クエリが表示されており、どのクエリによって負荷が発生しているのか?を判定できます。 SQL ステートメントダイジェスト SQL ステートメントダイジェストでは、 簡略化された SQL クエリが負荷の高いものから順に表示されています。 ダイジェストではパラメータが name = ? のように置き換えられており、同じクエリの影響をまとめた値になっています。 さらに詳細を見たいときは▷をクリックし、実際のパラメータが入った個々のクエリを見ることができます。 TUNAG では企業ごとに柔軟な設定が可能なため、予期せぬ設定で負荷が高まることがあります。 パラメータありの状態でクエリを見て企業を特定し、原因究明までスピーディに行えるようになりました。 監視の設定 上記は負荷がかかっているタイミングが特定できている前提で話をしていました。 負荷が高まっていること自体に気づくために、データベースのロードが一定数を超えたら通知されるようにしてみます。 設定可能な項目 CloudWatch Alarm でデータベースのロードを監視することができるので、設定手順を説明します。 データベースのロードに関するメトリクスは3種類あります。 DBLoad DBloadCPU DBLoadNonCPU DBLoadCPU は待機要因を CPU のみに限定したもの、DBLoadNonCPU は CPU 以外の要因で待ちが発生しているもの、DBLoad はその合計値となっています。 バックエンドチームでは次のような使い分けをしています。 DBLoadCPU: CPU 数不足の検知 DBLoadNonCPU: 非効率なクエリ発行の検知(インデックスが効いていない、DB への同時アクセス数が多すぎるなど) 設定手順 どの指標を使うにしても設定は同じなので、DBLoad のアラームを設定してみます。 まずは CloudWatch コンソールにアクセスし、サイドメニューの「アラーム」を選択、「アラームの作成」と進みます。 進んだ先の画面で、"DBLoad" で検索→「RDS > データベースメトリクス」を選択→監視したい DB インスタンスにチェック→メトリクスを選択と進みます。 メトリクスを取得する間隔や閾値は、普段負荷が高まるときの AAS を参考に決めましょう。 最後に、通知先を設定して終了です。 まとめ Performance Insights のダッシュボードの見方からアラートの設定方法までざっくりとまとめてみました。 これを使い始めてから劇的にクエリの改善が進んだので、まだ使っていないという方はぜひ触ってみてください。 最後に、株式会社スタメンでは一緒に働く仲間を募集しています。 ご興味のある方はぜひ下記のページをご覧ください! エンジニア採用サイト エンジニア募集ページ(Wantedly) 参考資料 パフォーマンスインサイトを使用する Performance Insights で Amazon RDS for MySQL をチューニングする アイキャッチ Photo by Chris Liverani on Unsplash
アバター
スタメン エンジニアの松谷( @uuushiro )です。 スタメンは、2020年3月より エンゲージメント診断サービス TERAS(テラス) の提供を開始します。創業プロダクトである TUNAG(ツナグ) に続いて2つ目のプロダクトになります! TERASは、エンゲージメント経営を実行できるサービス TUNAG のノウハウを元にした、組織のエンゲージメントを可視化する組織診断サービスです。サービスの概要は以下リンクからご覧ください! エンゲージメント診断サービス『TERAS』をリリース この記事では、エンゲージメント診断サービス TERAS の技術 アーキテクチャ の紹介をします。 TERAS は SPA(React)と API サーバ( Ruby on Rails )で作られたWebアプリケーションです。 アーキテクチャ の構想・実装にあたって、重要視した点は3つです。 実装・保守・運用コストをなるべく削減し、機能開発に集中できる基盤にしたい セキュリティ・スケーラビリティなどの非機能要件は初期からなるべく妥協したくない インフラをコード化して管理し、インフラ知識の 冗長化 のハードルを下げたい。また、再利用性をあげたい。 特に複雑なことはしておらず、結果としてシンプルな構成になりました。 以下で アーキテクチャ の全体図と個別詳細を紹介していきます。 TERAS アーキテクチャ 全体図 TERAS アーキテクチャ の全体図は以下になります。 ざっくり以下の項目に分けて紹介していきます。 ネットワーク CDN WEBサーバー デプロイ 秘匿情報管理 インフラのコード化 ネットワーク ネットワーク以下3つのサブネットに分けています。 グローバルIP を持てば、外部ネットワークと通信ができるサブネット(Public Subnet) NATを介してのみ外部ネットワークと通信ができるサブネット(Protected Subnet) 外部ネットワークと通信はできないサブネット(Private Subnet) TERAS では、Application Load Balancer を Public Subnet に配置し、ECS/Fargateタスクを Protected Subnet に配置することで、Application Load Balancer からのみアクセスを受け付けるようにしています。また、WEBサーバーとしてFargateタスクを利用していますが、Fargateタスクに対して直接Elastic IPを割り当てることができないので、外部へのアウトバウンド通信(メール配信サービスへの API アクセスなど)を許可するために、NATを Public Subnet に配置しています。RDSは、外部通信の必要性がないので、Private Subnetに配置しています。このように、ネットワークの役割毎に作成することで、階層化したアクセス制御を行いセキュリティを向上させています。 CDN TERAS では、 CDN として Amazon CloudFront を利用しています。導入理由は3点です。 パフォーマンス向上 エッジサーバーからコンテンツが送信される分、レスポンスが速い ただ、オリジンが東京リージョンなので実際にはそこまで差がでないかもしれない。 コスト削減 Amazon CloudFront からデータを送信するコストは Amazon S3 から送信するコストよりも安い セキュリティ AWS WAF の統合が可能で、 XSS や SQLインジェクション などの一般的なWEBアプリケーションの攻撃を防ぐことができる。(2019年11月に新しくリリースされた AWS WAF V2 の Managed Rule を利用し、WAF V1に比べ、ルールの設定が大幅に楽になった。) Amazon CloudFront は、世界中にノードがあるため大量の 帯域幅 があり、多くの DDOS攻撃 を軽減できる また、 Amazon CloudFront に関して、SPAにおける要件や、セキュリティの観点で対応した事項があるので以下で紹介します。 キャッシュコン トロール Amazon CloudFront のキャッシュコン トロール 機能(Behaviors)を活用し、 Amazon S3 (SPAサーバー)と Application Load Balancer ( API サーバー)のマルチオリジン構成にしています。クライアントからのリク エス トパターンを元に、キャッシュポリシーやオリジンへのアクセスルールを個別設定しています。TERAS では、 /api というリク エス トパターンの場合は、Application Load Balancerへ、その他のリク エス トパターンは Amazon S3 へ振り分けられるように設定しています。 また、 /api のような動的コンテンツに関しては、キャッシュされないように TTL を0に設定しています。 カスタムエラーページ React RouterなどSPA側でルーティングをしている場合、サブ ディレクト リ含むURLでブラウザをリロードをすると、S3のエラーページが表示されてしまいます。これは対象の ディレクト リがS3 バケット に存在しないことが原因です。 Amazon CloudFront の Error Pages に403または404エラーの場合には /index.html へ転送し、SPA側でルーティングの制御が行えるようにしています。 オリジンサーバーの保護 セキュリティ・パフォーマンスの観点において、外部から直接オリジンへアクセスされることを防ぎたいです。 Amazon S3 には、ブロックパブリックアクセスという、あらゆるルールより優先されるアクセス制御ポリシーがデフォルトで有効化されています。設定ミスなどデータが意図せず公開されることを防ぐために、基本ブロックパブリックアクセスは有効化のままで運用できるようにしたいです。 Amazon CloudFront の Origin Access Identity を使うことで、 ブロックパブリックアクセスを有効化したまま、 Amazon S3 へのアクセスを Amazon CloudFront からのリク エス トに限定することができます。 また、Application Load Balancer についても、 Amazon CloudFront でカスタムヘッダを追加し、Application Load Balancer側で値が一致すれば、CloudFrontから転送されてきたリク エス トとみなし、リク エス トを許可することができます。これによって、Application Load Balancer についても Amazon CloudFront からのリク エス トに限定することができます。 クロス ドメイン 通信が不要 SPAと API サーバが連携するアプリケーションを ホスティング する場合、SPAサーバーと API サーバーが別 ドメイン で ホスティング されることがあると思いますが、 Amazon CloudFront でマルチオリジン対応することで、同一 ドメイン で扱うことができ、 API サーバー側におけるCORS対応が不要になります。実装・検証 工数 を抑える1つの要因となりました。 WEBサーバー AWS Fargateを以下の理由で導入しました。 ホスト インスタンス の管理から解放 オートスケールの設定が不要 つまり、自分でサーバーを管理することなくコンテナを実行できます。EC2に比べて、コストは割高ですが人的 工数 の削減を考えると元が取れるのではないかと思います。 AWS Fargate については、以下の AWS Startup ブログ が大変分かりやすかったので興味のある方はそちらをご覧ください。 スタートアップのためのコンテナ入門 – AWS Fargate 編 デプロイ API サーバー( Amazon ECS) AWS CodePipeline で AWS CodeBuild と AWS CodeDeploy を連携しデプロイをしています。以下の理由で導入しました。 デプロイサーバーの構築・運用コスト削減 オートスケールとの相性が良い ECSとの統合機能が充実している Blue Green Deployment Canary Deployment Linear Deployment AWS 内でデプロイフローが完結することで、 AWS の認証情報などを外部に公開する必要がなくセキュアに まだ、 AWS CodePipeline関連のサービスの利用経験が浅いので課題が見つかるかもしれませんが、セキュリティ・スケーラビリティの観点や他 AWS サービスとの親和性においても良い選択なのではないかと思っています。 SPA( Amazon S3 ) AWS CodeBuild でビルドし、その生成物を S3 sync して、 Amazon CloudFront の invalidate を実行しキャッシュをクリアするだけです。 秘匿情報管理 System Manager の Parameter Store 秘匿情報を登録することで、 AWS CodeBuild、 Amazon ECS で 環境変数 に機密情報を渡せます。 Rails のcredentials機構を使うよりも楽で、ビルドやデプロイ時など必要な秘匿情報も合わせて一元管理できるのでとても便利でセキュアだと感じています。 インフラのコード化 これらの構成を AWS CloudFormation で作成しています。 AWS CodeDeploy のECS Blue Green Deployment については、未だ AWS CloudFormation 対応していないため、マネジメントコンソールから手動で登録しています。 この アーキテクチャ は、プロダクト固有の特殊な構成ではないので、今後 WEBアプリケーションの基盤構築の際に、再利用しやすくなっています。そして、今後新しく TERAS へ参加するメンバーもこの記事とインフラコードを見れば、全体像を把握することがでるので、システム知識のキャッチアップが速くなるのではないかと期待しています。 まとめ TERASの全体像と各項目について紹介させていただきました。インフラ構築・運用コストを抑え、かつセキュアでスケールするシステムを作る際の参考になれば幸いです。この構成はシンプルで、汎用性も高く、かつコード化をしているので、今後プロダクトが増えた際にも、割と簡単に再利用ができるのではないかと期待しています。 3月にリリースされ運用が始まりますが、まだまだ課題はたくさんあります。また、プロダクトが複数になったことで、セキュリティ・IAM管理・監視体制など、プロダクトごとにポリシーが乱立しないように、戦略立てて決めていく必要があるなと感じています。 最後に TUNAG、TERAS、新規プロダクトを横断的に設計・改善してくれるSREを募集しています! プロダクトが複数になり、TUNAG の大規模化に向けて一層インフラ面の強化を進めていくタイミングなので、とてもやりがいのある面白い仕事になると思います。興味を持ってくれた方は是非、下記のエンジニア採用サイトを見てください! スタメン エンジニア採用サイト インフラエンジニア募集ページ
アバター
皆様いかがお過ごしですか。スタメンでモバイル アプリ開発 を担当している @sokume です。 以前、 TUNAG iOSアプリの技術的な解説 で、 @temoki から発信していますが、読んでいただけましたでしょうか。今回の記事は Android 版の技術的な解説記事です。 言語 Google I/O 2019にて Kotlin-First 宣言があり、名実ともに Android の第1言語は Kotlin に変わりました。もちろんTUNAG Android アプリもすべて Kotlin で書かれています。 Kotlinも日々更新していますので、常に新しいバージョンに追従していける開発環境にしています。(記述時点ではv1.3.61ですね) アーキテクチャ ー Android Jetpack に含まれる、 Android Architecture Component をベースとした構成をとっています。 この構成をすべて利用するのでなく、TUNAG Android アプリに合わせた形で利用箇所の選定をしています。 サービス TUNAG は AWS + Ruby on Rails で構築していますが、mBaaS (mobile backend as a service) の Firebase を利用して以下の対応をおこなっております。 - Cloud Firestore 利用した即時性の高いチャット機能 - Authentication を利用したユーザー認証 - Crashlyitics を利用したクラッシュレポーティング - Cloud Messaging を利用したアプリへのプッシュ通知配信 - App Distribution を利用したアプリのベータ配信 (ここの点は TUNAG iOS と一緒の構成としています。) ライブラリ 前述の Android Jetpack や Android KTX はもちろん、 Material Components UI コンポーネント ライブラリを利用し、 Android に適したUIの実現を進めています。 HTTPクライアントは Retrofit2 と Okhttp3 を利用し、HTTPクライアントとからめて、 JSON パーサーは Moshi を利用しています。 画像の読み込みにもHTTPクライアントとからめて Picasso を利用しています。 (Square 製ライブラリが多めですね) Model層からのデータについてはリアクティブに処理するため、 RxKotlin / RxAndroid / RxJava を利用しています。 ReactiveExtensionを利用していますが、Activity/Fragment(View層)や ViewModel (ViewModel層)ではライフサイクル対応コルーチンスコープを利用し、ライフサイクルを意識した処理の制御が行える実装としています。 ベータテスト iOS アプリと同じく、スタメンのメンバー自身がTUNAGを利用しています。そのためアプリの新バージョンは常に社内メンバーでのテストを実施しています。 CIの仕組みは2019年11月に公開された Github Actions の利用を検討しています。 ワークフローという形で、 Github へのアクションを記述し、 Push や PullRequset といったユーザー操作をトリガーに、 ビルド実行 ➡ 配信 といった処理を継続的に実施する事ができます。 社内メンバーへの ベータテスト の配信は前述の Firebase App Distribution を利用して配信しています。 さいごに TUNAG Android アプリの構成を説明させていただきました。 TUNAG iOS アプリとはまったくの別のものとして考えず、同じ構成にすることで得られる利点などを考えた構成にしています。 もちろん、 Android アプリとして追求すべき点は妥協しません。 熱い仲間と一緒に、より良いサービスを一緒に作りませんか? 仲間を募集しています! まずは、スタメンのエンジニ アサイ ト stmn, inc. Engineers を見てみてください!
アバター
こんにちは! スタメンで Rails アプリの開発を担当しているシュール( @shule517 )です! Rails アプリケーションを安全に開発するために、自動テストはとても大切ですよね! スタメンではCircleCIで RSpec やRuboCopの実行をし、安全を確認して本番機へデプロイしています。 CircleCIの高速化を行った結果、倍速(約20分→10分)にすることができました!その時に対応したポイントを解説します。 CircleCIのキャッシュを有効活用! ① アセットプリ コンパイル 結果をキャッシュする CI全体の処理時間のうち4分の1はアセットプリ コンパイル に時間がかかっていました。アセットプリ コンパイル の結果をキャッシュすることで5分ほど高速化できました。 # プリコンパイルのキーを作成 - run : name : create assets precompile cache key command : | # プリコンパイル対象のファイルの最新コミット番号をキーにする git rev-parse $(git log --oneline -n 1 app/assets lib/assets vendor/assets Gemfile.lock | awk '{{print $1}}' ) > VERSION # キャッシュを復帰 - restore_cache : keys : # 同じバーションからキャッシュを復帰。無ければ、直近のキャッシュを復帰。 - assets-precompile-cache-{{ checksum "VERSION" }} - assets-precompile-cache- # プリコンパイルを実行 - run : name : assets:precompile command : | current_revision=VERSION previous_revision=public/assets/VERSION # プリコンパイル対象のファイルが変更されていなければスキップ if [ ! -e $previous_revision ] || ! diff $previous_revision $current_revision; then bundle exec rake assets:precompile cp -f $current_revision $previous_revision else echo "Skipped." fi # キャッシュを保存 - save_cache : key : assets-precompile-cache-{{ checksum "VERSION" }} paths : # プリコンパイル結果の保存先を指定 - public/assets - tmp/cache/assets ※実際に使用している設定ファイルとは異なります。 アセット関連のファイルが変更されていない場合はプリ コンパイル をスキップしたいため、キャッシュのキーをプリ コンパイル 対象のファイルの最新コミット番号にしました。また、アセット関連のファイルを修正している場合も、前回との差分だけをプリ コンパイル するようになるためかなり時間が節約できます。 プリ コンパイル の対象ファイルとして Gemfile.lock を入れている理由は、Gemをインストールした時にアセット関連のファイルが変更されることがあるためです。 アセットの変更がない場合、assets:precompileをスキップすることでCIの速度を改善した を参考にしました。 ② Gemのインストールをキャッシュする CircleCI上で使うGemをGemfileに記載し、インストールしたGemをキャッシュします。RuboCopの実行に必要なGemをキャッシュし、3分ほど速くなりました! group :test do # CircleCIでRuboCopを実行するために必要 # 修正前はCircleCIのタスクでgem installしていた gem ' rubocop ' gem ' rubocop-select ' gem ' rubocop-checkstyle_formatter ' end # キャッシュを復帰 - restore_cache : key : rails-gemfile-{{ checksum "Gemfile.lock" }} # Gemをインストール - run : bundle install --path vendor/bundle # キャッシュを保存 - save_cache : key : rails-gemfile-{{ checksum "Gemfile.lock" }} paths : - vendor/bundle restore_cache / save_cache の keyを checksum "Gemfile.lock" とすることで、 Gemfile.lock の内容が一致した時だけキャッシュを復帰します。つまり、 Gemfile を変更した時だけGemのインストールが実行されます。 CircleCI - キャッシュ設定の例 を参考にしました。 ③ ソースコード のチェックアウトをキャッシュする ソースコード をチェックアウトした後の.gitフォルダをキャッシュします。これで前回との差分だけをチェックアウトすればよくなり、30秒ほど速くなりました! # キャッシュを復帰 - restore_cache : keys : - source-v1-{{ .Branch }}-{{ .Revision }} - source-v1-{{ .Branch }}- - source-v1- # ソースコードをチェックアウト - checkout # キャッシュを保存 - save_cache : key : source-v1-{{ .Branch }}-{{ .Revision }} paths : - ".git" restore_cache に指定する keys を複数指定しておくと、その中から一番近いキャッシュデータを復帰してくれます。今回の設定方法だと、同じブランチのキャッシュを優先し、無ければ直近のキャッシュを使用します。 CircleCI - ソースコードのキャッシュ を参考にしました。 RSpec をバランスよく並列実行する! ① ボトルネック になっているコンテナを見つける CircleCIのTimingを見ると、分散している各コンテナの実行時間を見ることができます。16分台に完了しているコンテナもありますが、1番遅いコンテナに足を引っ張られてしまい全てが完了するのは22分です。処理を均等に分割できれば、20分以内に減らせそうですね!遅いコンテナから順に分散を検討しましょう。 ② ボトルネック になっているspecファイルを見つける RSpec の実行ログを保存することで、コンテナごとの各specファイルにかかった詳細な時間を見ることができます。どのspecファイルが ボトルネック になっているかを見つけます。 - run : name : bundle exec rspec command : bundle exec rspec --format RspecJunitFormatter --out test-results/rspec.xml - store_artifacts : path : test-results/rspec.xml ③ RSpec を高速化し、 ボトルネック を解消する ボトルネック のコンテナ、テストファイルを特定したら、 RSpec の高速化をしていきましょう!高速化で対応した内容がこちらです。 不要なテストデータを作らないようにする 不要なテストデータ作成で、テストの実行が遅くなっていた 未使用なテストデータを作っているletを削除した 使い回さないテストデータの場合は、`let!`→`let`に変更した E2Eテストのitをまとめる 1つのitで1つのexpecにするのは RSpec として正しいが、E2Eテストでは時間がかかりすぎる 同じ画面を確認する場合は、itをまとめることで高速化した itの中で複数のexpectを実行する場合は、`aggregate_failures`を使う RSpecでテストをまとめて検証する方法 を参考にしてください。 E2Eテストのsleepを削除し、have_xxxで待つ 画面描画を待つためにsleepをしていたため、テストが遅くなっていた テストケースの数 × sleepの時間だけ無駄な待ち時間が発生してしまう sleepの代わりにhave_xxxマッチャを使い、待ち時間を最小限に抑えた 巨大なspecファイルを分割する RSpec の分散はテストファイル(*_spec.rb)ごとに行っている 高速化を行っても、テストケースが多いテストファイルは ボトルネック になってしまう そのため、巨大なspecファイルを分割し、均等に分散できるようにした おわりに CircleCIの高速化を行い、待ち時間が減ることで開発のストレスがかなり減りました!みなさんがCircleCIを高速化する時に、参考になればとても嬉しいです。待ち時間を減らして、より楽しく効率的に開発をしていきましょう! 熱い仲間と一緒に、より良いサービスを一緒に作りませんか? 仲間を募集しています! まずは、スタメンのエンジニ アサイ ト stmn, inc. Engineers を見てみてください!
アバター
こんにちは。スタメンでモバイル アプリ開発 をしている @temoki です。 スタメンには 誕生祭 という制度があり、メンバーの誕生日には同じ部署のメンバーでお祝いし、その様子を弊社が提供しているサービス TUNAG で全社に共有しています。現在アルバイトで今年の春に新卒入社を予定しているエンジニ アメンバー のために昨年に実施した誕生祭では、プロダクト部ならではの手作りのお祝いをしたので、その様子をお伝えしたいと思います。 計画 TUNAG にはメンバーの誕生日を事前にお知らせする機能があります。そのお知らせをトリガーに本人を除いたメンバーで社内のチャットグループを作り、そこでひっそりと準備が始まります。 スタメンの企業理念は 一人でも多くの人に、感動を届け、幸せを広める。 です。この理念について弊社代表の加藤は「感動は相手の期待を超えることで生まれる」とよく私たちに伝えます。この誕生祭でも期待を超えるお祝いができるように、その人が最近話していたことや、TUNAG のプロフィール、最近の投稿の内容などの情報を集めながら、プレゼントは何にするか?どうやって祝うか?のア イデア を出していきます。 今回お祝いするメンバーは Android アプリエンジニアです。彼のデスクにはドロイド君フィギュアを置いてあったり、個人でも Android アプリをリリースしていたりと、 Android アプリ開発 への愛がとても強いです。そこで、 Android をテーマに皆でア イデア を出し合い、最終的には次のように計画しました。 プレゼントは Boothのドロイド君キャップ そのプレゼントをドロイド君から渡す!? バースデーケーキの代わりに Android のこれまでのバージョンのコードネームとなったお菓子を全て揃える 準備 計画を元に、各メンバーが準備を進めていきます。プレゼントは発注するだけですが、残りの2つのア イデア が大変です。 ドロイド君ヘルメットを作る プレゼントをドロイド君から渡すために、ドロイド君なりきりヘルメットを作ることにしました。元ネタはコチラ( あのドロイドくんヘルメット、設計書付きで誰でも手作り可 )です。 東急ハンズ や100円ショップをまわって材料を集め、本人がいない時を狙って制作を進めていきました。 半球状とディスク状の発砲スチーロールを組み合わせて整形 ドロイド君の緑色に近い水性アクリル塗料で塗っていく 塗りムラがないように入念にチェック デザイナーの手で目を入れて命を吹き込む Android のこれまでのバージョンのコードネームとなったお菓子 Android は 2009年にリリースされたバージョン 1.5 から、頭文字がアルファベット順で始まるお菓子の名前が付けられています。毎年 Android の新バージョンのリリース時期が近づくと、 Android アプリ開発 者の間では次のバージョンのお菓子は何かを予測する話題で盛り上がりますね。 このコードネームが昨年リリースされた Android 10 で廃止されたこともあり、この誕生祭のお祝いのケーキの代わりに、これらのお菓子を全て揃えてみようということになりました。前日にメンバーでいろんなお店を回ってなんとか全て集めることができました。 参考 (List of Android version names) Android ver. お菓子 1.5 C upcake ( カップ ケーキ) 1.6 D onut (ドーナツ) 2.0, 2.1 E clair (エクレア) 2.2 F royo ( フローズンヨーグルト ) 2.3 G ingerbread (ジンジャーブレッド) 3.0, 3.1, 3.2 H oneycomb (はちみつ) 4.0 I ce Cream Sandwich (アイスクリーム・サンドイッチ) 4.1, 4.2, 4.3 J elly Bean (ジェリー・ ビーン ) 4.4 K itKat ( キットカット ) 5.0, 5.1 L ollipop ( ロリポップ ) 6.0 M arshmallow (マシュマロ) 7.0, 7.1 N ougat (ヌガー) 8.0, 8.1 O reo (オレオ) 9.0 P ie (パイ) 当日 誕生祭の当日、オフィスフロアに突然バースデーソングが流れてイベントが始まります。まずはバースデーケーキの代わりの Android スイーツの登場です。ろうそくは Cupcake ( Android 1.5) にさして消してもらいました! 本人も驚きを隠せない様子 ずらりと並んだ Android スイーツ そして、さらなるサプライズ。 スペシャ ルゲストとしてドロイド君(をかぶったメンバー)が登場!ドロイド君になりきってプレゼントを渡しました。 ドロイド君は踊りが得意 だよ!というフリで華麗?なダンスも披露してくれましたよ。 I am Droid. 最後に本人から一言もらって誕生祭は終了です。この誕生祭を本当に喜んでくれて、来年からの正式入社に向けての意気込みを熱く語ってくれました!お祝いしたメンバーも、本人の笑顔を見てとても満足です。 さいごに 今回のエンジニアブログでは、普段はあまりお伝えできていないプロダクト部のハートフルなところをお伝えしましたが、いかがでしたでしょうか。プロダクト部では技術やプロダクトはもちろん、チームや仲間ひとりひとりに真剣に向き合いながら開発を進めています。弊社ではこのチームの中で一緒に働いてくれるエンジニアを募集しています。ご興味のある方はぜひスタメンのエンジニ アサイ ト stmn, inc. Engineers をご覧ください。
アバター
スタメンでエンジニアをしている 田中 です。 皆さんはJay Fieldsの著「 リファクタリング : Ruby エディション」という本をご存じでしょうか? リファクタリング における有名な本として、Martin Fowlerの著「 リファクタリング 」という本はご存じの方が多いかと思います。 こちらは当時広く知られていた Java (初版), JavaScript (第二版)でサンプルコードが書かれていました。 「 リファクタリング : Ruby エディション」は原著である「 リファクタリング 」を Ruby に対応させたもので、 Ruby の特徴を活かした リファクタリング の方法が載っており、一度は読んでみたいと思っていました。しかしながら、こちらの本は現在絶版のため入手することが難しいです。 今回、縁があってこちらの本をお借りする機会がありましたので、その内容についてご紹介します。 リファクタリング をする上で大切なこと いくつか良いなと思った文章があったので引用の形でご紹介します。 リファクタリング について リファクタリング とは、コードの外からふるまいを変えずに、内部構造を改良するようにして、ソフトウェアシステムを変えていくプロセスである。   コンピュータが理解できるコードなら誰でも書ける。優れた プログラマ が書くのは、人間が理解できるコードだ。   あなたが リファクタリング をしているのは、真実や美を追求するためではない。世界をわかりやすくして、ばたばたと揺れ動いているプログラムの支配権を再び握るために努力しているのである。 テストについて リファクタリング を始める前に、しっかりとしたテストが必要だ。   リファクタリング ではテストが命綱になる。 リファクタリング が成功するかどうかはよいテストが用意できているかどうかによって左右される。 パフォーマンスとの兼ね合い リファクタリング をしているときはわかりやすくすることに集中すべきで、パフォーマンスをあげることはその後で別の仕事として行うのが正しい方法である。 大切なことは ふるまいを変えない ふるまいを保証するためにテストを書く パフォーマンスはまた別のタスクとして考える 私自身は リファクタリング とパフォーマンスの役割の違いについては特に意識しないといけないと思いました。 リファクタリング 時にパフォーマンスも併せて考えてしまっていましたが、そもそもの目的が異なるので書くべきコードは違うものです。まずは リファクタリング によって分かりやすくした上で必要に応じてパフォーマンスチューニングといった流れを意識していきたいです。 具体的な手法 数多くの方法が紹介されていますが、ここでは3つに絞って紹介します。 サンドイッチメソッドの抽出 ほぼ同じ内容のコードのメソッドが2つあり、それらの違いがメソッドのちょうど中頃のみの場合に、重複部分を抽出してブロック付きのメソッドにする。 サンプル 変更前 def charge (amount, credit_card_number) begin connection = CreditCardServer .connect(...) connection.send(amount, credit_card_number) rescue IOError => e Logger .log " Could not submit order #{ @order_number } to the server: #{ e }" return nil ensure connection.close end end 変更後 def charge (amount, credit_card_number) connect do | connection | connection.send(amount, credit_card_number) end end def connect begin connection = CreditCardServer .connect(...) yield connection rescue IOError => e Logger .log " Could not submit order #{ @order_number } to the server: #{ e }" return nil ensure connection.close end end 前処理と後処理が必要なメソッドで具体的な処理の中身を変えたい場合に活用できそうだと感じました。ブロックを利用した書き方が Ruby っぽく、またこの書き方が私自身あまり使いこなせていないと思ったのでご紹介しました。 条件分岐の組み換え Ruby では nil ガードという書き方があるので、 三項演算子 で書かずに Ruby らしい書き方をしようということでした。この他にも Ruby で提供されている記法に則ることでよりシンプルに書けそうだと思いました。 サンプル 変更前 paraneters = params ? params : [] 変更後 parameters = params || [] 他の言語を経験していると、つい Ruby らしさを忘れた書き方をしてしまいがちなので、気をつけないといけないと思いました。 条件分岐のネストからガード節へ 正常な実行経路が分かりにくい条件分岐を持つメソッドがある場合に、すべての特殊条件をガード節で処理する。 サンプル 変更前 def pay_amount if @dead result = dead_amount else if @separated result = separated_amount else if @retired result = retired_amount else result = normal_amount end end end return result; end 変更後 def pay_amount return dead_amount if @dead return separated_amount if @separated return retired_amount if @retired normal_amount end 早期returnと呼ばれている方法ですが、全てのケースにて適用して良いというわけではありません。 たとえば、条件分岐があるときに両方とも正常なふるまいである場合はガード節へ移動してはいけません。 あくまで、異常系(特殊条件)は早期returnしようという考えのもとで上記のような リファクタリング を行う必要があるのだと思いました。 おわりに リファクタリング と Ruby における具体的な方法について、「 リファクタリング : Ruby エディション」をもとにご紹介しました。 Ruby らしさを活かした書き方が多くあり参考になりました。今回いくつかの方法をご紹介しましたが、その他にも様々な方法が載っています。しかし、今の私にすべての方法が必要かと言われるとそうとは言い切れません。扱うソフトウェアの規模の大きさや状態によって、何が必要かは変わってくるものだと思います。必要になる機会は今後必ずあると思うので、そのときに再度手にとって読み返したいと思います。 ただ、冒頭にもお伝えしたとおり、こちらの本は現在絶版のため入手することがとても難しいです。 復刊ドットコム というサイトにてリク エス トをすると、リク エス ト数によっては復刊されることもあるそうなので、ご興味ある方はリク エス トしていただけると幸いです。( こちら からどうぞ) 本記事を執筆中に復刊されました!!!元々の販売価格よりも高めではありますが、これまでなかなか手に入らなかったことを考えると新品でこの価格はかなりお買い得に感じます!!(私は注文しました!!) ご興味ある方は こちら からどうぞ!!! 最後になりますが、株式会社スタメンでは一緒に働くエンジニアを募集しています。ご興味のある方はぜひ エンジニア採用サイト をご覧ください。 Photo by João Silas on Unsplash
アバター
明けましておめでとうございます。エンジニアのミツモトです。 本年もよろしくお願いします。 ちょうど1年前、 Webアプリケーションエンジニアとしての1年目を振り返る を投稿しました。 あれから1年経過し、スタメンでの自身の役割に変化があったので、今回はその事をお伝えします。また、プライベートでは個人開発が進んだ1年だったので、そのことについても触れたいと思います。 目次 フロントエンドを担当 プロダクト・マネジメントを担当 個人開発 おわりに フロントエンドを担当 1年前はサーバーサイドエンジニアとして、 Ruby on Rails による開発を行っていました。体制の変更により、昨年2月からフロントエンドエンジニアとして React による開発を行い、スタメンで提供している TUNAG では、主にタイムラインと呼ばれる部分を担当しています。 転身した当時、フロントエンドエンジニアは自分1人でした。そこから少しずつ人数が増え、フロントエンドチームができ、現在は3人で開発しています。ちなみにフロントエンドチーム以外でも React による開発を行っており、社内で React を書けるエンジニアはこの1年で増えました。 初めは技術的な相談をできる人が少なく、 ソースコード を理解し、機能を追加するのに苦労しました。今は相談できる相手が社内にいるので、本当にありがたいです。「このコードは読みやすいか?」「 ディレクト リ構成をどうするか?」など、議論しながら日々開発をしています。 技術的な内容は、以前 Railsエンジニアがフロントエンド開発に入門してみた でもご紹介しましたが、TUNAG は元々 JavaScript × クラス コンポーネント でコーディングしていました。半年くらい前から TypeScript × 関数 コンポーネント で開発するようになり、状態管理は、規模の小さなものは React Hooks の useState、大きいものは Redux で行っています。 最近は、新規プロジェクトで Webpack による環境構築から React ・ Redux によるコーディングまで行っています。業務で SPA をゼロから構築するのは初めてですが、 package の選定・プロジェクト固有の仕様をどう実現するか?を考えながら実装するのは、とても面白いです。 プロダクト・マネジメントを担当 エンジニアリングを行う一方で、 TUNAG における新機能のプロダクト・マネジメントを担当する機会がありました。担当したのは、タイムラインの表示を最適化するため、関連した投稿をまとめるグループ機能です。 具体的な内容は割愛しますが、機能の対象・課題を整理して目的を定め、要件を定義し、リリース後にどのような効果があったか振り返る。という一連の流れを行いました。担当して感じたのは、要件を定義し、 UI に落とし込むことの難しさです。 エンジニアリングだと、基本的に明確なゴールがあり、それに向かい実装していきます。もちろん技術選定や細かな仕様で迷うことはありますが、最終的なアウトプットははっきりしています。プロダクト・マネジメントは、そのゴールをどこに置くか?を決めるものになります。最低限必要なものは何か考え、要件定義し、 UI として表現するのは、答えが1つではないため難しかったです。 また、実際にやってみると「折角この機能を作るなら、これもあった方がいいのでは?」という想像が広がります。それが過剰になると目的がズレます。すると、その後のデザイン・実装も大きくブレて、場合によっては作ったものが無駄になってしまうこともあります。常に「そもそも何がしたいか?」を振り返るようにしていました。 プロダクト・マネジメントに関して、以下の本がとても参考になりました。 リーン・スタートアップ ムダのない起業プロセスでイノベーションを生みだす 仮説を立て、それに基づく最小限の製品(機能)を作り、検証して次の施策を立てる。そのサイクルを早く回す。プロジェクトを進める上で、重要なことがたくさん書かれており、オススメです。 個人開発 プライベートでは、個人開発が進んだ1年でした。 最近はデスクトップ向けのToDoアプリを開発しています。 個人開発のアプリは、触れたことのない技術を試す最高の場所です。 今回のアプリでも、 * 認証: Firebase Authentication * 画像管理: Cloudinary * API : GraphQL など、やってみたかったことを試せました。 こういった技術を試すことで、ドキュメントやライブラリの ソースコード を読む機会が増えたのも良かったです。不明な点があれば、まず一次情報を見る習慣が付きました。より多くのユーザーに使ってもらえるよう、引き続き開発を進めていきます。 おわりに 最後までご覧いただきありがとうございました。 エンジニアがどんな風に働いているかお伝えできればと思い、自分の振り返りも兼ねて今回の記事を書きました。 スタメンでは新しいことに挑戦できる機会がたくさんあります。興味のある方は是非 こちら をご覧ください。
アバター
こんにちは。スタメンでバックエンドエンジニアをしております、 永井 です。 入社して半年ほど経ちますが、任される業務の幅や深さが広がっていき、毎日わくわくしながら働いています。 この前、 Ruby で生成したファイルを直接、 Ruby のGem 「 google -drive- ruby 」を使用して、社内で使っている Google Drive にフォルダ指定してアップロード(ダウンロード)する方法を勉強しました。 今回はその詳しい内容と方法を紹介したいと思います。 目次 「 google -drive- ruby 」とは? Google Drive の設定 「 google -drive- ruby 」でアップロードする おわりに 「 google -drive- ruby 」とは? 「 google -drive- ruby 」とは、 Google Drive にあるファイルや スプレッドシート に対して、書き込みや読み込みを行ったり、ファイルをアップロードしたりすることができる、 Ruby のGemです。 公式ドキュメントは こちら です。 Google Drive の設定 「 google -drive- ruby 」を使って、 Google Drive にファイルをアップロードするには、アップロード先の Google Drive から認証情報を取得しなければいけません。 ログインをするための認証情報には、「OAuth クライアント ID」と「サービス アカウント キー」の2種類がありますが、前者の「OAuth クライアント ID」を使用した認証方法は、手動で認証ボタンをクリックする必要があります。そのため、capybaraやwheneverを使った自動化処理、定期処理には向きません。今回は Ruby の方で自動化処理をしたかったため、後者の「サービス アカウント キー」を使用して、 Google Drive へログインします。 ちなみにサービスアカウントとは、人間ではない仮想的なアカウントであり、アプリケーションなどのシステムが使用します。詳しくは 公式ドキュメント を御覧ください。 それでは、まずサービスアカウントを作成するところから始めます。 1.   GCP(Google Cloud Platform) にログインします。 2. 左タブの「 API とサービス」から「認証情報」のページに飛びます。 3. 「認証情報を作成」というプルダウンから、「サービス アカウント キー」を選んで、認証情報をダウンロードします。   アップロードする前の準備として、認証情報で得た JSON ファイル内の private_email というメールアドレスを、アップロードしたいフォルダの共有に加えて、サービスアカウントに権限を付与する必要があります。 1.  アップロード先のフォルダから共有を選択 2. 先程のメールアドレスを入力 これで、アップロード先を指定することができます。   「 google -drive- ruby 」でアップロードする まず、「 google -drive- ruby 」をインストールします。 Gemfileに以下を記述。 gem ' google_drive ' bundle installを実行。 $ bundle install --path vendor/bundle これでGemがインストールされました。 ここから、 Ruby でアップロードをする処理に移っていきたいと思います。   private_key = OpenSSL :: PKey :: RSA .new( Rails .application.secrets.google_drive_private_key) client = Signet :: OAuth2 :: Client .new( token_credential_uri : Rails .application.secrets.google_drive_token_uri, audience : Rails .application.secrets.google_drive_token_uri, scope : %w( https://www.googleapis.com/auth/drive https://docs.google.com/feeds/ https://docs.googleusercontent.com/ https://spreadsheets.google.com/feeds/ ) , issuer : Rails .application.secrets.google_drive_client_email, signing_key : private_key ) client.fetch_access_token! GoogleDrive .login_with_oauth(client.access_token) ここでは、先程ダウンロードした、認証情報が含まれるファイル読み込んで、ログインするまでの処理を行っています。 client.fetch_ access _token! では Google サーバーから、アクセス トーク ンを取得してきます。そのアクセス トーク ンを login_with_auth メソッドの引数に渡して、 Google Drive にログインしています。 次に、フォルダを指定してファイルをアップロードしていきたいと思います。   # Google Driveにログイン session = GoogleDrive .login_with_oauth(client.access_token) # 適当にファイルを作成 file = File .open( " test.txt " , " w+ " ) # アップロードしたいフォルダを指定 folder = session.file_by_title( " sample_folder " ) # ファイルを指定して、sample_forlerにアップロード folder.upload_from_file(file, " hoge.pdf " , convert : false ) ログインした後、まずアップロードする先のフォルダを file_by_title でフォルダ名を指定して探します。もし指定したフォルダ名が存在しないければ、 nil が返ってきます。 そのフォルダに対して、 upload_from_file メソッドでアップロードを行います。第1引数にアップロードしたいファイル、第2引数に自由なタイトルを指定します。 ちなみに、第3引数に convert: false を指定すると、 Google Drive へアップロードした後に、 Google Drive の方でフォーマットを変更されずに保存されます。 アップロードが完了して、 Google Drive の指定のフォルダを見てみると、ちゃんとアップロードされていますね。 ダウンロードも同様に行うことができます。 # Google Driveにログイン session = GoogleDrive .login_with_oauth(client.access_token) file = session.file_by_title( " test.txt " ) # 引数にダウンロード先のパスを指定 file.download_to_file(path) file = session.file_by_title("test.txt") でダウンロードしたいファイル名を指定します。そして、 file.download_to_file(path) でダウンロード処理を実行します。 おわりに Ruby のgemである「 google -drive- ruby 」を使えば、簡単に指定の Google Drive フォルダにアップロードができました。今回は Google Drive API の知識を深めることができましたが、今後は、その他のG Suiteや、 GCP に関する知識も付けていきたいと思います。 最後に、株式会社スタメンでは一緒に働くエンジニアを募集しています。ご興味のある方はぜひ エンジニア採用サイト をご覧ください。 CPに関する知識も付けていきたいと思います。 最後に、株式会社スタメンでは一緒に働くエンジニアを募集しています。ご興味のある方はぜひ エンジニア採用サイト をご覧ください。
アバター
はじめに こんにちは!スタメンで内定者 インターン をしている梅村です。 内定をいただいてから今まで、約半年間 インターン を行ってきました。 今回は インターン の振り返りを記述しようと思います。 自己紹介 自分は現在、愛知県の大学に通う 修士 2年生です。大学では最適化を専攻していて、その中でも ポートフォリオ 最適化問題 に取り組んでいます。 今年の4月に20卒エンジニアとして内定をいただき、5月からプロダクト部で内定者 インターン をしています。 自分以外のスタメン新卒エンジニアは、 インターン から内定の流れでジョインしている人が多いですが、自分は内定から内定者 インターン の流れでスタメンにジョインしました。 大学の講義や研究でプログラミングは経験してきたものの、スタメンで インターン を始める前まで Rails に触れたことがありませんでした。しかし、半年間の インターン で、 Rails を用いた TUNAG の新機能開発・機能改善を行えるまで成長しました。 以下では、普段の1日のスケジュール、どのような業務を行ってきたかを記述していきます。 1日のスケジュール 9:15 - 出社 9:30〜9:45 - プロダクト部、所属チームで朝会 9:45〜12:30 - 開発 12:30〜13:30 - 昼休憩 13:30〜15:30 - 開発 15:30〜16:00 - プロジェクトミーティング 16:00〜18:00 - 開発 18:00〜18:30 - 日報作成 18:30 - 退社 以上が1日のスケジュールです。 朝会はプロダクト部全体で行った後に、チーム単位でも行います。機能のリリースがある場合は全体で共有します。チーム単位の朝会では、チーム一人ひとりのタスク共有・懸念事項の共有を行います。 朝会が終わった後は開発業務に取り掛かります。開発環境として会社から MacBook Pro の15インチ、30インチの4Kディスプレイ、キーボード、マウスが支給されます。自分は肩こり軽減のためにデュアルキーボードで開発しています。 昼休憩の過ごし方は人それぞれです。自分は、昼はよくコンビニへ行きますが、飲食店へ行ったり、弁当を持ってきている人もいます。デスクで作業しながら食事をしたり、共有スペースで会話しながら食事、家が近い人は家で食事している人もいます。 昼休憩が終わった後は、引き続き開発を行います。日々プログラミングばかりしているわけではなく、コードレビューやドキュメントの作成も行います。スタメンのプロダクト部では技術力だけでなく、読む力や ドキュメンテーション 力も磨けます。 終業時間近くになると日報を書き始めます。日報では、タスクの進行状況、今後の予定などから、最近学んだこと、日頃感じていることを記述します。 このような感じで1日が終わります。終業後はチームメンバーと勉強会をしたり、スタメンのオフィスを貸し出して もくもく会 を行うこともあります。 以下は、勉強会の写真です。 インターン ではありながらも、社員と同じ業務、勉強できる環境が揃っています。 これまでの開発内容 自分はWebアプリケーションチームに属していて、主にTUNAGの新機能開発、機能改善を行っています。Webアプリケーションチームでは、バックエンドは Rails 、フロントエンドはReactを用いて開発しています。自分は今までの開発業務では Rails しか触っていませんが、今後はReactも触る予定です。 インターン を始めて最初の業務は、プライバシーポリシーのリンク先のURLを変更する作業でした。簡単な変更でしたが、初めてのリリースだったので、とても緊張したのを覚えています。 しばらくしてある程度 Rails が触れるようになると、TUNAGの少し複雑な機能の修正を任されるようになりました。TUNAGに関してのお知らせを表示する際に、利用している機能ごとに出し分け、デ バイス ごとに出し分けできるようにする、という修正です。 それまでの業務は、ピンポイントで修正すれば済むようなものを行っていました。ですが、この修正部分は色々な箇所の影響を受けていた・与えていたので、そもそもどこを修正すればいいのか理解するまでに時間がかかりました。理解してからも、メソッドの書き方、画面の表示方法など、新しいことの連続で苦戦しましたが、とても良い勉強になりました。 その後は、新機能を2つ開発しました。新機能開発では、設計からモデル作成、コントローラー作成、ビュー作成を一通り行い、学んできたことの集大成となるような業務でした。新機能開発を通して、 マイルストーン の作成の難しさを実感しました。 マイルストーン 作成では、自分の持っているスキルと照らし合わせながら、何週間・何ヶ月先を考えます。自分は、 マイルストーン 作成時に細部まで考えきれておらず、途中で開発の流れの変更、最終的なリリース予定日をズラすことになりました。これからは意識的に前倒して開発していくことを心がけていきます。 インターン を通して学んだこと 半年間の インターン を通して、自分の意見を持つ重要さを学びました。スタメンは自社開発企業です。自分たちでサービスのロードマップを考え、開発していきます。より良いプロダクトを作っていくために、プロジェクトの責任者として意見を持ち、時には上司や他部署の人とも意見をぶつけ合わなければなりません。スタメンのプロダクト部では、ただ言われた通り開発するだけでなく、良いプロダクトを作るためにはどうすればいいか考えられるエンジニアを必要としています。 そこで自分はどうだったかというと、プロジェクトの責任者として意見を持てておらず、オープンな質問ばかりしていました。自分が意思決定をする場面でオープンな質問をしてしまい、何も決まらない。結局上司に聞いて遅れが発生する、ということが多々ありました。新機能開発ではこの問題が顕著に表れていて、成長した反面、課題も出てきたタスクとなりました。 ただ、 インターン の段階で自分の課題に気づけたのはとても幸運です。正式に入社するまでに、自分の意見を持つことを意識しながら開発を進めていきます。 おわりに スタメンでは若手に多くのチャンスが降ってきます。また、周りには優秀なエンジニアが多く、切磋琢磨できる環境です。自分は、 インターン の段階から多くを任せていただき、成長してきました。これから正式に入社して、良いプロダクトを作るために全力を注げる、成長していけると考えると、ワクワクが止まりません。 最後に、株式会社スタメンでは一緒に働くエンジニアを募集しています。ご興味のある方はぜひ エンジニアサイト をご覧ください。
アバター
スタメンのデザイナーの @kiyoshifuwa です。 先月、 Designship に参加してきました! セッション内容についての記事はすでに登壇者や参加者の方が出されていますので、本記事では特に印象に残った3つの学びについてお伝えします。 1.「あったらいいな」はやらない popIn株式会社 北村 崇さん「世界初を支えるデザイナーが考えること」のお話の中で、「あったらいいな」はやらない という話がとても印象に残っています。 開発を進めていく中で、あれもこれもやりたいというア イデア が色々出てくるのはよくあることです。しかし、安易に「あったらいいな」をやり始めてしまうと、「じゃあこれもやろう」「どこまでやるのか」などの議論が始まり収集が付かなくなります。 たくさんの仕事を限られた時間・リソースの中で順序よく進めるためには、「やるべきこと」を優先順位付けして進めていく必要があります。 これまでは、なにを基準に「やる」「やらない」を決めてよいのか自分の中で判断基準がなく、その都度プロジェクトマネージャーに判断を任せてしまっていました。 今回のセッションを受け、「やるべきこと」はやる、「あったらいいな」はやらないという明確な判断の軸が自分の中で生まれました。 また、「派生でできたもの」や「おまけ機能」はユーザーに取って必ずしも「親切」ではなく、かえって「不慣れ」を生み出し「不安」にさせてしまうことがあることも知りました。 それからは、「あったらいいな」が出てきたら、以下のふるいにかけて「やる」「やらない」の判断をするようにしました。 それはユーザーに求められていることなのか やらないと機能しないよ うな重 大な要素なのか 私たちの意志を込めることにおいて重要な役割があるのか 優先度が高い他のものを後回しにしてまで今やる必要があるのか   「開発途中で出てきたア イデア は全部無視!」ということではなくて、 やるべきだと判断したことは 「あったらいいな」から「やるべきこと」に明確に格上げする必要がある ということです。 自分の中で判断の軸がしっかり定まったことは、とても大きな収穫でした。 👇このセッションで学びました “世界初”を支えるデザイナーが考えること popIn株式会社 北村 崇さん スライドは こちら   2.考え方を考えてから考える   グロービス経営大学院 の論理力向上ワークショップに参加し、 「洗濯機の販売利益を上げるための施策を5分以内にたくさんリストアップする」という演習をしました。 私が参加したグループでは、とにかく急いでたくさんのア イデア を出さなければ!と、みんなで闇雲に思いついたア イデア を連ねていきました。その結果、途中でア イデア 出しが止まってしまいました。 また、包含関係にあるア イデア を並列に扱うなどしていたので、後から情報を整理して発表をすることが大変になってしまいました。 この演習で想定している最終的なゴールは、「ア イデア をたくさん出す」ことではなくその先の「施策を提案し、洗濯機の販売利益を上げる」ことですが、そこまで考えが至っていませんでした。 一方、ア イデア を効率的にたくさん出していたグループは この洗濯機が抱えている課題を想定する 「販売利益」とはなにを指すのかの確認 など、 前提 からし っかりメンバーで認識を合わせ、効率よく考えて ア イデア 出しを進めていました。順序立てて考えられているので、説明もとてもわかりやすかったです。 なにも考えず闇雲に進めていくよりも、課題や前提をしっかりと認識し、「考え方を考えてから考え始める」方が 無駄がなく効率よく進められる 主観や経験によって偏らない 説明がしやすい などのメリットがあることをこの演習で学びました。 このような内容は座って話を聞いているだけではイメージが湧かず「ふーん」で終わってしまいそうですが、失敗を経験したことで自分の中で深く印象に残ったので、実践形式で学べてとても良かったと思います。 👇このワークショップで学びました 【少人数限定】デザイナーのための論理力向上ワークショップ by グロービス 詳細は こちら   3.「優しいデザイン」=「使いやすいデザイン」だけじゃない クックパッド 株式会社宇野 雄さん「月間5,400万人を支えるユーザ体験への想いとその現実」は、「毎日の料理を楽しみにする」というミッションを実現するために、現実に対して「優しいデザイン」と「おせっかいなデザイン」という概念を用いているというお話でした。 「優しいデザイン」の例のひとつとして、「今月は10回も料理したよ」を「今まで10回も料理したよ」と表現するというお話がありました。 前者の「今月は10回も料理したよ」は、きちんとできたことを可視化して肯定しているのはとてもよいのですが、 「21回料理しなかった」ことも可視化 されてしまいます。 なので、あえて「今まで10回も料理した」という積み上げだけを伝えています。 事実は変わりませんが、「料理をしたこと」のみを伝えるようにしています。 「ユーザーにとって優しいデザインを作ること」が大切だということはもちろん理解しています。ただ、私の中には優しくする手段が「ボタンを押しやすくする」、「イラストで説明する」など、使いやすく・わかりやすくすることしかなかったので、ことばの表現や図の表現を変えるという手段は 目から鱗 でした。 それぞれのサービスのユーザーにとって優しいことは何なのか を考え、ことばや図を含め最適な表現をしなければならないと思ったセッションでした。 👇このセッションで学びました 月間5,400万人を支えるユーザ体験への想いとその現実 クックパッド 株式会社宇野 雄さん noteは こちら   さいごに 2日間 朝から夕方までインプット漬けで大満足のDesignship2019でした。 凝り固まっていた考え方が柔軟になり、一歩レベルアップした実感があります。 気になった項目があれば、 こちら で映像が配信されていますので ぜひご覧になってください! 株式会社スタメンでは一緒に働く仲間を募集しています。 こちら からぜひお気軽にお話を聞きに来てください!
アバター
はじめに こんにちは。スタメンでバックエンドの開発をしております、河井です。 今回の記事では、毎月開催されている社内勉強会について、運営方法を変えてみたこととその振り返りについて書いてみます。 背景 スタメンでは、毎月業務時間内に社内勉強会を開催しています。 今年の6月に運用を変えてみたのですが、それまでは社内勉強会の主催者は明確に決まっておらず、都度だれかが手を上げて主催して発表者を集める、という形でやっていました。 人数が少ないときはこれでも良かったものの、だんだんと人が増えてくるにつれて、発表者が偏ってきたり、会の時間が長くなりすぎたりと、誰か主催者を決めて運用ルールを決めないともたないな、と感じてきました。 というわけで、勉強会をより参加する価値のあるものにするため、社内勉強会の運用を考え直すことにしました。 勉強会で大切にしたいこと 今後の運用を考えるとき大事にしたことは以下の3点です。 業務での担当領域以外の情報を共有 エンジニアチーム全体でのコミュニケーション 純粋に技術を楽しむ 業務時間内で行うので、しっかりとやる価値のあるものにする必要があります。とはいえルールで縛りすぎて形だけになってしまうのは本末転倒なので、楽しく発表したり聞いたりできる場にしたいと思いました。 どう変えたか 最初に結論を書くと、次のようなルールで運用することにしました。 Webフロントエンド、ネイティブアプリ、Rails アプリケーション、バックエンドの各チーム※から代表者1人が発表 1人あたり15分 × 4人 発表ネタは必ずしも所属チームの技術に関連させる必要はない(業務で使った技術でも、趣味で勉強してみた内容でも) スライドや Qiita など何らかの形で資料にまとめること ※2019年12月現在、各チーム3、4人ほどのエンジニアが在籍 なぜチーム単位か 皆に発表する機会を与えつつ個人に負担をかけないようにするには、チームごとにして、チームの中でフォローし合えば良いのではと思った 個人単位で順番に回すとすると、業務が忙しい時期に発表が被るなどすると勉強会が負担になる 順番ではなく自由に発表するようにすると、発表する人や回ごとで発表者の人数が偏る 発表担当をチームごとに割り振ることで、話題の偏りをある程度避けられる 次回の発表担当を決めるついでに発表ネタの話をチームメンバーに相談できる といったように、ルールを設けつつも、自由を残す余地が生まれると考えたからです。 半年間運用してみて 参加したメンバーにアンケートを取ったところ、次のような声をもらっています。 良かったところ チームごとで決めることで、発表しない人も含めて全員が当事者になれるので、それが良い文化になっていると思う チーム内での情報共有は普段からしているから、勉強会がチームを超えた関わりの機会になっている 改善できそうなところ 技術的な解説をしっかりしようとすると15分では短いと感じる 発表に対するフィードバックがほしい などなど、良い点についてはチーム制ならではだと思うので当初の狙い通りの結果になったのではないかと思います。 改善点の1つ目については僕もなんとなく感じていたことで、今は月一回なので、隔週にして LT 会と長く話せる場をそれぞれ設けるのもアリかなーと考えています。 発表内容をいくつか 発表で出てきた内容をいくつかピックアップしてみます。 iOS と Android の User Interface の違いについて Gatsby + Netlify でブログを立ち上げるハンズオン 自作キーボード入門 Firestoreとセキュリティルール React Hooks ActiveRecord を読む ReactiveX入門 Maker Faire Tokyo 2019 参加レポート 業務に関連した話から、個人でやってみた技術的な趣味の話まで色々な領域の話が出てきています! 先日もこんな感じで盛り上がりました! 最後に 株式会社スタメンでは、一緒に開発する仲間を探しています。興味のある方はぜひ エンジニアサイト をご覧ください!
アバター
スタメンでエンジニアをしている 田中 です。 普段はRailsエンジニアとしてTUNAGの機能改善を行なっていますが、以前から挑戦したかったフロントエンド開発の機会をいただけたので、今回はフロントエンド開発に入門してみた感想を記述します。 フロントエンド開発環境 はじめに、スタメンのフロントエンド開発環境について説明します。 スタメンのエンジニアが作っている『TUNAG』の技術的な解説 にも一部記載がありますが、本記事ではフロントエンドにフォーカスを当てた内容で説明します。 使用言語 TypeScript: 3.5.3 主なライブラリ React: 16.8.6 React-Router: 5.0.1 Redux: 4.0.4 Redux-Saga: 1.0.5 styled-components: 4.3.2 ESLint: 6.2.1 Jest: 24.9.0 Enzyme: 3.10.0 元々はJavaScriptで記述していたこともあり、TUNAG全体ではJavaScriptのコードが多いですが、新規に作成する機能に関してはTypeScriptで実装しています。また今後はJavaScriptで書かれたコードをTypeScriptで書き換えることを計画しています。 入門してみた感想 React Hooksでスッキリ書ける React Hooksとは フック (hook) は React 16.8 で追加された新機能です。state などの React の機能を、クラスを書かずに使えるようになります 引用: フックの導入 – React 数ヶ月前からスタメンでも利用できるようになりました!(導入までの苦労話は こちら から) これまでクラスコンポーネントのみで利用できたstateやライフサイクルメソッドが、React Hooksの登場により関数コンポーネントでも書けるようになります。 コード例として、全く同じ挙動をするコードをクラスコンポーネントで書いた場合と関数コンポーネントで書いた場合を並べてみました。 基本 クラスコンポーネント class Welcome extends React.Component { render () { return <h1 > Hello , { this. props . name } < / h1>; } } 関数コンポーネント const Welcome = ( props ) => { return <h1 > Hello , { props . name } < / h1>; } 引用: コンポーネントと props – React state クラスコンポーネント class Example extends React.Component { constructor ( props ) { super ( props ) ; this. state = { count : 0 } ; } render () { return ( <div > <p > You clicked { this. state . count } times < / p> <button onClick={ () => this.setState ( { count: this.state.count + 1 } ) }> Click me < / button > < / div> ) ; } } 関数コンポーネント import React , { useState } from 'react' ; const Example = () => { // Declare a new state variable, which we'll call "count" const [ count , setCount ] = useState ( 0 ) ; return ( <div > <p > You clicked { count } times < / p> <button onClick={ () => setCount ( count + 1 ) }> Click me < / button > < / div> ) ; } 引用: ステートフックの利用法 – React ライフサイクルメソッド クラスコンポーネント class Example extends React.Component { constructor ( props ) { super ( props ) ; this. state = { count : 0 } ; } componentDidMount () { document . title = `You clicked ${ this. state . count } times` ; } componentDidUpdate () { document . title = `You clicked ${ this. state . count } times` ; } render () { return ( <div > <p > You clicked { this. state . count } times < / p> <button onClick={ () => this.setState ( { count: this.state.count + 1 } ) }> Click me < / button > < / div> ) ; } } 関数コンポーネント import React , { useState , useEffect } from 'react' ; const Example = () => { const [ count , setCount ] = useState ( 0 ) ; // Similar to componentDidMount and componentDidUpdate: useEffect (() => { // Update the document title using the browser API document . title = `You clicked ${ count } times` ; }) ; return ( <div > <p > You clicked { count } times < / p> <button onClick={ () => setCount ( count + 1 ) }> Click me < / button > < / div> ) ; } 引用: 副作用フックの利用法 – React ご覧いただいた通りですが、どの項目についても関数コンポーネントの方がスッキリと記述できます。React Hooksの話を聞いた当初は、「そんなに大きく変わるものなのかな・・・」と思っていましたが、実際に手を動かしてみるとシンプルで直感的に書けて良いな!ということがよく分かりました。 styled-componentsがとても良い styled-components とはコードの中にstyleを記述するCSS in JSのライブラリの1つです。 下記のようにコードの中にスタイルを記述することが出来るので、具体的にどこにスタイルが適用されているか分かりやすいです。 import React from 'react' ; import styled from 'styled-components' ; const Title = styled . h1 ` font-size: 1.5em; text-align: center; ` ; const Wrapper = styled . section ` padding: 4em; ` ; <Wrapper > <Title > Hello World , this is my first styled component ! < / Title> < / Wrapper > また、コンポーネントの継承により、共通のスタイルを適用できます。 const Button = styled . button ` padding: 12px; border-radius: 4px; ` ; const OKButton = styled ( Button ) ` backgroud-color: blue; ` ; const NGButton = styled ( Button ) ` backgroud-color: red; ` ; これまでスタイルを設定しても上手く適用できない場合、 idやclassの指定が間違っている BEMの記述が間違っている CSSの記述が間違っている のようなエラーになりうる要因がいくつかありましたが、styled-componentsでは適用したい箇所に直接指定できるため、CSSの記述に注力するだけで良くなりました。また、コンポーネント単位で設定できるため、影響範囲が限定的となり、スタイルの変更がしやすくなりました。 CSSに対して「上手く適用できない」「修正時の影響範囲が分かりにくい」といった苦手意識を持っていましたが、styled-componentsによってCSSと仲良くなれそうな気がします! ディレクトリ構成に悩む Railsでは rails new コマンドで基本的なディレクトリ構成が出来るため、生成されたとおりにファイルを配置すればよいのですが、Reactにおけるディレクトリ構成は開発者が設計する必要があります。そのため、世の中にはReduxの役割ごとに構成したり、Atomic Designに則った形で構成したりと様々なパターンが存在しています。 大雑把な説明となりますが、本記事を記述している時点における直近の開発では下記のような構成にしています。 Reduxの役割ごとにディレクトリを作成 Componentsディレクトリを作成し、その配下にcommonや各機能を入れる 過去の記事 とは異なる構成となっているので、その時々のベストな構成をこれからも探す必要があるのだろうなと思いました。 おわりに 初めてのフロントエンド開発はReact Hooksやstyled-components、そしてTypeScriptによる静的型付けによって効率的に開発を進めることが出来ました。言語の違いのみならず、様々な点でRailsとは大きく違いましたが、その違いを楽しみつつ開発することが出来ました。 また今回の開発をきっかけにフロントエンド開発にも携わるようになり、自身の技術領域が広がりました。こうした挑戦の機会がスタメンでは数多くあるため、成長できる良い環境だと改めて思いました。 最後に、株式会社スタメンでは一緒に働くエンジニアを募集しています。ご興味のある方はぜひ エンジニア採用サイト をご覧ください。
アバター