Skip to main content
This guide covers how to build, run, and test the InterviewGuide project for local development.

Prerequisites

Before building the project, ensure you have the following installed:

Backend Requirements

  • Java 21 (OpenJDK or Oracle JDK)
  • Gradle 8.8+ (included via wrapper)
  • PostgreSQL 14+ with pgvector extension
  • Redis 6+

Frontend Requirements

  • Node.js 18+
  • pnpm 10.26.2 (specified in package.json)
  • Modern web browser
The project uses Gradle Wrapper for backend and pnpm for frontend, so you don’t need to install these tools globally. The wrapper scripts will download the correct versions automatically.

Backend Development

Project Structure

The backend is a Gradle multi-project build:
interviewguide/
├── app/                    # Main Spring Boot application
│   ├── src/
│   └── build.gradle
├── gradle/                 # Gradle wrapper and dependency management
│   └── libs.versions.toml # Centralized version catalog
├── gradlew                # Unix wrapper script
├── gradlew.bat            # Windows wrapper script
└── settings.gradle        # Project configuration

Gradle Commands

Run the application in development mode:
./gradlew bootRun
This starts the Spring Boot application on http://localhost:8080.Run with specific profile:
./gradlew bootRun --args='--spring.profiles.active=dev'
Run with custom port:
./gradlew bootRun --args='--server.port=9090'
Hot reload is enabled via Spring Boot DevTools when running in development mode. Changes to Java files will trigger automatic restart.

Build Configuration

The main build configuration file:
plugins {
    id 'java'
    alias(libs.plugins.spring.boot)
    alias(libs.plugins.spring.dependency.management)
}

group = 'com.interview'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

dependencies {
    // Spring Boot Starters
    implementation 'org.springframework.boot:spring-boot-starter-webmvc'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-validation'

    // Spring AI 2.0
    implementation "org.springframework.ai:spring-ai-starter-model-openai:${libs.versions.spring.ai.get()}"
    implementation "org.springframework.ai:spring-ai-starter-vector-store-pgvector:${libs.versions.spring.ai.get()}"

    // Document parsing
    implementation libs.tika.core
    implementation libs.tika.parsers

    // Storage
    implementation "software.amazon.awssdk:s3:${libs.versions.aws.sdk.get()}"

    // Async processing
    implementation "org.redisson:redisson-spring-boot-starter:${libs.versions.redisson.get()}"

    // PDF export
    implementation "com.itextpdf:itext-core:${libs.versions.itext.get()}"
    implementation "com.itextpdf:font-asian:${libs.versions.itext.get()}"

    // Object mapping
    implementation "org.mapstruct:mapstruct:${libs.versions.mapstruct.get()}"
    annotationProcessor "org.mapstruct:mapstruct-processor:${libs.versions.mapstruct.get()}"

    // Lombok (must come before MapStruct processor)
    compileOnly libs.lombok
    annotationProcessor libs.lombok
    annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'

    // Database
    runtimeOnly 'org.postgresql:postgresql'

    // Testing
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation libs.junit.jupiter
}

tasks.named('test') {
    useJUnitPlatform()
}
Centralized dependency version management using Gradle Version Catalogs:
[versions]
spring-boot = "4.0.1"
spring-ai = "2.0.0-M1"
tika = "2.9.2"
lombok = "1.18.36"
junit-jupiter = "5.12.0"
redisson = "4.0.0"
mapstruct = "1.6.3"
aws-sdk = "2.29.51"
itext = "8.0.5"
pinyin4j = "2.5.0"

[libraries]
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" }
tika-core = { module = "org.apache.tika:tika-core", version.ref = "tika" }
tika-parsers = { module = "org.apache.tika:tika-parsers-standard-package", version.ref = "tika" }
pinyin4j = { module = "com.belerweb:pinyin4j", version.ref = "pinyin4j" }

[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }
spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.7" }
Benefits:
  • Centralized version management
  • Type-safe dependency references
  • Easier dependency updates
