This document outlines standardized conventions for Node.js application development, focusing on project structure, coding standards, and best practices for AI-assisted development. Following these guidelines will help maintain consistency, readability, and maintainability across projects.
- Project Structure
- JavaScript Coding Conventions
- Error Handling
- Node.js Specific Practices
- Tailwind CSS Conventions
- AI-Assisted Development Guidelines
- Documentation Standards
A consistent project structure helps maintain organization and improves developer onboarding. Use the following structure as a starting point:
my-node-app/
├── src/
│ ├── controllers/ # Request handlers
│ ├── models/ # Data structures
│ ├── routes/ # API endpoints
│ ├── services/ # Business logic
│ ├── middleware/ # Express middleware
│ ├── utils/ # Helper functions
│ ├── config/ # Configuration files
│ └── index.js # Application entry point
├── tests/ # Test files
├── public/ # Static assets
├── .env # Environment variables
├── .gitignore # Git ignore file
├── package.json # Dependencies and scripts
└── README.md # Project documentation
Every component should have a clear structure:
- Every component should have an
index.jsfile that exports the module's functionality1 - Group related functionality (controllers, models, routes) by feature or domain2
- Use consistent naming conventions across all files2
Follow these JavaScript conventions for consistency and readability:
- Use
constfor all references that are not reassigned34 - Use
letonly for references that will be reassigned34 - Never use
var34 - Declare one variable per declaration statement5
// Good
const user = getUser();
let count = 0;
// Bad
var user = getUser(),
count = 0;- Use the literal syntax for object and array creation3
// Good
const items = [];
const user = {};
// Bad
const items = new Array();
const user = new Object();- Use property value shorthand when possible3
// Good
const name = 'John';
const user = { name };
// Bad
const user = { name: name };- Use array methods like
map,filter, andreduceinstead of loops when appropriate6
- Use named function expressions instead of function declarations7
- Prefer arrow functions for anonymous functions7
- Keep functions small and focused on a single task5
- Return early from functions to avoid deep nesting5
// Good
function validateUser(user) {
if (!user) {
return false;
}
if (!user.name) {
return false;
}
return true;
}
// Bad
function validateUser(user) {
let isValid = false;
if (user) {
if (user.name) {
isValid = true;
}
}
return isValid;
}- Use async/await for asynchronous operations instead of callbacks8
- Always properly handle promises with try/catch blocks8
// Good
async function fetchUser(id) {
try {
const user = await getUserById(id);
return user;
} catch (error) {
console.error('Failed to fetch user:', error);
throw error;
}
}
// Bad
function fetchUser(id) {
return getUserById(id)
.then(user => user)
.catch(error => {
console.error('Failed to fetch user:', error);
throw error;
});
}Proper error handling is critical for robust applications:
- Create custom error classes for different error types8
- Use middleware for centralized error handling in Express applications8
- Log errors with appropriate severity levels8
- Include contextual information in error messages8
app.get('/user/:id', async (req, res, next) => {
try {
const user = await getUserById(req.params.id);
if (!user) {
return res.status(404).send('User not found');
}
res.send(user);
} catch (error) {
next(error); // Pass to error handling middleware
}
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something went wrong!');
});- Store configuration in environment variables, not in code8
- Use
.envfiles for local development, but never commit them8 - Load environment variables using
dotenvpackage8
// config.js
require('dotenv').config();
module.exports = {
dbHost: process.env.DB_HOST,
dbUser: process.env.DB_USER,
dbPass: process.env.DB_PASS,
port: process.env.PORT || 3000
};- Export a single responsibility per file1
- Use named exports for multiple functions/classes1
- Organize related functionality in a directory with an
index.jsfile1
When generating HTML with Tailwind CSS, follow these practices:
- Maintain a consistent order for utility classes following the "Concentric CSS" approach9:
- Positioning/visibility
- Box model
- Borders
- Backgrounds
- Typography
- Other visual adjustments
- Use fewer utility classes when possible (use
mx-2instead ofml-2 mr-2)9 - Prefix responsive utility classes with their breakpoint (e.g.,
lg:flex)9 - Group related utilities for readability9
<!-- Good -->
<div class="flex flex-col items-center justify-center p-4 lg:p-8 text-center">
<!-- Content -->
</div>
<!-- Bad (mixed order, verbose) -->
<div class="text-center p-4 flex items-center justify-center flex-col lg:p-8">
<!-- Content -->
</div>Leverage AI tools effectively for Node.js development:
- Assign well-defined, specific tasks to AI assistants10
- Reserve architecture and complex design decisions for human developers10
- Use AI for repetitive coding tasks, boilerplate code, and routine refactoring10
- Clear Task Definitions: Provide explicit context and examples when prompting AI10
# Good Prompt
"Create a controller function that handles user registration with the following requirements:
- Validate email and password inputs
- Hash password using bcrypt
- Return appropriate error responses for validation failures
- Return JWT token on success
Here's our User model structure: [model details]"
# Poor Prompt
"Write a user registration function"
- Implement Guardrails: Use unit tests, interfaces, and type definitions to keep AI-generated code on track10
- Test-Driven Development: Have AI write tests first, then implement features10
- Ask AI to write failing tests
- Implement features to make tests pass
- Iteratively run tests to confirm functionality
- Incremental Complexity: Break complex tasks into smaller chunks10
- Build functionality incrementally rather than all at once
- Focus on one abstraction layer at a time
- Always review AI-generated code for:
- Security vulnerabilities
- Performance issues
- Adherence to project conventions
- Edge cases and error handling
- Use JSDoc comments for functions and classes6
- Include examples in documentation when helpful6
- Only comment complex logic, not obvious operations6
/**
* Authenticates a user and generates a JWT token
* @param {string} email - User email address
* @param {string} password - User password
* @returns {Promise<string>} JWT token
* @throws {AuthError} If authentication fails
*/
async function authenticateUser(email, password) {
// Implementation
}- Use GitHub-Flavored Markdown for all documentation11
- Structure documentation with clear headings and sections11
- Use code fences with language identifiers for syntax highlighting11
## Authentication API
### POST /api/auth/login
Authenticates a user and returns a JWT token.
// Request body { "email": "user@example.com", "password": "password123" }
#### Response
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "id": "123", "name": "John Doe", "email": "user@example.com" } }
By following these conventions, teams can maintain consistency across projects and leverage AI tools effectively while maintaining high code quality and readability.
Footnotes
-
https://dreamix.eu/insights/node-js-project-structure-a-short-guide/ ↩ ↩2 ↩3 ↩4
-
https://reintech.io/blog/structuring-a-nodejs-project-a-comprehensive-guide-for-software-developers ↩ ↩2
-
https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Code_style_guide/JavaScript ↩ ↩2 ↩3 ↩4
-
https://dev.to/mehedihasan2810/nodejs-best-practices-a-guide-for-developers-4d65 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
https://gist.github.com/sandren/0f22e116f01611beab2b1195ab731b63 ↩ ↩2 ↩3 ↩4
-
https://engineering.axur.com/2025/05/09/best-practices-for-ai-assisted-coding.html ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Howto/Markdown_in_MDN ↩ ↩2 ↩3