能動的に価値を追求!レバレジーズのシステム開発

f:id:s-nagasawa-lvgs:20210707183141j:plain

こんにちは、レバテック開発部の長澤です。 タイトルの通り、今回は私の所属部署でのシステム開発について一部をご紹介します!

執筆の背景

私は現在4ヶ月目の中途入社の社員です。 まだわずかな期間ではありますが、すでにレバレジーズのシステム開発は前職までの経験と大きく違うことを実感しています。

転職前は、作成された仕様書にのっとり「機械的」に「工場」の様に開発することを求められていました。 どちらかといえばトップダウンでシステム仕様を決めることが多く、開発者の意見が採用されることは多くありません。 そのためか 「使われない新機能」 「報告するためだけのドキュメント」 「固定化した開発プロセス」 「負債を抱えたレガシー技術」 「形骸化した会議」 などなど、本質的ではない事象をよく見てきました。

そのような環境から一転、私は今とても充実して開発をしています。 なぜ充実しているのか?それは開発プロセスに秘密があります。 今回は「4つのレバレジーズ開発現場の特徴」を皆様にご紹介します!

1、本質の追求

  • 予定していなかった新技術もプロダクトの価値が向上するなら、開発途中に導入することも珍しくありません。
  • 開発時に発生した課題は対話をベースに議論を交わします。無駄なドキュメントは作りません。

2、フィードバック

  • 開発したサービスは利用者が多く、リリース当日の新機能でも利用者がその日に反応し、開発した達成感が得られます。
  • マーケティングチームから数値ベースでフィードバックがあり、利用状況の良し悪しから、開発観点からも改善施策の提案も行います。

3、高い専門性

  • リードエンジニアやDBスペシャリストも在籍しており、高度な技術について議論する事もあります。
  • DDD/クリーンアーキテクチャ、マイクロサービス、gRPC等、社内標準技術スタックに、モダンでより専門性を必要とする技術を採用しています。

4、戦略共有

  • 開発メンバー会議以外にも、マーケター・セールス・デザイナーを含めた部署横断でのプロジェクト進捗共有を毎週開催し、開発内容や優先度に対する認識をすり合わせています。
  • 仮説と根拠が伴った中長期戦略から、開発の方向性の納得感を得ることができ、自信を持った開発をしています。

最後に

いかがでしたでしょうか。 ご紹介した内容は取り組みのほんの一部ですが、プログラミングだけでは無く、様々な部署を通じてプロダクトを成長させていくために、広い視点で開発をしていることを感じていただけましたでしょうか。 システム以外の知識も必要とされる場合もあり、課題にぶつかることもありますが、乗り越えた際は、顧客志向やマーケティング等のシステム以外の観点についても自身の成長を実感しています。

レバテック開発部では、一緒にサービスを作り上げてくれる仲間を募集中です! ご興味のある方は、以下のリンクから是非ご応募ください。 https://recruit.jobcan.jp/leverages/

新卒エンジニアが1ヶ月かけてマーケティングを学んだ話

はじめに

 こんにちは。21卒エンジニアの田中、五十嵐、益子です。

 エンジニアの新入社員向け研修といえば、開発に関わる研修を中心に受けるのが一般的だと思います。レバレジーズでは、エンジニアもマーケティング職と同じプログラムでマーケティング研修を受けます。約1ヶ月間、マーケティングの基礎の学習から始まり、最終的には顧客理解に基づいた「重視すべきサービスが提供する価値の定義と改善施策」の提案を行いました。

 本記事では、実際に研修を受けた体験談を通じて、なぜレバレジーズのエンジニアはマーケティング研修を受けるのか、どのようなことを学ぶのか、配属後の業務にどのように活きているのかを紹介します。

顧客理解×エンジニアリング="いいサービス"

 レバレジーズにとってマーケティングとは、「顧客のニーズを満たすこと」であり、顧客に最適な解決策を提供することです。

 なぜレバレジーズのエンジニアは、マーケティングを学ぶのでしょうか。

 レバレジーズは、セールス・マーケター・デザイナー・エンジニアなど、さまざまな職種が社内にいるオールインハウスの組織です。職種の枠を超えたスピード感のあるコミュニケーションや連携を通じて、様々な事業を展開しています。その中で、顧客理解に基づいた"いいサービス"を作り上げるために、エンジニアはマーケターやデザイナーの考え方を理解した上で、密にコミュニケーションをとり、実践する必要があります。

 そのためにエンジニアもマーケティング研修を受けることで、マーケターが業務でどんなことをしているのか、どういった思考のプロセスが求められるのかを学びます。

こんなメンバーがマーケティング研修を受けました

田中

「新規の事業作りをしたい思いが強く、オールインハウス組織で、若いうちから職域を広げた働き方がしたいと考え、レバレジーズへの入社を決めました。マーケティング研修はすごく楽しみでしたが、正直どんなことをやるのか想像つきませんでした。」

五十嵐

「会社として急激に成長しており新規事業に携われる可能性が高く、他職種との関係性が密なため、求められるスキルの幅が広いと考えレバレジーズへの入社を決めました。マーケティング研修を受ける前は、研修が楽しみな気持ちが半分と、内定者インターンをしていたチームから離脱することに対する不安が半分ありました。」

益子

「エンジニアとして技術を大切にしながらも、マーケティングや事業開発まで職能を広げたいと思い、レバレジーズへの入社を決めました。学生時代は受託開発企業で働きつつ、マーケティングのゼミで事業計画書を作り、役員への事業提案もしていました。3人の中でも特にマーケティング研修を楽しみにしていたと思います。」

どんなことをしたのか

 次に、マーケティング研修の内容について紹介します。この研修では、レバレジーズのマーケターがどんな考え方や方法で業務に取り組んでいるのかを学びました。実務レベルで実際のレバレジーズの事業部のデータ分析を行い、データを元にペルソナ設計からUX(顧客体験)改善施策立案まで行いました。

 具体的には以下のプログラムです。

  • ロジカルシンキング研修
  • マーケティング概論
  • ビジネスモデル研修
  • UX研修
  • プロモーション研修
  • オウンドメディア研修
  • CRM研修
  • データ活用研修
  • プロセス研修

 これらの研修を受けた後に、最終アウトプットとして、レバレジーズの既存サービス改善施策提案を行いました。

 改善施策提案では、顧客が求めていることや提供している価値から、サービスとしての理想状態を自分たちで定義しました。そして、顧客インタビューの記録や実際のデータ分析を通じて、最適な顧客体験を提供するために何が足りないか、どのターゲットに対して施策を打つべきかを特定しました。

 顧客体験を考える際には、研修で学んだペルソナ設計やカスタマージャーニーマップ設計などのアプローチを活用。定義した顧客体験を実現するための施策を立案し、営業現場のヒアリングや期待できる効果検証・工数の見積もりなども行い、顧客の理想や現場の実情に即したアウトプットにこだわりました。

研修を受けて感じたこと

田中

 元々、エンジニアでも事業課題の解決やサービス改善施策立案をやりたいと思っていたので、マーケティングの基礎を学ぶ時間があることはすごく貴重な時間でした。研修では、より良いサービスやプロダクトを作るために顧客理解が大事なことを学びました。    現在は、「ハタラクティブ」というメディアの開発を担当していて、チームで顧客インタビューを実施し、顧客理解に基づいた改善施策を実行しています。

エンジニアの立場でも顧客インタビューに積極的に関わらせていただき、職種を問わずチーム全体で顧客のことを考えた改善施策を進めていくことに、レバレジーズの良さが表れていて、僕が目指していた職域を広げた働き方ができています。配属されたばかりですが、さらにマーケティングの思考を生かした施策提案などにも挑戦していきたいです。

五十嵐

 研修でマーケターの実務に近い経験をさせていただいたのは、とても貴重な経験で、たくさんのことを勉強させていただきました。僕は現在、新規開発の事業部に所属しています。新しいプロダクトを作る上で、まず顧客に対してどんな体験や価値を提供するかを考え、それを実現するためにどんな機能が必要かを定義する必要があります。

 最初にUXをどれだけ深く考えられるかがその後のプロダクトの価値を左右すると考えているので、新規開発でもUXを意識して業務に取り組んでいます。エンジニアリングだけでなく、幅広い知識を身につけて業務に望んでいきたいという、選考時に抱いていたことを実際に経験できています。今後、サービスがリリースされたら、マーケティング戦略が本格的に動き出すため、その際に、研修で学んだことを更に活かし、開発業務の枠を超えたエンジニアになれるように挑戦を続けていきたいです。

益子

 自分はエンジニアの枠を超えて、課題定義から戦略・戦術の策定、さらには事業開発まで関わりたいと考えていました。そんな自分にとって、社内のマーケターから社内で取り組むマーケティングを網羅的に学べる機会は、非常に貴重なものでした。

マーティング研修で得た知見は、既に業務にも活きていると感じています。開発業務において、各事業課題が設定された背景に意識が向くようになり、「仮説に対しての検証施策に対し、細かな変更に対応できる記述になっているのか」などの新しい視点を持つようになりました。    開発業務以外では、エンジニアリング以外の職域にも挑戦するために、顧客ニーズ・顧客行動の調査などの積極的な情報収集を始めました。まずは何を目的に、どのようなタスクが動いているのか、業務の現状を理解することから取り組んでいます。顧客ニーズの調査方法や顧客行動の調査方法は企業によって異なりますが、マーケティング研修で実務のプロセスで調査手法を学んだことで、スムーズに必要な情報を理解することができています。エンジニアとしての職域にとらわれず、マーケティングを含め、幅広い面から事業に貢献できるプレーヤーになるため、今後も積極的な取り組みを続けたいと思っています。

最後に

 3人とも配属先での業務も異なるため、研修で学んだことの活かし方が異なりますが、顧客体験や施策背景といった様々な視点を持って開発業務に取り組むことで、確実にそれぞれにとってプラスになっています。

今後は、サービスやプロダクトを利用する顧客について理解した上で、「いいサービス」の開発に取り組み、社会に影響を与えられる人材となるために、切磋琢磨して日々努力していきます。

 「マーケティングも学びたい、若いうちから職域を広げて将来的に、事業をリードするエンジニアを目指したい」と考えている方は是非レバレジーズで一緒に働きませんか。お待ちしています! https://leverages.jp/recruit/

GraphQL + Apollo Client + TypeScript + React で型安全なフロントエンド開発を実現した話

はじめに

こんにちは。レバレジーズ株式会社の大滝です。

私は、レバレジーズのHRテック事業部に所属し、新規SaaSサービスのフロントエンド開発を行っております。

今回は雑然としがちな新規開発、とりわけフロントエンド開発で避けたかった4つの課題を、技術的な観点から回避していった点を紹介したいと思います。

新規開発で回避したかった問題

私たちの開発は新規開発でしたので、できるだけ技術負債を作らないように、かつスピード感を持って開発を行う必要がありました。 そこでフロントエンド開発を行う上で回避したかったポイントがいくつかあります。

  1. バックエンドとフロントエンド間でAPI仕様確認と管理に時間がかかる
  2. 型安全ではない
  3. 画面によってコンポーネントのデザインがバラバラ
  4. 入力動作が遅い

また前提として、開発中のサービス全体がマイクロサービスアーキテクチャを採用しており複数のサービス間がGraphQLで通信されていると言う特徴がありました。

図1 マイクロサービスアーキテクチャ構成図

フロントエンドはBFF(Backends For Frontends)に接続し、BFFではバックエンドのマイクロサービスのAPIの集約を行っています。

問題の回避方法と技術選定

上記した問題をクリアするには適切な技術選定を行う必要がありました。

しかし技術選定の難易度が高かったため、弊社のテックリードや開発メンバーと協力し調査を行いました。

結果的に下記の他のマイクロサービスで使用している技術と近く、かつ社内ナレッジがある程度蓄積されていると言う観点から、Apollo Client(graphql-codegen)/TypeScript/Reactを採用し、フォームライブラリとして、React Hook Formを利用しました。

これらの技術により、1の課題に対して、フロントエンドはBFFのエンドポイントからschema(APIの型定義)を取り込みそこからコードを生成することで回避しました。 また、schemaから生成したコードをもとに静的型付き言語であるTypeScriptを用いて実装を行うことで2の課題を回避しました。

3の課題に対しては、デザインの再利用性を高められるようにAtomic designを採用し、それに相性の良いReactを用いました。 さらに、動作速度向上のためにReact Hook Formという依存関係が少なく、軽量なライブラリを用いることで動作速度を向上させることで4の課題を回避しました。

画面実装までのフロントエンド開発フロー

上記の課題をクリアした実際の開発の様子を紹介します。 実際の開発では下記のようなフローで開発を行っております。

図2 実際の開発の手順

この開発フローに沿って、下記の画像のような簡単なユーザーの住所を変更する画面を実際に作ってみます。

図3 ユーザーの住所を変更する画面

GraphQL schemaの実装

サンプルのGraphQLスキーマを用意しました。 今回取り込むschemaはこちらです

type User {
  firstName: String 
  lastName: String 
  address: String 
}

type Query {
  user:  User
}

氏名、住所を持っているUser情報を取得するQuery型に入れます。 今回はサンプルなので1名分のUserを取得する形にします。

フロントエンドでのschemaの取り込み

次に、このスキーマをフロントから取り込みます。 まずはQuery情報を記載するgraphqlファイルを作成します

query userSearch {
  user {
    firstName
    lastName
    address
  }
}

これをGraphQL Code Generatorという機能を使用して、上記のgraphqlファイルのスキーマ情報を取り込みます。 GraphQL Code Generatorはcodegen.yamlにエンドポイントやgraphqlファイルのディレクトリ等を記載してスキーマ情報を読み込みます。

React Hook Formを用いたFormの実装

取り込んだスキーマを使用できるFormを実装します。 React Hook Formを用いてテキストボックスを実装してみます。

今回はMaterial-UIのMuiTextFieldを使います。

textFields.tsxに下記のようにMuiTextFieldをReact Hook Formでラップします。

仕様としてはFormのデフォルト値、ヘルパーテキスト、エラーメッセージが表示でき、nameをキー、入力値をバリューとしてsubmitできるものとしておきます

export type FormTextProps = TextFieldProps & {
  name: string;
  defaultValue?: string;
  showError?: boolean;
  rules?: Exclude<RegisterOptions, 'valueAsNumber' | 'valueAsDate' | 'setValueAs'>;
};

const TextFields: React.FC<FormTextProps> = ({
  name,
  rules,
  defaultValue = '',
  error,
  showError = true,
  ...textFieldProps
}) => {
  const { control, errors } = useFormContext();

  return (
    <>
      <Controller
        control={control}
        name={name}
        defaultValue={defaultValue}
        rules={rules}
        error={!!(get(errors, name) || error)}
        render={({ onChange, onBlur, value }) => (
          <MuiTextField onChange={onChange} onBlur={onBlur} value={value} {...textFieldProps} />
        )}
      />
      {showError && (
        <ErrorMessage
          errors={errors}
          name={name}
          render={({ message }) => (
            <FormHelperText error={true}>
              {message}
            </FormHelperText>
          )}
        />
      )}
    </>
  );
};
export default memo<FormTextProps>(TextFields);

コンポーネントを実装する際のポイントですが、下記の5点を意識しています。

  • Material-UIのTextFieldPropsの型定義を拡張してReact Hook Formで扱いやすくする。
  • nameタグはReact Hook Formでsubmitした際のkeyにあたるので必ずpropsとして注入するように必須にする。
  • rulesはReact Hook FormのRegisterOptionsの型定義から必要なものを集めてくる。
  • defaultValueは指定していないとwarningになるので空文字を初期値として設定する。
  • メモ化して無駄なレンダリングを減らす。

Form値に入力した値の表示テスト

最後に新住所を入力して入力値をconsoleで確認できるところまで作ってみます。

ユーザーの氏名を表示して、住所を新しく登録する画面を作成していきます。

見栄えをよくするためにスタイルも当てていきます。

const SamplePage: React.FC<{}> = () => {
  const methods = useForm<{ testTextFields: string }>({
    mode: 'onBlur',
  });
  const { handleSubmit, getValues } = methods;

  const onSubmit = () => {
     console.log('submit:', getValues());
  };

  const { loading, data } = useUserSearchQuery();

  useEffect(() => {
     console.log(data);
  }, [data]);

  return (
    <FormProvider {...methods}>
      <form onSubmit={handleSubmit(onSubmit)}>
        {!!loading && <>loading...</>}
        {!!data && (
          <>
            <Box display="flex" justifyContent="center" mb={2} mt={2}>
              ユーザーの住所情報を変更してください
            </Box>
            <Grid container alignItems="center" justify="center">
              <Grid item xs={8} style={{ backgroundColor: '#668bcd0f' }}>
                <Box m={2}>
                  <Grid container justify="center">
                    <Grid item xs={4}>
                      姓: {data?.user?.firstName}
                    </Grid>
                    <Grid item xs={4}>
                      名: {data?.user?.lastName}
                    </Grid>
                    <Grid item xs={8}>
                      現在の住所: {data?.user?.address}
                    </Grid>
                  </Grid>
                </Box>
              </Grid>
              <Grid item xs={6}>
                <Box mt={4}>
                  <TextFields
                    name={'newAddress'}
                    label={'新住所'}
                    rules={{
                      required: {
                        message: 'この項目は必須です',
                        value: true,
                      },
                    }}
                    defaultValue={data?.user?.address || ''}
                    helperText={'新しい住所を入力してください'}
                    variant={'outlined'}
                    fullWidth={true}
                  />
                </Box>
              </Grid>
            </Grid>
            <Box mt={3} display="flex" justifyContent="center">
              <Button type="submit" variant="contained" color="primary">
                更新
              </Button>
            </Box>
          </>
        )}
      </form>
    </FormProvider>
  );
};

export default SamplePage;

APIで取得したデータを表示する際は、codegenでgenerateしたファイルからAPIをfetchするuseUserSearchQueryをインポートして使用します。

ここで使用しているqueryのhookはGraphQL Code Generatorにtypescript-react-apolloのpluginを入れて生成されるもので、手間のかかるAPIのエラーのハンドリング部分の実装をせずにhookをimportするだけですぐにAPIを使用することができます。

useUserSearchQueryの実態をgenerateされたファイルで確認してみます

export function useUserSearchQuery(baseOptions?: Apollo.QueryHookOptions<UserSearchQuery, UserSearchQueryVariables>) {
        const options = {...defaultOptions, ...baseOptions}
        return Apollo.useQuery<UserSearchQuery, UserSearchQueryVariables>(UserSearchDocument, options);
      }

UserSearchQuery型がGenericsで渡されているので戻り値は型安全になっています、また使用する際はスニペットが効くのでかなり開発しやすいです。

空の状態でsubmitした際にはバリデーションがかかり、onBlurでもバリデーションがかかるように実装しています。この時にuseFormにGenericsで渡したFormの型がReact Hook Formに登録されます。

今回はMaterial-UIのBoxとGridを用いて画面を実装しましたが、これによりレスポンシブにも対応できる作りになっています。

まとめ

簡単ではありますが、新規開発等でも型安全にかつスピード感を持って開発できるような開発手法を紹介いたしました。

このように、GraphQLのスキーマから型情報を取得しTypeScriptとReactを用いて型安全な実装ができる上に、React Hook Formを用いることで簡単にFormの値の制御が行うことができるので非常に使い勝手が良いです。

HRテック事業部では一緒にサービスを作ってくれる仲間を募集中です!ご興味を持たれた方は、下記リンクから是非ご応募ください。 https://recruit.jobcan.jp/leverages/

Slack APIとLambdaの仕様による板挟みを回避した話

はじめに

こんにちは。レバレジーズ株式会社エンジニアの原田です。

私は、レバレジーズのシステムマネジメントチームに所属し、社内の業務改善のため、さまざまなWebサービスの導入や社内ツールの開発を行っています。

例えば、SlackとDocBaseのWebサービス同士のグループを同期させるツールを開発しました。いくつか問題が起きたことがあったので、どうやって対策したのかを紹介させていただきます。

DocBaseとは

DocBaseは気軽に書き込めるナレッジ共有サービスで、弊社では毎日数百件ナレッジが作られ共有されています。 このナレッジの閲覧権限はグループで管理することができ、ユーザーをグループに参加させることで簡単にアクセス権を管理することができます。

同期ツールとは

同期ツールは、AWSのLambda上で動作し、下記のイベントでグループの作成やリネーム、グループ参加者の管理を自動で行うツールです。

  • Slackチャンネルが作成された
  • Slackチャンネルがリネームされた
  • Slackチャンネルに誰かが参加した
  • Slackチャンネルから誰かが退出した

