-
Notifications
You must be signed in to change notification settings - Fork 0
CodeReviewGuidelines
zahra mirkazemi edited this page Dec 7, 2025
·
1 revision
Goal: Ensure code is readable, maintainable, secure, performant, and follows best practices. Code reviews are collaborative—focus on constructive feedback and continuous improvement.
- General Code Quality
- TypeScript & Type Safety
- React Best Practices
- State Management
- Performance
- Imports & Organization
- CSS & Styling
- Security
- Testing
- Accessibility
- Git & Version Control
- Review Etiquette
- ✅ Use meaningful and consistent naming conventions
- ✅ Use UPPER_SNAKE_CASE for constants
- ✅ Use camelCase for variables and functions
- ✅ Use PascalCase for components and classes
- ✅ Use
onprefix for props (e.g.,onClick,onSubmit) - ✅ Use
handleprefix for internal functions (e.g.,handleClick,handleSubmit) - ✅ Use
is/has/shouldfor boolean variables (e.g.,isLoading,hasError,shouldRender) - ❌ Avoid abbreviations unless universally understood (e.g.,
btn→button)
- ✅ Fix all typos (install Code Spell Checker)
- ✅ Add comments just for:
- Complex logic or algorithms
- Non-obvious business rules
- ✅ Keep functions small and focused (Single Responsibility Principle)
- ✅ Break down large components into smaller, reusable components
- ✅ Remove all dead code, unused variables, and functions
- ❌ Don't leave commented-out code (use git history instead)
- ✅ Use strict equality:
===instead of==,!==instead of!= - ✅ Prefer
constoverlet(immutability by default) - ❌ Never use
var - ✅ Use optional chaining (
?.) for potentially null/undefined values, only in cases you have to. - ✅ Use nullish coalescing (
??) instead of||when checking fornull/undefinedspecifically
// ❌ Bad
const value = obj.prop || "default"; // Fails for 0, false, ''
// ✅ Good
const value = obj.prop ?? "default"; // Only fails for null/undefined- ✅ Replace hard-coded values with named constants
- ✅ Group related constants in separate files or enums
// ❌ Bad
if (status === 'pending') { }
// ✅ Good
const ORDER_STATUS = {
PENDING: 'pending',
ACTIVE: 'active',
COMPLETED: 'completed'
} as const;
if (status === ORDER_STATUS.PENDING) { }- ❌ Remove all
console.log,console.warn,debugger, and debugging statements - ✅ Use proper logging library for production (e.g., Sentry, LogRocket)
- ✅ Log meaningful error messages, not just
console.error(e)
- ✅ Always specify types for:
- Props (use
interfaceortype) - State variables
- Function arguments and return values
- API responses
- Props (use
- ✅ Use
interfacefor object shapes,typefor unions/intersections - ❌ Avoid
anytype (useunknownif type is truly unknown, then narrow it) - ✅ Use type imports:
import type { User } from './types' - ✅ Use TypeScript utility types:
Partial<T>,Pick<T>,Omit<T>,Record<K, V>
- ✅ Use type guards for runtime type checking
- ✅ Validate API responses with libraries like Zod or Yup
- ✅ Use functional components with hooks (avoid class components)
- ✅ Keep component files under 250 lines (split if larger)
- ✅ Use self-closing tags when no children:
<Component /> - ✅ Use React Fragment shorthand
<></>(use<Fragment key={id}>when key is needed) - ✅ Extract complex JSX logic into separate components or variables
// ❌ Bad
return (
<div>
{showModal && user && user.isActive && !user.isBanned && <Modal>...</Modal>}
</div>
);
// ✅ Good
const shouldShowModal = showModal && user?.isActive && !user?.isBanned;
return <div>{shouldShowModal && <Modal>...</Modal>}</div>;⚠️ Ensurevalueis boolean before usingvalue && <Component />- Prevents displaying
0,"", orNaNon screen
- Prevents displaying
- ✅ Destructure props in function signature
- ✅ Avoid prop drilling—use Context API or state management for deeply nested data
- ✅ Use
childrenprop for composition - ❌ Don't spread all props blindly:
{...props}(be explicit) - ✅ Define default props using default parameters
- ✅ Avoid defining functions or variables inside
useEffectunless necessary- Define them outside to prevent unnecessary re-renders
- ✅ Add dependencies to
useEffectarray - ✅ Use
useCallbackfor functions passed as props to memoized components - ✅ Use
useMemofor expensive computations - ✅ Clean up side effects in
useEffectreturn function
// ❌ Bad - function recreated on every render
useEffect(() => {
const fetchData = async () => {
/* ... */
};
fetchData();
}, []);
// ✅ Good - function defined outside
const fetchData = useCallback(async () => {
/* ... */
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);- ✅ Always use stable, unique keys for list items
- ❌ Never use array index as key (unless list never reorders)
- ✅ Use item ID or unique identifier
- ✅ Use
useStatefor component-local state - ✅ Use
useReducerfor complex state logic - ✅ Keep state as close to where it's used as possible (avoid lifting unnecessarily)
- ✅ Group related state into objects
// ❌ Bad - multiple related state variables
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [email, setEmail] = useState("");
// ✅ Good - grouped related state
const [user, setUser] = useState({
firstName: "",
lastName: "",
email: "",
});- ✅ Never assume data will be available when component renders
- ✅ Always show loading states until data is validated
// ❌ Bad - can crash if data is undefined
return <div>{data.user.name}</div>;
// ✅ Good - defensive programming
if (isLoading || isFetching || !data) {
return <Loader />;
}
return <div>{data.user.name}</div>;- ✅ Use
React.memofor expensive components that render often - ✅ Use
useMemofor expensive calculations - ✅ Use
useCallbackfor functions passed to memoized children - ✅ Lazy load components:
const Component = lazy(() => import('./Component')) - ✅ Debounce/throttle expensive operations (search inputs, scroll handlers)
- ✅ Use
React.Suspensefor lazy-loaded components - ✅ Virtualize long lists (react-window, react-virtualized)
- ✅ Check bundle size impact of new dependencies
- ✅ Import only what you need:
import { debounce } from 'lodash/debounce'notimport _ from 'lodash' - ✅ Use dynamic imports for large libraries
- ✅ Remove unused dependencies
- ✅ Optimize images (use WebP format, compress)
- ✅ Use lazy loading for images:
<img loading="lazy" /> - ✅ Provide multiple image sizes (responsive images)
- ✅ Use CDN for static assets
- React and third-party libraries
- Modules (actions, hooks, utilities)
- Components
- Types
- Constants
- Styles and icons (at the bottom)
- ✅ Use absolute imports (configured in
tsconfig.json) - ✅ For type imports, always use
import type - ✅ Remove unused imports
- ✅ Do not use units for zero values: use
0, not0remor0px - ✅ Prefer
remand%overpxfor scalability- Exception:
1pxor2pxfor borders/shadows can stay aspx
- Exception:
- ✅ Use
dvhinstead ofvh(fixes mobile viewport bugs - read more) - ✅ Use logical properties for internationalization (RTL support):
-
margin-inline-start/margin-inline-endinstead ofmargin-left/margin-right -
padding-inline-start/padding-inline-endinstead ofpadding-left/padding-right
-
- ❌ Avoid
!importantunless absolutely necessary (document why if used)
- ✅ Use CSS Modules or styled-components for component-scoped styles
- ✅ Avoid global styles unless necessary
- ✅ Always validate API responses contain only necessary data
- ✅ Ensure sensitive information is filtered out before reaching client:
- Authentication tokens
- API keys
- Internal IDs
- Personal identifiable information (PII)
- ✅ Sanitize user input before rendering (prevent XSS)
- ✅ Use
DOMPurifyfor rendering HTML from user input
- ✅ Never store sensitive tokens in localStorage (use httpOnly cookies)
- ✅ Implement proper error handling without exposing system details
- ✅ Check user permissions before rendering sensitive UI
- ✅ Validate file uploads (type, size, content)
- ✅ Keep dependencies up to date
- ✅ Review security advisories (
npm audit,yarn audit) - ✅ Avoid packages with known vulnerabilities
- ✅ Write tests for:
- Critical business logic
- Utility functions
- Complex components
- Edge cases and error states
- ✅ Aim for meaningful coverage, not just high percentages
- ✅ Test user interactions, not implementation details
- ✅ Use React Testing Library (test behavior, not implementation)
- ✅ Write tests that resemble how users interact with your app
- ✅ Mock API calls and external dependencies
- ✅ Test accessibility (screen reader interactions, keyboard navigation)
For accessibility, refer to the Accessibility Guidelines document.
- ✅ Write clear, descriptive commit messages
- ✅ Follow conventional commits format:
type(scope): description- Types:
feat,fix,docs,style,refactor,test,chore
- Types:
- ✅ Reference ticket/issue numbers
- ✅ Keep PRs small and focused (easier to review)
- ✅ Provide clear PR description with:
- What changed and why
- How to test
- Screenshots/videos for UI changes
- Relevant ticket links
- ✅ Self-review before requesting review
- ✅ Resolve merge conflicts before review
- ✅ Ensure CI/CD passes (tests, linting, build)
- ✅ Use descriptive branch names:
feature/user-auth,fix/login-bug - ✅ Delete branches after merging
- ✅ Keep branches up to date with main/develop
- ✅ Be constructive and respectful in feedback
- ✅ Explain why something should be changed
- ✅ Differentiate between:
- Blocking issues (must fix): bugs, security issues, breaking changes
- Suggestions (nice to have): style preferences, optimizations
- ✅ Praise good code and improvements
- ✅ Use prefixes:
[nit],[question],[suggestion],[blocking] - ✅ Review promptly (within 24 hours when possible)
- ❌ Don't bikeshed (argue over trivial matters)
// ❌ Bad feedback
"This is wrong."
// ✅ Good feedback
"[suggestion] Consider using `useMemo` here to avoid recalculating on every render.
This function is called frequently and the computation is expensive."
- ✅ Respond to all comments (even if just "Done" or "Acknowledged") (mostly I prefer add ✅ emoji for the comment!)
- ✅ Ask questions if feedback is unclear
- ✅ Be open to feedback—it's not personal
- ✅ Update PR description as you make changes
- ✅ Mark conversations as resolved after addressing
- ✅ Thank reviewers for their time
Use this checklist when reviewing PRs:
- Code does what it's supposed to do
- Edge cases are handled
- Error states are handled gracefully
- Loading states are shown appropriately
- Code is readable and maintainable
- No code duplication (DRY principle)
- Functions are small and focused
- Naming is clear and consistent
- No commented-out code
- No console logs or debuggers
- All props, state, and functions are properly typed
- No
anytypes (useunknownif needed) - Type imports use
import type
- Components are appropriately sized
- Hooks follow rules and best practices
- Keys are used correctly in lists
- Conditional rendering is safe (no
0or""displayed) - No unnecessary re-renders
- No obvious performance issues
- Expensive operations are memoized/optimized
- Images are optimized
- Bundle size impact is acceptable
- CSS follows project conventions
- Uses CSS variables where appropriate
- Responsive design works on all screen sizes
- RTL support (if applicable)
- No sensitive data exposed
- User input is validated and sanitized
- API responses are validated
- Keyboard navigation works
- Screen reader compatible
- Sufficient color contrast
- Semantic HTML used
- New functionality has tests
- Tests are meaningful and pass
- Edge cases are tested
- Complex logic is commented
- README updated if needed
- API changes are documented
Remember: The goal of code review is not just to catch bugs, but to:
- Ensure code quality and maintainability
- Share knowledge across the team
- Maintain consistency in the codebase
- Improve as developers together
Let's keep learning and improving together! 🚀