こんにちは。開発部でエンジニアインターンをしている伊藤優汰です。
今回は一年間インターンに参加したので本記事で振り返って行きたいと思います。
参加に至った経緯
私がインターンに参加するきっかけとなったのは、昨年の夏に開催されたPR TIMESハッカソンでした。

こちらのフィードバック面談でインターンを募集していると伺い、自分もインターンを探していたこともあってその場で参加したい旨を伝えました。
インターンではバックエンドを始め、様々な業務を体験させて頂きました。ここからはその中でも特に学びになった業務2つをご紹介します。
S3の署名付きURL発行ロジックをAWS SDKに置き換えた業務
タスク概要
PR TIMESのプレスキットの画像アップロードにおいて、これまではPHPのバージョンが古かったこともありAWS SDKの導入が難しく、API Gatewayを介して署名付きURLを発行していました。
現在ではPHPのバージョンが上がりAWS SDKの導入が容易になったため、当タスクで導入することになりました。
これまでの構成は以下の記事をご参照ください。

実際のタスクはDesign Docの作成→実装という流れで進みました。
Design Docについての目的や導入した経緯は過去の開発者ブログにて解説されているのでぜひご覧ください。

Design Doc作成での学び
これ以前に経験したタスクにおいては実装のみというものが多く、設計や要件定義から関与するのは初めての経験でした。
実装時にはコードに集中するので考慮すべき問題は限定されますが、企画から行うとなると「既存のAWSリソースはいつ削除するのか」、「フロントエンドのキャッシュを考慮して古いエンドポイントへのリクエストがいつまで続くのか」など幅広い視点で考える必要がありました。
Design Docの作成を通じて、技術者として一人前になるためには知識や実装力だけではなく、時と場合によって適切な技術選定や実装を見越した開発計画を立てる能力も求められるということを学びました。
実装での学び
実装については、特にテストの周りで多くの学びがありました。
インターンとして入って最初の頃のタスクは、単一のファイルにコードを実装することが多かったため、各関数ごとにUnitTestを実装すれば十分でした。
しかし、今回のタスクではAction層やRepo層などの複数の階層にまたがる実装が求められたため、テストコードも前者のように簡単には実装できませんでした。
例えば「署名付きURLが適切に発行できる」というテストを素朴に実装したとき、その機能はAWSのライブラリが大部分を提供しているので、単にライブラリ自体をテストすることになってしまうからです。
そのため、今回は「Action層において署名付きURLを含む適切なJSON(フロントに返す用等)を返すことができる」といった、その層の責務を果たすことを証明するテストを書くことが重要でした。
public function test_署名付きURLを発行する最上位の関数が正しいJSONを返す()
{
$company_id = PressKitTestDataList::HAPPY_PATH_COMPANY_ID;
$company_user_id = PressKitTestDataList::HAPPY_PATH_COMPANY_USER_ID;
$request = new Request(
'GET',
'press_kit/s3_put_presigned_url',
['target' => 'logos']
);
$logger = UnitTestLoggerFactory::createLogger();
$response = new JsonResponse();
$S3Client = GeneratePresignedUrlTestHelper::generateTestS3Client();
$response = PressKitAdminPutS3PreSignedUrlGetAction::run($logger, $response, $request, $company_id, $company_user_id, PRESS_KIT_S3_BUCKET_NAME, $S3Client);
$json_str = $response->getBody();
$json_data = json_decode($json_str, true);
$this->assertArrayHasKey('status', $json_data);
$this->assertArrayHasKey('message', $json_data);
$this->assertArrayHasKey('data', $json_data);
//dataに想定される署名付きURL等が含まれているのでチェックする
$this->assertArrayHasKey('presigned_url', $json_data['data']);
$this->assertArrayHasKey('object_key', $json_data['data']);
}サービスが提供する機能やそのシナリオを意識した個人開発では経験できないため、非常に良い学びとなりました。
DBへのアクセスを行う関数をLegacyDAOに置き換えた業務
タスク概要
現在PR TIMESではセキュリティを向上させるために、DBへのアクセスを内製の関数であるLegacyDAOへの置き換えを行っています。
/**
* @param PDO $pdo
* @param int $company_id
* @param int $company_user_id
* @return array{num: string}
*/
public static function tmp(PDO $pdo, int $company_id, int $company_user_id): array
{
$company_user_count = PrSql::execute(
$pdo,
'
SELECT
count(*) AS num
FROM
tmp
WHERE
company_id = :company_id@int
AND
company_user_id <> :company_user_id@int
AND
del_flg = FALSE
',
[
':company_id' => $company_id,
':company_user_id' => $company_user_id,
])->fetch(PDO::FETCH_ASSOC);
//PrSqlは数値の場合intが返ってくるので旧DAOに合わせるために変換する
$company_user_count['num'] = (string)$company_user_count['num'];
return $company_user_count;
}ただ、今回のリプレイス対象箇所には2つの問題がありました。
1つ目はトランザクションが共有できないという点です。これまでDBへのアクセスを担っていた既存の関数はpg関数を用いており、これをPDOに変更すると、接続を統一するためにはすべての使用箇所を書き換える必要があります。このため、pg関数とPDOでは接続が異なり、トランザクションが共有できないという問題が生じます。
2つ目は使用箇所が多いという点です。今回置き換えたLegacyDAOに関しては企業ユーザー関連のコードであり、最終的にはJIRAチケット47個という膨大な量の置き換え作業が必要でした。
学んだこと
正直に言って、今の自分を作り上げたのはLegacyDAOによるところが大きいと感じています。
学びは大きく分けて2つあります。
作業チケットは丁寧に書く
1つ目は丁寧に作業チケットを書くということです。丁寧にチケットを書くことは自分のためにも、チームのためにもなります。
ただテストケースやQA結果を書くだけではなく、本番デプロイ時の手順を細かく書いたり、インシデント発生時のRevert手順なども事前に想定し記載しました。
また前提知識がない人がチケットを見たときに意思決定や経緯がわかりやすいように、Slackの会話リンクなども丁寧に貼り付けました。

