I just read a great article (linked to on the chess Stack Exchange), where the author Dan Heisman talks about the different levels of chess playing that one will generally engage in.
The first is the “flip-coin” level of play. The player knows the rules of chess and knows how to make legal moves, but has no sense of strategy at all. If both players are “flip-coin” level, you can expect a random outcome to the match.
The second level of chess play is the “hope” level. With this you see opportunities on the board, but you hope that your opponent is unaware of them. This player will have a better chance of winning if matched up with a “flip-coin” player. Most people won’t progress past this stage, but they’ll win enough games against flip-coin players and some other hope players that they’ll be satisfied enough with their performance.
The third level is “real” chess. This is the level where you evaluate and anticipate your opponent’s moves, and so there should be no move they make that you haven’t already considered. This requires the most work, but offers the greatest likelihood of the 3 levels of victory.
The parallels to software development are uncanny.
The “flip-coin” developer can’t really give you an estimate of how long something will take. They know how programs work generally, but their debugging lacks any sort of scientific rigour. They make random changes until the programs finally work (maybe). They know enough that they can work on software, but their results are inconsistent.
The “hope” developer scoffs at the flip-coin developer. They find bugs with well-placed print statements. They might even know how to write a test (although they can only hope that it actually checks the things it needs to). They don’t defensively program, but fix things when they break. They’re competent enough that for the most part the projects they work on shouldn’t be total disasters.
The “real” developer (I just felt a chill writing that - please just stay with me for the rest of this) can give you accurate estimates, because they can evaluate and anticipate what will be necessary for the project. They are able to think methodically, and are able to anticipate the ways that a program can fail (which helps them write great tests). They can evaluate the requirements and come up with a design flexible enough for future movement. They will take the extra bit of time to think a problem through. They may work more up front, but their projects generally succeed.