Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5292032
Add workshop on breaking down the quote generator app (#316)
illicitonion Mar 29, 2025
1221600
Add prioritisation and estimation workshop (#317)
illicitonion Apr 4, 2025
2327d65
Add workshop on explaining code (#318)
illicitonion Apr 14, 2025
0ff6b52
Add technical interviewing workshop (#319)
illicitonion Apr 24, 2025
16026c4
Add workshop on sorting cards (#323)
illicitonion May 21, 2025
e2a1884
Add user needs and acceptance criteria workshop (#324)
illicitonion Jun 6, 2025
f83c13b
Update typo in the demo of finding names not addresses (#332)
pdjota Jul 12, 2025
61c0290
job-tech-workshop
kfklein15 Aug 15, 2025
2aa5e28
adding the actual file
kfklein15 Aug 15, 2025
0eabfaa
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
a48ed7d
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
c0ec66f
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
4fa8554
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
5f2d756
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
0b71962
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
8c59158
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
dc98f24
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
ee3c269
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
d5853d3
Update jobs-in-tech/README.md
kfklein15 Aug 15, 2025
d1a6ee3
Merge pull request #341 from CodeYourFuture/jobs-in-tech-workshop
kfklein15 Aug 15, 2025
4311273
Interview Introductions workshop (#342)
Ara225 Sep 24, 2025
df5978b
Add error handling workshops (#344)
cifarquhar Nov 7, 2025
72373fa
Changed app and apptest
HannaOdud Nov 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions breaking-down-quote-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Breaking down the quote generator

You should have already followed the prep to design, write, and deploy a quote generator.

We already broke down this project into steps. We're going to talk about the techniques that helped us to do this breaking down. This will help you to break down projects yourself in the future.

Get into groups of about 5, with at least one volunteer per group.

## Axes to break down by (20 minutes)

There are different ways we can break down a project. For instance:
* We could write the whole frontend, then the whole backend (or the whole backend then the whole frontend).
* We could build one entire feature in the frontend and backend, then another feature in the frontend and backend.
* We could write down the whole API between the backend and frontend, and then build one, then the other.

There are other ways we could break down the project too.

Spend 3 minutes thinking of different ways you could break down a project.

Spend 10 minutes discussing all of your ideas (including the suggestions above) in your group. What advantages does each approach have? What disadvantages does each have?

Are the answers different depending on the number of people working on the project?

Are the answers different depending on whether you have specialists (e.g. one person who is specialised in frontend and one specialised in backend) vs generalists (people who can do a bit of everything)?

Can you think of ways to combine approaches, to get the advantages of different approaches?

<details>

<summary>Hints for volunteers</summary>

We're generally trying to get towards:
* Breaking down by feature is useful (proves out end-to-end design, provides incremental value, ...).
* Thinking about data / APIs can help us to develop components in parallel.
* We can often interact with components in isolation for testing, e.g. `curl` testing against a backend, dummy backends for testing frontends, etc.
</details>

## User journey mapping

### Collecting features (5 minutes)

We often first break down projects by feature. We try to build entire end-to-end journeys for one thing a user can do at a time.

One example of a feature our quote generator has is "Add a quote".

Spend 2 minutes thinking of as many features as you can for our quote generator. These don't have to be things we _have_ implemented, they can also be things we _haven't implemented yet_. For instance, I can imagine after adding a quote, a user may want to share a link to _that_ quote with someone - our server doesn't support this right now (it only shows random quotes). But this is still a feature we can imagine.

In your group, make a list of all of the features you have thought of.

<details>

<summary>Some examples if you're really stuck</summary>

* Displaying a quote on first load.
* Displaying new quotes on demand.
* Adding new quotes.
* Seeing the quote you just added actually got added.
* Sharing a link to a specific quote.
* Displaying a new quote that is guaranteed not to have been show before.
* Showing statistics about how often a quote has been displayed.
* Up/down-voting quotes.
* Finding quotes on similar themes.
* Listing all of the quotes by a particular person.
* Auto-grouping similar quote authors (e.g. "FDR" and "Franklin Roosevelt" are the same person).

</details>

### Mapping features (20 minutes)

Pick three features and create user journey maps for them. Use [the user journey mapping intro you read](https://www.nngroup.com/articles/journey-mapping-101/) to help you - who wants to use each feature? What do they want to achieve? What steps would they need to achieve their goals? What would they experience on the way?

## Making a plan

### Unlocking parallelism and feedback (10 minutes)

When developing, we want to get feedback as quickly as we can. We don't want to have to build a whole backend and a whole frontend and a whole database in order to see whether any one piece works.

It can be helpful to think about the _interfaces_ between these components before building them. Asking questions like:
* For each operation, what data will we need to pass from the frontend to the backend?
* For each operation, what data will we need to pass from the backend to the frontend?

In our quote generator, this may mean asking questions like:
* What information does the frontend need from the backend in order to display a quote? (The answer here may be: An object containing two fields - the quote as a string, and the author as a string).
* What information does the frontend need to give the backend to save a quote?
* What information does the frontend need to give the backend in order to get a specific quote? (This question may suggest we need some kind of ID for each quote, which we can put in a URL, but don't show to the user in the page).
* What information does the frontend need to give the backend if it wants to guarantee the next quote it shows hasn't been shown to this user before?

For each of the three features your have mapped, write down what information needs to be sent between which components in order to perform an action. Be specific. Include names and types of each field.

### Agreeing on data formats and APIs

Now that we know what data we need to pass where, we can come up with the APIs we need to build. This can happen at multiple levels, e.g. you can decide:
* The backend APIs your frontend will call (and what the requests and responses will look like).
* The function one part of your frontend will call to to generate a component for some data.
* The frontend URLs that a user can visit to interact with the application, and what they will do.
* And more.

By agreeing on these things, we can start building them in parallel - one person can be working on one side of the API (like the backend endpoint, or a function) while another works on the code that will call it.

### Using dummy data

We can even use fake versions of these APIs with dummy data.

One of our user journeys is "When I load the page, a quote is displayed". Some ways we can develop this without having a whole backend:
* We can write the frontend code to display a quote with one hard-coded quote.
* We can write a `fetchQuote` function in the frontend which doesn't call a backend but instead always returns one quote _in the data format we've agreed_.
* We can fetch a quote, but write a small backend where the "get quote" endpoint always returns one quote (while someone makes the backend do something more complicated like choose a random quote).

These are all ways we can develop and test our frontend before completing the whole system. Agreeing on a data format, and APIs, makes it easier for us to do this.

### Making a plan (30 minutes)

For each of the three user journey maps you've created, make a plan for how you could break these down and implement them.

You should work out the data formats, work out the APIs, and work out how different people could be working on different parts of the implementation at the same time.

Write down tickets for each task that contain enough information that someone could pick up the ticket and work on it. Make sure to identify which tickets _depend_ on which other tickets (i.e. which ones need to be done before each other).
9 changes: 0 additions & 9 deletions devtools/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,6 @@ So we've explored the Elements panel, the Styles panel, and the Lighthouse panel

For example, you have just looked in the Elements panel. This is not really your HTML, this is [the DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction), an API the browser builds using your HTML. An example of how the DOM is different from HTML is that JavaScript can change what's in the page - this changes the DOM, but doesn't change the actual HTML. The [DOM isn't the only API](https://developer.mozilla.org/en-US/docs/Web/API) it builds. Let's look at another one right now!

<!-- {{<note type="exercise" title="A New Lens">}}-->

1. Set a [timer for 5 minutes](https://www.google.com/search?q=timer+for+5+minutes).
1. Find the [Accessibility Panel](https://developer.chrome.com/blog/full-accessibility-tree/) and check 'Enable full-page accessibility tree'
1. Now click the "Universal Man" button in the Elements Panel.
1. What is _Ignored_ in the Accessibility Tree that is present in the DOM?
1. When the timer goes off, share your answer with the other groups by pasting in the workshop thread.
<!-- {{</note>}}-->

### Reflect

<!-- {{<note type="exercise" title="Develop Your Skills">}}-->
Expand Down
14 changes: 13 additions & 1 deletion dom-merge-conflict/tasks/buttons-and-counter/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,41 @@ function increment(node) {
let current = node.textContent;
node.textContent = Number(current) + 1;
}
function decrement(node) {
let current = node.textContent;
node.textContent = Number(current) - 1;
}

export function App() {
const body = document.createElement("body");

const header = document.createElement("header");
header.innerHTML = `
<h1>Number Counter</h1>
<p>A simple counter. Press increment to increase the count by one.</p>
<p>A simple counter. Press increment to increase the count by one. Press decrement to decrease by </p>

`;
body.appendChild(header);

const main = document.createElement("main");
main.innerHTML = `
<p id="counter" data-testid="counter">0</p>
<button id="increment">Increment</button>
<button id="decrement">Decrement</button>

`;
body.appendChild(main);

const button = body.querySelector("#increment");
const buttonDecrem = body.querySelector("#decrement");
const counter = body.querySelector("#counter");
button.addEventListener("click", () => {
increment(counter);
});
buttonDecrem.addEventListener("click", () => {
decrement(counter);
});


return body;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe("button and counter", () => {
expect(getByTestId(container, "counter")).toHaveTextContent(/^2$/);
});

describe.skip("decrement button", () => {
describe("decrement button", () => { ///.skip removed
test("pressing Decrement decreases the counter", () => {
const button = getByRole(container, "button", {
name: "Decrement",
Expand Down
69 changes: 69 additions & 0 deletions error-handling/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Error Handling

## Learning Objectives

```objectives
- [ ] Give examples of good and bad error messages
- [ ] Explain what makes an error message helpful
```

## Understanding What Went Wrong

We have already seen examples of developer questions which can help us fix problems in our code:

1. What I _did_
2. What I _expected_
3. What actually _happened_

The problem is that figuring out what happened can be really tricky if the developer writing the code doesn't do a good job of **error handling**. In this workshop we're going to discuss error messages, why they are important and what makes some more helpful than others.

What makes an error message "good" depends on who it is intended for. For example, the runtime errors you may have seen while working through the exercises are great for developers while they're working on the code but they aren't very useful for the end user looking at an app on their phone. Knowing the most appropriate way to communicate problems is a key skill for a developer.

### Setup

- **Trainees**: Go to the `error-handling` folder in the [Workshops repo](https://github.com/CodeYourFuture/CYF-Workshops/), navigate to the `webpage` folder and open `index.html` in a browser.
- **Trainees**: Work in groups of 2 or 3.
- **Trainees**: Nominate a member of the group to take notes.
- **Volunteers**: Nominate someone to facilitate the group discussion. This could be a volunteer or a trainee.

<!--{{<note type="activity" title="Evaluating Error Messages, 15m" >}}-->

Open your web browser and take a look at the web page you just opened. You should see a text box with five buttons below it. When you type a message and press any of the buttons it should appear below the box. Try it out by typing "hello world" and see what happens.

The problem we have is that none of the buttons seem to be working! They all have the same problem but they aren't all telling us the same thing. We could probably open `script.js` and easily figure out where the problem is, but this won't be an option for the end users of our code.

In your groups, **without looking at the code**, answer the following questions for each button:

1. Can you find where the error message is being displayed? (Hint: there may not always be an error message)
2. Can you figure out what the problem is based on what the message is telling you?
3. Does the message tell you how to fix the problem?
4. Is the message more useful for a developer or a user?

<!--{{</note>}}-->

<!--{{<note type="activity" title="Class Discussion, 10m" >}}-->

Come back together to compare notes. As a group try to answer these questions:

1. Which buttons gave useful error messages? Which buttons weren't helpful?
2. Which button gave the clearest error message?
3. Which features of the messages were particularly useful for **developers** and which features were useful for **users**?
4. How could the messages be further improved for users?

<!--{{</note>}}-->

## Supporting the User

We have seen that communicating what went wrong is really important when building an application. Giving feedback to the user is useful, but how we give that feedback is important. We could improve the user experience even more by helping them avoid the problem altogether!

<!--{{<note type="activity" title="Improving Our Error Messages, 25m" >}}-->

Read [this article](https://piccalil.li/blog/how-to-write-error-messages-that-actually-help-users-rather-than-frustrate-them/) on writing helpful error messages. Think about the error messages we have seen so far - do they align with the suggestions made here?

It would be even better if we never needed to show our users any error messages, but that won't happen if we aren't clear about what we need them to do. Take another look at the application from the first part of this workshop and answer these questions:

1. What information is missing that might help the user?
2. How can we let the user know what the requirements are before they start typing?

<!--{{</note>}}-->

27 changes: 27 additions & 0 deletions error-handling/webpage/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="script.js" defer></script>
<title>Broken Buttons</title>
</head>
<body>
<header>
<h1>Broken Buttons</h1>
</header>
<main>
<p>Enter some text in the box below then click one of the buttons.</p>
<p>If everything works your message will be added below the box.</p>
<textarea name="submission" id="submission" cols="30" rows="10"></textarea>
<section>
<button id="console-button">Button 1</button>
<button id="silent-fail-button">Button 2</button>
<button id="poor-alert-button">Button 3</button>
<button id="dev-alert-button">Button 4</button>
<button id="better-alert-button">Button 5</button>
</section>
<p>You typed: <span id="output"></span></p>
</main>
</body>
</html>
58 changes: 58 additions & 0 deletions error-handling/webpage/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const textarea = document.querySelector("#submission");
const silentFailButton = document.querySelector("#silent-fail-button");
const consoleButton = document.querySelector("#console-button");
const poorAlertButton = document.querySelector("#poor-alert-button");
const devAlertButton = document.querySelector("#dev-alert-button");
const betterAlertButton = document.querySelector("#better-alert-button");
const outputArea = document.querySelector("#output");

const validateMessage = (message) => {
return message.length >= 250;
}

const appendMessage = (message) => {
outputArea.textContent = message;
}

silentFailButton.addEventListener("click", () => {
const message = textarea.value;
if (validateMessage(message)) {
appendMessage(message)
}
})

consoleButton.addEventListener("click", () => {
const message = textarea.value;
if (validateMessage(message)) {
appendMessage(message)
} else {
console.error("Invalid message - check failed at script.js:10")
}
})

poorAlertButton.addEventListener("click", () => {
const message = textarea.value;
if (validateMessage(message)) {
appendMessage(message)
} else {
alert("Something went wrong")
}
})

devAlertButton.addEventListener("click", () => {
const message = textarea.value;
if (validateMessage(message)) {
appendMessage(message)
} else {
alert("Failed with error code 03986")
}
})

betterAlertButton.addEventListener("click", () => {
const message = textarea.value;
if (validateMessage(message)) {
appendMessage(message)
} else {
alert(`Message must be at least 250 characters long - yours was ${message.length}. Please try again`)
}
})
Loading