Skip to content

Feature Toggles

Features still in development can ship to production switched off. Volare staff turn them on per browser to validate them on real data; customers always get the current experience.

No dependencies added. The toggles live in the Astro frontend. The backend has no toggle mechanism yet (see Backend).

A feature is on only when both conditions hold:

  1. The browser carries the staff cookie volare_internal=internal. Arkana issues it on every authenticated request, and /staff issues it on demand (see Staff-traffic exclusion for that cookie).
  2. The feature was turned on with the ff query parameter, on this request or an earlier one.
URL Effect
?ff=itinerary-v2 Turn a feature on
?ff=-itinerary-v2 Turn a feature off
?ff=reset Turn every feature off
?ff=itinerary-v2,other Several instructions, applied left to right

The result is stored in the volare_features cookie (30 days, httpOnly, secure outside local dev), so a feature stays on while navigating. Without the staff cookie both the parameter and the stored cookie are ignored, so a shared link never shows a hidden feature to a customer. Names not in the feature list are ignored, so a typo or a retired feature does nothing.

While any feature is on:

  • The navbar logo gets a dashed yellow outline and a “Pruebas” tag. Hovering the logo lists the active features; ?ff=reset turns them off. Pages that do not render the shared navbar (the checkout pages) show no mark.
  • The response carries Cache-Control: private, no-store, so a page rendered with a hidden feature can never be stored in a shared cache and served to customers. The same applies to any response that writes the toggle cookie, including ?ff=reset.
  • volare_internal is a plain-text cookie that anyone can create in the browser’s developer tools. Toggles hide unfinished UI; they are not access control. Never put anything sensitive behind one.
  • The staff cookie is scoped to .byvolare.com, so it never reaches the staging previews on workers.dev. To test there, create volare_internal=internal by hand in the developer tools.
File Role
src/shared/config/features.ts Feature list, Feature type, URL/cookie parsing, isFeatureEnabled
src/middleware.ts (resolveFeatureToggles) Applies ?ff=, writes the cookie, sets locals.features and the Cache-Control header
src/shared/ui/Navbar.astro → Navbar.react.tsx (previewFeatures) “Pruebas” mark on the logo
  1. Register it in FEATURES in src/shared/config/features.ts. The Feature type is derived from that object, so checking an unregistered name is a compile error. To offer it in Arkana, also add the key and a label to previewable in backend/config/feature_toggles.php (see Previewing from Arkana).

  2. Build the new version next to the current one (for example TextItineraryV2/ beside TextItinerary/) and branch on it in .astro frontmatter:

    ---
    import { isFeatureEnabled } from '../../../shared/config/features';
    const isItineraryV2 = isFeatureEnabled(Astro.locals, 'itinerary-v2');
    ---
    {isItineraryV2 ? <TextItineraryV2 {...props} /> : <TextItinerary {...props} />}

    React islands never read the cookies: pass the boolean down as a prop. Keep shared files (types, mappers) additive so the current output does not change.

    Don’t copy behaviour both versions need: move it into a shared module that neither version owns, so deleting the old version later removes nothing the new one uses. The itinerary-v2 versions share src/components/viaje/shared/ (floating-bar visibility, itinerary day transitions and tabs, the intro reveal script) and useConfiguratorDrawerTriggers.

  3. Validate it in production with ?ff=<key>, or from Arkana.

  4. Ship it by deleting the old code, the isFeatureEnabled checks, the FEATURES entry and its feature_toggles.previewable entry. No dead toggles stay behind.

Key What it changes
itinerary-v2 Product page: the story-style day-by-day itinerary (TextItineraryV2) and the price + “Reservar viaje” floating bar (ProductFloatingActionsV2, mapFloatingButtonsV2). Both switch together because the v2 itinerary drops the dismiss events the current bar relies on.
itinerary-module Product page: the day-by-day itinerary module (ItinerarySection, fed by mapProductToItineraryTrip) with its full-screen day view, each day grouped by time slot, preceded by the trip’s editorial paragraph (ItineraryIntro). Each day row is named by its place — the day label written in Itinerary Info, tidied on display (all-caps words recased, long dashes turned into -); overnight flight days read “Vuelo a ” or “Vuelo de regreso”. The full-screen day view keeps the day’s title. It keeps the current floating bar with shortItinerary on, which reads an itinerary shorter than one screen and scrolls directly. Takes precedence over itinerary-v2 when both are on. Customers also see it on any product whose Show the new day-by-day itinerary switch is on in Arkana (see Backend); the toggle previews it on every other product.

A product’s Preview on Frontend (and Preview Draft) dropdown has one option per entry in backend/config/feature_toggles.php → previewable, next to “Normal” and “With flight diagnostic”. Each option opens the exact state it names: “Normal” and “With flight diagnostic” add ff=reset, and a feature option adds ff=reset,<key>, so a feature left on by an earlier preview never carries over. No /staff hop is needed: the admin panel already re-issues the staff volare_internal cookie on every request (EnsureInternalTrafficCookie), so whoever clicks has it.

The backend list mirrors the frontend one and cannot be derived from it (the apps deploy separately). A key the frontend does not know would be ignored there and open a normal preview, so tests/Unit/Config/FeatureTogglesConfigTest.php fails when a previewable key is missing from FEATURES. It runs in CI, which has the whole monorepo and also triggers on changes to features.ts; inside Sail, which only mounts backend/, it is skipped. Adding a feature to Arkana stays optional.

There is no backend toggle mechanism yet: itinerary-v2 is frontend-only. The day-by-day itinerary module also has a per-product switch: products_by_market.show_itinerary_module (default off), exposed as show_itinerary_module on the product endpoints and set in Arkana (Show the new day-by-day itinerary). ProductPage.astro renders the module when that field is true or the itinerary-module toggle is on, so content launches it to customers product by product while staff keep previewing it anywhere. The product page is rendered per request from the API, so flipping the switch takes effect without a deploy. ?ff=reset and the “Normal” preview do not hide the module on a product whose switch is on. itinerary-module reads each day’s editorial items (Day Highlights & Activities) from the product payload, falling back to the included activities and their time_slot for products that have none yet; every request receives both, and the current itinerary ignores them. When a feature needs the backend to behave differently, the Astro server can forward the active features on its requests to the API. Laravel should trust that information only on requests authenticated with INTERNAL_API_TOKEN. Laravel Pennant would be a new dependency and needs approval; consider it when user-level or percentage rollouts are needed.