First Project

Overview

Before we build anything, we need a working project on your computer. In this lesson we'll create the project, install the tools it depends on, and confirm it opens in your browser without errors. By the end, you'll have an empty but fully functional starting point that every lesson after this one builds on top of.


Getting the Course Repository

Before we build anything, you need a copy of the course repository on your computer.

A repository is a folder that tracks changes to code over time. Think of it like a lab notebook where every entry is dated and nothing is ever erased: you can always go back to any earlier version, flip between saved states, and see exactly what changed and when.

The course repository contains the entire experiment project, organized into checkpoints. Each checkpoint is a named saved state of the project, called a branch. When you switch to a checkpoint branch, your project folder updates instantly to match that state. You'll start each lesson by switching to its checkpoint branch, so you always begin from the right starting point regardless of what you changed in a previous lesson.

Your own work lives in this same folder. As you make changes during a lesson, you're building on top of the checkpoint, in your own copy of the project. Nothing you do affects the course repository or anyone else's copy.

Step 1: Fork the repository

Forking creates your own personal copy of the course repository on GitHub. This matters because you'll eventually deploy your experiment to the internet, and the deployment service (Vercel) needs to connect to a repository you own and control. You can't deploy from the course repository directly.

  1. Go to the course repository in your browser:

    https://github.com/jakesosine/programming-for-behavioral-research
    
  2. Click the Fork button in the top-right corner of the page.

  3. Leave all the default settings and click Create fork.

GitHub will create a copy of the repository under your own account. The URL will look like github.com/your-username/programming-for-behavioral-research. This is the repository you will use for the rest of the course.

Step 2: Clone your fork

Now download your fork to your computer using GitHub Desktop.

  1. Open GitHub Desktop.
  2. Go to File → Clone Repository.
  3. Click the GitHub.com tab. Your fork should appear in the list. Select it and choose a folder on your computer to save it to.
  4. Click Clone.

Step 3: Install dependencies

The project lists all the tools it needs in a file called package.json. They don't come with the repository itself. You need to install them once on your machine, and this only needs to happen after a fresh clone, not every time you switch branches.

  1. Open the cloned folder in VS Code (in GitHub Desktop, go to Repository → Open in Visual Studio Code).

  2. Open a terminal inside VS Code: Terminal → New Terminal. If you don't see a Terminal menu, go to View → Terminal instead.

  3. Run the following command:

    npm install
    

Wait for it to finish. You'll see a lot of text scroll by. That's normal. When the cursor comes back, the installation is complete.

Step 4: Switch to the starting point for this lesson

Cloning gives you the entire repository, but you need to be on the right checkpoint to start. Think of checkpoints like chapters in a book, each one is a saved state of the project at a specific point in the course.

  1. In GitHub Desktop, look for the Current Branch button near the top of the window. It shows whichever branch is currently active.

  2. Click it and select the following branch from the list:

    01-first-project
    

You're now on the starting point for this lesson. Everything you build from here will be built on top of this checkpoint.


What Is Next.js?

Next.js is a full-stack framework, meaning one project handles both what participants see in their browser and the code that saves their data to the database. Everything lives in the same folder, uses the same language, and runs with a single command. For a project like this one, that matters: you're a researcher, not a software team. The fewer moving pieces you have to keep track of, the more time you spend on the science.

How Screens Work

Every screen a participant sees (the consent form, the task instructions, the button-press task itself) is called a page in web development. Think of it the same way you'd think of a screen in Qualtrics or a slide in a presentation: one distinct thing the participant is looking at before they move to the next.

Each page has its own address, the same kind you type into a browser. The consent screen might live at yourapp.com/consent. The debrief screen might live at yourapp.com/debrief. Participants move through your experiment by moving through these addresses in sequence.

In Next.js, you create a screen by creating a folder with that name inside the src/app/ folder of your project. The folder name becomes the address automatically. You won't need to write any rules connecting folder names to addresses. Add a folder, get a screen.

The experiment flow from Lesson 2 maps to folders like this:

What the participant seesFolder in your projectAddress in the browser
Prolific ID entry(project home)yourapp.com/
Informed consentconsent/yourapp.com/consent
Demographicsdemographics/yourapp.com/demographics
Instructionsinstructions/yourapp.com/instructions
Phase 1–3 tasktask/yourapp.com/task
Debriefdebrief/yourapp.com/debrief

Each folder contains a file that describes what that screen looks like and does. When you need to add or change a page, you describe it to Claude Code in plain language and it creates or edits the right file. You don't need to know the file naming conventions to work with this system.

Server vs. Client Components

This is the one concept that trips most people up, and it's worth getting right. Next.js has two kinds of components:

Server components run on the server, meaning the code executes on a computer in the cloud before anything is sent to the participant's browser. This is where you read from the database, check whether a participant has consented, or load configuration for a phase. Server components are the default. They're fast, secure, and simpler for most things.

Client components run in the participant's browser. Any interactive element needs to be a client component: clicking a button, watching a counter update, a timer ticking. You mark one with "use client" at the top of the file.

The button-press task itself will be a client component (the participant is clicking in real time). The pages that check consent or route between phases will be server components.

Server Actions

Server Actions are functions that run on the server but can be called from a client component, like clicking a button that saves a response to the database. They look like regular functions with "use server" at the top. This is how every database write in this course works. You'll see them used starting in Lesson 6, but for now just know they exist.

What This Means for Your Experiment

When a participant presses the response button during Phase 1, two things happen at once. Their browser registers the click and updates the counter on screen immediately. At the same time, that response gets recorded to your database so it's there when you export your data later.

The browser side handles everything the participant directly experiences: the button, the counter, the timer, any feedback. The server side handles everything that needs to persist: saving each response, tracking which phase the participant is in, preventing them from skipping ahead.

You won't write these two sides separately or coordinate between them manually. When Claude Code builds a component, it handles the wiring. What you need to know is this: if something is about what the participant sees and does, describe it in terms of the behavior (clicks, timers, feedback). If something is about what gets recorded or what controls the flow, describe it in terms of the data (save this response, check whether this phase is complete, route to debrief). Claude Code will put the code in the right place.

What Is Prisma?

Your experiment will collect data: every button press, every phase transition, every participant who consents and completes. That data needs to go somewhere reliable and come back out in a form you can analyze. The database handles storage. Prisma is the tool that sits between your code and the database, letting you read and write data without having to learn a separate database language.

Think of it this way: databases have their own query language (called SQL) that looks nothing like the rest of your code. Prisma translates. You describe what you want in plain JavaScript, and Prisma handles the database side. This matters because mixing two languages in one project doubles the places where bugs can hide.

There are three things Prisma does that are directly relevant to running a behavioral study:

1. One file defines exactly what you collect. There is a single file in the project, prisma/schema.prisma, that lists every piece of data the experiment stores: which fields exist, what type of value each one holds, and how they relate to each other. Think of it as a codebook that also creates the database structure. If you want to add a variable, you add it here. The database updates to match. You never manually create columns or worry about the database getting out of sync with your code.

2. Your code knows the shape of your data before it runs. Once the schema is defined, Prisma generates definitions that your code editor can use to catch errors before any participant ever opens the experiment. If a response timestamp field is supposed to hold a date and some part of your code tries to store the word "yes" there instead, the editor flags it immediately. This is the difference between finding a data integrity problem during development and finding it after data collection is complete.

3. A single command turns your schema into a real database. Writing the schema doesn't create anything in the database yet. It's a blueprint. When you're ready to build, you run one command and Prisma reads the schema, connects to Neon, and creates all the tables exactly as described. This process is called running a migration.

Think of it like designing a data collection form and then printing it. The schema is the design. The migration is the print run that produces the actual form you use. If you later add a new variable to the design (say, you want to record which hand a participant uses), you run the migration command again and Prisma adds that column to the live database without touching anything else. Every migration is saved as a dated file in your project, so you always have a record of what the database looked like at any point in the study.

Working With Prisma and Claude Code

You won't write Prisma syntax directly. The schema file reads almost like plain English, which means you can describe what you want to Claude Code in natural language and verify the result without needing to know the syntax.

Each field in the schema has a name and a type that tells the database what kind of value to expect. The type also determines what you can do with the data later: you can calculate averages from numbers but not from text, and you can sort timestamps chronologically but not boolean values. Here is what the Response record you'll build later in the course looks like:

model Response {
  id          String   @id @default(cuid()) // unique ID, created automatically
  phase       Int                            // whole number: which phase (1, 2, or 3)
  button      String                         // which circle was clicked: "R1", "R2", or "distractor"
  reinforced  Boolean                        // true or false: did this click produce a point?
  respondedAt DateTime @default(now())       // exact timestamp of the click
}

The four types you will see throughout this course:

  • String — text. Names, IDs, labels, notes.
  • DateTime — an exact point in time, down to the millisecond. Used for any timestamp.
  • Boolean — true or false only. Used for yes/no values like whether a press was reinforced.
  • Int — a whole number. Used for counts and ratios.

Prisma also has Float for numbers with a decimal (durations, rates) if your study needs them, though this project's schema doesn't use one.

"Add an optional notes field to the Participant model." "Change the phase field to only allow the values PHASE1, PHASE2, or PHASE3." Claude Code can make that change, update the database, and regenerate everything in one step. Your job is to describe the data you need to collect; Prisma and Claude Code handle the rest.

Thinking in Tables

A relational database organizes data into tables, like spreadsheets where every row is one record and every column is one piece of information about it.

Think of a spreadsheet tracking participant sessions. Each row is one session. The columns might be: participant ID, which phase they're in, when they started, when they finished. That's a table.

The "relational" part is that tables can reference each other. Instead of copying the participant's full information into every session row, you store their ID and look it up when you need it. This keeps data consistent: if something about the participant changes, you update it in one place, and every session that references that participant reflects the change automatically.

This course uses three tables that reference each other:

Participant
  ├── Demographics   (one record per participant)
  └── Response       (one record per button press)

One participant has at most one demographics record and many responses — one row for every button press, each tagged with the phase it happened in. Every response links back to its participant. This structure means you can answer research questions at any level: "how many total presses did this participant make in Phase 3?" or "did response patterns differ by education level?" without restructuring the data first.


Connecting to Neon

Neon is where the database actually lives. It's a hosted Postgres database service, which means you don't install or manage a database on your own computer. You create a project on Neon's website, and they run and maintain the database for you. Your experiment connects to it over the internet the same way a participant's browser connects to your app.

The Connection String

The database doesn't open to anyone who asks. Your project needs a connection string to reach it: a single URL that contains the database address and credentials bundled together. It looks something like this:

postgresql://username:password@host/database

You get this URL from Neon when you create a project. It's a secret. Anyone who has it can read and write your participant data, so it never goes into your code files or gets committed to GitHub.

One Database for This Course

In professional software development it's common to maintain two separate databases: one for testing during development and one for live production data. This prevents accidental writes to real participant data during development.

For this course, we're going to skip that and connect directly to your production Neon database from your local machine. This is a deliberate simplification. The experiment has a short data collection window, the participant pool is controlled through Prolific, and adding a second database would require extra setup steps that add complexity without meaningful benefit at this scale. Just be aware: when your local app is running and connected, any test data you write goes into the real database. We'll clean that up before going live.

Setting Up the .env File

Environment variables are values your app reads at startup that aren't stored in the code itself. The connection string lives here. The project includes a file called .env.example that shows the format but doesn't contain real values.

To set up your environment file:

  1. In VS Code, find .env.example in the file list on the left side.

  2. Right-click it and select Rename. Remove the word example so the name becomes .env exactly.

  3. Open the file. You'll see a line with a placeholder connection string:

    DATABASE_URL=postgresql://user:password@localhost:5432/mydb
    
  4. Replace the placeholder with your real Neon connection string:

    DATABASE_URL="postgresql://username:password@host/database"
    
  5. Save the file.

The .env file is already listed in .gitignore, which means Git will never include it in commits. Your credentials stay on your machine only.

Now let's get the connection string from Neon and fill it in.

Creating a Neon project:

  1. Go to neon.tech and sign in to your account.
  2. Click New Project.
  3. Give it a name (something like resurgence-experiment), choose a region close to you, and click Create Project.

Copying the connection string:

  1. Once the project is created, Neon will show a Connection Details panel.

  2. Look for the field labeled Connection string. It will look like:

    postgresql://username:password@host/database
    
  3. Click the copy button next to it.

Pasting it into your .env file:

  1. Switch back to VS Code and open your .env file.

  2. Replace the placeholder value on the DATABASE_URL line with the connection string you copied:

    DATABASE_URL="postgresql://username:password@host/database"
    
  3. Save the file.

Your project can now reach the database. Nothing has been created in Neon yet though: the tables don't exist until we run the first migration. That's the next lesson. For now, the connection is in place and ready to use.


Running the App

With dependencies installed, you can start the app and see it in your browser.

  1. In the VS Code terminal, run:

    npm run dev
    
  2. Wait a few seconds. When you see a line that says something like Ready in Xms, the app is running. The terminal will also display a local URL. You can click it directly to open the app in your browser.

  3. If clicking the link doesn't work, open your browser manually and go to:

    http://localhost:3000
    

You should see a starter page. It won't look like much yet. That's expected. This is just the blank starting point we'll build on top of.

What "localhost:3000" means. Your computer is acting as both the app and the user right now. localhost means "this machine," and 3000 is the port the app is listening on. The app is not on the internet. It's running only on your computer, visible only to you. When you deploy to Vercel later in the course, it will get a real public URL that participants can reach.

Changes show up immediately. While npm run dev is running, any edit you save in VS Code will automatically update in the browser within a second or two. You don't need to restart the server or refresh the page manually. This is called hot reloading and it makes it much faster to see the effect of a change.

To stop the app, click in the terminal and press Ctrl+C. You'll need to run npm run dev again the next time you want to work on the project.