このイベントをもとに、Slackチャンネルと同名のグループをDocBase上に用意します。その後、Slackチャンネルに参加しているメンバーをグループ参加者として追加する動作を行います。

ただし、稀にSlackからのイベントを取得できないことがあり、「ナレッジを閲覧することができない」お問い合わせが発生することがありました。そのため、定期的にSlackチャンネルの情報をDocbaseに一括して同期するバッチ処理を追加で作ることにしました。

バッチ処理の内部動作

当初、バッチ処理は以下の図のように動作させることを考えていました。

早速バッチ処理用のLambdaを作成し、Slack APIを使って実装を行いました。 動作確認のためテストを行ったところ、次のような問題が発生しました。

  • 一定期間内におけるSlack APIの実行回数上限を上回ってしまう
  • Slack APIの実行回数上限を超えないようウエイト処理を挟むと、Lambdaの実行時間上限を超えてしまい処理が中断される

この時上限に達することを想定していなかったため、どのように問題を解決すれば良いかとても困った記憶があります。

なぜ上限に達したのか

Slack APIには毎分実行できるAPIの実行回数が設定されており、それを超えると429エラーが返ってくるよう設計されています。 なのでAPIを実行した後に2 ~ 3秒のウエイト処理を実行することでこの実行回数上限は回避できる、という仕様が存在します。

また、Lambdaは15分以上実行させようとするとタイムアウトしてしまい、処理が中断してしまうという仕様が存在します。

今回の追加開発では全Slackチャンネル情報が必要になるため、Slackチャンネル数分APIを実行する必要がありました。 この時、APIの実行が必要な回数は3,000回を上回っており、ウエイト処理を実行させると15分以上処理に時間がかかるため、Lambdaが途中で処理を中断させてしまうのです。

どのように解決したか

Slack APIとLambdaの仕様をチームメンバーに伝え、どのようにこの問題を解決するか相談したところ「1度にまとめてやろうとせず、処理を分割して行う」方針で解決する話になりました。

処理を分割すれば、Lambdaの実行時間上限を超えないようSlack APIを実行できるのでSlack APIとLambdaの仕様どちらも解決可能です。

こうして、同期ツールのバッチ処理開発を行うことができ「記事が閲覧できない」というお問い合わせを大きく減少させることができました。

もし、同じように困っている方がいましたら、参考にしていただけますと幸いです。

まとめ

今回の問題に遭遇したことで、予め上限や制約などがないか調べる癖を付けると良いなと実感しました。

レバレジーズでは、業務上の問題や課題は、一人ひとりの問題ではなく、チームメンバー全員の問題や課題として扱うことで自然と知見を共有できるため、すぐに問題解決が行えます。

システムマネージメントチームでは一緒にレバレジーズを支えてくれる仲間を募集しています!ご興味を持たれた方は、下記リンクから是非ご応募ください。
https://recruit.jobcan.jp/leverages/

チームブレストから8言語検索のコスト削減とUX最適化を両立させた話

はじめに

レバレジーズ株式会社エンジニアのカラバージョ(Caraballo)です。今回は、8言語(*1)で求人情報を提供しているメディアであるWeXpats Jobsで実装した多言語検索のコスト最適化についてご紹介します。

(*1) 2021年2月現在。

なぜコストの最適化が必要だったのか?

チームの目標として、ユーザーエクスペリエンス(UX)を向上させるために日本語で書いてある求人情報を複数の言語で検索できるようにする必要がありました。

私たちのチームでの最初のアプローチは、Google translate APIを使用して各求人情報を翻訳し、Elasticsearchにインデックスを付ける予定でした。これは簡単なアプローチのようにみえますが、APIの費用が100万文字あたり$20USDであり費用対効果が低いことに気付きました。 月額だと約 $4,000USDの費用がかかる計算です。

この問題にどのように取り組んだのか?

まずはじめに、ブレインストーミングを行い問題を根本的なものに集約しました。つまり、「日本語のテキストデータを元にして他の言語での検索を効率的に行う方法は何か」ということです。

たとえば、次のテキストのように 「東京でReactを使用したフロントエンドエンジニアとしての職務」の中から、仕事を探すという文脈で意味を伝えたい重要な部分は [東京、React、使用、フロントエンドエンジニア、職務 ] の名詞であり、[で,を,した,としての]を省略しても他の言語に翻訳したときに元のテキストの意味をほとんど反映できます。

したがって、各求人情報の名詞を抽出して翻訳することで翻訳の必要があるAPI呼び出しの数が減る、さらに記事の間で何度も使われている名詞の翻訳結果をキャッシュすることでさらに翻訳の数を減らすことができました。 その結果、翻訳されるデータが多くなるほど、辞書が増え必要なAPI呼び出しが少なくなっていきます。

計画は次のとおりでした。

  1. 日本語のテキストをtokenizeし、名詞のみを抽出する。
  2. 抽出した名詞を共通の辞書に保存し、必要に応じて各言語に翻訳を追加する。
  3. 翻訳された名詞を準備して、Elasticsearchの各言語のインデックスを作成する。

実装

上記の要件を実装するために、今回はGolangを利用しました。

  1. 日本語の原文を取り込む
  2. トークナイザーを使用して名詞のみを抽出する
  3. 新しい単語がある場合、辞書に保存する
  4. 翻訳されていない単語を翻訳する
  5. 複数の言語でのテキストBLOBを作成する
  6. Logstashを使ってElasticsearchにデータを取り入れてインデックスを作成する

日本語をトークン化するために、次のgolangライブラリを使用しました。

