Skip to content

WIP: SDC Decomposition #1284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 36 additions & 0 deletions common-content/en/energisers/binary/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
+++
title="Binary Line-Up"
emoji="⬜ ⬛"
time=20
[tasks]
1="Convert decimal to binary using people"
2="Practice quick mental math with powers of 2"
[build]
render = 'never'
list = 'local'
publishResources = false
+++

### ⬜ ⬜ ⬛ ⬜

Let's count in binary using human beans!

Assign roles randomly. 1️⃣ One person will be the **caller**, 4️⃣ four people will be the **beans** and #️⃣ the rest will be the **counters**.

First four **beans**, line up facing away from the group. **Counters**, get in any kind of order so you know who is next.

The **caller** will say a number between 1 and 15.
For example, 8: 😀 ⬜ ⬜ ⬜

First **counter**, you will turn the beans around to represent the binary number. If you get it right, go back in the counter group and let the next person count. If you get it wrong, you must become another bean, and the range of numbers will increase!

<details>
<summary>Example of range increase</summary>

🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧 Round 1: Numbers 1-15 (4 beans)
🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧 Round 2: Numbers 1-31 (5 beans)
🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧 Round 3: Numbers 1-63 (6 beans)
🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧🧑🏽‍🔧 Round 4: Numbers 1-127 (7 beans)
...And so on until only one counter remains!

</details>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
+++
title = "Adding like/dislike"
headless = true
time = 120
facilitation = false
emoji= "👍👎"
objectives = [
"Identify what data needs to be stored and exchanged between a client and server.",
"Devise a scheme for differentiating messages with different meanings (e.g. a new message vs a new like).",
"Contrast giving updated values as absolute values or relative changes.",
"Implement an end-to-end feature involving data updates and reconciliation across a client and server.",
]
+++

The last requirement we have for our chat application is the ability to like/dislike a message (and see what messages have been liked/disliked).

{{<note type="Exercise">}}
Think about what information a client would need to provide to a server in order to like/dislike a message.

Think about what information a server would need to provide to a client in order to display how many likes/dislikes a message has.

Think about what information a server would need to provide to a client in order to _update_ how many likes/dislikes a message has.

Write these things down.
{{</note>}}

### Identifiers

One of the key new requirements to add liking/disliking a message is knowing _which_ message is being liked/disliked.

When a client wants to like a message, it needs some way of saying _this_ is the message I want to like.

This suggests we need a unique identifier for each message:
* When the server tells a client about a message, it needs to tell it what the identifier is for that message.
* When a client tells the server it wants to like a message, it needs to tell it the identifier for the message it wants to like.
* When the server tells a client a message has been liked, it needs to tell the client which message was liked, and the client needs to know enough about that message to be able to update the UI.

### Message formats

Now that your server will be sending multiple kinds of updates ("Here's a new message", or "Here's an update to the number of likes of an existing message"), you'll need to make sure the client knows the difference between these messages. The client will need to know how to act when it receives each kind of message.

### Changes or absolutes?

When new likes happen, a choice we need to make is whether the server should tell a client "this message was liked again" or should tell the client "this message now has 10 likes". Both of these can work.

{{<note type="Exercise">}}
Write down some advantages and disadvantages of a server -> client update being "+1 compared to before" or "now =10".

Choose which approach you want to take.
{{</note>}}

{{<note type="Exercise">}}
Implement liking and disliking messages.

If a message has a non-zero number of likes or dislikes, the frontend needs to show this.

The frontend needs to expose some way for a user to like or dislike any message.

When a user likes or dislikes a message, the frontend needs to tell the backend about this, and the backend needs to notify all clients of this.

When a frontend is notified by a backend about a new like or dislike, it needs to update the UI to show this.

You may do this in your polling implementation, WebSockets implementation, or both.
{{</note>}}
116 changes: 116 additions & 0 deletions common-content/en/module/decomposition/websockets/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
+++
title = "WebSockets"
headless = true
time = 120
facilitation = false
emoji= "🔌"
objectives = [
"Stream live updates from a server using WebSockets.",
"Discuss the trade-offs of using WebSockets or polling.",
"Describe properties of message formats (e.g. including a command name).",
]
+++

WebSockets are an API and protocol which allow creating a bi-directional communication channel between two programs.

They are commonly used in websites to establish a channel so that a backend can send updates to a frontend.

