読者です 読者をやめる 読者になる 読者になる

ペロリ流 開発要件のまとめ方

開発プロセス

こんにちは。開発部のマネージャーをやっている mizushimac です。

今回は開発するモノの要件のまとめ方についてペロリ開発部が実践している内容を少しご紹介したいと思います。みなさんの会社やプロジェクトではどうやって開発するモノの要件をまとめていますか?パワポですか? spreadsheet ですか? 流れ行く slack や github issue で議論しながらコメントに埋もれていき誰かが箇条書きでまとめますか? きっとカオスなことが多いかなと思いますのでこのエントリーが少しでもご参考になればと思います。

ちなみに、ペロリはカオスを楽しめる人を求めていますw

開発要件のまとめ方って色々あって難しい

私が学生の時に所属していたベンチャー企業では、数十MBもあるパワポに画面イメージや画面遷移図、コメントで色々と動作の補足が書いてあるものがあってそれをを見て開発をしていました。仕様の行間は企画の人に口頭確認、エラー処理とかは適当、いまも結構あるあるじゃないでしょうか。

次に新卒で入社した某 SIer の研修で出会ったのは、要件定義書という方眼紙のようになったエクセルのテンプレートにほぼフリーフォーマットで記述的に書いていくものです。 その頃、UMLオブジェクト指向に則ったユースケース・ドリブンな要件定義がプロジェクトにちょっとずつ適用され始めた時期で、これでエクセルで文字書かなくて良いのか!的な安易な発想でツール等を使ってユースケース図を書いてみたりしましたが、図だけでは表現もしきれず、大量の補足資料とユースケース記述が昔のエクセルの要件定義書とほぼ同じような内容だったり、結局どうやって要件をまとめればいいのかということの最適解みたいなものはモヤモヤしたまま過ごしていました。

開発効率は要件のまとめ方次第

「このように開発要件はまとめよ」という世の中の標準や明確な流行りがあるわけではないので、事業会社でサービスの開発要件をまとめるときは、企画する人が書きたいように、使いやすいフォーマットで、異なる粒度で、壮大なスコープでやりたいことを必死で表現しているという状況は共感いただけるのではないかと推測しています。

このような状況で開発を進めていると、

  • プランニング時に要件とスコープの確認や把握に時間がかかる
  • プランニング時に issue 分解や見積もりに時間がかかる
  • 要件漏れや要件履き違えが発生しやすく手戻りが発生することが多い

などなど、いずれも貴重なエンジニアやデザイナーの工数を逼迫する要因になり、結果として組織全体の開発スピードが落ちていることが多いと思います。 sprint の振り返りをして計画との乖離が出るときの問題の多くは、要件の把握漏れや要件履き違えなどに起因していたりします。

このリスクを軽減していくためにもう少しいいやり方はないものかと、エンジニアとして、プロダクト・オーナーとして日々考えてみるわけです。

mini spec というものとの出会い

sanfrancisco

私がペロリに来る前、サンフランシスコの拠点と協業しているときに「mini spec: 〇〇」という google docs を送ってきたエンジニアがいました。彼がやりたい、やるべきだと主張する、とある SDK の新機能(実際には iOS アプリ間のシングル・サインオンの機能)の要件とざっくり設計が書かれていたものでした。内容は、機能の目的、UI のイメージとフローチャート、トークンの種類や処理のイメージ等が含まれ非常に読みやすいもので、他のエンジニアからのコメントがついていて設計の残検討事項があるものの、直ぐに開発に入れるぐらいの具体さがあり、分量も 3 枚程度のものでした。

言語、カルチャーの違いや時差等でコミュニケーションに障壁がある環境だったからこそ、開発するモノの内容を伝える手段として最適なフォーマットにもなっていました。これはきっと、前提となるスキルや知識が異なるエンジニア、デザイナー、ビシネスの人たちとの共通のコミュニケーションツールになるに違いないと感じた瞬間でした。

そのドキュメント見て以来、私は mini spec を書く人間になりました。ここ 3 年ぐらい私が書く mini spec のフォーマットは変わっていませんし、今も継続しています。事業のステークホルダーや発案した企画者、エンジニアやデザイナーと日々話していて、これは開発する必要性が出てきたな、という内容がほぼ決まったタイミングで備忘録的にざっと書きます。そこで得たさまざまな粒度やフォーマットが異なる情報を集めて、情報を整理、自分だったらこう設計するな、という妄想をしながら洗練させていくのです。楽しい時間です。

簡単なものであれば 15 分程度で書き上がります。

ちなみに、「mini spec」で google 検索すると、mac や車の mini なモノのスペックが出てきますので注意してください。完全なる社内用語です。

mini spec はこう書く!

昨年、ペロリに入社してすぐに、私は mini spec という Qiita Team のテンプレートを登録し、早速 mini spec を書きはじめました。すると、自然に「いいね」、「ストック」されるようになり、愛読するエンジニアも現れ、他の企画者や一部のエンジニアも同じように mini spec を書き始めました。

qiita mini spec

さて、mini spec は以下の項目から成ります。書く内容と私なりのポイントを説明したいと思います。

解決すべき課題(Issue to be Solved)

現在顕在化している課題とその解決のニーズを記載します。

ここで意識するのは、いわゆる論文の前書きのように、である調で理系っぽく客観的に記述するようにしています。そのほうがちょっとカッコいいのと、誰が読んでも、それは大きな課題だなあ、やるべきだよなあ、と少し高めのサービス目線から重要性を感じてもらえるからです。

ユーザーストーリー(User Stories)

ユーザー視点でどうあるべきかのストーリーを、ユーザーが機能に気がつくところから箇条書きで順序を追って記載します。

ここで大事なのは、アクターに漏れがないかです。エンドユーザーだけでなく、コンテンツを運用する人や、分析する人、外部のパートナー等あらゆるアクターのユースケースを書きます。そうしないと、管理ツールってどうするの?みたいなあるあるが後で起きて露頭に迷うので最初から決めます。管理ツールがないなら、エンジニアが redis にデータを投入する等書きます。

そして、ここはストーリーなので特にエンドユーザー向けのユースケースは、ワクワクするような、ユーザー感情も一緒に記載するようにしています。

プラットフォーム(Platform)

Web 上か、アプリか、両方か、どの管理ツールか等を記載します。

Web はどうします?とかアプリの過去バージョンはどう対処しますか?といった予想される部分も決めて記載していきます。特に、MERY のように、PC の Web、スマホの Web、スマホアプリと複数のプラットフォームがある場合には丁寧に書きます。最近、AMP をよく忘れます。。。

