コネヒト開発者ブログ

コネヒト開発者ブログ

コネヒトにおける機械学習関連業務の紹介資料を公開します

更新履歴💡
2022-07-25:初版作成

コネヒトでは Tech vision の1つに 1 to 1 AI というStrategyを掲げているように、今後パーソナライズやレコメンデーションなどの分野に積極的にチャレンジしていくため、機械学習エンジニアの採用を強化しています。

それに伴い、コネヒトにおける

  • 機械学習の活用事例
  • 今後どんなチャレンジをしていく予定なのか
  • 機械学習エンジニアの働き方

などについて、より多くの方に知っていただきたいと思い本資料を公開しました。
(※資料中のリンクについては、speakerdeckページの説明欄や本記事の後半にも記載しております)

本資料を読んでより詳しく話を聞きたいと思った方は、ぜひカジュアルにお話しをさせてください!(各メンバーの Twitter DM 経由でお気軽にご連絡ください!)

採用選考にご興味を持っていただいた場合は、下記よりご応募ください。

bit.ly

上記資料にも添付しておりますが、メンバーが公開している登壇資料やブログ記事もぜひご覧ください。

機械学習の活用事例

MLプロジェクトの進め方

tech.connehito.com

tech.connehito.com

サービス内での活用事例

tech.connehito.com

tech.connehito.com

tech.connehito.com

社内での活用事例

tech.connehito.com

機械学習基盤

分析環境

tech.connehito.com

基盤のスコアリング

tech.connehito.com

SageMakerやStep Functionsを用いたMLOps

tech.connehito.com

tech.connehito.com

tech.connehito.com

tech.connehito.com

産学連携

connehito.com

働いている人・チームのインタビュー記事

www.wantedly.com

eh-career.com

www.wantedly.com

www.wantedly.com

テクノロジーで「家族像が実現できる社会をつくる」という価値提供をしていきたい、皆さんのご応募をお待ちしております!!

Firebase AnalyticsからBigQueryへの日次データ同期が突如不規則になった事象に対応した話

こんにちは。インフラエンジニアの永井(shnagai)です。

今回は、Firebase AnalyticsからBigQueryへの日次データ同期処理の時間が大幅にずれた際に取った対策について書こうと思います。

内容はざっくり下記3点です。

  • 背景説明
  • Firebase AnalyticsからBigQueryへのデータ同期
  • 解決策

背景説明

コネヒトでは、家族ノートという「ママリ」内の検索データとQ&Aデータを可視化したデータ分析サービスを運営しています。

info-kazokunote.mamari.jp

家族ノート内で使っている検索データは、FirebaseAnalyticsを使ってトラッキングしている行動ログをベースにしています。

サービス性質上FirebaseAnalyticsの行動ログは大切で、元のデータがうまく出来ていないとデータの欠損等にも繋がってしまいます。

※もちろんそう簡単に欠損を出すわけにはいかないので、後続の集計処理のスケジュールを工夫したりしてデータ欠損が出ない取り組みはしていますが。

Firebase AnalyticsからBigQueryへのデータ同期

データ同期のフロー

今回の事象の説明に入る前に、FirebaseAnalyticsからBigQueryへのデータ同期のフローをざっくり書くと下記のようになっています。

デバイス
↓ ①行動ログを送信
FirebaseAnalytics
↓ ②リアルタイムにデータ同期
BigQuery(events_intraday_yyyymmdd)
↓ ③日次テーブルとして書き出し
BigQuery(events_yyyymmdd)

ちなみにFirebaseAnalytics上の設定は下記のキャプチャのように「毎日」「ストリーミング」同期の両方をオンにしています

今回課題になった点

※再掲

デバイス
↓ ①行動ログを送信
FirebaseAnalytics
↓ ②リアルタイムにデータ同期
BigQuery(events_intraday_yyyymmdd)
↓ ③日次テーブルとして書き出し
BigQuery(events_yyyymmdd)

家族ノートでは、③の日次テーブルを起点に集計データを作って最終的に表示するBigQueryのビューを作成しています。

ただ、③の日次テーブルの書き出し時間は不定期で、BigQueryの events_yyyymmdd テーブルが何時に出来るのかは利用者側ではハンドリング出来ません。

これまでの経験則から、ある程度この時間までにはデータが出来ているというバッファをもったタイミングで後続の集計処理をスケジュールベースで動かしていました。