github.com/ikawaha/kagome/tokenizer
Kagome Japanese Morphological Analyzer(https://github.com/ikawaha/kagome/tree/master)

例:
package main

import (
    "fmt"
    "github.com/ikawaha/kagome/tokenizer"
)

func main() {
    nouns := GetTokens("東京でReactを使用したフロントエンドエンジニアとしての職務")
    fmt.Printf("%v", nouns)

}

func GetTokens(content string) []string {
    t := tokenizer.New()
    tokens := t.Analyze(content, tokenizer.Normal)
    var nouns []string
    for _, token := range tokens {
        if token.Class == tokenizer.DUMMY {
            continue
        }
        feature := token.Features()[0]
        switch feature {
        case "名詞":
            nouns = append(nouns, token.Surface)
        }
    }
    return nouns
}

output

[東京 React 使用 フロントエンドエンジニア 職務]

まとめ

今回は、多言語検索のためのコスト最適化の例をご紹介しました。 翻訳された名詞の辞書を作成することで、API呼び出しの数を大幅に減らすことができ、将来的にはWeXpats Jobs以外のサービスでも多言語検索サポート機能を使用できるように拡張していく予定です。

レバレジーズ株式会社では現在、サービスを開発し、優れたユーザーエクスペリエンスを作成するための新しい仲間を探しています。興味のある方は、こちらのリンクに応募してください。
https://recruit.jobcan.jp/leverages/

設計書メンテで消耗しないためにIDL開発へ切り替えた話

はじめに

こんにちは。レバテック開発部の園山です。私は、レバテック開発部のビジネスサポートグループに所属し、システム開発業務を担当しています。

本記事では、開発効率を向上するためにインターフェイス定義言語 (IDL) ベースの開発に切り替えて、設計書管理を行わず、どのように開発を行っているかについてご紹介します!

レバテックにある主な3つの機能として、「営業支援・管理ツール」「ユーザーや取引先企業が使用する登録者向けサービスシステム」「オウンドメディア」があり、現在、それらを1つの統合化したプラットフォームに集約するプロジェクトにおいて業務設計・開発を進めています。

今回のリプレイスにあたり、システム開発をより良いものにするためにはどうしたら良いかメンバー間で日々意見を出し合っており、そこで生まれた案をひとつずつ取り入れています。その中のひとつとして、ドキュメントで設計書を管理する体制を廃止しました!

これまでのやり方

これまでは、設計者が設計書をドキュメントに起こし、ドキュメントに書かれたものを実際の開発担当者が読み、開発を行っていました。複数の施策が同時並行で進んでいることが多く、仕様を更新する場合は設計担当者が設計書のコピーを作り、仕様を更新して、差分がわかるように打ち消し線や文字列の色を変える工夫をしていました。 結果、原本のドキュメントのメンテナンスが後回しになってしまったり、特殊仕様の認識合わせにコミュニケーションコストがかかっていました。

インターフェイス定義言語 (IDL) ベースの開発とは

インターフェイス定義言語 [IDL: Interface Definition Language](以下、「IDL」という) とは、特定のプログラミング言語とは別にオブジェクトのインターフェイスを指定するために使用される汎用言語のことで、本プロジェクト内では gRPC のプロトコルバッファーから IDL を使用しています。 プロトコルバッファー以外にも、OpenAPI(Swagger)、GraphQLや、Apache ThriftなどがIDLでSchemaを定義する技術になります。

プロトコルバッファーIDL は、オープンな仕様を持つプラットフォームに依存しないカスタム言語で、開発者は、入力/出力共にサービスを記述する .proto ファイルを作成し、API仕様を決めることができます。 これらの .proto ファイルはクライアントとサーバーの言語を生成できるので、TypeScript ⇄ PHP といった複数の異なるプラットフォームで通信でき、開発時にも.protoファイルを共有することで、コードの依存関係を取得することなく他のサービスを使用するためのコードを生成することが可能になっています。

IDLベースの開発は社内でも前例がありませんでしたが、1人が起案したところから始まり、サンプルコードを元に勉強会を実施して、実際に開発を進めながら習得していきました。 複雑な責務を持つマイクロサービスの場合、事前にある程度知識を深めるための資料を用意して、関係者間で認識合わせをしてから着手することもありましたが、基本はIDLベースで問題なく進めることができています。

違いを比較してみると

実際の開発シーン別にその違いを比較してみます。

開発あるある①:仕様が途中で変更になる
既存 設計者が設計書を更新 → 開発者が設計書を見て実装を修正
IDL 設計/開発者がIDLを更新 → 開発者はコード生成を再実行し、コンパイルエラーが発生していたらエラーを解消

開発あるある②:開発を分担していて連携部分が心配
既存 片方の実装が終わるまで動作確認ができず、実装完了後に不具合が見つかったりする
IDL IDLからコードを生成するため、定義通りのスキーマになることが保証され、連携部分の心配がなくなる

開発あるある③:設計書の管理が難しい
既存 施策単位や特殊仕様に応じてドキュメントが増えがちで、設計書をメンテナンスする優先順位が低く管理が行き届きづらい
IDL IDLから設計書を出力する(手動で差分を最新化する手間がなくなりました)

以前よりも変化を迅速にシステムへ反映させていくことができるようになりました!

やってみた感想

設計者と開発者の垣根がなくなったことが大きく、開発をしながら改善していく楽しさを実感しています。 デメリットは、慣れるまでの学習コストやある程度の設計スキルが必要なことで、I/Fだけでは見えない仕様パターンの考慮をどのようにメンバー間でコミュニケーションを取りながら進めていくかなど、チームルールを整備する必要がある点も課題に感じています。

まとめ

今回は開発効率を向上するためにインターフェイス定義言語 (IDL) ベースの開発に切り替えたことについてご紹介しました。IDL開発について何か1つでも参考になる点があれば幸いです。社内では、デメリットに挙げた点についても改善などの提案が常に行われており、解決へ向けて積極的に取り組んでいるため、また別の機会にご紹介できればと思います。

レバテック開発部では、一緒にサービスを作り上げてくれる仲間を募集中です! ご興味のある方は、以下のリンクから是非ご応募ください。 https://recruit.jobcan.jp/leverages/

プレイド社とマイクロサービス勉強会を開催しました~コロナ禍でも楽々!クローズド+リモート勉強会のススメ~

こんにちは。レバレジーズ株式会社のテックリードの竹下です。

2021/1/13に、KARTEのサービスを運営しているプレイド社とマイクロサービスに関して、合同勉強会を開催しました。

今回は、クローズドかつ、リモートで勉強会を開催したため、リアル開催やリモート一般公開と比べてどのようなメリットがあったかをご紹介していきます。

目次

開催の経緯

これまでレバレジーズでは、社内勉強会に加え、セミナールームを勉強会に貸し出すことで、社外とも交流を持ち、エンジニアが学べて成長できる環境を作ってきました。しかし、コロナ禍によって勉強会が全てリモート開催になったり、延期になってしまったことで、勉強会の機会が減っていました。

社内での勉強会は、社内の知見共有が中心となってしまうため、世の中の技術トレンドや、他社での取り組みを知る機会は多くありません。自社でリモート勉強会を開催しようと考えましたがレバレジーズは技術的な知名度がまだ低く、集客は難しいと判断しました。(開催したのはいいけど、社員しか参加してくれなかったら悲しいじゃないですか……😥)

一般公開では集客が難しそうなので、TypeScriptでマイクロサービス化という技術スタックをすでに運用しているプレイド社に「2社で勉強会をしませんか」と声をかけたのが開催の発端です。

勉強会の内容

勉強会のタイムスケジュールは、「マイクロサービス」をテーマに15分程度で発表を2社2名づつ行い、その後に懇親会という流れで行いました。 それぞれのタイトルと資料は下の通りです。(敬称略、公開確認取れたもののみ掲載しています)

  1. レバレジーズ株式会社 住村 「マイクロサービス五里霧中」
  2. 株式会社プレイド 大矢「Tilt.dev を使ったリモート k8s 開発環境」
  3. レバレジーズ株式会社 竹下 「開発効率爆上げを目指したインフラ技術スタック構想」Slide@Prezi
  4. 株式会社プレイド 山内「アンチパターンから学ぶマイクロサービス」

私の発表に関しては詳しい内容は、また後日改めてブログに書きますので、ご期待ください

クローズドで内容が充実する

業務に密接に関係する話ができる

クローズド開催にすることで、関係者は2社のみとなるため、お互いが興味のあるテーマの勉強会を開催することが可能になります。一般公開の勉強会を行う場合、集客性や、世の中のエンジニアの人に役に立つような内容にすることを考慮する必要があるため、幅広い人に興味を持って貰えるようなテーマ設定になりがちです。

今回プレイド社とは、「TypeScript」と「マイクロサービス」という2つの共通点があり、マイクロサービスに関する勉強会を開催する運びになりました。

踏み込んだ話ができる

クローズド開催になったことで、内容に関しても踏み込んだ内容まで発表することができました。一般公開した場合、その分野に詳しくない人も来ることが想定されるため、その人達にもわかるような発表をする必要があり、どうしても本題に入るのが遅くなってしまいます。

しかし、2社間だと前提とする知識が共通してあるため本題の説明に時間を多く割くことができました。(そのため、発表スライドだけを見てもらうと端折られているように感じる部分があるかもしれません。)

さらに、発表内容自体も「開発環境をどう作っているか」や「どんな失敗をしたか」「どういう挑戦をしているか」など、一般論に終始しない実務に根ざした内容が盛りだくさんになっていて、普通の勉強会ではなかなか聞くことの出来ない内容になっています。

懇親会も濃密

勉強会では発表も重要ですが、懇親会も発表を補完する機能を持っています。クローズドだとお互い実務に携わっている人が多く参加しているので、発表者の人に話を聞くだけでなく、他のエンジニアに実務の中でのノウハウを聞いたり、あるある話に花を咲かせることも可能です。また、プロジェクトマネージメントや気になっている技術のことなど、普段なら相手のバックグラウンドを探ってからでないと聞きにくいようなことも聞きやすく、勉強会のテーマ以上のことを学ぶことも出来ました。

開催者も参加者も楽できる

周知や参加者管理が楽

私は前職で隔週でScala勉強会の会場係を7,8年務めていたりScalaMatsuriの運営を5,6年手伝っていましたがが、参加者を集めたり内容を企画するのにいろいろ苦労をしてきました。connpass, atendなどのイベント告知サイトにイベント登録をしたり、発表を募ったり、キャパ制限があれば先着順や抽選をしたり、リアル会場なら会場への入場方法を案内したり、懇親会の店をとったりと様々な雑務が必要です。

しかし、クローズド+リモートにすることでそれらの手間がかなり軽減されました。周知はイベントサイトなど作る必要が無く、お互いの会社でSlackやメーリスで流すだけとなり、全員リモート参加なので人数制限や会場への案内も不要で、懇親会のお店の予約や準備もいりませんでした。そのため、勉強会の開催ノウハウや人員がいなくても手軽に開催が可能になります。今回は、初回ということもあり入念に準備しましたが、それでもミーティングが約1時間半くらいと、接続テスト30分程度で準備が出来ました。

勉強会を継続するには、勉強会の運営や管理をしていく人が必要になりますが、これぐらいの労力なら片手間に出来るので、ひとりでも継続して開催することができます。

リモートなので参加者が楽

リモートになったことで参加者も楽になっています。リアル開催の場合、会場へ移動する必要があるためどうしても参加障壁が上がります。今回は、19時開始でしたが、もしプレイド社(銀座本社)で開催した場合、レバレジーズは渋谷に本社を構えているため、遅くとも18時30分には退社し会場に向かう必要があります。懇親会含めると22時30分くらいまでやっていたため、家につくのは23時を過ぎてしまいます。レバレジーズ社とプレイド社ならまだ近いですが、大阪や福岡にある会社の場合は、そもそも参加すらできません。

しかし、リモートだと移動時間が全くなくなるので拘束時間は実際の勉強会と懇親会の時間だけになり、もし急遽業務の割り込みが合ったとしてもすぐに業務に戻ることも可能です。そのため、気軽に参加してもらうことが可能になり双方の参加者を増やすことが出来ます。

初めての発表の人も気が楽

私もはじめはそうでしたが、見ず知らずの人の前で発表をすることは初めての人にとってはハードルが高いものです。しかし、2社間クローズドにすることで半分は自社の人で見知った人も多くいるため、発表になれていない人にとっても発表がしやすいです。また、当日は資料を完全公開にする必要が無いため後で手直しも可能なため、発表慣れしていない人にとっては嬉しいかしれません。

まとめ

今回は、レバレジーズが現在取り組んでいるマイクロサービス化について、すでに運用して1年ほど経つプレイド社の知見を大いに学ぶことが出来ました。プレイド社のハマったポイントや、レバレジーズで現在抱えている問題をどのように解決しようとしているかなど、知りたいと思っていることを学ぶことが出来ました。 私もいろいろな勉強会に参加していますが、通常は入門的な内容が多かったり、今知りたいことと少しずれてたりするので、ここまで密度の高い勉強会はなかなか経験がありませんでした。

これは、クローズドかつリモートという形式を取ったことの効能だと感じました。 また、勉強会の開催の手間も少なく、ハードルも低いため頻繁かつ定期的に開催することも出来ると思います。今後もひとつ一の取組みとしてクローズドかつ、リモート形式での勉強会を継続して開催し、エンジニアの技術力UPの一助にしていきたいと思います。

レバレジーズと勉強会しませんか?

現在レバレジーズでは、マイクロサービス化、TypeScriptの導入、gRPCの採用、DDDやクリーンアーキテクチャの採用、Vue.js/Reactの導入、IaCによるインフラ管理など様々な技術スタックの刷新を行っています。もし、同じような技術を持っていたり、導入を考えている方いたら竹下や的場にご連絡ください!是非、一緒に勉強会を開催しましょう。

お問い合わせはこちらにお願いします。

レバレジーズの4事業を支える基幹システムのPMとは?

はじめに

こんにちは。プロジェクトマネジャーの丸山です。

最近、プロジェクトマネジメントに関する記事をたくさん見かけますが、 「社内システムのプロジェクトマネジメント」のテーマはそこまで出回ってないように思います。

そこで今回はレバレジーズの社内システムのプロジェクトマネジメントがどのように行われているのかを紹介します。

年商150億円を支える基幹システム!

プロジェクトマネジメントについて紹介する前に、対象のシステムについて紹介させて下さい。 営業活動を行う時に利用するシステムを社内の事業部に提供しています。この類を営業支援システム(SFA)と呼び、有名なものではセールスフォース・Hubspotなどがあります。

このシステムは、顧客管理・案件管理・進捗管理・書類管理・金銭管理といった基本的なSFAに付いている機能に加え、 連絡機能(電話・メール・LINEなど)やマーケティング情報の管理機能なども付いているので、営業だけでなく事業の全領域を支援しています。(よって基幹システムと題させて頂きました)

このシステムは、1つで4事業を支えています。看護師さんの紹介事業・派遣事業・介護士さんの紹介事業・派遣事業の4つです。 看護業界と介護業界の中身は異なる部分が多いですし、紹介事業と派遣事業のビジネスモデルも全く違いますが、人材業界という大きな枠組みでシステムを設計したことで、4事業の全領域を管理するシステムを作ることが出来ました。

このシステムが支援する4事業の年商は約150億円です。社員による月間アクセス数は250万PVとなります。

基幹システムの全体像

プロジェクトマネジメントをどのように行っているか??

それでは本題に入ります。 まず、全体の流れを簡単に示すと下記のようになります。

プランニング → 要件定義 → 基本・詳細設計 → 実装・テスト → 導入 → リリース → 効果測定 → プランニング

このサイクルをおよそ1ヶ月スパンで回しています。関係者が全員社内にいるため、コミュニケーション調整コストがほとんどかからない社内システムだからこそ、この短いスパンを実現出来ています。

それでは、各プロセスについて具体的に説明します。

プランニング

今後どのようなシステム改修をしていくか、4事業の責任者と話し合い、優先順位を付けます。

4事業の売上を最大化するために、事業部の全活動の中でどこを改善するべきか(事業課題)を話し合います。事業課題の中でシステム改修によって解決可能なものがあれば、「このような改修によってこの事業課題がこのくらいのインパクトで改善される」などの提案をします。採用された提案には優先順位を付け、優先度の高い改修のスケジュール調整を行います。

採用された提案の多くがプロジェクトマネジャー発案であり、エンジニアが考えてプロジェクトマネジャーに提供した提案も採用されています。 ほとんどの改修で、自分達がやる価値があると思ったことに取り組んでいるので、開発チーム全体のモチベーションが高く保てています。

要件定義

プランニングで決まったシステム改修案の要件を定義します。

関係者を集め、どのような改修をしようとしているか説明を行い、実現可能性・課題解決の方法・役割分担などについて話し合います。

営業関連の改修なら営業部のリーダー、マーケティング関連の改修ならマーケティング部の担当者と、集める関係者は改修案によって異なります。同じ会社の仲間なので、協力的に動いて下さる社員が多く、様々な領域のプロフェッショナルと意見交換が出来るので視野が広がります。

基本・詳細設計

要件をベースに基本・詳細設計をします。

基本・詳細設計は画面設計・入出力設計・機能設計・DB設計の4つで構成されています。画面設計は、関係者に要望をヒアリングしたりプロトタイプを見せたり意見交換をしながら行います。

要望をヒアリングした際に、4事業の文化の違いやチーム体制の違いにより意見が割れることも多いです。この場合には全ての要望を汲み取るべきか取捨選択するかを意思決定し、対立意見の関係者を説得することで意見を収束させます。

4事業を支えるシステムだからこそ、難しい部分もありますが、この苦労があるからこそ4事業を支えるシステムが成立します。

また、DB設計では4事業間の整合性を担保するために抽象化・正規化が適切に行われているかを細かくチェックします。最初は上手く設計が出来ませんでしたが、経験を経て上手く設計出来るようになりました。DB設計が上手くなっただけでなく、概念的に考える力がかなり成長したと感じます。

実装・テスト

実装・テストの実行はエンジニアが担当します。 プロジェクトマネジャーの担当はスケジューリングとサポートです。

開発チームでは2週間ごとに区切りをつける、スプリント形式で開発しています。スケジューリングとは、プランニングで定めた改修のスケジュールを守れるようにタスクをスプリントに割り振っていくことです。サポートとは、エンジニアに要件や基本・詳細設計を説明をしたり、開発で発生した問題を解決したり、実装・テストを進めるための支援活動のことを指します。

複数のプロジェクトが並行して走ることが多く、スケジューリングやリソース調整は大変になりますが、「スケジュール通りに開発を進める」という、プロジェクトマネジメントで一番基本となる能力の成長に繋がりました。

導入

全てのシステム利用者に改修内容を説明します。

要件定義や基本・詳細設計において全てのシステム利用者を巻き込むことは時間制約上不可能なので、改修に関する情報があまり伝わってないシステム利用者もいます。

その方々に向けて、「どのような事業課題に対してどのような改修をしたのか」を説明します。システムの最大の目的は「4事業の売上の最大化」であるため、それに基づいてシステム利用者が表面上不便に思う改修を行うこともあります。 そのような場合にも改修の理由を事前に説明して理解してもらうことで、システム利用者との信頼関係を保つことが出来ます。

システムへの関心が薄い相手もいる中で、まんべんなく理解を得ることは難しく、このプロセスも初めは苦戦しました。ただ、試行錯誤を重ねる内に分かりやすい説明と共感性の高いメッセージの発信が身について、システム改修について大部分の人に理解してもらえるようになりました。

リリース

エンジニアがリリース作業を行っている間にプロジェクトマネジャーがSlackでリリースを告知します。改修の内容が良ければ、告知後すぐにSlackのスタンプや称賛のコメントがたくさん返ってきます。反応が薄いときはもっと頑張らないとなと思いますし、反応があったときには純粋に嬉しい気持ちになります。

効果測定

システムのアクセスログや売上データを分析して、事業課題の改善インパクトを測定します。インパクトがプランニングで提案した時の基準に達していれば、システム改修によって事業課題を解決出来たと評価されます。

事業課題を解決出来た場合の達成感は、リリース時以上のものです。 リリース時の反応は重要であるものの指標の1つでしかなく、チームの最終的な使命は「事業課題の解決」と、それによる「4事業の売上の最大化」だからです。

事業課題を解決出来た際は、チームSlackで成果を共有し、達成の喜びを分かち合います。数多くの困難を共に潜り抜けたチームメンバーと共に達成の喜びを分かち合えたときは、全ての苦労が報われたように感じます。

効果測定が完了すると、その結果を踏まえて更なる事業課題が無いかプランニングで検討します。効果測定からプランニングに繋がることで、プロセスが循環しています。

まとめ

レバレジーズの基幹システムのPMとは、一言で表すと「4事業の売上最大化を目的としたシステム改修の仕掛け人」です。この仕事には主に2つの魅力があります。

システムの規模が大きいこと

扱っているシステムは基本的なSFAの枠組みを大きく超え、連絡機能(電話・メール・LINEなど)やマーケティング情報の管理機能などもついているので、事業の全てを支える基幹システムと言えます。そのうえ、ひとつのシステムで4事業、年商約150億円を支えています。

裁量権が大きいこと

システム開発において、上流として位置付けられる要件定義だけでなく、事業の責任者と事業課題を話し合うプランニングから参加しているため、システム開発に関する全ての意思決定について関わることが出来ます。

事業を成長させたい思いで自ら考え動かした改修案が、リリースされシステム利用者に喜ばれる、嬉しい経験も味わうことが出来ました。

4事業が関わるシステムなので、難しい意思決定が多いですが、コミュニケーション力や論理的思考力などのスキルの成長に繋がりました。

We are Hiring

レバレジーズではプロジェクトマネジャー・エンジニアを募集しています。 興味を持って頂けた方はこちらからご連絡頂けると幸いです。

https://recruit.jobcan.jp/leverages/

社内電話アプリをChrome拡張機能からElectronにリプレースした話

はじめに

こんにちは!エンジニアの呉です! 今回は社内で開発している電話アプリについて、Chrome拡張機能からElectronへリプレースした話をご紹介します。

リプレースしたきっかけ

■問題点

社内で開発している電話アプリでは、いくつかの問題が顕在化していました。

  • コードの見通し
    • 電話という特異的な機能に加えて、Chrome拡張機能独自のお作法によりコードの見通しが悪くなっていた
  • 手動リリース
    • ウェブストアのダッシュボードから審査の申請をする必要がある
  • リリースコントロールがしにくい
    • Chrome拡張機能の自動更新が最大5hのタイムラグが生じる(chroniumの対象コード)
    • ウェブストアの審査が介入するため、リリースが手間

■解決手段

今回これらの問題を解決する手段として、Electron + Vue.jsでリプレースをすることを決めました。

  • コードの見通し
    • 社内で知見の多いVue.jsを採用し、学習コストを低減
    • 言語はTypeScriptを採用し、型宣言による開発効率、保守性の向上
    • 表示のコンポーネント化、機能のモジュール化を行うことにより、それぞれの責務を明確にし、コード全体の見通しを向上
  • 手動リリース
    • GitHub Actionsを利用し、リリースを自動化
  • リリースコントロールがしにくい
    • electron-builderのelectron-updaterパッケージを使い、自動更新タイミングを自分でコントロールできるように解決
    • AWS S3へのアップロードを行うだけのため、審査介入によるリリースコストダウン

出来上がったもの

今回出来上がったものを簡単なご紹介します。

■Electronアプリケーション

主なディレクトリ構成は以下の通りです。

src
 ├── assets
 ├── background
 ├── components
 ├── constants
 ├── models
 ├── plugins
 ├── router
 ├── services
 ├── store
 └── views
ディレクトリ名 役割
assets グローバルで利用するCSSやフォント、ロゴなどのリソース
background Electronアプリケーションのライフサイクル制御とプロトコル設定、バージョンチェックなど
components 表示部品単位でのコンポーネント定義
constants 通話で利用する結果コードの定義や外部イベントの定数を定義
models APIや通話で利用する連絡先などのモデル定義と型定義
plugins API通信で利用するaxiosやGoogle OAuth、Slackなど外部ライブラリのラッパーを定義
router ページのルーティング定義
services 業務ロジックをサービス層として抽出し定義
store モジュール単位での状態管理とアクション定義
views ページ単位単位でのコンポーネント定義

■リリースフロー

リリースフロー

これまで他のプロジェクトでは、CircleCIを使ったリリースの自動化をしていましたが、今回はGitHub Actionsを使ったリリース方法を採用しました。

理由としては、

  1. 社内でElectronを使ったプロジェクトが多く発足する可能性を考慮し、プロジェクト独自のリリースフローで良いと判断
  2. GitHub Actionsのワークフローを定義のみで設定作業のコストを削減
  3. CircleCIのmacOSビルド環境(executor)を追加で契約する必要があったため、ランニングコストを削減

よかった点

今回電話アプリの開発を実際に担当している身として、前述の問題点に対してどうすべきなのか、どうしたらやりやすくなるのかをエンジニアサイドから考えた上での行動に起こしました。

■運用保守コスト

結果として、リプレースによる利用者の満足度を向上させるようなダイレクトなインパクトはありませんでしたが、エンジニアサイドの心理的安心感や運用保守コストダウンにより、間接的に利用者に機能提供するまでの開発効率を向上することができました。

■スキル

ElectronやGitHub Actionsなど社内でもあまり導入実績のない技術に対して挑戦することで、個人の成長を実感することができました!

■意思決定

今回のリプレースの提案に対しても「イイじゃん!イイじゃん!」と共感してもらった上で、その場で「じゃあいつまでにできそう?」とスピード感に若干驚きました(笑)

ボトムアップの提案に対してもスピーディに対応し、承認までの間隔が短く、提案することに対して億劫にならない環境だなーと私個人としてとても印象に残りました。

大変だった点

…ここまで良いことばかり書いてきましたが、もちろん良いことだけではありませんでした。

■Twilio

元々前任者がベースを開発していたこともあり、全容を完璧に把握できていたわけではなかったので、動作が変わらないように全体を見渡す時間がとてつもなくかかってしまいました。

■Appleの公証

macOSを利用している方もいるため、Appleの公証(アプリ署名)を行う必要がありました。

Apple Developer Programからの証明書発行、発行した証明書を用いてビルド・リリースの自動化で苦戦をしました。

最終的にはelectron-notarizeを使うことで解決しました。

まとめ

いかがでしたでしょうか?

社内のカイゼン事例として、社内電話アプリのChrome拡張機能からElectronにリプレースした話をご紹介いたしました。

今回エンジニアによるボトムアップからの提案に対してスピーディに実現ができたことが素直にとても嬉しかったです。

みなさんも「やりたい!」と思ったことをまずは声に出してみるところから始めてみてはいかがでしょうか。

We are Hiring

レバレジーズでは、一緒に今をより良くしていく仲間を募集しています。

弊社に少しでも興味を持っていただいた方は是非ご連絡いただけると幸いです。

https://recruit.jobcan.jp/leverages/

ネイティブアプリをFlutterにフルリプレイスした話

はじめに

こんにちは!エンジニアの藤野です! 今回はキャリアチケットが運営するキャリアチケットカフェのiOS/Android向けアプリをFlutterにフルリプレイスした話をご紹介します。

なぜFlutterに移行したのか

元々アプリ開発はiOSはSwift, AndroidはKotlinを使用し、開発を進めていましたが、開発を進めていく上で生産性が上がらない問題が発生していました。

最終的には1人でモバイル開発をしていたため、iOS/Androidのどちらとも実装する時間がなく、iOSのみに実装するといったOSによって機能が違うアプリになっていました。また、1人で仕様検討から実装・リリースまでを行っていたため、開発効率も下がっていました。レビュワー等もいなかったため、プルリクエストを投げた後もセルフマージで対応していました。

以上のような問題を解消するために、Flutterにフルリプレイスすることを決めました。Flutterにした理由は下記2つです。

  • 未経験でも取っ付き易い
  • OS関係なくひとつの言語で統一できる

他のチームメンバーにもFlutterを触ってもらうことで、開発できる人を増やせること、さらに生産性を向上させるメリットがあったため、Flutterへのフルリプレイスへ踏み切りました。

構成

今回使用した状態管理手法やディレクトリ構成を紹介します。 状態管理の手法はChangeNotifier+Providerパターンを採用しています。ProviderはPragmatic State Management in Flutter (Google I/O'19)で公式に推奨されています。 設計当初にはまだ登場していませんでしたが、最近はStateNotifier+Freezed+Providerを使った状態管理やRiverpodの登場などがあります。

ディレクトリ構成

ディレクトリ構成は以下の通りです。どのような役割になっているかも、ひとつずつ簡単に説明しています。

lib
 ├── config
 │    └─ route.dart
 ├── models
 │    └─ tmp
 │      ├─ tmp.dart
 │      ├─ tmp.freezed.dart
 │      └─ tmp.g.dart
 ├── resources
 │    ├─ api
 │    │   └─ tmp_api_provider.dart
 │    └─ repositories
 │      └─ tmp_repository.dart
 ├── screens
 │    ├─ common
 │    └─ tmp
 │      ├─ widgets
 │      └─ tmp_screen.dart
 ├── services
 ├── utils
 ├── assets
 ├── viewmodels
 │    ├─ common
 │    └─ tmp
 │      └─ tmp_view_model.dart
 └── main.dart
  • config
    • 設定ファイルを配置(ルーティングを設定するファイルなど)
  • models
    • 作成するモデルのディレクトリを作ってその中にdartファイルを配置する
    • freezedパッケージを使用
  • resources
    • api
      • APIとのやり取りをするファイルを配置
      • ファイル名 : xxxx_api_provider.dart
    • repositories
      • apiからデータを取得して各モデルの形にマッピングしたり、データ送ったりする
      • ファイル名 : xxxx_repository.dart
  • screens
    • 各画面ごとにディレクトリを作成してその中にファイルとwidgetsディレクトリを作成する
    • 基本的には1画面1screenを作成し、widgets配下に画面内で使用するWidgetを配置
  • services
    • 主に外部サービスとの連携周りの処理を書いたものを配置
  • utils
    • 定数などのファイルを配置
  • assets
    • 画像などの素材系を配置
  • viewmodels

構成は以上のようになっています。伝わりづらい部分はサンプルとして、 APIからデータを取得して表示するだけの簡単なアプリを作成しながら詳しく説明します。

1. パッケージの導入

providerパッケージやfreezedパッケージを利用するため、pubspec.yamlを以下のようにします。

dependencies:
  flutter:
    sdk: flutter
- cupertino_icons: ^0.1.3
+ provider:
+ freezed_annotation:
+ http:

dev_dependencies:
  flutter_test:
    sdk: flutter
+ json_serializable:
+ build_runner:
+ freezed:

2. モデルの作成

モデルはFreezedパッケージを使用しているため、以下のように書いた後に以下のコマンドを実行します。 flutter pub run build_runner build これによって event.freezed.dart ファイルと event.g.dart が生成されます。 詳しいFreezedパッケージの使い方はドキュメントを参照してください。

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:json_annotation/json_annotation.dart';

part 'event.freezed.dart';
part 'event.g.dart';

@freezed abstract class Event implements _$Event {
  factory Event({
    int id,
    String title,
    @nullable @JsonKey(name: 'created_at') String createdAt,
  }) = _Event;

  factory Event.fromJson(Map<String, dynamic> json) => _$EventFromJson(json);
}

3. APIからの取得処理実装

APIから実際にデータを取得する部分をプロバイダーとして作成します。

import 'package:http/http.dart' as http;

class EventApiProvider {
  Future getAll() async {
    return await http.get('http://localhost:8080/events');
  }
}

Repositoryでは上記で作成したプロバイダーから取得したデータをモデルの形にマッピング

import 'dart:convert';
import 'package:sample/models/event.dart';
import 'package:sample/resources/api/event_api_provider.dart';

class EventRepository {
  Future<List<Event>> getAll() async {
    final _response = await EventApiProvider().getAll();
    return json.decode(_response.body)
        .map<Event>((json) => Event.fromJson(json))
        .toList();
  }
}

4. ViewModelの作成

ここで先ほど作成したEventRepositoryを利用してデータを取得し、画面側へ結果を伝えます。

import 'package:flutter/material.dart';
import 'package:sample/models/event.dart';
import 'package:sample/resources/repositories/event_repository.dart';

class EventViewModel extends ChangeNotifier {
  final EventRepository _repository = EventRepository();
  List<Event> events = [];

  Future getAll() async {
    events =  await _repository.getAll();
    notifyListeners();
  }
}

5. Screenの作成

ボタンを押すとAPIからデータを取得し、取得結果を表示するような画面を作成します。 ボタン押下時にViewModel側の getAll() を呼び出してデータを取得、notifyListeners() が呼び出されたタイミングでListViewが再描画されます。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sample/viewmodels/event_view_models.dart';

class EventScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    EventViewModel _viewModel = Provider.of<EventViewModel>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Event List'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            FlatButton(
              onPressed:() async => _viewModel.getAll(),
              color: Colors.lightBlue,
              child: const Text('取得'),
            ),
            Expanded(
              child: ListView.builder(
                itemCount: _viewModel.events.length,
                itemBuilder: (BuildContext context, int index) {
                  return ListTile(
                    title: Text(
                        "${_viewModel.events[index].id} : ${_viewModel.events[index].title}"
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

6. main.dartの修正

各画面の遷移先は以下のようにルーティングファイルに記載します。

import 'package:flutter/material.dart';
import 'package:sample/screens/event_screen.dart';

final Map<String, WidgetBuilder> routes = {
  '/': (BuildContext context) => EventScreen()
};

そして MaterialAppのroutesに先ほど作成したルーティングを設定することによってEventScreenが呼び出されるようになります。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sample/config/route.dart';
import 'package:sample/viewmodels/event_view_models.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      child: MaterialApp(
        routes: routes,
      ),
      providers: [
        ChangeNotifierProvider(create: (context) => EventViewModel()),
      ],
    );
  }
}

完成!

取得ボタンを押すと、APIからデータを取得し表示します。

実際にリプレイスしてみて

メリット

  1. 開発工数の削減 Flutterで開発すると、SwiftとKotlinの別々で書いていたときよりも 体感4割ほど工数が削減できました。ただ、全て共通で同じ実装をできるかと言われるとそうでなく、OSに依存する機能などは個別に実装する必要があるため、その部分に関しては工数がかかってしまいます。 また、Flutterの大きな特徴の一つであるホットリロード機能がとても便利で、開発中のデバッグにかかる時間がかなり削減されました。

  2. 未経験でも開発が容易 今回のリプレイス作業は、自分以外のチームメンバーのほとんどがFlutter未経験者でしたが、全員スムーズに開発をすることができました。公式ドキュメントが充実していて、UI部分に関してもSwiftやKotlinよりも簡単に実装ができるため、Flutter未経験でも簡単にモバイルアプリを開発することができます。

  3. UIの実装が簡単 こちらもFlutterの大きな特徴ですが、UI部分は全てWidgetで構成されており、マテリアルデザインなどといった、様々なデザインが備わっています。そのため、ある程度のデザインであれば公式のライブラリを使用することで、簡単に実装をすることが可能です。

  4. コードレビューが楽 SwfitやAndroidではそれぞれStoryBoardとレイアウトファイル(xmlファイル)で実装していたため、コードレビューの際に変更箇所の差分が分かりにくく、苦戦していました。Flutterに移行したことで、変更箇所が分かりやすく、コードレビューが楽になりました。

デメリット

  1. OSに依存する機能などは個別に実装が必要 OS依存の機能を使わない場合に関しては、特に問題ありません。通知機能やAppleサインインなどのOS依存の機能を実装する場合は、その部分に関する知識が必要になってきます。 また、リリース時もiOSとAndroidで証明書が異なるため、別々に管理することが必要となります。

  2. パフォーマンスを考慮した設計 さまざまなWidgetを組み合わせて簡単にUI部分を実装することは可能ですが、何も気にせずに実装を進めていくと、ネイティブよりもパフォーマンスが低下し、アプリ実行時に全体的にもっさりした感じになります。そのため、複雑なUIなどの場合にはパフォーマンスを考慮して設計をしなければいけません。

開発を行ったチームメンバーの感想

モバイルアプリ開発未経験にも関わらず、リプレイス作業に携わってもらったチームメンバーから、以下のような感想を貰いました。

  • 環境構築がそこまで複雑ではないのですぐに開発をすることができた
  • UI部分はWidgetがHTMLみたいで直感的で書きやすい
  • UIパーツを組み上げる感覚で、思ったほど苦労せずに実装できた
  • Flutterの進歩が早い
  • 開発がスムーズ進められた(デバッグツールが使いやすい、ホットリロード機能最強!など)

メリットでもあるUI部分に関しては、直感的で未経験者でも開発がしやすいとのことです。

Flutter移行中に起きた問題とその対応

Flutterに移行する際にさまざまな問題等に直面したので、その内容と解決方法、注意点などをいくつかご紹介します。

UI設計をあらかじめした方が良い

今回は画面単位で担当者を割り振ったため、コンポーネント化出来るWidgetがコンポーネント化されていなかったりと煩雑になってしまいました。他にもContainer Widgetを使う実装があったので、パフォーマンスなども考慮して適切なWidgetを使うことをおすすめします。

iOSのバージョンアップデートには事前に対応しておくべき

今回iOS側にはUniversal Links機能を実装していたのですが、アプリのリリース前にiOSのバージョンがiOS 14に上がったことでAssociated Domains周りの仕様が若干変更されました。そのため、サーバサイド側の設定変更を余儀なくされました。 iOSの次バージョンがリリースされる3ヶ月ほど前の6,7月頃にはBeta版がリリースされるので、事前にBeta版を使ってあらかじめ対応するべきでした。

審査に落ちてしまった

iOS側の話が続きますが、いざリリース申請に出したところ、App Store Reviewガイドラインの5.1.1(xi)に引っ掛かっている理由のため、リジェクトにされました。 ガイドラインに引っ掛かった原因は、アプリ内に下記のようなRSSから記事を取得して表示する機能を実装していたものの、その中に「新型コロナウイルス」に関する記事が表示されていたため、リジェクトされていました。

そのため、サーバサイド側でRSSから記事を取得する際、タイトルに「新型コロナウイルス」に関する単語が入っている場合は表示しないよう、一時的に対応しました。その後、もう一度審査に出したところ、何の問題もなく審査を通過しました。 外部からの取得したデータを表示する場合などは、念のため気をつけましょう。

リリース周りが大変

iOSとAndroidではデメリットのひとつ目にも記載していますが、リリースまでの手順が異なるため、OSごとに対応が必要となってきます。ビルドからデプロイまで全て手動で行うのは大変でしたが、現在ではFlutterなどモバイルに特化したCI/CDツールであるCodemagicの導入を検討しています。

終わりに

今回、SwiftとKotlinで実装されたアプリをFlutterに移行した話をご紹介しました。チームメンバーのほとんどがFlutter未経験にもかかわらず、スムーズに移行を進めることができました。

この移行プロジェクトは自分からチームリーダーに提案をしたものですが、快く承諾していただきました。メンバーが提案しやすい環境があること、感謝しています。

未経験者でも、簡単にモバイルアプリ開発をすることができます。Flutterを触ったことのない方は是非、Flutterを触ってモバイルアプリ開発をしてみてください。

てっくらんち ~ クリーンアーキテクチャ入門 ~

はじめに

レバレジーズ株式会社エンジニアの高橋です! 本日から、社内で毎週金曜日に行なっている勉強会、通称「てっくらんち」を皆さんにお伝えします!!

「てっくらんち」ってどんな勉強会?

お昼にやっているゆる〜い勉強会です。

毎週金曜日に開催しており、社内エンジニアの発表をお昼ごはんを食べながら聞くスタイルの勉強会です。 発表者は毎回一人で、発表時間は15分〜30分程度、質疑応答も含めると30~45分ほどの内容になっています。

ジャンルは様々で、エンジニアの生存戦略的な内容から、Dockerの踏み込んだ話まで、技術に関する内容であれば何でもOKな形をとっています。 発表者も様々で、あらゆる部署のエンジニアや、フリーランスでJoinされている方、支店や外勤のエンジニアなど、社内の多くのエンジニアが参加しています。

本日のてっくらんち

  • 発表日:2020/06/19
  • 発表者:住村 翼
  • 題目:クリーンアーキテクチャ入門


てっくらんち ~ クリーンアーキテクチャ入門 ~

感想

今回は、住村さんから弊社でシステム化をしているお米予約システム&お米を炊く例にして、クリーンアーキテクチャの概要から、実際に実装をするときどうするかをお話していただきました。
特にDIに関して、メリット、デメリットを踏まえて教えていただいたので、実装するときに今後の実装に活かしていこうと思います。

We Are Hiring

レバレジーズでは「てっくらんち」のような社内での勉強会や「レバテックラボ」という外部の講師を招いたパブリックな勉強会も開催しております。
また、横浜に開発拠点ができるなど、新たな働き方も創出しています。
弊社に少しでも興味を持っていただいた方はご連絡いただけると幸いです。

https://recruit.jobcan.jp/leverages/list?category_id=5142recruit.jobcan.jp

コスト最適化のワークショップに参加してきました

こんにちは、msatoです。

AWS re:Invent2019のワークショップ「ENT206 Optimize AWS costs and utilization with AWS management tools」のレポートになります。

概要

この実践セッションでは、AWSのツールとサービスを使用して、ビジネス全体のコスト最適化を推進する方法を学びます。 AWS IAMポリシー、AWS予算、AWSコストエクスプローラーなど、コスト管理のコアツールを検討します。これらのツールを習得した後、Amazon Athenaを使用した高度な課金分析と、Amazon QuickSightを使用した視覚化について詳しく説明します。

原文

In this hands-on session, you learn how to use AWS tools and services to drive cost optimization throughout your business. We look at the core tools of cost management, such as AWS IAM policies, AWS Budgets, and AWS Cost Explorer. After mastering these tools, we go deeper into advanced billing analysis with Amazon Athena and visualizations using Amazon QuickSight.

スピーカー

Nathan Besh Cost Lead, Well-Architected , Amazon Web Services Arthur Basbaum AWS Cloud Economics , Amazon Web Services

レポート

Governance

AWS BudgetsとIAM Policiesでコストの統制を行います。

  • AWS Budgets
  • 使用量と支出を通知することができる(予算を超えたらアラートを飛ばす)
  • 予算の使用状況をレポートにして、配信することが可能
  • IAMポリシー
  • ユーザが起動できるインスタンスのタイプを指定することができる(t3.larage以下を起動できるようにするなどができる)

Visualization and Analysis

Cost ExplorerやGlue、Athena、QuickSightを使ってコストを可視化します。

  • Cost Explorer
  • 無料で使えるコスト分析ツール
  • コストと使用状況を分析し傾向やコスト要因を把握することができる
  • Glue
    • マネージド型 ETL (抽出、変換、ロード) サービス
  • Athena
  • Amazon S3 内のデータをSQLを使用して分析できるサービス
  • GlueとAthenaを活用したコスト可視化
  • コストのレポートをS3に保存する(請求ダッシュボードから可能)
  • Glueを使ってデータを分析しやすい形に整えます
  • Athenaで分析する

クエリの例)

Top10 アカウントIDごとのコスト

select "line_item_usage_account_id", round(sum("line_item_unblended_cost"),2) as cost from "workshopcur"."workshop_c_u_r"
where month(bill_billing_period_start_date) = 12 and year(bill_billing_period_start_date) = 2018
group by "line_item_usage_account_id"
order by cost desc
limit 10;

Top10 EC2 Costs

select "line_item_product_code", "line_item_line_item_description", round(sum("line_item_unblended_cost"),2) as cost from "workshopcur"."workshop_c_u_r"
where "line_item_product_code" like '%AmazonEC2%' and month(bill_billing_period_start_date) = 12 and year(bill_billing_period_start_date) = 2018
group by "line_item_product_code", "line_item_line_item_description"
order by cost desc
limit 10;

Right Sizing

インスタンスのサイズを適切にして、コストをカットします。

  • インスタンスのサイズを一つ落とすと、料金は半分になる
  • Cloudwatch
  • インスタンスで使用しているリソースの状況を確認する
  • Cost Explorer EC2 Optimaization
  • cloudwatchで取得しているメトリクスからインスタンスサイズをレコメンドする
  • cloudwatch agentからのメトリクスもレコメンドに使える

Pricing Models

Savings Planを使って、コストの割引を受けます

  • Savings Plans
  • 1年間または3年間、一定の利用料をコミットするだけで、その利用料に対して割引がてきようされる
  • Cost Explorer > Savings Plansから見積もりを確認することができる
  • RI report
  • Cost Explorer > Savings PlansからRIのレコメンドを確認できる

Well-Architected Tool

AWSのベストプラクティスにそったアーキテクチャにすることで、コストを最適化します。

  • Well-Architected Tool
  • サービス > Well-Architected Toolから使用できる
  • 運用上の優秀性、セキュリティ、信頼性、パフォーマンス効率、コスト最適化の5つの柱からなる質問に答えることで、現状とベストプラクティスを比較できる

まとめ

弊社でもリザーブドインスタンスを購入するなど、コストカットの取り組みは行ってきました。 このセッションを聞いてまだまだできることはありそうだと感じました。

コストについて知って、お得にAWSを使いましょう。

AWSツールを使用したGitflowに参加してきました

f:id:m-sato-lvgs:20200108172806p:plain

こんにちは、msatoです。

AWS re:Invent2019のワークショップ「DOP202-R2 - [REPEAT 2] Implementing GitFLow with AWS tools」レポートになります。

概要

短命の機能ブランチを利用することは、多くのチームにとって最適な開発方法です。このワークショップでは、AWSツールを使用してマージとリリースのタスクを自動化する方法を学びます。 AWS CodePipeline、AWS CodeCommit、AWS CodeBuild、AWS CodeDeployを使用してGitFlowを実装する方法の高レベルフレームワークについて説明します。また、事前に構築された例を見て、個々のユースケースにフレームワークをどのように採用できるかを調べる機会もあります。

原文 Utilizing short-lived feature branches is the development method of choice for many teams. In this workshop, you learn how to use AWS tools to automate merge-and-release tasks. We cover high-level frameworks for how to implement GitFlow using AWS CodePipeline, AWS CodeCommit, AWS CodeBuild, and AWS CodeDeploy. You also get an opportunity to walk through a prebuilt example and examine how the framework can be adopted for individual use cases.

スピーカー

Ashish Gore Sr. Technical Account Manager , Amazon Web Services Amit Jha Sr. Solutions Architect , Amazon Web Services

GitFlowとは

git-flowは、正確にいうと Vincent Driessen 氏が提唱する「A successful Git branching model」というブランチモデルをサポートするツール(コマンド)の名称です。

一般的には、モデルとツールのどちらの名称としても使われています。git-flowでは、役割が決められた5種類(場合によっては6種類)のブランチを切り替えながら開発を進めていきます。

ブランチの作成やマージに決まりを設けることで、複数人での開発時にもブランチをわかりやすい状態に保つことができ、不用意なマージによる問題を避けることが可能です。

[参考 Git-flow ~Gitのブランチモデルを知る~] (https://tracpath.com/bootcamp/learning_git_git_flow.html)

資料

AWS TOOLS GITFLOW WORKSHOP

レポート

Codeシリーズを始めとしたAWSサービスを使って、Gitflowを実現するワークショップです。

f:id:m-sato-lvgs:20200108174032p:plain

使用するAWSサービスの概要

今回のワークショップで、使用するAWSサービスの概要です。

CodeCommit: Gitリポジトリをホストするサービス CodePipeline: 継続的デリバリーサービス CodeBuild: ビルドサービス(ソースコードのコンパイル、テスト) CodeDeploy: デプロイを自動化するサービス Lambda: サーバレスでコードを実行できるサービス Cloudformation: AWSリソースのプロビジョニングサービス Elasticbeanstalk: ユーザがインフラを管理しなくても、アプリケーションをホスティングできるサービス

ブランチが作成された際の流れ

f:id:m-sato-lvgs:20200108174057p:plain

ブランチが作成された際に、自動で環境が立ち上がりコードがデプロイされます。 流れは以下です。削除時も同様です。

  1. ユーザがCodeCommitリポジトリにブランチを作成する
  2. Codecommitトリガーで、ブランチが作成されたのを検知してLambdaを呼び出す
  3. LambdaではCloudformationを実行する
  4. Cloudformationでは、CodePipelineとElasticBeanstalkが作成される。

まとめ

ブランチを作るだけで、環境が立ち上がりデプロイされるのはとても楽ですね。

個人的には、CodeCommitのトリガーが便利だと思いました。 AWSのサービスなので、Lambdaとの連携が簡単です。 (githubでもwebhook使えばできます)

Gitのブランチモデルに合わせて、最適なデプロイの流れを作っていきたいです。

AWS reinventを快適に過ごすためのTips

f:id:m-sato-lvgs:20200108172806p:plain

こんにちは、msatoです。

12月1日よりはじまったAWS re:Invent 2019に、当社からは私を含むエンジニア2人が参加しました。

re:Inventでの学習効果を最大限に高めるために、快適に過ごすためのTipsを紹介します。

f:id:m-sato-lvgs:20200108173436p:plain

移動編

reinventは複数の会場があり、会場によってはバスで数十分かかります。 地図上はさほど離れていない用に見えますが、端から端までで約4kmあります。 (MGM GrandからEncoreまで、徒歩で約50分)

会場内もとにかく広いです。 移動で消耗しないためのTipsを紹介します。

歩きやすい靴は必須

re:Inventでは、とにかく歩くことになります。

日によっては、歩数が20000歩を超えたこともありました。 (日本人の1日あたり平均歩数は、約8000歩)

靴ずれや移動で疲れないために、歩きやすい靴必須です。

f:id:m-sato-lvgs:20200108173601p:plain

帰国日が一番多かったですが、reInvent期間中は平均15,000歩くらいになっています。

セッションはできるだけ同じ会場にまとめる

できるだけ移動を少なくできるように、予定を組むことをおすすめします。 会場間の移動で、30分以上かかることもあるので時間を無駄にしてしまいます。

有意義に過ごすために、日によって会場をまとめる・overfloowルームを活用するなどしたほうがいいです。

こまめな給水を心がけよう

周りが砂漠のため乾燥しています。 疲労をためないために、こまめな水分補給をおすすめします。

給水スペースは会場の至るところにあるので、活用しましょう。

宿泊編

ホテルは「ザ・ベネチアン/パラッツオ」おすすめ

「ザ・ベネチアン/パラッツオ」がおすすめです。

理由は、メイン会場だからです。

メイン会場では、Keynoteや前夜祭(MidnightMadness)が行われます。 単純に行く回数が多いと思われるので、宿泊先にしてしまえば移動時間を短縮できます。 (私自身も、期間中毎日1回は「ザ・ベネチアン/パラッツオ」行きました)

水の確保が大切

ホテルの部屋にも水のペットボトルがありますが、高いです。 また自販機もないため、水の確保が面倒です。

近くのドラックストアでまとめて買うのがおすすめです。 「WealthMart」「CVS」などで買うことができます。

1ガロン(約4リットル)の容器で買うとコスパがいいです。 初日に1人 1~2本買っておけば手間が少ないです。

同じことを考える人は多いみたいで、初日は売り切れてました。 できるだけ早めの時間に買いに行くことをおすすめします。

写真撮り忘れましたが、こんな感じです。

f:id:m-sato-lvgs:20200108173636p:plain

ポータブル加湿器を持っていこう

ラスベガスはとにかく乾燥してます。 私は1日目、就寝時に口の中が乾きすぎて起きました。

日中のパフォーマンスを低下させたいために、睡眠の質は確保したいところです。

タオルを濡らして加湿するのもいいのですが、最近は持ち運びに便利な小型の加湿器があります。

値段もリーズナブル(1000~2000円)なので、日本で買っておくことをおすすめします。

セッション編

予約できなかったセッションでも、当日並べば受けれる事が多い

セッションは事前に予約することができます。 しかし、人気のセッションを予約するのはなかなか難しいです。

「どうしても受けたいけど、予約を取れなかった」場合は、当日並んでみましょう。 当日席が用意されているため、並んでみると受けれることが多いです。

開始の30分~1時間前に行けば高確率で受けれます。 人気のセッションは、1時間半前くらいに並ぶのをおすすめします。

ハードウェア系のワークショップ受けてみよう

ハードウェア系(Deepracer、Deepcomposer、Alexa、IoT系)のワークショップおすすめです。

おすすめの理由は、2点あります。

  • 普段触る機会がない人が多いと思うので、刺激になる
  • ワークショップでハードウェアを貰えることがある

今回は、DeepcomposerやDeepracerがもらえたそうです。 (聞いた話では、昨年はAlexaとかももらえたとか。)

知識も増えて、帰国後も学習できるハードウェアが手に入り一石二鳥ですね。

f:id:m-sato-lvgs:20200108173701j:plain AWS Deepcomposer

セッションカタログはこまめに確認しよう

reInventの期間中もセッションやワークショップは追加されます。

追加されたセッションは一瞬で埋まってしまうこともあります。 こまめに確認して、受けたいセッションを逃さないようにしましょう。

new launch」とかでセッションカタログを検索すると見つけやすいです。

最後に

実際にreInventに行ってみて、知っていたら便利そうなことをまとめました。 快適に過ごして、reInventの学習効果を最大限にしましょう。

AWS Lambdaとストレージゲートウェイを使用したグローバルストレージの設計に参加してきました

f:id:m-sato-lvgs:20200108172806p:plain

こんにちは、msatoです。

AWS re:Invent2019のワークショップ「ARC313 Architecting Global Storage with AWS Lambda and storage Gateway」のレポートになります。

概要

企業は、AWSテクノロジーのベストプラクティスを使用して、オンプレミスとクラウドの両方で世界中の生産施設に資産を同期する方法を探しています。このセッションでは、AWS Lambda、Amazon API Gateway、Amazon Simple Storage Service(Amazon S3)、AWS Storage Gateway、AWS Directory Service、およびAWS Fargateを使用して、世界中の従業員のコラボレーションを可能にするサーバーレスグローバルストレージプラットフォームを構築する方法を学びます。

Companies are searching for ways to synchronize their assets to their production facilities around the world, both on-premises and in the cloud, using best practices with AWS technologies. In this session learn how you can use AWS Lambda, Amazon API Gateway, Amazon Simple Storage Service (Amazon S3), AWS Storage Gateway, AWS Directory Service, and AWS Fargate to build a serverless global storage platform to enable employee collaboration around the world.

スピーカー

Paul Roberts Rob Hilton Principal Solutions Architect , Amazon Web Services

レポート

構成図

f:id:m-sato-lvgs:20200108172846j:plain f:id:m-sato-lvgs:20200108172903j:plain

動画ファイルを格納できるグローバルストレージの構築です。

具体的にはアテナに動画編集者がいて、トロントから確認したいというケースでした。 (アテナは読み書きが可能で、トロントでは読み込み)

AWSのマネージドサービスを使用して、サーバレスで作ります。

使用するサービス

  • S3
  • 動画ファイルを置きます
  • masterとstudio(読み込み専用)というバケットを作ります(masterがアテナで、studioがトロント)
  • StorageGateway
    • クライアントからSMBでS3をマウントさせるためです
  • AWS Fargete  - フロント部分に使います
  • AWS API Gateway
    • リクエストを受けて、lambdaを起動します
  • AWS Lambda
    • masterにアップロードされた際に、studioのバケットにコピーします

まとめ

オンプレで作ったらかなり手間がかかりますが、AWSのサービスを使うことで簡単に作れました。 また、サーバレスのためコストや運用の手間もほとんどありません。

StorageGateway使ったことがなかったので、ワークショップで実際手を動かしながら作れたのはいい経験になりました。

グローバルストレージを構築する機会はなかなか無いと思いますが、このアーキテクチャは色々応用が効く気がします。