また、このセクションは、担当するエンジニアが読者であることを強く意識します。つまりは、開発のスコープとざっくり設計です。ほぼほぼ githubリポジトリーの単位に区切って記載しますが、あくまで、提案として、自分がサクッと作るなら、こんなテーブル用意して、この辺のUIを変更して、サーバーとアプリではこんな処理をする、でもパフォーマンスにちょっと懸念が出るかも、この辺セキュリティー気をつけないとね、もっといいやり方ないでしょうか?といったあたりを簡潔に記載します。

決して担当のエンジニアに、「こう作りなさい」という意図ではなく、「こんな感じで作れると思うけどどう?」というニュアンスでざっくりと書いて伝えます。そのほうが、もっとよい設計や実装になると思っているのとエンジニアリングとして一番楽しいところはとっておきます。

KPI(Purpose)

現状の KPI と目標の KPI、もしくは定性的な目的を記載します。

ここも担当のエンジニアに、「この KPI 必達だからな」ではなく、「こういう KPI を追うからそこを最大化できるように、計測可能なように」ということを伝える感じです。なのであまり詳細な KPI は書きませんし、邪念がないほうがいい UX に仕上がるな、と思ったら「定性的だから追わない」と敢えて書く場合もあります。

リリース希望日(Target Due)

希望なのか、デッドラインなのか、理由も含めて記載します。

誰だって、自分の事業に関係するいい企画は早く仕上げて欲しいので、対応時期の希望を結構無茶なスケジュールで言うと思います。期日の根拠をスクリーニングして、本当に社外的なコミットや外部環境に勝機がある場合は特別扱いし、それ以外は他案件との優先度次第で流動的になるので記載しません。

体制 / 規模感(Team)

ざっくり何人でどれくらいの期間かを記載します。

スクラムで言えばいわゆるストーリーポイントですが、スクラムをまたがるとポイントの基準が違いますし、ステークホルダーには伝わりにくい部分もあるので、何人で何日くらいか、という形で記載しています。ただ、明確にエンジニアが見積もった結果ではないので、あくまでざっくり見積もりです。この段階で 1 sprint 以上かかるような場合は、Phase 分けして mini spec 単位で分割します。

リスク(Risks)

認識しているリスクがあれば記載、かつリスクへの対処法を記載します。

リスクは書き出したらキリがないですが、一番大事なのは許容するリスクを書いておくことです。このリスクは承知で挑むのだ、ということを伝えて腹落ち感が出るようにしています。

ステークホルダ(Stakeholders)

この要件に対してレビューが必要な人を by name で記載します。

Qiita のメンションを飛ばしますが、だいたい無反応なことが多いので slack でこれ読んでおいてください、これで進めますよ。と伝えます。

参照(Reference)

参考資料があれば記載します。

過去の mini spec だったり、既存仕様、ベンチマークにしている他社サービスなど index として並べます。

上記項目を網羅的に書くことによって、要件の確認漏れを自分自身で気がつくこともできますし、より精度の高い開発要件をまとめることができます。そして、それをベースにエンジニアやデザイナー、ステークホルダーからインプットをもらい最適なものに仕上げていきます。

mini spec が完成しているかが開発フェーズに入れるかの基準

MERY には、企画が優先度順に並んだ product back log があるのですが、基本的には mini spec (もしくは同等レベルの情報)が完成していないと開発スクラムの sprint back log には載せないルールにしています。そうでないと企画職でないエンジニアやデザイナーが大きめの要件の確認に工数を使ってしまい、開発の計画や日々の開発作業に、待ちの非効率、ベロシティー計測不能、要件とずれた無駄な実装等が生まれることになります。

また、企画系の会議の場では、優先度が高いにもかかわらず内容がフワフワしていて mini spec が完成していないものは企画の進捗が悪いことを伝える明確な理由になります。ゴールを明確にすることで企画と事前デザインのスピードを上げ、組織全体の開発スピードをあげるのに mini spec は役立つわけです。

仕上がった mini spec は、優先度が高いもの順に開発のプランニングに持ち込み、github の各リポジトリーの issue にスムーズに分解され見積もりされます。

MERY の mini spec は誰が書くか!?キミでしょ!

ペロリでは基本的に開発スクラムのプロダクト・オーナーはエンジニアが務めるようにしていますが、mini spec は、プロダクト・オーナー以外のエンジニアでもどんどん書いていいものだと思います。やはり実装の中身が見えているエンジニアのほうがより精度が高くて無駄のない mini spec が書けると思います。

私は、本社の新卒採用をしているのですが、エンジニア志望だがゆくゆくは企画に関わりたい、企画も技術も両方のスキルを高めたい、という will のある人によく出会います。どうしたらいいですか? という問いに対しては、積まれている issue は誰よりも爆速で終わらせて、空いた時間に自分がやりたい、やるべきだと思う mini spec を書いて社内やプロジェクトに幅広く発信しなさい、とアドバイスします。黙っていても、企画ド素人のエンジニアに企画の仕事がアサインされることはありません。 mini spec の内容が光るものであれば、コメントも賛同者も増えて勝手に優先度が上がり、部分的にでもその内容が sprint back log に積まれてくるはずなので、そうなったらこれはオレが言い出ししたんだぞ、ってドヤればいいのです。

というわけで、女の子にかわいいをさらに届け、事業インパクトのある mini spec をちょっと書いてみたいエンジニア、募集してます。

あ、次の機能の mini spec を書かなくては。。。

著者のプロフィール

www.wantedly.com

さらなる高みへ〜iOSのMERYでなめらかなスクロールを実現するためにやった4つのこと

こんにちは。iOSを主に担当していますアプリエンジニアのkazutoyoです。

MERYのアプリチームでは、チューニングを「さらなる高みへシリーズ」と名づけて、日々アプリの改善をしています。

今回はその中で行ったUITableViewやUICollectionViewのスクロール周りを滑らかにする改善についてやったことをご紹介したいと思います。

1. CALayerで角を丸くしている部分のパフォーマンスが悪い

item timeline

このようなカード型のViewが並んでいるCollectionViewがあったのですが、画像の角を丸くするのにCALayerで cornerRadius をつけているところのパフォーマンスがあまり良くないようでした。

これを次のようにCoreGraphicsでUIImageをmaskして角を丸めるようにしてやりました。

