Hands-on exercises12 exercises

Clean Code Mastery: exercises

Reading about clean code and doing it are different skills. Every module has a hands-on exercise: a working but messy TypeScript file with a passing test suite that pins its behaviour. You refactor it until it reads clean without turning a test red. Clone the repo, run the tests, and check your work against the reference solution.

How the exercises work

Each exercise ships a working but messy TypeScript file and a passing test suite that pins its behaviour. Your job is to refactor the code clean without turning a test red. A reference solution is checked in for every one.

git clone https://github.com/ehsangazar/self-paced-clean-code-mastery.git
pnpm install
pnpm test        # run every module
pnpm test 03     # run one module
1
Foundations - Why Clean Code Matters

Make an unreadable function readable

A working checkout function with single-letter names, magic numbers, and three levels of nesting. Refactor it for a reader without changing what it returns.

Open on GitHub
2
The Art of Naming

Rename so intent needs no explanation

A function whose every name is cryptic or actively misleading, including a variable called list that holds a count. The logic is easy once the names are honest.

Open on GitHub
3
Functions That Do One Thing

Split a function that does seven things

One function that validates, computes, formats, and logs at once, and mixes a command with a query. Break it into single-purpose pieces.

Open on GitHub
4
Comments and Self-Documenting Code

Delete the comments, keep the meaning

A function drowning in redundant, outdated, and commented-out noise, with one genuine why-comment buried in it. Make the code self-documenting and keep only the comment that earns its place.

Open on GitHub
5
Code Formatting and Aesthetics

Group and order code for the reader

A module whose structure fights the reader: helpers scattered, the entry point buried. Reorder and group it, changing nothing about what it does.

Open on GitHub
6
Control Flow and Logic

Flatten nesting with guard clauses

An access check nested five levels deep, with each denial reason buried in a distant else. Flatten it into guard clauses while preserving every decision.

Open on GitHub
7
Variables and State Management

Shrink scope and kill mutable state

A function leaning on wide mutable variables reused for two purposes, plus bare magic numbers. Tighten the scope, prefer values that do not change, and name the constants.

Open on GitHub
8
SOLID Principles in Practice

Refactor toward dependency inversion

A dispatcher that news up its own dependencies and switches on a type string. Introduce an interface and inject the channels so a new one needs no edits to the dispatcher.

Open on GitHub
9
Error Handling Done Right

Fail fast at the right boundary

A money transfer that validates late and throws one vague error for everything. Move validation to the boundary with guard clauses and typed, specific errors.

Open on GitHub
10
Testing and Test-Driven Development

Write tests that read as a specification

Clean production code paired with a deliberately bad test suite. This one inverts the format: the deliverable is the tests, rewritten to read as a spec.

Open on GitHub
11
Refactoring Legacy Code

Cover untested code, then change it safely

Gnarly untested legacy code with surprising edge cases. Write characterization tests to build a safety net, then refactor behind it.

Open on GitHub
12
The Professional Programmer

Review a change and argue the trade-offs

A real pull request that works but oversteps its ticket. Write the review: what blocks, what is a nit, and how to push back on the scope creep.

Open on GitHub