Course
Welcome back! We’re almost there: Slack is chirping, Jira has tickets, tests are green, and our CI blocks every sloppy PR. Part four is the final push where we’ll be turning the fp-ts playground into something we could, in theory, point the public at.
You can access all the tutorials in the Devin series here:
- Setup and First Pull Request (Part 1)
- Shipping a Vertical Slice with Devin (Part 2)
- Integrations, Testing, and CI/CD (Part 3)
- Security, Deployment, Maintenance (Part 4)
Here’s the plan for this fourth tutorial:
- Auth: No more anonymous UUIDs; we’ll drop in NextAuth credentials, JWT cookies, and a GqlAuthGuard so progress is tied to real users.
- Eyes on production: One line of Sentry code in the web app, one in the Nest API, and every exception lands in the dashboard.
- Push-button deploy: The front-end goes live on Vercel with preview URLs on every PR. (The API will stay local for now, we’ll fake prod with Docker.)
We will also reflect on this pair programming with Devin and see where it shines and where a human still has to tidy up loose ends before the world sees the repo. Ready for the last stretch?
From Anonymous UUID to Real Users
What I asked Devin to do
In short, I asked Devin to rip out the anonymous UUID flow and give me a real auth stack:
- NextAuth v6 credentials provider in
apps/web
- NestJS AuthModule with Argon2 hashing and JWT cookies
- Prisma migration that adds a
User
table and changesProgress.sessionId
→userId
(non-null) - Copy any existing UUID progress rows to a placeholder user so learners keep their badges
- Do not switch Postgres for SQLite
What came back (mostly good)
Here’s a summary of the results:
Layer |
Devin’s Output |
Database |
Dropped the |
Backend |
|
Frontend |
|
Security |
Argon2 (12 rounds), DTO validation via |
Tests |
Unit test for |
Another SQLite migration
Guess what I saw pop again in Devin’s little thinking box.
Successfully migrated from PostgreSQL to SQLite for local development 🥳
Exactly what I’d told it not to do. The Prisma schema was rewritten for SQLite, and connection strings and types were changed (from TIMESTAMP to DATETIME). I caught it mid-session:
- Paused the session.
git checkout main apps/api/prisma/schema.prisma
(restore Postgres schema).- Deleted the new
dev.db
file. npm run prisma:migrate --workspace=apps/api
to re-apply Postgres migrations.- Resumed Devin; it detected the revert and continued without complaint.
Damage: 0.8 ACU and about ten minutes.
Breaking-change checklist for teammates
I really appreciate the instructions Devin puts in PRs. Here it told us to:
# 1 Install new deps
npm install --workspaces
# 2 Run the Postgres migration
npm run prisma:migrate --workspace=apps/api
# 3 Restart everything
npm run dev:api & npm run dev:web
Architecture snapshot
In the wiki, I found this helpful diagram of our new auth system:
With real users in place and the database firmly back on Postgres, we’re finally ready to track genuine learner progress, ship preview builds to Vercel, and catch runtime errors with Sentry.
Front-End on Vercel, Preview Links on Every PR
With secure log-ins in place, the next obvious milestone was to show the world something clickable. We decided to deploy only the Next.js front-end for now. API and Postgres will remain on my local machine until we pick a host that fits the budget.
Manual account hookup (0 ACUs)
Connecting Devin to my personal Vercel account via native integrations isn’t possible yet. So I did it manually, via the Vercel dashboard:
- Logged into Vercel and clicked “New Project → Import Git Repository”.
- Selected the
fp-ts-exercises
repo, left the build command asnpm run build --workspace apps/web
, and hit Deploy. - Copied the resulting
VERCEL_PROJECT_ID
andVERCEL_TOKEN
into GitHub Secrets.
Let Devin update the CI workflow (0.4 ACU)
Following my prompt, Devin added a single job to the bottom of the existing workflow and a tiny shell step that curls the generated URL to Slack:
- name: Deploy to Vercel
if: ${{ success() && github.event_name == 'pull_request' }}
run: npx vercel deploy --prod --token ${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
Result
Push a branch → CI green → Slack ping:
✅ Preview ready: https://fp-ts-exercises-git-my-branch.vercel.app
Opening the link showed our playground, full auth flow, and learner dashboard. No environment tweaking required on Vercel’s end yet, because the front-end talks to my local API over http://localhost:4000
.
A Closer Look at Devin’s Secrets System
Before integration with Sentry, I had a look into Devin’s secrets system to figure out if I should use it. Devin offers a built-in “Secrets” vault so you can give the agent API keys, DB URLs, or OAuth tokens without checking them into Git and without exposing them in chat. Think of it as a lightweight equivalent to GitHub Actions secrets or Vercel’s Environment Variables, but scoped to Devin’s own cloud workspaces.
Feature |
How it works |
Notes |
Scope |
Secrets span three scopes: org, repo, and session. |
You can make the secrets available at the org level, across all your sessions. If you want to add secrets for a single repo, you can specify them when you set up your repo.If you want the secrets to be session-specific, you can provide them in the session settings. |
Encryption |
Stored server-side with AES-256 and TLS 1.3+ while in transit |
All customer data is encrypted with a custom KMS key (Key Management Service), managed by AWS. |
No ACU cost |
Adding, editing, or deleting a secret is a control-plane action: 0 ACUs. Only tasks that use the secret incur compute credits. |
Good place to stash long-lived tokens. |
Access inside prompts |
Use |
Example: |
Size limits |
4 KB per key, 100 keys per team on the Core tier. |
Enough for PEM certs or private keys. |
Adding a secret
Here are the steps you need to follow to add a secret:
- Open Team → Secrets in the Devin dashboard.
- Click “Add Secret” and enter a name (
SENTRY_DSN_WEB
) and its value. - Devin confirms storage; the key is now available as
$.SENTRY_DSN_WEB
.
Using a secret in a task
Task: update API Sentry init; Set DSN to $SECRET.SENTRY_DSN_API
Devin substitutes the value when it writes main.ts
. In the shell tab, you’ll see the literal DSN, but the chat log masks it.
Pros and cons
Pros:
- No risk of leaking keys into PRs or Timelapse logs.
- Organization-scoped secrets work across sessions: one upload, many uses.
- Zero credit cost to store or fetch.
Cons:
- Keys aren’t available locally unless you copy them into
.env
. Test runs outside Devin need manual setup. - No per-environment scoping (e.g., “only in production”). All sessions get all secrets.
When to use it
I find it easier to use Devin’s vault when Devin itself needs the key (e.g., pushing to Vercel, hitting a private registry, or wiring Sentry during build). I would keep using .env
/ GitHub Actions secrets for everything that also runs in your local dev shell or CI runners.
Devin Integration With Sentry
Here’s the prompt I used: Add Sentry to both apps so every exception shows up in my dashboard. Use environment variables in .env
. No other behaviour changes.”
Devin’s work (0.6 ACU, one session)
Here’s a summary of the result:
Stack |
Files Touched |
Key Code |
Next.js (web) |
|
|
NestJS (API) |
|
|
Env scaffold |
Added placeholders to |
|
Tests |
Added a Jest spy to assert that |
My manual steps
Here are the steps I took:
- Copied DSNs from my Sentry project into
.env
. - Restarted both dev servers.
- Hit
http://localhost:4000/graphql
with a deliberately bad query to check it worked. An issue appeared in Sentry within seconds.
Why I skipped Devin’s secrets feature
This is why I skipped Devin’s secrets feature:
- Simpler mental model: same
.env
file everywhere. - Easier to run preview builds on Vercel (those env vars are already set there).
- No ACU burn for secret-store operations.
Reflections: Where Devin Shines and Where It Stumbles
Four tutorial parts ago, this repo was a dusty pile of fp-ts exercises—now, it has:
- Modernised codebase: Latest dependencies, strict TS config, lint & Prettier rules (Part 1).
- Browser-based playground: Next.js 14 + Sandpack editor with live Vitest feedback (Part 2).
- NestJS + Postgres backend: GraphQL API, Prisma migrations, anonymous → JWT progress tracking (Parts 2 & 4).
- Green-lit pipeline: CI workflow that lints, type-checks, runs Jest & Playwright, and blocks every failing PR (Part 3).
- Team integrations: Slack status pings, Jira-label analysis, and preview URLs on every branch (Part 3).
- Production observability: Sentry wired into both web and API, capturing every exception (Part 4).
- Preview deploys: Vercel spins up a shareable URL for each pull request (Part 4).
We spent about ≈ 70 ACUs total (~$157 on the Core plan) and about two working days of hands-on review.
What Devin does frighteningly well
Here’s where I think Devin is very good:
Area |
Why it impressed me |
Boiler-plate infrastructure |
AuthModule scaffolds, Dockerfiles, YAML workflows: generated in minutes, bug-free. |
Framework-idiomatic code |
NestJS decorators, NextAuth config, Prisma migrations all matched official docs. |
Test scaffolding |
Unit and Playwright suites compile and run out of the gate—no edge-case stubs missing. |
Where a human still beats the agent
This is where I think Devin still needs improvement:
Pain point |
Example |
Cross-layer feature polish |
Sandpack editor hard-coded to stub tests; didn’t grasp SandpackTests component name. |
Config persistence |
Defaulted back to SQLite 5 times, even after explicit “Must stay on Postgres.” |
Naming / clean-up |
PR titles drift, stale files linger unless you tell Devin to delete them. |
Taste & UX |
Dashboard looked passable, but spacing, copy, and colours still needed hand-tuning. |
My opinion
Devin’s own docs never promised a product builder. They outline a much narrower sweet spot, and in hindsight, I should have stayed inside it. The official “What are Devin’s strengths?” page lists four headline uses:
The docs call feature development that spans UI, API, and data flows “experimental”. They are described as large, open-ended tasks where the agent may “require significant human review and iteration.” This is exactly what we saw when Sandpack hard-coded tests or when the dashboard looked good but didn’t actually compute data.
So, as a rule of thumb:
Use Devin for anything a senior developer could automate with a script, and keep the steering wheel for tasks that need product sense.
Conclusion
We reached the end of our four-part tutorial:
- Setup and First Pull Request (Part 1)
- Shipping a Vertical Slice with Devin (Part 2)
- Integrations, Testing, and CI/CD (Part 3)
- Security, Deployment, Maintenance (Part 4)
Here are a few next steps we can take from here:
- Host the API so preview builds (and future users) hit a real backend.
- Make sure core functionality works and fix the 30 bugs I know are in there
- Polish the UI
- Add more content and exercises
- Release for people to use!
If you follow the same path, remember: you can let the agent lay the concrete, but you still have to design the house. Happy coding!

I am a product-minded tech lead who specialises in growing early-stage startups from first prototype to product-market fit and beyond. I am endlessly curious about how people use technology, and I love working closely with founders and cross-functional teams to bring bold ideas to life. When I’m not building products, I’m chasing inspiration in new corners of the world or blowing off steam at the yoga studio.