このようにすることで、誰でも作業を行える環境が整い、インシデントが発生した場合にも素早く対応できます。
丁寧にチケットを書くようになった背景として、入社して一ヶ月の頃にインシデントを起こしてしまった経験があります。
個人開発しか経験したことのなかった私にとって、テストの重要性がまだ十分に理解できておらず、その結果テストケースの漏れが原因で前述のトランザクションの問題に引っ掛かかってしまい、インシデントを発生させてしまいました。
さらに、発生後のRevert作業についても、経験が不足していたため何をすべきかわからず、結果として棒立ちになり人任せになってしまいました。
この一連の経験から自分自身の準備を万全にするとともに、他のメンバーが迅速に対応できるように作業チケットを丁寧に書くようになりました。
諦めなければ何でも達成できる
2つ目は諦めなければ何でも達成できるということです。
概要で説明した通り、企業ユーザー関連の対応箇所は膨大でした。対応箇所が多く、途中で何度も挫けそうになってしまいました。
しかし、CTOの金子との1on1で「諦めなければ終わる。自分もこれまでこのマインドで乗り越えてきた。」という励ましの言葉をもらい、なんとか最後までやり遂げることができました。
今振り返るとあのとき諦めなくて良かったと思っています。あのとき諦めてしまったら、今後LegacyDAOのような難しいタスクが出てきたときに、「終わりが見えないから」という理由をつけて諦めることができてしまうからです。
この経験があるからこそ、もし今後自分に同様のタスクが降りかかってきても、LegacyDAOの経験があるから投げ出さないという自信が付きました。
終わりに
PR TIMESでの一年間のインターンを通してたくさんのことを学びました。技術面に関する知識やスキルはもちろんのこと、それに加えエンジニアとして大事なマインドなど実際に社会に出て働いてみないと経験できないようなこともたくさん経験させて頂きました。
これらの経験ができたのは、PR TIMESの「学生だから」「インターンだから」というくくりでタスクが制限されることは一切なく、熱意さえあればなんでも挑戦できる環境であったからだと思っています。
最後になりますが、このインターンシップを通して成長する機会を頂いたことに深く感謝しています。今後もPR TIMESでの経験を活かし、さらなる挑戦を続けていきたいと思います。
本当にありがとうございました!

