entry-header-eye-catch.html
entry-title-container.html

entry-header-author-info.html
Article by

【PIXIV MEETUP 2023登壇セッション】pixivisionを動かし続けるために

こんにちは、pixiv事業本部Webエンジニアリングチームでバックエンドエンジニアやデータ集計・整備などをやっているふぉのです。

先日、招待制にて開催されたPIXIV MEETUP 2023、LT1にて「pixivisionを動かし続けるために」というタイトルで発表しました。この記事は、その詳解版となりますので、スライドと併せて御覧ください。

speakerdeck.com

2つの「かいしゃく」で進めるアーキテクチャ改善着手までの道程〜pixivisionのバックエンドを託されて〜

突然ですが、ソフトウェアアーキテクチャ、良くしたいですよね?
可能であれば、実装しやすく、少なくとも数手、うまくいけば数十手先の変更まで、するっと受け容れるような良いアーキテクチャに従って楽しく書きたい。

ただ、現実問題として、手元の実装はそうなってはいない。 そんな時に実装と向き合う一助となればと思い、LTの内容を記事に詳解することとしました。

この記事の内容は、2021年8月にpixivisionの実装を託されてから、 2つの「かいしゃく」を行い、アーキテクチャ改善に着手するまでの流れを説明するものです。

特定の言語に依存したトリッキーな内容は話しません。多種多様なプログラムの実装や歴史的経緯を持つプログラムと向き合うときの考え方からオブジェクト指向プログラミング言語のアーキテクチャまでが守備範囲です。

pixivisionとは

皆さんは、pixivisionをご存知でしょうか。公式の説明は下記のとおりです。

イラストやマンガ、ノベルをはじめとした 「創意工夫から生まれる"とっておきの作品"」と オタクカルチャーを多言語で世界に向けて発信する、 毎日に退屈したくない人のための創作系メディアです。

ご存知でない方はこの機会にぜひ、スライドのQRを読み込むか、 下記リンクを用いて、pixivision訪れてみてください。

www.pixivision.net

機能のかいしゃく

まず最初に「解釈」をしました。一致すると嬉しいやつです。

初めて目にするコードが多い中、この解釈とは、様々な機能があり、その機能実装の経緯と意図を解釈することです。つぶさに調べ、資料にまとめ、丁寧に解釈していきます。文章のみで記述すると、表現の曖昧さや文章量の肥大が問題になるので、UMLも併せて資料をまとめていきます。

このとき意識したのは、古い機能と向き合うときに邪険にしないことです。確かに、慣れ親しんだコードと比べてしまうと、時の経過であったり、様々な事情により、独特の異質さを持っていることがあります。その異質さは実装を行う人にとってはある種のストレスになります。

だからといって、機能そのものは当時何らかの必要性があってその時の実装者が様々な事情を抱えて作り出したものです。なので、実装を解釈するときにそのストレスの存在は認めつつ、不要なバイアスとして資料に紛れ込まないように意識しました。

つぶさに調べてまとめる

UMLでの表現

UMLでの表現はWebアプリケーションの処理を記述しやすいシーケンス図、状態遷移をそのまま表記できるステートマシン図を主に用いています。自然言語よりは意味が固まっており、同様の意味をもつ自然言語やコードそのものよりは読み解きが早いという利点があるため積極的に利用しています。

文章での表現

また、文章での表現は必要な分だけとなるよう意識していますが、機能を解釈していく過程ではあまり削減はできません。実装の経緯は特に丁寧に書かざるを得ない部分です。

あとは、期待された動作と、実は異なる動作というのも文章で記述しています。
テストコードで表現されるべきものですが、自動テストのカバレッジが不十分な都合上、一旦文章でカバーするという形を取っています。

最後に今後の改修方針の議論のログや、一旦将来に検討したことを書いています。これは、いたずらにTODOコメントを増殖させるより、ドキュメントとしてエンジニアでない人とも議論できる形にまとめておいたほうが有益だという感覚を持っています。

資料はめんどい

そうです、UMLと文章、資料は面倒です。非常によくわかります。 書く、読む、維持する。全てコストがかかります。

しかしながら、gitなどに代表される、各位がお手元でご利用のVCS(Version Control System)というものは、コードの履歴を残すものであり、実装の経緯と意図を残すための枠組みではないのです。長大コメントが書かれたリリースコミットを読みたいかというとVCS内ではなく、普通にリリースノートを書いてくれ、となりますよね?

書かざるを得ない

だからこそ、最小の資料で最大の効果を得る仕掛けをちゃんとコストを使って維持します。

ページを分け過度に肥大化した資料を作らないこと、Archiveという状態にして見えなくし、惰性で残さないこと、それら維持するために資料奉行はある程度やむを得ないというスタンスです。

結果として、読める、掛ける、整っているを維持し続けています。 ある種ドキュメント基盤のようなものを前提として、機能の解釈を資料化していきました。