Multi-project configuration:
pluginManagement {
    repositories {
        maven { url 'https://maven.aliyun.com/repository/public/' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
        maven { url 'https://maven.aliyun.com/repository/spring-plugin/' }
        gradlePluginPortal()
    }
}

plugins {
    id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
}

rootProject.name = 'interview-guide'
include('app')
The project uses Aliyun Maven mirrors for faster dependency downloads in China. You can remove these if you prefer to use Maven Central directly.

Environment Configuration

Create an application.yml or application.properties file in app/src/main/resources/ (or use environment variables):
spring:
  application:
    name: interview-guide
  
  datasource:
    url: jdbc:postgresql://localhost:5432/interview_guide
    username: postgres
    password: your_password
  
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: false
  
  ai:
    openai:
      api-key: ${DASHSCOPE_API_KEY}
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      chat:
        options:
          model: qwen-plus
  
  data:
    redis:
      host: localhost
      port: 6379

app:
  storage:
    type: s3
    s3:
      endpoint: http://localhost:8333
      bucket: interview-guide
      access-key: ${S3_ACCESS_KEY}
      secret-key: ${S3_SECRET_KEY}
  
  file:
    allowed-types:
      - application/pdf
      - application/vnd.openxmlformats-officedocument.wordprocessingml.document
      - application/msword
      - text/plain
      - text/markdown
Use environment variables for sensitive values like API keys. Copy .env.example to .env and populate with your values.

Frontend Development

Project Structure

frontend/
├── src/
│   ├── pages/              # Route pages
│   ├── components/         # Reusable components
│   ├── api/                # API client
│   ├── types/              # TypeScript types
│   ├── utils/              # Utilities
│   ├── hooks/              # Custom hooks
│   ├── App.tsx             # Root component
│   └── main.tsx            # Application entry
├── public/                 # Static assets
├── index.html              # HTML template
├── package.json            # Dependencies
├── tsconfig.json           # TypeScript config
├── vite.config.ts          # Vite config
└── tailwind.config.js      # Tailwind CSS config

pnpm Commands

Install dependencies:
cd frontend
pnpm install
Start development server:
pnpm dev
This starts Vite dev server on http://localhost:5173 with hot module replacement (HMR).Development with custom port:
pnpm dev --port 3000
Development with host exposed:
pnpm dev --host
This allows access from other devices on your network.

Package Configuration

{
  "name": "ai-interview-frontend",
  "private": true,
  "version": "0.0.1",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@tailwindcss/postcss": "^4.1.18",
    "axios": "^1.7.7",
    "framer-motion": "^12.23.26",
    "lucide-react": "^0.468.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-markdown": "^9.0.1",
    "react-router-dom": "^7.11.0",
    "react-syntax-highlighter": "^16.1.0",
    "react-virtuoso": "^4.18.1",
    "recharts": "^3.6.0",
    "remark-breaks": "^4.0.0",
    "remark-gfm": "^4.0.0"
  },
  "devDependencies": {
    "@tailwindcss/typography": "^0.5.15",
    "@types/react": "^18.3.12",
    "@types/react-dom": "^18.3.1",
    "@types/react-syntax-highlighter": "^15.5.13",
    "@vitejs/plugin-react": "^4.3.3",
    "autoprefixer": "^10.4.23",
    "postcss": "^8.5.6",
    "tailwindcss": "^4.1.18",
    "typescript": "~5.6.2",
    "vite": "^5.4.10"
  },
  "packageManager": "[email protected]+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6"
}

Environment Variables

Create .env file in frontend/ directory:
# API base URL
VITE_API_BASE_URL=http://localhost:8080

# Optional: Enable debug logging
VITE_DEBUG=true
Access in code:
const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080';

Docker Deployment

For production deployment, use Docker Compose:
# Build and start all services
docker-compose up -d

# View logs
docker-compose logs -f

# Stop all services
docker-compose down

# Rebuild specific service
docker-compose up -d --build app
See Docker Deployment for detailed instructions.

Running Tests

Run all tests:
./gradlew test
Run specific test class:
./gradlew test --tests ResumeUploadServiceTest
Run tests with specific tag:
./gradlew test --tests '*IntegrationTest'
Generate test report:
./gradlew test
open app/build/reports/tests/test/index.html

Common Development Workflows

Full Stack Development

Terminal 1 - Backend:
./gradlew bootRun
Terminal 2 - Frontend:
cd frontend
pnpm dev
Terminal 3 - Infrastructure (if running locally):
# Start PostgreSQL
docker run -d --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres pgvector/pgvector:pg14

# Start Redis
docker run -d --name redis -p 6379:6379 redis:latest

# Start MinIO (S3-compatible storage)
docker run -d --name minio -p 9000:9000 -p 9001:9001 minio/minio server /data --console-address ":9001"

Hot Reload

  • Backend: Spring Boot DevTools automatically restarts the application when you change Java files
  • Frontend: Vite HMR updates the browser instantly when you change React components

Debugging

  1. Open the project in IntelliJ IDEA
  2. Navigate to App.java
  3. Right-click and select Debug ‘App’
  4. Set breakpoints in your code
  5. Use the debugger controls to step through code

Troubleshooting

Solution:
  1. Check your internet connection
  2. Clear Gradle cache: rm -rf ~/.gradle/caches
  3. Try running with --refresh-dependencies: ./gradlew build --refresh-dependencies
  4. If using Aliyun mirrors, ensure they are accessible
Solution:
  1. Ensure PostgreSQL is running
  2. Check database connection settings in application.yml
  3. Verify org.postgresql:postgresql is in dependencies
Solution:
  1. Delete node_modules and pnpm-lock.yaml
  2. Run pnpm install again
  3. Ensure you’re using the correct pnpm version: pnpm --version
Solution:
  1. Ensure CorsConfig is configured correctly in backend
  2. Check that frontend is using correct API base URL
  3. Verify backend is running and accessible
Solution:
  1. Clean build: ./gradlew clean
  2. Rebuild: ./gradlew build
  3. Ensure annotation processing is enabled in your IDE
  4. In IntelliJ IDEA: Settings → Build, Execution, Deployment → Compiler → Annotation Processors → Enable annotation processing

Project Structure

Understand the codebase organization

Code Style Guide

Learn about coding conventions

Local Setup

Complete local development environment setup

Docker Deployment

Deploy with Docker Compose

Build docs developers (and LLMs) love