diff --git a/.codex/skills-index.json b/.codex/skills-index.json index ae69db0..38de572 100644 --- a/.codex/skills-index.json +++ b/.codex/skills-index.json @@ -81,7 +81,7 @@ "name": "senior-fullstack", "source": "../../engineering-team/senior-fullstack", "category": "engineering", - "description": "Comprehensive fullstack development skill for building complete web applications with React, Next.js, Node.js, GraphQL, and PostgreSQL. Includes project scaffolding, code quality analysis, architecture patterns, and complete tech stack guidance. Use when building new projects, analyzing code quality, implementing design patterns, or setting up development workflows." + "description": "Fullstack development toolkit with project scaffolding for Next.js/FastAPI/MERN/Django stacks and code quality analysis. Use when scaffolding new projects, analyzing codebase quality, or implementing fullstack architecture patterns." }, { "name": "senior-ml-engineer", diff --git a/engineering-team/senior-fullstack/SKILL.md b/engineering-team/senior-fullstack/SKILL.md index 43f9d9e..f71d08c 100644 --- a/engineering-team/senior-fullstack/SKILL.md +++ b/engineering-team/senior-fullstack/SKILL.md @@ -1,209 +1,281 @@ --- name: senior-fullstack -description: Comprehensive fullstack development skill for building complete web applications with React, Next.js, Node.js, GraphQL, and PostgreSQL. Includes project scaffolding, code quality analysis, architecture patterns, and complete tech stack guidance. Use when building new projects, analyzing code quality, implementing design patterns, or setting up development workflows. +description: Fullstack development toolkit with project scaffolding for Next.js/FastAPI/MERN/Django stacks and code quality analysis. Use when scaffolding new projects, analyzing codebase quality, or implementing fullstack architecture patterns. --- # Senior Fullstack -Complete toolkit for senior fullstack with modern tools and best practices. +Fullstack development skill with project scaffolding and code quality analysis tools. -## Quick Start +--- -### Main Capabilities +## Table of Contents -This skill provides three core capabilities through automated scripts: +- [Trigger Phrases](#trigger-phrases) +- [Tools](#tools) +- [Workflows](#workflows) +- [Reference Guides](#reference-guides) -```bash -# Script 1: Fullstack Scaffolder -python scripts/fullstack_scaffolder.py [options] +--- -# Script 2: Project Scaffolder -python scripts/project_scaffolder.py [options] +## Trigger Phrases -# Script 3: Code Quality Analyzer -python scripts/code_quality_analyzer.py [options] -``` +Use this skill when you hear: +- "scaffold a new project" +- "create a Next.js app" +- "set up FastAPI with React" +- "analyze code quality" +- "check for security issues in codebase" +- "what stack should I use" +- "set up a fullstack project" +- "generate project boilerplate" -## Core Capabilities +--- -### 1. Fullstack Scaffolder +## Tools -Automated tool for fullstack scaffolder tasks. +### Project Scaffolder -**Features:** -- Automated scaffolding -- Best practices built-in -- Configurable templates -- Quality checks +Generates fullstack project structures with boilerplate code. + +**Supported Templates:** +- `nextjs` - Next.js 14+ with App Router, TypeScript, Tailwind CSS +- `fastapi-react` - FastAPI backend + React frontend + PostgreSQL +- `mern` - MongoDB, Express, React, Node.js with TypeScript +- `django-react` - Django REST Framework + React frontend **Usage:** + ```bash -python scripts/fullstack_scaffolder.py [options] +# List available templates +python scripts/project_scaffolder.py --list-templates + +# Create Next.js project +python scripts/project_scaffolder.py nextjs my-app + +# Create FastAPI + React project +python scripts/project_scaffolder.py fastapi-react my-api + +# Create MERN stack project +python scripts/project_scaffolder.py mern my-project + +# Create Django + React project +python scripts/project_scaffolder.py django-react my-app + +# Specify output directory +python scripts/project_scaffolder.py nextjs my-app --output ./projects + +# JSON output +python scripts/project_scaffolder.py nextjs my-app --json ``` -### 2. Project Scaffolder +**Parameters:** -Comprehensive analysis and optimization tool. +| Parameter | Description | +|-----------|-------------| +| `template` | Template name (nextjs, fastapi-react, mern, django-react) | +| `project_name` | Name for the new project directory | +| `--output, -o` | Output directory (default: current directory) | +| `--list-templates, -l` | List all available templates | +| `--json` | Output in JSON format | -**Features:** -- Deep analysis -- Performance metrics -- Recommendations -- Automated fixes +**Output includes:** +- Project structure with all necessary files +- Package configurations (package.json, requirements.txt) +- TypeScript configuration +- Docker and docker-compose setup +- Environment file templates +- Next steps for running the project + +--- + +### Code Quality Analyzer + +Analyzes fullstack codebases for quality issues. + +**Analysis Categories:** +- Security vulnerabilities (hardcoded secrets, injection risks) +- Code complexity metrics (cyclomatic complexity, nesting depth) +- Dependency health (outdated packages, known CVEs) +- Test coverage estimation +- Documentation quality **Usage:** + ```bash -python scripts/project_scaffolder.py [--verbose] +# Analyze current directory +python scripts/code_quality_analyzer.py . + +# Analyze specific project +python scripts/code_quality_analyzer.py /path/to/project + +# Verbose output with detailed findings +python scripts/code_quality_analyzer.py . --verbose + +# JSON output +python scripts/code_quality_analyzer.py . --json + +# Save report to file +python scripts/code_quality_analyzer.py . --output report.json ``` -### 3. Code Quality Analyzer +**Parameters:** -Advanced tooling for specialized tasks. +| Parameter | Description | +|-----------|-------------| +| `project_path` | Path to project directory (default: current directory) | +| `--verbose, -v` | Show detailed findings | +| `--json` | Output in JSON format | +| `--output, -o` | Write report to file | -**Features:** -- Expert-level automation -- Custom configurations -- Integration ready -- Production-grade output +**Output includes:** +- Overall score (0-100) with letter grade +- Security issues by severity (critical, high, medium, low) +- High complexity files +- Vulnerable dependencies with CVE references +- Test coverage estimate +- Documentation completeness +- Prioritized recommendations -**Usage:** -```bash -python scripts/code_quality_analyzer.py [arguments] [options] +**Sample Output:** + +``` +============================================================ +CODE QUALITY ANALYSIS REPORT +============================================================ + +Overall Score: 75/100 (Grade: C) +Files Analyzed: 45 +Total Lines: 12,500 + +--- SECURITY --- + Critical: 1 + High: 2 + Medium: 5 + +--- COMPLEXITY --- + Average Complexity: 8.5 + High Complexity Files: 3 + +--- RECOMMENDATIONS --- +1. [P0] SECURITY + Issue: Potential hardcoded secret detected + Action: Remove or secure sensitive data at line 42 ``` -## Reference Documentation +--- -### Tech Stack Guide +## Workflows -Comprehensive guide available in `references/tech_stack_guide.md`: +### Workflow 1: Start New Project -- Detailed patterns and practices -- Code examples -- Best practices -- Anti-patterns to avoid -- Real-world scenarios - -### Architecture Patterns - -Complete workflow documentation in `references/architecture_patterns.md`: - -- Step-by-step processes -- Optimization strategies -- Tool integrations -- Performance tuning -- Troubleshooting guide - -### Development Workflows - -Technical reference guide in `references/development_workflows.md`: - -- Technology stack details -- Configuration examples -- Integration patterns -- Security considerations -- Scalability guidelines - -## Tech Stack - -**Languages:** TypeScript, JavaScript, Python, Go, Swift, Kotlin -**Frontend:** React, Next.js, React Native, Flutter -**Backend:** Node.js, Express, GraphQL, REST APIs -**Database:** PostgreSQL, Prisma, NeonDB, Supabase -**DevOps:** Docker, Kubernetes, Terraform, GitHub Actions, CircleCI -**Cloud:** AWS, GCP, Azure - -## Development Workflow - -### 1. Setup and Configuration +1. Choose appropriate stack based on requirements +2. Scaffold project structure +3. Run initial quality check +4. Set up development environment ```bash -# Install dependencies +# 1. Scaffold project +python scripts/project_scaffolder.py nextjs my-saas-app + +# 2. Navigate and install +cd my-saas-app npm install -# or -pip install -r requirements.txt -# Configure environment -cp .env.example .env -``` +# 3. Configure environment +cp .env.example .env.local -### 2. Run Quality Checks +# 4. Run quality check +python ../scripts/code_quality_analyzer.py . -```bash -# Use the analyzer script -python scripts/project_scaffolder.py . - -# Review recommendations -# Apply fixes -``` - -### 3. Implement Best Practices - -Follow the patterns and practices documented in: -- `references/tech_stack_guide.md` -- `references/architecture_patterns.md` -- `references/development_workflows.md` - -## Best Practices Summary - -### Code Quality -- Follow established patterns -- Write comprehensive tests -- Document decisions -- Review regularly - -### Performance -- Measure before optimizing -- Use appropriate caching -- Optimize critical paths -- Monitor in production - -### Security -- Validate all inputs -- Use parameterized queries -- Implement proper authentication -- Keep dependencies updated - -### Maintainability -- Write clear code -- Use consistent naming -- Add helpful comments -- Keep it simple - -## Common Commands - -```bash -# Development +# 5. Start development npm run dev -npm run build -npm run test -npm run lint - -# Analysis -python scripts/project_scaffolder.py . -python scripts/code_quality_analyzer.py --analyze - -# Deployment -docker build -t app:latest . -docker-compose up -d -kubectl apply -f k8s/ ``` -## Troubleshooting +### Workflow 2: Audit Existing Codebase + +1. Run code quality analysis +2. Review security findings +3. Address critical issues first +4. Plan improvements + +```bash +# 1. Full analysis +python scripts/code_quality_analyzer.py /path/to/project --verbose + +# 2. Generate detailed report +python scripts/code_quality_analyzer.py /path/to/project --json --output audit.json + +# 3. Address P0 issues immediately +# 4. Create tickets for P1/P2 issues +``` + +### Workflow 3: Stack Selection + +Use the tech stack guide to evaluate options: + +1. **SEO Required?** → Next.js with SSR +2. **API-heavy backend?** → Separate FastAPI or NestJS +3. **Real-time features?** → Add WebSocket layer +4. **Team expertise** → Match stack to team skills + +See `references/tech_stack_guide.md` for detailed comparison. + +--- + +## Reference Guides + +### Architecture Patterns (`references/architecture_patterns.md`) + +- Frontend component architecture (Atomic Design, Container/Presentational) +- Backend patterns (Clean Architecture, Repository Pattern) +- API design (REST conventions, GraphQL schema design) +- Database patterns (connection pooling, transactions, read replicas) +- Caching strategies (cache-aside, HTTP cache headers) +- Authentication architecture (JWT + refresh tokens, sessions) + +### Development Workflows (`references/development_workflows.md`) + +- Local development setup (Docker Compose, environment config) +- Git workflows (trunk-based, conventional commits) +- CI/CD pipelines (GitHub Actions examples) +- Testing strategies (unit, integration, E2E) +- Code review process (PR templates, checklists) +- Deployment strategies (blue-green, canary, feature flags) +- Monitoring and observability (logging, metrics, health checks) + +### Tech Stack Guide (`references/tech_stack_guide.md`) + +- Frontend frameworks comparison (Next.js, React+Vite, Vue) +- Backend frameworks (Express, Fastify, NestJS, FastAPI, Django) +- Database selection (PostgreSQL, MongoDB, Redis) +- ORMs (Prisma, Drizzle, SQLAlchemy) +- Authentication solutions (Auth.js, Clerk, custom JWT) +- Deployment platforms (Vercel, Railway, AWS) +- Stack recommendations by use case (MVP, SaaS, Enterprise) + +--- + +## Quick Reference + +### Stack Decision Matrix + +| Requirement | Recommendation | +|-------------|---------------| +| SEO-critical site | Next.js with SSR | +| Internal dashboard | React + Vite | +| API-first backend | FastAPI or Fastify | +| Enterprise scale | NestJS + PostgreSQL | +| Rapid prototype | Next.js API routes | +| Document-heavy data | MongoDB | +| Complex queries | PostgreSQL | ### Common Issues -Check the comprehensive troubleshooting section in `references/development_workflows.md`. - -### Getting Help - -- Review reference documentation -- Check script output messages -- Consult tech stack documentation -- Review error logs - -## Resources - -- Pattern Reference: `references/tech_stack_guide.md` -- Workflow Guide: `references/architecture_patterns.md` -- Technical Guide: `references/development_workflows.md` -- Tool Scripts: `scripts/` directory +| Issue | Solution | +|-------|----------| +| N+1 queries | Use DataLoader or eager loading | +| Slow builds | Check bundle size, lazy load | +| Auth complexity | Use Auth.js or Clerk | +| Type errors | Enable strict mode in tsconfig | +| CORS issues | Configure middleware properly | diff --git a/engineering-team/senior-fullstack/references/architecture_patterns.md b/engineering-team/senior-fullstack/references/architecture_patterns.md index 6b049dc..7fa0133 100644 --- a/engineering-team/senior-fullstack/references/architecture_patterns.md +++ b/engineering-team/senior-fullstack/references/architecture_patterns.md @@ -1,103 +1,547 @@ -# Architecture Patterns +# Fullstack Architecture Patterns -## Overview +Proven architectural patterns for scalable fullstack applications covering frontend, backend, and their integration. -This reference guide provides comprehensive information for senior fullstack. +--- -## Patterns and Practices +## Table of Contents -### Pattern 1: Best Practice Implementation +- [Frontend Architecture](#frontend-architecture) +- [Backend Architecture](#backend-architecture) +- [API Design Patterns](#api-design-patterns) +- [Database Patterns](#database-patterns) +- [Caching Strategies](#caching-strategies) +- [Authentication Architecture](#authentication-architecture) -**Description:** -Detailed explanation of the pattern. +--- -**When to Use:** -- Scenario 1 -- Scenario 2 -- Scenario 3 +## Frontend Architecture + +### Component Architecture + +**Atomic Design Pattern** + +Organize components in hierarchical levels: + +``` +src/components/ +├── atoms/ # Button, Input, Icon +├── molecules/ # SearchInput, FormField +├── organisms/ # Header, Footer, Sidebar +├── templates/ # PageLayout, DashboardLayout +└── pages/ # Home, Profile, Settings +``` + +**When to use:** Large applications with design systems and multiple teams. + +**Container/Presentational Pattern** -**Implementation:** ```typescript -// Example code implementation -export class Example { - // Implementation details +// Presentational - pure rendering, no state +function UserCard({ name, email, avatar }: UserCardProps) { + return ( +
+ {name} +

{name}

+

{email}

+
+ ); +} + +// Container - handles data fetching and state +function UserCardContainer({ userId }: { userId: string }) { + const { data, loading } = useUser(userId); + if (loading) return ; + return ; } ``` -**Benefits:** -- Benefit 1 -- Benefit 2 -- Benefit 3 +**When to use:** When you need clear separation between UI and logic. -**Trade-offs:** -- Consider 1 -- Consider 2 -- Consider 3 +### State Management Patterns -### Pattern 2: Advanced Technique +**Server State vs Client State** -**Description:** -Another important pattern for senior fullstack. +| Type | Examples | Tools | +|------|----------|-------| +| Server State | User data, API responses | React Query, SWR | +| Client State | UI toggles, form inputs | Zustand, Jotai | +| URL State | Filters, pagination | Next.js router | + +**React Query for Server State:** -**Implementation:** ```typescript -// Advanced example -async function advancedExample() { - // Code here +function useUsers(filters: Filters) { + return useQuery({ + queryKey: ["users", filters], + queryFn: () => api.getUsers(filters), + staleTime: 5 * 60 * 1000, // 5 minutes + gcTime: 30 * 60 * 1000, // 30 minutes + }); +} + +// Mutations with optimistic updates +function useUpdateUser() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: api.updateUser, + onMutate: async (newUser) => { + await queryClient.cancelQueries({ queryKey: ["users"] }); + const previous = queryClient.getQueryData(["users"]); + queryClient.setQueryData(["users"], (old) => + old.map(u => u.id === newUser.id ? newUser : u) + ); + return { previous }; + }, + onError: (err, newUser, context) => { + queryClient.setQueryData(["users"], context.previous); + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ["users"] }); + }, + }); } ``` -## Guidelines +--- -### Code Organization -- Clear structure -- Logical separation -- Consistent naming -- Proper documentation +## Backend Architecture -### Performance Considerations -- Optimization strategies -- Bottleneck identification -- Monitoring approaches -- Scaling techniques +### Clean Architecture -### Security Best Practices -- Input validation -- Authentication -- Authorization -- Data protection +``` +src/ +├── domain/ # Business entities, no dependencies +│ ├── entities/ # User, Order, Product +│ └── interfaces/ # Repository interfaces +├── application/ # Use cases, application logic +│ ├── use-cases/ # CreateOrder, UpdateUser +│ └── services/ # OrderService, AuthService +├── infrastructure/ # External concerns +│ ├── database/ # Repository implementations +│ ├── http/ # Controllers, middleware +│ └── external/ # Third-party integrations +└── shared/ # Cross-cutting concerns + ├── errors/ + └── utils/ +``` -## Common Patterns +**Dependency Flow:** domain ← application ← infrastructure -### Pattern A -Implementation details and examples. +**Repository Pattern:** -### Pattern B -Implementation details and examples. +```typescript +// Domain interface +interface UserRepository { + findById(id: string): Promise; + findByEmail(email: string): Promise; + save(user: User): Promise; + delete(id: string): Promise; +} -### Pattern C -Implementation details and examples. +// Infrastructure implementation +class PostgresUserRepository implements UserRepository { + constructor(private db: Database) {} -## Anti-Patterns to Avoid + async findById(id: string): Promise { + const row = await this.db.query( + "SELECT * FROM users WHERE id = $1", + [id] + ); + return row ? this.toEntity(row) : null; + } -### Anti-Pattern 1 -What not to do and why. + private toEntity(row: UserRow): User { + return new User({ + id: row.id, + email: row.email, + name: row.name, + createdAt: row.created_at, + }); + } +} +``` -### Anti-Pattern 2 -What not to do and why. +### Middleware Pipeline -## Tools and Resources +```typescript +// Express middleware chain +app.use(cors()); +app.use(helmet()); +app.use(requestId()); +app.use(logger()); +app.use(authenticate()); +app.use(rateLimit()); +app.use("/api", routes); +app.use(errorHandler()); -### Recommended Tools -- Tool 1: Purpose -- Tool 2: Purpose -- Tool 3: Purpose +// Custom middleware example +function requestId() { + return (req: Request, res: Response, next: NextFunction) => { + req.id = req.headers["x-request-id"] || crypto.randomUUID(); + res.setHeader("x-request-id", req.id); + next(); + }; +} -### Further Reading -- Resource 1 -- Resource 2 -- Resource 3 +function errorHandler() { + return (err: Error, req: Request, res: Response, next: NextFunction) => { + const status = err instanceof AppError ? err.status : 500; + const message = status === 500 ? "Internal Server Error" : err.message; -## Conclusion + logger.error({ err, requestId: req.id }); + res.status(status).json({ error: message, requestId: req.id }); + }; +} +``` -Key takeaways for using this reference guide effectively. +--- + +## API Design Patterns + +### REST Best Practices + +**Resource Naming:** +- Use nouns, not verbs: `/users` not `/getUsers` +- Use plural: `/users` not `/user` +- Nest for relationships: `/users/{id}/orders` + +**HTTP Methods:** + +| Method | Purpose | Idempotent | +|--------|---------|------------| +| GET | Retrieve | Yes | +| POST | Create | No | +| PUT | Replace | Yes | +| PATCH | Partial update | No | +| DELETE | Remove | Yes | + +**Response Envelope:** + +```typescript +// Success response +{ + "data": { /* resource */ }, + "meta": { + "requestId": "abc-123", + "timestamp": "2024-01-15T10:30:00Z" + } +} + +// Paginated response +{ + "data": [/* items */], + "pagination": { + "page": 1, + "pageSize": 20, + "total": 150, + "totalPages": 8 + } +} + +// Error response +{ + "error": { + "code": "VALIDATION_ERROR", + "message": "Invalid input", + "details": [ + { "field": "email", "message": "Invalid email format" } + ] + }, + "meta": { "requestId": "abc-123" } +} +``` + +### GraphQL Architecture + +**Schema-First Design:** + +```graphql +type Query { + user(id: ID!): User + users(filter: UserFilter, page: PageInput): UserConnection! +} + +type Mutation { + createUser(input: CreateUserInput!): UserPayload! + updateUser(id: ID!, input: UpdateUserInput!): UserPayload! +} + +type User { + id: ID! + email: String! + profile: Profile + orders(first: Int, after: String): OrderConnection! +} + +type UserPayload { + user: User + errors: [Error!] +} +``` + +**Resolver Pattern:** + +```typescript +const resolvers = { + Query: { + user: async (_, { id }, { dataSources }) => { + return dataSources.userAPI.findById(id); + }, + }, + User: { + // Field resolver for related data + orders: async (user, { first, after }, { dataSources }) => { + return dataSources.orderAPI.findByUserId(user.id, { first, after }); + }, + }, +}; +``` + +**DataLoader for N+1 Prevention:** + +```typescript +const userLoader = new DataLoader(async (userIds: string[]) => { + const users = await db.query( + "SELECT * FROM users WHERE id = ANY($1)", + [userIds] + ); + // Return in same order as input + return userIds.map(id => users.find(u => u.id === id)); +}); +``` + +--- + +## Database Patterns + +### Connection Pooling + +```typescript +// PostgreSQL with connection pool +const pool = new Pool({ + host: process.env.DB_HOST, + database: process.env.DB_NAME, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + max: 20, // Maximum connections + idleTimeoutMillis: 30000, // Close idle connections + connectionTimeoutMillis: 2000, +}); + +// Prisma with connection pool +const prisma = new PrismaClient({ + datasources: { + db: { + url: `${process.env.DATABASE_URL}?connection_limit=20&pool_timeout=10`, + }, + }, +}); +``` + +### Transaction Patterns + +```typescript +// Unit of Work pattern +async function transferFunds(from: string, to: string, amount: number) { + return await prisma.$transaction(async (tx) => { + const sender = await tx.account.update({ + where: { id: from }, + data: { balance: { decrement: amount } }, + }); + + if (sender.balance < 0) { + throw new InsufficientFundsError(); + } + + await tx.account.update({ + where: { id: to }, + data: { balance: { increment: amount } }, + }); + + return tx.transaction.create({ + data: { fromId: from, toId: to, amount }, + }); + }); +} +``` + +### Read Replicas + +```typescript +// Route reads to replica +const readDB = new PrismaClient({ + datasources: { db: { url: process.env.READ_DATABASE_URL } }, +}); + +const writeDB = new PrismaClient({ + datasources: { db: { url: process.env.WRITE_DATABASE_URL } }, +}); + +class UserRepository { + async findById(id: string) { + return readDB.user.findUnique({ where: { id } }); + } + + async create(data: CreateUserData) { + return writeDB.user.create({ data }); + } +} +``` + +--- + +## Caching Strategies + +### Cache Layers + +``` +Request → CDN Cache → Application Cache → Database Cache → Database +``` + +**Cache-Aside Pattern:** + +```typescript +async function getUser(id: string): Promise { + const cacheKey = `user:${id}`; + + // 1. Try cache + const cached = await redis.get(cacheKey); + if (cached) { + return JSON.parse(cached); + } + + // 2. Fetch from database + const user = await db.user.findUnique({ where: { id } }); + if (!user) throw new NotFoundError(); + + // 3. Store in cache + await redis.set(cacheKey, JSON.stringify(user), "EX", 3600); + + return user; +} + +// Invalidate on update +async function updateUser(id: string, data: UpdateData): Promise { + const user = await db.user.update({ where: { id }, data }); + await redis.del(`user:${id}`); + return user; +} +``` + +**HTTP Cache Headers:** + +```typescript +// Immutable assets (hashed filenames) +res.setHeader("Cache-Control", "public, max-age=31536000, immutable"); + +// API responses +res.setHeader("Cache-Control", "private, max-age=0, must-revalidate"); +res.setHeader("ETag", generateETag(data)); + +// Static pages +res.setHeader("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400"); +``` + +--- + +## Authentication Architecture + +### JWT + Refresh Token Flow + +``` +1. User logs in → Server returns access token (15min) + refresh token (7d) +2. Client stores tokens (httpOnly cookie for refresh, memory for access) +3. Access token expires → Client uses refresh token to get new pair +4. Refresh token expires → User must log in again +``` + +**Implementation:** + +```typescript +// Token generation +function generateTokens(user: User) { + const accessToken = jwt.sign( + { sub: user.id, email: user.email }, + process.env.JWT_SECRET, + { expiresIn: "15m" } + ); + + const refreshToken = jwt.sign( + { sub: user.id, tokenVersion: user.tokenVersion }, + process.env.REFRESH_SECRET, + { expiresIn: "7d" } + ); + + return { accessToken, refreshToken }; +} + +// Refresh endpoint +app.post("/auth/refresh", async (req, res) => { + const refreshToken = req.cookies.refreshToken; + + try { + const payload = jwt.verify(refreshToken, process.env.REFRESH_SECRET); + const user = await db.user.findUnique({ where: { id: payload.sub } }); + + // Check token version (invalidation mechanism) + if (user.tokenVersion !== payload.tokenVersion) { + throw new Error("Token revoked"); + } + + const tokens = generateTokens(user); + setRefreshCookie(res, tokens.refreshToken); + res.json({ accessToken: tokens.accessToken }); + } catch { + res.status(401).json({ error: "Invalid refresh token" }); + } +}); +``` + +### Session-Based Auth + +```typescript +// Redis session store +app.use(session({ + store: new RedisStore({ client: redisClient }), + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: false, + cookie: { + secure: process.env.NODE_ENV === "production", + httpOnly: true, + sameSite: "lax", + maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days + }, +})); + +// Login +app.post("/auth/login", async (req, res) => { + const user = await authenticate(req.body.email, req.body.password); + req.session.userId = user.id; + res.json({ user }); +}); + +// Middleware +function requireAuth(req, res, next) { + if (!req.session.userId) { + return res.status(401).json({ error: "Authentication required" }); + } + next(); +} +``` + +--- + +## Decision Matrix + +| Pattern | Complexity | Scalability | When to Use | +|---------|-----------|-------------|-------------| +| Monolith | Low | Medium | MVPs, small teams | +| Modular Monolith | Medium | High | Growing teams | +| Microservices | High | Very High | Large orgs, diverse tech | +| REST | Low | High | CRUD APIs, public APIs | +| GraphQL | Medium | High | Complex data needs, mobile apps | +| JWT Auth | Low | High | Stateless APIs, microservices | +| Session Auth | Low | Medium | Traditional web apps | diff --git a/engineering-team/senior-fullstack/references/development_workflows.md b/engineering-team/senior-fullstack/references/development_workflows.md index 03cbf2d..1bffd83 100644 --- a/engineering-team/senior-fullstack/references/development_workflows.md +++ b/engineering-team/senior-fullstack/references/development_workflows.md @@ -1,103 +1,784 @@ -# Development Workflows +# Fullstack Development Workflows -## Overview +Complete development lifecycle workflows from local setup to production deployment. -This reference guide provides comprehensive information for senior fullstack. +--- -## Patterns and Practices +## Table of Contents -### Pattern 1: Best Practice Implementation +- [Local Development Setup](#local-development-setup) +- [Git Workflows](#git-workflows) +- [CI/CD Pipelines](#cicd-pipelines) +- [Testing Strategies](#testing-strategies) +- [Code Review Process](#code-review-process) +- [Deployment Strategies](#deployment-strategies) +- [Monitoring and Observability](#monitoring-and-observability) -**Description:** -Detailed explanation of the pattern. +--- -**When to Use:** -- Scenario 1 -- Scenario 2 -- Scenario 3 +## Local Development Setup + +### Docker Compose Development Environment + +```yaml +# docker-compose.yml +version: "3.8" + +services: + app: + build: + context: . + target: development + volumes: + - .:/app + - /app/node_modules + ports: + - "3000:3000" + environment: + - DATABASE_URL=postgresql://user:pass@db:5432/app + - REDIS_URL=redis://redis:6379 + depends_on: + - db + - redis + + db: + image: postgres:16-alpine + environment: + POSTGRES_USER: user + POSTGRES_PASSWORD: pass + POSTGRES_DB: app + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + +volumes: + postgres_data: +``` + +**Multistage Dockerfile:** + +```dockerfile +# Base stage +FROM node:20-alpine AS base +WORKDIR /app +RUN apk add --no-cache libc6-compat + +# Development stage +FROM base AS development +COPY package*.json ./ +RUN npm ci +COPY . . +CMD ["npm", "run", "dev"] + +# Builder stage +FROM base AS builder +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Production stage +FROM base AS production +ENV NODE_ENV=production +COPY --from=builder /app/package*.json ./ +RUN npm ci --only=production +COPY --from=builder /app/dist ./dist +USER node +CMD ["node", "dist/index.js"] +``` + +### Environment Configuration + +```bash +# .env.local (development) +DATABASE_URL="postgresql://user:pass@localhost:5432/app_dev" +REDIS_URL="redis://localhost:6379" +JWT_SECRET="development-secret-change-in-prod" +LOG_LEVEL="debug" + +# .env.test +DATABASE_URL="postgresql://user:pass@localhost:5432/app_test" +LOG_LEVEL="error" + +# .env.production (via secrets management) +DATABASE_URL="${DATABASE_URL}" +REDIS_URL="${REDIS_URL}" +JWT_SECRET="${JWT_SECRET}" +``` + +**Environment validation:** -**Implementation:** ```typescript -// Example code implementation -export class Example { - // Implementation details +import { z } from "zod"; + +const envSchema = z.object({ + NODE_ENV: z.enum(["development", "test", "production"]), + DATABASE_URL: z.string().url(), + REDIS_URL: z.string().url().optional(), + JWT_SECRET: z.string().min(32), + PORT: z.coerce.number().default(3000), +}); + +export const env = envSchema.parse(process.env); +``` + +--- + +## Git Workflows + +### Trunk-Based Development + +``` +main (protected) + │ + ├── feature/user-auth (short-lived, 1-2 days max) + │ └── squash merge → main + │ + ├── feature/payment-flow + │ └── squash merge → main + │ + └── release/v1.2.0 (cut from main for hotfixes) +``` + +**Branch naming:** +- `feature/description` - New features +- `fix/description` - Bug fixes +- `chore/description` - Maintenance tasks +- `release/vX.Y.Z` - Release branches + +### Commit Standards + +**Conventional Commits:** + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +**Types:** +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation +- `style`: Formatting +- `refactor`: Code restructuring +- `test`: Adding tests +- `chore`: Maintenance + +**Examples:** + +```bash +feat(auth): add password reset flow + +Implement password reset with email verification. +Tokens expire after 1 hour. + +Closes #123 + +--- + +fix(api): handle null response in user endpoint + +The API was returning 500 when user profile was incomplete. +Now returns partial data with null fields. + +--- + +chore(deps): update Next.js to 14.1.0 + +Breaking changes addressed: +- Updated Image component usage +- Migrated to new metadata API +``` + +### Pre-commit Hooks + +```json +// package.json +{ + "scripts": { + "prepare": "husky install" + }, + "lint-staged": { + "*.{ts,tsx}": ["eslint --fix", "prettier --write"], + "*.{json,md}": ["prettier --write"] + } } ``` -**Benefits:** -- Benefit 1 -- Benefit 2 -- Benefit 3 +```bash +# .husky/pre-commit +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" -**Trade-offs:** -- Consider 1 -- Consider 2 -- Consider 3 +npx lint-staged -### Pattern 2: Advanced Technique +# .husky/commit-msg +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" -**Description:** -Another important pattern for senior fullstack. +npx commitlint --edit $1 +``` + +--- + +## CI/CD Pipelines + +### GitHub Actions + +```yaml +# .github/workflows/ci.yml +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run lint + - run: npm run type-check + + test: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: test + POSTGRES_PASSWORD: test + POSTGRES_DB: test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run test:unit + - run: npm run test:integration + env: + DATABASE_URL: postgresql://test:test@localhost:5432/test + - uses: codecov/codecov-action@v3 + + build: + runs-on: ubuntu-latest + needs: [lint, test] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run build + - uses: actions/upload-artifact@v4 + with: + name: build + path: dist/ + + deploy-preview: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: build + path: dist/ + # Deploy to preview environment + - name: Deploy Preview + run: | + # Deploy logic here + echo "Deployed to preview-${{ github.event.pull_request.number }}" + + deploy-production: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + needs: build + environment: production + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: build + path: dist/ + - name: Deploy Production + run: | + # Production deployment + echo "Deployed to production" +``` + +### Database Migrations in CI + +```yaml +# Part of deploy job +- name: Run Migrations + run: | + npx prisma migrate deploy + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + +- name: Verify Migration + run: | + npx prisma migrate status +``` + +--- + +## Testing Strategies + +### Testing Pyramid + +``` + /\ + / \ E2E Tests (10%) + / \ - Critical user journeys + /──────\ + / \ Integration Tests (20%) + / \ - API endpoints + /────────────\ - Database operations + / \ + / \ Unit Tests (70%) +/──────────────────\ - Components, hooks, utilities +``` + +### Unit Testing -**Implementation:** ```typescript -// Advanced example -async function advancedExample() { - // Code here +// Component test with React Testing Library +import { render, screen, fireEvent } from "@testing-library/react"; +import { UserForm } from "./UserForm"; + +describe("UserForm", () => { + it("submits form with valid data", async () => { + const onSubmit = vi.fn(); + render(); + + fireEvent.change(screen.getByLabelText(/email/i), { + target: { value: "test@example.com" }, + }); + fireEvent.change(screen.getByLabelText(/name/i), { + target: { value: "John Doe" }, + }); + fireEvent.click(screen.getByRole("button", { name: /submit/i })); + + await waitFor(() => { + expect(onSubmit).toHaveBeenCalledWith({ + email: "test@example.com", + name: "John Doe", + }); + }); + }); + + it("shows validation error for invalid email", async () => { + render(); + + fireEvent.change(screen.getByLabelText(/email/i), { + target: { value: "invalid" }, + }); + fireEvent.click(screen.getByRole("button", { name: /submit/i })); + + expect(await screen.findByText(/invalid email/i)).toBeInTheDocument(); + }); +}); +``` + +### Integration Testing + +```typescript +// API integration test +import { createTestClient } from "./test-utils"; +import { db } from "@/lib/db"; + +describe("POST /api/users", () => { + beforeEach(async () => { + await db.user.deleteMany(); + }); + + it("creates user with valid data", async () => { + const client = createTestClient(); + + const response = await client.post("/api/users", { + email: "new@example.com", + name: "New User", + }); + + expect(response.status).toBe(201); + expect(response.data.user.email).toBe("new@example.com"); + + // Verify in database + const user = await db.user.findUnique({ + where: { email: "new@example.com" }, + }); + expect(user).toBeTruthy(); + }); + + it("returns 409 for duplicate email", async () => { + await db.user.create({ + data: { email: "existing@example.com", name: "Existing" }, + }); + + const client = createTestClient(); + + const response = await client.post("/api/users", { + email: "existing@example.com", + name: "Duplicate", + }); + + expect(response.status).toBe(409); + expect(response.data.error.code).toBe("EMAIL_EXISTS"); + }); +}); +``` + +### E2E Testing with Playwright + +```typescript +// e2e/auth.spec.ts +import { test, expect } from "@playwright/test"; + +test.describe("Authentication", () => { + test("user can log in and access dashboard", async ({ page }) => { + await page.goto("/login"); + + await page.fill('[name="email"]', "user@example.com"); + await page.fill('[name="password"]', "password123"); + await page.click('button[type="submit"]'); + + await expect(page).toHaveURL("/dashboard"); + await expect(page.locator("h1")).toHaveText("Welcome back"); + }); + + test("shows error for invalid credentials", async ({ page }) => { + await page.goto("/login"); + + await page.fill('[name="email"]', "wrong@example.com"); + await page.fill('[name="password"]', "wrongpassword"); + await page.click('button[type="submit"]'); + + await expect(page.locator('[role="alert"]')).toHaveText( + "Invalid email or password" + ); + }); +}); +``` + +--- + +## Code Review Process + +### PR Template + +```markdown +## Summary + + +## Type of Change +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation update + +## Changes Made + + +## Testing +- [ ] Unit tests added/updated +- [ ] Integration tests added/updated +- [ ] Manual testing completed + +## Screenshots + + +## Checklist +- [ ] Code follows style guidelines +- [ ] Self-review completed +- [ ] Documentation updated +- [ ] No new warnings +``` + +### Review Checklist + +**Functionality:** +- Does the code do what it's supposed to? +- Are edge cases handled? +- Is error handling appropriate? + +**Code Quality:** +- Is the code readable and maintainable? +- Are there any code smells? +- Is there unnecessary duplication? + +**Performance:** +- Are there N+1 queries? +- Is caching used appropriately? +- Are there memory leaks? + +**Security:** +- Is user input validated? +- Are there injection vulnerabilities? +- Is sensitive data protected? + +--- + +## Deployment Strategies + +### Blue-Green Deployment + +``` + Load Balancer + │ + ┌────────────┴────────────┐ + │ │ + ┌────┴────┐ ┌─────┴────┐ + │ Blue │ │ Green │ + │ (Live) │ │ (Idle) │ + └─────────┘ └──────────┘ + +1. Deploy new version to Green +2. Run smoke tests on Green +3. Switch traffic to Green +4. Blue becomes idle (rollback target) +``` + +### Canary Deployment + +``` + Load Balancer + │ + ┌────────────┴────────────┐ + │ │ + │ 95% 5% │ + ▼ ▼ + ┌─────────┐ ┌──────────┐ + │ Stable │ │ Canary │ + │ v1.0.0 │ │ v1.1.0 │ + └─────────┘ └──────────┘ + +1. Deploy canary with small traffic % +2. Monitor error rates, latency +3. Gradually increase traffic +4. Full rollout or rollback +``` + +### Feature Flags + +```typescript +// Feature flag service +const flags = { + newCheckoutFlow: { + enabled: true, + rolloutPercentage: 25, + allowedUsers: ["beta-testers"], + }, +}; + +function isFeatureEnabled(flag: string, userId: string): boolean { + const config = flags[flag]; + if (!config?.enabled) return false; + + // Check allowed users + if (config.allowedUsers?.includes(userId)) return true; + + // Check rollout percentage + const hash = hashUserId(userId); + return hash < config.rolloutPercentage; +} + +// Usage +if (isFeatureEnabled("newCheckoutFlow", user.id)) { + return ; +} +return ; +``` + +--- + +## Monitoring and Observability + +### Structured Logging + +```typescript +import pino from "pino"; + +const logger = pino({ + level: process.env.LOG_LEVEL || "info", + formatters: { + level: (label) => ({ level: label }), + }, +}); + +// Request logging middleware +app.use((req, res, next) => { + const start = Date.now(); + const requestId = req.headers["x-request-id"] || crypto.randomUUID(); + + res.on("finish", () => { + logger.info({ + type: "request", + requestId, + method: req.method, + path: req.path, + statusCode: res.statusCode, + duration: Date.now() - start, + userAgent: req.headers["user-agent"], + }); + }); + + next(); +}); + +// Application logging +logger.info({ userId: user.id, action: "login" }, "User logged in"); +logger.error({ err, orderId }, "Failed to process order"); +``` + +### Metrics Collection + +```typescript +import { Counter, Histogram } from "prom-client"; + +const httpRequestsTotal = new Counter({ + name: "http_requests_total", + help: "Total HTTP requests", + labelNames: ["method", "path", "status"], +}); + +const httpRequestDuration = new Histogram({ + name: "http_request_duration_seconds", + help: "HTTP request duration", + labelNames: ["method", "path"], + buckets: [0.1, 0.3, 0.5, 1, 3, 5, 10], +}); + +// Middleware +app.use((req, res, next) => { + const end = httpRequestDuration.startTimer({ + method: req.method, + path: req.route?.path || req.path, + }); + + res.on("finish", () => { + httpRequestsTotal.inc({ + method: req.method, + path: req.route?.path || req.path, + status: res.statusCode, + }); + end(); + }); + + next(); +}); +``` + +### Health Checks + +```typescript +app.get("/health", async (req, res) => { + const checks = { + database: await checkDatabase(), + redis: await checkRedis(), + memory: checkMemory(), + }; + + const healthy = Object.values(checks).every((c) => c.status === "healthy"); + + res.status(healthy ? 200 : 503).json({ + status: healthy ? "healthy" : "unhealthy", + checks, + timestamp: new Date().toISOString(), + }); +}); + +async function checkDatabase() { + try { + await db.$queryRaw`SELECT 1`; + return { status: "healthy" }; + } catch (error) { + return { status: "unhealthy", error: error.message }; + } +} + +function checkMemory() { + const used = process.memoryUsage(); + const heapUsedMB = Math.round(used.heapUsed / 1024 / 1024); + const heapTotalMB = Math.round(used.heapTotal / 1024 / 1024); + + return { + status: heapUsedMB < heapTotalMB * 0.9 ? "healthy" : "warning", + heapUsedMB, + heapTotalMB, + }; } ``` -## Guidelines +--- -### Code Organization -- Clear structure -- Logical separation -- Consistent naming -- Proper documentation +## Quick Reference -### Performance Considerations -- Optimization strategies -- Bottleneck identification -- Monitoring approaches -- Scaling techniques +### Daily Workflow -### Security Best Practices -- Input validation -- Authentication -- Authorization -- Data protection +```bash +# 1. Start work +git checkout main && git pull +git checkout -b feature/my-feature -## Common Patterns +# 2. Develop with hot reload +docker-compose up -d +npm run dev -### Pattern A -Implementation details and examples. +# 3. Test changes +npm run test +npm run lint -### Pattern B -Implementation details and examples. +# 4. Commit +git add -A +git commit -m "feat(scope): description" -### Pattern C -Implementation details and examples. +# 5. Push and create PR +git push -u origin feature/my-feature +gh pr create +``` -## Anti-Patterns to Avoid +### Release Workflow -### Anti-Pattern 1 -What not to do and why. +```bash +# 1. Ensure main is stable +git checkout main +npm run test:all -### Anti-Pattern 2 -What not to do and why. +# 2. Create release +npm version minor # or major/patch +git push --follow-tags -## Tools and Resources - -### Recommended Tools -- Tool 1: Purpose -- Tool 2: Purpose -- Tool 3: Purpose - -### Further Reading -- Resource 1 -- Resource 2 -- Resource 3 - -## Conclusion - -Key takeaways for using this reference guide effectively. +# 3. Verify deployment +# CI/CD deploys automatically +# Monitor dashboards +``` diff --git a/engineering-team/senior-fullstack/references/tech_stack_guide.md b/engineering-team/senior-fullstack/references/tech_stack_guide.md index 226036f..6764eae 100644 --- a/engineering-team/senior-fullstack/references/tech_stack_guide.md +++ b/engineering-team/senior-fullstack/references/tech_stack_guide.md @@ -1,103 +1,590 @@ -# Tech Stack Guide +# Fullstack Tech Stack Guide -## Overview +Technology selection guide with trade-offs, use cases, and integration patterns for modern fullstack development. -This reference guide provides comprehensive information for senior fullstack. +--- -## Patterns and Practices +## Table of Contents -### Pattern 1: Best Practice Implementation +- [Frontend Frameworks](#frontend-frameworks) +- [Backend Frameworks](#backend-frameworks) +- [Databases](#databases) +- [ORMs and Query Builders](#orms-and-query-builders) +- [Authentication Solutions](#authentication-solutions) +- [Deployment Platforms](#deployment-platforms) +- [Stack Recommendations](#stack-recommendations) -**Description:** -Detailed explanation of the pattern. +--- -**When to Use:** -- Scenario 1 -- Scenario 2 -- Scenario 3 +## Frontend Frameworks + +### Next.js + +**Best for:** Production React apps, SEO-critical sites, full-stack applications + +| Pros | Cons | +|------|------| +| Server components, streaming | Learning curve for advanced features | +| Built-in routing, API routes | Vercel lock-in concerns | +| Excellent DX and performance | Bundle size can grow | +| Strong TypeScript support | Complex mental model (client/server) | + +**When to choose:** +- Need SSR/SSG for SEO +- Building a product that may scale +- Want full-stack in one framework +- Team familiar with React -**Implementation:** ```typescript -// Example code implementation -export class Example { - // Implementation details +// App Router pattern +// app/users/page.tsx +async function UsersPage() { + const users = await db.user.findMany(); // Server component + return ; +} + +// app/users/[id]/page.tsx +export async function generateStaticParams() { + const users = await db.user.findMany(); + return users.map((user) => ({ id: user.id })); } ``` -**Benefits:** -- Benefit 1 -- Benefit 2 -- Benefit 3 +### React + Vite -**Trade-offs:** -- Consider 1 -- Consider 2 -- Consider 3 +**Best for:** SPAs, dashboards, internal tools -### Pattern 2: Advanced Technique +| Pros | Cons | +|------|------| +| Fast development with HMR | No SSR out of the box | +| Simple mental model | Manual routing setup | +| Flexible architecture | No built-in API routes | +| Smaller bundle potential | Need separate backend | -**Description:** -Another important pattern for senior fullstack. +**When to choose:** +- Building internal dashboards +- SEO not important +- Need maximum flexibility +- Prefer decoupled frontend/backend + +### Vue 3 + +**Best for:** Teams transitioning from jQuery, progressive enhancement + +| Pros | Cons | +|------|------| +| Gentle learning curve | Smaller ecosystem than React | +| Excellent documentation | Fewer enterprise adoptions | +| Single-file components | Composition API learning curve | +| Good TypeScript support | Two paradigms (Options/Composition) | + +**When to choose:** +- Team new to modern frameworks +- Progressive enhancement needed +- Prefer official solutions (Pinia, Vue Router) + +### Comparison Matrix + +| Feature | Next.js | React+Vite | Vue 3 | Svelte | +|---------|---------|------------|-------|--------| +| SSR | Built-in | Manual | Nuxt | SvelteKit | +| Bundle size | Medium | Small | Small | Smallest | +| Learning curve | Medium | Low | Low | Low | +| Enterprise adoption | High | High | Medium | Low | +| Job market | Large | Large | Medium | Small | + +--- + +## Backend Frameworks + +### Node.js Ecosystem + +**Express.js** -**Implementation:** ```typescript -// Advanced example -async function advancedExample() { - // Code here +import express from "express"; +import { userRouter } from "./routes/users"; + +const app = express(); +app.use(express.json()); +app.use("/api/users", userRouter); +app.listen(3000); +``` + +| Pros | Cons | +|------|------| +| Minimal, flexible | No structure opinions | +| Huge middleware ecosystem | Callback-based (legacy) | +| Well understood | Manual TypeScript setup | + +**Fastify** + +```typescript +import Fastify from "fastify"; + +const app = Fastify({ logger: true }); + +app.get("/users/:id", { + schema: { + params: { type: "object", properties: { id: { type: "string" } } }, + response: { 200: UserSchema }, + }, + handler: async (request) => { + return db.user.findUnique({ where: { id: request.params.id } }); + }, +}); +``` + +| Pros | Cons | +|------|------| +| High performance | Smaller ecosystem | +| Built-in validation | Different plugin model | +| TypeScript-first | Less community content | + +**NestJS** + +```typescript +@Controller("users") +export class UsersController { + constructor(private usersService: UsersService) {} + + @Get(":id") + findOne(@Param("id") id: string) { + return this.usersService.findOne(id); + } + + @Post() + @UseGuards(AuthGuard) + create(@Body() createUserDto: CreateUserDto) { + return this.usersService.create(createUserDto); + } } ``` -## Guidelines +| Pros | Cons | +|------|------| +| Strong architecture | Steep learning curve | +| Full-featured (GraphQL, WebSockets) | Heavy for small projects | +| Enterprise-ready | Decorator complexity | -### Code Organization -- Clear structure -- Logical separation -- Consistent naming -- Proper documentation +### Python Ecosystem -### Performance Considerations -- Optimization strategies -- Bottleneck identification -- Monitoring approaches -- Scaling techniques +**FastAPI** -### Security Best Practices -- Input validation -- Authentication -- Authorization -- Data protection +```python +from fastapi import FastAPI, Depends +from sqlalchemy.orm import Session -## Common Patterns +app = FastAPI() -### Pattern A -Implementation details and examples. +@app.get("/users/{user_id}", response_model=UserResponse) +async def get_user(user_id: int, db: Session = Depends(get_db)): + user = db.query(User).filter(User.id == user_id).first() + if not user: + raise HTTPException(status_code=404) + return user +``` -### Pattern B -Implementation details and examples. +| Pros | Cons | +|------|------| +| Auto-generated docs | Python GIL limitations | +| Type hints → validation | Async ecosystem maturing | +| High performance | Smaller than Django ecosystem | -### Pattern C -Implementation details and examples. +**Django** -## Anti-Patterns to Avoid +| Pros | Cons | +|------|------| +| Batteries included | Monolithic | +| Admin panel | ORM limitations | +| Mature ecosystem | Async support newer | -### Anti-Pattern 1 -What not to do and why. +### Framework Selection Guide -### Anti-Pattern 2 -What not to do and why. +| Use Case | Recommendation | +|----------|---------------| +| API-first startup | FastAPI or Fastify | +| Enterprise backend | NestJS or Django | +| Microservices | Fastify or Go | +| Rapid prototype | Express or Django | +| Full-stack TypeScript | Next.js API routes | -## Tools and Resources +--- -### Recommended Tools -- Tool 1: Purpose -- Tool 2: Purpose -- Tool 3: Purpose +## Databases -### Further Reading -- Resource 1 -- Resource 2 -- Resource 3 +### PostgreSQL -## Conclusion +**Best for:** Most applications, relational data, ACID compliance -Key takeaways for using this reference guide effectively. +```sql +-- JSON support +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + email VARCHAR(255) UNIQUE NOT NULL, + profile JSONB DEFAULT '{}', + created_at TIMESTAMPTZ DEFAULT NOW() +); + +-- Full-text search +CREATE INDEX users_search_idx ON users + USING GIN (to_tsvector('english', email || ' ' || profile->>'name')); + +SELECT * FROM users +WHERE to_tsvector('english', email || ' ' || profile->>'name') + @@ to_tsquery('john'); +``` + +| Feature | Rating | +|---------|--------| +| ACID compliance | Excellent | +| JSON support | Excellent | +| Full-text search | Good | +| Horizontal scaling | Requires setup | +| Managed options | Many (RDS, Supabase, Neon) | + +### MongoDB + +**Best for:** Document-heavy apps, flexible schemas, rapid prototyping + +```typescript +// Flexible schema +const userSchema = new Schema({ + email: { type: String, required: true, unique: true }, + profile: { + name: String, + preferences: Schema.Types.Mixed, // Any structure + }, + orders: [{ type: Schema.Types.ObjectId, ref: "Order" }], +}); +``` + +| Feature | Rating | +|---------|--------| +| Schema flexibility | Excellent | +| Horizontal scaling | Excellent | +| Transactions | Good (4.0+) | +| Joins | Limited | +| Managed options | Atlas | + +### Redis + +**Best for:** Caching, sessions, real-time features, queues + +```typescript +// Session storage +await redis.set(`session:${sessionId}`, JSON.stringify(user), "EX", 3600); + +// Rate limiting +const requests = await redis.incr(`rate:${ip}`); +if (requests === 1) await redis.expire(`rate:${ip}`, 60); +if (requests > 100) throw new TooManyRequestsError(); + +// Pub/Sub +redis.publish("notifications", JSON.stringify({ userId, message })); +``` + +### Database Selection Matrix + +| Requirement | PostgreSQL | MongoDB | MySQL | +|-------------|-----------|---------|-------| +| Complex queries | Best | Limited | Good | +| Schema flexibility | Good (JSONB) | Best | Limited | +| Transactions | Best | Good | Good | +| Horizontal scale | Manual | Built-in | Manual | +| Cloud managed | Many | Atlas | Many | + +--- + +## ORMs and Query Builders + +### Prisma + +**Best for:** TypeScript projects, schema-first development + +```typescript +// schema.prisma +model User { + id String @id @default(cuid()) + email String @unique + posts Post[] + profile Profile? + createdAt DateTime @default(now()) +} + +// Usage - fully typed +const user = await prisma.user.findUnique({ + where: { email: "user@example.com" }, + include: { posts: true, profile: true }, +}); +// user.posts is Post[] - TypeScript knows +``` + +| Pros | Cons | +|------|------| +| Excellent TypeScript | Generated client size | +| Schema migrations | Limited raw SQL support | +| Visual studio | Some edge case limitations | + +### Drizzle + +**Best for:** SQL-first TypeScript, performance-critical apps + +```typescript +// Schema definition +const users = pgTable("users", { + id: uuid("id").primaryKey().defaultRandom(), + email: varchar("email", { length: 255 }).notNull().unique(), + createdAt: timestamp("created_at").defaultNow(), +}); + +// Query - SQL-like syntax +const result = await db + .select() + .from(users) + .where(eq(users.email, "user@example.com")) + .leftJoin(posts, eq(posts.userId, users.id)); +``` + +| Pros | Cons | +|------|------| +| Lightweight | Newer, smaller community | +| SQL-like syntax | Fewer integrations | +| Fast runtime | Manual migrations | + +### SQLAlchemy (Python) + +```python +# Model definition +class User(Base): + __tablename__ = "users" + + id = Column(UUID, primary_key=True, default=uuid4) + email = Column(String(255), unique=True, nullable=False) + posts = relationship("Post", back_populates="author") + +# Query +users = session.query(User)\ + .filter(User.email.like("%@example.com"))\ + .options(joinedload(User.posts))\ + .all() +``` + +--- + +## Authentication Solutions + +### Auth.js (NextAuth) + +**Best for:** Next.js apps, social logins + +```typescript +// app/api/auth/[...nextauth]/route.ts +import NextAuth from "next-auth"; +import GitHub from "next-auth/providers/github"; +import Credentials from "next-auth/providers/credentials"; + +export const { handlers, auth, signIn, signOut } = NextAuth({ + providers: [ + GitHub, + Credentials({ + credentials: { email: {}, password: {} }, + authorize: async (credentials) => { + const user = await verifyCredentials(credentials); + return user; + }, + }), + ], + callbacks: { + jwt({ token, user }) { + if (user) token.role = user.role; + return token; + }, + }, +}); +``` + +| Pros | Cons | +|------|------| +| Many providers | Next.js focused | +| Session management | Complex customization | +| Database adapters | Breaking changes between versions | + +### Clerk + +**Best for:** Rapid development, hosted solution + +```typescript +// Middleware +import { clerkMiddleware } from "@clerk/nextjs/server"; + +export default clerkMiddleware(); + +// Usage +import { auth } from "@clerk/nextjs/server"; + +export async function GET() { + const { userId } = await auth(); + if (!userId) return new Response("Unauthorized", { status: 401 }); + // ... +} +``` + +| Pros | Cons | +|------|------| +| Beautiful UI components | Vendor lock-in | +| Managed infrastructure | Cost at scale | +| Multi-factor auth | Data residency concerns | + +### Custom JWT + +**Best for:** Full control, microservices + +```typescript +// Token generation +function generateTokens(user: User) { + const accessToken = jwt.sign( + { sub: user.id, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: "15m" } + ); + + const refreshToken = jwt.sign( + { sub: user.id, version: user.tokenVersion }, + process.env.REFRESH_SECRET, + { expiresIn: "7d" } + ); + + return { accessToken, refreshToken }; +} + +// Middleware +function authenticate(req, res, next) { + const token = req.headers.authorization?.replace("Bearer ", ""); + if (!token) return res.status(401).json({ error: "No token" }); + + try { + req.user = jwt.verify(token, process.env.JWT_SECRET); + next(); + } catch { + res.status(401).json({ error: "Invalid token" }); + } +} +``` + +--- + +## Deployment Platforms + +### Vercel + +**Best for:** Next.js, frontend-focused teams + +| Pros | Cons | +|------|------| +| Zero-config Next.js | Expensive at scale | +| Edge functions | Vendor lock-in | +| Preview deployments | Limited backend options | +| Global CDN | Cold starts | + +### Railway + +**Best for:** Full-stack apps, databases included + +| Pros | Cons | +|------|------| +| Simple deployment | Smaller community | +| Built-in databases | Limited regions | +| Good pricing | Fewer integrations | + +### AWS (ECS/Lambda) + +**Best for:** Enterprise, complex requirements + +| Pros | Cons | +|------|------| +| Full control | Complex setup | +| Cost-effective at scale | Steep learning curve | +| Any technology | Requires DevOps knowledge | + +### Deployment Selection + +| Requirement | Platform | +|-------------|----------| +| Next.js simplicity | Vercel | +| Full-stack + DB | Railway, Render | +| Enterprise scale | AWS, GCP | +| Container control | Fly.io, Railway | +| Budget startup | Railway, Render | + +--- + +## Stack Recommendations + +### Startup MVP + +``` +Frontend: Next.js 14 (App Router) +Backend: Next.js API Routes +Database: PostgreSQL (Neon/Supabase) +Auth: Auth.js or Clerk +Deploy: Vercel +Cache: Vercel KV or Upstash Redis +``` + +**Why:** Fastest time to market, single deployment, good scaling path. + +### SaaS Product + +``` +Frontend: Next.js 14 +Backend: Separate API (FastAPI or NestJS) +Database: PostgreSQL (RDS) +Auth: Custom JWT + Auth.js +Deploy: Vercel (frontend) + AWS ECS (backend) +Cache: Redis (ElastiCache) +Queue: SQS or BullMQ +``` + +**Why:** Separation allows independent scaling, team specialization. + +### Enterprise Application + +``` +Frontend: Next.js or React + Vite +Backend: NestJS or Go +Database: PostgreSQL (Aurora) +Auth: Keycloak or Auth0 +Deploy: Kubernetes (EKS/GKE) +Cache: Redis Cluster +Queue: Kafka or RabbitMQ +Observability: Datadog or Grafana Stack +``` + +**Why:** Maximum control, compliance requirements, team expertise. + +### Internal Tool + +``` +Frontend: React + Vite + Tailwind +Backend: Express or FastAPI +Database: PostgreSQL or SQLite +Auth: OIDC with corporate IdP +Deploy: Docker on internal infrastructure +``` + +**Why:** Simple, low maintenance, integrates with existing systems. + +--- + +## Quick Decision Guide + +| Question | If Yes → | If No → | +|----------|----------|---------| +| Need SEO? | Next.js SSR | React SPA | +| Complex backend? | Separate API | Next.js routes | +| Team knows Python? | FastAPI | Node.js | +| Need real-time? | Add WebSockets | REST is fine | +| Enterprise compliance? | Self-hosted | Managed services | +| Budget constrained? | Railway/Render | Vercel/AWS | +| Schema changes often? | MongoDB | PostgreSQL | diff --git a/engineering-team/senior-fullstack/scripts/code_quality_analyzer.py b/engineering-team/senior-fullstack/scripts/code_quality_analyzer.py index 1ddfaa7..bc2bec5 100755 --- a/engineering-team/senior-fullstack/scripts/code_quality_analyzer.py +++ b/engineering-team/senior-fullstack/scripts/code_quality_analyzer.py @@ -1,114 +1,691 @@ #!/usr/bin/env python3 """ Code Quality Analyzer -Automated tool for senior fullstack tasks + +Analyzes fullstack codebases for quality issues including: +- Code complexity metrics (cyclomatic complexity, cognitive complexity) +- Security vulnerabilities (hardcoded secrets, injection patterns) +- Dependency health (outdated packages, known vulnerabilities) +- Test coverage estimation +- Documentation quality + +Usage: + python code_quality_analyzer.py /path/to/project + python code_quality_analyzer.py . --json + python code_quality_analyzer.py /path/to/project --output report.json """ -import os -import sys -import json import argparse +import json +import os +import re +import sys +from collections import defaultdict from pathlib import Path -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Tuple -class CodeQualityAnalyzer: - """Main class for code quality analyzer functionality""" - - def __init__(self, target_path: str, verbose: bool = False): - self.target_path = Path(target_path) - self.verbose = verbose - self.results = {} - - def run(self) -> Dict: - """Execute the main functionality""" - print(f"🚀 Running {self.__class__.__name__}...") - print(f"📁 Target: {self.target_path}") - + +# File extensions to analyze +FRONTEND_EXTENSIONS = {".ts", ".tsx", ".js", ".jsx", ".vue", ".svelte"} +BACKEND_EXTENSIONS = {".py", ".go", ".java", ".rb", ".php", ".cs"} +CONFIG_EXTENSIONS = {".json", ".yaml", ".yml", ".toml", ".env"} +ALL_CODE_EXTENSIONS = FRONTEND_EXTENSIONS | BACKEND_EXTENSIONS + +# Skip patterns +SKIP_DIRS = {"node_modules", "vendor", ".git", "__pycache__", "dist", "build", + ".next", ".venv", "venv", "env", "coverage", ".pytest_cache"} + +# Security patterns to detect +SECURITY_PATTERNS = { + "hardcoded_secret": { + "pattern": r"(?:password|secret|api_key|apikey|token|auth)[\s]*[=:][\s]*['\"][^'\"]{8,}['\"]", + "severity": "critical", + "message": "Potential hardcoded secret detected" + }, + "sql_injection": { + "pattern": r"(?:execute|query|raw)\s*\(\s*[f'\"].*\{.*\}|%s|%d|\$\d", + "severity": "high", + "message": "Potential SQL injection vulnerability" + }, + "xss_vulnerable": { + "pattern": r"innerHTML\s*=|v-html", + "severity": "medium", + "message": "Potential XSS vulnerability - unescaped HTML rendering" + }, + "unsafe_react_html": { + "pattern": r"__html", + "severity": "medium", + "message": "React unsafe HTML pattern detected - ensure content is sanitized" + }, + "insecure_protocol": { + "pattern": r"http://(?!localhost|127\.0\.0\.1)", + "severity": "medium", + "message": "Insecure HTTP protocol used" + }, + "debug_code": { + "pattern": r"console\.log|print\(|debugger|DEBUG\s*=\s*True", + "severity": "low", + "message": "Debug code should be removed in production" + }, + "todo_fixme": { + "pattern": r"(?:TODO|FIXME|HACK|XXX):", + "severity": "info", + "message": "Unresolved TODO/FIXME comment" + } +} + +# Code smell patterns +CODE_SMELL_PATTERNS = { + "long_function": { + "description": "Function exceeds recommended length", + "threshold": 50 + }, + "deep_nesting": { + "description": "Excessive nesting depth", + "threshold": 4 + }, + "large_file": { + "description": "File exceeds recommended size", + "threshold": 500 + }, + "magic_number": { + "pattern": r"(? bool: + """Check if path should be skipped.""" + return any(skip in path.parts for skip in SKIP_DIRS) + + +def count_lines(filepath: Path) -> Tuple[int, int, int]: + """Count total lines, code lines, and comment lines.""" + try: + with open(filepath, "r", encoding="utf-8", errors="ignore") as f: + lines = f.readlines() + except Exception: + return 0, 0, 0 + + total = len(lines) + code = 0 + comments = 0 + in_block_comment = False + + for line in lines: + stripped = line.strip() + if not stripped: + continue + + # Block comments + if "/*" in stripped: + in_block_comment = True + if in_block_comment: + comments += 1 + if "*/" in stripped: + in_block_comment = False + continue + + # Line comments + if stripped.startswith(("//", "#", "--", "'")): + comments += 1 + else: + code += 1 + + return total, code, comments + + +def calculate_complexity(content: str, language: str) -> Dict: + """Calculate cyclomatic complexity estimate.""" + # Count decision points + decision_patterns = [ + r"\bif\b", r"\belse\b", r"\belif\b", r"\bfor\b", r"\bwhile\b", + r"\bcase\b", r"\bcatch\b", r"\b\?\b", r"\b&&\b", r"\b\|\|\b", + r"\band\b", r"\bor\b" + ] + + complexity = 1 # Base complexity + for pattern in decision_patterns: + complexity += len(re.findall(pattern, content, re.IGNORECASE)) + + # Count nesting depth + max_depth = 0 + current_depth = 0 + for char in content: + if char == "{": + current_depth += 1 + max_depth = max(max_depth, current_depth) + elif char == "}": + current_depth = max(0, current_depth - 1) + + return { + "cyclomatic": complexity, + "max_nesting": max_depth, + "rating": "low" if complexity < 10 else "medium" if complexity < 20 else "high" + } + + +def analyze_security(filepath: Path, content: str) -> List[Dict]: + """Scan for security issues.""" + issues = [] + lines = content.split("\n") + + for pattern_name, pattern_info in SECURITY_PATTERNS.items(): + regex = re.compile(pattern_info["pattern"], re.IGNORECASE) + for line_num, line in enumerate(lines, 1): + if regex.search(line): + issues.append({ + "file": str(filepath), + "line": line_num, + "type": pattern_name, + "severity": pattern_info["severity"], + "message": pattern_info["message"] + }) + + return issues + + +def analyze_dependencies(project_path: Path) -> Dict: + """Analyze project dependencies for issues.""" + findings = { + "package_managers": [], + "total_deps": 0, + "outdated": [], + "vulnerable": [], + "recommendations": [] + } + + # Check package.json + package_json = project_path / "package.json" + if package_json.exists(): + findings["package_managers"].append("npm") try: - self.validate_target() - self.analyze() - self.generate_report() - - print("✅ Completed successfully!") - return self.results - - except Exception as e: - print(f"❌ Error: {e}") - sys.exit(1) - - def validate_target(self): - """Validate the target path exists and is accessible""" - if not self.target_path.exists(): - raise ValueError(f"Target path does not exist: {self.target_path}") - - if self.verbose: - print(f"✓ Target validated: {self.target_path}") - - def analyze(self): - """Perform the main analysis or operation""" - if self.verbose: - print("📊 Analyzing...") - - # Main logic here - self.results['status'] = 'success' - self.results['target'] = str(self.target_path) - self.results['findings'] = [] - - # Add analysis results - if self.verbose: - print(f"✓ Analysis complete: {len(self.results.get('findings', []))} findings") - - def generate_report(self): - """Generate and display the report""" - print("\n" + "="*50) - print("REPORT") - print("="*50) - print(f"Target: {self.results.get('target')}") - print(f"Status: {self.results.get('status')}") - print(f"Findings: {len(self.results.get('findings', []))}") - print("="*50 + "\n") + with open(package_json) as f: + pkg = json.load(f) + deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})} + findings["total_deps"] += len(deps) + + for dep, version in deps.items(): + # Check against known vulnerabilities + if dep in KNOWN_VULNERABLE_DEPS: + vuln = KNOWN_VULNERABLE_DEPS[dep] + # Simplified version check + clean_version = re.sub(r"[^\d.]", "", version) + if clean_version and clean_version < vuln["vulnerable_below"]: + findings["vulnerable"].append({ + "package": dep, + "current": version, + "fix_version": vuln["vulnerable_below"], + "cve": vuln["cve"] + }) + except Exception: + pass + + # Check requirements.txt + requirements = project_path / "requirements.txt" + if requirements.exists(): + findings["package_managers"].append("pip") + try: + with open(requirements) as f: + lines = [l.strip() for l in f if l.strip() and not l.startswith("#")] + findings["total_deps"] += len(lines) + except Exception: + pass + + # Check go.mod + go_mod = project_path / "go.mod" + if go_mod.exists(): + findings["package_managers"].append("go") + + return findings + + +def analyze_test_coverage(project_path: Path) -> Dict: + """Estimate test coverage based on file analysis.""" + test_files = [] + source_files = [] + + for filepath in project_path.rglob("*"): + if should_skip(filepath) or not filepath.is_file(): + continue + + if filepath.suffix in ALL_CODE_EXTENSIONS: + name = filepath.stem.lower() + if "test" in name or "spec" in name or "_test" in name: + test_files.append(filepath) + elif not name.startswith("_"): + source_files.append(filepath) + + source_count = len(source_files) + test_count = len(test_files) + + # Estimate coverage ratio + if source_count == 0: + ratio = 0 + else: + ratio = min(100, int((test_count / source_count) * 100)) + + return { + "source_files": source_count, + "test_files": test_count, + "estimated_coverage": ratio, + "rating": "good" if ratio >= 70 else "adequate" if ratio >= 40 else "poor", + "recommendation": None if ratio >= 70 else f"Consider adding more tests ({70 - ratio}% gap to target)" + } + + +def analyze_documentation(project_path: Path) -> Dict: + """Analyze documentation quality.""" + docs = { + "has_readme": False, + "has_contributing": False, + "has_license": False, + "has_changelog": False, + "api_docs": [], + "score": 0 + } + + readme_patterns = ["README.md", "README.rst", "README.txt", "readme.md"] + for pattern in readme_patterns: + if (project_path / pattern).exists(): + docs["has_readme"] = True + docs["score"] += 30 + break + + if (project_path / "CONTRIBUTING.md").exists(): + docs["has_contributing"] = True + docs["score"] += 15 + + license_patterns = ["LICENSE", "LICENSE.md", "LICENSE.txt"] + for pattern in license_patterns: + if (project_path / pattern).exists(): + docs["has_license"] = True + docs["score"] += 15 + break + + changelog_patterns = ["CHANGELOG.md", "HISTORY.md", "CHANGES.md"] + for pattern in changelog_patterns: + if (project_path / pattern).exists(): + docs["has_changelog"] = True + docs["score"] += 10 + break + + # Check for API docs + api_doc_dirs = ["docs", "documentation", "api-docs"] + for doc_dir in api_doc_dirs: + doc_path = project_path / doc_dir + if doc_path.is_dir(): + docs["api_docs"].append(str(doc_path)) + docs["score"] += 30 + break + + return docs + + +def analyze_project(project_path: Path) -> Dict: + """Perform full project analysis.""" + results = { + "summary": { + "files_analyzed": 0, + "total_lines": 0, + "code_lines": 0, + "comment_lines": 0 + }, + "languages": defaultdict(lambda: {"files": 0, "lines": 0}), + "security": { + "critical": [], + "high": [], + "medium": [], + "low": [], + "info": [] + }, + "complexity": { + "high_complexity_files": [], + "average_complexity": 0 + }, + "code_smells": [], + "dependencies": {}, + "tests": {}, + "documentation": {}, + "overall_score": 100 + } + + complexity_scores = [] + security_issues = [] + + # Analyze source files + for filepath in project_path.rglob("*"): + if should_skip(filepath) or not filepath.is_file(): + continue + + if filepath.suffix not in ALL_CODE_EXTENSIONS: + continue + + results["summary"]["files_analyzed"] += 1 + + # Count lines + total, code, comments = count_lines(filepath) + results["summary"]["total_lines"] += total + results["summary"]["code_lines"] += code + results["summary"]["comment_lines"] += comments + + # Track by language + lang = "typescript" if filepath.suffix in {".ts", ".tsx"} else \ + "javascript" if filepath.suffix in {".js", ".jsx"} else \ + "python" if filepath.suffix == ".py" else \ + "go" if filepath.suffix == ".go" else "other" + results["languages"][lang]["files"] += 1 + results["languages"][lang]["lines"] += code + + # Read file content + try: + with open(filepath, "r", encoding="utf-8", errors="ignore") as f: + content = f.read() + except Exception: + continue + + # Complexity analysis + complexity = calculate_complexity(content, lang) + complexity_scores.append(complexity["cyclomatic"]) + if complexity["rating"] == "high": + results["complexity"]["high_complexity_files"].append({ + "file": str(filepath.relative_to(project_path)), + "complexity": complexity["cyclomatic"], + "nesting": complexity["max_nesting"] + }) + + # Security analysis + issues = analyze_security(filepath.relative_to(project_path), content) + security_issues.extend(issues) + + # Code smell: large file + if total > CODE_SMELL_PATTERNS["large_file"]["threshold"]: + results["code_smells"].append({ + "file": str(filepath.relative_to(project_path)), + "type": "large_file", + "details": f"{total} lines (threshold: {CODE_SMELL_PATTERNS['large_file']['threshold']})" + }) + + # Categorize security issues + for issue in security_issues: + severity = issue["severity"] + results["security"][severity].append(issue) + + # Calculate average complexity + if complexity_scores: + results["complexity"]["average_complexity"] = round( + sum(complexity_scores) / len(complexity_scores), 1 + ) + + # Dependency analysis + results["dependencies"] = analyze_dependencies(project_path) + + # Test coverage analysis + results["tests"] = analyze_test_coverage(project_path) + + # Documentation analysis + results["documentation"] = analyze_documentation(project_path) + + # Calculate overall score + score = 100 + + # Deduct for security issues + score -= len(results["security"]["critical"]) * 15 + score -= len(results["security"]["high"]) * 10 + score -= len(results["security"]["medium"]) * 5 + score -= len(results["security"]["low"]) * 2 + + # Deduct for high complexity + score -= len(results["complexity"]["high_complexity_files"]) * 3 + + # Deduct for code smells + score -= len(results["code_smells"]) * 2 + + # Deduct for vulnerable dependencies + score -= len(results["dependencies"].get("vulnerable", [])) * 10 + + # Deduct for poor test coverage + if results["tests"].get("estimated_coverage", 0) < 50: + score -= 15 + elif results["tests"].get("estimated_coverage", 0) < 70: + score -= 5 + + # Deduct for missing documentation + doc_score = results["documentation"].get("score", 0) + if doc_score < 50: + score -= 10 + elif doc_score < 75: + score -= 5 + + results["overall_score"] = max(0, min(100, score)) + results["grade"] = ( + "A" if score >= 90 else + "B" if score >= 80 else + "C" if score >= 70 else + "D" if score >= 60 else "F" + ) + + # Generate recommendations + results["recommendations"] = generate_recommendations(results) + + # Convert defaultdict to regular dict for JSON serialization + results["languages"] = dict(results["languages"]) + + return results + + +def generate_recommendations(analysis: Dict) -> List[Dict]: + """Generate prioritized recommendations.""" + recs = [] + + # Critical security issues + for issue in analysis["security"]["critical"][:3]: + recs.append({ + "priority": "P0", + "category": "security", + "issue": issue["message"], + "file": issue["file"], + "action": f"Remove or secure sensitive data at line {issue['line']}" + }) + + # Vulnerable dependencies + for vuln in analysis["dependencies"].get("vulnerable", [])[:3]: + recs.append({ + "priority": "P0", + "category": "security", + "issue": f"Vulnerable dependency: {vuln['package']} ({vuln['cve']})", + "action": f"Update to version {vuln['fix_version']} or later" + }) + + # High security issues + for issue in analysis["security"]["high"][:3]: + recs.append({ + "priority": "P1", + "category": "security", + "issue": issue["message"], + "file": issue["file"], + "action": "Review and fix security vulnerability" + }) + + # Test coverage + tests = analysis.get("tests", {}) + if tests.get("estimated_coverage", 0) < 50: + recs.append({ + "priority": "P1", + "category": "quality", + "issue": f"Low test coverage: {tests.get('estimated_coverage', 0)}%", + "action": "Add unit tests to improve coverage to at least 70%" + }) + + # High complexity files + for cplx in analysis["complexity"]["high_complexity_files"][:2]: + recs.append({ + "priority": "P2", + "category": "maintainability", + "issue": f"High complexity in {cplx['file']}", + "action": "Refactor to reduce cyclomatic complexity" + }) + + # Documentation + docs = analysis.get("documentation", {}) + if not docs.get("has_readme"): + recs.append({ + "priority": "P2", + "category": "documentation", + "issue": "Missing README.md", + "action": "Add README with project overview and setup instructions" + }) + + return recs[:10] + + +def print_report(analysis: Dict, verbose: bool = False) -> None: + """Print human-readable report.""" + print("=" * 60) + print("CODE QUALITY ANALYSIS REPORT") + print("=" * 60) + print() + + # Summary + summary = analysis["summary"] + print(f"Overall Score: {analysis['overall_score']}/100 (Grade: {analysis['grade']})") + print(f"Files Analyzed: {summary['files_analyzed']}") + print(f"Total Lines: {summary['total_lines']:,}") + print(f"Code Lines: {summary['code_lines']:,}") + print(f"Comment Lines: {summary['comment_lines']:,}") + print() + + # Languages + print("--- LANGUAGES ---") + for lang, stats in analysis["languages"].items(): + print(f" {lang}: {stats['files']} files, {stats['lines']:,} lines") + print() + + # Security + sec = analysis["security"] + total_sec = sum(len(sec[s]) for s in ["critical", "high", "medium", "low"]) + print("--- SECURITY ---") + print(f" Critical: {len(sec['critical'])}") + print(f" High: {len(sec['high'])}") + print(f" Medium: {len(sec['medium'])}") + print(f" Low: {len(sec['low'])}") + if total_sec > 0 and verbose: + print(" Issues:") + for severity in ["critical", "high", "medium"]: + for issue in sec[severity][:3]: + print(f" [{severity.upper()}] {issue['file']}:{issue['line']} - {issue['message']}") + print() + + # Complexity + cplx = analysis["complexity"] + print("--- COMPLEXITY ---") + print(f" Average Complexity: {cplx['average_complexity']}") + print(f" High Complexity Files: {len(cplx['high_complexity_files'])}") + print() + + # Dependencies + deps = analysis["dependencies"] + print("--- DEPENDENCIES ---") + print(f" Package Managers: {', '.join(deps.get('package_managers', ['none']))}") + print(f" Total Dependencies: {deps.get('total_deps', 0)}") + print(f" Vulnerable: {len(deps.get('vulnerable', []))}") + print() + + # Tests + tests = analysis["tests"] + print("--- TEST COVERAGE ---") + print(f" Source Files: {tests.get('source_files', 0)}") + print(f" Test Files: {tests.get('test_files', 0)}") + print(f" Estimated Coverage: {tests.get('estimated_coverage', 0)}% ({tests.get('rating', 'unknown')})") + print() + + # Documentation + docs = analysis["documentation"] + print("--- DOCUMENTATION ---") + print(f" README: {'Yes' if docs.get('has_readme') else 'No'}") + print(f" LICENSE: {'Yes' if docs.get('has_license') else 'No'}") + print(f" CONTRIBUTING: {'Yes' if docs.get('has_contributing') else 'No'}") + print(f" CHANGELOG: {'Yes' if docs.get('has_changelog') else 'No'}") + print(f" Score: {docs.get('score', 0)}/100") + print() + + # Recommendations + if analysis["recommendations"]: + print("--- RECOMMENDATIONS ---") + for i, rec in enumerate(analysis["recommendations"][:10], 1): + print(f"\n{i}. [{rec['priority']}] {rec['category'].upper()}") + print(f" Issue: {rec['issue']}") + print(f" Action: {rec['action']}") + + print() + print("=" * 60) + def main(): - """Main entry point""" parser = argparse.ArgumentParser( - description="Code Quality Analyzer" + description="Analyze fullstack codebase for quality issues", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s /path/to/project + %(prog)s . --verbose + %(prog)s /path/to/project --json --output report.json + """ ) parser.add_argument( - 'target', - help='Target path to analyze or process' + "project_path", + nargs="?", + default=".", + help="Path to project directory (default: current directory)" ) parser.add_argument( - '--verbose', '-v', - action='store_true', - help='Enable verbose output' + "--json", + action="store_true", + help="Output in JSON format" ) parser.add_argument( - '--json', - action='store_true', - help='Output results as JSON' + "--output", "-o", + help="Write output to file" ) parser.add_argument( - '--output', '-o', - help='Output file path' + "--verbose", "-v", + action="store_true", + help="Show detailed findings" ) - + args = parser.parse_args() - - tool = CodeQualityAnalyzer( - args.target, - verbose=args.verbose - ) - - results = tool.run() - + + project_path = Path(args.project_path).resolve() + if not project_path.exists(): + print(f"Error: Path does not exist: {project_path}", file=sys.stderr) + sys.exit(1) + + analysis = analyze_project(project_path) + if args.json: - output = json.dumps(results, indent=2) + output = json.dumps(analysis, indent=2) if args.output: - with open(args.output, 'w') as f: + with open(args.output, "w") as f: f.write(output) - print(f"Results written to {args.output}") + print(f"Report written to {args.output}") else: print(output) + else: + print_report(analysis, args.verbose) + if args.output: + with open(args.output, "w") as f: + json.dump(analysis, f, indent=2) + print(f"\nDetailed JSON report written to {args.output}") -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/engineering-team/senior-fullstack/scripts/fullstack_scaffolder.py b/engineering-team/senior-fullstack/scripts/fullstack_scaffolder.py deleted file mode 100755 index 3f09b5c..0000000 --- a/engineering-team/senior-fullstack/scripts/fullstack_scaffolder.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python3 -""" -Fullstack Scaffolder -Automated tool for senior fullstack tasks -""" - -import os -import sys -import json -import argparse -from pathlib import Path -from typing import Dict, List, Optional - -class FullstackScaffolder: - """Main class for fullstack scaffolder functionality""" - - def __init__(self, target_path: str, verbose: bool = False): - self.target_path = Path(target_path) - self.verbose = verbose - self.results = {} - - def run(self) -> Dict: - """Execute the main functionality""" - print(f"🚀 Running {self.__class__.__name__}...") - print(f"📁 Target: {self.target_path}") - - try: - self.validate_target() - self.analyze() - self.generate_report() - - print("✅ Completed successfully!") - return self.results - - except Exception as e: - print(f"❌ Error: {e}") - sys.exit(1) - - def validate_target(self): - """Validate the target path exists and is accessible""" - if not self.target_path.exists(): - raise ValueError(f"Target path does not exist: {self.target_path}") - - if self.verbose: - print(f"✓ Target validated: {self.target_path}") - - def analyze(self): - """Perform the main analysis or operation""" - if self.verbose: - print("📊 Analyzing...") - - # Main logic here - self.results['status'] = 'success' - self.results['target'] = str(self.target_path) - self.results['findings'] = [] - - # Add analysis results - if self.verbose: - print(f"✓ Analysis complete: {len(self.results.get('findings', []))} findings") - - def generate_report(self): - """Generate and display the report""" - print("\n" + "="*50) - print("REPORT") - print("="*50) - print(f"Target: {self.results.get('target')}") - print(f"Status: {self.results.get('status')}") - print(f"Findings: {len(self.results.get('findings', []))}") - print("="*50 + "\n") - -def main(): - """Main entry point""" - parser = argparse.ArgumentParser( - description="Fullstack Scaffolder" - ) - parser.add_argument( - 'target', - help='Target path to analyze or process' - ) - parser.add_argument( - '--verbose', '-v', - action='store_true', - help='Enable verbose output' - ) - parser.add_argument( - '--json', - action='store_true', - help='Output results as JSON' - ) - parser.add_argument( - '--output', '-o', - help='Output file path' - ) - - args = parser.parse_args() - - tool = FullstackScaffolder( - args.target, - verbose=args.verbose - ) - - results = tool.run() - - if args.json: - output = json.dumps(results, indent=2) - if args.output: - with open(args.output, 'w') as f: - f.write(output) - print(f"Results written to {args.output}") - else: - print(output) - -if __name__ == '__main__': - main() diff --git a/engineering-team/senior-fullstack/scripts/project_scaffolder.py b/engineering-team/senior-fullstack/scripts/project_scaffolder.py index 6a08095..1a102e1 100755 --- a/engineering-team/senior-fullstack/scripts/project_scaffolder.py +++ b/engineering-team/senior-fullstack/scripts/project_scaffolder.py @@ -1,114 +1,867 @@ #!/usr/bin/env python3 """ -Project Scaffolder -Automated tool for senior fullstack tasks +Fullstack Project Scaffolder + +Generates project structure and boilerplate for various fullstack architectures. +Supports Next.js, FastAPI+React, MERN, Django+React, and more. + +Usage: + python project_scaffolder.py nextjs my-app + python project_scaffolder.py fastapi-react my-api --with-docker + python project_scaffolder.py mern my-project --with-auth + python project_scaffolder.py --list-templates """ +import argparse +import json import os import sys -import json -import argparse from pathlib import Path from typing import Dict, List, Optional -class ProjectScaffolder: - """Main class for project scaffolder functionality""" - - def __init__(self, target_path: str, verbose: bool = False): - self.target_path = Path(target_path) - self.verbose = verbose - self.results = {} - - def run(self) -> Dict: - """Execute the main functionality""" - print(f"🚀 Running {self.__class__.__name__}...") - print(f"📁 Target: {self.target_path}") - - try: - self.validate_target() - self.analyze() - self.generate_report() - - print("✅ Completed successfully!") - return self.results - - except Exception as e: - print(f"❌ Error: {e}") - sys.exit(1) - - def validate_target(self): - """Validate the target path exists and is accessible""" - if not self.target_path.exists(): - raise ValueError(f"Target path does not exist: {self.target_path}") - - if self.verbose: - print(f"✓ Target validated: {self.target_path}") - - def analyze(self): - """Perform the main analysis or operation""" - if self.verbose: - print("📊 Analyzing...") - - # Main logic here - self.results['status'] = 'success' - self.results['target'] = str(self.target_path) - self.results['findings'] = [] - - # Add analysis results - if self.verbose: - print(f"✓ Analysis complete: {len(self.results.get('findings', []))} findings") - - def generate_report(self): - """Generate and display the report""" - print("\n" + "="*50) - print("REPORT") - print("="*50) - print(f"Target: {self.results.get('target')}") - print(f"Status: {self.results.get('status')}") - print(f"Findings: {len(self.results.get('findings', []))}") - print("="*50 + "\n") + +# Project templates with file structures +TEMPLATES = { + "nextjs": { + "name": "Next.js Full Stack", + "description": "Next.js 14+ with App Router, TypeScript, Tailwind CSS", + "structure": { + "src/app": ["layout.tsx", "page.tsx", "globals.css", "api/health/route.ts"], + "src/components/ui": ["Button.tsx", "Card.tsx", "Input.tsx"], + "src/components/layout": ["Header.tsx", "Footer.tsx"], + "src/lib": ["utils.ts", "db.ts"], + "src/types": ["index.ts"], + "public": [], + "": ["package.json", "tsconfig.json", "tailwind.config.ts", "next.config.js", + ".env.example", ".gitignore", "README.md"] + } + }, + "fastapi-react": { + "name": "FastAPI + React", + "description": "FastAPI backend with React frontend, PostgreSQL", + "structure": { + "backend/app": ["__init__.py", "main.py", "config.py", "database.py"], + "backend/app/api": ["__init__.py", "routes.py", "deps.py"], + "backend/app/models": ["__init__.py", "user.py"], + "backend/app/schemas": ["__init__.py", "user.py"], + "backend": ["requirements.txt", "alembic.ini", "Dockerfile"], + "frontend/src": ["App.tsx", "main.tsx", "index.css"], + "frontend/src/components": ["Layout.tsx"], + "frontend/src/hooks": ["useApi.ts"], + "frontend": ["package.json", "tsconfig.json", "vite.config.ts", "Dockerfile"], + "": ["docker-compose.yml", ".env.example", ".gitignore", "README.md"] + } + }, + "mern": { + "name": "MERN Stack", + "description": "MongoDB, Express, React, Node.js with TypeScript", + "structure": { + "server/src": ["index.ts", "config.ts", "database.ts"], + "server/src/routes": ["index.ts", "users.ts"], + "server/src/models": ["User.ts"], + "server/src/middleware": ["auth.ts", "error.ts"], + "server": ["package.json", "tsconfig.json", "Dockerfile"], + "client/src": ["App.tsx", "main.tsx"], + "client/src/components": ["Layout.tsx"], + "client/src/services": ["api.ts"], + "client": ["package.json", "tsconfig.json", "vite.config.ts", "Dockerfile"], + "": ["docker-compose.yml", ".env.example", ".gitignore", "README.md"] + } + }, + "django-react": { + "name": "Django + React", + "description": "Django REST Framework backend with React frontend", + "structure": { + "backend/config": ["__init__.py", "settings.py", "urls.py", "wsgi.py"], + "backend/apps/users": ["__init__.py", "models.py", "serializers.py", "views.py", "urls.py"], + "backend": ["manage.py", "requirements.txt", "Dockerfile"], + "frontend/src": ["App.tsx", "main.tsx"], + "frontend/src/components": ["Layout.tsx"], + "frontend": ["package.json", "tsconfig.json", "vite.config.ts", "Dockerfile"], + "": ["docker-compose.yml", ".env.example", ".gitignore", "README.md"] + } + } +} + + +def get_file_content(template: str, filepath: str, project_name: str) -> str: + """Generate file content based on template and file type.""" + filename = Path(filepath).name + + contents = { + # Next.js files + "layout.tsx": f'''import type {{ Metadata }} from "next"; +import "./globals.css"; + +export const metadata: Metadata = {{ + title: "{project_name}", + description: "Generated by project scaffolder", +}}; + +export default function RootLayout({{ + children, +}}: {{ + children: React.ReactNode; +}}) {{ + return ( + + {{children}} + + ); +}} +''', + "page.tsx": f'''export default function Home() {{ + return ( +
+

{project_name}

+

Welcome to your new project.

+
+ ); +}} +''', + "globals.css": '''@tailwind base; +@tailwind components; +@tailwind utilities; +''', + "route.ts": '''import { NextResponse } from "next/server"; + +export async function GET() { + return NextResponse.json({ + status: "healthy", + timestamp: new Date().toISOString(), + }); +} +''', + "Button.tsx": '''import { ButtonHTMLAttributes, forwardRef } from "react"; + +interface ButtonProps extends ButtonHTMLAttributes { + variant?: "primary" | "secondary" | "outline"; + size?: "sm" | "md" | "lg"; +} + +export const Button = forwardRef( + ({ className = "", variant = "primary", size = "md", ...props }, ref) => { + const base = "font-medium rounded-lg transition-colors"; + const variants = { + primary: "bg-blue-600 text-white hover:bg-blue-700", + secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300", + outline: "border border-gray-300 hover:bg-gray-50", + }; + const sizes = { sm: "px-3 py-1.5 text-sm", md: "px-4 py-2", lg: "px-6 py-3 text-lg" }; + return