Homework 01
Expected Workload
For a student who has kept up with the lecture material and is reasonably comfortable with Docker, Express, Redis, and basic HTML forms, this homework should take about 6 to 9 hours to complete carefully.
That estimate assumes you are not learning every tool from scratch during this assignment. It includes time to build the system, test the full flow, capture evidence, and write a clear brief.md.
A reasonable breakdown is:
- 1 to 1.5 hours to set up the multi-container project structure, Dockerfiles, and
compose.yml - 1 to 1.5 hours to build the API routes and basic Redis-backed order state
- 0.75 to 1.25 hours to build the worker and simulate asynchronous processing
- 0.75 to 1.25 hours to build the kiosk page and monitoring dashboard with simple server-rendered HTML
- 1 to 1.5 hours to build the
kiosk-simservice and make its behavior configurable - 0.75 to 1 hour to add and verify idempotency behavior for duplicate logical orders
- 0.5 to 1 hour to test the system end to end, collect screenshots or short videos, and finish
brief.md
The most reliable way to complete this assignment successfully is to treat it as a sequence of small milestones instead of one long build session. In practice, students usually lose the most time when they wait too long to start, try to build every part at once, or postpone testing and documentation until the end.
A good approach is to time-block this work on your calendar in advance. Reserve a few focused work sessions, decide what each session should accomplish before you begin, and stop each session with the project in a runnable state. That makes it much easier to recover if you get stuck and much less likely that you will run out of time near the deadline.
One reasonable schedule is:
- Session 1: 1.5 to 2 hours for project setup, Dockerfiles, Compose, and a minimal API that starts correctly
- Session 2: 1.5 to 2 hours for Redis-backed order handling, queueing, and a worker that processes jobs
- Session 3: 1.5 to 2 hours for the kiosk page, monitoring dashboard, and basic end-to-end browser flow
- Session 4: 1.5 to 2 hours for the
kiosk-simservice, duplicate-request handling, testing, evidence capture, and finishingbrief.md
If you prefer shorter sessions, split the work across more calendar blocks, but still assign each block a concrete goal. The key habit is to schedule the time intentionally rather than hoping you will "find time later."
You should approach this homework like a small delivery sprint: define milestones, implement incrementally, test continuously, and finish with clear deliverables.
Reliable Campus Kiosk Simulation
In this homework, you will build a small live system that simulates a real system that does the following:
- a kiosk page where a user places an order
- a background worker that processes the order later
- a kiosk simulator service that can behave like many kiosks submitting orders in parallel
- a monitoring dashboard that shows what is happening inside the system
- a duplicate-protection rule so the same order is not completed twice
This assignment is meant to help you learn how to simulate a system using the concepts and techniques from this course in a way that still feels practical, visual, and engaging. When you finish, you should be able to open your app, click around, watch orders move through the system, and explain how your simulation reflects course ideas such as coordination, state changes, and safety under retries.
This is also practice for the larger simulation project later in the course. Here, you are building one small slice well instead of building a huge system.
Key Learning Outcomes
By the end of this homework, you should be able to:
- Build and run a small multi-container web system with Docker and Docker Compose
- Create an Express application that serves both API routes and simple server-rendered HTML pages
- Use Redis as shared infrastructure for queueing work and storing system state
- separate fast request handling from slower background work with a worker process
- Simulate concurrent clients with a separate kiosk-sim service
- Design a simple operator view that makes system behavior easy to observe
- Explain and implement idempotency so duplicate requests do not create duplicate completed work
- Document a small distributed scalable system clearly enough that another person can run and verify it
Scenario
Imagine that your team was asked to build the software for a self-serve campus snack kiosk.
Students walk up to a kiosk screen, place orders, and expect a quick response. Behind the scenes, the real work should happen in the background. At the same time, a staff member or developer should be able to open a monitoring dashboard and watch orders move through the system.
Your simulation should have two different feelings:
- the kiosk page should feel like the customer view
- the monitoring dashboard should feel like the control room
That contrast is part of what makes this assignment interesting.
The important correctness rule is this:
If the same logical order is submitted more than once, your system must not create duplicate completed work.
That rule is the idempotency part of the assignment. In simple terms: retries are okay, duplicate completed side effects are not.
You may enforce idempotency at the API boundary, in the worker, or in both places, but your write-up must clearly explain why your system does not complete the same logical order twice.
What You Are Building
You are building one small web system with four running parts:
api- an Express server
- renders the kiosk page and monitoring dashboard
- accepts order requests
- stores and returns order state
worker- runs separately from the API
- pulls queued work from Redis
- simulates preparing the order
kiosk-sim- runs separately from the API
- simulates
Nkiosks sending orders in parallel - helps you demonstrate concurrent submissions and retry behavior
redis- holds queue data and shared order state
Use Docker and Docker Compose so the whole system starts together.
For example, it is reasonable to expose the main app on http://localhost:3000 during local development so a grader can open the kiosk page, the monitoring dashboard, and API routes from the same base URL.
When the system is working well, this should happen:
- A user opens the kiosk page.
- They submit an order.
- The page updates quickly.
- The worker processes the job in the background.
- The kiosk simulator service can generate many kiosk requests in parallel.
- The monitoring dashboard shows the order move from
queuedtoprocessingtocompleted. - If the same logical order is retried, the system does not complete it a second time.
Assignment Requirements
Your submission must include all of the following:
- A Dockerized multi-service system with these services:
apiworkerkiosk-simredis
- An Express API container.
- A worker container that processes queued jobs asynchronously.
- A kiosk simulator container that can simulate
Nkiosks placing orders in parallel. - Redis used as part of the running design, not just as an optional extra.
- A basic HTML kiosk simulator page for submitting orders.
- A basic HTML monitoring dashboard for observing system state.
- A
docker compose up --buildworkflow that starts the system.
Constraints
- Use Node.js and Express for the API. Other programming languages are also allowed; see below.
- Build a basic server-rendered UI using plain HTML.
- Use Redis for queueing and shared state.
- Simulate kiosk fulfillment with a short delay so the queue boundary is visible.
- Support a configurable kiosk load generator through the
kiosk-simservice. - Document exact setup, run, test, and cleanup commands.
- Keep the scope small and focused. One queue and one worker process is enough.
Programming Languages
The course shows examples primarily in TypeScript and JavaScript, and those are the recommended languages for this assignment.
You are welcome to use another language if you prefer. However, if you do, recognize that you may need to explain more in your submission because your audience may not already be familiar with that language, its tooling, or any libraries you choose.
If you use a non-recommended language, your write-up should make it easy for the course staff to understand how to run your project, what each major dependency does, and how your implementation maps to the concepts from the course.
What Success Looks Like
A strong submission is not the one with the fanciest design. A strong submission is the one that is easy to run and easy to understand.
Your project should make a grader think:
- "I can start this with one compose command."
- "I can place an order from the kiosk page."
- "I can watch the worker handle the order."
- "I can run the kiosk simulator and see parallel kiosk traffic hit the system."
- "I can see the monitor update."
- "I can retry the same order and the system stays correct."
That is the standard you should aim for.
Required API Contract
Your API must implement at least these routes.
You may add more routes for HTML pages, redirects, dashboards, or helper actions.
POST /orders
Accept a new logical order request.
This route may accept either JSON or standard HTML form submissions, as long as the same logical fields are provided and the server handles them consistently.
Required request data:
- A stable client-supplied logical order identifier such as
clientOrderId - An item name
- A quantity
About the identifier:
- This identifier must be created by the client, not by the API
- The same logical order must reuse the same
clientOrderIdon retries - Different kiosks should avoid collisions by including a kiosk identifier in the value
- A simple pattern such as
kiosk-03-00042is enough
You may also create a separate server-side orderId internally if you want, but that is optional and it is not the identifier used for idempotency.
If you build the kiosk page as a normal HTML form, a simple approach is to submit the form to a server route that reads form fields, including a visible clientOrderId, and then applies the same order-handling logic on the server side.
Suggested JSON body:
{
"clientOrderId": "kiosk-01-0001",
"item": "iced-latte",
"quantity": 2,
"customer": "Ava"
}
Required behavior:
- Validate required fields
- Store or reuse the stable logical identifier
- Enqueue work for a first-time request
- Return quickly instead of doing fulfillment inline
- If the same logical order is submitted again, return a non-error response and do not create duplicate work
Expected status codes:
202 Acceptedfor a newly accepted order200 OKfor a recognized duplicate submission that reuses existing order state400 Bad Requestfor invalid input
GET /orders/:clientOrderId
Return the current state of an order.
Expected behavior:
- Return
404if the order is unknown - Return
200with enough state to see whether the order isqueued,processing, orcompleted
Suggested response shape:
{
"clientOrderId": "kiosk-01-0001",
"status": "completed",
"item": "iced-latte",
"quantity": 2,
"duplicate": false
}
You may include additional fields such as timestamps, attempt counts, or worker notes if they help demonstrate your design.
Required UI Contract
Your project must expose at least two user-facing pages:
Kiosk Page
This page should let a user submit an order through a standard HTML form.
Required behavior:
- Support successful order submission through the browser
- Show whether the request was accepted or recognized as a duplicate
- Make the client-generated order identifier visible so the order can be tracked
- Make it possible to re-submit a previously used
clientOrderIdso a grader can intentionally demonstrate duplicate handling
Recommended browser behavior:
- When preparing a brand-new logical order, the page should generate a fresh
clientOrderId - The generated ID should include the kiosk identifier if your system models multiple kiosks
- After submission, the page should continue to show the last submitted
clientOrderId - A grader should be able to copy, reuse, or edit that same
clientOrderId
One simple approach is to keep the clientOrderId in a visible editable form field. The page can pre-fill a new value for a fresh order, but the grader should still be able to leave it unchanged to simulate a retry or change it manually to inject a specific duplicate into the system.
Monitoring Dashboard
This page should let a user observe the simulation as it runs.
Required behavior:
- Show recent orders or current order state
- Show enough information to distinguish
queued,processing, andcompleted - Allow a grader to refresh or revisit the page and observe updated state
Recommended behavior:
- Update often enough that a grader can visually watch orders move through the system
- Use a simple refresh strategy instead of building a complex frontend
- Keep the implementation understandable and easy to explain
You do not need live WebSockets or a complicated frontend framework for this page.
Simple reasonable options include:
- a manual refresh button
- a timed full-page reload every few seconds
- server-rendered templates plus a browser auto-refresh
Any of those approaches is acceptable if it makes the state changes easy to see.
Another very simple option is to use a normal server-rendered monitoring dashboard and let the browser reload the whole page every few seconds. For example:
<script>
setTimeout(() => window.location.reload(), 3000)
</script>
or a meta refresh tag:
<meta
http-equiv="refresh"
content="3" />
That approach is perfectly acceptable for this assignment if it keeps the monitor easy to understand.
Choose the approach that best matches your implementation style. The goal is not sophistication. The goal is to make system changes visible with minimal complexity.
The UI can be plain. Styling is not the point here. Correct interaction and observability are.
If you want a good mental model:
- The kiosk page is the "customer screen"
- The monitoring dashboard is the "operator screen"
Both can be simple. What matters is that they clearly show the system working.
Required Kiosk Simulator Service
Your project must also include a separate service named kiosk-sim whose job is to simulate many kiosks sending orders to the API in parallel.
This service is not a browser UI. It is a small load-generation or event-generation service that helps demonstrate the distributed-system behavior of your design under concurrent request traffic.
Required Structure Under code/
Your code/ folder should include a dedicated kiosk-sim/ service directory. A recommended structure is:
code/
api/
kiosk-sim/
Dockerfile
simulator.js
redis/ (optional if customized)
worker/
compose.yml
You may organize filenames differently, but the service should have its own folder and its own Dockerfile.
If your redis service uses the off-the-shelf Redis image from Docker Hub without any custom configuration, you do not need a separate code/redis/ folder.
What the kiosk-sim Container Should Do
At runtime, the service should:
- start
Nindependent kiosk loops or tasks - generate realistic order requests to the API
- submit those requests concurrently rather than strictly one at a time
- assign each kiosk a unique kiosk identifier
- generate client-created IDs that include that kiosk identifier
- randomly or at regular intervals send requests that intentionally reuse
previously sent
clientOrderIdvalues to help demonstrate idempotency behavior - log enough information to make the simulated kiosk activity understandable
The service can run continuously, for a fixed number of rounds, or until stopped by Docker Compose. Choose a simple design and explain it clearly.
Dockerfile Expectations
The kiosk-sim/Dockerfile should be small and focused, similar in spirit to the API and worker images. It should:
- install the dependencies needed to run the simulator
- copy the simulator source files into the image
- set a default command that starts the simulator process
For example, if you use Node.js, a simple Dockerfile could look like this:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "simulator.js"]
Compose Configuration and Environment Variables
Your compose.yml should define a kiosk-sim service and pass configuration through environment variables.
At minimum, support configuration for:
- the number of kiosks to simulate, such as
KIOSK_SIM_KIOSKS=5 - the API base URL, such as
KIOSK_SIM_API_BASE_URL=http://api:3000 - a kiosk ID prefix, such as
KIOSK_SIM_KIOSK_PREFIX=kiosk - the delay between submissions, such as
KIOSK_SIM_INTERVAL_MS=1000
You may add more variables if they help your design. Useful optional examples include:
KIOSK_SIM_MAX_ORDERS_PER_KIOSKKIOSK_SIM_RETRY_RATEKIOSK_SIM_REUSE_EXISTING_ID_RATEKIOSK_SIM_STARTUP_DELAY_MSKIOSK_SIM_ITEMS
The goal is that it is easy to change the load pattern by editing
compose.yml rather than editing source code. It is an important way to think
about designing systems - the more toggles you provide to a component in your
system, the easier it is to observe its behavior without changing the code.
What the Simulator Code Should Look Like
The implementation should be small and readable. A good design would usually include:
- Configuration loading from environment variables
- A helper that generates an order payload and stable
clientOrderId - A function that represents one kiosk's behavior
- A launcher that starts
Nkiosk tasks in parallel - Logging that shows which kiosk sent which order and what response came back
- A retry path that can intentionally resend a previously used
clientOrderId
A good ID strategy for the simulator is:
- give each kiosk a unique
kioskId, such askiosk-01 - let each kiosk maintain its own local counter
- build IDs such as
kiosk-01-0001,kiosk-01-0002, and so on
That approach makes two things clear:
- different kiosks should not collide because their kiosk IDs differ
- retries from the same kiosk can intentionally reuse the same
clientOrderId
In pseudocode, the structure might look like this:
read env config
for kioskId in 1..N:
start async kiosk loop
kiosk loop:
while work remains:
choose new or reused clientOrderId
build order payload
POST /orders to api
optionally retry some logical orders with the same clientOrderId
sleep for configured delay
If you use JavaScript, a simple version could look like this:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const sendOrder = async payload => {
const response = await fetch('http://api:3000/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
// Check the response.
// Log useful information.
// Return parsed response data if you want.
}
const runKiosk = async kioskId => {
while (true) {
// Decide whether this kiosk should create a new clientOrderId
// or reuse a previous one to simulate a retry.
const payload = {
// Build a clientOrderId that includes the kioskId.
// Add item, quantity, customer, and any other fields.
}
await sendOrder(payload)
await sleep(1000)
}
}
const main = async () => {
const kioskIds = [
// Create one kiosk ID per simulated kiosk.
]
await Promise.all(kioskIds.map(kioskId => runKiosk(kioskId)))
}
main().catch(error => {
console.error(error)
process.exit(1)
})
That example is intentionally minimal. It shows the main ideas:
- each kiosk gets its own kiosk ID
- each kiosk creates its own
clientOrderIdvalues - retries can intentionally reuse the previous
clientOrderId - all kiosks run in parallel with
Promise.all
Your code does not need to be elaborate. The important thing is that it clearly simulates parallel kiosks and helps demonstrate the behavior of the rest of the system.
Required System Behavior
Your implementation must demonstrate all of the following:
- The API accepts work quickly and does not perform the simulated fulfillment inline.
- The worker processes queued orders asynchronously.
- The
kiosk-simservice can simulate multiple kiosks placing orders in parallel. - Redis stores enough information to make system state visible and understandable.
- The kiosk simulator page uses plain HTML form handling.
- The monitoring dashboard uses plain HTML and makes refreshed state easy to observe.
- Repeating the same logical order does not lead to duplicate completed effects.
- A grader can reproduce the behavior from documented commands and logs.
Idempotency Requirement
You must explicitly describe your idempotency strategy in the write-up.
For this assignment, idempotency means:
If the same logical order enters the system more than once with the same client-created identifier, the system should recognize that it is the same logical operation and avoid producing duplicate completed work.
This is why the client-generated ID matters so much.
If the browser or kiosk simulator sends:
clientOrderId = kiosk-01-0007
and then later sends the same clientOrderId again, your system should treat that as a retry or duplicate submission of the same logical order, not as a brand-new order.
What idempotency does not mean:
- It does not mean every request is rejected after the first one
- It does not mean the client cannot retry
- It does not mean the server must return an error for duplicates
What it does mean:
- The client may safely retry
- The API may safely receive the same logical order more than once
- The worker should not produce duplicate completed side effects for that same logical order
For this assignment, you may assume that once a worker successfully claims a logical order for processing, that worker does not crash before either completing the work or recording the completed result. You do not need to design crash recovery for a worker that fails after claiming the order.
Recommended pattern:
- Build a stable key from
clientOrderId - Use Redis to claim that logical operation once
- Skip duplicate processing when the claim already exists
If you use SET ... NX in Redis, explain where it runs and why that placement protects correctness.
You may enforce this at the API boundary, in the worker, or in both places. The important thing is that your explanation matches your implementation.
A useful way to think about the flow is:
- The client creates
clientOrderId - The API receives the request and checks whether that logical order is already known
- If it is new, the API stores state and queues work
- If it is a duplicate, the API returns the existing state instead of creating new work
- If duplicate work somehow reaches the worker, the worker should still avoid completing the same logical order twice
Why not let the API create the idempotency identifier?
Because then every retry would arrive without a stable pre-existing identity. The server would see repeated requests, but it would have no reliable client-provided way to know that they represent the same logical order.
Why not build the identifier from customer name plus date and time alone?
Because retries might happen at a different time, and different legitimate orders can share similar customer data. A client-generated stable identifier is simpler and more reliable.
For this homework, a very reasonable pattern is:
- browser kiosk page creates
clientOrderId = kiosk-ui-0001 - browser retry reuses
clientOrderId = kiosk-ui-0001 - simulator kiosk
kiosk-03createsclientOrderId = kiosk-03-0042 - simulator retry reuses
clientOrderId = kiosk-03-0042
If your system can explain and demonstrate that pattern clearly, you are handling the core idempotency idea correctly.
Suggested Sprint Plan
Do the work in this order. This is the safest path through the assignment.
Step 1: Start With the Containers
Before you build the whole feature, make sure you can start:
- the API container
- the worker container
- the kiosk-sim container
- the Redis container
The first small win is getting docker compose up --build to work. Your services do not need to do anything to have them up and running - from there you can incrementally add features.
Step 2: Build the API Before the UI
Get POST /orders and GET /orders/:clientOrderId working first.
At this stage, it is okay if you test with curl or Postman before you build the HTML pages.
Your first goal is:
- Accept an order
- Store it
- Return a fast response
Step 3: Add the Queue
Once the API can accept an order, change it so it does not do the slow work directly.
Instead:
- Store the order state
- Put the job into Redis
- Return quickly
This is where the system starts to feel like a real scalable distributed application.
Step 4: Build the Worker
Now make the worker:
- Wait for jobs
- Take a job from Redis
- Simulate the work with a short delay
- Update order state
Once this works, your monitoring dashboard will have something meaningful to show.
Step 5: Add the Kiosk Page
Build a simple page with a form.
A good kiosk page should let the user:
- Enter order details
- Submit through a normal HTML form
- See a response right away
Keep it simple. A form and a response area are enough.
Step 6: Add the Kiosk Simulator Service
Build the separate kiosk-sim service after the core API and worker flow is working.
At this stage, your goal is to create a process that:
- Starts
Nsimulated kiosks - Sends orders concurrently to the API
- Gives each kiosk a distinct kiosk identifier
- Generates
clientOrderIdvalues that include the kiosk identity - Sometimes reuses a previously sent
clientOrderIdto simulate retries safely - Uses configurable environment variables from
compose.yml - Produces logs that help you observe parallel submissions
Keep this service small. It is a simulator, not a full application.
Step 7: Add the Monitoring Dashboard
Build a second page that helps someone watch the system.
A good monitoring dashboard might show:
- Recent orders
- Current status of each order
- Whether an order was a duplicate
- Timestamps or attempt counts
You can use a normal browser refresh, a dedicated refresh button, or a short timed refresh to make state changes visible. This should not be fancy - otherwise you risk spending too much time on things that are not important for the assignment.
Step 8: Add Idempotency Last
Once the normal flow works, test duplicate submissions.
Try sending the same logical order twice. Then make sure:
- The system does not do duplicate completed work
- The duplicate is visible in logs or state
- Your write-up explains why the design is safe
This last step turns the project from "works on the happy path" into "works like a real system."
Design Tips
If you are not sure how to organize the data, keep it simple.
One reasonable approach is:
- One Redis list for queued jobs
- One Redis key or hash per order
- One stable
clientOrderIdper logical order - One simple kiosk loop per simulated kiosk in the
kiosk-simservice
You do not need a database. Redis is enough for this assignment.
You also do not need fancy frontend code. Plain HTML plus server-rendered pages is enough.
Common Mistakes To Avoid
- Doing the slow work inside the request handler instead of in the worker.
- Building the UI first before the API contract works.
- Forgetting to store enough state to power the monitoring dashboard.
- Treating duplicate requests as an error instead of handling them safely.
- Generating the idempotency identifier on the server instead of having the client send a stable one.
- Using kiosk IDs that can collide across simulated kiosks.
- Hard-coding the kiosk simulator behavior instead of making it configurable through Compose environment variables.
- Making the system too large or too complicated.
- Submitting code without testing the full flow from browser to worker.
Suggested Validation Flow
You may use any commands you want, but your evidence should cover at least this flow:
docker compose up --build -d
docker compose logs -f worker
docker compose logs -f kiosk-sim
open the kiosk page in a browser
submit one order through the kiosk page
let the kiosk-sim service submit several orders in parallel
submit the same logical order again through the kiosk page using the same clientOrderId
open the monitoring dashboard and observe status changes
curl http://localhost:3000/orders/kiosk-01-0001
docker compose down
Your exact ports, field names, and output format may vary, but the evidence must make these behaviors clear:
- First submission is accepted and queued
- Parallel kiosk submissions can be observed
- Duplicate submission is recognized
- The kiosk page works through normal browser form submission
- A previously used client-generated ID can be resubmitted to demonstrate safe retries
- The monitoring dashboard clearly shows updated state after refresh or revisit
- Worker processes the order once
- Final order state is visible
Evidence Deliverables
Your evidence bundle should help us understand the system in a few minutes.
Include clear proof of these moments:
- The stack starts successfully
- The kiosk page is visible
- A new order is accepted
- The kiosk simulator generates parallel order activity
- The monitoring dashboard shows status changes
- A duplicate order is handled safely
- The worker logs show background processing
Good evidence is more valuable than a long explanation.
Submission Deliverables
Submit one folder named hw01/ with this exact structure:
hw01/
brief.md
shots/
vids/
code/
What Goes in Each Part
brief.md Implementation Brief
This is the main implementation brief that we will read.
It should include:
- Your name, umass email, spire id
- How to run the project
- How to test the project
- A short architecture explanation
- A short explanation of the
kiosk-simservice and its environment variables - A short explanation of your idempotency strategy, including how
clientOrderIdis created and reused - An AI Use Statement
- Links to the screenshots you want us to review
- Links to the videos you want us to review
Important rule:
We will only review screenshots and videos that are linked from
brief.md. We will only review enough to feel like it provides enough evidence. The videos, if any are provided, should be very short and self-explanatory.
If a file exists in shots/ or vids/ but is not linked from brief.md, you should assume it will not be reviewed.
shots/
Put screenshots here.
Use screenshots to show clear moments such as:
- The kiosk page
- The kiosk-sim logs or console output
- The monitoring dashboard
- A successful order submission
- Parallel kiosk activity
- A duplicate submission being handled safely
- Worker logs or status output
vids/
Put short video clips here.
Good videos are very short and focused. A few small clips are better than one long confusing video.
Useful video ideas:
- Placing an order from the kiosk page
- Watching the kiosk-sim service generate concurrent requests
- Watching the monitoring dashboard update
- Showing duplicate protection working
code/
Put your implementation here.
This folder should contain the actual project code, including:
- A dedicated folder for the API service called
api - A dedicated folder for the worker service called
worker - A dedicated folder for the kiosk simulator service called
kiosk-sim - The HTML views
- Dockerfiles
docker-compose.ymlorcompose.yml- Any small helper files needed to run the project
The grader should be able to enter code/ and run your project from there. Here is the recommended code folder structure:
.
├── api
│ ├── Dockerfile
│ └── server.js
├── compose.yml
├── kiosk-sim
│ ├── Dockerfile
│ └── simulator.js
├── redis
│ └── Dockerfile
└── worker
├── Dockerfile
└── server.js
The redis/ directory shown above is optional. Include it only if you are customizing the Redis container. If you use the standard Redis image directly from Docker Hub with no changes, compose.yml may reference that image and no code/redis/ folder is needed.
Scope Guidance
Do not overbuild this.
A strong submission is a small, clean system with:
- One API service
- One worker service
- One kiosk simulator service
- One Redis service
- One queue
- One simple kiosk page
- One simple kiosk-sim process that generates parallel traffic
- One simple monitoring dashboard
- One clear idempotency rule
- Clear reproducible evidence
You are practicing system structure and correctness, not building a feature-rich application.
If you are deciding between:
- A smaller system that works clearly
- A bigger system with extra features that are half-finished
pick the smaller system.
Required Deliverables
Submit a .zip file to Canvas by the assigned due date.
The zip file should contain exactly one folder named hw01/.
- One folder named
hw01/- with
brief.md,shots/,vids/, andcode/
- with
- A completed
brief.md- explain the architecture, HTML request flow, Redis usage, kiosk-sim design, idempotency strategy, and AI use
- Screenshots in
shots/- only screenshots linked from
brief.mdwill be reviewed
- only screenshots linked from
- Videos in
vids/- only videos linked from
brief.mdwill be reviewed
- only videos linked from
- Implementation in
code/- include the full runnable project
AI Use Expectation
AI use is expected and encouraged for this assignment.
This is not because the assignment should be outsourced. It is because the assignment asks you to connect several tools and layers quickly: Docker, Compose, Express, Redis, HTML, asynchronous workers, a small simulator service, and idempotency. Using AI for scaffolding, debugging, explaining errors, drafting handlers, or sketching templates is reasonable here.
You are still responsible for:
- understanding the code you submit
- verifying that the system actually works
- checking that duplicate requests do not create duplicate completed work
- accurately documenting how you used AI
Good ways to use AI on this assignment:
- ask for a starter Dockerfile
- ask for help wiring an HTML form submission flow
- ask for help debugging Redis connection errors
- ask for a simple worker loop
- ask for a simple kiosk simulator loop that runs
Nclients in parallel - ask how to explain idempotency in plain language
Bad use of AI:
- pasting in code you do not understand
- trusting generated code without testing it
- claiming behavior works when you did not actually verify it
Grading
Teaching staff should primarily grade the submission by reading brief.md.
The goal is fast, high-signal grading based on the document and its linked evidence, not on manually running code.
code/, shots/, and vids/ are still part of the submission, but we will mostly review only what is surfaced clearly through brief.md. Manual code execution is not expected during normal grading. If simple automated checks are available, staff may use them for basic validation, but the grading workflow should remain document-first.
Teaching Staff Guidance
Knock-Out Checklist
Check these first:
hw01/folder is presentbrief.mdis presentbrief.mdincludes student name, UMass email, and SPIRE IDbrief.mdlinks the screenshots and videos to reviewcode/,shots/, andvids/are presentbrief.mdincludes an AI Use Statement
If basic submission materials are missing, note that before doing detailed grading.
Quick Grading Rubric
| Criterion | Exceeds | Meets | Needs Improvement | Below Expectations | No Submission |
|---|---|---|---|---|---|
| Submission completeness | brief.md is complete, well organized, and makes the submission easy to review quickly. | brief.md is present and covers the expected sections well enough to review. | brief.md is present but uneven, thin, or missing some important parts. | brief.md is confusing, incomplete, or difficult to use. | No meaningful brief.md submission. |
| Architecture and implementation explanation | The document explains the system clearly, with strong structure and useful technical choices. | The document explains the main system structure and implementation choices clearly enough. | The explanation is partly clear but leaves important gaps. | The explanation is weak, vague, or hard to follow. | No meaningful explanation of the system. |
| Evidence of working behavior | Linked screenshots and videos make the main behaviors easy to see and easy to trust. | Linked evidence supports the main claims and is sufficient for quick review. | Some evidence exists, but it is incomplete, weak, or hard to interpret. | Evidence is sparse, confusing, or does not support the claims well. | No meaningful linked evidence to review. |
| Correctness reasoning | The document gives a clear, convincing explanation of async flow, system state, and idempotency. | The document explains the core correctness ideas well enough to support the submission. | Correctness reasoning is partly present but weak, shallow, or incomplete. | Correctness reasoning is unclear, incorrect, or mostly missing. | No meaningful reasoning about correctness. |
| Professional communication | The document is concise, readable, and includes the required metadata, AI disclosure, and useful evidence links. | The document is readable and includes the required metadata and disclosure items. | Communication is uneven or some required metadata or disclosure details are weak. | Communication is hard to follow or missing important required information. | No meaningful communication artifact. |
Fast Grading Guidance
- Start with the knock-out checklist.
- Read
brief.mdfirst. - Only review screenshots and videos linked from
brief.md. - Use best-fit judgment for each row.
- Prioritize whether the document makes the claimed work easy to understand and easy to verify.
Choose the category that matches what you see in the submission quickly. Do not worry about mistakes. If a student sees a mistake or has a valid argument for a different grade assignment for a particular criteria, they will contact you.