I have matured a lot since I first began studying computer science. I have learned a lot of tricks and things that could have really helped back in the day, but that's what you deal with when you learn. I will continue to learn as I gain more experience, but I will never be perfect. I will still make mistakes, have to debug my code, have to make changes, etc. Those types of things are inevitable. The benefit of experience is learning how to solve problems faster, and how to properly design things to prevent them from happening in the first place.
When I first learned to code using Perl/CGI, I created single long scripts that accomplished the task they were created for. This is all well and good, but after doing a few I started to see patterns in redundant code that could be eliminated by creating modules or functions that did the same sub-tasks generically. When I realized this, I extracted the redundant code into its own include files and made the functions take generic parameters so I could use the code flexibly. I then went into my old scripts and tore out the old junk and replaced it with these single line function calls that reduced the code content of the scripts by a large amount. Suddenly a 500 line script was only 300 lines and much easier to read.
That realization was the notion of using good planning and design to create generic code that could be reused by most all of my scripts so I never had redundant code and everything was optimized. It was a lot of fun using my new generic code, but it was annoying to have to go back to the old bad code and convert it. If I had just done things right in the first place I would have saved a lot of time. Hindsight is 20/20...
Thanks to the experiences of making bad code, learning techniques to make code generic, and retroactively applying it to old code while utilizing it for future code, I now have a much better method for creating software. I am a huge fan of functionality. The more features the better. My wife likes to use a quote that describes my mentality: "If it ain't broke, it doesn't have enough features." That's totally me in a nutshell.
The challenge with being of the "more is more" mindset is that even if you plan things out thoroughly, once you start coding you inevitably think of something more to add or change that leads to the code you have written requiring modification already. It's kind of a bummer, but those are the trade-offs for getting more capabilities. The way to best deal with these situations, for me, is to apply generic design from the beginning so that code with similar functionality is grouped into functions and included files that can be modified independently from the main application. This flexibility potentially allows for you to add those new features in with as little effort and modification as possible. This is one of the big benefits to object oriented programming in general. You can group functionality into classes and then just change an individual class to add new goodies.
When I begin a new project I start as high level as I can and write things down with pencil (not pen, because I need to erase a lot) and paper. I picture the whole project's interface, how it works, what it can do, and what its purpose is. Once I have drawn up the screens and listed out all of its functionality, I start designing the structuring of the internals while keeping in mind reusable code and flexible design constraints. I come up with a list of groupings by functionality and define the included modules and files by that list. Then I work on the functions that will need to be created to interface with the main files, and finally I start coding those functions when the structure is thoroughly thought out.
The important thing that I have really had to focus on is not rushing into coding. I used to just want to dive right in and get my hands dirty, but I realize now how much of a time waster it is. Instead, I spend more time than I think I need to on design and construction so that I can incorporate as many last minute features before coding as possible. Only when I am really happy and confident in the design do I then start implementing it.
The time from design through completion has been reduced a lot since I have adopted this paradigm of thinking. This also allows me to revisit code that I haven't touched or looked at for a while and add new features with relative ease. To me, the design is the key part to developing quality software these days. That's a notion I would have laughed at just a year and a half ago.
My advice to you, if you are still young in the world of software development, is to spend the time up front thinking of as much stuff as you want to incorporate into the project as possible. Then break the project down into its functional components and really structure the code well for reuse and future modification. You'll thank yourself in the end. Good luck!