I've been programming for a little bit over a year, and have some experience in JavaScript, PHP, a tiny tiny amount of Java, and mostly C++. I've noticed that I've gotten to the point where I can program for, say, a couple of hours without checking to see if it runs, and then when I'm done for the night everything will build just fine. At least in the general case. That's very different from when I first started, and I'd have to click Buld and Run every 10 minutes just to make sure I'm not writing buggy code on top of already buggy code.

But now I'm starting to see that even though my code will build fine, there will almost always be some logical errors that, upon review, I say to myself "Why would I even write that? It's a simple mistake, but it makes no sense. I should've known from the start." I'd like to be able to change that about myself, and not make so many silly, illogical errors so that my first run of code on any project will be less buggy and weird.

Any suggestions on fun or interesting ways to change this? I've heard of Code Complete, but it's quite expensive. I thought maybe I should learn a bit more about actual computer science before diving into that book. I've never really enjoyed project euler. My mom loves doing things like cryptograms, but I never really had much fun with them. So other than stuff like that, what do you guys do that you think might improve your logical abilities?

You could request Code Complete at your local library for free; however, I don't think it teaches spotting logic errors - rather, it teaches how to code cleanly to increase maintainability and decrease bugs from things like poor allocation/deallocation strategies.

Could you give some examples of the kind of logical errors you are trying to avoid?

In terms of just exercises most puzzle games are logically oriented. Sudoku immediately comes to mind. When you first start playing Sudoku you probably have to think for a minute just to decide if it's even possible, then as you play you start to develop mental guidelines that help you solver faster. Over time you can get ridiculously fast at it just by streamlining your thinking.

In terms of coding as a whole, there's no way to completely control all of the influences that effect your brain and/or thinking process. Unit testing is not a sign of weak capability, it's a sign that you're wise enough to find bugs early rather than indulge your ego and assume that you'll never have a problem in your code. It's true that you get better at coding over time, but that's because over time you're indulging in the best exercise possible: coding.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

It's focus, you have the skills to execute but your not focusing on the details to see the ramifications. Create and foster an attentive pause in your programming routine where you ask and answer questions about what and how your doing something. Like how will it affect other submodules? Is there a better object model than this? Is this the fatest / easiest way to my goal? etc.. just asking questions will force you to go out of autopilot mode ur in and that will expand the depth of your understanding and hopefully catch these design issues early on.

A couple of coding tips which you may already practice, but will reduce the bugs in your code:

1. Clearly comment your code. Comments will help in debugging problems, but they also help to avoid bugs in the first place as they encourage the coder to spend a bit more time thinking about what the code needs to do / is doing.

It's focus, you have the skills to execute but your not focusing on the details to see the ramifications. Create and foster an attentive pause in your programming routine where you ask and answer questions about what and how your doing something. Like how will it affect other submodules? Is there a better object model than this? Is this the fatest / easiest way to my goal? etc.. just asking questions will force you to go out of autopilot mode ur in and that will expand the depth of your understanding and hopefully catch these design issues early on.

That's a great answer. I think you're right. It's probably more focus than logic.

Human minds are prone to errors. I think it's just mental fatigue. Stepping back occasionally and review your code is just as important as writing code. Don't hammer it away for a few hours. I always stop after a few minutes of coding, reviewing what I wrote, asking myself if I'm happy with it, constantly reviewing my approaches and revising code along the way.

Sometimes, I would write a single class, and without testing it first, I stop coding and step away from the PC, and rethink if it's something that I want or if I miss anything. Then come back finish what I need, then finally test it, and fine tune it after I am sure that I want to go in that direction.

I have the same problem. I dont like the feeling of debugging the program after it didnt work, and finding like 5 errors and fixing them, just to find that nothing changed and its still broken. -,-

Im going to be applying these magical tools from now on to avoid this:
-Use asserts
-Use smart pointers
-Error somehow if an object is used in an (likely) invalid(/nonintended?) state and try to prevent those situations from being possible.

Thinking logically and knowing how to program are two separate but parallel paths on the way to becoming a good programmer. Being able to think in logical steps is one thing I was glad I was good at before I picked up my first programming book. Specifically training your logical brain is a bit different from learning to program though.

This might sound odd, but you might think about taking an Intro to Philosophy class or Intro to Logic class at one of your local colleges. If you can sort through the material, you should come away with a much better understanding of logic. It helped me quite a bit, and it was fun in the process.

Another good class to take is a college-level algebra class. I know I HATED algebra in high school and college, but thinking back, it was the really difficult problems that really give your logical brain a good work out. Teachers always want you to show your work, but I couldn't stand not taking shortcuts or just working it out in my head and throwing down the right answer. But if you really focus on committing each step to paper, your brain starts to understand better and better how to take a big problem and break it down into smaller and smaller steps. In retrospect, I'm pretty grateful 12th grade algebra teacher was such a hard-#^@ when it came to showing my work. Improved my thought-process.

