After going through all the most important JavaScript techniques, it's time to look at the bigger picture — how to organize code in larger projects. Poor code structure can lead to a disaster similar to what we experienced in Jurassic Park — chaos, errors, and difficulty managing the system.
Imagine all of Jurassic Park's infrastructure managed by one massive system. All enclosure security, dinosaur health monitoring, visitor flow management, catering, genetics labs — everything in one monolithic program. Disaster is just around the corner, right?
A well-designed project structure ensures:
There are many approaches to organizing code in larger JavaScript projects. Let's look at a few proven patterns that would work in a Jurassic Park management system.
One popular structure, especially in smaller apps, groups files by their type:
1jurassic-park-management/
2├── src/
3│ ├── components/ # UI components
4│ ├── services/ # API communication services
5│ ├── utils/ # Utility tools
6│ ├── models/ # Type and interface definitions
7│ ├── hooks/ # Custom hooks
8│ ├── constants/ # Application constants
9│ ├── assets/ # Images, fonts, etc.
10│ ├── styles/ # CSS/SCSS styles
11│ └── tests/ # Tests
12└── package.jsonAs the project grows, a feature-based structure is often better:
1jurassic-park-management/
2├── src/
3│ ├── features/
4│ │ ├── dinosaurs/
5│ │ │ ├── DinosaurList.jsx
6│ │ │ ├── DinosaurDetail.jsx
7│ │ │ ├── dinosaurService.js
8│ │ │ ├── dinosaurReducer.js
9│ │ │ └── dinosaur.test.js
10│ │ ├── security/
11│ │ │ ├── SecurityDashboard.jsx
12│ │ │ ├── securityService.js
13│ │ │ └── security.test.js
14│ │ └── monitoring/
15│ │ ├── MonitoringPanel.jsx
16│ │ ├── monitoringService.js
17│ │ └── monitoring.test.js
18│ ├── shared/
19│ │ ├── components/ # Shared components
20│ │ ├── utils/ # Shared utilities
21│ │ └── hooks/ # Shared hooks
22│ └── app/
23│ ├── store.js
24│ └── router.js
25└── package.jsonFor enterprise-level projects:
1jurassic-park-management/
2├── modules/
3│ ├── DinosaurModule/
4│ │ ├── domain/ # Business logic
5│ │ ├── infrastructure/ # External communication
6│ │ ├── presentation/ # UI
7│ │ └── index.js # Public API
8│ ├── SecurityModule/
9│ └── MonitoringModule/
10├── shared/
11└── app/1// Model
2class DinosaurModel {
3 constructor(data) {
4 this.id = data.id;
5 this.name = data.name;
6 this.species = data.species;
7 this.health = data.health;
8 }
9
10 isHealthy() { return this.health > 60; }
11 isDangerous() { return this.dangerLevel > 7; }
12}
13
14// View
15class DinosaurView {
16 render(dinosaur) {
17 return `<div class="dino-card ${dinosaur.isHealthy() ? 'healthy' : 'sick'}">
18 <h3>${dinosaur.name}</h3>
19 <p>${dinosaur.species}</p>
20 <p>Health: ${dinosaur.health}%</p>
21 </div>`;
22 }
23}
24
25// Controller
26class DinosaurController {
27 constructor(model, view, service) {
28 this.model = model;
29 this.view = view;
30 this.service = service;
31 }
32
33 async loadAndDisplay(id) {
34 const data = await this.service.getDinosaur(id);
35 const dinosaur = new this.model(data);
36 return this.view.render(dinosaur);
37 }
38}Regardless of folder structure and architecture, the key is to properly split code into smaller, cohesive modules.
Each module should have a clear, single responsibility:
1// dinosaurHealthMonitor.js - responsible only for monitoring dinosaur health
2export function checkVitalSigns(dinosaurId) {
3 //
4}
5
6export function alertHealthIssue(dinosaurId, issue) {
7 //
8}
9
10// enclosureSecurity.js - responsible only for enclosure security
11export function checkEnclosureSecurity(enclosureId) {
12 //
13}
14
15export function activateEmergencyProtocol(enclosureId) {
16 //
17}Modules should communicate through clean, well-defined interfaces:
1// Higher-level module uses lower-level module interfaces
2import { checkVitalSigns } from './dinosaurHealthMonitor';
3import { checkEnclosureSecurity } from './enclosureSecurity';
4
5// parkSafetySystem.js
6export async function performSafetyCheck() {
7 const dinosaurs = await fetchAllDinosaurs();
8 const enclosures = await fetchAllEnclosures();
9
10 // Use other module interfaces
11 const healthIssues = dinosaurs.map(checkVitalSigns);
12 const securityIssues = enclosures.map(checkEnclosureSecurity);
13
14 return {
15 healthIssues: healthIssues.filter(issue => issue !== null),
16 securityIssues: securityIssues.filter(issue => issue !== null)
17 };
18}Dependency injection is a technique that allows reversing dependencies between modules, making testing and changes easier:
1// Without DI
2function DinosaurMonitor() {
3 // Direct dependency
4 const vitalSignsMonitor = new VitalSignsMonitor();
5
6 return {
7 checkDinosaur(dinosaur) {
8 return vitalSignsMonitor.check(dinosaur);
9 }
10 };
11}
12
13// With DI
14function DinosaurMonitor(vitalSignsMonitor) {
15 return {
16 checkDinosaur(dinosaur) {
17 return vitalSignsMonitor.check(dinosaur);
18 }
19 };
20}
21
22// Now we can inject different implementations
23const realMonitor = DinosaurMonitor(new VitalSignsMonitor());
24const testMonitor = DinosaurMonitor(new MockVitalSignsMonitor());In large projects, managing imports can become problematic. Here are some techniques that help keep them organized:
Index files that re-export all public elements from a given directory:
1// dinosaurMonitoring/index.js
2export * from './vitalSigns';
3export * from './behaviorAnalysis';
4export * from './healthAlerts';
5
6// In another module
7import { checkVitalSigns, analyzeBehavior } from './dinosaurMonitoring';
8// Instead of
9import { checkVitalSigns } from './dinosaurMonitoring/vitalSigns';
10import { analyzeBehavior } from './dinosaurMonitoring/behaviorAnalysis';Many bundling tools (like webpack, Vite) allow defining aliases for import paths:
1// webpack.config.js
2module.exports = {
3 resolve: {
4 alias: {
5 '@core': path.resolve(__dirname, 'src/core'),
6 '@features': path.resolve(__dirname, 'src/features'),
7 '@shared': path.resolve(__dirname, 'src/shared'),
8 }
9 }
10};
11
12// In code
13import { activateProtocol } from '@core/security';
14import { DinosaurCard } from '@features/dinosaurs/components';This eliminates the problem of relative paths (../../..) and makes imports more readable.
In larger applications, state management becomes a key challenge. Here are several approaches:
Storing the entire application state in one place:
1// Redux actions for dinosaur monitoring system
2const UPDATE_DINOSAUR_HEALTH = 'UPDATE_DINOSAUR_HEALTH';
3const ALERT_HEALTH_ISSUE = 'ALERT_HEALTH_ISSUE';
4
5// Reducer
6function dinosaurHealthReducer(state = initialState, action) {
7 switch (action.type) {
8 case UPDATE_DINOSAUR_HEALTH:
9 return {
10 ...state,
11 dinosaurs: state.dinosaurs.map(dino =>
12 dino.id === action.payload.id
13 ? { ...dino, health: action.payload.health }
14 : dino
15 )
16 };
17 case ALERT_HEALTH_ISSUE:
18 return {
19 ...state,
20 alerts: [...state.alerts, action.payload]
21 };
22 default:
23 return state;
24 }
25}Splitting state into smaller, independent "slices":
1// State slice responsible for dinosaurs
2const dinosaurSlice = createSlice({
3 name: 'dinosaurs',
4 initialState: { list: [], loading: false, error: null },
5 reducers: {
6 fetchStart: (state) => {
7 state.loading = true;
8 },
9 fetchSuccess: (state, action) => {
10 state.list = action.payload;
11 state.loading = false;
12 },
13 updateHealth: (state, action) => {
14 const { id, health } = action.payload;
15 const dinosaur = state.list.find(d => d.id === id);
16 if (dinosaur) dinosaur.health = health;
17 }
18 }
19});
20
21// State slice responsible for enclosures
22const enclosureSlice = createSlice({
23 name: 'enclosures',
24 initialState: { list: [], securityStatus: {} },
25 reducers: {
26 //
27 }
28});Distinguishing between data coming from the server and UI state:
1// Server data - handled by React Query
2const { data: dinosaurs, isLoading, error } = useQuery(
3 'dinosaurs',
4 () => fetchDinosaurs()
5);
6
7// UI state - handled locally
8const [selectedDinosaurId, setSelectedDinosaurId] = useState(null);In large applications, managing side effects (API requests, I/O operations, etc.) becomes complex. Here are several approaches:
1// Redux Saga for dinosaur health monitoring
2function* monitorDinosaurHealthSaga() {
3 while (true) {
4 try {
5 // Fetch all dinosaurs
6 const dinosaurs = yield call(api.fetchAllDinosaurs);
7
8 // Check health of each dinosaur
9 for (const dinosaur of dinosaurs) {
10 const healthData = yield call(api.fetchDinosaurHealth, dinosaur.id);
11
12 // Update state
13 yield put(updateDinosaurHealth(dinosaur.id, healthData));
14
15 // If health is critical, report alert
16 if (healthData.status === 'critical') {
17 yield put(alertHealthIssue(dinosaur.id, healthData));
18 }
19 }
20
21 // Wait 5 minutes before next check
22 yield delay(5 * 60 * 1000);
23 } catch (error) {
24 console.error('Health monitoring error:', error);
25 yield put(monitoringError(error));
26 // Wait 1 minute before retrying
27 yield delay(60 * 1000);
28 }
29 }
30}1// React Query hook for managing dinosaur health data
2function useDinosaurHealth(dinosaurId) {
3 return useQuery(
4 ['dinosaurHealth', dinosaurId],
5 () => api.fetchDinosaurHealth(dinosaurId),
6 {
7 // Refresh data every 1 minute
8 refetchInterval: 60 * 1000,
9 // React to health changes
10 onSuccess: (data) => {
11 if (data.status === 'critical') {
12 notifyHealthIssue(dinosaurId, data);
13 }
14 }
15 }
16 );
17}
18
19// In a component
20function DinosaurMonitor({ dinosaurId }) {
21 const { data, isLoading, error } = useDinosaurHealth(dinosaurId);
22
23 if (isLoading) return <Loading />;
24 if (error) return <Error message={error.message} />;
25
26 return (
27 <div className={`health-status ${data.status}`}>
28 <h2>Dinosaur health: {data.status}</h2>
29 <div>Temperature: {data.temperature}°C</div>
30 <div>Heart rate: {data.heartRate} BPM</div>
31 <div>Updated: {formatTime(data.timestamp)}</div>
32 </div>
33 );
34}In large projects, comprehensive testing is crucial. Here are the levels of testing to consider:
Testing individual modules in isolation:
1// Testing the checkVitalSigns function from dinosaurHealthMonitor
2describe('dinosaurHealthMonitor', () => {
3 describe('checkVitalSigns', () => {
4 it('should correctly identify healthy dinosaurs', () => {
5 const healthyDino = {
6 id: 'trex1',
7 temperature: 38,
8 heartRate: 70,
9 bloodOxygen: 95
10 };
11
12 const result = checkVitalSigns(healthyDino);
13 expect(result.status).toBe('healthy');
14 expect(result.issues).toHaveLength(0);
15 });
16
17 it('should detect high temperature', () => {
18 const feverishDino = {
19 id: 'trex1',
20 temperature: 42, // Too high
21 heartRate: 70,
22 bloodOxygen: 95
23 };
24
25 const result = checkVitalSigns(feverishDino);
26 expect(result.status).toBe('issue');
27 expect(result.issues).toContain('high temperature');
28 });
29 });
30});Testing cooperation between modules:
1// Testing cooperation between health module and alert module
2describe('Health monitoring integration', () => {
3 it('should generate alerts for health issues', async () => {
4 // Prepare mocks
5 const mockAlertSystem = {
6 sendAlert: jest.fn()
7 };
8
9 const mockDinosaurData = {
10 id: 'raptor2',
11 temperature: 43, // Critically high
12 heartRate: 120, // Elevated
13 bloodOxygen: 85 // Low oxygen level
14 };
15
16 // Create monitoring system with mocked alert system
17 const healthMonitor = createHealthMonitor(mockAlertSystem);
18
19 // Call monitoring function
20 await healthMonitor.checkAndAlert(mockDinosaurData);
21
22 // Check if alert was sent
23 expect(mockAlertSystem.sendAlert).toHaveBeenCalledWith(
24 expect.objectContaining({
25 dinosaurId: 'raptor2',
26 severity: 'critical',
27 issues: expect.arrayContaining([
28 expect.stringMatching(/temperature/i),
29 expect.stringMatching(/heart rate/i),
30 expect.stringMatching(/oxygen/i)
31 ])
32 })
33 );
34 });
35});Testing complete user paths:
1// E2E test of enclosure breach response process
2describe('Enclosure breach response', () => {
3 it('should activate emergency protocol on fence failure', async () => {
4 // Log in as system administrator
5 await login('admin', 'password');
6
7 // Navigate to enclosure management panel
8 await navigateTo('enclosures');
9
10 // Simulate fence failure
11 await simulateFenceFailure('enclosure-b12');
12
13 // Check if alarm was activated
14 const alarmStatus = await getElement('.alarm-status');
15 expect(alarmStatus).toHaveClass('active');
16
17 // Check if emergency protocol was launched
18 const protocolStatus = await getElement('.emergency-protocol');
19 expect(protocolStatus.textContent).toContain('Active');
20
21 // Check if notifications were sent
22 const notifications = await getElements('.notification');
23 expect(notifications).toHaveLength(2); // To security team and management
24
25 // Simulate fence repair
26 await repairFence('enclosure-b12');
27
28 // Check if alarm was deactivated
29 const updatedAlarmStatus = await getElement('.alarm-status');
30 expect(updatedAlarmStatus).not.toHaveClass('active');
31 });
32});In large projects, documentation becomes essential. Here are different levels of documentation to consider:
1/**
2 * Checks the health status of a dinosaur.
3 * @param {string} dinoId - The unique dinosaur identifier
4 * @param {Object} [options] - Additional options
5 * @param {boolean} [options.detailed=false] - Return detailed report
6 * @returns {Promise<HealthStatus>} Health status object
7 * @throws {DinoNotFoundError} When the dinosaur is not found
8 * @example
9 * const status = await checkHealth('TRX-001', { detailed: true });
10 */
11async function checkHealth(dinoId, options = {}) {
12 const dino = await getDinosaurById(dinoId);
13 if (!dino) throw new DinoNotFoundError(dinoId);
14
15 return {
16 healthy: dino.health > 60,
17 level: dino.health,
18 lastChecked: new Date().toISOString()
19 };
20}Each module should have its own README.md file describing its functionality, interface, and usage:
1# Dinosaur Health Monitoring Module
2
3This module is responsible for monitoring and assessing the health status of dinosaurs in Jurassic Park.
4
5## Features
6
7- Regular checking of dinosaur vital signs
8- Detecting health issues and generating alerts
9- Historical health status tracking and trends
10- Automatic veterinary staff notifications
11
12## API
13
14### `checkVitalSigns(dinosaur)`
15
16Checks dinosaur health based on vital signs.
17
18### `monitorHealth(dinosaurId, interval)`
19
20Starts periodic dinosaur health monitoring.
21
22### `stopMonitoring(dinosaurId)`
23
24Stops dinosaur health monitoring.
25
26## Usage Examples
27
28~~~js
29// One-time health check
30const healthStatus = checkVitalSigns(dinosaur);
31
32// Start continuous monitoring
33const stopMonitoring = monitorHealth('trex1', 60000); // every minute
34
35// Stop monitoring
36stopMonitoring();This module uses:
Used by:
1
2### 3. Architecture Documentation (Diagrams, ADR)
3
4Documenting architectural decisions is crucial for understanding why the system was designed a certain way. Helpful tools include:
5
6- **Architecture diagrams** (C4, UML)
7- **Architecture Decision Records (ADR)** - documents describing important architectural decisions, their context, and consequences
8
9~~~text
10# ADR 001: Choosing Redux as the State Management System
11
12## Context
13
14The Jurassic Park management application needs a state management mechanism that handles:
15- Complex global state (dinosaur data, enclosures, visitors)
16- Asynchronous operations (communication with park systems)
17- State change history tracking (security audit)
18
19## Decision
20
21We decided to use Redux with Redux Toolkit and Redux Saga as the main state management mechanism.
22
23## Rationale
24
25- **Predictability**: Redux ensures unidirectional data flow
26- **Middleware**: Redux Saga simplifies managing complex async operations
27- **DevTools**: Redux developer tools facilitate debugging
28- **Ecosystem**: Rich ecosystem of libraries and tools
29- **Time Travel Debugging**: Ability to track state change history
30
31## Consequences
32
33### Positive
34- Clear data flow in the application
35- Easier debugging of state issues
36- Ability to audit user actions
37
38### Negative
39- Increased amount of code (actions, reducers, selectors)
40- Higher learning curve for new team members
41- Potential performance overhead if used improperlyDefine and enforce coding standards using tools like ESLint, Prettier:
1// .eslintrc.json
2{
3 "extends": [
4 "eslint:recommended",
5 "plugin:react/recommended"
6 ],
7 "rules": {
8 "react/prop-types": "error",
9 "no-unused-vars": "error",
10 "no-console": ["warn", { "allow": ["warn", "error"] }],
11 "prefer-const": "error"
12 }
13}1// typescript
2interface DinosaurVitalSigns {
3 id: string;
4 temperature: number;
5 heartRate: number;
6 bloodOxygen: number;
7}
8
9interface HealthAssessment {
10 status: 'healthy' | 'issue' | 'critical';
11 issues: string[];
12 lastChecked: Date;
13}
14
15function checkVitalSigns(dinosaur: DinosaurVitalSigns): HealthAssessment {
16 // implementation...
17}Set up CI/CD pipelines that automatically run tests, static code analysis, and deploy the application:
1# .github/workflows/ci.yml
2name: CI/CD
3
4on:
5 push:
6 branches: [ main, develop ]
7 pull_request:
8 branches: [ main, develop ]
9
10jobs:
11 test:
12 runs-on: ubuntu-latest
13 steps:
14 - uses: actions/checkout@v2
15 - name: Set up Node.js
16 uses: actions/setup-node@v2
17 with:
18 node-version: '14'
19 - name: Install dependencies
20 run: npm ci
21 - name: Run tests
22 run: npm test
23 - name: Run linting
24 run: npm run lint
25 - name: Build
26 run: npm run build
27
28 deploy:
29 needs: test
30 if: github.ref == 'refs/heads/main'
31 runs-on: ubuntu-latest
32 steps:
33 - uses: actions/checkout@v2
34 # ... deployment stepsEstablish a Code Review process that ensures code is reviewed by at least one other developer before merging into the main branch.
Implement application performance monitoring to quickly detect and fix issues:
1// Example custom performance monitoring tool configuration
2const performanceMonitor = {
3 eventTiming: {},
4
5 startTiming(eventName) {
6 this.eventTiming[eventName] = {
7 start: performance.now()
8 };
9 },
10
11 endTiming(eventName) {
12 if (this.eventTiming[eventName]) {
13 const start = this.eventTiming[eventName].start;
14 const duration = performance.now() - start;
15
16 // Log to monitoring server
17 logPerformanceMetric(eventName, duration);
18
19 // Generate alert if operation took too long
20 if (duration > performanceThresholds[eventName]) {
21 alertPerformanceIssue(eventName, duration);
22 }
23
24 delete this.eventTiming[eventName];
25 }
26 }
27};
28
29// Usage
30function loadDinosaurData(id) {
31 performanceMonitor.startTiming('loadDinosaurData');
32
33 return fetchDinosaurData(id)
34 .then(data => {
35 performanceMonitor.endTiming('loadDinosaurData');
36 return data;
37 })
38 .catch(error => {
39 performanceMonitor.endTiming('loadDinosaurData');
40 throw error;
41 });
42}Structuring code in larger JavaScript projects is a complex topic requiring a thoughtful approach. Key aspects are:
By following these principles, you can avoid the chaos that might lead to a "catastrophe" in your JavaScript project — just as proper organization and management would have helped avoid the catastrophe in Jurassic Park!