let imageRect = CGRectMake(0, 0, image.size.width, image.size.height)
UIGraphicsBeginImageContextWithOptions(image.size, false, 0)

// 上の角だけまるくする
let maskPath = UIBezierPath(roundedRect: imageRect, byRoundingCorners: [.TopLeft, .TopRight], cornerRadii: CGSizeMake(4.0, 4.0))

maskPath.addClip()
image.drawInRect(imageRect)

let roundedTopCornerImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

画像の加工も若干重い処理なのでパフォーマンスはそこまでよくないのですが、CALayerで角を丸めているよりもパフォーマンスが改善し、スクロールがカクつくことが少なくなりました。

2. 実際の表示に必要なサイズ以上の画像をUIImageViewにセットしている

ネットワーク上から読み込んだ画像など、実際の表示に必要なサイズより大きな画像の場合があります。

そのような画像をグリッドのCollectionViewなどで大量に表示される場合、多くのメモリが使われてしまう場合があります。

image grid

このようなときは画像をリサイズしてやってからUIImageViewにセットしてやるようにしました。

func resizeImage(image: UIImage, maxWidth: CGFloat) -> UIImage {
    // UIImageをリサイズする
    let sourceImage = CIImage(CGImage: image.CGImage!)
    let imageRect = sourceImage.extent
    let newSize = CGSizeMake(maxWidth, maxWidth / imageRect.size.width * imageRect.size.height)
    let scale = CGPointMake(newSize.width / imageRect.size.width, newSize.height / imageRect.size.height)
    
    var resizedImage = sourceImage.imageByApplyingTransform(CGAffineTransformMakeScale(scale.x, scale.y))
    resizedImage = resizedImage.imageByCroppingToRect(CGRectMake(0, 0, newSize.width, newSize.height))
    let context =  CIContext(options: [kCIContextUseSoftwareRenderer: false])
    
    return UIImage(CGImage: context.createCGImage(resizedImage, fromRect: resizedImage.extent))
}

3. 必要以外の場面でUIViewのアルファブレンドを使っていた

透過を持つViewの描画はパフォーマンスがあまりよくありません。

必要のない場面ではViewのDrawing設定のOpaqueの項目はチェックを入れておきましょう。

実際のアプリでどのViewがアルファブレンドを持っているか確認する方法として

  • iOS SimulatorのDebugメニューから「Color Blended Layers」を選択
  • InstrumentsのCore AnimationのDebug Optionから「Color Blended Layers」を選択

があります。

こちらを有効にすると次の画像のようにアルファブレンドをもつViewが赤く塗られます。

このオプションをつかってアプリ全体をチェックしてみると意外と必要のないアルファブレンドが見つかるのではないでしょうか。(MERYでは最初ほとんどが赤くなっていました)

4. UILabelを使わずにViewに直接テキストをdrawする

Instrumentsでいろいろ調べていると、UILabelが多く使われているCellのパフォーマンスがあまり良くないことがわかりました。

そこでNSStringのdrawInRectを使ってUILabelを使わないでテキストを描画するようにしました。

let font: UIFont = UIFont.systemFontOfSize(14)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 8

let attributes = [
    NSFontAttributeName: font,
    NSForegroundColorAttributeName: UIColor.grayColor(),
    NSParagraphStyleAttributeName: paragraphStyle
]

str.drawInRect(self.frame, withAttributes: attributes)

UILabelを使うより扱いにくくなるのですが、テキストを多く使うようなCellがある場合に効果があると思います。

まとめ

以上の改善により、MERY内のUITableViewやUICollectionViewを使っているところがサクサク動くようになりました。

smooth_scroll.gif

上のCollectionViewのスクロールではFPSが57~59あたりで動いています。(iPhone6で測定)

MERYのアプリではUITableViewやUICollectionViewといった多くのViewを一度に表示するような画面が多く使われており、この周りをチューニングするだけでアプリ全体の使い心地が大きく変りました。

細かいところではあるのですが、これらのことを気をつけることでより良いアプリになると思います。

MERYではエンジニアを募集しています!興味ある人はご応募ください!

Putting together AMP and Rails

(日本語版はこちら)

Hello!, this is Oskar from the MERY backend team, very nice to meet you!.

Recently we released the AMP version of the MERY article page, and we thought it was a good idea to share why and how we did it. This is the first post we write in English and that is my fault, please bear with me!

AMP? So... what is that, exactly?

Pretty much a new Google invention. It stands for Accelerated Mobile Pages and was launched on February 2016, so this is a very recent one. The main idea behind that pretentious name is trying to come with some standards for coding smartphone sites that loads and gets ready as fast as they possibly can. Makes totally sense, I mean nowadays smartphone sites are very similar in terms of functionality to their big desktop brothers, but most of the time these features are barely used while are consuming bandwidth and resources to load and get in order, resulting in quite performance degradation.

Apple and Facebook thought about this too with their own solutions but AMP, even if is promoted by Google, remains as open-source project, and there are also a couple of invaluable advantages that convinced us to implement it:

  • The AMP page is supposed to load in no time resulting in a great improvement in terms of user experience.
  • When searching from a smartphone, Google will display your pages inside a nice mobile news carousel. User will be able to swipe between articles from different pages and they appear just instantly. This is quite amazing, check it out:

AMP demo

It is integrated with the main search engine, so you can go ahead and try some keywords in the Google page (remember to access from smartphone user agent).

The basic rules

AMP is not a framework. Well technically it is a framework, but doesn't feel like it, is more like a subset of HTML. This means you still get to code more or less the same HTML you already know, but you have to respect some rules and if you do, they ensure your page will be faster than ever.

Before diving in how we tricked Rails to give us the markup we need, let's figure out what these rules are:

No Javascript

Yeah, you read it right: if you want to become an AMP Jedi, forget all you know about Javascript my young Padawan. That's it, really: you can't use Javascript. This seemed to be a tough and big one but wasn't that hard at the end. AMP provides components for the most common functionalities powered by Javascript: analytics, ads, embed video and so on... no need to panic like I did !!

CSS is now inline and must be thin