- draw pictures, diagrams, scribbles, ... it doesn't matter if its a picture or squares representing memory, with lines for pointers ... or if its a UML diagram of an activity, a flowchart, or just a scribble with 5 circles and some lines representing your logical state machine. Mental and physical visualization can help you think clearly and keep your thoughts focused.

As said before - pause and review

- take the time to think before you code, while you code, and after you code. You can vary the amount and the ratio based on your success and how repetitive your task is: when doing something similar over and over again the up-front thought needed is nearly zero, the coding is fast, so half the time will be in simple review / double-check, when solving a problem you've never even completely understood before, with ramifications you may not yet have noticed, you'll need a lot of up front time, and also a lot of coding time (where half the problem will show up) ... then a CAREFUL review when you think you are done, to identify what you have missed.

Know the difference between what you KNOW (for sure), what you THINK you know, and what you know you DON'T KNOW. As you plan, or read and review code - this distinction guides how much focus you need to bring to the table.

Also - the longer you spend debugging the same issue ... the more likely it is something "stupid" ... which really means, something in an area you are making an assumption about, that for whatever reason is not currently true (like a problem with code you thought was working yesterday).

Decompose ... separate ... divide and conquer.

When solving a problem, break it down into smaller chunks .. but just as important as chunk size or complexity is how SENSIBLE and therefore understandable and memorable a chunk is. 4 chunks for - parsing the file into memory, validating the contents for validity, reconciling the contents against the previous day, and updating the processing statistics - might be easier to code and debug for a really complex algorithm, then for instance something that parses a header and updates statistics, then parses the body, reconciles it and updates statistics, then parses the footer, validates the whole, and saves a copy ... for a simple algorithm Even though the first might have 1000 lines of code and the later 200 ... the brain can only handle so many thoughts at any given time ...

and you can take chunk 1 of the 1000 line idea ... and further break it down ...

which leads to the last suggestion

NAME everything ... CLEARLY ... and name it well. And as your design changes KEEP YOU NAME UP TO DATE. a function "UpdatePlayerPosition" that moves the player, unless it is under AI control, and then if raises an event for the AI which will do the update later in the "HandleAI" function instead - suggests someone grew the design without making sure the names and layers still fit together right. And a function called "ComputeTaxAndUpdateOrder" is in fact better than "ComputeTax" or "UpdateOrder" if in fact it does both things, regardless of what the function USED to do. Taking the time to name things well, means you also take the time to understand them correctly. Which pays huge dividends in reducing the number and severity of stupid errors.

I'm usually doing fairly well with my negative attitude. Don't look at it and think about how it's going to work, try to think of all the things that could go wrong and what will happen if they do. Paint it black, consider worst case scenarios and never be complacent and think "this seems so neat, it just has to be the 'right' solution".

As an alternative, do it by the book. Determine requirements, write tests and start planing and coding the actual code. It's going to cost you a lot of time, but generally writing tests can you let you see lots of issues to keep in mind during development.

I have been programming for 35 years, and still have the same problems. So I probably will never learn. This is what I do (which depends a little on whether I am working with a free time home project or working with a professional product):

Make small changes, and make sure they always work. If something big is needed, I break it down to small changes.

Switch between open and closed mode. When I am sitting at the keyboard, I am in closed mode, and can usually only implement what I have already decided and planned. I leave the keyboard and go somewhere else to get into open mode. In open mode, I let the brain free to think around. Perfect for me is to go for a run. That is when I come up with the good design ideas. Or sit down in the sofa with a sketch of paper.

Write code as if you are going to show it to a "beginner". When you explain the code to someone, and they say "of course, how else would it be done", then there is a high chance you got it right.

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." --Brian W. Kernighan

Be prepared to re-factor the source. When you start small, and expand, you inevitably come to a point where you find the original design is no longer easy to maintain or flexible. Get into open mode and find out how to make it look pretty. Still, I usually start with a simple design, because it can be hard in the beginning to know all the requirements.

Source code is like a piece of art. If you feel proud looking at it, there is a higher chance the design is good.

Read a lot on discussion forums. Join them, and express your view. You will either help others, or get feedback and learn new things yourself.

Work both top-down and bottom-up. When you work top-down, use stubs that makes the application possible to compile and run. I find it common, when in top-down mode, that I will have to wait with something while something else is being completed. It is easy to forget to go back and complete that other thing, so I always leave a "TODO" comment in the source code to help myself.

Keep things small. Classes, functions, files, etc. A class declaration (in C++) should fit on one page. A function should fit on one page. Every indentation level used in a function double the probability of bugs for me.