Skip to content
KheAi
Go back

Tech Lead SDLC & Daily Workflow

Edit page

What This Part Covers


What “Tech Lead” Means

A tech lead is not just a senior developer who writes more code. The role has three distinct responsibilities:

1. Technical decisions      — architecture, patterns, tooling, tradeoffs
2. Team enablement          — unblocking developers, setting standards, reviewing PRs
3. Delivery visibility      — translating technical state to PM/product, surfacing risks early

In a Meteor project, these were informal — one person knew everything and nothing was written down. In an enterprise NestJS team, they must be explicit.


1. The Full SDLC

Phase 1: Discovery (Before Any Code)

When a new feature arrives as a ticket:

As a developer:

1. Read the ticket carefully — understand WHAT and WHY, not just WHAT
2. Ask: what DB schema does this require?
3. Ask: what new GraphQL operations does this expose?
4. Ask: which existing modules does this touch?
5. Ask: is there a security concern (new user-facing fields, new permissions)?

As a tech lead:

1. Is this the right abstraction? (E.g., "should tags be a separate entity or just an array of strings?")
2. What's the migration risk?
3. Does this break multi-tenancy or RBAC boundaries?
4. What's the test strategy?
5. Is this one PR or multiple?

Design before coding — the 3-question check:

1. What does the DB table look like? (draw it)
2. What GraphQL operations do we expose? (write them as pseudocode)
3. Which existing module is this most like? (pick the reference)

Only when you can answer all three do you write the first file.

Phase 2: Branch & Build

# Always from latest main
git checkout main && git pull
git checkout -b feat/ticket-description

# Orient with the graph before building
claude
"Query the graph for the reference module I'll use.
Run impact analysis on any existing symbols I plan to change."

Build in the 9-step order (entity → DTOs → CQRS → service → resolver → module → register → migrate). This order matters because:

Building out of order causes circular dependency errors and forces rework.

Phase 3: Verify Locally

This is not optional. Every point must be green before the PR opens.

# 1. TypeScript compiles without errors
yarn api:dev
# Watch for red compile errors in the terminal output

# 2. Unit tests pass
yarn api:test

# 3. Migration tested
yarn migration:run
# Open Adminer, verify table/columns exist
yarn migration:revert
# Verify table is gone
yarn migration:run
# Run again to leave it clean

# 4. Smoke test in GraphQL Playground
# Test every new operation — success case AND auth rejection case

# 5. Lint
yarn lint

# 6. Scope verification
"Run detect_changes against main and confirm I only touched <module> files."

Phase 4: Code Review

The PR author’s job:

