Thank you for your interest in contributing to OpenLN! We welcome contributions from developers of all skill levels and backgrounds. This guide will help you get started and ensure a smooth contribution process.
- Bug Fixes: Help us squash bugs and improve stability
- New Features: Add exciting new functionality
- Performance Improvements: Optimize existing code
- Documentation: Improve or expand documentation
- Tests: Add or improve test coverage
- Bug Reports: Report issues you encounter
- Feature Requests: Suggest new features or improvements
- Design: UI/UX design improvements
- Community Support: Help other contributors in discussions
- Translation: Localization and internationalization
Follow our Development Setup Guide to get your local environment ready.
- Check good first issues for beginners
- Look at help wanted issues
- Browse open issues for all available tasks
- Comment on the issue saying you'd like to work on it
- Wait for a maintainer to assign it to you
- If you're a first-time contributor, start with smaller issues
# Fork the repository on GitHub
# Then clone your fork
git clone https://github.com/YOUR_USERNAME/Openln-Engine.git
cd Openln-Engine
# Add upstream remote
git remote add upstream https://github.com/Openln-git/Openln-Engine.git# Create and switch to a new branch
git checkout -b feature/your-feature-name
# Or for bug fixes
git checkout -b fix/bug-description- Write clean, readable code
- Follow our Code Style Guide
- Add tests for new functionality
- Update documentation as needed
# Run tests for both client and server
cd client && npm test
cd ../server && npm test
# Run linting
cd client && npm run lint
cd ../server && npm run lint
# Test the application manually
npm run dev# Add your changes
git add .
# Commit with a descriptive message
git commit -m "feat: add user profile customization feature"# Push to your fork
git push origin feature/your-feature-name
# Create a pull request on GitHub
# Fill out the PR template completelyUse conventional commit format:
feat:for new featuresfix:for bug fixesdocs:for documentation changesstyle:for formatting changesrefactor:for code refactoringtest:for adding testschore:for maintenance tasks
## Description
Brief description of the changes made.
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## How Has This Been Tested?
Describe the tests that you ran to verify your changes.
## Screenshots (if applicable)
Add screenshots to help explain your changes.
## Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes- Automated Checks: All CI checks must pass
- Code Review: At least one maintainer review required
- Testing: Manual testing by reviewers if needed
- Approval: Maintainer approval before merge
- Merge: Squash and merge into main branch
- DRY (Don't Repeat Yourself): Avoid code duplication
- KISS (Keep It Simple, Stupid): Prefer simple solutions
- YAGNI (You Aren't Gonna Need It): Don't over-engineer
- Clean Code: Write self-documenting code
// Use descriptive variable names
const userProfileData = getUserProfile();
// Use async/await over promises
async function fetchUserData(userId) {
try {
const response = await api.get(`/users/${userId}`);
return response.data;
} catch (error) {
logger.error('Failed to fetch user data:', error);
throw error;
}
}
// Use TypeScript interfaces
interface User {
id: string;
email: string;
profile: UserProfile;
}// Use functional components with hooks
import React, { useState, useEffect } from 'react';
interface UserCardProps {
userId: string;
onUserClick: (user: User) => void;
}
export const UserCard: React.FC<UserCardProps> = ({ userId, onUserClick }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUser = async () => {
try {
const userData = await getUserById(userId);
setUser(userData);
} catch (error) {
console.error('Error fetching user:', error);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return <LoadingSpinner />;
if (!user) return <ErrorMessage message="User not found" />;
return (
<div className="user-card" onClick={() => onUserClick(user)}>
<img src={user.avatar} alt={`${user.name}'s avatar`} />
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
};// RESTful route structure
router.get('/api/v1/users', getUsersController);
router.get('/api/v1/users/:id', getUserByIdController);
router.post('/api/v1/users', createUserController);
router.put('/api/v1/users/:id', updateUserController);
router.delete('/api/v1/users/:id', deleteUserController);
// Controller function structure
export const getUserByIdController = async (req, res, next) => {
try {
const { id } = req.params;
// Validate input
if (!mongoose.isValidObjectId(id)) {
return res.status(400).json({
success: false,
message: 'Invalid user ID format'
});
}
const user = await User.findById(id).select('-password');
if (!user) {
return res.status(404).json({
success: false,
message: 'User not found'
});
}
res.status(200).json({
success: true,
data: user
});
} catch (error) {
next(error);
}
};- Write tests for all new functionality
- Maintain or improve existing test coverage
- Use descriptive test names
- Follow the AAA pattern (Arrange, Act, Assert)
// Component testing with React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import { UserCard } from './UserCard';
describe('UserCard', () => {
const mockUser = {
id: '1',
name: 'John Doe',
email: 'john@example.com',
avatar: 'avatar.jpg'
};
it('should display user information correctly', () => {
render(<UserCard user={mockUser} onUserClick={jest.fn()} />);
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('john@example.com')).toBeInTheDocument();
expect(screen.getByAltText("John Doe's avatar")).toBeInTheDocument();
});
it('should call onUserClick when card is clicked', () => {
const mockOnClick = jest.fn();
render(<UserCard user={mockUser} onUserClick={mockOnClick} />);
fireEvent.click(screen.getByRole('button'));
expect(mockOnClick).toHaveBeenCalledWith(mockUser);
});
});// API endpoint testing with Jest and Supertest
import request from 'supertest';
import app from '../app';
import User from '../models/User';
describe('GET /api/v1/users/:id', () => {
beforeEach(async () => {
await User.deleteMany({});
});
it('should return user when valid ID is provided', async () => {
const user = await User.create({
name: 'Test User',
email: 'test@example.com',
password: 'hashedpassword'
});
const response = await request(app)
.get(`/api/v1/users/${user._id}`)
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.name).toBe('Test User');
expect(response.body.data.password).toBeUndefined();
});
it('should return 404 when user not found', async () => {
const nonExistentId = new mongoose.Types.ObjectId();
const response = await request(app)
.get(`/api/v1/users/${nonExistentId}`)
.expect(404);
expect(response.body.success).toBe(false);
expect(response.body.message).toBe('User not found');
});
});- Use JSDoc for function documentation
- Add inline comments for complex logic
- Update README files when needed
- Document API endpoints
/**
* Calculates the user's learning progress based on completed modules
* @param {string} userId - The unique identifier for the user
* @param {string} courseId - The unique identifier for the course
* @returns {Promise<number>} The progress percentage (0-100)
* @throws {Error} When user or course is not found
*/
async function calculateLearningProgress(userId, courseId) {
// Implementation here
}When reporting bugs, please include:
- Clear title: Describe the issue concisely
- Steps to reproduce: Detailed steps to recreate the bug
- Expected behavior: What should happen
- Actual behavior: What actually happens
- Environment: OS, browser, Node.js version
- Screenshots: If applicable
- Error messages: Full error messages and stack traces
For feature requests, include:
- Problem description: What problem does this solve?
- Proposed solution: How should it work?
- Alternatives: Other solutions you've considered
- Use cases: Real-world examples
- Priority: How important is this feature?
bug: Something isn't workingenhancement: New feature or requestgood first issue: Good for newcomershelp wanted: Extra attention is neededdocumentation: Improvements or additions to documentationquestion: Further information is requestedwontfix: This will not be worked on
priority: high: Critical issues that need immediate attentionpriority: medium: Important issues for next releasepriority: low: Nice to have improvements
We appreciate all contributors! Contributors are recognized in:
- GitHub contributors list
- Release notes
- Project documentation
- Community highlights
Active contributors who demonstrate:
- Consistent high-quality contributions
- Good understanding of the codebase
- Helpful community interaction
- Reliable code review skills
May be invited to become maintainers with additional privileges.
- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: General questions and ideas
- Code Reviews: Specific code-related questions
- Issues: We aim to respond within 48 hours
- Pull Requests: Initial review within 72 hours
- Questions: Response within 24 hours during weekdays
- Be respectful: Treat everyone with respect and kindness
- Be inclusive: Welcome people of all backgrounds and experience levels
- Be constructive: Provide helpful feedback and suggestions
- Be patient: Remember that everyone is learning
- Harassment or discrimination of any kind
- Trolling, insulting, or derogatory comments
- Publishing private information without permission
- Any conduct that would be inappropriate in a professional setting
Violations of the code of conduct may result in:
- Warning
- Temporary ban from contributions
- Permanent ban from the project
Report violations to the maintainers via email or private message.
Thank you for contributing to OpenLN! Your efforts help make education more accessible and personalized for everyone. π