Modules and Project Organization

Share one calculation across school reports

Two report screens need the same average rule. Copying the function into both screens risks inconsistent changes, while putting the entire application in one file makes responsibilities harder to find.

A module groups related code and exposes a supported interface. Other modules import that interface rather than duplicating its implementation. Module syntax and loading rules depend on the language and runtime.

  1. Use real files

    Write export and import in two runnable JavaScript modules.

  2. Define an interface

    Expose a focused marks calculation.

  3. Organize responsibilities

    Separate calculation from display and storage.

A report imports the calculation it needs

  1. Calculation module

    Export the supported operation.

    marks.mjs
  2. Report module

    Import and call it with student data.

    report.mjs
  3. Report output

    Display the returned result.

    37.5
Read these stages alongside the complete example. They explain the relationship; they do not execute code.

An exported interface is the supported way to use the module

An export makes a value available to importing modules. An import names the value and identifies its module location. In the Node.js example, .mjs marks JavaScript ES-module files and the relative import includes the file extension.

Modules can have private implementation details as well as exports. Keep the public interface focused on callers’ needs. A calculation module should not have to know which page displays its result or where a report will be stored.

Before starting, review one authoritative shared rule. These examples use JavaScript; other languages can use different syntax and rules.

Create two actual ES-module files

JavaScript ES modules · Save these two files together in a new practice folder. The first exports a calculation; the second imports it by a relative file path. With Node.js installed, run node report.mjs in that folder. Expected output is 37.5.

marks.mjs:

export function calculateAverage(maths, science) {
  return (maths + science) / 2;
}

report.mjs:

import { calculateAverage } from "./marks.mjs";
console.log(calculateAverage(40, 35));

Understand the rule inside the exported function

This standalone JavaScript version shows the calculation without module loading, so the lesson can display its checked result. The preceding two-file example is the real import/export implementation.

The rule produces the same value whether called locally or imported. A module boundary organizes access; it does not change arithmetic.

JavaScript · local teaching example

function calculateAverage(maths, science) {
  return (maths + science) / 2;
}
console.log(calculateAverage(40, 35));

Expected output

37.5

Arrange files by responsibilities that can change separately

A small school project might keep marks.mjs for calculations, report.mjs for presenting a report and storage.mjs for reading or saving data. A tests folder can hold checks for the calculations. These are suggested responsibilities, not a compulsory folder count.

Prefer a dependency direction where report code calls calculation and storage operations, while the calculation module does not import report UI code. A circular dependency occurs when modules depend back on one another; initialization order can then become harder to reason about.

Node.js CommonJS uses require and module.exports. Browser ES modules use a script with type="module" and browser loading rules. Use the format established by your project; do not mix import syntax into a CommonJS file without understanding the configuration.

Apply the rule to a small change

Add calculateTotal to the calculation module, then import it in a report. What calculation should the new export contain?

Worked answer

calculateTotal returns maths + science. Export it from marks.mjs and add its name to the import in report.mjs. Calling it with 40 and 35 displays 75; the standalone rule below verifies that calculation.

JavaScript · local teaching example

function calculateTotal(maths, science) {
  return maths + science;
}
console.log(calculateTotal(40, 35));

Expected output

75

Apply the change to both real files

JavaScript ES modules · Replace the two practice files with these versions and run node report.mjs. Expected output is 75. Updating both sides makes the exported name and imported name agree.

marks.mjs:

export function calculateTotal(maths, science) {
  return maths + science;
}

report.mjs:

import { calculateTotal } from "./marks.mjs";
console.log(calculateTotal(40, 35));

Recognize mistakes and limits

Module names, exported names and file paths must agree. A misspelled named import or missing relative file causes a loading error before the calculation can run. Check exact case on systems with case-sensitive file paths.

Do not split files merely to reach a target number of modules. Group code by a coherent responsibility and expose only what callers need. A tiny project may need very few modules.

Importing a module can execute its top-level code. Avoid surprising saves, network requests or other major side effects merely from loading a calculation module; make actions explicit through functions.

For the language or tool rules used here, see Node.js ES-module loading.

Check the core idea

Why does the report import calculateAverage rather than copy its body?
Choose one answer. You can try again.

Answer and explanation

Both reports can use one authoritative calculation definition.

Correct. A shared export reduces drifting copies of the rule.

Apply the idea to a new situation

Which dependency direction keeps the marks calculation focused?
Choose one answer. You can try again.

Answer and explanation

Report code imports the marks calculation; the calculation does not need report UI.

Correct. The reusable rule stays independent of its caller.

Share a focused interface through real module boundaries

Exports define supported operations and imports let callers use them. Keep file paths and formats correct, group coherent responsibilities and avoid surprising dependencies or import-time actions.

Connect this idea with function interfaces and checking exported rules independently.

See a practical application in organizing a React student-report project, where module boundaries follow responsibilities and components communicate through explicit inputs.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Tests and Code Reviews →