Progressive Programming Language Exercises
Curriculum Philosophy
The purpose of these exercises is not to teach language syntax in isolation.
Each project should force the learner to encounter the constructs that are commonly required when building real software:
- data modelling
- control flow
- functions and modularity
- collections
- error handling
- file I/O
- resource management
- abstraction
- serialization
- networking
- concurrency
- testing
- packaging
- process management
- performance
- failure handling
General rules
- Use the language's standard library initially.
- Do not introduce a framework merely to avoid implementing something.
- Each exercise should begin with a minimal implementation.
- Subsequent milestones add requirements rather than replacing the original implementation.
- The learner should write tests as soon as the program becomes non-trivial.
- Error paths are part of the exercise, not optional polish.
- The learner should be able to explain why a particular language construct was used.
- Avoid giving learners the implementation architecture upfront. Give them requirements and let the architecture emerge.
1. C
C should primarily teach the learner to reason about:
memory, representation, pointers, resources, processes, and the operating system.
Exercise 1 — Binary File Inspector
Objective
Build a command-line program capable of inspecting arbitrary files.
Command
bininspect <file>Example:
File: example.bin
Size: 182734 bytes
Printable ASCII: 63.4%
Zero bytes: 12483
Unique byte values: 247Milestone 1 — Basic File Reading
Implement:
- command-line argument parsing
- file opening
- file reading
- file size calculation
- error reporting
Milestone 2 — Byte Analysis
Calculate:
- frequency of every byte value
- number of zero bytes
- number of printable characters
- minimum/maximum byte value
Milestone 3 — Hex Dump
Implement:
bininspect --hex example.binOutput:
00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64
0000000b ...Milestone 4 — String Extraction
Implement:
bininspect --strings example.binExtract printable character sequences.
Concepts deliberately encountered
- primitive types
- arrays
- pointers
- pointer arithmetic
charunsigned charsize_t- structs
- functions
- command-line arguments
- file descriptors /
FILE * fopenfreadfseekmallocfree- header files
- compilation and linking
Completion criterion
The learner should be able to explain:
- why binary data should generally be treated as
unsigned char - why
sizeofis preferable to assumptions about type sizes - who owns dynamically allocated memory
- what happens when
fread()fails
Exercise 2 — Generic Data Structure Library
Build a small reusable collection library.
Implement:
vector
linked_list
hash_map
stringExample API
vector_t *vector_create(size_t element_size);
int vector_push(
vector_t *vector,
const void *element
);
void *vector_get(
vector_t *vector,
size_t index
);
void vector_destroy(vector_t *vector);Milestone 1
Implement a dynamic vector.
Requirements:
- automatic growth
- indexed access
- insertion
- removal
- destruction
Milestone 2
Make the vector generic.
It must support:
int
double
struct User
struct Packetwithout duplicating the implementation.
Milestone 3
Add callbacks for:
- comparison
- destruction
- iteration
Milestone 4
Build a hash map.
key → valueConcepts deliberately encountered
- pointers
- pointer-to-pointer
- dynamic allocation
- ownership
- opaque structures
- header/source separation
- function pointers
- callbacks
conststatic- macros
- generic programming using
void * - memory layout
Completion criterion
The learner should be able to answer:
What exactly is stored in memory when a
vector_tcontains 100 structures?
Exercise 3 — Mini Unix Shell
Build:
myshellInitial commands:
$ pwd
$ cd /tmp
$ echo hello
$ cat file.txt
$ ./program argumentMilestone 1
Implement:
- command parsing
- argument parsing
- built-in
cd - built-in
exit - external program execution
Milestone 2
Add:
program > output.txt
program < input.txtMilestone 3
Add pipelines:
cat file.txt | grep helloMilestone 4
Add background execution:
long-running-program &Milestone 5
Add signal handling.
Concepts
forkexecwait- processes
- process IDs
- file descriptors
- pipes
- signals
- environment variables
- process lifecycle
- parsing
- memory management
Completion criterion
The learner should be able to describe the lifecycle of:
ls | grep foo > output.txtfrom shell input to process termination.
2. C++
C++ should teach:
abstraction + resource ownership + generic programming + concurrency.
Exercise 1 — In-Memory Database
Build:
dbcliSupport:
CREATE USER
INSERT USER 42 Alice
GET USER 42
DELETE USER 42
LIST USERSMilestone 1
Implement classes representing:
User
Database
CommandMilestone 2
Use STL containers.
Milestone 3
Add validation and exceptions.
Milestone 4
Add multiple entity types.
User
Order
ProductConcepts
- classes
- constructors
- destructors
- methods
- references
conststd::stringstd::vectorstd::unordered_map- enums
- namespaces
- exceptions
- iterators
- algorithms
Exercise 2 — Persistent Database
Extend the database to persist data.
dbcli
↓
Database
↓
Storage
↓
database.dbMilestone 1
Binary serialization.
Milestone 2
Deserialization.
Milestone 3
Crash-safe writes.
Milestone 4
Add an index.
Milestone 5
Add transactions:
BEGIN
INSERT ...
UPDATE ...
COMMIT
ROLLBACKConcepts
- RAII
- smart pointers
std::unique_ptrstd::shared_ptr- move semantics
- copy semantics
- move constructors
- templates
- serialization
- filesystem
- exception safety
- resource ownership
Exercise 3 — HTTP Server
Build:
cpphttp --port 8080 --root ./wwwMilestone 1
Implement TCP connection handling.
Milestone 2
Parse:
GET /index.html HTTP/1.1Milestone 3
Generate HTTP responses.
Milestone 4
Serve static files.
Milestone 5
Implement routing.
GET /users
GET /users/42
POST /usersMilestone 6
Implement concurrency.
First:
one thread / connectionThen:
thread poolMilestone 7
Implement graceful shutdown.
Concepts
- sockets
- STL
- RAII
- smart pointers
- move semantics
- lambdas
- threads
- mutexes
- condition variables
- atomics
- filesystem
- error handling
- HTTP
Intermediate checkpoint
The learner should understand why RAII is particularly useful for:
socket
file
mutex
connection
memory3. Rust
Rust should deliberately force learners to understand:
ownership, borrowing, lifetimes, algebraic data types, traits, error handling, and safe concurrency.
Exercise 1 — Text Statistics
Build:
textstat file.txtOutput:
Lines: 1432
Words: 21894
Characters: 127331
Unique words: 4821Milestone 1
Basic file reading.
Milestone 2
Word counting.
Milestone 3
Frequency table.
Milestone 4
Add:
textstat --top 20 file.txtMilestone 5
Process multiple files.
Concepts
- variables
- mutability
- primitive types
String&str- slices
Vec<T>HashMap- functions
- modules
- iterators
OptionResult- pattern matching
- ownership
- borrowing
Critical exercise
Rewrite the program so that it does not load the entire input file into memory.
The learner should discover streaming and borrowing naturally.
Exercise 2 — Log Processing Engine
Input:
2026-09-14T10:32:11 GET /api/users 200 421
2026-09-14T10:32:12 GET /api/users 200 391
2026-09-14T10:32:13 POST /api/users 500 128Build:
logproc access.logOutput:
Requests: 100023
Successful: 99431
Errors: 592
Average latency: 31ms
P95 latency: 112msMilestone 1
Define:
struct LogEntry {
...
}Milestone 2
Introduce:
enum ParseError {
...
}Milestone 3
Implement filtering.
Milestone 4
Implement streaming processing.
Milestone 5
Add unit tests.
Milestone 6
Implement configurable output.
Milestone 7
Investigate zero-copy parsing.
Concepts
- structs
- enums
impl- methods
- traits
- generic functions
- iterators
- closures
- pattern matching
Result<T, E>Option<T>?- custom errors
- lifetimes
- borrowing
- zero-copy data
Exercise 3 — Concurrent TCP Server
Build:
kvserver 127.0.0.1:9000Protocol:
SET name Alice
GET name
DELETE nameMilestone 1
TCP server.
Milestone 2
Request parser.
Milestone 3
Key-value store.
Milestone 4
Multiple clients.
Milestone 5
Shared state.
Milestone 6
Worker pool.
Milestone 7
Graceful shutdown.
Concepts
TcpListenerTcpStream- threads
- channels
ArcMutexSendSync- ownership across threads
- synchronization
Drop- error propagation
Intermediate checkpoint
The learner should be able to explain why Rust rejects data races at compile time rather than relying solely on runtime discipline.
4. Java
Exercise 1 — File-Backed CLI
Build:
notes add "Learn Java"
notes list
notes delete 3Milestone 1
In-memory implementation.
Milestone 2
File persistence.
Milestone 3
Introduce interfaces.
NoteRepository
FileNoteRepositoryMilestone 4
Add validation and error handling.
Milestone 5
Add tests.
Concepts
- classes
- objects
- constructors
- methods
- interfaces
- inheritance
- polymorphism
- collections
- generics
- enums
- exceptions
- streams
- file I/O
- Maven
Exercise 2 — HTTP Server Using Sockets
Build an HTTP server using:
ServerSocket
Socket
InputStream
OutputStreamDo not use a servlet container.
Milestone 1
Accept a TCP connection.
Milestone 2
Parse an HTTP request.
Milestone 3
Return:
HTTP/1.1 200 OKMilestone 4
Implement routing.
GET /
GET /users
GET /users/42Milestone 5
Implement POST bodies.
Milestone 6
Implement error responses.
400
404
405
500Milestone 7
Add concurrent connections.
Concepts
- networking
- byte streams
- character encoding
- interfaces
- classes
- collections
- exceptions
- threads
ExecutorService- HTTP
- Maven
Exercise 3 — Dynamic WAR/Application Loader
Extend the HTTP server so that applications can be loaded dynamically.
Directory:
server/
applications/
application-a.war
application-b.warMilestone 1
Read a WAR file as an archive.
Milestone 2
Discover application metadata.
Milestone 3
Load classes dynamically.
Milestone 4
Define an application interface.
interface WebApplication {
Response handle(Request request);
}Milestone 5
Use separate class loaders.
Milestone 6
Implement application lifecycle.
load
start
handle
stop
unloadConcepts
- JAR/WAR structure
- class loaders
- reflection
- interfaces
- dynamic class loading
- lifecycle management
- resource management
- concurrency
- Maven
Intermediate checkpoint
The learner should understand the distinction between:
Java language
JVM
class loading
application framework
application serverrather than treating them as one system.
5. Python
Python should expose:
dynamic data modelling, modules, exceptions, iterators, generators, packaging, external APIs, persistence, and concurrency.
Exercise 1 — CSV Data Processor
Build:
csvtool data.csvSupport:
csvtool --count data.csv
csvtool --filter country=IN data.csv
csvtool --sort age data.csv
csvtool --group country data.csvMilestone 1
Read CSV.
Milestone 2
Represent records using dictionaries.
Milestone 3
Implement filtering.
Milestone 4
Implement sorting.
Milestone 5
Implement grouping.
Milestone 6
Add argparse.
Milestone 7
Add generators so large files can be processed without loading everything into memory.
Concepts
- variables
- lists
- dictionaries
- tuples
- sets
- functions
- comprehensions
- modules
- exceptions
- iterators
- generators
argparse- file I/O
Exercise 2 — REST Data Importer
Build:
REST API
↓
requests
↓
validation
↓
SQLAlchemy
↓
database
↓
CLIMilestone 1
Retrieve JSON from an API.
Milestone 2
Parse the response.
Milestone 3
Create SQLAlchemy models using declarative_base.
Milestone 4
Persist the records.
Milestone 5
Handle:
- pagination
- timeouts
- retries
- HTTP errors
- malformed responses
Milestone 6
Make imports idempotent.
Running:
mytool import
mytool importshould not create duplicates.
Milestone 7
Package it.
pip install mytoolMilestone 8
Provide:
mytool import
mytool list
mytool reportConcepts
- external packages
- virtual environments
- modules
- packages
- classes
- exceptions
- decorators
- context managers
- type hints
- HTTP
- SQLAlchemy
- CLI design
- packaging
Exercise 3 — Concurrent Job Runner
Build:
jobrunner jobs.yamlExample:
jobs:
- name: download
command: download.py
- name: process
command: process.py
depends_on:
- download
- name: report
command: report.py
depends_on:
- processMilestone 1
Sequential execution.
Milestone 2
Dependency handling.
Milestone 3
Parallel execution of independent jobs.
Milestone 4
Retries.
Milestone 5
Timeouts.
Milestone 6
Cancellation.
Milestone 7
Persistent job state.
Concepts
threadingmultiprocessingasyncio- queues
- futures
- subprocesses
- context managers
- decorators
- type hints
- serialization
- concurrency models
Intermediate checkpoint
The learner should be able to explain when to use:
threading
multiprocessing
asyncioand why.
6. TypeScript + Node.js
The distinction here is important:
Teach TypeScript as a language and Node.js as a runtime.
Do not start with Express.
Exercise 1 — Typed CLI Application
Build:
todo add "Learn TypeScript"
todo list
todo done 4
todo remove 4Persist to JSON.
Milestone 1
In-memory implementation.
Milestone 2
Define interfaces/types.
interface Todo {
id: number;
text: string;
completed: boolean;
}Milestone 3
Persistence.
Milestone 4
Error handling.
Milestone 5
Async file operations.
Milestone 6
Unit tests.
Concepts
- primitive types
- interfaces
- type aliases
- unions
- arrays
- objects
- functions
- optional properties
- generics
- modules
- promises
async/await- Node filesystem APIs
Exercise 2 — REST Synchronizer
Build:
REST API
↓
HTTP client
↓
unknown JSON
↓
validation
↓
domain model
↓
databaseMilestone 1
Retrieve remote JSON.
Milestone 2
Treat external input as:
unknownrather than:
anyMilestone 3
Validate the response.
Milestone 4
Map API DTOs into domain objects.
Milestone 5
Persist locally.
Milestone 6
Implement retries and timeouts.
Milestone 7
Handle pagination.
Milestone 8
Build a CLI.
Concepts
- interfaces
- type aliases
- generics
- discriminated unions
- type narrowing
unknownnever- promises
- async/await
- modules
- error handling
- runtime validation
- Node APIs
Critical lesson
The learner must explicitly understand:
TypeScript types
≠
runtime validationExternal JSON is untrusted runtime data regardless of its declared TypeScript type.
Exercise 3 — HTTP Server Without Express
Build:
http.createServer(...)Milestone 1
Return:
Hello WorldMilestone 2
Parse HTTP requests.
Milestone 3
Implement routing.
GET /
GET /users
GET /users/:idMilestone 4
Implement POST.
Milestone 5
Implement middleware.
request
↓
logging middleware
↓
authentication middleware
↓
router
↓
handlerMilestone 6
Implement streaming responses.
Milestone 7
Implement graceful shutdown.
Milestone 8
Investigate backpressure.
Concepts
- Node event loop
- callbacks
- promises
- async/await
- events
- streams
- buffers
- backpressure
- closures
- higher-order functions
- generics
- discriminated unions
- module architecture
Intermediate checkpoint
The learner should understand why Node can handle many concurrent I/O operations without creating one OS thread per request.
7. POSIX Shell
The purpose of shell exercises is not to turn the learner into a shell-language programmer.
It is to teach:
Unix processes + pipelines + file descriptors + exit status + orchestration.
Exercise 1 — Backup Utility
Build:
backup source destinationExample:
backup ~/documents /backupProduce:
/backup/documents-2026-09-14.tar.gzMilestone 1
Copy files.
Milestone 2
Recursive directory handling.
Milestone 3
Compression.
Milestone 4
Checksums.
Milestone 5
Dry-run mode.
backup --dry-run ...Milestone 6
Logging.
Concepts
- variables
- quoting
- command substitution
- positional parameters
- exit codes
- pipelines
- redirection
- globbing
findtar- compression
- checksums
Exercise 2 — Deployment Pipeline
Build:
deploy.shPipeline:
validate
↓
build
↓
test
↓
package
↓
deploy
↓
health checkRequirement
If any step fails, subsequent steps must not execute.
Add
deploy.sh --dry-run
deploy.sh --rollback
deploy.sh --verboseConcepts
- exit status
- command chaining
- environment variables
- temporary files
- traps
- signals
- atomic operations
- pipeline failure
- process orchestration
Critical checkpoint
The learner must understand why:
A | Bdoes not necessarily propagate the failure of A in every shell configuration.
8. Bash
After the POSIX exercise, move to Bash-specific functionality.
Exercise 1 — System Information Utility
Build:
sysinfoOutput:
Hostname:
Kernel:
Architecture:
CPU:
Memory:
Disk:
Uptime:
Processes:Milestone 1
Collect information.
Milestone 2
Format it.
Milestone 3
Add options.
sysinfo --cpu
sysinfo --memory
sysinfo --disk
sysinfo --jsonConcepts
- variables
- functions
case- parameter expansion
- command substitution
- arrays
- loops
- exit status
- functions
Exercise 2 — Deployment Pipeline
Repeat the POSIX deployment exercise, but this time require Bash-specific constructs.
Implement:
deploy.shwith:
- functions
- arrays
- configuration variables
trap- cleanup
- logging
- rollback
Concepts
- Bash arrays
- associative arrays
local[[ ]]- parameter expansion
- functions
- traps
- subshells
- file descriptors
Exercise 3 — Parallel Job Executor
Build:
batchrun -j 4 jobs.txtInput:
./job1.sh
./job2.sh
./job3.sh
./job4.sh
./job5.shAt most four jobs may execute simultaneously.
Output:
job1 SUCCESS 12.3s
job2 FAILED 4.2s
job3 SUCCESS 8.1s
job4 SUCCESS 9.7s
job5 SUCCESS 2.4sMilestone 1
Sequential execution.
Milestone 2
Background processes.
Milestone 3
Limit concurrency.
Milestone 4
Collect exit codes.
Milestone 5
Capture stdout/stderr.
Milestone 6
Handle SIGINT.
Milestone 7
Clean up child processes.
Concepts
- background processes
&wait$!- arrays
- associative arrays
- traps
- signals
- process groups
- file descriptors
- exit status
- concurrency
Intermediate checkpoint
The learner should understand that Bash concurrency is fundamentally process orchestration, not language-level multithreading.
9. Cross-Language Capstone
Once a learner completes the three projects for their chosen language, give them the same final specification.
Capstone — Minimal HTTP File Server
Objective
Build a minimal HTTP/1.1 server without an HTTP framework.
Required functionality
GET /
GET /index.html
GET /file.txt
404
400
405Phase 1 — TCP
Accept connections.
TCP connection
↓
read bytes
↓
closePhase 2 — HTTP
Parse:
GET /index.html HTTP/1.1
Host: localhostPhase 3 — Response
Return:
HTTP/1.1 200 OK
Content-Length: 123
Content-Type: text/html
...Phase 4 — Routing
Implement:
/
/index.html
/about.htmlPhase 5 — Static Files
Map URLs to files.
Phase 6 — Security
Prevent:
GET /../../../etc/passwdfrom escaping the document root.
Phase 7 — Concurrency
Support multiple simultaneous clients.
Phase 8 — Operational Behaviour
Implement:
- configuration
- logging
- graceful shutdown
- connection limits
- malformed-request handling
- timeouts
Phase 9 — Testing
Create tests for:
- valid requests
- malformed requests
- missing files
- traversal attempts
- large files
- concurrent clients
- client disconnects
Phase 10 — Benchmarking
Measure:
requests/sec
latency
memory consumption
CPU utilization10. Language-Specific Learning Outcomes
After completing the curriculum, the learner should have encountered approximately the following.
| Concept | C | C++ | Rust | Java | Python | TypeScript | Shell/Bash |
|---|---|---|---|---|---|---|---|
| Variables/control flow | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Functions | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Collections | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Structs/classes | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Interfaces/traits | ✓ | ✓ | ✓ | ✓ | ✓ | ||
| Generics | ✓ | ✓ | ✓ | ✓ | |||
| Error handling | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| File I/O | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Resource management | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Memory management | ✓ | ✓ | ✓ | ||||
| Ownership | manual | RAII | ownership | GC | GC | GC | |
| Networking | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Concurrency | processes | threads | threads | threads | threads/async | async | processes |
| Packaging | compiler/linker | build system | Cargo | Maven | pip | npm | scripts |
| Reflection/dynamic loading | ✓ | ✓ | |||||
| Type system depth | low | high | very high | high | dynamic | high | low |
| OS/process model | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ | ✓✓ |
11. Recommended Completion Gates
A learner should not advance merely because the program "works."
Use these gates.
Beginner → Competent
The learner can:
- build the program independently
- explain the major data structures
- handle normal errors
- split code into modules
- write basic tests
- use the compiler/build system independently
- read standard-library documentation
Competent → Intermediate
The learner can:
- redesign part of the application
- identify ownership/resource boundaries
- handle malformed input
- reason about failure
- introduce concurrency
- diagnose race/resource issues
- write meaningful tests
- package the program
- measure basic performance
- explain trade-offs in their implementation
12. Suggested Evaluation Rubric
Each project can be evaluated out of 100.
| Area | Points |
|---|---|
| Correct functionality | 25 |
| Error handling | 15 |
| Code organisation | 10 |
| Language idioms | 15 |
| Tests | 10 |
| Resource management | 10 |
| Documentation | 5 |
| Performance | 5 |
| Debugging/diagnostic quality | 5 |
The language idioms category is particularly important.
A program that works but looks like C written in C++, Java written in Python, or JavaScript written in TypeScript should not receive full credit.
13. Recommended "No Framework" Rule
For the first three exercises in each language:
| Language | Permitted |
|---|---|
| C | C standard library + OS APIs |
| C++ | C++ standard library + OS APIs |
| Rust | std + Cargo |
| Java | JDK + Maven |
| Python | Python stdlib initially; requests/SQLAlchemy in Exercise 2 |
| TypeScript | TypeScript + Node.js standard APIs |
| POSIX Shell | POSIX utilities |
| Bash | Bash + standard Unix utilities |
Only after the learner completes the framework-free implementations should they be allowed to replace portions with mature libraries.
For example:
Raw Node HTTP server
↓
understand HTTP
↓
Express/Fastify
Raw Java ServerSocket
↓
understand HTTP + concurrency
↓
Spring/Netty
Raw Rust TCP
↓
understand ownership/concurrency/networking
↓
Tokio/Axum/Actix
Raw C++ sockets
↓
understand resource management/concurrency
↓
Asio/Boost/etc.This creates a useful mental model:
Frameworks become abstractions that the learner understands, rather than magic that the learner memorises.
14. Final Curriculum Map
C
├── Binary File Inspector
│ └── memory / pointers / I/O
├── Generic Data Structure Library
│ └── allocation / callbacks / abstraction
└── Unix Shell
└── processes / pipes / signals / file descriptors
C++
├── In-Memory Database
│ └── classes / STL / exceptions
├── Persistent Database
│ └── RAII / smart pointers / move semantics / templates
└── HTTP Server
└── sockets / threads / synchronization / RAII
Rust
├── Text Statistics
│ └── ownership / borrowing / Result / iterators
├── Log Processing Engine
│ └── structs / enums / traits / lifetimes
└── Concurrent TCP Server
└── Arc / Mutex / channels / Send / Sync
Java
├── File-Backed CLI
│ └── classes / interfaces / collections / Maven
├── Socket HTTP Server
│ └── networking / streams / concurrency
└── Dynamic WAR Loader
└── reflection / class loaders / dynamic loading
Python
├── CSV Processor
│ └── collections / functions / generators
├── REST → SQLAlchemy → CLI
│ └── packages / HTTP / ORM / argparse
└── Concurrent Job Runner
└── subprocess / threads / multiprocessing / asyncio
TypeScript + Node
├── Typed CLI
│ └── types / interfaces / modules / async
├── REST Synchronizer
│ └── unknown / validation / generics / promises
└── HTTP Server
└── event loop / streams / backpressure / async
POSIX Shell
├── Backup Utility
│ └── commands / pipelines / exit codes
└── Deployment Pipeline
└── orchestration / traps / failure handling
Bash
├── System Information Utility
│ └── Bash language features
├── Deployment Pipeline
│ └── arrays / functions / traps
└── Parallel Job Executor
└── processes / wait / signals / concurrency