2つめの「かいしゃく」

次に、「介錯」をしました。看病することではなく、使われていない機能を片付けることです。

解釈を終えた機能故に、精度高く判断して、介錯できます。

敬意をもって看取ること、安全に消すこと、次に活かすことを考えていました。

敬意を持って看取るとは、その実装は現在から見てベストでないことは往々にしてありますが、その時必要に応じて作られたものです。なので、敬意を持って看取ります。

消すのは気持ちいいので必要な歯止めとも考えられます。必要であったものを消すときに過度に粗雑に消すとミスに繋がるためです。

次に、安全に消すことです。

管理画面以外、基本的にメンテナンスで停止することはありませんでした。無停止で行っています。つまり、致命的なミスをするとエラーがじゃんじゃかでてくるわけです。なので、安全は最優先になります。ごくごくレアケースを除き、自動テスト、手動テストによって表示される状態を維持したまま、移行を終わらせています。

そして最後に、次に活かすことです。

なぜ不要になってしまったかを考慮します。これも解釈の一部ではありますが、実際に消すことによって見えてくるものもあります。ただ、やってみた結論としては、やむを得ない場合がほとんどです。時間があまりにも残酷すぎることを体感させられますね。

看取った機能たち

看取った機能としては、消したもの吸収合併したものに分けられます。

消したもの

消したものは、使っていないから看取るという決断をしました。

独自ABテスト、管理画面の一部のロール、運用実体のない表示領域、記事末導線のテンプレート管理、公開済み記事の膨大な編集履歴。

現状取られているアーキテクチャの都合上、どうしても密結合になっており、本質的な改善に寄与しないながら、改修の影響範囲として毎度気にかける必要がありました。それが消されることで今後の改修が大幅に楽になっていきます。

吸収合併したもの

次に吸収合併したものですが、おおよそこれは2つのデータ形式で、まとめて肩寄せするというアプローチを取りました。

記事本体のデータ形式については、大昔に3つだったものが2つ(イラストまとめ形式、企画記事形式)になっていました。ただ、現在はその2つを更に1つに統合しました。

イラストまとめ形式は、イラストIDとそれに追加する説明の組のリストで、見出しの機能などがありませんでした。企画記事形式は、記事パーツというものの繰り返しであり、JSONの配列で記録されています。

これを全て企画記事形式に片寄せしましたが、運用には互換性を持たせるため、イラストまとめ形式の編集機能は残されており、データ形式だけ企画記事形式となっています。挙動としては、記事を作ったあと、企画記事形式でしかできない記事パーツを挿入すると、イラストまとめ形式のエディタでは編集できなくなるような仕組みになっています。

丁寧に看取ることで実現可能性のある範囲で互換性を担保した形です。

記事サムネイルに統一された概念がなく、イラストまとめ形式のサムネイル、企画記事形式のサムネイルという形になっており、イラストIDにクロップ座標がついたもの、内部のURLを指定したもの、を記事のデータと一緒くたに保存していた状態でした。

それを、両方利用可能、かつ整理して1つの概念として統一しました。

機能削減の結果

その結果、確実に実装量は減って、明らかに身軽になったというわけです。

DBのカラムを52個廃止し、不要な仕様を10機能以上廃止し、本番に滞留しマイグレーションの時間を異常に伸ばしていたレコードを21万件以上削除しました。

もちろん、必要なレコードに関しては、データ基盤に退避させました。弊社のデータ基盤については、pixiv insideのデータ基盤に関する他の記事を御覧ください。

inside.pixiv.blog

アーキテクチャ改善

身軽になったので、解釈が十分にできた機能が多くなり、全体の実装の整理について議論ができるようになります。加えて、その議論をこれまで整備した資料と照らし合わせて差分で検討できます。色々な案が出て、いくつかの改修を試みることがありますが、いまだ複雑性が高い箇所があり、実装を撤退する機能がある状態でした。

そこで、全体の構造を見直す時期ではないかとなり、アーキテクチャ改善に取り掛かります。

深まる検討と実装

まずは分割。ベタ書きの脱却と疎結合化がトピックに上がります。

ベタ書きについては、管理画面側の機能はスクリプト的、愚直に処理を列挙するような形で書かれたたものが多く、責務毎に分割していくなどの必要がありました。特定の機能を実現するための実装が、処理の流れの中に薄く広く書かれている状態になりがちなものを整理していきます。

そして疎結合化ですが、とある機能を実現する箇所のシグネチャ(一般的にインターフェイスだが、スタティックメソッドの利用が多いためシグネチャとしている)を決め、ランタイムで差し替え可能ではなくても、変更が全体の処理に及ぼす影響をおさえる方針を取っています。

次に、抽象化。これは、データのレベルでは記事サムネイルでやったことです。 2つのサムネイルの仕様があり、両方を正しく扱えるように大きな1つの抽象を定めました。

まずは実装を大きく整頓した