All the style code has to go inlined between an <style amp-custom> tag inside the <head> section of the page. Moreover, the CSS code is limited to 50Kb, Google thinks that's more than enough for rendering a pretty site. Oh, and you can't use !important, so you must be really sure you are coding it well (I always though that !important shouldn't be used in the first place but we all know we ended using it a lot anyways).

No forms, no inputs

AMP is supposed to just provide information to be read, mostly articles, so they thought forms and inputs are out of the scope. You still get to use buttons, if you need to.

<img> tags are now called <amp-img>

And this is not just a tag rename: you need to specify the size of the image beforehand. There are some options you can use for setting how you want the image to be render using the layout attribute. This ended up being one of the difficult ones, will get into that later.

<img src="image.png"/><amp-img src="image.png" height="250" width="200" layout="responsive"> </amp-img>

The application/ld+json script tag

Actually this is the only one script that can go in your AMP page. It contains a JSON object with information about your content. Be really careful with this one, our pages weren't indexed and after some headaches, we realized it was because of this. Be sure to check the specifications, the one giving us big time was the size of the image which was not big enough.

 <script type="application/ld+json">
    {
      "@context": "http://schema.org",
      "@type": "Article",
      "mainEntityOfPage": "<%= url_for(only_path: false) %>",
      "headline": "<%= @article.title %>",
      "datePublished": "<%= @article.public_at.utc.iso8601 %>",
      "dateModified": "<%= @article.updated_at.utc.iso8601 %>",
      // [...] Be sure to fill all!
   }
</script>

The setup

It is covered in more detail here, but long history short: you need to render a HTML file (no side JS or CSS files) with the following structure:

<!doctype html>
<html ⚡>
  <meta charset="utf-8">
  <link rel="canonical" href="my-non-amp-index.html">
  <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
  <script type="application/ld+json">
  {
    [... json object with information about your content ...]
  }
  </script>
  <script async src="https://cdn.ampproject.org/v0.js"></script>
  <style amp-boilerplate>
    [... amp basic rules, basically a copy paste...]
  </style>
  <style amp-custom>
    [... your css inlined here ...]
  </style-amp>
  <body>
    [... your html here ...]
  </body>
</html>

Then if you open your page in Chrome appending #development=1 at the end of the URL, then you will know everything is in peace with AMP if you see the following message in the console:

AMP validation succesfull

(aaah, we love that message)

Our goal

Knowing what we know now, we can say that our main goal was trying to render pretty much the same layout we have for smartphones but cleaning the code to the maximum so we are under the rules: no JS, minimum needed CSS and image tags who knows exactly how they have to render themselves.

Like most common Rails setups, at MERY we have two application templates: PC and smartphone. Then, each view, including partials, has its own pc or smartphone version as well.

Our first approach was just replicate the smartphone version and remove everything non-amp-friendly. But then we realized we would need to maintain three versions of the same page, and the article page is pretty much the main of our service so that's the one we are improving and updating the most.

Finally what we did was reusing smartphone styles, views and partials as much as possible. When this was not possible or didn't make much sense, then we created an amp version.

Allow me to go through the steps we took for amp-rizing ourselves:

New mime type, route and layout

- config/initializers/mime_types.rb
Mime::Type.register_alias 'text/html', :amp

With this, we are telling Rails there is a new format to be taken into account. There are two good reasons:

  • We can name our view files with .amp.erb and they will be rendered automatically.
  • We can use request.format.amp? inside smartphone templates for deciding what to display.
- config/routes.rb
get "/articles/:id/amp" => "articles#show_amp",  as: :articles_amp, format: :amp

Any route will work here, we just chosen maintaining the same article url with /amp for our new version.

- views/amp/layouts/application.amp.erb

The smartphone version of this file contains too many elements we can't use in amp right away: links to javascript and css files, embedded js for things like Google Analytics, Facebook, Twitter setups including metatags and external libraries....

If there is one file whose amp version is justified, is the layout one without any doubt.

After cleaning all that unusable code, we came out with more or less the amp template described here.

- views/amp/articles/show_amp.amp.erb

And this is the view file which will render after the action show_amp we added in the ArticlesController. We decided also to create a different action because there are many things loaded from database that won't be shown in the much more simplified amp version. I will go through the details later, but here we removed code related to SNS buttons, inline styles, image resizing javascript, some product carousel, and again code for Google Analytics and Ads.

The canonical and amphtml tags

This is how Google will know your AMP page exists, you must link each version in the following way:

  • AMP template, link to the original version with: <link rel="canonical" href="http://example.com/articles/123456" >
  • "Normal" version, link to AMP version with: <link rel="amphtml" href="http://example.com/articles/123456/amp" >

As we are using Rails, this can be done using the URL helpers:

  • AMP: <link rel="canonical" href="<%= article_url(@article) %>" >
  • "Normal": <link rel="amphtml" href="<%= url_for(only_path: false) %>/amp" >

Reusing the styles

At this point, the basic setup is done, not much trouble here: going to /amp should render the show_amp template through the application.amp layout. Well, styles are not in place, but the page should appear.

From here is when it gets tricky, basically we have two things to do:

  • Find a way of reusing the SCSS code we already have for smartphone, but sliced to the very minimum version
  • At the same time, grab that generated CSS file, compress and inject the code directly inside the <style amp-custom> tag placed in the head section of the application.amp.erb layout file.

For the first part, we created an alternative version of the main.scss file and we called it main_amp.scss. That file contains a lot of @import directives that builds the final css file. In the main_amp.scss file we just left the essentials:

//
// Utils
//
@import "utils/sp_sprites";

// =====================================
// Header/Footer
// =====================================
@import "modules/sp/header";
@import "modules/sp/footer";

// =====================================
// Parts(使い回せるパーツ)
// =====================================
@import "modules/mery_font";
@import "modules/amp/layout";
@import "modules/sp/typography";
@import "modules/sp/buttons";
@import "modules/sp/navigation";
@import "modules/sp/headerBanner";
@import "modules/amp/article";
[...]

As the final size was way bigger than the 50Kb limit, we also created some amp versions of big style files like layout and article, leaving just the minimum until the size was below the limit (in the previous code, /sp stands for smartphone).

Now the second part: inject this in the template file. As per Rails standards, we are using the stylesheet helper in our layout:

<%= stylesheet_link_tag 'main_amp' %>

Which will link to the compiled compressed version of the stylesheet:

<link rel="stylesheet" href="/styles/main_amp.26d3d0d3.css"/>

We want the same but instead of referencing it, we want to inject it directly into the template. If we know beforehand the name of the CSS file, then something like this will do:

  <style amp-custom>
     <%= File.read("main_amp.css").html_safe %>
  </style>

But the file name changes every time is regenerated by the assets pipeline, even more, the file is generate and uploaded to some CDN. So the next step is to figure out where and what is the name of the final css file and luckily for us, this can be done using the stylesheet_url helper.

This is what we ended having in our application.amp.erb:

<style amp-custom>
  <%= style_amp_css %>
</style>

And the style_amp_css code, in a helper, looks like this:

def style_amp_css
  css_route = stylesheet_url('main_amp.css')
  match = /href\s*=\s*"([^"]*)"/.match(css_route)
  if match
    css_url = match[1]
    css_uri = URI(css_url)
    original_css = Net::HTTP.get(css_uri)
    original_css.gsub('@charset "UTF-8";', '').gsub('!important', '').force_encoding("UTF-8").html_safe
  end
