Error correcting codes (or just codes) are systematic ways of introducing redundancy into data so that the original information can be recovered even when the data is corrupted. Codes are used ubiquitously in communication systems and data storage. The study of error correcting codes (or coding theory) started with the seminal works of Shannon and Hamming in the late 1940s and has been an active cross-disciplinary research area since then. This course will discuss the theoretical aspects of codes and will focus mostly on the worst-case noise model pioneered by Hamming. However, we will discuss quite a few results on the stochastic noise model pioneered by Shannon. The course will roughly cover three parts: (i) combinatorial aspects of codes, i.e. the limit of what can and cannot be achieved with codes; (ii) computationally efficient algorithms for using codes; and (ii) application of codes in theoretical computer science. Major developments in coding theory since the 1990s will be emphasized. This course is dual listed with CSE 445.