Build Landingi

KINDAreplaces $29/mosaves $348/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A landing page builder whose interesting half is the experiment engine: consistent variant assignment, conversion attribution that survives a page reload, and a statistical method that does not let you stop a test the moment it looks good.

estimated effort multi-daythe files for this build are in the project pack

PagesStatic generation with a small runtime for assignmentAssignmentDeterministic from a stable visitor identifierAttributionConversion tied to the assignment, not to the sessionStatisticsFixed horizon with hidden results, or a sequential methodRuntimeTypeScript with PostgreSQL

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the server, the page generator and the statistics.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree locally

    Why Assignments and conversions are counts that have to be exact, with one assignment per visitor enforced.

    Get it Docker or your package manager. open ↗

    Verify A unique constraint rejects a second assignment for one visitor

  3. decidefree to read; a statistician's hour is worth it for a commercial site

    Why Fixed horizon with hidden results and sequential testing lead to different interfaces, and retrofitting either onto the other is a rewrite of the decision path.

    Get it Read how each handles continuous monitoring, pick one, and write down the parameters: your minimum detectable effect and acceptable error rate.

    Verify A written note names the method, the detectable effect and the error rate

  4. have readyfree, and sometimes discouraging

    Why A test needing forty thousand visitors per arm on a page receiving two hundred a week will never conclude, and knowing that before building saves the experiment.

    Get it Compute the required sample from your detectable effect and compare it to your actual weekly traffic.

    Verify A calculation exists showing how long a test would take at your traffic

  5. decidefree

    Why It is fixed at experiment start and cannot change while running, so an imprecise definition invalidates the test rather than being tidied up later.

    Get it Define the event precisely, including repeat and cross-device behaviour.

    Verify A written definition covers the event, repeats and cross-device cases

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- pages(id, slug, published_version_id, created_at)
- versions(id, page_id, content_json, published_at) · immutable once published
- experiments(id, page_id, method, target_sample_per_variant, min_detectable_effect, started_at, ended_at, stopped_reason)
- variants(id, experiment_id, version_id, weight, label)
- assignments(visitor_id, experiment_id, variant_id, assigned_at) · one per visitor, never reassigned
- conversions(id, assignment_id, goal, at, value_minor) · attributed to the assignment
- results(experiment_id, computed_at, per_variant_json, decision, valid_under_method)
- An assignment is written once and never changed, because a visitor moved between variants mid-test corrupts both arms.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_URLsecretrequiredpostgres://localhost:5432/landingAssignments and conversions.
STATS_METHODrequiredfixed_horizon_hiddenNamed in the interface. The other honest option is a sequential method.
SHOW_RUNNING_SIGNIFICANCErequiredneverPresent as a constant. A running figure invites the stopping that manufactures results.
MIN_DETECTABLE_EFFECTrequired0.05Drives the required sample size. Smaller effects need far more traffic.
ACCEPTABLE_ERROR_RATErequired0.05Validated against a null simulation in the test suite.
ASSIGNMENT_COOKIE_DAYSrequired90How long a visitor keeps their variant. Shorter means more reassignment as new visitors.

The build, in order

  1. Pages that load fast enough to test

    A landing page renders quickly and is versioned immutably once published.

    1. An edited variant mid-test invalidates the whole experiment.

      Files src/versions.ts

    2. The page is in the measurement path. Slow pages change what you measure.

      Files src/build.ts

    3. It loads before assignment. Measure its size and hold it.

      Files runtime/assign.ts

    done when · tick each as it passes
    watch out
    • Editing a published page during a running experiment changes what the variant is halfway through, and the results describe two things that no longer exist. Snapshot the version at publish and bind the experiment to versions rather than to pages.
  2. Assignment that stays put

    A visitor sees the same variant every time, and you know they did.

    1. From a stable identifier, written once and never updated.

      Files src/assign.tssrc/assign.test.ts

    2. New weights apply to new visitors only.

      Files src/assign.ts

    3. Rendering the control then swapping is visible and it biases the result.

      Files runtime/assign.ts

    done when · tick each as it passes
    watch out
    • Reassigning visitors when you adjust traffic weights moves people between arms mid-test, which contaminates both and produces a result describing neither. Keep existing assignments fixed and apply new weights only to visitors who have not been assigned.
  3. Conversions attributed to what was seen

    A conversion counts for the variant the visitor actually saw.

    1. It is the only record of what the visitor actually saw.

      Files src/conversions.ts

    2. Changing what counts as success mid-test is choosing the answer.

      Files src/experiments.ts

    3. Both happen. Both need an answer written down, not an accident.

      Files src/conversions.ts

    done when · tick each as it passes
    watch out
    • Attributing a conversion to whichever variant is current at conversion time rather than to the recorded assignment mixes the arms whenever anything about the test changes, and the corruption is invisible in the totals. Attribute to the assignment, which is the only thing that records what was actually seen.
  4. Statistics that do not let you cheat

    A winner is declared only when the method says it may be.

    1. Fixed horizon with hidden results, or sequential. Not a running figure.

      Files src/stats.ts

    2. A thousand tests between identical variants. Count the false winners.

      Files src/stats.test.ts

    3. In the decision path. The friction is the feature.

      Files src/decide.ts

    done when · tick each as it passes
    watch out
    • Displaying a running significance figure invites stopping the moment it crosses the line, and doing that on two identical pages produces a false winner a large fraction of the time. Either hide the numbers until the planned sample is reached, or use a method whose guarantee holds when you look continuously, and say which one you chose.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more website builders.