end

It gets the href value, retrieves the remote content and does three things needed for the AMP validator:

  • Removes @charset "UTF-8" line added automatically by the sass parser. AMP hates it.
  • Removes the !important directive which is, well, important for our shared smartphone template css. We made a few adjustments so the AMP layout also looks well without them (mostly writing more specific rules)
  • Adds the UTF-8 encoding to the file, this is something Net::HTTP doesn't do well, so we had to do it ourselves.

The first 20 times we run this we got a CSS over 50Kb limit error, after cleaning the code to the maximum and compressing it, you can believe us there was a big smile in our faces when we saw the never desired enough message: AMP validation succesfull

Reusing the partials

Apart from the application.amp.erb and show.amp.erb files, we wanted to reuse as much as possible the code we have for the smartphone template, at the end, our goal was showing an AMP version of the same smartphone design. But it has to make sense, I mean, we don't want a view filled with if request.format.amp? conditionals that makes the code difficult to read and maintain. I think we did a good job there keeping a good balance, here is how we did it:

  • Created a sp_image_tag helper which will render img or amp-img on each case. Will go with this in more detail in the next section. Changed all the rails image_tag calls to sp_image_tag inside the shared views.
  • From AMP view we rendered smartphone views using the following syntax:
<%= render partial: "articles/header", formats: [:html], locals: {user: @user } %>

The image tag helper

The next step in our road is dealing with images. Instead of using <img> we need to use a new tag called <amp-img>. That is pretty easy, the hard part is that we need to tell AMP the size of the image. Most of the time we left that to CSS with something like width: 100% which renders the image with the correct aspect ratio, even more, if we specify the height and width in the img tag, the rendered image will normally be much bigger breaking the layout.

In AMP tags we have an attribute called layout that gives us some power over the way elements renders. It is very well explained here. Basically, we want the big images to be render with layout="responsive" attribute which will tell AMP to display the bigger image that fits in the layout without losing the aspect ratio. If the image is small, then we don't need a layout attribute at all, just render the image as it is.

So at this point we need to code some helper called sp_image_tag that:

  • Outputs img or amp-img tag depending on request.format.amp? condition
  • Adds width and height always in the case of amp-img, and adds layout="responsive" if the image is big.
  • Adds width and height in standard img for smartphone if needed too

Luckily for us we have in our database the width and height of most of the images we show, so here is the code:

def sp_image_tag(src, options={})
  if request.format.amp?
    options[:width]  = options[:ampwidth]  if options[:ampwidth]
    options[:height] = options[:ampheight] if options[:ampheight]
    [:ampwidth, :ampheight, :size].each{|k| options.delete(k)}
    options[:layout] = "responsive" if options[:width].to_i >= 250
    amp_image_tag(options.merge!(src: src)) // Will go through this one later
  else
    [:layout, :ampwidth, :ampheight].each{|k| options.delete(k)}
    image_tag(src, options)
  end
end

This in a shared partial view: <%= sp_image_tag "http://example.com/images/first.png", width: 400, height: 400 %>

Will render:

  • Smartphone: <img src="http://example.com/images/first.png" width="400px" height="400px"/>
  • AMP <amp-img src="http://example.com/images/first.png" width="400px" height="400px" layout="responsive"> </amp-img>

This one: <%= sp_image_tag "http://example.com/images/first.png", ampwidth: 200, ampheight: 400 %>

Will render:

  • Smartphone: <img src="http://example.com/images/first.png"/>
  • AMP <amp-img src="http://example.com/images/first.png" width="200px" height="400px"> </amp-img>

And this one: <%= sp_image_tag "http://example.com/images/first.png", ampwidth: 400, ampheight: 400, width: "100%" %>

Will render:

  • Smartphone: <img src="http://example.com/images/first.png" width="100%"/>
  • AMP <amp-img src="http://example.com/images/first.png" width="400px" height="400px" layout="responsive"> </amp-img>

Pretty much we have all the cases covered. But there is still another helper called amp_image_tag used inside the previous code:

def amp_image_tag(options={})
  content_tag("amp-img",
    "<amp-img fallback src='http://example.com/images/noimage2.png')}' width=350 height=350 layout='responsive'></amp-img>".html_safe,
    options.merge!(noloading: ""))
end

That one renders the amp-img tag itself but also adds a couple of things:

  • Another amp-img tag inside with a fallback attribute. If the image fails to load, then AMP will show the fallback image instead (ensure the fallback image exists!).
  • It adds the noloading attribute. That means it won't show a ajax kind of small loading animation in the image space, this is just a matter of taste, we just didn't like it.

Custom components

_ AMP has some components that can be used right away and solves most of the issues we may have because we can't use javascript. Just adding the correct library to the head section of your template and then rendering the tag with the proper attributes will make the trick:

And we are done

Let's see: the /amp route renders the application.amp.erb layout and show_amp.amp.erb views which uses most of the smartphone partials we already had. The CSS is generated reusing existing code and is below 50Kb, there are not inline styles, the markup doesn't contain img tags or any javascript code and the design looks quite good from our iPhone pointing to staging.

Yeah, we are done! :)

Before going to live, be sure to check these:

  • The #development=1 AMP page gives us the wonderful AMP validation succesfull

  • Confirm the Googlebots can access your page! Yeah, I said GooglebotS with 'S' because there are more than one, even a smartphone version!

  • Once more, go through the application/ld+json script tag ensuring it generates all the needed information

  • And don't forget to add to both versions the canonical and amphtml tags that links each other.

  • And go for it!!

If everything is fine, give Google one night, and check if your pages comes here: g.co/ampdemo !!


(日本語版)

¡Hola!

初めまして、MERYのバックエンドチームのオスカルです!

