Skip to content

Code Style Guide

Follow these standards to keep the codebase consistent and maintainable.

  1. Clarity over cleverness - Write code that’s easy to understand
  2. Consistency - Follow existing patterns in the codebase
  3. Simplicity - Don’t over-engineer solutions
  4. Document intent - Comments explain “why”, not “what”

We use Prettier with these settings:

{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}
// Variables and functions: camelCase
const userName = 'John';
function getUserData() { }
// Constants: UPPER_SNAKE_CASE
const MAX_RETRIES = 3;
// Classes and types: PascalCase
class UserService { }
interface UserProfile { }
type ResponseData = { };
// Files: kebab-case
user-profile.tsx
api-client.ts
// React components: PascalCase
UserProfile.tsx
// Prefer interfaces for objects
interface User {
id: string;
name: string;
email: string;
}
// Use type for unions/intersections
type Status = 'pending' | 'active' | 'deleted';
// Always type function parameters and returns
function getUser(id: string): Promise<User | null> {
// ...
}
// Avoid `any` - use `unknown` if type is truly unknown
function processData(data: unknown) {
if (typeof data === 'string') {
// TypeScript now knows data is string
}
}
// Prefer arrow functions for callbacks
const users = data.map((item) => item.user);
// Use regular functions for hoisting
function handleSubmit() { }
// Keep functions small and focused
// If a function is > 30 lines, consider splitting it
// Document complex functions
/**
* Fetches scholarship data from the API
* @param source - Scholarship source key
* @param options - Filter options
* @returns Array of scholarships or null on error
*/
async function fetchScholarships(
source: string,
options?: FilterOptions
): Promise<Scholarship[] | null> {
// ...
}
// Prefer async/await over .then()
async function getData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Failed to fetch:', error);
return null;
}
}
// Use Promise.all for parallel operations
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts()
]);
// Use function components with TypeScript
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
export function Button({ label, onClick, disabled = false }: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
className="btn-primary"
>
{label}
</button>
);
}
// Custom hooks start with "use"
function useScholarships(source: string) {
const [data, setData] = useState<Scholarship[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
// ...
}, [source]);
return { data, loading, error };
}
// Keep state as local as possible
// Lift state only when necessary
// Use URL state for shareable UI state (filters, pagination)
<!-- Order: layout → sizing → spacing → typography → colors → effects -->
<div class="
flex items-center justify-between
w-full max-w-4xl
p-4 mx-auto
text-sm font-medium
bg-white text-gray-900
rounded-lg shadow-md
">
/* Use @apply sparingly - prefer Tailwind utilities */
.glass-card {
@apply bg-white/80 backdrop-blur-md border border-white/20 rounded-lg;
}
<!-- Mobile-first approach -->
<div class="
flex flex-col
md:flex-row
gap-4
md:gap-8
">
---
// Imports
import Layout from '../layouts/Layout.astro';
import Button from '../components/Button';
// Props interface
interface Props {
title: string;
}
// Props and logic
const { title } = Astro.props;
const data = await fetchData();
---
<!-- Template -->
<Layout title={title}>
<main>
<h1>{title}</h1>
<Button client:load />
</main>
</Layout>
<style>
/* Scoped styles */
</style>
<!-- Only hydrate when visible -->
<HeavyComponent client:visible />
<!-- Hydrate on page load -->
<InteractiveComponent client:load />
<!-- Hydrate when idle -->
<LowPriorityComponent client:idle />
// Always return JSON
function jsonResponse(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: { 'Content-Type': 'application/json' }
});
}
// Consistent error format
return jsonResponse({
error: 'Not found',
code: 'NOT_FOUND'
}, 404);
try {
const data = await riskyOperation();
return jsonResponse(data);
} catch (error) {
console.error('Operation failed:', error);
return jsonResponse({
error: 'Internal server error',
message: process.env.NODE_ENV === 'development' ? error.message : undefined
}, 500);
}
<type>(<scope>): <description>
[body]
[footer]

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • style: Formatting (no code change)
  • refactor: Code restructuring
  • test: Tests
  • chore: Build/config changes

Examples:

feat(web): Add scholarship deadline reminders
fix(api): Handle empty roster response
docs: Update API reference
refactor(web): Extract scholarship filters to hook
src/
├── components/
│ ├── ui/ # Reusable UI components
│ ├── layout/ # Layout components
│ └── features/ # Feature-specific components
├── pages/ # Route pages
├── lib/ # Utilities, API clients
├── types/ # TypeScript types
└── styles/ # Global styles
// Descriptive test names
describe('ScholarshipCard', () => {
it('displays the scholarship title', () => { });
it('shows deadline in correct format', () => { });
it('hides expired scholarships by default', () => { });
});
// Arrange-Act-Assert pattern
it('filters scholarships by GPA', () => {
// Arrange
const scholarships = mockScholarships;
// Act
const filtered = filterByGpa(scholarships, 3.5);
// Assert
expect(filtered).toHaveLength(5);
expect(filtered.every(s => s.gpaRequirement <= 3.5)).toBe(true);
});

Before submitting code:

  • Code follows style guide
  • TypeScript has no errors
  • Linting passes
  • Build succeeds
  • Tests pass (if applicable)
  • Commit messages are clear