We study the problem of fairly allocating a set of indivisible goods to a set of people from an algorithmic perspective. Fair division has been a central topic in the economic literature and several concepts of fairness have been suggested. The criterion that we focus on is the maximum envy between any pair of players. An allocation is called envy-free if every player prefers her own share than the share of any other player. When the goods are divisible or when there is sufficient amount of one divisible good, envy-free allocations always exist. In the presence of indivisibilities however this is not the case. We first show that when all goods are indivisible, there always exist allocations in which the envy is bounded by the maximum marginal utility and we present a simple polynomial time algorithm for computing such allocations. We further show that our algorithm can be applied to the continuous cake-cutting model as well and obtain a procedure that produces -envy-free allocations with a linear number of cuts. We then look at the optimization problem of finding an allocation with minimum possible envy. In the general case, there is no polynomial time algorithm (or even approximation algorithm) for the problem, unless P = NP. We consider natural special cases (e.g. additive utilities) which are closely related to a class of job scheduling problems. Polynomial time approximation algorithms as well as inapproximability results are obtained. Finally we investigate the problem of designing truthful mechanisms for producing allocations with bounded envy.