最近、私たちはMERY記事ページのAMPバージョンをリリースしました。そして、「なぜ」と「どのようにして」私たちがAMPに対応したのかを共有することは、よいアイディアだと考えています。この記事は私たちが英語で投稿する最初のもので―それは私のせいなのですが―ちょっとだけ辛抱してお付き合いください!

AMP? えっと、AMPって何?

まあ、Googleの新しい発明品です。Accelerated Mobile Pagesのアクロニムで2016年の2月にローンチされた、とても新しいものです。その派手な名前の裏に隠されたアイディアは、モバイルサイトの読み込みと表示をできるだけ速くするために一種のコーディング規約を定めようとするものです。これはとても有意義なことです。すなわち、今日のモバイルサイトはデスクトップの兄弟と同様の機能を備えていますが、多くの機能はほとんど使われないにも関わらず順調に端末の帯域とリソースを消費して、結果的にパフォーマンスもひどいものになります。

この問題に関してAppleApple NewsFacebookInstant Articlesという独自の解決策を考えています。AMPはGoogleによって推進されてはいますが、オープンソースプロジェクトであり、また、私たちが実装すべきと確信するいくつかの重要な利点があります:

  • AMPに対応したページは瞬時にロードされるので、UXを格段に向上させることができます。
  • スマートフォンから検索した時、Googleのナイスなニュース枠のカルーセルにページを表示してくれる可能性があります。これはとてもクールです。

AMP demo

この機能は既にGoogle検索エンジンに統合されているので、キーワードを入れて試してみることができます(スマートフォンのUser agentでアクセスすることをお忘れなく)。

基本的なルール

AMPはフレームワークではありません。いや、技術的にはフレームワークなのですが、どちらかと言えばHTMLのサブセットという感じを受けます。そのようなわけで、大体は今まで慣れ親しんできたHTMLでコーディングすることができるのですが、いくつかのルールを守る必要があります。そして、そのルールが守られていれば、今までにないくらいあなたのサイトは高速になります。

私たちがどのようにRailsを巧みに操って必要なマークアップを出力させたかを見る前に、それらのルールがどのようなものか把握しましょう。

Javascriptは使えない

その通り!「もし君がAMPのジェダイになりたければ、JavaScriptについての全てを忘れたまえ。若きパダワンよ。」これが真実です。JavaScriptは使うことができません。これはとても辛くて困難なことのように思えますが、結果的にはそれほど難しいことではありませんでした。AMPはJavaScriptでよく使われる機能を備えたコンポーネントを提供してくれています: アナリティクス、広告、ビデオ埋め込みなどなど。私がかつて陥ったようなパニックになる必要はありません!

CSSはインラインで小さいサイズで

全てのCSSコードはheadセクションの<style amp-custom>タグの中にインライン化しなければなりません。さらに、そのサイズは50KBに制限されており、Googleは高品質なサイトを作るにはそれで十分だと考えているようです。ああ、そして、!importantは使うことができません。そのため、本当にきちんとしたコーディングをする必要があります。(もちろん、!importantは使うべきではないと私も常々思っていますが、サイトが拡大するにつれて結局たくさん使ってしまうものですよね)。

フォームやinputは使えない

AMPは記事などの読むための情報を提供することを前提としているため、フォームや入力といったものは対象外と考えられているようです。ただし、必要であればボタンを使うことはできます。

<img>タグの代わりに<amp-img>を使う

これはただ単にタグの名前が変わるだけではなく、画像のサイズを事前に指定する必要も出てきます。また、画像がどのようにレンダリングされるかを指定するためのオプションをlayout属性で指定することができます。これは結果的に難しい問題の一つでした(詳細は後述します)。

<img src="image.png"/><amp-img src="image.png" height="250" width="200" layout="responsive"> </amp-img>

application/ld+jsonスクリプトタグ

これがAMPページに存在する唯一のスクリプトで、コンテンツに関する情報を含んだJSONオブジェクトです。このタグはとても注意深く扱って下さい。過去、私たちのページはいつまでもインデックスされず、頭痛に悩まされ続けた末、これが原因だと気付きました。仕様をきちんと確認して下さい。私たちが最も悩まされた問題は、画像サイズが十分大きくないことによるものでした。

 <script type="application/ld+json">
    {
      "@context": "http://schema.org",
      "@type": "Article",
      "mainEntityOfPage": "<%= url_for(only_path: false) %>",
      "headline": "<%= @article.title %>",
      "datePublished": "<%= @article.public_at.utc.iso8601 %>",
      "dateModified": "<%= @article.updated_at.utc.iso8601 %>",
      // [...] Be sure to fill all!
   }
</script>

基本的な設定

詳しいことは全てここで書かれていますが、手短に言えば、以下の構造を持ったHTLMファイル一つが必要です(他にJSやCSSは要らないです)。

<!doctype html>
<html ⚡>
  <meta charset="utf-8">
  <link rel="canonical" href="my-non-amp-index.html">
  <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
  <script type="application/ld+json">
  {
    [... json object with information about your content ...]
  }
  </script>
  <script async src="https://cdn.ampproject.org/v0.js"></script>
  <style amp-boilerplate>
    [... amp basic rules, basically a copy paste...]
  </style>
  <style amp-custom>
    [... your css inlined here ...]
  </style-amp>
  <body>
    [... your html here ...]
  </body>
</html>

そして、このページのURLに#development=1を付けてGoogle Chromeで開けば、AMPによって平和が訪れたことを知ることになるでしょう(ただし、以下のメッセージがコンソールに出た場合)。

AMP validation succesfull

(aaah, we love that message)

私たちの目的

AMPの基本的なルールを知ってもらった今、私たちの目的を伝えることができます。それは、私たちのモバイルアプリと同じ品質とレイアウトで、AMPのルール―JavaScriptなし、必要最小限のCSS、自分がどうやって描画されるか知っている画像―に最大限則ったクリーンなコードを書くことです。

多くのRailsアプリがそうであるように、MERYでもPCとスマートフォン向けの二つのアプリケーションテンプレートがあります。そして、個々のビュー(パーシャルテンプレートも含む)もPCとスマートフォンそれぞれのバージョンがあります。

私たちの最初のアプローチは、単にスマートフォン向けのテンプレートをコピーして、AMPフレンドリーでない要素を削除するというものでした。しかし、それによって私たちは同じページの三つのバージョンを管理する必要があることに気づきました。また、記事ページは私たちのサービスのメインコンテンツなので、頻繁に改善やアップデートが行われます。