そんな中、今年の6月1日のタイミングから時間のズレが大きくなり、元のスケジュールではカバーできない状況が発生しました。(これまでの時間帯に日次テーブルが出来る日もあれば大幅にタイミングが遅れる日も散見され始めました)

具体的に言うと、集計テーブルに対するデータのテストでアラートが出る日が増えてきており何かしらの対応が必要になりました。

解決策

解決策は簡単で、集計テーブルの対象を③ events_yyyymmdd のみから、③events_yyyymmdd or ② events_intraday_yyyymmddという形に変えました。

events_intraday テーブルの仕様がよくわからず、結果としてデータ重複等予期せぬことが起きるのは怖かったので、1ヶ月ほど併行して出来上がるデータに差分がないかをチェックしました。

結果、うまく動いた日は同じ数のイベントがあり、旧来の処理がコケた日も新処理ではデータがうまく出来ていることが確認出来たので正式に新方式に切り替えることにしました。

これまでのクエリ:

FROM
    `hogehoge.analytics_99999999.events_*`
WHERE (_TABLE_SUFFIX = FORMAT_DATE("%Y%m%d", DATE_SUB(@run_date, INTERVAL 1 DAY))

新方式のクエリ:

FROM
    `hogehoge.analytics_99999999.events_*`
WHERE (_TABLE_SUFFIX = FORMAT_DATE("%Y%m%d", DATE_SUB(@run_date, INTERVAL 1 DAY))
      OR _TABLE_SUFFIX = CONCAT('intraday_', FORMAT_DATE("%Y%m%d", DATE_SUB(@run_date, INTERVAL 1 DAY))))

どうしようか困っていた時に、下記ツイートのアイデアに非常に助けられました。

この場でお礼を伝えさせていただきます!ありがとうございます。

今回はニッチなネタでしたが、このブログを読んでたまたま同じ悩みに遭遇している誰かの救いになれば幸いです。

CakePHP Fixture Factories を導入しました

こんにちは。プロダクト開発部の @su-kun1899 です。

今回はママリの CakePHP アプリケーションに Fixture Factories を導入した事例を紹介します。

Fixture Factories とは何か

Fixture Factories は、モデルやデータベースに依存するテストコードにおいて、テーブルの作成やデータ初期化を行うためのプラグインです。

github.com

CakePHP には元々 Fixture という仕組みが提供されていますが、 Fixture Factories はより柔軟に扱うことができます。

https://book.cakephp.org/4/en/development/testing.html#test-fixtures

導入したきっかけ

アプリケーションの規模が大きくなり、機能が増えてくると、テーブル(モデル)ごとに一律データを管理する Fixuture は管理や運用の負荷が高まっていきます。

ママリでもテストケースとデータの依存関係が強くなり、テストデータを少し変更すると既存のテストが壊れて修正が必要になるなど、気軽に変更できないことが多くなってきていました。

Fabricate を導入するなど対応は行っていましたが、関連データの生成やシーケンシャルな値の発行等でママリでのユースケースにはマッチせず、十分な解決策には至っていませんでした。

GitHub - sizuhiko/Fabricate: PHP data generator for Testing inspired on Fabrication and factory-girl from the Ruby world.

そこで、Fixture Factories を導入することにしました。

Fixture Factories のいいところ

一部ですが、特に便利だなと思っているところを紹介します。

公式が推している

公式ドキュメントでも明確に Fixture 肥大化時の解決手段として言及されています。

https://book.cakephp.org/4/ja/development/testing.html#id20

github.com

API が直感的かつ柔軟

主観を多分に含みますが、かなり使いやすく読みやすいと思います。

<?php
// Entity を生成するとき
$article = ArticleFactory::make()->getEntity();
<?php
// Entity を永続化するとき
$article = ArticleFactory::make()->persist();
<?php
// フィールドを書き換えるとき (未指定のフィールドはデフォルト値で生成される)
$article = ArticleFactory::make(['title' => 'Foo'])->getEntity();
$article = ArticleFactory::make()->setField('title', 'Foo')->getEntity();
$article = ArticleFactory::make()->patchData(['title' => 'Foo'])->getEntity();
<?php
// テストデータを複数件まとめて作るとき
$articles = ArticleFactory::make(2)->getEntities();
// フィールド書き換えと同時に行うこともできます
$articles = ArticleFactory::make(['title' => 'Foo'], 3)->getEntities();
$articles = ArticleFactory::make(3)->setField('title', 'Foo')->getEntities();

関連データの生成が柔軟

モデルの Association に従って、スムーズに関連データの生成が行なえます。

# Bake コマンドで -m オプションを指定すると、関連データ生成のメソッドも生やしてくれます
bin/cake bake fixture_factory -m Articles
<?php
// 関連データをまとめて作る
$country = CountryFactory::make()->withCities()->persist();
<?php
// 関連データの値や件数も柔軟に変更できる
$country = CountryFactory::make()->withCities(3)->persist();
$country = CountryFactory::make()->withCities(['is_capital' => true])->persist();
$country = CountryFactory::make()->withCities(['is_capital' => true], 3)->persist();

Faker が使える

初期データは Factory の setDefaultTemplate メソッドで定義するのですが、 Faker が使えるのでテストデータ生成がスムーズです。

<?php
class ArticleFactory extends BaseFactory
{
    // ~~~~ 略 ~~~~~
    protected function setDefaultTemplate(): void
    {
          $this->setDefaultData(function(Generator $faker) {
               return [
                    'title' => $faker->text(30),
                    'body'  => $faker->text(1000),
               ];
          })
          ->withAuthors(2);
    }
    // ~~~~ 略 ~~~~~
}

呼び出し元で callback を使って利用することも可能です。

<?php
$article = ArticleFactory::make(
    fn(ArticleFactory $factory, Generator $faker) => [
        'title' => $faker->text,
    ]
)->persist();

ちなみに Faker は CakePHP 側の defaultLocale を参照するので、 ja_JP を指定しておくと日本語のテストデータ生成もできます。

その他 Tips

使用するケースは限られるかもしれませんが、参考までに。

データを組み合わせごと複製する

特定の組み合わせのデータを、そのまま任意の数複製することができます。

下記の例では、 is_admin が true / false で 5 件ずつ、合計 10 件のテストデータが生成されます。

<?php
$users = UserFactory::make(
    [
        ['is_admin' => true],
        ['is_admin' => false],
    ],
    5
)->persist();

テストのときだけ Association を書き換える

あまりないと思いますが、 Factory で getTable をオーバーライドすることで、 テストのときだけ有効な Association を定義することができます。

何らかの事情でプロダクションコードでは Association を定義できないが、テストの場合は Association をあるものとしてテストデータ生成を効率化したいケースなどで利用できます。

<?php
class ArticleFactory extends BaseFactory
{
    // ~~~~ 略 ~~~~~
    public function getTable(): Table
    {
        $table = parent::getTable();

        // 一時的に Association を定義
        $table->hasOne('Authors')->setForeignKey('author_id');

        return $table;
    }
    // ~~~~ 略 ~~~~~
}

おわりに

今回は Fixture Factories の導入と、その便利な機能の一部について紹介しました。

かなり便利さを実感しており、どんどん活用していこうと思います!

PR

コネヒトでは一緒にテストを改善していく仲間を募集しています!

hrmos.co

2022年7月最近のスマイル制度活用事例

コネヒトには「スマイル制度」という制度があります。これは開発組織でのインプットとアウトプットの活性化を促進する制度です。とてもコネヒトらしい制度になっているので、詳しくはスマイル制度 - Connehito Tech Visionや、制度が生まれた当時の記事をご覧ください。

tech.connehito.com

その後、カジュアル面談などで社外の方から「実際どういったものに使われているんですか?」と質問いただくケースがちらほらあるため、どういったインプット・アウトプットが行われたかを発信していければと思います!

インプット事例

アウトプット事例

イベント開催、LT、個人ブログ、OSSコントリビュートと色々な種類のアウトプットがありました。

speakerdeck.com

connehito.connpass.com

zenn.dev

masatakashiwagi.github.io

github.com

以上です!

社内Go勉強会を始めました

こんにちは!Webエンジニアのaboyです。最近はママリの検索体験を最高にする仕事をしています。

今回は、最近コネヒト社内でやっているGo勉強会の取り組みを紹介します。

なぜGo勉強会を始めたか

コネヒトが掲げるTech Visionの戦略の内のひとつ「Let’s Go」を盛り上げるためです。

コネヒトは、エンジニア組織及び技術領域において、何を大事にし何に投資していくかと言った未来の構想や方向性をまとめたTech Visionを掲げています。その中に、Go言語を新たに武器にしていくLet’s Goという戦略があるのですが、まだ社内にGo言語の事例も少なく、Goに関わる機会もほとんどありませんでした。

そんな中で、4月から新しく構築するシステムでGoを使うことになり、コネヒトでGoを盛り上げていくのに良いタイミングでもあったので、組織としてGoに触れる量を増やすために勉強会を始めました。コネヒトにはすでにWeb全般のテーマを扱う社内勉強会があったのですが、特定技術の話に集中しづらいため、Goに特化したものとして始めることにしました。

最初の案内

やってみてる感想

計5回開催した

4月から計5回やってみて、とりあえず社内に「Goの催しをやってるぞ」という空気は出せているかなと思います。「Tech Visionにはあるけどあんまり盛り上がっていない」状態からは少し改善したかなと。

会の内容は、気になった記事などのネタを持ち込んで、それについて雑談するという感じです。例えば以下のようなテーマはGoに全く触れたことがない人でも話題に入りやすく、盛り上がりました。

  • ディレクトリ構成どうしてる?
  • コーディング規約はある?Linterとかは?
  • Webアプリケーションフレームワークどういうのある?どう選ぶ?
  • ロギング、スタックトレースどうする?

あとは、外部の記事を紹介するのも学びが多いですが、自分が学んだことを紹介するほうが自分の言葉で話せることが多く、盛り上がる傾向があると感じます。

参加人数は5〜8人程度で、元々Goに興味がある人、以前の職場でGoを扱っていた人等が参加してくれています。コネヒトのエンジニアのうち3分の1程度が参加してくれているのでまずまずかなと思っています。参加してくれるメンバーは皆チャットをたくさん盛り上げてくれるのでとても助かっています。

一方で、人はそれなりに集まりますが、ネタが集まりにくいことが課題です。業務でGoを扱っているメンバーがまだ少ないため、意図的にネタを仕入れるようにしないと集まりにくい構造になっています。これについては今業務でGoを書いている僕が、前回開催時から今回までに学んだことを話すコーナーを設けることで一定確保するようにしてみています。

最近のaboyコーナー

また、参加者によってGoへの興味や習熟度が異なるため、扱うテーマは悩みどころです。今のコネヒトはこれからGoを導入していくフェーズなので、PHPなど既存のアプリケーションと共通の話題を混ぜた方が興味を持ってもらいやすいのではないかと感じています。前述したテーマの他には、例えばテストや静的解析のような話題ですね。

おわりに

社内でやっているGo勉強会について紹介しました!継続して、社内を盛り上げていきたいと思います ԅ( ˘ω˘ԅ)

それから、GoのLT会も開催しているので、興味がある方はぜひご参加ください〜。

connehito.connpass.com

最後に、コネヒトでは成長中のサービスを支えるために一緒に働く仲間を様々な職種で探しています。 少しでも興味もたれた方は、是非気軽にオンラインでカジュアルにお話出来ると嬉しいです!

www.wantedly.com

ECS×Fargateのオートスケールをチューニングしてサービス運営費を削減した話

こんにちは。インフラエンジニアの永井(shnagai)です。

今回は、ECS×Fargateで運用しているサービスの「ターゲット追跡ServiceAutoScalling」をチューニングをしたことで、費用が約半分になるという大きな成果を残すことが出来たのでその内容を経緯と共にまとめています。

内容はざっくり下記3点です。

  • なぜオートスケールのチューニングをしたのか?
  • 「ターゲット追跡ServiceAutoScalling」のチューニング方法
  • どんな結果になったか?

なぜオートスケールのチューニングをしたのか?

コネヒトではWebのアーキテクチャはほとんどECS×Fargateの基盤で動かしています。そして、オートスケールとして「ターゲット追跡ServiceAutoScalling」を使うことで、Fargateのメリットを最大限活かす形で運用コスト低くサービス運用を実現しています。

ここらの話は下記のスライドやブログに詳しく書いているので興味がある方はご覧ください。

ECS×Fargateで実現する運用コストほぼ0なコンテナ運用の仕組み/ ecs fargate low cost operation

speakerdeck.com

ECS×Fargate ターゲット追跡ServiceAutoScallingを使ったスパイク対策と費用削減 - コネヒト開発者ブログ

tech.connehito.com

これまで約1.5年ほど初期に設定したオートスケールのしきい値で問題なくサービス運用出来ていたのですが、ある時を境にレイテンシ悪化のアラートが頻発するようになりました。

よくわからないレイテンシ悪化だったので、アプリケーション側やインフラ側のレイテンシ悪化前後での変更、そしてアクセス特性の分析といった調査をして原因切り分けを行っていきました。

その中で、現状のオートスケールのしきい値 (CPU使用率を30%程に収束させるような「ターゲット追跡ServiceAutoScalling」の設定)では、特定コンテナのLoadAverage(LA)が跳ね上がり、そのコンテナの処理が遅くなることで平均レイテンシが上振れしているという事象を突き止めました。

スパイク的なアクセスに今までは耐えられていたのですが事象がしばらく続いてしきい値を調整しても事象は収まらなかったので、 「ターゲット追跡ServiceAutoScalling」1本で行くのは限界と判断し、新たにECSの「ステップスケーリングポリシー」を導入しました。

ECSのステップスケーリングポリシーとは何か?

「ターゲット追跡ServiceAutoScalling」がCPU使用率/メモリ使用率/リクエスト数の3つから追跡するメトリクスと値を選択することで、ECS側でタスク必要数 (起動するコンテナ数)をしきい値に収束するように動的にコントロールしてくれる機能なのに対して、「ステップスケーリングポリシー」は特定のトリガが発生した時に一気にスケールアウトして タスク必要数 を増やすような用途に使えます。

ステップスケーリングポリシー

docs.aws.amazon.com

今回の対応だと、 CPU Utilization Max の値がしきい値を超えたタイミングで、タスクを3つスケールアウトするような設定を入れました。具体の挙動としては、「ステップスケーリングポリシー」を設定すると、裏でCloudWatchAlarmが作られて、そのアラームをトリガーとしてECS側でスケールアウトが走るような仕組みになっています。

これを入れることで、スパイクの初動の段階で一気にスケールアウトが走るようになりレイテンシ悪化のアラームを抑えることに成功しました。

次なる課題

「ステップスケーリングポリシー」を導入したことによってレイテンシアラームは抑えることに成功したのですが、思想的に十分な余裕を持ってリクエストを迎えるための設定になるのでタスクの起動数が上振れ(費用増)するようになりました。「ターゲット追跡ServiceAutoScalling」のしきい値はそのままに、「ステップスケーリングポリシー」でオートスケールを追加したので当然の結果です。

ここからは、「ターゲット追跡ServiceAutoScalling」のチューニングをして、Fargateタスクの平均起動数を下げることで費用削減にチャレンジしました。

「ターゲット追跡ServiceAutoScalling」のチューニング方法

チューニングの方針として、「タスクあたりのリクエスト数(1コンテナが捌くリクエスト数)」を上げるのを目的に、「ターゲット追跡ServiceAutoScalling」でしきい値としているCPUUtilization avgを上げていくアプローチをとりました。

下記のキャプチャは、CloudWatchで見られる RequestCountPerTarget の推移なのですが、短期で見ると振れ幅ありつつも長期トレンドでは徐々に下がっていることがわかります。これが下がるということは、全体のリクエスト数は少しずつ成長しているとした場合に、1タスクあたりのコスト効率が悪化していることを意味します。

「ターゲット追跡ServiceAutoScalling」のチューニング方法としては、下記を繰り返して最適値を探りました。

  1. しきい値とするCPUUtilization avgの値を1%インクリメント
  2. 2日間問題が起きないかという様子を見る
    1. ガードレールメトリクスとして、アラートならずともレイテンシ悪化がゆるやかに起きていないかをチェックする
  3. 問題なければ1に戻る。該当期間にアラートが出たらしきい値を元に戻して2の様子見期間へ。

この繰り返しで1ヶ月程かけて最適値を探りました。チューニング方針が慎重すぎないか?という感想をもしかしたらもたれるかもしれません。

ただ、本番運用サービスでチューニングを行うので出来るだけリスクを抑えたいという判断から時間はかかりますが、1%ずつの調整を行うことにしました。

また、しきい値調整のオペレーション自体は軽く、監視も基本的にはアラートがならない限りはアクションしないという方針で取り組んだので、長い時間をかけることが出来たというのも正直なところです。

モニタリングには、CloudWatchを使ってモニタを作り、チューニングが与える変化をウォッチしていました。

下記の4項目を一つのモニタにまとめています。

  • ALBのReqCount(該当サービスのリクエスト数)
  • ECS CPUUtilization avg(該当ECSサービスのCPU使用率 平均)
  • ECS CPUUtilization max(該当ECSサービスのCPU使用率 最大)
  • ECS RunningTaskCount(該当ECSサービスで起動しているタスク数)

例えば、このモニタからこんなことが読み解けるような作りになっています。

  • ALBのReqCountが急激に上がった時に、ECS CPUUtilization maxがしきい値を超えてECS RunningTaskCountが3タスクスケールアウトしており、ECS CPUUtilization avgはしきい値前後で収束している
  • 夜間でALBのReqCountが落ちてくると、ECS CPUUtilization avgが収束し、RunningTaskCountが最小値の値までスケールインした

また、CloudWatchは水平/垂直の注釈を入れれるので設定変更のタイミングやしきい値に注釈を入れることでグラフを見ただけで読み取れる情報が格段に増えます。この機能は便利だなと思っています。

どんな結果になったか?

チューニングは、アラートが鳴るギリギリのところまでレイテンシが悪化する状況が見えてきたところで一旦インクリメントは終わりとして、しきい値から少しゆるめた落ち着いたところでFixさせました。

結果として目標においていた、以前の水準まで「タスクあたりのリクエスト数(1コンテナが捌くリクエスト数)」を上げることに成功しました。また、それに応じてSavingsPlansで購入する Compute Savings Plans を半分ほどに削減することが出来、システム運営費の削減に成功しました。

※この半年後のSavingsPlansの切れるタイミングで、測定したところ従来の半分くらいの購入で良いことが判明した

最後に宣伝です! コネヒトでは一緒に成長中のサービスを支えるために働く仲間を様々な職種で探しています。 少しでも興味もたれた方は、是非気軽にオンラインでカジュアルにお話出来るとうれしいです。

コネヒト株式会社

hrmos.co

コネヒトはPHPerKaigi 2022にゴールドスポンサーとして協賛します!

こんにちは!@otukutun です。今回は弊社が協賛し、弊社社員が登壇するイベントを紹介します。

PHPerKaigi 2022に協賛いたします

コネヒトではメインプロダクトである「ママリ」を始めとして開発のメイン言語としてPHPを活用しており、フレームワークとしてはCakePHPを採用しています(その他、技術スタックを知りたい場合はこちらをご覧ください)

その縁もあり、この度 PHPerKaigi 2022 にゴールドスポンサーとして協賛させていただくこととなりました!

イベント概要

公式サイトからの引用になりますが、PHPerKaigiは

PHPerKaigi(ペチパーカイギ)は、PHPer、つまり、現在PHPを使用している方、過去にPHPを使用していた方、これからPHPを使いたいと思っている方、そしてPHPが大好きな方たちが、技術的なノウハウとPHP愛を共有するためのイベントです。 今年はPHPerKaigi初のオフラインとオンラインのハイブリッド開催です。

となっており、いろんな方が楽しめる間口の広いPHPのイベントになっていそうです!今回ははじめてのハイブリッド開催なので、都合がつく方は両方で参加して楽しむこともできそうですね!

発表内容やタイムスケジュールを知りたい方はこちらをご覧ください!

タイムテーブル | PHPerKaigi 2022 #phperkaigi - fortee.jp

また、最新情報はTwitterで告知されるので PHPerKaigi 公式Twitterも要チェックです!

@phperkaigi

コネヒトからの登壇者の紹介

弊社からは@takoba_ が登壇します。

2022/04/11 12:10〜 Track B レギュラートーク(20分)
CakePHP Fixture Factories の登場によって変化する、PHPプロジェクトにおけるテストフィクスチャ管理の選択肢

fortee.jp

CakePHPでテストデータ動的な生成方法について説明がありそうなので個人的にもすごく興味あります。

チケット購入

こちらからチケット購入できますのでよければぜひ!

www.eventbrite.com

最後に

ここまで読んでくださった皆様、ありがとうございました。 そして、 PHPerチャレンジ中の皆様、お目当てのPHPerトークンはこちらです!

#AFTER_TECH_COMPANY

それでは!

コネヒトではPHPerを積極採用中です!

hrmos.co