You can read [an introduction to WebSockets](https://docs.developer.tech.gov.sg/docs/data-engineering-initiative-playbook/Chapter5/Introduction_to_WebSockets), as well as roughly [what a client looks like](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications), and [what a server does](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers).

On the server side, we will be using the [websocket npm package](https://www.npmjs.com/package/websocket) which lists a server example in its README.

> [!TIP]
>
> This sprint, you will need to submit _both_ a copy of your code which supports polling, _and_ a copy which supports WebSockets.
>
> You probably want to make a copy of your polling code, and have two separate (similar) pages in your repo.

On the backend, you can create a WebSocket server by adding this code:

```js
import { server as WebSocketServer } from "websocket";
const server = http.createServer(app);
const webSocketServer = new WebSocketServer({ httpServer: server });
```

You will then need to follow the example in the `websocket` npm package's documentation to have your server handle requests.

On the client-side, you will need to make a new `WebSocket` connection to the server.

Some things to think about when implementing WebSockets updates:

### Learn new APIs in isolation

It will be easier for you to learn a new API (like WebSockets) with a simple example.

At the end, you will want your WebSocket to stream new messages from the server to the client, but maybe to explore WebSockets you want the server to always report the message "Hello" when it's connected to, so you can test things out more easily? Or even write a whole new website which _only_ makes a WebSocket connection and displays a message?

Once you have an example WebSocket working, and understand how it works, it should be easier for you to apply that to the real problem you're trying to solve.

### Think about the protocol you want

WebSockets let you send arbitrary text (or binary) messages.

In our quote server, we switched from our backend returning a pre-formatted string of a quote, to returning a JSON object so we could get the parts ourselves.

Think about what structure would be useful for our client and our server to know about.

If we're going to add more messages in the future (e.g. for "liking" a message), how will the receiver of the message know what kind of message the one it receives is?

One thing we often do is wrap our message in an object, with a field specifically saying what the command is.

e.g. instead of sending:
```json
{
"user": "Azin",
"message": "Hello!"
}
```

we may send:

```json
{
"command": "send-message",
"message": {
"user": "Azin",
"message": "Hello!"
}
}
```

This means that if we add new commands in the future, we don't need to change our existing code.

### Think about timings

When we first load a page, we need to get all of the messages that already exist.

After that, we can ask to be notified of new messages.

There are a few ways we could do that. An interesting question is what happens _between_ these events?

Imagine we made an HTTP GET request to ask for all of the messages, then created a WebSocket to get new messages. What happens if someone sent a message between when we got our response, and when the WebSocket was connected? How can we make sure we don't miss any messages?

Or imagine we made a WebSocket request, and expected to receive a list of all previous messages, and then to keep receiving updates. Does the server need to remember which messages have already been sent to each client?

{{<note type="Exercise">}}
Write down your strategy for how to make sure that after initially getting the existing messages, your client won't miss any new messages.
{{</note>}}

### Remember WebSockets are bi-directional

Now, we're using a `POST` request to send a new message, and a `WebSocket` to stream receiving new messages.

But we know that WebSockets are bi-directional - we can both send and receive information on them.

We could change our sending logic to also use our WebSocket. Or we could keep using HTTP POST requests. Both of these approaches work.

{{<note type="Exercise">}}
Think: What advantages does each approach have?

Why might we want to change our implementation to use a WebSocket for sending messages?

Why might we want to keep using POST requests for sending messages?

Why might we want to support _both_ on our server? Why might we only want to support one on our server?
{{</note>}}
8 changes: 8 additions & 0 deletions common-content/en/module/logic/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
+++
title="Logic"
description="Mental models for logical reasoning"
emoji="🧑🏾‍💻"
layout="block-viewer"
hide_from_overview="true"
noindex="true"
+++
51 changes: 51 additions & 0 deletions common-content/en/module/logic/abduction/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
+++
title = "Abduction"
time = 60
emoji= "🔎"
[build]
render = 'never'
list = 'local'
publishResources = false
[objectives]
1="Define abduction"
2="Use evidence to reason to the best explanation"
+++

> Abduction is reasoning to the best explanation for all the evidence we observe

```mermaid
flowchart TD
ev1[Evidence 1] --> h1[Hypothesis A]
ev2[Evidence 2] --> h1
ev3[Evidence 3] --> h1
ev1 --> h2[Hypothesis B]
ev2 --> h2
ev3 -.-> h2
h1 --> be[Best Explanation]
h2 -.- be
style h2 stroke-dasharray: 5 5
style ev3 stroke-dasharray: 5 5
```

In [Sherlock Holmes: Consulting Detective](https://www.spacecowboys.fr/case3-the-murderess), we think like detectives. Each case presents us with mysterious evidence that needs explaining. Unlike deduction which proves only what _must_ be true, or induction which only finds patterns that are probably true, abduction seeks the most complete explanation.

> _Given_ a woman was found dead in her apartment
> _And_ her jewelry was untouched
> _And_ there were no signs of forced entry
> _Then_ the killer likely knew the victim (but we can't be certain)

Each lead we follow adds new evidence. A witness statement might support our theory, contradict it, or suggest a completely different explanation. We must:

- Keep track of all evidence
- Form multiple possible theories
- Test each theory against _all_ the evidence
- Choose the explanation that best fits everything we know
- Be ready to revise our theory when new evidence appears

It's quite a lot like problem solving we've done before, isn't it? Now go solve a case:

{{<blocklink
src="https://www.spacecowboys.fr/case3-the-murderess"
name="Sherlock Holmes: Consulting Detective - Case 3"
caption="Use abductive reasoning to best explain the evidence"
time="5" >}}
51 changes: 51 additions & 0 deletions common-content/en/module/logic/binary-logic/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
+++
title = "Boolean Logic"
time = 45
emoji = "🎭"
[build]
render = 'never'
list = 'local'
publishResources = false
[objectives]
1="Define binary logic and its role in computing"
2="Express logical statements as truth tables"
+++

> Boolean logic uses only **true** or **false** to reason about the world.

In the real world, we use logic to make decisions all the time. For example: if it's raining and you don't have an umbrella, you will get wet. This can be represented as a truth table:

{{<columns>}}

| Is Raining | Has Umbrella | Is Wet |
| ---------- | ------------ | ------ |
| F | F | F |
| F | T | F |
| T | F | T |
| T | T | F |

Truth tables show all possible combinations and all possible outcomes.
<--->

> _Given_ A is true (1)
> _And_ B is true (1)
> _Then_ A AND B is true (1)

{{</columns>}}

In computers, we use binary logic to derive conclusions from data. Each bit can represent a logical state: {{<tooltip title="true">}}Represented as 1 in binary{{</tooltip>}} or {{<tooltip title="false">}}Represented as 0 in binary{{</tooltip>}}.

| Raining | Umbrella | Wet |
| ------- | -------- | --- |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |

This is fundamental to how computers work. Every operation a computer performs, from simple addition to complex decision-making, ultimately comes down to chains of basic logical operations on 1s and 0s.

Try building some truth tables yourself in your notebook. Here are some examples to get you started:

- "You can get a loyalty reward if you have the app AND have made 10 purchases"
- "The alarm will sound if the door is open OR motion is detected, UNLESS the system is disabled"
- "Trainees pass the course if they complete coursework AND attend class AND complete their steps"
1 change: 1 addition & 0 deletions common-content/en/module/logic/bisection/bisection.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions common-content/en/module/logic/bisection/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
+++
title = "Bisection"
time = 10
emoji= "✂️"
[build]
render = 'never'
list = 'local'
publishResources = false
[objectives]
1="Define bisection search"
2="Apply binary search to guess a number"
+++

> In bisection, we start with a large problem space and cut it in half with each guess.

![bisection.svg](bisection.svg)

In software development, bisection helps us find exactly when a change occurred. For example:

> _Given_ our code worked last week but not today
> _When_ we test the middle version and it works
> _Then_ the problem must be in the newer half

With each test, we:

1. Select the middle version
2. Test if it works
3. Eliminate half the versions
4. Repeat until we find the exact change

This [binary search](https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/a/binary-search) technique is remarkably efficient. Even with thousands of versions, we'll find the problematic change in just a few tests. Git and other version control systems include [built-in bisect tools](https://git-scm.com/docs/git-bisect) for this purpose.

{{<blocklink
src="https://www.mathsisfun.com/games/guess_number.html"
name="Higher or Lower"
caption="Guess the number efficiently"
time="2" >}}
Loading
Loading