Linear Block Codes Digital Communication Problem
In this answer, I will focus on error correction. I will use as little coding theory as possible, and I'll try to approach the problem from the point of view of a novice taking their first steps in code design.

Our objective is to find the best (8,4) linear block code possible, that is, one that corrects as many bit errors as possible. Is it possible to know in advance how good an (8,4) code can be? To answer this question, we have an invaluable tool: the Hamming bound. It says that a $(n,k)$ code that can correct $t$ errors must satisfy

$$ \sum_{i=0}^t \binom{n}{i} \leq 2^{n-k}. $$

It is easy to write a program that takes $n$ and $k$ as arguments, and outputs the largest $t$ that satisfies the bound. For $n=8$ and $k=4$, we find that the largest possible $t$ is 1. Note that, since the minimum distance is given by

$$ d_{min} = \lfloor (t-1)/2 \rfloor, $$

we still don't know whether the best code has $d_{min}=3$ or $d_{min}=4$. Both will have the same error correcting capabilites, but the code with minimum distance 4 has additional error detecting capabilities.

(Note that, even if there exist numbers $(n,k,t)$ that satisfy the Hamming bound, it is not guaranteed that a $(n,k,t)$ code actually exists. However, if the bound is not satisfied, we know for sure that no such code exists.)

Now that we have a goalpost ($d_{min}=4$), we can focus on finding a code that achieves it. We know that the generator matrix $G$ has $k$ rows and $n$ columns. In its systematic form, $G=[I_k|P]$, where $P$ has $k$ rows and $n-k$ columns. For our (8,4) code, we have to find a matrix $P$ with 4 rows and 4 columns. 

Since we don't have any other tools at this point, we can try a brute-force approach, since there are "only" $2^{16}$ possible matrices $P$ (actually a bit less, since we don't want to repeat any columns). Let's try this matrix:

$$ P=\begin{bmatrix}1 &amp; 1 &amp; 1 &amp; 1 \\ 0 &amp; 1 &amp; 1 &amp; 1 \\ 0 &amp; 0 &amp; 1 &amp; 1 \\ 0 &amp; 0 &amp; 0 &amp; 1\end{bmatrix}. $$

It is not hard to write a program that generates all 16 possible code words using $P$. The final step is to verify if our code has $d_{min}=4$. This is easy, since the code is linear; all we have to do is find the non-zero code word with smallest weigth (with smallest number of ones). For the matrix $P$ given above, it turns out that $d_{min}=2$, so the code is not the best possible.

It is easy to find generator matrices with $d_{min}=3$; extending the distance to 4 takes a bit more work.

In conclusion:


The Hamming bound can be used to determine what values of $t$ and (approximate) $d_{min}$ we can expect from a code with small $n$ and $k$. For large $n$ and $k$, the achievable value of $t$ may be much less than that given by the bound. Surprisingly, determinining $t$ for given $n$ and $k$ is (for the general case) an unsolved problem.
Design by brute force (by trying many $P$ matrices) is feasible only for small codes. Even for an (8,4) code you may have to try more than 60,000 possibilities. The number grows exponentially with the code size. The amount of work required to generate the entire code, and to determine $d_{min}$ by inspection, soon grows so large that it becomes entirely unfeasible.
It is for this reason that so much research has been done into techniques that allow for less complex design of large codes. When you study other codes, such as cyclic and convolutional codes, keep in mind this motivation. You probably shouldn't try to solve your problem using the brute-force technique outlined here except to get a feel for the sheer magnitude of the computational complexity involved in the design of good codes, and also in their encoding and decoding procedures.