Computer Science, as taught, is a combination of Mathematics and Computer Programming. Mathematics is the part that is the theory of computing, and programming is the art of applying it.
I'll dub the Mathematics party "theoretical", and the programming part "applied".
Now, both parts are hard. But are hard for somewhat different reasons.
Applied Computer Science
Programming is hard for a few reasons. Every programming problem is judged first not by your instructor, but by the world's most harsh marker; the compiler/interpreter. Every program you write ends up being instructions for some idiot-savant computer to follow.
It is like trying to write an essay in English, but if your letters aren't formed right, you have any spelling mistakes, or there is any grammar errors, the teacher isn't able to actually read your essay. Instead, they are forced to examine each word in isolation, then after verifying them themselves they can look at each sentence in isolation, etc.
This creates a sharp discontinuity in your initial ability to get things done. The syntax of the languages you are using is, almost unavoidably, harsh on relative beginners.
Once you pass that tier, you end up with yet another situation where there are discontinuous errors that are harsh on relative beginners.
Programming is the art of managing insane amounts of complexity while telling a complete idiot how to solve a problem, exactly.
The ability for computers to do things exactly as described, extremely quickly, means programs are some of the most complex things humanity has ever designed on purpose. As someone learning programming, you end up building increasingly complex things. Each stage of this effort has to not only be good, but perfect, as errors in a lower tier of complexity will compound and make the next one impossible to do.
When you write a statement, your variables and operators can't have errors, or the statement won't work.
When you write a function, your statements can't have errors, or the function won't work.
When you write an algorithm, your functions can't have errors, or the algorithm won't work.
When you connect algorithms into a simple program, the algorithms can't have errors, or the program won't work.
You have to reach near-100% reliability on each tier, and learn how to deal with the less-than-100% reliability, before you can get any kind of reliability on the next.
To handle this, programmers have come up with increasingly abstract patterns. Each of these patterns is [b]hard[/b] to master, as they encapsulate extremely complex problems with seemingly arbitrary rules.
A short list of these patterns that programming students find hard to master might include:
- Variables
- Assignment vs Equality
- Control flow and looping
- Recursion
- Sub procedures/functions
- Pointers and indirection
- Code generation and macros
- Resource management and lifetime
- Threading
- Encapsulation
- Purity
- Mutability
- Asynchronicity
(in no particular order, and not comprehensive).
People learning programming often run into one of these, and just can't get it. And unless you get it, you can't get past that barrier, and often fail out. (One of the advantages of Java and other managed languages to teach programming is that you can neglect much of pointers and resource management and teach other stuff at beginner levels; the downside is you graduate people who don't have to learn that stuff.)
These are all levels of abstraction programmers have developed to manage increasingly complex programs. Strategies that work at lower levels of complexity fail at higher ones, so students who are using "ill advised" strategies can easily pass a course then fall apart in the next one.
Theoretical Computer Science
This branch is all about understanding the idea of computation, as opposed to the art. You do need it in order to do certain kinds of applied computer science problems, so even in "professional" courses it is taught. And as academics are teaching it, often it is included anyhow, as the theoretical computer science often is needed to expand the entire field of knowledge of computer science.
This is a branch of mathematics, and like most kinds of mathematics requires iterative mastery. In order to do multiplication effectively, you have to not only be able to do addition, but you have to master it. That very simple step is mirrored along the entire tower of mathematical knowledge.
In many other fields, you can muddle along with the previous foundation layer having issues, and maybe patch it up later. But when you try to do that in mathematics things just fall apart.
Mathematics in much of the world is initially taught by people who hate and are bad at mathematics -- elementary school teachers. So we end up with a lot of people with a poor foundation in ciphering showing up in secondary education, then muddling though classes without every achieving mastery, then deciding to go into a lucrative field (programming). And without mastery of the previous tiers of mathematics, the theoretical computer science they teach is extremely difficult.
The student, whose goal was "get a highly paid programming job", finds this abstract computer science both hard and difficult to connect to their goal. Their problems with the material are foundational; they don't have the background required. The educators problem is that teaching that background requires fixing their 10 years of primary and secondary mathematics the student came in with.
Cutting theoretical computer science for applied computer science courses is plausible, but then the students aren't able to predict very well how to make programs do new tasks without taking forever to do them (programming efficiency, algorithmic complexity), or deal with logic problems surrounding multi-threaded programming, or a myriad of other important skills in the applied field.
Real World
Programming is currently a lucrative, expanding field. This has a number of impacts.
Students are drawn to it not out of intrinsic interest or inclination, but because they want to make money at it.
Experts are harder to convince to be teachers, as not-teaching is lucrative, and teaching is difficult.
The number of students grows over time, so the previous generation of students (who supply the teachers) is smaller. This makes the problem of finding teachers harder.
The field itself is young. With other fields, we know millions of ways not to teach it; with computer science, we haven't had time to make as many mistakes and refine our education.
On top of that, the profession is a relatively solitary one. So it draws introverts; convincing introverts to have a career teaching students is an extra problem, reducing the pool of potential teachers further.