I was reading a Reddit post today in a thread about learning new skills and was inspired to consider my own answer to the question: "how does one learn to be a programmer?"
The Logical Programmer
The logical programmer is fundamentally a philosopher.
Any extant philosophy can be applied to some aspect of software development, or programming more broadly if you bend just so, but in particular, programming is an exercise in creating ordered systems in a formal syntax. Implicitly, software is an expression of logic systems fist laid down in classical philosophy and formalized for mass consumption by The Laws of Thought by George Boole. A programmer in her aspect of philosopher builds systems of thought which follow a consistent line of reasoning, and it is in striving for an internally consistent philosophy that she produces Code, with a capital "C" rather than code.
In order to become a logical programmer, studying philosophy is a good first step. The experience of constructing philosophical proofs, one of the first things taught in many courses on the subject, is the programmer's most basic skill. Once the basics of inference and logic are well understood, it becomes second nature for the logical programmer to reason about ever more complex systems. A practiced philosopher may often elect to construct his own framework of thought to inspire his work.
The Persistent Programmer
The persistent programmer practices diligently.
As with any craft or skill, practice makes all the difference. In Malcolm Gladwell's book, Outliers: The Story Of Success, it's claimed that 10,000 hours of practice marks the point at which one becomes a "phenom;" not just an expert, but a paragon of skill. For the persistent programmer that's not just time spent working at a programming job. In that setting more time is often consumed by tasks wholly unrelated to writing code than practicing the craft. Effective practice will reward effort with understanding.
Persistence is not only about practice, but as an initiate it should be the first focus. As a programmer, you will often find yourself up against very difficult problems. Some will have been solved before, and some will be completely novel, but the work to solve difficult problems is as much or more an exercise in persistence than cleverness.
It’s not that I’m so smart, it’s just that I stay with problems longer.
- Albert Einstein (attributed)
It's important to recognize that programmers practice not only to build upon the corpus of their knowledge and skill, but also to simply keep up with an ever-shifting landscape of tools, technologies, methodologies, and languages. As the complexity of the domain grows, it becomes absurd to think that any single person can master all disciplines in their entirety; it would be like trying to drink a waterfall.
Instead, programmers practice the most relevant skills with the most applicable tools for their current endeavors, focusing their efforts for maximum benefit in the short to medium term. In this way, expertise is fluid, and localized. Diligent practice is the paddle that keeps the persistent programmer from being washed down river.
The Practical Programmer
The practical programmer is comfortable with abstraction.
Programmers must be practical as a matter of course. Pragmatism with regards to the constraints of time and resources is a necessary and learned skill but for programming it mostly comes down to abstraction. A programmer who is comfortable with abstraction will not worry overmuch about the fine details of a system, but paint with broad strokes. There is not a person alive who understands computers and software in their entirety; from the bare silicon to your eyes. When someone explains how a web browser works, or how a memory bus functions, it's never (or rarely) to a level of completion such that someone could build the thing being explained from scratch, and that's ok because programmers, especially experienced programmers, become comfortable with abstractions early in their experience and learn to leverage those abstractions for a variety of purposes.
Abstractions are the programmer's bread and butter. Programmers speak in abstractions to peers in order to convey complex ideas succinctly and build upon them in the same way. By building on metaphors, the practical programmer not only reduces large problems to their basic components, but also shapes her philosophy from an ephemeral collection of broad ideas into a working model for practical application. Abstractions in software in the form of common libraries, tools, or frameworks lets the programmer focus less on the minutia of an application, and more on the problem at hand.
Since curiosity is so often a pronounced trait among aspiring programmers, the instinct to ask "how does that work?" is strong, and to be encouraged. Looking in depth into a particular function, algorithm, or tool leads one to appreciate the subject of one's study for it's utility and grace and may provide insight into how the virtues of a thing may be applied elsewhere, or how it's vices may manifest. When first learning to become a programmer, it's the tendency of many to become overwhelmed at the sheer depth and breadth of the discipline, and so neophytes, like children, easily fall into a trap of endlessly asking, "how?" and "why?" While curiosity is to be encouraged, it's important to remember that not everything needs to be perfectly understood in order to be useful. Accepting that some things work simply because they do, and allowing the abstraction of it all to be a comfort rather than a distraction will serve a nascent programmer far better in the near term.
The Shrewd Programmer
The shrewd programmer does not accept answers at face-value, but tests them.
With the rise of the search engine, it's become ever easier to find answers to questions that arise in the course of solving a problem. The shrewd programmer sees these answers, or hears them from others, but doesn't not just nod and accept them without question. Rather, she will play with these answers and discover for herself what holds true. Experimentation is at the heart of learning to be a programmer. It is never enough to simply know a thing because it's been told. A new programmer should satisfy the desire to understand a thing by using it; probing it's limits and capabilities.
Never stop experimenting, and trust nothing completely that you have not verified. This is the corollary to The Practical Programmer, and speaks to the temperance of curiosity with pragmatism. Where abstractions let programmers be creative and scaffold the creation of complex systems, they also aren't always correct, and few (if any) are worthy of unconditional faith.
This is not to say that The Practical Programmer is a fool. If the new programmer never fails to ask "why?" or "how?" then no broader understanding may be gained until the utmost depth of a thing has been explored and understood, and it is The Practical Programmer who understands that breadth of knowledge and experience makes one flexible and adaptable. Likewise, if one simply accepts abstraction out of hand, and does not question the thing, it may break down in ways that can't easily be understood. The Shrewd Programmer learns to test assumptions, metaphors, and abstractions in such a way that they can be confident in them without the need to fully understand their inner workings completely. The Shrewd Programmer gains knowledge by responding to many of her own questions with the same answer, "let's find out!"