// Code review guidelines focusing on quality, maintainability, and SOLID principles. Covers readability, performance, security, testing, and professional code review practices for JavaScript and web applications.
| name | code-reviewer |
| description | Code review guidelines focusing on quality, maintainability, and SOLID principles. Covers readability, performance, security, testing, and professional code review practices for JavaScript and web applications. |
Version: 1.0
Focus: Code quality, maintainability, and best practices
Purpose: Review code professionally and improve codebase quality
Code is written once, read many times.
Good code:
Bad code:
Primary goals:
Not the goal:
Before approving:
Variables: Descriptive nouns
// โ Bad
const d = new Date()
const x = users.filter(u => u.a)
// โ
Good
const currentDate = new Date()
const activeUsers = users.filter(user => user.isActive)
Functions: Verb phrases
// โ Bad
function user(id) { ... }
function data() { ... }
// โ
Good
function getUser(id) { ... }
function fetchData() { ... }
Constants: SCREAMING_SNAKE_CASE
// โ Bad
const maxretries = 3
const apiurl = 'https://api.example.com'
// โ
Good
const MAX_RETRIES = 3
const API_URL = 'https://api.example.com'
Booleans: Question words (is, has, can, should)
// โ Bad
const loading = true
const admin = false
// โ
Good
const isLoading = true
const hasPermission = false
const canEdit = true
const shouldRetry = false
Rule: Functions should do ONE thing well.
// โ Bad: Too long, does multiple things
function processUserData(user) {
// Validate (50 lines)
if (!user) throw new Error('...')
if (!user.email) throw new Error('...')
// ... more validation
// Transform (50 lines)
const normalized = {
email: user.email.toLowerCase(),
// ... more transformation
}
// Save (50 lines)
const saved = await db.save(normalized)
// ... more saving logic
// Send email (50 lines)
await sendEmail(user.email, ...)
// ... more email logic
return saved
}
// โ
Good: Split into focused functions
function processUserData(user) {
validateUser(user)
const normalized = normalizeUserData(user)
const saved = await saveUser(normalized)
await notifyUser(saved)
return saved
}
// โ Bad: Deep nesting (hard to read)
function processOrder(order) {
if (order) {
if (order.items) {
if (order.items.length > 0) {
if (order.status === 'pending') {
// Process order
}
}
}
}
}
// โ
Good: Early returns
function processOrder(order) {
if (!order) return
if (!order.items || order.items.length === 0) return
if (order.status !== 'pending') return
// Process order
}
When to comment:
When NOT to comment:
// โ Bad: Obvious comment
// Set name to user's name
const name = user.name
// Increment counter by 1
counter++
// โ
Good: Explains WHY
// Use exponential backoff to avoid overwhelming API
const delay = Math.pow(2, retryCount) * 1000
// TODO: Optimize this query (currently O(nยฒ))
for (const user of users) {
for (const post of posts) { ... }
}
Each class/function should have ONE reason to change.
// โ Bad: User class does too much
class User {
constructor(name, email) {
this.name = name
this.email = email
}
save() { /* Database logic */ }
sendEmail() { /* Email logic */ }
generateReport() { /* Report logic */ }
}
// โ
Good: Separate responsibilities
class User {
constructor(name, email) {
this.name = name
this.email = email
}
}
class UserRepository {
save(user) { /* Database logic */ }
}
class EmailService {
sendWelcome(user) { /* Email logic */ }
}
Open for extension, closed for modification.
// โ Bad: Must modify function to add new payment types
function processPayment(type, amount) {
if (type === 'credit_card') {
// Process credit card
} else if (type === 'paypal') {
// Process PayPal
}
// Adding new type requires modifying this function
}
// โ
Good: Extensible without modification
class PaymentProcessor {
constructor() {
this.processors = new Map()
}
register(type, processor) {
this.processors.set(type, processor)
}
process(type, amount) {
const processor = this.processors.get(type)
return processor.process(amount)
}
}
// Add new payment types without modifying core code
paymentProcessor.register('credit_card', new CreditCardProcessor())
paymentProcessor.register('paypal', new PayPalProcessor())
Subtypes must be substitutable for base types.
// โ Bad: Square violates expectations of Rectangle
class Rectangle {
setWidth(w) { this.width = w }
setHeight(h) { this.height = h }
getArea() { return this.width * this.height }
}
class Square extends Rectangle {
setWidth(w) {
this.width = w
this.height = w // Violates Rectangle contract!
}
}
// โ
Good: Composition over inheritance
class Rectangle {
constructor(width, height) {
this.width = width
this.height = height
}
getArea() { return this.width * this.height }
}
class Square {
constructor(side) {
this.side = side
}
getArea() { return this.side * this.side }
}
Clients shouldn't depend on interfaces they don't use.
// โ Bad: Fat interface
interface Worker {
work()
eat()
sleep()
}
class Robot implements Worker {
work() { ... }
eat() { throw new Error('Robots don't eat!') }
sleep() { throw new Error('Robots don't sleep!') }
}
// โ
Good: Split interfaces
interface Workable {
work()
}
interface Eatable {
eat()
}
class Human implements Workable, Eatable {
work() { ... }
eat() { ... }
}
class Robot implements Workable {
work() { ... }
}
Depend on abstractions, not concretions.
// โ Bad: High-level depends on low-level
class EmailService {
send(to, subject, body) {
// Directly uses Gmail API
const gmail = new GmailAPI()
gmail.send(to, subject, body)
}
}
// โ
Good: Depends on abstraction
class EmailService {
constructor(emailProvider) {
this.provider = emailProvider // Abstraction
}
send(to, subject, body) {
this.provider.send(to, subject, body)
}
}
// Can swap providers easily
const gmailService = new EmailService(new GmailProvider())
const sendgridService = new EmailService(new SendGridProvider())
// โ Bad
if (user.age > 18) { ... }
setTimeout(retry, 5000)
// โ
Good
const LEGAL_AGE = 18
if (user.age > LEGAL_AGE) { ... }
const RETRY_DELAY_MS = 5000
setTimeout(retry, RETRY_DELAY_MS)
// โ Bad: Does everything
class Application {
handleRequest() { ... }
connectDatabase() { ... }
sendEmail() { ... }
generateReport() { ... }
processPayment() { ... }
// ... 50 more methods
}
// โ
Good: Focused classes
class Router { handleRequest() { ... } }
class Database { connect() { ... } }
class EmailService { send() { ... } }
// โ Bad: Repeated logic
function calculateDiscountA(price) {
return price * 0.9
}
function calculateDiscountB(price) {
return price * 0.9
}
// โ
Good: DRY (Don't Repeat Yourself)
function calculateDiscount(price, discountPercent) {
return price * (1 - discountPercent / 100)
}
const priceA = calculateDiscount(100, 10)
const priceB = calculateDiscount(200, 10)
// โ Bad: Too many parameters
function createUser(name, email, age, address, phone, company, role) {
// ...
}
// โ
Good: Use object parameter
function createUser({ name, email, age, address, phone, company, role }) {
// Destructure what you need
}
// Or even better: Use a class/type
interface UserParams {
name: string
email: string
age: number
// ...
}
function createUser(params: UserParams) {
// ...
}
// โ Vulnerable
const query = `SELECT * FROM users WHERE id = ${userId}`
// โ
Safe: Parameterized queries
const query = 'SELECT * FROM users WHERE id = ?'
db.query(query, [userId])
// โ Vulnerable
element.innerHTML = userInput
// โ
Safe: Sanitize or use textContent
element.textContent = userInput
// Or use DOMPurify
element.innerHTML = DOMPurify.sanitize(userInput)
// โ Bad: Plain text password
const user = { password: 'secret123' }
// โ
Good: Hashed password
const bcrypt = require('bcrypt')
const hashedPassword = await bcrypt.hash('secret123', 10)
const user = { passwordHash: hashedPassword }
// โ Bad: No authorization check
app.delete('/users/:id', async (req, res) => {
await db.deleteUser(req.params.id)
res.json({ success: true })
})
// โ
Good: Verify ownership/permissions
app.delete('/users/:id', async (req, res) => {
const user = await db.getUser(req.params.id)
if (user.id !== req.user.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' })
}
await db.deleteUser(req.params.id)
res.json({ success: true })
})
// โ Bad: N+1 queries
const users = await db.getUsers()
for (const user of users) {
user.posts = await db.getPosts(user.id) // N queries!
}
// โ
Good: Single query with JOIN
const users = await db.getUsersWithPosts()
// โ Bad: Creates new object every render
function Component() {
const style = { color: 'red' } // New object!
return <div style={style}>Hello</div>
}
// โ
Good: Memoize or move outside
const STYLE = { color: 'red' }
function Component() {
return <div style={STYLE}>Hello</div>
}
// โ Bad: Import entire library
import _ from 'lodash'
// โ
Good: Import only what you need
import debounce from 'lodash/debounce'
// โ Bad: No tests
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0)
}
// โ
Good: Tested
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0)
}
test('calculateTotal sums item prices', () => {
const items = [
{ price: 10 },
{ price: 20 },
{ price: 30 }
]
expect(calculateTotal(items)).toBe(60)
})
test('calculateTotal handles empty array', () => {
expect(calculateTotal([])).toBe(0)
})
Good tests are:
โ Bad feedback:
"This code is terrible."
โ Good feedback:
"This function is doing multiple things. Consider splitting it into
validateUser()andsaveUser()for better maintainability."
โ Vague:
"This could be better."
โ Specific:
"This O(nยฒ) loop could be optimized to O(n) by using a Map for lookups instead of repeated
array.find()."
โ Just criticism:
"Don't use
var."
โ Explain reasoning:
"Use
constorletinstead ofvar.varhas function scope which can lead to unexpected behavior due to hoisting.const/lethave block scope which is more predictable."
Use labels: