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).
How a feature turns on
Section titled “How a feature turns on”A feature is on only when both conditions hold:
- The browser carries the staff cookie
volare_internal=internal. Arkana issues it on every authenticated request, and/staffissues it on demand (see Staff-traffic exclusion for that cookie). - The feature was turned on with the
ffquery 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=resetturns 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.
Limits
Section titled “Limits”volare_internalis 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 onworkers.dev. To test there, createvolare_internal=internalby hand in the developer tools.
Module structure
Section titled “Module structure”| 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 |
Lifecycle of a feature
Section titled “Lifecycle of a feature”-
Register it in
FEATURESinsrc/shared/config/features.ts. TheFeaturetype 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 topreviewableinbackend/config/feature_toggles.php(see Previewing from Arkana). -
Build the new version next to the current one (for example
TextItineraryV2/besideTextItinerary/) and branch on it in.astrofrontmatter:---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-v2versions sharesrc/components/viaje/shared/(floating-bar visibility, itinerary day transitions and tabs, the intro reveal script) anduseConfiguratorDrawerTriggers. -
Validate it in production with
?ff=<key>, or from Arkana. -
Ship it by deleting the old code, the
isFeatureEnabledchecks, theFEATURESentry and itsfeature_toggles.previewableentry. No dead toggles stay behind.
Active features
Section titled “Active features”| 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 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. |
Previewing from Arkana
Section titled “Previewing from Arkana”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.
Backend
Section titled “Backend”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.