Skip to content

Add error handling fundamentals to Data Groups Sprint 1#1949

Open
abdishakoor-dev wants to merge 8 commits into
mainfrom
error-handling-fundamentals
Open

Add error handling fundamentals to Data Groups Sprint 1#1949
abdishakoor-dev wants to merge 8 commits into
mainfrom
error-handling-fundamentals

Conversation

@abdishakoor-dev

@abdishakoor-dev abdishakoor-dev commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

First attempt for #1946

Adds three js2 blocks (Throwing errors, try/catch, Throw or return?) that teach exceptions and error handling using the existing calculateMedian exercise, and wires them into Data Groups Sprint 1
prep, after "Implementing all the cases".

Trainees currently learn fetch, promises, and async/await without ever being taught how to throw or catch an error. This covers the synchronous foundation.

Two follow ups needed once this lands:

Preview: https://deploy-preview-1949--cyf-curriculum.netlify.app/itp/data-groups/sprints/1/prep/#throwing-errors

@netlify

netlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Deploy Preview for cyf-common ready!

Name Link
🔨 Latest commit 4d3ca33
🔍 Latest deploy log https://app.netlify.com/projects/cyf-common/deploys/6a6373c25679b80008a56474
😎 Deploy Preview https://deploy-preview-1949--cyf-common.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Deploy Preview for cyf-curriculum ready!

