Skip to content
Lantorian

Get started8 min read

Machine, Git and conventions

Everything you need to clone a project, run it, and ship code the team can review without friction.

Set up your machine

One version of each tool for the whole team: no more "works on my machine".

ToolHow
PHP 8.4, Composer, Laravel installerOfficial php.new script (Linux, WSL) or Laravel Herd (macOS, Windows).
Node.js 24 LTSThrough fnm or nvm, never the system Node. The project pins the version in .nvmrc.
PostgreSQL, RedisDocker Compose ships with every API project (docker compose up -d), or DBngin on macOS.
EditorVS Code or PhpStorm, with format on save enabled.
VS Code extensionsLaravel (official), PHP Intelephense, ESLint, Prettier, Tailwind CSS IntelliSense, i18n Ally (translation keys).
install.sh
# PHP 8.4 + Composer + Laravel installer (Linux / macOS / WSL)
/bin/bash -c "$(curl -fsSL https://php.new/install/linux/8.4)"

# Node.js 24 LTS via fnm
curl -fsSL https://fnm.vercel.app/install | bash
fnm install 24 && fnm default 24

# Check everything
php -v && composer -V && laravel --version && node -v && npm -v

First run

Run these commands in order. If a test fails before your first change, tell your mentor: it's not yours to fix silently.

api/
git clone git@github.com:lantorian/<project>-api.git && cd <project>-api
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate --seed

composer run dev   # server + queue + logs + vite, all at once
composer test      # must be green before you touch anything

The Git flow

One branch per ticket, small readable commits, a reviewed pull request before every merge. Nobody pushes straight to main.

mainsquash merge
A short-lived branch, typed commits, a PR approved by CI, then a squash merge.
  1. 1

    Branch from an up-to-date main

    Format: type/ticket-number-description, for example feat/142-invoice-export.
  2. 2

    Commit in small steps

    One commit = one intention. Use git add -p to review what you stage.
  3. 3

    Rebase before pushing

    git rebase origin/main keeps history linear. Don't merge main into your branch.
  4. 4

    Open the PR

    Conventional Commits title, a description with context, screenshots when the UI changes, and the checklist ticked.
  5. 5

    Address the review

    Reply to every comment, push the fixes, then a reviewer merges with squash merge.
typical-day.sh
git switch main && git pull --rebase
git switch -c feat/142-invoice-export

# ...code, test, commit small steps
git add -p
git commit -m "feat(invoice): add csv export action"

# keep your branch up to date, then open the PR
git fetch origin && git rebase origin/main
git push -u origin feat/142-invoice-export

Commit messages

We follow Conventional Commits: type(scope): imperative description, in English, lowercase, no trailing period.

TypeWhen to use it
featNew user-facing feature.
fixBug fix.
refactorCode change with no behavior change.
testAdding or fixing tests only.
docsDocumentation, README, comments.
choreDependencies, configuration, tooling.
perfMeasurable performance improvement.

Avoid

git log --oneline
wip
fix
update files
final version OK

Do

git log --oneline
feat(invoice): add csv export action
test(invoice): cover empty invoice list
fix(auth): refresh token before expiry
refactor(user): extract CreateUser action

Naming conventions

A name says what the thing does. When in doubt, copy what already exists in the project.

ItemRuleExample
PHP classPascalCase, role suffixCreateInvoiceAction
PHP methodcamelCase, verbmarkAsPaid()
SQL tableplural snake_caseinvoice_lines
API URLkebab-case, plural resources/api/v1/invoice-lines
React componentPascalCaseInvoiceTable
Front-end filekebab-caseinvoice-table.tsx
React hookuse prefixuse-invoices.ts
Translation keycamelCase, grouped by screeninvoices.table.emptyTitle

Quality tooling

The same commands run on your machine and in CI. Pint formats PHP, Larastan checks types, ESLint and Prettier handle TypeScript.

composer.json
{
  "scripts": {
    "dev": "npx concurrently \"php artisan serve\" \"php artisan queue:listen\" \"php artisan pail\" \"npm run dev\"",
    "lint": "pint --test",
    "format": "pint",
    "analyse": "phpstan analyse --memory-limit=1G",
    "test": "pest --parallel",
    "test:coverage": "pest --parallel --coverage --min=80",
    "ci": ["@lint", "@analyse", "@test"]
  }
}

Asking for help

Being stuck alone for a day costs more than a question. When you ask, include: what you're trying to do, what you tried, the full error message.