guava/tutorials
Guava Tutorials is a Laravel package for adding in-app tutorials and guided onboarding flows to your application. Define steps, target users, and track progress through a simple, flexible API, helping new users learn features without leaving your app. It keeps tutorial content organized and easy to...
Architecture fit. guava/tutorials maps onto a well-understood Laravel package shape: service provider, publishable config/migrations/lang/views/assets, a tutorial model with ordered steps, action payloads, and user progress records. For a Laravel monolith with Blade-rendered authenticated pages, that is a native fit — no separate service, no external data store, no new vendor relationship. The package owns the scaffolding (schema, ordering, publish controls, overlay rendering); it explicitly does not own audience targeting, funnel analytics, A/B assignment, or selector validity. Those are product surfaces we would own regardless of build-vs-buy, which is the core argument for renting the scaffolding rather than building it.
The architectural risk that matters at the TPM level is not "does it work" but "where does it sit in our dependency graph." Two unknowns gate the decision:
Integration feasibility. Feasible for a timeboxed pilot. The install path is conventional (composer require, publish, migrate), and the assessment material converges on a one-to-two-developer-day spike to prove the loop end to end: define one tutorial, render it on one authenticated page, complete it, see the progress record. Productionising that same flow with real content, targeting rules, theming, and instrumentation is a sprint, not a day. The gap between those two numbers is the honest planning unit — the spike de-risks the unknowns, it does not deliver the feature.
The highest-leverage architectural decision is a thin internal boundary (TutorialRegistry or equivalent) that answers "which tutorial, if any, should this user see right now?" Every downstream consumer — Blade component, completion endpoint, analytics — talks to that boundary, never to vendor classes. This is what keeps the package an implementation detail we can swap, fork, or drop, and it is the reason a low-maturity dependency is acceptable here: exit cost is bounded by design, not by hope.
Technical risk. Ranked by expected cost:
config:cache fails or silently drops it, and Octane's long-lived workers can hold stale bindings across deploys. This is a hard gate: our deployment runs config:cache, so it must be exercised in the spike, not on launch day.data-tutorial="..."), a CI assertion that every configured target exists in the rendered DOM for its page, and ideally a skip-if-missing guard implemented in our boundary.Key questions to close before committing.
config:cache boot cleanly with closure-bearing tutorial definitions present? Does Octane?auth and inside tenancy scoping without a fork?Stack fit. Strong for Laravel + Blade with server-rendered authenticated pages. The package's model — tutorials composed of ordered steps, each with markdown body and one or more actions (highlight, navigate, dispatch) — matches how activation flows are actually authored. It is a weaker fit if the customer surface is a heavily client-rendered SPA: the overlay would be a foreign object in our own frontend, and the "render once high in the layout" pattern has no clean analogue. Confirm the rendering target before scoping.
Migration path. There is no migration in the data sense — greenfield install, new tables, no legacy to reconcile. The migration work is organizational: tutorial content needs an authoring home (seeder or admin panel), an owner, and a review path. Treat that as a real dependency, not a follow-up. The failure mode is a package fully integrated with no content pipeline behind it.
Compatibility and configuration discipline. Everything meaningful is a config edit, not a fork: model bindings, route registration, view/component names, table names. Read the config file before writing a line of application code — it is the contract. Two rules that protect future upgrades:
Tutorial model, extend the vendor class rather than rebuilding it, and re-read the vendor class after each minor upgrade to confirm table name, relationships, and ordering scope still line up.Sequencing. Four phases, with explicit gates.
config:cache behavior with closures, schema acceptability. Kill criteria apply here — if any answer is a no, reassess before authoring content. Discovering a fork requirement in week three is the outcome this phase exists to prevent.tutorial_shown, tutorial_step_viewed, tutorial_completed, tutorial_dismissed through the existing pipeline. Retrofitting analytics means relaunching the flow to get a baseline.data-tutorial selector convention, do one theming pass against our design system. Treat the visual layer as a theming pass, not a drop-in — an RTL alignment fix landing as a patch in a minor release is a clear signal the presentation layer is still maturing.Rendering placement. Register the overlay component once in the authenticated layout and let the registry decide whether anything renders. Per-page mounting invites double-mounting and makes "which tutorial wins" unanswerable.
Maintenance. Low-to-moderate, and mostly ours. Pin ^1.1, review each release deliberately, budget a few hours a quarter for release review and smoke tests. The recurring tax is not the package — it's the published surface. Keep published views/assets minimal and diff against vendor versions on upgrade. Add three cheap CI checks that pay for themselves: target-selector existence per page, config:cache boot with closure definitions present (plus an Octane smoke test if we run Octane), and a visual regression baseline covering one RTL and one LTR page so an RTL fix never ships as an LTR regression.
Support. No SLA, no commercial path, single maintainer, no dependent ecosystem to lean on for community answers. Operationalize accordingly: name an internal owner (not a team — a person), document the boundary so a fork is a days-not-months decision, and contribute fixes upstream where cheap — missing translation keys are usually cheaper to upstream than to maintain as a permanent override file.
Scaling. The tutorial read path is ordinary Eloquent queries and a Blade render; it scales with the host app and introduces no new scaling axis. Two real pressure points: overlay payload weight per page render (keep closure actions pure — no queries we would not want running on every render), and progress table growth, which is bounded by users × tutorials and is trivially manageable with a firstOrCreate keyed on (tutorial, step, user) keeping duplicates out. The registry's eligibility query is the one thing worth watching as tutorials and audience rules accumulate — index accordingly.
Failure modes. Catalogued by how loudly they fail:
config:cache failure or stale Octane bindings from closure-bearing definitions. Gated in the spike.Ramp-up. Shallow for any Laravel developer — the install path is conventional and the API surface is small by design. The non-obvious parts are all in this assessment, which is why the boundary wrapper and the four gating questions should be written down rather than rediscovered. Plan roughly one to two developer-days to a proven loop, a sprint to a productionised pilot, and a named owner thereafter.
Recommendation. Proceed to the timeboxed spike. The package is a credible cheap-rental option for a commodity surface — self-hosted, Laravel-native, and structurally easy to exit — provided we keep the boundary, instrument from day one, and treat the four gating questions as genuine kill criteria rather than formality. Do not place it on a business-critical surface without the named owner and fallback plan in place.
How can I help you explore Laravel packages today?