最終的に私たちがやったことは、スマートフォン向けのスタイル、ビュー、パーシャルを可能な限り再利用することでした。そして、再利用が不可能だったり、あまりにも難解になった時は、AMPバージョンを作りました。

少し長くなりますが、私たちがMERYをAMP化した際のステップを順を追って説明します。

新しいMIMEタイプ、ルーティング、レイアウト

- config/initializers/mime_types.rb
Mime::Type.register_alias 'text/html', :amp

この設定によって、Railsに新たに対応すべきフォーマットを指定しています。これには二つの利点があります。

  • ビューファイル名を.amp.erbとすることができて、自動的にレンダリングされます。
  • スマートフォン用テンプレートでrequest.format.amp?というメソッドを使うことができるようになって、出し分けができるようになります。
- config/routes.rb
get "/articles/:id/amp" => "articles#show_amp",  as: :articles_amp, format: :amp

ルーティングはこのように書くことができます。私たちは記事ページのURLに単に/ampをつけたものをAMPバージョンとしました。

- views/amp/layouts/application.amp.erb

既存のスマートフォン向けのレイアウトファイルはAMPで使えない要素で溢れていました: JavaScriptCSSファイルへのリンク、Google AnalyticsFacebookTwitterの埋め込みスクリプトやメタタグ、外部ライブラリなどなど。

もしAMPバージョンのファイルとして一つだけ存在が許されるものがあるとしたら、それは明らかにレイアウトファイルでしょう。

全ての使えないコードを綺麗にした後、ここで示されているものとほとんど同じものができました。

- views/amp/articles/show_amp.amp.erb

そして、これが私たちがArticlesControllerに追加したshow_ampアクションによって描画されるビューファイルです。私たちは新しいアクションを定義することに決めました。なぜなら、既存のアクションにはシンプルなAMPバージョンでは使われることのないオブジェクトが多数あったからです。詳細は後述しますが、ここではSNSボタン、インラインスタイル、画像の調整スクリプト、いくつかの商品カルーセルGoogle Analyticsと広告を削除しました。

canonicalamphtmlタグ

これがGoogleがAMPページがあるかどうかを知るための方法です。以下のように、それぞれのバージョンをリンクする必要があります。

  • AMPのテンプレートでノーマルバージョンにリンクするには: <link rel="canonical" href="http://example.com/123456" >
  • ノーマルバージョンでAMPバージョンにリンクするには: <link rel="amphtml" href="http://example.com/123456/amp" >

私たちはRailsを使っているので、これはURLヘルパーで達成することができます。

  • AMP: <link rel="canonical" href="<%= article_url(@article) %>" >
  • ノーマル: <link rel="amphtml" href="<%= url_for(only_path: false) %>/amp" >

スタイルの再利用

ここまでで基本的な設定は終わりで、大きなトラブルもありませんでした。/ampにアクセスするとapplication.ampレイアウトを通してshow_ampテンプレートが描画されます。もちろん、まだスタイルはあたっていませんが、ページは表示されます。

ここからはちょっとトリッキーになります。基本的に、私たちのやるべきことは二つあります。

  • 現在のスマートフォン向けのSCSSを再利用しつつ、かつとても小さくします。
  • 同時に、生成されたCSSファイルを圧縮して、application.amp.erbheadセクション内の<style amp-custom>タグに直接注入します。

最初の段階では、main.scssのAMPバージョンのファイルmain_amp.scssを作りました。このファイルは最終的なCSSを出力するための多くの@importディレクティブを含んでおり、main_amp.scssでは本質的なものだけを残しました。

//
// Utils
//
@import "utils/sp_sprites";

// =====================================
// Header/Footer
// =====================================
@import "modules/sp/header";
@import "modules/sp/footer";

// =====================================
// Parts(使い回せるパーツ)
// =====================================
@import "modules/mery_font";
@import "modules/amp/layout";
@import "modules/sp/typography";
@import "modules/sp/buttons";
@import "modules/sp/navigation";
@import "modules/sp/headerBanner";
@import "modules/amp/article";
[...]

最終的なサイズが50KBの制限を超えてしまったため、特に大きなlayoutarticleのスタイルファイルのAMPバージョンを作り、サイズが制限内に収まるまで不必要なものをそぎ落としました(上記のコードでは、/spスマートフォンの略語です)。

二段階目では、これをテンプレートファイルに注入しました。Railsに従って、私たちはスタイルシート用のヘルパーを使っています。

<%= stylesheet_link_tag 'main_amp' %>

そして、これがコンパイルされたCSSへのリンクになります。

<link rel="stylesheet" href="/styles/main_amp.26d3d0d3.css"/>

私たちは同じCSSがほしいわけですが、参照するのではなく、テンプレートに直接注入して欲しいのです。もし私たちがCSSのファイル名を事前に知っていたら、こんな感じで実現できるでしょう。

<style amp-custom>
  <%= File.read("main_amp.css").html_safe %>
</style>

しかしながら、ファイル名はアセットパイプラインによって再生成されますし、さらにCDNにアップロードされます。よって、次のステップは最終的なCSSファイルの場所と名前を知ることです。幸運なことに、これはstylesheet_urlヘルパーを使うことで実現できます。

最終的に、私たちのapplication.amp.erbはこのような形になりました。

<style amp-custom>
  <%= style_amp_css %>
</style>

style_amp_cssはヘルパーとしてこう定義されています。

def style_amp_css
  css_route = stylesheet_url('main_amp.css')
  match = /href\s*=\s*"([^"]*)"/.match(css_route)
  if match
    css_url = match[1]
    css_uri = URI(css_url)
    original_css = Net::HTTP.get(css_uri)
    original_css.gsub('@charset "UTF-8";', '').gsub('!important', '').force_encoding("UTF-8").html_safe
  end
end

このメソッドはhref属性の値を取って、リモートのCSSファイルを取得した後にAMPのために三つのことを行います。

  • SASSパーサーが自動的に付ける@charset "UTF-8"の行はAMPに嫌われているので削除します。
  • !importantディレクティブ―私たちの共通CSSにおいて重要なもの―を削除します。そして、この指示文がなくてもAMPレイアウトがきれいになるように少し調整を加えました(大体は、より限定されたルールを書きました)。
  • ファイルをUTF-8エンコードしました。これはNet::HTTPがうまくできないため、自分たちでやる必要がありました。

最初の20回くらいはCSSが50KB制限を超えているエラーに陥りましたが、できるだけコードをきれいにしたり圧縮した末に、私たちの顔に大きな微笑みをもたらしたあのずっと待ち望んでいたメッセージが現れました。