色々と検討しつつも、端的に眼の前にあるコードが触りにくいのはなぜかと考えました。

ピクシブのPHPプロダクトのほとんどはpixiv.gitという1つのリポジトリにまとまっています(所謂モノリポ)。そして、その中のpixivisionの機能がまとめられているディレクトリにpixivisionの機能が基本1ファイル1クラス、ディレクトリも含め、47ファイルの形で平置きされていました。

これを、ディレクトリを含め29ファイルの形に整理しました。

単純に認知コストが下がります。

整頓前の何が辛かったか

全てが平置きされているので、コードを完全に読み解くか、ツールを使ってコースタックを出すまで依存関係が見通せませんでした。なので、記事に関するよろずのことを扱っている神クラスが片付けられません。その整理を依存関係の見通しが悪いことにより阻んでいます。

そしてなにより、2017年の段階で、大半の処理が2種類に分けられるとわかっていましたが、実装に反映されていなかったので、それを適用できた部分が非常に大きいです。

2種類の分類

Adminは管理画面/ビジネスユーザー側の処理で、Publicは公開画面/記事を表示する側の処理です。

Admin側は、CMSだけではなく業務システム的な側面もあり、Publicは単純にAdminの作った記事データを正しく配信するためのWebシステムという形になります。www.pixivision.netから参照されているのはPublicとなります。

整頓方針

PublicとAdminという分類はあるものの、そもそもそれをPublicとAdminと読んでおらずどう呼ぶかも決まっていなかったのでまずは名付けました。

そして、先程の整頓にディレクトリ名として利用し、分離方法をSpotlight Public Admin Responsive Segregation と名付けました。頭文字からエスパーズと呼ぶようにしましたが、Public, Admin単体とちがって、呼ぶ機会はあまりないです。

CQRS(コマンドクエリ責務分離)とセットで考えて、3種類に処理を整頓していきます。

CQRS/SPARS

Public Admin
Command (表示側での書き込み) N/A         管理画面での書き込み Admin/ArticleCommandRoutine
Query 表示側での読み込み Public/AdminQueryRoutine 管理画面での読み込み Admin/ArticleQueryRoutine

CQRSとの関係

Adminは管理画面で、表示と編集を両方行うため、Command, Query両方を実装し、Publicは表示に限られているので、Queryのみを実装することになります。

一番強調したいのは、公開系統に書き込み処理がないので、それを織り込んだ形で処理を整頓してしまうことで、考慮しなくて良いパターンが確実に1つできる、ということです。

整頓結果

整頓結果として、まず、パッケージングを意識し始めることができました。

現状のコードはディレクトリ構造をパッケージやネームスペースとして厳密に切り分ける実装となっていませんが、今後はそのような構造に寄せていっても支障がない状態に近づいています。

次に、影響範囲がパッケージングに現れるため、とある場所を触った場合の影響範囲がわかりやすくなりました。「こちらを触っても直接Publicは壊れない」というのは改修時に大きな安心に繋がります。

そして、このパターンで整頓しきれないクラスは、PublicとAdminにまたがった機能であり、今後のリファクタ方針が浮かび上がってきました。PublicとAdminの共通部分を適切に抽象化するために、OOPやDDDにおける各種原則の実践が視野に入ってきます。

一番大事なことは、ここまでの過程で、惰性で配置するのではなく、もっと意図した配置とアーキテクチャへ向かう基礎ができたことです。

アーキテクチャ改善の続き

どのようなアーキテクチャにすべきかを突き詰めると、アーキテクチャはあくまでもメンテナンス性を高める手段であるため、一番身構えるべき変更がわかっていないという問題が出てきます。

それを脱するために、pixivisionを今後どうしていくか、今の実装をどう変えて何を実現したいかという問いが発生します。どの現場にも言えることですが、限られたリソースで優先順位をつけて改修を実施しなければならないため、精度高く、意図を持って決めておく必要があります。その問いの答えはAdminを用いて記事を編集しているチームとの共同作業で導き出していく必要が出てきました。

これにより、保守だけの状態が終わり、あるべき問いに帰結しました。

アーキテクチャ改善はあくまで手段であるため、次の変更がわからないままの闇雲な変更は却ってメンテナンス性を下げることになるためです。

未来へ届ける

実は、冒頭のpixivisionのリンク先はID=1、最初の記事です。

ここまでの改修で一貫していたのは「過去の記事がそつなく表示できること」で、この通り表示ができるという形でそれは実現しています。これまでpixivisionに携わったすべての人が意図してか意図せずか、記事で紹介した作品と作者を尊敬し、未来に届けるという営みを連綿と続けてきました。その営みを真摯に、されど楽しく受け継いでいきたいと考えています。

icon
fono
2019年4月入社。pixiv事業本部ウェブエンジニアリングチーム所属。データ基盤の利活用、データ集計なども行う。趣味はバイクと自宅(実家)サーバー。