Skip to content

Performance

Key Points

  • Regularly run Lighthouse audits to ensure the site is performant.
  • Strive for the best possible Lighthouse scores.
    • Ideally in the 80-100 range, but definitely above 50.
  • Learn & apply the most performant methods available:
  • Ensure Miva's TTFBs are below or near 500ms

KPIs & Standards

Name Code Definition Good Average Poor
Time to First Byte TTFB Measures the duration from the user or client making an HTTP request to the first byte of the page being received by the client's browser. It is a measurement used as an indication of the responsiveness of a webserver or other network resources. < 500 ms. 500 ms. to 1500 ms. >= 1500 ms.
First Contentful Paint FCP The time when the browser first rendered any text, image (including background images){target=_blank}, non-white canvas or SVG. This includes text with pending webfonts. This is the first time users could start consuming page content < 1800 ms. 1800 ms. to 3000 ms. >= 3000 ms.
Largest Contentful Paint LCP A user-centric metric for measuring perceived load speed because it marks the point in the page load timeline when the page's main content has likely loaded---a fast LCP helps reassure the user that the page is useful. < 2500 ms. 2500 ms. to 4000 ms. >= 4000 ms.
First Input Delay FID A user-centric metric for measuring load responsiveness because it quantifies the experience users feel when trying to interact with unresponsive pages---a low FID helps ensure that the page is usable. < 100 ms. 100 ms. to 300 ms. >= 300 ms.
Cumulative Layout Shift CLS A user-centric metric for measuring visual stability because it helps quantify how often users experience unexpected layout shifts---a low CLS helps ensure that the page is delightful. < 0.1 0.1 to 0.25 >= 0.25

Best Practices

Use Efficient Processes

The code should be efficient, and should not affect TTFB in a way where the site would go down.

Incorrect

<mvt:assign name="l.settings:has_forbidden_items" value="0" />

<mvt:foreach iterator="group" array="basket:groups">
    <mvt:item name="customfields" param="Read_Product_ID( l.settings:group:product_id, 'forbidden', l.settings:group:customfield_values:customfields:forbidden )" />
    <mvt:if expr="l.settings:group:customfield_values:customfields:forbidden">
        <mvt:assign name="l.settings:has_forbidden_items" value="1" />
    </mvt:if>
</mvt:foreach>
Reasoning: Continues to loop when it's not necessary.

Correct

<mvt:assign name="l.settings:has_forbidden_items" value="0" />

<mvt:foreach iterator="group" array="basket:groups">
    <mvt:item name="customfields" param="Read_Product_ID( l.settings:group:product_id, 'forbidden', l.settings:group:customfield_values:customfields:forbidden )" />
    <mvt:if expr="l.settings:group:customfield_values:customfields:forbidden">
        <mvt:assign name="l.settings:has_forbidden_items" value="1" />
        <mvt:foreachstop />
    </mvt:if>
</mvt:foreach>
Reasoning: Once the field has been found, it stops looping.

Use Efficient Functions

Incorrect

<mvt:do file="g.Module_Library_DB" name="l.product_loaded" value="Product_Load_Code( l.product_code, l.product )" />
Reasoning: Not utilizing the "Cached" function when applicable, to possibly reduce a DB lookup.

Correct

<mvt:do file="g.Module_Library_DB" name="l.product_loaded" value="Product_Load_Code_Cached( l.product_code, l.product )" />
Reasoning: Utilizes the "Cached" function when applicable, to possibly reduce a DB lookup.

Avoid Infinite Loops

There should not be a way to create an infinite loop.

Incorrect

<mvt:if expr="g.Empty_Cart">
    <mvt:do file="g.Module_Library_DB" name="l.success" value="Runtime_Basket_Empty( g.Basket:basket_id )" />
    <mvt:do file="g.Module_Feature_PGR_DB" name="l.success" value="BasketDiscountTotal_Delete_All_Basket( g.Basket:basket_id )" />

    <mvt:assign name="l.result" value="miva_output_header( 'Status', '302 Moved Temporarily' )" />
    <mvt:eval expr="miva_output_header( 'Location', 'https://' $ encodeentities(g.domain:name) $ s.request_uri )" />
    <mvt:exit />
</mvt:if>
<a href="&mvte:urls:BASK:auto_sep;Empty_Cart=1">Empty Cart</a>
Reasoning: The code will infinitely loop because the query-string ?Empty_Cart=1 will continue to get passed in the s.request_uri of the following Location headers.

Correct

<mvt:if expr="g.Empty_Cart">
    <mvt:do file="g.Module_Library_DB" name="l.success" value="Runtime_Basket_Empty( g.Basket:basket_id )" />
    <mvt:do file="g.Module_Feature_PGR_DB" name="l.success" value="BasketDiscountTotal_Delete_All_Basket( g.Basket:basket_id )" />

    <mvt:assign name="l.result" value="miva_output_header( 'Status', '302 Moved Temporarily' )" />
    <mvt:assign name="l.result" value="miva_output_header( 'Location', encodeentities( l.settings:urls:BASK:auto ) )" />
    <mvt:exit />
</mvt:if>
<a href="&mvte:urls:BASK:auto_sep;Empty_Cart=1">Empty Cart</a>
Reasoning: The redirect to the new location is a fix value that cannot create an infinite loop.