| title | Legacy: Your First Stacked PR |
|---|---|
| slug | legacy/tutorials/your-first-stack |
Learn how to split a large feature into reviewable chunks in 15 minutes
This hands-on tutorial teaches you how to use Stacked Pull Requests to break down a large feature into small, focused PRs that ship faster.
- Create a stack of 3 dependent PRs
- Use the OCH CLI for efficient workflow
- Navigate the review process
- Merge the stack successfully
Time: 15-20 minutes Difficulty: Beginner
Before starting, make sure you have:
- OpenCodeHub account
- A repository where you have write access
- Git installed locally
- OCH CLI installed (
npm install -g opencodehub-cli) - Basic Git knowledge (branch, commit, push)
First time with OCH CLI?
# Install
npm install -g opencodehub-cli
# Login
och auth login
# Enter your email and password when promptedYou're building a user authentication system. Instead of creating one massive PR, you'll split it into 3 logical layers:
- Database Layer - User table schema
- Service Layer - Authentication logic
- API Layer - Login/logout endpoints
Each PR builds on the previous one, but can be reviewed independently.
1.1. Clone your repository:
cd ~/projects
git clone https://github.com/yourorg/your-repo.git
cd your-repo1.2. Ensure main is up to date:
git checkout main
git pull origin main1.3. Verify OCH CLI is connected:
och auth whoami
# Should show: "Logged in as: your-email@example.com"This is the foundation of your stack.
2.1. Create the first branch:
och stack create auth-databaseThis creates a new branch stack/auth-database from main.
2.2. Create the migration file:
mkdir -p db/migrations
cat > db/migrations/001_create_users_table.sql << 'EOF'
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_users_email ON users(email);
EOF2.3. Commit your changes:
git add db/migrations/
git commit -m "feat: add users table migration
- Create users table with email and password_hash
- Add email index for fast lookups
- Set up timestamps"2.4. Submit to create the PR:
och stack submit
# Output:
# ✓ Pushing stack/auth-database
# ✓ Creating pull request
#
# PR #123 created: feat: add users table migration
# https://git.yourcompany.com/yourorg/your-repo/pulls/1232.5. Verify in web UI:
Open the PR link. You should see:
- Title: "feat: add users table migration"
- Files changed:
db/migrations/001_create_users_table.sql - Status: Draft or Ready for Review
Now, build on top of your first PR.
3.1. Create the second stacked branch:
och stack create auth-serviceThis creates stack/auth-service from your current branch automatically.
3.2. Create the auth service:
mkdir -p src/services
cat > src/services/auth.ts << 'EOF'
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { db } from '../db';
export class AuthService {
async register(email: string, password: string) {
const hash = await bcrypt.hash(password, 10);
const user = await db.users.create({
email,
password_hash: hash
});
return { id: user.id, email: user.email };
}
async login(email: string, password: string) {
const user = await db.users.findByEmail(email);
if (!user) {
throw new Error('Invalid credentials');
}
const valid = await bcrypt.compare(password, user.password_hash);
if (!valid) {
throw new Error('Invalid credentials');
}
const token = jwt.sign(
{ userId: user.id, email: user.email },
process.env.JWT_SECRET!,
{ expiresIn: '7d' }
);
return { token, user: { id: user.id, email: user.email } };
}
}
EOF3.3. Commit:
git add src/services/
git commit -m "feat: implement authentication service
- Add user registration with bcrypt hashing
- Add login with JWT token generation
- Implement password verification
Depends on: PR #123 (users table)"3.4. Submit:
och stack submit
# Output:
# ✓ Pushing stack/auth-service
# ✓ Creating pull request
# ✓ Linking to base PR #123
#
# PR #124 created: feat: implement authentication service
# Stack: #123 → #124
# https://git.yourcompany.com/yourorg/your-repo/pulls/1243.5. Check the stack:
och stack view
# Output:
# 📚 Current Stack
# ┌─ main (base)
# ├─ #123: auth-database ⏳ Draft
# └─ #124: auth-service ⏳ DraftPerfect! You now have a stack of 2 PRs.
Final layer: the HTTP endpoints.
4.1. Create third branch:
och stack create auth-api4.2. Create API endpoints:
mkdir -p src/api
cat > src/api/auth.ts << 'EOF'
import { Router } from 'express';
import { AuthService } from '../services/auth';
const router = Router();
const authService = new AuthService();
router.post('/register', async (req, res) => {
try {
const { email, password } = req.body;
// Validation
if (!email || !password) {
return res.status(400).json({ error: 'Email and password required' });
}
const user = await authService.register(email, password);
res.status(201).json({ user });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
const result = await authService.login(email, password);
res.json(result);
} catch (error) {
res.status(401).json({ error: error.message });
}
});
export default router;
EOF4.3. Commit and submit:
git add src/api/
git commit -m "feat: add authentication API endpoints
- POST /register - create new user
- POST /login - authenticate and get token
- Input validation
- Error handling
Depends on: PR #124 (auth service)"
och stack submit
# Output:
# ✓ Pushing stack/auth-api
# ✓ Creating pull request
# ✓ Linking to base PR #124
#
# PR #125 created: feat: add authentication API endpoints
# Stack: #123 → #124 → #1254.4. View complete stack:
och stack view
# Output:
# 📚 Current Stack
# ┌─ main (base)
# ├─ #123: auth-database ⏳ Draft
# ├─ #124: auth-service ⏳ Draft
# └─ #125: auth-api ⏳ Draft (current)🎉 Congratulations! You've created your first stack of 3 PRs!
Now let's get these PRs reviewed and merged.
5.1. Mark PRs as ready:
# Mark first PR ready for review
curl -X PATCH https://git.yourcompany.com/api/prs/123 \
-H "Authorization: Bearer $TOKEN" \
-d '{"draft": false}'
# Or via web UI: Click "Ready for Review" on PR #1235.2. Request reviews:
In the web UI for PR #123:
- Click "Reviewers" → Select team members
- Add comment: "This is the first PR in a stack of 3. Reviewing these in order would be helpful!"
5.3. While waiting, add tests:
# Switch back to first branch
git checkout stack/auth-database
# Add test file
cat > db/migrations/001_create_users_table.test.ts << 'EOF'
import { test } from 'vitest';
import { db } from '../db';
test('creates users table', async () => {
// Run migration
await db.migrate();
// Verify table exists
const tables = await db.raw(`
SELECT table_name
FROM information_schema.tables
WHERE table_name = 'users'
`);
expect(tables.length).toBe(1);
});
EOF
git add .
git commit -m "test: add migration test"
git push
# PR #123 automatically updates!5.4. Monitor review progress:
# Check PR status
och pr status 123
# Output:
# PR #123: feat: add users table migration
# Status: 🔍 In Review
# Approvals: 1/1 required
# CI: ✅ Passing
# Comments: 2Once PR #123 is approved, the magic happens!
6.1. Use the merge queue:
# Add all PRs to queue
och queue add 123 124 125
# Or add individually as they get approved:
och queue add 123 # Merges first
# PR #124 auto-rebases onto new main
och queue add 124 # Merges second
# PR #125 auto-rebases onto new main
och queue add 125 # Merges third6.2. Watch it merge automatically:
# Monitor queue
och queue watch
# Output (live updates):
# Position PR Status ETA
# 1 #123 🏃 Running CI 1 min
# 2 #124 ⏳ Waiting 3 min
# 3 #125 ⏳ Waiting 5 min
#
# [1 min later]
# ✅ PR #123 merged!
# 🔄 Rebasing PR #124...
# 🏃 Running CI for #124...
#
# [2 min later]
# ✅ PR #124 merged!
# 🔄 Rebasing PR #125...6.3. Celebrate! 🎉
# Check final status
git checkout main
git pull
git log --oneline -3
# Output:
# abc123 feat: add authentication API endpoints
# def456 feat: implement authentication service
# ghi789 feat: add users table migrationAll 3 PRs merged! Your auth system is live!
✅ How to create a stack of dependent PRs
✅ Using och stack create for easy stacking
✅ Submitting stacks with och stack submit
✅ Viewing stack visualization
✅ Using the merge queue for automatic merging
✅ How auto-rebasing works
Stacked PRs are better because:
- Each PR was ~50-100 lines (easy to review)
- Reviews happened in parallel (faster)
- Each merge was low-risk (incremental)
- Clear history (logical progression)
vs. One Large PR:
- Would be ~200-300 lines
- Single review bottleneck
- High-risk merge
- Messy history
Try stacking your next feature:
- 🎨 Frontend feature → Split UI, logic, styles
- 🔧 Backend feature → Split models, services, controllers
- 📊 Data pipeline → Split ingestion, processing, output
Learn more about:
- 💬 Discord - Ask questions
- 📖 Documentation - Read more guides
- 🐛 GitHub - Report issues
"och command not found"
# Install CLI
npm install -g opencodehub-cli
# Or use npx
npx opencodehub-cli stack create auth-database"Permission denied"
# Login again
och auth login
# Verify permissions
och auth whoami"Can't create stack - conflicts"
# Make sure main is up to date
git checkout main
git pull
# Start fresh
git checkout -b stack/auth-database main"PR not linking to stack"
# Manually link via web UI:
# Go to PR → Settings → Dependencies → Select base PRCongratulations on completing your first stacked PR workflow! 🚀
You're now ready to ship code faster with OpenCodeHub.