# 1. Stage selectively — never git add .
git add apps/api/src/modules/product/
git add apps/api/src/migrations/*product*
git add apps/api/src/app/app.module.ts

# 2. Verify what's staged
git diff --staged

# 3. /code-review before pushing
claude
/code-review

# 4. Commit with Commitizen
yarn cz

# 5. Push and open PR
git push -u origin feat/product-module
"Open a PR for feat/product-module. Fill the template."

The PR description must include:

## Summary
- What was added/changed (bullet points)
- Why it was needed (link to ticket)

## Migration
- Table added: `product`
- Columns: name, price, status, user_id (FK)
- run + revert both tested locally

## Testing
- Unit: product.service.spec.ts (6 tests), product.cqrs.spec.ts (6 tests)
- E2E: product.e2e-spec.ts (create, list, update, delete, auth rejection)

## Checklist
- [x] Tests pass (yarn api:test)
- [x] Lint passes (yarn lint)
- [x] Migration run + revert tested
- [x] Smoke tested in GraphQL Playground
- [x] No .env secrets committed

Phase 5: Deployment

The migration runs first, then the API deploy. (See Part 12 for the ECS task pattern.)

1. CI passes on main ✓
2. Migration one-off ECS task runs against production DB
   - If exit code 0 → proceed
   - If exit code 1 → stop, run revert, investigate
3. ECS rolling deploy (new tasks start, old tasks drain with --stop-timeout=30s)
4. Smoke test production: `curl https://api.prod/graphql -d '{"query":"{ __typename }"}'`
5. Monitor error rate in CloudWatch for 10 minutes
6. If clean → done
7. If errors spike → roll back (update ECS service to previous image)

Phase 6: Post-Deployment

# Update knowledge graphs after deploy
graphify update .
node .gitnexus/run.cjs analyze

# Close ticket
"Mark CU-1234 as complete in ClickUp."

# Update memory if there were non-obvious decisions
"Remember: Product module uses soft-delete via deletedAt column.
Every FindMany query in ProductService must add { deletedAt: { is: null } } filter."

2. Daily Workflow

Morning (10 minutes)

git checkout main && git pull

# Check what's in progress
"Show my ClickUp tasks for today's sprint."

# Start infrastructure if needed
yarn docker:dev

# Orient Claude for the day
claude
"Check the gitnexus index status and tell me if it needs updating."

Pick the most important ticket for the day. One ticket at a time. Multitasking between two features on two branches produces shallow work on both.

During Development (the Focus Block)

  1. Orient first: graphify query for the reference pattern
  2. Impact check: gitnexus impact on anything you’ll modify
  3. Build in order: entity → DTOs → CQRS → service → resolver → module
  4. Test as you go: don’t save all testing for the end
  5. Commit small, logical units: one commit per logical step, not one commit per file

The 20-minute rule: if you’ve been stuck on one problem for 20 minutes, ask Claude.

"I'm getting a 'Cannot resolve dependency' error in ProductModule.
The error says NestJS can't inject ProductService. Here's my module file: <paste>"

The error tables in Parts 08 and 14 cover the common cases. For novel errors, Claude with the codebase context usually finds the cause in seconds.

Pre-Commit Checklist (Never Skip)

[ ] TypeScript compiles (no red in yarn api:dev output)
[ ] Unit tests pass (yarn api:test)
[ ] Lint clean (yarn lint)
[ ] Migration tested run + revert
[ ] Smoke tested in Playground
[ ] detect_changes confirms scope
[ ] /code-review run and issues fixed

If any item fails: fix it before committing. A failing CI pipeline wastes reviewer time and signals carelessness.

End of Day (5 minutes)

# If PR is not ready — save progress
git add -p              # selective add of clean work
yarn cz                 # commit what's stable as wip: or feat: partial

# Save context to Claude memory
"Remember: Product module is 60% done — entity, DTOs, CQRS inputs complete.
Still need: service, resolver, module file, migration, tests."

# Mark ClickUp task
"Update CU-1234: in progress, 60% done, remaining: service + resolver + tests."

3. Sprint Ceremonies

Sprint Planning (tech lead’s role)

Before the meeting:

"Query the graph for all modules that reference UserEntity.
I'm planning to add a userId column to TagEntity this sprint
and need to understand potential impact."

During the meeting:

Red flags to call out in planning:

Sprint Review (tech lead’s role)

Retrospective

Questions worth asking:


4. Code Review Standards

What to Check in Every PR

Security (non-negotiable):

[ ] Every new mutation has @UseGuards(AuthJwtGuard)
[ ] No userId, tenantId as @Field() on any input type
[ ] No new secrets in code or .env committed
[ ] Validation decorators on all input fields

Architecture:

[ ] Entity extends AbstractEntity
[ ] Module uses NestjsQueryGraphQLModule.forFeature (not plain TypeOrmModule)
[ ] Handler files contain one-liners — zero logic
[ ] Resolver doesn't import Repository directly
[ ] CqrsModule in module imports

Database:

[ ] @Index() on every FK column
[ ] @Index() on every frequently-queried column
[ ] Migration generated (not synchronize:true)
[ ] Migration run + revert tested

Tests:

[ ] Unit tests cover happy path + error cases
[ ] Handler tests verify delegation pattern
[ ] At least one E2E test exists

How to Write Review Comments

Bad (no actionable next step):

"This doesn't look right."
"Performance issue here."

Good (specific, with reason and fix):

"Handler has logic (lines 12-15): the slug uniqueness check belongs in the service,
not the handler. Handlers should be: `return this.service.createOne(command.args)`
Move the check to ProductService.createOne()."

"Missing @Index() on userId column (product.entity.ts:24).
Without this index, getTodos queries will do full table scans when tenants have
thousands of products. Add: @Index() above @Column() userId."

Good review comments:

  1. Quote the exact line(s)
  2. Explain WHY it’s a problem
  3. Suggest the specific fix

Approval Philosophy

Don’t block on personal style when the code follows the project’s established patterns. Consistency is more valuable than any one developer’s preferences.


5. Architecture Decision Records (ADRs)

An ADR documents a significant technical decision: what was decided, why, and what alternatives were rejected. They prevent the same debate happening twice.

When to Write an ADR

ADR Template

# ADR-001: Use Bull Queues for All Async Operations

## Status
Accepted (2026-06-01)

## Context
Some operations (email, AI evaluation, report generation) are too slow to run
inline in a GraphQL mutation. We need a way to defer work.

## Decision
All async operations use Bull queues backed by Redis.
No inline async work in mutation handlers.

## Alternatives Considered
- `setTimeout` / `setImmediate`: lost on process restart, no retry
- Direct async/await in background: no queue, no monitoring, no retry
- AWS SQS: added infrastructure complexity for same result

## Consequences
- Requires Redis in every environment
- Adds bull-board for local debugging
- Jobs survive API restarts
- Automatic retry with exponential backoff
- Dead letter queue for failed jobs

Store ADRs in:

docs/adr/
├── 001-bull-queues-for-async.md
├── 002-rs256-jwt-dual-keypairs.md
├── 003-shared-table-multitenancy.md

Index them in CLAUDE.md:

## Architecture Decisions
Key decisions are in docs/adr/. Reference before proposing alternatives.

6. Onboarding New Developers

A new developer on an enterprise NestJS team needs 4 things in order:

Day 0 — Machine Setup:

Day 1 — Run the System:

Week 1 — Build the First Module (Tag):

Week 2 — Build the Second Module (with FK + ownership):

After Week 2 — Graduate to agents:

The Golden Rule for onboarding:

New developers must build the first two modules manually before using agents. Agents accelerate; they don’t teach. You need the muscle memory first.


7. Technical Debt Management

Technical debt is not bad code — it’s a trade-off between speed and quality, made explicitly or accidentally.

Classify Debt

TypeExamplePriority
Security debtHS256 JWT in a moduleCritical — fix now
Architecture debtLogic in a handlerHigh — fix next sprint
Performance debtMissing @Index() on FKMedium — fix before scale
Test debtNo E2E test for a moduleMedium — fix before release
Cleanup debtUnused importLow — fix opportunistically

Managing Debt in Practice

Track it: Create ClickUp tickets labeled chore for each debt item. Don’t fix debt in the same PR as a feature — it muddles the diff.

Allocate time: Reserve 20% of sprint capacity for debt reduction (the “chore” tickets).

The boy scout rule: “Leave the campground cleaner than you found it.” If you’re in a file and you see a missing @Index() that’s quick to add, add it and include it in your PR. Don’t create a separate ticket for a 3-line fix.

Never refactor inside a feature PR: If you need to refactor a module to add the feature cleanly, do the refactor in a separate PR first, then build the feature. Mixed PRs are impossible to review.


8. The Senior Developer Mindset

The things that distinguish a senior engineer from a mid-level engineer on this stack:

Mid-levelSenior
Follows the patternKnows why the pattern exists
Writes code that worksWrites code that’s safe to change
Asks “how do I do X?”Asks “should we do X?”
Fixes the bugFinds the category of bug and adds a rule to prevent it
Merges the featureThinks about what the migration looks like in production
Uses agents to generate codeUses agents as assistants, reviews everything generated
Knows the codebaseMaintains the documentation of the codebase

The last row is the most important for this era of AI-assisted development. A senior developer in 2026 is not the person who writes the most code — it’s the person who maintains the highest quality signal in the knowledge systems so the whole team (human and AI) can move fast safely.


Complete Series: What You Can Do Now

PartCapability
01Explain the NestJS stack vs Meteor — philosophy, not just syntax
02Bootstrap a production-grade Nx monorepo from scratch
03Understand NestJS DI, request lifecycle, module system
04Model data in TypeORM, run migrations safely
05Write typed CQRS — commands, queries, thin handlers
06Full GraphQL API + cursor pagination + Next.js frontend
07RS256 JWT auth, guards, refresh tokens
08Build a complete module (Tag) — all 9 steps
09FK relations, DataLoader, ownership enforcement
10Unit tests (mock repos) + E2E tests (real DB)
11Bull queues for async, Redis PubSub for real-time
12CI/CD, Docker, production migration strategy
13Multi-tenancy, RBAC, dual-auth portals
14Claude Code, agents, graphify, gitnexus — the AI layer
15GitHub MCP, ClickUp/Lark — project management integration
16Persistent memory, knowledge graphs, code intelligence
17Tech lead SDLC, daily workflow, code review, ADRs, onboarding

You now have everything needed to be a productive senior enterprise NestJS developer — not just someone who follows patterns, but someone who understands them well enough to enforce them, explain them to others, and extend them safely.


Edit page
Share this post:

Next Post
Customizing AstroPaper theme color schemes
Previous Post
Predefined color schemes