Name Link
🔨 Latest commit 4d3ca33
🔍 Latest deploy log https://app.netlify.com/projects/cyf-curriculum/deploys/6a6373c2074b9200081a5abd
😎 Deploy Preview https://deploy-preview-1949--cyf-curriculum.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 97 (🟢 up 12 from production)
Accessibility: 100 (no change from production)
Best Practices: 92 (no change from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@abdishakoor-dev
abdishakoor-dev force-pushed the error-handling-fundamentals branch 2 times, most recently from e21ffc4 to 8feddc2 Compare July 22, 2026 08:54
Trainees currently learn fetch, promises, and async/await without
being taught how to throw or catch an error. Adds three js2 blocks
(throwing errors, try/catch, throw vs return) built around the
existing calculateMedian exercise, and wires them into Sprint 1 prep.

Refs #1946
@abdishakoor-dev
abdishakoor-dev force-pushed the error-handling-fundamentals branch from 8feddc2 to 70093be Compare July 22, 2026 08:59

@illicitonion illicitonion left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Left a few minor comments.

Exercise-wise, as well as updating the existing ones, do we want to add a few new exercises to get people throwing and catching more?

Also, do we want to add a callback to this in https://curriculum.codeyourfuture.io/sdc/tools/sprints/5/prep/ to reflect on static checking being better than dynamic checking?


### Using `throw`

In Structuring Data, you [interpreted error traces](/itp/structuring-data/sprints/2/prep/#interpreting-this-error) when JavaScript threw a `SyntaxError` at you. Now we can {{<tooltip title="throw">}}Throwing an error stops normal execution immediately. The error travels up through the calling functions until something handles it. If nothing handles it, the program crashes and prints the error trace.{{</tooltip>}} errors from our own code, using the `throw` keyword:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This link works, but could plausibly break quite easily (e.g. if that block was re-titled) and we would be unlikely to notice (the link would just start going to the first prep block). Accordingly, we generally try to avoid linking directly to specific prep blocks.

I don't love that. Maybe we could at some point add a "check if the anchors linked to actually exist" check so we'll notice if they break and can fix them.

I'm not sure I'm suggesting a specific action here, but just an FYI...


### Using `throw`

In Structuring Data, you [interpreted error traces](/itp/structuring-data/sprints/2/prep/#interpreting-this-error) when JavaScript threw a `SyntaxError` at you. Now we can {{<tooltip title="throw">}}Throwing an error stops normal execution immediately. The error travels up through the calling functions until something handles it. If nothing handles it, the program crashes and prints the error trace.{{</tooltip>}} errors from our own code, using the `throw` keyword:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In Structuring Data, you [interpreted error traces](/itp/structuring-data/sprints/2/prep/#interpreting-this-error) when JavaScript threw a `SyntaxError` at you. Now we can {{<tooltip title="throw">}}Throwing an error stops normal execution immediately. The error travels up through the calling functions until something handles it. If nothing handles it, the program crashes and prints the error trace.{{</tooltip>}} errors from our own code, using the `throw` keyword:
In Structuring Data, you [interpreted error traces](/itp/structuring-data/sprints/2/prep/#interpreting-this-error) when JavaScript threw a `SyntaxError` at you. We can also {{<tooltip title="throw">}}Throwing an error stops normal execution immediately. The error travels up through the calling functions until something handles it. If nothing handles it, the program crashes and prints the error trace.{{</tooltip>}} errors from our own code, using the `throw` keyword:


```js
function calculateMedian(list) {
if (Array.isArray(list) === false) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (Array.isArray(list) === false) {
if (!Array.isArray(list)) {

throw new Error("calculateMedian requires an array of numbers");
}
if (list.length === 0) {
throw new Error("calculateMedian requires a non-empty array");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't check they're all numbers - do we want to?

});
```

Note that we wrap the call in a function. If we called `calculateMedian([])` directly, the error would be thrown before `expect` could check anything.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Note that we wrap the call in a function. If we called `calculateMedian([])` directly, the error would be thrown before `expect` could check anything.
Note that we wrap the call in a function. When we call a function with arguments, those arguments get evaluated before the function is called. If we called `expect(calculateMedian([])).toThrow()` directly, the error would be thrown before `expect` was called, and so before `expect` could check anything.


```js
test("throws an error when given an empty list", () => {
expect(() => calculateMedian([])).toThrow();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we add an error message (fragment) to the toThrow() call, to show people a better example of usage?

}
```

[`Array.isArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) checks whether a value is an array. If the input isn't an array, or is an empty one, we `throw` a new [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) with a message explaining what went wrong.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's probably worth an aside talking about what a good error message looks like?

e.g. a common piece of feedback I give in code reviews is "Include an indication of what the bad data was"


### Expected situations: use a return value

If a situation is a normal part of using the function, handle it with ordinary code. Searching an array for a value that isn't there is not an error; that's why [`indexOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) returns `-1` rather than throwing. The caller expects both outcomes and can check with an `if`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add some guidance on what value to choose, maybe with some examples? e.g. that -1 is a good thing to return from indexOf because it probably fits in with how it's used (whereas returning null would be more annoying), that string methods may want to return "" or null depending on context, etc?

correct="0"
>}}

{{<multiple-choice

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd maybe answer this with "it depends"? Sometimes "continue despite bad data" is a useful answer for a user and sometimes "error if anything is wrong" is a useful answer...

{{<multiple-choice
question="divide(a, b) is called with b equal to 0. What should divide do?"
answers="Throw an error | Return 0 | Return Infinity because that is what JavaScript does"
feedback="Right. divide cannot do what its name promises with a zero divisor, and a zero here usually means a bug in the calling code. Some teams make a different, documented choice, but it must be a deliberate one. | Zero is a made-up answer that hides the caller's bug and will be used as if it were real. | JavaScript's own operator does evaluate to Infinity, but for our function that quietly passes a strange value along instead of surfacing the problem where it happened."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
feedback="Right. divide cannot do what its name promises with a zero divisor, and a zero here usually means a bug in the calling code. Some teams make a different, documented choice, but it must be a deliberate one. | Zero is a made-up answer that hides the caller's bug and will be used as if it were real. | JavaScript's own operator does evaluate to Infinity, but for our function that quietly passes a strange value along instead of surfacing the problem where it happened."
feedback="Right. divide cannot do what its name promises with a zero divisor, and a zero here usually means a bug in the calling code. Some teams make a different, documented choice, but it must be a deliberate one. | Zero is a made-up answer that hides the caller's bug and will be used as if it were real. | JavaScript's own operator does evaluate to Infinity, but for our function that would quietly pass a strange value along instead of surfacing the problem where it happened."

abdishakoor-dev added a commit that referenced this pull request Jul 24, 2026
@abdishakoor-dev
abdishakoor-dev force-pushed the error-handling-fundamentals branch from 12ca65f to 961a67b Compare July 24, 2026 08:35
Matches review feedback: the guarded implementation checked the
input was a non-empty array, but not that its elements were
numbers, so calculateMedian(["a", "b"]) would still silently
return nonsense.
Calling with no argument does crash on its own (list is undefined),
but with a generic engine error that doesn't mention calculateMedian
or say a list was expected. The existing non-array guard already
catches this case, so add an example and test showing that.
toThrow(string) only checks the message contains a substring, not
that it equals it. toThrow(new Error(...)) is Jest's documented way
to assert the exact message, more precise given we now write the
messages ourselves. Also add the missing non-array test case, and
bump time to 20 minutes to reflect the added content.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 📋 Backlog

Development

Successfully merging this pull request may close these issues.

2 participants