Computer Science vs. Programming
Really worrying situation. I do well with CS subjects but at the end of day it is all useless as I don't know how to code.
That is normal. Computer Science and Programming are two completely different things. You should not expect that studying one will not automatically make you learn the other, any more than you should expect studying astronomy will automatically make your learn how to blow glass.
It has been said that Computer Science is really a misnomer, and is akin to calling Astronomy "Telescope Science" – just because computers can be used to investigate information and processes doesn't mean that they are somehow inherent to that science, just like the fact telescopes can be used to investigate planets and stars doesn't mean that telescopes are somehow inherent to astronomy.
There are some languages, for example German, French, and Italian, where the scientific discipline makes no reference to computers at all: in German, it is called Informatik, in French informatique, in Italian informatica – all are a neologism based on information and the Greek suffix -ik. In Spanish, it is called ciencias de la informática (similar to German, French, and Italian) or ciencias de la computación: note the subtle difference to English, it is the science of computation, not the science of computers. Danish uses the terms datalogi (a neologism formed by combining data with the -logi suffix as in geology, meteorology, metrology, etc.) for the stricter sense of the science of information, data, computation, and processes, and informatik for a broader inter-disciplinary view of the effects of "datalogi" on society, politics, humanity, and the broader world in general; what might be called Social Informatics in English.
As you can see, in many languages, there is a clear distinction made between "Informatics" and computers. It is a rather unfortunate accident of history that the language which confuses the two also happens to be the lingua franca for it.
… and Software Engineering
How do I learn to code to make projects? I can make small programs easily. I can't use programming to create good stuff.
You studied Computer Science to become good at Computer Science. In the same way, you need to study Programming to become good at Programming. In addition, you also need to study Software Engineering to become good at what they call "programming in-the-large", i.e. programming large and complex systems collaboratively in a team over a longer period of time.
Books that teach Programming
What should I start with? Recommend a book and course that teaches me enough to get started with projects.
I am personally a huge fan of How to Design Programs (HtDP). It teaches you, well, how to design programs. And it does this by giving you recipes to follow for how to analyze problems, solve them, transform them into algorithms and further into working programs.
Note that "recipe" is basically another word for "program", so in other words, the book teaches you programs for humans to run in their heads in order to generate programs to be executed by computers. How cool is that? :-)
Note that the current, second edition of HtDP does not include Imperative Programming. That material was present in the first edition, but was removed to be covered in an as-of-yet unwritten second volume How to Design Components / Classes. However, if you are interested in Imperative Programming, the first edition of HtDP is still available as is an early draft of How to Design Classes.
Note that HtDP assumes no programming knowledge and is targeted at high school students. But don't let that stop you: it just means that you'll probably be able to finish some early chapters faster, but I don't think you will be bored.
Concrete Abstractions is also a good read along similar veins. Like HtDP, it doesn't assume any programming knowledge.
Another book that you might hear mentioned is Structure and Interpretation of Computer Programs aka SICP. It is one of the greatest programming books ever written, and again, it doesn't assume any programming knowledge.
It is, however, geared to complete newbies who study at MIT. And so, while it does not assume any programming knowledge, it does assume quite a bit of domain knowledge, e.g. in the fields of electrical engineering, physics and math. Note: these have nothing to do with the concepts being taught, they are just needed to understand the exercises and examples. So, it might be better to read HtDP or Concrete Abstractions first, and then read SICP. However, since you are familiar with CS in general, I don't think you will have many problems.
Learning Programming vs. Learning Programming Languages
All three of these books teach Programming. What they don't teach, is a complex Programming Language. I personally think that is a good thing. Learning a programming language takes a lot of time, just like learning anything takes a long time. So, any second you spend learning a programming language, you are not spending learning programming.
In my opinion, learning programming is more important than learning programming languages. You will almost never be in a position to choose your programming language anyway, and will have to learn whatever gets thrown at you. Let's face it: almost nobody gets hired to develop something completely novel completely from scratch based on no constraints whatsoever. You will almost certainly join an already existing team working on an already existing system, where the programming language to use has been chosen and set in stone long ago.
All three (series of) books I mentioned above use very simple languages that can be fully taught / learned and understood in minutes.
How to learn Software Engineering
What all three books also (unfortunately) don't teach is Software Engineering. HtDP does at least teach Testing and a bit of Debugging, and even suggests something similar to Test-Driven Development / Design. HtDP also teaches documenting requirements.
Unfortunately, and much to my regret, I cannot recommend any books or courses for learning Software Engineering. That is not to say there aren't any, only that I don't know of them. I, personally, consider myself to have been taught Software Engineering very badly, and been permanently damaged.
There is the Software Engineering Body of Knowledge (SWEBoK) and the Software Engineering 2004 (SE2004), but I feel that both of those are not really up-to-date with modern real-world Software Engineering practices and ignore modern real-world Software Engineering challenges. (For example, it is almost impossible nowadays in our interconnected world to disentangle good software engineering from security engineering.)
Personally, I found it very enlightening to study both the Manifesto for Agile Software Development and the Twelve Principles of Agile Software and the Manifesto for Software Craftsmanship. Please note: I am not saying that Agile and Software Craftsmanship are the be-all-end-all of Software Engineering. I am merely saying that I personally found it interesting to study those two documents and their history, and think about what experiences made the people who wrote them become convinced that this is the best way to engineer software. The authors of both of those documents are experienced software engineers, who have both delivered many successful large, complex projects, and have seen many large, complex projects fail. What made them draw those particular conclusions from those failures and successes? I find that fascinating.
However, here is the hard truth: whether it is Computer Science, Programming, Programming Languages, or Software Engineering, or even playing an instrument, painting, woodworking, sports, etc., the only way to truly master something is lots of deliberate practice. Note that the "deliberate" part of "deliberate practice" is important. Sitting and noodling "Knocking on Heaven's Door" on your acoustic guitar is not practicing guitar, that's playing a song. Going to the beach is not practicing swimming.
And writing small tools for yourself is not practicing programming, that's working. Someone once said: if you don't delete what you wrote immediately after writing it, then you're working, not practicing.
Some people swear by so-called Code Kata. Kata are an idea from several martial arts; they are a series of formalized techniques that are practiced repetitively and are performed in front of an audience.
Similarly, Code Kata are simple exercises that you solve over and over again, ideally at some point performing them in front of an audience. Some people have tried setting their performances to music, such that, for example, some big refactoring which results in the deletion of a bunch of code coincides with a big climax in the piece.
One of the best ways of learning Software Engineering, or well, pretty much anything, is to find a mentor. Especially in the Software Craftsmanship movement, which models itself somewhat after the concept of Master Craftsmen in e.g. carpentry, mentorships / apprenticeships are fairly common, so you might find a mentor in one of their programs. You don't have to go to such extremes as e.g. Corey Haines did, who actually spent the better part of a year on something akin to traditional Journeyman Years, but it wouldn't be the worst way to learn either.
A note on programming languages: there are thousands of them. It is impossible to learn them all. However, programming languages implement Programming Paradigms and there are only a few dozen of those. Programming Paradigms, in turn, are composed of Concepts, and there's only about 20 of those. So, if you understand Concepts, that will help you understand Paradigms, which in turn will help you understand Languages, without actually having to learn dozens of paradigms or hundreds of languages.
Peter van Roy has made a nice poster with the 34 most important Programming Paradigms. A more thorough explanation of that poster is contained in the article Programming Paradigms for Dummies: What Every Programmer Should Know which appeared as a chapter in the book New Computational Paradigms for Computer Music, edited by G. Assayag and A. Gerzso. Some examples of paradigms include the well-known imperative programming, functional programming, ADT imperative programming, ADT functional programming, and sequential object-oriented programming / stateful functional programming, but also lesser known ones such as concurrent constraint programming, active object programming / object-capability programming, constraint (logic) programming, or multi-agent dataflow programming.
Paradigms, in turn, are composed of Programming Concepts. E.g. sequential object-oriented programming is composed of record, closure, cell, and procedure. If you add thread, you get concurrent OO programming. The most important concepts include some well known ones like cell (state), procedure, and record, but also lesser known ones like by-need synchronization, nondeterministic choice, or solver.
I would argue that understanding about 1/3 of those 18 concepts allows you to understand about 90% of the top 100 mainstream languages, if not more.
This is an interesting example, where Computer Science concepts, more specifically concepts from the field of Programming Language Theory are directly helpful in an industrial context, helping you to quickly learn programming languages by analyzing their paradigms and concepts.
By far the best explanations of programming paradigms are found in Peter van Roy's works. Especially in the book Concepts, Techniques, and Models of Computer Programming by Peter Van Roy and Seif Haridi. (Here's the companion wiki.) CTM uses the multi-paradigm Distributed Oz programming language to introduce all the major programming paradigms.
My own experience has been that really understanding a programming paradigm is only possible
- one paradigm at a time and
- in languages which force you into the paradigm
Learning Concepts through Paradigms through Languages
Ideally, you would use a language which takes the paradigm to the extreme. In multi-paradigm languages, it is much too easy to "cheat" and fall back on a paradigm that you are more comfortable with. And using a paradigm as a library is only really possible in languages like Scheme which are specifically designed for this kind of programming. Learning lazy functional programming in Java, for example, is not a good idea, although there are libraries for that.
Here's some of my favorites:
- object-orientation in general: Self
- prototype-based object-orientation: Self
- class-based object-orientation: Newspeak
- static class-based object-orientation: Eiffel
- multiple dispatch based OO: Dylan
- functional + object-orientation: Scala
- functional programming: Haskell
- pure functional programming: Haskell
- lazy pure functional programming: Haskell
- static functional programming: Haskell
- dynamic functional programming: Clojure
- imperative programming: Lua
- concurrent programming: Clojure
- message-passing concurrent programming: Erlang
- metaprogramming: Racket, Scheme
- language-oriented programming: Intentional Domain Workbench, unfortunately defunct; try JetBrains MPS instead
What about syntax?
The most-often talked about part of programming languages are their respective syntaxes. Personally, I also find them the least important. You can learn to ignore syntax, or if it really bothers you, write a preprocessor. You cannot easily change a language's semantics or type system.
Most mainstream languages nowadays borrow their syntax heavily from a small number of influential ancestors: blocks structured with curly braces from B (e.g. C, C++, Java, C#, ECMAScript, PHP, Rust, Go), blocks structured with keywords from Algol (e.g. Ruby), blocks structured through indentation from ISWIM (Python, Haskell), member selection with
. from … I don't know where :-D (almost all of them), message sending with whitespace from Smalltalk (Objective-C), types in front of identifiers from Algol, types after identifiers separated with a colon from maths, types after identifiers separated with whitespace, plus the Lisp family and ML family.
Once you have seen some of them, you have seen (almost) all of them, and they are easy to learn. Also, modern code authoring tools help tremendously with intelligent autocompletion for writing code, and specific syntactical intricacies are often not that important for reading and understanding code. For example, many coding styles require parenthesizing complex expressions, so that knowing the subtle differences in operator precedences between different languages becomes a non-issue: except in interview puzzles designed to stump you (for which you can prepare and memorize them, and promptly forget them after the interview), or in really badly written code (which you need to carefully study anyway, since operator precedence typically is the least of your problems), you simply will not encounter them.