AMP validation succesfull

パーシャルテンプレートの再利用

一旦application.amp.erbshow_amp.erbファイルのことを忘れて、私たちが成し遂げたいことを思い返してみると、スマートフォンのテンプレートを可能な限り再利用しつつ同じデザインのAMPバージョンを作りだすことでした。そして、それは納得できる形である必要があります。すなわち、私たちはビューテンプレートをif request.format.amp?といった条件文で満たされた、読みにくくメンテしにくいものにはしたくありません。私たちはこの点においてよいバランスを取ることができたと考えていて、そのために私たちがしたことが以下の二つです。

  • imgamp-imgタグを適切に描画するsp_image_tagヘルパーを作りました(詳細については次の節で説明します)。そして、Railsimage_tag呼び出しをsp_image_tagに書き換えました。
  • AMPのビューからは、以下のようにスマートフォンのビューを描画するようにしています。
<%= render partial: "articles/header", formats: [:html], locals: {user: @user } %>

画像タグのヘルパー

私たちの道の次のステップは画像をどうにかすることです。<img>タグを使う代わりに、<amp-img>タグを使う必要があるのです。これは実際とても簡単でした。難しいのは、画像のサイズをAMPに教える必要がある点でした。これまでほとんどの場合、私たちはサイズの問題をCSSに任せてきました: width: 100%と記述すれば、画像をレイアウトに合わせて正しいアスペクト比で表示してくれます。そして、もしheightwidth<img>タグに指定すると、画像は通常大き過ぎるためレイアウトが崩れてしまいます。

AMPタグには、layout属性という私たちに要素のレンダリングを制御する力を与えてくれるものが在ります(ここで詳しく説明されています)。基本的には、私たちは大きな画像にlayout="responsive"属性を付け、AMPに画像のアスペクト比を保ちつつレイアウトにフィットさせるよう指定したいのですが、画像が小さい場合はlayout属性は付けずにそのまま表示させたいのです。

そのため、以下の要件を満たすsp_image_tagヘルパーが私たちには必要でした。

  • request.format.amp?条件に従ってimgまたはamp-imgタグを出力する
  • amp-imgの時は常にwidthheightを付け、また、画像が大きい場合はlayout="responsive"を付ける
  • 普通のimgタグにも必要であればwidthheightを付ける

幸運なことに、私たちが扱うほとんどの画像の幅と高さはデータベースに保存されていました。そして、sp_image_tagは以下のようになりました。

def sp_image_tag(src, options={})
  if request.format.amp?
    options[:width]  = options[:ampwidth]  if options[:ampwidth]
    options[:height] = options[:ampheight] if options[:ampheight]
    [:ampwidth, :ampheight, :size].each{|k| options.delete(k)}
    options[:layout] = "responsive" if options[:width].to_i >= 250
    amp_image_tag(options.merge!(src: src)) // Will go through this one later
  else
    [:layout, :ampwidth, :ampheight].each{|k| options.delete(k)}
    image_tag(src, options)
  end
end

とある共通のパーシャルビューテンプレートのこの行は、

<%= sp_image_tag "http://example.com/images/first.png", width: 400, height: 400 %>

以下のようになります。

  • Smartphone: <img src="http://example.com/images/first.png" width="400px" height="400px"/>
  • AMP <amp-img src="http://example.com/images/first.png" width="400px" height="400px" layout="responsive"> </amp-img>

また、こちらは、

<%= sp_image_tag "http://example.com/images/first.png", ampwidth: 200, ampheight: 400 %>

こうなります:

  • Smartphone: <img src="http://example.com/images/first.png"/>
  • AMP <amp-img src="http://example.com/images/first.png" width="200px" height="400px"> </amp-img>

そして更に、

<%= sp_image_tag "http://example.com/images/first.png", ampwidth: 400, ampheight: 400, width: "100%" %>

こうです:

  • Smartphone: <img src="http://example.com/images/first.png" width="100%"/>
  • AMP <amp-img src="http://example.com/images/first.png" width="400px" height="400px" layout="responsive"> </amp-img>

これでほとんど私たちの対応は終わりました。あと一つだけ、sp_image_tagで使われていたamp_image_tagヘルパーが残っていました。

def amp_image_tag(options={})
  content_tag("amp-img",
    "<amp-img fallback src='http://example.com/images/noimage2.png')}' width=350 height=350 layout='responsive'></amp-img>".html_safe,
    options.merge!(noloading: ""))
end

これはamp-imgタグを返しますが、合わせていくつかのことをしています:

  • fallback属性の付いた別のamp-imgタグを内包しています。もし画像のロードが失敗した場合に、AMPではこのfallback画像が代わりに表示されます(fallback画像が存在することをちゃんと確認してくださいね!)
  • noloading属性を付けています。これによって、AJAX的なローディングのアニメーションが出なくなります。これは単なる好みの問題で、私たちの好みには合わなかったということです。

カスタムコンポーネント

AMPはJavaScriptが使えないことによる多くの問題を簡単に解決するためのいくつかのコンポーネントを用意しています。テンプレートのheadセクションに適切なライブラリを指定して、正しい属性を持ったタグをレンダリングすれば、魔法のようにうまく動きます。

最後に

さて、/ampにアクセスすると、application.amp.erbレイアウトと既存のスマートフォン向けパーシャルテンプレートを多用したshow_amp.amp.erbレンダリングされます。インラインのスタイルはなく、CSSは既存のコードを再利用しつつ50KB以下で再生成され自動的に展開されます。マークアップにはimgタグやJavaScriptコードも入っていません。そして、ステージングに向けたiPhoneから見たデザインはとても素晴らしいものでした!

Yeah, we are done! :)

公開する前に、最後に以下をチェックしましょう。

  • #development=1を付けたAMPページが素晴らしきAMP validation succesfullを表示することを。
  • Googleボット「達」がページにアクセスできることを確認しましょう。そう、Googleボットはスマートフォン向けのものでもいくつかあるのです。
  • もう一度、application/ld+jsonスクリプトタグに必要な情報が全て入っていることを確認しましょう。
  • 通常バージョンとAMPバージョンのcanonicalamphtmlタグが相互にリンクされていることを確認しましょう。
  • リリースしましょう!

もし全てがうまくいったら、Googleボットを一晩だけ待ってあげて、g.co/ampdemoにあなたのページが載っているかをチェックしましょう!

© peroli, Inc.